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