* use the %GTK_ACCESSIBLE_ROLE_GRID_CELL role.
*/
-typedef struct _Cell Cell;
-typedef struct _CellAugment CellAugment;
-
struct _GtkGridView
{
GtkListBase parent_instance;
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,
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;
- }
-}
-
-/*<private>
- * 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,
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;
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;
}
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,
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);
{
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;
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);
measured = TRUE;
}
- i += cell->parent.n_items;
+ i += tile->n_items;
if (i >= n_columns)
{
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,
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;
/* 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));
}
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;