forked from AuroraMiddleware/gtk
builder: Add GtkBuilderScope
GtkBuilderScope is an interface that provides the scope that a builder instance operates in. It creates closures and resolves types. Language bindings are meant to use this interface to customize the behavior of builder files, in particular when instantiating templates. A default implementation for C is provided via GtkBuilderCScope (to keep with the awkward naming that glib uses for closures). It is derivable on purpose so that languages or extensions that extend C can use it. The reftest code in fact does derive GtkBuilderCScope for its own scope implementation that implements looking up symbols in modules. gtk-widget-factory was updated to use the new GtkBuilderCScope to add its custom callback symbols. So it does it different from gtk-demo, which uses the normal way of exporting symbols for dlsym() and thereby makes the 2 demos test the 2 ways GtkBuilder uses for looking up symbols.
This commit is contained in:
parent
1f94028ff7
commit
f8a7f30a0d
@ -1663,6 +1663,7 @@ static void
|
||||
activate (GApplication *app)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
GtkBuilderScope *scope;
|
||||
GtkWindow *window;
|
||||
GtkWidget *widget;
|
||||
GtkWidget *widget2;
|
||||
@ -1716,18 +1717,23 @@ activate (GApplication *app)
|
||||
g_object_unref (provider);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
gtk_builder_add_callback_symbol (builder, "on_entry_icon_release", (GCallback)on_entry_icon_release);
|
||||
gtk_builder_add_callback_symbol (builder, "on_scale_button_value_changed", (GCallback)on_scale_button_value_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "on_scale_button_query_tooltip", (GCallback)on_scale_button_query_tooltip);
|
||||
gtk_builder_add_callback_symbol (builder, "on_record_button_toggled", (GCallback)on_record_button_toggled);
|
||||
gtk_builder_add_callback_symbol (builder, "on_page_combo_changed", (GCallback)on_page_combo_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "on_range_from_changed", (GCallback)on_range_from_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "on_range_to_changed", (GCallback)on_range_to_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "tab_close_cb", (GCallback)tab_close_cb);
|
||||
gtk_builder_add_callback_symbol (builder, "increase_icon_size", (GCallback)increase_icon_size);
|
||||
gtk_builder_add_callback_symbol (builder, "decrease_icon_size", (GCallback)decrease_icon_size);
|
||||
gtk_builder_add_callback_symbol (builder, "reset_icon_size", (GCallback)reset_icon_size);
|
||||
gtk_builder_add_callback_symbol (builder, "osd_frame_pressed", (GCallback)osd_frame_pressed);
|
||||
scope = gtk_builder_cscope_new ();
|
||||
gtk_builder_cscope_add_callback_symbols (GTK_BUILDER_CSCOPE (scope),
|
||||
"on_entry_icon_release", (GCallback)on_entry_icon_release,
|
||||
"on_scale_button_value_changed", (GCallback)on_scale_button_value_changed,
|
||||
"on_scale_button_query_tooltip", (GCallback)on_scale_button_query_tooltip,
|
||||
"on_record_button_toggled", (GCallback)on_record_button_toggled,
|
||||
"on_page_combo_changed", (GCallback)on_page_combo_changed,
|
||||
"on_range_from_changed", (GCallback)on_range_from_changed,
|
||||
"on_range_to_changed", (GCallback)on_range_to_changed,
|
||||
"tab_close_cb", (GCallback)tab_close_cb,
|
||||
"increase_icon_size", (GCallback)increase_icon_size,
|
||||
"decrease_icon_size", (GCallback)decrease_icon_size,
|
||||
"reset_icon_size", (GCallback)reset_icon_size,
|
||||
"osd_frame_pressed", (GCallback)osd_frame_pressed,
|
||||
NULL);
|
||||
gtk_builder_set_scope (builder, scope);
|
||||
g_object_unref (scope);
|
||||
gtk_builder_add_from_resource (builder, "/org/gtk/WidgetFactory4/widget-factory.ui", NULL);
|
||||
|
||||
window = (GtkWindow *)gtk_builder_get_object (builder, "window");
|
||||
|
@ -516,21 +516,41 @@ GTK_BUILDABLE_CLASS
|
||||
GTK_BUILDABLE_GET_IFACE
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuilderscope</FILE>
|
||||
<TITLE>GtkBuilderScope</TITLE>
|
||||
gtk_builder_cscope_new
|
||||
gtk_builder_cscope_add_callback_symbol
|
||||
gtk_builder_cscope_add_callback_symbols
|
||||
gtk_builder_cscope_lookup_callback_symbol
|
||||
<SUBSECTION Standard>
|
||||
GTK_BUILDER_SCOPE
|
||||
GTK_IS_BUILDER_SCOPE
|
||||
GTK_TYPE_BUILDER_SCOPE
|
||||
GTK_BUILDER_SCOPE_INTERFACE
|
||||
GTK_IS_BUILDER_SCOPE_INTERFACE
|
||||
GTK_BUILDER_SCOPE_GET_INTERFACE
|
||||
GTK_BUILDER_CSCOPE
|
||||
GTK_IS_BUILDER_CSCOPE
|
||||
GTK_TYPE_BUILDER_CSCOPE
|
||||
GTK_BUILDER_CSCOPE_CLASS
|
||||
GTK_IS_BUILDER_CSCOPE_CLASS
|
||||
GTK_BUILDER_CSCOPE_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_builder_scope_get_type
|
||||
gtk_builder_cscope_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuilder</FILE>
|
||||
<TITLE>GtkBuilder</TITLE>
|
||||
GtkBuilder
|
||||
GtkBuilderClosureFunc
|
||||
GtkBuilderError
|
||||
gtk_builder_new
|
||||
gtk_builder_new_from_file
|
||||
gtk_builder_new_from_resource
|
||||
gtk_builder_new_from_string
|
||||
gtk_builder_add_callback_symbol
|
||||
gtk_builder_add_callback_symbols
|
||||
gtk_builder_lookup_callback_symbol
|
||||
gtk_builder_create_closure
|
||||
gtk_builder_create_cclosure
|
||||
gtk_builder_add_from_file
|
||||
gtk_builder_add_from_resource
|
||||
gtk_builder_add_from_string
|
||||
@ -543,10 +563,11 @@ gtk_builder_get_objects
|
||||
gtk_builder_expose_object
|
||||
gtk_builder_set_current_object
|
||||
gtk_builder_get_current_object
|
||||
gtk_builder_set_scope
|
||||
gtk_builder_get_scope
|
||||
gtk_builder_set_translation_domain
|
||||
gtk_builder_get_translation_domain
|
||||
gtk_builder_get_type_from_name
|
||||
gtk_builder_set_closure_func
|
||||
gtk_builder_value_from_string
|
||||
gtk_builder_value_from_string_type
|
||||
GTK_BUILDER_WARN_INVALID_CHILD_TYPE
|
||||
@ -562,7 +583,6 @@ GTK_BUILDER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_builder_get_type
|
||||
gtk_builder_error_quark
|
||||
GtkBuilderPrivate
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
@ -4668,7 +4688,7 @@ gtk_widget_class_bind_template_child_internal_private
|
||||
gtk_widget_class_bind_template_child_full
|
||||
gtk_widget_class_bind_template_callback
|
||||
gtk_widget_class_bind_template_callback_full
|
||||
gtk_widget_class_set_closure_func
|
||||
gtk_widget_class_set_template_scope
|
||||
|
||||
<SUBSECTION>
|
||||
gtk_widget_observe_children
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtkbuildable.h>
|
||||
#include <gtk/gtkbuilder.h>
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkcalendar.h>
|
||||
#include <gtk/gtkcellarea.h>
|
||||
|
442
gtk/gtkbuilder.c
442
gtk/gtkbuilder.c
@ -162,23 +162,6 @@
|
||||
* “last_modification_time” attribute is also allowed, but it does not
|
||||
* have a meaning to the builder.
|
||||
*
|
||||
* By default, GTK+ tries to find functions (like the handlers for
|
||||
* signals) by using g_module_symbol(), but this can be changed by
|
||||
* passing a custom #GtkBuilderClosureFunc to gtk_builder_set_closure_func().
|
||||
* Bindings in particular will want to make use of this functionality to
|
||||
* allow language-specific name mangling and namespacing.
|
||||
*
|
||||
* The default closure function uses symbols explicitly added to @builder
|
||||
* with prior calls to gtk_builder_add_callback_symbol(). In the case that
|
||||
* symbols are not explicitly added; it uses #GModule’s introspective
|
||||
* features (by opening the module %NULL) to look at the application’s symbol
|
||||
* table. From here it tries to match the signal function names given in the
|
||||
* interface description with symbols in the application.
|
||||
*
|
||||
* Note that unless gtk_builder_add_callback_symbol() is called for
|
||||
* all signal callbacks which are referenced by the loaded XML, this
|
||||
* functionality will require that #GModule be supported on the platform.
|
||||
*
|
||||
* If you rely on #GModule support to lookup callbacks in the symbol table,
|
||||
* the following details should be noted:
|
||||
*
|
||||
@ -230,16 +213,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h> /* strlen */
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderprivate.h"
|
||||
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderscopeprivate.h"
|
||||
#include "gtkdebug.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkicontheme.h"
|
||||
#include "gtktestutils.h"
|
||||
|
||||
static void gtk_builder_finalize (GObject *object);
|
||||
static void gtk_builder_set_property (GObject *object,
|
||||
@ -254,6 +237,7 @@ static void gtk_builder_get_property (GObject *object,
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CURRENT_OBJECT,
|
||||
PROP_SCOPE,
|
||||
PROP_TRANSLATION_DOMAIN,
|
||||
LAST_PROP
|
||||
};
|
||||
@ -274,19 +258,14 @@ typedef struct
|
||||
{
|
||||
gchar *domain;
|
||||
GHashTable *objects;
|
||||
GHashTable *callbacks;
|
||||
GSList *delayed_properties;
|
||||
GSList *signals;
|
||||
GSList *bindings;
|
||||
GModule *module;
|
||||
gchar *filename;
|
||||
gchar *resource_prefix;
|
||||
GType template_type;
|
||||
GObject *current_object;
|
||||
|
||||
GtkBuilderClosureFunc closure_func;
|
||||
gpointer closure_data;
|
||||
GDestroyNotify closure_destroy;
|
||||
GtkBuilderScope *scope;
|
||||
} GtkBuilderPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
|
||||
@ -297,6 +276,7 @@ gtk_builder_dispose (GObject *object)
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));
|
||||
|
||||
g_clear_object (&priv->current_object);
|
||||
g_clear_object (&priv->scope);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_parent_class)->dispose (object);
|
||||
}
|
||||
@ -338,6 +318,18 @@ gtk_builder_class_init (GtkBuilderClass *klass)
|
||||
G_TYPE_OBJECT,
|
||||
GTK_PARAM_READWRITE);
|
||||
|
||||
/**
|
||||
* GtkBuilder:scope:
|
||||
*
|
||||
* The scope the builder is operating in
|
||||
*/
|
||||
builder_props[PROP_SCOPE] =
|
||||
g_param_spec_object ("scope",
|
||||
P_("Scope"),
|
||||
P_("The scope the builder is operating in"),
|
||||
GTK_TYPE_BUILDER_SCOPE,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
||||
|
||||
g_object_class_install_properties (gobject_class, LAST_PROP, builder_props);
|
||||
}
|
||||
|
||||
@ -361,18 +353,11 @@ gtk_builder_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));
|
||||
|
||||
if (priv->closure_destroy)
|
||||
priv->closure_destroy (priv->closure_data);
|
||||
|
||||
g_clear_pointer (&priv->module, g_module_close);
|
||||
|
||||
g_free (priv->domain);
|
||||
g_free (priv->filename);
|
||||
g_free (priv->resource_prefix);
|
||||
|
||||
g_hash_table_destroy (priv->objects);
|
||||
if (priv->callbacks)
|
||||
g_hash_table_destroy (priv->callbacks);
|
||||
|
||||
g_slist_free_full (priv->signals, (GDestroyNotify)_free_signal_info);
|
||||
|
||||
@ -393,6 +378,10 @@ gtk_builder_set_property (GObject *object,
|
||||
gtk_builder_set_current_object (builder, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
gtk_builder_set_scope (builder, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_TRANSLATION_DOMAIN:
|
||||
gtk_builder_set_translation_domain (builder, g_value_get_string (value));
|
||||
break;
|
||||
@ -418,6 +407,10 @@ gtk_builder_get_property (GObject *object,
|
||||
g_value_set_object (value, priv->current_object);
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
g_value_set_object (value, priv->scope);
|
||||
break;
|
||||
|
||||
case PROP_TRANSLATION_DOMAIN:
|
||||
g_value_set_string (value, priv->domain);
|
||||
break;
|
||||
@ -428,79 +421,6 @@ gtk_builder_get_property (GObject *object,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to map a type name to a _get_type function
|
||||
* and call it, eg:
|
||||
*
|
||||
* GtkWindow -> gtk_window_get_type
|
||||
* GtkHBox -> gtk_hbox_get_type
|
||||
* GtkUIManager -> gtk_ui_manager_get_type
|
||||
* GWeatherLocation -> gweather_location_get_type
|
||||
*
|
||||
* Keep in sync with testsuite/gtk/typename.c !
|
||||
*/
|
||||
static gchar *
|
||||
type_name_mangle (const gchar *name)
|
||||
{
|
||||
GString *symbol_name = g_string_new ("");
|
||||
gint i;
|
||||
|
||||
for (i = 0; name[i] != '\0'; i++)
|
||||
{
|
||||
/* skip if uppercase, first or previous is uppercase */
|
||||
if ((name[i] == g_ascii_toupper (name[i]) &&
|
||||
i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
|
||||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
|
||||
name[i-1] == g_ascii_toupper (name[i-1]) &&
|
||||
name[i-2] == g_ascii_toupper (name[i-2])))
|
||||
g_string_append_c (symbol_name, '_');
|
||||
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
|
||||
}
|
||||
g_string_append (symbol_name, "_get_type");
|
||||
|
||||
return g_string_free (symbol_name, FALSE);
|
||||
}
|
||||
|
||||
GModule *
|
||||
gtk_builder_get_module (GtkBuilder *builder)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
if (priv->module == NULL)
|
||||
{
|
||||
if (!g_module_supported ())
|
||||
return NULL;
|
||||
|
||||
priv->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
|
||||
}
|
||||
|
||||
return priv->module;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_resolve_type_lazily (GtkBuilder *builder,
|
||||
const gchar *name)
|
||||
{
|
||||
GModule *module;
|
||||
GTypeGetFunc func;
|
||||
gchar *symbol;
|
||||
GType gtype = G_TYPE_INVALID;
|
||||
|
||||
module = gtk_builder_get_module (builder);
|
||||
if (!module)
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
symbol = type_name_mangle (name);
|
||||
|
||||
if (g_module_symbol (module, symbol, (gpointer)&func))
|
||||
gtype = func ();
|
||||
|
||||
g_free (symbol);
|
||||
|
||||
return gtype;
|
||||
}
|
||||
|
||||
/*
|
||||
* GtkBuilder virtual methods
|
||||
*/
|
||||
@ -1761,30 +1681,58 @@ gtk_builder_set_current_object (GtkBuilder *self,
|
||||
}
|
||||
|
||||
/**
|
||||
* GtkBuilderClosureFunc:
|
||||
* @builder: a #GtkBuilder
|
||||
* @function_name: name of the function to create a closure for
|
||||
* @swapped: if the closure should swap user data and instance
|
||||
* @object: (nullable): object to use as user data for the closure
|
||||
* @user_data: user data passed when setting the function
|
||||
* @error: location for error when creating the closure fails
|
||||
* gtk_builder_get_scope:
|
||||
* @self: a #GtkBuilder
|
||||
*
|
||||
* Prototype of function used to create closures by @builder. It is meant
|
||||
* for influencing how @function_name is resolved.
|
||||
* Gets the scope in use that was set via gtk_builder_set_scope().
|
||||
*
|
||||
* This function is most useful for bindings and can be used with
|
||||
* gtk_builder_set_closure_func() or gtk_widget_class_set_closure_func()
|
||||
* to allow creating closures for functions defined in the binding's
|
||||
* language.
|
||||
* See the #GtkBuilderScope documentation for details.
|
||||
*
|
||||
* If the given @function_name does not match a function name or when the
|
||||
* arguments cannot be supported by the bindings, bindings should return
|
||||
* %NULL and set @error. Usually %GTK_BUILDER_ERROR_INVALID_FUNCTION will
|
||||
* be the right error code to use.
|
||||
* Returns: (transfer none): the current scope
|
||||
**/
|
||||
GtkBuilderScope *
|
||||
gtk_builder_get_scope (GtkBuilder *self)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (self), NULL);
|
||||
|
||||
return priv->scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_set_current_object:
|
||||
* @self: a #GtkBuilder
|
||||
* @scope: (nullable) (transfer none): the scope to use or
|
||||
* %NULL for the default
|
||||
*
|
||||
* Returns: (nullable): a new #GClosure or %NULL when no closure could
|
||||
* be created and @error was set.
|
||||
*/
|
||||
* Sets the scope the builder should operate in.
|
||||
*
|
||||
* If @scope is %NULL a new #GtkBuilderCScope will be created.
|
||||
*
|
||||
* See the #GtkBuilderScope documentation for details.
|
||||
**/
|
||||
void
|
||||
gtk_builder_set_scope (GtkBuilder *self,
|
||||
GtkBuilderScope *scope)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (self));
|
||||
g_return_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope));
|
||||
|
||||
if (scope && priv->scope == scope)
|
||||
return;
|
||||
|
||||
g_clear_object (&priv->scope);
|
||||
|
||||
if (scope)
|
||||
priv->scope = g_object_ref (scope);
|
||||
else
|
||||
priv->scope = gtk_builder_cscope_new ();
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), builder_props[PROP_SCOPE]);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_builder_connect_signals (GtkBuilder *builder,
|
||||
@ -2561,21 +2509,15 @@ GType
|
||||
gtk_builder_get_type_from_name (GtkBuilder *builder,
|
||||
const gchar *type_name)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GType type;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
type = gtk_builder_scope_get_type_from_name (priv->scope, builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
type = gtk_builder_resolve_type_lazily (builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
gtk_test_register_all_types ();
|
||||
type = g_type_from_name (type_name);
|
||||
}
|
||||
}
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
if (G_TYPE_IS_CLASSED (type))
|
||||
g_type_class_unref (g_type_class_ref (type));
|
||||
@ -2643,176 +2585,11 @@ _gtk_builder_get_template_type (GtkBuilder *builder)
|
||||
return priv->template_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_add_callback_symbol:
|
||||
* @builder: a #GtkBuilder
|
||||
* @callback_name: The name of the callback, as expected in the XML
|
||||
* @callback_symbol: (scope async): The callback pointer
|
||||
*
|
||||
* Adds the @callback_symbol to the scope of @builder under the given @callback_name.
|
||||
*
|
||||
* Using this function overrides the behavior of gtk_builder_create_closure()
|
||||
* for any callback symbols that are added. Using this method allows for better
|
||||
* encapsulation as it does not require that callback symbols be declared in
|
||||
* the global namespace.
|
||||
*/
|
||||
void
|
||||
gtk_builder_add_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (builder));
|
||||
g_return_if_fail (callback_name && callback_name[0]);
|
||||
g_return_if_fail (callback_symbol != NULL);
|
||||
|
||||
if (!priv->callbacks)
|
||||
priv->callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
g_hash_table_insert (priv->callbacks, g_strdup (callback_name), callback_symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_add_callback_symbols:
|
||||
* @builder: a #GtkBuilder
|
||||
* @first_callback_name: The name of the callback, as expected in the XML
|
||||
* @first_callback_symbol: (scope async): The callback pointer
|
||||
* @...: A list of callback name and callback symbol pairs terminated with %NULL
|
||||
*
|
||||
* A convenience function to add many callbacks instead of calling
|
||||
* gtk_builder_add_callback_symbol() for each symbol.
|
||||
*/
|
||||
void
|
||||
gtk_builder_add_callback_symbols (GtkBuilder *builder,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...)
|
||||
{
|
||||
va_list var_args;
|
||||
const gchar *callback_name;
|
||||
GCallback callback_symbol;
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (builder));
|
||||
g_return_if_fail (first_callback_name && first_callback_name[0]);
|
||||
g_return_if_fail (first_callback_symbol != NULL);
|
||||
|
||||
callback_name = first_callback_name;
|
||||
callback_symbol = first_callback_symbol;
|
||||
|
||||
va_start (var_args, first_callback_symbol);
|
||||
|
||||
do {
|
||||
|
||||
gtk_builder_add_callback_symbol (builder, callback_name, callback_symbol);
|
||||
|
||||
callback_name = va_arg (var_args, const gchar*);
|
||||
|
||||
if (callback_name)
|
||||
callback_symbol = va_arg (var_args, GCallback);
|
||||
|
||||
} while (callback_name != NULL);
|
||||
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_lookup_callback_symbol: (skip)
|
||||
* @builder: a #GtkBuilder
|
||||
* @callback_name: The name of the callback
|
||||
*
|
||||
* Fetches a symbol previously added to @builder
|
||||
* with gtk_builder_add_callback_symbols()
|
||||
*
|
||||
* This function is intended for possible use in language bindings
|
||||
* or for any case that one might be customizing signal connections
|
||||
* using gtk_builder_set_closure_func().
|
||||
*
|
||||
* Returns: (nullable): The callback symbol in @builder for @callback_name, or %NULL
|
||||
*/
|
||||
GCallback
|
||||
gtk_builder_lookup_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
|
||||
g_return_val_if_fail (callback_name && callback_name[0], NULL);
|
||||
|
||||
if (!priv->callbacks)
|
||||
return NULL;
|
||||
|
||||
return g_hash_table_lookup (priv->callbacks, callback_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_set_closure_func: (skip)
|
||||
* @builder: a #GtkBuilder
|
||||
* @closure_func: (allow-none): function to call when creating
|
||||
* closures or %NULL to use the default
|
||||
* @user_data: (nullable): user data to pass to @closure_func
|
||||
* @user_destroy: destroy function for user data
|
||||
*
|
||||
* Sets the function to call for creating closures.
|
||||
* gtk_builder_create_closure() will use this function instead
|
||||
* of gtk_builder_create_cclosure().
|
||||
*
|
||||
* This is useful for bindings.
|
||||
**/
|
||||
void
|
||||
gtk_builder_set_closure_func (GtkBuilder *builder,
|
||||
GtkBuilderClosureFunc closure_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (builder));
|
||||
|
||||
if (priv->closure_destroy)
|
||||
priv->closure_destroy (priv->closure_data);
|
||||
|
||||
priv->closure_func = closure_func;
|
||||
priv->closure_data = user_data;
|
||||
priv->closure_destroy = user_destroy;
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
|
||||
GCallback callback,
|
||||
gboolean swapped,
|
||||
GObject *object)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GClosure *closure;
|
||||
|
||||
if (object == NULL)
|
||||
object = priv->current_object;
|
||||
|
||||
if (object)
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_object_swap (callback, object);
|
||||
else
|
||||
closure = g_cclosure_new_object (callback, object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_swap (callback, NULL, NULL);
|
||||
else
|
||||
closure = g_cclosure_new (callback, NULL, NULL);
|
||||
}
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_create_closure:
|
||||
* @builder: a #GtkBuilder
|
||||
* @function_name: name of the function to look up
|
||||
* @swapped: %TRUE to create a swapped closure
|
||||
* @flags: closure creation flags
|
||||
* @object: (nullable): Object to create the closure with
|
||||
* @error: (allow-none): return location for an error, or %NULL
|
||||
*
|
||||
@ -2830,7 +2607,7 @@ gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
|
||||
GClosure *
|
||||
gtk_builder_create_closure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
@ -2841,66 +2618,7 @@ gtk_builder_create_closure (GtkBuilder *builder,
|
||||
g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
if (priv->closure_func)
|
||||
return priv->closure_func (builder, function_name, swapped, object, priv->closure_data, error);
|
||||
else
|
||||
return gtk_builder_create_cclosure (builder, function_name, swapped, object, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_create_cclosure: (skip)
|
||||
* @builder: a #GtkBuilder
|
||||
* @function_name: name of the function to look up
|
||||
* @swapped: %TRUE to create a swapped closure
|
||||
* @object: (nullable): Object to create the closure with
|
||||
* @error: (allow-none): return location for an error, or %NULL
|
||||
*
|
||||
* This is the default function used by gtk_builder_set_closure_func(). Some bindings
|
||||
* with C support may want to call this function as a fallback from their closure
|
||||
* function.
|
||||
*
|
||||
* This function has no purpose otherwise.
|
||||
*
|
||||
* This function will prefer callbacks added via gtk_builder_add_callback_symbol()
|
||||
* to looking up public symbols.
|
||||
*
|
||||
* Returns: (nullable): A new closure for invoking @function_name
|
||||
**/
|
||||
GClosure *
|
||||
gtk_builder_create_cclosure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
GModule *module = gtk_builder_get_module (builder);
|
||||
GCallback func;
|
||||
|
||||
func = gtk_builder_lookup_callback_symbol (builder, function_name);
|
||||
if (func)
|
||||
return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
|
||||
|
||||
if (module == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not look up function `%s`: GModule is not supported.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_module_symbol (module, function_name, (gpointer)&func))
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"No function named `%s`.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
|
||||
return gtk_builder_scope_create_closure (priv->scope, builder, function_name, flags, object, error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,7 @@
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkapplication.h>
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@ -98,11 +98,6 @@ GType gtk_builder_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilder* gtk_builder_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_set_closure_func (GtkBuilder *builder,
|
||||
GtkBuilderClosureFunc closure_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_builder_add_from_file (GtkBuilder *builder,
|
||||
const gchar *filename,
|
||||
@ -152,6 +147,11 @@ void gtk_builder_set_translation_domain (GtkBuilder *builder,
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const gchar* gtk_builder_get_translation_domain (GtkBuilder *builder);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilderScope *gtk_builder_get_scope (GtkBuilder *builder);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_set_scope (GtkBuilder *builder,
|
||||
GtkBuilderScope *scope);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_builder_get_type_from_name (GtkBuilder *builder,
|
||||
const char *type_name);
|
||||
|
||||
@ -176,27 +176,9 @@ GtkBuilder * gtk_builder_new_from_string (const gchar *string,
|
||||
gssize length);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_add_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_add_callback_symbols (GtkBuilder *builder,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GCallback gtk_builder_lookup_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GClosure * gtk_builder_create_closure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GClosure * gtk_builder_create_cclosure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
|
||||
|
459
gtk/gtkbuilderscope.c
Normal file
459
gtk/gtkbuilderscope.c
Normal file
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkbuilderscopeprivate.h"
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtktestutils.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkbuilderscope
|
||||
* @Title: GtkBuilderScope
|
||||
* @Short_description: Bindings for GtkBuilder
|
||||
* @See_also: #GtkBuilder, #GClosure
|
||||
*
|
||||
* #GtkBuilderScope is an interface to provide support to #GtkBuilder, primarily
|
||||
* for looking up programming-language-specific values for strings that are
|
||||
* given in a #GtkBuilder UI file.
|
||||
*
|
||||
* The primary intended audience is bindings that want to provide deeper integration
|
||||
* of #GtkBuilder into the language.
|
||||
*
|
||||
* A #GtkBuilderScope instance may be used with multiple #GtkBuilder objects, even
|
||||
* at once.
|
||||
*
|
||||
* By default, GTK will use its own implementation of #GtkBuilderScope for the C
|
||||
* language which can be created via gtk_builder_cscope_new().
|
||||
*
|
||||
* #GtkBuilderCScope instances use symbols explicitly added to @builder
|
||||
* with prior calls to gtk_builder_scope_add_callback_symbol(). If developers want
|
||||
* to do that, they are encouraged to create their own scopes for that purpose.
|
||||
*
|
||||
* In the case that symbols are not explicitly added; GTK will uses #GModule’s
|
||||
* introspective features (by opening the module %NULL) to look at the application’s
|
||||
* symbol table. From here it tries to match the signal function names given in the
|
||||
* interface description with symbols in the application.
|
||||
*
|
||||
* Note that unless gtk_builder_scope_add_callback_symbol() is called for
|
||||
* all signal callbacks which are referenced by the loaded XML, this
|
||||
* functionality will require that #GModule be supported on the platform.
|
||||
*/
|
||||
|
||||
G_DEFINE_INTERFACE (GtkBuilderScope, gtk_builder_scope, G_TYPE_OBJECT)
|
||||
|
||||
static GType
|
||||
gtk_builder_scope_default_get_type_from_name (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name)
|
||||
{
|
||||
GType type;
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
if (type != G_TYPE_INVALID)
|
||||
return type;
|
||||
|
||||
gtk_test_register_all_types ();
|
||||
return g_type_from_name (type_name);
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_scope_default_create_closure (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Creating closures is not supported by %s",
|
||||
G_OBJECT_TYPE_NAME (self));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_scope_default_init (GtkBuilderScopeInterface *iface)
|
||||
{
|
||||
iface->get_type_from_name = gtk_builder_scope_default_get_type_from_name;
|
||||
iface->create_closure = gtk_builder_scope_default_create_closure;
|
||||
}
|
||||
|
||||
GType
|
||||
gtk_builder_scope_get_type_from_name (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
|
||||
|
||||
return GTK_BUILDER_SCOPE_GET_IFACE (self)->get_type_from_name (self, builder, type_name);
|
||||
}
|
||||
|
||||
GClosure *
|
||||
gtk_builder_scope_create_closure (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), NULL);
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
|
||||
g_return_val_if_fail (function_name != NULL, NULL);
|
||||
g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
return GTK_BUILDER_SCOPE_GET_IFACE (self)->create_closure (self, builder, function_name, flags, object, error);
|
||||
}
|
||||
|
||||
/*** GTK_BUILDER_CSCOPE ***/
|
||||
|
||||
typedef struct _GtkBuilderCScopePrivate GtkBuilderCScopePrivate;
|
||||
|
||||
struct _GtkBuilderCScopePrivate
|
||||
{
|
||||
GModule *module;
|
||||
GHashTable *callbacks;
|
||||
};
|
||||
|
||||
static void gtk_builder_cscope_scope_init (GtkBuilderScopeInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkBuilderCScope, gtk_builder_cscope, G_TYPE_OBJECT,
|
||||
G_ADD_PRIVATE(GtkBuilderCScope)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDER_SCOPE,
|
||||
gtk_builder_cscope_scope_init))
|
||||
|
||||
static GModule *
|
||||
gtk_builder_cscope_get_module (GtkBuilderCScope *self)
|
||||
{
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
if (priv->module == NULL)
|
||||
{
|
||||
if (!g_module_supported ())
|
||||
return NULL;
|
||||
|
||||
priv->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
|
||||
}
|
||||
|
||||
return priv->module;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to map a type name to a _get_type function
|
||||
* and call it, eg:
|
||||
*
|
||||
* GtkWindow -> gtk_window_get_type
|
||||
* GtkHBox -> gtk_hbox_get_type
|
||||
* GtkUIManager -> gtk_ui_manager_get_type
|
||||
* GWeatherLocation -> gweather_location_get_type
|
||||
*
|
||||
* Keep in sync with testsuite/gtk/typename.c !
|
||||
*/
|
||||
static gchar *
|
||||
type_name_mangle (const gchar *name)
|
||||
{
|
||||
GString *symbol_name = g_string_new ("");
|
||||
gint i;
|
||||
|
||||
for (i = 0; name[i] != '\0'; i++)
|
||||
{
|
||||
/* skip if uppercase, first or previous is uppercase */
|
||||
if ((name[i] == g_ascii_toupper (name[i]) &&
|
||||
i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
|
||||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
|
||||
name[i-1] == g_ascii_toupper (name[i-1]) &&
|
||||
name[i-2] == g_ascii_toupper (name[i-2])))
|
||||
g_string_append_c (symbol_name, '_');
|
||||
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
|
||||
}
|
||||
g_string_append (symbol_name, "_get_type");
|
||||
|
||||
return g_string_free (symbol_name, FALSE);
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_cscope_resolve_type_lazily (GtkBuilderCScope *self,
|
||||
const gchar *name)
|
||||
{
|
||||
GModule *module;
|
||||
GType (*func) (void);
|
||||
gchar *symbol;
|
||||
GType gtype = G_TYPE_INVALID;
|
||||
|
||||
module = gtk_builder_cscope_get_module (self);
|
||||
if (!module)
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
symbol = type_name_mangle (name);
|
||||
|
||||
if (g_module_symbol (module, symbol, (gpointer)&func))
|
||||
gtype = func ();
|
||||
|
||||
g_free (symbol);
|
||||
|
||||
return gtype;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_cscope_get_type_from_name (GtkBuilderScope *scope,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name)
|
||||
{
|
||||
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
|
||||
GType type;
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
if (type != G_TYPE_INVALID)
|
||||
return type;
|
||||
|
||||
type = gtk_builder_cscope_resolve_type_lazily (self, type_name);
|
||||
if (type != G_TYPE_INVALID)
|
||||
return type;
|
||||
|
||||
gtk_test_register_all_types ();
|
||||
type = g_type_from_name (type_name);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_cscope_create_closure_for_funcptr (GtkBuilderCScope *self,
|
||||
GtkBuilder *builder,
|
||||
GCallback callback,
|
||||
gboolean swapped,
|
||||
GObject *object)
|
||||
{
|
||||
GClosure *closure;
|
||||
|
||||
if (object == NULL)
|
||||
object = gtk_builder_get_current_object (builder);
|
||||
|
||||
if (object)
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_object_swap (callback, object);
|
||||
else
|
||||
closure = g_cclosure_new_object (callback, object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_swap (callback, NULL, NULL);
|
||||
else
|
||||
closure = g_cclosure_new (callback, NULL, NULL);
|
||||
}
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_cscope_create_closure (GtkBuilderScope *scope,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
|
||||
GModule *module = gtk_builder_cscope_get_module (self);
|
||||
GCallback func;
|
||||
gboolean swapped;
|
||||
|
||||
swapped = flags & GTK_BUILDER_CLOSURE_SWAPPED;
|
||||
|
||||
func = gtk_builder_cscope_lookup_callback_symbol (self, function_name);
|
||||
if (func)
|
||||
return gtk_builder_cscope_create_closure_for_funcptr (self, builder, func, swapped, object);
|
||||
|
||||
if (module == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not look up function `%s`: GModule is not supported.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_module_symbol (module, function_name, (gpointer)&func))
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"No function named `%s`.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gtk_builder_cscope_create_closure_for_funcptr (self, builder, func, swapped, object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_scope_init (GtkBuilderScopeInterface *iface)
|
||||
{
|
||||
iface->get_type_from_name = gtk_builder_cscope_get_type_from_name;
|
||||
iface->create_closure = gtk_builder_cscope_create_closure;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (object);
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->callbacks, g_hash_table_destroy);
|
||||
g_clear_pointer (&priv->module, g_module_close);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_cscope_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_class_init (GtkBuilderCScopeClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_builder_cscope_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_init (GtkBuilderCScope *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_cscope_new:
|
||||
*
|
||||
* Creates a new #GtkbuilderCScope object to use with future #GtkBuilder
|
||||
* instances.
|
||||
*
|
||||
* Calling this function is only necessary if you want to add custom
|
||||
* callbacks via gtk_builder_cscope_add_callback_symbol().
|
||||
*
|
||||
* Returns: a new #GtkBuilderCScope
|
||||
**/
|
||||
GtkBuilderScope *
|
||||
gtk_builder_cscope_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_BUILDER_CSCOPE, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_cscope_add_callback_symbol:
|
||||
* @self: a #GtkBuilderCScope
|
||||
* @callback_name: The name of the callback, as expected in the XML
|
||||
* @callback_symbol: (scope async): The callback pointer
|
||||
*
|
||||
* Adds the @callback_symbol to the scope of @builder under the given @callback_name.
|
||||
*
|
||||
* Using this function overrides the behavior of gtk_builder_create_closure()
|
||||
* for any callback symbols that are added. Using this method allows for better
|
||||
* encapsulation as it does not require that callback symbols be declared in
|
||||
* the global namespace.
|
||||
*/
|
||||
void
|
||||
gtk_builder_cscope_add_callback_symbol (GtkBuilderCScope *self,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol)
|
||||
{
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER_CSCOPE (self));
|
||||
g_return_if_fail (callback_name && callback_name[0]);
|
||||
g_return_if_fail (callback_symbol != NULL);
|
||||
|
||||
if (!priv->callbacks)
|
||||
priv->callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
g_hash_table_insert (priv->callbacks, g_strdup (callback_name), callback_symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_cscope_add_callback_symbols: (skip)
|
||||
* @self: a #GtkBuilderCScope
|
||||
* @first_callback_name: The name of the callback, as expected in the XML
|
||||
* @first_callback_symbol: (scope async): The callback pointer
|
||||
* @...: A list of callback name and callback symbol pairs terminated with %NULL
|
||||
*
|
||||
* A convenience function to add many callbacks instead of calling
|
||||
* gtk_builder_add_callback_symbol() for each symbol.
|
||||
*/
|
||||
void
|
||||
gtk_builder_cscope_add_callback_symbols (GtkBuilderCScope *self,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...)
|
||||
{
|
||||
va_list var_args;
|
||||
const gchar *callback_name;
|
||||
GCallback callback_symbol;
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER_CSCOPE (self));
|
||||
g_return_if_fail (first_callback_name && first_callback_name[0]);
|
||||
g_return_if_fail (first_callback_symbol != NULL);
|
||||
|
||||
callback_name = first_callback_name;
|
||||
callback_symbol = first_callback_symbol;
|
||||
|
||||
va_start (var_args, first_callback_symbol);
|
||||
|
||||
do {
|
||||
|
||||
gtk_builder_cscope_add_callback_symbol (self, callback_name, callback_symbol);
|
||||
|
||||
callback_name = va_arg (var_args, const gchar*);
|
||||
|
||||
if (callback_name)
|
||||
callback_symbol = va_arg (var_args, GCallback);
|
||||
|
||||
} while (callback_name != NULL);
|
||||
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_lookup_callback_symbol: (skip)
|
||||
* @self: a #GtkBuilderCScope
|
||||
* @callback_name: The name of the callback
|
||||
*
|
||||
* Fetches a symbol previously added to @self
|
||||
* with gtk_builder_add_callback_symbols().
|
||||
*
|
||||
* Returns: (nullable): The callback symbol in @builder for @callback_name, or %NULL
|
||||
*/
|
||||
GCallback
|
||||
gtk_builder_cscope_lookup_callback_symbol (GtkBuilderCScope *self,
|
||||
const gchar *callback_name)
|
||||
{
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_CSCOPE (self), NULL);
|
||||
g_return_val_if_fail (callback_name && callback_name[0], NULL);
|
||||
|
||||
if (priv->callbacks == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_hash_table_lookup (priv->callbacks, callback_name);
|
||||
}
|
||||
|
116
gtk/gtkbuilderscope.h
Normal file
116
gtk/gtkbuilderscope.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_BUILDER_SCOPE_H__
|
||||
#define __GTK_BUILDER_SCOPE_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_BUILDER_SCOPE (gtk_builder_scope_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_INTERFACE (GtkBuilderScope, gtk_builder_scope, GTK, BUILDER_SCOPE, GObject)
|
||||
|
||||
/**
|
||||
* GtkBuilderClosureFlags:
|
||||
* @GTK_BUILDER_CLOSURE_SWAPPED: The closure should be created swapped. See
|
||||
* g_cclosure_new_swap() for details.
|
||||
*
|
||||
* The list of flags that can be passed to gtk_builder_scope_create_closure().
|
||||
* New values may be added in the future for new features, so external
|
||||
* implementations of GtkBuilderScopeInterface should test the flags for unknown
|
||||
* values and raise a %@GTK_BUILDER_ERROR_INVALID_ATTRIBUTE error when they
|
||||
* encounter one.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_BUILDER_CLOSURE_SWAPPED = (1 << 0)
|
||||
} GtkBuilderClosureFlags;
|
||||
|
||||
/**
|
||||
* GtkBuilderScopeInterface:
|
||||
* @get_type_from_name: Try to lookup a #GType via the its name. See
|
||||
* gtk_builder_get_type_from_name() for more details.
|
||||
* The C implementation will use g_type_from_name() and if that fails try to guess the
|
||||
* correct function name for registering the type and then use dlsym() to load it.
|
||||
* The default implementation just tries g_type_from_name() and otherwise fails.
|
||||
* @create_closure: Create a closure with the given arguments. See gtk_builder_create_closure()
|
||||
* for more details on those.
|
||||
* The C implementation will try to use dlsym() to locate the function name and then
|
||||
* g_cclosure_new() to create a closure for the symbol.
|
||||
* The default implementation just fails and returns %NULL.
|
||||
*
|
||||
* The virtual function table to implement for #GtkBuilderScope implementations.
|
||||
* Default implementations for each function do exist, but they usually just fail,
|
||||
* so it is suggested that implementations implement all of them.
|
||||
*/
|
||||
struct _GtkBuilderScopeInterface
|
||||
{
|
||||
/*< private >*/
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/*< public >*/
|
||||
GType (* get_type_from_name) (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name);
|
||||
|
||||
GClosure * (* create_closure) (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct _GtkBuilderCScopeClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
#define GTK_TYPE_BUILDER_CSCOPE (gtk_builder_cscope_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_DERIVABLE_TYPE (GtkBuilderCScope, gtk_builder_cscope, GTK, BUILDER_CSCOPE, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilderScope * gtk_builder_cscope_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_cscope_add_callback_symbol (GtkBuilderCScope *self,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_cscope_add_callback_symbols (GtkBuilderCScope *self,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GCallback gtk_builder_cscope_lookup_callback_symbol(GtkBuilderCScope *self,
|
||||
const gchar *callback_name);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_SCOPE_H__ */
|
40
gtk/gtkbuilderscopeprivate.h
Normal file
40
gtk/gtkbuilderscopeprivate.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_BUILDER_SCOPE_PRIVATE_H__
|
||||
#define __GTK_BUILDER_SCOPE_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GType gtk_builder_scope_get_type_from_name (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name);
|
||||
GClosure * gtk_builder_scope_create_closure (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_SCOPE_PRIVATE_H__ */
|
@ -35,6 +35,7 @@ G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkAdjustment GtkAdjustment;
|
||||
typedef struct _GtkBuilder GtkBuilder;
|
||||
typedef struct _GtkBuilderScope GtkBuilderScope;
|
||||
typedef struct _GtkClipboard GtkClipboard;
|
||||
typedef struct _GtkEventController GtkEventController;
|
||||
typedef struct _GtkGesture GtkGesture;
|
||||
@ -51,13 +52,6 @@ typedef struct _GtkWidget GtkWidget;
|
||||
typedef struct _GtkWidgetPath GtkWidgetPath;
|
||||
typedef struct _GtkWindow GtkWindow;
|
||||
|
||||
typedef GClosure* (* GtkBuilderClosureFunc) (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GObject *object,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* GTK_INVALID_LIST_POSITION:
|
||||
*
|
||||
|
@ -489,10 +489,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
GBytes *data;
|
||||
GSList *children;
|
||||
GSList *callbacks;
|
||||
GtkBuilderClosureFunc closure_func;
|
||||
gpointer closure_data;
|
||||
GDestroyNotify closure_destroy;
|
||||
GtkBuilderScope *scope;
|
||||
} GtkWidgetTemplate;
|
||||
|
||||
struct _GtkWidgetClassPrivate
|
||||
@ -11996,28 +11993,6 @@ template_child_class_free (AutomaticChildClass *child_class)
|
||||
}
|
||||
}
|
||||
|
||||
static CallbackSymbol *
|
||||
callback_symbol_new (const gchar *name,
|
||||
GCallback callback)
|
||||
{
|
||||
CallbackSymbol *cb = g_slice_new0 (CallbackSymbol);
|
||||
|
||||
cb->callback_name = g_strdup (name);
|
||||
cb->callback_symbol = callback;
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
static void
|
||||
callback_symbol_free (CallbackSymbol *callback)
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
g_free (callback->callback_name);
|
||||
g_slice_free (CallbackSymbol, callback);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
template_data_free (GtkWidgetTemplate *template_data)
|
||||
{
|
||||
@ -12025,11 +12000,8 @@ template_data_free (GtkWidgetTemplate *template_data)
|
||||
{
|
||||
g_bytes_unref (template_data->data);
|
||||
g_slist_free_full (template_data->children, (GDestroyNotify)template_child_class_free);
|
||||
g_slist_free_full (template_data->callbacks, (GDestroyNotify)callback_symbol_free);
|
||||
|
||||
if (template_data->closure_data &&
|
||||
template_data->closure_destroy)
|
||||
template_data->closure_destroy (template_data->closure_data);
|
||||
g_object_unref (template_data->scope);
|
||||
|
||||
g_slice_free (GtkWidgetTemplate, template_data);
|
||||
}
|
||||
@ -12154,25 +12126,11 @@ gtk_widget_init_template (GtkWidget *widget)
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
|
||||
if (template->scope)
|
||||
gtk_builder_set_scope (builder, template->scope);
|
||||
|
||||
gtk_builder_set_current_object (builder, G_OBJECT (widget));
|
||||
|
||||
/* Setup closure handling. All signal data from a template receive the
|
||||
* template instance as user data automatically.
|
||||
*
|
||||
* A GtkBuilderClosureFunc can be provided to gtk_widget_class_set_signal_closure_func()
|
||||
* in order for templates to be usable by bindings.
|
||||
*/
|
||||
if (template->closure_func)
|
||||
gtk_builder_set_closure_func (builder, template->closure_func, template->closure_data, NULL);
|
||||
|
||||
/* Add any callback symbols declared for this GType to the GtkBuilder namespace */
|
||||
for (l = template->callbacks; l; l = l->next)
|
||||
{
|
||||
CallbackSymbol *callback = l->data;
|
||||
|
||||
gtk_builder_add_callback_symbol (builder, callback->callback_name, callback->callback_symbol);
|
||||
}
|
||||
|
||||
/* This will build the template XML as children to the widget instance, also it
|
||||
* will validate that the template is created for the correct GType and assert that
|
||||
* there is no infinite recursion.
|
||||
@ -12310,7 +12268,9 @@ gtk_widget_class_set_template_from_resource (GtkWidgetClass *widget_class,
|
||||
* @callback_symbol: (scope async): The callback symbol
|
||||
*
|
||||
* Declares a @callback_symbol to handle @callback_name from the template XML
|
||||
* defined for @widget_type. See gtk_builder_add_callback_symbol().
|
||||
* defined for @widget_type. This function is not supported after
|
||||
* gtk_widget_class_set_template_scope() has been used on @widget_class.
|
||||
* See gtk_builder_cscope_add_callback_symbol().
|
||||
*
|
||||
* Note that this must be called from a composite widget classes class
|
||||
* initializer after calling gtk_widget_class_set_template().
|
||||
@ -12320,48 +12280,50 @@ gtk_widget_class_bind_template_callback_full (GtkWidgetClass *widget_class,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol)
|
||||
{
|
||||
CallbackSymbol *cb;
|
||||
GtkWidgetTemplate *template;
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class));
|
||||
g_return_if_fail (widget_class->priv->template != NULL);
|
||||
g_return_if_fail (callback_name && callback_name[0]);
|
||||
g_return_if_fail (callback_symbol != NULL);
|
||||
|
||||
cb = callback_symbol_new (callback_name, callback_symbol);
|
||||
widget_class->priv->template->callbacks = g_slist_prepend (widget_class->priv->template->callbacks, cb);
|
||||
template = widget_class->priv->template;
|
||||
if (template->scope == NULL)
|
||||
template->scope = gtk_builder_cscope_new ();
|
||||
|
||||
if (GTK_IS_BUILDER_CSCOPE (template->scope))
|
||||
{
|
||||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (template->scope),
|
||||
callback_name,
|
||||
callback_symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_critical ("Adding a callback to %s, but scope is not a GtkBuilderCScope.", G_OBJECT_CLASS_NAME (widget_class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_class_set_closure_func:
|
||||
* gtk_widget_class_set_template_scope:
|
||||
* @widget_class: A #GtkWidgetClass
|
||||
* @closure_func: The #GtkBuilderClosureFunc to use when creating closure in the class template
|
||||
* @closure_data: The data to pass to @closure_func
|
||||
* @closure_data_destroy: The #GDestroyNotify to free @closure_data, this will only be used at
|
||||
* class finalization time, when no classes of type @widget_type are in use anymore.
|
||||
* @scope: (transfer none): The #GtkBuilderScope to use when loading the class template
|
||||
*
|
||||
* For use in language bindings, this will override the default #GtkBuilderClosureFunc to be
|
||||
* For use in language bindings, this will override the default #GtkBuilderScope to be
|
||||
* used when parsing GtkBuilder XML from this class’s template data.
|
||||
*
|
||||
* Note that this must be called from a composite widget classes class
|
||||
* initializer after calling gtk_widget_class_set_template().
|
||||
*/
|
||||
void
|
||||
gtk_widget_class_set_closure_func (GtkWidgetClass *widget_class,
|
||||
GtkBuilderClosureFunc closure_func,
|
||||
gpointer closure_data,
|
||||
GDestroyNotify closure_data_destroy)
|
||||
gtk_widget_class_set_template_scope (GtkWidgetClass *widget_class,
|
||||
GtkBuilderScope *scope)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class));
|
||||
g_return_if_fail (widget_class->priv->template != NULL);
|
||||
g_return_if_fail (GTK_IS_BUILDER_SCOPE (scope));
|
||||
|
||||
/* Defensive, destroy any previously set data */
|
||||
if (widget_class->priv->template->closure_data &&
|
||||
widget_class->priv->template->closure_destroy)
|
||||
widget_class->priv->template->closure_destroy (widget_class->priv->template->closure_data);
|
||||
|
||||
widget_class->priv->template->closure_func = closure_func;
|
||||
widget_class->priv->template->closure_data = closure_data;
|
||||
widget_class->priv->template->closure_destroy = closure_data_destroy;
|
||||
g_set_object (&widget_class->priv->template->scope, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -847,7 +847,9 @@ void gtk_widget_remove_tick_callback (GtkWidget *widget,
|
||||
* Binds a callback function defined in a template to the @widget_class.
|
||||
*
|
||||
* This macro is a convenience wrapper around the
|
||||
* gtk_widget_class_bind_template_callback_full() function.
|
||||
* gtk_widget_class_bind_template_callback_full() function. It is not
|
||||
* supported after gtk_widget_class_set_template_scope() has been used
|
||||
* on @widget_class.
|
||||
*/
|
||||
#define gtk_widget_class_bind_template_callback(widget_class, callback) \
|
||||
gtk_widget_class_bind_template_callback_full (GTK_WIDGET_CLASS (widget_class), \
|
||||
@ -956,10 +958,8 @@ void gtk_widget_class_bind_template_callback_full (GtkWidgetClass *
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_widget_class_set_closure_func (GtkWidgetClass *widget_class,
|
||||
GtkBuilderClosureFunc closure_func,
|
||||
gpointer closure_data,
|
||||
GDestroyNotify closure_destroy);
|
||||
void gtk_widget_class_set_template_scope (GtkWidgetClass *widget_class,
|
||||
GtkBuilderScope *scope);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_widget_class_bind_template_child_full (GtkWidgetClass *widget_class,
|
||||
const gchar *name,
|
||||
|
@ -178,6 +178,7 @@ gtk_public_sources = files([
|
||||
'gtkbuildable.c',
|
||||
'gtkbuilder.c',
|
||||
'gtkbuilderparser.c',
|
||||
'gtkbuilderscope.c',
|
||||
'gtkbutton.c',
|
||||
'gtkcalendar.c',
|
||||
'gtkcellarea.c',
|
||||
@ -443,6 +444,7 @@ gtk_public_headers = files([
|
||||
'gtkboxlayout.h',
|
||||
'gtkbuildable.h',
|
||||
'gtkbuilder.h',
|
||||
'gtkbuilderscope.h',
|
||||
'gtkbutton.h',
|
||||
'gtkcalendar.h',
|
||||
'gtkcenterbox.h',
|
||||
|
@ -140,6 +140,11 @@ test_type (gconstpointer data)
|
||||
if ((pspec->flags & G_PARAM_READABLE) == 0)
|
||||
continue;
|
||||
|
||||
/* This is set via construct property */
|
||||
if (g_type_is_a (type, GTK_TYPE_BUILDER) &&
|
||||
strcmp (pspec->name, "scope") == 0)
|
||||
continue;
|
||||
|
||||
if (g_type_is_a (type, GDK_TYPE_CLIPBOARD) &&
|
||||
strcmp (pspec->name, "display") == 0)
|
||||
continue;
|
||||
|
@ -31,6 +31,142 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define REFTEST_TYPE_SCOPE (reftest_scope_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (ReftestScope, reftest_scope, REFTEST, SCOPE, GtkBuilderCScope)
|
||||
|
||||
static GtkBuilderScopeInterface *parent_scope_iface;
|
||||
|
||||
struct _ReftestScope
|
||||
{
|
||||
GtkBuilderCScope parent_instance;
|
||||
|
||||
char *directory;
|
||||
};
|
||||
|
||||
static GClosure *
|
||||
reftest_scope_create_closure (GtkBuilderScope *scope,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
ReftestScope *self = REFTEST_SCOPE (scope);
|
||||
ReftestModule *module;
|
||||
GCallback func;
|
||||
GClosure *closure;
|
||||
char **split;
|
||||
|
||||
split = g_strsplit (function_name, ":", -1);
|
||||
|
||||
switch (g_strv_length (split))
|
||||
{
|
||||
case 1:
|
||||
closure = parent_scope_iface->create_closure (scope, builder, split[0], flags, object, error);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
module = reftest_module_new (self->directory, split[0]);
|
||||
if (module == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not load module '%s' from '%s' when looking up '%s': %s", split[0], self->directory, function_name, g_module_error ());
|
||||
return NULL;
|
||||
}
|
||||
func = reftest_module_lookup (module, split[1]);
|
||||
if (!func)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"failed to lookup function for name '%s' in module '%s'", split[1], split[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (object)
|
||||
{
|
||||
if (flags & GTK_BUILDER_CLOSURE_SWAPPED)
|
||||
closure = g_cclosure_new_object_swap (func, object);
|
||||
else
|
||||
closure = g_cclosure_new_object (func, object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags & GTK_BUILDER_CLOSURE_SWAPPED)
|
||||
closure = g_cclosure_new_swap (func, NULL, NULL);
|
||||
else
|
||||
closure = g_cclosure_new (func, NULL, NULL);
|
||||
}
|
||||
|
||||
if (module)
|
||||
g_closure_add_finalize_notifier (closure, module, (GClosureNotify) reftest_module_unref);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not find function named '%s'", function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_strfreev (split);
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
static void
|
||||
reftest_scope_scope_init (GtkBuilderScopeInterface *iface)
|
||||
{
|
||||
iface->create_closure = reftest_scope_create_closure;
|
||||
|
||||
parent_scope_iface = g_type_interface_peek_parent (iface);
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (ReftestScope, reftest_scope, GTK_TYPE_BUILDER_CSCOPE,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDER_SCOPE,
|
||||
reftest_scope_scope_init))
|
||||
|
||||
static void
|
||||
reftest_scope_finalize (GObject *object)
|
||||
{
|
||||
ReftestScope *self = REFTEST_SCOPE (object);
|
||||
|
||||
g_free (self->directory);
|
||||
|
||||
G_OBJECT_CLASS (reftest_scope_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
reftest_scope_class_init (ReftestScopeClass *scope_class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (scope_class);
|
||||
|
||||
object_class->finalize = reftest_scope_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
reftest_scope_init (ReftestScope *self)
|
||||
{
|
||||
}
|
||||
|
||||
static GtkBuilderScope *
|
||||
reftest_scope_new (const char *directory)
|
||||
{
|
||||
ReftestScope *result;
|
||||
|
||||
g_return_val_if_fail (directory != NULL, NULL);
|
||||
|
||||
result = g_object_new (REFTEST_TYPE_SCOPE, NULL);
|
||||
|
||||
result->directory = g_strdup (directory);
|
||||
|
||||
return GTK_BUILDER_SCOPE (result);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
builder_get_toplevel (GtkBuilder *builder)
|
||||
{
|
||||
@ -145,119 +281,26 @@ snapshot_widget (GtkWidget *widget)
|
||||
return surface;
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
create_closure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GObject *object,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
ReftestModule *module;
|
||||
const char *directory;
|
||||
GCallback func;
|
||||
GClosure *closure;
|
||||
char **split;
|
||||
|
||||
directory = user_data;
|
||||
split = g_strsplit (function_name, ":", -1);
|
||||
|
||||
switch (g_strv_length (split))
|
||||
{
|
||||
case 1:
|
||||
func = gtk_builder_lookup_callback_symbol (builder, split[0]);
|
||||
|
||||
if (func)
|
||||
{
|
||||
module = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
module = reftest_module_new_self ();
|
||||
if (module == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"glib compiled without module support.");
|
||||
return NULL;
|
||||
}
|
||||
func = reftest_module_lookup (module, split[0]);
|
||||
if (!func)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"failed to lookup function for name '%s'", split[0]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (g_getenv ("REFTEST_MODULE_DIR"))
|
||||
directory = g_getenv ("REFTEST_MODULE_DIR");
|
||||
module = reftest_module_new (directory, split[0]);
|
||||
if (module == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not load module '%s' from '%s' when looking up '%s': %s", split[0], directory, function_name, g_module_error ());
|
||||
return NULL;
|
||||
}
|
||||
func = reftest_module_lookup (module, split[1]);
|
||||
if (!func)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"failed to lookup function for name '%s' in module '%s'", split[1], split[0]);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not find function named '%s'", function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_strfreev (split);
|
||||
|
||||
if (object)
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_object_swap (func, object);
|
||||
else
|
||||
closure = g_cclosure_new_object (func, object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_swap (func, NULL, NULL);
|
||||
else
|
||||
closure = g_cclosure_new (func, NULL, NULL);
|
||||
}
|
||||
|
||||
if (module)
|
||||
g_closure_add_finalize_notifier (closure, module, (GClosureNotify) reftest_module_unref);
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
cairo_surface_t *
|
||||
reftest_snapshot_ui_file (const char *ui_file)
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkBuilder *builder;
|
||||
GtkBuilderScope *scope;
|
||||
GError *error = NULL;
|
||||
char *directory;
|
||||
|
||||
if (g_getenv ("REFTEST_MODULE_DIR"))
|
||||
directory = g_strdup (g_getenv ("REFTEST_MODULE_DIR"));
|
||||
else
|
||||
directory = g_path_get_dirname (ui_file);
|
||||
scope = reftest_scope_new (directory);
|
||||
g_free (directory);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
gtk_builder_set_closure_func (builder, create_closure, directory, g_free);
|
||||
gtk_builder_set_scope (builder, scope);
|
||||
g_object_unref (scope);
|
||||
|
||||
gtk_builder_add_from_file (builder, ui_file, &error);
|
||||
g_assert_no_error (error);
|
||||
window = builder_get_toplevel (builder);
|
||||
|
Loading…
Reference in New Issue
Block a user