From aadaf4adf2b555e659643d488a7c6b5cf6d81c60 Mon Sep 17 00:00:00 2001 From: Matthijs Velsink Date: Sun, 7 Apr 2024 14:37:55 +0200 Subject: [PATCH] shortcutmanager: Track propagation phase of added controllers GtkShortcutManager allows adding controllers to it. For the default implementation, they get added to one of two models, based on the propagation phase (either GTK_PHASE_CAPTURE or GTK_PHASE_BUBBLE). However, when a controller is removed, its presence in the manager gets checked against the current propagation phase of the controller, which may have changed from when it was added. This can lead to crashes if the controller was not disposed properly since it still has a reference in one of the two models of the GtkShortcutManager. To fix this, add a callback for `notify::propagation-phase`, which removes the controller from all possible models and readds it again with its current phase. This callback is only disconnected permanently when the controller is manually removed with `gtk_shortcut_manager_default_remove_controller()`. Closes #6246 --- gtk/gtkshortcutmanager.c | 46 ++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/gtk/gtkshortcutmanager.c b/gtk/gtkshortcutmanager.c index ab96eaef4f..0a7940246a 100644 --- a/gtk/gtkshortcutmanager.c +++ b/gtk/gtkshortcutmanager.c @@ -82,8 +82,8 @@ gtk_shortcut_manager_get_model (GtkShortcutManager *self, } static void -gtk_shortcut_manager_default_add_controller (GtkShortcutManager *self, - GtkShortcutController *controller) +gtk_shortcut_manager_add_controller (GtkShortcutManager *self, + GtkShortcutController *controller) { GtkFlattenListModel *model; GtkPropagationPhase phase; @@ -98,13 +98,12 @@ gtk_shortcut_manager_default_add_controller (GtkShortcutManager *self, } static void -gtk_shortcut_manager_default_remove_controller (GtkShortcutManager *self, - GtkShortcutController *controller) +gtk_shortcut_manager_remove_controller_for_phase (GtkShortcutManager *self, + GtkShortcutController *controller, + GtkPropagationPhase phase) { GtkFlattenListModel *model; - GtkPropagationPhase phase; - phase = gtk_event_controller_get_propagation_phase (GTK_EVENT_CONTROLLER (controller)); model = gtk_shortcut_manager_get_model (self, phase); if (model) { @@ -117,6 +116,41 @@ gtk_shortcut_manager_default_remove_controller (GtkShortcutManager *self, } } +static void +propagation_phase_changed (GtkShortcutController *controller, + GParamSpec *pspec, + GtkShortcutManager *self) +{ + /* Remove from all models and readd */ + gtk_shortcut_manager_remove_controller_for_phase (self, controller, GTK_PHASE_CAPTURE); + gtk_shortcut_manager_remove_controller_for_phase (self, controller, GTK_PHASE_BUBBLE); + + gtk_shortcut_manager_add_controller (self, controller); +} + +static void +gtk_shortcut_manager_default_add_controller (GtkShortcutManager *self, + GtkShortcutController *controller) +{ + gtk_shortcut_manager_add_controller (self, controller); + + g_signal_connect_object (controller, "notify::propagation-phase", + G_CALLBACK (propagation_phase_changed), self, G_CONNECT_DEFAULT); + +} + +static void +gtk_shortcut_manager_default_remove_controller (GtkShortcutManager *self, + GtkShortcutController *controller) +{ + GtkPropagationPhase phase; + + phase = gtk_event_controller_get_propagation_phase (GTK_EVENT_CONTROLLER (controller)); + gtk_shortcut_manager_remove_controller_for_phase (self, controller, phase); + + g_signal_handlers_disconnect_by_func (controller, propagation_phase_changed, self); +} + static void gtk_shortcut_manager_default_init (GtkShortcutManagerInterface *iface) {