/
text.hpp
493 lines (417 loc) · 14.2 KB
/
text.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/*
Copyright (C) 2008 - 2016 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_TEXT_HPP_INCLUDED
#define GUI_WIDGETS_TEXT_HPP_INCLUDED
//#include "gui/core/event/dispatcher.hpp"
#include "gui/widgets/control.hpp"
#include "font/text.hpp" // We want the file in src/
#include <string>
#include "utils/functional.hpp"
namespace gui2
{
/**
* Abstract base class for text items.
*
* All other text classes should inherit from this base class.
*
* The NOTIFY_MODIFIED event is send when the text is modified.
*
* @todo Validate whether the NOTIFY_MODIFIED is always fired properly. The
* current implementation is added for some quick testing so some cases might
* be forgotten.
*
* Common signal handlers:
* - connect_signal_pre_key_press
*/
class ttext_ : public tcontrol
{
public:
ttext_();
~ttext_();
/** See @ref tcontrol::set_active. */
virtual void set_active(const bool active) override;
/** See @ref tcontrol::get_active. */
virtual bool get_active() const override;
/** See @ref tcontrol::get_state. */
virtual unsigned get_state() const override;
/***** ***** ***** ***** expose some functions ***** ***** ***** *****/
void set_maximum_length(const size_t maximum_length);
size_t get_length() const
{
return text_.get_length();
}
/***** ***** ***** setters / getters for members ***** ****** *****/
/**
* The set_value is virtual for the @ref tpassword_box class.
*
* That class overrides the set_value function to replace it with asterisk.
* There might be more generic way to do it when more classes are needed.
*/
virtual void set_value(const std::string& text);
std::string get_value() const
{
return text_.text();
}
const std::string& text() const
{
return text_.text();
}
/** Set the text_changed callback. */
void set_text_changed_callback(
std::function<void(ttext_* textbox, const std::string text)> cb)
{
text_changed_callback_ = cb;
}
/**
* Sets or clears the text selection.
*
* Setting the selection range will re-position the cursor depending on the
* selection direction, specified by the length's sign. Selecting beyond the
* start or end of the text is safe; the final selection will be limited
* accordingly.
*
* @note The range extents are measured in Unicode characters, not bytes.
* Using byte offsets may produce unexpected results depending on the
* text contents.
*
* @param start Start offset, in characters.
* @param length Selection length, in characters. If zero, the
* current selection is canceled. If negative, the
* selection extends towards the start of the text
* and the cursor will be re-positioned in that
* direction as well; otherwise the selection and
* cursor extend towards the end.
*/
void set_selection(size_t start, int length);
protected:
/**
* Moves the cursor to the end of the line.
*
* @param select Select the text from the original cursor
* position till the end of the line?
*/
virtual void goto_end_of_line(const bool select = false) = 0;
/**
* Moves the cursor to the end of all text.
*
* For a single line text this is the same as goto_end_of_line().
*
* @param select Select the text from the original cursor
* position till the end of the data?
*/
void goto_end_of_data(const bool select = false)
{
set_cursor(text_.get_length(), select);
}
/**
* Moves the cursor to the beginning of the line
*
* @param select Select the text from the original cursor
* position till the beginning of the line?
*/
virtual void goto_start_of_line(const bool select = false) = 0;
/**
* Moves the cursor to the beginning of the data.
*
* @param select Select the text from the original cursor
* position till the beginning of the data?
*/
void goto_start_of_data(const bool select = false)
{
set_cursor(0, select);
}
/** Selects all text. */
void select_all()
{
selection_start_ = 0;
goto_end_of_data(true);
}
/**
* Moves the cursor at the wanted position.
*
* @param offset The wanted new cursor position.
* @param select Select the text from the original cursor
* position till the new position?
*/
void set_cursor(const size_t offset, const bool select);
/**
* Inserts a character at the cursor.
*
* This function is preferred over set_text since it's optimized for
* updating the internal bookkeeping.
*
* @param unicode The unicode value of the character to insert.
*/
virtual void insert_char(const utf8::string& unicode);
/**
* Deletes the character.
*
* @param before_cursor If true it deletes the character before the
* cursor (backspace) else the character after
* the cursor (delete).
*/
virtual void delete_char(const bool before_cursor) = 0;
/** Deletes the current selection. */
virtual void delete_selection() = 0;
/** Copies the current selection. */
virtual void copy_selection(const bool mouse);
/** Pastes the current selection. */
virtual void paste_selection(const bool mouse);
/***** ***** ***** ***** expose some functions ***** ***** ***** *****/
gui2::tpoint get_cursor_position(const unsigned column,
const unsigned line = 0) const
{
return text_.get_cursor_position(column, line);
}
tpoint get_column_line(const tpoint& position) const
{
return text_.get_column_line(position);
}
void set_font_size(const unsigned font_size)
{
text_.set_font_size(font_size);
}
void set_font_style(const font::ttext::FONT_STYLE font_style)
{
text_.set_font_style(font_style);
}
void set_maximum_width(const int width)
{
text_.set_maximum_width(width);
}
void set_maximum_height(const int height, const bool multiline)
{
text_.set_maximum_height(height, multiline);
}
void set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
{
text_.set_ellipse_mode(ellipse_mode);
}
/***** ***** ***** setters / getters for members ***** ****** *****/
size_t get_selection_start() const
{
return selection_start_;
}
void set_selection_start(const size_t selection_start);
size_t get_selection_length() const
{
return selection_length_;
}
void set_selection_length(const int selection_length);
private:
/** Note the order of the states must be the same as defined in
* settings.hpp. */
enum tstate {
ENABLED,
DISABLED,
FOCUSED,
COUNT
};
void set_state(const tstate state);
virtual void toggle_cursor_timer(bool enable);
/** Implements blinking cursor functionality. */
virtual void cursor_timer_callback();
virtual void reset_cursor_state();
/**
* Current state of the widget.
*
* The state of the widget determines what to render and how the widget
* reacts to certain 'events'.
*/
tstate state_;
/** The text entered in the widget. */
font::ttext text_;
/** Start of the selected text. */
size_t selection_start_;
/**
* Length of the selected text.
*
* * positive selection_len_ means selection to the right.
* * negative selection_len_ means selection to the left.
* * selection_len_ == 0 means no selection.
*/
int selection_length_;
size_t cursor_timer_;
unsigned short cursor_alpha_;
unsigned short cursor_blink_rate_ms_;
/****** handling of special keys first the pure virtuals *****/
/**
* Every key can have several behaviors.
*
* Unmodified No modifier is pressed.
* Control The control key is pressed.
* Shift The shift key is pressed.
* Alt The alt key is pressed.
*
* If modifiers together do something else as the sum of the modifiers
* it's listed separately eg.
*
* Control Moves 10 steps at the time.
* Shift Selects the text.
* Control + Shift Inserts 42 in the text.
*
* There are some predefined actions for results.
* Unhandled The key/modifier is ignored and also reported
* unhandled.
* Ignored The key/modifier is ignored and it's
* _expected_ the inherited classes do the same.
* Implementation defined The key/modifier is ignored and it's expected
* the inherited classes will define some meaning
* to it.
*/
/**
* Up arrow key pressed.
*
* The behavior is implementation defined.
*/
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool& handled) = 0;
/**
* Down arrow key pressed.
*
* The behavior is implementation defined.
*/
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool& handled) = 0;
/**
* Clears the current line.
*
* Unmodified Clears the current line.
* Control Ignored.
* Shift Ignored.
* Alt Ignored.
*/
virtual void handle_key_clear_line(SDL_Keymod modifier, bool& handled) = 0;
/**
* Left arrow key pressed.
*
* Unmodified Moves the cursor a character to the left.
* Control Like unmodified but a word instead of a letter
* at the time.
* Shift Selects the text while moving.
* Alt Ignored.
*/
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool& handled);
/**
* Right arrow key pressed.
*
* Unmodified Moves the cursor a character to the right.
* Control Like unmodified but a word instead of a letter
* at the time.
* Shift Selects the text while moving.
* Alt Ignored.
*/
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool& handled);
/**
* Home key pressed.
*
* Unmodified Moves the cursor a to the beginning of the
* line.
* Control Like unmodified but to the beginning of the
* data.
* Shift Selects the text while moving.
* Alt Ignored.
*/
virtual void handle_key_home(SDL_Keymod modifier, bool& handled);
/**
* End key pressed.
*
* Unmodified Moves the cursor a to the end of the line.
* Control Like unmodified but to the end of the data.
* Shift Selects the text while moving.
* Alt Ignored.
*/
virtual void handle_key_end(SDL_Keymod modifier, bool& handled);
/**
* Backspace key pressed.
*
* Unmodified Deletes the character before the cursor,
* ignored if at the beginning of the data.
* Control Ignored.
* Shift Ignored.
* Alt Ignored.
*/
virtual void handle_key_backspace(SDL_Keymod modifier, bool& handled);
/**
* Delete key pressed.
*
* Unmodified If there is a selection that's deleted.
* Else if not at the end of the data the
* character after the cursor is deleted.
* Else the key is ignored.
* ignored if at the beginning of the data.
* Control Ignored.
* Shift Ignored.
* Alt Ignored.
*/
virtual void handle_key_delete(SDL_Keymod modifier, bool& handled);
/**
* Page up key.
*
* Unmodified Unhandled.
* Control Ignored.
* Shift Ignored.
* Alt Ignored.
*/
virtual void handle_key_page_up(SDL_Keymod /*modifier*/, bool& /*handled*/)
{
}
/**
* Page down key.
*
* Unmodified Unhandled.
* Control Ignored.
* Shift Ignored.
* Alt Ignored.
*/
virtual void handle_key_page_down(SDL_Keymod /*modifier*/, bool& /*handled*/)
{
}
protected:
/**
* Default key handler if none of the above functions is called.
*
* Unmodified If invalid unicode it's ignored.
* Else if text selected the selected text is
* replaced with the unicode character send.
* Else the unicode character is inserted after
* the cursor.
* Control Ignored.
* Shift Ignored (already in the unicode value).
* Alt Ignored.
*/
virtual void handle_key_default(bool& handled,
SDL_Keycode key,
SDL_Keymod modifier,
const utf8::string& unicode);
private:
/**
* Text changed callback.
*
* This callback is called in key_press after the key_press event has been
* handled by the control. The parameters to the function are:
* - The widget invoking the callback
* - The new text of the textbox.
*/
std::function<void(ttext_* textbox, const std::string text)>
text_changed_callback_;
/***** ***** ***** signal handlers ***** ****** *****/
void signal_handler_middle_button_click(const event::tevent event,
bool& handled);
void signal_handler_sdl_key_down(const event::tevent event,
bool& handled,
const SDL_Keycode key,
SDL_Keymod modifier,
const utf8::string& unicode);
void signal_handler_receive_keyboard_focus(const event::tevent event);
void signal_handler_lose_keyboard_focus(const event::tevent event);
};
} // namespace gui2
#endif