diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c index b27fd05f17..95cf2fafd4 100644 --- a/gtk/gtksizerequest.c +++ b/gtk/gtksizerequest.c @@ -31,168 +31,6 @@ #include "gtksizegroup-private.h" #include "gtkwidgetprivate.h" -/* looks for a cached size request for this for_size. If not - * found, returns the oldest entry so it can be overwritten - * - * Note that this caching code was originally derived from - * the Clutter toolkit. - */ - - -/* This function checks if 'request_needed' flag is present - * and resets the cache state if a request is needed for - * a given orientation. - */ -static SizeRequestCache * -init_cache (GtkWidget *widget, - GtkSizeGroupMode orientation) -{ - GtkSizeRequestMode mode; - SizeRequestCache *cache; - - cache = _gtk_widget_peek_request_cache (widget); - - if (orientation == GTK_SIZE_GROUP_HORIZONTAL && - _gtk_widget_get_width_request_needed (widget)) - { - mode = gtk_widget_get_request_mode (widget); - - if (mode == GTK_SIZE_REQUEST_CONSTANT_SIZE) - { - if (cache->sizes) - { - g_slice_free (ContextualSizes, cache->sizes); - cache->sizes = NULL; - } - } - else - { - if (!cache->sizes) - cache->sizes = g_slice_new0 (ContextualSizes); - - memset (cache->sizes->widths, 0x0, GTK_SIZE_REQUEST_CACHED_SIZES * sizeof (SizeRequest)); - cache->sizes->cached_widths = 0; - cache->sizes->last_cached_width = 0; - } - - cache->cached_width.minimum_size = -1; - cache->cached_width.natural_size = -1; - } - else if (orientation == GTK_SIZE_GROUP_VERTICAL && - _gtk_widget_get_height_request_needed (widget)) - { - mode = gtk_widget_get_request_mode (widget); - - if (mode == GTK_SIZE_REQUEST_CONSTANT_SIZE) - { - if (cache->sizes) - { - g_slice_free (ContextualSizes, cache->sizes); - cache->sizes = NULL; - } - } - else - { - if (!cache->sizes) - cache->sizes = g_slice_new0 (ContextualSizes); - - memset (cache->sizes->heights, 0x0, GTK_SIZE_REQUEST_CACHED_SIZES * sizeof (SizeRequest)); - cache->sizes->cached_heights = 0; - cache->sizes->last_cached_height = 0; - } - - cache->cached_height.minimum_size = -1; - cache->cached_height.natural_size = -1; - } - - return cache; -} - -static gboolean -get_cached_size (GtkWidget *widget, - GtkSizeGroupMode orientation, - gint for_size, - CachedSize **result) -{ - SizeRequestCache *cache; - SizeRequest *cached_sizes; - guint i, n_sizes; - - cache = init_cache (widget, orientation); - - if (for_size < 0) - { - if (orientation == GTK_SIZE_GROUP_HORIZONTAL) - *result = &cache->cached_width; - else - *result = &cache->cached_height; - - if ((*result)->minimum_size < 0) - return FALSE; - else - return TRUE; - } - - if (orientation == GTK_SIZE_GROUP_HORIZONTAL) - { - cached_sizes = cache->sizes->widths; - n_sizes = cache->sizes->cached_widths; - } - else - { - cached_sizes = cache->sizes->heights; - n_sizes = cache->sizes->cached_widths; - } - - /* Search for an already cached size */ - for (i = 0; i < n_sizes; i++) - { - if (cached_sizes[i].for_size == for_size) - { - *result = &cached_sizes[i].cached_size; - return TRUE; - } - } - - /* If not found, pull a new size from the cache, the returned size cache - * will immediately be used to cache the new computed size so we go ahead - * and increment the last_cached_width/height right away */ - if (orientation == GTK_SIZE_GROUP_HORIZONTAL) - { - if (cache->sizes->cached_widths < GTK_SIZE_REQUEST_CACHED_SIZES) - { - cache->sizes->cached_widths++; - cache->sizes->last_cached_width = cache->sizes->cached_widths - 1; - } - else - { - if (++cache->sizes->last_cached_width == GTK_SIZE_REQUEST_CACHED_SIZES) - cache->sizes->last_cached_width = 0; - } - - cache->sizes->widths[cache->sizes->last_cached_width].for_size = for_size; - *result = &cache->sizes->widths[cache->sizes->last_cached_width].cached_size; - } - else /* GTK_SIZE_GROUP_VERTICAL */ - { - if (cache->sizes->cached_heights < GTK_SIZE_REQUEST_CACHED_SIZES) - { - cache->sizes->cached_heights++; - cache->sizes->last_cached_height = cache->sizes->cached_heights - 1; - } - else - { - if (++cache->sizes->last_cached_height == GTK_SIZE_REQUEST_CACHED_SIZES) - cache->sizes->last_cached_height = 0; - } - - cache->sizes->heights[cache->sizes->last_cached_height].for_size = for_size; - *result = &cache->sizes->heights[cache->sizes->last_cached_height].cached_size; - } - - return FALSE; -} - #ifndef G_DISABLE_CHECKS static GQuark recursion_check_quark = 0; @@ -245,6 +83,239 @@ pop_recursion_check (GtkWidget *widget, #endif } + +static void +clear_cache (SizeRequestCache *cache, + GtkSizeGroupMode orientation) +{ + SizeRequest **sizes; + gint i; + + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + sizes = cache->widths; + + cache->widths = NULL; + cache->cached_widths = 0; + cache->last_cached_width = 0; + cache->cached_base_width = FALSE; + } + else + { + sizes = cache->heights; + + cache->heights = NULL; + cache->cached_heights = 0; + cache->last_cached_height = 0; + cache->cached_base_height = FALSE; + } + + if (sizes) + { + for (i = 0; sizes[i] != NULL; i++) + g_slice_free (SizeRequest, sizes[i]); + + g_slice_free1 (sizeof (SizeRequest *) * GTK_SIZE_REQUEST_CACHED_SIZES, sizes); + } +} + +void +_gtk_widget_free_cached_sizes (GtkWidget *widget) +{ + SizeRequestCache *cache; + + cache = _gtk_widget_peek_request_cache (widget); + + clear_cache (cache, GTK_SIZE_GROUP_HORIZONTAL); + clear_cache (cache, GTK_SIZE_GROUP_VERTICAL); +} + +/* This function checks if 'request_needed' flag is present + * and resets the cache state if a request is needed for + * a given orientation. + */ +static SizeRequestCache * +init_cache (GtkWidget *widget) +{ + SizeRequestCache *cache; + + cache = _gtk_widget_peek_request_cache (widget); + + if (_gtk_widget_get_width_request_needed (widget)) + clear_cache (cache, GTK_SIZE_GROUP_HORIZONTAL); + else if (_gtk_widget_get_height_request_needed (widget)) + clear_cache (cache, GTK_SIZE_GROUP_VERTICAL); + + return cache; +} + +/* looks for a cached size request for this for_size. If not + * found, returns the oldest entry so it can be overwritten + * + * Note that this caching code was originally derived from + * the Clutter toolkit but has evolved for other GTK+ requirements. + */ +static gboolean +get_cached_size (GtkWidget *widget, + GtkSizeGroupMode orientation, + gint for_size, + CachedSize **result) +{ + SizeRequestCache *cache; + SizeRequest **cached_sizes; + guint i, n_sizes; + + cache = init_cache (widget); + + if (for_size < 0) + { + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + *result = &cache->cached_width; + return cache->cached_base_width; + } + else + { + *result = &cache->cached_height; + return cache->cached_base_height; + } + } + + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + cached_sizes = cache->widths; + n_sizes = cache->cached_widths; + } + else + { + cached_sizes = cache->heights; + n_sizes = cache->cached_heights; + } + + /* Search for an already cached size */ + for (i = 0; i < n_sizes; i++) + { + if (cached_sizes[i]->lower_for_size <= for_size && + cached_sizes[i]->upper_for_size >= for_size) + { + *result = &cached_sizes[i]->cached_size; + return TRUE; + } + } + + return FALSE; +} + +static void +commit_cached_size (GtkWidget *widget, + GtkSizeGroupMode orientation, + gint for_size, + gint minimum_size, + gint natural_size) +{ + SizeRequestCache *cache; + SizeRequest **cached_sizes; + guint i, n_sizes; + + cache = _gtk_widget_peek_request_cache (widget); + + /* First handle caching of the base requests */ + if (for_size < 0) + { + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + cache->cached_width.minimum_size = minimum_size; + cache->cached_width.natural_size = natural_size; + cache->cached_base_width = TRUE; + } + else + { + cache->cached_height.minimum_size = minimum_size; + cache->cached_height.natural_size = natural_size; + cache->cached_base_height = TRUE; + } + return; + } + + /* Check if the minimum_size and natural_size is already + * in the cache and if this result can be used to extend + * that cache entry + */ + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + cached_sizes = cache->widths; + n_sizes = cache->cached_widths; + } + else + { + cached_sizes = cache->heights; + n_sizes = cache->cached_heights; + } + + for (i = 0; i < n_sizes; i++) + { + if (cached_sizes[i]->cached_size.minimum_size == minimum_size && + cached_sizes[i]->cached_size.natural_size == natural_size) + { + cached_sizes[i]->lower_for_size = MIN (cached_sizes[i]->lower_for_size, for_size); + cached_sizes[i]->upper_for_size = MAX (cached_sizes[i]->upper_for_size, for_size); + return; + } + } + + /* If not found, pull a new size from the cache, the returned size cache + * will immediately be used to cache the new computed size so we go ahead + * and increment the last_cached_width/height right away */ + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + if (cache->cached_widths < GTK_SIZE_REQUEST_CACHED_SIZES) + { + cache->cached_widths++; + cache->last_cached_width = cache->cached_widths - 1; + } + else + { + if (++cache->last_cached_width == GTK_SIZE_REQUEST_CACHED_SIZES) + cache->last_cached_width = 0; + } + + if (!cache->widths) + cache->widths = g_slice_alloc0 (sizeof (SizeRequest *) * GTK_SIZE_REQUEST_CACHED_SIZES); + + if (!cache->widths[cache->last_cached_width]) + cache->widths[cache->last_cached_width] = g_slice_new (SizeRequest); + + cache->widths[cache->last_cached_width]->lower_for_size = for_size; + cache->widths[cache->last_cached_width]->upper_for_size = for_size; + cache->widths[cache->last_cached_width]->cached_size.minimum_size = minimum_size; + cache->widths[cache->last_cached_width]->cached_size.natural_size = natural_size; + } + else /* GTK_SIZE_GROUP_VERTICAL */ + { + if (cache->cached_heights < GTK_SIZE_REQUEST_CACHED_SIZES) + { + cache->cached_heights++; + cache->last_cached_height = cache->cached_heights - 1; + } + else + { + if (++cache->last_cached_height == GTK_SIZE_REQUEST_CACHED_SIZES) + cache->last_cached_height = 0; + } + + if (!cache->heights) + cache->heights = g_slice_alloc0 (sizeof (SizeRequest *) * GTK_SIZE_REQUEST_CACHED_SIZES); + + if (!cache->heights[cache->last_cached_height]) + cache->heights[cache->last_cached_height] = g_slice_new (SizeRequest); + + cache->heights[cache->last_cached_height]->lower_for_size = for_size; + cache->heights[cache->last_cached_height]->upper_for_size = for_size; + cache->heights[cache->last_cached_height]->cached_size.minimum_size = minimum_size; + cache->heights[cache->last_cached_height]->cached_size.natural_size = natural_size; + } +} + /* This is the main function that checks for a cached size and * possibly queries the widget class to compute the size if it's * not cached. If the for_size here is -1, then get_preferred_width() @@ -259,14 +330,14 @@ compute_size_for_orientation (GtkWidget *widget, { CachedSize *cached_size; gboolean found_in_cache = FALSE; - gint adjusted_min, adjusted_natural; + gint min_size = 0; + gint nat_size = 0; found_in_cache = get_cached_size (widget, orientation, for_size, &cached_size); if (!found_in_cache) { - gint min_size = 0; - gint nat_size = 0; + gint adjusted_min, adjusted_natural, adjusted_for_size = for_size; if (orientation == GTK_SIZE_GROUP_HORIZONTAL) { @@ -292,11 +363,11 @@ compute_size_for_orientation (GtkWidget *widget, &minimum_height, &natural_height, &ignored_position, - &for_size); + &adjusted_for_size); push_recursion_check (widget, orientation, for_size); GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, - MAX (for_size, minimum_height), + MAX (adjusted_for_size, minimum_height), &min_size, &nat_size); pop_recursion_check (widget, orientation); } @@ -311,9 +382,9 @@ compute_size_for_orientation (GtkWidget *widget, } else { - int ignored_position = 0; - int minimum_width; - int natural_width; + gint ignored_position = 0; + gint minimum_width; + gint natural_width; /* Pull the base natural width from the cache as it's needed to adjust * the proposed 'for_size' */ @@ -325,11 +396,11 @@ compute_size_for_orientation (GtkWidget *widget, &minimum_width, &natural_width, &ignored_position, - &for_size); + &adjusted_for_size); push_recursion_check (widget, orientation, for_size); GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, - MAX (for_size, minimum_width), + MAX (adjusted_for_size, minimum_width), &min_size, &nat_size); pop_recursion_check (widget, orientation); } @@ -341,16 +412,13 @@ compute_size_for_orientation (GtkWidget *widget, G_OBJECT_TYPE_NAME (widget), widget, min_size, nat_size); } - cached_size->minimum_size = min_size; - cached_size->natural_size = nat_size; - if (orientation == GTK_SIZE_GROUP_HORIZONTAL) _gtk_widget_set_width_request_needed (widget, FALSE); else _gtk_widget_set_height_request_needed (widget, FALSE); - adjusted_min = cached_size->minimum_size; - adjusted_natural = cached_size->natural_size; + adjusted_min = min_size; + adjusted_natural = nat_size; GTK_WIDGET_GET_CLASS (widget)->adjust_size_request (widget, orientation == GTK_SIZE_GROUP_HORIZONTAL ? GTK_ORIENTATION_HORIZONTAL : @@ -358,14 +426,14 @@ compute_size_for_orientation (GtkWidget *widget, &adjusted_min, &adjusted_natural); - if (adjusted_min < cached_size->minimum_size || - adjusted_natural < cached_size->natural_size) + if (adjusted_min < min_size || + adjusted_natural < nat_size) { g_warning ("%s %p adjusted size %s min %d natural %d must not decrease below min %d natural %d", G_OBJECT_TYPE_NAME (widget), widget, orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal", adjusted_min, adjusted_natural, - cached_size->minimum_size, cached_size->natural_size); + min_size, nat_size); /* don't use the adjustment */ } else if (adjusted_min > adjusted_natural) @@ -374,14 +442,14 @@ compute_size_for_orientation (GtkWidget *widget, G_OBJECT_TYPE_NAME (widget), widget, orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal", adjusted_min, adjusted_natural, - cached_size->minimum_size, cached_size->natural_size); + min_size, nat_size); /* don't use the adjustment */ } else { /* adjustment looks good */ - cached_size->minimum_size = adjusted_min; - cached_size->natural_size = adjusted_natural; + min_size = adjusted_min; + nat_size = adjusted_natural; } /* Update size-groups with our request and update our cached requests @@ -389,26 +457,31 @@ compute_size_for_orientation (GtkWidget *widget, */ _gtk_size_group_bump_requisition (widget, orientation, - &cached_size->minimum_size, - &cached_size->natural_size); + &min_size, + &nat_size); + + commit_cached_size (widget, orientation, for_size, min_size, nat_size); + } + else + { + min_size = cached_size->minimum_size; + nat_size = cached_size->natural_size; } if (minimum_size) - *minimum_size = cached_size->minimum_size; + *minimum_size = min_size; if (natural_size) - *natural_size = cached_size->natural_size; + *natural_size = nat_size; - g_assert (cached_size->minimum_size <= cached_size->natural_size); + g_assert (min_size <= nat_size); GTK_NOTE (SIZE_REQUEST, g_print ("[%p] %s\t%s: %d is minimum %d and natural: %d (hit cache: %s)\n", widget, G_OBJECT_TYPE_NAME (widget), orientation == GTK_SIZE_GROUP_HORIZONTAL ? "width for height" : "height for width" , - for_size, - cached_size->minimum_size, - cached_size->natural_size, + for_size, min_size, nat_size, found_in_cache ? "yes" : "no")); } diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 00f9be591f..714f850b1b 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -10669,8 +10669,7 @@ gtk_widget_finalize (GObject *object) if (priv->context) g_object_unref (priv->context); - if (priv->requests.sizes) - g_slice_free (ContextualSizes, priv->requests.sizes); + _gtk_widget_free_cached_sizes (widget); if (g_object_is_floating (object)) g_warning ("A floating object was finalized. This means that someone\n" diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 9dfec658e2..7d298c6471 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -29,12 +29,13 @@ G_BEGIN_DECLS -/* With GtkWidget, a widget may be requested - * its width for 2 or maximum 3 heights in one resize - * (Note this define is limited by the bitfield sizes - * defined on the SizeRequestCache structure). +/* Cache as many ranges of height-for-width + * (or width-for-height) as can be rational + * for a said widget to have, if a label can + * only wrap to 3 lines, only 3 caches will + * ever be allocated for it. */ -#define GTK_SIZE_REQUEST_CACHED_SIZES (2) +#define GTK_SIZE_REQUEST_CACHED_SIZES (5) typedef struct { gint minimum_size; @@ -43,26 +44,24 @@ typedef struct { typedef struct { - /* the size this request is for */ - gint for_size; + gint lower_for_size; /* The minimum for_size with the same result */ + gint upper_for_size; /* The maximum for_size with the same result */ CachedSize cached_size; } SizeRequest; typedef struct { - SizeRequest widths[GTK_SIZE_REQUEST_CACHED_SIZES]; - SizeRequest heights[GTK_SIZE_REQUEST_CACHED_SIZES]; + SizeRequest **widths; + SizeRequest **heights; - guint cached_widths : 2; - guint cached_heights : 2; - guint last_cached_width : 2; - guint last_cached_height : 2; -} ContextualSizes; + CachedSize cached_width; + CachedSize cached_height; -typedef struct { - ContextualSizes *sizes; - - CachedSize cached_width; - CachedSize cached_height; + guint cached_widths : 3; + guint cached_heights : 3; + guint last_cached_width : 3; + guint last_cached_height : 3; + guint cached_base_width : 1; + guint cached_base_height : 1; } SizeRequestCache; void _gtk_widget_set_visible_flag (GtkWidget *widget, @@ -114,6 +113,7 @@ gboolean _gtk_widget_get_translation_to_window (GtkWidget *widget, GdkWindow *window, int *x, int *y); +void _gtk_widget_free_cached_sizes (GtkWidget *widget); G_END_DECLS