]> git.tdb.fi Git - libs/gltk.git/blob - source/panel.cpp
Do not navigate out of a widget holding the pointer grab
[libs/gltk.git] / source / panel.cpp
1 #include <algorithm>
2 #include <msp/core/maputils.h>
3 #include <msp/core/refptr.h>
4 #include "button.h"
5 #include "column.h"
6 #include "draghandle.h"
7 #include "dropdown.h"
8 #include "entry.h"
9 #include "grid.h"
10 #include "hslider.h"
11 #include "image.h"
12 #include "indicator.h"
13 #include "label.h"
14 #include "list.h"
15 #include "panel.h"
16 #include "part.h"
17 #include "row.h"
18 #include "stack.h"
19 #include "toggle.h"
20 #include "vslider.h"
21
22 using namespace std;
23
24 namespace Msp {
25 namespace GLtk {
26
27 Panel::Panel():
28         layout(0)
29 {
30         input_type = INPUT_NAVIGATION;
31 }
32
33 Panel::~Panel()
34 {
35         delete layout;
36         layout = 0;
37 }
38
39 void Panel::set_layout(Layout *l)
40 {
41         l->set_container(*this);
42         delete layout;
43         layout = l;
44 }
45
46 void Panel::autosize_special(const Part &part, Geometry &ageom) const
47 {
48         if(part.get_name()=="children" && layout)
49                 layout->autosize(ageom);
50 }
51
52 void Panel::render_special(const Part &part, GL::Renderer &renderer) const
53 {
54         if(part.get_name()=="children")
55         {
56                 for(list<Container::Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
57                         if((*i)->widget->is_visible())
58                                 (*i)->widget->render(renderer);
59         }
60 }
61
62 bool Panel::navigate(Navigation nav)
63 {
64         if(Container::navigate(nav))
65                 return true;
66
67         if(nav==NAV_UP || nav==NAV_DOWN || nav==NAV_LEFT || nav==NAV_RIGHT)
68         {
69                 int nav_x = (nav==NAV_RIGHT ? 1 : nav==NAV_LEFT ? -1 : 0);
70                 int nav_y = (nav==NAV_UP ? 1 : nav==NAV_DOWN ? -1 : 0);
71
72                 int origin_x, origin_y, origin_dim;
73                 if(input_focus)
74                 {
75                         const Geometry &fgeom = input_focus->get_geometry();
76                         origin_x = fgeom.x+(nav_x*0.5+0.5)*fgeom.w;
77                         origin_y = fgeom.y+(nav_y*0.5+0.5)*fgeom.h;
78                         origin_dim = abs(nav_x)*fgeom.h+abs(nav_y)*fgeom.w;
79                 }
80                 else
81                 {
82                         origin_x = geom.w*(0.5-nav_x*0.5);
83                         origin_y = geom.h*(0.5-nav_y*0.5);
84                         origin_dim = abs(nav_x)*geom.h+abs(nav_y)*geom.w;
85                 }
86
87                 if(pointer_grabbed && pointer_focus==input_focus)
88                         return false;
89
90                 Widget *sibling = find_next_child(origin_x, origin_y, origin_dim, nav_x, nav_y);
91                 if(!sibling && input_focus)
92                 {
93                         const Geometry &fgeom = input_focus->get_geometry();
94                         origin_x -= fgeom.w*(nav_x*0.5);
95                         origin_y -= fgeom.h*(nav_y*0.5);
96                         sibling = find_next_child(origin_x, origin_y, origin_dim, nav_x, nav_y);
97                 }
98
99                 if(sibling)
100                 {
101                         set_input_focus(sibling);
102                         if(Panel *panel = dynamic_cast<Panel *>(sibling))
103                                 panel->navigate(nav);
104                         return true;
105                 }
106         }
107
108         return false;
109 }
110
111 Widget *Panel::find_next_child(int origin_x, int origin_y, int origin_dim, int nav_x, int nav_y) const
112 {
113         Widget *sibling = 0;
114         int best_score = 0;
115         for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
116         {
117                 if((*i)->widget==input_focus || !(*i)->widget->is_focusable())
118                         continue;
119
120                 const Geometry &cgeom = (*i)->widget->get_geometry();
121                 int dx = compute_delta(cgeom.x, cgeom.w, origin_x, origin_dim, nav_x);
122                 int dy = compute_delta(cgeom.y, cgeom.h, origin_y, origin_dim, nav_y);
123
124                 int score = -1;
125                 if(nav_y && nav_y*dy>=0)
126                         score = nav_y*dy+abs(dx)*4;
127                 else if(nav_x && nav_x*dx>=0)
128                         score = nav_x*dx+abs(dy)*4;
129
130                 if(score>=0 && (!sibling || score<best_score))
131                 {
132                         sibling = (*i)->widget;
133                         best_score = score;
134                 }
135         }
136
137         return sibling;
138 }
139
140 int Panel::compute_delta(int pos, int dim, int origin_pos, int origin_dim, int nav)
141 {
142         if(nav<0)
143                 return pos+dim-origin_pos;
144         else if(nav>0)
145                 return pos-origin_pos;
146         else if(pos+dim<origin_pos-origin_dim/2)
147                 return pos+dim+origin_dim/2-origin_pos;
148         else if(pos>origin_pos+origin_dim/2)
149                 return pos-origin_pos-origin_dim/2;
150         else
151                 return 0;
152 }
153
154 void Panel::on_geometry_change()
155 {
156         if(layout)
157                 layout->update();
158 }
159
160 void Panel::on_child_added(Widget &wdg)
161 {
162         if(layout)
163         {
164                 layout->add_widget(wdg);
165                 signal_autosize_changed.emit();
166         }
167 }
168
169 void Panel::on_child_removed(Widget &wdg)
170 {
171         if(layout)
172         {
173                 layout->remove_widget(wdg);
174                 signal_autosize_changed.emit();
175         }
176 }
177
178
179 Panel::Loader::Loader(Panel &p, map<string, Widget *> &m):
180         DataFile::DerivedObjectLoader<Panel, Widget::Loader>(p),
181         wdg_map(m),
182         last_widget(0)
183 {
184         add_child_type<Button>("button");
185         add("column",    &Loader::arrangement<Column>);
186         add("constraint",&Loader::constraint);
187         add_child_type<DragHandle>("draghandle");
188         add_child_type<Dropdown>("dropdown");
189         add_child_type<Entry>("entry");
190         add("expand",    &Loader::expand);
191         add("ghost",     &Loader::ghost);
192         add("gravity",   &Loader::gravity);
193         add("grid",      &Loader::grid);
194         add_child_type<HSlider>("hslider");
195         add_child_type<Image>("image");
196         add_child_type<Indicator>("indicator");
197         add_child_type<Label>("label");
198         add("layout",    &Loader::layout);
199         add_child_type<List>("list");
200         add_child_type<Panel>("panel");
201         add("row",       &Loader::arrangement<Row>);
202         add("stack",     &Loader::arrangement<Stack>);
203         add_child_type<Toggle>("toggle");
204         add_child_type<VSlider>("vslider");
205 }
206
207 template<typename T>
208 void Panel::Loader::add_child_type(const string &keyword)
209 {
210         add(keyword, &Loader::child<T>);
211         add(keyword, &Loader::unnamed_child<T>);
212 }
213
214 Layout &Panel::Loader::get_layout()
215 {
216         if(!obj.layout)
217                 obj.set_layout(new Layout);
218
219         return *obj.layout;
220 }
221
222 Widget &Panel::Loader::get_last_widget()
223 {
224         if(!last_widget)
225                 throw logic_error("no widget loaded");
226
227         return *last_widget;
228 }
229
230 template<typename T>
231 void Panel::Loader::arrangement()
232 {
233         T arr(get_layout());
234         ArrangedLoader<T> ldr(*this, arr);
235         load_sub_with(ldr);
236 }
237
238 template<typename T>
239 void Panel::Loader::child(const string &n)
240 {
241         unnamed_child<T>();
242         wdg_map[n] = last_widget;
243 }
244
245 void Panel::Loader::constraint(Layout::ConstraintType type, const string &n)
246 {
247         Widget &src = get_last_widget();
248         Widget &tgt = *get_item(wdg_map, n);
249         get_layout().add_constraint(src, type, tgt);
250 }
251
252 void Panel::Loader::expand(bool h, bool v)
253 {
254         get_layout().set_expand(get_last_widget(), h, v);
255 }
256
257 void Panel::Loader::ghost(bool g)
258 {
259         get_layout().set_ghost(get_last_widget(), g);
260 }
261
262 void Panel::Loader::gravity(int h, int v)
263 {
264         get_layout().set_gravity(get_last_widget(), h, v);
265 }
266
267 void Panel::Loader::grid(unsigned cols)
268 {
269         Grid grd(get_layout(), cols);
270         ArrangedLoader<Grid> ldr(*this, grd);
271         load_sub_with(ldr);
272 }
273
274 void Panel::Loader::layout()
275 {
276         Layout::Loader ldr(get_layout(), wdg_map);
277         load_sub_with(ldr);
278 }
279
280 template<typename T>
281 void Panel::Loader::unnamed_child()
282 {
283         RefPtr<T> chl = new T();
284         load_sub(*chl);
285         obj.add(*chl.get());
286         last_widget = chl.release();
287 }
288
289 template<>
290 void Panel::Loader::unnamed_child<Panel>()
291 {
292         RefPtr<Panel> pnl = new Panel();
293         load_sub(*pnl, wdg_map);
294         obj.add(*pnl.get());
295         last_widget = pnl.release();
296 }
297
298 template<typename T>
299 Panel::ArrangedLoader<T>::ArrangedLoader(Loader &ldr, T &arr):
300         arr_loader(arr)
301 {
302         add_auxiliary_loader(ldr);
303         add_auxiliary_loader(arr_loader);
304 }
305
306 } // namespace GLtk
307 } // namespace Msp