initial commit
[ld42_outofspace] / src / goatkit / widget.cc
1 /*
2 GoatKit - a themable/animated widget toolkit for games
3 Copyright (C) 2014-2015 John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include <math.h>
21 #include <string>
22 #include <sstream>
23 #include "widget.h"
24 #include "boolanm.h"
25 #include "theme.h"
26 #include "screen.h"
27
28 namespace goatkit {
29
30 struct WidgetImpl {
31         Screen *scr;
32         std::string name, text;
33         BBox box;
34
35         BoolAnim visible, active, press, hover, focus;
36
37         struct {
38                 EventCallback func;
39                 void *cls;
40         } cb[NUM_EVENTS];
41 };
42
43
44 Widget::Widget()
45 {
46         static int widget_count;
47
48         widget = new WidgetImpl;
49         widget->scr = 0;
50
51         std::stringstream sstr;
52         sstr << get_type_name() << widget_count++;
53         widget->name = sstr.str();
54
55         widget->box.bmin = Vec2(0, 0);
56         widget->box.bmax = Vec2(1, 1);
57
58         widget->visible.set(true);
59         widget->active.set(true);
60
61         widget->hover.set_transition_duration(250);
62         widget->press.set_transition_duration(50);
63
64         memset(widget->cb, 0, sizeof widget->cb);
65 }
66
67 Widget::~Widget()
68 {
69         delete widget;
70 }
71
72 void Widget::set_screen(Screen *scr)
73 {
74         widget->scr = scr;
75 }
76
77 Screen *Widget::get_screen() const
78 {
79         return widget->scr;
80 }
81
82 const char *Widget::get_type_name() const
83 {
84         return "widget";
85 }
86
87 void Widget::set_name(const char *name)
88 {
89         widget->name = std::string(name);
90 }
91
92 const char *Widget::get_name() const
93 {
94         return widget->name.c_str();
95 }
96
97 void Widget::set_text(const char *text)
98 {
99         widget->text = std::string(text);
100 }
101
102 const char *Widget::get_text() const
103 {
104         return widget->text.c_str();
105 }
106
107 void Widget::show()
108 {
109         widget->visible.change(true);
110 }
111
112 void Widget::hide()
113 {
114         widget->visible.change(false);
115 }
116
117 float Widget::get_visibility() const
118 {
119         return widget->visible.get_value();
120 }
121
122 bool Widget::is_visible() const
123 {
124         return widget->visible.get_state();
125 }
126
127 void Widget::set_visibility_transition(long msec)
128 {
129         widget->visible.set_transition_duration(msec);
130 }
131
132 long Widget::get_visibility_transition() const
133 {
134         return widget->visible.get_transition_duration();
135 }
136
137 void Widget::activate()
138 {
139         widget->active.change(true);
140 }
141
142 void Widget::deactivate()
143 {
144         widget->active.change(false);
145 }
146
147 float Widget::get_active() const
148 {
149         return widget->active.get_value();
150 }
151
152 bool Widget::is_active() const
153 {
154         return widget->active.get_state();
155 }
156
157 void Widget::set_active_transition(long msec)
158 {
159         widget->active.set_transition_duration(msec);
160 }
161
162 long Widget::get_active_transition() const
163 {
164         return widget->active.get_transition_duration();
165 }
166
167 void Widget::press()
168 {
169         widget->press.change(true);
170 }
171
172 void Widget::release()
173 {
174         widget->press.change(false);
175 }
176
177 float Widget::get_pressed() const
178 {
179         return widget->press.get_value();
180 }
181
182 bool Widget::is_pressed() const
183 {
184         return widget->press.get_state();
185 }
186
187 void Widget::set_press_transition(long msec)
188 {
189         widget->press.set_transition_duration(msec);
190 }
191
192 long Widget::get_press_transition() const
193 {
194         return widget->press.get_transition_duration();
195 }
196
197 void Widget::mousein()
198 {
199         widget->hover.change(true);
200 }
201
202 void Widget::mouseout()
203 {
204         widget->hover.change(false);
205         if(widget->press) {
206                 widget->press.change(false);
207         }
208 }
209
210 float Widget::get_under_mouse() const
211 {
212         return widget->hover.get_value();
213 }
214
215 bool Widget::is_under_mouse() const
216 {
217         return widget->hover.get_state();
218 }
219
220 void Widget::set_hover_transition(long msec)
221 {
222         widget->hover.set_transition_duration(msec);
223 }
224
225 long Widget::get_hover_transition() const
226 {
227         return widget->hover.get_transition_duration();
228 }
229
230 bool Widget::can_focus() const
231 {
232         return false;
233 }
234
235 void Widget::focusin()
236 {
237         widget->focus.change(true);
238 }
239
240 void Widget::focusout()
241 {
242         widget->focus.change(false);
243 }
244
245 float Widget::get_focus() const
246 {
247         return widget->focus.get_value();
248 }
249
250 bool Widget::is_focused() const
251 {
252         return widget->focus.get_state();
253 }
254
255 void Widget::set_focus_transition(long msec)
256 {
257         widget->focus.set_transition_duration(msec);
258 }
259
260 long Widget::get_focus_transition() const
261 {
262         return widget->focus.get_transition_duration();
263 }
264
265 void Widget::set_position(float x, float y)
266 {
267         set_position(Vec2(x, y));
268 }
269
270 void Widget::set_position(const Vec2 &pos)
271 {
272         Vec2 sz = get_size();
273
274         widget->box.bmin = pos;
275         widget->box.bmax.x = pos.x + sz.x;
276         widget->box.bmax.y = pos.y + sz.y;
277 }
278
279 const Vec2 &Widget::get_position() const
280 {
281         return widget->box.bmin;
282 }
283
284 void Widget::set_size(float x, float y)
285 {
286         set_size(Vec2(x, y));
287 }
288
289 void Widget::set_size(const Vec2 &sz)
290 {
291         widget->box.bmax.x = widget->box.bmin.x + sz.x;
292         widget->box.bmax.y = widget->box.bmin.y + sz.y;
293 }
294
295 const Vec2 Widget::get_size() const
296 {
297         return Vec2(widget->box.bmax.x - widget->box.bmin.x,
298                         widget->box.bmax.y - widget->box.bmin.y);
299 }
300
301
302 const BBox &Widget::get_box() const
303 {
304         return widget->box;
305 }
306
307 bool Widget::hit_test(const Vec2 &pt) const
308 {
309         return pt.x >= widget->box.bmin.x && pt.x < widget->box.bmax.x &&
310                 pt.y >= widget->box.bmin.y && pt.y < widget->box.bmax.y;
311 }
312
313 void Widget::draw() const
314 {
315         WidgetDrawFunc draw_func = default_draw_func;
316
317         if(theme) {
318                 draw_func = theme->get_draw_func(get_type_name());
319         }
320
321         draw_func(this);
322 }
323
324 // dummy event handlers
325 void Widget::on_mouse_button(const ButtonEvent &ev)
326 {
327 }
328
329 void Widget::on_mouse_motion(const MotionEvent &ev)
330 {
331 }
332
333 void Widget::on_mouse_focus(const FocusEvent &ev)
334 {
335 }
336
337 void Widget::on_key(const KeyEvent &ev)
338 {
339 }
340
341 void Widget::on_click()
342 {
343 }
344
345 void Widget::on_double_click()
346 {
347 }
348
349 void Widget::on_change()
350 {
351 }
352
353
354 #define CALL_CB(w, ev) \
355         do { \
356                 if((w)->widget->cb[ev.type].func) { \
357                         (w)->widget->cb[ev.type].func((w), ev, (w)->widget->cb[ev.type].cls); \
358                 } \
359         } while(0)
360
361 #define CALL_CB_TYPE(w, t) \
362         do { \
363                 Event ev; \
364                 ev.type = (t); \
365                 CALL_CB(w, ev); \
366         } while(0)
367
368 /* the event dispatcher generates high-level events (click, etc)
369  * and calls the on_whatever() functions for both low and high-level
370  * events.
371  * The on_whatever functions are called *after* any other actions performed
372  * here, to give subclasses the opportunity to override them easily, by
373  * overriding the on_ functions, without having to override handle_event itself
374  */
375 // TODO also call callbacks here I guess...
376 void Widget::handle_event(const Event &ev)
377 {
378         switch(ev.type) {
379         case EV_MOUSE_BUTTON:
380                 if(ev.button.press) {
381                         press();
382                 } else {
383                         if(is_pressed()) {
384                                 CALL_CB_TYPE(this, EV_CLICK);
385                                 on_click();
386                         }
387                         release();
388                 }
389
390                 on_mouse_button(ev.button);
391                 break;
392
393         case EV_MOUSE_MOTION:
394                 on_mouse_motion(ev.motion);
395                 break;
396
397         case EV_MOUSE_FOCUS:
398                 if(ev.focus.enter) {
399                         mousein();
400                 } else {
401                         mouseout();
402                 }
403                 on_mouse_focus(ev.focus);
404                 break;
405
406         case EV_KEY:
407                 on_key(ev.key);
408                 break;
409
410         case EV_CHANGE:
411                 on_change();
412                 break;
413
414         default:
415                 break;
416         }
417
418         CALL_CB(this, ev);
419 }
420
421
422 void Widget::set_callback(EventType evtype, EventCallback func, void *cls)
423 {
424         widget->cb[evtype].func = func;
425         widget->cb[evtype].cls = cls;
426 }
427
428
429 }       // namespace goatkit