]> git.tdb.fi Git - libs/gltk.git/blob - source/panel.cpp
Implement next/previous navigation in Panel
[libs/gltk.git] / source / panel.cpp
1 #include <msp/core/algorithm.h>
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         else if(nav==NAV_NEXT || nav==NAV_PREVIOUS)
108         {
109                 vector<Widget *>::iterator i = find(nav_order, input_focus);
110
111                 if(nav==NAV_NEXT)
112                 {
113                         if(i!=nav_order.end())
114                                 ++i;
115                         if(i==nav_order.end())
116                                 i = nav_order.begin();
117                 }
118                 else
119                 {
120                         if(i==nav_order.begin())
121                                 i = nav_order.end();
122                         --i;
123                 }
124
125                 set_input_focus(*i);
126
127                 return true;
128         }
129
130         return false;
131 }
132
133 Widget *Panel::find_next_child(int origin_x, int origin_y, int origin_dim, int nav_x, int nav_y) const
134 {
135         Widget *sibling = 0;
136         int best_score = 0;
137         for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
138         {
139                 if((*i)->widget==input_focus || !(*i)->widget->is_focusable())
140                         continue;
141
142                 const Geometry &cgeom = (*i)->widget->get_geometry();
143                 int dx = compute_delta(cgeom.x, cgeom.w, origin_x, origin_dim, nav_x);
144                 int dy = compute_delta(cgeom.y, cgeom.h, origin_y, origin_dim, nav_y);
145
146                 int score = -1;
147                 if(nav_y && nav_y*dy>=0)
148                         score = nav_y*dy+abs(dx)*4;
149                 else if(nav_x && nav_x*dx>=0)
150                         score = nav_x*dx+abs(dy)*4;
151
152                 if(score>=0 && (!sibling || score<best_score))
153                 {
154                         sibling = (*i)->widget;
155                         best_score = score;
156                 }
157         }
158
159         return sibling;
160 }
161
162 int Panel::compute_delta(int pos, int dim, int origin_pos, int origin_dim, int nav)
163 {
164         if(nav<0)
165                 return pos+dim-origin_pos;
166         else if(nav>0)
167                 return pos-origin_pos;
168         else if(pos+dim<origin_pos-origin_dim/2)
169                 return pos+dim+origin_dim/2-origin_pos;
170         else if(pos>origin_pos+origin_dim/2)
171                 return pos-origin_pos-origin_dim/2;
172         else
173                 return 0;
174 }
175
176 void Panel::on_geometry_change()
177 {
178         if(layout)
179                 layout->update();
180 }
181
182 void Panel::on_child_added(Widget &wdg)
183 {
184         if(wdg.get_input_type()!=INPUT_NONE)
185                 nav_order.push_back(&wdg);
186
187         if(layout)
188         {
189                 layout->add_widget(wdg);
190                 signal_autosize_changed.emit();
191         }
192 }
193
194 void Panel::on_child_removed(Widget &wdg)
195 {
196         vector<Widget *>::iterator i = std::remove(nav_order.begin(), nav_order.end(), &wdg);
197         if(i!=nav_order.end())
198                 nav_order.erase(i, nav_order.end());
199
200         if(layout)
201         {
202                 layout->remove_widget(wdg);
203                 signal_autosize_changed.emit();
204         }
205 }
206
207
208 Panel::Loader::Loader(Panel &p, map<string, Widget *> &m):
209         DataFile::DerivedObjectLoader<Panel, Widget::Loader>(p),
210         wdg_map(m),
211         last_widget(0)
212 {
213         add_child_type<Button>("button");
214         add("column",    &Loader::arrangement<Column>);
215         add("constraint",&Loader::constraint);
216         add_child_type<DragHandle>("draghandle");
217         add_child_type<Dropdown>("dropdown");
218         add_child_type<Entry>("entry");
219         add("expand",    &Loader::expand);
220         add("ghost",     &Loader::ghost);
221         add("gravity",   &Loader::gravity);
222         add("grid",      &Loader::grid);
223         add_child_type<HSlider>("hslider");
224         add_child_type<Image>("image");
225         add_child_type<Indicator>("indicator");
226         add_child_type<Label>("label");
227         add("layout",    &Loader::layout);
228         add_child_type<List>("list");
229         add_child_type<Panel>("panel");
230         add("row",       &Loader::arrangement<Row>);
231         add("stack",     &Loader::arrangement<Stack>);
232         add_child_type<Toggle>("toggle");
233         add_child_type<VSlider>("vslider");
234 }
235
236 template<typename T>
237 void Panel::Loader::add_child_type(const string &keyword)
238 {
239         add(keyword, &Loader::child<T>);
240         add(keyword, &Loader::unnamed_child<T>);
241 }
242
243 Layout &Panel::Loader::get_layout()
244 {
245         if(!obj.layout)
246                 obj.set_layout(new Layout);
247
248         return *obj.layout;
249 }
250
251 Widget &Panel::Loader::get_last_widget()
252 {
253         if(!last_widget)
254                 throw logic_error("no widget loaded");
255
256         return *last_widget;
257 }
258
259 template<typename T>
260 void Panel::Loader::arrangement()
261 {
262         T arr(get_layout());
263         ArrangedLoader<T> ldr(*this, arr);
264         load_sub_with(ldr);
265 }
266
267 template<typename T>
268 void Panel::Loader::child(const string &n)
269 {
270         unnamed_child<T>();
271         wdg_map[n] = last_widget;
272 }
273
274 void Panel::Loader::constraint(Layout::ConstraintType type, const string &n)
275 {
276         Widget &src = get_last_widget();
277         Widget &tgt = *get_item(wdg_map, n);
278         get_layout().add_constraint(src, type, tgt);
279 }
280
281 void Panel::Loader::expand(bool h, bool v)
282 {
283         get_layout().set_expand(get_last_widget(), h, v);
284 }
285
286 void Panel::Loader::ghost(bool g)
287 {
288         get_layout().set_ghost(get_last_widget(), g);
289 }
290
291 void Panel::Loader::gravity(int h, int v)
292 {
293         get_layout().set_gravity(get_last_widget(), h, v);
294 }
295
296 void Panel::Loader::grid(unsigned cols)
297 {
298         Grid grd(get_layout(), cols);
299         ArrangedLoader<Grid> ldr(*this, grd);
300         load_sub_with(ldr);
301 }
302
303 void Panel::Loader::layout()
304 {
305         Layout::Loader ldr(get_layout(), wdg_map);
306         load_sub_with(ldr);
307 }
308
309 template<typename T>
310 void Panel::Loader::unnamed_child()
311 {
312         RefPtr<T> chl = new T();
313         load_sub(*chl);
314         obj.add(*chl.get());
315         last_widget = chl.release();
316 }
317
318 template<>
319 void Panel::Loader::unnamed_child<Panel>()
320 {
321         RefPtr<Panel> pnl = new Panel();
322         load_sub(*pnl, wdg_map);
323         obj.add(*pnl.get());
324         last_widget = pnl.release();
325 }
326
327 template<typename T>
328 Panel::ArrangedLoader<T>::ArrangedLoader(Loader &ldr, T &arr):
329         arr_loader(arr)
330 {
331         add_auxiliary_loader(ldr);
332         add_auxiliary_loader(arr_loader);
333 }
334
335 } // namespace GLtk
336 } // namespace Msp