From a98c1528cedfb6936b736936d31d3b33361c98eb Mon Sep 17 00:00:00 2001 From: Martin Kupec Date: Tue, 1 Sep 2015 17:54:14 +0200 Subject: [PATCH 1/3] Fix properties setting when constructing GLib.Objects This is a proper fix of old FIXME. It defers setting of managed properties during GLib.Object construction until we have paired Managed and Unmanaged objects. --- glib/Object.cs | 57 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/glib/Object.cs b/glib/Object.cs index df0ce8fbf..50a2ce6ea 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) { @@ -406,7 +409,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,16 +422,21 @@ 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; } @@ -511,17 +521,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; From 98e64226b7c94142b36d6ba58addcad12b9749f5 Mon Sep 17 00:00:00 2001 From: Martin Kupec Date: Tue, 1 Sep 2015 23:55:41 +0200 Subject: [PATCH 2/3] Add possibility to pick glib type name for custom classes This patch adds TypeName attribute which can be used on custom class derived from GLib.Object in order to set GLib type name of the class. --- glib/GType.cs | 21 +++++++++-- glib/Makefile.am | 1 + glib/Object.cs | 2 +- glib/TypeNameAttribute.cs | 41 +++++++++++++++++++++ glib/glib.csproj | 1 + sample/CustomWidgetBuilder.cs | 69 +++++++++++++++++++++++++++++++++++ sample/CustomWidgetBuilder.ui | 11 ++++++ sample/Makefile.am | 5 ++- 8 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 glib/TypeNameAttribute.cs create mode 100644 sample/CustomWidgetBuilder.cs create mode 100644 sample/CustomWidgetBuilder.ui 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 50a2ce6ea..3f444eeab 100644 --- a/glib/Object.cs +++ b/glib/Object.cs @@ -619,7 +619,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/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/sample/CustomWidgetBuilder.cs b/sample/CustomWidgetBuilder.cs new file mode 100644 index 000000000..e255038ce --- /dev/null +++ b/sample/CustomWidgetBuilder.cs @@ -0,0 +1,69 @@ +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() + { + } + + + [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; + }; + } + } +} diff --git a/sample/CustomWidgetBuilder.ui b/sample/CustomWidgetBuilder.ui new file mode 100644 index 000000000..334f95b40 --- /dev/null +++ b/sample/CustomWidgetBuilder.ui @@ -0,0 +1,11 @@ + + + + + + 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 From 7468caf8ddd41a0e64f001d55dcd244ed9f1b73a Mon Sep 17 00:00:00 2001 From: Martin Kupec Date: Tue, 8 Sep 2015 22:56:13 +0200 Subject: [PATCH 3/3] Register signals when creating glib object This is needed for Gtk.Builder to properly parse signal registration. The registred signals are not actually used but Gtk.Builder check for their presence and fails if not present. --- glib/Object.cs | 28 ++++++++++++++++++++ glib/SignalClosure.cs | 12 ++++++--- gtk/Widget.cs | 48 ++++++----------------------------- sample/CustomWidgetBuilder.cs | 16 ++++++++++++ sample/CustomWidgetBuilder.ui | 1 + 5 files changed, 62 insertions(+), 43 deletions(-) diff --git a/glib/Object.cs b/glib/Object.cs index 3f444eeab..f80b66462 100644 --- a/glib/Object.cs +++ b/glib/Object.cs @@ -246,6 +246,7 @@ private void ClassInit (IntPtr gobject_class_handle) } AddProperties (gobject_class_handle); + AddSignals (); } private void InitializeProperties (GInterfaceAdapter adapter, IntPtr gobject_class_handle) @@ -327,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 ()) { @@ -440,6 +450,24 @@ static IntPtr ConstructorCallback (IntPtr gtypeval, uint n_construct_properties, 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); 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/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 index e255038ce..60997b381 100644 --- a/sample/CustomWidgetBuilder.cs +++ b/sample/CustomWidgetBuilder.cs @@ -27,9 +27,19 @@ static public void Register() 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 { @@ -65,5 +75,11 @@ public MainWindow(Builder builder, IntPtr handle) : base(handle) 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 index 334f95b40..0a1c47a16 100644 --- a/sample/CustomWidgetBuilder.ui +++ b/sample/CustomWidgetBuilder.ui @@ -5,6 +5,7 @@ True Hello world! +