Browse Source

Added ICursorImpl.

pull/4887/head
Steven Kirk 5 years ago
parent
commit
ca408e55b5
  1. 2
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  2. 2
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  3. 2
      src/Avalonia.Controls/TopLevel.cs
  4. 11
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  5. 10
      src/Avalonia.Headless/HeadlessPlatformStubs.cs
  6. 2
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  7. 6
      src/Avalonia.Input/Cursor.cs
  8. 4
      src/Avalonia.Input/Platform/ICursorFactory.cs
  9. 14
      src/Avalonia.Input/Platform/ICursorImpl.cs
  10. 6
      src/Avalonia.Native/Cursor.cs
  11. 2
      src/Avalonia.Native/WindowImplBase.cs
  12. 19
      src/Avalonia.X11/X11CursorFactory.cs
  13. 8
      src/Avalonia.X11/X11Window.cs
  14. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  15. 10
      src/Linux/Avalonia.LinuxFramebuffer/Stubs.cs
  16. 6
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  17. 52
      src/Windows/Avalonia.Win32/CursorFactory.cs
  18. 3
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  19. 15
      src/Windows/Avalonia.Win32/WindowImpl.cs
  20. 16
      tests/Avalonia.Controls.UnitTests/CursorFactoryMock.cs

2
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)
{
}

2
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.

2
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)
{

11
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

10
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() { }
}
}

2
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)
{
}

6
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)
{

4
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
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
{
}
}

6
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();
}

2
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)
{

19
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() { }
}
}

8
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);
}
}

2
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)
{
}

10
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

6
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

52
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
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);

15
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);
}
}
}

16
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()
{
}
}
}
}

Loading…
Cancel
Save