Skip to content
This repository has been archived by the owner on Jul 10, 2024. It is now read-only.

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
HendrikMennen committed Jul 1, 2024
1 parent 40a6ffb commit b149aa7
Showing 1 changed file with 200 additions and 0 deletions.
200 changes: 200 additions & 0 deletions src/OneWare.Essentials/Behaviours/ContextDragBubbleBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Xaml.Interactions.DragAndDrop;
using Avalonia.Xaml.Interactivity;

namespace OneWare.Essentials.Behaviours;

public class ContextDragBubbleBehavior : Behavior<Control>
{
private Point _dragStartPoint;
private PointerEventArgs? _triggerEvent;
private bool _lock;
private bool _captured;

/// <summary>
///
/// </summary>
public static readonly StyledProperty<object?> ContextProperty =
AvaloniaProperty.Register<ContextDragBehavior, object?>(nameof(Context));

/// <summary>
///
/// </summary>
public static readonly StyledProperty<IDragHandler?> HandlerProperty =
AvaloniaProperty.Register<ContextDragBehavior, IDragHandler?>(nameof(Handler));

/// <summary>
///
/// </summary>
public static readonly StyledProperty<double> HorizontalDragThresholdProperty =
AvaloniaProperty.Register<ContextDragBehavior, double>(nameof(HorizontalDragThreshold), 3);

/// <summary>
///
/// </summary>
public static readonly StyledProperty<double> VerticalDragThresholdProperty =
AvaloniaProperty.Register<ContextDragBehavior, double>(nameof(VerticalDragThreshold), 3);

/// <summary>
///
/// </summary>
public object? Context
{
get => GetValue(ContextProperty);
set => SetValue(ContextProperty, value);
}

/// <summary>
///
/// </summary>
public IDragHandler? Handler
{
get => GetValue(HandlerProperty);
set => SetValue(HandlerProperty, value);
}

/// <summary>
///
/// </summary>
public double HorizontalDragThreshold
{
get => GetValue(HorizontalDragThresholdProperty);
set => SetValue(HorizontalDragThresholdProperty, value);
}

/// <summary>
///
/// </summary>
public double VerticalDragThreshold
{
get => GetValue(VerticalDragThresholdProperty);
set => SetValue(VerticalDragThresholdProperty, value);
}

/// <inheritdoc />
protected override void OnAttachedToVisualTree()
{
AssociatedObject?.AddHandler(InputElement.PointerPressedEvent, AssociatedObject_PointerPressed, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
AssociatedObject?.AddHandler(InputElement.PointerReleasedEvent, AssociatedObject_PointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
AssociatedObject?.AddHandler(InputElement.PointerMovedEvent, AssociatedObject_PointerMoved, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
AssociatedObject?.AddHandler(InputElement.PointerCaptureLostEvent, AssociatedObject_CaptureLost, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
}

/// <inheritdoc />
protected override void OnDetachedFromVisualTree()
{
AssociatedObject?.RemoveHandler(InputElement.PointerPressedEvent, AssociatedObject_PointerPressed);
AssociatedObject?.RemoveHandler(InputElement.PointerReleasedEvent, AssociatedObject_PointerReleased);
AssociatedObject?.RemoveHandler(InputElement.PointerMovedEvent, AssociatedObject_PointerMoved);
AssociatedObject?.RemoveHandler(InputElement.PointerCaptureLostEvent, AssociatedObject_CaptureLost);
}

private async Task DoDragDrop(PointerEventArgs triggerEvent, object? value)

Check warning on line 96 in src/OneWare.Essentials/Behaviours/ContextDragBubbleBehavior.cs

View workflow job for this annotation

GitHub Actions / build

Use "Async" suffix in names of methods that return an awaitable type (https://github.com/Microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD200.md)

Check warning on line 96 in src/OneWare.Essentials/Behaviours/ContextDragBubbleBehavior.cs

View workflow job for this annotation

GitHub Actions / build

Use "Async" suffix in names of methods that return an awaitable type (https://github.com/Microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD200.md)
{
var data = new DataObject();
data.Set(ContextDropBehavior.DataFormat, value!);

var effect = DragDropEffects.None;

if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Alt))
{
effect |= DragDropEffects.Link;
}
else if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
effect |= DragDropEffects.Move;
}
else if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Control))
{
effect |= DragDropEffects.Copy;
}
else
{
effect |= DragDropEffects.Move;
}

await DragDrop.DoDragDrop(triggerEvent, data, effect);
}

private void Released()
{
_triggerEvent = null;
_lock = false;
}

private void AssociatedObject_PointerPressed(object? sender, PointerPressedEventArgs e)
{
var properties = e.GetCurrentPoint(AssociatedObject).Properties;
if (properties.IsLeftButtonPressed)
{
if (e.Source is Control control
&& AssociatedObject?.DataContext == control.DataContext)
{
if ((control as ISelectable ?? control.Parent as ISelectable)?.IsSelected ?? false) e.Handled = true; //avoid deselection on drag
_dragStartPoint = e.GetPosition(null);
_triggerEvent = e;
_lock = true;
_captured = true;
}
}
}

private void AssociatedObject_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (_captured)
{
if (e.InitialPressMouseButton == MouseButton.Left && _triggerEvent is not null)
{
Released();
}

_captured = false;
}
}

private async void AssociatedObject_PointerMoved(object? sender, PointerEventArgs e)

Check warning on line 159 in src/OneWare.Essentials/Behaviours/ContextDragBubbleBehavior.cs

View workflow job for this annotation

GitHub Actions / build

Avoid "async void" methods, because any exceptions not handled by the method will crash the process (https://github.com/Microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD100.md)

Check warning on line 159 in src/OneWare.Essentials/Behaviours/ContextDragBubbleBehavior.cs

View workflow job for this annotation

GitHub Actions / build

Avoid "async void" methods, because any exceptions not handled by the method will crash the process (https://github.com/Microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD100.md)
{
var properties = e.GetCurrentPoint(AssociatedObject).Properties;
if (_captured
&& properties.IsLeftButtonPressed &&
_triggerEvent is not null)
{
var point = e.GetPosition(null);
var diff = _dragStartPoint - point;
var horizontalDragThreshold = HorizontalDragThreshold;
var verticalDragThreshold = VerticalDragThreshold;

if (Math.Abs(diff.X) > horizontalDragThreshold || Math.Abs(diff.Y) > verticalDragThreshold)
{
if (_lock)
{
_lock = false;
}
else
{
return;
}

var context = Context ?? AssociatedObject?.DataContext;

Handler?.BeforeDragDrop(sender, _triggerEvent, context);

await DoDragDrop(_triggerEvent, context);

Handler?.AfterDragDrop(sender, _triggerEvent, context);

_triggerEvent = null;
}
}
}

private void AssociatedObject_CaptureLost(object? sender, PointerCaptureLostEventArgs e)
{
Released();
_captured = false;
}
}

0 comments on commit b149aa7

Please sign in to comment.