Ver código fonte

Improved DragSource

the drag source now gets the preferred drag effect and handles keyboard
inputs too
boombuler 7 anos atrás
pai
commit
97f581af49
1 arquivos alterados com 81 adições e 48 exclusões
  1. 81 48
      src/Avalonia.Controls/DragDrop/DragSource.cs

+ 81 - 48
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<DragDropEffects> _result = new Subject<DragDropEffects>();
+
+        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<RawMouseEventArgs>().Subscribe(e => ProcessMouseEvents(e, allowedEffects)))
+                using (_inputManager.PreProcess.OfType<RawMouseEventArgs>().Subscribe(ProcessMouseEvents))
                 {
-                    var effect = await _result.FirstAsync();
-                    return effect;
+                    using (_inputManager.PreProcess.OfType<RawKeyEventArgs>().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<TopLevel>().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;
             }
         }
     }