diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 1f65a80c6c..23293e2693 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -4626,6 +4626,7 @@ gtk_window_set_interactive_debugging gtk_widget_observe_children +gtk_widget_observe_controllers GTK_WINDOW diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index c684d51b33..5b174a7a62 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -8275,6 +8275,11 @@ gtk_widget_dispose (GObject *object) GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); GSList *sizegroups; + if (priv->children_observer) + gtk_list_list_model_clear (priv->children_observer); + if (priv->controller_observer) + gtk_list_list_model_clear (priv->controller_observer); + if (priv->parent && GTK_IS_CONTAINER (priv->parent)) gtk_container_remove (GTK_CONTAINER (priv->parent), widget); else if (priv->parent) @@ -8285,9 +8290,6 @@ gtk_widget_dispose (GObject *object) while (priv->paintables) gtk_widget_paintable_set_widget (priv->paintables->data, NULL); - if (priv->children_observer) - gtk_list_list_model_clear (priv->children_observer); - priv->visible = FALSE; if (_gtk_widget_get_realized (widget)) gtk_widget_unrealize (widget); @@ -12920,6 +12922,9 @@ gtk_widget_add_controller (GtkWidget *widget, GTK_EVENT_CONTROLLER_GET_CLASS (controller)->set_widget (controller, widget); priv->event_controllers = g_list_prepend (priv->event_controllers, controller); + + if (priv->controller_observer) + gtk_list_list_model_item_added_at (priv->controller_observer, 0); } /** @@ -12938,6 +12943,7 @@ gtk_widget_remove_controller (GtkWidget *widget, GtkEventController *controller) { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + GList *before, *list; g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (GTK_IS_EVENT_CONTROLLER (controller)); @@ -12945,8 +12951,13 @@ gtk_widget_remove_controller (GtkWidget *widget, GTK_EVENT_CONTROLLER_GET_CLASS (controller)->unset_widget (controller); - priv->event_controllers = g_list_remove (priv->event_controllers, controller); + list = g_list_find (priv->event_controllers, controller); + before = list->prev; + priv->event_controllers = g_list_delete_link (priv->event_controllers, list); g_object_unref (controller); + + if (priv->controller_observer) + gtk_list_list_model_item_removed (priv->controller_observer, before); } GList * @@ -13225,6 +13236,7 @@ gtk_widget_child_observer_destroyed (gpointer widget) priv->children_observer = NULL; } + /** * gtk_widget_observe_children: * @widget: a #GtkWidget @@ -13262,6 +13274,78 @@ gtk_widget_observe_children (GtkWidget *widget) return G_LIST_MODEL (priv->children_observer); } +static void +gtk_widget_controller_observer_destroyed (gpointer widget) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + + priv->controller_observer = NULL; +} + +static gpointer +gtk_widget_controller_list_get_first (gpointer widget) +{ + return GTK_WIDGET (widget)->priv->event_controllers; +} + +static gpointer +gtk_widget_controller_list_get_next (gpointer item, + gpointer widget) +{ + return g_list_next (item); +} + +static gpointer +gtk_widget_controller_list_get_prev (gpointer item, + gpointer widget) +{ + return g_list_previous (item); +} + +static gpointer +gtk_widget_controller_list_get_item (gpointer item, + gpointer widget) +{ + return g_object_ref (((GList *) item)->data); +} + +/** + * gtk_widget_observe_controllers: + * @widget: a #GtkWidget + * + * Returns a #GListModel to track the #GtkEventControllers of @widget. + * + * Calling this function will enable extra internal bookkeeping to track + * controllers and emit signals on the returned listmodel. It may slow down + * operations a lot. + * + * Applications should try hard to avoid calling this function because of + * the slowdowns. + * + * Returns: (transfer full): a #GListModel tracking @widget's controllers + **/ +GListModel * +gtk_widget_observe_controllers (GtkWidget *widget) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + if (priv->controller_observer) + return g_object_ref (G_LIST_MODEL (priv->controller_observer)); + + priv->controller_observer = gtk_list_list_model_new (GTK_TYPE_EVENT_CONTROLLER, + gtk_widget_controller_list_get_first, + gtk_widget_controller_list_get_next, + gtk_widget_controller_list_get_prev, + NULL, + gtk_widget_controller_list_get_item, + widget, + gtk_widget_controller_observer_destroyed); + + return G_LIST_MODEL (priv->controller_observer); +} + /** * gtk_widget_get_first_child: * @widget: a #GtkWidget diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index e00f832bcb..b92ba1229d 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -1033,6 +1033,8 @@ GtkWidget * gtk_widget_get_prev_sibling (GtkWidget *widget); GDK_AVAILABLE_IN_ALL GListModel * gtk_widget_observe_children (GtkWidget *widget); GDK_AVAILABLE_IN_ALL +GListModel * gtk_widget_observe_controllers (GtkWidget *widget); +GDK_AVAILABLE_IN_ALL void gtk_widget_insert_after (GtkWidget *widget, GtkWidget *parent, GtkWidget *previous_sibling);