]> git.tdb.fi Git - libs/gltk.git/blob - source/list.cpp
Fix some problems with List and Dropdown
[libs/gltk.git] / source / list.cpp
1 #include <msp/gl/matrix.h>
2 #include <msp/gl/meshbuilder.h>
3 #include "graphic.h"
4 #include "list.h"
5 #include "part.h"
6 #include "style.h"
7 #include "text.h"
8 #include "vslider.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GLtk {
14
15 List::List():
16         sel_index(-1),
17         first(0),
18         n_visible(1),
19         row_height(1),
20         items_part(0)
21 {
22         add(slider);
23         slider.set_step(1);
24         slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
25 }
26
27 void List::autosize()
28 {
29         autosize_rows(5);
30 }
31
32 void List::autosize_rows(unsigned n)
33 {
34         if(!style)
35                 return;
36
37         Widget::autosize();
38
39         if(items_part)
40         {
41                 const Sides &margin = items_part->get_margin();
42                 const GL::Font &font = style->get_font();
43                 float font_size = style->get_font_size();
44
45                 unsigned max_w = 0;
46                 for(vector<string>::iterator i=items.begin(); i!=items.end(); ++i)
47                 {
48                         unsigned w = static_cast<unsigned>(font.get_string_width(*i)*font_size);
49                         max_w = max(max_w, w);
50                 }
51
52                 geom.w = max(geom.w, max_w+margin.left+margin.right);
53                 geom.h = max(geom.h, n*row_height+margin.top+margin.bottom);
54         }
55
56         if(const Part *slider_part = style->get_part("slider"))
57         {
58                 Geometry sgeom = slider_part->get_geometry();
59                 if(!sgeom.w || !sgeom.h)
60                 {
61                         slider.autosize();
62                         if(!sgeom.w)
63                                 sgeom.w = slider.get_geometry().w;
64                         if(!sgeom.h)
65                                 sgeom.h = slider.get_geometry().h;
66                 }
67
68                 const Sides &margin = slider_part->get_margin();
69                 geom.w = max(geom.w, sgeom.w+margin.left+margin.right);
70                 geom.h = max(geom.h, sgeom.h+margin.top+margin.bottom);
71
72                 reposition_slider();
73         }
74
75         check_view_range();
76         rebuild();
77 }
78
79 void List::autosize_all()
80 {
81         autosize_rows(items.size());
82 }
83
84 void List::append(const string &v)
85 {
86         items.push_back(v);
87         items_changed();
88 }
89
90 void List::insert(unsigned i, const string &v)
91 {
92         if(i>items.size())
93                 throw out_of_range("List::insert");
94
95         items.insert(items.begin()+i, v);
96         items_changed();
97 }
98
99 void List::remove(unsigned i)
100 {
101         if(i>items.size())
102                 throw out_of_range("List::remove");
103
104         items.erase(items.begin()+i);
105         if(sel_index>static_cast<int>(i))
106                 --sel_index;
107         else if(sel_index==static_cast<int>(i))
108                 sel_index = -1;
109
110         items_changed();
111 }
112
113 void List::clear()
114 {
115         items.clear();
116         sel_index = -1;
117         items_changed();
118 }
119
120 void List::items_changed()
121 {
122         check_view_range();
123         signal_autosize_changed.emit();
124         rebuild();
125 }
126
127 void List::set_selected_index(int i)
128 {
129         if(i<0)
130                 sel_index = -1;
131         else if(i<static_cast<int>(items.size()))
132         {
133                 sel_index = i;
134                 signal_item_selected.emit(sel_index, items[sel_index]);
135                 rebuild();
136         }
137         else
138                 throw out_of_range("List::set_selected_index");
139 }
140
141 const string &List::get_selected() const
142 {
143         if(sel_index<0)
144                 throw logic_error("sel_index<0");
145
146         return items[sel_index];
147 }
148
149 void List::rebuild_special(const Part &part, CachedPart &cache)
150 {
151         if(part.get_name()=="items")
152         {
153                 const Sides &margin = part.get_margin();
154                 Geometry pgeom = geom;
155                 pgeom.h = row_height+margin.top+margin.bottom;
156
157                 const GL::Font &font = style->get_font();
158                 cache.texture = &font.get_texture();
159                 cache.clear_mesh();
160
161                 GL::MeshBuilder bld(*cache.mesh);
162                 bld.color(style->get_font_color());
163                 bld.matrix() *= GL::Matrix::translation(margin.left, geom.h-pgeom.h-font.get_descent()*style->get_font_size(), 0);
164
165                 for(unsigned i=0; (i<n_visible && first+i<items.size()); ++i)
166                 {
167                         if(i!=0)
168                                 bld.matrix() *= GL::Matrix::translation(0, -static_cast<int>(row_height), 0);
169
170                         GL::MatrixStack::Push _pushm(bld.matrix());
171                         bld.matrix() *= GL::Matrix::scaling(style->get_font_size());
172
173                         style->get_font().build_string(items[first+i], bld);
174                 }
175         }
176         else if(part.get_name()=="selection")
177         {
178                 if(sel_index>=static_cast<int>(first) && sel_index<static_cast<int>(first+n_visible))
179                 {
180                         const Sides &margin = part.get_margin();
181
182                         Geometry pgeom = geom;
183                         pgeom.h = row_height;
184                         pgeom.w -= margin.left+margin.right;
185
186                         Geometry rgeom = part.get_geometry();
187                         rgeom.y += geom.h-margin.top-row_height*(sel_index-first+1);
188                         rgeom.x += margin.left;
189                         part.get_alignment().apply(rgeom, pgeom);
190
191                         cache.texture = part.get_graphic(state)->get_texture();
192                         cache.clear_mesh();
193
194                         GL::MeshBuilder bld(*cache.mesh);
195                         bld.matrix() *= GL::Matrix::translation(rgeom.x, rgeom.y, 0);
196                         part.get_graphic(state)->build(rgeom.w, rgeom.h, bld);
197                 }
198                 else
199                         cache.texture = 0;
200         }
201 }
202
203 void List::render_special(const Part &part, GL::Renderer &renderer) const
204 {
205         if(part.get_name()=="slider")
206                 slider.render(renderer);
207 }
208
209 void List::button_press(int x, int y, unsigned btn)
210 {
211         Container::button_press(x, y, btn);
212         if(!click_focus && btn==1)
213         {
214                 if(items_part)
215                         y += items_part->get_margin().top;
216
217                 unsigned i = (geom.h-1-y)/row_height;
218                 if(i<n_visible && first+i<items.size())
219                 {
220                         sel_index = first+i;
221
222                         signal_item_selected.emit(sel_index, items[sel_index]);
223                         rebuild();
224                 }
225         }
226 }
227
228 void List::on_geometry_change()
229 {
230         reposition_slider();
231
232         check_view_range();
233 }
234
235 void List::on_style_change()
236 {
237         if(!style)
238         {
239                 items_part = 0;
240                 return;
241         }
242
243         reposition_slider();
244
245         items_part = style->get_part("items");
246
247         const GL::Font &font = style->get_font();
248         row_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*style->get_font_size());
249
250         check_view_range();
251 }
252
253 void List::reposition_slider()
254 {
255         if(!style)
256                 return;
257
258         if(const Part *slider_part = style->get_part("slider"))
259         {
260                 Geometry sgeom = slider_part->get_geometry();
261                 slider_part->get_alignment().apply(sgeom, geom, slider_part->get_margin());
262                 slider.set_geometry(sgeom);
263         }
264 }
265
266 void List::check_view_range()
267 {
268         unsigned h = geom.h;
269         if(items_part)
270         {
271                 const Sides &margin = items_part->get_margin();
272                 h -= margin.top+margin.bottom;
273         }
274
275         n_visible = h/row_height;
276
277         if(first+n_visible>items.size())
278         {
279                 if(items.size()>n_visible)
280                         first = items.size()-n_visible;
281                 else
282                         first = 0;
283         }
284
285         if(items.size()>n_visible)
286         {
287                 slider.set_range(0, items.size()-n_visible);
288                 slider.set_value(items.size()-n_visible-first);
289         }
290         else
291         {
292                 slider.set_range(0, 0);
293                 slider.set_value(0);
294         }
295 }
296
297 void List::slider_value_changed(double value)
298 {
299         if(items.size()>n_visible)
300         {
301                 first = items.size()-n_visible-static_cast<unsigned>(value);
302                 rebuild();
303         }
304 }
305
306
307 List::Loader::Loader(List &l):
308         Widget::Loader(l)
309 {
310         add("item", &Loader::item);
311 }
312
313 void List::Loader::item(const string &v)
314 {
315         dynamic_cast<List &>(obj).append(v);
316 }
317
318 } // namespace GLtk
319 } // namespace Msp