Compare commits
25 Commits
path-point
...
wip/matthi
Author | SHA1 | Date | |
---|---|---|---|
|
0b156637a5 | ||
|
73696a8521 | ||
|
b772b4600d | ||
|
7aa7d7be16 | ||
|
8793ab183d | ||
|
b1353457d4 | ||
|
49b03b8a84 | ||
|
5cc40f1e5a | ||
|
b1723c1b0c | ||
|
bfa2e6725e | ||
|
5620ffcbfd | ||
|
d94fc2d4e2 | ||
|
702ee8cb1e | ||
|
0fe56ad497 | ||
|
4e18d469bc | ||
|
b694aef7a6 | ||
|
631ee08d9a | ||
|
bbe9e9bcdd | ||
|
ff46f050f5 | ||
|
9a66ec81bf | ||
|
e5f0e17378 | ||
|
c7d295dd9b | ||
|
2e30806d42 | ||
|
56907ad66f | ||
|
9cb94c1547 |
@@ -19,16 +19,18 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkgridview.h"
|
||||
#include "gtkgridviewprivate.h"
|
||||
|
||||
#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,12 +89,19 @@ struct _GtkGridView
|
||||
|
||||
GtkListItemManager *item_manager;
|
||||
GtkListItemFactory *factory;
|
||||
GtkListItemFactory *header_factory;
|
||||
guint min_columns;
|
||||
guint max_columns;
|
||||
gboolean single_click_activate;
|
||||
/* set in size_allocate */
|
||||
guint n_columns;
|
||||
double column_width;
|
||||
|
||||
/* for debugging between get_position_from_allocation and size_allocate */
|
||||
unsigned int anchor_position;
|
||||
unsigned int anchor_column;
|
||||
unsigned int anchor_n_columns;
|
||||
gboolean anchor_has_sections;
|
||||
};
|
||||
|
||||
struct _GtkGridViewClass
|
||||
@@ -105,6 +114,7 @@ enum
|
||||
PROP_0,
|
||||
PROP_ENABLE_RUBBERBAND,
|
||||
PROP_FACTORY,
|
||||
PROP_HEADER_FACTORY,
|
||||
PROP_MAX_COLUMNS,
|
||||
PROP_MIN_COLUMNS,
|
||||
PROP_MODEL,
|
||||
@@ -257,6 +267,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 +286,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 +295,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 +322,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 +353,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,6 +435,79 @@ gtk_grid_view_get_allocation (GtkListBase *base,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Returns the section that position falls into
|
||||
*/
|
||||
static void
|
||||
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)
|
||||
{
|
||||
unsigned int start;
|
||||
|
||||
get_section_for_position (items, position, &start, NULL);
|
||||
|
||||
return (position - start) % n_columns;
|
||||
}
|
||||
|
||||
/* Determine whether a tile is contained in a single row,
|
||||
* or spans multiple rows.
|
||||
*/
|
||||
gboolean
|
||||
gtk_grid_view_is_multirow_tile (GtkListItemManager *items,
|
||||
unsigned int n_columns,
|
||||
GtkListTile *tile)
|
||||
{
|
||||
unsigned int position;
|
||||
unsigned int start, start2;
|
||||
unsigned int col;
|
||||
|
||||
if (tile->n_items <= 1)
|
||||
return FALSE;
|
||||
|
||||
position = gtk_list_tile_get_position (items, tile);
|
||||
get_section_for_position (items, position, &start, NULL);
|
||||
get_section_for_position (items, position + tile->n_items - 1, &start2, NULL);
|
||||
|
||||
if (start != start2)
|
||||
return TRUE;
|
||||
|
||||
col = (position - start) % n_columns;
|
||||
|
||||
return col + tile->n_items - 1 > n_columns;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
int x,
|
||||
@@ -394,6 +518,7 @@ gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
GtkGridView *self = GTK_GRID_VIEW (base);
|
||||
GtkListTile *tile;
|
||||
guint pos;
|
||||
guint col;
|
||||
|
||||
tile = gtk_list_item_manager_get_nearest_tile (self->item_manager, x, y);
|
||||
if (tile == NULL)
|
||||
@@ -411,44 +536,43 @@ gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
}
|
||||
|
||||
pos = gtk_list_tile_get_position (self->item_manager, tile);
|
||||
col = gtk_grid_view_get_column_for_position (self->item_manager, self->n_columns, pos);
|
||||
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
int xspacing, yspacing;
|
||||
guint row_height;
|
||||
guint row_index;
|
||||
|
||||
gtk_list_base_get_border_spacing (base, &xspacing, &yspacing);
|
||||
|
||||
/* offset in x direction */
|
||||
pos += column_index (self, xspacing, MAX (tile->area.width - 1, x - tile->area.x));
|
||||
if (area)
|
||||
{
|
||||
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;
|
||||
}
|
||||
pos += column_index (self, xspacing, MAX (tile->area.width - 1, x - tile->area.x)) - col;
|
||||
|
||||
/* 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 + 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 + yspacing);
|
||||
area->height = row_height;
|
||||
}
|
||||
row_height = (tile->area.height + yspacing) / rows_in_tile - yspacing;
|
||||
row_index = MIN (tile->area.height - 1, y - tile->area.y) / (row_height + yspacing);
|
||||
pos += self->n_columns * row_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (area)
|
||||
{
|
||||
area->y = tile->area.y;
|
||||
area->height = tile->area.height;
|
||||
}
|
||||
row_height = tile->area.height;
|
||||
row_index = 0;
|
||||
}
|
||||
|
||||
col = gtk_grid_view_get_column_for_position (self->item_manager, self->n_columns, pos);
|
||||
|
||||
if (area)
|
||||
{
|
||||
area->x = column_start (self, xspacing, col);
|
||||
area->width = column_end (self, xspacing, col) - area->x;
|
||||
area->y = tile->area.y + row_index * (row_height + yspacing);
|
||||
area->height = row_height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,6 +580,11 @@ gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
*area = tile->area;
|
||||
}
|
||||
|
||||
self->anchor_position = pos;
|
||||
self->anchor_column = col;
|
||||
self->anchor_n_columns = self->n_columns;
|
||||
self->anchor_has_sections = gtk_list_item_manager_get_has_sections (self->item_manager);
|
||||
|
||||
*position = pos;
|
||||
|
||||
return TRUE;
|
||||
@@ -632,6 +761,86 @@ gtk_grid_view_compute_n_columns (GtkGridView *self,
|
||||
return n_columns;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_grid_view_split_tiles_by_columns (GtkListItemManager *items,
|
||||
guint n_columns)
|
||||
{
|
||||
GtkListTile *tile;
|
||||
|
||||
tile = gtk_list_item_manager_get_first (items);
|
||||
|
||||
while (tile != NULL)
|
||||
{
|
||||
/* Handle multirow tiles first */
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
guint pos, col;
|
||||
guint end, remaining;
|
||||
|
||||
pos = gtk_list_tile_get_position (items, tile);
|
||||
col = gtk_grid_view_get_column_for_position (items, n_columns, pos);
|
||||
get_section_for_position (items, pos, NULL, &end);
|
||||
|
||||
if (col > 0)
|
||||
{
|
||||
/* Determine if the first row needs to be split off */
|
||||
remaining = MIN (n_columns - col, end - 1 - pos);
|
||||
if (remaining > 0 && tile->n_items > remaining)
|
||||
{
|
||||
gtk_list_tile_split (items, tile, remaining);
|
||||
tile = gtk_rb_tree_node_get_next (tile);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
pos += tile->n_items - 1;
|
||||
col = gtk_grid_view_get_column_for_position (items, n_columns, pos);
|
||||
get_section_for_position (items, pos, NULL, &end);
|
||||
|
||||
if (col < n_columns - 1)
|
||||
{
|
||||
/* Determine if the last row needs to be split off */
|
||||
remaining = MIN (n_columns - (col - 1), end - 1 - pos);
|
||||
if (remaining > 0 && col + 1 < tile->n_items)
|
||||
{
|
||||
gtk_list_tile_split (items, tile, tile->n_items - (col + 1));
|
||||
tile = gtk_rb_tree_node_get_next (tile);
|
||||
tile = gtk_rb_tree_node_get_next (tile);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tile = gtk_rb_tree_node_get_next (tile);
|
||||
}
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
/* Verify some invariants:
|
||||
* - there are no removed tiles left
|
||||
* - multirow tiles start in column 0
|
||||
* - non-multirow tiles are contained in one row
|
||||
*/
|
||||
for (tile = gtk_list_item_manager_get_first (items);
|
||||
tile != NULL;
|
||||
tile = gtk_rb_tree_node_get_next (tile))
|
||||
{
|
||||
g_assert (tile->type != GTK_LIST_TILE_REMOVED);
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
unsigned int pos, col;
|
||||
|
||||
pos = gtk_list_tile_get_position (items, tile);
|
||||
col = gtk_grid_view_get_column_for_position (items, n_columns, pos);
|
||||
|
||||
if (gtk_grid_view_is_multirow_tile (items, n_columns, tile))
|
||||
g_assert (col == 0);
|
||||
else
|
||||
g_assert (col + tile->n_items - 1 <= n_columns);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_grid_view_measure_list (GtkWidget *widget,
|
||||
int for_size,
|
||||
@@ -670,7 +879,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);
|
||||
@@ -679,7 +888,10 @@ gtk_grid_view_measure_list (GtkWidget *widget,
|
||||
measured = TRUE;
|
||||
}
|
||||
|
||||
i += tile->n_items;
|
||||
if (gtk_list_tile_is_footer (tile) || gtk_list_tile_is_header (tile))
|
||||
i = n_columns;
|
||||
else
|
||||
i += tile->n_items;
|
||||
|
||||
if (i >= n_columns)
|
||||
{
|
||||
@@ -775,34 +987,34 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
self->column_width = ((orientation == GTK_ORIENTATION_VERTICAL ? width : height) + xspacing) / self->n_columns - xspacing;
|
||||
self->column_width = MAX (self->column_width, col_min);
|
||||
|
||||
/* step 2: determine height of known rows */
|
||||
/* step 2: split tiles as required */
|
||||
gtk_grid_view_split_tiles_by_columns (self->item_manager, self->n_columns);
|
||||
|
||||
/* step 3: determine height of known rows */
|
||||
heights = g_array_new (FALSE, FALSE, sizeof (int));
|
||||
|
||||
tile = gtk_list_item_manager_get_first (self->item_manager);
|
||||
while (tile != NULL)
|
||||
{
|
||||
/* if it's a multirow tile, handle it here */
|
||||
if (tile->n_items > 1 && tile->n_items >= self->n_columns)
|
||||
if (gtk_grid_view_is_multirow_tile (self->item_manager, self->n_columns, tile))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* Not a multirow tile */
|
||||
i = 0;
|
||||
row_height = 0;
|
||||
|
||||
for (i = 0, start = tile;
|
||||
i < self->n_columns && tile != NULL;
|
||||
tile = gtk_rb_tree_node_get_next (tile))
|
||||
{
|
||||
g_assert (!gtk_grid_view_is_multirow_tile (self->item_manager, self->n_columns, 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,
|
||||
gtk_list_tile_is_header (tile) ? width : self->column_width,
|
||||
&min, &nat, NULL, NULL);
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
size = min;
|
||||
@@ -812,32 +1024,59 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
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 (gtk_list_tile_is_footer (tile) || gtk_list_tile_is_header (tile))
|
||||
i = self->n_columns;
|
||||
else
|
||||
i += tile->n_items;
|
||||
}
|
||||
|
||||
if (row_height > 0)
|
||||
{
|
||||
for (i = 0;
|
||||
start != tile;
|
||||
start = gtk_rb_tree_node_get_next (start))
|
||||
{
|
||||
int n_columns;
|
||||
int tile_height;
|
||||
|
||||
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))
|
||||
{
|
||||
g_assert (i == 0);
|
||||
n_columns = self->n_columns;
|
||||
if (gtk_list_item_manager_get_has_sections (self->item_manager))
|
||||
tile_height = row_height;
|
||||
else
|
||||
tile_height = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
n_columns = start->n_items;
|
||||
tile_height = row_height;
|
||||
}
|
||||
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
start,
|
||||
column_end (self, xspacing, i + start->n_items - 1)
|
||||
column_end (self, xspacing, i + n_columns - 1)
|
||||
- column_start (self, xspacing, i),
|
||||
row_height);
|
||||
i += start->n_items;
|
||||
tile_height);
|
||||
i += n_columns;
|
||||
}
|
||||
g_assert (i <= self->n_columns);
|
||||
}
|
||||
}
|
||||
|
||||
/* step 3: determine height of rows with only unknown items */
|
||||
/* step 4: determine height of rows with only unknown items */
|
||||
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 */
|
||||
/* step 5: determine height for remaining rows and set each row's position */
|
||||
y = 0;
|
||||
i = 0;
|
||||
for (tile = gtk_list_item_manager_get_first (self->item_manager);
|
||||
@@ -848,55 +1087,71 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
tile,
|
||||
column_start (self, xspacing, i),
|
||||
y);
|
||||
if (tile->n_items >= self->n_columns && tile->widget == NULL)
|
||||
|
||||
if (gtk_list_tile_is_footer (tile))
|
||||
{
|
||||
if (i > 0)
|
||||
i = self->n_columns;
|
||||
|
||||
if (!gtk_list_item_manager_get_has_sections (self->item_manager) && i == 0)
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
tile,
|
||||
column_end (self, xspacing, self->n_columns - 1)
|
||||
- column_start (self, xspacing, 0),
|
||||
0);
|
||||
}
|
||||
else if (gtk_list_tile_is_header (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)
|
||||
- column_start (self, xspacing, 0),
|
||||
(unknown_row_height + yspacing) * (tile->n_items / self->n_columns) - yspacing);
|
||||
y += tile->area.height + yspacing;
|
||||
i = self->n_columns;
|
||||
|
||||
if (!gtk_list_item_manager_get_has_sections (self->item_manager))
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
tile,
|
||||
column_end (self, xspacing, self->n_columns - 1)
|
||||
- column_start (self, xspacing, 0),
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tile->area.height == 0)
|
||||
if (gtk_list_tile_get_position (self->item_manager, tile) == self->anchor_position &&
|
||||
i != self->anchor_column)
|
||||
{
|
||||
g_print ("BAD: anchor column mismatch (anchor %u, column %u != %u, n_columns %u vs %u, sections %d vs %d)\n",
|
||||
self->anchor_position,
|
||||
self->anchor_column, i,
|
||||
self->anchor_n_columns, self->n_columns,
|
||||
self->anchor_has_sections, gtk_list_item_manager_get_has_sections (self->item_manager));
|
||||
}
|
||||
|
||||
if (gtk_grid_view_is_multirow_tile (self->item_manager, self->n_columns, tile))
|
||||
{
|
||||
g_assert (i == 0);
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
tile,
|
||||
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
|
||||
{
|
||||
/* 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,
|
||||
column_end (self, xspacing, i + tile->n_items - 1) - tile->area.x,
|
||||
unknown_row_height);
|
||||
i += tile->n_items;
|
||||
}
|
||||
i += tile->n_items;
|
||||
}
|
||||
|
||||
if (i >= self->n_columns)
|
||||
{
|
||||
g_assert (i == self->n_columns);
|
||||
y += tile->area.height + yspacing;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
/* Add a filler tile for empty space in the bottom right */
|
||||
if (i > 0)
|
||||
{
|
||||
GtkListTile *footer = gtk_list_item_manager_get_last (self->item_manager);
|
||||
g_assert (gtk_list_tile_is_footer (footer));
|
||||
tile = gtk_rb_tree_node_get_previous (footer);
|
||||
gtk_list_tile_set_area_position (self->item_manager,
|
||||
footer,
|
||||
column_start (self, xspacing, i),
|
||||
y);
|
||||
gtk_list_tile_set_area_size (self->item_manager,
|
||||
footer,
|
||||
column_end (self, xspacing, self->n_columns - 1) - footer->area.x,
|
||||
tile->area.height);
|
||||
}
|
||||
|
||||
/* step 4: allocate the rest */
|
||||
/* step 6: allocate the widgets */
|
||||
gtk_list_base_allocate (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
@@ -952,6 +1207,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);
|
||||
}
|
||||
@@ -974,6 +1230,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;
|
||||
@@ -1018,6 +1278,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;
|
||||
@@ -1071,6 +1335,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;
|
||||
@@ -1109,6 +1375,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)
|
||||
*
|
||||
@@ -1342,6 +1621,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`
|
||||
|
@@ -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
|
||||
|
40
gtk/gtkgridviewprivate.h
Normal file
40
gtk/gtkgridviewprivate.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2023 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtk/gtktypes.h"
|
||||
#include "gtk/gtkenums.h"
|
||||
#include "gtk/gtkgridview.h"
|
||||
#include "gtk/gtklistitemmanagerprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
unsigned int gtk_grid_view_get_column_for_position (GtkListItemManager *items,
|
||||
unsigned int n_columns,
|
||||
unsigned int position);
|
||||
|
||||
gboolean gtk_grid_view_is_multirow_tile (GtkListItemManager *items,
|
||||
unsigned int n_columns,
|
||||
GtkListTile *tile);
|
||||
|
||||
|
||||
void gtk_grid_view_split_tiles_by_columns (GtkListItemManager *items,
|
||||
guint n_columns);
|
||||
|
||||
G_END_DECLS
|
@@ -39,6 +39,8 @@
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
#include <gdk/gdkrgbaprivate.h>
|
||||
|
||||
/* Allow shadows to overdraw without immediately culling the widget at the viewport
|
||||
* boundary.
|
||||
* Choose this so that roughly 1 extra widget gets drawn on each side of the viewport,
|
||||
@@ -1142,6 +1144,124 @@ gtk_list_base_move_cursor (GtkWidget *widget,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GskRenderNode *
|
||||
gtk_list_base_dump_tiles (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkSnapshot *snapshot;
|
||||
GtkListTile *tile;
|
||||
cairo_rectangle_int_t viewport;
|
||||
guint i, focus, anchor, selected;
|
||||
PangoLayout *layout;
|
||||
char *s;
|
||||
|
||||
focus = gtk_list_base_get_focus_position (self);
|
||||
anchor = gtk_list_base_get_anchor (self);
|
||||
selected = gtk_list_item_tracker_get_position (priv->item_manager, priv->selected);
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
|
||||
i = 0;
|
||||
for (tile = gtk_list_item_manager_get_first (priv->item_manager);
|
||||
tile != NULL;
|
||||
tile = gtk_rb_tree_node_get_next (tile))
|
||||
{
|
||||
if (tile->widget)
|
||||
{
|
||||
GdkRGBA color;
|
||||
if (gtk_list_tile_is_header (tile))
|
||||
color = GDK_RGBA("FF00FF");
|
||||
else if (i == focus)
|
||||
color = GDK_RGBA("00FF00");
|
||||
else if (i == anchor)
|
||||
color = GDK_RGBA("FFFF00");
|
||||
else if (i == selected)
|
||||
color = GDK_RGBA("0000FF");
|
||||
else
|
||||
color = GDK_RGBA("FFFFFF");
|
||||
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&color,
|
||||
&GRAPHENE_RECT_INIT(
|
||||
tile->area.x, tile->area.y,
|
||||
tile->area.width, tile->area.height
|
||||
));
|
||||
|
||||
/* This should really look at the ListItem */
|
||||
s = g_strdup_printf ("%u", i);
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), s);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (tile->area.x + 2, tile->area.y + 2));
|
||||
gtk_snapshot_append_layout (snapshot, layout, &GDK_RGBA("000000"));
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- tile->area.x - 2, - tile->area.y - 2));
|
||||
g_object_unref (layout);
|
||||
g_free (s);
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkRGBA color;
|
||||
|
||||
if (gtk_list_tile_is_footer (tile))
|
||||
color = GDK_RGBA("800080");
|
||||
if (tile->n_items == 0)
|
||||
color = GDK_RGBA("A07070");
|
||||
else
|
||||
color = GDK_RGBA("808080");
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&color,
|
||||
&GRAPHENE_RECT_INIT(
|
||||
tile->area.x, tile->area.y,
|
||||
tile->area.width, tile->area.height
|
||||
));
|
||||
}
|
||||
|
||||
gtk_snapshot_append_border (snapshot,
|
||||
&GSK_ROUNDED_RECT_INIT(
|
||||
tile->area.x, tile->area.y,
|
||||
tile->area.width, tile->area.height
|
||||
),
|
||||
(float[4]) { 1, 1, 1, 1 },
|
||||
(GdkRGBA[4]) { GDK_RGBA("000000"), GDK_RGBA("000000"), GDK_RGBA("000000"), GDK_RGBA("000000") });
|
||||
|
||||
i += tile->n_items;
|
||||
}
|
||||
|
||||
gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
|
||||
gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
|
||||
&viewport.y, NULL, &viewport.height);
|
||||
gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
|
||||
gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self)),
|
||||
&viewport.x, NULL, &viewport.width);
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&GDK_RGBA("0000F040"),
|
||||
&GRAPHENE_RECT_INIT(
|
||||
viewport.x, viewport.y,
|
||||
viewport.width, viewport.height
|
||||
));
|
||||
|
||||
|
||||
return gtk_snapshot_free_to_node (snapshot);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_base_copy_tiles_to_clipboard (GtkWidget *widget,
|
||||
GVariant *args,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
||||
GskRenderNode *node;
|
||||
|
||||
node = gtk_list_base_dump_tiles (self);
|
||||
if (node == NULL)
|
||||
return TRUE;
|
||||
|
||||
gdk_clipboard_set (gtk_widget_get_clipboard (widget),
|
||||
GSK_TYPE_RENDER_NODE,
|
||||
node);
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_add_move_binding (GtkWidgetClass *widget_class,
|
||||
guint keyval,
|
||||
@@ -1320,6 +1440,8 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
|
||||
|
||||
gtk_widget_class_add_binding (widget_class, GDK_KEY_R, GDK_CONTROL_MASK | GDK_SHIFT_MASK, gtk_list_base_copy_tiles_to_clipboard, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@@ -95,3 +95,5 @@ GtkListTabBehavior gtk_list_base_get_tab_behavior (GtkListBase
|
||||
|
||||
void gtk_list_base_allocate (GtkListBase *self);
|
||||
|
||||
GskRenderNode *gtk_list_base_dump_tiles (GtkListBase *self);
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
gtk_tests = [
|
||||
# testname, optional extra sources
|
||||
['testsections'],
|
||||
['testfilelauncher'],
|
||||
['input'],
|
||||
['testpopup'],
|
||||
|
317
tests/testsections.c
Normal file
317
tests/testsections.c
Normal file
@@ -0,0 +1,317 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
setup_item (GtkSignalListItemFactory *self,
|
||||
GObject *object)
|
||||
{
|
||||
GtkListItem *list_item = GTK_LIST_ITEM (object);
|
||||
GtkWidget *child = gtk_label_new ("");
|
||||
|
||||
gtk_label_set_xalign (GTK_LABEL (child), 0);
|
||||
gtk_list_item_set_child (list_item, child);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_item (GtkSignalListItemFactory *self,
|
||||
GObject *object)
|
||||
{
|
||||
GtkListItem *list_item = GTK_LIST_ITEM (object);
|
||||
GObject *item = gtk_list_item_get_item (list_item);
|
||||
GtkWidget *child = gtk_list_item_get_child (list_item);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (child),
|
||||
gtk_string_object_get_string (GTK_STRING_OBJECT (item)));
|
||||
}
|
||||
|
||||
static void
|
||||
setup_header (GtkSignalListItemFactory *self,
|
||||
GObject *object)
|
||||
{
|
||||
GtkListHeader *header = GTK_LIST_HEADER (object);
|
||||
GtkWidget *child = gtk_label_new ("");
|
||||
|
||||
gtk_label_set_xalign (GTK_LABEL (child), 0);
|
||||
gtk_list_header_set_child (header, child);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_first (GObject *this)
|
||||
{
|
||||
const char *s = gtk_string_object_get_string (GTK_STRING_OBJECT (this));
|
||||
char buffer[6] = { 0, };
|
||||
|
||||
g_unichar_to_utf8 (g_unichar_toupper (g_utf8_get_char (s)), buffer);
|
||||
|
||||
return g_strdup (buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_header (GtkSignalListItemFactory *self,
|
||||
GObject *object)
|
||||
{
|
||||
GtkListHeader *header = GTK_LIST_HEADER (object);
|
||||
GObject *item = gtk_list_header_get_item (header);
|
||||
GtkWidget *child = gtk_list_header_get_child (header);
|
||||
PangoAttrList *attrs;
|
||||
char *string;
|
||||
|
||||
string = get_first (item);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (child), string);
|
||||
attrs = pango_attr_list_new ();
|
||||
pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
|
||||
pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
|
||||
gtk_label_set_attributes (GTK_LABEL (child), attrs);
|
||||
pango_attr_list_unref (attrs);
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
static const char *strings[] = {
|
||||
"Alpha", "Andromeda", "Anaphylaxis", "Anaheim", "Beer", "Branch", "Botulism", "Banana",
|
||||
"Bee", "Crane", "Caldera", "Copper", "Crowd", "Dora", "Dolphin", "Dam", "Ding",
|
||||
NULL,
|
||||
};
|
||||
|
||||
gboolean done_reading = FALSE;
|
||||
|
||||
static gboolean
|
||||
dump_sections (gpointer data)
|
||||
{
|
||||
GtkSectionModel *model = data;
|
||||
|
||||
if (!done_reading)
|
||||
return G_SOURCE_CONTINUE;
|
||||
|
||||
for (unsigned int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i++)
|
||||
{
|
||||
unsigned int s, e;
|
||||
gtk_section_model_get_section (model, i, &s, &e);
|
||||
g_print ("(%u %u)\n", s, e - 1);
|
||||
i = e;
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
read_lines_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
GBufferedInputStream *stream = G_BUFFERED_INPUT_STREAM (object);
|
||||
GtkStringList *stringlist = data;
|
||||
GError *error = NULL;
|
||||
gsize size;
|
||||
GPtrArray *lines;
|
||||
gssize n_filled;
|
||||
const char *buffer, *newline;
|
||||
|
||||
n_filled = g_buffered_input_stream_fill_finish (stream, result, &error);
|
||||
if (n_filled < 0)
|
||||
{
|
||||
g_print ("Could not read data: %s\n", error->message);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (stringlist);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer = g_buffered_input_stream_peek_buffer (stream, &size);
|
||||
|
||||
if (n_filled == 0)
|
||||
{
|
||||
if (size)
|
||||
gtk_string_list_take (stringlist, g_utf8_make_valid (buffer, size));
|
||||
g_object_unref (stringlist);
|
||||
done_reading = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
lines = NULL;
|
||||
while ((newline = memchr (buffer, '\n', size)))
|
||||
{
|
||||
if (newline > buffer)
|
||||
{
|
||||
if (lines == NULL)
|
||||
lines = g_ptr_array_new_with_free_func (g_free);
|
||||
g_ptr_array_add (lines, g_utf8_make_valid (buffer, newline - buffer));
|
||||
}
|
||||
if (g_input_stream_skip (G_INPUT_STREAM (stream), newline - buffer + 1, NULL, &error) < 0)
|
||||
{
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
buffer = g_buffered_input_stream_peek_buffer (stream, &size);
|
||||
}
|
||||
if (lines == NULL)
|
||||
{
|
||||
g_buffered_input_stream_set_buffer_size (stream, g_buffered_input_stream_get_buffer_size (stream) + 4096);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ptr_array_add (lines, NULL);
|
||||
gtk_string_list_splice (stringlist, g_list_model_get_n_items (G_LIST_MODEL (stringlist)), 0, (const char **) lines->pdata);
|
||||
g_ptr_array_free (lines, TRUE);
|
||||
}
|
||||
|
||||
g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data);
|
||||
}
|
||||
|
||||
static void
|
||||
file_is_open_cb (GObject *file,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GFileInputStream *file_stream;
|
||||
GBufferedInputStream *stream;
|
||||
|
||||
file_stream = g_file_read_finish (G_FILE (file), result, &error);
|
||||
if (file_stream == NULL)
|
||||
{
|
||||
g_print ("Could not open file: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
g_object_unref (data);
|
||||
return;
|
||||
}
|
||||
|
||||
stream = G_BUFFERED_INPUT_STREAM (g_buffered_input_stream_new (G_INPUT_STREAM (file_stream)));
|
||||
g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data);
|
||||
g_object_unref (stream);
|
||||
}
|
||||
|
||||
static void
|
||||
load_file (GtkStringList *list,
|
||||
GFile *file)
|
||||
{
|
||||
gtk_string_list_splice (list, 0, g_list_model_get_n_items (G_LIST_MODEL (list)), NULL);
|
||||
g_file_read_async (file, G_PRIORITY_HIGH_IDLE, NULL, file_is_open_cb, g_object_ref (list));
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_cb (GtkCheckButton *check, GtkWidget *list)
|
||||
{
|
||||
GtkListItemFactory *header_factory = NULL;
|
||||
|
||||
if (gtk_check_button_get_active (check))
|
||||
{
|
||||
header_factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (header_factory, "setup", G_CALLBACK (setup_header), NULL);
|
||||
g_signal_connect (header_factory, "bind", G_CALLBACK (bind_header), NULL);
|
||||
}
|
||||
|
||||
if (GTK_IS_LIST_VIEW (list))
|
||||
gtk_list_view_set_header_factory (GTK_LIST_VIEW (list), header_factory);
|
||||
else
|
||||
gtk_grid_view_set_header_factory (GTK_GRID_VIEW (list), header_factory);
|
||||
|
||||
g_clear_object (&header_factory);
|
||||
}
|
||||
|
||||
static void
|
||||
value_changed_cb (GtkAdjustment *adj, gpointer data)
|
||||
{
|
||||
g_print ("horizontal adjustment changed to %f\n", gtk_adjustment_get_value (adj));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *lv;
|
||||
GtkWidget *gv;
|
||||
GtkWidget *header;
|
||||
GtkWidget *toggle;
|
||||
GtkWidget *switcher;
|
||||
GtkWidget *stack;
|
||||
GtkListItemFactory *factory;
|
||||
GtkExpression *expression;
|
||||
GtkSortListModel *sortmodel;
|
||||
GtkSelectionModel *selection;
|
||||
GtkStringList *stringlist;
|
||||
GtkAdjustment *adj;
|
||||
|
||||
stringlist = gtk_string_list_new (NULL);
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
GFile *file = g_file_new_for_commandline_arg (argv[1]);
|
||||
load_file (stringlist, file);
|
||||
g_object_unref (file);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; strings[i]; i++)
|
||||
gtk_string_list_append (stringlist, strings[i]);
|
||||
done_reading = TRUE;
|
||||
}
|
||||
|
||||
gtk_init ();
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
|
||||
|
||||
header = gtk_header_bar_new ();
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
|
||||
toggle = gtk_check_button_new ();
|
||||
gtk_widget_set_valign (toggle, GTK_ALIGN_CENTER);
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), toggle);
|
||||
|
||||
stack = gtk_stack_new ();
|
||||
gtk_window_set_child (GTK_WINDOW (window), stack);
|
||||
|
||||
switcher = gtk_stack_switcher_new ();
|
||||
gtk_header_bar_set_title_widget (GTK_HEADER_BAR (header), switcher);
|
||||
|
||||
gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack));
|
||||
|
||||
expression = gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string");
|
||||
sortmodel = gtk_sort_list_model_new (G_LIST_MODEL (stringlist),
|
||||
GTK_SORTER (gtk_string_sorter_new (expression)));
|
||||
expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 0, NULL, (GCallback) get_first, NULL, NULL);
|
||||
gtk_sort_list_model_set_section_sorter (sortmodel, GTK_SORTER (gtk_string_sorter_new (expression)));
|
||||
selection = GTK_SELECTION_MODEL (gtk_no_selection_new (G_LIST_MODEL (sortmodel)));
|
||||
|
||||
sw = gtk_scrolled_window_new ();
|
||||
gtk_stack_add_titled (GTK_STACK (stack), sw, "list", "List");
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
|
||||
lv = gtk_list_view_new (g_object_ref (selection), factory);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), lv);
|
||||
|
||||
g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), lv);
|
||||
|
||||
sw = gtk_scrolled_window_new ();
|
||||
gtk_stack_add_titled (GTK_STACK (stack), sw, "grid", "Grid");
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
|
||||
gv = gtk_grid_view_new (g_object_ref (selection), factory);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), gv);
|
||||
|
||||
g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), gv);
|
||||
|
||||
gtk_grid_view_set_min_columns (GTK_GRID_VIEW (gv), 5);
|
||||
|
||||
adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw));
|
||||
g_signal_connect (adj, "value-changed", G_CALLBACK (value_changed_cb), NULL);
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
g_timeout_add (500, dump_sections, selection);
|
||||
|
||||
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||
g_main_context_iteration (NULL, FALSE);
|
||||
|
||||
g_object_unref (selection);
|
||||
g_object_unref (stringlist);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -20,6 +20,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "gtk/gtklistitemmanagerprivate.h"
|
||||
#include "gtk/gtklistbaseprivate.h"
|
||||
#include "gtk/gtkgridviewprivate.h"
|
||||
|
||||
static GListModel *
|
||||
create_source_model (guint min_size, guint max_size)
|
||||
@@ -374,7 +375,43 @@ print_changes_cb (GListModel *model,
|
||||
}
|
||||
|
||||
static void
|
||||
test_exhaustive (void)
|
||||
check_column_tile_invariants (GtkListItemManager *items,
|
||||
unsigned int n_columns)
|
||||
{
|
||||
GtkListTile *tile;
|
||||
|
||||
for (tile = gtk_list_item_manager_get_first (items);
|
||||
tile != NULL;
|
||||
tile = gtk_rb_tree_node_get_next (tile))
|
||||
{
|
||||
g_assert (tile->type != GTK_LIST_TILE_REMOVED);
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
unsigned int pos, col;
|
||||
|
||||
pos = gtk_list_tile_get_position (items, tile);
|
||||
col = gtk_grid_view_get_column_for_position (items, n_columns, pos);
|
||||
|
||||
if (gtk_grid_view_is_multirow_tile (items, n_columns, tile))
|
||||
g_assert_true (col == 0);
|
||||
else
|
||||
g_assert_true (col + tile->n_items - 1 <= n_columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void
|
||||
check_grid_view (GtkListItemManager *items)
|
||||
{
|
||||
for (unsigned int n_columns = 1; n_columns < 10; n_columns++)
|
||||
{
|
||||
gtk_list_item_manager_gc_tiles (items);
|
||||
gtk_grid_view_split_tiles_by_columns (items, n_columns);
|
||||
check_column_tile_invariants (items, n_columns);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_exhaustive (gboolean grid)
|
||||
{
|
||||
GtkListItemTracker *trackers[N_TRACKERS];
|
||||
GListStore *store;
|
||||
@@ -484,7 +521,10 @@ test_exhaustive (void)
|
||||
}
|
||||
}
|
||||
|
||||
check_list_item_manager (items, trackers, N_TRACKERS);
|
||||
if (grid)
|
||||
check_grid_view (items);
|
||||
else
|
||||
check_list_item_manager (items, trackers, N_TRACKERS);
|
||||
|
||||
for (i = 0; i < N_TRACKERS; i++)
|
||||
gtk_list_item_tracker_free (items, trackers[i]);
|
||||
@@ -492,6 +532,18 @@ test_exhaustive (void)
|
||||
gtk_window_destroy (GTK_WINDOW (widget));
|
||||
}
|
||||
|
||||
static void
|
||||
test_exhaustive_list (void)
|
||||
{
|
||||
test_exhaustive (FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
test_exhaustive_grid (void)
|
||||
{
|
||||
test_exhaustive (TRUE);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@@ -499,7 +551,8 @@ main (int argc, char *argv[])
|
||||
|
||||
g_test_add_func ("/listitemmanager/create", test_create);
|
||||
g_test_add_func ("/listitemmanager/create_with_items", test_create_with_items);
|
||||
g_test_add_func ("/listitemmanager/exhaustive", test_exhaustive);
|
||||
g_test_add_func ("/listitemmanager/exhaustive", test_exhaustive_list);
|
||||
g_test_add_func ("/gridview/split", test_exhaustive_grid);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Reference in New Issue
Block a user