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.
Merge ../birdfont-2.x
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 double gx, gy; 103 double x1, x2, y1, y2; 104 double scale_box; 105 double w, h; 106 double glyph_width, glyph_height; 107 Surface s; 108 Context c; 109 110 g = ((!) glyphs).get_current (); 111 112 if (likely (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 c.translate (gx - Glyph.xc () - g.get_lsb (), g.get_baseline () + gy - Glyph.yc ()); 139 g.draw_layers (c); 140 141 c.restore (); 142 143 cache = s; 144 g.overview_thumbnail = s; 145 146 GlyphCanvas.redraw (); 147 } 148 149 public void draw_background () { 150 double scale_box; 151 Surface s; 152 Context c; 153 154 scale_box = width / DEFAULT_WIDTH; 155 adjust_scale (); 156 157 s = Screen.create_background_surface ((int) width, (int) height - 20); 158 c = new Context (s); 159 160 if (glyphs != null) { 161 draw_glyph_from_font (); 162 } else { 163 c.scale (Screen.get_scale (), Screen.get_scale ()); 164 165 c.save (); 166 167 bool glyph_found; 168 string? font_file; 169 170 Theme.color (c, "Overview Glyph"); 171 172 font_file = FontCache.fallback_font.get_default_font_file (); 173 glyph_found = draw_overview_glyph (c, (!) font_file, width, height, character); 174 175 if (!glyph_found) { 176 font_file = find_font (FallbackFont.font_config, (!) character.to_string ()); 177 178 if (font_file != null) { 179 string path = (!) font_file; 180 181 if (!path.has_suffix("LastResort.ttf")) { 182 draw_overview_glyph (c, path, width, height, character); 183 } 184 } 185 } 186 187 c.restore (); 188 189 cache = s; 190 GlyphCanvas.redraw (); 191 } 192 } 193 194 public static void reset_label () { 195 label_background = null; 196 selected_label_background = null; 197 } 198 199 void truncate_label () { 200 double w = has_icons () ? width - 43 : width; 201 label.truncate (w); 202 } 203 204 public string get_name () { 205 StringBuilder s; 206 207 if (glyphs != null) { 208 return ((!) glyphs).get_name (); 209 } 210 211 s = new StringBuilder (); 212 s.append_unichar (character); 213 214 return s.str; 215 } 216 217 public static double full_width () { 218 return width + margin; 219 } 220 221 public static double full_height () { 222 return height + margin; 223 } 224 225 public bool click (uint button, double px, double py) { 226 bool a; 227 GlyphCollection g; 228 bool s = (x <= px <= x + width) && (y <= py <= y + height); 229 230 if (has_icons () && glyphs != null && version_menu != null) { 231 g = (!) glyphs; 232 VersionList versions = (!) version_menu; 233 versions.set_position (x + width - 21, y + height - 18); 234 a = versions.menu_item_action (px, py); // select one item on the menu 235 236 if (a) { 237 return s; 238 } 239 240 versions.menu_icon_action (px, py); // click in the open menu 241 } 242 243 info.set_position (x + width - 17, y + height - 22.5); 244 if (has_icons () && info.is_over_icon (px, py)) { 245 MainWindow.get_overview ().set_character_info (info); 246 } 247 248 return s; 249 } 250 251 public bool double_click (uint button, double px, double py) { 252 selected = (x <= px <= x + width) && (y <= py <= y + height); 253 return selected; 254 } 255 256 public bool is_on_screen (WidgetAllocation allocation) { 257 return y + height > 0 && y < allocation.height; 258 } 259 260 public void draw (WidgetAllocation allocation, Context cr) { 261 if (!is_on_screen (allocation)) { 262 return; 263 } 264 265 cr.save (); 266 Theme.color (cr, "Background 1"); 267 cr.rectangle (x, y, width, height); 268 cr.fill (); 269 cr.restore (); 270 271 cr.save (); 272 Theme.color (cr, "Overview Item Border"); 273 cr.rectangle (x, y, width, height); 274 cr.set_line_width (1); 275 cr.stroke (); 276 cr.restore (); 277 278 draw_thumbnail (cr, x, y + height); 279 draw_caption (cr); 280 draw_menu (cr); 281 } 282 283 public void adjust_scale () { 284 double x1, x2, y1, y2, glyph_width, glyph_height, scale, gx; 285 Glyph g; 286 Font font; 287 288 if (glyphs != null) { 289 font = BirdFont.get_current_font (); 290 g = ((!) glyphs).get_current (); 291 292 if (g.boundaries (out x1, out y1, out x2, out y2)) { 293 glyph_width = x2 - x1; 294 glyph_height = y2 - y1; 295 296 if (glyph_scale == 1) { 297 // caption height is 20 298 glyph_scale = (height - 20) / (font.top_limit - font.bottom_limit); 299 } 300 301 scale = glyph_scale; 302 gx = ((width / scale) - glyph_width) / 2; 303 304 if (gx < 0) { 305 glyph_scale = 1 + 2 * gx / width; 306 } 307 } 308 } 309 } 310 311 private void draw_thumbnail (Context cr, double x, double y) { 312 if (cache != null) { 313 cr.save (); 314 cr.set_antialias (Cairo.Antialias.NONE); 315 cr.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); 316 cr.set_source_surface ((!) cache, (int) (x * Screen.get_scale ()), (int) ((y - height)) * Screen.get_scale ()); 317 cr.paint (); 318 cr.restore (); 319 } 320 } 321 322 public bool has_icons () { 323 return width > 50; 324 } 325 326 public void draw_caption (Context cr) { 327 draw_label_background (cr); 328 329 cr.save (); 330 331 if (glyphs != null) { 332 if (selected) { 333 Theme.text_color (label, "Overview Selected Foreground"); 334 } else { 335 Theme.text_color (label, "Overview Foreground"); 336 } 337 338 label.draw_at_baseline (cr, x + 0.08 * width, y + height - 6); 339 } 340 341 cr.restore (); 342 } 343 344 public void create_label_background_cache (Context cr) { 345 Context cc; 346 Cairo.Pattern p; 347 Surface cache; 348 349 // unselected item 350 cache = Screen.create_background_surface ((int) width + 1, 20); 351 cc = new Context (cache); 352 cc.scale(Screen.get_scale(), Screen.get_scale()); 353 354 cc.rectangle (0, 0, width, 20 - 1); 355 p = new Cairo.Pattern.linear (0.0, 0, 0.0, 20); 356 Theme.gradient (p, "Overview Item 1", "Overview Item 2"); 357 cc.set_source (p); 358 359 cc.fill (); 360 361 if (has_icons ()) { 362 if (version_menu != null) { 363 draw_menu_icon (cc, false); 364 } 365 366 draw_character_info_icon (cc); 367 } 368 369 label_background = (!) cache; 370 371 // selected item 372 cache = Screen.create_background_surface ((int) width + 1, 20); 373 cc = new Context (cache); 374 cc.scale(Screen.get_scale(), Screen.get_scale()); 375 376 cc.rectangle (0, 0, width, 20 - 1); 377 378 Theme.color (cc, "Selected Overview Item"); 379 380 cc.fill (); 381 382 if (has_icons ()) { 383 draw_menu_icon (cc, true); 384 draw_character_info_icon (cc); 385 } 386 387 selected_label_background = (!) cache; 388 389 // deselected item without menu icon 390 cache = Screen.create_background_surface ((int) width, 20); 391 cc = new Context (cache); 392 cc.scale(Screen.get_scale(), Screen.get_scale()); 393 cc.rectangle (0, 0, width - 1, 20 - 1); 394 p = new Cairo.Pattern.linear (0.0, 0, 0.0, 20); 395 Theme.gradient (p, "Overview Item 1", "Overview Item 2"); 396 cc.set_source (p); 397 cc.fill (); 398 399 if (has_icons ()) { 400 draw_character_info_icon (cc); 401 } 402 403 label_background_no_menu = (!) 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 cc.rectangle (0, 0, width, 20 - 1); 410 Theme.color (cc, "Selected Overview Item"); 411 cc.fill (); 412 413 if (has_icons ()) { 414 draw_character_info_icon (cc); 415 } 416 417 selected_label_background_no_menu = (!) cache; 418 } 419 420 bool has_menu () { 421 return glyphs != null; 422 } 423 424 public void clear_cache () { 425 cache = null; 426 427 if (glyphs != null) { 428 GlyphCollection gc = (!) glyphs; 429 gc.get_current ().overview_thumbnail = null; 430 } 431 } 432 433 public void draw_label_background (Context cr) { 434 Surface cache; 435 bool icon; 436 437 if (unlikely (label_background == null)) { 438 create_label_background_cache (cr); 439 } 440 441 if (label_background != null 442 && selected_label_background != null 443 && label_background_no_menu != null 444 && selected_label_background_no_menu != null) { 445 446 icon = has_menu (); 447 if (selected && icon) { 448 cache = (!) selected_label_background; 449 } else if (!selected && icon) { 450 cache = (!) label_background; 451 } else if (selected && !icon) { 452 cache = (!) selected_label_background_no_menu; 453 } else { 454 cache = (!) label_background_no_menu; 455 } 456 457 Screen.paint_background_surface (cr, cache, (int) x, (int) (y + height - 19)); 458 } 459 } 460 461 private void draw_character_info_icon (Context cr) { 462 info.draw_icon (cr, selected, width - 17, -1.5); 463 } 464 465 public void hide_menu () { 466 if (!is_null (version_menu)) { 467 VersionList v = (!) version_menu; 468 v.menu_visible = false; 469 } 470 } 471 472 private void draw_menu_icon (Context cc, bool selected) { 473 Text icon; 474 475 icon = new Text ("dropdown_menu", 17); 476 icon.load_font ("icons.bf"); 477 478 if (selected) { 479 Theme.text_color (icon, "Overview Selected Foreground"); 480 } else { 481 Theme.text_color (icon, "Overview Foreground"); 482 } 483 484 icon.draw_at_top (cc, width - 32, 0); 485 } 486 487 private void draw_menu (Context cr) { 488 if (glyphs == null) { 489 return; 490 } 491 492 if (version_menu == null) { 493 return; 494 } 495 496 VersionList v = (!) version_menu; 497 498 if (!v.menu_visible) { 499 return; 500 } 501 502 v.draw_menu (cr); 503 } 504 } 505 506 } 507