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