Clean up input method code in windows with better unicode handling
This commit is contained in:
parent
c337976413
commit
7184119409
@ -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
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -386,6 +386,8 @@ namespace irr
|
||||
//! returns the win32 cursor control
|
||||
CCursorControl* getWin32CursorControl();
|
||||
|
||||
HWND getHandle() const { return HWnd; }
|
||||
|
||||
private:
|
||||
|
||||
//! create the driver
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user