csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
6.7 KiB
170 lines
6.7 KiB
using System;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using System.Reactive.Subjects;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Avalonia.Controls.DragDrop.Raw;
|
|
using Avalonia.Controls.Platform;
|
|
using Avalonia.Input;
|
|
using Avalonia.Input.Raw;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Threading;
|
|
using Avalonia.VisualTree;
|
|
|
|
namespace Avalonia.Controls.DragDrop
|
|
{
|
|
class DragSource : IPlatformDragSource
|
|
{
|
|
private const InputModifiers MOUSE_INPUTMODIFIERS = InputModifiers.LeftMouseButton|InputModifiers.MiddleMouseButton|InputModifiers.RightMouseButton;
|
|
private readonly IDragDropDevice _dragDrop;
|
|
private readonly IInputManager _inputManager;
|
|
|
|
|
|
private readonly Subject<DragDropEffects> _result = new Subject<DragDropEffects>();
|
|
private IDataObject _draggedData;
|
|
private IInputElement _lastRoot;
|
|
private InputModifiers? _initialInputModifiers;
|
|
private object _lastCursor;
|
|
|
|
public DragSource()
|
|
{
|
|
_inputManager = AvaloniaLocator.Current.GetService<IInputManager>();
|
|
_dragDrop = AvaloniaLocator.Current.GetService<IDragDropDevice>();
|
|
}
|
|
|
|
public async Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
|
|
{
|
|
Dispatcher.UIThread.VerifyAccess();
|
|
if (_draggedData == null)
|
|
{
|
|
_draggedData = data;
|
|
_lastRoot = null;
|
|
|
|
using (_inputManager.PreProcess.OfType<RawMouseEventArgs>().Subscribe(e => ProcessMouseEvents(e, allowedEffects)))
|
|
{
|
|
var effect = await _result.FirstAsync();
|
|
return effect;
|
|
}
|
|
}
|
|
return DragDropEffects.None;
|
|
}
|
|
|
|
private DragDropEffects RaiseDragEvent(RawDragEventType type, IInputElement root, Point pt, DragDropEffects allowedEffects)
|
|
{
|
|
RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, allowedEffects);
|
|
var tl = root.GetSelfAndVisualAncestors().OfType<TopLevel>().FirstOrDefault();
|
|
tl.PlatformImpl.Input(rawEvent);
|
|
|
|
SetCursor(root, rawEvent.Effects);
|
|
return rawEvent.Effects;
|
|
}
|
|
|
|
private Cursor GetCursorForDropEffect(DragDropEffects effects)
|
|
{
|
|
// Todo. Needs to choose cursor by effect.
|
|
if (effects == DragDropEffects.None)
|
|
return new Cursor(StandardCursorType.No);
|
|
return new Cursor(StandardCursorType.Hand);
|
|
}
|
|
|
|
private void SetCursor(IInputElement root, DragDropEffects effect)
|
|
{
|
|
if (_lastRoot != root)
|
|
{
|
|
if (_lastRoot is InputElement ieLast)
|
|
{
|
|
if (_lastCursor == AvaloniaProperty.UnsetValue)
|
|
ieLast.ClearValue(InputElement.CursorProperty);
|
|
else
|
|
ieLast.Cursor = _lastCursor as Cursor;
|
|
}
|
|
|
|
if (root is InputElement ieNew)
|
|
{
|
|
if (!ieNew.IsSet(InputElement.CursorProperty))
|
|
_lastCursor = AvaloniaProperty.UnsetValue;
|
|
else
|
|
_lastCursor = root.Cursor;
|
|
}
|
|
else
|
|
_lastCursor = null;
|
|
|
|
_lastRoot = root;
|
|
}
|
|
|
|
if (root is InputElement ie)
|
|
ie.Cursor = GetCursorForDropEffect(effect);
|
|
}
|
|
|
|
private void ProcessMouseEvents(RawMouseEventArgs e, DragDropEffects allowedEffects)
|
|
{
|
|
if (!_initialInputModifiers.HasValue)
|
|
_initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS;
|
|
|
|
void CancelDragging()
|
|
{
|
|
if (_lastRoot != null)
|
|
RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects);
|
|
SetCursor(null, DragDropEffects.None);
|
|
_result.OnNext(DragDropEffects.None);
|
|
e.Handled = true;
|
|
}
|
|
void AcceptDragging()
|
|
{
|
|
var result = RaiseDragEvent(RawDragEventType.Drop, e.Root, e.Position, allowedEffects) & allowedEffects;
|
|
SetCursor(null, DragDropEffects.None);
|
|
_result.OnNext(result);
|
|
e.Handled = true;
|
|
}
|
|
|
|
switch (e.Type)
|
|
{
|
|
case RawMouseEventType.LeftButtonDown:
|
|
case RawMouseEventType.RightButtonDown:
|
|
case RawMouseEventType.MiddleButtonDown:
|
|
case RawMouseEventType.NonClientLeftButtonDown:
|
|
CancelDragging();
|
|
return;
|
|
case RawMouseEventType.LeaveWindow:
|
|
RaiseDragEvent(RawDragEventType.DragLeave, e.Root, e.Position, allowedEffects);
|
|
break;
|
|
case RawMouseEventType.LeftButtonUp:
|
|
if (_initialInputModifiers.Value.HasFlag(InputModifiers.LeftMouseButton))
|
|
AcceptDragging();
|
|
else
|
|
CancelDragging();
|
|
return;
|
|
case RawMouseEventType.MiddleButtonUp:
|
|
if (_initialInputModifiers.Value.HasFlag(InputModifiers.MiddleMouseButton))
|
|
AcceptDragging();
|
|
else
|
|
CancelDragging();
|
|
return;
|
|
case RawMouseEventType.RightButtonUp:
|
|
if (_initialInputModifiers.Value.HasFlag(InputModifiers.RightMouseButton))
|
|
AcceptDragging();
|
|
else
|
|
CancelDragging();
|
|
return;
|
|
case RawMouseEventType.Move:
|
|
var mods = e.InputModifiers & MOUSE_INPUTMODIFIERS;
|
|
if (_initialInputModifiers.Value != mods)
|
|
{
|
|
CancelDragging();
|
|
return;
|
|
}
|
|
|
|
if (e.Root != _lastRoot)
|
|
{
|
|
if (_lastRoot != null)
|
|
RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects);
|
|
RaiseDragEvent(RawDragEventType.DragEnter, e.Root, e.Position, allowedEffects);
|
|
}
|
|
else
|
|
RaiseDragEvent(RawDragEventType.DragOver, e.Root, e.Position, allowedEffects);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|