[gtk/wip/otte/listview: 14/32] listview: Implement extending selections



commit 5166156f7b4d007fdc49d3f0ffe27ba9a22dd2ed
Author: Benjamin Otte <otte redhat com>
Date:   Sun Oct 6 04:10:09 2019 +0200

    listview: Implement extending selections
    
    Shift-clicking to extend selections now also works, imitating the
    behavior of normal clicking and Windows Explorer (but not treeview):
    
    1. We track the last selected item (normally, not via extend-clicking).
    
    2. When shift-selecting, we modify the range from the last selected item
       to this item the same way we modify the regular item when not using
       shift:
    
    2a. If Ctrl is not pressed, we select the range and unselect everything
        else.
    
    2b. If Ctrl is pressed, we make the range have the same selection state
        as the last selected item:
        - If the last selected item is selected, select the range.
        - If the last selected item is not selected, unselect the range.

 gtk/gtklistview.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 56 insertions(+), 4 deletions(-)
---
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 29cc7ef372..53a17c44b8 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -68,6 +68,8 @@ struct _GtkListView
 
   GtkListItemTracker *anchor;
   double anchor_align;
+  /* the last item that was selected - basically the location to extend selections from */
+  GtkListItemTracker *selected;
 };
 
 struct _ListRow
@@ -567,6 +569,11 @@ gtk_list_view_dispose (GObject *object)
       gtk_list_item_tracker_free (self->item_manager, self->anchor);
       self->anchor = NULL;
     }
+  if (self->selected)
+    {
+      gtk_list_item_tracker_free (self->item_manager, self->selected);
+      self->selected = NULL;
+    }
   g_clear_object (&self->item_manager);
 
   G_OBJECT_CLASS (gtk_list_view_parent_class)->dispose (object);
@@ -711,22 +718,66 @@ gtk_list_view_select_item (GtkWidget  *widget,
   GtkSelectionModel *selection_model;
   guint pos;
   gboolean modify, extend;
+  gboolean success = FALSE;
 
   selection_model = gtk_list_item_manager_get_model (self->item_manager);
   g_variant_get (parameter, "(ubb)", &pos, &modify, &extend);
 
-  /* XXX: handle extend by tracking the item to extend from */
+  if (extend)
+    {
+      guint start_pos = gtk_list_item_tracker_get_position (self->item_manager, self->selected);
+      if (start_pos != GTK_INVALID_LIST_POSITION)
+        {
+          guint max = MAX (start_pos, pos);
+          guint min = MIN (start_pos, pos);
+          if (modify)
+            {
+              if (gtk_selection_model_is_selected (selection_model, start_pos))
+                {
+                  success = gtk_selection_model_select_range (selection_model,
+                                                              min,
+                                                              max - min + 1,
+                                                              FALSE);
+                }
+              else
+                {
+                  success = gtk_selection_model_unselect_range (selection_model,
+                                                                min,
+                                                                max - min + 1);
+                }
+            }
+          else
+            {
+              success = gtk_selection_model_select_range (selection_model,
+                                                          min,
+                                                          max - min + 1,
+                                                          TRUE);
+            }
+        }
+      /* If there's no range to select or selecting ranges isn't supported
+       * by the model, fall through to normal setting.
+       */
+    }
+  if (success)
+    return;
 
   if (modify)
     {
       if (gtk_selection_model_is_selected (selection_model, pos))
-        gtk_selection_model_unselect_item (selection_model, pos);
+        success = gtk_selection_model_unselect_item (selection_model, pos);
       else
-        gtk_selection_model_select_item (selection_model, pos, FALSE);
+        success = gtk_selection_model_select_item (selection_model, pos, FALSE);
     }
   else
     {
-      gtk_selection_model_select_item (selection_model, pos, TRUE);
+      success = gtk_selection_model_select_item (selection_model, pos, TRUE);
+    }
+  if (success)
+    {
+      gtk_list_item_tracker_set_position (self->item_manager,
+                                          self->selected,
+                                          pos,
+                                          0, 0);
     }
 }
 
@@ -871,6 +922,7 @@ gtk_list_view_init (GtkListView *self)
 {
   self->item_manager = gtk_list_item_manager_new (GTK_WIDGET (self), ListRow, ListRowAugment, 
list_row_augment);
   self->anchor = gtk_list_item_tracker_new (self->item_manager);
+  self->selected = gtk_list_item_tracker_new (self->item_manager);
 
   self->adjustment[GTK_ORIENTATION_HORIZONTAL] = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
   self->adjustment[GTK_ORIENTATION_VERTICAL] = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]