diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index fbf4000a79..a1656e6527 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -103,6 +103,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Input.Gestures + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0001 T:Avalonia.Input.IDataObject @@ -247,6 +253,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + CP0001 + T:Avalonia.Controls.ContextRequestedEventArgs + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0001 T:Avalonia.Controls.Diagnostics.IPopupHostProvider @@ -499,6 +511,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Input.Gestures + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0001 T:Avalonia.Input.IDataObject @@ -643,6 +661,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + CP0001 + T:Avalonia.Controls.ContextRequestedEventArgs + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0001 T:Avalonia.Controls.Diagnostics.IPopupHostProvider @@ -823,6 +847,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + F:Avalonia.Input.HoldingState.Cancelled + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0002 F:Avalonia.Media.DrawingImage.ViewboxProperty @@ -865,6 +895,24 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Data.CompiledBindingPathBuilder.StreamObservable + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.CompiledBindingPathBuilder.StreamTask + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.CompiledBindingPathBuilder.TypeCast(System.Type) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0002 M:Avalonia.Data.ReflectionBinding.#ctor(System.String,Avalonia.Data.BindingMode) @@ -913,6 +961,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Input.HoldingRoutedEventArgs.#ctor(Avalonia.Input.HoldingState,Avalonia.Point,Avalonia.Input.PointerType) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0002 M:Avalonia.Input.IInputRoot.get_KeyboardNavigationHandler @@ -1321,6 +1375,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + CP0002 + F:Avalonia.Controls.Control.ContextRequestedEvent + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0002 F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty @@ -1939,6 +1999,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + F:Avalonia.Input.HoldingState.Cancelled + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0002 F:Avalonia.Media.DrawingImage.ViewboxProperty @@ -1981,6 +2047,24 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Data.CompiledBindingPathBuilder.StreamObservable + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.CompiledBindingPathBuilder.StreamTask + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Data.CompiledBindingPathBuilder.TypeCast(System.Type) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0002 M:Avalonia.Data.ReflectionBinding.#ctor(System.String,Avalonia.Data.BindingMode) @@ -2029,6 +2113,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Input.HoldingRoutedEventArgs.#ctor(Avalonia.Input.HoldingState,Avalonia.Point,Avalonia.Input.PointerType) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0002 M:Avalonia.Input.IInputRoot.get_KeyboardNavigationHandler @@ -2437,6 +2527,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + CP0002 + F:Avalonia.Controls.Control.ContextRequestedEvent + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0002 F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty @@ -3817,6 +3913,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0009 + T:Avalonia.Input.HoldingRoutedEventArgs + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0009 T:Avalonia.Controls.Primitives.AdornerLayer @@ -3853,6 +3955,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0009 + T:Avalonia.Input.HoldingRoutedEventArgs + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0009 T:Avalonia.Controls.Primitives.AdornerLayer diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs index d033956664..bef2c8212d 100644 --- a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Interactivity; using ControlCatalog.ViewModels; diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs index 8c36f81fcd..a71398181c 100644 --- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using Avalonia.Controls; +using Avalonia.Input; using ControlCatalog.ViewModels; namespace ControlCatalog.Pages diff --git a/samples/ControlCatalog/Pages/GesturePage.cs b/samples/ControlCatalog/Pages/GesturePage.cs index 7bc3b96c49..2906091daa 100644 --- a/samples/ControlCatalog/Pages/GesturePage.cs +++ b/samples/ControlCatalog/Pages/GesturePage.cs @@ -52,7 +52,7 @@ namespace ControlCatalog.Pages } }; - RotationGesture.AddHandler(Gestures.PinchEvent, (s, e) => + RotationGesture.AddHandler(InputElement.PinchEvent, (s, e) => { AngleSlider.Value = e.Angle; }); @@ -94,7 +94,7 @@ namespace ControlCatalog.Pages } }; - control.AddHandler(Gestures.PinchEvent, (s, e) => + control.AddHandler(InputElement.PinchEvent, (s, e) => { InitComposition(control!); @@ -114,7 +114,7 @@ namespace ControlCatalog.Pages } }); - control.AddHandler(Gestures.PinchEndedEvent, (s, e) => + control.AddHandler(InputElement.PinchEndedEvent, (s, e) => { InitComposition(control!); @@ -124,7 +124,7 @@ namespace ControlCatalog.Pages } }); - control.AddHandler(Gestures.ScrollGestureEvent, (s, e) => + control.AddHandler(InputElement.ScrollGestureEvent, (s, e) => { InitComposition(control!); @@ -171,7 +171,7 @@ namespace ControlCatalog.Pages } }; - control.AddHandler(Gestures.PullGestureEvent, (s, e) => + control.AddHandler(InputElement.PullGestureEvent, (s, e) => { Vector3D center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0); InitComposition(ball!); @@ -183,7 +183,7 @@ namespace ControlCatalog.Pages } }); - control.AddHandler(Gestures.PullGestureEndedEvent, (s, e) => + control.AddHandler(InputElement.PullGestureEndedEvent, (s, e) => { InitComposition(ball!); if (ballCompositionVisual != null) diff --git a/samples/IntegrationTestApp/Pages/GesturesPage.axaml b/samples/IntegrationTestApp/Pages/GesturesPage.axaml index eb028e226f..e171cc62f9 100644 --- a/samples/IntegrationTestApp/Pages/GesturesPage.axaml +++ b/samples/IntegrationTestApp/Pages/GesturesPage.axaml @@ -19,7 +19,7 @@ AutomationProperties.ControlTypeOverride="Image" Tapped="GestureBorder_Tapped" DoubleTapped="GestureBorder_DoubleTapped" - Gestures.RightTapped="GestureBorder_RightTapped"/> + RightTapped="GestureBorder_RightTapped"/> /// Provides event data for the ContextRequested event. @@ -14,7 +13,7 @@ namespace Avalonia.Controls /// Initializes a new instance of the ContextRequestedEventArgs class. /// public ContextRequestedEventArgs() - : base(Control.ContextRequestedEvent) + : base(InputElement.ContextRequestedEvent) { } @@ -34,10 +33,10 @@ namespace Avalonia.Controls } /// - /// Gets the x- and y-coordinates of the pointer position, optionally evaluated against a coordinate origin of a supplied . + /// Gets the x- and y-coordinates of the pointer position, optionally evaluated against a coordinate origin of a supplied . /// /// - /// Any -derived object that is connected to the same object tree. + /// Any -derived object that is connected to the same object tree. /// To specify the object relative to the overall coordinate system, use a relativeTo value of null. /// /// @@ -48,7 +47,7 @@ namespace Avalonia.Controls /// /// true if the context request was initiated by a pointer device; otherwise, false. /// - public bool TryGetPosition(Control? relativeTo, out Point point) + public bool TryGetPosition(InputElement? relativeTo, out Point point) { if (_pointerEventArgs is null) { diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index 4929cd0ea6..3ae504a77f 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -1,15 +1,23 @@ using System; using System.Threading; using Avalonia.Interactivity; -using Avalonia.Platform; -using Avalonia.Threading; using Avalonia.Reactive; +using Avalonia.Threading; using Avalonia.VisualTree; namespace Avalonia.Input { - public static class Gestures + internal static class Gestures { + // These events are only used internally and not propagated through a route. + // They have routed event args as their target type because their args generated from + // PointerEventArgs. In order to prevent having identical classes with just their base class + // being different, these events use routed args. + public static event EventHandler? Holding; + public static event EventHandler? Tapped; + public static event EventHandler? RightTapped; + public static event EventHandler? DoubleTapped; + private record struct GestureState(GestureStateType Type, IPointer Pointer); private enum GestureStateType { @@ -23,98 +31,6 @@ namespace Avalonia.Input private static Point s_lastPressPoint; private static CancellationTokenSource? s_holdCancellationToken; - /// - /// Defines the IsHoldingEnabled attached property. - /// - public static readonly AttachedProperty IsHoldingEnabledProperty = - AvaloniaProperty.RegisterAttached("IsHoldingEnabled", typeof(Gestures), true); - - /// - /// Defines the IsHoldWithMouseEnabled attached property. - /// - public static readonly AttachedProperty IsHoldWithMouseEnabledProperty = - AvaloniaProperty.RegisterAttached("IsHoldWithMouseEnabled", typeof(Gestures), false); - - public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( - "Tapped", - RoutingStrategies.Bubble, - typeof(Gestures)); - - public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( - "DoubleTapped", - RoutingStrategies.Bubble, - typeof(Gestures)); - - public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( - "RightTapped", - RoutingStrategies.Bubble, - typeof(Gestures)); - - public static readonly RoutedEvent ScrollGestureEvent = - RoutedEvent.Register( - "ScrollGesture", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent ScrollGestureInertiaStartingEvent = - RoutedEvent.Register( - "ScrollGestureInertiaStarting", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent ScrollGestureEndedEvent = - RoutedEvent.Register( - "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PointerTouchPadGestureMagnifyEvent = - RoutedEvent.Register( - "PointerTouchPadGestureMagnify", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PointerTouchPadGestureRotateEvent = - RoutedEvent.Register( - "PointerTouchPadGestureRotate", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PointerTouchPadGestureSwipeEvent = - RoutedEvent.Register( - "PointerTouchPadGestureSwipe", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PinchEvent = - RoutedEvent.Register( - "Pinch", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PinchEndedEvent = - RoutedEvent.Register( - "PinchEnded", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PullGestureEvent = - RoutedEvent.Register( - "PullGesture", RoutingStrategies.Bubble, typeof(Gestures)); - - /// - /// Occurs when a user performs a press and hold gesture (with a single touch, mouse, or pen/stylus contact). - /// - public static readonly RoutedEvent HoldingEvent = - RoutedEvent.Register( - "Holding", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PullGestureEndedEvent = - RoutedEvent.Register( - "PullGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); - - public static bool GetIsHoldingEnabled(StyledElement element) - { - return element.GetValue(IsHoldingEnabledProperty); - } - public static void SetIsHoldingEnabled(StyledElement element, bool value) - { - element.SetValue(IsHoldingEnabledProperty, value); - } - - public static bool GetIsHoldWithMouseEnabled(StyledElement element) - { - return element.GetValue(IsHoldWithMouseEnabledProperty); - } - public static void SetIsHoldWithMouseEnabled(StyledElement element, bool value) - { - element.SetValue(IsHoldWithMouseEnabledProperty, value); - } - static Gestures() { InputElement.PointerPressedEvent.RouteFinished.Subscribe(PointerPressed); @@ -122,102 +38,6 @@ namespace Avalonia.Input InputElement.PointerMovedEvent.RouteFinished.Subscribe(PointerMoved); } - public static void AddTappedHandler(Interactive element, EventHandler handler) - { - element.AddHandler(TappedEvent, handler); - } - - public static void AddDoubleTappedHandler(Interactive element, EventHandler handler) - { - element.AddHandler(DoubleTappedEvent, handler); - } - - public static void AddRightTappedHandler(Interactive element, EventHandler handler) - { - element.AddHandler(RightTappedEvent, handler); - } - - public static void AddHoldingHandler(Interactive element, EventHandler handler) => - element.AddHandler(HoldingEvent, handler); - - public static void AddPinchHandler(Interactive element, EventHandler handler) => - element.AddHandler(PinchEvent, handler); - - public static void AddPinchEndedHandler(Interactive element, EventHandler handler) => - element.AddHandler(PinchEndedEvent, handler); - - public static void AddPullGestureHandler(Interactive element, EventHandler handler) => - element.AddHandler(PullGestureEvent, handler); - - public static void AddPullGestureEndedHandler(Interactive element, EventHandler handler) => - element.AddHandler(PullGestureEndedEvent, handler); - - public static void AddPointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler handler) => - element.AddHandler(PointerTouchPadGestureMagnifyEvent, handler); - - public static void AddPointerTouchPadGestureRotateHandler(Interactive element, EventHandler handler) => - element.AddHandler(PointerTouchPadGestureRotateEvent, handler); - - public static void AddPointerTouchPadGestureSwipeHandler(Interactive element, EventHandler handler) => - element.AddHandler(PointerTouchPadGestureSwipeEvent, handler); - - public static void AddScrollGestureHandler(Interactive element, EventHandler handler) => - element.AddHandler(ScrollGestureEvent, handler); - - public static void AddScrollGestureEndedHandler(Interactive element, EventHandler handler) => - element.AddHandler(ScrollGestureEndedEvent, handler); - - public static void AddScrollGestureInertiaStartingHandler(Interactive element, EventHandler handler) => - element.AddHandler(ScrollGestureInertiaStartingEvent, handler); - - public static void RemoveTappedHandler(Interactive element, EventHandler handler) - { - element.RemoveHandler(TappedEvent, handler); - } - - public static void RemoveDoubleTappedHandler(Interactive element, EventHandler handler) - { - element.RemoveHandler(DoubleTappedEvent, handler); - } - - public static void RemoveRightTappedHandler(Interactive element, EventHandler handler) - { - element.RemoveHandler(RightTappedEvent, handler); - } - - public static void RemoveHoldingHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(HoldingEvent, handler); - - public static void RemovePinchHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(PinchEvent, handler); - - public static void RemovePinchEndedHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(PinchEndedEvent, handler); - - public static void RemovePullGestureHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(PullGestureEvent, handler); - - public static void RemovePullGestureEndedHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(PullGestureEndedEvent, handler); - - public static void RemovePointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(PointerTouchPadGestureMagnifyEvent, handler); - - public static void RemovePointerTouchPadGestureRotateHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(PointerTouchPadGestureRotateEvent, handler); - - public static void RemovePointerTouchPadGestureSwipeHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(PointerTouchPadGestureSwipeEvent, handler); - - public static void RemoveScrollGestureHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(ScrollGestureEvent,handler); - - public static void RemoveScrollGestureEndedHandler(Interactive element,EventHandler handler) => - element.RemoveHandler(ScrollGestureEndedEvent,handler); - - public static void RemoveScrollGestureInertiaStartingHandler(Interactive element, EventHandler handler) => - element.RemoveHandler(ScrollGestureInertiaStartingEvent, handler); - private static object? GetCaptured(RoutedEventArgs? args) { if (args is not PointerEventArgs pointerEventArgs) @@ -237,11 +57,11 @@ namespace Avalonia.Input var e = (PointerPressedEventArgs)ev; var visual = (Visual)source; - if(s_gestureState != null) + if (s_gestureState != null) { - if(s_gestureState.Value.Type == GestureStateType.Holding && source is Interactive i) + if (s_gestureState.Value.Type == GestureStateType.Holding && source is Interactive i) { - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); + Holding?.Invoke(i, new HoldingRoutedEventArgs(HoldingState.Canceled, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); } s_holdCancellationToken?.Cancel(); s_holdCancellationToken?.Dispose(); @@ -263,22 +83,23 @@ namespace Avalonia.Input { DispatcherTimer.RunOnce(() => { - if (s_gestureState != null && !token.IsCancellationRequested && source is InputElement i && GetIsHoldingEnabled(i) && (e.Pointer.Type != PointerType.Mouse || GetIsHoldWithMouseEnabled(i))) + if (s_gestureState != null && !token.IsCancellationRequested && source is InputElement i && InputElement.GetIsHoldingEnabled(i) && + (e.Pointer.Type != PointerType.Mouse || InputElement.GetIsHoldWithMouseEnabled(i))) { s_gestureState = new GestureState(GestureStateType.Holding, s_gestureState.Value.Pointer); - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Started, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); + Holding?.Invoke(i, new HoldingRoutedEventArgs(HoldingState.Started, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); } }, settings.HoldWaitDuration); } } else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) { - if (s_lastPress.TryGetTarget(out var target) && - target == source && + if (s_lastPress.TryGetTarget(out var target) && + target == source && source is Interactive i) { s_gestureState = new GestureState(GestureStateType.DoubleTapped, e.Pointer); - i.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e)); + DoubleTapped?.Invoke(i, new TappedEventArgs(InputElement.DoubleTappedEvent, e)); } } } @@ -307,17 +128,19 @@ namespace Avalonia.Input { if (s_gestureState?.Type == GestureStateType.Holding) { - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Completed, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); + Holding?.Invoke(i, new HoldingRoutedEventArgs(HoldingState.Completed, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); + + RightTapped?.Invoke(i, new TappedEventArgs(InputElement.RightTappedEvent, e)); } else if (e.InitialPressMouseButton == MouseButton.Right) { - i.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); + RightTapped?.Invoke(i, new TappedEventArgs(InputElement.RightTappedEvent, e)); } //GestureStateType.DoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called. //This behaviour matches UWP behaviour. else if (s_gestureState?.Type != GestureStateType.DoubleTapped) { - i.RaiseEvent(new TappedEventArgs(TappedEvent, e)); + Tapped?.Invoke(i, new TappedEventArgs(InputElement.TappedEvent, e)); } } s_gestureState = null; @@ -351,15 +174,15 @@ namespace Avalonia.Input if (s_gestureState.Value.Type == GestureStateType.Holding) { - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); + Holding?.Invoke(i, new HoldingRoutedEventArgs(HoldingState.Canceled, s_lastPressPoint, s_gestureState.Value.Pointer.Type, e)); } + + s_holdCancellationToken?.Cancel(); + s_holdCancellationToken?.Dispose(); + s_holdCancellationToken = null; + s_gestureState = null; } } - - s_holdCancellationToken?.Cancel(); - s_holdCancellationToken?.Dispose(); - s_holdCancellationToken = null; - s_gestureState = null; } } } diff --git a/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs b/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs index efb0c01599..ffc5dfec70 100644 --- a/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs +++ b/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs @@ -6,7 +6,7 @@ namespace Avalonia.Input public class HoldingRoutedEventArgs : RoutedEventArgs { /// - /// Gets the state of the event. + /// Gets the state of the event. /// public HoldingState HoldingState { get; } @@ -20,25 +20,18 @@ namespace Avalonia.Input /// public PointerType PointerType { get; } - internal PointerEventArgs? PointerEventArgs { get; } + internal PointerEventArgs PointerEventArgs { get; } /// /// Initializes a new instance of the class. /// - public HoldingRoutedEventArgs(HoldingState holdingState, Point position, PointerType pointerType) : base(Gestures.HoldingEvent) + internal HoldingRoutedEventArgs(HoldingState holdingState, Point position, PointerType pointerType, PointerEventArgs pointerEventArgs) : base(InputElement.HoldingEvent) { + PointerEventArgs = pointerEventArgs; HoldingState = holdingState; Position = position; PointerType = pointerType; } - - /// - /// Initializes a new instance of the class. - /// - internal HoldingRoutedEventArgs(HoldingState holdingState, Point position, PointerType pointerType, PointerEventArgs pointerEventArgs) : this(holdingState, position, pointerType) - { - PointerEventArgs = pointerEventArgs; - } } public enum HoldingState @@ -56,6 +49,6 @@ namespace Avalonia.Input /// /// An additional contact is detected or a subsequent gesture (such as a slide) is detected. /// - Cancelled, + Canceled, } } diff --git a/src/Avalonia.Base/Input/InputElement.Gestures.cs b/src/Avalonia.Base/Input/InputElement.Gestures.cs new file mode 100644 index 0000000000..25f6362d60 --- /dev/null +++ b/src/Avalonia.Base/Input/InputElement.Gestures.cs @@ -0,0 +1,241 @@ +using System; +using Avalonia.Interactivity; + +namespace Avalonia.Input +{ + public partial class InputElement + { + private bool _isContextMenuOnHolding; + + /// + /// Defines the IsHoldingEnabled attached property. + /// + public static readonly AttachedProperty IsHoldingEnabledProperty = + AvaloniaProperty.RegisterAttached("IsHoldingEnabled", typeof(InputElement), true); + + /// + /// Defines the IsHoldWithMouseEnabled attached property. + /// + public static readonly AttachedProperty IsHoldWithMouseEnabledProperty = + AvaloniaProperty.RegisterAttached("IsHoldWithMouseEnabled", typeof(InputElement), false); + + public static readonly RoutedEvent PinchEvent = + RoutedEvent.Register( + "Pinch", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent PinchEndedEvent = + RoutedEvent.Register( + "PinchEnded", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent PullGestureEvent = + RoutedEvent.Register( + "PullGesture", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent PullGestureEndedEvent = + RoutedEvent.Register( + "PullGestureEnded", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent ScrollGestureEvent = + RoutedEvent.Register( + "ScrollGesture", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent ScrollGestureInertiaStartingEvent = + RoutedEvent.Register( + "ScrollGestureInertiaStarting", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent ScrollGestureEndedEvent = + RoutedEvent.Register( + "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent PointerTouchPadGestureMagnifyEvent = + RoutedEvent.Register( + "PointerTouchPadGestureMagnify", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent PointerTouchPadGestureRotateEvent = + RoutedEvent.Register( + "PointerTouchPadGestureRotate", RoutingStrategies.Bubble, typeof(InputElement)); + + public static readonly RoutedEvent PointerTouchPadGestureSwipeEvent = + RoutedEvent.Register( + "PointerTouchPadGestureSwipe", RoutingStrategies.Bubble, typeof(InputElement)); + + /// + /// Defines the event. + /// + public static readonly RoutedEvent TappedEvent = + RoutedEvent.Register( + nameof(Tapped), + RoutingStrategies.Bubble); + + /// + /// Defines the event. + /// + public static readonly RoutedEvent RightTappedEvent = + RoutedEvent.Register( + nameof(RightTapped), + RoutingStrategies.Bubble); + + /// + /// Defines the event. + /// + public static readonly RoutedEvent HoldingEvent = + RoutedEvent.Register( + nameof(Holding), + RoutingStrategies.Bubble); + + /// + /// Defines the event. + /// + public static readonly RoutedEvent DoubleTappedEvent = + RoutedEvent.Register( + nameof(DoubleTapped), + RoutingStrategies.Bubble); + + public static bool GetIsHoldingEnabled(StyledElement element) + { + return element.GetValue(IsHoldingEnabledProperty); + } + public static void SetIsHoldingEnabled(StyledElement element, bool value) + { + element.SetValue(IsHoldingEnabledProperty, value); + } + + public static bool GetIsHoldWithMouseEnabled(StyledElement element) + { + return element.GetValue(IsHoldWithMouseEnabledProperty); + } + public static void SetIsHoldWithMouseEnabled(StyledElement element, bool value) + { + element.SetValue(IsHoldWithMouseEnabledProperty, value); + } + + public static void AddPinchHandler(Interactive element, EventHandler handler) => + element.AddHandler(PinchEvent, handler); + + public static void AddPinchEndedHandler(Interactive element, EventHandler handler) => + element.AddHandler(PinchEndedEvent, handler); + + public static void AddPullGestureHandler(Interactive element, EventHandler handler) => + element.AddHandler(PullGestureEvent, handler); + + public static void AddPullGestureEndedHandler(Interactive element, EventHandler handler) => + element.AddHandler(PullGestureEndedEvent, handler); + + public static void RemovePinchHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(PinchEvent, handler); + + public static void RemovePinchEndedHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(PinchEndedEvent, handler); + + public static void RemovePullGestureHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(PullGestureEvent, handler); + + public static void RemovePullGestureEndedHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(PullGestureEndedEvent, handler); + + public static void AddPointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler handler) => + element.AddHandler(PointerTouchPadGestureMagnifyEvent, handler); + + public static void AddPointerTouchPadGestureRotateHandler(Interactive element, EventHandler handler) => + element.AddHandler(PointerTouchPadGestureRotateEvent, handler); + + public static void AddPointerTouchPadGestureSwipeHandler(Interactive element, EventHandler handler) => + element.AddHandler(PointerTouchPadGestureSwipeEvent, handler); + + public static void AddScrollGestureHandler(Interactive element, EventHandler handler) => + element.AddHandler(ScrollGestureEvent, handler); + + public static void AddScrollGestureEndedHandler(Interactive element, EventHandler handler) => + element.AddHandler(ScrollGestureEndedEvent, handler); + + public static void AddScrollGestureInertiaStartingHandler(Interactive element, EventHandler handler) => + element.AddHandler(ScrollGestureInertiaStartingEvent, handler); + + public static void RemovePointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(PointerTouchPadGestureMagnifyEvent, handler); + + public static void RemovePointerTouchPadGestureRotateHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(PointerTouchPadGestureRotateEvent, handler); + + public static void RemovePointerTouchPadGestureSwipeHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(PointerTouchPadGestureSwipeEvent, handler); + + public static void RemoveScrollGestureHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(ScrollGestureEvent, handler); + + public static void RemoveScrollGestureEndedHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(ScrollGestureEndedEvent, handler); + + public static void RemoveScrollGestureInertiaStartingHandler(Interactive element, EventHandler handler) => + element.RemoveHandler(ScrollGestureInertiaStartingEvent, handler); + + /// + /// Occurs when a tap gesture occurs on the control. + /// + public event EventHandler? Tapped + { + add { AddHandler(TappedEvent, value); } + remove { RemoveHandler(TappedEvent, value); } + } + + /// + /// Occurs when a right tap gesture occurs on the control. + /// + public event EventHandler? RightTapped + { + add { AddHandler(RightTappedEvent, value); } + remove { RemoveHandler(RightTappedEvent, value); } + } + + /// + /// Occurs when a hold gesture occurs on the control. + /// + public event EventHandler? Holding + { + add { AddHandler(HoldingEvent, value); } + remove { RemoveHandler(HoldingEvent, value); } + } + + /// + /// Occurs when a double-tap gesture occurs on the control. + /// + public event EventHandler? DoubleTapped + { + add { AddHandler(DoubleTappedEvent, value); } + remove { RemoveHandler(DoubleTappedEvent, value); } + } + + private static void OnPreviewHolding(object? sender, HoldingRoutedEventArgs e) + { + if (sender is InputElement inputElement) + { + inputElement.RaiseEvent(e); + + if (!e.Handled && e.HoldingState == HoldingState.Started) + { + var contextEvent = new ContextRequestedEventArgs(e.PointerEventArgs); + inputElement.RaiseEvent(contextEvent); + e.Handled = contextEvent.Handled; + + if (contextEvent.Handled) + { + inputElement._isContextMenuOnHolding = true; + } + } + else if (e.HoldingState == HoldingState.Canceled && inputElement._isContextMenuOnHolding) + { + inputElement.RaiseEvent(new RoutedEventArgs(InputElement.ContextCanceledEvent) + { + Source = inputElement + }); + + inputElement._isContextMenuOnHolding = false; + } + else if (e.HoldingState == HoldingState.Completed) + { + inputElement._isContextMenuOnHolding = false; + } + } + } + } +} diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs index f929dcbe05..98b988f8cd 100644 --- a/src/Avalonia.Base/Input/InputElement.cs +++ b/src/Avalonia.Base/Input/InputElement.cs @@ -16,7 +16,7 @@ namespace Avalonia.Input /// Implements input-related functionality for a control. /// [PseudoClasses(":disabled", ":focus", ":focus-visible", ":focus-within", ":pointerover")] - public class InputElement : Interactive, IInputElement + public partial class InputElement : Interactive, IInputElement { /// /// Defines the property. @@ -203,24 +203,20 @@ namespace Avalonia.Input RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// - /// Defines the event. + /// Provides event data for the event. /// - public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; - - /// - /// Defines the event. - /// - public static readonly RoutedEvent RightTappedEvent = Gestures.RightTappedEvent; - - /// - /// Defines the event. - /// - public static readonly RoutedEvent HoldingEvent = Gestures.HoldingEvent; + public static readonly RoutedEvent ContextRequestedEvent = + RoutedEvent.Register( + nameof(ContextRequested), + RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// - /// Defines the event. + /// Provides event data for the event. /// - public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; + public static readonly RoutedEvent ContextCanceledEvent = + RoutedEvent.Register( + nameof(ContextCanceled), + RoutingStrategies.Tunnel | RoutingStrategies.Bubble); private bool _isEffectivelyEnabled = true; private bool _isFocused; @@ -257,6 +253,12 @@ namespace Avalonia.Input DoubleTappedEvent.AddClassHandler((x, e) => x.OnDoubleTapped(e)); HoldingEvent.AddClassHandler((x, e) => x.OnHolding(e)); + Gestures.Tapped += (s, e) => (s as InputElement)?.RaiseEvent(e); + Gestures.RightTapped += (s, e) => (s as InputElement)?.RaiseEvent(e); + Gestures.DoubleTapped += (s, e) => (s as InputElement)?.RaiseEvent(e); + + Gestures.Holding += OnPreviewHolding; + // Gesture only handlers PointerMovedEvent.AddClassHandler((x, e) => x.OnGesturePointerMoved(e), handledEventsToo: true); PointerPressedEvent.AddClassHandler((x, e) => x.OnGesturePointerPressed(e), handledEventsToo: true); @@ -419,42 +421,6 @@ namespace Avalonia.Input remove { RemoveHandler(PointerWheelChangedEvent, value); } } - /// - /// Occurs when a tap gesture occurs on the control. - /// - public event EventHandler? Tapped - { - add { AddHandler(TappedEvent, value); } - remove { RemoveHandler(TappedEvent, value); } - } - - /// - /// Occurs when a right tap gesture occurs on the control. - /// - public event EventHandler? RightTapped - { - add { AddHandler(RightTappedEvent, value); } - remove { RemoveHandler(RightTappedEvent, value); } - } - - /// - /// Occurs when a hold gesture occurs on the control. - /// - public event EventHandler? Holding - { - add { AddHandler(HoldingEvent, value); } - remove { RemoveHandler(HoldingEvent, value); } - } - - /// - /// Occurs when a double-tap gesture occurs on the control. - /// - public event EventHandler? DoubleTapped - { - add { AddHandler(DoubleTappedEvent, value); } - remove { RemoveHandler(DoubleTappedEvent, value); } - } - /// /// Gets or sets a value indicating whether the control can receive focus. /// @@ -518,6 +484,24 @@ namespace Avalonia.Input internal set { SetAndRaise(IsPointerOverProperty, ref _isPointerOver, value); } } + /// + /// Occurs when the user has completed a context input gesture, such as a right-click. + /// + public event EventHandler? ContextRequested + { + add => AddHandler(ContextRequestedEvent, value); + remove => RemoveHandler(ContextRequestedEvent, value); + } + + /// + /// Occurs when the context input gesture continues into another gesture, to notify the element that the context flyout should not be opened. + /// + public event EventHandler? ContextCanceled + { + add => AddHandler(ContextCanceledEvent, value); + remove => RemoveHandler(ContextCanceledEvent, value); + } + /// /// Gets or sets a value that indicates whether the control is included in tab navigation. /// diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index d9766c1707..2d112efda1 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using Avalonia.Reactive; +using Avalonia.Input.GestureRecognizers; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Metadata; -using Avalonia.Platform; -using Avalonia.Utilities; using Avalonia.VisualTree; -using Avalonia.Input.GestureRecognizers; #pragma warning disable CS0618 namespace Avalonia.Input @@ -263,7 +260,7 @@ namespace Avalonia.Input if (source != null) { - var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureMagnifyEvent, source, + var e = new PointerDeltaEventArgs(InputElement.PointerTouchPadGestureMagnifyEvent, source, _pointer, root.RootElement, p, timestamp, props, inputModifiers, delta); source?.RaiseEvent(e); @@ -283,7 +280,7 @@ namespace Avalonia.Input if (source != null) { - var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureRotateEvent, source, + var e = new PointerDeltaEventArgs(InputElement.PointerTouchPadGestureRotateEvent, source, _pointer, root.RootElement, p, timestamp, props, inputModifiers, delta); source?.RaiseEvent(e); @@ -303,7 +300,7 @@ namespace Avalonia.Input if (source != null) { - var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureSwipeEvent, source, + var e = new PointerDeltaEventArgs(InputElement.PointerTouchPadGestureSwipeEvent, source, _pointer, root.RootElement, p, timestamp, props, inputModifiers, delta); source?.RaiseEvent(e); diff --git a/src/Avalonia.Base/Input/PinchEventArgs.cs b/src/Avalonia.Base/Input/PinchEventArgs.cs index be373f443a..4c037f30d7 100644 --- a/src/Avalonia.Base/Input/PinchEventArgs.cs +++ b/src/Avalonia.Base/Input/PinchEventArgs.cs @@ -4,13 +4,13 @@ namespace Avalonia.Input { public class PinchEventArgs : RoutedEventArgs { - public PinchEventArgs(double scale, Point scaleOrigin) : base(Gestures.PinchEvent) + public PinchEventArgs(double scale, Point scaleOrigin) : base(InputElement.PinchEvent) { Scale = scale; ScaleOrigin = scaleOrigin; } - public PinchEventArgs(double scale, Point scaleOrigin, double angle, double angleDelta) : base(Gestures.PinchEvent) + public PinchEventArgs(double scale, Point scaleOrigin, double angle, double angleDelta) : base(InputElement.PinchEvent) { Scale = scale; ScaleOrigin = scaleOrigin; @@ -41,7 +41,7 @@ namespace Avalonia.Input public class PinchEndedEventArgs : RoutedEventArgs { - public PinchEndedEventArgs() : base(Gestures.PinchEndedEvent) + public PinchEndedEventArgs() : base(InputElement.PinchEndedEvent) { } } diff --git a/src/Avalonia.Base/Input/PullGestureEventArgs.cs b/src/Avalonia.Base/Input/PullGestureEventArgs.cs index 34d95c87f4..14b78f8730 100644 --- a/src/Avalonia.Base/Input/PullGestureEventArgs.cs +++ b/src/Avalonia.Base/Input/PullGestureEventArgs.cs @@ -1,4 +1,3 @@ -using System; using Avalonia.Interactivity; namespace Avalonia.Input @@ -12,8 +11,8 @@ namespace Avalonia.Input private static int _nextId = 1; internal static int GetNextFreeId() => _nextId++; - - public PullGestureEventArgs(int id, Vector delta, PullDirection pullDirection) : base(Gestures.PullGestureEvent) + + public PullGestureEventArgs(int id, Vector delta, PullDirection pullDirection) : base(InputElement.PullGestureEvent) { Id = id; Delta = delta; @@ -26,7 +25,7 @@ namespace Avalonia.Input public int Id { get; } public PullDirection PullDirection { get; } - public PullGestureEndedEventArgs(int id, PullDirection pullDirection) : base(Gestures.PullGestureEndedEvent) + public PullGestureEndedEventArgs(int id, PullDirection pullDirection) : base(InputElement.PullGestureEndedEvent) { Id = id; PullDirection = pullDirection; diff --git a/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs b/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs index ac770fd0a2..150ac17226 100644 --- a/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs +++ b/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs @@ -14,7 +14,7 @@ namespace Avalonia.Input public static int GetNextFreeId() => _nextId++; - public ScrollGestureEventArgs(int id, Vector delta) : base(Gestures.ScrollGestureEvent) + public ScrollGestureEventArgs(int id, Vector delta) : base(InputElement.ScrollGestureEvent) { Id = id; Delta = delta; @@ -25,7 +25,7 @@ namespace Avalonia.Input { public int Id { get; } - public ScrollGestureEndedEventArgs(int id) : base(Gestures.ScrollGestureEndedEvent) + public ScrollGestureEndedEventArgs(int id) : base(InputElement.ScrollGestureEndedEvent) { Id = id; } @@ -36,7 +36,7 @@ namespace Avalonia.Input public int Id { get; } public Vector Inertia { get; } - internal ScrollGestureInertiaStartingEventArgs(int id, Vector inertia) : base(Gestures.ScrollGestureInertiaStartingEvent) + internal ScrollGestureInertiaStartingEventArgs(int id, Vector inertia) : base(InputElement.ScrollGestureInertiaStartingEvent) { Id = id; Inertia = inertia; diff --git a/src/Avalonia.Base/Input/TappedEventArgs.cs b/src/Avalonia.Base/Input/TappedEventArgs.cs index eaffa1d8bc..a235d495d6 100644 --- a/src/Avalonia.Base/Input/TappedEventArgs.cs +++ b/src/Avalonia.Base/Input/TappedEventArgs.cs @@ -1,6 +1,4 @@ -using System; using Avalonia.Interactivity; -using Avalonia.VisualTree; namespace Avalonia.Input { @@ -17,7 +15,7 @@ namespace Avalonia.Input public IPointer Pointer => lastPointerEventArgs.Pointer; public KeyModifiers KeyModifiers => lastPointerEventArgs.KeyModifiers; public ulong Timestamp => lastPointerEventArgs.Timestamp; - + public Point GetPosition(Visual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); } } diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 7790251f81..c3d66b711f 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using Avalonia.Automation.Peers; using System.Linq; +using Avalonia.Automation; +using Avalonia.Automation.Peers; using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Styling; -using Avalonia.Automation; using Avalonia.Reactive; +using Avalonia.Styling; namespace Avalonia.Controls { @@ -65,7 +65,7 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty WindowManagerAddShadowHintProperty = + public static readonly StyledProperty WindowManagerAddShadowHintProperty = Popup.WindowManagerAddShadowHintProperty.AddOwner(); /// @@ -205,6 +205,7 @@ namespace Avalonia.Controls if (e.OldValue is ContextMenu oldMenu) { control.ContextRequested -= ControlContextRequested; + control.ContextCanceled -= ControlContextCanceled; control.AttachedToVisualTree -= ControlOnAttachedToVisualTree; control.DetachedFromVisualTree -= ControlDetachedFromVisualTree; oldMenu._attachedControls?.Remove(control); @@ -214,13 +215,14 @@ namespace Avalonia.Controls if (e.NewValue is ContextMenu) { control.ContextRequested += ControlContextRequested; + control.ContextCanceled += ControlContextCanceled; control.AttachedToVisualTree += ControlOnAttachedToVisualTree; control.DetachedFromVisualTree += ControlDetachedFromVisualTree; } - + if (control.IsAttachedToVisualTree) { - AttachControlToContextMenu(control); + AttachControlToContextMenu(control); } } @@ -292,9 +294,9 @@ namespace Avalonia.Controls IPopupHost? IPopupHostProvider.PopupHost => _popup?.Host; - event Action? IPopupHostProvider.PopupHostChanged - { - add => _popupHostChangedHandler += value; + event Action? IPopupHostProvider.PopupHostChanged + { + add => _popupHostChangedHandler += value; remove => _popupHostChangedHandler -= value; } @@ -384,7 +386,7 @@ namespace Avalonia.Controls RoutedEvent = ClosedEvent, Source = this, }); - + _popupHostChangedHandler?.Invoke(null); } @@ -403,6 +405,17 @@ namespace Avalonia.Controls } } + private static void ControlContextCanceled(object? sender, RoutedEventArgs e) + { + if (!e.Handled + && sender is Control control + && control.ContextMenu is ContextMenu contextMenu + && contextMenu.IsOpen) + { + contextMenu.Close(); + } + } + private static void ControlContextRequested(object? sender, ContextRequestedEventArgs e) { if (sender is Control control @@ -412,14 +425,14 @@ namespace Avalonia.Controls { var requestedByPointer = e.TryGetPosition(null, out _); contextMenu.Open( - control, - e.Source as Control ?? control, + control, + e.Source as Control ?? control, requestedByPointer ? contextMenu.Placement : PlacementMode.Bottom); e.Handled = true; } } - - + + private static void ControlOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e) { AttachControlToContextMenu(sender); diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 6a6b9f6083..8efe5d6d66 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -23,7 +23,6 @@ namespace Avalonia.Controls /// The control class extends and adds the following features: /// /// - A property to allow user-defined data to be attached to the control. - /// - and other context menu related members. /// public class Control : InputElement, IDataTemplateHost, IVisualBrushInitialize, ISetterValue { @@ -58,14 +57,6 @@ namespace Avalonia.Controls RoutedEvent.Register( "RequestBringIntoView", RoutingStrategies.Bubble); - - /// - /// Provides event data for the event. - /// - public static readonly RoutedEvent ContextRequestedEvent = - RoutedEvent.Register( - nameof(ContextRequested), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. @@ -163,15 +154,6 @@ namespace Avalonia.Controls set => SetValue(TagProperty, value); } - /// - /// Occurs when the user has completed a context input gesture, such as a right-click. - /// - public event EventHandler? ContextRequested - { - add => AddHandler(ContextRequestedEvent, value); - remove => RemoveHandler(ContextRequestedEvent, value); - } - /// /// Occurs when the control has been fully constructed in the visual tree and both /// layout and render are complete. @@ -389,20 +371,6 @@ namespace Avalonia.Controls ScheduleOnLoadedCore(); } - protected override void OnHolding(HoldingRoutedEventArgs e) - { - base.OnHolding(e); - - if (e.Source == this && !e.Handled && e.HoldingState == HoldingState.Started) - { - // Trigger ContentRequest when hold has started - var contextEvent = e.PointerEventArgs is { } ev ? new ContextRequestedEventArgs(ev) : new ContextRequestedEventArgs(); - RaiseEvent(contextEvent); - - e.Handled = contextEvent.Handled; - } - } - /// protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) { diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index 11fc695d15..a28cecd149 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -352,13 +352,13 @@ namespace Avalonia.Controls.Primitives { base.OnAttachedToVisualTree(e); _parentScroller = this.GetVisualParent() as ScrollContentPresenter; - _parentScroller?.AddHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + _parentScroller?.AddHandler(InputElement.ScrollGestureEndedEvent, OnScrollGestureEnded); } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); - _parentScroller?.RemoveHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + _parentScroller?.RemoveHandler(InputElement.ScrollGestureEndedEvent, OnScrollGestureEnded); _parentScroller = null; } diff --git a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs index dd0b5810bf..19d1f52850 100644 --- a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs @@ -4,12 +4,11 @@ using System.Linq; using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; -using Avalonia.Input.Platform; using Avalonia.Input.Raw; +using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Logging; using Avalonia.Reactive; -using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives { @@ -22,11 +21,11 @@ namespace Avalonia.Controls.Primitives /// public static readonly StyledProperty HorizontalOffsetProperty = Popup.HorizontalOffsetProperty.AddOwner(); - + /// public static readonly StyledProperty VerticalOffsetProperty = Popup.VerticalOffsetProperty.AddOwner(); - + /// public static readonly StyledProperty PlacementAnchorProperty = Popup.PlacementAnchorProperty.AddOwner(); @@ -50,19 +49,19 @@ namespace Avalonia.Controls.Primitives /// public static readonly StyledProperty OverlayDismissEventPassThroughProperty = Popup.OverlayDismissEventPassThroughProperty.AddOwner(); - + /// /// Defines the property /// public static readonly StyledProperty OverlayInputPassThroughElementProperty = Popup.OverlayInputPassThroughElementProperty.AddOwner(); - + /// /// Defines the property /// public static readonly StyledProperty PlacementConstraintAdjustmentProperty = Popup.PlacementConstraintAdjustmentProperty.AddOwner(); - + private readonly Lazy _popupLazy; private Rect? _enlargedPopupRect; private PixelRect? _enlargePopupRectScreenPixelRect; @@ -147,7 +146,7 @@ namespace Avalonia.Controls.Primitives get => GetValue(OverlayDismissEventPassThroughProperty); set => SetValue(OverlayDismissEventPassThroughProperty, value); } - + /// /// Gets or sets an element that should receive pointer input events even when underneath /// the flyout's overlay. @@ -164,7 +163,7 @@ namespace Avalonia.Controls.Primitives get => GetValue(PlacementConstraintAdjustmentProperty); set => SetValue(PlacementConstraintAdjustmentProperty, value); } - + IPopupHost? IPopupHostProvider.PopupHost => Popup?.Host; event Action? IPopupHostProvider.PopupHostChanged @@ -172,7 +171,7 @@ namespace Avalonia.Controls.Primitives add => _popupHostChangedHandler += value; remove => _popupHostChangedHandler -= value; } - + public event EventHandler? Closing; public event EventHandler? Opening; @@ -331,7 +330,7 @@ namespace Avalonia.Controls.Primitives if (Popup?.Host is PopupRoot root) { // Get the popup root bounds and convert to screen coordinates - + var tmp = root.Bounds.Inflate(100); _enlargePopupRectScreenPixelRect = new PixelRect(root.PointToScreen(tmp.TopLeft), root.PointToScreen(tmp.BottomRight)); } @@ -344,7 +343,7 @@ namespace Avalonia.Controls.Primitives return; } - if (Popup?.Host is PopupRoot && pArgs.Root.RootElement is {} eventRoot) + if (Popup?.Host is PopupRoot && pArgs.Root.RootElement is { } eventRoot) { // As long as the pointer stays within the enlargedPopupRect // the flyout stays open. If it leaves, close it @@ -373,7 +372,7 @@ namespace Avalonia.Controls.Primitives { Opening?.Invoke(this, args); } - + protected virtual void OnClosing(CancelEventArgs args) { Closing?.Invoke(this, args); @@ -475,14 +474,27 @@ namespace Avalonia.Controls.Primitives if (args.OldValue is FlyoutBase) { c.ContextRequested -= OnControlContextRequested; + c.ContextCanceled -= OnControlContextCanceled; } if (args.NewValue is FlyoutBase) { c.ContextRequested += OnControlContextRequested; + c.ContextCanceled += OnControlContextCanceled; } } } + private static void OnControlContextCanceled(object? sender, RoutedEventArgs e) + { + if (!e.Handled + && sender is Control control + && control.ContextFlyout is { } flyout + && flyout.IsOpen) + { + flyout.Hide(); + } + } + private static void OnControlContextRequested(object? sender, ContextRequestedEventArgs e) { if (!e.Handled @@ -525,7 +537,7 @@ namespace Avalonia.Controls.Primitives internal static void SetPresenterClasses(Control? presenter, Classes classes) { - if(presenter is null) + if (presenter is null) { return; } diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 6527a14d5d..89993d8e66 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; -using Avalonia.Reactive; +using System.Linq; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Input.GestureRecognizers; +using Avalonia.Layout; +using Avalonia.Reactive; using Avalonia.Utilities; using Avalonia.VisualTree; -using System.Linq; -using Avalonia.Layout; namespace Avalonia.Controls.Presenters { @@ -118,9 +118,9 @@ namespace Avalonia.Controls.Presenters public ScrollContentPresenter() { AddHandler(RequestBringIntoViewEvent, BringIntoViewRequested); - AddHandler(Gestures.ScrollGestureEvent, OnScrollGesture); - AddHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); - AddHandler(Gestures.ScrollGestureInertiaStartingEvent, OnScrollGestureInertiaStartingEnded); + AddHandler(InputElement.ScrollGestureEvent, OnScrollGesture); + AddHandler(InputElement.ScrollGestureEndedEvent, OnScrollGestureEnded); + AddHandler(InputElement.ScrollGestureInertiaStartingEvent, OnScrollGestureInertiaStartingEnded); this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription); } diff --git a/src/Avalonia.Controls/Primitives/ItemSelectionEventTriggers.cs b/src/Avalonia.Controls/Primitives/ItemSelectionEventTriggers.cs index 196e2b98ed..bebd8080f3 100644 --- a/src/Avalonia.Controls/Primitives/ItemSelectionEventTriggers.cs +++ b/src/Avalonia.Controls/Primitives/ItemSelectionEventTriggers.cs @@ -31,7 +31,7 @@ public static class ItemSelectionEventTriggers } => false, // Select on mouse press, unless the mouse can generate gestures - { Pointer.Type: PointerType.Mouse } => eventArgs.RoutedEvent == (Gestures.GetIsHoldWithMouseEnabled(selectable) ? + { Pointer.Type: PointerType.Mouse } => eventArgs.RoutedEvent == (InputElement.GetIsHoldWithMouseEnabled(selectable) ? InputElement.PointerReleasedEvent : (RoutedEvent)InputElement.PointerPressedEvent), // Pen "right clicks" are used for context menus, and gestures are only processed for primary input diff --git a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs index 7ff02711d6..48063d90f8 100644 --- a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs +++ b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs @@ -89,8 +89,8 @@ namespace Avalonia.Controls.PullToRefresh if (_refreshInfoProvider != null && _interactionSource != null) { - _interactionSource.RemoveHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); - _interactionSource.RemoveHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); + _interactionSource.RemoveHandler(InputElement.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); + _interactionSource.RemoveHandler(InputElement.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); } _refreshInfoProvider = null; @@ -129,8 +129,8 @@ namespace Avalonia.Controls.PullToRefresh if (_interactionSource != null) { _interactionSource.GestureRecognizers.Add(_pullGestureRecognizer); - _interactionSource.AddHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); - _interactionSource.AddHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); + _interactionSource.AddHandler(InputElement.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); + _interactionSource.AddHandler(InputElement.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); _isVisualizerInteractionSourceAttached = true; } @@ -169,11 +169,11 @@ namespace Avalonia.Controls.PullToRefresh visualizerComposition.ImplicitAnimations = animation; } - if(_scrollViewer != null) + if (_scrollViewer != null) { var scollContentComposition = ElementComposition.GetElementVisual(_scrollViewer); - if(scollContentComposition != null) + if (scollContentComposition != null) { var compositor = scollContentComposition.Compositor; @@ -217,8 +217,8 @@ namespace Avalonia.Controls.PullToRefresh if (_pullGestureRecognizer != null && _refreshInfoProvider != null) { element?.GestureRecognizers.Add(_pullGestureRecognizer); - _interactionSource?.AddHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); - _interactionSource?.AddHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); + _interactionSource?.AddHandler(InputElement.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); + _interactionSource?.AddHandler(InputElement.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); _isVisualizerInteractionSourceAttached = true; } } diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index e0a35b9ab2..d46bf5084e 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using Avalonia.Base.UnitTests.Utilities; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.GestureRecognizers; @@ -9,7 +7,6 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Threading; using Avalonia.UnitTests; -using Avalonia.Utilities; using Moq; using Xunit; // ReSharper disable RedundantArgumentDefaultValue @@ -19,7 +16,7 @@ namespace Avalonia.Base.UnitTests.Input public class GesturesTests { private readonly MouseTestHelper _mouse = new MouseTestHelper(); - + [Fact] public void Tapped_Should_Follow_Pointer_Pressed_Released() { @@ -64,7 +61,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true); + root.AddHandler(InputElement.TappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Middle); @@ -81,7 +78,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true); + root.AddHandler(InputElement.TappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Right); @@ -114,7 +111,7 @@ namespace Avalonia.Base.UnitTests.Input }; _mouse.Click(border, MouseButton.Left); - root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true); + root.AddHandler(InputElement.TappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Left); @@ -132,7 +129,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.RightTappedEvent, (_, _) => raised = true); + root.AddHandler(InputElement.RightTappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Right); @@ -185,7 +182,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true); + root.AddHandler(InputElement.DoubleTappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Middle); _mouse.Down(border, MouseButton.Middle, clickCount: 2); @@ -203,7 +200,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true); + root.AddHandler(InputElement.DoubleTappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Right); _mouse.Down(border, MouseButton.Right, clickCount: 2); @@ -220,22 +217,22 @@ namespace Avalonia.Base.UnitTests.Input iSettingsMock.Setup(x => x.GetTapSize(It.IsAny())).Returns(new Size(16, 16)); AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(iSettingsMock.Object); - + using var app = UnitTestApplication.Start(); Border border = new Border(); - Gestures.SetIsHoldWithMouseEnabled(border, true); + InputElement.SetIsHoldWithMouseEnabled(border, true); var root = new TestRoot { Child = border }; - HoldingState holding = HoldingState.Cancelled; + HoldingState holding = HoldingState.Canceled; + + root.AddHandler(InputElement.HoldingEvent, (_, e) => holding = e.HoldingState); - root.AddHandler(Gestures.HoldingEvent, (_, e) => holding = e.HoldingState); - _mouse.Down(border); - Assert.False(holding != HoldingState.Cancelled); - + Assert.False(holding != HoldingState.Canceled); + // Verify timer duration, but execute it immediately. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests()); Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval); @@ -247,7 +244,80 @@ namespace Avalonia.Base.UnitTests.Input Assert.True(holding == HoldingState.Completed); } - + + [Fact] + public void Started_Hold_Gesture_Should_Raise_Context_Requested_Event() + { + using var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + iSettingsMock.Setup(x => x.GetTapSize(It.IsAny())).Returns(new Size(16, 16)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + + using var app = UnitTestApplication.Start(); + + var flyout = new Flyout(); + Border border = new Border() + { + ContextFlyout = flyout + }; + InputElement.SetIsHoldWithMouseEnabled(border, true); + var root = new TestRoot + { + Child = border + }; + + var contextRequested = false; + + flyout.Opened += (s, e) => contextRequested = true; + + _mouse.Down(border); + var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests()); + timer.ForceFire(); + _mouse.Up(border); + + Assert.True(contextRequested); + } + + [Fact] + public void Cancelled_Hold_Gesture_Should_Cancel_Context_Flyout() + { + using var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + iSettingsMock.Setup(x => x.GetTapSize(It.IsAny())).Returns(new Size(16, 16)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + + using var app = UnitTestApplication.Start(); + + var flyout = new Flyout(); + Border border = new Border() + { + ContextFlyout = flyout + }; + InputElement.SetIsHoldWithMouseEnabled(border, true); + var root = new TestRoot + { + Child = border + }; + + var contextRequested = false; + var contextCanceled = false; + + flyout.Opened += (s, e) => contextRequested = true; + flyout.Closed += (s, e) => contextCanceled = true; + + _mouse.Down(border); + var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests()); + timer.ForceFire(); + _mouse.Move(border, new Point(100, 100)); + + Assert.True(contextRequested); + Assert.True(contextCanceled); + } + [Fact] public void Hold_Should_Not_Raised_When_Pointer_Released_Before_Timer() { @@ -256,25 +326,25 @@ namespace Avalonia.Base.UnitTests.Input iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(iSettingsMock.Object); - + using var app = UnitTestApplication.Start(); Border border = new Border(); - Gestures.SetIsHoldWithMouseEnabled(border, true); + InputElement.SetIsHoldWithMouseEnabled(border, true); var root = new TestRoot { Child = border }; var raised = false; - root.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Started); - + root.AddHandler(InputElement.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Started); + _mouse.Down(border); Assert.False(raised); - + _mouse.Up(border); Assert.False(raised); - + // Verify timer duration, but execute it immediately. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests()); Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval); @@ -282,7 +352,7 @@ namespace Avalonia.Base.UnitTests.Input Assert.False(raised); } - + [Fact] public void Hold_Should_Not_Raised_When_Pointer_Is_Moved_Before_Timer() { @@ -291,25 +361,25 @@ namespace Avalonia.Base.UnitTests.Input iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(iSettingsMock.Object); - + using var app = UnitTestApplication.Start(); Border border = new Border(); - Gestures.SetIsHoldWithMouseEnabled(border, true); + InputElement.SetIsHoldWithMouseEnabled(border, true); var root = new TestRoot { Child = border }; var raised = false; - root.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); - + root.AddHandler(InputElement.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); + _mouse.Down(border); Assert.False(raised); _mouse.Move(border, position: new Point(20, 20)); Assert.False(raised); - + // Verify timer duration, but execute it immediately. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests()); Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval); @@ -326,19 +396,19 @@ namespace Avalonia.Base.UnitTests.Input iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(iSettingsMock.Object); - + using var app = UnitTestApplication.Start(); Border border = new Border(); - Gestures.SetIsHoldWithMouseEnabled(border, true); + InputElement.SetIsHoldWithMouseEnabled(border, true); var root = new TestRoot { Child = border }; var cancelled = false; - root.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled); - + root.AddHandler(InputElement.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Canceled); + _mouse.Down(border); Assert.False(cancelled); @@ -366,15 +436,15 @@ namespace Avalonia.Base.UnitTests.Input using var app = UnitTestApplication.Start(); Border border = new Border(); - Gestures.SetIsHoldWithMouseEnabled(border, true); + InputElement.SetIsHoldWithMouseEnabled(border, true); var root = new TestRoot() { Child = border }; var cancelled = false; - root.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled); - + root.AddHandler(InputElement.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Canceled); + _mouse.Down(border); var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests()); @@ -400,16 +470,16 @@ namespace Avalonia.Base.UnitTests.Input .Bind().ToConstant(iSettingsMock.Object); using var app = UnitTestApplication.Start(); - + Border border = new Border(); - Gestures.SetIsHoldWithMouseEnabled(border, true); + InputElement.SetIsHoldWithMouseEnabled(border, true); var testRoot = new TestRoot { Child = border }; var raised = false; - testRoot.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); + testRoot.AddHandler(InputElement.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); var secondMouse = new MouseTestHelper(); @@ -424,7 +494,7 @@ namespace Avalonia.Base.UnitTests.Input Assert.False(raised); } - + private static void AddHandlers( TestRoot root, Border border, @@ -454,10 +524,10 @@ namespace Avalonia.Base.UnitTests.Input border.AddHandler(InputElement.PointerPressedEvent, (_, _) => result.Add("bp")); border.AddHandler(InputElement.PointerReleasedEvent, (_, _) => result.Add("br")); - root.AddHandler(Gestures.TappedEvent, (_, _) => result.Add("dt")); - root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => result.Add("ddt")); - border.AddHandler(Gestures.TappedEvent, (_, _) => result.Add("bt")); - border.AddHandler(Gestures.DoubleTappedEvent, (_, _) => result.Add("bdt")); + root.AddHandler(InputElement.TappedEvent, (_, _) => result.Add("dt")); + root.AddHandler(InputElement.DoubleTappedEvent, (_, _) => result.Add("ddt")); + border.AddHandler(InputElement.TappedEvent, (_, _) => result.Add("bt")); + border.AddHandler(InputElement.DoubleTappedEvent, (_, _) => result.Add("bdt")); } [Fact] @@ -478,7 +548,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true); + root.AddHandler(InputElement.PinchEvent, (_, _) => raised = true); var firstPoint = new Point(5, 5); var secondPoint = new Point(10, 10); @@ -506,7 +576,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true); + root.AddHandler(InputElement.PinchEvent, (_, _) => raised = true); var firstPoint = new Point(5, 5); var secondPoint = new Point(10, 10); @@ -537,7 +607,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true); + root.AddHandler(InputElement.PinchEvent, (_, _) => raised = true); var firstPoint = new Point(5, 5); var secondPoint = new Point(10, 10); @@ -576,7 +646,7 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - root.AddHandler(Gestures.ScrollGestureEvent, (_, _) => raised = true); + root.AddHandler(InputElement.ScrollGestureEvent, (_, _) => raised = true); var firstTouch = new TouchTestHelper(); diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index 51ed2296a3..914351f245 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -83,7 +83,7 @@ namespace Avalonia.Controls.UnitTests ContextMenu = sut }; var contextRequestedCount = 0; - target.AddHandler(Control.ContextRequestedEvent, (s, a) => contextRequestedCount++, Interactivity.RoutingStrategies.Tunnel); + target.AddHandler(InputElement.ContextRequestedEvent, (s, a) => contextRequestedCount++, Interactivity.RoutingStrategies.Tunnel); var window = PreparedWindow(target); window.Show(); diff --git a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs index 43a6e4b42a..4d8a90f5e7 100644 --- a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs +++ b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs @@ -459,7 +459,7 @@ namespace Avalonia.Controls.UnitTests ContextFlyout = flyout }; var contextRequestedCount = 0; - target.AddHandler(Control.ContextRequestedEvent, (s, a) => contextRequestedCount++, Interactivity.RoutingStrategies.Tunnel); + target.AddHandler(InputElement.ContextRequestedEvent, (s, a) => contextRequestedCount++, Interactivity.RoutingStrategies.Tunnel); var window = PreparedWindow(target); window.Show(); diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs index 9faa3fdee0..f0f9b820f7 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs @@ -149,7 +149,7 @@ namespace Avalonia.Controls.UnitTests Prepare(target); var contextRaised = false; - target.AddHandler(Control.ContextRequestedEvent, (sender, args) => + target.AddHandler(InputElement.ContextRequestedEvent, (sender, args) => { contextRaised = true; args.Handled = true; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs index 91e9b1a7ba..a5453d4436 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs @@ -26,14 +26,14 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml [Fact] public void Attached_Event_Is_Assigned() { - var xaml = @"