输入法编程之 光标跟随

    技术2022-05-11  73

    光标跟随是输入法系统最常见的特性。要实现这一特性,需要获取IME支持程序(最常见如word,notepad等)中插入符号caret的坐标位置。在Windows的IME环境中,可以通过使用IME核心数据结构INPUTCONTEXT的cfCompForm成员来获取IME宿主程序中文本光标位置。cfCompForm具有如下结构: typedef tagCANDIDATEFORM { //列表窗口信息//由IMC_GETCANDIDATEPOS和IMC_SETCANDIDATEPOS消息处理DWORD dwIndex; //列表窗口序号DWORD dwStyle; //属性://=CFS_CANDIDATEPOS 指定显示位置//=CFS_EXCLUDE 不可显示//=CFS_DEFAULT 根据需要显示POINT ptCurrentPos; //坐标位置REC rcArea; //不可显示区} CANDIDATEFORM; 其中ptCurrentPos就是我们需要的光标位置,不过因为这是客户区的坐标,需要转化为屏幕坐标才能使用。要使得输入法上下文结构INPUTCONTEXT中已经填入了正确的坐标位置,需要实现WM_IME_NOTIFY消息响应事件。在WM_IME_NOTIFY消息的子消息IMN_SETCOMPOSITIONWINDOW(设置编码窗口消息)被触发时,系统会返回正确的坐标位置。WM_IME_NOTIFY消息响应函数类似如下形式: /* * IMENotifyHandle():                                                *                                                                     * Handle WM_IME_NOTIFY messages.                                      */LONG IMENotifyHandle(HIMC hUICurIMC, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){  LONG lRet = 0L;  LPINPUTCONTEXT lpIMC; 

      if (!(lpIMC = ImmLockIMC(hUICurIMC)))      return 0L;

      switch (wParam)  {  case IMN_CLOSESTATUSWINDOW:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW/n");

       /// hide the status window   g_pStatus->Hide();      break;     case IMN_OPENSTATUSWINDOW:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW/n");      /// create the status window, but don't show   g_pStatus->Create(hWnd);   break;     case IMN_OPENCANDIDATE:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_OPENCANDIDATE/n");   break;     case IMN_CHANGECANDIDATE:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_CHANGECANDIDATE/n");   break;     case IMN_CLOSECANDIDATE:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_CLOSECANDIDATE/n");   break;     case IMN_SETCONVERSIONMODE:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_SETCONVERSIONMODE/n");

       /// repaint the status window   g_pStatus->Repaint();   break;     case IMN_SETSENTENCEMODE:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_SETSENTENCEMODE/n");   break;     case IMN_SETOPENSTATUS:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_SETOPENSTATUS/n"); 

       /// repaint the status window   g_pStatus->Repaint();   break;     case IMN_SETCANDIDATEPOS:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_SETCANDIDATEPOS/n");   break;     case IMN_SETCOMPOSITIONFONT:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT/n");   break;     case IMN_SETCOMPOSITIONWINDOW:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW/n");

       /// adjust the postion of comp and cand windows       POINT ptSrc;   SIZE szOffset;   HDC hDC; 

       ptSrc = lpIMC->cfCompForm.ptCurrentPos;   ClientToScreen(lpIMC->hWnd, &ptSrc);   hDC = GetDC(lpIMC->hWnd);      GetTextExtentPoint(hDC,"A",1,&szOffset);      ReleaseDC(lpIMC->hWnd,hDC);

       g_ptTopLeft.x = ptSrc.x + szOffset.cx;   g_ptTopLeft.y = ptSrc.y + szOffset.cy;       break;     case IMN_GUIDELINE:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_GUIDELINE/n");   break;     case IMN_SETSTATUSWINDOWPOS:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS/n");   break;     case IMN_PRIVATE:   TRACE("UIWnd:WM_IME_NOTIFY:IMN_PRIVATE/n");   break;     default:   break;  }    ImmUnlockIMC(hUICurIMC);

      return lRet;}其中,需要注意的是要确保获取正确的位置,必须有先发送过WM_IME_STARTCOMPOSITION消息,这个消息一般在刚开始输入新拼音时候发送!关于IME消息处理可以看MSDN相关文档。    即便如此,仍然不能保证在所有程序中,输入法都能正确地体现光标跟随,我遇到的情况是在UtraEdit中,上述代码毫无作用,IMN_SETCOMPOSITIONWINDOW根本就没有被系统触发,这时我只好通过GetCaretPos来侥幸地获取光标位置。 

    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1349102

     

    最新回复(0)