mirror of
https://github.com/Ryujinx/GtkSharp.git
synced 2024-12-22 20:55:27 +00:00
GtkSharp.Container: implement https://github.com/mono/gtk-sharp/blob/master/gtk/glue/container.c
This commit is contained in:
parent
34ab03a3c2
commit
10f9819446
156
Source/Libs/GtkSharp/Container.Forall.cs
Normal file
156
Source/Libs/GtkSharp/Container.Forall.cs
Normal file
|
@ -0,0 +1,156 @@
|
|||
using GtkSharp;
|
||||
|
||||
namespace Gtk
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public partial class Container
|
||||
{
|
||||
internal static D TryGetDelegate<D>(IntPtr _cb)
|
||||
{
|
||||
try {
|
||||
var d = Marshal.GetDelegateForFunctionPointer<D>(_cb);
|
||||
return d;
|
||||
} catch {
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
static ForAllNativeDelegate ForAll_cb_delegate;
|
||||
|
||||
static ForAllNativeDelegate ForAllVMCallback {
|
||||
get {
|
||||
if (ForAll_cb_delegate == null)
|
||||
ForAll_cb_delegate = new ForAllNativeDelegate(ForAll_cb);
|
||||
return ForAll_cb_delegate;
|
||||
}
|
||||
}
|
||||
|
||||
static void OverrideForAll(GLib.GType gtype)
|
||||
{
|
||||
OverrideForAll(gtype, ForAllVMCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets callback forall in GtkContainerClass-Struct
|
||||
/// </summary>
|
||||
/// <param name="gtype"></param>
|
||||
/// <param name="callback"></param>
|
||||
static void OverrideForAll(GLib.GType gtype, ForAllNativeDelegate callback)
|
||||
{
|
||||
unsafe {
|
||||
IntPtr* raw_ptr = (IntPtr*) (((long) gtype.GetClassPtr()) + (long) class_abi.GetFieldOffset("forall"));
|
||||
*raw_ptr = Marshal.GetFunctionPointerForDelegate((Delegate) callback);
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void ForAllNativeDelegate(IntPtr inst, bool include_internals, IntPtr cb, IntPtr data);
|
||||
|
||||
internal class ForAllCallbackHandler
|
||||
{
|
||||
internal IntPtr cb { get; }
|
||||
internal IntPtr data { get; }
|
||||
internal bool include_internals { get; }
|
||||
|
||||
internal ForAllCallbackHandler(IntPtr cb, IntPtr data, bool include_internals)
|
||||
{
|
||||
this.cb = cb;
|
||||
this.data = data;
|
||||
this.include_internals = include_internals;
|
||||
}
|
||||
|
||||
internal void Invoke(Widget w)
|
||||
{
|
||||
var nativeCb = TryGetDelegate<CallbackNative>(cb);
|
||||
var inv = new GtkSharp.CallbackInvoker(nativeCb, data);
|
||||
inv.Handler?.Invoke(w);
|
||||
}
|
||||
}
|
||||
|
||||
static void ForAll_cb(IntPtr inst, bool include_internals, IntPtr cb, IntPtr data)
|
||||
{
|
||||
try {
|
||||
//GtkContainer's unmanaged dispose calls forall, but by that time the managed object is gone
|
||||
//so it couldn't do anything useful, and resurrecting it would cause a resurrection cycle.
|
||||
//In that case, just chain to the native base in case it can do something.
|
||||
|
||||
var fcb = new ForAllCallbackHandler(cb, data, include_internals);
|
||||
if (GLib.Object.TryGetObject(inst) is Container container) {
|
||||
container.ForAll(include_internals, fcb.Invoke);
|
||||
} else {
|
||||
gtksharp_container_base_forall(inst, include_internals, fcb.Invoke, data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
GLib.ExceptionManager.RaiseUnhandledException(e, false);
|
||||
}
|
||||
}
|
||||
|
||||
[GLib.DefaultSignalHandler(Type = typeof(Gtk.Container), ConnectionMethod = nameof(OverrideForAll))]
|
||||
protected virtual void ForAll(bool include_internals, Callback callback)
|
||||
{
|
||||
InternalForAll(include_internals, callback);
|
||||
}
|
||||
|
||||
private void InternalForAll(bool include_internals, Callback callback)
|
||||
{
|
||||
if (!(callback.Target is ForAllCallbackHandler)) {
|
||||
throw new ApplicationException(
|
||||
$"{nameof(ForAll)} can only be called as \"base.{nameof(ForAll)}()\". Use {nameof(Forall)}() or {nameof(Foreach)}().");
|
||||
}
|
||||
|
||||
gtksharp_container_base_forall(Handle, include_internals, callback, IntPtr.Zero);
|
||||
}
|
||||
|
||||
static ForAllNativeDelegate InternalForAllNativeDelegate(GLib.GType gtype)
|
||||
{
|
||||
ForAllNativeDelegate unmanaged = null;
|
||||
unsafe {
|
||||
IntPtr* raw_ptr = (IntPtr*) (((long) gtype.GetClassPtr()) + (long) class_abi.GetFieldOffset("forall"));
|
||||
if (*raw_ptr == IntPtr.Zero)
|
||||
return default;
|
||||
|
||||
unmanaged = Marshal.GetDelegateForFunctionPointer<ForAllNativeDelegate>(*raw_ptr);
|
||||
}
|
||||
|
||||
return unmanaged;
|
||||
}
|
||||
|
||||
|
||||
static void gtksharp_container_base_forall(IntPtr container, bool include_internals, Callback cb, IntPtr data)
|
||||
{
|
||||
// gtksharp_container_base_forall from gtkglue:
|
||||
// Find and call the first base callback that's not the GTK# callback. The GTK# callback calls down the whole
|
||||
// managed override chain, so calling it on a subclass-of-a-managed-container-subclass causes a stack overflow.
|
||||
|
||||
// GtkContainerClass *parent = (GtkContainerClass *) G_OBJECT_GET_CLASS (container);
|
||||
// while ((parent = g_type_class_peek_parent (parent))) {
|
||||
// if (strncmp (G_OBJECT_CLASS_NAME (parent), "__gtksharp_", 11) != 0) {
|
||||
// if (parent->forall) {
|
||||
// (*parent->forall) (container, include_internals, cb, data);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
if (container == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
var obj = TryGetObject(container);
|
||||
var parent = obj.NativeType;
|
||||
while ((parent = parent.GetBaseType()) != GLib.GType.None) {
|
||||
if (parent.ToString().StartsWith("__gtksharp_")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var forAll = InternalForAllNativeDelegate(parent);
|
||||
if (forAll != default && cb.Target is ForAllCallbackHandler cbh) {
|
||||
GtkSharp.CallbackWrapper cb_wrapper = new GtkSharp.CallbackWrapper(cb);
|
||||
forAll(container, include_internals, cbh.cb, data);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -123,40 +123,6 @@ namespace Gtk
|
|||
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void ForallDelegate(IntPtr container, bool include_internals, IntPtr cb, IntPtr data);
|
||||
|
||||
static ForallDelegate ForallOldCallback;
|
||||
static ForallDelegate ForallCallback;
|
||||
|
||||
public struct CallbackInvoker
|
||||
{
|
||||
IntPtr cb;
|
||||
IntPtr data;
|
||||
|
||||
internal CallbackInvoker(IntPtr cb, IntPtr data)
|
||||
{
|
||||
this.cb = cb;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
internal IntPtr Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
internal IntPtr Callback
|
||||
{
|
||||
get
|
||||
{
|
||||
return cb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility code for old ChildType() virtual method
|
||||
static IntPtr ObsoleteChildType_cb(IntPtr raw)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue