Merge branch 'wip/otte/listview-border-spacing' into 'main'
Implement CSS border-spacing support for listview and gridview See merge request GNOME/gtk!5626
This commit is contained in:
@@ -151,6 +151,30 @@ dump (GtkGridView *self)
|
||||
g_print (" => %u widgets in %u list rows\n", n_widgets, n_list_rows);
|
||||
}
|
||||
|
||||
static int
|
||||
column_index (GtkGridView *self,
|
||||
int spacing,
|
||||
int x)
|
||||
{
|
||||
return (x + spacing / 2.0) / (self->column_width + spacing);
|
||||
}
|
||||
|
||||
static int
|
||||
column_start (GtkGridView *self,
|
||||
int spacing,
|
||||
int col)
|
||||
{
|
||||
return ceil ((self->column_width + spacing) * col);
|
||||
}
|
||||
|
||||
static int
|
||||
column_end (GtkGridView *self,
|
||||
int spacing,
|
||||
int col)
|
||||
{
|
||||
return ceil (self->column_width * (col + 1) + (spacing * col));
|
||||
}
|
||||
|
||||
static GtkListTile *
|
||||
gtk_grid_view_split (GtkListBase *base,
|
||||
GtkListTile *tile,
|
||||
@@ -159,8 +183,11 @@ gtk_grid_view_split (GtkListBase *base,
|
||||
GtkGridView *self = GTK_GRID_VIEW (base);
|
||||
GtkListTile *split;
|
||||
guint col, row_height;
|
||||
int xspacing, yspacing;
|
||||
|
||||
row_height = tile->area.height / MAX (tile->n_items / self->n_columns, 1);
|
||||
gtk_list_base_get_border_spacing (base, &xspacing, &yspacing);
|
||||
|
||||
row_height = (tile->area.height + yspacing) / MAX (tile->n_items / self->n_columns, 1) - yspacing;
|
||||
|
||||
/* split off the multirow at the top */
|
||||
if (n_items >= self->n_columns)
|
||||
@@ -174,14 +201,14 @@ gtk_grid_view_split (GtkListBase *base,
|
||||
tile,
|
||||
&(GdkRectangle) {
|
||||
split->area.x,
|
||||
split->area.y + row_height * top_rows,
|
||||
split->area.y + (row_height + yspacing) * top_rows,
|
||||
split->area.width,
|
||||
split->area.height - row_height * top_rows,
|
||||
split->area.height - (row_height + yspacing) * top_rows,
|
||||
});
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
split,
|
||||
split->area.width,
|
||||
row_height * top_rows);
|
||||
row_height * top_rows + yspacing * (top_rows - 1));
|
||||
n_items -= top_items;
|
||||
if (n_items == 0)
|
||||
return tile;
|
||||
@@ -195,9 +222,9 @@ gtk_grid_view_split (GtkListBase *base,
|
||||
split,
|
||||
&(GdkRectangle) {
|
||||
tile->area.x,
|
||||
tile->area.y + row_height,
|
||||
tile->area.y + row_height + yspacing,
|
||||
tile->area.width,
|
||||
tile->area.height - row_height,
|
||||
tile->area.height - row_height - yspacing,
|
||||
});
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
tile,
|
||||
@@ -209,19 +236,20 @@ gtk_grid_view_split (GtkListBase *base,
|
||||
g_assert (tile->n_items <= self->n_columns);
|
||||
|
||||
/* now it's a single row, do a split at the column boundary */
|
||||
col = tile->area.x / self->column_width;
|
||||
col = column_index (self, xspacing, tile->area.x);
|
||||
split = gtk_list_tile_split (self->item_manager, tile, n_items);
|
||||
gtk_list_tile_set_area (self->item_manager,
|
||||
split,
|
||||
&(GdkRectangle) {
|
||||
ceil ((col + n_items) * self->column_width),
|
||||
column_start (self, xspacing, col + n_items),
|
||||
tile->area.y,
|
||||
ceil ((col + n_items + split->n_items) * self->column_width),
|
||||
column_end (self, xspacing, col + n_items + split->n_items - 1)
|
||||
- column_start (self, xspacing, col + n_items),
|
||||
tile->area.height,
|
||||
});
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
tile,
|
||||
ceil ((col + n_items) * self->column_width) - tile->area.x,
|
||||
column_end (self, xspacing, col + n_items - 1) - tile->area.x,
|
||||
tile->area.height);
|
||||
|
||||
return split;
|
||||
@@ -235,25 +263,61 @@ gtk_grid_view_get_allocation (GtkListBase *base,
|
||||
GtkGridView *self = GTK_GRID_VIEW (base);
|
||||
GtkListTile *tile;
|
||||
guint offset;
|
||||
int xspacing, yspacing;
|
||||
|
||||
tile = gtk_list_item_manager_get_nth (self->item_manager, pos, &offset);
|
||||
if (tile == NULL || tile->area.width <= 0 || tile->area.height <= 0)
|
||||
if (tile == NULL)
|
||||
return FALSE;
|
||||
|
||||
gtk_list_base_get_border_spacing (base, &xspacing, &yspacing);
|
||||
|
||||
if (tile->area.width <= 0 || tile->area.height <= 0)
|
||||
{
|
||||
/* item is not allocated yet */
|
||||
GtkListTile *other;
|
||||
|
||||
for (other = gtk_rb_tree_node_get_previous (tile);
|
||||
other;
|
||||
other = gtk_rb_tree_node_get_previous (other))
|
||||
{
|
||||
if (other->area.width || other->area.height)
|
||||
{
|
||||
area->x = other->area.x + other->area.width;
|
||||
area->y = other->area.y + other->area.height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (other == NULL)
|
||||
{
|
||||
for (other = gtk_rb_tree_node_get_next (tile);
|
||||
other;
|
||||
other = gtk_rb_tree_node_get_next (other))
|
||||
{
|
||||
if (other->area.width || other->area.height)
|
||||
{
|
||||
area->x = other->area.x;
|
||||
area->y = other->area.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*area = tile->area;
|
||||
|
||||
if (tile->n_items > self->n_columns)
|
||||
{
|
||||
area->height /= (tile->n_items / self->n_columns);
|
||||
area->y += (offset / self->n_columns) * area->height;
|
||||
area->height = (area->height + yspacing) / (tile->n_items / self->n_columns) - yspacing;
|
||||
area->y += (offset / self->n_columns) * (area->height + yspacing);
|
||||
offset %= self->n_columns;
|
||||
}
|
||||
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
guint col = area->x / self->column_width;
|
||||
area->x = ceil ((col + offset) * self->column_width);
|
||||
area->width = ceil ((col + offset + 1) * self->column_width) - area->x;
|
||||
guint col = column_index (self, xspacing, area->x);
|
||||
area->x = column_start (self, xspacing, col + offset);
|
||||
area->width = column_end (self, xspacing, col + offset) - area->x;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -269,15 +333,8 @@ gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
GtkGridView *self = GTK_GRID_VIEW (base);
|
||||
GtkListTile *tile;
|
||||
guint pos;
|
||||
GdkRectangle bounds;
|
||||
|
||||
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);
|
||||
|
||||
tile = gtk_list_item_manager_get_tile_at (self->item_manager, x, y);
|
||||
tile = gtk_list_item_manager_get_nearest_tile (self->item_manager, x, y);
|
||||
if (tile == NULL)
|
||||
return FALSE;
|
||||
|
||||
@@ -295,26 +352,30 @@ gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
pos = gtk_list_tile_get_position (self->item_manager, tile);
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
int xspacing, yspacing;
|
||||
|
||||
gtk_list_base_get_border_spacing (base, &xspacing, &yspacing);
|
||||
|
||||
/* offset in x direction */
|
||||
pos += (x - tile->area.x) / self->column_width;
|
||||
pos += column_index (self, xspacing, MAX (tile->area.width - 1, x - tile->area.x));
|
||||
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;
|
||||
guint col = MIN (column_index (self, xspacing, x), self->n_columns - 1);
|
||||
area->x = column_start (self, xspacing, col);
|
||||
area->width = column_end (self, xspacing, col) - 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;
|
||||
guint row_height = (tile->area.height + yspacing) / rows_in_tile - yspacing;
|
||||
guint row_index = MIN (tile->area.height - 1, y - tile->area.y) / (row_height + yspacing);
|
||||
pos += self->n_columns * row_index;
|
||||
|
||||
if (area)
|
||||
{
|
||||
area->y = tile->area.y + row_index * row_height;
|
||||
area->y = tile->area.y + row_index * (row_height + yspacing);
|
||||
area->height = row_height;
|
||||
}
|
||||
}
|
||||
@@ -345,29 +406,37 @@ gtk_grid_view_get_items_in_rect (GtkListBase *base,
|
||||
{
|
||||
GtkGridView *self = GTK_GRID_VIEW (base);
|
||||
guint first_row, last_row, first_column, last_column;
|
||||
GdkRectangle bounds;
|
||||
cairo_rectangle_int_t area;
|
||||
int xspacing, yspacing;
|
||||
GtkBitset *result;
|
||||
|
||||
gtk_list_base_get_border_spacing (base, &xspacing, &yspacing);
|
||||
result = gtk_bitset_new_empty ();
|
||||
|
||||
/* 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;
|
||||
|
||||
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);
|
||||
first_column = MAX (column_index (self, xspacing, rect->x), 0);
|
||||
if (column_end (self, xspacing, first_column) <= rect->x)
|
||||
first_column++;
|
||||
last_column = MIN (column_index (self, xspacing, rect->x + rect->width), self->n_columns - 1);
|
||||
if (column_start (self, xspacing, last_column) > rect->x + rect->width)
|
||||
last_column--;
|
||||
/* 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))
|
||||
if (!gtk_grid_view_get_position_from_allocation (base, column_start (self, xspacing, 0), rect->y, &first_row, &area))
|
||||
g_return_val_if_reached (result);
|
||||
if (!gtk_grid_view_get_position_from_allocation (base, 0, bounds.y + bounds.height - 1, &last_row, NULL))
|
||||
if (area.y + area.height < rect->y)
|
||||
first_row += self->n_columns;
|
||||
if (!gtk_grid_view_get_position_from_allocation (base, column_start (self, xspacing, 0), rect->y + rect->height, &last_row, NULL))
|
||||
g_return_val_if_reached (result);
|
||||
if (area.y >= rect->y + rect->height)
|
||||
last_row -= self->n_columns;
|
||||
|
||||
gtk_bitset_add_rectangle (result,
|
||||
first_row + first_column,
|
||||
last_column - first_column + 1,
|
||||
(last_row - first_row) / self->n_columns + 1,
|
||||
self->n_columns);
|
||||
if (first_column <= last_column && first_row <= last_row)
|
||||
{
|
||||
gtk_bitset_add_rectangle (result,
|
||||
first_row + first_column,
|
||||
last_column - first_column + 1,
|
||||
(last_row - first_row) / self->n_columns + 1,
|
||||
self->n_columns);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -469,16 +538,20 @@ gtk_grid_view_measure_across (GtkWidget *widget,
|
||||
int *natural)
|
||||
{
|
||||
GtkGridView *self = GTK_GRID_VIEW (widget);
|
||||
int xspacing;
|
||||
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (widget), &xspacing, NULL);
|
||||
|
||||
gtk_grid_view_measure_column_size (self, minimum, natural);
|
||||
|
||||
*minimum *= self->min_columns;
|
||||
*natural *= self->max_columns;
|
||||
*minimum = (*minimum + xspacing) * self->min_columns - xspacing;
|
||||
*natural = (*natural + xspacing) * self->max_columns - xspacing;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_grid_view_compute_n_columns (GtkGridView *self,
|
||||
guint for_size,
|
||||
int border_spacing,
|
||||
int min,
|
||||
int nat)
|
||||
{
|
||||
@@ -487,9 +560,9 @@ gtk_grid_view_compute_n_columns (GtkGridView *self,
|
||||
/* rounding down is exactly what we want here, so int division works */
|
||||
if (gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self),
|
||||
gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self))) == GTK_SCROLL_MINIMUM)
|
||||
n_columns = for_size / MAX (1, min);
|
||||
n_columns = (for_size + border_spacing) / MAX (1, min + border_spacing);
|
||||
else
|
||||
n_columns = for_size / MAX (1, nat);
|
||||
n_columns = (for_size + border_spacing) / MAX (1, nat + border_spacing);
|
||||
|
||||
n_columns = CLAMP (n_columns, self->min_columns, self->max_columns);
|
||||
|
||||
@@ -508,11 +581,13 @@ gtk_grid_view_measure_list (GtkWidget *widget,
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
GtkListTile *tile;
|
||||
int height, row_height, child_min, child_nat, column_size, col_min, col_nat;
|
||||
int xspacing, yspacing;
|
||||
gboolean measured;
|
||||
GArray *heights;
|
||||
guint n_unknown, n_columns;
|
||||
guint i;
|
||||
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (self), &xspacing, &yspacing);
|
||||
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), gtk_list_base_get_orientation (GTK_LIST_BASE (self)));
|
||||
heights = g_array_new (FALSE, FALSE, sizeof (int));
|
||||
n_unknown = 0;
|
||||
@@ -520,7 +595,7 @@ gtk_grid_view_measure_list (GtkWidget *widget,
|
||||
|
||||
gtk_grid_view_measure_column_size (self, &col_min, &col_nat);
|
||||
for_size = MAX (for_size, col_min * (int) self->min_columns);
|
||||
n_columns = gtk_grid_view_compute_n_columns (self, for_size, col_min, col_nat);
|
||||
n_columns = gtk_grid_view_compute_n_columns (self, for_size, xspacing, col_min, col_nat);
|
||||
column_size = for_size / n_columns;
|
||||
|
||||
i = 0;
|
||||
@@ -551,7 +626,7 @@ gtk_grid_view_measure_list (GtkWidget *widget,
|
||||
{
|
||||
g_array_append_val (heights, row_height);
|
||||
i -= n_columns;
|
||||
height += row_height;
|
||||
height += row_height + yspacing;
|
||||
measured = FALSE;
|
||||
row_height = 0;
|
||||
}
|
||||
@@ -565,14 +640,17 @@ gtk_grid_view_measure_list (GtkWidget *widget,
|
||||
if (measured)
|
||||
{
|
||||
g_array_append_val (heights, row_height);
|
||||
height += row_height;
|
||||
height += row_height + yspacing;
|
||||
}
|
||||
else
|
||||
n_unknown++;
|
||||
}
|
||||
|
||||
if (n_unknown)
|
||||
height += n_unknown * gtk_grid_view_get_unknown_row_size (self, heights);
|
||||
height += n_unknown * (gtk_grid_view_get_unknown_row_size (self, heights) + yspacing);
|
||||
/* if we have a height, we have at least one row, and because we added spacing for every row... */
|
||||
if (height)
|
||||
height -= yspacing;
|
||||
|
||||
g_array_free (heights, TRUE);
|
||||
|
||||
@@ -609,12 +687,13 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
int min_row_height, unknown_row_height, row_height, col_min, col_nat;
|
||||
GtkOrientation orientation;
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
int y;
|
||||
int y, xspacing, yspacing;
|
||||
guint i;
|
||||
|
||||
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
|
||||
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), orientation);
|
||||
min_row_height = ceil ((double) height / GTK_GRID_VIEW_MAX_VISIBLE_ROWS);
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (self), &xspacing, &yspacing);
|
||||
|
||||
/* step 0: exit early if list is empty */
|
||||
tile = gtk_list_tile_gc (self->item_manager, gtk_list_item_manager_get_first (self->item_manager));
|
||||
@@ -628,6 +707,7 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
gtk_grid_view_measure_column_size (self, &col_min, &col_nat);
|
||||
self->n_columns = gtk_grid_view_compute_n_columns (self,
|
||||
orientation == GTK_ORIENTATION_VERTICAL ? width : height,
|
||||
xspacing,
|
||||
col_min, col_nat);
|
||||
self->column_width = (orientation == GTK_ORIENTATION_VERTICAL ? width : height) / self->n_columns;
|
||||
self->column_width = MAX (self->column_width, col_min);
|
||||
@@ -683,7 +763,8 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
{
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
start,
|
||||
ceil (self->column_width * (i + start->n_items)) - ceil (self->column_width * i),
|
||||
column_end (self, xspacing, i + start->n_items - 1)
|
||||
- column_start (self, xspacing, i),
|
||||
row_height);
|
||||
i += start->n_items;
|
||||
}
|
||||
@@ -704,7 +785,7 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
{
|
||||
gtk_list_tile_set_area_position (self->item_manager,
|
||||
tile,
|
||||
ceil (self->column_width * i),
|
||||
column_start (self, xspacing, i),
|
||||
y);
|
||||
if (tile->n_items >= self->n_columns && tile->widget == NULL)
|
||||
{
|
||||
@@ -712,9 +793,10 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
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),
|
||||
unknown_row_height * (tile->n_items / self->n_columns));
|
||||
y += tile->area.height;
|
||||
column_end (self, xspacing, self->n_columns - 1)
|
||||
- column_start (self, xspacing, 0),
|
||||
(unknown_row_height + yspacing) * (tile->n_items / self->n_columns) - yspacing);
|
||||
y += tile->area.height + yspacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -724,7 +806,7 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
* 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),
|
||||
column_end (self, xspacing, i + tile->n_items - 1) - tile->area.x,
|
||||
unknown_row_height);
|
||||
}
|
||||
i += tile->n_items;
|
||||
@@ -733,7 +815,7 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
if (i >= self->n_columns)
|
||||
{
|
||||
g_assert (i == self->n_columns);
|
||||
y += tile->area.height;
|
||||
y += tile->area.height + yspacing;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -745,11 +827,11 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
filler = gtk_list_tile_split (self->item_manager, tile, tile->n_items);
|
||||
gtk_list_tile_set_area_position (self->item_manager,
|
||||
filler,
|
||||
ceil (self->column_width * i),
|
||||
column_start (self, xspacing, i),
|
||||
y);
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
filler,
|
||||
ceil (self->column_width * self->n_columns) - filler->area.x,
|
||||
column_end (self, xspacing, self->n_columns - 1) - filler->area.x,
|
||||
tile->area.height);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include "gtkadjustment.h"
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkcsspositionvalueprivate.h"
|
||||
#include "gtkdragsourceprivate.h"
|
||||
#include "gtkdropcontrollermotion.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
@@ -1454,7 +1456,14 @@ static GtkBitset *
|
||||
gtk_list_base_get_items_in_rect (GtkListBase *self,
|
||||
const GdkRectangle *rect)
|
||||
{
|
||||
return GTK_LIST_BASE_GET_CLASS (self)->get_items_in_rect (self, rect);
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GdkRectangle bounds;
|
||||
|
||||
gtk_list_item_manager_get_tile_bounds (priv->item_manager, &bounds);
|
||||
if (!gdk_rectangle_intersect (&bounds, rect, &bounds))
|
||||
return gtk_bitset_new_empty ();
|
||||
|
||||
return GTK_LIST_BASE_GET_CLASS (self)->get_items_in_rect (self, &bounds);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -1988,6 +1997,30 @@ gtk_list_base_get_orientation (GtkListBase *self)
|
||||
return priv->orientation;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_base_get_border_spacing (GtkListBase *self,
|
||||
int *xspacing,
|
||||
int *yspacing)
|
||||
{
|
||||
GtkCssStyle *style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (self)));
|
||||
GtkCssValue *border_spacing = style->size->border_spacing;
|
||||
|
||||
if (gtk_list_base_get_orientation (self) == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
if (xspacing)
|
||||
*xspacing = _gtk_css_position_value_get_y (border_spacing, 0);
|
||||
if (yspacing)
|
||||
*yspacing = _gtk_css_position_value_get_x (border_spacing, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xspacing)
|
||||
*xspacing = _gtk_css_position_value_get_x (border_spacing, 0);
|
||||
if (yspacing)
|
||||
*yspacing = _gtk_css_position_value_get_y (border_spacing, 0);
|
||||
}
|
||||
}
|
||||
|
||||
GtkListItemManager *
|
||||
gtk_list_base_get_manager (GtkListBase *self)
|
||||
{
|
||||
|
||||
@@ -62,6 +62,9 @@ struct _GtkListBaseClass
|
||||
GtkOrientation gtk_list_base_get_orientation (GtkListBase *self);
|
||||
#define gtk_list_base_get_opposite_orientation(self) OPPOSITE_ORIENTATION(gtk_list_base_get_orientation(self))
|
||||
guint gtk_list_base_get_focus_position (GtkListBase *self);
|
||||
void gtk_list_base_get_border_spacing (GtkListBase *self,
|
||||
int *xspacing,
|
||||
int *yspacing);
|
||||
GtkListItemManager * gtk_list_base_get_manager (GtkListBase *self);
|
||||
GtkScrollablePolicy gtk_list_base_get_scroll_policy (GtkListBase *self,
|
||||
GtkOrientation orientation);
|
||||
|
||||
@@ -247,39 +247,94 @@ gtk_list_item_manager_get_nth (GtkListItemManager *self,
|
||||
return tile;
|
||||
}
|
||||
|
||||
/* This computes Manhattan distance */
|
||||
static int
|
||||
rectangle_distance (const cairo_rectangle_int_t *rect,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
int x_dist, y_dist;
|
||||
|
||||
if (rect->x > x)
|
||||
x_dist = rect->x - x;
|
||||
else if (rect->x + rect->width < x)
|
||||
x_dist = x - (rect->x + rect->width);
|
||||
else
|
||||
x_dist = 0;
|
||||
|
||||
if (rect->y > y)
|
||||
y_dist = rect->y - y;
|
||||
else if (rect->y + rect->height < y)
|
||||
y_dist = y - (rect->y + rect->height);
|
||||
else
|
||||
y_dist = 0;
|
||||
|
||||
return x_dist + y_dist;
|
||||
}
|
||||
|
||||
static GtkListTile *
|
||||
gtk_list_tile_get_tile_at (GtkListItemManager *self,
|
||||
GtkListTile *tile,
|
||||
int x,
|
||||
int y)
|
||||
int y,
|
||||
int *distance)
|
||||
{
|
||||
GtkListTileAugment *aug;
|
||||
GtkListTile *subtile;
|
||||
GtkListTile *left, *right, *result;
|
||||
int dist, left_dist, right_dist;
|
||||
|
||||
aug = gtk_list_tile_get_augment (self, tile);
|
||||
if (!gdk_rectangle_contains_point (&aug->area, x, y))
|
||||
return NULL;
|
||||
|
||||
subtile = gtk_rb_tree_node_get_left (tile);
|
||||
if (subtile)
|
||||
left = gtk_rb_tree_node_get_left (tile);
|
||||
if (left)
|
||||
{
|
||||
subtile = gtk_list_tile_get_tile_at (self, subtile, x, y);
|
||||
if (subtile)
|
||||
return subtile;
|
||||
aug = gtk_list_tile_get_augment (self, left);
|
||||
left_dist = rectangle_distance (&aug->area, x, y);
|
||||
}
|
||||
|
||||
if (gdk_rectangle_contains_point (&tile->area, x, y))
|
||||
return tile;
|
||||
|
||||
subtile = gtk_rb_tree_node_get_right (tile);
|
||||
if (subtile)
|
||||
else
|
||||
left_dist = *distance;
|
||||
right = gtk_rb_tree_node_get_right (tile);
|
||||
if (right)
|
||||
{
|
||||
subtile = gtk_list_tile_get_tile_at (self, subtile, x, y);
|
||||
if (subtile)
|
||||
return subtile;
|
||||
aug = gtk_list_tile_get_augment (self, right);
|
||||
right_dist = rectangle_distance (&aug->area, x, y);
|
||||
}
|
||||
else
|
||||
right_dist = *distance;
|
||||
|
||||
return NULL;
|
||||
dist = rectangle_distance (&tile->area, x, y);
|
||||
result = NULL;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
if (dist < left_dist && dist < right_dist)
|
||||
{
|
||||
if (dist >= *distance)
|
||||
return result;
|
||||
|
||||
*distance = dist;
|
||||
return tile;
|
||||
}
|
||||
|
||||
if (left_dist < right_dist)
|
||||
{
|
||||
if (left_dist >= *distance)
|
||||
return result;
|
||||
|
||||
left = gtk_list_tile_get_tile_at (self, left, x, y, distance);
|
||||
if (left)
|
||||
result = left;
|
||||
left_dist = G_MAXINT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (right_dist >= *distance)
|
||||
return result;
|
||||
|
||||
right = gtk_list_tile_get_tile_at (self, right, x, y, distance);
|
||||
if (right)
|
||||
result = right;
|
||||
right_dist = G_MAXINT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -299,7 +354,35 @@ gtk_list_item_manager_get_tile_at (GtkListItemManager *self,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
return gtk_list_tile_get_tile_at (self, gtk_list_item_manager_get_root (self), x, y);
|
||||
int distance = 1;
|
||||
|
||||
return gtk_list_tile_get_tile_at (self, gtk_list_item_manager_get_root (self), x, y, &distance);
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_list_item_manager_get_nearest_tile:
|
||||
* @self: a GtkListItemManager
|
||||
* @x: x coordinate of tile
|
||||
* @y: y coordinate of tile
|
||||
*
|
||||
* Finds the tile closest to the coordinates at (x, y). If no
|
||||
* tile occupies the coordinates (for example, if the tile is out of bounds),
|
||||
* Manhattan distance is used to find the nearest tile.
|
||||
*
|
||||
* If multiple tiles have the same distance, the one closest to the start
|
||||
* will be returned.
|
||||
*
|
||||
* Returns: (nullable): The tile nearest to (x, y) or NULL if there are no
|
||||
* tile
|
||||
**/
|
||||
GtkListTile *
|
||||
gtk_list_item_manager_get_nearest_tile (GtkListItemManager *self,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
int distance = G_MAXINT;
|
||||
|
||||
return gtk_list_tile_get_tile_at (self, gtk_list_item_manager_get_root (self), x, y, &distance);
|
||||
}
|
||||
|
||||
guint
|
||||
|
||||
@@ -78,6 +78,10 @@ gpointer gtk_list_item_manager_get_nth (GtkListItemMana
|
||||
GtkListTile * gtk_list_item_manager_get_tile_at (GtkListItemManager *self,
|
||||
int x,
|
||||
int y);
|
||||
GtkListTile * gtk_list_item_manager_get_nearest_tile (GtkListItemManager *self,
|
||||
int x,
|
||||
int y);
|
||||
|
||||
|
||||
guint gtk_list_tile_get_position (GtkListItemManager *self,
|
||||
GtkListTile *tile);
|
||||
|
||||
@@ -186,20 +186,6 @@ dump (GtkListView *self)
|
||||
g_print (" => %u widgets in %u list rows\n", n_widgets, n_list_rows);
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_list_view_get_list_height (GtkListView *self)
|
||||
{
|
||||
GtkListTile *tile;
|
||||
GtkListTileAugment *aug;
|
||||
|
||||
tile = gtk_list_item_manager_get_root (self->item_manager);
|
||||
if (tile == NULL)
|
||||
return 0;
|
||||
|
||||
aug = gtk_list_tile_get_augment (self->item_manager, tile);
|
||||
return aug->area.height;
|
||||
}
|
||||
|
||||
static GtkListTile *
|
||||
gtk_list_view_split (GtkListBase *base,
|
||||
GtkListTile *tile,
|
||||
@@ -207,22 +193,23 @@ gtk_list_view_split (GtkListBase *base,
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (base);
|
||||
GtkListTile *new_tile;
|
||||
guint row_height;
|
||||
int spacing, row_height;
|
||||
|
||||
row_height = tile->area.height / tile->n_items;
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (self), NULL, &spacing);
|
||||
row_height = (tile->area.height - (tile->n_items - 1) * spacing) / tile->n_items;
|
||||
|
||||
new_tile = gtk_list_tile_split (self->item_manager, tile, n_items);
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
tile,
|
||||
tile->area.width,
|
||||
row_height * tile->n_items);
|
||||
row_height * tile->n_items + spacing * (tile->n_items - 1));
|
||||
gtk_list_tile_set_area (self->item_manager,
|
||||
new_tile,
|
||||
&(GdkRectangle) {
|
||||
tile->area.x,
|
||||
tile->area.y + tile->area.height,
|
||||
tile->area.y + tile->area.height + spacing,
|
||||
tile->area.width,
|
||||
row_height * new_tile->n_items
|
||||
row_height * new_tile->n_items + spacing * (new_tile->n_items - 1)
|
||||
});
|
||||
|
||||
return new_tile;
|
||||
@@ -242,45 +229,51 @@ gtk_list_view_get_allocation (GtkListBase *base,
|
||||
return FALSE;
|
||||
|
||||
*area = tile->area;
|
||||
if (tile->n_items)
|
||||
area->height /= tile->n_items;
|
||||
if (offset)
|
||||
area->y += offset * area->height;
|
||||
|
||||
return area->width > 0 && area->height > 0;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
gtk_list_view_get_items_in_rect (GtkListBase *base,
|
||||
const cairo_rectangle_int_t *rect)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (base);
|
||||
guint first, last, n_items;
|
||||
GtkBitset *result;
|
||||
GtkListTile *tile;
|
||||
|
||||
result = gtk_bitset_new_empty ();
|
||||
|
||||
if (rect->y >= gtk_list_view_get_list_height (self))
|
||||
return result;
|
||||
|
||||
n_items = gtk_list_base_get_n_items (base);
|
||||
if (n_items == 0)
|
||||
return result;
|
||||
|
||||
tile = gtk_list_item_manager_get_tile_at (self->item_manager, 0, rect->y);
|
||||
if (tile)
|
||||
first = gtk_list_tile_get_position (self->item_manager, tile);
|
||||
if (area->width || area->height)
|
||||
{
|
||||
if (tile->n_items)
|
||||
area->height /= tile->n_items;
|
||||
if (offset)
|
||||
area->y += offset * area->height;
|
||||
}
|
||||
else
|
||||
first = rect->y < 0 ? 0 : n_items - 1;
|
||||
tile = gtk_list_item_manager_get_tile_at (self->item_manager, 0, rect->y + rect->height);
|
||||
if (tile)
|
||||
last = gtk_list_tile_get_position (self->item_manager, tile);
|
||||
else
|
||||
last = rect->y + rect->height < 0 ? 0 : n_items - 1;
|
||||
{
|
||||
/* item is not allocated yet */
|
||||
GtkListTile *other;
|
||||
int spacing;
|
||||
|
||||
gtk_bitset_add_range_closed (result, first, last);
|
||||
return result;
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (self), NULL, &spacing);
|
||||
|
||||
for (other = gtk_rb_tree_node_get_previous (tile);
|
||||
other;
|
||||
other = gtk_rb_tree_node_get_previous (other))
|
||||
{
|
||||
if (other->area.width || other->area.height)
|
||||
{
|
||||
area->x = other->area.x;
|
||||
area->width = other->area.width;
|
||||
area->y = other->area.y + other->area.height + spacing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (other == NULL)
|
||||
{
|
||||
for (other = gtk_rb_tree_node_get_next (tile);
|
||||
other;
|
||||
other = gtk_rb_tree_node_get_next (other))
|
||||
{
|
||||
if (other->area.width || other->area.height)
|
||||
{
|
||||
area->x = other->area.x;
|
||||
area->width = other->area.width;
|
||||
area->y = MAX (0, other->area.y - spacing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
@@ -307,9 +300,8 @@ gtk_list_view_get_position_from_allocation (GtkListBase *base,
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (base);
|
||||
GtkListTile *tile;
|
||||
int row_height, tile_pos;
|
||||
|
||||
tile = gtk_list_item_manager_get_tile_at (self->item_manager, x, y);
|
||||
tile = gtk_list_item_manager_get_nearest_tile (self->item_manager, x, y);
|
||||
if (tile == NULL)
|
||||
return FALSE;
|
||||
|
||||
@@ -325,22 +317,60 @@ gtk_list_view_get_position_from_allocation (GtkListBase *base,
|
||||
}
|
||||
|
||||
*pos = gtk_list_tile_get_position (self->item_manager, tile);
|
||||
row_height = (tile->area.height / tile->n_items);
|
||||
tile_pos = (y - tile->area.y) / row_height;
|
||||
|
||||
*pos += tile_pos;
|
||||
|
||||
if (area)
|
||||
*area = tile->area;
|
||||
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
area->x = tile->area.x;
|
||||
area->width = tile->area.width;
|
||||
area->y = tile->area.y + tile_pos * row_height;
|
||||
area->height = row_height;
|
||||
int row_height, tile_pos, spacing;
|
||||
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (self), NULL, &spacing);
|
||||
row_height = (tile->area.height - (tile->n_items - 1) * spacing) / tile->n_items;
|
||||
if (y >= tile->area.y + tile->area.height)
|
||||
tile_pos = tile->n_items - 1;
|
||||
else
|
||||
tile_pos = (y - tile->area.y) / (row_height + spacing);
|
||||
|
||||
*pos += tile_pos;
|
||||
if (area)
|
||||
{
|
||||
area->y = tile->area.y + tile_pos * (row_height + spacing);
|
||||
area->height = row_height;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
gtk_list_view_get_items_in_rect (GtkListBase *base,
|
||||
const cairo_rectangle_int_t *rect)
|
||||
{
|
||||
guint first, last;
|
||||
cairo_rectangle_int_t area;
|
||||
GtkBitset *result;
|
||||
|
||||
result = gtk_bitset_new_empty ();
|
||||
|
||||
if (!gtk_list_view_get_position_from_allocation (base, rect->x, rect->y, &first, &area))
|
||||
return result;
|
||||
if (area.y + area.height < rect->y)
|
||||
first++;
|
||||
|
||||
if (!gtk_list_view_get_position_from_allocation (base,
|
||||
rect->x + rect->width - 1,
|
||||
rect->y + rect->height - 1,
|
||||
&last, &area))
|
||||
return result;
|
||||
if (area.y >= rect->y + rect->height)
|
||||
last--;
|
||||
|
||||
if (last >= first)
|
||||
gtk_bitset_add_range_closed (result, first, last);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_list_view_move_focus_across (GtkListBase *base,
|
||||
guint pos,
|
||||
@@ -413,9 +443,14 @@ gtk_list_view_measure_list (GtkWidget *widget,
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (widget);
|
||||
GtkListTile *tile;
|
||||
int min, nat, child_min, child_nat;
|
||||
int min, nat, child_min, child_nat, spacing;
|
||||
GArray *min_heights, *nat_heights;
|
||||
guint n_unknown;
|
||||
guint n_unknown, n_items;
|
||||
|
||||
n_items = gtk_list_base_get_n_items (GTK_LIST_BASE (self));
|
||||
if (n_items == 0)
|
||||
return;
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (self), NULL, &spacing);
|
||||
|
||||
min_heights = g_array_new (FALSE, FALSE, sizeof (int));
|
||||
nat_heights = g_array_new (FALSE, FALSE, sizeof (int));
|
||||
@@ -451,8 +486,8 @@ gtk_list_view_measure_list (GtkWidget *widget,
|
||||
g_array_free (min_heights, TRUE);
|
||||
g_array_free (nat_heights, TRUE);
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
*minimum = min + spacing * (n_items - 1);
|
||||
*natural = nat + spacing * (n_items - 1);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -481,7 +516,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
GtkListView *self = GTK_LIST_VIEW (widget);
|
||||
GtkListTile *tile;
|
||||
GArray *heights;
|
||||
int min, nat, row_height, y, list_width;
|
||||
int min, nat, row_height, y, list_width, spacing;
|
||||
GtkOrientation orientation, opposite_orientation;
|
||||
GtkScrollablePolicy scroll_policy, opposite_scroll_policy;
|
||||
|
||||
@@ -489,6 +524,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
|
||||
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), orientation);
|
||||
opposite_scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), opposite_orientation);
|
||||
gtk_list_base_get_border_spacing (GTK_LIST_BASE (self), NULL, &spacing);
|
||||
|
||||
/* step 0: exit early if list is empty */
|
||||
tile = gtk_list_tile_gc (self->item_manager, gtk_list_item_manager_get_first (self->item_manager));
|
||||
@@ -540,9 +576,15 @@ gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
{
|
||||
gtk_list_tile_set_area_position (self->item_manager, tile, 0, y);
|
||||
if (tile->widget == NULL)
|
||||
gtk_list_tile_set_area_size (self->item_manager, tile, list_width, row_height * tile->n_items);
|
||||
{
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
tile,
|
||||
list_width,
|
||||
row_height * tile->n_items
|
||||
+ spacing * (tile->n_items - 1));
|
||||
}
|
||||
|
||||
y += tile->area.height;
|
||||
y += tile->area.height + spacing;
|
||||
}
|
||||
|
||||
/* step 4: allocate the rest */
|
||||
|
||||
Reference in New Issue
Block a user