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