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)); }