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