diff --git a/Source/Libs/GtkSharp/CellRenderer.GetSize.cs b/Source/Libs/GtkSharp/CellRenderer.GetSize.cs
new file mode 100644
index 000000000..41145e44a
--- /dev/null
+++ b/Source/Libs/GtkSharp/CellRenderer.GetSize.cs
@@ -0,0 +1,167 @@
+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);
}
}
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