[gtk/wip/otte/listview: 46/155] listview: Add initial support for displaying selections



commit f1497f394c64b119c02597c1f6e573ef0fe8e434
Author: Benjamin Otte <otte redhat com>
Date:   Wed Oct 3 18:53:06 2018 +0200

    listview: Add initial support for displaying selections

 gtk/gtklistitemmanager.c        | 25 +++++++++------
 gtk/gtklistitemmanagerprivate.h |  5 +--
 gtk/gtklistview.c               | 67 ++++++++++++++++++++++++++++++++++++++---
 3 files changed, 81 insertions(+), 16 deletions(-)
---
diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c
index 1e74c8d8e10..e7c11f483f6 100644
--- a/gtk/gtklistitemmanager.c
+++ b/gtk/gtklistitemmanager.c
@@ -28,7 +28,7 @@ struct _GtkListItemManager
   GObject parent_instance;
 
   GtkWidget *widget;
-  GListModel *model;
+  GtkSelectionModel *model;
   GtkListItemFactory *factory;
 };
 
@@ -107,7 +107,7 @@ gtk_list_item_manager_get_factory (GtkListItemManager *self)
 
 void
 gtk_list_item_manager_set_model (GtkListItemManager *self,
-                                 GListModel         *model)
+                                 GtkSelectionModel  *model)
 {
   g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
   g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
@@ -121,7 +121,7 @@ gtk_list_item_manager_set_model (GtkListItemManager *self,
     self->model = g_object_ref (model);
 }
 
-GListModel *
+GtkSelectionModel *
 gtk_list_item_manager_get_model (GtkListItemManager *self)
 {
   g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
@@ -255,14 +255,16 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
 {
   GtkListItem *result;
   gpointer item;
+  gboolean selected;
 
   g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
   g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
 
   result = gtk_list_item_factory_create (self->factory);
 
-  item = g_list_model_get_item (self->model, position);
-  gtk_list_item_factory_bind (self->factory, result, position, item, FALSE);
+  item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
+  selected = gtk_selection_model_is_selected (self->model, position);
+  gtk_list_item_factory_bind (self->factory, result, position, item, selected);
   g_object_unref (item);
   gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling);
 
@@ -297,7 +299,7 @@ gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager       *self,
   g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
 
   /* XXX: can we avoid temporarily allocating items on failure? */
-  item = g_list_model_get_item (self->model, position);
+  item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
   if (g_hash_table_steal_extended (change->items, item, NULL, (gpointer *) &result))
     {
       gtk_list_item_factory_update (self->factory, result, position, FALSE);
@@ -334,9 +336,11 @@ gtk_list_item_manager_move_list_item (GtkListItemManager     *self,
                                       GtkWidget              *prev_sibling)
 {
   gpointer item;
+  gboolean selected;
 
-  item = g_list_model_get_item (self->model, position);
-  gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item, FALSE);
+  item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
+  selected = gtk_selection_model_is_selected (self->model, position);
+  gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item, selected);
   gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling);
   g_object_unref (item);
 }
@@ -355,10 +359,13 @@ gtk_list_item_manager_update_list_item (GtkListItemManager *self,
                                         GtkWidget          *item,
                                         guint               position)
 {
+  gboolean selected;
+
   g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
   g_return_if_fail (GTK_IS_LIST_ITEM (item));
 
-  gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, FALSE);
+  selected = gtk_selection_model_is_selected (self->model, position);
+  gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, selected);
 }
 
 /*
diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h
index d34e346a21e..47a4434d7df 100644
--- a/gtk/gtklistitemmanagerprivate.h
+++ b/gtk/gtklistitemmanagerprivate.h
@@ -24,6 +24,7 @@
 #include "gtk/gtktypes.h"
 
 #include "gtk/gtklistitemfactoryprivate.h"
+#include "gtk/gtkselectionmodel.h"
 
 G_BEGIN_DECLS
 
@@ -46,8 +47,8 @@ void                    gtk_list_item_manager_set_factory       (GtkListItemMana
                                                                  GtkListItemFactory     *factory);
 GtkListItemFactory *    gtk_list_item_manager_get_factory       (GtkListItemManager     *self);
 void                    gtk_list_item_manager_set_model         (GtkListItemManager     *self,
-                                                                 GListModel             *model);
-GListModel *            gtk_list_item_manager_get_model         (GtkListItemManager     *self);
+                                                                 GtkSelectionModel      *model);
+GtkSelectionModel *     gtk_list_item_manager_get_model         (GtkListItemManager     *self);
 
 guint                   gtk_list_item_manager_get_size          (GtkListItemManager     *self);
 
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 35cf3b6da3e..494a2a591bf 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -27,6 +27,8 @@
 #include "gtklistitemfactoryprivate.h"
 #include "gtklistitemmanagerprivate.h"
 #include "gtkscrollable.h"
+#include "gtkselectionmodel.h"
+#include "gtksingleselection.h"
 #include "gtkwidgetprivate.h"
 
 /* Maximum number of list items created by the listview.
@@ -908,7 +910,7 @@ gtk_list_view_model_items_changed_cb (GListModel  *model,
       guint i, offset, anchor_pos;
       
       row = gtk_list_view_get_row (self, position, &offset);
-      for (new_row = gtk_rb_tree_node_get_previous (row);
+      for (new_row = row ? gtk_rb_tree_node_get_previous (row) : gtk_rb_tree_get_last (self->rows);
            new_row && new_row->widget == NULL;
            new_row = gtk_rb_tree_node_get_previous (new_row))
         { }
@@ -969,6 +971,9 @@ gtk_list_view_model_items_changed_cb (GListModel  *model,
           anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
 
           anchor_pos = position + (anchor_pos - position) * added / removed;
+          if (anchor_pos >= g_list_model_get_n_items (self->model) &&
+              anchor_pos > 0)
+            anchor_pos--;
         }
       gtk_list_view_set_anchor (self, anchor_pos, self->anchor_align, change, position);
     }
@@ -997,6 +1002,34 @@ gtk_list_view_model_items_changed_cb (GListModel  *model,
   gtk_list_item_manager_end_change (self->item_manager, change);
 }
 
+static void
+gtk_list_view_model_selection_changed_cb (GListModel  *model,
+                                          guint        position,
+                                          guint        n_items,
+                                          GtkListView *self)
+{
+  ListRow *row;
+  guint offset;
+
+  row = gtk_list_view_get_row (self, position, &offset);
+
+  if (offset)
+    {
+      position += row->n_rows - offset;
+      n_items -= row->n_rows - offset;
+      row = gtk_rb_tree_node_get_next (row);
+    }
+
+  while (n_items > 0)
+    {
+      if (row->widget)
+        gtk_list_item_manager_update_list_item (self->item_manager, row->widget, position);
+      position += row->n_rows;
+      n_items -= MIN (n_items, row->n_rows);
+      row = gtk_rb_tree_node_get_next (row);
+    }
+}
+
 static void
 gtk_list_view_clear_model (GtkListView *self)
 {
@@ -1005,6 +1038,9 @@ gtk_list_view_clear_model (GtkListView *self)
 
   gtk_list_view_remove_rows (self, NULL, 0, g_list_model_get_n_items (self->model));
 
+  g_signal_handlers_disconnect_by_func (self->model,
+                                        gtk_list_view_model_selection_changed_cb,
+                                        self);
   g_signal_handlers_disconnect_by_func (self->model,
                                         gtk_list_view_model_items_changed_cb,
                                         self);
@@ -1264,9 +1300,12 @@ gtk_list_view_get_model (GtkListView *self)
 /**
  * gtk_list_view_set_model:
  * @self: a #GtkListView
- * @file: (allow-none) (transfer none): the model to use or %NULL for none
+ * @model: (allow-none) (transfer none): the model to use or %NULL for none
+ *
+ * Sets the #GListModel to use.
  *
- * Sets the #GListModel to use for
+ * If the @model is a #GtkSelectionModel, it is used for managing the selection.
+ * Otherwise, @self creates a #GtkSingleSelection for the selection.
  **/
 void
 gtk_list_view_set_model (GtkListView *self,
@@ -1280,20 +1319,38 @@ gtk_list_view_set_model (GtkListView *self,
 
   gtk_list_view_clear_model (self);
 
-  gtk_list_item_manager_set_model (self->item_manager, model);
-
   if (model)
     {
+      GtkSelectionModel *selection_model;
+
       self->model = g_object_ref (model);
 
+      if (GTK_IS_SELECTION_MODEL (model))
+        selection_model = GTK_SELECTION_MODEL (g_object_ref (model));
+      else
+        selection_model = GTK_SELECTION_MODEL (gtk_single_selection_new (model));
+
+      gtk_list_item_manager_set_model (self->item_manager, selection_model);
+
       g_signal_connect (model,
                         "items-changed",
                         G_CALLBACK (gtk_list_view_model_items_changed_cb),
                         self);
+      g_signal_connect (selection_model,
+                        "selection-changed",
+                        G_CALLBACK (gtk_list_view_model_selection_changed_cb),
+                        self);
+
+      g_object_unref (selection_model);
 
       gtk_list_view_add_rows (self, 0, g_list_model_get_n_items (model));
       gtk_list_view_set_anchor (self, 0, 0, NULL, (guint) -1);
     }
+  else
+    {
+      gtk_list_item_manager_set_model (self->item_manager, NULL);
+    }
+
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
 }


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