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