From 37d15305a3b753118d0ae8daf23167f0eaee5ae8 Mon Sep 17 00:00:00 2001 From: Olivier Dufour Date: Sun, 20 Nov 2011 19:41:56 +0100 Subject: [PATCH] GInterface: Add properties support --- generator/ClassBase.cs | 11 +- generator/InterfaceGen.cs | 26 ++-- glib/GType.cs | 16 ++- glib/Object.cs | 98 +++++++++++++-- glib/ParamSpec.cs | 29 ++++- sample/CustomScrollableWidget.cs | 197 +++++++++++++++++++++++++++++++ sample/Makefile.am | 6 +- 7 files changed, 356 insertions(+), 27 deletions(-) create mode 100644 sample/CustomScrollableWidget.cs diff --git a/generator/ClassBase.cs b/generator/ClassBase.cs index a3b1d0e4e..95d5df8c6 100644 --- a/generator/ClassBase.cs +++ b/generator/ClassBase.cs @@ -316,7 +316,16 @@ namespace GtkSharp.Generation { p = (Property) klass.GetProperty (name); klass = klass.Parent; } - + if (p == null) { + foreach (string iface in interfaces) { + ClassBase igen = SymbolTable.Table.GetClassGen (iface); + if (igen == null) + continue; + p = igen.GetPropertyRecursively (name); + if (p != null) + break; + } + } return p; } diff --git a/generator/InterfaceGen.cs b/generator/InterfaceGen.cs index e237d559b..6bf34d95c 100644 --- a/generator/InterfaceGen.cs +++ b/generator/InterfaceGen.cs @@ -107,13 +107,20 @@ namespace GtkSharp.Generation { sw.WriteLine (); sw.WriteLine ("\t\tstatic void Initialize (IntPtr ptr, IntPtr data)"); sw.WriteLine ("\t\t{"); - sw.WriteLine ("\t\t\tIntPtr ifaceptr = new IntPtr (ptr.ToInt64 () + class_offset);"); - sw.WriteLine ("\t\t\t{0} native_iface = ({0}) Marshal.PtrToStructure (ifaceptr, typeof ({0}));", class_struct_name); - foreach (InterfaceVM vm in interface_vms) - sw.WriteLine ("\t\t\tnative_iface." + vm.Name + " = iface." + vm.Name + ";"); - sw.WriteLine ("\t\t\tMarshal.StructureToPtr (native_iface, ifaceptr, false);"); - sw.WriteLine ("\t\t\tGCHandle gch = (GCHandle) data;"); - sw.WriteLine ("\t\t\tgch.Free ();"); + if (interface_vms.Count > 0) { + sw.WriteLine ("\t\t\tIntPtr ifaceptr = new IntPtr (ptr.ToInt64 () + class_offset);"); + sw.WriteLine ("\t\t\t{0} native_iface = ({0}) Marshal.PtrToStructure (ifaceptr, typeof ({0}));", class_struct_name); + foreach (InterfaceVM vm in interface_vms) { + sw.WriteLine ("\t\t\tnative_iface." + vm.Name + " = iface." + vm.Name + ";"); + } + sw.WriteLine ("\t\t\tMarshal.StructureToPtr (native_iface, ifaceptr, false);"); + sw.WriteLine ("\t\t\tGCHandle gch = (GCHandle) data;"); + sw.WriteLine ("\t\t\tgch.Free ();"); + } + + foreach (Property prop in props.Values) { + sw.WriteLine ("\t\t\tGLib.Object.OverrideProperty (data, \"" + prop.CName + "\");"); + } sw.WriteLine ("\t\t}"); sw.WriteLine (); } @@ -309,7 +316,10 @@ namespace GtkSharp.Generation { vm_table.Remove (vm.Name); } } - + foreach (Property prop in props.Values) { + sw.WriteLine ("\t\t[GLib.Property (\"" + prop.CName + "\")]"); + prop.GenerateDecl (sw, "\t\t"); + } AppendCustom (sw, gen_info.CustomDir, Name + "Implementor"); sw.WriteLine ("\t}"); diff --git a/glib/GType.cs b/glib/GType.cs index 77e14f41d..68bba6511 100755 --- a/glib/GType.cs +++ b/glib/GType.cs @@ -296,6 +296,14 @@ namespace GLib { return klass; } + public IntPtr GetDefaultInterfacePtr () + { + IntPtr klass = g_type_default_interface_peek (val); + if (klass == IntPtr.Zero) + klass = g_type_default_interface_ref (val); + return klass; + } + public GType GetBaseType () { IntPtr parent = g_type_parent (this.Val); @@ -370,7 +378,7 @@ namespace GLib { if (gtypes.Contains (t)) return (GType) gtypes [t]; } - + PropertyInfo pi = t.GetProperty ("GType", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public); if (pi != null) return (GType) pi.GetValue (null, null); @@ -405,6 +413,12 @@ namespace GLib { [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr g_type_class_ref (IntPtr gtype); + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_type_default_interface_peek (IntPtr gtype); + + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_type_default_interface_ref (IntPtr gtype); + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr g_type_from_name (string name); diff --git a/glib/Object.cs b/glib/Object.cs index 1b84e89fc..06ed9e4ac 100644 --- a/glib/Object.cs +++ b/glib/Object.cs @@ -34,6 +34,7 @@ namespace GLib { IntPtr handle; ToggleRef tref; bool disposed = false; + static uint idx = 1; static Dictionary Objects = new Dictionary(); ~Object () @@ -161,6 +162,15 @@ namespace GLib { } } + static Dictionary> interface_properties; + static Dictionary> IProperties { + get { + if (interface_properties == null) + interface_properties = new Dictionary> (); + return interface_properties; + } + } + struct GTypeClass { public IntPtr gtype; } @@ -230,6 +240,36 @@ namespace GLib { return raw; } + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] + static extern void g_object_class_override_property (IntPtr klass, uint prop_id, IntPtr name); + + public static void OverrideProperty (IntPtr declaring_class, string name) + { + IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); + g_object_class_override_property (declaring_class, idx, native_name); + idx++; + } + + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_object_class_find_property (IntPtr klass, IntPtr name); + + static IntPtr FindClassProperty (GType type, string name) + { + IntPtr g_iface = type.GetDefaultInterfacePtr (); + IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); + return g_object_class_find_property (g_iface, native_name); + } + + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_object_interface_find_property (IntPtr klass, IntPtr name); + + static IntPtr FindInterfaceProperty (GType type, string name) + { + IntPtr g_iface = type.GetDefaultInterfacePtr (); + IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); + return g_object_interface_find_property (g_iface, native_name); + } + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern void g_object_class_install_property (IntPtr klass, uint prop_id, IntPtr param_spec); @@ -242,10 +282,8 @@ namespace GLib { return pspec.Handle; } - static void AddProperties (GType gtype, System.Type t, bool register_instance_prop) + static void AddProperties (GType gtype, System.Type t, bool register_instance_prop, ref bool handlers_overridden) { - uint idx = 1; - if (register_instance_prop) { IntPtr declaring_class = gtype.GetClassPtr (); ParamSpec pspec = new ParamSpec ("gtk-sharp-managed-instance", "", "", GType.Pointer, ParamFlags.Writable | ParamFlags.ConstructOnly); @@ -253,7 +291,6 @@ namespace GLib { idx++; } - bool handlers_overridden = register_instance_prop; foreach (PropertyInfo pinfo in t.GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) { foreach (object attr in pinfo.GetCustomAttributes (typeof (PropertyAttribute), false)) { if(pinfo.GetIndexParameters().Length > 0) @@ -305,6 +342,16 @@ namespace GLib { static void SetPropertyCallback(IntPtr handle, uint property_id, ref GLib.Value value, IntPtr param_spec) { + // FIXME: Here is a big quick hack to avoid race condition when trying to set up adjustment with contructor + // Because Raw is set too late + if (param_spec != IntPtr.Zero) { + ParamSpec foo = new ParamSpec(param_spec); + if (foo.Name == "gtk-sharp-managed-instance") { + GCHandle gch = (GCHandle) (IntPtr) value.Val; + Object o = (GLib.Object) gch.Target; + o.Raw = handle; + } + } if (!Properties.ContainsKey (param_spec)) return; @@ -324,17 +371,43 @@ namespace GLib { [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern void g_type_add_interface_static (IntPtr gtype, IntPtr iface_type, ref GInterfaceInfo info); - static void AddInterfaces (GType gtype, Type t) + static void AddInterfaces (GType gtype, Type t, ref bool handlers_overridden) { foreach (Type iface in t.GetInterfaces ()) { - if (!iface.IsDefined (typeof (GInterfaceAttribute), true) || iface.IsAssignableFrom (t.BaseType)) + if (!iface.IsDefined (typeof (GInterfaceAttribute), true)) continue; GInterfaceAttribute attr = iface.GetCustomAttributes (typeof (GInterfaceAttribute), false) [0] as GInterfaceAttribute; GInterfaceAdapter adapter = Activator.CreateInstance (attr.AdapterType, null) as GInterfaceAdapter; - - GInterfaceInfo info = adapter.Info; - g_type_add_interface_static (gtype.Val, adapter.GType.Val, ref info); + if (!handlers_overridden) { + IntPtr class_ptr = gtype.GetClassPtr (); + GObjectClass gobject_class = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass)); + gobject_class.get_prop_cb = GetPropertyHandler; + gobject_class.set_prop_cb = SetPropertyHandler; + Marshal.StructureToPtr (gobject_class, class_ptr, false); + handlers_overridden = true; + } + + if (!iface.IsAssignableFrom (t.BaseType)) { + GInterfaceInfo info = adapter.Info; + info.Data = gtype.GetClassPtr (); + //FIXME: overiding prop is done inside the init of interface adapter + // not sure that it is the good solution but + // it is the only one I found without exception or loop + g_type_add_interface_static (gtype.Val, adapter.GType.Val, ref info); + } + foreach (PropertyInfo p in iface.GetProperties ()) { + PropertyAttribute[] attrs = p.GetCustomAttributes (typeof (PropertyAttribute), true) as PropertyAttribute []; + if (attrs.Length == 0) + continue; + PropertyAttribute property_attr = attrs [0]; + PropertyInfo declared_prop = t.GetProperty (p.Name, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); + if (declared_prop == null) + continue; + IntPtr param_spec = FindInterfaceProperty (adapter.GType, property_attr.Name); + + Properties [param_spec] = declared_prop; + } } } @@ -342,6 +415,7 @@ namespace GLib { { GType gtype = GType.RegisterGObjectType (t); bool is_first_subclass = gtype.GetBaseType () == gtype.GetThresholdType (); + if (is_first_subclass) { IntPtr class_ptr = gtype.GetClassPtr (); GObjectClass gobject_class = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass)); @@ -350,10 +424,12 @@ namespace GLib { gobject_class.set_prop_cb = SetPropertyHandler; Marshal.StructureToPtr (gobject_class, class_ptr, false); } - AddProperties (gtype, t, is_first_subclass); + idx = 1; + bool handlers_overridden = is_first_subclass; + AddProperties (gtype, t, is_first_subclass, ref handlers_overridden); ConnectDefaultHandlers (gtype, t); InvokeTypeInitializers (gtype, t); - AddInterfaces (gtype, t); + AddInterfaces (gtype, t, ref handlers_overridden); return gtype; } diff --git a/glib/ParamSpec.cs b/glib/ParamSpec.cs index 64a91ed3b..74134eaf8 100644 --- a/glib/ParamSpec.cs +++ b/glib/ParamSpec.cs @@ -64,10 +64,11 @@ namespace GLib { handle = g_param_spec_int64 (p_name, p_nick, p_blurb, Int64.MinValue, Int64.MaxValue, 0, flags); else if (type == GType.UInt64) handle = g_param_spec_uint64 (p_name, p_nick, p_blurb, 0, UInt64.MaxValue, 0, flags); - /* - else if (type == GType.Enum) - else if (type == GType.Flags) - * TODO: + else if (type.GetBaseType () == GType.Enum) + handle = g_param_spec_enum (p_name, p_nick, p_blurb, type.Val, (int) (Enum.GetValues((Type)type).GetValue (0)), flags); + /*else if (type == GType.Flags) + * g_param_spec_flags (p_name, p_nick, p_blurb, type.Val, Enum.GetValues((Type)type) [0], flags); + * TODO: * Both g_param_spec_enum and g_param_spec_flags expect default property values and the members of the enum seemingly cannot be enumerated */ else if (type == GType.Float) @@ -85,7 +86,7 @@ namespace GLib { else if (g_type_is_a (type.Val, GType.Object.Val)) handle = g_param_spec_object (p_name, p_nick, p_blurb, type.Val, flags); else - throw new ArgumentException ("type"); + throw new ArgumentException ("type:" + type.ToString ()); GLib.Marshaller.Free (p_name); GLib.Marshaller.Free (p_nick); @@ -108,6 +109,21 @@ namespace GLib { } } + public string Name { + get { + GParamSpec spec = (GParamSpec) Marshal.PtrToStructure (Handle, typeof (GParamSpec)); + return Marshaller.Utf8PtrToString (spec.name); + } + } + + public string ToString () + { + GParamSpec spec = (GParamSpec) Marshal.PtrToStructure (Handle, typeof (GParamSpec)); + GType valtype= new GType (spec.value_type); + GType ownertype= new GType (spec.owner_type); + return "ParamSpec: name=" + Marshaller.Utf8PtrToString (spec.name) + " value_type=" + valtype.ToString() + " owner_type=" + ownertype.ToString(); + } + struct GTypeInstance { public IntPtr g_class; } @@ -136,6 +152,9 @@ namespace GLib { [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr g_param_spec_boolean (IntPtr name, IntPtr nick, IntPtr blurb, bool dval, int flags); + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_param_spec_enum (IntPtr name, IntPtr nick, IntPtr blurb, IntPtr enum_type, int dval, int flags); + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr g_param_spec_int (IntPtr name, IntPtr nick, IntPtr blurb, int min, int max, int dval, int flags); diff --git a/sample/CustomScrollableWidget.cs b/sample/CustomScrollableWidget.cs new file mode 100644 index 000000000..768fc9bed --- /dev/null +++ b/sample/CustomScrollableWidget.cs @@ -0,0 +1,197 @@ +using GLib; +using Gtk; +using System; + +class CustomScrollableWidgetTest { + public static int Main (string[] args) + { + Gtk.Application.Init (); + Window win = new Window ("Custom Scrollable Widget Test"); + win.DeleteEvent += new DeleteEventHandler (OnQuit); + + ScrolledWindow scroll = new ScrolledWindow (); + scroll.HscrollbarPolicy = PolicyType.Automatic; + scroll.VscrollbarPolicy = PolicyType.Automatic; + + CustomScrollableWidget cw = new CustomScrollableWidget ("This one label that is repeated"); + scroll.Add (cw); + + win.Add (scroll); + win.DefaultWidth = 200; + win.DefaultHeight = 200; + win.ShowAll (); + Gtk.Application.Run (); + return 0; + } + + static void OnQuit (object sender, DeleteEventArgs args) + { + Gtk.Application.Quit (); + } +} + +abstract class CustomBase : Widget +{ + public CustomBase () : base () + { } +} + +class CustomScrollableWidget : CustomBase, ScrollableImplementor { + private int num_rows = 20; + private string label; + private Pango.Layout layout; + + public CustomScrollableWidget (string custom_label) : base () + { + label = custom_label; + layout = null; + + HasWindow = false; + } + + private Pango.Layout Layout { + get { + if (layout == null) + layout = CreatePangoLayout (label); + return layout; + } + } + + private Gdk.Rectangle ContentArea { + get { + Gdk.Rectangle area; + area.X = Allocation.X; + area.Y = Allocation.Y; + + int layoutWidth, layoutHeight; + Layout.GetPixelSize (out layoutWidth, out layoutHeight); + area.Width = layoutWidth; + area.Height = layoutHeight * num_rows; + + return area; + } + } + + protected override bool OnDrawn (Cairo.Context cr) + { + int layout_x = - HadjustmentValue; + int layout_y = - VadjustmentValue; + + int layoutWidth, layoutHeight; + Layout.GetPixelSize (out layoutWidth, out layoutHeight); + + for (int i = 0; i < num_rows; i++) { + Layout.SetText (String.Format ("{0} {1}", label, i)); + StyleContext.RenderLayout (cr, layout_x, layout_y, Layout); + layout_y += layoutHeight; + } + + return base.OnDrawn (cr); + } + + protected override void OnSizeAllocated (Gdk.Rectangle allocation) + { + base.OnSizeAllocated (allocation); + + if (hadjustment != null) { + hadjustment.PageSize = allocation.Width; + hadjustment.PageIncrement = allocation.Width; + UpdateAdjustments (); + } + + if (vadjustment != null) { + vadjustment.PageSize = allocation.Height; + vadjustment.PageIncrement = allocation.Height; + UpdateAdjustments (); + } + } + + + private Adjustment hadjustment; + public Adjustment Hadjustment { + get { return hadjustment; } + set { + if (value == hadjustment) { + return; + } + hadjustment = value; + if (hadjustment == null) { + return; + } + hadjustment.ValueChanged += OnHadjustmentChanged; + UpdateAdjustments (); + } + } + + private Adjustment vadjustment; + public Adjustment Vadjustment { + get { return vadjustment; } + set { + if (value == vadjustment) { + return; + } + vadjustment = value; + if (vadjustment == null) { + return; + } + vadjustment.ValueChanged += OnVadjustmentChanged; + UpdateAdjustments (); + } + } + + private int HadjustmentValue { + get { return hadjustment == null ? 0 : (int)hadjustment.Value; } + } + + private int VadjustmentValue { + get { return vadjustment == null ? 0 : (int)vadjustment.Value; } + } + + public Gtk.ScrollablePolicy HscrollPolicy { + get; set; + } + + public Gtk.ScrollablePolicy VscrollPolicy { + get; set; + } + + private void UpdateAdjustments () + { + int layoutWidth, layoutHeight; + Layout.GetPixelSize (out layoutWidth, out layoutHeight); + + if (hadjustment != null) { + hadjustment.Upper = ContentArea.Width; + hadjustment.StepIncrement = 10.0; + if (hadjustment.Value + hadjustment.PageSize > hadjustment.Upper) { + hadjustment.Value = hadjustment.Upper - hadjustment.PageSize; + } + Console.WriteLine (String.Format ("H adj={0} upper={1} PageSize={2} PageInc={3}", + HadjustmentValue, hadjustment.Upper, hadjustment.PageSize, hadjustment.PageIncrement)); + hadjustment.Change (); + } + + if (vadjustment != null) { + vadjustment.Upper = ContentArea.Height; + vadjustment.StepIncrement = layoutHeight; + if (vadjustment.Value + vadjustment.PageSize > vadjustment.Upper) { + vadjustment.Value = vadjustment.Upper - vadjustment.PageSize; + } + Console.WriteLine (String.Format ("V adj={0} upper={1} PageSize={2} PageInc={3}", + VadjustmentValue, vadjustment.Upper, vadjustment.PageSize, vadjustment.PageIncrement)); + vadjustment.Change (); + } + } + + private void OnHadjustmentChanged (object o, EventArgs args) + { + UpdateAdjustments (); + QueueDraw (); + } + + private void OnVadjustmentChanged (object o, EventArgs args) + { + UpdateAdjustments (); + QueueDraw (); + } +} diff --git a/sample/Makefile.am b/sample/Makefile.am index ffc4296d8..dc1290548 100755 --- a/sample/Makefile.am +++ b/sample/Makefile.am @@ -8,7 +8,7 @@ DOTNET_TARGETS= DOTNET_ASSEMBLY= endif -TARGETS = gtk-hello-world.exe button.exe calendar.exe subclass.exe menu.exe treeviewdemo.exe managedtreeviewdemo.exe nodeviewdemo.exe treemodeldemo.exe actions.exe spawn.exe assistant.exe registerprop.exe gexceptiontest.exe native-instantiation.exe polarfixed.exe cairo-sample.exe scribble.exe testdnd.exe custom-cellrenderer.exe custom-widget.exe #scribble-xinput.exe $(DOTNET_TARGETS) +TARGETS = gtk-hello-world.exe button.exe calendar.exe subclass.exe menu.exe treeviewdemo.exe managedtreeviewdemo.exe nodeviewdemo.exe treemodeldemo.exe actions.exe spawn.exe assistant.exe registerprop.exe gexceptiontest.exe native-instantiation.exe polarfixed.exe cairo-sample.exe scribble.exe testdnd.exe custom-cellrenderer.exe custom-widget.exe custom-scrollable.exe #scribble-xinput.exe $(DOTNET_TARGETS) DEBUGS = $(addsuffix .mdb, $(TARGETS)) @@ -79,6 +79,9 @@ drawing-sample.exe: $(srcdir)/DrawingSample.cs $(assemblies) $(DOTNET_ASSEMBLIES custom-widget.exe: $(srcdir)/CustomWidget.cs $(assemblies) $(CSC) $(CSFLAGS) -out:custom-widget.exe $(references) $(srcdir)/CustomWidget.cs +custom-scrollable.exe: $(srcdir)/CustomScrollableWidget.cs $(assemblies) + $(CSC) $(CSFLAGS) -out:custom-scrollable.exe $(references) $(srcdir)/CustomScrollableWidget.cs + actions.exe: $(srcdir)/Actions.cs $(CSC) $(CSFLAGS) -unsafe -out:actions.exe $(references) $(srcdir)/Actions.cs @@ -118,6 +121,7 @@ EXTRA_DIST = \ CustomCellRenderer.cs \ DrawingSample.cs \ CustomWidget.cs \ + CustomScrollableWidget.cs \ Actions.cs \ PropertyRegistration.cs \ PolarFixed.cs