diff --git a/glib/GType.cs b/glib/GType.cs index 7f63e2bae..32e5eb878 100644 --- a/glib/GType.cs +++ b/glib/GType.cs @@ -95,19 +95,32 @@ public static GType FromName (string native_name) public static readonly GType Variant = new GType ((IntPtr) TypeFundamentals.TypeVariant); + static HashSet managedTypes = new HashSet (); static IDictionary types = new Dictionary (); static IDictionary gtypes = new Dictionary (); public static void Register (GType native_type, System.Type type) + { + Register (native_type, type, false); + } + + public static void Register (GType native_type, System.Type type, bool managed) { lock (types) { if (native_type != GType.Pointer && native_type != GType.Boxed && native_type != ManagedValue.GType) types[native_type.Val] = type; if (type != null) gtypes[type] = native_type; + if (managed) + managedTypes.Add(native_type); } } + public static bool IsManaged (GType gtype) + { + return managedTypes.Contains(gtype); + } + static GType () { g_type_init (); @@ -318,7 +331,7 @@ public GType GetBaseType () public GType GetThresholdType () { GType curr_type = this; - while (curr_type.ToString ().StartsWith ("__gtksharp_")) + while (IsManaged (curr_type)) curr_type = curr_type.GetBaseType (); return curr_type; } @@ -364,7 +377,9 @@ static string BuildEscapedName (System.Type t) internal static GType RegisterGObjectType (Object.ClassInitializer gobject_class_initializer) { GType parent_gtype = LookupGObjectType (gobject_class_initializer.Type.BaseType); - string name = BuildEscapedName (gobject_class_initializer.Type); + + TypeNameAttribute nattr = (TypeNameAttribute)Attribute.GetCustomAttribute (gobject_class_initializer.Type, typeof (TypeNameAttribute), false); + string name = nattr != null ? nattr.Name : BuildEscapedName (gobject_class_initializer.Type); IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); GTypeQuery query; @@ -376,7 +391,7 @@ internal static GType RegisterGObjectType (Object.ClassInitializer gobject_class GType gtype = new GType (g_type_register_static (parent_gtype.Val, native_name, ref info, 0)); GLib.Marshaller.Free (native_name); - Register (gtype, gobject_class_initializer.Type); + Register (gtype, gobject_class_initializer.Type, true); return gtype; } diff --git a/glib/Makefile.am b/glib/Makefile.am index 4502f44b5..eb840d20c 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -90,6 +90,7 @@ sources = \ ToggleRef.cs \ TypeFundamentals.cs \ TypeInitializerAttribute.cs \ + TypeNameAttribute.cs \ ValueArray.cs \ Value.cs \ Variant.cs \ diff --git a/glib/Object.cs b/glib/Object.cs index df0ce8fbf..f80b66462 100644 --- a/glib/Object.cs +++ b/glib/Object.cs @@ -36,6 +36,7 @@ public class Object : IWrapper, IDisposable { bool disposed = false; static uint idx = 1; static Dictionary Objects = new Dictionary(); + static Dictionary> PropertiesToSet = new Dictionary>(); ~Object () { @@ -89,7 +90,9 @@ public static Object TryGetObject (IntPtr o) ToggleRef toggle_ref; lock (Objects) { - toggle_ref = (ToggleRef) Objects[o]; + if (!Objects.TryGetValue (o, out toggle_ref)) { + return null; + } } if (toggle_ref != null) { @@ -243,6 +246,7 @@ private void ClassInit (IntPtr gobject_class_handle) } AddProperties (gobject_class_handle); + AddSignals (); } private void InitializeProperties (GInterfaceAdapter adapter, IntPtr gobject_class_handle) @@ -324,6 +328,15 @@ void AddProperties (IntPtr gobject_class_handle) } } + void AddSignals() + { + foreach (EventInfo einfo in Type.GetEvents (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) { + foreach (object attr in einfo.GetCustomAttributes (typeof (SignalAttribute), false)) { + RegisterSignal (((SignalAttribute)attr).CName, gtype, GLib.Signal.Flags.RunLast, GLib.GType.None, new GLib.GType [0], null); + } + } + } + void AddInterfaceProperties () { foreach (Type iface in Type.GetInterfaces ()) { @@ -406,7 +419,9 @@ static IntPtr ConstructorCallback (IntPtr gtypeval, uint n_construct_properties, GType gtype = new GLib.GType (gtypeval); GObjectClass threshold_class = (GObjectClass) Marshal.PtrToStructure (gtype.GetThresholdType ().GetClassPtr (), typeof (GObjectClass)); IntPtr raw = threshold_class.constructor_cb (gtypeval, n_construct_properties, construct_properties); - bool construct_needed = true; + Dictionary deferred; + + GLib.Object obj = null; for (int i = 0; i < n_construct_properties; i++) { IntPtr p = new IntPtr (construct_properties.ToInt64 () + i * 2 * IntPtr.Size); @@ -417,19 +432,42 @@ static IntPtr ConstructorCallback (IntPtr gtypeval, uint n_construct_properties, Value val = (Value) Marshal.PtrToStructure (Marshal.ReadIntPtr (p, IntPtr.Size), typeof (Value)); if ((IntPtr) val.Val != IntPtr.Zero) { GCHandle gch = (GCHandle) (IntPtr) val.Val; - Object o = (GLib.Object) gch.Target; - o.Raw = raw; - construct_needed = false; + obj = (GLib.Object) gch.Target; + obj.Raw = raw; break; } } - if (construct_needed) - GetObject (raw, false); + if (obj == null) + obj = GetObject (raw, false); + if(PropertiesToSet.TryGetValue(raw, out deferred)) { + foreach(var item in deferred) { + SetDeferredProperty(obj, item.Value, item.Key); + } + PropertiesToSet.Remove(raw); + } return raw; } + [DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)] + static extern uint g_signal_newv (IntPtr signal_name, IntPtr gtype, GLib.Signal.Flags signal_flags, IntPtr closure, IntPtr accumulator, IntPtr accu_data, IntPtr c_marshaller, IntPtr return_type, uint n_params, [MarshalAs (UnmanagedType.LPArray)] IntPtr[] param_types); + + protected static uint RegisterSignal (string signal_name, GLib.GType gtype, GLib.Signal.Flags signal_flags, GLib.GType return_type, GLib.GType[] param_types, GLib.ClosureMarshal marshaler) + { + IntPtr[] native_param_types = new IntPtr [param_types.Length]; + for (int parm_idx = 0; parm_idx < param_types.Length; parm_idx++) + native_param_types [parm_idx] = param_types [parm_idx].Val; + + IntPtr native_signal_name = GLib.Marshaller.StringToPtrGStrdup (signal_name); + try { + IntPtr closure = marshaler != null ? GLib.SignalClosure.CreateClosure (marshaler) : IntPtr.Zero; + return g_signal_newv (native_signal_name, gtype.Val, signal_flags, closure, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, return_type.Val, (uint) param_types.Length, native_param_types); + } finally { + GLib.Marshaller.Free (native_signal_name); + } + } + [DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)] static extern void g_object_class_override_property (IntPtr klass, uint prop_id, IntPtr name); @@ -511,17 +549,30 @@ static GetPropertyDelegate GetPropertyHandler { 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; - } + // There are multiple issues in this place. + // We cannot construct an object here as it can be in construction + // from ConstructorCallback thus managed object already created. + // + // We cannot use the "gtk-sharp-managed-instance" property as when + // constructed by Gtk.Builder it is set to null. + // + // We defer setting the properties to later time when + // we have unmanaged and managed objects paired. + GLib.Object obj = TryGetObject(handle); + if(obj != null) { + SetDeferredProperty(obj, value, param_spec); + return; } - GLib.Object obj = GLib.Object.GetObject (handle, false); + Dictionary deferred; + if(!PropertiesToSet.TryGetValue(handle, out deferred)) { + deferred = new Dictionary(); + PropertiesToSet.Add(handle, deferred); + } + deferred[param_spec] = value; + } + + static void SetDeferredProperty(GLib.Object obj, GLib.Value value, IntPtr param_spec) + { var type = (Type)obj.LookupGType (); Dictionary props; @@ -596,7 +647,7 @@ struct GParameter { protected virtual void CreateNativeObject (string[] names, GLib.Value[] vals) { GType gtype = LookupGType (); - bool is_managed_subclass = gtype.ToString ().StartsWith ("__gtksharp"); + bool is_managed_subclass = GType.IsManaged (gtype); GParameter[] parms = new GParameter [is_managed_subclass ? names.Length + 1 : names.Length]; for (int i = 0; i < names.Length; i++) { parms [i].name = GLib.Marshaller.StringToPtrGStrdup (names [i]); diff --git a/glib/SignalClosure.cs b/glib/SignalClosure.cs index 9e2b4166b..a4d1d9538 100644 --- a/glib/SignalClosure.cs +++ b/glib/SignalClosure.cs @@ -56,6 +56,9 @@ struct GClosure { public IntPtr notifiers; } + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + public delegate void ClosureMarshal (IntPtr closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data); + internal delegate void ClosureInvokedHandler (object o, ClosureInvokedArgs args); internal class SignalClosure : IDisposable { @@ -91,6 +94,12 @@ public SignalClosure (IntPtr obj, string signal_name, Delegate custom_marshaler, this.custom_marshaler = custom_marshaler; } + public static IntPtr CreateClosure (ClosureMarshal marshaler) { + IntPtr raw_closure = g_closure_new_simple (Marshal.SizeOf (typeof (GClosure)), IntPtr.Zero); + g_closure_set_marshal (raw_closure, marshaler); + return raw_closure; + } + public event EventHandler Disposed; public event ClosureInvokedHandler Invoked; @@ -135,9 +144,6 @@ static ClosureMarshal Marshaler { } } - [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - delegate void ClosureMarshal (IntPtr closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data); - static void MarshalCallback (IntPtr raw_closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data) { string message = String.Empty; diff --git a/glib/TypeNameAttribute.cs b/glib/TypeNameAttribute.cs new file mode 100644 index 000000000..edadff098 --- /dev/null +++ b/glib/TypeNameAttribute.cs @@ -0,0 +1,41 @@ +// TypeNameAttribute.cs +// +// Copyright (c) 2015 Martin Kupec +// Copyright (c) 2015 Ales Kurecka +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the Lesser GNU General +// Public License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program; if not, write to the +// Free Software Foundation, Inc., 59 Temple Place - Suite 330, +// Boston, MA 02111-1307, USA. + + +namespace GLib { + + using System; + + [AttributeUsage (AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class TypeNameAttribute : Attribute { + private readonly string name; + + public TypeNameAttribute (string name) + { + this.name = name; + } + + public string Name + { + get { + return name; + } + } + } +} diff --git a/glib/glib.csproj b/glib/glib.csproj index 762ceb644..d5ac83723 100644 --- a/glib/glib.csproj +++ b/glib/glib.csproj @@ -102,6 +102,7 @@ + diff --git a/gtk/Widget.cs b/gtk/Widget.cs index 540072aee..60c3ac01b 100644 --- a/gtk/Widget.cs +++ b/gtk/Widget.cs @@ -53,38 +53,6 @@ struct GClosure { IntPtr notifiers; } - [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - delegate void ClosureMarshal (IntPtr closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data); - - [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr g_closure_new_simple (int closure_size, IntPtr dummy); - - [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] - static extern void g_closure_set_marshal (IntPtr closure, ClosureMarshal marshaler); - - static IntPtr CreateClosure (ClosureMarshal marshaler) { - IntPtr raw_closure = g_closure_new_simple (Marshal.SizeOf (typeof (GClosure)), IntPtr.Zero); - g_closure_set_marshal (raw_closure, marshaler); - return raw_closure; - } - - [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] - static extern uint g_signal_newv (IntPtr signal_name, IntPtr gtype, GLib.Signal.Flags signal_flags, IntPtr closure, IntPtr accumulator, IntPtr accu_data, IntPtr c_marshaller, IntPtr return_type, uint n_params, [MarshalAs (UnmanagedType.LPArray)] IntPtr[] param_types); - - static uint RegisterSignal (string signal_name, GLib.GType gtype, GLib.Signal.Flags signal_flags, GLib.GType return_type, GLib.GType[] param_types, ClosureMarshal marshaler) - { - IntPtr[] native_param_types = new IntPtr [param_types.Length]; - for (int parm_idx = 0; parm_idx < param_types.Length; parm_idx++) - native_param_types [parm_idx] = param_types [parm_idx].Val; - - IntPtr native_signal_name = GLib.Marshaller.StringToPtrGStrdup (signal_name); - try { - return g_signal_newv (native_signal_name, gtype.Val, signal_flags, CreateClosure (marshaler), IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, return_type.Val, (uint) param_types.Length, native_param_types); - } finally { - GLib.Marshaller.Free (native_signal_name); - } - } - static void ActivateMarshal_cb (IntPtr raw_closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data) { try { @@ -101,15 +69,15 @@ static void ActivateMarshal_cb (IntPtr raw_closure, IntPtr return_val, uint n_pa } } - static ClosureMarshal ActivateMarshalCallback; + static GLib.ClosureMarshal ActivateMarshalCallback; static void ConnectActivate (GLib.GType gtype) { if (ActivateMarshalCallback == null) - ActivateMarshalCallback = new ClosureMarshal (ActivateMarshal_cb); + ActivateMarshalCallback = new GLib.ClosureMarshal (ActivateMarshal_cb); GtkWidgetClass klass = GetClassStruct (gtype, false); - klass.ActivateSignal = RegisterSignal ("activate_signal", gtype, GLib.Signal.Flags.RunLast, GLib.GType.None, new GLib.GType [0], ActivateMarshalCallback); + klass.ActivateSignal = GLib.Object.RegisterSignal ("activate_signal", gtype, GLib.Signal.Flags.RunLast, GLib.GType.None, new GLib.GType [0], ActivateMarshalCallback); OverrideClassStruct (gtype, klass); } @@ -156,11 +124,11 @@ static void BindingMarshal_cb (IntPtr raw_closure, IntPtr return_val, uint n_par } } - static ClosureMarshal binding_delegate; - static ClosureMarshal BindingDelegate { + static GLib.ClosureMarshal binding_delegate; + static GLib.ClosureMarshal BindingDelegate { get { if (binding_delegate == null) - binding_delegate = new ClosureMarshal (BindingMarshal_cb); + binding_delegate = new GLib.ClosureMarshal (BindingMarshal_cb); return binding_delegate; } } @@ -195,7 +163,7 @@ static void ClassInit (GLib.GType gtype, Type t) string signame = t.Name.Replace (".", "_") + "_bindings"; IntPtr native_signame = GLib.Marshaller.StringToPtrGStrdup (signame); - RegisterSignal (signame, gtype, GLib.Signal.Flags.RunLast | GLib.Signal.Flags.Action, GLib.GType.None, new GLib.GType[] {GLib.GType.Long}, BindingDelegate); + GLib.Object.RegisterSignal (signame, gtype, GLib.Signal.Flags.RunLast | GLib.Signal.Flags.Action, GLib.GType.None, new GLib.GType[] {GLib.GType.Long}, BindingDelegate); if (binding_invokers == null) binding_invokers = new List (); @@ -408,4 +376,4 @@ public virtual void Destroy () InternalDestroyed -= NativeDestroyHandler; } } -} \ No newline at end of file +} diff --git a/sample/CustomWidgetBuilder.cs b/sample/CustomWidgetBuilder.cs new file mode 100644 index 000000000..60997b381 --- /dev/null +++ b/sample/CustomWidgetBuilder.cs @@ -0,0 +1,85 @@ +using Gtk; +using System; +using UI = Gtk.Builder.ObjectAttribute; + +namespace CustomWidgetBuilder +{ + [GLib.TypeName("MyWidget")] + public class MyWidget : Entry + { + public MyWidget() + { + this.Init(); + } + + + public MyWidget(IntPtr ptr) : base(ptr) + { + this.Init(); + } + + + static public void Register() + { + GLib.Object.RegisterGType(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + + private void Init() + { + this.Changed += (sender, e) => { + if (this.MySignal != null) + this.MySignal(sender, e); + + GLib.Signal.Emit(this, "my-signal"); + }; + } + + + [GLib.Signal("my-signal")] + public event EventHandler MySignal = null; + + + [GLib.Property("my-text")] + public string MyText + { + get { return base.Text; } + set { base.Text = value; } + } + } + + + public class MainWindow : Window + { + #pragma warning disable 169 + [UI] MyWidget m_mywidget; + #pragma warning restore 169 + + + static void Main(string[] args) + { + Application.Init(); + MyWidget.Register(); + Builder builder = new Builder(new System.IO.FileStream("CustomWidgetBuilder.ui", System.IO.FileMode.Open)); + MainWindow wnd = new MainWindow(builder, builder.GetObject("m_window").Handle); + wnd.Show(); + Application.Run(); + } + + + public MainWindow(Builder builder, IntPtr handle) : base(handle) + { + builder.Autoconnect(this); + this.DeleteEvent += (o, args) => { + Gtk.Application.Quit(); + args.RetVal = true; + }; + } + + + private void MySignalHandler(object sender, EventArgs e) + { + this.m_mywidget.MyText = this.m_mywidget.MyText.ToUpper(); + } + } +} diff --git a/sample/CustomWidgetBuilder.ui b/sample/CustomWidgetBuilder.ui new file mode 100644 index 000000000..0a1c47a16 --- /dev/null +++ b/sample/CustomWidgetBuilder.ui @@ -0,0 +1,12 @@ + + + + + + True + Hello world! + + + + + diff --git a/sample/Makefile.am b/sample/Makefile.am index 118152cb9..a9e465978 100755 --- a/sample/Makefile.am +++ b/sample/Makefile.am @@ -8,7 +8,7 @@ DOTNET_TARGETS= DOTNET_ASSEMBLY= endif -TARGETS = gtk-hello-world.exe async-sample.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 cairo-png.exe variantdemo.exe #scribble-xinput.exe $(DOTNET_TARGETS) +TARGETS = gtk-hello-world.exe async-sample.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-widget-builder.exe custom-scrollable.exe cairo-png.exe variantdemo.exe #scribble-xinput.exe $(DOTNET_TARGETS) DEBUGS = $(addsuffix .mdb, $(TARGETS)) @@ -82,6 +82,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-widget-builder.exe: $(srcdir)/CustomWidgetBuilder.cs $(assemblies) + $(CSC) $(CSFLAGS) -out:custom-widget-builder.exe $(references) $(srcdir)/CustomWidgetBuilder.cs + custom-scrollable.exe: $(srcdir)/CustomScrollableWidget.cs $(assemblies) $(CSC) $(CSFLAGS) -out:custom-scrollable.exe $(references) $(srcdir)/CustomScrollableWidget.cs