listbase: Cancel rubberband if not handling drag
authorAntónio Fernandes <antoniof@gnome.org>
Fri, 24 Jun 2022 08:05:29 +0000 (09:05 +0100)
committerCorey Berla <corey@berla.me>
Mon, 19 Dec 2022 07:31:31 +0000 (21:31 -1000)
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

index 03bcb48288cec10164bb72e017b893c1845974eb..d5799d2a2efddc6fe51d1b9fd3bc3752514cb8c4 100644 (file)
@@ -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