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