.
1 /*
2 Copyright (C) 2012, 2014 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
17 namespace BirdFont {
18
19 public class Tool : Widget {
20
21 public double x = 0;
22 public double y = 0;
23 public double w = 33 * Toolbox.get_scale ();
24 public double h = (33 / 1.11) * Toolbox.get_scale ();
25
26 double scale;
27
28 public bool active = false;
29 public bool selected = false;
30
31 Text icon_font;
32
33 public signal void select_action (Tool selected);
34 public signal void deselect_action (Tool selected);
35
36 public signal void press_action (Tool selected, int button, int x, int y);
37 public signal void double_click_action (Tool selected, int button, int x, int y);
38 public signal void move_action (Tool selected, int x, int y);
39 public signal void move_out_action (Tool selected);
40 public signal void release_action (Tool selected, int button, int x, int y);
41
42 /** Returns true if tool is listening for scroll wheel actions. */
43 public signal bool scroll_wheel_up_action (Tool selected);
44 public signal bool scroll_wheel_down_action (Tool selected);
45
46 public signal void key_press_action (Tool selected, uint32 keyval);
47 public signal void key_release_action (Tool selected, uint32 keyval);
48
49 public signal void panel_press_action (Tool selected, uint button, double x, double y);
50 public signal void panel_release_action (Tool selected, uint button, double x, double y);
51
52 /** @return true is event is consumed. */
53 public signal bool panel_move_action (Tool selected, double x, double y);
54
55 public signal void draw_action (Tool selected, Context cr, Glyph glyph);
56
57 public string name = "";
58
59 static int next_id = 1;
60
61 int id;
62
63 public bool new_selection = false;
64
65 bool show_bg = true;
66
67 public string tip = "";
68
69 // keyboard bindings
70 public uint modifier_flag;
71 public unichar key;
72
73 public bool persistent = false;
74 public bool editor_events = false;
75
76 bool waiting_for_tooltip = false;
77 bool showing_this_tooltip = false;
78 static Tool active_tooltip = new Tool ();
79
80 bool visible = true;
81 public bool is_tool_modifier = false;
82
83 /** Create tool with a certain name and load icon "name".png */
84 public Tool (string? name = null, string tip = "") {
85 this.tip = tip;
86
87 icon_font = new Text ();
88
89 scale = w / 111.0; // scale to 320 dpi
90
91 if (name != null) {
92 set_icon ((!) name);
93 this.name = (!) name;
94 }
95
96 id = next_id;
97 next_id++;
98
99 panel_press_action.connect ((self, button, x, y) => {
100 });
101
102 move_out_action.connect ((self) => {
103 MainWindow.get_toolbox ().hide_tooltip ();
104 active_tooltip.showing_this_tooltip = false;
105 });
106
107 panel_move_action.connect ((self, x, y) => {
108 if (is_active ()) {
109 wait_for_tooltip ();
110 }
111 return false;
112 });
113 }
114
115 public override double get_height () {
116 return 33 * scale;
117 }
118
119 public override double get_width () {
120 return 33 * scale;
121 }
122
123 public void set_tool_visibility (bool v) {
124 visible = v;
125 }
126
127 public bool tool_is_visible () {
128 return visible;
129 }
130
131 void wait_for_tooltip () {
132 TimeoutSource timer_show;
133 int timeout_interval = 1500;
134
135 if (active_tooltip != this) {
136 if (active_tooltip.showing_this_tooltip) {
137 timeout_interval = 1;
138 }
139
140 active_tooltip.showing_this_tooltip = false;
141 showing_this_tooltip = false;
142 active_tooltip = this;
143
144 if (!waiting_for_tooltip) {
145 waiting_for_tooltip = true;
146 timer_show = new TimeoutSource (timeout_interval);
147 timer_show.set_callback (() => {
148 if (tip != "" && active_tooltip.is_active () && !active_tooltip.showing_this_tooltip) {
149 show_tooltip ();
150 }
151 waiting_for_tooltip = false;
152 return waiting_for_tooltip;
153 });
154 timer_show.attach (null);
155 }
156 }
157 }
158
159 public static void show_tooltip () {
160 TimeoutSource timer_hide;
161 Toolbox toolbox;
162
163 toolbox = MainWindow.get_toolbox ();
164
165 // hide tooltip label later
166 if (!active_tooltip.showing_this_tooltip) {
167 timer_hide = new TimeoutSource (1500);
168 timer_hide.set_callback (() => {
169 if (!active_tooltip.is_active ()) {
170 toolbox.hide_tooltip ();
171 active_tooltip.showing_this_tooltip = false;
172 active_tooltip = new Tool ();
173 }
174 return active_tooltip.showing_this_tooltip;
175 });
176 timer_hide.attach (null);
177 }
178
179 active_tooltip.showing_this_tooltip = true;
180
181 toolbox.hide_tooltip ();
182 toolbox.show_tooltip (active_tooltip.tip, (int) active_tooltip.x, (int) active_tooltip.y);
183 }
184
185 public void set_icon (string name) {
186 bool found;
187 string icon_file;
188
189 icon_file = Theme.get_icon_file ();
190 icon_font = new Text ((!) name);
191 found = icon_font.load_font (icon_file);
192 icon_font.use_cache (true);
193 icon_font.set_font_size (35);
194
195 if (!found) {
196 warning (@"Icon font for toolbox was not found. ($(icon_file))");
197 }
198 }
199
200 public bool is_active () {
201 return active;
202 }
203
204 public void set_show_background (bool bg) {
205 show_bg = bg;
206 }
207
208 public int get_id () {
209 return id;
210 }
211
212 public string get_name () {
213 return name;
214 }
215
216 public bool is_selected () {
217 return selected;
218 }
219
220 public string get_tip () {
221 return tip;
222 }
223
224 public new bool is_over (double xp, double yp) {
225 bool r = (x <= xp <= x + w && y <= yp <= y + h);
226 return r;
227 }
228
229 public bool set_selected (bool a) {
230 new_selection = true;
231 selected = a;
232 set_active (a);
233
234 if (!a) {
235 deselect_action (this);
236 }
237
238 return true;
239 }
240
241 /** @return true if this tool changes state, */
242 public bool set_active (bool ac) {
243 bool ret = (active != ac);
244 active = ac;
245 return ret;
246 }
247
248 public override void draw (Context cr) {
249 double xt = x;
250 double yt = y;
251
252 double bgx, bgy;
253 double iconx, icony;
254
255 string border = "Button Border 3";
256 string background = "Button Border 3";
257
258 cr.save ();
259
260 bgx = xt;
261 bgy = yt;
262
263 // Button in four states
264 if (selected) {
265 border = "Button Border 1";
266 background = "Button Background 1";
267 }
268
269 if (selected && active) {
270 border = "Button Border 2";
271 background = "Button Background 2";
272 }
273
274 if (!selected) {
275 border = "Button Border 3";
276 background = "Button Background 3";
277 }
278
279 if (!selected && active) {
280 border = "Button Border 4";
281 background = "Button Background 4";
282 }
283
284 Theme.color (cr, background);
285 draw_rounded_rectangle (cr, bgx, bgy, 34, 28, 4);
286 cr.fill ();
287
288 cr.set_line_width (1);
289 Theme.color (cr, border);
290 draw_rounded_rectangle (cr, bgx, bgy, 34, 28, 4);
291 cr.stroke ();
292
293 iconx = bgx + w / 2 - icon_font.get_sidebearing_extent () / 2;
294 icony = bgy + h / 2 - icon_font.get_height () / 2;
295
296 if (!selected) {
297 Theme.text_color (icon_font, "Tool Foreground");
298 } else {
299 Theme.text_color (icon_font, "Selected Tool Foreground");
300 }
301
302 icon_font.widget_x = iconx;
303 icon_font.widget_y = icony;
304
305 icon_font.draw (cr);
306
307 cr.restore ();
308 }
309
310 /** Run pending events in main loop before continuing. */
311 public static void @yield () {
312 int t = 0;
313 TimeoutSource time = new TimeoutSource (500);
314 bool timeout;
315 unowned MainContext context;
316 bool acquired;
317
318 if (TestBirdFont.is_slow_test ()) {
319 timeout = false;
320
321 time.set_callback (() => {
322 timeout = true;
323 return false;
324 });
325
326 time.attach (null);
327 } else {
328 timeout = true;
329 }
330
331 context = MainContext.default ();
332 acquired = context.acquire ();
333
334 if (unlikely (!acquired)) {
335 warning ("Failed to acquire main loop.\n");
336 return;
337 }
338
339 while (context.pending () || TestBirdFont.is_slow_test ()) {
340 context.iteration (true);
341 t++;
342
343 if (!context.pending () && TestBirdFont.is_slow_test ()) {
344 if (timeout) break;
345 }
346 }
347
348 context.release ();
349 }
350
351 public void set_persistent (bool p) {
352 persistent = p;
353 }
354
355 public virtual void before_undo () {
356 }
357
358 public virtual void after_undo () {
359 }
360
361 }
362
363 }
364