CEGUI中文输入的完美解决
[ 2009-03-18 16:25:18 | 作者: Admin ]
字体大小: 大 | 中 | 小
CEGUI中实现中文输入是一个老话题了,网上的资料也很多,但是实现的都不是那么完美,其中最重要的问题就是输入法界面的跟随和输入状态时对按键的屏蔽。
先来说下如何把中文输入进入。 先添加一个中文注入的函数:
/////// 中文输入注入字符 (Added by Azure) static bool ChnInjectChar(CEGUI::utf32 code_point); ///////
函数的实现如下:
bool Win32AppHelper::ChnInjectChar(CEGUI::utf32 code_point) {
#ifndef UNICODE
static char s_tempChar[3] = \"\"; static wchar_t s_tempWchar[2] = L\"\"; static bool s_flag = false;
unsigned char uch = (unsigned char)code_point; if( uch >= 0xA1 ) {
if( !s_flag ) {
s_tempChar[0] = (char)uch; //第一个字节 s_flag = true; return true; }
else if( uch >= 0xA1 )
{
s_tempChar[1] = (char)uch; //第二个字节 s_flag = false;
MultiByteToWideChar( 0, 0, s_tempChar, 2, s_tempWchar, 1); //转成宽字节 s_tempWchar[1] = L'\\0';
CEGUI::utf32 code = (CEGUI::utf32)s_tempWchar[0]; return CEGUI::System::getSingleton().injectChar( code ); } else {
return CEGUI::System::getSingleton().injectChar(code_point); } } else {
s_flag = false;
return CEGUI::System::getSingleton().injectChar(code_point); } #else
return CEGUI::System::getSingleton().injectChar(code_point ); #endif }
此函数是我从网上抄来的一个,没有什么特别的,挺好用的。
然后在WndProc回调函数中添加: case WM_CHAR: // 不要这个
//CEGUI::System::getSingleton().injectChar((CEGUI::utf32)wParam);
// 改用自己的注入
ChnInjectChar((CEGUI::utf32)wParam); break;
这样中文就可以基本输入了,但是还有很多问题,原来不能BackSpace删除,和游标移动啊!
下面我们来添加控制按键的处理。
由于wParam不能直接传入CEGUI中,我们必须写一个虚拟按键到扫描码的翻译函数,我们添加下面一个函数。
/////// 虚拟按键转扫描码 (Added by Azure)
static UINT VirtualKeyToScanCode(WPARAM wParam, LPARAM lParam); 该函数的实现为:
UINT Win32AppHelper::VirtualKeyToScanCode(WPARAM wParam, LPARAM lParam) {
if(HIWORD(lParam) & 0x0F00) {
UINT scancode = MapVirtualKey(wParam, 0); return scancode | 0x80; } else {
return HIWORD(lParam) & 0x00FF; } }
同样的我们在WndProc消息回调中添加代码: case WM_KEYDOWN: {
//输入法跟随 IMEFollow(hWnd);
//输入法状态时,输入不传递到UI系统中去。 UINT vk = (UINT)ImmGetVirtualKey(hWnd); if(vk == wParam) break;
CEGUI::System::getSingleton().injectKeyDown((CEGUI::utf32)(VirtualKeyToScanCode(wParam, lParam))); } break;
case WM_KEYUP:
CEGUI::System::getSingleton().injectKeyUp((CEGUI::utf32)(VirtualKeyToScanCode(wParam, lParam))); break;
有两个陌生的函数,IMEFollow(hWnd) 和 ImmGetVirtualKey() 分别是为了输入法跟随,和过滤掉输入法处理过的按键,比较关键。
关于输入法跟随的函数体为:
/////// 获得输入框的坐标 (Added by Azure)
static bool getFocusedInputBoxCoord(POINT& point, float& height);
////// 输入法跟随 (Added by Azure) static bool IMEFollow(HWND hWnd); 实现为:
bool Win32AppHelper::getFocusedInputBoxCoord(POINT& point, float& height) {
//寻找到有输入焦点的EditBox的左上坐标
//遍历所有窗口
CEGUI::WindowManager::WindowIterator wit = CEGUI::WindowManager::getSingleton().getIterator(); while(!wit.isAtEnd()) {
const CEGUI::Window* widget = (*wit)->getActiveChild(); //如果是EditBox或者MultiLineEditBox if(widget) {
CEGUI::String windowType = widget->getType();
if(windowType == \"Vanilla/Editbox\") //根据具体的scheme来修改。 {
const CEGUI::UVector2& winPos = widget->getPosition(); height = widget->getPixelRect().getHeight();
CEGUI::Vector2 winPos1 = CEGUI::CoordConverter::windowToScreen(*widget, winPos);
point.x = winPos1.d_x; point.y = winPos1.d_y; return true; } } wit++; }
return false; }
bool Win32AppHelper::IMEFollow(HWND hWnd)
{
//判断输入法是否打开
if (!ImmIsIME(GetKeyboardLayout(0))) return false;
//获得输入框左上坐标 bool result; POINT point; float height;
result = getFocusedInputBoxCoord(point, height); if(!result) return false;
//获得客户区的坐标 RECT rect;
GetClientRect(hWnd, &rect); point.x+=rect.left; point.y+=rect.top;
//设置输入法位置
HIMC hImc = ImmGetContext(hWnd); if(hImc==NULL) return false; COMPOSITIONFORM form;
ImmGetCompositionWindow(hImc, &form); form.ptCurrentPos.x = point.x;
form.ptCurrentPos.y = point.y + height; ImmSetCompositionWindow(hImc, &form);
return true; }
这样一来一个完整的CEGUI输入法解决方案就完成了。