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; 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 () { 67 if (glyphs != null) { 68 version_menu = new VersionList ((!) glyphs); 69 version_menu.add_glyph_item.connect ((glyph) => { 70 ((!) glyphs).insert_glyph (glyph, true); 71 }); 72 73 version_menu.signal_delete_item.connect ((glyph_index) => { 74 OverView v = MainWindow.get_overview (); 75 version_menu = new VersionList ((!) glyphs); 76 v.update_item_list (); 77 GlyphCanvas.redraw (); 78 }); 79 } 80 81 info = new CharacterInfo (character, glyphs); 82 83 if (glyphs == null) { 84 label = new Text (); 85 } else { 86 label = new Text ((!) character.to_string (), 17); 87 truncate_label (); 88 } 89 90 draw_background (); 91 } 92 93 public void draw_glyph_from_font () { 94 if (glyphs == null) { 95 return; 96 } 97 98 Glyph g; 99 Font font; 100 double gx, gy; 101 double x1, x2, y1, y2; 102 double scale_box; 103 double w, h; 104 double glyph_width, glyph_height; 105 Surface s; 106 Context c; 107 Color color = Color.black (); 108 109 g = ((!) glyphs).get_current (); 110 111 if (g.overview_thumbnail != null) { 112 cache = g.overview_thumbnail; 113 return; 114 } 115 116 w = width; 117 h = height; 118 119 scale_box = width / DEFAULT_WIDTH; 120 121 s = Screen.create_background_surface ((int) width, (int) height - 20); 122 c = new Context (s); 123 124 c.save (); 125 g.boundaries (out x1, out y1, out x2, out y2); 126 127 if (g.color_svg_data != null) { 128 font = BirdFont.get_current_font (); 129 glyph_width = g.right_limit - g.left_limit; 130 glyph_height = font.top_position - font.bottom_position; 131 } else { 132 glyph_width = x2 - x1; 133 glyph_height = y2 - y1; 134 } 135 136 c.save (); 137 c.scale (glyph_scale * Screen.get_scale (), glyph_scale * Screen.get_scale ()); 138 139 g.add_help_lines (); 140 141 if (g.color_svg_data != null) { 142 gx = 10; 143 gy = (h / glyph_scale) - 25 / glyph_scale; 144 c.translate (gx - Glyph.xc () - g.get_lsb (), g.get_baseline () + gy - Glyph.yc ()); 145 g.draw_svg (c); 146 } else { 147 gx = ((w / glyph_scale) - glyph_width) / 2 - g.get_left_side_bearing (); 148 gy = (h / glyph_scale) - 25 / glyph_scale; 149 c.translate (gx - Glyph.xc () - g.get_lsb (), g.get_baseline () + gy - Glyph.yc ()); 150 g.draw_paths (c, color); 151 } 152 153 c.restore (); 154 155 cache = s; 156 GlyphCanvas.redraw (); 157 } 158 159 public void draw_background () { 160 double scale_box; 161 Surface s; 162 Context c; 163 164 scale_box = width / DEFAULT_WIDTH; 165 166 s = Screen.create_background_surface ((int) width, (int) height - 20); 167 c = new Context (s); 168 169 if (glyphs != null) { 170 draw_glyph_from_font (); 171 } else { 172 c.scale (Screen.get_scale (), Screen.get_scale ()); 173 174 c.save (); 175 176 bool glyph_found; 177 string? font_file; 178 179 Theme.color (c, "Overview Glyph"); 180 181 font_file = FontCache.fallback_font.get_default_font_file (); 182 glyph_found = draw_overview_glyph (c, (!) font_file, width, height, character); 183 184 if (!glyph_found) { 185 font_file = find_font (FallbackFont.font_config, (!) character.to_string ()); 186 187 if (font_file != null) { 188 string path = (!) font_file; 189 190 if (!path.has_suffix("LastResort.ttf")) { 191 draw_overview_glyph (c, path, width, height, character); 192 } 193 } 194 } 195 196 c.restore (); 197 198 cache = s; 199 GlyphCanvas.redraw (); 200 } 201 } 202 203 public static void reset_label () { 204 label_background = null; 205 selected_label_background = null; 206 } 207 208 void truncate_label () { 209 double w = has_icons () ? width - 43 : width; 210 label.truncate (w); 211 } 212 213 public string get_name () { 214 StringBuilder s; 215 216 if (glyphs != null) { 217 return ((!) glyphs).get_name (); 218 } 219 220 s = new StringBuilder (); 221 s.append_unichar (character); 222 223 return s.str; 224 } 225 226 public static double full_width () { 227 return width + margin; 228 } 229 230 public static double full_height () { 231 return height + margin; 232 } 233 234 public bool click (uint button, double px, double py) { 235 bool a; 236 GlyphCollection g; 237 bool s = (x <= px <= x + width) && (y <= py <= y + height); 238 239 if (has_icons () && glyphs != null) { 240 g = (!) glyphs; 241 version_menu.set_position (x + width - 21, y + height - 18); 242 a = version_menu.menu_item_action (px, py); // select one item on the menu 243 244 if (a) { 245 return s; 246 } 247 248 version_menu.menu_icon_action (px, py); // click in the open menu 249 } 250 251 info.set_position (x + width - 17, y + height - 22.5); 252 if (has_icons () && info.is_over_icon (px, py)) { 253 MainWindow.get_overview ().set_character_info (info); 254 } 255 256 return s; 257 } 258 259 public bool double_click (uint button, double px, double py) { 260 selected = (x <= px <= x + width) && (y <= py <= y + height); 261 return selected; 262 } 263 264 public bool is_on_screen (WidgetAllocation allocation) { 265 return y + height > 0 && y < allocation.height; 266 } 267 268 public void draw (WidgetAllocation allocation, Context cr) { 269 if (!is_on_screen (allocation)) { 270 return; 271 } 272 273 cr.save (); 274 Theme.color (cr, "Background 1"); 275 cr.rectangle (x, y, width, height); 276 cr.fill (); 277 cr.restore (); 278 279 cr.save (); 280 Theme.color (cr, "Overview Item Border"); 281 cr.rectangle (x, y, width, height); 282 cr.set_line_width (1); 283 cr.stroke (); 284 cr.restore (); 285 286 draw_thumbnail (cr, x, y + height); 287 draw_caption (cr); 288 draw_menu (cr); 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 300 if (g.boundaries (out x1, out y1, out x2, out y2)) { 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 319 private void draw_thumbnail (Context cr, double x, double y) { 320 if (cache != null) { 321 cr.save (); 322 cr.set_antialias (Cairo.Antialias.NONE); 323 cr.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); 324 cr.set_source_surface ((!) cache, (int) (x * Screen.get_scale ()), (int) ((y - height)) * Screen.get_scale ()); 325 cr.paint (); 326 cr.restore (); 327 } 328 } 329 330 public bool has_icons () { 331 return width > 50; 332 } 333 334 public void draw_caption (Context cr) { 335 draw_label_background (cr); 336 337 cr.save (); 338 339 if (glyphs != null) { 340 if (selected) { 341 Theme.text_color (label, "Overview Selected Foreground"); 342 } else { 343 Theme.text_color (label, "Overview Foreground"); 344 } 345 346 label.draw_at_baseline (cr, x + 0.08 * width, y + height - 6); 347 } 348 349 cr.restore (); 350 } 351 352 public void create_label_background_cache (Context cr) { 353 Context cc; 354 Cairo.Pattern p; 355 Surface cache; 356 357 // unselected item 358 cache = Screen.create_background_surface ((int) width + 1, 20); 359 cc = new Context (cache); 360 cc.scale(Screen.get_scale(), Screen.get_scale()); 361 362 cc.rectangle (0, 0, width, 20 - 1); 363 p = new Cairo.Pattern.linear (0.0, 0, 0.0, 20); 364 Theme.gradient (p, "Overview Item 1", "Overview Item 2"); 365 cc.set_source (p); 366 367 cc.fill (); 368 369 if (has_icons ()) { 370 draw_menu_icon (cc, false); 371 draw_character_info_icon (cc); 372 } 373 374 label_background = (!) cache; 375 376 // selected item 377 cache = Screen.create_background_surface ((int) width + 1, 20); 378 cc = new Context (cache); 379 cc.scale(Screen.get_scale(), Screen.get_scale()); 380 381 cc.rectangle (0, 0, width, 20 - 1); 382 383 Theme.color (cc, "Selected Overview Item"); 384 385 cc.fill (); 386 387 if (has_icons ()) { 388 draw_menu_icon (cc, true); 389 draw_character_info_icon (cc); 390 } 391 392 selected_label_background = (!) cache; 393 394 // deselected item without menu icon 395 cache = Screen.create_background_surface ((int) width, 20); 396 cc = new Context (cache); 397 cc.scale(Screen.get_scale(), Screen.get_scale()); 398 cc.rectangle (0, 0, width - 1, 20 - 1); 399 p = new Cairo.Pattern.linear (0.0, 0, 0.0, 20); 400 Theme.gradient (p, "Overview Item 1", "Overview Item 2"); 401 cc.set_source (p); 402 cc.fill (); 403 404 if (has_icons ()) { 405 draw_character_info_icon (cc); 406 } 407 408 label_background_no_menu = (!) cache; 409 410 // selected item 411 cache = Screen.create_background_surface ((int) width + 1, 20); 412 cc = new Context (cache); 413 cc.scale(Screen.get_scale(), Screen.get_scale()); 414 cc.rectangle (0, 0, width, 20 - 1); 415 Theme.color (cc, "Selected Overview Item"); 416 cc.fill (); 417 418 if (has_icons ()) { 419 draw_character_info_icon (cc); 420 } 421 422 selected_label_background_no_menu = (!) cache; 423 } 424 425 bool has_menu () { 426 return glyphs != null; 427 } 428 429 public void clear_cache () { 430 cache = null; 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 version_menu.menu_visible = false; 468 } 469 } 470 471 private void draw_menu_icon (Context cc, bool selected) { 472 Text icon; 473 474 icon = new Text ("dropdown_menu", 17); 475 icon.load_font ("icons.bf"); 476 477 if (selected) { 478 Theme.text_color (icon, "Overview Selected Foreground"); 479 } else { 480 Theme.text_color (icon, "Overview Foreground"); 481 } 482 483 icon.draw_at_top (cc, width - 32, 0); 484 } 485 486 private void draw_menu (Context cr) { 487 if (glyphs == null || !version_menu.menu_visible) { 488 return; 489 } 490 491 version_menu.draw_menu (cr); 492 } 493 } 494 495 } 496