diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 3f951d6bb1a..0c487046a9a 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -117,16 +117,20 @@ private void _client_SelectionChanged(object? sender, EventArgs e) private void OnSelectionChanged() { - if (Client is null) + if (Client is null || _inputConnection is null) { return; } + OnSurroundingTextChanged(); + var selection = Client.Selection; - _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); + _inputConnection.SetSelection(selection.Start, selection.End); + + var composition = _inputConnection.EditableWrapper.CurrentComposition; - _inputConnection?.SetSelection(selection.Start, selection.End); + _imm.UpdateSelection(_host, selection.Start, selection.End, composition.Start, composition.End); } private void _client_SurroundingTextChanged(object? sender, EventArgs e) @@ -140,8 +144,6 @@ public void OnBatchEditedEnded() { if (_inputConnection is null || _inputConnection.IsInBatchEdit) return; - - OnSurroundingTextChanged(); OnSelectionChanged(); } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 402defea07e..93ac672b558 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -406,6 +406,8 @@ private void OnCaretIndexChanged(AvaloniaPropertyChangedEventArgs e) if (IsUndoEnabled && _undoRedoHelper.TryGetLastState(out state) && state.Text == Text) _undoRedoHelper.UpdateLastState(); + using var _ = _imClient.BeginChange(); + var newValue = e.GetNewValue(); SetCurrentValue(SelectionStartProperty, newValue); SetCurrentValue(SelectionEndProperty, newValue); @@ -1216,6 +1218,8 @@ protected override void OnKeyDown(KeyEventArgs e) var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; + using var _ = _imClient.BeginChange(); + bool Match(List gestures) => gestures.Any(g => g.Matches(e)); bool DetectSelection() => e.KeyModifiers.HasAllFlags(keymap.SelectionModifiers); @@ -1547,6 +1551,8 @@ protected override void OnPointerPressed(PointerPressedEventArgs e) var text = Text; var clickInfo = e.GetCurrentPoint(this); + using var _ = _imClient.BeginChange(); + if (text != null && (e.Pointer.Type == PointerType.Mouse || e.ClickCount >= 2) && clickInfo.Properties.IsLeftButtonPressed && !(clickInfo.Pointer?.Captured is Border)) { @@ -1631,6 +1637,7 @@ protected override void OnPointerMoved(PointerEventArgs e) { return; } + using var _ = _imClient.BeginChange(); // selection should not change during pointer move if the user right clicks if (e.Pointer.Captured == _presenter && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) @@ -1713,6 +1720,8 @@ protected override void OnPointerReleased(PointerReleasedEventArgs e) return; } + using var _ = _imClient.BeginChange(); + if (e.Pointer.Type != PointerType.Mouse && !_isDoubleTapped) { var text = Text; @@ -1857,6 +1866,8 @@ private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting, boo return; } + using var _ = _imClient.BeginChange(); + var text = Text ?? string.Empty; var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; @@ -2017,6 +2028,8 @@ public void ScrollToLine(int lineIndex) /// public void SelectAll() { + using var _ = _imClient.BeginChange(); + SetCurrentValue(SelectionStartProperty, 0); SetCurrentValue(SelectionEndProperty, Text?.Length ?? 0); } @@ -2034,6 +2047,8 @@ internal bool DeleteSelection() if (IsReadOnly) return true; + using var _ = _imClient.BeginChange(); + var (start, end) = GetSelectionRange(); if (start != end) @@ -2141,6 +2156,8 @@ private void SetSelectionForControlBackspace() var text = Text ?? string.Empty; var selectionStart = CaretIndex; + using var _ = _imClient.BeginChange(); + MoveHorizontal(-1, true, false, false); if (SelectionEnd > 0 && @@ -2160,6 +2177,8 @@ private void SetSelectionForControlDelete() return; } + using var _ = _imClient.BeginChange(); + SetCurrentValue(SelectionStartProperty, CaretIndex); MoveHorizontal(1, true, true, false); diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index faf2450a9a4..ed008ed79b8 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -2,6 +2,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Input.TextInput; using Avalonia.Media.TextFormatting; +using Avalonia.Reactive; using Avalonia.Utilities; namespace Avalonia.Controls @@ -10,6 +11,8 @@ internal class TextBoxTextInputMethodClient : TextInputMethodClient { private TextBox? _parent; private TextPresenter? _presenter; + private bool _selectionChanged; + private bool _isInChange; public override Visual TextViewVisual => _presenter!; @@ -190,8 +193,30 @@ private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEven if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty) { - RaiseSelectionChanged(); + if (_isInChange) + _selectionChanged = true; + else + RaiseSelectionChanged(); } } + + internal IDisposable BeginChange() + { + if (_isInChange) + return Disposable.Empty; + + _isInChange = true; + return Disposable.Create(RaiseEvents); + } + + private void RaiseEvents() + { + _isInChange = false; + + if (_selectionChanged) + RaiseSelectionChanged(); + + _selectionChanged = false; + } } }