diff --git a/native/Avalonia.Native/src/OSX/cursor.mm b/native/Avalonia.Native/src/OSX/cursor.mm
index b6f9ed5071..1732d6e71f 100644
--- a/native/Avalonia.Native/src/OSX/cursor.mm
+++ b/native/Avalonia.Native/src/OSX/cursor.mm
@@ -62,6 +62,28 @@ public:
return S_OK;
}
+
+ virtual HRESULT CreateCustomCursor (void* bitmapData, size_t length, AvnPixelSize hotPixel, IAvnCursor** retOut) override
+ {
+ if(bitmapData == nullptr || retOut == nullptr)
+ {
+ return E_POINTER;
+ }
+
+ NSData *imageData = [NSData dataWithBytes:bitmapData length:length];
+ NSImage *image = [[NSImage alloc] initWithData:imageData];
+
+
+ NSPoint hotSpot;
+ hotSpot.x = hotPixel.Width;
+ hotSpot.y = hotPixel.Height;
+
+ *retOut = new Cursor([[NSCursor new] initWithImage: image hotSpot: hotSpot]);
+
+ (*retOut)->AddRef();
+
+ return S_OK;
+ }
};
extern IAvnCursorFactory* CreateCursorFactory()
diff --git a/samples/ControlCatalog/Assets/avalonia-32.png b/samples/ControlCatalog/Assets/avalonia-32.png
new file mode 100644
index 0000000000..7b443e7a25
Binary files /dev/null and b/samples/ControlCatalog/Assets/avalonia-32.png differ
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index f001425964..142c532d75 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -22,6 +22,10 @@
+
+
+
diff --git a/samples/ControlCatalog/Pages/CursorPage.xaml b/samples/ControlCatalog/Pages/CursorPage.xaml
new file mode 100644
index 0000000000..a28039ea3f
--- /dev/null
+++ b/samples/ControlCatalog/Pages/CursorPage.xaml
@@ -0,0 +1,29 @@
+
+
+
+ Cursor
+ Defines a cursor (mouse pointer)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/CursorPage.xaml.cs b/samples/ControlCatalog/Pages/CursorPage.xaml.cs
new file mode 100644
index 0000000000..9e9e9ba8b9
--- /dev/null
+++ b/samples/ControlCatalog/Pages/CursorPage.xaml.cs
@@ -0,0 +1,20 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using ControlCatalog.ViewModels;
+
+namespace ControlCatalog.Pages
+{
+ public class CursorPage : UserControl
+ {
+ public CursorPage()
+ {
+ this.InitializeComponent();
+ DataContext = new CursorPageViewModel();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
new file mode 100644
index 0000000000..f1cc0637dc
--- /dev/null
+++ b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia;
+using Avalonia.Input;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+using MiniMvvm;
+
+namespace ControlCatalog.ViewModels
+{
+ public class CursorPageViewModel : ViewModelBase
+ {
+ public CursorPageViewModel()
+ {
+ StandardCursors = Enum.GetValues(typeof(StandardCursorType))
+ .Cast()
+ .Select(x => new StandardCursorModel(x))
+ .ToList();
+
+ var loader = AvaloniaLocator.Current.GetService();
+ var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png"));
+ var bitmap = new Bitmap(s);
+ CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16));
+ }
+
+ public IEnumerable StandardCursors { get; }
+
+ public Cursor CustomCursor { get; }
+
+ public class StandardCursorModel
+ {
+ public StandardCursorModel(StandardCursorType type)
+ {
+ Type = type;
+ Cursor = new Cursor(type);
+ }
+
+ public StandardCursorType Type { get; }
+
+ public Cursor Cursor { get; }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..e5adc8c6ed
--- /dev/null
+++ b/src/Avalonia.Controls/ApiCompatBaseline.txt
@@ -0,0 +1,6 @@
+Compat issues with assembly Avalonia.Controls:
+MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
+Total Issues: 4
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
index 522103c7bd..ca0e9d48b8 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
+++ b/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)
{
}
diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
index 7514f214aa..09f38042a1 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
@@ -98,7 +98,7 @@ namespace Avalonia.Platform
/// Sets the cursor associated with the toplevel.
///
/// The cursor. Use null for default cursor
- void SetCursor(IPlatformHandle cursor);
+ void SetCursor(ICursorImpl cursor);
///
/// Gets or sets a method called when the underlying implementation is destroyed.
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index 4e43ce13b7..7a92836ddf 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -165,7 +165,7 @@ namespace Avalonia.Controls
this.GetObservable(PointerOverElementProperty)
.Select(
x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty())
- .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
+ .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformImpl));
if (((IStyleHost)this).StylingParent is IResourceHost applicationResources)
{
diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
index fe4c580bbb..67b832318a 100644
--- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
+++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
@@ -47,7 +47,7 @@ namespace Avalonia.DesignerSupport.Remote
var threading = new InternalPlatformThreadingInterface();
AvaloniaLocator.CurrentMutable
.Bind().ToSingleton()
- .Bind().ToSingleton()
+ .Bind().ToSingleton()
.Bind().ToConstant(Keyboard)
.Bind().ToConstant(instance)
.Bind().ToConstant(threading)
diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
index f6783dc0b7..eedfc52d9d 100644
--- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs
+++ b/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)
{
}
@@ -192,9 +192,15 @@ namespace Avalonia.DesignerSupport.Remote
public Task