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