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:
Benau 2015-10-27 01:06:17 +08:00
parent af68319da7
commit 2096532d0e
11 changed files with 554 additions and 33 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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