]> git.tdb.fi Git - libs/gltk.git/blob - source/container.cpp
6e1a3562f5280a853d1996f6b99e973f0a0d25eb
[libs/gltk.git] / source / container.cpp
1 #include "container.h"
2 #include "part.h"
3
4 using namespace std;
5
6 namespace Msp {
7 namespace GLtk {
8
9 hierarchy_error::hierarchy_error(const string &w):
10         logic_error(w)
11 { }
12
13
14 Container::Container():
15         click_focus(0),
16         click_button(0),
17         pointer_focus(0),
18         pointer_grabbed(false),
19         input_focus(0),
20         saved_input_focus(0),
21         touch_focus(0),
22         children_rebuild_needed(false)
23 { }
24
25 Container::~Container()
26 {
27         while(!children.empty())
28                 delete children.front()->widget;
29 }
30
31 void Container::add(Widget &wdg)
32 {
33         wdg.set_parent(this);
34         children.push_back(create_child(&wdg));
35         if(wdg.get_animation_interval())
36                 check_animation_interval();
37         children_rebuild_needed = true;
38         signal_rebuild_needed.emit();
39         on_child_added(wdg);
40 }
41
42 void Container::remove(Widget &wdg)
43 {
44         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
45                 if((*i)->widget==&wdg)
46                 {
47                         wdg.set_parent(0);
48                         delete *i;
49                         children.erase(i);
50                         if(wdg.get_animation_interval())
51                                 check_animation_interval();
52                         on_child_removed(wdg);
53                         return;
54                 }
55
56         throw hierarchy_error("widget not in container");
57 }
58
59 Container::Child *Container::create_child(Widget *wdg)
60 {
61         return new Child(*this, wdg);
62 }
63
64 Geometry Container::determine_child_geometry(const Widget &child, const Part &part) const
65 {
66         Geometry pgeom = part.get_geometry();
67         if(!pgeom.w || !pgeom.h)
68         {
69                 Geometry cgeom;
70                 child.autosize(cgeom);
71                 if(!pgeom.w)
72                         pgeom.w = cgeom.w;
73                 if(!pgeom.h)
74                         pgeom.h = cgeom.h;
75         }
76
77         part.get_alignment().apply(pgeom, geom, part.get_margin());
78         return pgeom;
79 }
80
81 void Container::autosize_child(const Widget &child, const Part &part, Geometry &ageom) const
82 {
83         Geometry cgeom = determine_child_geometry(child, part);
84         const Sides &margin = part.get_margin();
85         ageom.w = max(ageom.w, cgeom.w+margin.left+margin.right);
86         ageom.h = max(ageom.h, cgeom.h+margin.top+margin.bottom);
87 }
88
89 void Container::reposition_child(Widget &child, const Part &part) const
90 {
91         child.set_geometry(determine_child_geometry(child, part));
92 }
93
94 list<Widget *> Container::get_children() const
95 {
96         list<Widget *> result;
97         for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
98                 result.push_back((*i)->widget);
99         return result;
100 }
101
102 Widget *Container::get_child_at(int x, int y) const
103 {
104         for(list<Child *>::const_iterator i=children.end(); i!=children.begin();)
105                 if((*--i)->widget->is_visible() && (*i)->widget->get_geometry().is_inside(x, y))
106                         return (*i)->widget;
107
108         return 0;
109 }
110
111 Widget *Container::get_descendant_at(int x, int y) const
112 {
113         Widget *wdg = get_child_at(x, y);
114         if(Container *cont = dynamic_cast<Container *>(wdg))
115         {
116                 const Geometry &cgeom = wdg->get_geometry();
117                 Widget *wdg2 = cont->get_descendant_at(x-cgeom.x, y-cgeom.y);
118                 if(wdg2)
119                         return wdg2;
120         }
121         return wdg;
122 }
123
124 void Container::raise(Widget &wdg)
125 {
126         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
127                 if((*i)->widget==&wdg)
128                 {
129                         children.splice(children.end(), children, i);
130                         return;
131                 }
132
133         throw hierarchy_error("widget not in container");
134 }
135
136 void Container::set_pointer_focus(Widget *wdg, bool grab)
137 {
138         if(wdg!=pointer_focus)
139         {
140                 if(pointer_focus)
141                         pointer_focus->pointer_leave();
142
143                 pointer_focus = wdg;
144                 pointer_grabbed = grab;
145
146                 if(pointer_focus)
147                         pointer_focus->pointer_enter();
148         }
149         else
150                 pointer_grabbed = grab;
151 }
152
153 void Container::set_input_focus(Widget *wdg)
154 {
155         if(wdg!=input_focus)
156         {
157                 if(input_focus)
158                         input_focus->focus_out();
159
160                 input_focus = wdg;
161                 on_input_focus_changed(input_focus);
162
163                 if(input_focus)
164                         input_focus->focus_in();
165         }
166 }
167
168 Widget *Container::get_final_input_focus() const
169 {
170         if(Container *container = dynamic_cast<Container *>(input_focus))
171                 if(Widget *focus = container->get_final_input_focus())
172                         return focus;
173
174         return input_focus;
175 }
176
177 void Container::check_animation_interval()
178 {
179         Time::TimeDelta shortest;
180         for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
181         {
182                 const Time::TimeDelta &child_iv = (*i)->widget->get_animation_interval();
183                 if(child_iv && (!shortest || child_iv<shortest))
184                         shortest = child_iv;
185         }
186
187         if(shortest!=anim_interval)
188                 set_animation_interval(shortest);
189 }
190
191 void Container::rebuild_hierarchy()
192 {
193         Widget::rebuild_hierarchy();
194
195         if(children_rebuild_needed)
196         {
197                 children_rebuild_needed = false;
198                 for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
199                         (*i)->widget->rebuild_hierarchy();
200         }
201 }
202
203 void Container::button_press(int x, int y, unsigned btn)
204 {
205         if(Widget *child = get_pointer_target(x, y, false))
206         {
207                 if(!click_focus)
208                 {
209                         set_pointer_focus(child);
210                         if(child->is_focusable())
211                                 set_input_focus(child);
212
213                         click_focus = child;
214                         click_button = btn;
215                 }
216
217                 const Geometry &cgeom = child->get_geometry();
218                 child->button_press(x-cgeom.x, y-cgeom.y, btn);
219         }
220 }
221
222 void Container::button_release(int x, int y, unsigned btn)
223 {
224         if(Widget *child = get_pointer_target(x, y, false))
225         {
226                 if(child==click_focus && btn==click_button)
227                 {
228                         click_focus = 0;
229                         if(!pointer_focus)
230                                 set_pointer_focus(get_child_at(x, y));
231                 }
232
233                 const Geometry &cgeom = child->get_geometry();
234                 child->button_release(x-cgeom.x, y-cgeom.y, btn);
235         }
236 }
237
238 void Container::pointer_motion(int x, int y)
239 {
240         Widget *child = get_pointer_target(x, y, false);
241         if(!pointer_grabbed)
242                 set_pointer_focus((child && child->get_geometry().is_inside(x, y)) ? child : 0);
243
244         if(child)
245         {
246                 const Geometry &cgeom = child->get_geometry();
247                 child->pointer_motion(x-cgeom.x, y-cgeom.y);
248         }
249 }
250
251 Widget *Container::get_pointer_target(int x, int y, bool touch) const
252 {
253         if(pointer_grabbed)
254                 return pointer_focus;
255         else if(!touch && click_focus)
256                 return click_focus;
257         else if(touch && touch_focus)
258                 return touch_focus;
259         else
260         {
261                 Widget *child = get_child_at(x, y);
262                 if(child && child->is_enabled())
263                         return child;
264                 else
265                         return 0;
266         }
267 }
268
269 void Container::pointer_leave()
270 {
271         Widget::pointer_leave();
272         set_pointer_focus(0);
273 }
274
275 void Container::touch_press(int x, int y, unsigned finger)
276 {
277         if(Widget *child = get_pointer_target(x, y, true))
278         {
279                 // TODO track focus for each finger separately
280                 if(!touch_focus)
281                         touch_focus = child;
282
283                 const Geometry &cgeom = child->get_geometry();
284                 child->touch_press(x-cgeom.x, y-cgeom.y, finger);
285         }
286 }
287
288 void Container::touch_release(int x, int y, unsigned finger)
289 {
290         if(Widget *child = get_pointer_target(x, y, true))
291         {
292                 // TODO track focus for each finger separately
293                 if(child==touch_focus)
294                         touch_focus = 0;
295
296                 const Geometry &cgeom = child->get_geometry();
297                 child->touch_release(x-cgeom.x, y-cgeom.y, finger);
298         }
299 }
300
301 void Container::touch_motion(int x, int y, unsigned finger)
302 {
303         if(Widget *child = get_pointer_target(x, y, true))
304         {
305                 const Geometry &cgeom = child->get_geometry();
306                 child->touch_motion(x-cgeom.x, y-cgeom.y, finger);
307         }
308 }
309
310 bool Container::key_press(unsigned key, unsigned mod)
311 {
312         if(input_focus && input_focus->is_enabled())
313                 return input_focus->key_press(key, mod);
314         else
315                 return false;
316 }
317
318 bool Container::key_release(unsigned key, unsigned mod)
319 {
320         if(input_focus && input_focus->is_enabled())
321                 return input_focus->key_release(key, mod);
322         else
323                 return false;
324 }
325
326 bool Container::character(wchar_t ch)
327 {
328         if(input_focus && input_focus->is_enabled())
329                 return input_focus->character(ch);
330         else
331                 return false;
332 }
333
334 void Container::focus_in()
335 {
336         if(saved_input_focus && saved_input_focus->get_parent()==this)
337                 set_input_focus(saved_input_focus);
338         Widget::focus_in();
339 }
340
341 void Container::focus_out()
342 {
343         saved_input_focus = input_focus;
344         set_input_focus(0);
345         Widget::focus_out();
346 }
347
348 bool Container::navigate(Navigation nav)
349 {
350         if(input_focus && input_focus->is_enabled())
351                 return input_focus->navigate(nav);
352         else
353                 return false;
354 }
355
356 void Container::animate(const Time::TimeDelta &dt)
357 {
358         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
359         {
360                 const Time::TimeDelta &child_iv = (*i)->widget->get_animation_interval();
361                 if(!child_iv)
362                         continue;
363
364                 (*i)->time_since_animate += dt;
365                 if((*i)->time_since_animate>=child_iv)
366                 {
367                         Time::TimeDelta child_dt = (*i)->time_since_animate;
368                         (*i)->time_since_animate = min((*i)->time_since_animate-child_iv, child_iv);
369                         (*i)->widget->animate(child_dt);
370                 }
371         }
372 }
373
374 void Container::on_reparent()
375 {
376         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
377         {
378                 if(Container *c = dynamic_cast<Container *>((*i)->widget))
379                         c->on_reparent();
380                 (*i)->widget->update_style();
381         }
382 }
383
384 void Container::on_input_focus_changed(Widget *wdg)
385 {
386         if(wdg)
387                 raise(*wdg);
388 }
389
390
391 Container::Child::Child(Container &c, Widget *w):
392         container(c),
393         widget(w)
394 {
395         widget->signal_visibility_changed.connect(sigc::mem_fun(this, &Child::visibility_changed));
396         widget->signal_request_focus.connect(sigc::mem_fun(this, &Child::request_focus));
397         widget->signal_grab_pointer.connect(sigc::mem_fun(this, &Child::grab_pointer));
398         widget->signal_ungrab_pointer.connect(sigc::mem_fun(this, &Child::ungrab_pointer));
399         widget->signal_request_animation.connect(sigc::mem_fun(this, &Child::request_animation));
400         widget->signal_rebuild_needed.connect(sigc::mem_fun(this, &Child::rebuild_needed));
401 }
402
403 Container::Child::~Child()
404 {
405         visibility_changed(false);
406 }
407
408 void Container::Child::visibility_changed(bool v)
409 {
410         if(!v)
411         {
412                 if(widget==container.click_focus)
413                         container.click_focus = 0;
414                 if(widget==container.pointer_focus)
415                         container.set_pointer_focus(0);
416                 if(widget==container.input_focus)
417                         container.set_input_focus(0);
418         }
419 }
420
421 void Container::Child::request_focus()
422 {
423         if(container.parent && container.visible)
424                 container.set_focus();
425         if(container.state&FOCUS)
426                 container.set_input_focus(widget);
427 }
428
429 void Container::Child::grab_pointer()
430 {
431         if(!container.pointer_grabbed)
432         {
433                 container.set_pointer_focus(widget, true);
434                 container.signal_grab_pointer.emit();
435         }
436 }
437
438 void Container::Child::ungrab_pointer()
439 {
440         if(container.pointer_grabbed && container.pointer_focus==widget)
441         {
442                 // XXX Should set to the widget under pointer
443                 container.set_pointer_focus(0);
444                 container.signal_ungrab_pointer.emit();
445         }
446 }
447
448 void Container::Child::request_animation(const Time::TimeDelta &interval)
449 {
450         if(!interval)
451                 time_since_animate = Time::zero;
452         container.check_animation_interval();
453 }
454
455 void Container::Child::rebuild_needed()
456 {
457         container.children_rebuild_needed = true;
458         container.signal_rebuild_needed.emit();
459 }
460
461 } // namespace GLtk
462 } // namespace Msp