summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2023-02-22 23:33:16 +0100
committerBenjamin Otte <otte.benjamin@googlemail.com>2023-03-05 15:23:20 +0000
commit3ca4acdc1a7ba7f1b018834236f41dbbd5b9d2c0 (patch)
treebbad1c66edce2c7f1e8dd5f9e77aa54b39ee9c98
parentc705dba2eef98513763835c644f31ec528df5eeb (diff)
downloadgtk+-3ca4acdc1a7ba7f1b018834236f41dbbd5b9d2c0.tar.gz
list: Allow storing size in the ListTile
... 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.)
-rw-r--r--gtk/gtklistbase.c26
-rw-r--r--gtk/gtklistbaseprivate.h1
-rw-r--r--gtk/gtklistitemmanager.c79
-rw-r--r--gtk/gtklistitemmanagerprivate.h15
-rw-r--r--gtk/gtklistview.c16
5 files changed, 127 insertions, 10 deletions
diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c
index 648d84f446..5b1af350c3 100644
--- a/gtk/gtklistbase.c
+++ b/gtk/gtklistbase.c
@@ -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,
diff --git a/gtk/gtklistbaseprivate.h b/gtk/gtklistbaseprivate.h
index 75d7466784..2d4fb23e1a 100644
--- a/gtk/gtklistbaseprivate.h
+++ b/gtk/gtklistbaseprivate.h
@@ -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,
diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c
index d4b1d3b764..f5bf4aa999 100644
--- a/gtk/gtklistitemmanager.c
+++ b/gtk/gtklistitemmanager.c
@@ -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)
diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h
index d241412e07..37a49ce83d 100644
--- a/gtk/gtklistitemmanagerprivate.h
+++ b/gtk/gtklistitemmanagerprivate.h
@@ -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);
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 4fc1b6ad3b..0c3aa5db1f 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -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));
}