]> git.tdb.fi Git - libs/gltk.git/blob - source/list.cpp
Implement autosize for List and use it from Dropdown
[libs/gltk.git] / source / list.cpp
1 /* $Id$
2
3 This file is part of libmspgltk
4 Copyright © 2007-2009  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <msp/gl/immediate.h>
9 #include <msp/gl/matrix.h>
10 #include <msp/gl/transform.h>
11 #include "graphic.h"
12 #include "list.h"
13 #include "part.h"
14 #include "style.h"
15 #include "text.h"
16 #include "vslider.h"
17
18 using namespace std;
19
20 namespace Msp {
21 namespace GLtk {
22
23 List::List(const Resources &r):
24         Widget(r),
25         Container(r),
26         sel_index(-1),
27         first(0),
28         n_visible(1),
29         row_height(1),
30         items_part(0),
31         slider(res)
32 {
33         add(slider);
34         slider.set_step(1);
35         slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
36
37         update_style();
38 }
39
40 void List::autosize()
41 {
42         float font_size = style->get_font()->get_default_size();
43
44         geom.w = 0;
45         for(vector<string>::iterator i=items.begin(); i!=items.end(); ++i)
46         {
47                 unsigned w = static_cast<unsigned>(style->get_font()->get_string_width(*i)*font_size);
48                 geom.w = max(geom.w, w);
49         }
50
51         geom.h = items.size()*row_height;
52
53         if(items_part)
54         {
55                 const Sides &margin = items_part->get_margin();
56                 geom.w += margin.left+margin.right;
57                 geom.h += margin.top+margin.bottom;
58         }
59 }
60
61 void List::append(const string &v)
62 {
63         items.push_back(v);
64         check_view_range();
65 }
66
67 void List::insert(unsigned i, const string &v)
68 {
69         if(i>items.size())
70                 throw InvalidParameterValue("Index out of range");
71
72         items.insert(items.begin()+i, v);
73         check_view_range();
74 }
75
76 void List::remove(unsigned i)
77 {
78         if(i>items.size())
79                 throw InvalidParameterValue("Index out of range");
80
81         items.erase(items.begin()+i);
82         if(sel_index>static_cast<int>(i))
83                 --sel_index;
84         else if(sel_index==static_cast<int>(i))
85                 sel_index = -1;
86
87         check_view_range();
88 }
89
90 void List::clear()
91 {
92         items.clear();
93         sel_index = -1;
94
95         check_view_range();
96 }
97
98 void List::set_selected_index(int i)
99 {
100         if(i<0)
101                 sel_index = -1;
102         else if(i<static_cast<int>(items.size()))
103         {
104                 sel_index = i;
105                 signal_item_selected.emit(sel_index, items[sel_index]);
106         }
107         else
108                 throw InvalidParameterValue("Index out of range");
109 }
110
111 const string &List::get_selected() const
112 {
113         if(sel_index<0)
114                 throw InvalidState("No selection");
115
116         return items[sel_index];
117 }
118
119 void List::button_press(int x, int y, unsigned btn)
120 {
121         Container::button_press(x, y, btn);
122         if(!click_focus && btn==1)
123         {
124                 if(items_part)
125                         y += items_part->get_margin().top;
126
127                 unsigned i = (geom.h-1-y)/row_height;
128                 if(i<n_visible && first+i<items.size())
129                 {
130                         sel_index = first+i;
131
132                         signal_item_selected.emit(sel_index, items[sel_index]);
133                 }
134         }
135 }
136
137 void List::render_special(const Part &part) const
138 {
139         if(part.get_name()=="items")
140         {
141                 const Sides &margin = part.get_margin();
142                 Geometry pgeom = geom;
143                 pgeom.h = row_height+margin.top+margin.bottom;
144
145                 GL::PushMatrix push_mtx;
146                 GL::translate(0, geom.h-pgeom.h, 0);
147
148                 for(unsigned i=0; (i<n_visible && first+i<items.size()); ++i)
149                 {
150                         if(i!=0)
151                                 GL::translate(0, -static_cast<int>(row_height), 0);
152                         Text(*style, items[first+i]).render(part, pgeom);
153                 }
154         }
155         else if(part.get_name()=="selection")
156         {
157                 if(sel_index>=static_cast<int>(first) && sel_index<static_cast<int>(first+n_visible))
158                 {
159                         const Sides &margin = part.get_margin();
160
161                         Geometry pgeom = geom;
162                         pgeom.h = row_height;
163                         pgeom.w -= margin.left+margin.right;
164
165                         Geometry rgeom = part.get_geometry();
166                         rgeom.y += geom.h-margin.top-row_height*(sel_index-first+1);
167                         rgeom.x += margin.left;
168                         part.get_alignment().apply(rgeom, pgeom);
169
170                         GL::push_matrix();
171                         GL::translate(rgeom.x, rgeom.y, 0);
172                         part.get_graphic(state)->render(rgeom.w, rgeom.h);
173                         GL::pop_matrix();
174                 }
175         }
176         else if(part.get_name()=="slider")
177                 slider.render();
178 }
179
180 void List::on_geometry_change()
181 {
182         reposition_slider();
183
184         check_view_range();
185 }
186
187 void List::on_style_change()
188 {
189         reposition_slider();
190
191         items_part = style->get_part("items");
192
193         const GL::Font &font = *style->get_font();
194         row_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font.get_default_size());
195
196         check_view_range();
197 }
198
199 void List::reposition_slider()
200 {
201         if(const Part *slider_part = style->get_part("slider"))
202         {
203                 Geometry sgeom = slider_part->get_geometry();
204                 slider_part->get_alignment().apply(sgeom, geom, slider_part->get_margin());
205                 slider.set_geometry(sgeom);
206         }
207 }
208
209 void List::check_view_range()
210 {
211         unsigned h = geom.h;
212         if(items_part)
213         {
214                 const Sides &margin = items_part->get_margin();
215                 h -= margin.top+margin.bottom;
216         }
217
218         n_visible = h/row_height;
219
220         if(first+n_visible>items.size())
221         {
222                 if(items.size()>n_visible)
223                         first = items.size()-n_visible;
224                 else
225                         first = 0;
226         }
227
228         if(items.size()>n_visible)
229         {
230                 slider.set_range(0, items.size()-n_visible);
231                 slider.set_value(items.size()-n_visible-first);
232         }
233         else
234         {
235                 slider.set_range(0, 0);
236                 slider.set_value(0);
237         }
238 }
239
240 void List::slider_value_changed(double value)
241 {
242         if(items.size()>n_visible)
243                 first = items.size()-n_visible-static_cast<unsigned>(value);
244 }
245
246
247 List::Loader::Loader(List &l):
248         Widget::Loader(l)
249 {
250         add("item", &Loader::item);
251 }
252
253 void List::Loader::item(const string &v)
254 {
255         dynamic_cast<List &>(wdg).append(v);
256 }
257
258 } // namespace GLtk
259 } // namespace Msp