1 #include <msp/core/algorithm.h>
10 hierarchy_error::hierarchy_error(const string &w):
15 Container::~Container()
17 while(!children.empty())
18 delete children.front()->widget;
21 void Container::add(Widget &wdg)
24 children.push_back(create_child(&wdg));
25 if(wdg.get_animation_interval())
26 check_animation_interval();
27 children_rebuild_needed = true;
28 signal_rebuild_needed.emit();
32 void Container::remove(Widget &wdg)
34 auto i = find_if(children, [&wdg](const Child *c){ return c->widget==&wdg; });
36 throw hierarchy_error("widget not in container");
38 if(&wdg==saved_input_focus)
39 saved_input_focus = nullptr;
40 wdg.set_parent(nullptr);
43 if(wdg.get_animation_interval())
44 check_animation_interval();
45 on_child_removed(wdg);
48 Container::Child *Container::create_child(Widget *wdg)
50 return new Child(*this, wdg);
53 Geometry Container::determine_child_geometry(const Widget &child, const Part &part) const
55 Geometry pgeom = part.get_geometry();
56 if(!pgeom.w || !pgeom.h)
59 child.autosize(cgeom);
66 part.get_alignment().apply(pgeom, geom, part.get_margin());
70 void Container::autosize_child(const Widget &child, const Part &part, Geometry &ageom) const
72 Geometry cgeom = determine_child_geometry(child, part);
73 const Sides &margin = part.get_margin();
74 ageom.w = max(ageom.w, cgeom.w+margin.left+margin.right);
75 ageom.h = max(ageom.h, cgeom.h+margin.top+margin.bottom);
78 void Container::reposition_child(Widget &child, const Part &part) const
80 child.set_geometry(determine_child_geometry(child, part));
83 list<Widget *> Container::get_children() const
85 list<Widget *> result;
86 for(const Child *c: children)
87 result.push_back(c->widget);
91 Widget *Container::find_child_at(int x, int y) const
93 for(auto i=children.end(); i!=children.begin();)
94 if((*--i)->widget->is_visible() && (*i)->widget->get_geometry().is_inside(x, y))
100 Widget *Container::find_descendant_at(int x, int y) const
102 Widget *wdg = find_child_at(x, y);
103 if(Container *cont = dynamic_cast<Container *>(wdg))
105 const Geometry &cgeom = wdg->get_geometry();
106 Widget *wdg2 = cont->find_descendant_at(x-cgeom.x, y-cgeom.y);
113 void Container::raise(Widget &wdg)
115 auto i = find_if(children, [&wdg](const Child *c){ return c->widget==&wdg; });
116 if(i==children.end())
117 throw hierarchy_error("widget not in container");
119 children.splice(children.end(), children, i);
122 void Container::set_pointer_focus(Widget *wdg, bool grab)
124 if(wdg!=pointer_focus)
127 pointer_focus->pointer_leave();
130 pointer_grabbed = grab;
133 pointer_focus->pointer_enter();
136 pointer_grabbed = grab;
139 void Container::set_input_focus(Widget *wdg)
144 input_focus->focus_out();
147 on_input_focus_changed(input_focus);
150 input_focus->focus_in();
154 Widget *Container::get_final_input_focus() const
156 if(Container *container = dynamic_cast<Container *>(input_focus))
157 if(Widget *focus = container->get_final_input_focus())
163 void Container::check_animation_interval()
165 Time::TimeDelta shortest;
166 for(const Child *c: children)
168 const Time::TimeDelta &child_iv = c->widget->get_animation_interval();
169 if(child_iv && (!shortest || child_iv<shortest))
173 if(shortest!=anim_interval)
174 set_animation_interval(shortest);
177 void Container::rebuild_hierarchy()
179 Widget::rebuild_hierarchy();
181 if(children_rebuild_needed)
183 children_rebuild_needed = false;
184 for(Child *c: children)
185 c->widget->rebuild_hierarchy();
189 void Container::button_press(int x, int y, unsigned btn)
191 if(Widget *child = get_pointer_target(x, y, false))
195 set_pointer_focus(child);
196 if(child->is_focusable())
197 set_input_focus(child);
203 const Geometry &cgeom = child->get_geometry();
204 child->button_press(x-cgeom.x, y-cgeom.y, btn);
208 void Container::button_release(int x, int y, unsigned btn)
210 if(Widget *child = get_pointer_target(x, y, false))
212 if(child==click_focus && btn==click_button)
214 click_focus = nullptr;
216 set_pointer_focus(find_child_at(x, y));
219 const Geometry &cgeom = child->get_geometry();
220 child->button_release(x-cgeom.x, y-cgeom.y, btn);
224 void Container::pointer_motion(int x, int y)
226 Widget *child = get_pointer_target(x, y, false);
228 set_pointer_focus((child && child->get_geometry().is_inside(x, y)) ? child : nullptr);
232 const Geometry &cgeom = child->get_geometry();
233 child->pointer_motion(x-cgeom.x, y-cgeom.y);
237 Widget *Container::get_pointer_target(int x, int y, bool touch) const
240 return pointer_focus;
241 else if(!touch && click_focus)
243 else if(touch && touch_focus)
247 Widget *child = find_child_at(x, y);
248 if(child && child->is_enabled())
255 void Container::pointer_leave()
257 Widget::pointer_leave();
258 set_pointer_focus(nullptr);
261 void Container::touch_press(int x, int y, unsigned finger)
263 if(Widget *child = get_pointer_target(x, y, true))
265 // TODO track focus for each finger separately
269 const Geometry &cgeom = child->get_geometry();
270 child->touch_press(x-cgeom.x, y-cgeom.y, finger);
274 void Container::touch_release(int x, int y, unsigned finger)
276 if(Widget *child = get_pointer_target(x, y, true))
278 // TODO track focus for each finger separately
279 if(child==touch_focus)
280 touch_focus = nullptr;
282 const Geometry &cgeom = child->get_geometry();
283 child->touch_release(x-cgeom.x, y-cgeom.y, finger);
287 void Container::touch_motion(int x, int y, unsigned finger)
289 if(Widget *child = get_pointer_target(x, y, true))
291 const Geometry &cgeom = child->get_geometry();
292 child->touch_motion(x-cgeom.x, y-cgeom.y, finger);
296 bool Container::key_press(unsigned key, unsigned mod)
298 if(input_focus && input_focus->is_enabled())
299 return input_focus->key_press(key, mod);
304 bool Container::key_release(unsigned key, unsigned mod)
306 if(input_focus && input_focus->is_enabled())
307 return input_focus->key_release(key, mod);
312 bool Container::character(wchar_t ch)
314 if(input_focus && input_focus->is_enabled())
315 return input_focus->character(ch);
320 void Container::focus_in()
322 if(saved_input_focus)
323 set_input_focus(saved_input_focus);
327 void Container::focus_out()
329 saved_input_focus = input_focus;
330 set_input_focus(nullptr);
334 bool Container::navigate(Navigation nav)
336 if(input_focus && input_focus->is_enabled())
337 return input_focus->navigate(nav);
342 void Container::animate(const Time::TimeDelta &dt)
344 for(Child *c: children)
346 const Time::TimeDelta &child_iv = c->widget->get_animation_interval();
350 c->time_since_animate += dt;
351 if(c->time_since_animate>=child_iv)
353 Time::TimeDelta child_dt = c->time_since_animate;
354 c->time_since_animate = min(c->time_since_animate-child_iv, child_iv);
355 c->widget->animate(child_dt);
360 void Container::on_reparent()
362 for(const Child *c: children)
364 if(Container *o = dynamic_cast<Container *>(c->widget))
366 c->widget->update_style();
370 void Container::on_input_focus_changed(Widget *wdg)
377 Container::Child::Child(Container &c, Widget *w):
381 widget->signal_visibility_changed.connect(sigc::mem_fun(this, &Child::visibility_changed));
382 widget->signal_request_focus.connect(sigc::mem_fun(this, &Child::request_focus));
383 widget->signal_grab_pointer.connect(sigc::mem_fun(this, &Child::grab_pointer));
384 widget->signal_ungrab_pointer.connect(sigc::mem_fun(this, &Child::ungrab_pointer));
385 widget->signal_request_animation.connect(sigc::mem_fun(this, &Child::request_animation));
386 widget->signal_rebuild_needed.connect(sigc::mem_fun(this, &Child::rebuild_needed));
389 Container::Child::~Child()
391 visibility_changed(false);
394 void Container::Child::visibility_changed(bool v)
398 if(widget==container.click_focus)
399 container.click_focus = nullptr;
400 if(widget==container.pointer_focus)
401 container.set_pointer_focus(nullptr);
402 if(widget==container.input_focus)
403 container.set_input_focus(nullptr);
407 void Container::Child::request_focus()
409 if(container.parent && container.visible)
410 container.set_focus();
411 if(container.state&FOCUS)
412 container.set_input_focus(widget);
415 void Container::Child::grab_pointer()
417 if(!container.pointer_grabbed)
419 container.set_pointer_focus(widget, true);
420 container.signal_grab_pointer.emit();
424 void Container::Child::ungrab_pointer()
426 if(container.pointer_grabbed && container.pointer_focus==widget)
428 // XXX Should set to the widget under pointer
429 container.set_pointer_focus(nullptr);
430 container.signal_ungrab_pointer.emit();
434 void Container::Child::request_animation(const Time::TimeDelta &interval)
437 time_since_animate = Time::zero;
438 container.check_animation_interval();
441 void Container::Child::rebuild_needed()
443 container.children_rebuild_needed = true;
444 container.signal_rebuild_needed.emit();