Make stk more-i18n friendly
Now supertuxkart can do: Windows (fully test in win8 and win7 built with vs2015, mingw not tested): Input characters using ime (CJK tested). Fix crashes when try to paste unicode text into editbox in stk. Linux: Different keymap can be used in stk now, ie typing russian,hebrew (not bidi-aware) is now possible. IME not supported. Mac: No work has been done, sorry mac fanboys/girls:( Though testing is welcome, esp when pasting words into stk. Limitation: No ime box is shown in fullscreen (Windows)Only the used language in current setting of non-Unicode programs can be typed with its supported IME.
This commit is contained in:
parent
af68319da7
commit
2096532d0e
@ -49,6 +49,14 @@ 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'
|
||||
@ -142,6 +150,20 @@ 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
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
@ -411,6 +433,20 @@ struct SEvent
|
||||
s32 UserData2;
|
||||
};
|
||||
|
||||
#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
|
||||
{
|
||||
@ -420,6 +456,9 @@ struct SEvent
|
||||
struct SJoystickEvent JoystickEvent;
|
||||
struct SLogEvent LogEvent;
|
||||
struct SUserEvent UserEvent;
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
struct SInputMethodEvent InputMethodEvent;
|
||||
#endif
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -26,11 +26,19 @@ public:
|
||||
}
|
||||
|
||||
//! Copies text to the clipboard
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
virtual void copyToClipboard(const wchar_t* text) const = 0;
|
||||
#else
|
||||
virtual void copyToClipboard(const c8* text) const = 0;
|
||||
#endif
|
||||
|
||||
//! Get text from the clipboard
|
||||
/** \return Returns 0 if no string is in there. */
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
virtual const wchar_t* getTextFromClipboard() const = 0;
|
||||
#else
|
||||
virtual const c8* getTextFromClipboard() const = 0;
|
||||
#endif
|
||||
|
||||
//! Get the processor speed in megahertz
|
||||
/** \param MHz The integer variable to store the speed in.
|
||||
|
@ -286,7 +286,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
||||
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
core::stringw s;
|
||||
#else
|
||||
core::stringc s;
|
||||
#endif
|
||||
s = Text.subString(realmbgn, realmend - realmbgn).c_str();
|
||||
Operator->copyToClipboard(s.c_str());
|
||||
}
|
||||
@ -299,7 +303,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
||||
|
||||
// copy
|
||||
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
core::stringw sc;
|
||||
#else
|
||||
core::stringc sc;
|
||||
#endif
|
||||
sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
|
||||
Operator->copyToClipboard(sc.c_str());
|
||||
|
||||
@ -329,7 +337,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
||||
|
||||
// add new character
|
||||
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
const wchar_t* p = Operator->getTextFromClipboard();
|
||||
#else
|
||||
const c8* p = Operator->getTextFromClipboard();
|
||||
#endif
|
||||
if (p)
|
||||
{
|
||||
if (MarkBegin == MarkEnd)
|
||||
|
@ -599,6 +599,18 @@ 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
|
||||
|
@ -14,6 +14,7 @@ extern bool GLContextDebugBit;
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <time.h>
|
||||
#include <locale.h>
|
||||
#include "IEventReceiver.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
@ -83,6 +84,7 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param)
|
||||
: CIrrDeviceStub(param),
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
display(0), visual(0), screennr(0), window(0), StdHints(0), SoftwareImage(0),
|
||||
XInputMethod(0), XInputContext(0),
|
||||
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
||||
glxWin(0),
|
||||
Context(0),
|
||||
@ -134,6 +136,10 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param)
|
||||
if (!VideoDriver)
|
||||
return;
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
createInputContext();
|
||||
#endif
|
||||
|
||||
createGUIAndScene();
|
||||
}
|
||||
|
||||
@ -168,6 +174,8 @@ CIrrDeviceLinux::~CIrrDeviceLinux()
|
||||
VideoDriver = NULL;
|
||||
}
|
||||
|
||||
destroyInputContext();
|
||||
|
||||
if (display)
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
||||
@ -1154,6 +1162,123 @@ void CIrrDeviceLinux::createDriver()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
bool CIrrDeviceLinux::createInputContext()
|
||||
{
|
||||
// One one side it would be nicer to let users do that - on the other hand
|
||||
// not setting the environment locale will not work when using i18n X11 functions.
|
||||
// So users would have to call it always or their input is broken badly.
|
||||
// We can restore immediately - so won't mess with anything in users apps.
|
||||
core::stringc oldLocale(setlocale(LC_CTYPE, NULL));
|
||||
setlocale(LC_CTYPE, ""); // use environment locale
|
||||
|
||||
if ( !XSupportsLocale() )
|
||||
{
|
||||
os::Printer::log("Locale not supported. Falling back to non-i18n input.", ELL_WARNING);
|
||||
setlocale(LC_CTYPE, oldLocale.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
XInputMethod = XOpenIM(display, NULL, NULL, NULL);
|
||||
if ( !XInputMethod )
|
||||
{
|
||||
setlocale(LC_CTYPE, oldLocale.c_str());
|
||||
os::Printer::log("XOpenIM failed to create an input method. Falling back to non-i18n input.", ELL_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
XIMStyles *im_supported_styles;
|
||||
XGetIMValues(XInputMethod, XNQueryInputStyle, &im_supported_styles, (char*)NULL);
|
||||
XIMStyle bestStyle = 0;
|
||||
// TODO: If we want to support languages like chinese or japanese as well we probably have to work with callbacks here.
|
||||
XIMStyle supportedStyle = XIMPreeditNone | XIMStatusNone;
|
||||
for (int i=0; i < im_supported_styles->count_styles; ++i)
|
||||
{
|
||||
XIMStyle style = im_supported_styles->supported_styles[i];
|
||||
if ((style & supportedStyle) == style) /* if we can handle it */
|
||||
{
|
||||
bestStyle = style;
|
||||
break;
|
||||
}
|
||||
}
|
||||
XFree(im_supported_styles);
|
||||
|
||||
if ( !bestStyle )
|
||||
{
|
||||
XDestroyIC(XInputContext);
|
||||
XInputContext = 0;
|
||||
|
||||
os::Printer::log("XInputMethod has no input style we can use. Falling back to non-i18n input.", ELL_WARNING);
|
||||
setlocale(LC_CTYPE, oldLocale.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
XInputContext = XCreateIC(XInputMethod,
|
||||
XNInputStyle, bestStyle,
|
||||
XNClientWindow, window,
|
||||
(char*)NULL);
|
||||
if (!XInputContext )
|
||||
{
|
||||
os::Printer::log("XInputContext failed to create an input context. Falling back to non-i18n input.", ELL_WARNING);
|
||||
setlocale(LC_CTYPE, oldLocale.c_str());
|
||||
return false;
|
||||
}
|
||||
XSetICFocus(XInputContext);
|
||||
setlocale(LC_CTYPE, oldLocale.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void CIrrDeviceLinux::destroyInputContext()
|
||||
{
|
||||
if ( XInputContext )
|
||||
{
|
||||
XUnsetICFocus(XInputContext);
|
||||
XDestroyIC(XInputContext);
|
||||
XInputContext = 0;
|
||||
}
|
||||
if ( XInputMethod )
|
||||
{
|
||||
XCloseIM(XInputMethod);
|
||||
XInputMethod = 0;
|
||||
}
|
||||
}
|
||||
|
||||
EKEY_CODE CIrrDeviceLinux::getKeyCode(XEvent &event)
|
||||
{
|
||||
EKEY_CODE keyCode = (EKEY_CODE)0;
|
||||
|
||||
SKeyMap mp;
|
||||
mp.X11Key = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, 0);
|
||||
// mp.X11Key = XKeycodeToKeysym(XDisplay, event.xkey.keycode, 0); // deprecated, if we still find platforms which need that we have to use some define
|
||||
const s32 idx = KeyMap.binary_search(mp);
|
||||
if (idx != -1)
|
||||
{
|
||||
keyCode = (EKEY_CODE)KeyMap[idx].Win32Key;
|
||||
}
|
||||
if (keyCode == 0)
|
||||
{
|
||||
// Any value is better than none, that allows at least using the keys.
|
||||
// Worst case is that some keys will be identical, still better than _all_
|
||||
// unknown keys being identical.
|
||||
if ( !mp.X11Key )
|
||||
{
|
||||
keyCode = (EKEY_CODE)event.xkey.keycode;
|
||||
os::Printer::log("No such X11Key, using event keycode", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);
|
||||
}
|
||||
else if (idx == -1)
|
||||
{
|
||||
keyCode = (EKEY_CODE)mp.X11Key;
|
||||
os::Printer::log("EKEY_CODE not found, using orig. X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyCode = (EKEY_CODE)mp.X11Key;
|
||||
os::Printer::log("EKEY_CODE is 0, using orig. X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);
|
||||
}
|
||||
}
|
||||
return keyCode;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! runs the device. Returns false if device wants to be deleted
|
||||
bool CIrrDeviceLinux::run()
|
||||
@ -1330,52 +1455,60 @@ bool CIrrDeviceLinux::run()
|
||||
(next_event.xkey.keycode == event.xkey.keycode) &&
|
||||
(next_event.xkey.time - event.xkey.time) < 2) // usually same time, but on some systems a difference of 1 is possible
|
||||
{
|
||||
/* Ignore the key release event */
|
||||
// Ignore the key release event
|
||||
break;
|
||||
}
|
||||
}
|
||||
// fall-through in case the release should be handled
|
||||
|
||||
irrevent.EventType = irr::EET_KEY_INPUT_EVENT;
|
||||
irrevent.KeyInput.PressedDown = false;
|
||||
irrevent.KeyInput.Char = 0; // on release that's undefined
|
||||
irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;
|
||||
irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;
|
||||
irrevent.KeyInput.Key = getKeyCode(event);
|
||||
|
||||
postEventFromUser(irrevent);
|
||||
break;
|
||||
|
||||
case KeyPress:
|
||||
{
|
||||
SKeyMap mp;
|
||||
char buf[8]={0};
|
||||
XLookupString(&event.xkey, buf, sizeof(buf), &mp.X11Key, NULL);
|
||||
|
||||
irrevent.EventType = irr::EET_KEY_INPUT_EVENT;
|
||||
irrevent.KeyInput.PressedDown = (event.type == KeyPress);
|
||||
// mbtowc(&irrevent.KeyInput.Char, buf, sizeof(buf));
|
||||
irrevent.KeyInput.Char = ((wchar_t*)(buf))[0];
|
||||
irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;
|
||||
irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;
|
||||
|
||||
event.xkey.state &= ~(ControlMask|ShiftMask); // ignore shift-ctrl states for figuring out the key
|
||||
XLookupString(&event.xkey, buf, sizeof(buf), &mp.X11Key, NULL);
|
||||
const s32 idx = KeyMap.binary_search(mp);
|
||||
if (idx != -1)
|
||||
if ( XInputContext )
|
||||
{
|
||||
irrevent.KeyInput.Key = (EKEY_CODE)KeyMap[idx].Win32Key;
|
||||
}
|
||||
else
|
||||
{
|
||||
irrevent.KeyInput.Key = (EKEY_CODE)0;
|
||||
}
|
||||
if (irrevent.KeyInput.Key == 0)
|
||||
{
|
||||
// 1:1 mapping to windows-keys would require testing for keyboard type (us, ger, ...)
|
||||
// So unless we do that we will have some unknown keys here.
|
||||
if (idx == -1)
|
||||
wchar_t buf[8]={0};
|
||||
Status status;
|
||||
int strLen = XwcLookupString(XInputContext, &event.xkey, buf, sizeof(buf), &mp.X11Key, &status);
|
||||
if ( status == XBufferOverflow )
|
||||
{
|
||||
os::Printer::log("Could not find EKEY_CODE, using orig. X11 keycode instead", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);
|
||||
os::Printer::log("XwcLookupString needs a larger buffer", ELL_INFORMATION);
|
||||
}
|
||||
if ( strLen > 0 && (status == XLookupChars || status == XLookupBoth) )
|
||||
{
|
||||
if ( strLen > 1 )
|
||||
os::Printer::log("Additional returned characters dropped", ELL_INFORMATION);
|
||||
irrevent.KeyInput.Char = buf[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
os::Printer::log("EKEY_CODE is 0, using orig. X11 keycode instead", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);
|
||||
irrevent.KeyInput.Char = 0;
|
||||
}
|
||||
// Any value is better than none, that allows at least using the keys.
|
||||
// Worst case is that some keys will be identical, still better than _all_
|
||||
// unknown keys being identical.
|
||||
irrevent.KeyInput.Key = (EKEY_CODE)event.xkey.keycode;
|
||||
}
|
||||
else // Old version without InputContext. Does not support i18n, but good to have as fallback.
|
||||
{
|
||||
union
|
||||
{
|
||||
char buf[8];
|
||||
wchar_t wbuf[2];
|
||||
} tmp = {0};
|
||||
XLookupString(&event.xkey, tmp.buf, sizeof(tmp.buf), &mp.X11Key, NULL);
|
||||
irrevent.KeyInput.Char = tmp.wbuf[0];
|
||||
}
|
||||
|
||||
irrevent.EventType = irr::EET_KEY_INPUT_EVENT;
|
||||
irrevent.KeyInput.PressedDown = true;
|
||||
irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;
|
||||
irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;
|
||||
irrevent.KeyInput.Key = getKeyCode(event);
|
||||
|
||||
postEventFromUser(irrevent);
|
||||
}
|
||||
|
@ -153,6 +153,12 @@ namespace irr
|
||||
bool restoreResolution();
|
||||
bool changeResolution();
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
bool createInputContext();
|
||||
void destroyInputContext();
|
||||
EKEY_CODE getKeyCode(XEvent &event);
|
||||
#endif
|
||||
|
||||
//! Implementation of the linux cursor control
|
||||
class CCursorControl : public gui::ICursorControl
|
||||
{
|
||||
@ -390,6 +396,8 @@ namespace irr
|
||||
XSetWindowAttributes attributes;
|
||||
XSizeHints* StdHints;
|
||||
XImage* SoftwareImage;
|
||||
XIM XInputMethod;
|
||||
XIC XInputContext;
|
||||
mutable core::stringc Clipboard;
|
||||
#ifdef _IRR_LINUX_X11_VIDMODE_
|
||||
XF86VidModeModeInfo oldVideoMode;
|
||||
|
@ -34,6 +34,9 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <imm.h>
|
||||
#pragma comment(lib, "imm32.lib")
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
@ -692,6 +695,28 @@ irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd)
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@ -802,6 +827,16 @@ 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;
|
||||
}
|
||||
|
||||
@ -818,6 +853,30 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
case WM_ERASEBKGND:
|
||||
return 0;
|
||||
|
||||
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::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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_SYSKEYUP:
|
||||
case WM_KEYDOWN:
|
||||
@ -879,6 +938,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
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);
|
||||
|
||||
@ -932,6 +998,12 @@ 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:
|
||||
@ -1115,6 +1187,9 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@ -1870,6 +1945,8 @@ void CIrrDeviceWin32::handleSystemMessages()
|
||||
// No message translation because we don't use WM_CHAR and it would conflict with our
|
||||
// deadkey handling.
|
||||
|
||||
TranslateMessage(&msg);
|
||||
|
||||
if (ExternalWindow && msg.hwnd == HWnd)
|
||||
WndProc(HWnd, msg.message, msg.wParam, msg.lParam);
|
||||
else
|
||||
|
@ -52,6 +52,37 @@ const core::stringc& COSOperator::getOperatingSystemVersion() const
|
||||
|
||||
|
||||
//! copies text to the clipboard
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
void COSOperator::copyToClipboard(const wchar_t* text) const
|
||||
{
|
||||
if (wcslen(text)==0)
|
||||
return;
|
||||
|
||||
// Windows version
|
||||
#if defined(_IRR_XBOX_PLATFORM_)
|
||||
#elif defined(_IRR_WINDOWS_API_)
|
||||
if (!OpenClipboard(NULL) || text == 0)
|
||||
return;
|
||||
|
||||
EmptyClipboard();
|
||||
|
||||
HGLOBAL clipbuffer;
|
||||
wchar_t * buffer;
|
||||
|
||||
clipbuffer = GlobalAlloc(GMEM_DDESHARE, wcslen(text)*sizeof(wchar_t) + sizeof(wchar_t));
|
||||
buffer = (wchar_t*)GlobalLock(clipbuffer);
|
||||
|
||||
wcscpy(buffer, text);
|
||||
|
||||
GlobalUnlock(clipbuffer);
|
||||
SetClipboardData(CF_UNICODETEXT, clipbuffer); //Windwos converts between CF_UNICODETEXT and CF_TEXT automatically.
|
||||
CloseClipboard();
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void COSOperator::copyToClipboard(const c8* text) const
|
||||
{
|
||||
if (strlen(text)==0)
|
||||
@ -89,10 +120,34 @@ void COSOperator::copyToClipboard(const c8* text) const
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//! gets text from the clipboard
|
||||
//! \return Returns 0 if no string is in there.
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
const wchar_t* COSOperator::getTextFromClipboard() const
|
||||
{
|
||||
#if defined(_IRR_XBOX_PLATFORM_)
|
||||
return 0;
|
||||
#elif defined(_IRR_WINDOWS_API_)
|
||||
if (!OpenClipboard(NULL))
|
||||
return 0;
|
||||
|
||||
wchar_t * buffer = 0;
|
||||
|
||||
HANDLE hData = GetClipboardData( CF_UNICODETEXT ); //Windwos converts between CF_UNICODETEXT and CF_TEXT automatically.
|
||||
buffer = (wchar_t*)GlobalLock( hData );
|
||||
GlobalUnlock( hData );
|
||||
CloseClipboard();
|
||||
return buffer;
|
||||
|
||||
#else
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
const c8* COSOperator::getTextFromClipboard() const
|
||||
{
|
||||
#if defined(_IRR_XBOX_PLATFORM_)
|
||||
@ -122,6 +177,7 @@ const c8* COSOperator::getTextFromClipboard() const
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool COSOperator::getProcessorSpeedMHz(u32* MHz) const
|
||||
|
@ -27,11 +27,19 @@ public:
|
||||
virtual const core::stringc& getOperatingSystemVersion() const;
|
||||
|
||||
//! copies text to the clipboard
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
virtual void copyToClipboard(const wchar_t* text) const;
|
||||
#else
|
||||
virtual void copyToClipboard(const c8* text) const;
|
||||
#endif
|
||||
|
||||
//! gets text from the clipboard
|
||||
//! \return Returns 0 if no string is in there.
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
virtual const wchar_t* getTextFromClipboard() const;
|
||||
#else
|
||||
virtual const c8* getTextFromClipboard() const;
|
||||
#endif
|
||||
|
||||
//! gets the processor speed in megahertz
|
||||
//! \param Mhz:
|
||||
|
@ -26,6 +26,15 @@
|
||||
numerical
|
||||
*/
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
#define UTF16_IS_SURROGATE_LO(c) (((c) & 0xFC00) == 0xDC00)
|
||||
#define UTF16_IS_SURROGATE_HI(c) (((c) & 0xFC00) == 0xD800)
|
||||
|
||||
namespace irr
|
||||
{
|
||||
void updateICPos(void* hWnd, s32 x, s32 y, s32 height);
|
||||
}
|
||||
#endif
|
||||
|
||||
StkTime::TimeType getTime()
|
||||
{
|
||||
@ -229,6 +238,12 @@ 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;
|
||||
@ -282,7 +297,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
|
||||
core::stringw s;
|
||||
s = Text.subString(realmbgn, realmend - realmbgn).c_str();
|
||||
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
Operator->copyToClipboard(s.c_str());
|
||||
#else
|
||||
Operator->copyToClipboard(StringUtils::wide_to_utf8(s.c_str()).c_str());
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case KEY_KEY_X:
|
||||
@ -295,7 +314,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
// copy
|
||||
core::stringw sc;
|
||||
sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
|
||||
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
Operator->copyToClipboard(sc.c_str());
|
||||
#else
|
||||
Operator->copyToClipboard(StringUtils::wide_to_utf8(sc.c_str()).c_str());
|
||||
#endif
|
||||
|
||||
if (isEnabled())
|
||||
{
|
||||
@ -324,20 +347,32 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
||||
|
||||
// add new character
|
||||
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
const wchar_t* p = Operator->getTextFromClipboard();
|
||||
#else
|
||||
const c8* p = Operator->getTextFromClipboard();
|
||||
#endif
|
||||
if (p)
|
||||
{
|
||||
if (MarkBegin == MarkEnd)
|
||||
{
|
||||
// insert text
|
||||
core::stringw s = Text.subString(0, CursorPos);
|
||||
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
s.append(StringUtils::utf8_to_wide(p));
|
||||
#else
|
||||
s.append(p);
|
||||
#endif
|
||||
s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
|
||||
|
||||
if (!Max || s.size()<=Max) // thx to Fish FH for fix
|
||||
{
|
||||
Text = s;
|
||||
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
s = StringUtils::utf8_to_wide(p);
|
||||
#else
|
||||
s = p;
|
||||
#endif
|
||||
CursorPos += s.size();
|
||||
}
|
||||
}
|
||||
@ -346,13 +381,21 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
// replace text
|
||||
|
||||
core::stringw s = Text.subString(0, realmbgn);
|
||||
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
s.append(StringUtils::utf8_to_wide(p));
|
||||
#else
|
||||
s.append(p);
|
||||
#endif
|
||||
s.append( Text.subString(realmend, Text.size()-realmend) );
|
||||
|
||||
if (!Max || s.size()<=Max) // thx to Fish FH for fix
|
||||
{
|
||||
Text = s;
|
||||
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
s = StringUtils::utf8_to_wide(p);
|
||||
#else
|
||||
s = p;
|
||||
#endif
|
||||
CursorPos = realmbgn + s.size();
|
||||
}
|
||||
}
|
||||
@ -734,10 +777,102 @@ bool CGUIEditBox::processKey(const SEvent& event)
|
||||
|
||||
calculateScrollPos();
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
switch(event.KeyInput.Key)
|
||||
{
|
||||
// If cursor points the surrogate low, send KEY_LEFT event.
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
if (MultiLine || (WordWrap && BrokenText.size() > 1) )
|
||||
{
|
||||
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
|
||||
{
|
||||
SEvent leftEvent;
|
||||
leftEvent = event;
|
||||
leftEvent.KeyInput.Key = KEY_LEFT;
|
||||
Environment->postEventFromUser(leftEvent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
// If cursor points the surrogate low, send a same event.
|
||||
case KEY_LEFT:
|
||||
case KEY_RIGHT:
|
||||
case KEY_DELETE:
|
||||
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
|
||||
Environment->postEventFromUser(event);
|
||||
break;
|
||||
// If cursor points front of the surrogate high, send a same event.
|
||||
case KEY_BACK:
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
if (UTF16_IS_SURROGATE_HI(Text[CursorPos-1]))
|
||||
Environment->postEventFromUser(event);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#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:
|
||||
{
|
||||
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->getDimension(L"|").Height);
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! calculate the position of input composition window
|
||||
core::position2di CGUIEditBox::calculateICPos()
|
||||
{
|
||||
core::position2di pos;
|
||||
IGUIFont* font = OverrideFont;
|
||||
IGUISkin* skin = Environment->getSkin();
|
||||
if (!OverrideFont)
|
||||
font = skin->getFont();
|
||||
|
||||
//drop the text that clipping on the right side
|
||||
if (WordWrap | MultiLine)
|
||||
{
|
||||
// todo : It looks like a heavy drinker. Strange!!
|
||||
pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, BrokenTextPositions[getLineFromPos(CursorPos)] + BrokenText[getLineFromPos(CursorPos)].size() - CursorPos).c_str()).Width;
|
||||
pos.Y = CurrentTextRect.UpperLeftCorner.Y + font->getDimension(L"|").Height + (Border ? 3 : 0) - ((MultiLine | WordWrap) ? 3 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, Text.size() - CursorPos).c_str()).Width;
|
||||
pos.Y = AbsoluteRect.getCenter().Y + (Border ? 3 : 0); //bug? The text is always drawn in the height of the center. SetTextAlignment() doesn't influence.
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
//! draws the element and its children
|
||||
void CGUIEditBox::draw()
|
||||
{
|
||||
@ -1028,6 +1163,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
|
||||
if (Environment->hasFocus(this) && !m_rtl)
|
||||
{
|
||||
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
--CursorPos;
|
||||
}
|
||||
#endif
|
||||
if (MouseMarking)
|
||||
{
|
||||
setTextMarkers( MarkBegin, CursorPos );
|
||||
@ -1042,6 +1184,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
|
||||
if (MouseMarking)
|
||||
{
|
||||
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
--CursorPos;
|
||||
}
|
||||
#endif
|
||||
setTextMarkers( MarkBegin, CursorPos );
|
||||
calculateScrollPos();
|
||||
return true;
|
||||
@ -1054,6 +1203,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
|
||||
BlinkStartTime = getTime();
|
||||
MouseMarking = true;
|
||||
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
--CursorPos;
|
||||
}
|
||||
#endif
|
||||
setTextMarkers(CursorPos, CursorPos );
|
||||
calculateScrollPos();
|
||||
return true;
|
||||
@ -1070,6 +1226,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
|
||||
// move cursor
|
||||
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
--CursorPos;
|
||||
}
|
||||
#endif
|
||||
s32 newMarkBegin = MarkBegin;
|
||||
if (!MouseMarking)
|
||||
newMarkBegin = CursorPos;
|
||||
|
@ -137,6 +137,11 @@ using namespace gui;
|
||||
|
||||
bool processKey(const SEvent& event);
|
||||
bool processMouse(const SEvent& event);
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
bool processIMEEvent(const SEvent& event);
|
||||
//! calculates the input composition position
|
||||
core::position2di calculateICPos();
|
||||
#endif
|
||||
s32 getCursorPos(s32 x, s32 y);
|
||||
|
||||
bool MouseMarking;
|
||||
|
Loading…
x
Reference in New Issue
Block a user