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