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. */
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'
@ -168,20 +160,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
};
};

View File

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

View File

@ -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)
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);
// 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];
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;
wchar_t keyChars[10] = {0};
UINT scanCode = HIWORD(lParam);
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)
{
if (conversionResult >= 1)
{
for (int i = 0; i < conversionResult; i++)
{
event.KeyInput.Char = keyChars[i];
dev->postEventFromUser(event);
event.EventType = irr::EET_IMPUT_METHOD_EVENT;
event.InputMethodEvent.Event = irr::EIME_CHANGE_POS;
event.InputMethodEvent.Handle = hWnd;
if (dev)
}
}
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);
}

View File

@ -386,6 +386,8 @@ namespace irr
//! returns the win32 cursor control
CCursorControl* getWin32CursorControl();
HWND getHandle() const { return HWnd; }
private:
//! create the driver

View File

@ -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<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 // 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();
}
}
}

View File

@ -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<GlyphLayout> m_glyph_layouts;
// Pre-edit surrogate chars
std::vector<wchar_t> m_surrogate_chars;
void correctCursor(s32& cursor_pos, bool left);
void updateSurrogatePairText();
};