diff --git a/src/Avalonia.Controls/DragDrop/DragSource.cs b/src/Avalonia.Controls/DragDrop/DragSource.cs index 2048a0c0da..fd3d8ef143 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -19,13 +19,14 @@ namespace Avalonia.Controls.DragDrop private const InputModifiers MOUSE_INPUTMODIFIERS = InputModifiers.LeftMouseButton|InputModifiers.MiddleMouseButton|InputModifiers.RightMouseButton; private readonly IDragDropDevice _dragDrop; private readonly IInputManager _inputManager; - - private readonly Subject _result = new Subject(); + + private DragDropEffects _allowedEffects; private IDataObject _draggedData; private IInputElement _lastRoot; - private InputModifiers? _initialInputModifiers; + private Point _lastPosition; private object _lastCursor; + private InputModifiers? _initialInputModifiers; public DragSource() { @@ -40,35 +41,58 @@ namespace Avalonia.Controls.DragDrop { _draggedData = data; _lastRoot = null; + _lastPosition = default(Point); + _allowedEffects = allowedEffects; - using (_inputManager.PreProcess.OfType().Subscribe(e => ProcessMouseEvents(e, allowedEffects))) + using (_inputManager.PreProcess.OfType().Subscribe(ProcessMouseEvents)) { - var effect = await _result.FirstAsync(); - return effect; + using (_inputManager.PreProcess.OfType().Subscribe(ProcessKeyEvents)) + { + var effect = await _result.FirstAsync(); + return effect; + } } } return DragDropEffects.None; } - private DragDropEffects RaiseDragEvent(RawDragEventType type, IInputElement root, Point pt, DragDropEffects allowedEffects) + + private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputElement root, Point pt, InputModifiers modifiers) { - RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, allowedEffects); + _lastPosition = pt; + + RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, _allowedEffects); var tl = root.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); tl.PlatformImpl.Input(rawEvent); - - SetCursor(root, rawEvent.Effects); - return rawEvent.Effects; + + var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers); + UpdateCursor(root, effect); + return effect; + } + + private DragDropEffects GetPreferredEffect(DragDropEffects effect, InputModifiers modifiers) + { + if (effect == DragDropEffects.Copy || effect == DragDropEffects.Move || effect == DragDropEffects.Link || effect == DragDropEffects.None) + return effect; // No need to check for the modifiers. + if (effect.HasFlag(DragDropEffects.Link) && modifiers.HasFlag(InputModifiers.Alt)) + return DragDropEffects.Link; + if (effect.HasFlag(DragDropEffects.Copy) && modifiers.HasFlag(InputModifiers.Control)) + return DragDropEffects.Copy; + return DragDropEffects.Move; } 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); + if (effects.HasFlag(DragDropEffects.Copy)) + return new Cursor(StandardCursorType.DragCopy); + if (effects.HasFlag(DragDropEffects.Move)) + return new Cursor(StandardCursorType.DragMove); + if (effects.HasFlag(DragDropEffects.Link)) + return new Cursor(StandardCursorType.DragLink); + return new Cursor(StandardCursorType.No); } - private void SetCursor(IInputElement root, DragDropEffects effect) + private void UpdateCursor(IInputElement root, DragDropEffects effect) { if (_lastRoot != root) { @@ -96,25 +120,45 @@ namespace Avalonia.Controls.DragDrop if (root is InputElement ie) ie.Cursor = GetCursorForDropEffect(effect); } - - private void ProcessMouseEvents(RawMouseEventArgs e, DragDropEffects allowedEffects) + + private void CancelDragging() { - if (!_initialInputModifiers.HasValue) - _initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS; + if (_lastRoot != null) + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, InputModifiers.None); + UpdateCursor(null, DragDropEffects.None); + _result.OnNext(DragDropEffects.None); + } - void CancelDragging() + private void ProcessKeyEvents(RawKeyEventArgs e) + { + if (e.Type == RawKeyEventType.KeyDown && e.Key == Key.Escape) { if (_lastRoot != null) - RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects); - SetCursor(null, DragDropEffects.None); + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, e.Modifiers); + UpdateCursor(null, DragDropEffects.None); _result.OnNext(DragDropEffects.None); e.Handled = true; } - void AcceptDragging() + else if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl || e.Key == Key.LeftAlt || e.Key == Key.RightAlt) + RaiseEventAndUpdateCursor(RawDragEventType.DragOver, _lastRoot, _lastPosition, e.Modifiers); + } + + private void ProcessMouseEvents(RawMouseEventArgs e) + { + if (!_initialInputModifiers.HasValue) + _initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS; + + + void CheckDraggingAccepted(InputModifiers changedMouseButton) { - var result = RaiseDragEvent(RawDragEventType.Drop, e.Root, e.Position, allowedEffects) & allowedEffects; - SetCursor(null, DragDropEffects.None); - _result.OnNext(result); + if (_initialInputModifiers.Value.HasFlag(changedMouseButton)) + { + var result = RaiseEventAndUpdateCursor(RawDragEventType.Drop, e.Root, e.Position, e.InputModifiers); + UpdateCursor(null, DragDropEffects.None); + _result.OnNext(result); + } + else + CancelDragging(); e.Handled = true; } @@ -125,45 +169,34 @@ namespace Avalonia.Controls.DragDrop case RawMouseEventType.MiddleButtonDown: case RawMouseEventType.NonClientLeftButtonDown: CancelDragging(); + e.Handled = true; return; case RawMouseEventType.LeaveWindow: - RaiseDragEvent(RawDragEventType.DragLeave, e.Root, e.Position, allowedEffects); - break; + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, e.Root, e.Position, e.InputModifiers); break; case RawMouseEventType.LeftButtonUp: - if (_initialInputModifiers.Value.HasFlag(InputModifiers.LeftMouseButton)) - AcceptDragging(); - else - CancelDragging(); - return; + CheckDraggingAccepted(InputModifiers.LeftMouseButton); break; case RawMouseEventType.MiddleButtonUp: - if (_initialInputModifiers.Value.HasFlag(InputModifiers.MiddleMouseButton)) - AcceptDragging(); - else - CancelDragging(); - return; + CheckDraggingAccepted(InputModifiers.MiddleMouseButton); break; case RawMouseEventType.RightButtonUp: - if (_initialInputModifiers.Value.HasFlag(InputModifiers.RightMouseButton)) - AcceptDragging(); - else - CancelDragging(); - return; + CheckDraggingAccepted(InputModifiers.RightMouseButton); break; case RawMouseEventType.Move: var mods = e.InputModifiers & MOUSE_INPUTMODIFIERS; if (_initialInputModifiers.Value != mods) { CancelDragging(); + e.Handled = true; 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); + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), e.InputModifiers); + RaiseEventAndUpdateCursor(RawDragEventType.DragEnter, e.Root, e.Position, e.InputModifiers); } else - RaiseDragEvent(RawDragEventType.DragOver, e.Root, e.Position, allowedEffects); - return; + RaiseEventAndUpdateCursor(RawDragEventType.DragOver, e.Root, e.Position, e.InputModifiers); + break; } } }