Browse Source

Move Gesture events to InputElement (#20789)

* move gesture events from Gestures to InputElement. Fix holding gesture interactions with context menu

* update api diff

* address review comments

* fix some test types

* rename Cancelled to Canceled

* update api diff
text_selector_zindex
Emmanuel Hansen 3 weeks ago
committed by GitHub
parent
commit
dc8dc3bd2e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 108
      api/Avalonia.nupkg.xml
  2. 1
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
  3. 1
      samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
  4. 12
      samples/ControlCatalog/Pages/GesturePage.cs
  5. 2
      samples/IntegrationTestApp/Pages/GesturesPage.axaml
  6. 13
      src/Avalonia.Base/Input/ContextRequestedEventArgs.cs
  7. 225
      src/Avalonia.Base/Input/Gestures.cs
  8. 17
      src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs
  9. 241
      src/Avalonia.Base/Input/InputElement.Gestures.cs
  10. 86
      src/Avalonia.Base/Input/InputElement.cs
  11. 11
      src/Avalonia.Base/Input/MouseDevice.cs
  12. 6
      src/Avalonia.Base/Input/PinchEventArgs.cs
  13. 5
      src/Avalonia.Base/Input/PullGestureEventArgs.cs
  14. 6
      src/Avalonia.Base/Input/ScrollGestureEventArgs.cs
  15. 2
      src/Avalonia.Base/Input/TappedEventArgs.cs
  16. 19
      src/Avalonia.Controls/ContextMenu.cs
  17. 32
      src/Avalonia.Controls/Control.cs
  18. 4
      src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
  19. 16
      src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
  20. 12
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  21. 2
      src/Avalonia.Controls/Primitives/ItemSelectionEventTriggers.cs
  22. 12
      src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs
  23. 132
      tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs
  24. 2
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
  25. 2
      tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
  26. 2
      tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs
  27. 6
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs

108
api/Avalonia.nupkg.xml

@ -103,6 +103,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Input.Gestures</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Input.IDataObject</Target>
@ -247,6 +253,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.ContextRequestedEventArgs</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Diagnostics.IPopupHostProvider</Target>
@ -499,6 +511,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Input.Gestures</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Input.IDataObject</Target>
@ -643,6 +661,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.ContextRequestedEventArgs</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Diagnostics.IPopupHostProvider</Target>
@ -823,6 +847,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Input.HoldingState.Cancelled</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Media.DrawingImage.ViewboxProperty</Target>
@ -865,6 +895,24 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.CompiledBindingPathBuilder.StreamObservable</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.CompiledBindingPathBuilder.StreamTask</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.CompiledBindingPathBuilder.TypeCast(System.Type)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.ReflectionBinding.#ctor(System.String,Avalonia.Data.BindingMode)</Target>
@ -913,6 +961,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.HoldingRoutedEventArgs.#ctor(Avalonia.Input.HoldingState,Avalonia.Point,Avalonia.Input.PointerType)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.IInputRoot.get_KeyboardNavigationHandler</Target>
@ -1321,6 +1375,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Control.ContextRequestedEvent</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty</Target>
@ -1939,6 +1999,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Input.HoldingState.Cancelled</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Media.DrawingImage.ViewboxProperty</Target>
@ -1981,6 +2047,24 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.CompiledBindingPathBuilder.StreamObservable</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.CompiledBindingPathBuilder.StreamTask</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.CompiledBindingPathBuilder.TypeCast(System.Type)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Data.ReflectionBinding.#ctor(System.String,Avalonia.Data.BindingMode)</Target>
@ -2029,6 +2113,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.HoldingRoutedEventArgs.#ctor(Avalonia.Input.HoldingState,Avalonia.Point,Avalonia.Input.PointerType)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.IInputRoot.get_KeyboardNavigationHandler</Target>
@ -2437,6 +2527,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Control.ContextRequestedEvent</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty</Target>
@ -3817,6 +3913,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Input.HoldingRoutedEventArgs</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.AdornerLayer</Target>
@ -3853,6 +3955,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Input.HoldingRoutedEventArgs</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.AdornerLayer</Target>

1
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;

1
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

12
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)

2
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"/>
<Border Name="GestureBorder2" Background="Green" IsVisible="False"
AutomationProperties.AccessibilityView="Content"
AutomationProperties.ControlTypeOverride="Image"

13
src/Avalonia.Controls/ContextRequestedEventArgs.cs → src/Avalonia.Base/Input/ContextRequestedEventArgs.cs

@ -1,7 +1,6 @@
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Interactivity;
namespace Avalonia.Controls
namespace Avalonia.Input
{
/// <summary>
/// Provides event data for the ContextRequested event.
@ -14,7 +13,7 @@ namespace Avalonia.Controls
/// Initializes a new instance of the ContextRequestedEventArgs class.
/// </summary>
public ContextRequestedEventArgs()
: base(Control.ContextRequestedEvent)
: base(InputElement.ContextRequestedEvent)
{
}
@ -34,10 +33,10 @@ namespace Avalonia.Controls
}
/// <summary>
/// Gets the x- and y-coordinates of the pointer position, optionally evaluated against a coordinate origin of a supplied <see cref="Control"/>.
/// Gets the x- and y-coordinates of the pointer position, optionally evaluated against a coordinate origin of a supplied <see cref="InputElement"/>.
/// </summary>
/// <param name="relativeTo">
/// Any <see cref="Control"/>-derived object that is connected to the same object tree.
/// Any <see cref="InputElement"/>-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.
/// </param>
/// <param name="point">
@ -48,7 +47,7 @@ namespace Avalonia.Controls
/// <returns>
/// true if the context request was initiated by a pointer device; otherwise, false.
/// </returns>
public bool TryGetPosition(Control? relativeTo, out Point point)
public bool TryGetPosition(InputElement? relativeTo, out Point point)
{
if (_pointerEventArgs is null)
{

225
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<HoldingRoutedEventArgs>? Holding;
public static event EventHandler<TappedEventArgs>? Tapped;
public static event EventHandler<TappedEventArgs>? RightTapped;
public static event EventHandler<TappedEventArgs>? 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;
/// <summary>
/// Defines the IsHoldingEnabled attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsHoldingEnabledProperty =
AvaloniaProperty.RegisterAttached<StyledElement, bool>("IsHoldingEnabled", typeof(Gestures), true);
/// <summary>
/// Defines the IsHoldWithMouseEnabled attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsHoldWithMouseEnabledProperty =
AvaloniaProperty.RegisterAttached<StyledElement, bool>("IsHoldWithMouseEnabled", typeof(Gestures), false);
public static readonly RoutedEvent<TappedEventArgs> TappedEvent = RoutedEvent.Register<TappedEventArgs>(
"Tapped",
RoutingStrategies.Bubble,
typeof(Gestures));
public static readonly RoutedEvent<TappedEventArgs> DoubleTappedEvent = RoutedEvent.Register<TappedEventArgs>(
"DoubleTapped",
RoutingStrategies.Bubble,
typeof(Gestures));
public static readonly RoutedEvent<TappedEventArgs> RightTappedEvent = RoutedEvent.Register<TappedEventArgs>(
"RightTapped",
RoutingStrategies.Bubble,
typeof(Gestures));
public static readonly RoutedEvent<ScrollGestureEventArgs> ScrollGestureEvent =
RoutedEvent.Register<ScrollGestureEventArgs>(
"ScrollGesture", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<ScrollGestureInertiaStartingEventArgs> ScrollGestureInertiaStartingEvent =
RoutedEvent.Register<ScrollGestureInertiaStartingEventArgs>(
"ScrollGestureInertiaStarting", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<ScrollGestureEndedEventArgs> ScrollGestureEndedEvent =
RoutedEvent.Register<ScrollGestureEndedEventArgs>(
"ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureMagnifyEvent =
RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerTouchPadGestureMagnify", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureRotateEvent =
RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerTouchPadGestureRotate", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureSwipeEvent =
RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerTouchPadGestureSwipe", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<PinchEventArgs> PinchEvent =
RoutedEvent.Register<PinchEventArgs>(
"Pinch", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<PinchEndedEventArgs> PinchEndedEvent =
RoutedEvent.Register<PinchEndedEventArgs>(
"PinchEnded", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<PullGestureEventArgs> PullGestureEvent =
RoutedEvent.Register<PullGestureEventArgs>(
"PullGesture", RoutingStrategies.Bubble, typeof(Gestures));
/// <summary>
/// Occurs when a user performs a press and hold gesture (with a single touch, mouse, or pen/stylus contact).
/// </summary>
public static readonly RoutedEvent<HoldingRoutedEventArgs> HoldingEvent =
RoutedEvent.Register<HoldingRoutedEventArgs>(
"Holding", RoutingStrategies.Bubble, typeof(Gestures));
public static readonly RoutedEvent<PullGestureEndedEventArgs> PullGestureEndedEvent =
RoutedEvent.Register<PullGestureEndedEventArgs>(
"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<RoutedEventArgs> handler)
{
element.AddHandler(TappedEvent, handler);
}
public static void AddDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.AddHandler(DoubleTappedEvent, handler);
}
public static void AddRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.AddHandler(RightTappedEvent, handler);
}
public static void AddHoldingHandler(Interactive element, EventHandler<HoldingRoutedEventArgs> handler) =>
element.AddHandler(HoldingEvent, handler);
public static void AddPinchHandler(Interactive element, EventHandler<PinchEventArgs> handler) =>
element.AddHandler(PinchEvent, handler);
public static void AddPinchEndedHandler(Interactive element, EventHandler<PinchEndedEventArgs> handler) =>
element.AddHandler(PinchEndedEvent, handler);
public static void AddPullGestureHandler(Interactive element, EventHandler<PullGestureEventArgs> handler) =>
element.AddHandler(PullGestureEvent, handler);
public static void AddPullGestureEndedHandler(Interactive element, EventHandler<PullGestureEndedEventArgs> handler) =>
element.AddHandler(PullGestureEndedEvent, handler);
public static void AddPointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.AddHandler(PointerTouchPadGestureMagnifyEvent, handler);
public static void AddPointerTouchPadGestureRotateHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.AddHandler(PointerTouchPadGestureRotateEvent, handler);
public static void AddPointerTouchPadGestureSwipeHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.AddHandler(PointerTouchPadGestureSwipeEvent, handler);
public static void AddScrollGestureHandler(Interactive element, EventHandler<RoutedEventArgs> handler) =>
element.AddHandler(ScrollGestureEvent, handler);
public static void AddScrollGestureEndedHandler(Interactive element, EventHandler<ScrollGestureEndedEventArgs> handler) =>
element.AddHandler(ScrollGestureEndedEvent, handler);
public static void AddScrollGestureInertiaStartingHandler(Interactive element, EventHandler<ScrollGestureInertiaStartingEventArgs> handler) =>
element.AddHandler(ScrollGestureInertiaStartingEvent, handler);
public static void RemoveTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.RemoveHandler(TappedEvent, handler);
}
public static void RemoveDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.RemoveHandler(DoubleTappedEvent, handler);
}
public static void RemoveRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.RemoveHandler(RightTappedEvent, handler);
}
public static void RemoveHoldingHandler(Interactive element, EventHandler<RoutedEventArgs> handler) =>
element.RemoveHandler(HoldingEvent, handler);
public static void RemovePinchHandler(Interactive element, EventHandler<PinchEventArgs> handler) =>
element.RemoveHandler(PinchEvent, handler);
public static void RemovePinchEndedHandler(Interactive element, EventHandler<PinchEndedEventArgs> handler) =>
element.RemoveHandler(PinchEndedEvent, handler);
public static void RemovePullGestureHandler(Interactive element, EventHandler<PullGestureEventArgs> handler) =>
element.RemoveHandler(PullGestureEvent, handler);
public static void RemovePullGestureEndedHandler(Interactive element, EventHandler<PullGestureEndedEventArgs> handler) =>
element.RemoveHandler(PullGestureEndedEvent, handler);
public static void RemovePointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.RemoveHandler(PointerTouchPadGestureMagnifyEvent, handler);
public static void RemovePointerTouchPadGestureRotateHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.RemoveHandler(PointerTouchPadGestureRotateEvent, handler);
public static void RemovePointerTouchPadGestureSwipeHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.RemoveHandler(PointerTouchPadGestureSwipeEvent, handler);
public static void RemoveScrollGestureHandler(Interactive element, EventHandler<ScrollGestureEventArgs> handler) =>
element.RemoveHandler(ScrollGestureEvent,handler);
public static void RemoveScrollGestureEndedHandler(Interactive element,EventHandler<ScrollGestureEndedEventArgs> handler) =>
element.RemoveHandler(ScrollGestureEndedEvent,handler);
public static void RemoveScrollGestureInertiaStartingHandler(Interactive element, EventHandler<ScrollGestureInertiaStartingEventArgs> handler) =>
element.RemoveHandler(ScrollGestureInertiaStartingEvent, handler);
private static object? GetCaptured(RoutedEventArgs? args)
{
if (args is not PointerEventArgs pointerEventArgs)
@ -241,7 +61,7 @@ namespace Avalonia.Input
{
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,10 +83,11 @@ 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);
}
@ -278,7 +99,7 @@ namespace Avalonia.Input
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,9 +174,7 @@ 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();
@ -364,3 +185,5 @@ namespace Avalonia.Input
}
}
}
}
}

17
src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs

@ -6,7 +6,7 @@ namespace Avalonia.Input
public class HoldingRoutedEventArgs : RoutedEventArgs
{
/// <summary>
/// Gets the state of the <see cref="Gestures.HoldingEvent"/> event.
/// Gets the state of the <see cref="InputElement.HoldingEvent"/> event.
/// </summary>
public HoldingState HoldingState { get; }
@ -20,25 +20,18 @@ namespace Avalonia.Input
/// </summary>
public PointerType PointerType { get; }
internal PointerEventArgs? PointerEventArgs { get; }
internal PointerEventArgs PointerEventArgs { get; }
/// <summary>
/// Initializes a new instance of the <see cref="HoldingRoutedEventArgs"/> class.
/// </summary>
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;
}
/// <summary>
/// Initializes a new instance of the <see cref="HoldingRoutedEventArgs"/> class.
/// </summary>
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
/// <summary>
/// An additional contact is detected or a subsequent gesture (such as a slide) is detected.
/// </summary>
Cancelled,
Canceled,
}
}

241
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;
/// <summary>
/// Defines the IsHoldingEnabled attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsHoldingEnabledProperty =
AvaloniaProperty.RegisterAttached<StyledElement, bool>("IsHoldingEnabled", typeof(InputElement), true);
/// <summary>
/// Defines the IsHoldWithMouseEnabled attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsHoldWithMouseEnabledProperty =
AvaloniaProperty.RegisterAttached<StyledElement, bool>("IsHoldWithMouseEnabled", typeof(InputElement), false);
public static readonly RoutedEvent<PinchEventArgs> PinchEvent =
RoutedEvent.Register<PinchEventArgs>(
"Pinch", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<PinchEndedEventArgs> PinchEndedEvent =
RoutedEvent.Register<PinchEndedEventArgs>(
"PinchEnded", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<PullGestureEventArgs> PullGestureEvent =
RoutedEvent.Register<PullGestureEventArgs>(
"PullGesture", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<PullGestureEndedEventArgs> PullGestureEndedEvent =
RoutedEvent.Register<PullGestureEndedEventArgs>(
"PullGestureEnded", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<ScrollGestureEventArgs> ScrollGestureEvent =
RoutedEvent.Register<ScrollGestureEventArgs>(
"ScrollGesture", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<ScrollGestureInertiaStartingEventArgs> ScrollGestureInertiaStartingEvent =
RoutedEvent.Register<ScrollGestureInertiaStartingEventArgs>(
"ScrollGestureInertiaStarting", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<ScrollGestureEndedEventArgs> ScrollGestureEndedEvent =
RoutedEvent.Register<ScrollGestureEndedEventArgs>(
"ScrollGestureEnded", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureMagnifyEvent =
RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerTouchPadGestureMagnify", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureRotateEvent =
RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerTouchPadGestureRotate", RoutingStrategies.Bubble, typeof(InputElement));
public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureSwipeEvent =
RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerTouchPadGestureSwipe", RoutingStrategies.Bubble, typeof(InputElement));
/// <summary>
/// Defines the <see cref="Tapped"/> event.
/// </summary>
public static readonly RoutedEvent<TappedEventArgs> TappedEvent =
RoutedEvent.Register<InputElement, TappedEventArgs>(
nameof(Tapped),
RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="RightTapped"/> event.
/// </summary>
public static readonly RoutedEvent<TappedEventArgs> RightTappedEvent =
RoutedEvent.Register<InputElement, TappedEventArgs>(
nameof(RightTapped),
RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="Holding"/> event.
/// </summary>
public static readonly RoutedEvent<HoldingRoutedEventArgs> HoldingEvent =
RoutedEvent.Register<InputElement, HoldingRoutedEventArgs>(
nameof(Holding),
RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="DoubleTapped"/> event.
/// </summary>
public static readonly RoutedEvent<TappedEventArgs> DoubleTappedEvent =
RoutedEvent.Register<InputElement, TappedEventArgs>(
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<PinchEventArgs> handler) =>
element.AddHandler(PinchEvent, handler);
public static void AddPinchEndedHandler(Interactive element, EventHandler<PinchEndedEventArgs> handler) =>
element.AddHandler(PinchEndedEvent, handler);
public static void AddPullGestureHandler(Interactive element, EventHandler<PullGestureEventArgs> handler) =>
element.AddHandler(PullGestureEvent, handler);
public static void AddPullGestureEndedHandler(Interactive element, EventHandler<PullGestureEndedEventArgs> handler) =>
element.AddHandler(PullGestureEndedEvent, handler);
public static void RemovePinchHandler(Interactive element, EventHandler<PinchEventArgs> handler) =>
element.RemoveHandler(PinchEvent, handler);
public static void RemovePinchEndedHandler(Interactive element, EventHandler<PinchEndedEventArgs> handler) =>
element.RemoveHandler(PinchEndedEvent, handler);
public static void RemovePullGestureHandler(Interactive element, EventHandler<PullGestureEventArgs> handler) =>
element.RemoveHandler(PullGestureEvent, handler);
public static void RemovePullGestureEndedHandler(Interactive element, EventHandler<PullGestureEndedEventArgs> handler) =>
element.RemoveHandler(PullGestureEndedEvent, handler);
public static void AddPointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.AddHandler(PointerTouchPadGestureMagnifyEvent, handler);
public static void AddPointerTouchPadGestureRotateHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.AddHandler(PointerTouchPadGestureRotateEvent, handler);
public static void AddPointerTouchPadGestureSwipeHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.AddHandler(PointerTouchPadGestureSwipeEvent, handler);
public static void AddScrollGestureHandler(Interactive element, EventHandler<ScrollGestureEventArgs> handler) =>
element.AddHandler(ScrollGestureEvent, handler);
public static void AddScrollGestureEndedHandler(Interactive element, EventHandler<ScrollGestureEndedEventArgs> handler) =>
element.AddHandler(ScrollGestureEndedEvent, handler);
public static void AddScrollGestureInertiaStartingHandler(Interactive element, EventHandler<ScrollGestureInertiaStartingEventArgs> handler) =>
element.AddHandler(ScrollGestureInertiaStartingEvent, handler);
public static void RemovePointerTouchPadGestureMagnifyHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.RemoveHandler(PointerTouchPadGestureMagnifyEvent, handler);
public static void RemovePointerTouchPadGestureRotateHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.RemoveHandler(PointerTouchPadGestureRotateEvent, handler);
public static void RemovePointerTouchPadGestureSwipeHandler(Interactive element, EventHandler<PointerDeltaEventArgs> handler) =>
element.RemoveHandler(PointerTouchPadGestureSwipeEvent, handler);
public static void RemoveScrollGestureHandler(Interactive element, EventHandler<ScrollGestureEventArgs> handler) =>
element.RemoveHandler(ScrollGestureEvent, handler);
public static void RemoveScrollGestureEndedHandler(Interactive element, EventHandler<ScrollGestureEndedEventArgs> handler) =>
element.RemoveHandler(ScrollGestureEndedEvent, handler);
public static void RemoveScrollGestureInertiaStartingHandler(Interactive element, EventHandler<ScrollGestureInertiaStartingEventArgs> handler) =>
element.RemoveHandler(ScrollGestureInertiaStartingEvent, handler);
/// <summary>
/// Occurs when a tap gesture occurs on the control.
/// </summary>
public event EventHandler<TappedEventArgs>? Tapped
{
add { AddHandler(TappedEvent, value); }
remove { RemoveHandler(TappedEvent, value); }
}
/// <summary>
/// Occurs when a right tap gesture occurs on the control.
/// </summary>
public event EventHandler<TappedEventArgs>? RightTapped
{
add { AddHandler(RightTappedEvent, value); }
remove { RemoveHandler(RightTappedEvent, value); }
}
/// <summary>
/// Occurs when a hold gesture occurs on the control.
/// </summary>
public event EventHandler<HoldingRoutedEventArgs>? Holding
{
add { AddHandler(HoldingEvent, value); }
remove { RemoveHandler(HoldingEvent, value); }
}
/// <summary>
/// Occurs when a double-tap gesture occurs on the control.
/// </summary>
public event EventHandler<TappedEventArgs>? 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;
}
}
}
}
}

86
src/Avalonia.Base/Input/InputElement.cs

@ -16,7 +16,7 @@ namespace Avalonia.Input
/// Implements input-related functionality for a control.
/// </summary>
[PseudoClasses(":disabled", ":focus", ":focus-visible", ":focus-within", ":pointerover")]
public class InputElement : Interactive, IInputElement
public partial class InputElement : Interactive, IInputElement
{
/// <summary>
/// Defines the <see cref="Focusable"/> property.
@ -203,24 +203,20 @@ namespace Avalonia.Input
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="Tapped"/> event.
/// Provides event data for the <see cref="ContextRequested"/> event.
/// </summary>
public static readonly RoutedEvent<TappedEventArgs> TappedEvent = Gestures.TappedEvent;
/// <summary>
/// Defines the <see cref="RightTapped"/> event.
/// </summary>
public static readonly RoutedEvent<TappedEventArgs> RightTappedEvent = Gestures.RightTappedEvent;
/// <summary>
/// Defines the <see cref="Holding"/> event.
/// </summary>
public static readonly RoutedEvent<HoldingRoutedEventArgs> HoldingEvent = Gestures.HoldingEvent;
public static readonly RoutedEvent<ContextRequestedEventArgs> ContextRequestedEvent =
RoutedEvent.Register<InputElement, ContextRequestedEventArgs>(
nameof(ContextRequested),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="DoubleTapped"/> event.
/// Provides event data for the <see cref="ContextCanceled"/> event.
/// </summary>
public static readonly RoutedEvent<TappedEventArgs> DoubleTappedEvent = Gestures.DoubleTappedEvent;
public static readonly RoutedEvent<RoutedEventArgs> ContextCanceledEvent =
RoutedEvent.Register<InputElement, RoutedEventArgs>(
nameof(ContextCanceled),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
private bool _isEffectivelyEnabled = true;
private bool _isFocused;
@ -257,6 +253,12 @@ namespace Avalonia.Input
DoubleTappedEvent.AddClassHandler<InputElement>((x, e) => x.OnDoubleTapped(e));
HoldingEvent.AddClassHandler<InputElement>((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<InputElement>((x, e) => x.OnGesturePointerMoved(e), handledEventsToo: true);
PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnGesturePointerPressed(e), handledEventsToo: true);
@ -419,42 +421,6 @@ namespace Avalonia.Input
remove { RemoveHandler(PointerWheelChangedEvent, value); }
}
/// <summary>
/// Occurs when a tap gesture occurs on the control.
/// </summary>
public event EventHandler<TappedEventArgs>? Tapped
{
add { AddHandler(TappedEvent, value); }
remove { RemoveHandler(TappedEvent, value); }
}
/// <summary>
/// Occurs when a right tap gesture occurs on the control.
/// </summary>
public event EventHandler<TappedEventArgs>? RightTapped
{
add { AddHandler(RightTappedEvent, value); }
remove { RemoveHandler(RightTappedEvent, value); }
}
/// <summary>
/// Occurs when a hold gesture occurs on the control.
/// </summary>
public event EventHandler<HoldingRoutedEventArgs>? Holding
{
add { AddHandler(HoldingEvent, value); }
remove { RemoveHandler(HoldingEvent, value); }
}
/// <summary>
/// Occurs when a double-tap gesture occurs on the control.
/// </summary>
public event EventHandler<TappedEventArgs>? DoubleTapped
{
add { AddHandler(DoubleTappedEvent, value); }
remove { RemoveHandler(DoubleTappedEvent, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the control can receive focus.
/// </summary>
@ -518,6 +484,24 @@ namespace Avalonia.Input
internal set { SetAndRaise(IsPointerOverProperty, ref _isPointerOver, value); }
}
/// <summary>
/// Occurs when the user has completed a context input gesture, such as a right-click.
/// </summary>
public event EventHandler<ContextRequestedEventArgs>? ContextRequested
{
add => AddHandler(ContextRequestedEvent, value);
remove => RemoveHandler(ContextRequestedEvent, value);
}
/// <summary>
/// Occurs when the context input gesture continues into another gesture, to notify the element that the context flyout should not be opened.
/// </summary>
public event EventHandler<RoutedEventArgs>? ContextCanceled
{
add => AddHandler(ContextCanceledEvent, value);
remove => RemoveHandler(ContextCanceledEvent, value);
}
/// <summary>
/// Gets or sets a value that indicates whether the control is included in tab navigation.
/// </summary>

11
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);

6
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)
{
}
}

5
src/Avalonia.Base/Input/PullGestureEventArgs.cs

@ -1,4 +1,3 @@
using System;
using Avalonia.Interactivity;
namespace Avalonia.Input
@ -13,7 +12,7 @@ namespace Avalonia.Input
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;

6
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;

2
src/Avalonia.Base/Input/TappedEventArgs.cs

@ -1,6 +1,4 @@
using System;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace Avalonia.Input
{

19
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
{
@ -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,6 +215,7 @@ namespace Avalonia.Controls
if (e.NewValue is ContextMenu)
{
control.ContextRequested += ControlContextRequested;
control.ContextCanceled += ControlContextCanceled;
control.AttachedToVisualTree += ControlOnAttachedToVisualTree;
control.DetachedFromVisualTree += ControlDetachedFromVisualTree;
}
@ -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

32
src/Avalonia.Controls/Control.cs

@ -23,7 +23,6 @@ namespace Avalonia.Controls
/// The control class extends <see cref="InputElement"/> and adds the following features:
///
/// - A <see cref="Tag"/> property to allow user-defined data to be attached to the control.
/// - <see cref="ContextRequestedEvent"/> and other context menu related members.
/// </remarks>
public class Control : InputElement, IDataTemplateHost, IVisualBrushInitialize, ISetterValue
{
@ -59,14 +58,6 @@ namespace Avalonia.Controls
"RequestBringIntoView",
RoutingStrategies.Bubble);
/// <summary>
/// Provides event data for the <see cref="ContextRequested"/> event.
/// </summary>
public static readonly RoutedEvent<ContextRequestedEventArgs> ContextRequestedEvent =
RoutedEvent.Register<Control, ContextRequestedEventArgs>(
nameof(ContextRequested),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="Loaded"/> event.
/// </summary>
@ -163,15 +154,6 @@ namespace Avalonia.Controls
set => SetValue(TagProperty, value);
}
/// <summary>
/// Occurs when the user has completed a context input gesture, such as a right-click.
/// </summary>
public event EventHandler<ContextRequestedEventArgs>? ContextRequested
{
add => AddHandler(ContextRequestedEvent, value);
remove => RemoveHandler(ContextRequestedEvent, value);
}
/// <summary>
/// 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;
}
}
/// <inheritdoc/>
protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{

4
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;
}

16
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
{
@ -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

12
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);
}

2
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

12
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;
}
@ -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;
}
}

132
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
@ -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);
@ -224,17 +221,17 @@ 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
};
HoldingState holding = HoldingState.Cancelled;
HoldingState holding = HoldingState.Canceled;
root.AddHandler(Gestures.HoldingEvent, (_, e) => holding = e.HoldingState);
root.AddHandler(InputElement.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());
@ -248,6 +245,79 @@ 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<IPlatformSettings>();
iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
iSettingsMock.Setup(x => x.GetTapSize(It.IsAny<PointerType>())).Returns(new Size(16, 16));
AvaloniaLocator.CurrentMutable.BindToSelf(this)
.Bind<IPlatformSettings>().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<IPlatformSettings>();
iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
iSettingsMock.Setup(x => x.GetTapSize(It.IsAny<PointerType>())).Returns(new Size(16, 16));
AvaloniaLocator.CurrentMutable.BindToSelf(this)
.Bind<IPlatformSettings>().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()
{
@ -260,14 +330,14 @@ 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 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);
@ -295,14 +365,14 @@ 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 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);
@ -330,14 +400,14 @@ 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);
Assert.False(cancelled);
@ -366,14 +436,14 @@ 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);
@ -402,14 +472,14 @@ namespace Avalonia.Base.UnitTests.Input
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();
@ -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();

2
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();

2
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();

2
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;

6
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 = @"<Button xmlns='https://github.com/avaloniaui' Gestures.Tapped='OnTapped'/>";
var xaml = @"<Button xmlns='https://github.com/avaloniaui' InputElement.Tapped='OnTapped'/>";
var target = new MyButton();
AvaloniaRuntimeXamlLoader.Load(xaml, rootInstance: target);
target.RaiseEvent(new RoutedEventArgs
{
RoutedEvent = Gestures.TappedEvent,
RoutedEvent = InputElement.TappedEvent,
});
Assert.True(target.WasTapped);
@ -51,7 +51,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.NotNull(target);
target.RaiseEvent(new TappedEventArgs(Gestures.DoubleTappedEvent, null!));
target.RaiseEvent(new TappedEventArgs(InputElement.DoubleTappedEvent, null!));
Assert.True(host.WasTapped);
}

Loading…
Cancel
Save