예제 #1
0
        private void UpdateNearCaretCompositionWindow()
        {
            ITextView view;
            Rect rectUi;
            GeneralTransform transform;
            Point milPointTopLeft;
            Point milPointBottomRight;
            Point milPointCaret;
            Rect rectCaret;
            CompositionTarget compositionTarget;
            IntPtr hwnd;

            if (!IsInKeyboardFocus)
            {
                return;
            }

            if (_source == null)
            {
                return;
            }

            // get hwnd from _source.
            new UIPermission(UIPermissionWindow.AllWindows).Assert();//Blessed Assert
            try
            {
                hwnd = ((IWin32Window)_source).Handle;
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }

            rectUi = UiScope.VisualContentBounds;
            view = _editor.TextView;

            //
            // 





            // During incremental layout update, the region of the view covered by
            // the selection may not be ready yet.
            if (!_editor.Selection.End.HasValidLayout)
            {
                _updateCompWndPosAtNextLayoutUpdate = true;
                return;
            }

            compositionTarget = _source.CompositionTarget;

            // HwndSource.CompositionTarget may return null if the target hwnd is being destroyed and disposed.
            if (compositionTarget == null || compositionTarget.RootVisual == null)
            {
                return;
            }

            // If the mouse click happens before rendering, the seleciton move notification is generated.
            // However the visual tree is not completely connected yet. We need to check it.
            if (!compositionTarget.RootVisual.IsAncestorOf(RenderScope))
            {
                return;
            }

            IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd));
            if (himc != IntPtr.Zero)
            {
                rectCaret = view.GetRectangleFromTextPosition(_editor.Selection.End.CreatePointer(LogicalDirection.Backward));

                // Take the points of the renderScope.
                milPointTopLeft = new Point(rectUi.Left, rectUi.Top);
                milPointBottomRight = new Point(rectUi.Right, rectUi.Bottom);

                // Take the "extended" union of the first and last char's bounding box.
                // milPointCaret = new Point(rectCaret.Left, rectCaret.Top);
                milPointCaret = new Point(rectCaret.Left, rectCaret.Bottom);

                // Transform to root visual coordinates.
                transform = RenderScope.TransformToAncestor(compositionTarget.RootVisual);
                transform.TryTransform(milPointTopLeft, out milPointTopLeft);
                transform.TryTransform(milPointBottomRight, out milPointBottomRight);
                transform.TryTransform(milPointCaret, out milPointCaret);

                // Transform to device units.
                milPointTopLeft = compositionTarget.TransformToDevice.Transform(milPointTopLeft);
                milPointBottomRight = compositionTarget.TransformToDevice.Transform(milPointBottomRight);
                milPointCaret = compositionTarget.TransformToDevice.Transform(milPointCaret);

                // Build COMPOSITIONFORM. COMPOSITIONFORM is window coodidate.
                NativeMethods.COMPOSITIONFORM compform = new NativeMethods.COMPOSITIONFORM();
                compform.dwStyle = NativeMethods.CFS_RECT;
                compform.rcArea.left = ConvertToInt32(milPointTopLeft.X);
                compform.rcArea.right = ConvertToInt32(milPointBottomRight.X);
                compform.rcArea.top = ConvertToInt32(milPointTopLeft.Y);
                compform.rcArea.bottom = ConvertToInt32(milPointBottomRight.Y);
                compform.ptCurrentPos = new NativeMethods.POINT(ConvertToInt32(milPointCaret.X), ConvertToInt32(milPointCaret.Y));

                // Call IMM32 to set new candidate position to hIMC.
                // ImmSetCompositionWindow fails when
                //  - himc belongs to other threads.
                //  - fail to lock IMC.
                // Those cases are ignorable for us.
                // In addition, it does not set win32 last error and we have no clue to handle error.
                UnsafeNativeMethods.ImmSetCompositionWindow(new HandleRef(this, himc), ref compform);
                UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc));
            }
        }
예제 #2
0
        // Set candidate window position.
        // Borrowed from https://github.com/chromium/chromium/blob/master/ui/base/ime/win/imm32_manager.cc
        public void SetCandidateWindow(TsfSharp.Rect caretRect)
        {
            int x = caretRect.Left;
            int y = caretRect.Top;

            if (PRIMARYLANGID(_inputLanguageId) == LANG_CHINESE)
            {
                // 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.
                var candidateForm = new NativeMethods.CANDIDATEFORM();
                candidateForm.dwStyle        = NativeMethods.CFS_CANDIDATEPOS;
                candidateForm.ptCurrentPos.X = x;
                candidateForm.ptCurrentPos.Y = y;
                NativeMethods.ImmSetCandidateWindow(_defaultImc, ref candidateForm);
            }

            if (PRIMARYLANGID(_inputLanguageId) == LANG_JAPANESE)
            {
                NativeMethods.SetCaretPos(x, caretRect.Bottom);
            }
            else
            {
                NativeMethods.SetCaretPos(x, y);
            }

            // Set composition window position also to ensure move the candidate window position.
            var compositionForm = new NativeMethods.COMPOSITIONFORM();

            compositionForm.dwStyle        = NativeMethods.CFS_POINT;
            compositionForm.ptCurrentPos.X = x;
            compositionForm.ptCurrentPos.Y = y;
            NativeMethods.ImmSetCompositionWindow(_defaultImc, ref compositionForm);

            if (PRIMARYLANGID(_inputLanguageId) == 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;
            }

            // Need to return here since some Chinese IMEs would stuck if set
            // candidate window position with CFS_EXCLUDE style.
            if (PRIMARYLANGID(_inputLanguageId) == LANG_CHINESE)
            {
                return;
            }

            // 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 NativeMethods.CANDIDATEFORM();

            compositionForm.dwStyle        = NativeMethods.CFS_EXCLUDE;
            compositionForm.ptCurrentPos.X = x;
            compositionForm.ptCurrentPos.Y = y;
            compositionForm.rcArea.Left    = x;
            compositionForm.rcArea.Top     = y;
            compositionForm.rcArea.Right   = caretRect.Right;
            compositionForm.rcArea.Bottom  = caretRect.Bottom;
            NativeMethods.ImmSetCandidateWindow(_defaultImc, ref excludeRectangle);
        }
예제 #3
0
        /// <summary>
        /// WndProc
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            //            LogWriter logWriter = new LogWriter(@"C:\log\MyTextbox.txt");
            //            logWriter.Write(m.ToString());

            IntPtr hIMC;

            switch (m.Msg)
            {
                case NativeMethods.WM_CHAR:
                    hIMC = NativeMethods.ImmGetContext(this.Handle);
                    if (NativeMethods.ImmGetOpenStatus(hIMC) == 0)
                    {
                        char chr = Convert.ToChar(m.WParam.ToInt32() & 0xff);
                        Insert(chr.ToString());
            //                        caret.SetPos(caret.GetPos().X + , caret.GetPos().Y);
                    }
                    NativeMethods.ImmReleaseContext(this.Handle, hIMC);
                    Invalidate();
                    break;
                case NativeMethods.WM_IME_STARTCOMPOSITION:
                    hIMC = NativeMethods.ImmGetContext(this.Handle);

                    // ImmSetCompositionWindowとImmSetCandidateWindowがしっかり機能しているのかがイマイチ分からない。
                    // 変換ウィンドウの位置を設定
                    NativeMethods.COMPOSITIONFORM cf = new NativeMethods.COMPOSITIONFORM();
                    cf.dwStyle = NativeMethods.CFS_POINT;
                    cf.ptCurrentPos = new Point(100, 0);
                    cf.rcArea = new Rectangle();
                    NativeMethods.ImmSetCompositionWindow(hIMC, ref cf);

                    // 候補文字ウィンドウの位置調整を行う
                    NativeMethods.CANDIDATEFORM lpCandidate = new NativeMethods.CANDIDATEFORM();
                    lpCandidate.dwIndex = 0;
                    lpCandidate.dwStyle = NativeMethods.CFS_CANDIDATEPOS;
                    lpCandidate.ptCurrentPos = new Point(10, 50);
                    NativeMethods.ImmSetCandidateWindow(hIMC, ref lpCandidate);

                    NativeMethods.ImmReleaseContext(this.Handle, hIMC);
                    break;
                case NativeMethods.WM_IME_COMPOSITION:
                    this.ImeComposition(m);
                    break;
                case NativeMethods.WM_IME_ENDCOMPOSITION:

                    break;
                case NativeMethods.WM_IME_NOTIFY:
                    switch (m.WParam.ToInt32())
                    {
                        case NativeMethods.IMN_OPENCANDIDATE:
                            // 候補文字ウィンドウが表示された
                            this.SetCandidateWindowPos(m.HWnd);
                            break;
                        case NativeMethods.IMN_CLOSECANDIDATE:
                        case NativeMethods.IMN_CHANGECANDIDATE:
                        case NativeMethods.IMN_SETOPENSTATUS:
                            // 何もしない。
                            break;
                    }
                    break;

            }
            base.WndProc(ref m);
        }