Clean up input method code in windows with better unicode handling

This commit is contained in:
Benau 2019-06-26 13:14:52 +08:00
parent c337976413
commit 7184119409
6 changed files with 123 additions and 194 deletions

View File

@ -61,14 +61,6 @@ namespace irr
user receiver then no text will be sent to the console. */ user receiver then no text will be sent to the console. */
EET_LOG_TEXT_EVENT, 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. //! A user event with user data.
/** This is not used by Irrlicht and can be used to send user /** This is not used by Irrlicht and can be used to send user
specific data though the system. The Irrlicht 'window handle' specific data though the system. The Irrlicht 'window handle'
@ -167,20 +159,6 @@ namespace irr
EMBSM_FORCE_32_BIT = 0x7fffffff 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 //! Enumeration for all touch input events
enum ETOUCH_INPUT_EVENT enum ETOUCH_INPUT_EVENT
@ -590,21 +568,6 @@ struct SEvent
EAPPLICATION_EVENT_TYPE EventType; 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; EEVENT_TYPE EventType;
union union
{ {
@ -620,9 +583,6 @@ struct SEvent
struct SUserEvent UserEvent; struct SUserEvent UserEvent;
struct SSystemEvent SystemEvent; struct SSystemEvent SystemEvent;
struct SApplicationEvent ApplicationEvent; struct SApplicationEvent ApplicationEvent;
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
struct SInputMethodEvent InputMethodEvent;
#endif
}; };
}; };

View File

@ -573,18 +573,6 @@ bool CGUIEnvironment::postEventFromUser(const SEvent& event)
} }
break; 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: default:
break; break;
} // end switch } // end switch

View File

@ -12,6 +12,7 @@
#include "os.h" #include "os.h"
#include "utils/string_utils.hpp" #include "utils/string_utils.hpp"
#include "utils/utf8.h"
#include "CTimer.h" #include "CTimer.h"
#include "irrString.h" #include "irrString.h"
@ -951,29 +952,6 @@ irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd)
return 0; 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) LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ {
#ifndef WM_MOUSEWHEEL #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; return 0;
} }
@ -1112,25 +1081,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_IME_CHAR: 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); dev = getDeviceFromHWnd(hWnd);
if (dev) if (!dev)
dev->postEventFromUser(event); 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; 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.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0);
event.KeyInput.Control = ((allKeys[VK_CONTROL] & 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 wchar_t keyChars[10] = {0};
// Using ToUnicode instead would be shorter, but would to my knowledge not run on 95 and 98.
WORD keyChars[2];
UINT scanCode = HIWORD(lParam); UINT scanCode = HIWORD(lParam);
int conversionResult = ToAsciiEx(wParam,scanCode,allKeys,keyChars,0,KEYBOARD_INPUT_HKL); int conversionResult = ToUnicodeEx((UINT)wParam,scanCode,allKeys,keyChars,_countof(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;
// allow composing characters like '@' with Alt Gr on non-US keyboards // allow composing characters like '@' with Alt Gr on non-US keyboards
if ((allKeys[VK_MENU] & 0x80) != 0) if ((allKeys[VK_MENU] & 0x80) != 0)
event.KeyInput.Control = 0; event.KeyInput.Control = 0;
dev = getDeviceFromHWnd(hWnd); dev = getDeviceFromHWnd(hWnd);
if (dev) if (dev)
dev->postEventFromUser(event); {
if (conversionResult >= 1)
event.EventType = irr::EET_IMPUT_METHOD_EVENT; {
event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; for (int i = 0; i < conversionResult; i++)
event.InputMethodEvent.Handle = hWnd; {
event.KeyInput.Char = keyChars[i];
if (dev) dev->postEventFromUser(event);
dev->postEventFromUser(event); }
}
else
dev->postEventFromUser(event);
}
if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP) if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
return DefWindowProc(hWnd, message, wParam, lParam); return DefWindowProc(hWnd, message, wParam, lParam);
@ -1255,12 +1203,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
dev->switchToFullScreen(); 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; break;
case WM_USER: case WM_USER:
@ -1445,8 +1387,6 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params)
// inform driver about the window size etc. // inform driver about the window size etc.
resizeIfNecessary(); resizeIfNecessary();
// for reset the position of composition window
updateICPos(HWnd, 0, 0, 0);
} }

View File

@ -307,7 +307,7 @@ namespace irr
//! Return a system-specific size which is supported for cursors. Larger icons will fail, smaller icons might work. //! Return a system-specific size which is supported for cursors. Larger icons will fail, smaller icons might work.
virtual core::dimension2di getSupportedIconSize() const; virtual core::dimension2di getSupportedIconSize() const;
void update(); void update();
private: private:
@ -386,6 +386,8 @@ namespace irr
//! returns the win32 cursor control //! returns the win32 cursor control
CCursorControl* getWin32CursorControl(); CCursorControl* getWin32CursorControl();
HWND getHandle() const { return HWnd; }
private: private:
//! create the driver //! create the driver

View File

@ -20,6 +20,7 @@
#include "guiengine/screen_keyboard.hpp" #include "guiengine/screen_keyboard.hpp"
#include "utils/string_utils.hpp" #include "utils/string_utils.hpp"
#include "utils/time.hpp" #include "utils/time.hpp"
#include "utils/utf8.h"
#include "../../../lib/irrlicht/include/IrrCompileConfig.h" #include "../../../lib/irrlicht/include/IrrCompileConfig.h"
#include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h" #include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h"
@ -31,6 +32,10 @@
#include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.h" #include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.h"
#endif #endif
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
#include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.h"
#endif
/* /*
todo: todo:
optional scrollbars optional scrollbars
@ -172,13 +177,6 @@ _raqm_get_grapheme_break (hb_codepoint_t ch,
}; };
#endif #endif
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
namespace irr
{
void updateICPos(void* hWnd, s32 x, s32 y, s32 height);
}
#endif
//! constructor //! constructor
CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border, CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,
IGUIEnvironment* environment, IGUIElement* parent, s32 id, IGUIEnvironment* environment, IGUIElement* parent, s32 id,
@ -396,12 +394,6 @@ bool CGUIEditBox::OnEvent(const SEvent& event)
} }
} }
break; 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: case EET_KEY_INPUT_EVENT:
if (processKey(event)) if (processKey(event))
return true; return true;
@ -852,7 +844,6 @@ bool CGUIEditBox::processKey(const SEvent& event)
if (text_changed) if (text_changed)
{ {
updateGlyphLayouts(); updateGlyphLayouts();
sendGuiEvent(EGET_EDITBOX_CHANGED);
} }
// Set new text markers // 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 //! calculate the position of input composition window
core::position2di CGUIEditBox::calculateICPos() core::position2di CGUIEditBox::calculateICPos()
{ {
@ -914,7 +873,6 @@ core::position2di CGUIEditBox::calculateICPos()
} }
#endif
//! draws the element and its children //! draws the element and its children
void CGUIEditBox::draw() void CGUIEditBox::draw()
{ {
@ -922,6 +880,7 @@ void CGUIEditBox::draw()
if (!IsVisible) if (!IsVisible)
return; return;
updateSurrogatePairText();
GUIEngine::ScreenKeyboard* screen_kbd = GUIEngine::ScreenKeyboard::getCurrent(); GUIEngine::ScreenKeyboard* screen_kbd = GUIEngine::ScreenKeyboard::getCurrent();
bool has_screen_kbd = (screen_kbd && screen_kbd->getEditBox() == this); 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; x += m_scroll_pos;
if (x < 0) if (x < 0)
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()); font->getInverseShaping(), font->getScale());
} }
@ -1277,6 +1236,18 @@ void CGUIEditBox::inputChar(char32_t c)
if (c < 32) if (c < 32)
return; 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) if ((u32)getTextCount() < m_max_chars || m_max_chars == 0)
{ {
std::u32string sub_str; std::u32string sub_str;
@ -1306,7 +1277,6 @@ void CGUIEditBox::inputChar(char32_t c)
m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200; m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
updateGlyphLayouts(); updateGlyphLayouts();
setTextMarkers(0, 0); setTextMarkers(0, 0);
sendGuiEvent(EGET_EDITBOX_CHANGED);
calculateScrollPos(); calculateScrollPos();
} }
} }
@ -1368,6 +1338,7 @@ void CGUIEditBox::calculateScrollPos()
m_scroll_pos = 0; m_scroll_pos = 0;
// todo: adjust scrollbar // todo: adjust scrollbar
core::position2di pos = calculateICPos();
#if defined(_IRR_COMPILE_WITH_X11_DEVICE_) #if defined(_IRR_COMPILE_WITH_X11_DEVICE_)
if (irr_driver->getDevice()->getType() == irr::EIDT_X11) if (irr_driver->getDevice()->getType() == irr::EIDT_X11)
{ {
@ -1375,10 +1346,29 @@ void CGUIEditBox::calculateScrollPos()
irr_driver->getDevice()); irr_driver->getDevice());
if (dl) 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<CIrrDeviceWin32*>(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
#endif // SERVER_ONLY #endif // SERVER_ONLY
} }
@ -1436,7 +1426,7 @@ void CGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWr
out->addBool ("AutoScroll", AutoScroll); out->addBool ("AutoScroll", AutoScroll);
out->addBool ("PasswordBox", PasswordBox); out->addBool ("PasswordBox", PasswordBox);
core::stringw ch = L" "; core::stringw ch = L" ";
ch[0] = PasswordChar; ch[0] = (wchar_t)PasswordChar;
out->addString("PasswordChar", ch.c_str()); out->addString("PasswordChar", ch.c_str());
out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
@ -1535,3 +1525,54 @@ void CGUIEditBox::updateGlyphLayouts()
Text = StringUtils::utf32ToWide(m_edit_text); Text = StringUtils::utf32ToWide(m_edit_text);
#endif #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();
}
}
}

View File

@ -147,13 +147,8 @@ namespace GUIEngine
void updateCursorDistance(); void updateCursorDistance();
bool processKey(const SEvent& event); bool processKey(const SEvent& event);
bool processMouse(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 //! calculates the input composition position
core::position2di calculateICPos(); core::position2di calculateICPos();
#endif
s32 getCursorPos(s32 x, s32 y); s32 getCursorPos(s32 x, s32 y);
void updateGlyphLayouts(); void updateGlyphLayouts();
@ -192,7 +187,10 @@ namespace GUIEngine
* windows */ * windows */
std::u32string m_edit_text; std::u32string m_edit_text;
std::vector<GlyphLayout> m_glyph_layouts; std::vector<GlyphLayout> m_glyph_layouts;
// Pre-edit surrogate chars
std::vector<wchar_t> m_surrogate_chars;
void correctCursor(s32& cursor_pos, bool left); void correctCursor(s32& cursor_pos, bool left);
void updateSurrogatePairText();
}; };