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