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