gtkiconview: slice layout work across main loop iterations
Make GtkIconView layout resumable and time-slice row processing so large models do not monopolize the main loop. Preserve layout state across idle iterations, avoid redundant relayout from size allocation, and keep hit testing and visible-range queries safe while layout is still in progress.
This commit is contained in:
parent
6501755d7b
commit
7af69af9c8
1 changed files with 116 additions and 40 deletions
|
|
@ -120,6 +120,12 @@ struct _GtkIconViewPrivate
|
|||
GtkAdjustment *vadjustment;
|
||||
|
||||
guint layout_idle_id;
|
||||
GList *layout_items;
|
||||
gint layout_y, layout_maximum_width;
|
||||
gint layout_row, layout_item_width, layout_next_index;
|
||||
guint layout_in_progress : 1;
|
||||
guint layout_scanning_width : 1;
|
||||
guint layout_restart : 1;
|
||||
|
||||
gboolean doing_rubberband;
|
||||
gint rubberband_x1, rubberband_y1;
|
||||
|
|
@ -304,6 +310,7 @@ static void gtk_icon_view_queue_draw_path (GtkIco
|
|||
GtkTreePath *path);
|
||||
static void gtk_icon_view_queue_draw_item (GtkIconView *icon_view,
|
||||
GtkIconViewItem *item);
|
||||
static gboolean layout_callback (gpointer user_data);
|
||||
static void gtk_icon_view_queue_layout (GtkIconView *icon_view);
|
||||
static void gtk_icon_view_set_cursor_item (GtkIconView *icon_view,
|
||||
GtkIconViewItem *item,
|
||||
|
|
@ -1491,6 +1498,8 @@ gtk_icon_view_size_allocate (GtkWidget *widget,
|
|||
GtkIconView *icon_view = GTK_ICON_VIEW (widget);
|
||||
|
||||
GtkAdjustment *hadjustment, *vadjustment;
|
||||
gboolean relayout = (icon_view->priv->layout_idle_id != 0 ||
|
||||
widget->allocation.width != allocation->width);
|
||||
|
||||
widget->allocation = *allocation;
|
||||
|
||||
|
|
@ -1504,7 +1513,8 @@ gtk_icon_view_size_allocate (GtkWidget *widget,
|
|||
MAX (icon_view->priv->height, allocation->height));
|
||||
}
|
||||
|
||||
gtk_icon_view_layout (icon_view);
|
||||
if (relayout)
|
||||
gtk_icon_view_layout (icon_view);
|
||||
|
||||
gtk_icon_view_allocate_children (icon_view);
|
||||
|
||||
|
|
@ -1588,6 +1598,10 @@ gtk_icon_view_expose (GtkWidget *widget,
|
|||
for (icons = icon_view->priv->items; icons; icons = icons->next)
|
||||
{
|
||||
GtkIconViewItem *item = icons->data;
|
||||
|
||||
if (icon_view->priv->layout_in_progress &&
|
||||
item->index >= icon_view->priv->layout_next_index)
|
||||
break;
|
||||
GdkRectangle area;
|
||||
|
||||
area.x = item->x;
|
||||
|
|
@ -2810,11 +2824,8 @@ gtk_icon_view_set_adjustment_upper (GtkAdjustment *adj,
|
|||
static void
|
||||
gtk_icon_view_layout (GtkIconView *icon_view)
|
||||
{
|
||||
gint y = 0, maximum_width = 0;
|
||||
GList *icons;
|
||||
GtkWidget *widget;
|
||||
gint row;
|
||||
gint item_width;
|
||||
gint64 deadline;
|
||||
|
||||
if (icon_view->priv->layout_idle_id != 0)
|
||||
{
|
||||
|
|
@ -2823,54 +2834,100 @@ gtk_icon_view_layout (GtkIconView *icon_view)
|
|||
}
|
||||
|
||||
if (icon_view->priv->model == NULL)
|
||||
return;
|
||||
{
|
||||
icon_view->priv->layout_in_progress = FALSE;
|
||||
icon_view->priv->layout_items = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
widget = GTK_WIDGET (icon_view);
|
||||
deadline = g_get_monotonic_time () + 2000;
|
||||
|
||||
item_width = icon_view->priv->item_width;
|
||||
|
||||
if (item_width < 0)
|
||||
if (!icon_view->priv->layout_in_progress ||
|
||||
icon_view->priv->layout_restart)
|
||||
{
|
||||
for (icons = icon_view->priv->items; icons; icons = icons->next)
|
||||
{
|
||||
GtkIconViewItem *item = icons->data;
|
||||
gtk_icon_view_calculate_item_size (icon_view, item);
|
||||
item_width = MAX (item_width, item->width);
|
||||
}
|
||||
icon_view->priv->layout_in_progress = TRUE;
|
||||
icon_view->priv->layout_restart = FALSE;
|
||||
icon_view->priv->layout_items = icon_view->priv->items;
|
||||
icon_view->priv->layout_y = icon_view->priv->margin;
|
||||
icon_view->priv->layout_maximum_width = 0;
|
||||
icon_view->priv->layout_row = 0;
|
||||
icon_view->priv->layout_item_width = icon_view->priv->item_width;
|
||||
icon_view->priv->layout_next_index = 0;
|
||||
icon_view->priv->layout_scanning_width =
|
||||
icon_view->priv->layout_item_width < 0;
|
||||
|
||||
if (!icon_view->priv->layout_scanning_width &&
|
||||
icon_view->priv->layout_items)
|
||||
{
|
||||
gtk_icon_view_set_cell_data (icon_view,
|
||||
icon_view->priv->layout_items->data);
|
||||
adjust_wrap_width (icon_view, icon_view->priv->layout_items->data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
icons = icon_view->priv->items;
|
||||
y += icon_view->priv->margin;
|
||||
row = 0;
|
||||
|
||||
if (icons)
|
||||
while (icon_view->priv->layout_scanning_width &&
|
||||
icon_view->priv->layout_items)
|
||||
{
|
||||
gtk_icon_view_set_cell_data (icon_view, icons->data);
|
||||
adjust_wrap_width (icon_view, icons->data);
|
||||
GtkIconViewItem *item = icon_view->priv->layout_items->data;
|
||||
|
||||
gtk_icon_view_calculate_item_size (icon_view, item);
|
||||
icon_view->priv->layout_item_width =
|
||||
MAX (icon_view->priv->layout_item_width, item->width);
|
||||
icon_view->priv->layout_items = icon_view->priv->layout_items->next;
|
||||
|
||||
if (g_get_monotonic_time () >= deadline)
|
||||
goto yield;
|
||||
}
|
||||
|
||||
do
|
||||
if (icon_view->priv->layout_scanning_width)
|
||||
{
|
||||
icons = gtk_icon_view_layout_single_row (icon_view, icons,
|
||||
item_width, row,
|
||||
&y, &maximum_width);
|
||||
row++;
|
||||
icon_view->priv->layout_scanning_width = FALSE;
|
||||
icon_view->priv->layout_items = icon_view->priv->items;
|
||||
|
||||
if (icon_view->priv->layout_items)
|
||||
{
|
||||
gtk_icon_view_set_cell_data (icon_view,
|
||||
icon_view->priv->layout_items->data);
|
||||
adjust_wrap_width (icon_view, icon_view->priv->layout_items->data);
|
||||
}
|
||||
}
|
||||
while (icons != NULL);
|
||||
|
||||
if (maximum_width != icon_view->priv->width)
|
||||
icon_view->priv->width = maximum_width;
|
||||
while (icon_view->priv->layout_items)
|
||||
{
|
||||
icon_view->priv->layout_items =
|
||||
gtk_icon_view_layout_single_row (icon_view,
|
||||
icon_view->priv->layout_items,
|
||||
icon_view->priv->layout_item_width,
|
||||
icon_view->priv->layout_row++,
|
||||
&icon_view->priv->layout_y,
|
||||
&icon_view->priv->layout_maximum_width);
|
||||
|
||||
y += icon_view->priv->margin;
|
||||
icon_view->priv->layout_next_index =
|
||||
icon_view->priv->layout_items
|
||||
? ((GtkIconViewItem *) icon_view->priv->layout_items->data)->index
|
||||
: G_MAXINT;
|
||||
|
||||
if (y != icon_view->priv->height)
|
||||
icon_view->priv->height = y;
|
||||
if (icon_view->priv->layout_restart ||
|
||||
(icon_view->priv->layout_items &&
|
||||
g_get_monotonic_time () >= deadline))
|
||||
goto yield;
|
||||
}
|
||||
|
||||
icon_view->priv->layout_in_progress = FALSE;
|
||||
|
||||
if (icon_view->priv->layout_maximum_width != icon_view->priv->width)
|
||||
icon_view->priv->width = icon_view->priv->layout_maximum_width;
|
||||
|
||||
icon_view->priv->layout_y += icon_view->priv->margin;
|
||||
|
||||
if (icon_view->priv->layout_y != icon_view->priv->height)
|
||||
icon_view->priv->height = icon_view->priv->layout_y;
|
||||
|
||||
gtk_icon_view_set_adjustment_upper (icon_view->priv->hadjustment,
|
||||
icon_view->priv->width);
|
||||
icon_view->priv->width);
|
||||
gtk_icon_view_set_adjustment_upper (icon_view->priv->vadjustment,
|
||||
icon_view->priv->height);
|
||||
icon_view->priv->height);
|
||||
|
||||
if (icon_view->priv->width != widget->requisition.width ||
|
||||
icon_view->priv->height != widget->requisition.height)
|
||||
|
|
@ -2878,8 +2935,8 @@ gtk_icon_view_layout (GtkIconView *icon_view)
|
|||
|
||||
if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
|
||||
gdk_window_resize (icon_view->priv->bin_window,
|
||||
MAX (icon_view->priv->width, widget->allocation.width),
|
||||
MAX (icon_view->priv->height, widget->allocation.height));
|
||||
MAX (icon_view->priv->width, widget->allocation.width),
|
||||
MAX (icon_view->priv->height, widget->allocation.height));
|
||||
|
||||
if (icon_view->priv->scroll_to_path)
|
||||
{
|
||||
|
|
@ -2890,13 +2947,21 @@ gtk_icon_view_layout (GtkIconView *icon_view)
|
|||
icon_view->priv->scroll_to_path = NULL;
|
||||
|
||||
gtk_icon_view_scroll_to_path (icon_view, path,
|
||||
icon_view->priv->scroll_to_use_align,
|
||||
icon_view->priv->scroll_to_row_align,
|
||||
icon_view->priv->scroll_to_col_align);
|
||||
icon_view->priv->scroll_to_use_align,
|
||||
icon_view->priv->scroll_to_row_align,
|
||||
icon_view->priv->scroll_to_col_align);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
return;
|
||||
|
||||
yield:
|
||||
if (icon_view->priv->layout_idle_id == 0)
|
||||
icon_view->priv->layout_idle_id =
|
||||
gdk_threads_add_idle (layout_callback, icon_view);
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -3409,6 +3474,9 @@ layout_callback (gpointer user_data)
|
|||
static void
|
||||
gtk_icon_view_queue_layout (GtkIconView *icon_view)
|
||||
{
|
||||
if (icon_view->priv->layout_in_progress)
|
||||
icon_view->priv->layout_restart = TRUE;
|
||||
|
||||
if (icon_view->priv->layout_idle_id != 0)
|
||||
return;
|
||||
|
||||
|
|
@ -3500,6 +3568,10 @@ gtk_icon_view_get_item_at_coords (GtkIconView *icon_view,
|
|||
{
|
||||
GtkIconViewItem *item = items->data;
|
||||
|
||||
if (icon_view->priv->layout_in_progress &&
|
||||
item->index >= icon_view->priv->layout_next_index)
|
||||
break;
|
||||
|
||||
if (x >= item->x - icon_view->priv->column_spacing/2 && x <= item->x + item->width + icon_view->priv->column_spacing/2 &&
|
||||
y >= item->y - icon_view->priv->row_spacing/2 && y <= item->y + item->height + icon_view->priv->row_spacing/2)
|
||||
{
|
||||
|
|
@ -5207,6 +5279,10 @@ gtk_icon_view_get_visible_range (GtkIconView *icon_view,
|
|||
{
|
||||
GtkIconViewItem *item = icons->data;
|
||||
|
||||
if (icon_view->priv->layout_in_progress &&
|
||||
item->index >= icon_view->priv->layout_next_index)
|
||||
break;
|
||||
|
||||
if ((item->x + item->width >= (int)icon_view->priv->hadjustment->value) &&
|
||||
(item->y + item->height >= (int)icon_view->priv->vadjustment->value) &&
|
||||
(item->x <= (int) (icon_view->priv->hadjustment->value + icon_view->priv->hadjustment->page_size)) &&
|
||||
|
|
|
|||
Loading…
Reference in a new issue