Browse Source

Added ICursorImpl.

pull/4887/head
Steven Kirk 6 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 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. /// Sets the cursor associated with the toplevel.
/// </summary> /// </summary>
/// <param name="cursor">The cursor. Use null for default cursor</param> /// <param name="cursor">The cursor. Use null for default cursor</param>
void SetCursor(IPlatformHandle cursor); void SetCursor(ICursorImpl cursor);
/// <summary> /// <summary>
/// Gets or sets a method called when the underlying implementation is destroyed. /// 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) this.GetObservable(PointerOverElementProperty)
.Select( .Select(
x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>()) 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) 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 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 class CursorFactoryStub : ICursorFactory
{ {
public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB"); public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new PlatformHandle(IntPtr.Zero, "STUB"); public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
private class CursorStub : ICursorImpl
{
public void Dispose() { }
}
} }
class IconLoaderStub : IPlatformIconLoader class IconLoaderStub : IPlatformIconLoader

10
src/Avalonia.Headless/HeadlessPlatformStubs.cs

@ -54,14 +54,12 @@ namespace Avalonia.Headless
class HeadlessCursorFactoryStub : ICursorFactory class HeadlessCursorFactoryStub : ICursorFactory
{ {
public IPlatformHandle GetCursor(StandardCursorType cursorType) public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
{ public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
return new PlatformHandle(new IntPtr((int)cursorType), "STUB");
}
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 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); 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) 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) public static Cursor Parse(string s)
{ {

4
src/Avalonia.Input/Platform/ICursorFactory.cs

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

2
src/Avalonia.Native/WindowImplBase.cs

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

19
src/Avalonia.X11/X11CursorFactory.cs

@ -56,7 +56,7 @@ namespace Avalonia.X11
.ToDictionary(id => id, id => XLib.XCreateFontCursor(_display, id)); .ToDictionary(id => id, id => XLib.XCreateFontCursor(_display, id));
} }
public IPlatformHandle GetCursor(StandardCursorType cursorType) public ICursorImpl GetCursor(StandardCursorType cursorType)
{ {
IntPtr handle; IntPtr handle;
if (cursorType == StandardCursorType.None) if (cursorType == StandardCursorType.None)
@ -69,10 +69,10 @@ namespace Avalonia.X11
? _cursors[shape] ? _cursors[shape]
: _cursors[CursorFontShape.XC_top_left_arrow]; : _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); 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); 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 PixelSize _pixelSize;
private readonly IUnmanagedBlob _blob; private readonly IUnmanagedBlob _blob;
@ -117,10 +117,9 @@ namespace Avalonia.X11
Handle = XLib.XcursorImageLoadCursor(display, _blob.Address); Handle = XLib.XcursorImageLoadCursor(display, _blob.Address);
} }
public IntPtr Handle { get; }
public string HandleDescriptor => "XCURSOR"; public string HandleDescriptor => "XCURSOR";
public void Dispose() public override void Dispose()
{ {
XLib.XcursorImageDestroy(Handle); XLib.XcursorImageDestroy(Handle);
_blob.Dispose(); _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); UpdateSizeHints(null);
} }
public void SetCursor(IPlatformHandle cursor) public void SetCursor(ICursorImpl cursor)
{ {
if (cursor == null) if (cursor == null)
XDefineCursor(_x11.Display, _handle, _x11.DefaultCursor); XDefineCursor(_x11.Display, _handle, _x11.DefaultCursor);
else else if (cursor is CursorImpl impl)
{ {
if (cursor.HandleDescriptor != "XCURSOR") XDefineCursor(_x11.Display, _handle, impl.Handle);
throw new ArgumentException("Expected XCURSOR handle type");
XDefineCursor(_x11.Display, _handle, cursor.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 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 internal class CursorFactoryStub : ICursorFactory
{ {
public IPlatformHandle GetCursor(StandardCursorType cursorType) public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
{ public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
return new PlatformHandle(IntPtr.Zero, null);
}
public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) private class CursorStub : ICursorImpl
{ {
return new PlatformHandle(IntPtr.Zero, null); public void Dispose() { }
} }
} }
internal class PlatformSettings : IPlatformSettings 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) protected override void OnTextInput(TextCompositionEventArgs e)
=> _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, e.Text)); => _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) if (cursor == null)
Cursor = Cursors.Arrow; Cursor = Cursors.Arrow;
else if (cursor.HandleDescriptor == "HCURSOR") else if (cursor is IPlatformHandle handle)
Cursor = CursorShim.FromHCursor(cursor.Handle); Cursor = CursorShim.FromHCursor(handle.Handle);
} }
Action<RawInputEventArgs> ITopLevelImpl.Input { get; set; } //TODO 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;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using SdBitmap = System.Drawing.Bitmap; using SdBitmap = System.Drawing.Bitmap;
@ -36,8 +34,7 @@ namespace Avalonia.Win32
IntPtr cursor = UnmanagedMethods.LoadCursor(mh, new IntPtr(id)); IntPtr cursor = UnmanagedMethods.LoadCursor(mh, new IntPtr(id));
if (cursor != IntPtr.Zero) if (cursor != IntPtr.Zero)
{ {
PlatformHandle phCursor = new PlatformHandle(cursor, PlatformConstants.CursorHandleType); Cache.Add(cursorType, new CursorImpl(cursor, false));
Cache.Add(cursorType, phCursor);
} }
} }
} }
@ -77,25 +74,23 @@ namespace Avalonia.Win32
{StandardCursorType.DragLink, 32516}, {StandardCursorType.DragLink, 32516},
}; };
private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache = private static readonly Dictionary<StandardCursorType, CursorImpl> Cache =
new Dictionary<StandardCursorType, IPlatformHandle>(); new Dictionary<StandardCursorType, CursorImpl>();
public IPlatformHandle GetCursor(StandardCursorType cursorType) public ICursorImpl GetCursor(StandardCursorType cursorType)
{ {
IPlatformHandle rv; if (!Cache.TryGetValue(cursorType, out var rv))
if (!Cache.TryGetValue(cursorType, out rv))
{ {
Cache[cursorType] = rv = new CursorImpl(
rv = UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType])),
new PlatformHandle( false);
UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType])), Cache.Add(cursorType, rv);
PlatformConstants.CursorHandleType);
} }
return rv; return rv;
} }
public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
{ {
using var source = LoadSystemDrawingBitmap(cursor); using var source = LoadSystemDrawingBitmap(cursor);
using var mask = AlphaToMask(source); using var mask = AlphaToMask(source);
@ -109,9 +104,7 @@ namespace Avalonia.Win32
ColorBitmap = source.GetHbitmap(), ColorBitmap = source.GetHbitmap(),
}; };
return new PlatformHandle( return new CursorImpl(UnmanagedMethods.CreateIconIndirect(ref info), true);
UnmanagedMethods.CreateIconIndirect(ref info),
PlatformConstants.CursorHandleType);
} }
private SdBitmap LoadSystemDrawingBitmap(IBitmapImpl bitmap) 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")] [DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect([In] ref ICONINFO iconInfo); public static extern IntPtr CreateIconIndirect([In] ref ICONINFO iconInfo);
[DllImport("user32.dll")]
public static extern bool DestroyIcon(IntPtr hIcon);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg); 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); SetWindowText(_hwnd, title);
} }
public void SetCursor(IPlatformHandle cursor) public void SetCursor(ICursorImpl cursor)
{ {
var hCursor = cursor?.Handle ?? DefaultCursor; var impl = cursor as CursorImpl;
SetClassLong(_hwnd, ClassLongIndex.GCLP_HCURSOR, hCursor);
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.Input;
using Avalonia.Platform; using Avalonia.Platform;
@ -6,14 +5,21 @@ namespace Avalonia.Controls.UnitTests
{ {
public class CursorFactoryMock : ICursorFactory 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