private void DestroyImeWindow(IntPtr hwnd) { if (systemCaret) { ImeNative.DestroyCaret(); systemCaret = false; } }
private void CloseImeComposition() { if (hasImeComposition) { // Set focus to 0, which destroys IME suggestions window. ImeNative.SetFocus(IntPtr.Zero); // Restore focus. ImeNative.SetFocus(source.Handle); } }
private void OnImeSetContext(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam) { // We handle the IME Composition Window ourselves (but let the IME Candidates // Window be handled by IME through DefWindowProc()), so clear the // ISC_SHOWUICOMPOSITIONWINDOW flag: ImeNative.DefWindowProc(hwnd, msg, wParam, (IntPtr)(lParam.ToInt64() & ~ImeNative.ISC_SHOWUICOMPOSITIONWINDOW)); // TODO: should we call ImmNotifyIME? CreateImeWindow(hwnd); MoveImeWindow(hwnd); }
private void CreateImeWindow(IntPtr hwnd) { // Chinese/Japanese IMEs somehow ignore function calls to // ::ImmSetCandidateWindow(), and use the position of the current system // caret instead -::GetCaretPos(). // Therefore, we create a temporary system caret for Chinese IMEs and use // it during this input context. // Since some third-party Japanese IME also uses ::GetCaretPos() to determine // their window position, we also create a caret for Japanese IMEs. languageCodeId = PrimaryLangId(InputLanguageManager.Current.CurrentInputLanguage.KeyboardLayoutId); if (languageCodeId == ImeNative.LANG_JAPANESE || languageCodeId == ImeNative.LANG_CHINESE) { if (!systemCaret) { if (ImeNative.CreateCaret(hwnd, IntPtr.Zero, 1, 1)) { systemCaret = true; } } } }
private void MoveImeWindow(IntPtr hwnd) { if (compositionBounds.Count == 0) { return; } var hIMC = ImeNative.ImmGetContext(hwnd); var rc = compositionBounds[0]; var x = rc.X + rc.Width; var y = rc.Y + rc.Height; const int kCaretMargin = 1; // As written in a comment in ImeInput::CreateImeWindow(), // Chinese IMEs ignore function calls to ::ImmSetCandidateWindow() // when a user disables TSF (Text Service Framework) and CUAS (Cicero // Unaware Application Support). // On the other hand, when a user enables TSF and CUAS, Chinese IMEs // ignore the position of the current system caret and uses the // parameters given to ::ImmSetCandidateWindow() with its 'dwStyle' // parameter CFS_CANDIDATEPOS. // Therefore, we do not only call ::ImmSetCandidateWindow() but also // set the positions of the temporary system caret if it exists. var candidatePosition = new ImeNative.CANDIDATEFORM { dwIndex = 0, dwStyle = (int)ImeNative.CFS_CANDIDATEPOS, ptCurrentPos = new ImeNative.POINT(x, y), rcArea = new ImeNative.RECT(0, 0, 0, 0) }; ImeNative.ImmSetCandidateWindow(hIMC, ref candidatePosition); if (systemCaret) { ImeNative.SetCaretPos(x, y); } if (languageCodeId == ImeNative.LANG_KOREAN) { // Chinese IMEs and Japanese IMEs require the upper-left corner of // the caret to move the position of their candidate windows. // On the other hand, Korean IMEs require the lower-left corner of the // caret to move their candidate windows. y += kCaretMargin; } // Japanese IMEs and Korean IMEs also use the rectangle given to // ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE // to move their candidate windows when a user disables TSF and CUAS. // Therefore, we also set this parameter here. var excludeRectangle = new ImeNative.CANDIDATEFORM { dwIndex = 0, dwStyle = (int)ImeNative.CFS_EXCLUDE, ptCurrentPos = new ImeNative.POINT(x, y), rcArea = new ImeNative.RECT(rc.X, rc.Y, x, y + kCaretMargin) }; ImeNative.ImmSetCandidateWindow(hIMC, ref excludeRectangle); ImeNative.ImmReleaseContext(hwnd, hIMC); }