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