]> git.tdb.fi Git - libs/gltk.git/blob - source/entry.cpp
Adjust some slider checks
[libs/gltk.git] / source / entry.cpp
1 /* $Id$
2
3 This file is part of libmspgltk
4 Copyright © 2007-2011  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <msp/gl/matrix.h>
9 #include <msp/gl/texture.h>
10 #include <msp/gl/transform.h>
11 #include <msp/input/keys.h>
12 #include "entry.h"
13 #include "graphic.h"
14 #include "part.h"
15 #include "style.h"
16 #include "vslider.h"
17
18 using namespace std;
19
20 namespace Msp {
21 namespace GLtk {
22
23 Entry::Entry(const string &t):
24         text(),
25         multiline(false),
26         edit_pos(0),
27         first_row(0),
28         visible_rows(1),
29         text_part(0),
30         slider(0)
31 {
32         set_text(t);
33 }
34
35 void Entry::autosize()
36 {
37         if(!style)
38                 return;
39
40         Widget::autosize();
41
42         if(text_part)
43         {
44                 const Sides &margin = text_part->get_margin();
45                 const GL::Font &font = *style->get_font();
46                 unsigned en_width = static_cast<unsigned>(font.get_string_width("n")*font.get_native_size());
47                 geom.w = max(geom.w, 10*en_width+margin.left+margin.right);
48
49                 unsigned line_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font.get_native_size());
50                 if(multiline)
51                 {
52                         unsigned line_spacing = font.get_native_size()*6/5;
53                         geom.h = max(geom.h, line_height+line_spacing*2+margin.top+margin.bottom);
54                 }
55                 else
56                         geom.h = max(geom.h, line_height+margin.top+margin.bottom);
57         }
58
59         if(multiline)
60         {
61                 if(const Part *slider_part = style->get_part("slider"))
62                 {
63                         Geometry sgeom = slider_part->get_geometry();
64                         if(!sgeom.w || !sgeom.h)
65                         {
66                                 slider->autosize();
67                                 if(!sgeom.w)
68                                         sgeom.w = slider->get_geometry().w;
69                                 if(!sgeom.h)
70                                         sgeom.h = slider->get_geometry().h;
71                         }
72
73                         const Sides &margin = slider_part->get_margin();
74                         geom.w = max(geom.w, sgeom.w+margin.left+margin.right);
75                         geom.h = max(geom.h, sgeom.h+margin.top+margin.bottom);
76
77                         reposition_slider();
78                 }
79
80                 check_view_range();
81         }
82 }
83
84 void Entry::set_text(const string &t)
85 {
86         text = t;
87         edit_pos = text.size();
88
89         if(multiline)
90                 check_view_range();
91 }
92
93 void Entry::set_multiline(bool m)
94 {
95         multiline = m;
96         if(multiline)
97         {
98                 if(!slider)
99                 {
100                         slider = new VSlider;
101                         add(*slider);
102                         slider->set_step(1);
103                         slider->signal_value_changed.connect(sigc::mem_fun(this, &Entry::slider_value_changed));
104                         reposition_slider();
105                 }
106                 check_view_range();
107         }
108 }
109
110 void Entry::render_special(const Part &part) const
111 {
112         if(part.get_name()=="text")
113                 text.render(part, geom, first_row);
114         else if(part.get_name()=="cursor")
115         {
116                 if(!text_part || !part.get_graphic(state))
117                         return;
118
119                 unsigned row, col;
120                 text.offset_to_coords(edit_pos, row, col);
121
122                 if(row<first_row || row>=first_row+visible_rows)
123                         return;
124
125                 Geometry rgeom = text.coords_to_geometry(*text_part, geom, first_row, row, col);
126
127                 GL::push_matrix();
128                 GL::translate(rgeom.x, rgeom.y, 0);
129                 part.get_graphic(state)->render(part.get_geometry().w, part.get_geometry().h);
130                 GL::pop_matrix();
131         }
132         else if(part.get_name()=="slider" && multiline)
133                 slider->render();
134 }
135
136 void Entry::key_press(unsigned key, unsigned, wchar_t ch)
137 {
138         if(key==Input::KEY_LEFT)
139         {
140                 if(edit_pos>0)
141                 {
142                         --edit_pos;
143                         check_view_range();
144                 }
145         }
146         else if(key==Input::KEY_RIGHT)
147         {
148                 if(edit_pos<text.size())
149                 {
150                         ++edit_pos;
151                         check_view_range();
152                 }
153         }
154         else if(key==Input::KEY_DOWN && multiline)
155         {
156                 unsigned row, col;
157                 text.offset_to_coords(edit_pos, row, col);
158                 edit_pos = text.coords_to_offset(row+1, col);
159                 check_view_range();
160         }
161         else if(key==Input::KEY_UP && multiline)
162         {
163                 unsigned row, col;
164                 text.offset_to_coords(edit_pos, row, col);
165                 if(row>0)
166                 {
167                         edit_pos = text.coords_to_offset(row-1, col);
168                         check_view_range();
169                 }
170                 else
171                         edit_pos = 0;
172         }
173         else if(key==Input::KEY_BACKSPACE)
174         {
175                 if(edit_pos>0)
176                 {
177                         text.erase(--edit_pos, 1);
178                         check_view_range();
179                 }
180         }
181         else if(key==Input::KEY_ENTER)
182         {
183                 if(multiline)
184                 {
185                         text.insert(edit_pos++, "\n");
186                         check_view_range();
187                 }
188                 else
189                         signal_enter.emit();
190         }
191         else if(ch>=' ')
192         {
193                 text.insert(edit_pos, Codecs::encode<Codecs::Utf8>(Codecs::ustring(1, ch)));
194                 ++edit_pos;
195         }
196 }
197
198 void Entry::on_geometry_change()
199 {
200         reposition_slider();
201
202         if(multiline)
203                 check_view_range();
204 }
205
206 void Entry::on_style_change()
207 {
208         text.set_style(style);
209
210         if(!style)
211         {
212                 text_part = 0;
213                 return;
214         }
215
216         text_part = style->get_part("text");
217
218         reposition_slider();
219
220         if(multiline)
221                 check_view_range();
222 }
223
224 void Entry::reposition_slider()
225 {
226         if(!style || !slider)
227                 return;
228
229         if(const Part *slider_part = style->get_part("slider"))
230         {
231                 Geometry sgeom = slider_part->get_geometry();
232                 if(!sgeom.w || !sgeom.h)
233                 {
234                         slider->autosize();
235                         if(!sgeom.w)
236                                 sgeom.w = slider->get_geometry().w;
237                         if(!sgeom.h)
238                                 sgeom.h = slider->get_geometry().h;
239                 }
240
241                 slider_part->get_alignment().apply(sgeom, geom, slider_part->get_margin());
242                 slider->set_geometry(sgeom);
243         }
244 }
245
246 void Entry::check_view_range()
247 {
248         if(!multiline || !text_part)
249                 return;
250
251         const GL::Font *font = style->get_font();
252         float font_size = font->get_default_size();
253         unsigned line_spacing = static_cast<unsigned>(font_size*6/5);
254
255         const Sides &margin = text_part->get_margin();
256         visible_rows = max((geom.h-margin.top-margin.bottom)/line_spacing, 1U);
257
258         unsigned row, col;
259         text.offset_to_coords(edit_pos, row, col);
260
261         if(first_row>row)
262                 first_row = row;
263         else if(row>=first_row+visible_rows)
264                 first_row = row+1-visible_rows;
265
266         unsigned scroll = max(text.get_n_lines(), visible_rows)-visible_rows;
267         slider->set_range(0, scroll);
268         slider->set_value(scroll-first_row);
269 }
270
271 void Entry::slider_value_changed(double value)
272 {
273         if(text.get_n_lines()>visible_rows)
274                 first_row = text.get_n_lines()-visible_rows-static_cast<unsigned>(value);
275 }
276
277
278 Entry::Loader::Loader(Entry &ent):
279         Widget::Loader(ent)
280 { }
281
282 } // namespace GLtk
283 } // namespace Msp