Added ICursorImpl.

This commit is contained in:
Steven Kirk
2020-10-20 14:32:28 +02:00
parent a832d63915
commit ca408e55b5
20 changed files with 121 additions and 71 deletions
@@ -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)
{
}
@@ -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
View File
@@ -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
View File
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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)
{
@@ -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);
}
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
@@ -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
View File
@@ -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
@@ -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
View File
@@ -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;
}
}
}
}
@@ -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
View File
@@ -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);
}
}
}
@@ -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()
{
}
}
}
}