]> git.tdb.fi Git - libs/gltk.git/blob - source/widget.cpp
Minor refactoring
[libs/gltk.git] / source / widget.cpp
1 #include <msp/gl/matrix.h>
2 #include <msp/strings/format.h>
3 #include <msp/strings/utils.h>
4 #include "container.h"
5 #include "resources.h"
6 #include "root.h"
7 #include "widget.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace GLtk {
13
14 Widget::~Widget()
15 {
16         if(parent)
17         {
18                 Container *p = parent;
19                 parent = nullptr;
20                 p->remove(*this);
21         }
22 }
23
24 void Widget::set_position(int x, int y)
25 {
26         set_geometry(Geometry(x, y, geom.w, geom.h));
27 }
28
29 void Widget::set_size(unsigned w, unsigned h)
30 {
31         set_geometry(Geometry(geom.x, geom.y, w, h));
32 }
33
34 void Widget::autosize()
35 {
36         Geometry ageom;
37         autosize(ageom);
38         set_geometry(ageom);
39 }
40
41 void Widget::autosize(Geometry &ageom) const
42 {
43         if(!style)
44                 return;
45
46         ageom = Geometry(geom.x, geom.y, 0, 0);
47         for(const Part &p: style->get_parts())
48         {
49                 if(p.get_name().empty())
50                 {
51                         const Geometry &pgeom = p.get_geometry();
52                         const Sides &pmargin = p.get_margin();
53                         ageom.w = max(ageom.w, pgeom.w+pmargin.left+pmargin.right);
54                         ageom.h = max(ageom.h, pgeom.h+pmargin.top+pmargin.bottom);
55                 }
56                 else
57                         autosize_special(p, ageom);
58         }
59 }
60
61 void Widget::set_geometry(const Geometry &g)
62 {
63         bool size_changed = (g.w!=geom.w || g.h!=geom.h);
64         geom = g;
65         if(size_changed)
66         {
67                 on_size_change();
68                 mark_rebuild();
69         }
70 }
71
72 void Widget::map_coords_to_ancestor(int &x, int &y, const Widget &ancestor) const
73 {
74         for(const Widget *w=this; w; w=w->get_parent())
75         {
76                 if(w==&ancestor)
77                         return;
78
79                 const Geometry &wgeom = w->get_geometry();
80                 x += wgeom.x;
81                 y += wgeom.y;
82         }
83
84         throw hierarchy_error("not an ancestor");
85 }
86
87 void Widget::map_coords_to_root(int &x, int &y) const
88 {
89         for(const Widget *w=this; w; w=w->get_parent())
90         {
91                 const Geometry &wgeom = w->get_geometry();
92                 x += wgeom.x;
93                 y += wgeom.y;
94         }
95 }
96
97 void Widget::set_parent(Container *p)
98 {
99         if(parent && p)
100                 throw hierarchy_error("widget already parented");
101         else if(p==parent)
102                 return;
103
104         try
105         {
106                 parent = p;
107
108                 on_reparent();
109                 update_style();
110         }
111         catch(...)
112         {
113                 // The container has not yet added the widget as its child
114                 parent = nullptr;
115                 throw;
116         }
117 }
118
119 void Widget::set_style(const string &s)
120 {
121         style_name = s;
122         update_style();
123 }
124
125 void Widget::update_style()
126 {
127         Widget *top;
128         for(top=this; top->parent; top=top->parent) ;
129         Root *root = dynamic_cast<Root *>(top);
130         if(!root)
131                 style = nullptr;
132         else
133         {
134                 string sname = get_class();
135                 append(sname, "-", style_name);
136
137                 style = &root->get_resources().get<Style>(sname);
138         }
139
140         on_style_change();
141         signal_autosize_changed.emit();
142         mark_rebuild();
143 }
144
145 void Widget::set_tooltip(const string &t)
146 {
147         tooltip = t;
148 }
149
150 void Widget::set_visible(bool v)
151 {
152         if(v==visible)
153                 return;
154
155         visible = v;
156
157         signal_visibility_changed.emit(visible);
158 }
159
160 void Widget::set_focus()
161 {
162         if(!parent)
163                 throw hierarchy_error("no parent");
164         if(!visible)
165                 throw logic_error("!visible");
166
167         signal_request_focus.emit();
168 }
169
170 void Widget::set_enabled(bool e)
171 {
172         set_state(DISABLED, (e ? NORMAL : DISABLED));
173 }
174
175 void Widget::set_state(State mask, State bits)
176 {
177         State old_state = state;
178         state = (state&~mask)|bits;
179         if(style && style->compare_states(old_state, state))
180                 mark_rebuild();
181 }
182
183 void Widget::set_animation_interval(const Time::TimeDelta &iv)
184 {
185         if(iv<Time::zero)
186                 throw invalid_argument("Widget::set_animation_interval");
187
188         anim_interval = iv;
189         signal_request_animation.emit(anim_interval);
190 }
191
192 void Widget::stop_animation()
193 {
194         set_animation_interval(Time::zero);
195 }
196
197 void Widget::mark_rebuild()
198 {
199         if(rebuild_needed)
200                 return;
201
202         rebuild_needed = true;
203         signal_rebuild_needed.emit();
204 }
205
206 void Widget::rebuild_hierarchy()
207 {
208         if(rebuild_needed)
209         {
210                 rebuild_needed = false;
211                 rebuild();
212         }
213 }
214
215 void Widget::rebuild()
216 {
217         if(!style)
218                 return;
219
220         PartCache::Rebuild rebuild_cache(part_cache);
221         for(const Part &p: style->get_parts())
222         {
223                 if(p.get_name().empty())
224                         p.build(geom, state, part_cache);
225                 else
226                         rebuild_special(p);
227         }
228 }
229
230 void Widget::rebuild_special(const Part &part)
231 {
232         part_cache.insert_special(part);
233 }
234
235 void Widget::render(GL::Renderer &renderer) const
236 {
237         if(!style)
238                 throw logic_error(format("Attempt to render a widget with null style (class=\"%s\", style_name=\"%s\")", get_class(), style_name));
239
240         static const GL::Tag texture_tag("ui_tex");
241
242         GL::Renderer::Push _push(renderer);
243         int x = 0;
244         int y = 0;
245         map_coords_to_root(x, y);
246         renderer.set_matrix(GL::Matrix::translation(x, y, 0));
247         for(const CachedPart &p: part_cache.get_parts())
248         {
249                 if(p.mesh && p.texture)
250                 {
251                         renderer.set_texture(texture_tag, p.texture, &style->get_sampler());
252                         p.mesh->draw(renderer);
253                 }
254                 else if(p.part)
255                         render_special(*p.part, renderer);
256         }
257 }
258
259 void Widget::pointer_enter()
260 {
261         set_state(HOVER);
262 }
263
264 void Widget::pointer_leave()
265 {
266         clear_state(HOVER);
267 }
268
269 void Widget::touch_press(int x, int y, unsigned finger)
270 {
271         if(finger==0)
272                 button_press(x, y, 1);
273 }
274
275 void Widget::touch_release(int x, int y, unsigned finger)
276 {
277         if(finger==0)
278                 button_release(x, y, 1);
279 }
280
281 void Widget::touch_motion(int x, int y, unsigned finger)
282 {
283         if(finger==0)
284                 pointer_motion(x, y);
285 }
286
287 void Widget::focus_in()
288 {
289         set_state(FOCUS);
290 }
291
292 void Widget::focus_out()
293 {
294         clear_state(FOCUS);
295 }
296
297
298 Widget::Loader::Loader(Widget &w):
299         DataFile::ObjectLoader<Widget>(w)
300 {
301         add("position", &Loader::position);
302         add("size",     &Loader::size);
303         add("style",    &Loader::style);
304         add("visible",  &Widget::visible);
305 }
306
307 void Widget::Loader::position(int x, int y)
308 {
309         obj.set_position(x, y);
310 }
311
312 void Widget::Loader::size(unsigned w, unsigned h)
313 {
314         obj.set_size(w, h);
315 }
316
317 void Widget::Loader::style(const string &s)
318 {
319         obj.set_style(s);
320 }
321
322 } // namespace GLtk
323 } // namespace Msp