diff --git a/data/gui/widget/text_box_default.cfg b/data/gui/widget/text_box_default.cfg index edda73709ad70..94049f9abbe7d 100644 --- a/data/gui/widget/text_box_default.cfg +++ b/data/gui/widget/text_box_default.cfg @@ -68,6 +68,15 @@ color = "([255, 255, 255, cursor_alpha])" thickness = 1 [/line] + + [line] + x1 = "(composition_offset + {X_OFFSET})" + y1 = "(text_y_offset + text_font_height - 2)" + x2 = "(composition_offset + {X_OFFSET} + composition_width)" + y2 = "(text_y_offset + text_font_height - 2)" + color = "([140, 140, 0, if(composition_width > 0, 255, 0)])" + thickness = 2 + [/line] #enddef #define _GUI_RESOLUTION RESOLUTION MIN_WIDTH DEFAULT_WIDTH HEIGHT X_OFFSET EXTRA_WIDTH FONT_SIZE BACKGROUND_ENABLED BACKGROUND_DISABLED diff --git a/src/gui/core/event/dispatcher.cpp b/src/gui/core/event/dispatcher.cpp index b39fc66d97223..1d8e0ba77d156 100644 --- a/src/gui/core/event/dispatcher.cpp +++ b/src/gui/core/event/dispatcher.cpp @@ -81,6 +81,8 @@ bool dispatcher::has_event(const ui_event event, const event_queue_type event_ty event, dispatcher_implementation::has_handler(event_type, *this)) || find( event, dispatcher_implementation::has_handler(event_type, *this)) + || find( + event, dispatcher_implementation::has_handler(event_type, *this)) || find( event, dispatcher_implementation::has_handler(event_type, *this)) || find( @@ -158,6 +160,12 @@ bool dispatcher::fire(const ui_event event, widget& target, const SDL_Event& sdl return fire_event(event, this, &target, sdlevent); } +bool dispatcher::fire(const ui_event event, widget& target, const std::string& text, int32_t start, int32_t len) +{ + assert(find(event, event_in_set())); + return fire_event(event, this, &target, text, start, len); +} + bool dispatcher::fire(const ui_event event, widget& target, const point& pos, const point& distance) { assert(find(event, event_in_set())); diff --git a/src/gui/core/event/dispatcher.hpp b/src/gui/core/event/dispatcher.hpp index b9d196c12bceb..03c00ae809075 100644 --- a/src/gui/core/event/dispatcher.hpp +++ b/src/gui/core/event/dispatcher.hpp @@ -115,10 +115,10 @@ typedef std::function signal_message_function; - /** +/** * Callback function signature. * - * This function is used for the callbacks in set_event_message. + * This function is used for the callbacks in set_event_raw_event. */ typedef std::function signal_raw_event_function; +/** + * Callback function signature. + * + * This function is used for the callbacks in set_event_text_input. + */ +typedef std::function signal_text_input_function; + /** Hotkey function handler signature. */ typedef std::function hotkey_function; @@ -267,6 +280,22 @@ class dispatcher widget& target, const SDL_Event& sdlevent); + /** + * Fires an event which takes text input parameters + * @param event The event to fire. + * @param target The widget that should receive the event. + * Normally this is the window holding the + * widget. + * @param text The text involved in the event + * @param start The start point for IME editing + * @param len The selection length for IME editing + */ + bool fire(const ui_event event, + widget& target, + const std::string& text, + int32_t start, + int32_t len); + /** * The position where to add a new callback in the signal handler. * @@ -569,6 +598,39 @@ class dispatcher signal_raw_event_queue_.disconnect_signal(E, position, signal); } + /** + * Connect a signal for callback in set_text_input. + * + * @tparam E The event the callback needs to react to. + * @param signal The callback function. + * @param position The position to place the callback. + */ + template + utils::enable_if_t::value> + connect_signal(const signal_text_input_function& signal, + const queue_position position = back_child) + { + signal_text_input_queue_.connect_signal(E, position, signal); + } + + /** + * Disconnect a signal for callback in set_text_input. + * + * @tparam E The event the callback was used for. + * @param signal The callback function. + * @param position The place where the function was added. + * Needed remove the event from the right + * place. (The function doesn't care whether + * was added in front or back.) + */ + template + utils::enable_if_t::value> + disconnect_signal(const signal_text_input_function& signal, + const queue_position position = back_child) + { + signal_text_input_queue_.disconnect_signal(E, position, signal); + } + /** * The behavior of the mouse events. * @@ -776,6 +838,9 @@ class dispatcher /** Signal queue for callbacks in set_raw_event. */ signal_queue signal_raw_event_queue_; + /** Signal queue for callbacks in set_event_text_input. */ + signal_queue signal_text_input_queue_; + /** Are we connected to the event handler. */ bool connected_; diff --git a/src/gui/core/event/dispatcher_private.hpp b/src/gui/core/event/dispatcher_private.hpp index 6db1cc5d23998..62977076b7325 100644 --- a/src/gui/core/event/dispatcher_private.hpp +++ b/src/gui/core/event/dispatcher_private.hpp @@ -110,6 +110,7 @@ struct dispatcher_implementation IMPLEMENT_EVENT_SIGNAL_WRAPPER(notification) IMPLEMENT_EVENT_SIGNAL_WRAPPER(message) IMPLEMENT_EVENT_SIGNAL_WRAPPER(raw_event) + IMPLEMENT_EVENT_SIGNAL_WRAPPER(text_input) #undef IMPLEMENT_EVENT_SIGNAL_WRAPPER #undef IMPLEMENT_EVENT_SIGNAL diff --git a/src/gui/core/event/distributor.cpp b/src/gui/core/event/distributor.cpp index 02d27ea65b04d..c14471d1d73bd 100644 --- a/src/gui/core/event/distributor.cpp +++ b/src/gui/core/event/distributor.cpp @@ -563,6 +563,12 @@ distributor::distributor(widget& owner, owner_.connect_signal(std::bind( &distributor::signal_handler_sdl_key_down, this, _5, _6, _7)); + owner_.connect_signal(std::bind( + &distributor::signal_handler_sdl_text_input, this, _5, _6, _7)); + + owner_.connect_signal(std::bind( + &distributor::signal_handler_sdl_text_editing, this, _5, _6, _7)); + owner_.connect_signal(std::bind( &distributor::signal_handler_notify_removal, this, _1, _2)); @@ -574,6 +580,12 @@ distributor::~distributor() owner_.disconnect_signal(std::bind( &distributor::signal_handler_sdl_key_down, this, _5, _6, _7)); + owner_.disconnect_signal(std::bind( + &distributor::signal_handler_sdl_text_input, this, _5, _6, _7)); + + owner_.disconnect_signal(std::bind( + &distributor::signal_handler_sdl_text_editing, this, _5, _6, _7)); + owner_.disconnect_signal(std::bind( &distributor::signal_handler_notify_removal, this, _1, _2)); } @@ -634,13 +646,12 @@ void distributor::keyboard_remove_from_chain(widget* w) } } -void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, - const SDL_Keymod modifier, - const utf8::string& unicode) +template +void distributor::signal_handler_keyboard_internal(event::ui_event evt, P1&& p1, P2&& p2, P3&& p3) { /** @todo Test whether recursion protection is needed. */ - DBG_GUI_E << LOG_HEADER << event::SDL_KEY_DOWN << ".\n"; + DBG_GUI_E << LOG_HEADER << evt << ".\n"; if(keyboard_focus_) { // Attempt to cast to styled_widget, to avoid sending events if the @@ -648,13 +659,9 @@ void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, // is enabled and ready to receive events. styled_widget* control = dynamic_cast(keyboard_focus_); if(!control || control->get_active()) { - DBG_GUI_E << LOG_HEADER << "Firing: " << event::SDL_KEY_DOWN + DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".\n"; - if(owner_.fire(event::SDL_KEY_DOWN, - *keyboard_focus_, - key, - modifier, - unicode)) { + if(owner_.fire(evt, *keyboard_focus_, p1, p2, p3)) { return; } } @@ -691,14 +698,29 @@ void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, continue; } - DBG_GUI_E << LOG_HEADER << "Firing: " << event::SDL_KEY_DOWN << ".\n"; - if(owner_.fire(event::SDL_KEY_DOWN, **ritor, key, modifier, unicode)) { + DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".\n"; + if(owner_.fire(evt, **ritor, p1, p2, p3)) { return; } } } +void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, const SDL_Keymod modifier, const utf8::string& unicode) +{ + signal_handler_keyboard_internal(event::SDL_KEY_DOWN, key, modifier, unicode); +} + +void distributor::signal_handler_sdl_text_input(const utf8::string& unicode, int32_t start, int32_t end) +{ + signal_handler_keyboard_internal(event::SDL_TEXT_INPUT, unicode, start, end); +} + +void distributor::signal_handler_sdl_text_editing(const utf8::string& unicode, int32_t start, int32_t end) +{ + signal_handler_keyboard_internal(event::SDL_TEXT_EDITING, unicode, start, end); +} + void distributor::signal_handler_notify_removal(dispatcher& w, const ui_event event) { diff --git a/src/gui/core/event/distributor.hpp b/src/gui/core/event/distributor.hpp index 828f08870d654..4f5e8d07da889 100644 --- a/src/gui/core/event/distributor.hpp +++ b/src/gui/core/event/distributor.hpp @@ -356,6 +356,12 @@ class distributor : const SDL_Keymod modifier, const utf8::string& unicode); + void signal_handler_sdl_text_input(const utf8::string& unicode, int32_t start, int32_t len); + void signal_handler_sdl_text_editing(const utf8::string& unicode, int32_t start, int32_t len); + + template + void signal_handler_keyboard_internal(event::ui_event evt, P1&& p1, P2&& p2, P3&& p3); + void signal_handler_notify_removal(dispatcher& widget, const ui_event event); }; diff --git a/src/gui/core/event/handler.cpp b/src/gui/core/event/handler.cpp index 5b90e64237b30..9eeadcd831c44 100644 --- a/src/gui/core/event/handler.cpp +++ b/src/gui/core/event/handler.cpp @@ -279,6 +279,15 @@ class sdl_event_handler : public events::sdl_handler */ void text_input(const std::string& unicode); + /** + * Fires a text editing event. + * + * @param unicode The unicode value for the text being edited. + * @param start The start position for the text being edited. + * @param len The selection length for the text being edited. + */ + void text_editing(const std::string& unicode, int32_t start, int32_t len); + /** * Fires a keyboard event which has no parameters. * @@ -424,6 +433,10 @@ void sdl_event_handler::handle_event(const SDL_Event& event) text_input(event.text.text); break; + case SDL_TEXTEDITING: + text_editing(event.edit.text, event.edit.start, event.edit.length); + break; + case SDL_FINGERMOTION: touch_motion(point(event.tfinger.x, event.tfinger.y), point(event.tfinger.dx, event.tfinger.dy)); break; @@ -719,6 +732,21 @@ void sdl_event_handler::key_down(const SDL_Event& event) void sdl_event_handler::text_input(const std::string& unicode) { key_down(SDLK_UNKNOWN, static_cast(0), unicode); + + if(dispatcher* dispatcher = keyboard_dispatcher()) { + dispatcher->fire(SDL_TEXT_INPUT, + dynamic_cast(*dispatcher), + unicode, -1, -1); + } +} + +void sdl_event_handler::text_editing(const std::string& unicode, int32_t start, int32_t end) +{ + if(dispatcher* dispatcher = keyboard_dispatcher()) { + dispatcher->fire(SDL_TEXT_EDITING, + dynamic_cast(*dispatcher), + unicode, start, end); + } } bool sdl_event_handler::hotkey_pressed(const hotkey::hotkey_ptr key) diff --git a/src/gui/core/event/handler.hpp b/src/gui/core/event/handler.hpp index 9e733478f0904..c0be440993c28 100644 --- a/src/gui/core/event/handler.hpp +++ b/src/gui/core/event/handler.hpp @@ -89,6 +89,8 @@ enum ui_event { SDL_WHEEL_UP, /**< An SDL wheel up event. */ SDL_WHEEL_DOWN, /**< An SDL wheel down event. */ SDL_KEY_DOWN, /**< An SDL key down event. */ + SDL_TEXT_INPUT, /**< An SDL text input (commit) event. */ + SDL_TEXT_EDITING, /**< An SDL text editing (IME) event. */ NOTIFY_REMOVAL, /**< Sent by a widget to notify others it's being destroyed. */ NOTIFY_MODIFIED, /**< @@ -222,6 +224,15 @@ set_event_message; */ typedef boost::mpl::set > set_event_raw_event; +/** + * Helper for catching use error of dispatcher::connect_signal. + * + * This version is for callbacks of text input events. + */ +typedef boost::mpl::set, + boost::mpl::int_> +set_event_text_input; + /** * Connects a dispatcher to the event handler. * diff --git a/src/gui/widgets/text_box.cpp b/src/gui/widgets/text_box.cpp index 3dab53dbf3c48..b5f46e804bfb7 100644 --- a/src/gui/widgets/text_box.cpp +++ b/src/gui/widgets/text_box.cpp @@ -137,6 +137,10 @@ void text_box::update_canvas() const unsigned start = get_selection_start(); const int length = get_selection_length(); + // Set the cursor info. + const unsigned edit_start = get_composition_start(); + const int edit_length = get_composition_length(); + set_maximum_length(max_input_length_); PangoEllipsizeMode ellipse_mode = PANGO_ELLIPSIZE_NONE; @@ -162,6 +166,19 @@ void text_box::update_canvas() end_offset = get_cursor_position(start).x; } + // Set the composition info + unsigned comp_start_offset = 0; + unsigned comp_end_offset = 0; + if(edit_length == 0) { + // No nothing. + } else if(edit_length > 0) { + comp_start_offset = get_cursor_position(edit_start).x; + comp_end_offset = get_cursor_position(edit_start + edit_length).x; + } else { + comp_start_offset = get_cursor_position(edit_start + edit_length).x; + comp_end_offset = get_cursor_position(edit_start).x; + } + /***** Set in all canvases *****/ const int max_width = get_text_maximum_width(); @@ -182,6 +199,9 @@ void text_box::update_canvas() tmp.set_variable("selection_offset", wfl::variant(start_offset)); tmp.set_variable("selection_width", wfl::variant(end_offset - start_offset)); tmp.set_variable("text_wrap_mode", wfl::variant(ellipse_mode)); + + tmp.set_variable("composition_offset", wfl::variant(comp_start_offset)); + tmp.set_variable("composition_width", wfl::variant(comp_end_offset - comp_start_offset)); } } @@ -298,23 +318,15 @@ bool text_box::history_down() return true; } -void text_box::handle_key_default(bool& handled, - SDL_Keycode key, - SDL_Keymod modifier, - const utf8::string& unicode) +void text_box::handle_key_tab(SDL_Keymod modifier, bool& handled) { - if(key == SDLK_TAB && (modifier & KMOD_CTRL)) { + if(modifier & KMOD_CTRL) { if(!(modifier & KMOD_SHIFT)) { handled = history_up(); } else { handled = history_down(); } } - - if(!handled) { - // Inherited. - text_box_base::handle_key_default(handled, key, modifier, unicode); - } } void text_box::handle_key_clear_line(SDL_Keymod /*modifier*/, bool& handled) diff --git a/src/gui/widgets/text_box.hpp b/src/gui/widgets/text_box.hpp index fc0949d6729e6..390c3967a202f 100644 --- a/src/gui/widgets/text_box.hpp +++ b/src/gui/widgets/text_box.hpp @@ -247,10 +247,7 @@ class text_box : public text_box_base bool history_down(); /** Inherited from text_box_base. */ - void handle_key_default(bool& handled, - SDL_Keycode key, - SDL_Keymod modifier, - const utf8::string& unicode) override; + void handle_key_tab(SDL_Keymod modifier, bool& handled) override; /** Inherited from text_box_base. */ void handle_key_clear_line(SDL_Keymod modifier, bool& handled) override; diff --git a/src/gui/widgets/text_box_base.cpp b/src/gui/widgets/text_box_base.cpp index 22f0b3a2ee6e2..2bbe9ade082ae 100644 --- a/src/gui/widgets/text_box_base.cpp +++ b/src/gui/widgets/text_box_base.cpp @@ -38,6 +38,7 @@ text_box_base::text_box_base() , text_() , selection_start_(0) , selection_length_(0) + , ime_in_progress_(false) , cursor_timer_(0) , cursor_alpha_(0) , cursor_blink_rate_ms_(750) @@ -51,8 +52,10 @@ text_box_base::text_box_base() #endif connect_signal(std::bind( - &text_box_base::signal_handler_sdl_key_down, this, _2, _3, _5, _6, _7)); - + &text_box_base::signal_handler_sdl_key_down, this, _2, _3, _5, _6)); + connect_signal(std::bind(&text_box_base::handle_commit, this, _3, _5)); + connect_signal(std::bind(&text_box_base::handle_editing, this, _3, _5, _6, _7)); + connect_signal(std::bind( &text_box_base::signal_handler_receive_keyboard_focus, this, _2)); connect_signal( @@ -160,6 +163,16 @@ void text_box_base::insert_char(const utf8::string& unicode) } } +void text_box_base::interrupt_composition() +{ + ime_in_progress_ = false; + ime_length = 0; + // We need to inform the IME that text input is no longer in progress. + // Unfortunately, this does not seem to work. :( + SDL_StopTextInput(); + SDL_StartTextInput(); +} + void text_box_base::copy_selection(const bool mouse) { if(selection_length_ == 0) { @@ -386,17 +399,52 @@ void text_box_base::handle_key_delete(SDL_Keymod /*modifier*/, bool& handled) fire(event::NOTIFY_MODIFIED, *this, nullptr); } -void text_box_base::handle_key_default(bool& handled, - SDL_Keycode /*key*/, - SDL_Keymod /*modifier*/, - const utf8::string& unicode) +void text_box_base::handle_commit(bool& handled, const utf8::string& unicode) { DBG_GUI_E << LOG_SCOPE_HEADER << '\n'; if(unicode.size() > 1 || unicode[0] != 0) { handled = true; + if(ime_in_progress_) { + selection_start_ = ime_start_point_; + selection_length_ = ime_length_; + ime_in_progress_ = false; + ime_length_ = 0; + } insert_char(unicode); fire(event::NOTIFY_MODIFIED, *this, nullptr); + + if(text_changed_callback_) { + text_changed_callback_(this, this->text()); + } + } +} + +void text_box_base::handle_editing(bool& handled, const utf8::string& unicode, int32_t start, int32_t len) +{ + if(unicode.size() > 1 || unicode[0] != 0) { + handled = true; + if(!ime_in_progress_) { + ime_in_progress_ = true; + delete_selection(); + ime_start_point_ = selection_start_; + text_cached_ = text_.text(); + SDL_Rect rect = get_rectangle(); + SDL_SetTextInputRect(&rect); + } + ime_cursor_ = start; + ime_length_ = utf8::size(unicode); + std::string new_text(text_cached_); + new_text.insert(ime_start_point_, unicode); + text_.set_text(new_text, false); + + // Update status + set_cursor(ime_start_point_ + ime_cursor_, false); + if(len > 0) { + set_cursor(ime_start_point_ + ime_cursor_ + len, true); + } + update_canvas(); + set_is_dirty(true); } } @@ -413,8 +461,7 @@ void text_box_base::signal_handler_middle_button_click(const event::ui_event eve void text_box_base::signal_handler_sdl_key_down(const event::ui_event event, bool& handled, const SDL_Keycode key, - SDL_Keymod modifier, - const utf8::string& unicode) + SDL_Keymod modifier) { DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n"; @@ -456,8 +503,7 @@ void text_box_base::signal_handler_sdl_key_down(const event::ui_event event, case SDLK_a: if(!(modifier & KMOD_CTRL)) { - handle_key_default(handled, key, modifier, unicode); - break; + return; } // If ctrl-a is used for home drop the styled_widget modifier @@ -470,8 +516,7 @@ void text_box_base::signal_handler_sdl_key_down(const event::ui_event event, case SDLK_e: if(!(modifier & KMOD_CTRL)) { - handle_key_default(handled, key, modifier, unicode); - break; + return; } // If ctrl-e is used for end drop the styled_widget modifier @@ -487,11 +532,10 @@ void text_box_base::signal_handler_sdl_key_down(const event::ui_event event, break; case SDLK_u: - if(modifier & KMOD_CTRL) { - handle_key_clear_line(modifier, handled); - } else { - handle_key_default(handled, key, modifier, unicode); + if(!(modifier & KMOD_CTRL)) { + return; } + handle_key_clear_line(modifier, handled); break; case SDLK_DELETE: @@ -500,8 +544,7 @@ void text_box_base::signal_handler_sdl_key_down(const event::ui_event event, case SDLK_c: if(!(modifier & copypaste_modifier)) { - handle_key_default(handled, key, modifier, unicode); - break; + return; } // atm we don't care whether there is something to copy or paste @@ -512,8 +555,7 @@ void text_box_base::signal_handler_sdl_key_down(const event::ui_event event, case SDLK_x: if(!(modifier & copypaste_modifier)) { - handle_key_default(handled, key, modifier, unicode); - break; + return; } copy_selection(false); @@ -523,16 +565,33 @@ void text_box_base::signal_handler_sdl_key_down(const event::ui_event event, case SDLK_v: if(!(modifier & copypaste_modifier)) { - handle_key_default(handled, key, modifier, unicode); - break; + return; } paste_selection(false); handled = true; break; + case SDLK_RETURN: + case SDLK_KP_ENTER: + if(!ime_in_progress_ || (modifier & (KMOD_CTRL | KMOD_ALT | KMOD_GUI | KMOD_SHIFT))) { + return; + } + // The IME will handle it, we just need to make sure nothing else handles it too. + handled = true; + break; + + case SDLK_ESCAPE: + if(!ime_in_progress_ || (modifier & (KMOD_CTRL | KMOD_ALT | KMOD_GUI | KMOD_SHIFT))) { + return; + } + interrupt_composition(); + handled = true; + break; + default: - handle_key_default(handled, key, modifier, unicode); + // Don't call the text changed callback if nothing happened. + return; } if(text_changed_callback_) { diff --git a/src/gui/widgets/text_box_base.hpp b/src/gui/widgets/text_box_base.hpp index 9bba176c79b97..d0247c39bd8ef 100644 --- a/src/gui/widgets/text_box_base.hpp +++ b/src/gui/widgets/text_box_base.hpp @@ -250,6 +250,23 @@ class text_box_base : public styled_widget } void set_selection_length(const int selection_length); + size_t get_composition_start() const + { + return ime_start_point_; + } + + size_t get_composition_length() const + { + return ime_length_; + } + +public: + bool is_composing() const + { + return ime_in_progress_; + } + + void interrupt_composition(); private: /** Note the order of the states must be the same as defined in @@ -280,6 +297,9 @@ class text_box_base : public styled_widget /** The text entered in the widget. */ font::pango_text text_; + /** Cached version of the text without any pending IME modifications. */ + std::string text_cached_; + /** Start of the selected text. */ size_t selection_start_; @@ -292,6 +312,12 @@ class text_box_base : public styled_widget */ int selection_length_; + // Values to support input method editors + bool ime_in_progress_; + int ime_start_point_; + int ime_cursor_; + int ime_length_; + size_t cursor_timer_; unsigned short cursor_alpha_; @@ -441,23 +467,24 @@ class text_box_base : public styled_widget { } -protected: /** - * Default key handler if none of the above functions is called. + * Tab key. * - * 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. + * Unmodified Implementation defined. + * Control Implementation defined. + * Shift Implementation defined. + * Alt Implementation defined. */ - virtual void handle_key_default(bool& handled, - SDL_Keycode key, - SDL_Keymod modifier, + virtual void handle_key_tab(SDL_Keymod /*modifier*/, bool& /*handled*/) + { + } + +protected: + virtual void handle_commit(bool& handled, const utf8::string& unicode); + virtual void handle_editing(bool& handled, + const utf8::string& unicode, + int32_t start, int32_t len); private: /** @@ -479,8 +506,13 @@ class text_box_base : public styled_widget void signal_handler_sdl_key_down(const event::ui_event event, bool& handled, const SDL_Keycode key, - SDL_Keymod modifier, - const utf8::string& unicode); + SDL_Keymod modifier); + + void signal_handler_sdl_text_input(const event::ui_event event, + bool& handled, + const utf8::string& unicode, + int32_t start, + int32_t len); void signal_handler_receive_keyboard_focus(const event::ui_event event); void signal_handler_lose_keyboard_focus(const event::ui_event event); diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp index 1c0f853da8975..2cbeee56718a6 100644 --- a/src/gui/widgets/window.cpp +++ b/src/gui/widgets/window.cpp @@ -42,6 +42,7 @@ #include "gui/dialogs/tooltip.hpp" #include "gui/widgets/button.hpp" #include "gui/widgets/container_base.hpp" +#include "gui/widgets/text_box_base.hpp" #include "gui/core/register_widget.hpp" #include "gui/widgets/grid.hpp" #include "gui/widgets/helper.hpp" @@ -1405,6 +1406,15 @@ void window::signal_handler_sdl_key_down(const event::ui_event event, { DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n"; + if(text_box_base* tb = dynamic_cast(event_distributor_->keyboard_focus())) { + if(tb->is_composing()) { + if(handle_tab && !tab_order.empty() && key == SDLK_TAB) { + tb->interrupt_composition(); + } else { + return; + } + } + } if(!enter_disabled_ && (key == SDLK_KP_ENTER || key == SDLK_RETURN)) { set_retval(OK); handled = true; diff --git a/src/key.cpp b/src/key.cpp index 789160c63e7e0..70c42a81ed915 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -30,7 +30,6 @@ bool CKey::is_uncomposable(const SDL_KeyboardEvent &event) { case SDLK_RETURN: case SDLK_ESCAPE: case SDLK_BACKSPACE: - case SDLK_BACKQUOTE: case SDLK_TAB: case SDLK_F1: case SDLK_F2: