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