]> git.tdb.fi Git - libs/gltk.git/blob - source/entry.cpp
9129e1b0ebbdd90bef2b641629b271a9fe618f0b
[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)
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         {
51                 Geometry sgeom = part.get_geometry();
52                 if(!sgeom.w || !sgeom.h)
53                 {
54                         slider->autosize();
55                         if(!sgeom.w)
56                                 sgeom.w = slider->get_geometry().w;
57                         if(!sgeom.h)
58                                 sgeom.h = slider->get_geometry().h;
59                 }
60
61                 const Sides &margin = part.get_margin();
62                 ageom.w = max(ageom.w, sgeom.w+margin.left+margin.right);
63                 ageom.h = max(ageom.h, sgeom.h+margin.top+margin.bottom);
64         }
65 }
66
67 void Entry::set_text(const string &t)
68 {
69         text = t;
70         edit_pos = text.size();
71
72         if(multiline)
73                 check_view_range();
74
75         rebuild();
76 }
77
78 void Entry::set_edit_size(unsigned w, unsigned h)
79 {
80         edit_width = w;
81         edit_height = h;
82         signal_autosize_changed.emit();
83 }
84
85 void Entry::set_multiline(bool m)
86 {
87         multiline = m;
88         if(multiline)
89         {
90                 if(!slider)
91                 {
92                         slider = new VSlider;
93                         add(*slider);
94                         slider->set_step(1);
95                         slider->signal_value_changed.connect(sigc::mem_fun(this, &Entry::slider_value_changed));
96                         reposition_slider();
97                 }
98                 check_view_range();
99         }
100 }
101
102 void Entry::rebuild_special(const Part &part)
103 {
104         if(part.get_name()=="text")
105                 text.build(part, geom, first_row, part_cache);
106         else if(part.get_name()=="cursor")
107         {
108                 const Graphic *graphic = part.get_graphic(state);
109                 if(!text_part || !graphic || !graphic->get_texture())
110                         return;
111
112                 unsigned row, col;
113                 text.offset_to_coords(edit_pos, row, col);
114
115                 if(row<first_row || row>=first_row+visible_rows)
116                         return;
117
118                 Geometry rgeom = text.coords_to_geometry(*text_part, geom, first_row, row, col);
119
120                 GL::MeshBuilder bld(part_cache.create_mesh(part, *graphic->get_texture()));
121                 bld.matrix() *= GL::Matrix::translation(rgeom.x, rgeom.y, 0);
122                 graphic->build(part.get_geometry().w, part.get_geometry().h, bld);
123         }
124         else
125                 Widget::rebuild_special(part);
126 }
127
128 void Entry::render_special(const Part &part, GL::Renderer &renderer) const
129 {
130         if(part.get_name()=="slider" && multiline)
131                 slider->render(renderer);
132 }
133
134 void Entry::key_press(unsigned key, unsigned)
135 {
136         got_key_press = true;
137         if(key==Input::KEY_LEFT)
138         {
139                 if(edit_pos>0)
140                         set_edit_position(edit_pos-1);
141         }
142         else if(key==Input::KEY_RIGHT)
143         {
144                 if(edit_pos<text.size())
145                         set_edit_position(edit_pos+1);
146         }
147         else if(key==Input::KEY_DOWN && multiline)
148         {
149                 unsigned row, col;
150                 text.offset_to_coords(edit_pos, row, col);
151                 set_edit_position(text.coords_to_offset(row+1, col));
152         }
153         else if(key==Input::KEY_UP && multiline)
154         {
155                 unsigned row, col;
156                 text.offset_to_coords(edit_pos, row, col);
157                 set_edit_position(row>0 ? text.coords_to_offset(row-1, col) : 0);
158         }
159         else if(key==Input::KEY_BACKSPACE)
160         {
161                 if(edit_pos>0)
162                 {
163                         text.erase(--edit_pos, 1);
164                         check_view_range();
165                         rebuild();
166                 }
167         }
168         else if(key==Input::KEY_ENTER)
169         {
170                 if(multiline)
171                 {
172                         text.insert(edit_pos++, "\n");
173                         check_view_range();
174                         rebuild();
175                 }
176                 else
177                         signal_enter.emit();
178         }
179 }
180
181 void Entry::character(wchar_t ch)
182 {
183         if(got_key_press && ch>=' ')
184         {
185                 text.insert(edit_pos, StringCodec::encode<StringCodec::Utf8>(StringCodec::ustring(1, ch)));
186                 ++edit_pos;
187                 rebuild();
188         }
189 }
190
191 void Entry::focus_out()
192 {
193         Widget::focus_out();
194         got_key_press = false;
195 }
196
197 void Entry::on_geometry_change()
198 {
199         reposition_slider();
200
201         if(multiline)
202                 check_view_range();
203 }
204
205 void Entry::on_style_change()
206 {
207         text.set_style(style);
208
209         if(!style)
210         {
211                 text_part = 0;
212                 return;
213         }
214
215         text_part = style->get_part("text");
216
217         reposition_slider();
218
219         if(multiline)
220                 check_view_range();
221 }
222
223 void Entry::set_edit_position(unsigned ep)
224 {
225         edit_pos = ep;
226         check_view_range();
227         rebuild();
228 }
229
230 void Entry::reposition_slider()
231 {
232         if(!style || !slider)
233                 return;
234
235         if(const Part *slider_part = style->get_part("slider"))
236         {
237                 Geometry sgeom = slider_part->get_geometry();
238                 if(!sgeom.w || !sgeom.h)
239                 {
240                         slider->autosize();
241                         if(!sgeom.w)
242                                 sgeom.w = slider->get_geometry().w;
243                         if(!sgeom.h)
244                                 sgeom.h = slider->get_geometry().h;
245                 }
246
247                 slider_part->get_alignment().apply(sgeom, geom, slider_part->get_margin());
248                 slider->set_geometry(sgeom);
249         }
250 }
251
252 void Entry::check_view_range()
253 {
254         if(!multiline || !text_part)
255                 return;
256
257         float font_size = style->get_font_size();
258         unsigned line_spacing = static_cast<unsigned>(font_size*6/5);
259
260         const Sides &margin = text_part->get_margin();
261         visible_rows = max((geom.h-margin.top-margin.bottom)/line_spacing, 1U);
262
263         unsigned row, col;
264         text.offset_to_coords(edit_pos, row, col);
265
266         if(first_row>row)
267                 first_row = row;
268         else if(row>=first_row+visible_rows)
269                 first_row = row+1-visible_rows;
270
271         unsigned scroll = max(text.get_n_lines(), visible_rows)-visible_rows;
272         slider->set_range(0, scroll);
273         slider->set_value(scroll-first_row);
274 }
275
276 void Entry::slider_value_changed(double value)
277 {
278         if(text.get_n_lines()>visible_rows)
279                 first_row = text.get_n_lines()-visible_rows-static_cast<unsigned>(value);
280 }
281
282 } // namespace GLtk
283 } // namespace Msp