diff --git a/lib/irrlicht/include/IEventReceiver.h b/lib/irrlicht/include/IEventReceiver.h index 82a5a9cae..6094392cd 100644 --- a/lib/irrlicht/include/IEventReceiver.h +++ b/lib/irrlicht/include/IEventReceiver.h @@ -61,14 +61,6 @@ namespace irr user receiver then no text will be sent to the console. */ EET_LOG_TEXT_EVENT, -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) - //! A input method event - /** Input method events are created by the input method message and passed to IrrlichtDevice::postEventFromUser. - Windows: Implemented. - Linux / Other: Not yet implemented. */ - EET_IMPUT_METHOD_EVENT, -#endif - //! A user event with user data. /** This is not used by Irrlicht and can be used to send user specific data though the system. The Irrlicht 'window handle' @@ -167,20 +159,6 @@ namespace irr EMBSM_FORCE_32_BIT = 0x7fffffff }; - -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) - //! Enumeration for all input method events - enum EINPUT_METHOD_EVENT - { - //! a character from input method. - EIME_CHAR_INPUT = 0, - - //! change position of composition window - EIME_CHANGE_POS, - - EIME_FORCE_32_BIT = 0x7fffffff - }; -#endif //! Enumeration for all touch input events enum ETOUCH_INPUT_EVENT @@ -590,21 +568,6 @@ struct SEvent EAPPLICATION_EVENT_TYPE EventType; }; -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) - struct SInputMethodEvent - { - //! Parent window handle for IMM functions (Windows only) - void* Handle; - - //! Character from Input Method - wchar_t Char; - - //! Type of input method event - EINPUT_METHOD_EVENT Event; - }; -#endif - - EEVENT_TYPE EventType; union { @@ -620,9 +583,6 @@ struct SEvent struct SUserEvent UserEvent; struct SSystemEvent SystemEvent; struct SApplicationEvent ApplicationEvent; -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) - struct SInputMethodEvent InputMethodEvent; -#endif }; }; diff --git a/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp b/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp index 583bdc5b0..2b46f6539 100644 --- a/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp +++ b/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp @@ -573,18 +573,6 @@ bool CGUIEnvironment::postEventFromUser(const SEvent& event) } break; -#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ - case EET_IMPUT_METHOD_EVENT: - { - // todo : if CGUIEdit has not focus, close input method. Use WM_NOTIFY message. - if (Focus) - { - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return Focus->OnEvent(event); - } - } - break; -#endif default: break; } // end switch diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 97eb8d474..141f161fa 100755 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -12,6 +12,7 @@ #include "os.h" #include "utils/string_utils.hpp" +#include "utils/utf8.h" #include "CTimer.h" #include "irrString.h" @@ -951,29 +952,6 @@ irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd) return 0; } - -namespace irr -{ - void updateICPos(void* hWnd, s32 x, s32 y, s32 height) - { - COMPOSITIONFORM cf; - HWND hwnd = (HWND)hWnd; - HIMC hIMC = ImmGetContext(hwnd); - - if(hIMC) - { - cf.dwStyle = CFS_POINT; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y - height; - - ImmSetCompositionWindow(hIMC, &cf); - - ImmReleaseContext(hwnd, hIMC); - } - } -} // end of namespace irr - - LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { #ifndef WM_MOUSEWHEEL @@ -1085,15 +1063,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } } - if (m->irrMessage == irr::EMIE_LMOUSE_PRESSED_DOWN || m->irrMessage == irr::EMIE_LMOUSE_LEFT_UP) - { - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; - event.InputMethodEvent.Handle = hWnd; - - if (dev) - dev->postEventFromUser(event); - } return 0; } @@ -1112,25 +1081,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_IME_CHAR: { - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHAR_INPUT; - event.InputMethodEvent.Handle = hWnd; - - event.KeyInput.Char = 0; - event.KeyInput.Key = irr::IRR_KEY_OEM_CLEAR; - event.KeyInput.Shift = 0; - event.KeyInput.Control = 0; - - char bits[2] = { (char)((wParam & 0xff00) >> 8), (char)(wParam & 0xff) }; - if (bits[0] == 0) - event.InputMethodEvent.Char = (wchar_t)wParam; - else - MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, bits, 2, &event.InputMethodEvent.Char, 1); - dev = getDeviceFromHWnd(hWnd); - if (dev) - dev->postEventFromUser(event); - + if (!dev) + return 0; + event.EventType = irr::EET_KEY_INPUT_EVENT; + event.KeyInput.PressedDown = true; + event.KeyInput.Char = (char32_t)wParam; + event.KeyInput.Key = irr::IRR_KEY_UNKNOWN; + event.KeyInput.Shift = false; + event.KeyInput.Control = false; + dev->postEventFromUser(event); return 0; } @@ -1169,41 +1129,29 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) event.KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0); event.KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0); + event.KeyInput.Char = 0; - // Handle unicode and deadkeys in a way that works since Windows 95 and nt4.0 - // Using ToUnicode instead would be shorter, but would to my knowledge not run on 95 and 98. - WORD keyChars[2]; + wchar_t keyChars[10] = {0}; UINT scanCode = HIWORD(lParam); - int conversionResult = ToAsciiEx(wParam,scanCode,allKeys,keyChars,0,KEYBOARD_INPUT_HKL); - if (conversionResult == 1) - { - WORD unicodeChar; - MultiByteToWideChar( - KEYBOARD_INPUT_CODEPAGE, - MB_PRECOMPOSED, // default - (LPCSTR)keyChars, - sizeof(keyChars), - (WCHAR*)&unicodeChar, - 1 ); - event.KeyInput.Char = unicodeChar; - } - else - event.KeyInput.Char = 0; - + int conversionResult = ToUnicodeEx((UINT)wParam,scanCode,allKeys,keyChars,_countof(keyChars),0,KEYBOARD_INPUT_HKL); // allow composing characters like '@' with Alt Gr on non-US keyboards if ((allKeys[VK_MENU] & 0x80) != 0) event.KeyInput.Control = 0; dev = getDeviceFromHWnd(hWnd); if (dev) - dev->postEventFromUser(event); - - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; - event.InputMethodEvent.Handle = hWnd; - - if (dev) - dev->postEventFromUser(event); + { + if (conversionResult >= 1) + { + for (int i = 0; i < conversionResult; i++) + { + event.KeyInput.Char = keyChars[i]; + dev->postEventFromUser(event); + } + } + else + dev->postEventFromUser(event); + } if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP) return DefWindowProc(hWnd, message, wParam, lParam); @@ -1255,12 +1203,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) dev->switchToFullScreen(); } } - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; - event.InputMethodEvent.Handle = hWnd; - - if (dev) - dev->postEventFromUser(event); break; case WM_USER: @@ -1445,8 +1387,6 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) // inform driver about the window size etc. resizeIfNecessary(); - // for reset the position of composition window - updateICPos(HWnd, 0, 0, 0); } diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.h b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.h index 4ff8ab92e..8e67f22ca 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.h +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.h @@ -307,7 +307,7 @@ namespace irr //! Return a system-specific size which is supported for cursors. Larger icons will fail, smaller icons might work. virtual core::dimension2di getSupportedIconSize() const; - void update(); + void update(); private: @@ -386,6 +386,8 @@ namespace irr //! returns the win32 cursor control CCursorControl* getWin32CursorControl(); + HWND getHandle() const { return HWnd; } + private: //! create the driver diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp index d577473bc..38f0d969c 100644 --- a/src/guiengine/widgets/CGUIEditBox.cpp +++ b/src/guiengine/widgets/CGUIEditBox.cpp @@ -20,6 +20,7 @@ #include "guiengine/screen_keyboard.hpp" #include "utils/string_utils.hpp" #include "utils/time.hpp" +#include "utils/utf8.h" #include "../../../lib/irrlicht/include/IrrCompileConfig.h" #include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h" @@ -31,6 +32,10 @@ #include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.h" #endif +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ +#include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.h" +#endif + /* todo: optional scrollbars @@ -172,13 +177,6 @@ _raqm_get_grapheme_break (hb_codepoint_t ch, }; #endif -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) -namespace irr -{ - void updateICPos(void* hWnd, s32 x, s32 y, s32 height); -} -#endif - //! constructor CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment, IGUIElement* parent, s32 id, @@ -396,12 +394,6 @@ bool CGUIEditBox::OnEvent(const SEvent& event) } } break; -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) - case EET_IMPUT_METHOD_EVENT: - if (processIMEEvent(event)) - return true; - break; -#endif case EET_KEY_INPUT_EVENT: if (processKey(event)) return true; @@ -852,7 +844,6 @@ bool CGUIEditBox::processKey(const SEvent& event) if (text_changed) { updateGlyphLayouts(); - sendGuiEvent(EGET_EDITBOX_CHANGED); } // Set new text markers @@ -871,38 +862,6 @@ bool CGUIEditBox::processKey(const SEvent& event) } -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) -bool CGUIEditBox::processIMEEvent(const SEvent& event) -{ - switch(event.InputMethodEvent.Event) - { - case EIME_CHAR_INPUT: - inputChar(event.InputMethodEvent.Char); - return true; - case EIME_CHANGE_POS: - { - updateCursorDistance(); - core::position2di pos = calculateICPos(); - - IGUIFont* font = OverrideFont; - IGUISkin* skin = Environment->getSkin(); - - if (!OverrideFont) - font = skin->getFont(); - - irr::updateICPos(event.InputMethodEvent.Handle, pos.X,pos.Y, font->getHeightPerLine()); - - return true; - } - default: - break; - } - - return false; -} -#endif - -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) || defined(_IRR_COMPILE_WITH_X11_DEVICE_) //! calculate the position of input composition window core::position2di CGUIEditBox::calculateICPos() { @@ -914,7 +873,6 @@ core::position2di CGUIEditBox::calculateICPos() } -#endif //! draws the element and its children void CGUIEditBox::draw() { @@ -922,6 +880,7 @@ void CGUIEditBox::draw() if (!IsVisible) return; + updateSurrogatePairText(); GUIEngine::ScreenKeyboard* screen_kbd = GUIEngine::ScreenKeyboard::getCurrent(); bool has_screen_kbd = (screen_kbd && screen_kbd->getEditBox() == this); @@ -1200,7 +1159,7 @@ s32 CGUIEditBox::getCursorPos(s32 x, s32 y) x += m_scroll_pos; if (x < 0) x = 0; - return getCurosrFromDimension(x, y, m_glyph_layouts, font->getHeightPerLine(), + return getCurosrFromDimension((f32)x, (f32)y, m_glyph_layouts, font->getHeightPerLine(), font->getInverseShaping(), font->getScale()); } @@ -1277,6 +1236,18 @@ void CGUIEditBox::inputChar(char32_t c) if (c < 32) return; + if (c < 65536) + { + wchar_t wc = c & 65535; + if (utf8::internal::is_surrogate(wc) || !m_surrogate_chars.empty()) + { + // Handle utf16 to 32 conversion together later, including any emoji + // joint character (which is not surrogate) + m_surrogate_chars.push_back(wc); + return; + } + } + if ((u32)getTextCount() < m_max_chars || m_max_chars == 0) { std::u32string sub_str; @@ -1306,7 +1277,6 @@ void CGUIEditBox::inputChar(char32_t c) m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200; updateGlyphLayouts(); setTextMarkers(0, 0); - sendGuiEvent(EGET_EDITBOX_CHANGED); calculateScrollPos(); } } @@ -1368,6 +1338,7 @@ void CGUIEditBox::calculateScrollPos() m_scroll_pos = 0; // todo: adjust scrollbar + core::position2di pos = calculateICPos(); #if defined(_IRR_COMPILE_WITH_X11_DEVICE_) if (irr_driver->getDevice()->getType() == irr::EIDT_X11) { @@ -1375,10 +1346,29 @@ void CGUIEditBox::calculateScrollPos() irr_driver->getDevice()); if (dl) { - dl->setIMELocation(calculateICPos()); + dl->setIMELocation(pos); } } +#elif defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + CIrrDeviceWin32* win = NULL; + if (irr_driver->getDevice()->getType() == irr::EIDT_WIN32) + win = dynamic_cast(irr_driver->getDevice()); + if (!win) + return; + + COMPOSITIONFORM cf; + HIMC hIMC = ImmGetContext(win->getHandle()); + + if (hIMC) + { + cf.dwStyle = CFS_POINT; + cf.ptCurrentPos.x = pos.X; + cf.ptCurrentPos.y = pos.Y - font->getHeightPerLine(); + ImmSetCompositionWindow(hIMC, &cf); + ImmReleaseContext(win->getHandle(), hIMC); + } #endif + #endif // SERVER_ONLY } @@ -1436,7 +1426,7 @@ void CGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWr out->addBool ("AutoScroll", AutoScroll); out->addBool ("PasswordBox", PasswordBox); core::stringw ch = L" "; - ch[0] = PasswordChar; + ch[0] = (wchar_t)PasswordChar; out->addString("PasswordChar", ch.c_str()); out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); @@ -1535,3 +1525,54 @@ void CGUIEditBox::updateGlyphLayouts() Text = StringUtils::utf32ToWide(m_edit_text); #endif } + +void CGUIEditBox::updateSurrogatePairText() +{ + if (!m_surrogate_chars.empty()) + { + wchar_t last_char = m_surrogate_chars.back(); + if (utf8::internal::is_trail_surrogate(last_char) || + !utf8::internal::is_surrogate(last_char)) + { + const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end; + const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin; + m_surrogate_chars.push_back(0); + std::u32string result = StringUtils::wideToUtf32(m_surrogate_chars.data()); + if (m_mark_begin == m_mark_end) + { + // insert text + std::u32string sub_str = m_edit_text.substr(0, m_cursor_pos); + sub_str += result; + sub_str += m_edit_text.substr(m_cursor_pos, m_edit_text.size() - m_cursor_pos); + + if (m_max_chars == 0 || sub_str.size() <= m_max_chars) // thx to Fish FH for fix + { + m_edit_text = sub_str; + m_cursor_pos = m_cursor_pos + (s32)result.size(); + } + } + else + { + // replace text + std::u32string sub_str = m_edit_text.substr(0, realmbgn); + sub_str += result; + sub_str += m_edit_text.substr(realmend, m_edit_text.size() - realmend); + + if (m_max_chars == 0 || sub_str.size() <= m_max_chars) // thx to Fish FH for fix + { + m_edit_text = sub_str; + m_cursor_pos = realmbgn + (s32)sub_str.size(); + } + } + m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200; + updateGlyphLayouts(); + setTextMarkers(0, 0); + if (m_cursor_pos > getTextCount()) + m_cursor_pos = getTextCount(); + if (m_cursor_pos < 0) + m_cursor_pos = 0; + calculateScrollPos(); + m_surrogate_chars.clear(); + } + } +} diff --git a/src/guiengine/widgets/CGUIEditBox.hpp b/src/guiengine/widgets/CGUIEditBox.hpp index c7360a826..f23dc7456 100644 --- a/src/guiengine/widgets/CGUIEditBox.hpp +++ b/src/guiengine/widgets/CGUIEditBox.hpp @@ -147,13 +147,8 @@ namespace GUIEngine void updateCursorDistance(); bool processKey(const SEvent& event); bool processMouse(const SEvent& event); -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) - bool processIMEEvent(const SEvent& event); -#endif -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) || defined(_IRR_COMPILE_WITH_X11_DEVICE_) //! calculates the input composition position core::position2di calculateICPos(); -#endif s32 getCursorPos(s32 x, s32 y); void updateGlyphLayouts(); @@ -192,7 +187,10 @@ namespace GUIEngine * windows */ std::u32string m_edit_text; std::vector m_glyph_layouts; + // Pre-edit surrogate chars + std::vector m_surrogate_chars; void correctCursor(s32& cursor_pos, bool left); + void updateSurrogatePairText(); };