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