diff --git a/source/gui/CInput.cpp b/source/gui/CInput.cpp index c748a4e3cc..b13ffe90a1 100644 --- a/source/gui/CInput.cpp +++ b/source/gui/CInput.cpp @@ -47,7 +47,8 @@ extern int g_yres; //------------------------------------------------------------------- CInput::CInput() : m_iBufferPos(-1), m_iBufferPos_Tail(-1), m_SelectingText(false), m_HorizontalScroll(0.f), - m_PrevTime(0.0), m_CursorVisState(true), m_CursorBlinkRate(0.5), m_iComposedLength(0), m_iComposedPos(0), m_ComposingText(false) + m_PrevTime(0.0), m_CursorVisState(true), m_CursorBlinkRate(0.5), m_ComposingText(false), + m_iComposedLength(0), m_iComposedPos(0), m_iInsertPos(0) { AddSetting(GUIST_float, "buffer_zone"); AddSetting(GUIST_CStrW, "caption"); @@ -78,6 +79,15 @@ CInput::~CInput() { } +void CInput::ClearComposedText() +{ + CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting; + pCaption->erase(m_iInsertPos, m_iComposedLength); + m_iBufferPos = m_iInsertPos; + m_iComposedLength = 0; + m_iComposedPos = 0; +} + InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev) { ENSURE(m_iBufferPos != -1); @@ -102,10 +112,16 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev) if (SelectingText()) DeleteCurSelection(); + if (m_ComposingText) + { + ClearComposedText(); + m_ComposingText = false; + } + if (m_iBufferPos == (int)pCaption->length()) - *pCaption += text; + pCaption->append(text); else - *pCaption = pCaption->Left(m_iBufferPos) + text + pCaption->Right((long)pCaption->length()-m_iBufferPos); + pCaption->insert(m_iBufferPos, text); UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1); @@ -121,31 +137,39 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev) // Text is being composed with an IME // TODO: indicate this by e.g. underlining the uncommitted text CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting; - std::wstring text = wstring_from_utf8(ev->ev.edit.text); + const char *rawText = ev->ev.edit.text; + int rawLength = strlen(rawText); + std::wstring wtext = wstring_from_utf8(rawText); - // Ignore spurious events - if (!m_ComposingText && ev->ev.edit.start == 0 && text.length() == 0) - return IN_HANDLED; - - debug_printf(L"SDL_TEXTEDITING: text=%hs, start=%d, length=%d\n", ev->ev.edit.text, ev->ev.edit.start, ev->ev.edit.length); + debug_printf(L"SDL_TEXTEDITING: text=%hs, start=%d, length=%d\n", rawText, ev->ev.edit.start, ev->ev.edit.length); m_WantedX=0.0f; - m_ComposingText = ev->ev.edit.start != 0 || text.length() != 0; - if (m_ComposingText && SelectingText()) + if (SelectingText()) DeleteCurSelection(); - // Composed text is replaced each time - if (m_iBufferPos == (int)pCaption->length()) - *pCaption = pCaption->Left(m_iBufferPos-m_iComposedPos) + text; + // Remember cursor position when text composition begins + if (!m_ComposingText) + m_iInsertPos = m_iBufferPos; else - *pCaption = pCaption->Left(m_iBufferPos-m_iComposedPos) + text + pCaption->Right((long)pCaption->length()-m_iBufferPos+m_iComposedPos-m_iComposedLength); + { + // Composed text is replaced each time + ClearComposedText(); + } - m_iBufferPos = m_iBufferPos - m_iComposedPos + ev->ev.edit.start; - m_iComposedPos = ev->ev.edit.start; - m_iComposedLength = text.length(); + m_ComposingText = ev->ev.edit.start != 0 || rawLength != 0; + if (m_ComposingText) + { + pCaption->insert(m_iInsertPos, wtext); + + // The text buffer is limited to SDL_TEXTEDITINGEVENT_TEXT_SIZE bytes, yet start + // increases without limit, so don't let it advance beyond the composed text length + m_iComposedLength = wtext.length(); + m_iComposedPos = ev->ev.edit.start < m_iComposedLength ? ev->ev.edit.start : m_iComposedLength; + m_iBufferPos = m_iInsertPos + m_iComposedPos; - // TODO: composed text selection - what does ev.edit.length do? - m_iBufferPos_Tail = -1; + // TODO: composed text selection - what does ev.edit.length do? + m_iBufferPos_Tail = -1; + } UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1); @@ -1048,6 +1072,7 @@ void CInput::HandleMessage(SGUIMessage &Message) rect.x = m_CachedActualSize.TopLeft().x; rect.y = m_CachedActualSize.TopLeft().y; SDL_SetTextInputRect(&rect); + SDL_StartTextInput(); #endif break; @@ -1063,6 +1088,7 @@ void CInput::HandleMessage(SGUIMessage &Message) evt.ev.edit.text[0] = 0; ManuallyHandleEvent(&evt); } + SDL_StopTextInput(); #endif m_iBufferPos = -1; diff --git a/source/gui/CInput.h b/source/gui/CInput.h index 59890536b3..4ca46cd74f 100644 --- a/source/gui/CInput.h +++ b/source/gui/CInput.h @@ -136,6 +136,9 @@ protected: // Called every time the auto-scrolling should be checked. void UpdateAutoScroll(); + // Clear composed IME input when supported (SDL2 only). + void ClearComposedText(); + protected: // Cursor position // (the second one is for selection of larger areas, -1 if not used) @@ -154,6 +157,8 @@ protected: bool m_ComposingText; // The length and position of the current IME composition int m_iComposedLength, m_iComposedPos; + // The position to insert committed text + int m_iInsertPos; // the outer vector is lines, and the inner is X positions // in a row. So that we can determine where characters are diff --git a/source/ps/CConsole.cpp b/source/ps/CConsole.cpp index ba5333606a..189f01d451 100644 --- a/source/ps/CConsole.cpp +++ b/source/ps/CConsole.cpp @@ -92,6 +92,14 @@ void CConsole::ToggleVisible() { m_bToggle = true; m_bVisible = !m_bVisible; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + // TODO: this should be based on input focus, not visibility + if (m_bVisible) + SDL_StartTextInput(); + else + SDL_StopTextInput(); +#endif } void CConsole::SetVisible(bool visible) diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 7306bea649..62d1ea71f3 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -660,9 +660,7 @@ static void InitSDL() } atexit(SDL_Quit); -#if SDL_VERSION_ATLEAST(2, 0, 0) - SDL_StartTextInput(); -#else +#if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_EnableUNICODE(1); #endif }