From 7857c1a66bd8f026d72d661751471de8f2b2e508 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 16 May 2022 14:10:20 +0100 Subject: [PATCH] Add gtk_widget_clear_template() The dual of gtk_widget_init_template(), which should be used to clear the template data associated with a specific GtkWidget type. --- gtk/gtkwidget.c | 83 +++++++++++++++++++++++++++++++++++++++++++------ gtk/gtkwidget.h | 3 ++ 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 3d1a46897d..432e10301b 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -7437,16 +7437,7 @@ gtk_widget_real_destroy (GtkWidget *object) GObject *child_object = gtk_widget_get_template_child (widget, class_type, child_class->name); - - g_assert (child_object); - - if (!G_IS_OBJECT (child_object)) - { - g_critical ("Automated component '%s' of class '%s' seems to" - " have been prematurely finalized", - child_class->name, g_type_name (class_type)); - } - else + if (G_IS_OBJECT (child_object)) { FinalizeAssertion *assertion = g_slice_new0 (FinalizeAssertion); assertion->child_class = child_class; @@ -10978,6 +10969,78 @@ out: g_object_unref (builder); } +/** + * gtk_widget_clear_template: + * @widget: the widget with a template + * @widget_type: the type of the widget to finalize the template for + * + * Clears the template data for the given widget. + * + * This function is the opposite of [method@Gtk.Widget.init_template], and + * it is used to clear all the template data from a widget instance. + * + * You should call this function inside the `GObjectClass.dispose()` + * implementation of any widget that called `gtk_widget_init_template()`. + * Typically, you will want to call this function last, right before + * chaining up to the parent type's dispose implementation, e.g. + * + * ```c + * static void + * some_widget_dispose (GObject *gobject) + * { + * SomeWidget *self = SOME_WIDGET (gobject); + * + * // Clear the template data for SomeWidget + * gtk_widget_clear_template (GTK_WIDGET (self), SOME_TYPE_WIDGET); + * + * G_OBJECT_CLASS (some_widget_parent_class)->dispose (gobject); + * } + * ``` + * + * Since: 4.8 + */ +void +gtk_widget_clear_template (GtkWidget *widget, + GType widget_type) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (g_type_name (widget_type) != NULL); + + GtkWidgetTemplate *template = GTK_WIDGET_GET_CLASS (widget)->priv->template; + g_return_if_fail (template != NULL); + + /* Tear down the automatic child data */ + GHashTable *auto_child_hash = get_auto_child_hash (widget, widget_type, FALSE); + + for (GSList *l = template->children; l != NULL; l = l->next) + { + AutomaticChildClass *child_class = l->data; + + /* This will drop the reference on the template children */ + if (auto_child_hash != NULL) + { + gpointer child = g_hash_table_lookup (auto_child_hash, child_class->name); + + g_assert (child != NULL); + + /* We have to explicitly unparent direct children of this widget */ + if (GTK_IS_WIDGET (child) && _gtk_widget_get_parent (child) == widget) + gtk_widget_unparent (child); + + g_hash_table_remove (auto_child_hash, child_class->name); + } + + /* Nullify the field last, to avoid re-entrancy issues */ + if (child_class->offset != 0) + { + gpointer field_p; + + field_p = G_STRUCT_MEMBER_P (widget, child_class->offset); + (* (gpointer *) field_p) = NULL; + } + } +} + /** * gtk_widget_class_set_template: * @widget_class: A `GtkWidgetClass` diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 0788fcdd81..fede1b634e 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -833,6 +833,9 @@ GDK_AVAILABLE_IN_ALL GObject *gtk_widget_get_template_child (GtkWidget *widget, GType widget_type, const char *name); +GDK_AVAILABLE_IN_4_8 +void gtk_widget_clear_template (GtkWidget *widget, + GType widget_type); GDK_AVAILABLE_IN_ALL void gtk_widget_class_set_template (GtkWidgetClass *widget_class, GBytes *template_bytes);