From 5abb18cd82347f51d3754126961dca2a9445aa36 Mon Sep 17 00:00:00 2001 From: GabrielDuf Date: Tue, 6 Oct 2026 11:56:03 -0400 Subject: [PATCH] Activate a clickable SettingsCard on release instead of on press SettingsCard invoked the click from its PointerPressed handler, so a touch screen fired it the instant the finger landed: the press synthesizes a left button down before the user has moved far enough for Avalonia's ScrollGestureRecognizer to claim the gesture. The page scrolled and the card under the initial touch navigated away at the same time, which is what made the Settings and Package Manager Preferences pages unusable with touch. The card marking the press handled does not suppress the scroll, because the recognizers run as handledEventsToo class handlers. Follow the protocol Avalonia's own Button uses. The press only records that the card saw it; the release invokes the click, gated on the initial button being the left one and on the release point still hitting the card; and PointerCaptureLost clears the pending press. That last handler is what cancels the navigation: when the scroll starts, ScrollGestureRecognizer captures the pointer, which routes through Pointer.CaptureGestureRecognizer to Capture(null) and raises PointerCaptureLost on every ancestor of the implicitly captured element, the card included. The touch end is then delivered to the recognizer rather than to the card. Keyboard activation through Enter and Space is unchanged. Fixes #5475 --- .../Views/Controls/Settings/SettingsCard.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs index 41b8562bf4..3027afdcd4 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs @@ -6,6 +6,7 @@ using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.VisualTree; using UniGetUI.Avalonia.Views.Controls; using UniGetUI.Core.Tools; using ICommand = System.Windows.Input.ICommand; @@ -54,6 +55,7 @@ public class SettingsCard : UserControl private Control? _headerIcon; private object? _rightContent; private bool _isClickEnabled; + private bool _pointerPressed; /// /// Keep compact toggle-style content at the right edge when the card wraps. @@ -239,6 +241,8 @@ public SettingsCard() base.Content = _border; PointerPressed += OnPointerPressed; + PointerReleased += OnPointerReleased; + PointerCaptureLost += OnPointerCaptureLost; KeyDown += OnKeyDown; SizeChanged += (_, e) => UpdateResponsiveLayout(e.NewSize.Width); // Keyboard focus keeps the SettingsCard itself as the focus target. A card-specific @@ -395,10 +399,28 @@ private void OnPointerPressed(object? sender, PointerPressedEventArgs e) // Pointer activation leaves the card's normal stroke untouched; keyboard focus is // handled solely by the external focus adorner. + _pointerPressed = true; + e.Handled = true; + } + + private void OnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + if (!_pointerPressed) return; + _pointerPressed = false; + + if (!_isClickEnabled) return; + if (e.InitialPressMouseButton is not MouseButton.Left) return; + if (!this.GetVisualsAt(e.GetPosition(this)).Any(c => this == c || this.IsVisualAncestorOf(c))) return; + InvokeClick(); e.Handled = true; } + private void OnPointerCaptureLost(object? sender, PointerCaptureLostEventArgs e) + { + _pointerPressed = false; + } + private void OnKeyDown(object? sender, KeyEventArgs e) { if (!_isClickEnabled) return;