Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making custom widgets work in Gtk.Builder #134

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions glib/GType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,32 @@ public static GType FromName (string native_name)
public static readonly GType Variant = new GType ((IntPtr) TypeFundamentals.TypeVariant);


static HashSet<GType> managedTypes = new HashSet<GType> ();
static IDictionary<IntPtr, Type> types = new Dictionary<IntPtr, Type> ();
static IDictionary<Type, GType> gtypes = new Dictionary<Type, GType> ();

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 ();
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions glib/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ sources = \
ToggleRef.cs \
TypeFundamentals.cs \
TypeInitializerAttribute.cs \
TypeNameAttribute.cs \
ValueArray.cs \
Value.cs \
Variant.cs \
Expand Down
87 changes: 69 additions & 18 deletions glib/Object.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class Object : IWrapper, IDisposable {
bool disposed = false;
static uint idx = 1;
static Dictionary<IntPtr, ToggleRef> Objects = new Dictionary<IntPtr, ToggleRef>();
static Dictionary<IntPtr, Dictionary<IntPtr, GLib.Value>> PropertiesToSet = new Dictionary<IntPtr, Dictionary<IntPtr, GLib.Value>>();

~Object ()
{
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 ()) {
Expand Down Expand Up @@ -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<IntPtr, GLib.Value> 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);

Expand All @@ -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);

Expand Down Expand Up @@ -511,17 +549,30 @@ static void GetPropertyCallback (IntPtr handle, uint property_id, ref GLib.Value

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<IntPtr, GLib.Value> deferred;
if(!PropertiesToSet.TryGetValue(handle, out deferred)) {
deferred = new Dictionary<IntPtr, GLib.Value>();
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<IntPtr, PropertyInfo> props;
Expand Down Expand Up @@ -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]);
Expand Down
12 changes: 9 additions & 3 deletions glib/SignalClosure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -135,9 +144,6 @@ public void Invoke (ClosureInvokedArgs args)
}
}

[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;
Expand Down
41 changes: 41 additions & 0 deletions glib/TypeNameAttribute.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
1 change: 1 addition & 0 deletions glib/glib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<Compile Include="SourceFuncs.cs" />
<Compile Include="GLibSharp.SourceDummyMarshalNative.cs" />
<Compile Include="GLibSharp.SourceFuncNative.cs" />
<Compile Include="TypeNameAttribute.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Core" />
Expand Down
48 changes: 8 additions & 40 deletions gtk/Widget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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<BindingInvoker> ();
Expand Down Expand Up @@ -408,4 +376,4 @@ public virtual void Destroy ()
InternalDestroyed -= NativeDestroyHandler;
}
}
}
}
Loading