From 65ad492c24ba11db52e24f50ce532a19cdd49e75 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 5 May 2012 04:33:47 +0200 Subject: [PATCH] iconview: Redo size requests Instead of just returning the last allocated numbers, we now compute the proper sizes from scratch. This is a bit less trivial, but it results in proper height-for-width handling. --- gtk/gtkiconview.c | 320 ++++++++++++++++++++++++++++++++------- gtk/gtkiconviewprivate.h | 2 - 2 files changed, 263 insertions(+), 59 deletions(-) diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 1afeed674..aeead95a9 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -66,8 +66,6 @@ #define SCROLL_EDGE_SIZE 15 -#define GTK_ICON_VIEW_PRIORITY_LAYOUT (GDK_PRIORITY_REDRAW + 5) - typedef struct _GtkIconViewChild GtkIconViewChild; struct _GtkIconViewChild { @@ -138,12 +136,23 @@ static void gtk_icon_view_unrealize (GtkWidget static void gtk_icon_view_style_updated (GtkWidget *widget); static void gtk_icon_view_state_flags_changed (GtkWidget *widget, GtkStateFlags previous_state); +static GtkSizeRequestMode gtk_icon_view_get_request_mode (GtkWidget *widget); static void gtk_icon_view_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural); +static void gtk_icon_view_get_preferred_width_for_height + (GtkWidget *widget, + gint height, + gint *minimum, + gint *natural); static void gtk_icon_view_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural); +static void gtk_icon_view_get_preferred_height_for_width + (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural); static void gtk_icon_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gboolean gtk_icon_view_draw (GtkWidget *widget, @@ -197,7 +206,6 @@ 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 void gtk_icon_view_queue_layout (GtkIconView *icon_view); static void gtk_icon_view_start_rubberbanding (GtkIconView *icon_view, GdkDevice *device, gint x, @@ -349,8 +357,11 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) widget_class->realize = gtk_icon_view_realize; widget_class->unrealize = gtk_icon_view_unrealize; widget_class->style_updated = gtk_icon_view_style_updated; + widget_class->get_request_mode = gtk_icon_view_get_request_mode; widget_class->get_preferred_width = gtk_icon_view_get_preferred_width; widget_class->get_preferred_height = gtk_icon_view_get_preferred_height; + widget_class->get_preferred_width_for_height = gtk_icon_view_get_preferred_width_for_height; + widget_class->get_preferred_height_for_width = gtk_icon_view_get_preferred_height_for_width; widget_class->size_allocate = gtk_icon_view_size_allocate; widget_class->draw = gtk_icon_view_draw; widget_class->motion_notify_event = gtk_icon_view_motion; @@ -1216,12 +1227,6 @@ gtk_icon_view_destroy (GtkWidget *widget) gtk_icon_view_set_model (icon_view, NULL); - if (icon_view->priv->layout_idle_id != 0) - { - g_source_remove (icon_view->priv->layout_idle_id); - icon_view->priv->layout_idle_id = 0; - } - if (icon_view->priv->scroll_to_path != NULL) { gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); @@ -1360,20 +1365,253 @@ gtk_icon_view_style_updated (GtkWidget *widget) gtk_widget_queue_resize (widget); } +static gint +gtk_icon_view_get_n_items (GtkIconView *icon_view) +{ + GtkIconViewPrivate *priv = icon_view->priv; + + if (priv->model == NULL) + return 0; + + return gtk_tree_model_iter_n_children (priv->model, NULL); +} + +static void +cell_area_get_preferred_size (GtkIconView *icon_view, + GtkCellAreaContext *context, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural) +{ + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size > 0) + gtk_cell_area_get_preferred_width_for_height (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + for_size, + minimum, natural); + else + gtk_cell_area_get_preferred_width (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + minimum, natural); + } + else + { + if (for_size > 0) + gtk_cell_area_get_preferred_height_for_width (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + for_size, + minimum, natural); + else + gtk_cell_area_get_preferred_height (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + minimum, natural); + } +} + +static void +gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural) +{ + GtkIconViewPrivate *priv = icon_view->priv; + GtkCellAreaContext *context; + GList *items; + + context = gtk_cell_area_create_context (priv->cell_area); + + for_size -= 2 * priv->item_padding; + + if (for_size > 0) + { + /* This is necessary for the context to work properly */ + for (items = priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + _gtk_icon_view_set_cell_data (icon_view, item); + cell_area_get_preferred_size (icon_view, context, 1 - orientation, -1, NULL, NULL); + } + } + + for (items = priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + _gtk_icon_view_set_cell_data (icon_view, item); + cell_area_get_preferred_size (icon_view, context, orientation, for_size, NULL, NULL); + } + + cell_area_get_preferred_size (icon_view, context, orientation, for_size, minimum, natural); + + if (minimum) + *minimum += 2 * priv->item_padding; + if (natural) + *natural += 2 * priv->item_padding; + + g_object_unref (context); +} + +static void +gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view, + GtkOrientation orientation, + gint size, + gint *min_columns, + gint *max_columns) +{ + GtkIconViewPrivate *priv = icon_view->priv; + int minimum, natural; + + if (priv->columns > 0) + { + *min_columns = priv->columns; + *max_columns = priv->columns; + return; + } + + size -= 2 * priv->margin; + + gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &minimum, &natural); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + size += priv->column_spacing; + minimum += priv->column_spacing; + natural += priv->column_spacing; + } + else + { + size += priv->row_spacing; + minimum += priv->row_spacing; + natural += priv->row_spacing; + } + + if (size <= minimum) + *max_columns = 1; + else + *max_columns = size / minimum; + if (size <= natural) + *min_columns = 1; + else + *min_columns = size / natural; +} + +static GtkSizeRequestMode +gtk_icon_view_get_request_mode (GtkWidget *widget) +{ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + static void -gtk_icon_view_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural) +gtk_icon_view_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) { - *minimum = *natural = GTK_ICON_VIEW (widget)->priv->width; + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat; + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, -1, &item_min, &item_nat); + + if (priv->columns > 0) + { + *minimum = item_min * priv->columns + priv->column_spacing * (priv->columns - 1); + *natural = item_nat * priv->columns + priv->column_spacing * (priv->columns - 1); + } + else + { + int n_items = gtk_icon_view_get_n_items (icon_view); + + *minimum = n_items ? item_min : 0; + *natural = item_nat * n_items + priv->column_spacing * (n_items - 1); + } + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; } static void -gtk_icon_view_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural) +gtk_icon_view_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum, + gint *natural) { - *minimum = *natural = GTK_ICON_VIEW (widget)->priv->height; + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat, min_rows, max_rows, n_items; + + gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_VERTICAL, height, &min_rows, &max_rows); + n_items = gtk_icon_view_get_n_items (icon_view); + + height = height + priv->row_spacing - 2 * priv->margin; + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, height / max_rows - priv->row_spacing, &item_min, NULL); + *minimum = item_min * ((n_items + max_rows - 1) / max_rows); + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, height / min_rows - priv->row_spacing, NULL, &item_nat); + *natural = item_nat * ((n_items + min_rows - 1) / min_rows); + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; +} + +static void +gtk_icon_view_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat, n_items; + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, -1, &item_min, &item_nat); + n_items = gtk_icon_view_get_n_items (icon_view); + + if (priv->columns > 0) + { + int n_rows = (n_items + priv->columns - 1) / priv->columns; + + *minimum = item_min * n_rows + priv->row_spacing * (n_rows - 1); + *natural = item_nat * n_rows + priv->row_spacing * (n_rows - 1); + } + else + { + *minimum = n_items ? item_min : 0; + *natural = item_nat * n_items + priv->row_spacing * (n_items - 1); + } + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; +} + +static void +gtk_icon_view_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat, min_columns, max_columns, n_items; + + gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_HORIZONTAL, width, &min_columns, &max_columns); + n_items = gtk_icon_view_get_n_items (icon_view); + + width = width + priv->column_spacing - 2 * priv->margin; + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, width / max_columns - priv->column_spacing, &item_min, NULL); + *minimum = (item_min + priv->row_spacing) * ((n_items + max_columns - 1) / max_columns) - priv->row_spacing; + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, width / min_columns - priv->column_spacing, NULL, &item_nat); + *natural = (item_nat + priv->row_spacing) * ((n_items + min_columns - 1) / min_columns) - priv->row_spacing; + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; } static void @@ -1398,6 +1636,8 @@ gtk_icon_view_size_allocate (GtkWidget *widget, gtk_widget_set_allocation (widget, allocation); + gtk_icon_view_layout (icon_view); + if (gtk_widget_get_realized (widget)) { gdk_window_move_resize (gtk_widget_get_window (widget), @@ -2640,12 +2880,6 @@ gtk_icon_view_layout (GtkIconView *icon_view) gint item_width; gboolean size_changed = FALSE; - if (icon_view->priv->layout_idle_id != 0) - { - g_source_remove (icon_view->priv->layout_idle_id); - icon_view->priv->layout_idle_id = 0; - } - if (icon_view->priv->model == NULL) return; @@ -2773,7 +3007,7 @@ gtk_icon_view_invalidate_sizes (GtkIconView *icon_view) } /* Re-layout the items */ - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); } static void @@ -2930,31 +3164,6 @@ gtk_icon_view_queue_draw_item (GtkIconView *icon_view, gdk_window_invalidate_rect (icon_view->priv->bin_window, &rect, TRUE); } -static gboolean -layout_callback (gpointer user_data) -{ - GtkIconView *icon_view; - - icon_view = GTK_ICON_VIEW (user_data); - - icon_view->priv->layout_idle_id = 0; - - gtk_icon_view_layout (icon_view); - - return FALSE; -} - -static void -gtk_icon_view_queue_layout (GtkIconView *icon_view) -{ - if (icon_view->priv->layout_idle_id != 0) - return; - - icon_view->priv->layout_idle_id = - gdk_threads_add_idle_full (GTK_ICON_VIEW_PRIORITY_LAYOUT, - layout_callback, icon_view, NULL); -} - void _gtk_icon_view_set_cursor_item (GtkIconView *icon_view, GtkIconViewItem *item, @@ -3215,7 +3424,7 @@ gtk_icon_view_row_inserted (GtkTreeModel *model, verify_items (icon_view); - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); } static void @@ -3266,7 +3475,7 @@ gtk_icon_view_row_deleted (GtkTreeModel *model, verify_items (icon_view); - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); if (emit) g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); @@ -3314,7 +3523,7 @@ gtk_icon_view_rows_reordered (GtkTreeModel *model, g_list_free (icon_view->priv->items); icon_view->priv->items = items; - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); verify_items (icon_view); } @@ -4763,14 +4972,11 @@ gtk_icon_view_set_model (GtkIconView *icon_view, icon_view); gtk_icon_view_build_items (icon_view); - - gtk_icon_view_layout (icon_view); } g_object_notify (G_OBJECT (icon_view), "model"); - if (gtk_widget_get_realized (GTK_WIDGET (icon_view))) - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); } /** @@ -5426,7 +5632,7 @@ gtk_icon_view_set_columns (GtkIconView *icon_view, if (icon_view->priv->cell_area) gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); g_object_notify (G_OBJECT (icon_view), "columns"); } diff --git a/gtk/gtkiconviewprivate.h b/gtk/gtkiconviewprivate.h index b510037d5..093a19c4d 100644 --- a/gtk/gtkiconviewprivate.h +++ b/gtk/gtkiconviewprivate.h @@ -50,8 +50,6 @@ struct _GtkIconViewPrivate GtkSelectionMode selection_mode; - guint layout_idle_id; - GdkWindow *bin_window; GList *children; -- 2.43.2