From: Benjamin Otte Date: Sat, 30 Nov 2019 00:17:10 +0000 (+0100) Subject: builder: Add GtkBuilderScope X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~20^2~542^2~1 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=f8a7f30a0dd1643376f942619603719333a8d6bf;p=gtk4.git 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. --- 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 +
+gtkbuilderscope +GtkBuilderScope +gtk_builder_cscope_new +gtk_builder_cscope_add_callback_symbol +gtk_builder_cscope_add_callback_symbols +gtk_builder_cscope_lookup_callback_symbol + +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 + +gtk_builder_scope_get_type +gtk_builder_cscope_get_type +
+
gtkbuilder GtkBuilder 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 gtk_builder_get_type gtk_builder_error_quark -GtkBuilderPrivate
@@ -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 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 #include #include +#include #include #include #include diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 0be335e63b..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 #include /* 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 can be included directly." #endif -#include +#include #include 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 . + * + * Authors: Benjamin Otte + */ + +#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..660fa3775d --- /dev/null +++ b/gtk/gtkbuilderscope.h @@ -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 . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_BUILDER_SCOPE_H__ +#define __GTK_BUILDER_SCOPE_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +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__ */ 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 . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_BUILDER_SCOPE_PRIVATE_H__ +#define __GTK_BUILDER_SCOPE_PRIVATE_H__ + +#include + +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 d8ad82be0f..49558bddea 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); - /** * GTK_INVALID_LIST_POSITION: * 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 dcf15b20e6..eab367b712 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 ffd567302a..827c7b2708 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..6d9ca10cde 100644 --- a/testsuite/reftests/reftest-snapshot.c +++ b/testsuite/reftests/reftest-snapshot.c @@ -31,6 +31,142 @@ #include +#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; - 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);