From 95ccb0c9c69dda3873a2a1ec1e0ce8f922f1d8c2 Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Thu, 12 Jan 2023 20:24:28 +0100 Subject: [PATCH 1/3] ScrollGestureRecognizer ends its current fling gesture (touch or pen) if ScrollGestureEventArgs is no longer handled by the Presenter. --- .../Input/GestureRecognizers/ScrollGestureRecognizer.cs | 9 ++++++++- src/Avalonia.Base/Input/ScrollGestureEventArgs.cs | 4 ++++ .../Presenters/ScrollContentPresenter.cs | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index cec98ec66b..790439245a 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -189,7 +189,14 @@ namespace Avalonia.Input.GestureRecognizers var speed = _inertia * Math.Pow(0.15, st.Elapsed.TotalSeconds); var distance = speed * elapsedSinceLastTick.TotalSeconds; - _target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, distance)); + var scrollGestureEventArgs = new ScrollGestureEventArgs(_gestureId, distance); + _target!.RaiseEvent(scrollGestureEventArgs); + + if (!scrollGestureEventArgs.Handled || scrollGestureEventArgs.ShouldEndScrollGesture) + { + EndGesture(); + return false; + } // EndGesture using InertialScrollSpeedEnd only in the direction of scrolling if (CanVerticallyScroll && CanHorizontallyScroll && Math.Abs(speed.X) < InertialScrollSpeedEnd && Math.Abs(speed.Y) <= InertialScrollSpeedEnd) diff --git a/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs b/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs index fd1d0f42c3..f1a0887b60 100644 --- a/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs +++ b/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs @@ -6,6 +6,10 @@ namespace Avalonia.Input { public int Id { get; } public Vector Delta { get; } + /// + /// When set the ScrollGestureRecognizer should stop its current active scroll gesture. + /// + public bool ShouldEndScrollGesture { get; set; } private static int _nextId = 1; public static int GetNextFreeId() => _nextId++; diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 5a691ce665..0cfe4bada1 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -428,6 +428,8 @@ namespace Avalonia.Controls.Presenters Offset = newOffset; e.Handled = !IsScrollChainingEnabled || offsetChanged; + + e.ShouldEndScrollGesture = !IsScrollChainingEnabled && !offsetChanged; } } From 79b30658f15b11820a5a086923d3fed2bbdf2a07 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 13 Jan 2023 10:19:45 +0000 Subject: [PATCH 2/3] add TopLevel.GetTopLevel api --- samples/ControlCatalog/Pages/DialogsPage.xaml.cs | 2 +- samples/ControlCatalog/Pages/NotificationsPage.xaml.cs | 2 +- src/Avalonia.Controls/ComboBox.cs | 2 +- src/Avalonia.Controls/Primitives/OverlayPopupHost.cs | 2 +- src/Avalonia.Controls/Primitives/Popup.cs | 2 +- src/Avalonia.Controls/TopLevel.cs | 10 ++++++++++ src/Avalonia.Native/AvaloniaNativeDragSource.cs | 3 +-- src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs | 3 +-- 8 files changed, 17 insertions(+), 9 deletions(-) diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 67e9ef4e40..46f25f4d8d 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -397,7 +397,7 @@ CanPickFolder: {storageProvider.CanPickFolder}"; } Window GetWindow() => this.VisualRoot as Window ?? throw new NullReferenceException("Invalid Owner"); - TopLevel GetTopLevel() => this.VisualRoot as TopLevel ?? throw new NullReferenceException("Invalid Owner"); + TopLevel GetTopLevel() => TopLevel.GetTopLevel(this) ?? throw new NullReferenceException("Invalid Owner"); private void InitializeComponent() { diff --git a/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs b/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs index bfd49a2c00..6f83e5c366 100644 --- a/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs @@ -27,7 +27,7 @@ namespace ControlCatalog.Pages { base.OnAttachedToVisualTree(e); - _viewModel.NotificationManager = new Avalonia.Controls.Notifications.WindowNotificationManager(VisualRoot as TopLevel); + _viewModel.NotificationManager = new Avalonia.Controls.Notifications.WindowNotificationManager(TopLevel.GetTopLevel(this)); } } } diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 8da99b5f25..6018779e34 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -383,7 +383,7 @@ namespace Avalonia.Controls _subscriptionsOnOpen.Clear(); - var toplevel = this.GetVisualRoot() as TopLevel; + var toplevel = TopLevel.GetTopLevel(this); if (toplevel != null) { toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) => diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index e0f72cae54..e265f4eb6a 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -123,7 +123,7 @@ namespace Avalonia.Controls.Primitives public static IPopupHost CreatePopupHost(Visual target, IAvaloniaDependencyResolver? dependencyResolver) { - var platform = (target.GetVisualRoot() as TopLevel)?.PlatformImpl?.CreatePopup(); + var platform = TopLevel.GetTopLevel(target)?.PlatformImpl?.CreatePopup(); if (platform != null) return new PopupRoot((TopLevel)target.GetVisualRoot()!, platform, dependencyResolver); diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index bf79198939..7cac12eabe 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -358,7 +358,7 @@ namespace Avalonia.Controls.Primitives return; } - var topLevel = placementTarget.VisualRoot as TopLevel; + var topLevel = TopLevel.GetTopLevel(placementTarget); if (topLevel == null) { diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 1f5b8f80d2..9970a0f1c7 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -341,6 +341,16 @@ namespace Avalonia.Controls { return PlatformImpl?.PointToScreen(p) ?? default; } + + /// + /// Gets the for which the given is hosted in. + /// + /// The visual to query its TopLevel + /// The TopLevel + public static TopLevel? GetTopLevel(Visual? visual) + { + return visual == null ? null : visual.VisualRoot as TopLevel; + } /// /// Creates the layout manager for this . diff --git a/src/Avalonia.Native/AvaloniaNativeDragSource.cs b/src/Avalonia.Native/AvaloniaNativeDragSource.cs index bec45c2d71..7f4c462ee0 100644 --- a/src/Avalonia.Native/AvaloniaNativeDragSource.cs +++ b/src/Avalonia.Native/AvaloniaNativeDragSource.cs @@ -6,7 +6,6 @@ using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.Native.Interop; -using Avalonia.VisualTree; namespace Avalonia.Native { @@ -26,7 +25,7 @@ namespace Avalonia.Native if (element == null) return null; var visual = (Visual)element; - return visual.GetVisualRoot() as TopLevel; + return TopLevel.GetTopLevel(visual); } class DndCallback : NativeCallbackBase, IAvnDndResultCallback diff --git a/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs b/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs index cf1bdc1671..998ff4a427 100644 --- a/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs +++ b/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs @@ -1,7 +1,6 @@ using System; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.VisualTree; using Avalonia.Win32.Interop; namespace Avalonia.Win32.Input @@ -34,7 +33,7 @@ namespace Avalonia.Win32.Input protected override void PlatformCapture(IInputElement element) { - var hwnd = (((element as Visual)?.GetVisualRoot() as TopLevel)?.PlatformImpl as WindowImpl) + var hwnd = (TopLevel.GetTopLevel(element as Visual)?.PlatformImpl as WindowImpl) ?.Handle.Handle; if (hwnd.HasValue && hwnd != IntPtr.Zero) From 7cd51c91615e3b02e8aa0827d179e77c315db89c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 13 Jan 2023 14:34:22 -0500 Subject: [PATCH 3/3] Update samples/ControlCatalog/Pages/DialogsPage.xaml.cs --- samples/ControlCatalog/Pages/DialogsPage.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 46f25f4d8d..8db6e76dca 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -396,7 +396,7 @@ CanPickFolder: {storageProvider.CanPickFolder}"; return item.TryGetUri(out var uri) ? uri.ToString() : item.Name; } - Window GetWindow() => this.VisualRoot as Window ?? throw new NullReferenceException("Invalid Owner"); + Window GetWindow() => TopLevel.GetTopLevel(this) as Window ?? throw new NullReferenceException("Invalid Owner"); TopLevel GetTopLevel() => TopLevel.GetTopLevel(this) ?? throw new NullReferenceException("Invalid Owner"); private void InitializeComponent()