]> git.tdb.fi Git - libs/gltk.git/blob - list.cpp
7041f733759da99585d2b4dde562458a9684ea3e
[libs/gltk.git] / 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         }
136         else
137                 throw out_of_range("List::set_selected_index");
138 }
139
140 const string &List::get_selected() const
141 {
142         if(sel_index<0)
143                 throw logic_error("sel_index<0");
144
145         return items[sel_index];
146 }
147
148 void List::rebuild_special(const Part &part, CachedPart &cache)
149 {
150         if(part.get_name()=="items")
151         {
152                 const Sides &margin = part.get_margin();
153                 Geometry pgeom = geom;
154                 pgeom.h = row_height+margin.top+margin.bottom;
155
156                 const GL::Font &font = style->get_font();
157                 cache.texture = &font.get_texture();
158                 cache.clear_mesh();
159
160                 GL::MeshBuilder bld(*cache.mesh);
161                 bld.color(style->get_font_color());
162                 bld.matrix() *= GL::Matrix::translation(pgeom.x, pgeom.y+geom.h-pgeom.h, 0);
163
164                 for(unsigned i=0; (i<n_visible && first+i<items.size()); ++i)
165                 {
166                         if(i!=0)
167                                 bld.matrix() *= GL::Matrix::translation(0, -static_cast<int>(row_height), 0);
168
169                         GL::MatrixStack::Push _pushm(bld.matrix());
170                         bld.matrix() *= GL::Matrix::scaling(style->get_font_size());
171
172                         style->get_font().build_string(items[first+i], bld);
173                 }
174         }
175         else if(part.get_name()=="selection")
176         {
177                 if(sel_index>=static_cast<int>(first) && sel_index<static_cast<int>(first+n_visible))
178                 {
179                         const Sides &margin = part.get_margin();
180
181                         Geometry pgeom = geom;
182                         pgeom.h = row_height;
183                         pgeom.w -= margin.left+margin.right;
184
185                         Geometry rgeom = part.get_geometry();
186                         rgeom.y += geom.h-margin.top-row_height*(sel_index-first+1);
187                         rgeom.x += margin.left;
188                         part.get_alignment().apply(rgeom, pgeom);
189
190                         cache.texture = part.get_graphic(state)->get_texture();
191                         cache.clear_mesh();
192
193                         GL::MeshBuilder bld(*cache.mesh);
194                         bld.matrix() *= GL::Matrix::translation(rgeom.x, rgeom.y, 0);
195                         part.get_graphic(state)->build(rgeom.w, rgeom.h, bld);
196                 }
197                 else
198                         cache.texture = 0;
199         }
200 }
201
202 void List::render_special(const Part &part, GL::Renderer &renderer) const
203 {
204         if(part.get_name()=="slider")
205                 slider.render(renderer);
206 }
207
208 void List::button_press(int x, int y, unsigned btn)
209 {
210         Container::button_press(x, y, btn);
211         if(!click_focus && btn==1)
212         {
213                 if(items_part)
214                         y += items_part->get_margin().top;
215
216                 unsigned i = (geom.h-1-y)/row_height;
217                 if(i<n_visible && first+i<items.size())
218                 {
219                         sel_index = first+i;
220
221                         signal_item_selected.emit(sel_index, items[sel_index]);
222                         rebuild();
223                 }
224         }
225 }
226
227 void List::on_geometry_change()
228 {
229         reposition_slider();
230
231         check_view_range();
232 }
233
234 void List::on_style_change()
235 {
236         if(!style)
237         {
238                 items_part = 0;
239                 return;
240         }
241
242         reposition_slider();
243
244         items_part = style->get_part("items");
245
246         const GL::Font &font = style->get_font();
247         row_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*style->get_font_size());
248
249         check_view_range();
250 }
251
252 void List::reposition_slider()
253 {
254         if(!style)
255                 return;
256
257         if(const Part *slider_part = style->get_part("slider"))
258         {
259                 Geometry sgeom = slider_part->get_geometry();
260                 slider_part->get_alignment().apply(sgeom, geom, slider_part->get_margin());
261                 slider.set_geometry(sgeom);
262         }
263 }
264
265 void List::check_view_range()
266 {
267         unsigned h = geom.h;
268         if(items_part)
269         {
270                 const Sides &margin = items_part->get_margin();
271                 h -= margin.top+margin.bottom;
272         }
273
274         n_visible = h/row_height;
275
276         if(first+n_visible>items.size())
277         {
278                 if(items.size()>n_visible)
279                         first = items.size()-n_visible;
280                 else
281                         first = 0;
282         }
283
284         if(items.size()>n_visible)
285         {
286                 slider.set_range(0, items.size()-n_visible);
287                 slider.set_value(items.size()-n_visible-first);
288         }
289         else
290         {
291                 slider.set_range(0, 0);
292                 slider.set_value(0);
293         }
294 }
295
296 void List::slider_value_changed(double value)
297 {
298         if(items.size()>n_visible)
299         {
300                 first = items.size()-n_visible-static_cast<unsigned>(value);
301                 rebuild();
302         }
303 }
304
305
306 List::Loader::Loader(List &l):
307         Widget::Loader(l)
308 {
309         add("item", &Loader::item);
310 }
311
312 void List::Loader::item(const string &v)
313 {
314         dynamic_cast<List &>(obj).append(v);
315 }
316
317 } // namespace GLtk
318 } // namespace Msp