diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
index 11d9b5d2e1..eb9d460959 100644
--- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
+++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
@@ -45,7 +45,9 @@
KeyTransform.cs
+
+
diff --git a/src/Gtk/Avalonia.Gtk3/CursorFactory.cs b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs
new file mode 100644
index 0000000000..ac547b8bc2
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Gtk3.Interop;
+using Avalonia.Input;
+using Avalonia.Platform;
+using CursorType = Avalonia.Gtk3.GdkCursorType;
+namespace Avalonia.Gtk3
+{
+ class CursorFactory : IStandardCursorFactory
+ {
+ private static readonly Dictionary CursorTypeMapping = new Dictionary
+
+ {
+ {StandardCursorType.AppStarting, CursorType.Watch},
+ {StandardCursorType.Arrow, CursorType.LeftPtr},
+ {StandardCursorType.Cross, CursorType.Cross},
+ {StandardCursorType.Hand, CursorType.Hand1},
+ {StandardCursorType.Ibeam, CursorType.Xterm},
+ {StandardCursorType.No, "gtk-cancel"},
+ {StandardCursorType.SizeAll, CursorType.Sizing},
+ //{ StandardCursorType.SizeNorthEastSouthWest, 32643 },
+ {StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow},
+ //{ StandardCursorType.SizeNorthWestSouthEast, 32642 },
+ {StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow},
+ {StandardCursorType.UpArrow, CursorType.BasedArrowUp},
+ {StandardCursorType.Wait, CursorType.Watch},
+ {StandardCursorType.Help, "gtk-help"},
+ {StandardCursorType.TopSide, CursorType.TopSide},
+ {StandardCursorType.BottomSize, CursorType.BottomSide},
+ {StandardCursorType.LeftSide, CursorType.LeftSide},
+ {StandardCursorType.RightSide, CursorType.RightSide},
+ {StandardCursorType.TopLeftCorner, CursorType.TopLeftCorner},
+ {StandardCursorType.TopRightCorner, CursorType.TopRightCorner},
+ {StandardCursorType.BottomLeftCorner, CursorType.BottomLeftCorner},
+ {StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner}
+ };
+
+ private static readonly Dictionary Cache =
+ new Dictionary();
+
+ private IntPtr GetCursor(object desc)
+ {
+ IntPtr rv;
+ var name = desc as string;
+ if (name != null)
+ {
+ var theme = Native.GtkIconThemeGetDefault();
+ IntPtr icon, error;
+ using (var u = new Utf8Buffer(name))
+ icon = Native.GtkIconThemeLoadIcon(theme, u, 32, 0, out error);
+ rv = icon == IntPtr.Zero
+ ? Native.GdkCursorNew(GdkCursorType.XCursor)
+ : Native.GdkCursorNewFromPixbuf(Native.GdkGetDefaultDisplay(), icon, 0, 0);
+ }
+ else
+ {
+ rv = Native.GdkCursorNew((CursorType)desc);
+ }
+
+
+ return rv;
+ }
+
+ public IPlatformHandle GetCursor(StandardCursorType cursorType)
+ {
+ IPlatformHandle rv;
+ if (!Cache.TryGetValue(cursorType, out rv))
+ {
+ Cache[cursorType] =
+ rv =
+ new PlatformHandle(
+ GetCursor(CursorTypeMapping[cursorType]),
+ "GTKCURSOR");
+ }
+
+ return rv;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Gtk/Avalonia.Gtk3/GdkCursor.cs b/src/Gtk/Avalonia.Gtk3/GdkCursor.cs
new file mode 100644
index 0000000000..48815e1273
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/GdkCursor.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Gtk3
+{
+ enum GdkCursorType
+ {
+ CursorIsPixmap = -1,
+ XCursor = 0,
+ Arrow = 2,
+ BasedArrowDown = 4,
+ BasedArrowUp = 6,
+ Boat = 8,
+ Bogosity = 10,
+ BottomLeftCorner = 12,
+ BottomRightCorner = 14,
+ BottomSide = 16,
+ BottomTee = 18,
+ BoxSpiral = 20,
+ CenterPtr = 22,
+ Circle = 24,
+ Clock = 26,
+ CoffeeMug = 28,
+ Cross = 30,
+ CrossReverse = 32,
+ Crosshair = 34,
+ DiamondCross = 36,
+ Dot = 38,
+ Dotbox = 40,
+ DoubleArrow = 42,
+ DraftLarge = 44,
+ DraftSmall = 46,
+ DrapedBox = 48,
+ Exchange = 50,
+ Fleur = 52,
+ Gobbler = 54,
+ Gumby = 56,
+ Hand1 = 58,
+ Hand2 = 60,
+ Heart = 62,
+ Icon = 64,
+ IronCross = 66,
+ LeftPtr = 68,
+ LeftSide = 70,
+ LeftTee = 72,
+ Leftbutton = 74,
+ LlAngle = 76,
+ LrAngle = 78,
+ Man = 80,
+ Middlebutton = 82,
+ Mouse = 84,
+ Pencil = 86,
+ Pirate = 88,
+ Plus = 90,
+ QuestionArrow = 92,
+ RightPtr = 94,
+ RightSide = 96,
+ RightTee = 98,
+ Rightbutton = 100,
+ RtlLogo = 102,
+ Sailboat = 104,
+ SbDownArrow = 106,
+ SbHDoubleArrow = 108,
+ SbLeftArrow = 110,
+ SbRightArrow = 112,
+ SbUpArrow = 114,
+ SbVDoubleArrow = 116,
+ Shuttle = 118,
+ Sizing = 120,
+ Spider = 122,
+ Spraycan = 124,
+ Star = 126,
+ Target = 128,
+ Tcross = 130,
+ TopLeftArrow = 132,
+ TopLeftCorner = 134,
+ TopRightCorner = 136,
+ TopSide = 138,
+ TopTee = 140,
+ Trek = 142,
+ UlAngle = 144,
+ Umbrella = 146,
+ UrAngle = 148,
+ Watch = 150,
+ Xterm = 152,
+ LastCursor = 153,
+ }
+}
\ No newline at end of file
diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
index 943d83b57f..68583da480 100644
--- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
+++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
@@ -31,7 +31,7 @@ namespace Avalonia.Gtk3
AvaloniaLocator.CurrentMutable.Bind().ToConstant(Instance)
.Bind().ToSingleton()
- .Bind().ToConstant(new CursorFactoryStub())
+ .Bind().ToConstant(new CursorFactory())
.Bind().ToConstant(Keyboard)
.Bind().ToConstant(Mouse)
.Bind().ToConstant(Instance)
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
index 4112bf3f91..25c10fd32b 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
@@ -140,6 +140,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate IntPtr gdk_screen_get_root_window(IntPtr screen);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+ public delegate IntPtr gdk_cursor_new(GdkCursorType type);
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
@@ -170,6 +173,18 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)]
public delegate IntPtr gdk_pixbuf_new_from_file(Utf8Buffer filename, out IntPtr error);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate IntPtr gtk_icon_theme_get_default();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate IntPtr gtk_icon_theme_load_icon(IntPtr icon_theme, Utf8Buffer icon_name, gint size, int flags,out IntPtr error);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+ public delegate IntPtr gdk_cursor_new_from_pixbuf(IntPtr disp, IntPtr pixbuf, int x, int y);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+ public delegate IntPtr gdk_window_set_cursor(IntPtr window, IntPtr cursor);
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)]
public delegate IntPtr gdk_pixbuf_new_from_stream(GInputStream stream, IntPtr cancel, out IntPtr error);
@@ -257,12 +272,17 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_display_get_default GdkGetDefaultDisplay;
public static D.gdk_screen_get_width GdkScreenGetWidth;
public static D.gdk_screen_get_root_window GdkScreenGetRootWindow;
+ public static D.gdk_cursor_new GdkCursorNew;
public static D.gdk_window_get_origin GdkWindowGetOrigin;
public static D.gdk_window_get_pointer GdkWindowGetPointer;
public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag;
public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
public static D.gdk_pixbuf_new_from_file GdkPixbufNewFromFile;
+ public static D.gtk_icon_theme_get_default GtkIconThemeGetDefault;
+ public static D.gtk_icon_theme_load_icon GtkIconThemeLoadIcon;
+ public static D.gdk_cursor_new_from_pixbuf GdkCursorNewFromPixbuf;
+ public static D.gdk_window_set_cursor GdkWindowSetCursor;
public static D.gdk_pixbuf_new_from_stream GdkPixbufNewFromStream;
public static D.gdk_pixbuf_save_to_bufferv GdkPixbufSaveToBufferv;
diff --git a/src/Gtk/Avalonia.Gtk3/Stubs.cs b/src/Gtk/Avalonia.Gtk3/Stubs.cs
index 8ebc1e9c59..6d89384838 100644
--- a/src/Gtk/Avalonia.Gtk3/Stubs.cs
+++ b/src/Gtk/Avalonia.Gtk3/Stubs.cs
@@ -1,11 +1,9 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
-using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Platform;
@@ -13,11 +11,6 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
- class CursorFactoryStub : IStandardCursorFactory
- {
- public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB");
- }
-
class SystemDialogStub : ISystemDialogImpl
{
public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) => Task.FromResult(new string[0]);
diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
index 0a8ae49906..e24e435c08 100644
--- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
+++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
@@ -259,7 +259,7 @@ namespace Avalonia.Gtk3
public void SetCursor(IPlatformHandle cursor)
{
- //STUB
+ Native.GdkWindowSetCursor(Native.GtkWidgetGetWindow(GtkWidget), cursor?.Handle ?? IntPtr.Zero);
}
public void Show() => Native.GtkWindowPresent(GtkWidget);