From efbd2289405553c6dad96a99501203f330896340 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ant=C3=B3nio=20Fernandes?= Date: Fri, 24 Jun 2022 09:05:29 +0100 Subject: [PATCH] listbase: Cancel rubberband if not handling drag If the drag events are claimed by another gesture (e.g. a GtkDragSource in an item widget), list base still commits a rubberband selection, for a rubberband which wasn't even visible yet. This is a problem for the GNOME Files application which needs both rubberbanding and drag-n-drop. My previous fix[0] was enough for the case where the event sequence is claimed right before the first GtkDragGesture::drag-update emission, but it's useless if the event is claimed later (e.g. after the drag treashold), because a rubberband already exists by that time. Therefore, the complete solution requres checking whether the event sequence is no longer being handled by our gesture, and commit the selection changes only if it is, but otherwise cleanup the rubberband. This is what GtkFlowBox does already, so let's do the same here. [0] commit dc4540fae98d4f707ce1030b0f8d161c987646e0 --- gtk/gtklistbase.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c index 03bcb48288..d5799d2a2e 100644 --- a/gtk/gtklistbase.c +++ b/gtk/gtklistbase.c @@ -1567,25 +1567,16 @@ gtk_list_base_start_rubberband (GtkListBase *self, } static void -gtk_list_base_stop_rubberband (GtkListBase *self, - gboolean modify, - gboolean extend) +gtk_list_base_apply_rubberband_selection (GtkListBase *self, + gboolean modify, + gboolean extend) { GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); - GtkListItemManagerItem *item; GtkSelectionModel *model; if (!priv->rubberband) return; - for (item = gtk_list_item_manager_get_first (priv->item_manager); - item != NULL; - item = gtk_rb_tree_node_get_next (item)) - { - if (item->widget) - gtk_widget_unset_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE); - } - model = gtk_list_item_manager_get_model (priv->item_manager); if (model != NULL) { @@ -1646,6 +1637,24 @@ gtk_list_base_stop_rubberband (GtkListBase *self, gtk_bitset_unref (mask); gtk_bitset_unref (rubberband_selection); } +} + +static void +gtk_list_base_stop_rubberband (GtkListBase *self) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + GtkListItemManagerItem *item; + + if (!priv->rubberband) + return; + + for (item = gtk_list_item_manager_get_first (priv->item_manager); + item != NULL; + item = gtk_rb_tree_node_get_next (item)) + { + if (item->widget) + gtk_widget_unset_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE); + } gtk_list_item_tracker_free (priv->item_manager, priv->rubberband->start_tracker); g_clear_pointer (&priv->rubberband->widget, gtk_widget_unparent); @@ -1758,14 +1767,23 @@ gtk_list_base_drag_end (GtkGestureDrag *gesture, GtkListBase *self) { GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + GdkEventSequence *sequence; gboolean modify, extend; if (!priv->rubberband) return; + sequence = gtk_gesture_get_last_updated_sequence (GTK_GESTURE (gesture)); + if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence)) + { + gtk_list_base_stop_rubberband (self); + return; + } + gtk_list_base_drag_update (gesture, offset_x, offset_y, self); get_selection_modifiers (GTK_GESTURE (gesture), &modify, &extend); - gtk_list_base_stop_rubberband (self, modify, extend); + gtk_list_base_apply_rubberband_selection (self, modify, extend); + gtk_list_base_stop_rubberband (self); } void -- 2.30.2