Compare commits

..

11 Commits

Author SHA1 Message Date
Matthias Clasen
eea6087f49 gridview: Reorganize size_allocate a bit more
Treat headers more like multirow tiles, and
footers more like regular items. Also, explicitly
set the height of all tiles to 0 at the outset,
so we have a certain way to know which tiles have
already had their size set to row_height.
2023-05-27 22:43:01 -04:00
Matthias Clasen
2e6e952c2e gridview: Don't consider headers for height
When determining the height of unknown items,
don't throw headers into the mix. They are
different.
2023-05-27 22:43:01 -04:00
Matthias Clasen
284f087429 testsuite: Make checks match
The gridview tests were not using the same
assertions as the code in GtkGridView. Make
them match, so the tests pass.
2023-05-27 22:43:01 -04:00
Matthias Clasen
9ee508e768 gridview: Make rubberband selection section-aware
Compute the right items to include in a rectangular
selection even if it stretches across section boundaries.

This code currently does the simplest thing that can
work. If it turns out to be a problem for big selections,
we can try to make it smarter.
2023-05-27 22:43:01 -04:00
Matthias Clasen
1a320a9aa3 gridview: Make focus movement section-aware
Make up-down focus movements preserve the column
across section boundaries.

This code does the simplest thing that can work.
If this turns out to be a problem, it can be
made smarter.

Note that the code currently prefers to jump over
short sections to preserve the column. If there
are many short sections, this not be desired,
and we may want to make it tweakable.
2023-05-27 22:43:01 -04:00
Matthias Clasen
e4133ff1de gridview: Fix measure with sections
We must not include the header width when
determining the column width, but we do
want to take it into account when finding
the overall gridview width.
2023-05-27 22:43:01 -04:00
Matthias Clasen
98e99a075c gridview: Make helpers section-aware
Make get_column_for_position and is_multirow_tile
take sections into account.
2023-05-27 22:43:01 -04:00
Matthias Clasen
0891a0dee7 gridview: Add a section helper 2023-05-27 22:43:01 -04:00
Matthias Clasen
12af6ad1d4 gridview: Always set header and footer size
Update tile allocation for headers and foots in cases
where we go from having headers to not having headers.

Before this commit, turning sections off in testsections
would leave the global header and footer with some empty
space allocated.
2023-05-27 22:43:01 -04:00
Matthias Clasen
f5ba7d0da7 gridview: Allocate headers
Allocate the right amount of space to headers and footers
to make sections break the grid in the expected way.
2023-05-27 22:43:01 -04:00
Matthias Clasen
a02a765703 gridview: Copy section plumbing
Add a header factory, and implement prepare_section and
create_header_widget.

Allocating header and footer tiles properly will be
left to future commits.
2023-05-27 22:43:01 -04:00
4 changed files with 391 additions and 80 deletions

View File

@@ -23,12 +23,14 @@
#include "gtkbitset.h"
#include "gtklistbaseprivate.h"
#include "gtklistheaderwidgetprivate.h"
#include "gtklistitemfactory.h"
#include "gtklistitemmanagerprivate.h"
#include "gtklistitemwidgetprivate.h"
#include "gtkmultiselection.h"
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
#include "gtksectionmodel.h"
/* Maximum number of list items created by the gridview.
* For debugging, you can set this to G_MAXUINT to ensure
@@ -87,6 +89,7 @@ struct _GtkGridView
GtkListItemManager *item_manager;
GtkListItemFactory *factory;
GtkListItemFactory *header_factory;
guint min_columns;
guint max_columns;
gboolean single_click_activate;
@@ -105,6 +108,7 @@ enum
PROP_0,
PROP_ENABLE_RUBBERBAND,
PROP_FACTORY,
PROP_HEADER_FACTORY,
PROP_MAX_COLUMNS,
PROP_MIN_COLUMNS,
PROP_MODEL,
@@ -257,6 +261,13 @@ gtk_grid_view_split (GtkListBase *base,
return split;
}
static void
gtk_grid_view_prepare_section (GtkListBase *base,
GtkListTile *tile,
guint position)
{
}
/* We define the listview as **inert** when the factory isn't used. */
static gboolean
gtk_grid_view_is_inert (GtkGridView *self)
@@ -269,7 +280,8 @@ gtk_grid_view_is_inert (GtkGridView *self)
static void
gtk_grid_view_update_factories_with (GtkGridView *self,
GtkListItemFactory *factory)
GtkListItemFactory *factory,
GtkListItemFactory *header_factory)
{
GtkListTile *tile;
@@ -277,8 +289,26 @@ gtk_grid_view_update_factories_with (GtkGridView *self,
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (tile->widget)
gtk_list_factory_widget_set_factory (GTK_LIST_FACTORY_WIDGET (tile->widget), factory);
switch (tile->type)
{
case GTK_LIST_TILE_ITEM:
if (tile->widget)
gtk_list_factory_widget_set_factory (GTK_LIST_FACTORY_WIDGET (tile->widget), factory);
break;
case GTK_LIST_TILE_HEADER:
if (tile->widget)
gtk_list_header_widget_set_factory (GTK_LIST_HEADER_WIDGET (tile->widget), header_factory);
break;
case GTK_LIST_TILE_UNMATCHED_HEADER:
case GTK_LIST_TILE_FOOTER:
case GTK_LIST_TILE_UNMATCHED_FOOTER:
case GTK_LIST_TILE_REMOVED:
g_assert (tile->widget == NULL);
break;
default:
g_assert_not_reached();
break;
}
}
}
@@ -286,13 +316,14 @@ static void
gtk_grid_view_update_factories (GtkGridView *self)
{
gtk_grid_view_update_factories_with (self,
gtk_grid_view_is_inert (self) ? NULL : self->factory);
gtk_grid_view_is_inert (self) ? NULL : self->factory,
gtk_grid_view_is_inert (self) ? NULL : self->header_factory);
}
static void
gtk_grid_view_clear_factories (GtkGridView *self)
{
gtk_grid_view_update_factories_with (self, NULL);
gtk_grid_view_update_factories_with (self, NULL, NULL);
}
static GtkListItemBase *
@@ -316,6 +347,20 @@ gtk_grid_view_create_list_widget (GtkListBase *base)
return GTK_LIST_ITEM_BASE (result);
}
static GtkListHeaderBase *
gtk_grid_view_create_header_widget (GtkListBase *base)
{
GtkGridView *self = GTK_GRID_VIEW (base);
GtkListItemFactory *factory;
if (gtk_grid_view_is_inert (self))
factory = NULL;
else
factory = self->header_factory;
return GTK_LIST_HEADER_BASE (gtk_list_header_widget_new (factory));
}
static gboolean
gtk_grid_view_get_allocation (GtkListBase *base,
guint pos,
@@ -384,14 +429,50 @@ gtk_grid_view_get_allocation (GtkListBase *base,
return TRUE;
}
/* Returns the column that the given item will fall in.
/* Returns the section that position falls into
*/
void
gtk_grid_view_get_section_for_position (GtkListItemManager *items,
unsigned int position,
unsigned int *section_start,
unsigned int *section_end)
{
GListModel *model;
unsigned int start, end;
model = G_LIST_MODEL (gtk_list_item_manager_get_model (items));
if (!gtk_list_item_manager_get_has_sections (items))
{
start = 0;
end = g_list_model_get_n_items (model);
}
else
{
gtk_section_model_get_section (GTK_SECTION_MODEL (model), position, &start, &end);
}
if (section_start)
*section_start = start;
if (section_end)
*section_end = end;
}
/* Returns the column that the given item will fall in, taking
* sections into account. Note that this depends on whether
* we are currently showing sections, and on the number of
* columns that the grid is allocating.
*/
unsigned int
gtk_grid_view_get_column_for_position (GtkListItemManager *items,
unsigned int n_columns,
unsigned int position)
{
return position % n_columns;
unsigned int start;
gtk_grid_view_get_section_for_position (items, position, &start, NULL);
return (position - start) % n_columns;
}
/* Determine whether a tile is contained in a single row,
@@ -403,14 +484,19 @@ gtk_grid_view_is_multirow_tile (GtkListItemManager *items,
GtkListTile *tile)
{
unsigned int position;
unsigned int start, end;
unsigned int col;
if (tile->n_items <= 1)
return FALSE;
position = gtk_list_tile_get_position (items, tile);
gtk_grid_view_get_section_for_position (items, position, &start, &end);
col = position % n_columns;
if (end < position + tile->n_items)
return TRUE;
col = (position - start) % n_columns;
return col + tile->n_items > n_columns;
}
@@ -521,38 +607,104 @@ gtk_grid_view_get_items_in_rect (GtkListBase *base,
if (area.y >= rect->y + rect->height)
last_row -= self->n_columns;
if (first_column <= last_column && first_row <= last_row)
if (gtk_list_item_manager_get_has_sections (self->item_manager))
{
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);
for (unsigned int pos = first_row; pos < last_row + self->n_columns; pos++)
{
unsigned int col;
col = gtk_grid_view_get_column_for_position (self->item_manager,
self->n_columns,
pos);
if (col >= first_column && col <= last_column)
gtk_bitset_add (result, pos);
}
}
else
{
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;
}
static unsigned int
find_previous_item_in_column (GtkGridView *self,
unsigned int position)
{
unsigned int col, pos;
if (position == 0)
return position;
col = gtk_grid_view_get_column_for_position (self->item_manager,
self->n_columns,
position);
pos = position;
do
{
pos--;
if (col == gtk_grid_view_get_column_for_position (self->item_manager,
self->n_columns,
pos))
return pos;
} while (pos > 0);
return position;
}
static unsigned int
find_next_item_in_column (GtkGridView *self,
unsigned int position)
{
unsigned int col;
unsigned int n_items;
n_items = g_list_model_get_n_items (G_LIST_MODEL (gtk_list_item_manager_get_model (self->item_manager)));
col = gtk_grid_view_get_column_for_position (self->item_manager,
self->n_columns,
position);
for (unsigned int p = position + 1; p < n_items; p++)
{
if (col == gtk_grid_view_get_column_for_position (self->item_manager,
self->n_columns,
p))
return p;
}
return position;
}
static guint
gtk_grid_view_move_focus_along (GtkListBase *base,
guint pos,
int steps)
{
GtkGridView *self = GTK_GRID_VIEW (base);
unsigned int prev_pos = pos;
steps *= self->n_columns;
for (unsigned int i = 0; i < abs (steps); i++)
{
if (steps < 0)
pos = find_previous_item_in_column (self, pos);
else
pos = find_next_item_in_column (self, pos);
}
if (steps < 0)
{
if (pos >= self->n_columns)
pos -= MIN (pos, -steps);
}
else
{
guint n_items = gtk_list_base_get_n_items (base);
if (n_items / self->n_columns > pos / self->n_columns)
pos += MIN (n_items - pos - 1, steps);
}
if (prev_pos == pos)
gtk_widget_keynav_failed (GTK_WIDGET (self), steps < 0 ? GTK_DIR_UP : GTK_DIR_DOWN);
return pos;
}
@@ -562,14 +714,19 @@ gtk_grid_view_move_focus_across (GtkListBase *base,
guint pos,
int steps)
{
unsigned int prev_pos = pos;
if (steps < 0)
return pos - MIN (pos, -steps);
pos = pos - MIN (pos, -steps);
else
{
guint n_items = gtk_list_base_get_n_items (base);
pos += MIN (n_items - pos - 1, steps);
}
if (prev_pos == pos)
gtk_widget_keynav_failed (GTK_WIDGET (base), steps < 0 ? GTK_DIR_LEFT : GTK_DIR_RIGHT);
return pos;
}
@@ -595,32 +752,49 @@ gtk_grid_view_get_unknown_row_size (GtkGridView *self,
static void
gtk_grid_view_measure_column_size (GtkGridView *self,
int *minimum,
int *natural)
int *natural,
int *header_minimum,
int *header_natural)
{
GtkOrientation opposite;
GtkListTile *tile;
int min, nat, child_min, child_nat;
int min, nat, header_min, header_nat;
min = 0;
nat = 0;
header_min = 0;
header_nat = 0;
opposite = gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self));
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
int child_min, child_nat;
if (tile->widget == NULL)
continue;
gtk_widget_measure (tile->widget,
opposite, -1,
&child_min, &child_nat, NULL, NULL);
min = MAX (min, child_min);
nat = MAX (nat, child_nat);
if (tile->type == GTK_LIST_TILE_ITEM)
{
min = MAX (min, child_min);
nat = MAX (nat, child_nat);
}
else
{
header_min = MAX (header_min, child_min);
header_nat = MAX (header_nat, child_nat);
}
}
*minimum = min;
*natural = nat;
*header_minimum = header_min;
*header_natural = header_nat;
}
static void
@@ -631,13 +805,14 @@ gtk_grid_view_measure_across (GtkWidget *widget,
{
GtkGridView *self = GTK_GRID_VIEW (widget);
int xspacing;
int col_min, col_nat, header_min, header_nat;
gtk_list_base_get_border_spacing (GTK_LIST_BASE (widget), &xspacing, NULL);
gtk_grid_view_measure_column_size (self, minimum, natural);
gtk_grid_view_measure_column_size (self, &col_min, &col_nat, &header_min, &header_nat);
*minimum = (*minimum + xspacing) * self->min_columns - xspacing;
*natural = (*natural + xspacing) * self->max_columns - xspacing;
*minimum = MAX ((col_min + xspacing) * self->min_columns - xspacing, header_min);
*natural = MAX ((col_nat + xspacing) * self->max_columns - xspacing, header_nat);
}
static guint
@@ -673,7 +848,7 @@ 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;
int xspacing, yspacing, header_min, header_nat;
gboolean measured;
GArray *heights;
guint n_unknown, n_columns;
@@ -685,7 +860,7 @@ gtk_grid_view_measure_list (GtkWidget *widget,
n_unknown = 0;
height = 0;
gtk_grid_view_measure_column_size (self, &col_min, &col_nat);
gtk_grid_view_measure_column_size (self, &col_min, &col_nat, &header_min, &header_nat);
for_size = MAX (for_size, col_min * (int) self->min_columns);
n_columns = gtk_grid_view_compute_n_columns (self, for_size, xspacing, col_min, col_nat);
column_size = (for_size + xspacing) / n_columns - xspacing;
@@ -701,7 +876,7 @@ gtk_grid_view_measure_list (GtkWidget *widget,
{
gtk_widget_measure (tile->widget,
gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
column_size,
gtk_list_tile_is_header (tile) ? for_size : column_size,
&child_min, &child_nat, NULL, NULL);
if (scroll_policy == GTK_SCROLL_MINIMUM)
row_height = MAX (row_height, child_min);
@@ -710,7 +885,10 @@ gtk_grid_view_measure_list (GtkWidget *widget,
measured = TRUE;
}
i += tile->n_items;
if (gtk_list_tile_is_header (tile) || gtk_list_tile_is_footer (tile))
i = n_columns;
else
i += tile->n_items;
if (i >= n_columns)
{
@@ -819,6 +997,7 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
GtkListTile *tile, *start;
GArray *heights;
int min_row_height, unknown_row_height, row_height, col_min, col_nat;
int header_min, header_nat;
GtkOrientation orientation;
GtkScrollablePolicy scroll_policy;
int y, xspacing, yspacing;
@@ -840,7 +1019,7 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
}
/* step 1: determine width of the list */
gtk_grid_view_measure_column_size (self, &col_min, &col_nat);
gtk_grid_view_measure_column_size (self, &col_min, &col_nat, &header_min, &header_nat);
self->n_columns = gtk_grid_view_compute_n_columns (self,
orientation == GTK_ORIENTATION_VERTICAL ? width : height,
xspacing,
@@ -857,8 +1036,10 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
tile = gtk_list_item_manager_get_first (self->item_manager);
while (tile != NULL)
{
if (gtk_grid_view_is_multirow_tile (self->item_manager, self->n_columns, tile))
if (gtk_grid_view_is_multirow_tile (self->item_manager, self->n_columns, tile) ||
gtk_list_tile_is_header (tile))
{
gtk_list_tile_set_area_size (self->item_manager, tile, 0, 0);
tile = gtk_rb_tree_node_get_next (tile);
continue;
}
@@ -869,6 +1050,9 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
i < self->n_columns && tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
g_assert (!gtk_list_tile_is_header (start));
gtk_list_tile_set_area_size (self->item_manager, tile, 0, 0);
if (tile->widget)
{
int min, nat, size;
@@ -881,10 +1065,14 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
else
size = nat;
size = MAX (size, min_row_height);
g_array_append_val (heights, size);
if (tile->type == GTK_LIST_TILE_ITEM)
g_array_append_val (heights, size);
row_height = MAX (row_height, size);
}
i += tile->n_items;
if (gtk_list_tile_is_footer (tile))
i = self->n_columns;
else
i += tile->n_items;
}
if (row_height > 0)
@@ -894,34 +1082,21 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
start = gtk_rb_tree_node_get_next (start))
{
unsigned int n_columns;
unsigned int tile_height;
g_assert (!gtk_list_tile_is_header (start));
if (gtk_list_tile_is_footer (start))
{
n_columns = self->n_columns - i;
if (n_columns != 0 && n_columns != self->n_columns)
tile_height = row_height;
else
tile_height = 0;
}
else if (gtk_list_tile_is_header (start))
{
n_columns = self->n_columns;
tile_height = 0;
}
n_columns = self->n_columns - i;
else
{
n_columns = start->n_items;
tile_height = row_height;
}
n_columns = start->n_items;
gtk_list_tile_set_area_size (self->item_manager,
start,
column_end (self, xspacing, i + n_columns - 1)
- column_start (self, xspacing, i),
tile_height);
row_height);
i = (i + start->n_items) % self->n_columns;
i = (i + n_columns) % self->n_columns;
}
}
}
@@ -930,6 +1105,8 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
unknown_row_height = gtk_grid_view_get_unknown_row_size (self, heights);
g_array_free (heights, TRUE);
g_assert (unknown_row_height > 0);
/* step 5: determine height for remaining rows and set each row's position */
y = 0;
i = 0;
@@ -945,7 +1122,6 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
if (gtk_grid_view_is_multirow_tile (self->item_manager, self->n_columns, tile))
{
g_assert (i == 0);
g_assert (tile->n_items % self->n_columns == 0);
gtk_list_tile_set_area_size (self->item_manager,
tile,
column_end (self, xspacing, self->n_columns - 1)
@@ -953,13 +1129,59 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
(unknown_row_height + yspacing) * (tile->n_items / self->n_columns) - yspacing);
y += tile->area.height + yspacing;
}
else
else if (gtk_list_tile_is_header (tile))
{
int size;
g_assert (i == 0);
if (tile->widget)
{
int min, nat;
gtk_widget_measure (tile->widget,
gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
(self->column_width + xspacing) * self->n_columns,
&min, &nat, NULL, NULL);
if (scroll_policy == GTK_SCROLL_MINIMUM)
size = min;
else
size = nat;
size = MAX (size, min_row_height);
}
else if (gtk_list_item_manager_get_has_sections (self->item_manager))
size = unknown_row_height;
else
size = 0;
gtk_list_tile_set_area_size (self->item_manager,
tile,
column_end (self, xspacing, i + tile->n_items - 1) - tile->area.x,
unknown_row_height);
i += tile->n_items;
column_end (self, xspacing, self->n_columns - 1)
- column_start (self, xspacing, 0),
size);
y += tile->area.height + yspacing;
}
else
{
unsigned int n_items = tile->n_items;
if (gtk_list_tile_is_footer (tile))
{
if (i == 0)
n_items = 0;
else
n_items = self->n_columns - i;
}
if (tile->area.height == 0)
gtk_list_tile_set_area_size (self->item_manager,
tile,
column_end (self, xspacing, i + n_items - 1) - tile->area.x,
unknown_row_height);
i += n_items;
}
if (i >= self->n_columns)
@@ -1026,6 +1248,7 @@ gtk_grid_view_dispose (GObject *object)
self->item_manager = NULL;
g_clear_object (&self->factory);
g_clear_object (&self->header_factory);
G_OBJECT_CLASS (gtk_grid_view_parent_class)->dispose (object);
}
@@ -1048,6 +1271,10 @@ gtk_grid_view_get_property (GObject *object,
g_value_set_object (value, self->factory);
break;
case PROP_HEADER_FACTORY:
g_value_set_object (value, self->header_factory);
break;
case PROP_MAX_COLUMNS:
g_value_set_uint (value, self->max_columns);
break;
@@ -1092,6 +1319,10 @@ gtk_grid_view_set_property (GObject *object,
gtk_grid_view_set_factory (self, g_value_get_object (value));
break;
case PROP_HEADER_FACTORY:
gtk_grid_view_set_header_factory (self, g_value_get_object (value));
break;
case PROP_MAX_COLUMNS:
gtk_grid_view_set_max_columns (self, g_value_get_uint (value));
break;
@@ -1145,6 +1376,8 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
list_base_class->split = gtk_grid_view_split;
list_base_class->create_list_widget = gtk_grid_view_create_list_widget;
list_base_class->prepare_section = gtk_grid_view_prepare_section;
list_base_class->create_header_widget = gtk_grid_view_create_header_widget;
list_base_class->get_allocation = gtk_grid_view_get_allocation;
list_base_class->get_items_in_rect = gtk_grid_view_get_items_in_rect;
list_base_class->get_position_from_allocation = gtk_grid_view_get_position_from_allocation;
@@ -1183,6 +1416,19 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkGridView:header-factory: (attributes org.gtk.Property.get=gtk_grid_view_get_header_factory org.gtk.Property.set=gtk_grid_view_set_header_factory)
*
* Factory for creating header widgets.
*
* Since: 4.12
*/
properties[PROP_HEADER_FACTORY] =
g_param_spec_object ("header-factory", NULL, NULL,
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkGridView:max-columns: (attributes org.gtk.Property.get=gtk_grid_view_get_max_columns org.gtk.Property.set=gtk_grid_view_set_max_columns)
*
@@ -1416,6 +1662,69 @@ gtk_grid_view_set_factory (GtkGridView *self,
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
}
/**
* gtk_grid_view_get_header_factory: (attributes org.gtk.Method.get_property=header-factory)
* @self: a `GtkGridView`
*
* Gets the factory that's currently used to populate section headers.
*
* Returns: (nullable) (transfer none): The factory in use
*
* Since: 4.12
*/
GtkListItemFactory *
gtk_grid_view_get_header_factory (GtkGridView *self)
{
g_return_val_if_fail (GTK_IS_GRID_VIEW (self), NULL);
return self->header_factory;
}
/**
* gtk_grid_view_set_header_factory: (attributes org.gtk.Method.set_property=header-factory)
* @self: a `GtkGridView`
* @factory: (nullable) (transfer none): the factory to use
*
* Sets the `GtkListItemFactory` to use for populating the
* [class@Gtk.ListHeader] objects used in section headers.
*
* If this factory is set to %NULL, the list will not show section headers.
*
* Since: 4.12
*/
void
gtk_grid_view_set_header_factory (GtkGridView *self,
GtkListItemFactory *factory)
{
gboolean had_sections;
g_return_if_fail (GTK_IS_GRID_VIEW (self));
g_return_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory));
had_sections = gtk_list_item_manager_get_has_sections (self->item_manager);
if (!g_set_object (&self->header_factory, factory))
return;
gtk_list_item_manager_set_has_sections (self->item_manager, factory != NULL);
if (!gtk_grid_view_is_inert (self) &&
had_sections && gtk_list_item_manager_get_has_sections (self->item_manager))
{
GtkListTile *tile;
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (tile->widget && tile->type == GTK_LIST_TILE_HEADER)
gtk_list_header_widget_set_factory (GTK_LIST_HEADER_WIDGET (tile->widget), factory);
}
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEADER_FACTORY]);
}
/**
* gtk_grid_view_get_max_columns: (attributes org.gtk.Method.get_property=max-columns)
* @self: a `GtkGridView`

View File

@@ -56,6 +56,14 @@ void gtk_grid_view_set_factory (GtkGridView
GDK_AVAILABLE_IN_ALL
GtkListItemFactory *
gtk_grid_view_get_factory (GtkGridView *self);
GDK_AVAILABLE_IN_4_12
void gtk_grid_view_set_header_factory (GtkGridView *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_4_12
GtkListItemFactory *
gtk_grid_view_get_header_factory (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
guint gtk_grid_view_get_min_columns (GtkGridView *self);
GDK_AVAILABLE_IN_ALL

View File

@@ -25,6 +25,11 @@
G_BEGIN_DECLS
void gtk_grid_view_get_section_for_position (GtkListItemManager *items,
unsigned int position,
unsigned int *start,
unsigned int *end);
unsigned int gtk_grid_view_get_column_for_position (GtkListItemManager *items,
unsigned int n_columns,
unsigned int position);

View File

@@ -374,16 +374,6 @@ print_changes_cb (GListModel *model,
g_test_message ("%u/%u: removing %u and adding %u items", position, g_list_model_get_n_items (model), removed, added);
}
static gboolean
is_footer (GtkListTile *tile)
{
if (tile == NULL)
return FALSE;
return tile->type == GTK_LIST_TILE_FOOTER ||
tile->type == GTK_LIST_TILE_UNMATCHED_FOOTER;
}
static void
check_tile_invariants_for_columns (GtkListItemManager *items,
unsigned int n_columns)
@@ -397,18 +387,17 @@ check_tile_invariants_for_columns (GtkListItemManager *items,
g_assert (tile->type != GTK_LIST_TILE_REMOVED);
if (tile->n_items > 1)
{
unsigned int pos, col, col2;
gboolean before_footer;
unsigned int pos, col, col2, end;
pos = gtk_list_tile_get_position (items, tile);
col = gtk_grid_view_get_column_for_position (items, n_columns, pos);
col2 = gtk_grid_view_get_column_for_position (items, n_columns, pos + tile->n_items - 1);
before_footer = is_footer (gtk_rb_tree_node_get_next (tile));
gtk_grid_view_get_section_for_position (items, pos + tile->n_items - 1, NULL, &end);
if (gtk_grid_view_is_multirow_tile (items, n_columns, tile))
{
g_assert_true (col == 0);
g_assert_true (col2 == n_columns - 1 || before_footer);
g_assert_true (col2 == n_columns - 1 || pos + tile->n_items == end);
}
else
{