list: Allow storing size in the ListTile
authorBenjamin Otte <otte@redhat.com>
Wed, 22 Feb 2023 22:33:16 +0000 (23:33 +0100)
committerBenjamin Otte <otte.benjamin@googlemail.com>
Sun, 5 Mar 2023 15:23:20 +0000 (15:23 +0000)
... and use it to handle ListView allocations.

Nothing spectacular, just proof of concept.

The code introduces the idea that every tile stores its area (others
would call it "allocation", but I avoided that because tiles aren't
widgets). This should allow moving lots of code into gtklistbase.c and
not require special handling inside ListView and GridView.

And that in turn hopefully makes it easier to add more features (like
sections and so on.)

gtk/gtklistbase.c
gtk/gtklistbaseprivate.h
gtk/gtklistitemmanager.c
gtk/gtklistitemmanagerprivate.h
gtk/gtklistview.c

index 648d84f446a817964ebcc9ae9bff53b3b2272186..5b1af350c31652bb3968301852c5dee865035b78 100644 (file)
@@ -1432,6 +1432,32 @@ gtk_list_base_size_allocate_child (GtkListBase *self,
   gtk_widget_size_allocate (child, &child_allocation, -1);
 }
 
+void
+gtk_list_base_allocate_children (GtkListBase *self)
+{
+  GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+  GtkListTile *tile;
+  int dx, dy;
+  
+  gtk_list_base_get_adjustment_values (self, OPPOSITE_ORIENTATION (priv->orientation), &dx, NULL, NULL);
+  gtk_list_base_get_adjustment_values (self, priv->orientation, &dy, NULL, NULL);
+
+  for (tile = gtk_list_item_manager_get_first (priv->item_manager);
+       tile != NULL;
+       tile = gtk_rb_tree_node_get_next (tile))
+    {
+      if (tile->widget)
+        {
+          gtk_list_base_size_allocate_child (GTK_LIST_BASE (self),
+                                             tile->widget,
+                                             tile->area.x - dx,
+                                             tile->area.y - dy,
+                                             tile->area.width,
+                                             tile->area.height);
+        }
+    }
+}
+
 static void
 gtk_list_base_widget_to_list (GtkListBase *self,
                               double       x_widget,
index 75d746678477351caebd5a0198c87205d0784b31..2d4fb23e1ac115a2b3f5fcb3abb3dc27a75e6576 100644 (file)
@@ -107,6 +107,7 @@ void                   gtk_list_base_set_enable_rubberband      (GtkListBase
                                                                  gboolean                enable);
 gboolean               gtk_list_base_get_enable_rubberband      (GtkListBase            *self);
 void                   gtk_list_base_allocate_rubberband        (GtkListBase            *self);
+void                   gtk_list_base_allocate_children          (GtkListBase            *self);
 
 void                   gtk_list_base_size_allocate_child        (GtkListBase            *self,
                                                                  GtkWidget              *child,
index d4b1d3b7640a0ef310c364d9a0c0d666a7535228..f5bf4aa9999ed175096a9f1b54931f65bcad6ee4 100644 (file)
@@ -74,6 +74,22 @@ static void             gtk_list_item_manager_release_list_item (GtkListItemMana
                                                                  GtkWidget              *widget);
 G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT)
 
+static void
+potentially_empty_rectangle_union (cairo_rectangle_int_t       *self,
+                                   const cairo_rectangle_int_t *area)
+{
+  if (area->width <= 0 || area->height <= 0)
+    return;
+
+  if (self->width <= 0 || self->height <= 0)
+    {
+      *self = *area;
+      return;
+    }
+
+  gdk_rectangle_union (self, area, self);
+}
+
 void
 gtk_list_item_manager_augment_node (GtkRbTree *tree,
                                     gpointer   node_augment,
@@ -85,12 +101,14 @@ gtk_list_item_manager_augment_node (GtkRbTree *tree,
   GtkListTileAugment *aug = node_augment;
 
   aug->n_items = tile->n_items;
+  aug->area = tile->area;
 
   if (left)
     {
       GtkListTileAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
 
       aug->n_items += left_aug->n_items;
+      potentially_empty_rectangle_union (&aug->area, &left_aug->area);
     }
 
   if (right)
@@ -98,6 +116,7 @@ gtk_list_item_manager_augment_node (GtkRbTree *tree,
       GtkListTileAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
 
       aug->n_items += right_aug->n_items;
+      potentially_empty_rectangle_union (&aug->area, &right_aug->area);
     }
 }
 
@@ -249,6 +268,66 @@ gtk_list_tile_get_augment (GtkListItemManager *self,
   return gtk_rb_tree_get_augment (self->items, tile);
 }
 
+/*
+ * gtk_list_tile_set_area:
+ * @self: the list item manager
+ * @tile: tile to set area for
+ * @area: (nullable): area to set or NULL to clear
+ *     the area
+ *
+ * Updates the area of the tile.
+ *
+ * The area is given in the internal coordinate system,
+ * so the x/y flip due to orientation and the left/right
+ * flip for RTL languages will happen later.
+ *
+ * This function should only be called from inside size_allocate().
+ **/
+void
+gtk_list_tile_set_area (GtkListItemManager          *self,
+                        GtkListTile                 *tile,
+                        const cairo_rectangle_int_t *area)
+{
+  cairo_rectangle_int_t empty_area = { 0, 0, 0, 0 };
+
+  if (!area)
+    area = &empty_area;
+
+  if (gdk_rectangle_equal (&tile->area, area))
+    return;
+
+  tile->area = *area;
+  gtk_rb_tree_node_mark_dirty (tile);
+}
+
+void
+gtk_list_tile_set_area_position (GtkListItemManager *self,
+                                 GtkListTile        *tile,
+                                 int                 x,
+                                 int                 y)
+{
+  if (tile->area.x == x && tile->area.y == y)
+    return;
+
+  tile->area.x = x;
+  tile->area.y = y;
+  gtk_rb_tree_node_mark_dirty (tile);
+}
+
+void
+gtk_list_tile_set_area_size (GtkListItemManager *self,
+                             GtkListTile        *tile,
+                             int                 width,
+                             int                 height)
+{
+  if (tile->area.width == width && tile->area.height == height)
+    return;
+
+  tile->area.width = width;
+  tile->area.height = height;
+  gtk_rb_tree_node_mark_dirty (tile);
+}
+
 static void
 gtk_list_item_tracker_unset_position (GtkListItemManager *self,
                                       GtkListItemTracker *tracker)
index d241412e071db0a794f7e7cc4d64cab75a5fe63c..37a49ce83dce54211fb89347ea2e5cffb2b6f326 100644 (file)
@@ -47,11 +47,15 @@ struct _GtkListTile
 {
   GtkWidget *widget;
   guint n_items;
+  /* area occupied by tile. May be empty if tile has no allcoation */
+  cairo_rectangle_int_t area;
 };
 
 struct _GtkListTileAugment
 {
   guint n_items;
+  /* union of all areas of tile and children */
+  cairo_rectangle_int_t area;
 };
 
 
@@ -81,6 +85,17 @@ guint                   gtk_list_tile_get_position              (GtkListItemMana
                                                                  GtkListTile            *tile);
 gpointer                gtk_list_tile_get_augment               (GtkListItemManager     *self,
                                                                  GtkListTile            *tile);
+void                    gtk_list_tile_set_area                  (GtkListItemManager     *self,
+                                                                 GtkListTile            *tile,
+                                                                 const cairo_rectangle_int_t *area);
+void                    gtk_list_tile_set_area_position         (GtkListItemManager     *self,
+                                                                 GtkListTile            *tile,
+                                                                 int                     x,
+                                                                 int                     y);
+void                    gtk_list_tile_set_area_size             (GtkListItemManager     *self,
+                                                                 GtkListTile            *tile,
+                                                                 int                     width,
+                                                                 int                     height);
 
 void                    gtk_list_item_manager_set_factory       (GtkListItemManager     *self,
                                                                  GtkListItemFactory     *factory);
index 4fc1b6ad3b97330bf1b0fd25170ceb280f8fae1d..0c3aa5db1f6af5086b011c6ca3251c2d6c18c603 100644 (file)
@@ -589,7 +589,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
   ListRow *row;
   GArray *heights;
   int min, nat, row_height;
-  int x, y;
+  int x, y, y0;
   GtkOrientation orientation, opposite_orientation;
   GtkScrollablePolicy scroll_policy, opposite_scroll_policy;
 
@@ -635,6 +635,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
       if (row->height != row_height)
         {
           row->height = row_height;
+          gtk_list_tile_set_area_size (self->item_manager, &row->parent, self->list_width, row_height);
           gtk_rb_tree_node_mark_dirty (row);
         }
       g_array_append_val (heights, row_height);
@@ -654,6 +655,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
       if (row->height != row_height)
         {
           row->height = row_height;
+          gtk_list_tile_set_area_size (self->item_manager, &row->parent, self->list_width, row_height * row->parent.n_items);
           gtk_rb_tree_node_mark_dirty (row);
         }
     }
@@ -667,6 +669,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
                                     &x, &y);
   x = -x;
   y = -y;
+  y0 = y;
 
   /* step 4: actually allocate the widgets */
 
@@ -674,19 +677,12 @@ gtk_list_view_size_allocate (GtkWidget *widget,
        row != NULL;
        row = gtk_rb_tree_node_get_next (row))
     {
-      if (row->parent.widget)
-        {
-          gtk_list_base_size_allocate_child (GTK_LIST_BASE (self),
-                                             row->parent.widget,
-                                             x,
-                                             y,
-                                             self->list_width,
-                                             row->height);
-        }
+      gtk_list_tile_set_area_position (self->item_manager, &row->parent, 0, y - y0);
 
       y += row->height * row->parent.n_items;
     }
 
+  gtk_list_base_allocate_children (GTK_LIST_BASE (self));
   gtk_list_base_allocate_rubberband (GTK_LIST_BASE (self));
 }