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