diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 3f4d881735..8b82530022 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty CaretBrushProperty = AvaloniaProperty.Register(nameof(CaretBrush)); - + public static readonly DirectProperty SelectionStartProperty = TextBox.SelectionStartProperty.AddOwner( o => o.SelectionStart, @@ -44,7 +44,7 @@ namespace Avalonia.Controls.Presenters TextBox.SelectionEndProperty.AddOwner( o => o.SelectionEnd, (o, v) => o.SelectionEnd = v); - + /// /// Defines the property. /// @@ -80,7 +80,7 @@ namespace Avalonia.Controls.Presenters /// public static readonly StyledProperty LineHeightProperty = TextBlock.LineHeightProperty.AddOwner(); - + /// /// Defines the property. /// @@ -114,7 +114,7 @@ namespace Avalonia.Controls.Presenters } public event EventHandler? CaretBoundsChanged; - + /// /// Gets or sets a brush used to paint the control's background. /// @@ -202,7 +202,7 @@ namespace Avalonia.Controls.Presenters get => GetValue(TextWrappingProperty); set => SetValue(TextWrappingProperty, value); } - + /// /// Gets or sets the line height. By default, this is set to , which determines the appropriate height automatically. /// @@ -232,11 +232,11 @@ namespace Avalonia.Controls.Presenters { return _textLayout; } - + _textLayout = CreateTextLayout(); UpdateCaret(_lastCharacterHit); - + return _textLayout; } } @@ -285,7 +285,7 @@ namespace Avalonia.Controls.Presenters get => GetValue(CaretBrushProperty); set => SetValue(CaretBrushProperty, value); } - + public int SelectionStart { get @@ -313,7 +313,7 @@ namespace Avalonia.Controls.Presenters SetAndRaise(SelectionEndProperty, ref _selectionEnd, value); } } - + protected override bool BypassFlowDirectionPolicies => true; /// @@ -330,9 +330,9 @@ namespace Avalonia.Controls.Presenters var foreground = Foreground; var maxWidth = MathUtilities.IsZero(constraint.Width) ? double.PositiveInfinity : constraint.Width; var maxHeight = MathUtilities.IsZero(constraint.Height) ? double.PositiveInfinity : constraint.Height; - + var textLayout = new TextLayout(text, typeface, FontSize, foreground, TextAlignment, - TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides, + TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides, flowDirection: FlowDirection, lineHeight: LineHeight); return textLayout; @@ -398,7 +398,7 @@ namespace Avalonia.Controls.Presenters { return; } - + var caretBrush = CaretBrush?.ToImmutable(); if (caretBrush is null) @@ -423,13 +423,13 @@ namespace Avalonia.Controls.Presenters context.DrawLine(new ImmutablePen(caretBrush), p1, p2); } - + private (Point, Point) GetCaretPoints() { var x = Math.Floor(_caretBounds.X) + 0.5; var y = Math.Floor(_caretBounds.Y) + 0.5; var b = Math.Ceiling(_caretBounds.Bottom) - 0.5; - + var caretIndex = _lastCharacterHit.FirstCharacterIndex + _lastCharacterHit.TrailingLength; var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(caretIndex, _lastCharacterHit.TrailingLength > 0); var textLine = TextLayout.TextLines[lineIndex]; @@ -438,7 +438,7 @@ namespace Avalonia.Controls.Presenters { x -= 1; } - + return (new Point(x, y), new Point(x, b)); } @@ -524,17 +524,7 @@ namespace Avalonia.Controls.Presenters var start = Math.Min(selectionStart, selectionEnd); var length = Math.Max(selectionStart, selectionEnd) - start; - List>? textStyleOverrides = null; - - if (length > 0 && SelectionForegroundBrush != null) - { - textStyleOverrides = new List> - { - new ValueSpan(start, length, - new GenericTextRunProperties(typeface, FontSize, - foregroundBrush: SelectionForegroundBrush)) - }; - } + IReadOnlyList>? textStyleOverrides = null; var foreground = Foreground; @@ -542,20 +532,26 @@ namespace Avalonia.Controls.Presenters { var preeditHighlight = new ValueSpan(_caretIndex, _preeditText.Length, new GenericTextRunProperties(typeface, FontSize, - foregroundBrush: foreground, - textDecorations: TextDecorations.Underline)); + foregroundBrush: Foreground, + textDecorations: TextDecorations.Underline, + backgroundBrush: SelectionBrush)); - if (textStyleOverrides == null) + textStyleOverrides = new[] + { + preeditHighlight + }; + } + else + { + if (length > 0 && SelectionForegroundBrush != null) { - textStyleOverrides = new List> + textStyleOverrides = new[] { - preeditHighlight + new ValueSpan(start, length, + new GenericTextRunProperties(typeface, FontSize, + foregroundBrush: SelectionForegroundBrush)) }; } - else - { - textStyleOverrides.Add(preeditHighlight); - } } if (PasswordChar != default(char) && !RevealPassword) @@ -574,7 +570,7 @@ namespace Avalonia.Controls.Presenters protected virtual void InvalidateTextLayout() { _textLayout = null; - + InvalidateMeasure(); } @@ -623,7 +619,7 @@ namespace Avalonia.Controls.Presenters private void CaretTimerTick(object? sender, EventArgs e) { _caretBlink = !_caretBlink; - + InvalidateVisual(); } @@ -633,7 +629,7 @@ namespace Avalonia.Controls.Presenters var textLine = TextLayout.TextLines[lineIndex]; var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(textPosition)); - + var nextCaretCharacterHit = textLine.GetNextCaretCharacterHit(characterHit); if (nextCaretCharacterHit.FirstCharacterIndex <= textPosition) @@ -653,8 +649,8 @@ namespace Avalonia.Controls.Presenters _navigationPosition = _caretBounds.Position; CaretChanged(); - } - + } + public void MoveCaretToPoint(Point point) { var hit = TextLayout.HitTestPoint(point); @@ -685,7 +681,7 @@ namespace Avalonia.Controls.Presenters } var textLine = TextLayout.TextLines[lineIndex]; - + currentY += textLine.Height; } else @@ -701,9 +697,9 @@ namespace Avalonia.Controls.Presenters } var navigationPosition = _navigationPosition; - + MoveCaretToPoint(new Point(currentX, currentY)); - + _navigationPosition = navigationPosition.WithY(_caretBounds.Y); CaretChanged(); @@ -714,11 +710,11 @@ namespace Avalonia.Controls.Presenters if (Text is null) { return default; - } + } var characterHit = _lastCharacterHit; var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - + var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(caretIndex, false); if (lineIndex < 0) @@ -740,11 +736,11 @@ namespace Avalonia.Controls.Presenters { characterHit = new CharacterHit(caretIndex); } - + if (caretIndex >= Text.Length) { characterHit = new CharacterHit(Text.Length); - + break; } @@ -756,10 +752,10 @@ namespace Avalonia.Controls.Presenters if (caretIndex <= CaretIndex) { lineIndex++; - + continue; } - + break; } } @@ -786,7 +782,7 @@ namespace Avalonia.Controls.Presenters return characterHit; } - + public void MoveCaretHorizontal(LogicalDirection direction = LogicalDirection.Forward) { if (FlowDirection == FlowDirection.RightToLeft) @@ -808,9 +804,9 @@ namespace Avalonia.Controls.Presenters private void UpdateCaret(CharacterHit characterHit) { _lastCharacterHit = characterHit; - + var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - + var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(caretIndex, characterHit.TrailingLength > 0); var textLine = TextLayout.TextLines[lineIndex]; var distanceX = textLine.GetDistanceFromCharacterHit(characterHit); @@ -825,7 +821,7 @@ namespace Avalonia.Controls.Presenters } var caretBounds = new Rect(distanceX, distanceY, 0, textLine.Height); - + if (caretBounds != _caretBounds) { _caretBounds = caretBounds; @@ -846,40 +842,40 @@ namespace Avalonia.Controls.Presenters base.OnDetachedFromVisualTree(e); _caretTimer.Stop(); - + _caretTimer.Tick -= CaretTimerTick; } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); - + switch (change.Property.Name) { - case nameof (Foreground): - case nameof (FontSize): - case nameof (FontStyle): - case nameof (FontWeight): - case nameof (FontFamily): - case nameof (FontStretch): + case nameof(Foreground): + case nameof(FontSize): + case nameof(FontStyle): + case nameof(FontWeight): + case nameof(FontFamily): + case nameof(FontStretch): - case nameof (Text): - case nameof (PreeditText): - case nameof (TextAlignment): - case nameof (TextWrapping): + case nameof(Text): + case nameof(PreeditText): + case nameof(TextAlignment): + case nameof(TextWrapping): - case nameof (SelectionStart): - case nameof (SelectionEnd): - case nameof (SelectionForegroundBrush): + case nameof(SelectionStart): + case nameof(SelectionEnd): + case nameof(SelectionForegroundBrush): - case nameof (PasswordChar): - case nameof (RevealPassword): + case nameof(PasswordChar): + case nameof(RevealPassword): case nameof(FlowDirection): - { - InvalidateTextLayout(); - break; - } + { + InvalidateTextLayout(); + break; + } } } } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 4c9e9327d4..20b11b3060 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1142,7 +1142,7 @@ namespace Avalonia.Controls protected override void OnPointerPressed(PointerPressedEventArgs e) { - if (_presenter == null) + if (_presenter == null || !string.IsNullOrEmpty(_presenter.PreeditText)) { return; } diff --git a/src/Windows/Avalonia.Win32/Input/Imm32CaretManager.cs b/src/Windows/Avalonia.Win32/Input/Imm32CaretManager.cs index 38605efa22..ca52bf6e67 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32CaretManager.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32CaretManager.cs @@ -7,14 +7,11 @@ namespace Avalonia.Win32.Input { private bool _isCaretCreated; - public void TryCreate(int _langId, IntPtr hwnd) + public void TryCreate(IntPtr hwnd) { if (!_isCaretCreated) { - if (_langId == LANG_ZH || _langId == LANG_JA) - { - _isCaretCreated = CreateCaret(hwnd, IntPtr.Zero, 2, 10); - } + _isCaretCreated = CreateCaret(hwnd, IntPtr.Zero, 2, 2); } } @@ -31,6 +28,7 @@ namespace Avalonia.Win32.Input if (_isCaretCreated) { DestroyCaret(); + _isCaretCreated = false; } } diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 3adefd965f..9affb4fd5f 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -1,4 +1,6 @@ using System; +using System.Runtime.InteropServices; +using System.Text; using Avalonia.Input.TextInput; using Avalonia.Threading; @@ -12,102 +14,125 @@ namespace Avalonia.Win32.Input class Imm32InputMethod : ITextInputMethodImpl { public IntPtr HWND { get; private set; } - private IntPtr _defaultImc; + private IntPtr _currentHimc; private WindowImpl _parent; - private bool _active; - private bool _showCompositionWindow; + private ITextInputMethodClient _client; + private Imm32CaretManager _caretManager = new(); - private bool _showCandidateList; + private ushort _langId; private const int _caretMargin = 1; - - public void SetLanguageAndWindow(WindowImpl parent, IntPtr hwnd, IntPtr HKL) + + public bool IsActive => _client != null; + + public bool IsComposing { get; set; } + + public bool ShowCompositionWindow => false; + + public void CreateCaret() + { + _caretManager.TryCreate(HWND); + } + + public void EnableImm() { - if (HWND != hwnd) + var himc = ImmGetContext(HWND); + + if(himc == IntPtr.Zero) + { + himc = ImmCreateContext(); + } + + if(himc != _currentHimc) { - _defaultImc = IntPtr.Zero; + if(_currentHimc != IntPtr.Zero) + { + DisableImm(); + } + + ImmAssociateContext(HWND, himc); + + ImmReleaseContext(HWND, himc); + + _currentHimc = himc; + + _caretManager.TryCreate(HWND); } + } + + public void DisableImm() + { + _caretManager.TryDestroy(); + + Reset(); + + ImmAssociateContext(HWND, IntPtr.Zero); + + _caretManager.TryDestroy(); + + _currentHimc = IntPtr.Zero; + } + + public void SetLanguageAndWindow(WindowImpl parent, IntPtr hwnd, IntPtr HKL) + { HWND = hwnd; + _parent = parent; - _active = false; - _langId = PRIMARYLANGID(LGID(HKL)); - _showCompositionWindow = true; - _showCandidateList = true; - IsComposing = false; + var langId= PRIMARYLANGID(LGID(HKL)); + + if(langId != _langId) + { + DisableImm(); + } + + _langId = langId; + + EnableImm(); } public void ClearLanguageAndWindow() { - if (HWND != IntPtr.Zero && _defaultImc != IntPtr.Zero) - { - ImmReleaseContext(HWND, _defaultImc); - } + DisableImm(); - _defaultImc = IntPtr.Zero; HWND = IntPtr.Zero; _parent = null; - _active = false; + _client = null; _langId = 0; - _showCompositionWindow = false; - _showCandidateList = false; IsComposing = false; } //Dependant on CurrentThread. When Avalonia will support Multiple Dispatchers - //every Dispatcher should have their own InputMethod. - public static Imm32InputMethod Current { get; } = new Imm32InputMethod(); + public static Imm32InputMethod Current { get; } = new Imm32InputMethod(); - private IntPtr DefaultImc + public void Reset() { - get + Dispatcher.UIThread.Post(() => { - if (_defaultImc == IntPtr.Zero && - HWND != IntPtr.Zero) - { - _defaultImc = ImmGetContext(HWND); - ImmReleaseContext(HWND, _defaultImc); - } + var himc = ImmGetContext(HWND); - if (_defaultImc == IntPtr.Zero) + if (IsComposing) { - _defaultImc = ImmCreateContext(); + ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); + + IsComposing = false; } - return _defaultImc; - } - } - - public void Reset() - { - if (IsComposing) - { - Dispatcher.UIThread.Post(() => - { - ImmNotifyIME(DefaultImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); - ImmReleaseContext(HWND, DefaultImc); - IsComposing = false; - }); - } + ImmReleaseContext(HWND, himc); + }); } public void SetClient(ITextInputMethodClient client) { - _active = client is { }; + _client = client; + Dispatcher.UIThread.Post(() => { - if (_active) + if (IsActive) { - if (DefaultImc != IntPtr.Zero) - { - _caretManager.TryCreate(_langId, HWND); - // Load the default IME context. - // NOTE(hbono) - // IMM ignores this call if the IME context is loaded. Therefore, we do - // not have to check whether or not the IME context is loaded. - ImmAssociateContext(HWND, _defaultImc); - } + EnableImm(); } else { @@ -116,14 +141,8 @@ namespace Avalonia.Win32.Input // mouse button and selected a password input while composing a text. // For this case, we have to complete the ongoing composition and // clean up the resources attached to this object BEFORE DISABLING THE IME. - if (IsComposing) - { - ImmNotifyIME(DefaultImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); - ImmReleaseContext(HWND, DefaultImc); - IsComposing = false; - } - ImmAssociateContext(HWND, IntPtr.Zero); - _caretManager.TryDestroy(); + + DisableImm(); } }); } @@ -131,19 +150,23 @@ namespace Avalonia.Win32.Input public void SetCursorRect(Rect rect) { var focused = GetActiveWindow() == HWND; + if (!focused) { return; } + Dispatcher.UIThread.Post(() => { - IntPtr himc = DefaultImc; + var himc = ImmGetContext(HWND); + if (himc == IntPtr.Zero) { return; } MoveImeWindow(rect, himc); + ImmReleaseContext(HWND, himc); }); } @@ -157,8 +180,7 @@ namespace Avalonia.Win32.Input var s = _parent?.DesktopScaling ?? 1; var (x1, y1, x2, y2) = ((int) (p1.X * s), (int) (p1.Y * s), (int) (p2.X * s), (int) (p2.Y * s)); - if (!_showCompositionWindow && - _langId == LANG_ZH) + if (!ShowCompositionWindow && _langId == LANG_ZH) { // Chinese IMEs ignore function calls to ::ImmSetCandidateWindow() // when a user disables TSF (Text Service Framework) and CUAS (Cicero @@ -175,12 +197,13 @@ namespace Avalonia.Win32.Input dwStyle = CFS_CANDIDATEPOS, ptCurrentPos = new POINT {X = x2, Y = y2} }; + ImmSetCandidateWindow(himc, ref candidateForm); } - + _caretManager.TryMove(x2, y2); - if (_showCompositionWindow) + if (ShowCompositionWindow) { ConfigureCompositionWindow(x1, y1, himc, y2 - y1); // Don't need to set the position of candidate window. @@ -214,6 +237,7 @@ namespace Avalonia.Win32.Input ptCurrentPos = new POINT {X = x1, Y = y1}, rcArea = new RECT {left = x1, top = y1, right = x2, bottom = y2 + _caretMargin} }; + ImmSetCandidateWindow(himc, ref excludeRectangle); } @@ -224,6 +248,7 @@ namespace Avalonia.Win32.Input dwStyle = CFS_POINT, ptCurrentPos = new POINT {X = x1, Y = y1}, }; + ImmSetCompositionWindow(himc, ref compForm); var logFont = new LOGFONT() @@ -231,6 +256,7 @@ namespace Avalonia.Win32.Input lfHeight = height, lfQuality = 5 //CLEARTYPE_QUALITY }; + ImmSetCompositionFont(himc, ref logFont); } @@ -238,8 +264,43 @@ namespace Avalonia.Win32.Input { // we're skipping this. not usable on windows } + + public void CompositionChanged() + { + if (!IsComposing) + { + return; + } + + if(!IsActive || !_client.SupportsPreedit) + { + return; + } + + var composition = GetCompositionString(); + + _client.SetPreeditText(composition); + } - public bool IsComposing { get; set; } + private string GetCompositionString() + { + var himc = ImmGetContext(HWND); + + var length = ImmGetCompositionString(himc, GCS.GCS_COMPSTR, IntPtr.Zero, 0); + + var buffer = new byte[length]; + + unsafe + { + fixed (byte* bufferPtr = buffer) + { + var error = ImmGetCompositionString(himc, GCS.GCS_COMPSTR, (IntPtr)bufferPtr, (uint)length); + + return Encoding.Unicode.GetString(buffer, 0, buffer.Length); + } + } + + } ~Imm32InputMethod() { diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index ea01d5cbdf..9d1920498b 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1766,6 +1766,46 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); + [Flags] + public enum GCS : uint + { + /// Retrieve or update the attribute of the composition string. + GCS_COMPATTR = 0x0010, + + /// Retrieve or update clause information of the composition string. + GCS_COMPCLAUSE = 0x0020, + + /// Retrieve or update the attributes of the reading string of the current composition. + GCS_COMPREADATTR = 0x0002, + + /// Retrieve or update the clause information of the reading string of the composition string. + GCS_COMPREADCLAUSE = 0x0004, + + /// Retrieve or update the reading string of the current composition. + GCS_COMPREADSTR = 0x0001, + + /// Retrieve or update the current composition string. + GCS_COMPSTR = 0x0008, + + /// Retrieve or update the cursor position in composition string. + GCS_CURSORPOS = 0x0080, + + /// Retrieve or update the starting position of any changes in composition string. + GCS_DELTASTART = 0x0100, + + /// Retrieve or update clause information of the result string. + GCS_RESULTCLAUSE = 0x1000, + + /// Retrieve or update clause information of the reading string. + GCS_RESULTREADCLAUSE = 0x0400, + + /// Retrieve or update the reading string. + GCS_RESULTREADSTR = 0x0200, + + /// Retrieve or update the string of the composition result. + GCS_RESULTSTR = 0x0800, + } + [DllImport("imm32.dll", SetLastError = true)] public static extern IntPtr ImmGetContext(IntPtr hWnd); [DllImport("imm32.dll", SetLastError = true)] @@ -1788,6 +1828,29 @@ namespace Avalonia.Win32.Interop public static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref COMPOSITIONFORM lpComp); [DllImport("imm32.dll")] public static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT lf); + + [DllImport("imm32.dll", SetLastError = false, CharSet = CharSet.Unicode)] + public static extern int ImmGetCompositionString(IntPtr hIMC, GCS dwIndex, [Out, Optional] IntPtr lpBuf, uint dwBufLen); + + public static string ImmGetCompositionString(IntPtr hIMC, GCS dwIndex) + { + int bufferLength = ImmGetCompositionString(hIMC, dwIndex, IntPtr.Zero, 0); + + if (bufferLength > 0) + { + var buffer = new byte[bufferLength]; + + fixed(byte* bufferPtr = buffer) + { + var error = ImmGetCompositionString(hIMC, dwIndex, (IntPtr)bufferPtr, (uint)bufferLength); + + return Marshal.PtrToStringUni((IntPtr)bufferPtr); + } + } + + return null; + } + [DllImport("imm32.dll")] public static extern bool ImmNotifyIME(IntPtr hIMC, int dwAction, int dwIndex, int dwValue); [DllImport("user32.dll")] @@ -1827,7 +1890,13 @@ namespace Avalonia.Win32.Interop public const int CFS_EXCLUDE = 0x0080; public const int CFS_POINT = 0x0002; public const int CFS_RECT = 0x0001; - public const uint ISC_SHOWUICOMPOSITIONWINDOW = 0x80000000; + + // lParam for WM_IME_SETCONTEXT + public const long ISC_SHOWUICANDIDATEWINDOW = 0x00000001; + public const long ISC_SHOWUICOMPOSITIONWINDOW = 0x80000000; + public const long ISC_SHOWUIGUIDELINE = 0x40000000; + public const long ISC_SHOWUIALLCANDIDATEWINDOW = 0x0000000F; + public const long ISC_SHOWUIALL = 0xC000000F; public const int NI_COMPOSITIONSTR = 21; public const int CPS_COMPLETE = 1; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 4c7b9a0348..9022bc4179 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -527,22 +527,22 @@ namespace Avalonia.Win32 } case WindowsMessage.WM_PAINT: - { - using(NonPumpingSyncContext.Use()) - using (_rendererLock.Lock()) { - if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) + using (NonPumpingSyncContext.Use()) + using (_rendererLock.Lock()) { - var f = RenderScaling; - var r = ps.rcPaint; - Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, - (r.bottom - r.top) / f)); - EndPaint(_hwnd, ref ps); + if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) + { + var f = RenderScaling; + var r = ps.rcPaint; + Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, + (r.bottom - r.top) / f)); + EndPaint(_hwnd, ref ps); + } } - } - return IntPtr.Zero; - } + return IntPtr.Zero; + } case WindowsMessage.WM_ENTERSIZEMOVE: @@ -551,7 +551,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_SIZE: { - using(NonPumpingSyncContext.Use()) + using (NonPumpingSyncContext.Use()) using (_rendererLock.Lock()) { // Do nothing here, just block until the pending frame render is completed on the render thread @@ -651,13 +651,19 @@ namespace Avalonia.Win32 } case WindowsMessage.WM_IME_SETCONTEXT: { - // TODO if we implement preedit, disable the composition window: - // lParam = new IntPtr((int)(((uint)lParam.ToInt64()) & ~ISC_SHOWUICOMPOSITIONWINDOW)); + DefWindowProc(Hwnd, msg, wParam, (IntPtr)(lParam.ToInt64() & ~ISC_SHOWUICOMPOSITIONWINDOW)); + UpdateInputMethod(GetKeyboardLayout(0)); + + return IntPtr.Zero; + } + case WindowsMessage.WM_IME_COMPOSITION: + { + Imm32InputMethod.Current.CompositionChanged(); + break; } case WindowsMessage.WM_IME_CHAR: - case WindowsMessage.WM_IME_COMPOSITION: case WindowsMessage.WM_IME_COMPOSITIONFULL: case WindowsMessage.WM_IME_CONTROL: case WindowsMessage.WM_IME_KEYDOWN: @@ -667,6 +673,7 @@ namespace Avalonia.Win32 break; case WindowsMessage.WM_IME_STARTCOMPOSITION: Imm32InputMethod.Current.IsComposing = true; + return IntPtr.Zero; break; case WindowsMessage.WM_IME_ENDCOMPOSITION: Imm32InputMethod.Current.IsComposing = false; @@ -687,7 +694,7 @@ namespace Avalonia.Win32 return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); #endif - if(shouldTakeFocus) + if (shouldTakeFocus) { SetFocus(_hwnd); } @@ -916,14 +923,15 @@ namespace Avalonia.Win32 { // note: for non-ime language, also create it so that emoji panel tracks cursor var langid = LGID(hkl); + if (langid == _langid && Imm32InputMethod.Current.HWND == Hwnd) { return; } + _langid = langid; Imm32InputMethod.Current.SetLanguageAndWindow(this, Hwnd, hkl); - } private static int ToInt32(IntPtr ptr)