66#include " manager/event_dispatcher.hpp"
77#include " textinput.hpp"
88
9+ #if defined(_HAVE_ICU_DEP)
10+ #include < unicode/uchar.h>
11+ #endif
12+
913
1014using namespace std ::chrono_literals;
1115
@@ -129,26 +133,43 @@ ui::TextInput::handle_event( //NOLINT(readability-function-cognitive-complexity)
129133 { EventHandleType::RequestAction, this }
130134 };
131135 }
136+ // TODO(Totto): in some cases this is caught before that, and never triggered
132137 case SDLK_BACKSPACE: {
133- const auto remove_all = (event.key .keysym .mod & KMOD_CTRL) != 0 ;
138+ const auto sdl_key = sdl::Key{ event.key .keysym };
139+
140+ const auto ctrl_pressed = sdl_key.has_modifier (sdl::Modifier::CTRL);
141+
142+ const auto shift_pressed = sdl_key.has_modifier (sdl::Modifier::SHIFT);
134143
135144 if (not m_text.empty ()) {
136- if (remove_all) {
137- m_text = " " ;
138- m_cursor_position = 0 ;
145+ // NOTE: if both modifiers are pressed, we prioritize ctrl
146+ if (ctrl_pressed) {
147+ remove_at_cursor (RemoveMode::All);
148+ recalculate_textures (true );
149+ return true ;
150+ }
151+
152+ if (shift_pressed) {
153+ remove_at_cursor (RemoveMode::LastWord);
139154 recalculate_textures (true );
140155 return true ;
141156 }
142157
143- remove_at_cursor ();
158+
159+ remove_at_cursor (RemoveMode::OneChar);
144160 recalculate_textures (true );
145161 }
146162
147163 return true ;
148164 }
149165 case SDLK_LEFT: {
150166 if (m_cursor_position != 0 ) {
151- if ((event.key .keysym .mod & KMOD_CTRL) != 0 ) {
167+
168+ const auto sdl_key = sdl::Key{ event.key .keysym };
169+
170+ const auto ctrl_pressed = sdl_key.has_modifier (sdl::Modifier::CTRL);
171+
172+ if (ctrl_pressed) {
152173 m_cursor_position = 0 ;
153174 } else {
154175 --m_cursor_position;
@@ -161,7 +182,12 @@ ui::TextInput::handle_event( //NOLINT(readability-function-cognitive-complexity)
161182 case SDLK_RIGHT: {
162183 const u32 current_string_length = static_cast <u32 >(utf8::distance (m_text.cbegin (), m_text.cend ()));
163184 if (m_cursor_position < current_string_length) {
164- if ((event.key .keysym .mod & KMOD_CTRL) != 0 ) {
185+
186+ const auto sdl_key = sdl::Key{ event.key .keysym };
187+
188+ const auto ctrl_pressed = sdl_key.has_modifier (sdl::Modifier::CTRL);
189+
190+ if (ctrl_pressed) {
165191 m_cursor_position = current_string_length;
166192
167193 } else {
@@ -173,7 +199,12 @@ ui::TextInput::handle_event( //NOLINT(readability-function-cognitive-complexity)
173199 return true ;
174200 }
175201 case SDLK_v: {
176- if ((event.key .keysym .mod & KMOD_CTRL) != 0 ) {
202+
203+ const auto sdl_key = sdl::Key{ event.key .keysym };
204+
205+ const auto ctrl_pressed = sdl_key.has_modifier (sdl::Modifier::CTRL);
206+
207+ if (ctrl_pressed) {
177208
178209 if (SDL_HasClipboardText () != SDL_FALSE) {
179210 char * text = SDL_GetClipboardText ();
@@ -193,7 +224,11 @@ ui::TextInput::handle_event( //NOLINT(readability-function-cognitive-complexity)
193224 return false ;
194225 }
195226 case SDLK_c: {
196- if ((event.key .keysym .mod & KMOD_CTRL) != 0 ) {
227+ const auto sdl_key = sdl::Key{ event.key .keysym };
228+
229+ const auto ctrl_pressed = sdl_key.has_modifier (sdl::Modifier::CTRL);
230+
231+ if (ctrl_pressed) {
197232 const int result = SDL_SetClipboardText (m_text.c_str ());
198233 if (result != 0 ) {
199234 throw helper::MinorError{
@@ -407,27 +442,101 @@ bool ui::TextInput::add_string(const std::string& add) {
407442 return true ;
408443}
409444
410- bool ui::TextInput::remove_at_cursor () {
445+
446+ bool ui::TextInput::remove_at_cursor (RemoveMode remove_mode) {
411447
412448 if (m_cursor_position == 0 ) {
413449 return false ;
414450 }
415451
452+ if (remove_mode == RemoveMode::All) {
453+ m_text = " " ;
454+ m_cursor_position = 0 ;
455+ return true ;
456+ }
457+
458+
416459 const u32 current_string_length = static_cast <u32 >(utf8::distance (m_text.cbegin (), m_text.cend ()));
417460
418461 // cursor_position is the range [0, length] (inclusively !)
419462 if (m_cursor_position > current_string_length) {
420463 throw std::runtime_error (" cursor_postion is invalid!" );
421464 }
422465
423- auto start = m_text.begin ();
424- utf8::advance (start, m_cursor_position - 1 , m_text.end ());
425- auto end = start;
426- utf8::next (end, m_text.end ());
427- m_text.erase (start, end);
428466
429- --m_cursor_position;
430- return true ;
467+ if (remove_mode == RemoveMode::OneChar) {
468+
469+ auto start = m_text.begin ();
470+ utf8::advance (start, m_cursor_position - 1 , m_text.end ());
471+ auto end = start;
472+ utf8::next (end, m_text.end ());
473+ m_text.erase (start, end);
474+
475+ --m_cursor_position;
476+ return true ;
477+ }
478+
479+ if (remove_mode == RemoveMode::LastWord) {
480+
481+ auto get_char_cat = [](u32 code_point) -> int {
482+ #if defined(_HAVE_ICU_DEP)
483+ // see: https://en.wikipedia.org/wiki/Unicode_character_property#General_Category
484+ return u_charType (code_point);
485+ #else
486+ if (code_point >= 0x80 ) {
487+ return 1 ;
488+ }
489+
490+ int int_code_point = static_cast <int >(code_point);
491+
492+ if (isalnum (int_code_point)) {
493+ return 2 ;
494+ }
495+
496+ if (isblank (int_code_point)) {
497+ return 3 ;
498+ }
499+
500+ if (iscntrl (int_code_point)) {
501+ return 4 ;
502+ }
503+
504+ return 5 ;
505+
506+ #endif
507+ };
508+
509+ auto start = m_text.begin ();
510+ utf8::advance (start, m_cursor_position, m_text.end ());
511+ auto end = start;
512+ u32 remove_amount = 0 ;
513+ int char_cat = -1 ;
514+
515+ while (true ) {
516+ auto temp = start;
517+ auto code_point = utf8::prior (temp, m_text.begin ());
518+
519+ if (char_cat < 0 ) {
520+ char_cat = get_char_cat (code_point);
521+ } else if (char_cat != get_char_cat (code_point)) {
522+ break ;
523+ }
524+
525+ remove_amount++;
526+
527+ start = temp;
528+
529+ if (temp == m_text.begin ()) {
530+ break ;
531+ }
532+ }
533+ m_text.erase (start, end);
534+
535+ m_cursor_position -= remove_amount;
536+ return true ;
537+ }
538+
539+ utils::unreachable ();
431540}
432541
433542void ui::TextInput::on_focus () {
0 commit comments