The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

OverViewItem.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/OverViewItem.vala.
Editable .notdef character
1 /* 2 Copyright (C) 2012 2014 2015 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 using Cairo; 16 using Math; 17 18 [CCode (cname = "draw_overview_glyph")] 19 public extern bool draw_overview_glyph (Context context, string font_file, double width, double height, unichar character); 20 21 namespace BirdFont { 22 23 public class OverViewItem : GLib.Object { 24 public unichar character = '\0'; 25 public GlyphCollection? glyphs; 26 public double x; 27 public double y; 28 29 public bool selected { 30 get; set; 31 } 32 33 public CharacterInfo info; 34 35 public static double DEFAULT_WIDTH = 100; 36 public static double DEFAULT_HEIGHT = 130; 37 public static double DEFAULT_MARGIN = 20; 38 39 public static double width = 100; 40 public static double height = 130; 41 public static double margin = 20; 42 43 public static double glyph_scale = 1.0; 44 45 public VersionList? version_menu = null; 46 Text label; 47 48 private Surface? cache = null; 49 50 public static Surface? label_background = null; 51 public static Surface? selected_label_background = null; 52 public static Surface? label_background_no_menu = null; 53 public static Surface? selected_label_background_no_menu = null; 54 55 public OverViewItem () { 56 } 57 58 public void set_character (unichar character) { 59 this.character = character; 60 } 61 62 public void set_glyphs (GlyphCollection? gc) { 63 glyphs = gc; 64 } 65 66 public void generate_graphics (bool generate_versions = true) { 67 if (glyphs != null && generate_versions) { 68 VersionList versions = new VersionList ((!) glyphs); 69 70 versions.add_glyph_item.connect ((glyph) => { 71 ((!) glyphs).insert_glyph (glyph, true); 72 }); 73 74 versions.signal_delete_item.connect ((glyph_index) => { 75 OverView v = MainWindow.get_overview (); 76 version_menu = new VersionList ((!) glyphs); 77 v.update_item_list (); 78 GlyphCanvas.redraw (); 79 }); 80 81 version_menu = versions; 82 } 83 84 info = new CharacterInfo (character, glyphs); 85 86 if (glyphs == null) { 87 label = new Text (); 88 } else { 89 label = new Text ((!) character.to_string (), 17); 90 truncate_label (); 91 } 92 93 draw_background (); 94 } 95 96 public void draw_glyph_from_font () { 97 if (glyphs == null) { 98 return; 99 } 100 101 Glyph g; 102 Font font; 103 double gx, gy; 104 double x1, x2, y1, y2; 105 double scale_box; 106 double w, h; 107 double glyph_width, glyph_height; 108 Surface s; 109 Context c; 110 Color color = Color.black (); 111 112 g = ((!) glyphs).get_current (); 113 114 if (g.overview_thumbnail != null) { 115 cache = g.overview_thumbnail; 116 return; 117 } 118 119 w = width; 120 h = height; 121 122 scale_box = width / DEFAULT_WIDTH; 123 124 s = Screen.create_background_surface ((int) width, (int) height - 20); 125 c = new Context (s); 126 127 c.save (); 128 g.boundaries (out x1, out y1, out x2, out y2); 129 130 if (g.color_svg_data != null) { 131 font = BirdFont.get_current_font (); 132 glyph_width = g.right_limit - g.left_limit; 133 glyph_height = font.top_position - font.bottom_position; 134 } else { 135 glyph_width = x2 - x1; 136 glyph_height = y2 - y1; 137 } 138 139 c.save (); 140 c.scale (glyph_scale * Screen.get_scale (), glyph_scale * Screen.get_scale ()); 141 142 g.add_help_lines (); 143 144 if (g.color_svg_data != null) { 145 gx = 10; 146 gy = (h / glyph_scale) - 25 / glyph_scale; 147 c.translate (gx - Glyph.xc () - g.get_lsb (), g.get_baseline () + gy - Glyph.yc ()); 148 g.draw_svg (c); 149 } else { 150 gx = ((w / glyph_scale) - glyph_width) / 2 - g.get_left_side_bearing (); 151 gy = (h / glyph_scale) - 25 / glyph_scale; 152 c.translate (gx - Glyph.xc () - g.get_lsb (), g.get_baseline () + gy - Glyph.yc ()); 153 g.draw_paths (c, color); 154 } 155 156 c.restore (); 157 158 cache = s; 159 GlyphCanvas.redraw (); 160 } 161 162 public void draw_background () { 163 double scale_box; 164 Surface s; 165 Context c; 166 167 scale_box = width / DEFAULT_WIDTH; 168 169 s = Screen.create_background_surface ((int) width, (int) height - 20); 170 c = new Context (s); 171 172 if (glyphs != null) { 173 draw_glyph_from_font (); 174 } else { 175 c.scale (Screen.get_scale (), Screen.get_scale ()); 176 177 c.save (); 178 179 bool glyph_found; 180 string? font_file; 181 182 Theme.color (c, "Overview Glyph"); 183 184 font_file = FontCache.fallback_font.get_default_font_file (); 185 glyph_found = draw_overview_glyph (c, (!) font_file, width, height, character); 186 187 if (!glyph_found) { 188 font_file = find_font (FallbackFont.font_config, (!) character.to_string ()); 189 190 if (font_file != null) { 191 string path = (!) font_file; 192 193 if (!path.has_suffix("LastResort.ttf")) { 194 draw_overview_glyph (c, path, width, height, character); 195 } 196 } 197 } 198 199 c.restore (); 200 201 cache = s; 202 GlyphCanvas.redraw (); 203 } 204 } 205 206 public static void reset_label () { 207 label_background = null; 208 selected_label_background = null; 209 } 210 211 void truncate_label () { 212 double w = has_icons () ? width - 43 : width; 213 label.truncate (w); 214 } 215 216 public string get_name () { 217 StringBuilder s; 218 219 if (glyphs != null) { 220 return ((!) glyphs).get_name (); 221 } 222 223 s = new StringBuilder (); 224 s.append_unichar (character); 225 226 return s.str; 227 } 228 229 public static double full_width () { 230 return width + margin; 231 } 232 233 public static double full_height () { 234 return height + margin; 235 } 236 237 public bool click (uint button, double px, double py) { 238 bool a; 239 GlyphCollection g; 240 bool s = (x <= px <= x + width) && (y <= py <= y + height); 241 242 if (has_icons () && glyphs != null && version_menu != null) { 243 g = (!) glyphs; 244 VersionList versions = (!) version_menu; 245 versions.set_position (x + width - 21, y + height - 18); 246 a = versions.menu_item_action (px, py); // select one item on the menu 247 248 if (a) { 249 return s; 250 } 251 252 versions.menu_icon_action (px, py); // click in the open menu 253 } 254 255 info.set_position (x + width - 17, y + height - 22.5); 256 if (has_icons () && info.is_over_icon (px, py)) { 257 MainWindow.get_overview ().set_character_info (info); 258 } 259 260 return s; 261 } 262 263 public bool double_click (uint button, double px, double py) { 264 selected = (x <= px <= x + width) && (y <= py <= y + height); 265 return selected; 266 } 267 268 public bool is_on_screen (WidgetAllocation allocation) { 269 return y + height > 0 && y < allocation.height; 270 } 271 272 public void draw (WidgetAllocation allocation, Context cr) { 273 if (!is_on_screen (allocation)) { 274 return; 275 } 276 277 cr.save (); 278 Theme.color (cr, "Background 1"); 279 cr.rectangle (x, y, width, height); 280 cr.fill (); 281 cr.restore (); 282 283 cr.save (); 284 Theme.color (cr, "Overview Item Border"); 285 cr.rectangle (x, y, width, height); 286 cr.set_line_width (1); 287 cr.stroke (); 288 cr.restore (); 289 290 draw_thumbnail (cr, x, y + height); 291 draw_caption (cr); 292 draw_menu (cr); 293 } 294 295 public void adjust_scale () { 296 double x1, x2, y1, y2, glyph_width, glyph_height, scale, gx; 297 Glyph g; 298 Font font; 299 300 if (glyphs != null) { 301 font = BirdFont.get_current_font (); 302 g = ((!) glyphs).get_current (); 303 304 if (g.boundaries (out x1, out y1, out x2, out y2)) { 305 glyph_width = x2 - x1; 306 glyph_height = y2 - y1; 307 308 if (glyph_scale == 1) { 309 // caption height is 20 310 glyph_scale = (height - 20) / (font.top_limit - font.bottom_limit); 311 } 312 313 scale = glyph_scale; 314 gx = ((width / scale) - glyph_width) / 2; 315 316 if (gx < 0) { 317 glyph_scale = 1 + 2 * gx / width; 318 } 319 } 320 } 321 } 322 323 private void draw_thumbnail (Context cr, double x, double y) { 324 if (cache != null) { 325 cr.save (); 326 cr.set_antialias (Cairo.Antialias.NONE); 327 cr.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); 328 cr.set_source_surface ((!) cache, (int) (x * Screen.get_scale ()), (int) ((y - height)) * Screen.get_scale ()); 329 cr.paint (); 330 cr.restore (); 331 } 332 } 333 334 public bool has_icons () { 335 return width > 50; 336 } 337 338 public void draw_caption (Context cr) { 339 draw_label_background (cr); 340 341 cr.save (); 342 343 if (glyphs != null) { 344 if (selected) { 345 Theme.text_color (label, "Overview Selected Foreground"); 346 } else { 347 Theme.text_color (label, "Overview Foreground"); 348 } 349 350 label.draw_at_baseline (cr, x + 0.08 * width, y + height - 6); 351 } 352 353 cr.restore (); 354 } 355 356 public void create_label_background_cache (Context cr) { 357 Context cc; 358 Cairo.Pattern p; 359 Surface cache; 360 361 // unselected item 362 cache = Screen.create_background_surface ((int) width + 1, 20); 363 cc = new Context (cache); 364 cc.scale(Screen.get_scale(), Screen.get_scale()); 365 366 cc.rectangle (0, 0, width, 20 - 1); 367 p = new Cairo.Pattern.linear (0.0, 0, 0.0, 20); 368 Theme.gradient (p, "Overview Item 1", "Overview Item 2"); 369 cc.set_source (p); 370 371 cc.fill (); 372 373 if (has_icons ()) { 374 if (version_menu != null) { 375 draw_menu_icon (cc, false); 376 } 377 378 draw_character_info_icon (cc); 379 } 380 381 label_background = (!) cache; 382 383 // selected item 384 cache = Screen.create_background_surface ((int) width + 1, 20); 385 cc = new Context (cache); 386 cc.scale(Screen.get_scale(), Screen.get_scale()); 387 388 cc.rectangle (0, 0, width, 20 - 1); 389 390 Theme.color (cc, "Selected Overview Item"); 391 392 cc.fill (); 393 394 if (has_icons ()) { 395 draw_menu_icon (cc, true); 396 draw_character_info_icon (cc); 397 } 398 399 selected_label_background = (!) cache; 400 401 // deselected item without menu icon 402 cache = Screen.create_background_surface ((int) width, 20); 403 cc = new Context (cache); 404 cc.scale(Screen.get_scale(), Screen.get_scale()); 405 cc.rectangle (0, 0, width - 1, 20 - 1); 406 p = new Cairo.Pattern.linear (0.0, 0, 0.0, 20); 407 Theme.gradient (p, "Overview Item 1", "Overview Item 2"); 408 cc.set_source (p); 409 cc.fill (); 410 411 if (has_icons ()) { 412 draw_character_info_icon (cc); 413 } 414 415 label_background_no_menu = (!) cache; 416 417 // selected item 418 cache = Screen.create_background_surface ((int) width + 1, 20); 419 cc = new Context (cache); 420 cc.scale(Screen.get_scale(), Screen.get_scale()); 421 cc.rectangle (0, 0, width, 20 - 1); 422 Theme.color (cc, "Selected Overview Item"); 423 cc.fill (); 424 425 if (has_icons ()) { 426 draw_character_info_icon (cc); 427 } 428 429 selected_label_background_no_menu = (!) cache; 430 } 431 432 bool has_menu () { 433 return glyphs != null; 434 } 435 436 public void clear_cache () { 437 cache = null; 438 } 439 440 public void draw_label_background (Context cr) { 441 Surface cache; 442 bool icon; 443 444 if (unlikely (label_background == null)) { 445 create_label_background_cache (cr); 446 } 447 448 if (label_background != null 449 && selected_label_background != null 450 && label_background_no_menu != null 451 && selected_label_background_no_menu != null) { 452 453 icon = has_menu (); 454 if (selected && icon) { 455 cache = (!) selected_label_background; 456 } else if (!selected && icon) { 457 cache = (!) label_background; 458 } else if (selected && !icon) { 459 cache = (!) selected_label_background_no_menu; 460 } else { 461 cache = (!) label_background_no_menu; 462 } 463 464 Screen.paint_background_surface (cr, cache, (int) x, (int) (y + height - 19)); 465 } 466 } 467 468 private void draw_character_info_icon (Context cr) { 469 info.draw_icon (cr, selected, width - 17, -1.5); 470 } 471 472 public void hide_menu () { 473 if (!is_null (version_menu)) { 474 VersionList v = (!) version_menu; 475 v.menu_visible = false; 476 } 477 } 478 479 private void draw_menu_icon (Context cc, bool selected) { 480 Text icon; 481 482 icon = new Text ("dropdown_menu", 17); 483 icon.load_font ("icons.bf"); 484 485 if (selected) { 486 Theme.text_color (icon, "Overview Selected Foreground"); 487 } else { 488 Theme.text_color (icon, "Overview Foreground"); 489 } 490 491 icon.draw_at_top (cc, width - 32, 0); 492 } 493 494 private void draw_menu (Context cr) { 495 if (glyphs == null) { 496 return; 497 } 498 499 if (version_menu == null) { 500 return; 501 } 502 503 VersionList v = (!) version_menu; 504 505 if (!v.menu_visible) { 506 return; 507 } 508 509 v.draw_menu (cr); 510 } 511 } 512 513 } 514