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