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