The Battle for Wesnoth  1.15.2+dev
text_box_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "cursor.hpp"
20 #include "desktop/clipboard.hpp"
21 #include "gui/core/log.hpp"
22 #include "gui/core/timer.hpp"
24 
25 #include "utils/functional.hpp"
26 
27 #include <limits>
28 
29 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
30 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
31 
32 namespace gui2
33 {
34 
35 text_box_base::text_box_base(const implementation::builder_styled_widget& builder, const std::string& control_type)
36  : styled_widget(builder, control_type)
37  , state_(ENABLED)
38  , text_()
39  , selection_start_(0)
40  , selection_length_(0)
41  , ime_composing_(false)
42  , ime_start_point_(0)
43  , cursor_timer_(0)
44  , cursor_alpha_(0)
45  , cursor_blink_rate_ms_(750)
46  , text_changed_callback_()
47 {
48 #ifdef __unix__
49  // pastes on UNIX systems.
50  connect_signal<event::MIDDLE_BUTTON_CLICK>(std::bind(
52 
53 #endif
54 
55  connect_signal<event::SDL_KEY_DOWN>(std::bind(
56  &text_box_base::signal_handler_sdl_key_down, this, _2, _3, _5, _6));
57  connect_signal<event::SDL_TEXT_INPUT>(std::bind(&text_box_base::handle_commit, this, _3, _5));
58  connect_signal<event::SDL_TEXT_EDITING>(std::bind(&text_box_base::handle_editing, this, _3, _5, _6, _7));
59 
60  connect_signal<event::RECEIVE_KEYBOARD_FOCUS>(std::bind(
62  connect_signal<event::LOSE_KEYBOARD_FOCUS>(
64 
65  connect_signal<event::MOUSE_ENTER>(
66  std::bind(&text_box_base::signal_handler_mouse_enter, this, _2, _3));
67  connect_signal<event::MOUSE_LEAVE>(
68  std::bind(&text_box_base::signal_handler_mouse_leave, this, _2, _3));
69 
70  toggle_cursor_timer(true);
71 }
72 
74 {
75  toggle_cursor_timer(false);
76  update_mouse_cursor(false);
77 }
78 
79 void text_box_base::set_active(const bool active)
80 {
81  if(get_active() != active) {
82  set_state(active ? ENABLED : DISABLED);
83  }
84 }
85 
87 {
88  return state_ != DISABLED;
89 }
90 
91 unsigned text_box_base::get_state() const
92 {
93  return state_;
94 }
95 
96 void text_box_base::set_maximum_length(const std::size_t maximum_length)
97 {
98  if(maximum_length == 0) {
99  return;
100  }
101 
102  const bool need_update = text_.get_length() > maximum_length;
103 
104  text_.set_maximum_length(maximum_length);
105 
106  if(need_update) {
107  if(selection_start_ > maximum_length) {
108  selection_start_ = maximum_length;
109  selection_length_ = 0;
110  } else if(selection_start_ + selection_length_ > maximum_length) {
111  selection_length_ = maximum_length - selection_start_;
112  }
113  update_canvas();
114  set_is_dirty(true);
115  }
116 }
117 
118 void text_box_base::set_value(const std::string& text)
119 {
120  if(text != text_.text()) {
121  text_.set_text(text, false);
122 
123  // default to put the cursor at the end of the buffer.
125  selection_length_ = 0;
126  update_canvas();
127  set_is_dirty(true);
128  }
129 }
130 
131 void text_box_base::set_cursor(const std::size_t offset, const bool select)
132 {
134 
135  if(select) {
136 
137  if(selection_start_ == offset) {
138  selection_length_ = 0;
139  } else {
140  selection_length_ = -static_cast<int>(selection_start_ - offset);
141  }
142 
143 #ifdef __unix__
144  // selecting copies on UNIX systems.
145  copy_selection(true);
146 #endif
147  update_canvas();
148  set_is_dirty(true);
149 
150  } else {
151  assert(offset <= text_.get_length());
152  selection_start_ = offset;
153  selection_length_ = 0;
154 
155  update_canvas();
156  set_is_dirty(true);
157  }
158 }
159 
160 void text_box_base::insert_char(const std::string& unicode)
161 {
163 
164  if(text_.insert_text(selection_start_, unicode)) {
165 
166  // Update status
167  set_cursor(selection_start_ + utf8::size(unicode), false);
168  update_canvas();
169  set_is_dirty(true);
170  }
171 }
172 
174 {
175  if(!is_composing()) {
176  return 0;
177  }
178 
179  size_t text_length = utf8::size(text_.text());
180  size_t text_cached_length = utf8::size(text_cached_);
181  if(text_length < text_cached_length) {
182  return 0;
183  }
184 
186 }
187 
189 {
190  ime_composing_ = false;
191  // We need to inform the IME that text input is no longer in progress.
192  SDL_StopTextInput();
193  SDL_StartTextInput();
194 }
195 
196 void text_box_base::copy_selection(const bool mouse)
197 {
198  if(selection_length_ == 0) {
199  return;
200  }
201 
202  unsigned end, start = selection_start_;
203  const std::string txt = text_.text();
204 
205  if(selection_length_ > 0) {
206  end = utf8::index(txt, start + selection_length_);
207  start = utf8::index(txt, start);
208  } else {
209  // inverse selection: selection_start_ is in fact the end
210  end = utf8::index(txt, start);
211  start = utf8::index(txt, start + selection_length_);
212  }
213  desktop::clipboard::copy_to_clipboard(txt.substr(start, end - start), mouse);
214 }
215 
216 void text_box_base::paste_selection(const bool mouse)
217 {
218  const std::string& text = desktop::clipboard::copy_from_clipboard(mouse);
219  if(text.empty()) {
220  return;
221  }
222 
224 
226 
227  update_canvas();
228  set_is_dirty(true);
229  fire(event::NOTIFY_MODIFIED, *this, nullptr);
230 }
231 
232 void text_box_base::set_selection_start(const std::size_t selection_start)
233 {
234  if(selection_start != selection_start_) {
235  selection_start_ = selection_start;
236  set_is_dirty(true);
237  }
238 }
239 
240 void text_box_base::set_selection_length(const int selection_length)
241 {
242  if(selection_length != selection_length_) {
243  selection_length_ = selection_length;
244  set_is_dirty(true);
245  }
246 }
247 
248 void text_box_base::set_selection(std::size_t start, int length)
249 {
250  const std::size_t text_size = text_.get_length();
251 
252  if(start >= text_size) {
253  start = text_size;
254  }
255 
256  if(length == 0) {
257  set_cursor(start, false);
258  return;
259  }
260 
261  // The text pos/size type differs in both signedness and size with the
262  // selection length. Such is life.
263  const int sel_start = std::min<std::size_t>(start, std::numeric_limits<int>::max());
264  const int sel_max_length = std::min<std::size_t>(text_size - start, std::numeric_limits<int>::max());
265 
266  const bool backwards = length < 0;
267 
268  if(backwards && -length > sel_start) {
269  length = -sel_start;
270  } else if(!backwards && length > sel_max_length) {
271  length = sel_max_length;
272  }
273 
274  set_selection_start(start);
275  set_selection_length(length);
276 
277  update_canvas();
278 }
279 
281 {
282  if(state != state_) {
283  state_ = state;
284  set_is_dirty(true);
285  }
286 }
287 
289 {
290  if(!cursor_blink_rate_ms_) {
291  return;
292  }
293 
294  if(cursor_timer_) {
296  }
297 
298  cursor_timer_ = enable
300  : 0;
301 }
302 
304 {
305  unsigned was_alpha = cursor_alpha_;
306  switch(state_) {
307  case DISABLED:
308  cursor_alpha_ = 0;
309  return;
310  case ENABLED:
311  cursor_alpha_ = 255;
312  return;
313  default:
314  if(get_window() != open_window_stack.back()) {
315  cursor_alpha_ = 0;
316  } else {
317  cursor_alpha_ = (~cursor_alpha_) & 0xFF;
318  }
319  }
320 
321  if(was_alpha == cursor_alpha_) {
322  return;
323  }
324 
325  for(auto& tmp : get_canvases()) {
326  tmp.set_variable("cursor_alpha", wfl::variant(cursor_alpha_));
327  }
328 
329  set_is_dirty(true);
330 }
331 
333 {
334  if(!cursor_blink_rate_ms_) {
335  return;
336  }
337 
338  cursor_alpha_ = 255;
339 
340  for(auto& tmp : get_canvases()) {
341  tmp.set_variable("cursor_alpha", wfl::variant(cursor_alpha_));
342  }
343 
344  // Restart the blink timer.
345  toggle_cursor_timer(true);
346 }
347 
348 void text_box_base::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
349 {
350  /** @todo implement the ctrl key. */
351  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
352 
353  handled = true;
354  const int offset = selection_start_ - 1 + selection_length_;
355  if(offset >= 0) {
356  set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
357  }
358 }
359 
360 void text_box_base::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
361 {
362  /** @todo implement the ctrl key. */
363  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
364 
365  handled = true;
366  const std::size_t offset = selection_start_ + 1 + selection_length_;
367  if(offset <= text_.get_length()) {
368  set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
369  }
370 }
371 
372 void text_box_base::handle_key_home(SDL_Keymod modifier, bool& handled)
373 {
374  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
375 
376  handled = true;
377  if(modifier & KMOD_CTRL) {
378  goto_start_of_data((modifier & KMOD_SHIFT) != 0);
379  } else {
380  goto_start_of_line((modifier & KMOD_SHIFT) != 0);
381  }
382 }
383 
384 void text_box_base::handle_key_end(SDL_Keymod modifier, bool& handled)
385 {
386  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
387 
388  handled = true;
389  if(modifier & KMOD_CTRL) {
390  goto_end_of_data((modifier & KMOD_SHIFT) != 0);
391  } else {
392  goto_end_of_line((modifier & KMOD_SHIFT) != 0);
393  }
394 }
395 
396 void text_box_base::handle_key_backspace(SDL_Keymod /*modifier*/, bool& handled)
397 {
398  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
399 
400  handled = true;
401  if(selection_length_ != 0) {
403  } else if(selection_start_) {
404  delete_char(true);
405  if(is_composing()) {
406  if(get_composition_length() == 0) {
407  ime_composing_ = false;
408  }
409  }
410  }
411  fire(event::NOTIFY_MODIFIED, *this, nullptr);
412 }
413 
414 void text_box_base::handle_key_delete(SDL_Keymod /*modifier*/, bool& handled)
415 {
416  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
417 
418  handled = true;
419  if(selection_length_ != 0) {
421  } else if(selection_start_ < text_.get_length()) {
422  delete_char(false);
423  if(is_composing()) {
424  if(get_composition_length() == 0) {
425  ime_composing_ = false;
426  }
427  }
428  }
429  fire(event::NOTIFY_MODIFIED, *this, nullptr);
430 }
431 
432 void text_box_base::handle_commit(bool& handled, const std::string& unicode)
433 {
434  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
435 
436  if(unicode.size() > 1 || unicode[0] != 0) {
437  handled = true;
438  if(is_composing()) {
440  ime_composing_ = false;
441  }
442  insert_char(unicode);
443  fire(event::NOTIFY_MODIFIED, *this, nullptr);
444 
446  text_changed_callback_(this, this->text());
447  }
448  }
449 }
450 
451 /**
452  * SDL_TEXTEDITING handler. See example at https://wiki.libsdl.org/Tutorials/TextInput
453  */
454 void text_box_base::handle_editing(bool& handled, const std::string& unicode, int32_t start, int32_t len)
455 {
456  if(unicode.size() > 1 || unicode[0] != 0) {
457  handled = true;
458  std::size_t new_len = utf8::size(unicode);
459  if(!is_composing()) {
460  ime_composing_ = true;
463  text_cached_ = text_.text();
464  SDL_Rect rect = get_rectangle();
465  if(new_len > 0) {
467  rect.w = get_cursor_position(ime_start_point_ + new_len).x - rect.x;
468  } else {
469  rect.x += get_cursor_position(ime_start_point_ + new_len).x;
470  rect.w = get_cursor_position(ime_start_point_).x - rect.x;
471  }
472  SDL_SetTextInputRect(&rect);
473  }
474 
475 #ifdef __unix__
476  // In SDL_TextEditingEvent, size of editing_text is limited
477  // If length of composition text is more than the limit,
478  // Linux (ibus) implementation of SDL separates it into multiple
479  // SDL_TextEditingEvent.
480  // start is start position of the separated event in entire composition text
481  if(start == 0) {
482  text_.set_text(text_cached_, false);
483  }
484  text_.insert_text(ime_start_point_ + start, unicode);
485 #else
486  std::string new_text(text_cached_);
487  utf8::insert(new_text, ime_start_point_, unicode);
488  text_.set_text(new_text, false);
489 
490 #endif
491  int maximum_length = text_.get_length();
492 
493  // Update status
494  set_cursor(std::min(maximum_length, ime_start_point_ + start), false);
495  if(len > 0) {
496  set_cursor(std::min(maximum_length, ime_start_point_ + start + len), true);
497  }
498  update_canvas();
499  set_is_dirty(true);
500  }
501 }
502 
504  bool& handled)
505 {
506  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
507 
508  paste_selection(true);
509 
510  handled = true;
511 }
512 
514  bool& handled,
515  const SDL_Keycode key,
516  SDL_Keymod modifier)
517 {
518 
519  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
520 
521 /*
522  * For copy, cut and paste we use a different key on the MAC. Even for 'select
523  * all', contradicting the comment in widgets/textbox.cpp:495.
524  *
525  * The reason for that is, by coupling 'select all' to the behavior for copy,
526  * cut and paste, the text box behavior as a whole gets consistent with default
527  * macOS hotkey idioms.
528  */
529 #ifdef __APPLE__
530  // Idiomatic modifier key in macOS computers.
531  const SDL_Keycode modifier_key = KMOD_GUI;
532 #else
533  // Idiomatic modifier key in Microsoft desktop environments. Common in
534  // GNU/Linux as well, to some extent.
535  const SDL_Keycode modifier_key = KMOD_CTRL;
536 #endif
537 
538  switch(key) {
539 
540  case SDLK_LEFT:
541  handle_key_left_arrow(modifier, handled);
542  break;
543 
544  case SDLK_RIGHT:
545  handle_key_right_arrow(modifier, handled);
546  break;
547 
548  case SDLK_UP:
549  handle_key_up_arrow(modifier, handled);
550  break;
551 
552  case SDLK_DOWN:
553  handle_key_down_arrow(modifier, handled);
554  break;
555 
556  case SDLK_PAGEUP:
557  handle_key_page_up(modifier, handled);
558  break;
559 
560  case SDLK_PAGEDOWN:
561  handle_key_page_down(modifier, handled);
562  break;
563 
564  case SDLK_a:
565  if(!(modifier & modifier_key)) {
566  return;
567  }
568 
569  select_all();
570  break;
571 
572  case SDLK_HOME:
573  handle_key_home(modifier, handled);
574  break;
575 
576  case SDLK_END:
577  handle_key_end(modifier, handled);
578  break;
579 
580  case SDLK_BACKSPACE:
581  handle_key_backspace(modifier, handled);
582  break;
583 
584  case SDLK_u:
585  if(!(modifier & KMOD_CTRL)) {
586  return;
587  }
588  handle_key_clear_line(modifier, handled);
589  break;
590 
591  case SDLK_DELETE:
592  handle_key_delete(modifier, handled);
593  break;
594 
595  case SDLK_c:
596  if(!(modifier & modifier_key)) {
597  return;
598  }
599 
600  // atm we don't care whether there is something to copy or paste
601  // if nothing is there we still don't want to be chained.
602  copy_selection(false);
603  handled = true;
604  break;
605 
606  case SDLK_x:
607  if(!(modifier & modifier_key)) {
608  return;
609  }
610 
611  copy_selection(false);
613  handled = true;
614  break;
615 
616  case SDLK_v:
617  if(!(modifier & modifier_key)) {
618  return;
619  }
620 
621  paste_selection(false);
622  handled = true;
623  break;
624 
625  case SDLK_RETURN:
626  case SDLK_KP_ENTER:
627  if(!is_composing() || (modifier & (KMOD_CTRL | KMOD_ALT | KMOD_GUI | KMOD_SHIFT))) {
628  return;
629  }
630  // The IME will handle it, we just need to make sure nothing else handles it too.
631  handled = true;
632  break;
633 
634  case SDLK_ESCAPE:
635  if(!is_composing() || (modifier & (KMOD_CTRL | KMOD_ALT | KMOD_GUI | KMOD_SHIFT))) {
636  return;
637  }
639  handled = true;
640  break;
641 
642  default:
643  // Don't call the text changed callback if nothing happened.
644  return;
645  }
646 
648  text_changed_callback_(this, this->text());
649  }
650 }
651 
653 {
654  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
655 
657 }
658 
660 {
661  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
662 
664 }
665 
667  bool& handled)
668 {
669  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
670 
671  if(state_ != FOCUSED) {
673  }
674 
675  update_mouse_cursor(true);
676 
677  handled = true;
678 }
679 
681  bool& handled)
682 {
683  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
684 
685  if(state_ != FOCUSED) {
687  }
688 
689  update_mouse_cursor(false);
690 
691  handled = true;
692 }
693 
695 {
696  // Someone else may set the mouse cursor for us to something unusual (e.g.
697  // the WAIT cursor) so we ought to mess with that only if it's set to
698  // NORMAL or IBEAM.
699 
700  if(enable && cursor::get() == cursor::NORMAL) {
702  } else if(!enable && cursor::get() == cursor::IBEAM) {
704  }
705 }
706 
707 
708 } // namespace gui2
Define the common log macros for the gui toolkit.
bool is_composing() const
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
virtual unsigned get_state() const override
See styled_widget::get_state.
virtual void goto_start_of_line(const bool select=false)=0
Moves the cursor to the beginning of the line.
state_t
Note the order of the states must be the same as defined in settings.hpp.
text_box_base(const implementation::builder_styled_widget &builder, const std::string &control_type)
void select_all()
Selects all text.
virtual void handle_editing(bool &handled, const std::string &unicode, int32_t start, int32_t length)
SDL_TEXTEDITING handler.
virtual void goto_end_of_line(const bool select=false)=0
Moves the cursor to the end of the line.
virtual void paste_selection(const bool mouse)
Pastes the current selection.
void set_selection(std::size_t start, int length)
Sets or clears the text selection.
std::size_t cursor_timer_
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:175
std::string copy_from_clipboard(const bool)
Copies text from the clipboard.
Definition: clipboard.cpp:40
std::size_t get_length() const
Gets the length of the text in bytes.
Definition: text.hpp:205
virtual void handle_commit(bool &handled, const std::string &unicode)
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:283
virtual bool get_active() const override
See styled_widget::get_active.
virtual void handle_key_end(SDL_Keymod modifier, bool &handled)
End key pressed.
void set_maximum_length(const std::size_t maximum_length)
virtual void handle_key_backspace(SDL_Keymod modifier, bool &handled)
Backspace key pressed.
void set_selection_length(const int selection_length)
SDL_Rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:307
void goto_start_of_data(const bool select=false)
Moves the cursor to the beginning of the data.
virtual void copy_selection(const bool mouse)
Copies the current selection.
virtual void toggle_cursor_timer(bool enable)
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key, SDL_Keymod modifier)
pango_text & set_maximum_length(const std::size_t maximum_length)
Definition: text.cpp:435
void signal_handler_middle_button_click(const event::ui_event event, bool &handled)
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)=0
Every key can have several behaviors.
size_t get_composition_length() const
Get length of composition text by IME.
unsigned short cursor_blink_rate_ms_
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)
Right arrow key pressed.
virtual void handle_key_clear_line(SDL_Keymod modifier, bool &handled)=0
Clears the current line.
int x
x coordinate.
Definition: point.hpp:44
Generic file dialog.
Definition: field-fwd.hpp:22
Sent by a widget to notify others its contents or state are modified.
Definition: handler.hpp:96
void goto_end_of_data(const bool select=false)
Moves the cursor to the end of all text.
void set_selection_start(const std::size_t selection_start)
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool &handled)=0
Down arrow key pressed.
void signal_handler_mouse_enter(const event::ui_event event, bool &handled)
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
std::size_t selection_start_
Start of the selected text.
virtual void handle_key_delete(SDL_Keymod modifier, bool &handled)
Delete key pressed.
unsigned short cursor_alpha_
std::string text_cached_
Cached version of the text without any pending IME modifications.
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:463
std::function< void(text_box_base *textbox, const std::string text)> text_changed_callback_
Text changed callback.
virtual void update_canvas()
Updates the canvas(ses).
std::vector< canvas > & get_canvases()
int selection_length_
Length of the selected text.
virtual void handle_key_page_down(SDL_Keymod, bool &)
Page down key.
point get_cursor_position(const unsigned column, const unsigned line=0) const
unsigned insert_text(const unsigned offset, const std::string &text)
Inserts UTF-8 text.
Definition: text.cpp:121
void signal_handler_receive_keyboard_focus(const event::ui_event event)
virtual void handle_key_page_up(SDL_Keymod, bool &)
Page up key.
void signal_handler_lose_keyboard_focus(const event::ui_event event)
virtual void insert_char(const std::string &unicode)
Inserts a character at the cursor.
#define DBG_GUI_E
Definition: log.hpp:34
virtual void delete_char(const bool before_cursor)=0
Deletes the character.
window * get_window()
Get the parent window.
Definition: widget.cpp:114
#define LOG_SCOPE_HEADER
Contains the gui2 timer routines.
CURSOR_TYPE get()
Definition: cursor.cpp:215
std::size_t add_timer(const uint32_t interval, const std::function< void(std::size_t id)> &callback, const bool repeat)
Adds a new timer.
Definition: timer.cpp:126
std::string & insert(std::string &str, const std::size_t pos, const std::string &insert)
Insert a UTF-8 string at the specified position.
Definition: unicode.cpp:99
font::pango_text text_
The text entered in the widget.
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
Base class for all visible items.
const std::string & text() const
Definition: text.hpp:223
virtual void set_value(const std::string &text)
The set_value is virtual for the password_box class.
void copy_to_clipboard(const std::string &text, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:35
state_t state_
Current state of the widget.
const std::string & text() const
virtual void cursor_timer_callback()
Implements blinking cursor functionality.
void update_mouse_cursor(bool enable)
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:130
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:28
void set_state(const state_t state)
void set_cursor(const std::size_t offset, const bool select)
Moves the cursor at the wanted position.
virtual void reset_cursor_state()
virtual void delete_selection()=0
Deletes the current selection.
#define LOG_HEADER
ui_event
The event send to the dispatcher.
Definition: handler.hpp:55
virtual void set_active(const bool active) override
See styled_widget::set_active.
virtual void handle_key_home(SDL_Keymod modifier, bool &handled)
Home key pressed.
std::vector< window * > open_window_stack
Keeps track of any open windows of any type (modal, non-modal, or tooltip) in the order in which they...
Definition: handler.cpp:1106
bool remove_timer(const std::size_t id)
Removes a timer.
Definition: timer.cpp:167