[WIP] Implement IME support for GUI2 textboxes (#1758)

Implement IME support for GUI2 textboxes
This commit is contained in:
Celtic Minstrel 2017-06-11 16:47:46 -04:00 committed by GitHub
parent 8b0c9af52d
commit 5d43078ba4
14 changed files with 349 additions and 67 deletions

View file

@ -68,6 +68,15 @@
color = "([255, 255, 255, cursor_alpha])"
thickness = 1
[/line]
[rectangle]
x = "(composition_offset + {X_OFFSET})"
y = "(text_y_offset + text_font_height - 2)"
w = "(composition_width)"
h = "2"
fill_color = "([140, 140, 0, if(composition_width > 0, 255, 0)])"
border_thickness = 0
[/rectangle]
#enddef
#define _GUI_RESOLUTION RESOLUTION MIN_WIDTH DEFAULT_WIDTH HEIGHT X_OFFSET EXTRA_WIDTH FONT_SIZE BACKGROUND_ENABLED BACKGROUND_DISABLED

View file

@ -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<set_event_keyboard>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_text_input>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_touch>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_notification>(
@ -158,6 +160,12 @@ bool dispatcher::fire(const ui_event event, widget& target, const SDL_Event& sdl
return fire_event<signal_raw_event_function>(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<set_event_text_input>(event, event_in_set()));
return fire_event<signal_text_input_function>(event, this, &target, text, start, len);
}
bool dispatcher::fire(const ui_event event, widget& target, const point& pos, const point& distance)
{
assert(find<set_event_touch>(event, event_in_set()));

View file

@ -115,10 +115,10 @@ typedef std::function<void(dispatcher& dispatcher,
bool& halt,
message& message)> 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<void(dispatcher& dispatcher,
const ui_event event,
@ -126,6 +126,19 @@ typedef std::function<void(dispatcher& dispatcher,
bool& halt,
const SDL_Event& sdlevent)> signal_raw_event_function;
/**
* Callback function signature.
*
* This function is used for the callbacks in set_event_text_input.
*/
typedef std::function<void(dispatcher& dispatcher,
const ui_event event,
bool& handled,
bool& halt,
const std::string& text,
int32_t current_pos,
int32_t select_len)> signal_text_input_function;
/** Hotkey function handler signature. */
typedef std::function<bool(dispatcher& dispatcher,
hotkey::HOTKEY_COMMAND id)> hotkey_function;
@ -267,6 +280,22 @@ public:
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 @@ public:
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 <ui_event E>
utils::enable_if_t<has_key<set_event_text_input, E>::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 <ui_event E>
utils::enable_if_t<has_key<set_event_text_input, E>::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 @@ private:
/** Signal queue for callbacks in set_raw_event. */
signal_queue<signal_raw_event_function> signal_raw_event_queue_;
/** Signal queue for callbacks in set_event_text_input. */
signal_queue<signal_text_input_function> signal_text_input_queue_;
/** Are we connected to the event handler. */
bool connected_;

View file

@ -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

View file

@ -22,6 +22,7 @@
#include "gui/widgets/settings.hpp"
#include "gui/widgets/widget.hpp"
#include "gui/widgets/window.hpp"
#include "gui/widgets/text_box_base.hpp"
#include "utils/functional.hpp"
@ -563,6 +564,12 @@ distributor::distributor(widget& owner,
owner_.connect_signal<event::SDL_KEY_DOWN>(std::bind(
&distributor::signal_handler_sdl_key_down, this, _5, _6, _7));
owner_.connect_signal<event::SDL_TEXT_INPUT>(std::bind(
&distributor::signal_handler_sdl_text_input, this, _5, _6, _7));
owner_.connect_signal<event::SDL_TEXT_EDITING>(std::bind(
&distributor::signal_handler_sdl_text_editing, this, _5, _6, _7));
owner_.connect_signal<event::NOTIFY_REMOVAL>(std::bind(
&distributor::signal_handler_notify_removal, this, _1, _2));
@ -574,6 +581,12 @@ distributor::~distributor()
owner_.disconnect_signal<event::SDL_KEY_DOWN>(std::bind(
&distributor::signal_handler_sdl_key_down, this, _5, _6, _7));
owner_.disconnect_signal<event::SDL_TEXT_INPUT>(std::bind(
&distributor::signal_handler_sdl_text_input, this, _5, _6, _7));
owner_.disconnect_signal<event::SDL_TEXT_EDITING>(std::bind(
&distributor::signal_handler_sdl_text_editing, this, _5, _6, _7));
owner_.disconnect_signal<event::NOTIFY_REMOVAL>(std::bind(
&distributor::signal_handler_notify_removal, this, _1, _2));
}
@ -634,13 +647,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<typename Fcn, typename P1, typename P2, typename P3>
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,16 +660,17 @@ void distributor::signal_handler_sdl_key_down(const SDL_Keycode key,
// is enabled and ready to receive events.
styled_widget* control = dynamic_cast<styled_widget*>(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;
}
}
if(text_box_base* tb = dynamic_cast<text_box_base*>(keyboard_focus_)) {
if(tb->is_composing()) {
return; // Skip the keyboard chain if composition is in progress.
}
}
}
for(std::vector<widget*>::reverse_iterator ritor
@ -691,14 +704,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<signal_keyboard_function>(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<signal_text_input_function>(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<signal_text_input_function>(event::SDL_TEXT_EDITING, unicode, start, end);
}
void distributor::signal_handler_notify_removal(dispatcher& w,
const ui_event event)
{

View file

@ -356,6 +356,12 @@ private:
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<typename Fcn, typename P1, typename P2, typename P3>
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);
};

View file

@ -279,6 +279,15 @@ private:
*/
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<SDL_Keymod>(0), unicode);
if(dispatcher* dispatcher = keyboard_dispatcher()) {
dispatcher->fire(SDL_TEXT_INPUT,
dynamic_cast<widget&>(*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<widget&>(*dispatcher),
unicode, start, end);
}
}
bool sdl_event_handler::hotkey_pressed(const hotkey::hotkey_ptr key)
@ -929,6 +957,12 @@ std::ostream& operator<<(std::ostream& stream, const ui_event event)
case SDL_KEY_DOWN:
stream << "SDL key down";
break;
case SDL_TEXT_INPUT:
stream << "SDL text input";
break;
case SDL_TEXT_EDITING:
stream << "SDL text editing";
break;
case NOTIFY_REMOVAL:
stream << "notify removal";

View file

@ -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<boost::mpl::int_<SDL_RAW_EVENT> > 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_<SDL_TEXT_INPUT>,
boost::mpl::int_<SDL_TEXT_EDITING>>
set_event_text_input;
/**
* Connects a dispatcher to the event handler.
*

View file

@ -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)

View file

@ -247,10 +247,7 @@ private:
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;

View file

@ -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<event::SDL_KEY_DOWN>(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<event::SDL_TEXT_INPUT>(std::bind(&text_box_base::handle_commit, this, _3, _5));
connect_signal<event::SDL_TEXT_EDITING>(std::bind(&text_box_base::handle_editing, this, _3, _5, _6, _7));
connect_signal<event::RECEIVE_KEYBOARD_FOCUS>(std::bind(
&text_box_base::signal_handler_receive_keyboard_focus, this, _2));
connect_signal<event::LOSE_KEYBOARD_FOCUS>(
@ -160,6 +163,15 @@ 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.
SDL_StopTextInput();
SDL_StartTextInput();
}
void text_box_base::copy_selection(const bool mouse)
{
if(selection_length_ == 0) {
@ -386,17 +398,60 @@ 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;
size_t new_len = utf8::size(unicode);
if(!ime_in_progress_) {
ime_in_progress_ = true;
delete_selection();
ime_start_point_ = selection_start_;
text_cached_ = text_.text();
SDL_Rect rect = get_rectangle();
if(new_len > 0) {
rect.x += get_cursor_position(ime_start_point_).x;
rect.w = get_cursor_position(ime_start_point_ + new_len).x - rect.x;
} else {
rect.x += get_cursor_position(ime_start_point_ + new_len).x;
rect.w = get_cursor_position(ime_start_point_).x - rect.x;
}
SDL_SetTextInputRect(&rect);
}
ime_cursor_ = start;
ime_length_ = new_len;
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 +468,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 +510,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 +523,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 +539,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 +551,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 +562,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 +572,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_) {

View file

@ -250,6 +250,23 @@ protected:
}
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 @@ private:
/** 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 @@ private:
*/
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 @@ private:
{
}
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 @@ private:
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);

View file

@ -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"
@ -687,6 +688,10 @@ int window::show(const bool restore, const unsigned auto_close_timeout)
font::undraw_floating_labels(video_.getSurface());
}
if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
tb->interrupt_composition();
}
return retval_;
}
@ -1405,6 +1410,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<text_box_base*>(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;

View file

@ -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: