Browse Source

Basic generic + Windows Drag support.

boombuler 7 years ago
parent
commit
7b3942685e

+ 3 - 1
src/Avalonia.Controls/Application.cs

@@ -14,6 +14,7 @@ using Avalonia.Threading;
 using System.Reactive.Concurrency;
 using Avalonia.Controls.DragDrop;
 using Avalonia.Controls.DragDrop.Raw;
+using Avalonia.Controls.Platform;
 
 namespace Avalonia
 {
@@ -237,7 +238,8 @@ namespace Avalonia
                 .Bind<ILayoutManager>().ToSingleton<LayoutManager>()
                 .Bind<IApplicationLifecycle>().ToConstant(this)
                 .Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
-                .Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance);
+                .Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance)
+                .Bind<IPlatformDragSource>().ToTransient<DragSource>();
         }
     }
 }

+ 6 - 4
src/Avalonia.Controls/DragDrop/DragDrop.cs

@@ -1,4 +1,6 @@
-using Avalonia.Interactivity;
+using System.Threading.Tasks;
+using Avalonia.Controls.Platform;
+using Avalonia.Interactivity;
 
 namespace Avalonia.Controls.DragDrop
 {
@@ -25,10 +27,10 @@ namespace Avalonia.Controls.DragDrop
         /// Starts a dragging operation with the given <see cref="IDataObject"/> and returns the applied drop effect from the target.
         /// <seealso cref="DataObject"/>
         /// </summary>
-        public static DragDropEffects DoDragDrop(this Interactive source, IDataObject data, DragDropEffects allowedEffects)
+        public static Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
         {
-
-            return DragDropEffects.None;
+            var src = AvaloniaLocator.Current.GetService<IPlatformDragSource>();
+            return src?.DoDragDrop(data, allowedEffects) ?? Task.FromResult(DragDropEffects.None);
         }
     }
 }

+ 129 - 0
src/Avalonia.Controls/DragDrop/DragSource.cs

@@ -0,0 +1,129 @@
+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 IInputRoot _lastRoot;
+        private InputModifiers? _initialInputModifiers;
+
+        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);
+            return rawEvent.Effects;
+        }
+
+        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);
+                _result.OnNext(DragDropEffects.None);
+                e.Handled = true;
+            }
+            void AcceptDragging()
+            {
+                var result = RaiseDragEvent(RawDragEventType.Drop, e.Root, e.Position, allowedEffects) & allowedEffects;
+                _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);
+                        _lastRoot = e.Root;
+                    }
+                    else
+                        RaiseDragEvent(RawDragEventType.DragOver, e.Root, e.Position, allowedEffects);
+                    return;
+            }
+        }
+    }
+}

+ 14 - 0
src/Avalonia.Controls/Platform/IPlatformDragSource.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Controls.DragDrop;
+using Avalonia.Interactivity;
+
+namespace Avalonia.Controls.Platform
+{
+    public interface IPlatformDragSource
+    {
+        Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects);
+    }
+}

+ 28 - 0
src/Windows/Avalonia.Win32/DragSource.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Controls.DragDrop;
+using Avalonia.Controls.Platform;
+using Avalonia.Interactivity;
+using Avalonia.Threading;
+using Avalonia.Win32.Interop;
+
+namespace Avalonia.Win32
+{
+    class DragSource : IPlatformDragSource
+    {
+        public Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
+        {
+            Dispatcher.UIThread.VerifyAccess();
+
+            OleDragSource src = new OleDragSource();
+            DataObject dataObject = new DataObject(data);
+            int allowed = (int)OleDropTarget.ConvertDropEffect(allowedEffects);
+
+            int[] finalEffect = new int[1];
+            UnmanagedMethods.DoDragDrop(dataObject, src, allowed, finalEffect);
+
+            return Task.FromResult(OleDropTarget.ConvertDropEffect((DropEffect)finalEffect[0]));}
+    }
+}

+ 1 - 3
src/Windows/Avalonia.Win32/OleDragSource.cs

@@ -33,9 +33,7 @@ namespace Avalonia.Win32
 
         public int GiveFeedback(int dwEffect)
         {
-            if (dwEffect != 0)
-                return DRAGDROP_S_USEDEFAULTCURSORS;
-            return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
+            return DRAGDROP_S_USEDEFAULTCURSORS;
         }
     }
 }

+ 2 - 2
src/Windows/Avalonia.Win32/OleDropTarget.cs

@@ -23,7 +23,7 @@ namespace Avalonia.Win32
             _target = target;
         }
 
-        static DropEffect ConvertDropEffect(DragDropEffects operation)
+        public static DropEffect ConvertDropEffect(DragDropEffects operation)
         {
             DropEffect result = DropEffect.None;
             if (operation.HasFlag(DragDropEffects.Copy))
@@ -35,7 +35,7 @@ namespace Avalonia.Win32
             return result;
         }
 
-        static DragDropEffects ConvertDropEffect(DropEffect effect)
+        public static DragDropEffects ConvertDropEffect(DropEffect effect)
         {
             DragDropEffects result = DragDropEffects.None;
             if (effect.HasFlag(DropEffect.Copy))

+ 2 - 1
src/Windows/Avalonia.Win32/Win32Platform.cs

@@ -84,7 +84,8 @@ namespace Avalonia.Win32
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop(60))
                 .Bind<ISystemDialogImpl>().ToSingleton<SystemDialogImpl>()
                 .Bind<IWindowingPlatform>().ToConstant(s_instance)
-                .Bind<IPlatformIconLoader>().ToConstant(s_instance);
+                .Bind<IPlatformIconLoader>().ToConstant(s_instance)
+                .Bind<IPlatformDragSource>().ToSingleton<DragSource>();
 
             UseDeferredRendering = deferredRendering;
             _uiThread = UnmanagedMethods.GetCurrentThreadId();