From 98c7fde1417ed7a5674dfbbe97320f55ef509d6d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 8 Oct 2025 17:16:35 +0000 Subject: [PATCH] Only allow focus change on pointer release for touch and pen input (#19753) --- src/Avalonia.Base/Input/FocusManager.cs | 25 +++++++++++++++---- .../Primitives/TextSelectionCanvas.cs | 13 +++------- .../Primitives/TextSelectionHandle.cs | 5 ---- src/Avalonia.Controls/TextBox.cs | 2 ++ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Base/Input/FocusManager.cs b/src/Avalonia.Base/Input/FocusManager.cs index 0faf58b069..ed741a36ae 100644 --- a/src/Avalonia.Base/Input/FocusManager.cs +++ b/src/Avalonia.Base/Input/FocusManager.cs @@ -34,7 +34,11 @@ namespace Avalonia.Input { InputElement.PointerPressedEvent.AddClassHandler( typeof(IInputElement), - new EventHandler(OnPreviewPointerPressed), + new EventHandler(OnPreviewPointerEventHandler), + RoutingStrategies.Tunnel); + InputElement.PointerReleasedEvent.AddClassHandler( + typeof(IInputElement), + new EventHandler(OnPreviewPointerEventHandler), RoutingStrategies.Tunnel); } @@ -194,6 +198,17 @@ namespace Avalonia.Input /// True if the element can be focused. internal static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && IsVisible(e); + private static bool CanPointerFocus(IInputElement e, PointerEventArgs ev) + { + if (CanFocus(e)) + { + if (ev.Pointer.Type == PointerType.Mouse || ev is PointerReleasedEventArgs) + return true; + } + + return false; + } + /// /// Gets the focus scope of the specified control, traversing popups. /// @@ -241,21 +256,21 @@ namespace Avalonia.Input /// /// The event sender. /// The event args. - private static void OnPreviewPointerPressed(object? sender, RoutedEventArgs e) + private static void OnPreviewPointerEventHandler(object? sender, RoutedEventArgs e) { if (sender is null) return; - var ev = (PointerPressedEventArgs)e; + var ev = (PointerEventArgs)e; var visual = (Visual)sender; - if (sender == e.Source && ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) + if (sender == e.Source && (ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed || (e as PointerReleasedEventArgs)?.InitialPressMouseButton == MouseButton.Left)) { Visual? element = ev.Pointer?.Captured as Visual ?? e.Source as Visual; while (element != null) { - if (element is IInputElement inputElement && CanFocus(inputElement)) + if (element is IInputElement inputElement && CanPointerFocus(inputElement, ev)) { inputElement.Focus(NavigationMethod.Pointer, ev.KeyModifiers); diff --git a/src/Avalonia.Controls/Primitives/TextSelectionCanvas.cs b/src/Avalonia.Controls/Primitives/TextSelectionCanvas.cs index 4248ea65a1..049188008a 100644 --- a/src/Avalonia.Controls/Primitives/TextSelectionCanvas.cs +++ b/src/Avalonia.Controls/Primitives/TextSelectionCanvas.cs @@ -276,7 +276,6 @@ namespace Avalonia.Controls.Primitives { _textBox.RemoveHandler(TextBox.TextChangingEvent, TextChanged); _textBox.RemoveHandler(KeyDownEvent, TextBoxKeyDown); - _textBox.RemoveHandler(PointerReleasedEvent, TextBoxPointerReleased); _textBox.PropertyChanged -= TextBoxPropertyChanged; _textBox.EffectiveViewportChanged -= TextBoxEffectiveViewportChanged; @@ -294,7 +293,6 @@ namespace Avalonia.Controls.Primitives { _textBox.AddHandler(TextBox.TextChangingEvent, TextChanged, handledEventsToo: true); _textBox.AddHandler(KeyDownEvent, TextBoxKeyDown, handledEventsToo: true); - _textBox.AddHandler(PointerReleasedEvent, TextBoxPointerReleased, handledEventsToo: true); _textBox.PropertyChanged += TextBoxPropertyChanged; _textBox.EffectiveViewportChanged += TextBoxEffectiveViewportChanged; @@ -371,15 +369,12 @@ namespace Avalonia.Controls.Primitives return false; } - private void TextBoxPointerReleased(object? sender, PointerReleasedEventArgs e) + internal void Show() { - if (e.Pointer.Type != PointerType.Mouse) - { - ShowHandles = true; + ShowHandles = true; - MoveHandlesToSelection(); - EnsureVisible(); - } + MoveHandlesToSelection(); + EnsureVisible(); } private void TextBoxPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) diff --git a/src/Avalonia.Controls/Primitives/TextSelectionHandle.cs b/src/Avalonia.Controls/Primitives/TextSelectionHandle.cs index 550f82fe84..6518b96bce 100644 --- a/src/Avalonia.Controls/Primitives/TextSelectionHandle.cs +++ b/src/Avalonia.Controls/Primitives/TextSelectionHandle.cs @@ -151,11 +151,6 @@ namespace Avalonia.Controls.Primitives { var vector = e.GetPosition(VisualRoot as Visual) - _lastPoint.Value; - var tapSize = TopLevel.GetTopLevel(this)?.PlatformSettings?.GetTapSize(PointerType.Touch) ?? new Size(10, 10); - - if (Math.Abs(vector.X) < tapSize.Width && Math.Abs(vector.Y) < tapSize.Height) - return; - ev = new VectorEventArgs { RoutedEvent = DragDeltaEvent, diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index cece9d8c7d..35f1c34b5e 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1868,6 +1868,8 @@ namespace Avalonia.Controls SetCurrentValue(SelectionEndProperty, caretIndex); } + _presenter.TextSelectionHandleCanvas?.Show(); + if (SelectionStart != SelectionEnd) { _presenter.TextSelectionHandleCanvas?.ShowContextMenu();