diff options
Diffstat (limited to 'gtk/gtkextendedlayout.c')
-rw-r--r-- | gtk/gtkextendedlayout.c | 444 |
1 files changed, 192 insertions, 252 deletions
diff --git a/gtk/gtkextendedlayout.c b/gtk/gtkextendedlayout.c index dcc3e6ca4e..0ca1658aa3 100644 --- a/gtk/gtkextendedlayout.c +++ b/gtk/gtkextendedlayout.c @@ -31,6 +31,28 @@ #define DEBUG_EXTENDED_LAYOUT 0 +/* With extended layout, a widget may be requested + * its width for 2 or 3 heights in one resize + */ +#define N_CACHED_SIZES 3 + +typedef struct +{ + guint age; + gint for_size; + gint minimum_size; + gint natural_size; +} DesiredSize; + +typedef struct { + DesiredSize desired_widths[N_CACHED_SIZES]; + DesiredSize desired_heights[N_CACHED_SIZES]; + guint cached_width_age; + guint cached_height_age; +} ExtendedLayoutCache; + +static GQuark quark_cache = 0; + GType gtk_extended_layout_get_type (void) @@ -45,28 +67,27 @@ gtk_extended_layout_get_type (void) NULL, 0, NULL, 0); g_type_interface_add_prerequisite (extended_layout_type, GTK_TYPE_WIDGET); + + quark_cache = g_quark_from_static_string ("gtk-extended-layout-cache"); } return extended_layout_type; } - - - /* looks for a cached size request for this for_size. If not * found, returns the oldest entry so it can be overwritten */ static gboolean -_gtk_extended_layout_get_cached_desired_size (gfloat for_size, - GtkDesiredSize *cached_sizes, - GtkDesiredSize **result) +get_cached_desired_size (gfloat for_size, + DesiredSize *cached_sizes, + DesiredSize **result) { guint i; *result = &cached_sizes[0]; - for (i = 0; i < GTK_N_CACHED_SIZES; i++) + for (i = 0; i < N_CACHED_SIZES; i++) { - GtkDesiredSize *cs; + DesiredSize *cs; cs = &cached_sizes[i]; @@ -85,6 +106,165 @@ _gtk_extended_layout_get_cached_desired_size (gfloat for_size, return FALSE; } +static void +destroy_cache (ExtendedLayoutCache *cache) +{ + g_slice_free (ExtendedLayoutCache, cache); +} + +ExtendedLayoutCache * +get_cache (GtkExtendedLayout *layout, gboolean create) +{ + ExtendedLayoutCache *cache; + + cache = g_object_get_qdata (G_OBJECT (layout), quark_cache); + if (!cache && create) + { + cache = g_slice_new0 (ExtendedLayoutCache); + + cache->cached_width_age = 1; + cache->cached_height_age = 1; + + g_object_set_qdata_full (G_OBJECT (layout), quark_cache, cache, + (GDestroyNotify)destroy_cache); + } + + return cache; +} + +static void +do_size_request (GtkWidget *widget) +{ + if (GTK_WIDGET_REQUEST_NEEDED (widget)) + { + gtk_widget_ensure_style (widget); + GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED); + g_signal_emit_by_name (widget, + "size-request", + &widget->requisition); + } +} + +static void +compute_size_for_orientation (GtkExtendedLayout *layout, + GtkSizeGroupMode orientation, + gint for_size, + gint *minimum_size, + gint *natural_size) +{ + ExtendedLayoutCache *cache; + DesiredSize *cached_size; + GtkWidget *widget; + gboolean found_in_cache = FALSE; + + g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout)); + g_return_if_fail (minimum_size != NULL || natural_size != NULL); + + widget = GTK_WIDGET (layout); + cache = get_cache (layout, TRUE); + + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + cached_size = &cache->desired_widths[0]; + + if (GTK_WIDGET_WIDTH_REQUEST_NEEDED (layout) == FALSE) + found_in_cache = get_cached_desired_size (for_size, cache->desired_widths, &cached_size); + } + else + { + cached_size = &cache->desired_heights[0]; + + if (GTK_WIDGET_WIDTH_REQUEST_NEEDED (layout) == FALSE) + found_in_cache = get_cached_desired_size (for_size, cache->desired_heights, &cached_size); + } + + if (!found_in_cache) + { + gint min_size = 0, nat_size = 0; + gint group_size, requisition_size; + + /* Unconditional size request runs but is often unhandled. */ + do_size_request (widget); + + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + requisition_size = widget->requisition.width; + + if (for_size < 0) + GTK_EXTENDED_LAYOUT_GET_IFACE (layout)-> + get_desired_width (layout, &min_size, &nat_size); + else + GTK_EXTENDED_LAYOUT_GET_IFACE (layout)-> + get_width_for_height (layout, for_size, &min_size, &nat_size); + } + else + { + requisition_size = widget->requisition.height; + + if (for_size < 0) + GTK_EXTENDED_LAYOUT_GET_IFACE (layout)-> + get_desired_height (layout, &min_size, &nat_size); + else + GTK_EXTENDED_LAYOUT_GET_IFACE (layout)-> + get_height_for_width (layout, for_size, &min_size, &nat_size); + } + + /* Support for dangling "size-request" signals and forward derived + * classes that will not default to a ->get_desired_width() that + * returns the values in the ->requisition cache. + */ + min_size = MAX (min_size, requisition_size); + nat_size = MAX (nat_size, requisition_size); + + cached_size->minimum_size = min_size; + cached_size->natural_size = nat_size; + cached_size->for_size = for_size; + + if (orientation == GTK_SIZE_GROUP_HORIZONTAL) + { + cached_size->age = cache->cached_width_age; + cache->cached_width_age++; + + GTK_PRIVATE_UNSET_FLAG (layout, GTK_WIDTH_REQUEST_NEEDED); + } + else + { + cached_size->age = cache->cached_height_age; + cache->cached_height_age++; + + GTK_PRIVATE_UNSET_FLAG (layout, GTK_HEIGHT_REQUEST_NEEDED); + } + + /* Get size groups to compute the base requisition once one of the values have been cached, + * then go ahead and update the cache with the sizegroup computed value. + */ + group_size = + _gtk_size_group_bump_requisition (GTK_WIDGET (layout), + orientation, cached_size->minimum_size); + cached_size->minimum_size = MAX (cached_size->minimum_size, group_size); + cached_size->natural_size = MAX (cached_size->natural_size, group_size); + } + + /* Output the MAX()s of the cached size and the size computed by GtkSizeGroup. */ + if (minimum_size) + *minimum_size = cached_size->minimum_size; + + if (natural_size) + *natural_size = cached_size->natural_size; + + g_assert (cached_size->minimum_size <= cached_size->natural_size); + +#if DEBUG_EXTENDED_LAYOUT + g_message ("%s size for orientation %s: %d is minimum %d and natural: %d", + G_OBJECT_TYPE_NAME (layout), + orientation == GTK_SIZE_GROUP_HORIZONTAL ? "horizontal" : "vertical", + height, + cached_size->minimum_size, + cached_size->natural_size); +#endif + +} + /** * gtk_extended_layout_is_height_for_width: * @layout: a #GtkExtendedLayout instance @@ -127,66 +307,7 @@ gtk_extended_layout_get_desired_width (GtkExtendedLayout *layout, gint *minimum_width, gint *natural_width) { - GtkWidgetAuxInfo *aux_info; - gboolean found_in_cache = FALSE; - GtkDesiredSize *cached_size; - - g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout)); - g_return_if_fail (minimum_width != NULL || natural_width != NULL); - - aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (layout), TRUE); - - cached_size = &aux_info->desired_widths[0]; - - if (GTK_WIDGET_WIDTH_REQUEST_NEEDED (layout) == FALSE) - found_in_cache = _gtk_extended_layout_get_cached_desired_size (-1, aux_info->desired_widths, - &cached_size); - - if (!found_in_cache) - { - GtkRequisition requisition; - gint minimum_width = 0, natural_width = 0; - - /* Unconditionally envoke size-request and use those return values as - * the base end of our values */ - _gtk_size_group_compute_requisition (GTK_WIDGET (layout), &requisition); - - /* Envoke this after, default GtkWidgetClass will simply copy over widget->requisition - */ - GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_desired_width (layout, - &minimum_width, - &natural_width); - - minimum_width = MAX (minimum_width, requisition.width); - natural_width = MAX (natural_width, requisition.width); - - /* XXX Possibly we should update this with minimum values instead */ - _gtk_size_group_bump_requisition (GTK_WIDGET (layout), GTK_SIZE_GROUP_HORIZONTAL, natural_width); - - cached_size->minimum_size = minimum_width; - cached_size->natural_size = natural_width; - cached_size->for_size = -1; - cached_size->age = aux_info->cached_width_age; - - aux_info->cached_width_age ++; - - GTK_PRIVATE_UNSET_FLAG (layout, GTK_WIDTH_REQUEST_NEEDED); - } - - if (minimum_width) - *minimum_width = cached_size->minimum_size; - - if (natural_width) - *natural_width = cached_size->natural_size; - - g_assert (!minimum_width || !natural_width || *minimum_width <= *natural_width); - -#if DEBUG_EXTENDED_LAYOUT - g_message ("%s returning minimum width: %d and natural width: %d", - G_OBJECT_TYPE_NAME (layout), - cached_size->minimum_size, - cached_size->natural_size); -#endif + compute_size_for_orientation (layout, GTK_SIZE_GROUP_HORIZONTAL, -1, minimum_width, natural_width); } @@ -205,66 +326,7 @@ gtk_extended_layout_get_desired_height (GtkExtendedLayout *layout, gint *minimum_height, gint *natural_height) { - GtkWidgetAuxInfo *aux_info; - gboolean found_in_cache = FALSE; - GtkDesiredSize *cached_size; - - g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout)); - g_return_if_fail (minimum_height != NULL || natural_height != NULL); - - aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (layout), TRUE); - - cached_size = &aux_info->desired_heights[0]; - - if (GTK_WIDGET_HEIGHT_REQUEST_NEEDED (layout) == FALSE) - found_in_cache = _gtk_extended_layout_get_cached_desired_size (-1, aux_info->desired_heights, - &cached_size); - - if (!found_in_cache) - { - GtkRequisition requisition; - gint minimum_height = 0, natural_height = 0; - - /* Unconditionally envoke size-request and use those return values as - * the base end of our values */ - _gtk_size_group_compute_requisition (GTK_WIDGET (layout), &requisition); - - /* Envoke this after, default GtkWidgetClass will simply copy over widget->requisition - */ - GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_desired_height (layout, - &minimum_height, - &natural_height); - - minimum_height = MAX (minimum_height, requisition.height); - natural_height = MAX (natural_height, requisition.height); - - /* XXX Possibly we should update this with minimum values instead */ - _gtk_size_group_bump_requisition (GTK_WIDGET (layout), GTK_SIZE_GROUP_VERTICAL, natural_height); - - cached_size->minimum_size = minimum_height; - cached_size->natural_size = natural_height; - cached_size->for_size = -1; - cached_size->age = aux_info->cached_height_age; - - aux_info->cached_height_age ++; - - GTK_PRIVATE_UNSET_FLAG (layout, GTK_HEIGHT_REQUEST_NEEDED); - } - - if (minimum_height) - *minimum_height = cached_size->minimum_size; - - if (natural_height) - *natural_height = cached_size->natural_size; - - g_assert (!minimum_height || !natural_height || *minimum_height <= *natural_height); - -#if DEBUG_EXTENDED_LAYOUT - g_message ("%s returning minimum height: %d and natural height: %d", - G_OBJECT_TYPE_NAME (layout), - cached_size->minimum_size, - cached_size->natural_size); -#endif + compute_size_for_orientation (layout, GTK_SIZE_GROUP_VERTICAL, -1, minimum_height, natural_height); } @@ -287,67 +349,7 @@ gtk_extended_layout_get_width_for_height (GtkExtendedLayout *layout, gint *minimum_width, gint *natural_width) { - GtkWidgetAuxInfo *aux_info; - gboolean found_in_cache = FALSE; - GtkDesiredSize *cached_size; - - g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout)); - g_return_if_fail (minimum_width != NULL || natural_width != NULL); - - aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (layout), TRUE); - - cached_size = &aux_info->desired_widths[0]; - - if (GTK_WIDGET_WIDTH_REQUEST_NEEDED (layout) == FALSE) - found_in_cache = _gtk_extended_layout_get_cached_desired_size (height, aux_info->desired_widths, - &cached_size); - - if (!found_in_cache) - { - GtkRequisition requisition; - gint minimum_width = 0, natural_width = 0; - - /* Unconditionally envoke size-request and use those return values as - * the base end of our values */ - _gtk_size_group_compute_requisition (GTK_WIDGET (layout), &requisition); - - GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_width_for_height (layout, - height, - &minimum_width, - &natural_width); - - minimum_width = MAX (minimum_width, requisition.width); - natural_width = MAX (natural_width, requisition.width); - - /* XXX Possibly we should update this with minimum values instead */ - _gtk_size_group_bump_requisition (GTK_WIDGET (layout), GTK_SIZE_GROUP_HORIZONTAL, natural_width); - - cached_size->minimum_size = minimum_width; - cached_size->natural_size = natural_width; - cached_size->for_size = height; - cached_size->age = aux_info->cached_width_age; - - aux_info->cached_width_age++; - - GTK_PRIVATE_UNSET_FLAG (layout, GTK_WIDTH_REQUEST_NEEDED); - } - - - if (minimum_width) - *minimum_width = cached_size->minimum_size; - - if (natural_width) - *natural_width = cached_size->natural_size; - - g_assert (!minimum_width || !natural_width || *minimum_width <= *natural_width); - -#if DEBUG_EXTENDED_LAYOUT - g_message ("%s width for height: %d is minimum %d and natural: %d", - G_OBJECT_TYPE_NAME (layout), height, - cached_size->minimum_size, - cached_size->natural_size); -#endif - + compute_size_for_orientation (layout, GTK_SIZE_GROUP_HORIZONTAL, height, minimum_width, natural_width); } /** @@ -368,71 +370,9 @@ gtk_extended_layout_get_height_for_width (GtkExtendedLayout *layout, gint *minimum_height, gint *natural_height) { - GtkWidgetAuxInfo *aux_info; - gboolean found_in_cache = FALSE; - GtkDesiredSize *cached_size; - - g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout)); - g_return_if_fail (minimum_height != NULL || natural_height != NULL); - - aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (layout), TRUE); - - cached_size = &aux_info->desired_heights[0]; - - if (GTK_WIDGET_HEIGHT_REQUEST_NEEDED (layout) == FALSE) - found_in_cache = _gtk_extended_layout_get_cached_desired_size (width, aux_info->desired_heights, - &cached_size); - - if (!found_in_cache) - { - GtkRequisition requisition; - gint minimum_height = 0, natural_height = 0; - - /* Unconditionally envoke size-request and use those return values as - * the base end of our values */ - _gtk_size_group_compute_requisition (GTK_WIDGET (layout), &requisition); - - GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_height_for_width (layout, - width, - &minimum_height, - &natural_height); - - minimum_height = MAX (minimum_height, requisition.height); - natural_height = MAX (natural_height, requisition.height); - - /* XXX Possibly we should update this with minimum values instead */ - _gtk_size_group_bump_requisition (GTK_WIDGET (layout), GTK_SIZE_GROUP_VERTICAL, natural_height); - - cached_size->minimum_size = minimum_height; - cached_size->natural_size = natural_height; - cached_size->for_size = width; - cached_size->age = aux_info->cached_height_age; - - aux_info->cached_height_age++; - - GTK_PRIVATE_UNSET_FLAG (layout, GTK_HEIGHT_REQUEST_NEEDED); - } - - - if (minimum_height) - *minimum_height = cached_size->minimum_size; - - if (natural_height) - *natural_height = cached_size->natural_size; - - g_assert (!minimum_height || !natural_height || *minimum_height <= *natural_height); - -#if DEBUG_EXTENDED_LAYOUT - g_message ("%s height for width: %d is minimum %d and natural: %d", - G_OBJECT_TYPE_NAME (layout), width, - cached_size->minimum_size, - cached_size->natural_size); -#endif + compute_size_for_orientation (layout, GTK_SIZE_GROUP_VERTICAL, width, minimum_height, natural_height); } - - - /** * gtk_extended_layout_get_desired_size: * @layout: a #GtkExtendedLayout instance |