Browse Source

Internal PointerCaptureChanging event (#19833)

* PointerCaptureChanging event

* PR feedback

* Announce implicit capture to platform

---------

Co-authored-by: Jan Kučera <miloush@users.noreply.github.com>
Co-authored-by: Max Katz <maxkatz6@outlook.com>
pull/19876/head
Jan Kučera 4 months ago
committed by GitHub
parent
commit
47470f9304
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 32
      src/Avalonia.Base/Input/InputElement.cs
  2. 4
      src/Avalonia.Base/Input/MouseDevice.cs
  3. 53
      src/Avalonia.Base/Input/Pointer.cs
  4. 17
      src/Avalonia.Base/Input/PointerEventArgs.cs

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

@ -178,6 +178,14 @@ namespace Avalonia.Input
nameof(PointerReleased),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="PointerCaptureChanging"/> routed event.
/// </summary>
internal static readonly RoutedEvent<PointerCaptureChangingEventArgs> PointerCaptureChangingEvent =
RoutedEvent.Register<InputElement, PointerCaptureChangingEventArgs>(
nameof(PointerCaptureChanging),
RoutingStrategies.Direct);
/// <summary>
/// Defines the <see cref="PointerCaptureLost"/> routed event.
/// </summary>
@ -240,6 +248,7 @@ namespace Avalonia.Input
PointerMovedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerMoved(e));
PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerPressed(e));
PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e));
PointerCaptureChangingEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerCaptureChanging(e));
PointerCaptureLostEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerCaptureLost(e));
PointerWheelChangedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerWheelChanged(e));
@ -381,9 +390,19 @@ namespace Avalonia.Input
remove { RemoveHandler(PointerReleasedEvent, value); }
}
/// <summary>
/// Occurs when the control or its child control is about to lose capture,
/// event will not be triggered for a parent control if capture was transferred to another child of that parent control.
/// </summary>
internal event EventHandler<PointerCaptureChangingEventArgs>? PointerCaptureChanging
{
add => AddHandler(PointerCaptureChangingEvent, value);
remove => RemoveHandler(PointerCaptureChangingEvent, value);
}
/// <summary>
/// Occurs when the control or its child control loses the pointer capture for any reason,
/// event will not be triggered for a parent control if capture was transferred to another child of that parent control
/// event will not be triggered for a parent control if capture was transferred to another child of that parent control.
/// </summary>
public event EventHandler<PointerCaptureLostEventArgs>? PointerCaptureLost
{
@ -770,6 +789,17 @@ namespace Avalonia.Input
/// <returns>Last focusable element if available/>.</returns>
protected internal virtual InputElement? GetLastFocusableElementOverride() => null;
/// <summary>
/// Invoked when an unhandled <see cref="PointerCaptureChangingEvent"/> reaches an element in its
/// route that is derived from this class. Implement this method to add class handling
/// for this event.
/// </summary>
/// <param name="e">Data about the event.</param>
internal virtual void OnPointerCaptureChanging(PointerCaptureChangingEventArgs e)
{
}
/// <summary>
/// Invoked when an unhandled <see cref="PointerCaptureLostEvent"/> reaches an element in its
/// route that is derived from this class. Implement this method to add class handling

4
src/Avalonia.Base/Input/MouseDevice.cs

@ -131,7 +131,7 @@ namespace Avalonia.Input
if (source != null)
{
_pointer.Capture(source);
_pointer.Capture(source, CaptureSource.Implicit);
var settings = ((IInputRoot?)(source as Interactive)?.GetVisualRoot())?.PlatformSettings;
if (settings is not null)
@ -206,7 +206,7 @@ namespace Avalonia.Input
}
finally
{
_pointer.Capture(null);
_pointer.Capture(null, CaptureSource.Implicit);
_pointer.CaptureGestureRecognizer(null);
_pointer.IsGestureRecognitionSkipped = false;
_lastMouseDownButton = default;

53
src/Avalonia.Base/Input/Pointer.cs

@ -6,6 +6,13 @@ using Avalonia.VisualTree;
namespace Avalonia.Input
{
internal enum CaptureSource
{
Explicit,
Implicit,
Platform
}
public class Pointer : IPointer, IDisposable
{
private static int s_NextFreePointerId = 1000;
@ -30,46 +37,60 @@ namespace Avalonia.Input
protected virtual void PlatformCapture(IInputElement? element)
{
}
internal void PlatformCaptureLost()
{
if (Captured != null)
Capture(null, platformInitiated: true);
Capture(null, CaptureSource.Platform);
}
public void Capture(IInputElement? control)
{
Capture(control, platformInitiated: false);
Capture(control, CaptureSource.Explicit);
}
private void Capture(IInputElement? control, bool platformInitiated)
internal void Capture(IInputElement? control, CaptureSource source)
{
var oldCapture = Captured;
if (oldCapture == control)
return;
if (oldCapture is Visual v1)
v1.DetachedFromVisualTree -= OnCaptureDetached;
var oldVisual = oldCapture as Visual;
IInputElement? commonParent = null;
if (oldVisual != null)
{
commonParent = FindCommonParent(control, oldCapture);
foreach (var notifyTarget in oldVisual.GetSelfAndVisualAncestors().OfType<IInputElement>())
{
if (notifyTarget == commonParent)
break;
var args = new PointerCaptureChangingEventArgs(notifyTarget, this, control, source);
notifyTarget.RaiseEvent(args);
if (args.Handled)
return;
}
}
if (oldVisual != null)
oldVisual.DetachedFromVisualTree -= OnCaptureDetached;
Captured = control;
if (!platformInitiated)
if (source != CaptureSource.Platform)
PlatformCapture(control);
if (oldCapture is Visual v2)
{
var commonParent = FindCommonParent(control, oldCapture);
foreach (var notifyTarget in v2.GetSelfAndVisualAncestors().OfType<IInputElement>())
if (oldVisual != null)
foreach (var notifyTarget in oldVisual.GetSelfAndVisualAncestors().OfType<IInputElement>())
{
if (notifyTarget == commonParent)
break;
notifyTarget.RaiseEvent(new PointerCaptureLostEventArgs(notifyTarget, this));
}
}
if (Captured is Visual v3)
v3.DetachedFromVisualTree += OnCaptureDetached;
if (Captured is Visual newVisual)
newVisual.DetachedFromVisualTree += OnCaptureDetached;
if (Captured != null)
CaptureGestureRecognizer(null);
@ -92,7 +113,7 @@ namespace Avalonia.Input
public IInputElement? Captured { get; private set; }
public PointerType Type { get; }
public bool IsPrimary { get; }

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

@ -202,11 +202,26 @@ namespace Avalonia.Input
{
public IPointer Pointer { get; }
[Unstable("This constructor might be removed in 12.0. If you need to remove capture, use stable methods on the IPointer instance.,")]
[Unstable("This constructor might be removed in 12.0. If you need to remove capture, use stable methods on the IPointer instance.")]
public PointerCaptureLostEventArgs(object source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
{
Pointer = pointer;
Source = source;
}
}
internal class PointerCaptureChangingEventArgs : RoutedEventArgs
{
public IPointer Pointer { get; }
public CaptureSource CaptureSource { get; }
public IInputElement? NewValue { get; }
internal PointerCaptureChangingEventArgs(object source, IPointer pointer, IInputElement? newValue, CaptureSource captureSource) : base(InputElement.PointerCaptureChangingEvent)
{
Pointer = pointer;
Source = source;
NewValue = newValue;
CaptureSource = captureSource;
}
}
}

Loading…
Cancel
Save