[gtk/wip/otte/listview: 7/151] builder: Add GtkBuilderScope
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/listview: 7/151] builder: Add GtkBuilderScope
- Date: Mon, 2 Dec 2019 21:08:18 +0000 (UTC)
commit aec47fdc3e62924ecd9dbf38f215e85b9df38ffe
Author: Benjamin Otte <otte redhat com>
Date: Sat Nov 30 01:17:10 2019 +0100
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.
demos/widget-factory/widget-factory.c | 30 ++-
docs/reference/gtk/gtk4-sections.txt | 36 ++-
gtk/gtk.h | 1 +
gtk/gtkbuilder.c | 458 +++++++--------------------------
gtk/gtkbuilder.h | 32 +--
gtk/gtkbuilderscope.c | 459 ++++++++++++++++++++++++++++++++++
gtk/gtkbuilderscope.h | 99 ++++++++
gtk/gtkbuilderscopeprivate.h | 40 +++
gtk/gtktypes.h | 8 +-
gtk/gtkwidget.c | 98 +++-----
gtk/gtkwidget.h | 10 +-
gtk/meson.build | 2 +
testsuite/gtk/defaultvalue.c | 5 +
testsuite/reftests/reftest-snapshot.c | 249 ++++++++++--------
14 files changed, 929 insertions(+), 598 deletions(-)
---
diff --git a/demos/widget-factory/widget-factory.c b/demos/widget-factory/widget-factory.c
index 565383483c..94ddcfae68 100644
--- a/demos/widget-factory/widget-factory.c
+++ b/demos/widget-factory/widget-factory.c
@@ -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");
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index efa6c32df7..96de634b5d 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -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
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 270aad8c6b..1fd949bd5f 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -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>
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 178ad9110e..4eec7c3974 100644
--- a/gtk/gtkbuilder.c
+++ b/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
- *
- * Prototype of function used to create closures by @builder. It is meant
- * for influencing how @function_name is resolved.
- *
- * 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.
- *
- * 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: (nullable): a new #GClosure or %NULL when no closure could
- * be created and @error was set.
- */
+ * gtk_builder_get_scope:
+ * @self: a #GtkBuilder
+ *
+ * Gets the scope in use that was set via gtk_builder_set_scope().
+ *
+ * See the #GtkBuilderScope documentation for details.
+ *
+ * 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
+ *
+ * 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
*
@@ -2828,11 +2605,11 @@ gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
* Returns: (nullable): A new closure for invoking @function_name
**/
GClosure *
-gtk_builder_create_closure (GtkBuilder *builder,
- const char *function_name,
- gboolean swapped,
- GObject *object,
- GError **error)
+gtk_builder_create_closure (GtkBuilder *builder,
+ const char *function_name,
+ GtkBuilderClosureFlags flags,
+ GObject *object,
+ GError **error)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
@@ -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);
}
/**
diff --git a/gtk/gtkbuilder.h b/gtk/gtkbuilder.h
index 4c7e6e934c..d5e4bd3297 100644
--- a/gtk/gtkbuilder.h
+++ b/gtk/gtkbuilder.h
@@ -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);
diff --git a/gtk/gtkbuilderscope.c b/gtk/gtkbuilderscope.c
new file mode 100644
index 0000000000..8ae5a6602c
--- /dev/null
+++ b/gtk/gtkbuilderscope.c
@@ -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);
+}
+
diff --git a/gtk/gtkbuilderscope.h b/gtk/gtkbuilderscope.h
new file mode 100644
index 0000000000..72999b1e0f
--- /dev/null
+++ b/gtk/gtkbuilderscope.h
@@ -0,0 +1,99 @@
+/*
+ * 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_swapped() 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;
+
+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__ */
diff --git a/gtk/gtkbuilderscopeprivate.h b/gtk/gtkbuilderscopeprivate.h
new file mode 100644
index 0000000000..ebdc698034
--- /dev/null
+++ b/gtk/gtkbuilderscopeprivate.h
@@ -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__ */
diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h
index 83a462448d..dce9585766 100644
--- a/gtk/gtktypes.h
+++ b/gtk/gtktypes.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);
-
G_END_DECLS
#endif /* __GTK_TYPES_H__ */
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index c2a4ef1cbf..57a8421e62 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -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,24 +12126,10 @@ gtk_widget_init_template (GtkWidget *widget)
builder = gtk_builder_new ();
- gtk_builder_set_current_object (builder, G_OBJECT (widget));
+ if (template->scope)
+ gtk_builder_set_scope (builder, template->scope);
- /* 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);
- }
+ gtk_builder_set_current_object (builder, G_OBJECT (widget));
/* 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
@@ -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);
}
/**
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index ff8ac40027..fb1b6f28fe 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -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,
diff --git a/gtk/meson.build b/gtk/meson.build
index 4d492dd995..fa3995ce05 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -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',
diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c
index 4c79386cac..0164107422 100644
--- a/testsuite/gtk/defaultvalue.c
+++ b/testsuite/gtk/defaultvalue.c
@@ -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;
diff --git a/testsuite/reftests/reftest-snapshot.c b/testsuite/reftests/reftest-snapshot.c
index e7f7dcff49..29d5455b32 100644
--- a/testsuite/reftests/reftest-snapshot.c
+++ b/testsuite/reftests/reftest-snapshot.c
@@ -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, G_TYPE_OBJECT,
+ 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;
- directory = g_path_get_dirname (ui_file);
+ 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);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]