From 83dce7d38f5acb1959062a2e32fb8783478fc855 Mon Sep 17 00:00:00 2001 From: Xottab-DUTY Date: Thu, 19 Oct 2023 16:57:09 +0500 Subject: [PATCH] Proper text input handling in all known cases (closes #844, closes #1100) Thanks @Plotja --- src/xrEngine/editor_base.cpp | 36 ++++++++++++++++++------- src/xrEngine/editor_base.h | 7 +++-- src/xrEngine/editor_base_input.cpp | 12 ++++----- src/xrEngine/line_edit_control.cpp | 7 ++--- src/xrEngine/line_editor.cpp | 10 ++----- src/xrEngine/line_editor.h | 3 +-- src/xrEngine/xr_input.cpp | 38 +++++++++++++++++++++++++++ src/xrEngine/xr_input.h | 5 ++++ src/xrUICore/EditBox/UICustomEdit.cpp | 23 +++++++--------- 9 files changed, 94 insertions(+), 47 deletions(-) diff --git a/src/xrEngine/editor_base.cpp b/src/xrEngine/editor_base.cpp index 9352a3e77a2..80390f11380 100644 --- a/src/xrEngine/editor_base.cpp +++ b/src/xrEngine/editor_base.cpp @@ -110,6 +110,31 @@ void ide::OnAppEnd() Device.seqRender.Remove(this); } +void ide::UpdateTextInput(bool force_disable /*= false*/) +{ + if (force_disable) + { + if (m_text_input_enabled) + { + pInput->DisableTextInput(); + m_text_input_enabled = false; + } + return; + } + + const ImGuiIO& io = ImGui::GetIO(); + + if (m_text_input_enabled != io.WantTextInput) + { + m_text_input_enabled = io.WantTextInput; + + if (m_text_input_enabled) + pInput->EnableTextInput(); + else + pInput->DisableTextInput(); + } +} + void ide::OnFrame() { const float frametime = m_timer.GetElapsed_sec(); @@ -118,22 +143,13 @@ void ide::OnFrame() ImGuiIO& io = ImGui::GetIO(); io.DeltaTime = frametime; - // When shown, input being is updated - // through IInputReceiver interface - if (m_state == visible_state::full) - { - if (io.WantTextInput) - SDL_StartTextInput(); - else - SDL_StopTextInput(); - } - m_render->Frame(); ImGui::NewFrame(); switch (m_state) { case visible_state::full: + UpdateTextInput(); ShowMain(); [[fallthrough]]; diff --git a/src/xrEngine/editor_base.h b/src/xrEngine/editor_base.h index 427283ab056..896c342b733 100644 --- a/src/xrEngine/editor_base.h +++ b/src/xrEngine/editor_base.h @@ -75,8 +75,8 @@ class ENGINE_API ide final : void OnAppStart() override; void OnAppEnd() override; - void IR_Capture() override; - void IR_Release() override; + void IR_OnActivate() override; + void IR_OnDeactivate() override; void IR_OnMousePress(int key) override; void IR_OnMouseRelease(int key) override; @@ -109,6 +109,8 @@ class ENGINE_API ide final : void RegisterTool(ide_tool* tool); void UnregisterTool(const ide_tool* tool); + void UpdateTextInput(bool force_disable = false); + private: CTimer m_timer; IImGuiRender* m_render{}; @@ -117,6 +119,7 @@ class ENGINE_API ide final : visible_state m_state; bool m_show_weather_editor; // to be refactored + bool m_text_input_enabled{}; xr_vector m_tools; }; diff --git a/src/xrEngine/editor_base_input.cpp b/src/xrEngine/editor_base_input.cpp index fc8765a4c83..8e2b40eb06a 100644 --- a/src/xrEngine/editor_base_input.cpp +++ b/src/xrEngine/editor_base_input.cpp @@ -62,17 +62,16 @@ void ide::OnAppDeactivate() io.AddFocusEvent(false); } -void ide::IR_Capture() +void ide::IR_OnActivate() { - IInputReceiver::IR_Capture(); ImGuiIO& io = ImGui::GetIO(); io.MouseDrawCursor = true; } -void ide::IR_Release() +void ide::IR_OnDeactivate() { - SDL_StopTextInput(); - IInputReceiver::IR_Release(); + UpdateTextInput(true); + ImGuiIO& io = ImGui::GetIO(); io.MouseDrawCursor = false; } @@ -212,7 +211,8 @@ void ide::IR_OnKeyboardHold(int /*key*/) void ide::IR_OnTextInput(pcstr text) { ImGuiIO& io = ImGui::GetIO(); - io.AddInputCharactersUTF8(text); + if (io.WantTextInput) + io.AddInputCharactersUTF8(text); } void ide::IR_OnControllerPress(int key, float x, float y) diff --git a/src/xrEngine/line_edit_control.cpp b/src/xrEngine/line_edit_control.cpp index 8cf310287ae..10f49c4f581 100644 --- a/src/xrEngine/line_edit_control.cpp +++ b/src/xrEngine/line_edit_control.cpp @@ -152,15 +152,12 @@ void line_edit_control::clear_states() void line_edit_control::on_ir_capture() { - SDL_PumpEvents(); - SDL_StartTextInput(); - SDL_FlushEvents(SDL_TEXTEDITING, SDL_TEXTINPUT); + pInput->EnableTextInput(); } void line_edit_control::on_ir_release() { - SDL_StopTextInput(); - SDL_FlushEvents(SDL_TEXTEDITING, SDL_TEXTINPUT); + pInput->DisableTextInput(); } void line_edit_control::init(size_t str_buffer_size, init_mode mode) diff --git a/src/xrEngine/line_editor.cpp b/src/xrEngine/line_editor.cpp index 179576d1114..9509bc5b3e6 100644 --- a/src/xrEngine/line_editor.cpp +++ b/src/xrEngine/line_editor.cpp @@ -20,16 +20,10 @@ void line_editor::IR_OnKeyboardHold(int dik) { m_control.on_key_hold(dik); } void line_editor::IR_OnKeyboardRelease(int dik) { m_control.on_key_release(dik); } void line_editor::IR_OnTextInput(const char *text) { m_control.on_text_input(text); } -void line_editor::IR_Capture() +void line_editor::IR_OnActivate() { - IInputReceiver::IR_Capture(); m_control.on_ir_capture(); -} - -void line_editor::IR_Release() -{ - m_control.on_ir_release(); - IInputReceiver::IR_Release(); + IInputReceiver::IR_OnDeactivate(); } void line_editor::IR_OnDeactivate() diff --git a/src/xrEngine/line_editor.h b/src/xrEngine/line_editor.h index 356ced0a32e..56351552bf0 100644 --- a/src/xrEngine/line_editor.h +++ b/src/xrEngine/line_editor.h @@ -22,8 +22,7 @@ class line_editor : public IInputReceiver IC line_edit_control& control() { return m_control; } void on_frame(); - void IR_Capture() final; - void IR_Release() final; + void IR_OnActivate() final; void IR_OnDeactivate() final; protected: diff --git a/src/xrEngine/xr_input.cpp b/src/xrEngine/xr_input.cpp index 11f127f085a..5850fe6fe6d 100644 --- a/src/xrEngine/xr_input.cpp +++ b/src/xrEngine/xr_input.cpp @@ -228,6 +228,13 @@ void CInput::KeyUpdate() if (count) SetCurrentInputType(KeyboardMouse); + // If textInputCounter has changed, + // we assume that text input target changed. + // Theoretically, this is not always true, though. + // But we always can change the solution. + // If we find out something not work as expected. + const auto cnt = textInputCounter; + for (int i = 0; i < count; ++i) { const SDL_Event& event = events[i]; @@ -245,6 +252,8 @@ void CInput::KeyUpdate() break; case SDL_TEXTINPUT: + if (cnt != textInputCounter) + continue; // if input target changed, skip this frame cbStack.back()->IR_OnTextInput(event.text.text); break; @@ -536,6 +545,35 @@ bool CInput::InputIsGrabbed() const return inputGrabbed; } +void CInput::EnableTextInput() +{ + ++textInputCounter; + + if (textInputCounter == 1) + SDL_StartTextInput(); + + SDL_PumpEvents(); + SDL_FlushEvents(SDL_TEXTEDITING, SDL_TEXTINPUT); +} + +void CInput::DisableTextInput() +{ + --textInputCounter; + if (textInputCounter < 0) + textInputCounter = 0; + + if (textInputCounter == 0) + SDL_StopTextInput(); + + SDL_PumpEvents(); + SDL_FlushEvents(SDL_TEXTEDITING, SDL_TEXTINPUT); +} + +bool CInput::IsTextInputEnabled() const +{ + return textInputCounter > 0; +} + void CInput::RegisterKeyMapChangeWatcher(pureKeyMapChanged* watcher, int priority /*= REG_PRIORITY_NORMAL*/) { seqKeyMapChanged.Add(watcher, priority); diff --git a/src/xrEngine/xr_input.h b/src/xrEngine/xr_input.h index 49e9a2b3c90..6af33d63398 100644 --- a/src/xrEngine/xr_input.h +++ b/src/xrEngine/xr_input.h @@ -123,6 +123,7 @@ class ENGINE_API CInput InputStatistics stats; bool exclusiveInput; bool inputGrabbed; + int textInputCounter{}; MessageRegistry seqKeyMapChanged; @@ -144,6 +145,10 @@ class ENGINE_API CInput void GrabInput(const bool grab); bool InputIsGrabbed() const; + void EnableTextInput(); + void DisableTextInput(); + bool IsTextInputEnabled() const; + void RegisterKeyMapChangeWatcher(pureKeyMapChanged* watcher, int priority = REG_PRIORITY_NORMAL); void RemoveKeyMapChangeWatcher(pureKeyMapChanged* watcher); diff --git a/src/xrUICore/EditBox/UICustomEdit.cpp b/src/xrUICore/EditBox/UICustomEdit.cpp index d26b2d9c292..fcc3aff7d62 100644 --- a/src/xrUICore/EditBox/UICustomEdit.cpp +++ b/src/xrUICore/EditBox/UICustomEdit.cpp @@ -93,13 +93,9 @@ void CUICustomEdit::SendMessage(CUIWindow* pWnd, s16 msg, void* pData) //кто-то другой захватил клавиатуру if (msg == WINDOW_KEYBOARD_CAPTURE_LOST && m_bInputFocus) { - m_bInputFocus = false; + CaptureFocus(false); GetMessageTarget()->SendMessage(this, EDIT_TEXT_COMMIT, NULL); } - else if (msg == WINDOW_FOCUS_RECEIVED) - ec().on_ir_capture(); - else if (msg == WINDOW_FOCUS_LOST) - ec().on_ir_release(); } bool CUICustomEdit::OnMouseAction(float x, float y, EUIMessages mouse_action) @@ -108,15 +104,13 @@ bool CUICustomEdit::OnMouseAction(float x, float y, EUIMessages mouse_action) { if (mouse_action == WINDOW_LBUTTON_DB_CLICK && !m_bInputFocus) { - GetParent()->SetKeyboardCapture(this, true); - m_bInputFocus = true; + CaptureFocus(true); } } if (mouse_action == WINDOW_LBUTTON_DOWN && !m_bInputFocus) { - GetParent()->SetKeyboardCapture(this, true); - m_bInputFocus = true; + CaptureFocus(true); } return false; } @@ -124,9 +118,7 @@ bool CUICustomEdit::OnMouseAction(float x, float y, EUIMessages mouse_action) bool CUICustomEdit::OnKeyboardAction(int dik, EUIMessages keyboard_action) { if (!m_bInputFocus) - { return false; - } switch (keyboard_action) { @@ -148,6 +140,9 @@ bool CUICustomEdit::OnKeyboardAction(int dik, EUIMessages keyboard_action) bool CUICustomEdit::OnTextInput(pcstr text) { + if (!m_bInputFocus) + return false; + ec().on_text_input(text); return true; } @@ -274,7 +269,7 @@ void CUICustomEdit::press_escape() } else { - m_bInputFocus = false; + CaptureFocus(false); GetParent()->SetKeyboardCapture(this, false); GetMessageTarget()->SendMessage(this, EDIT_TEXT_CANCEL, NULL); } @@ -282,7 +277,7 @@ void CUICustomEdit::press_escape() void CUICustomEdit::press_commit() { - m_bInputFocus = false; + CaptureFocus(false); GetParent()->SetKeyboardCapture(this, false); GetMessageTarget()->SendMessage(this, EDIT_TEXT_COMMIT, NULL); } @@ -292,7 +287,7 @@ void CUICustomEdit::press_tab() if (!m_next_focus_capturer) return; - m_bInputFocus = false; + CaptureFocus(false); GetParent()->SetKeyboardCapture(this, false); GetMessageTarget()->SendMessage(this, EDIT_TEXT_COMMIT, NULL); GetParent()->SetKeyboardCapture(m_next_focus_capturer, true);