Browse Source

Added ICursorImpl.

Steven Kirk 5 years ago
parent
commit
ca408e55b5

+ 1 - 1
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@@ -61,7 +61,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
 
         public virtual PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1);
 
-        public virtual void SetCursor(IPlatformHandle cursor)
+        public virtual void SetCursor(ICursorImpl cursor)
         {
         }
 

+ 1 - 1
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@@ -98,7 +98,7 @@ namespace Avalonia.Platform
         /// Sets the cursor associated with the toplevel.
         /// </summary>
         /// <param name="cursor">The cursor. Use null for default cursor</param>
-        void SetCursor(IPlatformHandle cursor);
+        void SetCursor(ICursorImpl cursor);
 
         /// <summary>
         /// Gets or sets a method called when the underlying implementation is destroyed.

+ 1 - 1
src/Avalonia.Controls/TopLevel.cs

@@ -162,7 +162,7 @@ namespace Avalonia.Controls
             this.GetObservable(PointerOverElementProperty)
                 .Select(
                     x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
-                .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
+                .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformImpl));
 
             if (((IStyleHost)this).StylingParent is IResourceHost applicationResources)
             {

+ 8 - 3
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -73,7 +73,7 @@ namespace Avalonia.DesignerSupport.Remote
 
         public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
 
-        public void SetCursor(IPlatformHandle cursor)
+        public void SetCursor(ICursorImpl cursor)
         {
         }
 
@@ -194,8 +194,13 @@ namespace Avalonia.DesignerSupport.Remote
 
     class CursorFactoryStub : ICursorFactory
     {
-        public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB");
-        public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new PlatformHandle(IntPtr.Zero, "STUB");
+        public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
+        public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
+
+        private class CursorStub : ICursorImpl
+        {
+            public void Dispose() { }
+        }
     }
 
     class IconLoaderStub : IPlatformIconLoader

+ 4 - 6
src/Avalonia.Headless/HeadlessPlatformStubs.cs

@@ -54,14 +54,12 @@ namespace Avalonia.Headless
 
     class HeadlessCursorFactoryStub : ICursorFactory
     {
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
-        {
-            return new PlatformHandle(new IntPtr((int)cursorType), "STUB");
-        }
+        public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
+        public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
 
-        public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
+        private class CursorStub : ICursorImpl
         {
-            return new PlatformHandle(IntPtr.Zero, "STUB");
+            public void Dispose() { }
         }
     }
 

+ 1 - 1
src/Avalonia.Headless/HeadlessWindowImpl.cs

@@ -67,7 +67,7 @@ namespace Avalonia.Headless
 
         public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
 
-        public void SetCursor(IPlatformHandle cursor)
+        public void SetCursor(ICursorImpl cursor)
         {
 
         }

+ 3 - 3
src/Avalonia.Input/Cursor.cs

@@ -53,9 +53,9 @@ namespace Avalonia.Input
     {
         public static readonly Cursor Default = new Cursor(StandardCursorType.Arrow);
 
-        internal Cursor(IPlatformHandle platformCursor)
+        internal Cursor(ICursorImpl platformImpl)
         {
-            PlatformCursor = platformCursor;
+            PlatformImpl = platformImpl;
         }
 
         public Cursor(StandardCursorType cursorType)
@@ -68,7 +68,7 @@ namespace Avalonia.Input
         {
         }
 
-        public IPlatformHandle PlatformCursor { get; }
+        public ICursorImpl PlatformImpl { get; }
 
         public static Cursor Parse(string s)
         {

+ 2 - 2
src/Avalonia.Input/Platform/ICursorFactory.cs

@@ -6,7 +6,7 @@ namespace Avalonia.Platform
 {
     public interface ICursorFactory
     {
-        IPlatformHandle GetCursor(StandardCursorType cursorType);
-        IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot);
+        ICursorImpl GetCursor(StandardCursorType cursorType);
+        ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot);
     }
 }

+ 14 - 0
src/Avalonia.Input/Platform/ICursorImpl.cs

@@ -0,0 +1,14 @@
+using System;
+using Avalonia.Input;
+
+#nullable enable
+
+namespace Avalonia.Platform
+{
+    /// <summary>
+    /// Represents a platform implementation of a <see cref="Cursor"/>.
+    /// </summary>
+    public interface ICursorImpl : IDisposable
+    {
+    }
+}

+ 3 - 3
src/Avalonia.Native/Cursor.cs

@@ -5,7 +5,7 @@ using Avalonia.Native.Interop;
 
 namespace Avalonia.Native
 {
-    class AvaloniaNativeCursor : IPlatformHandle, IDisposable
+    class AvaloniaNativeCursor : ICursorImpl, IDisposable
     {
         public IAvnCursor Cursor { get; private set; }
         public IntPtr Handle => IntPtr.Zero;
@@ -33,13 +33,13 @@ namespace Avalonia.Native
             _native = native;
         }
 
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
+        public ICursorImpl GetCursor(StandardCursorType cursorType)
         {
             var cursor = _native.GetCursor((AvnStandardCursorType)cursorType);
             return new AvaloniaNativeCursor( cursor );
         }
 
-        public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
+        public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
         {
             throw new NotImplementedException();
         }

+ 1 - 1
src/Avalonia.Native/WindowImplBase.cs

@@ -389,7 +389,7 @@ namespace Avalonia.Native
         public Action Deactivated { get; set; }
         public Action Activated { get; set; }
 
-        public void SetCursor(IPlatformHandle cursor)
+        public void SetCursor(ICursorImpl cursor)
         {
             if (_native == null)
             {

+ 13 - 6
src/Avalonia.X11/X11CursorFactory.cs

@@ -56,7 +56,7 @@ namespace Avalonia.X11
                 .ToDictionary(id => id, id => XLib.XCreateFontCursor(_display, id));
         }
 
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
+        public ICursorImpl GetCursor(StandardCursorType cursorType)
         {
             IntPtr handle;
             if (cursorType == StandardCursorType.None)
@@ -69,10 +69,10 @@ namespace Avalonia.X11
                 ? _cursors[shape]
                 : _cursors[CursorFontShape.XC_top_left_arrow];
             }
-            return new PlatformHandle(handle, "XCURSOR");
+            return new CursorImpl(handle);
         }
 
-        public unsafe IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
+        public unsafe ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
         {
             return new XImageCursor(_display, cursor, hotSpot);
         }
@@ -85,7 +85,7 @@ namespace Avalonia.X11
             return XLib.XCreatePixmapCursor(display, pixmap, pixmap, ref color, ref color, 0, 0);
         }
 
-        private unsafe class XImageCursor : IFramebufferPlatformSurface, IPlatformHandle, IDisposable
+        private unsafe class XImageCursor : CursorImpl, IFramebufferPlatformSurface, IPlatformHandle
         {
             private readonly PixelSize _pixelSize;
             private readonly IUnmanagedBlob _blob;
@@ -117,10 +117,9 @@ namespace Avalonia.X11
                 Handle = XLib.XcursorImageLoadCursor(display, _blob.Address);
             }
 
-            public IntPtr Handle { get; }
             public string HandleDescriptor => "XCURSOR";
 
-            public void Dispose()
+            public override void Dispose()
             {
                 XLib.XcursorImageDestroy(Handle);
                 _blob.Dispose();
@@ -135,4 +134,12 @@ namespace Avalonia.X11
             }
         }
     }
+
+    class CursorImpl : ICursorImpl
+    {
+        public CursorImpl() { }
+        public CursorImpl(IntPtr handle) => Handle = handle;
+        public IntPtr Handle { get; protected set; }
+        public virtual void Dispose() { }
+    }
 }

+ 3 - 5
src/Avalonia.X11/X11Window.cs

@@ -878,15 +878,13 @@ namespace Avalonia.X11
             UpdateSizeHints(null);
         }
 
-        public void SetCursor(IPlatformHandle cursor)
+        public void SetCursor(ICursorImpl cursor)
         {
             if (cursor == null)
                 XDefineCursor(_x11.Display, _handle, _x11.DefaultCursor);
-            else
+            else if (cursor is CursorImpl impl)
             {
-                if (cursor.HandleDescriptor != "XCURSOR")
-                    throw new ArgumentException("Expected XCURSOR handle type");
-                XDefineCursor(_x11.Display, _handle, cursor.Handle);
+                XDefineCursor(_x11.Display, _handle, impl.Handle);
             }
         }
 

+ 1 - 1
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@@ -57,7 +57,7 @@ namespace Avalonia.LinuxFramebuffer
 
         public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
 
-        public void SetCursor(IPlatformHandle cursor)
+        public void SetCursor(ICursorImpl cursor)
         {
         }
 

+ 4 - 6
src/Linux/Avalonia.LinuxFramebuffer/Stubs.cs

@@ -6,14 +6,12 @@ namespace Avalonia.LinuxFramebuffer
 {
     internal class CursorFactoryStub : ICursorFactory
     {
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
-        {
-            return new PlatformHandle(IntPtr.Zero, null);
-        }
+        public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
+        public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
 
-        public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
+        private class CursorStub : ICursorImpl
         {
-            return new PlatformHandle(IntPtr.Zero, null);
+            public void Dispose() { }
         }
     }
     internal class PlatformSettings : IPlatformSettings

+ 3 - 3
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@@ -225,12 +225,12 @@ namespace Avalonia.Win32.Interop.Wpf
         protected override void OnTextInput(TextCompositionEventArgs e) 
             => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, e.Text));
 
-        void ITopLevelImpl.SetCursor(IPlatformHandle cursor)
+        void ITopLevelImpl.SetCursor(ICursorImpl cursor)
         {
             if (cursor == null)
                 Cursor = Cursors.Arrow;
-            else if (cursor.HandleDescriptor == "HCURSOR")
-                Cursor = CursorShim.FromHCursor(cursor.Handle);
+            else if (cursor is IPlatformHandle handle)
+                Cursor = CursorShim.FromHCursor(handle.Handle);
         }
 
         Action<RawInputEventArgs> ITopLevelImpl.Input { get; set; } //TODO

+ 34 - 18
src/Windows/Avalonia.Win32/CursorFactory.cs

@@ -3,9 +3,7 @@ using System.Collections.Generic;
 using System.Drawing;
 using System.Drawing.Imaging;
 using System.IO;
-using System.Runtime.InteropServices;
 using Avalonia.Input;
-using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using Avalonia.Win32.Interop;
 using SdBitmap = System.Drawing.Bitmap;
@@ -36,8 +34,7 @@ namespace Avalonia.Win32
                 IntPtr cursor = UnmanagedMethods.LoadCursor(mh, new IntPtr(id));
                 if (cursor != IntPtr.Zero)
                 {
-                    PlatformHandle phCursor = new PlatformHandle(cursor, PlatformConstants.CursorHandleType);
-                    Cache.Add(cursorType, phCursor);
+                    Cache.Add(cursorType, new CursorImpl(cursor, false));
                 }
             }
         }
@@ -77,25 +74,23 @@ namespace Avalonia.Win32
             {StandardCursorType.DragLink, 32516},
         };
 
-        private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =
-            new Dictionary<StandardCursorType, IPlatformHandle>();
+        private static readonly Dictionary<StandardCursorType, CursorImpl> Cache =
+            new Dictionary<StandardCursorType, CursorImpl>();
 
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
+        public ICursorImpl GetCursor(StandardCursorType cursorType)
         {
-            IPlatformHandle rv;
-            if (!Cache.TryGetValue(cursorType, out rv))
+            if (!Cache.TryGetValue(cursorType, out var rv))
             {
-                Cache[cursorType] =
-                    rv =
-                        new PlatformHandle(
-                            UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType])),
-                            PlatformConstants.CursorHandleType);
+                rv = new CursorImpl(
+                    UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType])),
+                    false);
+                Cache.Add(cursorType, rv);
             }
 
             return rv;
         }
 
-        public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
+        public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
         {
             using var source = LoadSystemDrawingBitmap(cursor);
             using var mask = AlphaToMask(source);
@@ -109,9 +104,7 @@ namespace Avalonia.Win32
                 ColorBitmap = source.GetHbitmap(),
             };
 
-            return new PlatformHandle(
-                UnmanagedMethods.CreateIconIndirect(ref info),
-                PlatformConstants.CursorHandleType);
+            return new CursorImpl(UnmanagedMethods.CreateIconIndirect(ref info), true);
         }
 
         private SdBitmap LoadSystemDrawingBitmap(IBitmapImpl bitmap)
@@ -168,4 +161,27 @@ namespace Avalonia.Win32
             }
         }
     }
+
+    internal class CursorImpl : ICursorImpl, IPlatformHandle
+    {
+        private readonly bool _isCustom;
+
+        public CursorImpl(IntPtr handle, bool isCustom)
+        {
+            Handle = handle;
+            _isCustom = isCustom;
+        }
+
+        public IntPtr Handle { get; private set; }
+        public string HandleDescriptor => PlatformConstants.CursorHandleType;
+
+        public void Dispose()
+        {
+            if (_isCustom && Handle != IntPtr.Zero)
+            {
+                UnmanagedMethods.DestroyIcon(Handle);
+                Handle = IntPtr.Zero;
+            }
+        }
+    }
 }

+ 3 - 0
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -1034,6 +1034,9 @@ namespace Avalonia.Win32.Interop
         [DllImport("user32.dll")]
         public static extern IntPtr CreateIconIndirect([In] ref ICONINFO iconInfo);
 
+        [DllImport("user32.dll")]
+        public static extern bool DestroyIcon(IntPtr hIcon);
+
         [DllImport("user32.dll")]
         public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
 

+ 10 - 5
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -557,14 +557,19 @@ namespace Avalonia.Win32
             SetWindowText(_hwnd, title);
         }
 
-        public void SetCursor(IPlatformHandle cursor)
+        public void SetCursor(ICursorImpl cursor)
         {
-            var hCursor = cursor?.Handle ?? DefaultCursor;
-            SetClassLong(_hwnd, ClassLongIndex.GCLP_HCURSOR, hCursor);
+            var impl = cursor as CursorImpl;
 
-            if (_owner.IsPointerOver)
+            if (cursor is null || impl is object)
             {
-                UnmanagedMethods.SetCursor(hCursor);
+                var hCursor = impl?.Handle ?? DefaultCursor;
+                SetClassLong(_hwnd, ClassLongIndex.GCLP_HCURSOR, hCursor);
+
+                if (_owner.IsPointerOver)
+                {
+                    UnmanagedMethods.SetCursor(hCursor);
+                }
             }
         }
 

+ 11 - 5
tests/Avalonia.Controls.UnitTests/CursorFactoryMock.cs

@@ -1,4 +1,3 @@
-using System;
 using Avalonia.Input;
 using Avalonia.Platform;
 
@@ -6,14 +5,21 @@ namespace Avalonia.Controls.UnitTests
 {
     public class CursorFactoryMock : ICursorFactory
     {
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
+        public ICursorImpl GetCursor(StandardCursorType cursorType)
         {
-            return new PlatformHandle(IntPtr.Zero, cursorType.ToString());
+            return new MockCursorImpl();
         }
 
-        public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
+        public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
         {
-            return new PlatformHandle(IntPtr.Zero, "Custom");
+            return new MockCursorImpl();
+        }
+
+        private class MockCursorImpl : ICursorImpl
+        {
+            public void Dispose()
+            {
+            }
         }
     }
 }