From: Benjamin Otte Date: Wed, 1 Mar 2023 14:25:17 +0000 (+0100) Subject: gridview: Redo tile management X-Git-Tag: archive/raspbian/4.12.3+ds-1+rpi1~1^2^2^2~22^2~5^2~61^2~11 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=776910d03d2b28a6e90b47f60e15a229316b5a8f;p=gtk4.git gridview: Redo tile management Instead of the custom size property, use the new tile size. Also introduce the ability to split tiles, so that gridview can split a layout that would look like (question mark denoting cells without a widget, which in this case would be a single tile) █ █ █ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? into 3 rectangular tiles like so: █ █ █ A A B B B B B B B B B B C C C This of course also means we need to be able to merge those tiles again when cells got added/deleted or the gridview was resized. For that job, gtk_list_tile_gc() exists now, which removes tiles without items and merges adjacent tiles without widgets. --- diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c index 9109c104b1..4980bed78d 100644 --- a/gtk/gtkgridview.c +++ b/gtk/gtkgridview.c @@ -82,9 +82,6 @@ * use the %GTK_ACCESSIBLE_ROLE_GRID_CELL role. */ -typedef struct _Cell Cell; -typedef struct _CellAugment CellAugment; - struct _GtkGridView { GtkListBase parent_instance; @@ -103,18 +100,6 @@ struct _GtkGridViewClass GtkListBaseClass parent_class; }; -struct _Cell -{ - GtkListTile parent; - guint size; /* total, only counting cells in first column */ -}; - -struct _CellAugment -{ - GtkListTileAugment parent; - guint size; /* total, only counting first column */ -}; - enum { PROP_0, @@ -141,176 +126,32 @@ static guint signals[LAST_SIGNAL] = { 0 }; static void G_GNUC_UNUSED dump (GtkGridView *self) { - Cell *cell; + GtkListTile *tile; guint n_widgets, n_list_rows, n_items; n_widgets = 0; n_list_rows = 0; n_items = 0; //g_print ("ANCHOR: %u - %u\n", self->anchor_start, self->anchor_end); - for (cell = gtk_list_item_manager_get_first (self->item_manager); - cell; - cell = gtk_rb_tree_node_get_next (cell)) + for (tile = gtk_list_item_manager_get_first (self->item_manager); + tile; + tile = gtk_rb_tree_node_get_next (tile)) { - if (cell->parent.widget) + if (tile->widget) n_widgets++; n_list_rows++; - n_items += cell->parent.n_items; - g_print ("%6u%6u %5ux%3u %s (%upx)\n", - cell->parent.n_items, n_items, + n_items += tile->n_items; + g_print ("%6u%6u %5ux%3u %s (%d,%d,%d,%d)\n", + tile->n_items, n_items, n_items / (self->n_columns ? self->n_columns : self->min_columns), n_items % (self->n_columns ? self->n_columns : self->min_columns), - cell->parent.widget ? " (widget)" : "", cell->size); + tile->widget ? " (widget)" : "", + tile->area.x, tile->area.y, tile->area.width, tile->area.height); } g_print (" => %u widgets in %u list rows\n", n_widgets, n_list_rows); } -static void -cell_augment (GtkRbTree *tree, - gpointer node_augment, - gpointer node, - gpointer left, - gpointer right) -{ - Cell *cell = node; - CellAugment *aug = node_augment; - - gtk_list_item_manager_augment_node (tree, node_augment, node, left, right); - - aug->size = cell->size; - - if (left) - { - CellAugment *left_aug = gtk_rb_tree_get_augment (tree, left); - - aug->size += left_aug->size; - } - - if (right) - { - CellAugment *right_aug = gtk_rb_tree_get_augment (tree, right); - - aug->size += right_aug->size; - } -} - -/* - * gtk_grid_view_get_cell_at_y: - * @self: a `GtkGridView` - * @y: an offset in direction of @self's orientation - * @position: (out caller-allocates) (optional): stores the position - * index of the returned row - * @offset: (out caller-allocates) (optional): stores the offset - * in pixels between y and top of cell. - * @size: (out caller-allocates) (optional): stores the height - * of the cell - * - * Gets the Cell that occupies the leftmost position in the row at offset - * @y into the primary direction. - * - * If y is larger than the height of all cells, %NULL will be returned. - * In particular that means that for an empty grid, %NULL is returned - * for any value. - * - * Returns: (nullable): The first cell at offset y - **/ -static Cell * -gtk_grid_view_get_cell_at_y (GtkGridView *self, - int y, - guint *position, - int *offset, - int *size) -{ - Cell *cell, *tmp; - guint pos; - - cell = gtk_list_item_manager_get_root (self->item_manager); - pos = 0; - - while (cell) - { - tmp = gtk_rb_tree_node_get_left (cell); - if (tmp) - { - CellAugment *aug = (CellAugment *) gtk_list_tile_get_augment (self->item_manager, &tmp->parent); - if (y < aug->size) - { - cell = tmp; - continue; - } - y -= aug->size; - pos += aug->parent.n_items; - } - - if (y < cell->size) - break; - y -= cell->size; - pos += cell->parent.n_items; - - cell = gtk_rb_tree_node_get_right (cell); - } - - if (cell == NULL) - { - if (position) - *position = 0; - if (offset) - *offset = 0; - if (size) - *size = 0; - return NULL; - } - - /* We know have the (range of) cell(s) that contains this offset. - * Now for the hard part of computing which index this actually is. - */ - if (offset || position || size) - { - guint n_items = cell->parent.n_items; - guint no_widget_rows, skip; - - /* skip remaining items at end of row */ - if (pos % self->n_columns) - { - skip = self->n_columns - pos % self->n_columns; - if (n_items <= skip) - { - g_warning ("ran out of items"); - if (position) - *position = 0; - if (offset) - *offset = 0; - if (size) - *size = 0; - return NULL; - } - n_items -= skip; - pos += skip; - } - - /* Skip all the rows this index doesn't go into */ - no_widget_rows = (n_items - 1) / self->n_columns; - skip = MIN (y / self->unknown_row_height, no_widget_rows); - y -= skip * self->unknown_row_height; - pos += self->n_columns * skip; - - if (position) - *position = pos; - if (offset) - *offset = y; - if (size) - { - if (skip < no_widget_rows) - *size = self->unknown_row_height; - else - *size = cell->size - no_widget_rows * self->unknown_row_height; - } - } - - return cell; -} - static gboolean gtk_grid_view_get_allocation_along (GtkListBase *base, guint pos, @@ -318,75 +159,23 @@ gtk_grid_view_get_allocation_along (GtkListBase *base, int *size) { GtkGridView *self = GTK_GRID_VIEW (base); - Cell *cell, *tmp; - int y; + GtkListTile *tile; + guint remaining; - cell = gtk_list_item_manager_get_root (self->item_manager); - y = 0; - pos -= pos % self->n_columns; - - while (cell) + tile = gtk_list_item_manager_get_nth (self->item_manager, pos, &remaining); + if (tile->n_items <= self->n_columns) { - tmp = gtk_rb_tree_node_get_left (cell); - if (tmp) - { - CellAugment *aug = (CellAugment *) gtk_list_tile_get_augment (self->item_manager, &tmp->parent); - if (pos < aug->parent.n_items) - { - cell = tmp; - continue; - } - pos -= aug->parent.n_items; - y += aug->size; - } - - if (pos < cell->parent.n_items) - break; - y += cell->size; - pos -= cell->parent.n_items; - - cell = gtk_rb_tree_node_get_right (cell); - } - - if (cell == NULL) - { - if (offset) - *offset = 0; - if (size) - *size = 0; - return FALSE; + *offset = tile->area.y; + *size = tile->area.height; } - - /* We know have the (range of) cell(s) that contains this offset. - * Now for the hard part of computing which index this actually is. - */ - if (offset || size) + else { - guint n_items = cell->parent.n_items; - guint skip; - - /* skip remaining items at end of row */ - if (pos % self->n_columns) - { - skip = pos % self->n_columns; - n_items -= skip; - pos -= skip; - } - - /* Skip all the rows this index doesn't go into */ - skip = pos / self->n_columns; - n_items -= skip * self->n_columns; - y += skip * self->unknown_row_height; + guint rows_in_tile = tile->n_items / self->n_columns; + guint row_height = tile->area.height / rows_in_tile; + guint row_index = remaining / self->n_columns; - if (offset) - *offset = y; - if (size) - { - if (n_items > self->n_columns) - *size = self->unknown_row_height; - else - *size = cell->size - skip * self->unknown_row_height; - } + *offset = tile->area.y + row_index * row_height; + *size = row_height; } return TRUE; @@ -412,63 +201,83 @@ gtk_grid_view_get_allocation_across (GtkListBase *base, return TRUE; } -static int -gtk_grid_view_compute_total_height (GtkGridView *self) -{ - Cell *cell; - CellAugment *aug; - - cell = gtk_list_item_manager_get_root (self->item_manager); - if (cell == NULL) - return 0; - aug = (CellAugment *) gtk_list_tile_get_augment (self->item_manager, &cell->parent); - return aug->size; -} - static gboolean gtk_grid_view_get_position_from_allocation (GtkListBase *base, - int across, - int along, + int x, + int y, guint *position, cairo_rectangle_int_t *area) { GtkGridView *self = GTK_GRID_VIEW (base); - int offset, size; - guint pos, n_items; + GtkListTile *tile; + guint pos; + GdkRectangle bounds; - if (across >= self->column_width * self->n_columns) + gtk_list_item_manager_get_tile_bounds (self->item_manager, &bounds); + if (bounds.width <= 0 || bounds.height <= 0) return FALSE; + x = CLAMP (x, bounds.x, bounds.x + bounds.width - 1); + y = CLAMP (y, bounds.y, bounds.y + bounds.height - 1); - n_items = gtk_list_base_get_n_items (base); - along = CLAMP (along, 0, gtk_grid_view_compute_total_height (self) - 1); - across = across < 0 ? 0 : across; - - if (!gtk_grid_view_get_cell_at_y (self, - along, - &pos, - &offset, - &size)) + tile = gtk_list_item_manager_get_tile_at (self->item_manager, x, y); + if (tile == NULL) return FALSE; - pos += floor (across / self->column_width); - - if (pos >= n_items) + while (tile && tile->n_items == 0) + tile = gtk_rb_tree_node_get_previous (tile); + if (tile == NULL) { - /* Ugh, we're in the last row and don't have enough items - * to fill the row. - * Do it the hard way then... */ - pos = n_items - 1; + tile = gtk_list_item_manager_get_first (self->item_manager); + while (tile && tile->n_items == 0) + tile = gtk_rb_tree_node_get_next (tile); + if (tile == NULL) + return FALSE; } - *position = pos; - if (area) + pos = gtk_list_tile_get_position (self->item_manager, tile); + if (tile->n_items > 1) + { + /* offset in x direction */ + pos += (x - tile->area.x) / self->column_width; + if (area) + { + guint col = MIN (x / self->column_width, self->n_columns - 1); + area->x = ceil (col * self->column_width); + area->width = ceil ((col + 1) * self->column_width) - area->x; + } + + /* offset in y direction */ + if (tile->n_items > self->n_columns) + { + guint rows_in_tile = tile->n_items / self->n_columns; + guint row_height = tile->area.height / rows_in_tile; + guint row_index = (y - tile->area.y) / row_height; + pos += self->n_columns * row_index; + + if (area) + { + area->y = tile->area.y + row_index * row_height; + area->height = row_height; + } + } + else + { + if (area) + { + area->y = tile->area.y; + area->height = tile->area.height; + } + } + + } + else { - area->x = ceil (self->column_width * (pos % self->n_columns)); - area->width = ceil (self->column_width * (1 + pos % self->n_columns)) - area->x; - area->y = along - offset; - area->height = size; + if (area) + *area = tile->area; } + *position = pos; + return TRUE; } @@ -477,24 +286,24 @@ gtk_grid_view_get_items_in_rect (GtkListBase *base, const GdkRectangle *rect) { GtkGridView *self = GTK_GRID_VIEW (base); - guint first_row, last_row, first_column, last_column, n_items; + guint first_row, last_row, first_column, last_column; + GdkRectangle bounds; GtkBitset *result; result = gtk_bitset_new_empty (); - if (rect->y >= gtk_grid_view_compute_total_height (self)) + /* limit rect to the region that actually overlaps items */ + gtk_list_item_manager_get_tile_bounds (self->item_manager, &bounds); + if (!gdk_rectangle_intersect (&bounds, rect, &bounds)) return result; - n_items = gtk_list_base_get_n_items (base); - if (n_items == 0) - return result; - - first_column = fmax (floor (rect->x / self->column_width), 0); - last_column = fmin (floor ((rect->x + rect->width) / self->column_width), self->n_columns - 1); - if (!gtk_grid_view_get_cell_at_y (self, rect->y, &first_row, NULL, NULL)) - first_row = rect->y < 0 ? 0 : n_items - 1; - if (!gtk_grid_view_get_cell_at_y (self, rect->y + rect->height, &last_row, NULL, NULL)) - last_row = rect->y + rect->height < 0 ? 0 : n_items - 1; + first_column = fmax (floor (bounds.x / self->column_width), 0); + last_column = fmin (floor ((bounds.x + bounds.width) / self->column_width), self->n_columns - 1); + /* match y = 0 here because we care about the rows, not the cells */ + if (!gtk_grid_view_get_position_from_allocation (base, 0, bounds.y, &first_row, NULL)) + g_return_val_if_reached (result); + if (!gtk_grid_view_get_position_from_allocation (base, 0, bounds.y + bounds.height - 1, &last_row, NULL)) + g_return_val_if_reached (result); gtk_bitset_add_rectangle (result, first_row + first_column, @@ -570,22 +379,21 @@ gtk_grid_view_measure_column_size (GtkGridView *self, int *natural) { GtkOrientation opposite; - Cell *cell; + GtkListTile *tile; int min, nat, child_min, child_nat; min = 0; nat = 0; opposite = gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self)); - for (cell = gtk_list_item_manager_get_first (self->item_manager); - cell != NULL; - cell = gtk_rb_tree_node_get_next (cell)) + for (tile = gtk_list_item_manager_get_first (self->item_manager); + tile != NULL; + tile = gtk_rb_tree_node_get_next (tile)) { - /* ignore unavailable cells */ - if (cell->parent.widget == NULL) + if (tile->widget == NULL) continue; - gtk_widget_measure (cell->parent.widget, + gtk_widget_measure (tile->widget, opposite, -1, &child_min, &child_nat, NULL, NULL); min = MAX (min, child_min); @@ -640,7 +448,7 @@ gtk_grid_view_measure_list (GtkWidget *widget, { GtkGridView *self = GTK_GRID_VIEW (widget); GtkScrollablePolicy scroll_policy; - Cell *cell; + GtkListTile *tile; int height, row_height, child_min, child_nat, column_size, col_min, col_nat; gboolean measured; GArray *heights; @@ -660,13 +468,13 @@ gtk_grid_view_measure_list (GtkWidget *widget, i = 0; row_height = 0; measured = FALSE; - for (cell = gtk_list_item_manager_get_first (self->item_manager); - cell != NULL; - cell = gtk_rb_tree_node_get_next (cell)) + for (tile = gtk_list_item_manager_get_first (self->item_manager); + tile != NULL; + tile = gtk_rb_tree_node_get_next (tile)) { - if (cell->parent.widget) + if (tile->widget) { - gtk_widget_measure (cell->parent.widget, + gtk_widget_measure (tile->widget, gtk_list_base_get_orientation (GTK_LIST_BASE (self)), column_size, &child_min, &child_nat, NULL, NULL); @@ -677,7 +485,7 @@ gtk_grid_view_measure_list (GtkWidget *widget, measured = TRUE; } - i += cell->parent.n_items; + i += tile->n_items; if (i >= n_columns) { @@ -731,17 +539,6 @@ gtk_grid_view_measure (GtkWidget *widget, gtk_grid_view_measure_across (widget, for_size, minimum, natural); } -static void -cell_set_size (Cell *cell, - guint size) -{ - if (cell->size == size) - return; - - cell->size = size; - gtk_rb_tree_node_mark_dirty (cell); -} - static void gtk_grid_view_size_allocate (GtkWidget *widget, int width, @@ -749,12 +546,12 @@ gtk_grid_view_size_allocate (GtkWidget *widget, int baseline) { GtkGridView *self = GTK_GRID_VIEW (widget); - Cell *cell, *start; + GtkListTile *tile, *start; GArray *heights; int min_row_height, row_height, col_min, col_nat; GtkOrientation orientation, opposite_orientation; GtkScrollablePolicy scroll_policy; - gboolean known; + GdkRectangle bounds; int x, y; guint i; @@ -781,147 +578,120 @@ gtk_grid_view_size_allocate (GtkWidget *widget, /* step 2: determine height of known rows */ heights = g_array_new (FALSE, FALSE, sizeof (int)); - i = 0; - row_height = 0; - start = NULL; - for (cell = gtk_list_item_manager_get_first (self->item_manager); - cell != NULL; - cell = gtk_rb_tree_node_get_next (cell)) + for (tile = gtk_list_tile_gc (self->item_manager, gtk_list_item_manager_get_first (self->item_manager)); + tile != NULL; + tile = gtk_list_tile_gc (self->item_manager, tile)) { - if (i == 0) - start = cell; - - if (cell->parent.widget) + /* if it's a multirow tile, handle it here */ + if (tile->n_items > 1 && tile->n_items >= self->n_columns) { - int min, nat, size; - gtk_widget_measure (cell->parent.widget, - gtk_list_base_get_orientation (GTK_LIST_BASE (self)), - self->column_width, - &min, &nat, NULL, NULL); - if (scroll_policy == GTK_SCROLL_MINIMUM) - size = min; - else - size = nat; - size = MAX (size, min_row_height); - g_array_append_val (heights, size); - row_height = MAX (row_height, size); + if (tile->n_items % self->n_columns) + gtk_list_tile_split (self->item_manager, tile, tile->n_items / self->n_columns * self->n_columns); + tile = gtk_rb_tree_node_get_next (tile); + continue; } - cell_set_size (cell, 0); - i += cell->parent.n_items; - if (i >= self->n_columns) - { - i %= self->n_columns; + /* Not a multirow tile */ + i = 0; + row_height = 0; - cell_set_size (start, start->size + row_height); - start = cell; - row_height = 0; + for (i = 0, start = tile; + i < self->n_columns && tile != NULL; + tile = gtk_list_tile_gc (self->item_manager, gtk_rb_tree_node_get_next (tile))) + { + if (tile->widget) + { + int min, nat, size; + gtk_widget_measure (tile->widget, + gtk_list_base_get_orientation (GTK_LIST_BASE (self)), + self->column_width, + &min, &nat, NULL, NULL); + if (scroll_policy == GTK_SCROLL_MINIMUM) + size = min; + else + size = nat; + size = MAX (size, min_row_height); + g_array_append_val (heights, size); + row_height = MAX (row_height, size); + } + if (tile->n_items > self->n_columns - i) + gtk_list_tile_split (self->item_manager, tile, self->n_columns - i); + i += tile->n_items; + } + if (row_height > 0) + { + for (i = 0; + start != tile; + start = gtk_rb_tree_node_get_next (start)) + { + gtk_list_tile_set_area_size (self->item_manager, + start, + ceil (self->column_width * (i + start->n_items)) - ceil (self->column_width * i), + row_height); + i += start->n_items; + } + g_assert (i <= self->n_columns); } } - if (i > 0) - cell_set_size (start, start->size + row_height); /* step 3: determine height of rows with only unknown items */ self->unknown_row_height = gtk_grid_view_get_unknown_row_size (self, heights); g_array_free (heights, TRUE); + /* step 4: determine height for remaining rows and set each row's position */ + y = 0; i = 0; - known = FALSE; - for (start = cell = gtk_list_item_manager_get_first (self->item_manager); - cell != NULL; - cell = gtk_rb_tree_node_get_next (cell)) + for (tile = gtk_list_item_manager_get_first (self->item_manager); + tile != NULL; + tile = gtk_rb_tree_node_get_next (tile)) { - if (i == 0) - start = cell; - - if (cell->parent.widget) - known = TRUE; - - i += cell->parent.n_items; - if (i >= self->n_columns) + gtk_list_tile_set_area_position (self->item_manager, + tile, + ceil (self->column_width * i), + y); + if (tile->n_items >= self->n_columns && tile->widget == NULL) { - if (!known) - cell_set_size (start, start->size + self->unknown_row_height); - - i -= self->n_columns; - known = FALSE; - - if (i >= self->n_columns) + g_assert (i == 0); + g_assert (tile->n_items % self->n_columns == 0); + gtk_list_tile_set_area_size (self->item_manager, + tile, + ceil (self->column_width * self->n_columns), + self->unknown_row_height * (tile->n_items / self->n_columns)); + y += tile->area.height; + } + else + { + if (tile->area.height == 0) { - cell_set_size (cell, cell->size + self->unknown_row_height * (i / self->n_columns)); - i %= self->n_columns; + /* this case is for the last row - it may not be a full row so it won't + * be a multirow tile but it may have no widgets either */ + gtk_list_tile_set_area_size (self->item_manager, + tile, + ceil (self->column_width * (i + tile->n_items)) - ceil (self->column_width * i), + self->unknown_row_height); } - start = cell; + i += tile->n_items; + } + + if (i >= self->n_columns) + { + g_assert (i == self->n_columns); + y += tile->area.height; + i = 0; } } - if (i > 0 && !known) - cell_set_size (start, start->size + self->unknown_row_height); + + gtk_list_item_manager_get_tile_bounds (self->item_manager, &bounds); /* step 4: update the adjustments */ gtk_list_base_update_adjustments (GTK_LIST_BASE (self), - self->column_width * self->n_columns, - gtk_grid_view_compute_total_height (self), + bounds.width, + bounds.height, gtk_widget_get_size (widget, opposite_orientation), gtk_widget_get_size (widget, orientation), &x, &y); - /* step 5: run the size_allocate loop */ - x = -x; - y = -y; - i = 0; - row_height = 0; - - g_assert (self->n_columns > 0); - - for (cell = gtk_list_item_manager_get_first (self->item_manager); - cell != NULL; - cell = gtk_rb_tree_node_get_next (cell)) - { - if (cell->parent.widget) - { - row_height += cell->size; - - gtk_list_base_size_allocate_child (GTK_LIST_BASE (self), - cell->parent.widget, - x + ceil (self->column_width * i), - y, - ceil (self->column_width * (i + 1)) - ceil (self->column_width * i), - row_height); - i++; - if (i >= self->n_columns) - { - y += row_height; - i -= self->n_columns; - row_height = 0; - } - } - else - { - i += cell->parent.n_items; - /* skip remaining row if we didn't start one */ - if (i > cell->parent.n_items && i >= self->n_columns) - { - i -= self->n_columns; - y += row_height; - row_height = 0; - } - - row_height += cell->size; - - /* skip rows that are completely contained by this cell */ - if (i >= self->n_columns) - { - guint unknown_rows, unknown_height; - - unknown_rows = i / self->n_columns; - unknown_height = unknown_rows * self->unknown_row_height; - row_height -= unknown_height; - y += unknown_height; - i %= self->n_columns; - g_assert (row_height >= 0); - } - } - } + gtk_list_base_allocate_children (GTK_LIST_BASE (widget)); gtk_list_base_allocate_rubberband (GTK_LIST_BASE (widget)); } @@ -1043,9 +813,9 @@ gtk_grid_view_class_init (GtkGridViewClass *klass) list_base_class->list_item_name = "child"; list_base_class->list_item_role = GTK_ACCESSIBLE_ROLE_GRID_CELL; - list_base_class->list_item_size = sizeof (Cell); - list_base_class->list_item_augment_size = sizeof (CellAugment); - list_base_class->list_item_augment_func = cell_augment; + list_base_class->list_item_size = sizeof (GtkListTile); + list_base_class->list_item_augment_size = sizeof (GtkListTileAugment); + list_base_class->list_item_augment_func = gtk_list_item_manager_augment_node; list_base_class->get_allocation_along = gtk_grid_view_get_allocation_along; list_base_class->get_allocation_across = gtk_grid_view_get_allocation_across; list_base_class->get_items_in_rect = gtk_grid_view_get_items_in_rect; diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c index 09f8e89a03..608a9fd76f 100644 --- a/gtk/gtklistitemmanager.c +++ b/gtk/gtklistitemmanager.c @@ -158,6 +158,24 @@ gtk_list_item_manager_new_for_size (GtkWidget *widget, return self; } +void +gtk_list_item_manager_get_tile_bounds (GtkListItemManager *self, + GdkRectangle *out_bounds) +{ + GtkListTile *tile; + GtkListTileAugment *aug; + + tile = gtk_rb_tree_get_root (self->items); + if (tile == NULL) + { + *out_bounds = &(GdkRectangle) { 0, 0, 0, 0 }; + return; + } + + aug = gtk_rb_tree_get_augment (self->items, tile); + *out_bounds = aug->area; +} + gpointer gtk_list_item_manager_get_first (GtkListItemManager *self) { @@ -553,6 +571,81 @@ gtk_list_item_manager_merge_list_items (GtkListItemManager *self, return TRUE; } +/* + * gtk_list_tile_split: + * @self: the listitemmanager + * @tile: a tile to split into two + * @n_items: nuber of items to keep in tile + * + * Splits the given tile into two tiles. The original + * tile will remain with @n_items items, the remaining + * items will be given to the new tile, which will be + * nserted after the tile. + * + * It is valid for either tile to have 0 items after + * the split. + * + * Returns: The new tile + **/ +GtkListTile * +gtk_list_tile_split (GtkListItemManager *self, + GtkListTile *tile, + guint n_items) +{ + GtkListTile *result; + + g_assert (n_items <= tile->n_items); + + result = gtk_rb_tree_insert_after (self->items, tile); + result->n_items = tile->n_items - n_items; + tile->n_items = n_items; + gtk_rb_tree_node_mark_dirty (tile); + + return result; +} + +/* + * gtk_list_tile_gc: + * @self: the listitemmanager + * @tile: a tile + * + * Tries to get rid of tiles when they aren't needed anymore, + * either because their referenced listitems were deleted or + * because they can be merged with the next item(s). + * + * Note that this only looks forward, but never backward. + * + * Returns: The next tile + **/ +GtkListTile * +gtk_list_tile_gc (GtkListItemManager *self, + GtkListTile *tile) +{ + GtkListTile *next; + + while (tile) + { + next = gtk_rb_tree_node_get_next (tile); + + if (tile->n_items == 0) + { + gtk_rb_tree_remove (self->items, tile); + tile = next; + continue; + } + + if (next == NULL) + break; + + if (gtk_list_item_manager_merge_list_items (self, tile, next)) + continue; + + break; + } + + return tile; +} + static void gtk_list_item_manager_release_items (GtkListItemManager *self, GQueue *released) diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h index 0977c9c92c..d5ee20fc6a 100644 --- a/gtk/gtklistitemmanagerprivate.h +++ b/gtk/gtklistitemmanagerprivate.h @@ -75,6 +75,8 @@ void gtk_list_item_manager_augment_node (GtkRbTree gpointer node, gpointer left, gpointer right); +void gtk_list_item_manager_get_tile_bounds (GtkListItemManager *self, + GdkRectangle *out_bounds); gpointer gtk_list_item_manager_get_root (GtkListItemManager *self); gpointer gtk_list_item_manager_get_first (GtkListItemManager *self); gpointer gtk_list_item_manager_get_nth (GtkListItemManager *self, @@ -100,6 +102,12 @@ void gtk_list_tile_set_area_size (GtkListItemMana int width, int height); +GtkListTile * gtk_list_tile_split (GtkListItemManager *self, + GtkListTile *tile, + guint n_items); +GtkListTile * gtk_list_tile_gc (GtkListItemManager *self, + GtkListTile *tile); + void gtk_list_item_manager_set_factory (GtkListItemManager *self, GtkListItemFactory *factory); GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self);