... 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_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,
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,
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,
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)
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);
}
}
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)
{
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;
};
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);
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;
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);
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);
}
}
&x, &y);
x = -x;
y = -y;
+ y0 = y;
/* step 4: actually allocate the widgets */
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));
}