From 38a43f7a7e3b3aa9d0299305c59e5d0a705f82da Mon Sep 17 00:00:00 2001 From: lytico Date: Tue, 21 Apr 2020 17:28:34 +0200 Subject: [PATCH 1/3] GtkSharp.CellRenderer: implement https://github.com/mono/gtk-sharp/blob/master/gtk/glue/gtk/cellrenderer.c --- Source/Libs/GtkSharp/CellRenderer.GetSize.cs | 166 +++++++++++++++++++ Source/Libs/GtkSharp/CellRenderer.cs | 4 - 2 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 Source/Libs/GtkSharp/CellRenderer.GetSize.cs diff --git a/Source/Libs/GtkSharp/CellRenderer.GetSize.cs b/Source/Libs/GtkSharp/CellRenderer.GetSize.cs new file mode 100644 index 000000000..393f8aa4b --- /dev/null +++ b/Source/Libs/GtkSharp/CellRenderer.GetSize.cs @@ -0,0 +1,166 @@ +using GtkSharp; + +namespace Gtk +{ + + using System; + using System.Runtime.InteropServices; + + public partial class CellRenderer + { + static GetSizeNativeDelegate GetSize_cb_delegate; + + static GetSizeNativeDelegate GetSizeVMCallback { + get { + if (GetSize_cb_delegate == null) + GetSize_cb_delegate = new GetSizeNativeDelegate (GetSize_cb); + return GetSize_cb_delegate; + } + } + + static void OverrideGetSize (GLib.GType gtype) + { + OverrideGetSize (gtype, GetSizeVMCallback); + } + + /// + /// sets callback GetSize in GtkCellRendererClass-Struct + /// + /// + /// + static void OverrideGetSize (GLib.GType gtype, GetSizeNativeDelegate callback) + { + unsafe { + IntPtr* raw_ptr = (IntPtr*) (((long) gtype.GetClassPtr ()) + (long) class_abi.GetFieldOffset ("get_size")); + *raw_ptr = Marshal.GetFunctionPointerForDelegate ((Delegate) callback); + } + } + + // We have to implement this VM manually because x_offset, y_offset, width and height params may be NULL and therefore cannot be treated as "out int" + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void GetSizeNativeDelegate (IntPtr item, IntPtr widget, IntPtr cell_area_ptr, IntPtr x_offset, IntPtr y_offset, IntPtr width, IntPtr height); + + static void GetSize_cb (IntPtr item, IntPtr widget, IntPtr cell_area_ptr, IntPtr x_offset, IntPtr y_offset, IntPtr width, IntPtr height) + { + try { + CellRenderer obj = GLib.Object.GetObject (item, false) as CellRenderer; + Gtk.Widget widg = GLib.Object.GetObject (widget, false) as Gtk.Widget; + Gdk.Rectangle cell_area = Gdk.Rectangle.Zero; + if (cell_area_ptr != IntPtr.Zero) + cell_area = Gdk.Rectangle.New (cell_area_ptr); + int a, b, c, d; + + obj.OnGetSize (widg, ref cell_area, out a, out b, out c, out d); + if (x_offset != IntPtr.Zero) + Marshal.WriteInt32 (x_offset, a); + if (y_offset != IntPtr.Zero) + Marshal.WriteInt32 (y_offset, b); + if (width != IntPtr.Zero) + Marshal.WriteInt32 (width, c); + if (height != IntPtr.Zero) + Marshal.WriteInt32 (height, d); + } catch (Exception e) { + GLib.ExceptionManager.RaiseUnhandledException (e, false); + } + } + + [GLib.DefaultSignalHandler (Type = typeof(Gtk.CellRenderer), ConnectionMethod = nameof(OverrideGetSize))] + protected virtual void OnGetSize (Gtk.Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height) + { + InternalOnGetSize (widget, ref cell_area, out x_offset, out y_offset, out width, out height); + } + + private void InternalOnGetSize (Gtk.Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height) + { + IntPtr native_cell_area = GLib.Marshaller.StructureToPtrAlloc (cell_area); + gtksharp_cellrenderer_base_get_size (Handle, widget?.Handle ?? IntPtr.Zero, native_cell_area, out x_offset, out y_offset, out width, out height); + cell_area = Gdk.Rectangle.New (native_cell_area); + Marshal.FreeHGlobal (native_cell_area); + } + + static GetSizeNativeDelegate InternalGetSizeNativeDelegate (GLib.GType gtype) + { + GetSizeNativeDelegate unmanaged = null; + unsafe { + IntPtr* raw_ptr = (IntPtr*) (((long) gtype.GetClassPtr ()) + (long) class_abi.GetFieldOffset ("get_size")); + if (*raw_ptr == IntPtr.Zero) + return default; + + unmanaged = Marshal.GetDelegateForFunctionPointer (*raw_ptr); + } + + return unmanaged; + } + + static void gtksharp_cellrenderer_base_get_size (IntPtr cell, IntPtr widget, IntPtr cell_area, out int x_offset, out int y_offset, out int width, out int height) + { + /* + const gchar *__gtype_prefix = "__gtksharp_"; + #define HAS_PREFIX(a) (*((guint64 *)(a)) == *((guint64 *) __gtype_prefix)) + + static GObjectClass * + get_threshold_class (GObject *obj) + { + GType gtype = G_TYPE_FROM_INSTANCE (obj); + while (HAS_PREFIX (g_type_name (gtype))) + gtype = g_type_parent (gtype); + GObjectClass *klass = g_type_class_peek (gtype); + if (klass == NULL) klass = g_type_class_ref (gtype); + return klass; + } + + void gtksharp_cellrenderer_base_get_size (GtkCellRenderer *cell, GtkWidget *widget, GdkRectangle *cell_area, gint *x_offset, gint *y_offset, gint *width, gint *height) + { + GtkCellRendererClass *klass = (GtkCellRendererClass *) get_threshold_class (G_OBJECT (cell)); + if (klass->get_size) + (* klass->get_size) (cell, widget, cell_area, x_offset, y_offset, width, height); + } + */ + + x_offset = 0; + y_offset = 0; + width = 0; + height = 0; + + if (cell == IntPtr.Zero) { + return; + } + + var obj = TryGetObject (cell); + var parent = obj.NativeType; + while ((parent = parent.GetBaseType ()) != GLib.GType.None) { + if (parent.ToString ().StartsWith ("__gtksharp_")) { + continue; + } + + var GetSize = InternalGetSizeNativeDelegate (parent); + if (GetSize != default) { + var a = Marshal.AllocHGlobal (sizeof(int)); + var b = Marshal.AllocHGlobal (sizeof(int)); + var c = Marshal.AllocHGlobal (sizeof(int)); + var d = Marshal.AllocHGlobal (sizeof(int)); + try { + GetSize (cell, widget, cell_area, a, b, c, d); + if (a != IntPtr.Zero) + Marshal.WriteInt32 (a, x_offset); + if (b != IntPtr.Zero) + Marshal.WriteInt32 (b, y_offset); + if (c != IntPtr.Zero) + Marshal.WriteInt32 (c, width); + if (d != IntPtr.Zero) + Marshal.WriteInt32 (d, height); + } finally { + Marshal.FreeHGlobal (a); + Marshal.FreeHGlobal (b); + Marshal.FreeHGlobal (c); + Marshal.FreeHGlobal (d); + } + } + } + + return; + } + + } + +} \ No newline at end of file diff --git a/Source/Libs/GtkSharp/CellRenderer.cs b/Source/Libs/GtkSharp/CellRenderer.cs index 55543caa1..650e2309c 100644 --- a/Source/Libs/GtkSharp/CellRenderer.cs +++ b/Source/Libs/GtkSharp/CellRenderer.cs @@ -48,10 +48,6 @@ namespace Gtk { gtk_cell_renderer_render2 (Handle, context == null ? IntPtr.Zero : context.Handle, widget == null ? IntPtr.Zero : widget.Handle, ref background_area, ref cell_area, ref expose_area, (int) flags); } - // We have to implement this VM manually because x_offset, y_offset, width and height params may be NULL and therefore cannot be treated as "out int" - // TODO: Implement "nullable" attribute for value type parameters in GAPI - [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - delegate void OnGetSizeDelegate (IntPtr item, IntPtr widget, IntPtr cell_area_ptr, IntPtr x_offset, IntPtr y_offset, IntPtr width, IntPtr height); } } From 87bdffacc7bdc273fedd956e505e7e4ed6a0bbd4 Mon Sep 17 00:00:00 2001 From: lytico Date: Tue, 21 Apr 2020 18:51:12 +0200 Subject: [PATCH 2/3] Samples: add CustomCellRenderer, adopted from https://github.com/mono/gtk-sharp/blob/master/sample/CustomCellRenderer.cs --- .../Sections/Widgets/CellRendererSection.cs | 53 +++++++++++++ .../CustomWidgets/CustomCellRenderer.cs | 77 +++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 Source/Samples/Sections/Widgets/CellRendererSection.cs create mode 100644 Source/Samples/Sections/Widgets/CustomWidgets/CustomCellRenderer.cs diff --git a/Source/Samples/Sections/Widgets/CellRendererSection.cs b/Source/Samples/Sections/Widgets/CellRendererSection.cs new file mode 100644 index 000000000..4df97157b --- /dev/null +++ b/Source/Samples/Sections/Widgets/CellRendererSection.cs @@ -0,0 +1,53 @@ +using Gtk; +using System; +namespace Samples +{ + + [Section (ContentType = typeof(CustomCellRenderer), Category = Category.Widgets)] + class CellRendererSection : ListSection + { + public CellRendererSection () + { + AddItem (CreateCellRenderer ()); + + } + ListStore liststore; + + public (string, Widget) CreateCellRenderer () + { + liststore = new ListStore (typeof (float), typeof (string)); + liststore.AppendValues (0.5f, "50%"); + + TreeView view = new TreeView (liststore); + + view.AppendColumn ("Progress", new CellRendererText (), "text", 1); + view.AppendColumn ("Progress", new CustomCellRenderer (), "percent", 0); + GLib.Timeout.Add (50, new GLib.TimeoutHandler (update_percent)); + + return (nameof(CustomCellRenderer), view); + } + + bool increasing = true; + bool update_percent () + { + TreeIter iter; + liststore.GetIterFirst (out iter); + float perc = (float) liststore.GetValue (iter, 0); + + if ((perc > 0.99) || (perc < 0.01 && perc > 0)) { + increasing = !increasing; + } + + if (increasing) + perc += 0.01f; + else + perc -= 0.01f; + + liststore.SetValue (iter, 0, perc); + liststore.SetValue (iter, 1, Convert.ToInt32 (perc * 100) + "%"); + + return true; + } + } + +} \ No newline at end of file diff --git a/Source/Samples/Sections/Widgets/CustomWidgets/CustomCellRenderer.cs b/Source/Samples/Sections/Widgets/CustomWidgets/CustomCellRenderer.cs new file mode 100644 index 000000000..3810927e7 --- /dev/null +++ b/Source/Samples/Sections/Widgets/CustomWidgets/CustomCellRenderer.cs @@ -0,0 +1,77 @@ +// CustomCellRenderer.cs : C# implementation of an example custom cellrenderer +// from http://scentric.net/tutorial/sec-custom-cell-renderers.html +// +// Author: Todd Berman +// +// (c) 2004 Todd Berman + +// adopted from: https://github.com/mono/gtk-sharp/commits/2.99.3/sample/CustomCellRenderer.cs + +using System; +using Gtk; +using Gdk; +using GLib; + +namespace Samples +{ + + public class CustomCellRenderer : CellRenderer + { + + private float percent; + + [GLib.Property ("percent")] + public float Percentage { + get { return percent; } + set { percent = value; } + } + + protected override void OnGetSize (Widget widget, ref Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height) + { + base.OnGetSize (widget, ref cell_area, out x_offset, out y_offset, out width, out height); + + int calc_width = (int) this.Xpad * 2 + 100; + int calc_height = (int) this.Ypad * 2 + 10; + + width = calc_width; + height = calc_height; + + x_offset = 0; + y_offset = 0; + if (!cell_area.Equals (Rectangle.Zero)) { + x_offset = (int) (this.Xalign * (cell_area.Width - calc_width)); + x_offset = Math.Max (x_offset, 0); + + y_offset = (int) (this.Yalign * (cell_area.Height - calc_height)); + y_offset = Math.Max (y_offset, 0); + } + } + + protected override void OnRender (Cairo.Context cr, Widget widget, Rectangle background_area, Rectangle cell_area, CellRendererState flags) + { + int x = (int) (cell_area.X + this.Xpad); + int y = (int) (cell_area.Y + this.Ypad); + int width = (int) (cell_area.Width - this.Xpad * 2); + int height = (int) (cell_area.Height - this.Ypad * 2); + + widget.StyleContext.Save (); + widget.StyleContext.AddClass ("trough"); + widget.StyleContext.RenderBackground (cr, x, y, width, height); + widget.StyleContext.RenderFrame (cr, x, y, width, height); + + Border padding = widget.StyleContext.GetPadding (StateFlags.Normal); + x += padding.Left; + y += padding.Top; + width -= padding.Left + padding.Right; + height -= padding.Top + padding.Bottom; + + widget.StyleContext.Restore (); + + widget.StyleContext.Save (); + widget.StyleContext.AddClass ("progressbar"); + widget.StyleContext.RenderActivity (cr, x, y, (int) (width * Percentage), height); + widget.StyleContext.Restore (); + } + } + +} \ No newline at end of file From fb33b67d25ea6eb2c467010f92850372ccc7d4e2 Mon Sep 17 00:00:00 2001 From: lytico Date: Tue, 21 Apr 2020 18:54:32 +0200 Subject: [PATCH 3/3] GtkSharp.CellRenderer.gtksharp_cellrenderer_base_get_size: adjust loop exit --- Source/Libs/GtkSharp/CellRenderer.GetSize.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Libs/GtkSharp/CellRenderer.GetSize.cs b/Source/Libs/GtkSharp/CellRenderer.GetSize.cs index 393f8aa4b..41145e44a 100644 --- a/Source/Libs/GtkSharp/CellRenderer.GetSize.cs +++ b/Source/Libs/GtkSharp/CellRenderer.GetSize.cs @@ -129,6 +129,7 @@ namespace Gtk var obj = TryGetObject (cell); var parent = obj.NativeType; while ((parent = parent.GetBaseType ()) != GLib.GType.None) { + if (parent.ToString ().StartsWith ("__gtksharp_")) { continue; } @@ -156,9 +157,9 @@ namespace Gtk Marshal.FreeHGlobal (d); } } - } - return; + return; + } } }