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