不過去年網友zevoid在留言中提到
可以使用 SetWindowLong 把Message Handler取代掉這個點子
似乎可以讓修改的幅度更小,程式更精簡
讓我不禁燃起來修改的欲望
只是去年初到年中在忙畢業的事,年底工作又忙,這件事就這樣放置著
最近事情較少,便正式開始執行
找到zevoid網友的blog http://www.wretch.cc/blog/zevoid/651189
重新研究後,在此再發一篇Ogre中文輸入 Final改
讓大家看看是不是更好用
首先最新的處理辦法,就只需要改一個地方:FrameListener的物件
而且不用更動ExampleFrameListener,只要繼承後再處理就行
這樣聽起來就簡單很多,實作如下:
新的FrameListener命名為ImeFrameListener
除了引用並繼承ExampleFrameListener外,當然要加入imm.h
#include <ExampleFrameListener.h> #include <imm.h> using namespace Ogre; class ImeFrameListener : public ExampleFrameListener { public: ImeFrameListener( RenderWindow* win, Camera* cam, bool bufferedKeys = false, bool bufferedMouse = false, bool bufferedJoy = false ) };在新的FrameListener裡加入Window Procedure函式來準備取代原先Ogre的
同時,此Window Procedure要宣告為Static才行
這邊的Procedure,我們命名為IMEProc
static LRESULT CALLBACK IMEProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)另外還需的是,仍然是
GetIMECompResultString(const Ogre::UTFString& tempString)
與
GetIMECompString(const Ogre::UTFString& tempString)
同時,在兩者前加入virtual後,再加入空的內容
因為這兩者只需在實際使用CEGUI或QuickGUI等GUI時再決定輸入函式
virtual void GetIMECompResultString(const Ogre::String& tempString){}; virtual void GetIMECompString(const Ogre::UTFString& tempString){};接下來,實作的部分
首先在ImeFrameListener的建構式裡取得目前視窗的Handle,也就是HWND
這邊使用RenderWindow::getCustomAttrubite(const String &name, void* pData)
取得HWND後[1],我們把目前的物件放到Window的UserData中
使用SetWindowLongPtr(HWND, INT, LONG_PTR);
最後使用SetWindowLong(HWND, INT, LONG)
把原本的WindowProcedure替換成IMEProc
HWND hwnd = NULL; win->getCustomAttribute("WINDOW", &hwnd);//取得此視窗的Handler SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this);//將此物件指標存入視窗使用者資料 SetWindowLong(hwnd, GWL_WNDPROC, (LONG)(ImeFrameListener::IMEProc));//設定新的訊息處理
接著就是IMEProc的實作
在switch(message)之前我們要取得ImeFrameListner的物件指標
使用GetWindowLongPtr( HWND, INT )來取得
接下來WM_IME_COMPOSITION的部分就跟之前的一模一樣
只是把ExampleCFrameListener::getSingletonPtr()改成取得的物件指標就好
//取得目前的物件指標 ImeFrameListener* ifListener = (ImeFrameListener*)GetWindowLongPtr(hwnd, GWLP_USERDATA); //使用UTF字串物件來取得全形字 UTFString tempString; tempString.clear(); switch (message) { //自己處理的IME轉換 case WM_IME_COMPOSITION: HIMC hImc = ImmGetContext(hwnd); //正式取得轉換的字串 if (lParam & GCS_RESULTSTR) { DWORD dwBufLen; wchar_t* tCharPtr; char* aCharPtr; dwBufLen = ImmGetCompositionString( hImc, GCS_RESULTSTR, 0, 0 ); if(dwBufLen > 0) { tCharPtr = new wchar_t[dwBufLen]; aCharPtr = new char[dwBufLen]; memset( tCharPtr, 0, sizeof(wchar_t) * dwBufLen ); ImmGetCompositionString( hImc, GCS_RESULTSTR, aCharPtr, dwBufLen ); DWORD slength = MultiByteToWideChar(CP_ACP, 0, aCharPtr, dwBufLen, 0, 0 ); MultiByteToWideChar(CP_ACP, 0, aCharPtr, dwBufLen, tCharPtr, slength); tempString = tCharPtr; ifListener->GetIMECompResultString(tempString); } }// if(lParam... //取得轉換中的字串 else if (lParam & GCS_COMPSTR) { DWORD dwBufLen; wchar_t* tCharPtr; dwBufLen = ImmGetCompositionStringW( hImc, GCS_COMPSTR, 0, 0 ); if(dwBufLen > 0) { tCharPtr = new wchar_t[dwBufLen]; memset( tCharPtr, 0, sizeof(wchar_t) * dwBufLen ); ImmGetCompositionStringW( hImc, GCS_COMPSTR, tCharPtr, dwBufLen); tempString = tCharPtr; ifListener->GetIMECompString(tempString); } else { ifListener->GetIMECompString(""); } }// if(lParam... ImmReleaseContext(hwnd, hImc); break; }
這樣,大致就完成了
編譯時,不要忘了再加入imm32的函式庫就成了
哇,真是清爽多了
以上任意取得HWND與替換WndProc除了可以使用IME輸入外
其實像替換掉視窗標題啦,換圖示之類的都辦得到了
應用十分廣,就看大家怎麼用了
最後附上下載完整的ImeFrameListener.h
還有以CEGUI為範本實作輸入法的程式碼OgreMain.cpp
本CEGUI使用最新的0.7.6
沒有留言:
張貼留言