ClutterBoxLayout: Blessing with proper h4w geometry management

The box layout was broken for height-for-width requests in the opposing orientation of the box.

https://bugzilla.gnome.org/show_bug.cgi?id=679483
This commit is contained in:
Tristan Van Berkom 2012-07-31 12:29:49 -04:00 committed by Emmanuele Bassi
parent 8536314dbf
commit d037890fc4

View File

@ -159,6 +159,23 @@ G_DEFINE_TYPE (ClutterBoxLayout,
clutter_box_layout, clutter_box_layout,
CLUTTER_TYPE_LAYOUT_MANAGER); CLUTTER_TYPE_LAYOUT_MANAGER);
typedef struct _RequestedSize
{
ClutterActor *actor;
gfloat minimum_size;
gfloat natural_size;
} RequestedSize;
static gint distribute_natural_allocation (gint extra_space,
guint n_requested_sizes,
RequestedSize *sizes);
static void count_expand_children (ClutterLayoutManager *layout,
ClutterContainer *container,
gint *visible_children,
gint *expand_children);
/* /*
* ClutterBoxChild * ClutterBoxChild
*/ */
@ -451,158 +468,261 @@ clutter_box_layout_set_container (ClutterLayoutManager *layout,
} }
static void static void
get_preferred_width (ClutterBoxLayout *self, get_child_size (ClutterActor *actor,
ClutterActor *container, ClutterOrientation orientation,
gfloat for_height, gfloat for_size,
gfloat *min_width_p, gfloat *min_size_p,
gfloat *natural_width_p) gfloat *natural_size_p)
{
if (orientation == CLUTTER_ORIENTATION_HORIZONTAL)
clutter_actor_get_preferred_width (actor, for_size, min_size_p, natural_size_p);
else
clutter_actor_get_preferred_height (actor, for_size, min_size_p, natural_size_p);
}
/* Handle the request in the orientation of the box (i.e. width request of horizontal box) */
static void
get_preferred_size_for_orientation (ClutterBoxLayout *self,
ClutterActor *container,
gfloat for_size,
gfloat *min_size_p,
gfloat *natural_size_p)
{ {
ClutterBoxLayoutPrivate *priv = self->priv; ClutterBoxLayoutPrivate *priv = self->priv;
ClutterActorIter iter;
ClutterActor *child; ClutterActor *child;
gint n_children = 0; gint n_children = 0;
gboolean is_rtl, is_vertical; gfloat minimum, natural;
if (min_width_p) minimum = natural = 0;
*min_width_p = 0;
if (natural_width_p) clutter_actor_iter_init (&iter, container);
*natural_width_p = 0; while (clutter_actor_iter_next (&iter, &child))
if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
{
ClutterTextDirection text_dir;
text_dir = clutter_actor_get_text_direction (container);
is_rtl = (text_dir == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE;
}
else
is_rtl = FALSE;
is_vertical = priv->orientation == CLUTTER_ORIENTATION_VERTICAL;
for (child = (is_rtl) ? clutter_actor_get_last_child (container)
: clutter_actor_get_first_child (container);
child != NULL;
child = (is_rtl) ? clutter_actor_get_previous_sibling (child)
: clutter_actor_get_next_sibling (child))
{ {
gfloat child_min = 0, child_nat = 0; gfloat child_min = 0, child_nat = 0;
if (!CLUTTER_ACTOR_IS_VISIBLE (child)) if (!CLUTTER_ACTOR_IS_VISIBLE (child))
continue; continue;
n_children++; n_children++;
clutter_actor_get_preferred_width (child, get_child_size (child, priv->orientation,
!is_vertical ? for_height : -1, for_size, &child_min, &child_nat);
&child_min,
&child_nat);
if (is_vertical) minimum += child_min;
{ natural += child_nat;
if (min_width_p)
*min_width_p = MAX (child_min, *min_width_p);
if (natural_width_p)
*natural_width_p = MAX (child_nat, *natural_width_p);
}
else
{
if (min_width_p)
*min_width_p += child_min;
if (natural_width_p)
*natural_width_p += child_nat;
}
} }
if (n_children > 1)
if (!is_vertical && n_children > 1)
{ {
if (min_width_p) minimum += priv->spacing * (n_children - 1);
*min_width_p += priv->spacing * (n_children - 1); natural += priv->spacing * (n_children - 1);
if (natural_width_p)
*natural_width_p += priv->spacing * (n_children - 1);
} }
if (min_size_p)
*min_size_p = minimum;
if (natural_size_p)
*natural_size_p = natural;
} }
static void static void
get_preferred_height (ClutterBoxLayout *self, get_base_size_for_opposite_orientation (ClutterBoxLayout *self,
ClutterActor *container, ClutterActor *container,
gfloat for_width, gfloat *min_size_p,
gfloat *min_height_p, gfloat *natural_size_p)
gfloat *natural_height_p)
{ {
ClutterBoxLayoutPrivate *priv = self->priv; ClutterBoxLayoutPrivate *priv = self->priv;
ClutterActorIter iter;
ClutterActor *child; ClutterActor *child;
gint n_children = 0; gint n_children = 0;
gboolean is_rtl, is_vertical; gfloat minimum, natural;
ClutterOrientation opposite_orientation =
priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL
? CLUTTER_ORIENTATION_VERTICAL
: CLUTTER_ORIENTATION_HORIZONTAL;
if (min_height_p) minimum = natural = 0;
*min_height_p = 0;
if (natural_height_p) clutter_actor_iter_init (&iter, container);
*natural_height_p = 0; while (clutter_actor_iter_next (&iter, &child))
if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
{ {
ClutterTextDirection text_dir; gfloat child_min = 0, child_nat = 0;
text_dir = clutter_actor_get_text_direction (container); if (!CLUTTER_ACTOR_IS_VISIBLE (child))
is_rtl = (text_dir == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE; continue;
n_children++;
get_child_size (child, opposite_orientation, -1, &child_min, &child_nat);
minimum = MAX (minimum, child_min);
natural = MAX (natural, child_nat);
}
if (min_size_p)
*min_size_p = minimum;
if (natural_size_p)
*natural_size_p = natural;
}
/* Handle the request in the opposite orientation of the box
* (i.e. height request of horizontal box)
*
* This operation requires a virtual allocation in the natural
* orientation of the box, after that each element must be asked
* for the size-for-virtually-allocated-size and the maximums of
* each child sample will be reported as the overall
* "size-for-size-in-opposite-orientation"
*/
static void
get_preferred_size_for_opposite_orientation (ClutterBoxLayout *self,
ClutterActor *container,
gfloat for_size,
gfloat *min_size_p,
gfloat *natural_size_p)
{
ClutterLayoutManager *layout = CLUTTER_LAYOUT_MANAGER (self);
ClutterBoxLayoutPrivate *priv = self->priv;
ClutterContainer *real_container = CLUTTER_CONTAINER (container);
ClutterActor *child;
ClutterActorIter iter;
gint nvis_children = 0, n_extra_widgets = 0;
gint nexpand_children = 0, i;
RequestedSize *sizes;
gfloat minimum, natural, size, extra = 0;
ClutterOrientation opposite_orientation =
priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL
? CLUTTER_ORIENTATION_VERTICAL
: CLUTTER_ORIENTATION_HORIZONTAL;
minimum = natural = 0;
count_expand_children (layout, real_container,
&nvis_children, &nexpand_children);
if (nvis_children < 1)
{
if (min_size_p)
*min_size_p = 0;
if (natural_size_p)
*natural_size_p = 0;
return;
}
/* First collect the requested sizes in the natural orientation of the box */
sizes = g_newa (RequestedSize, nvis_children);
size = for_size;
i = 0;
clutter_actor_iter_init (&iter, container);
while (clutter_actor_iter_next (&iter, &child))
{
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
continue;
get_child_size (child, priv->orientation, -1,
&sizes[i].minimum_size,
&sizes[i].natural_size);
size -= sizes[i].minimum_size;
i++;
}
if (priv->is_homogeneous)
{
size = for_size - (nvis_children - 1) * priv->spacing;
extra = size / nvis_children;
n_extra_widgets = ((gint)size) % nvis_children;
} }
else else
is_rtl = FALSE; {
/* Bring children up to size first */
size = distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
is_vertical = priv->orientation == CLUTTER_ORIENTATION_VERTICAL; /* Calculate space which hasn't distributed yet,
* and is available for expanding children.
*/
if (nexpand_children > 0)
{
extra = size / nexpand_children;
n_extra_widgets = ((gint)size) % nexpand_children;
}
}
for (child = (is_rtl) ? clutter_actor_get_last_child (container) /* Distribute expand space to children */
: clutter_actor_get_first_child (container); i = 0;
child != NULL; clutter_actor_iter_init (&iter, container);
child = (is_rtl) ? clutter_actor_get_previous_sibling (child) while (clutter_actor_iter_next (&iter, &child))
: clutter_actor_get_next_sibling (child)) {
ClutterLayoutMeta *meta;
ClutterBoxChild *box_child;
/* If widget is not visible, skip it. */
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
continue;
meta = clutter_layout_manager_get_child_meta (layout, real_container, child);
box_child = CLUTTER_BOX_CHILD (meta);
if (priv->is_homogeneous)
{
sizes[i].minimum_size = extra;
if (n_extra_widgets > 0)
{
sizes[i].minimum_size++;
n_extra_widgets--;
}
}
else
{
if (clutter_actor_needs_expand (child, priv->orientation) || box_child->expand)
{
sizes[i].minimum_size += extra;
if (n_extra_widgets > 0)
{
sizes[i].minimum_size++;
n_extra_widgets--;
}
}
}
i++;
}
/* Virtual allocation finished, now we can finally ask for the right size-for-size */
i = 0;
clutter_actor_iter_init (&iter, container);
while (clutter_actor_iter_next (&iter, &child))
{ {
gfloat child_min = 0, child_nat = 0; gfloat child_min = 0, child_nat = 0;
if (!CLUTTER_ACTOR_IS_VISIBLE (child)) if (!CLUTTER_ACTOR_IS_VISIBLE (child))
continue; continue;
n_children++; get_child_size (child, opposite_orientation,
sizes[i].minimum_size,
&child_min, &child_nat);
clutter_actor_get_preferred_height (child, minimum = MAX (minimum, child_min);
is_vertical ? for_width : -1, natural = MAX (natural, child_nat);
&child_min,
&child_nat);
if (!is_vertical) i++;
{
if (min_height_p)
*min_height_p = MAX (child_min, *min_height_p);
if (natural_height_p)
*natural_height_p = MAX (child_nat, *natural_height_p);
}
else
{
if (min_height_p)
*min_height_p += child_min;
if (natural_height_p)
*natural_height_p += child_nat;
}
} }
if (is_vertical && n_children > 1) if (min_size_p)
{ *min_size_p = minimum;
if (min_height_p)
*min_height_p += priv->spacing * (n_children - 1);
if (natural_height_p) if (natural_size_p)
*natural_height_p += priv->spacing * (n_children - 1); *natural_size_p = natural;
}
} }
static void static void
allocate_box_child (ClutterBoxLayout *self, allocate_box_child (ClutterBoxLayout *self,
ClutterContainer *container, ClutterContainer *container,
@ -657,11 +777,21 @@ clutter_box_layout_get_preferred_width (ClutterLayoutManager *layout,
gfloat *min_width_p, gfloat *min_width_p,
gfloat *natural_width_p) gfloat *natural_width_p)
{ {
ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout); ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout);
ClutterBoxLayoutPrivate *priv = self->priv;
get_preferred_width (self, CLUTTER_ACTOR (container), for_height, if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL)
min_width_p, {
natural_width_p); if (for_height < 0)
get_base_size_for_opposite_orientation (self, CLUTTER_ACTOR (container),
min_width_p, natural_width_p);
else
get_preferred_size_for_opposite_orientation (self, CLUTTER_ACTOR (container), for_height,
min_width_p, natural_width_p);
}
else
get_preferred_size_for_orientation (self, CLUTTER_ACTOR (container), for_height,
min_width_p, natural_width_p);
} }
static void static void
@ -671,11 +801,21 @@ clutter_box_layout_get_preferred_height (ClutterLayoutManager *layout,
gfloat *min_height_p, gfloat *min_height_p,
gfloat *natural_height_p) gfloat *natural_height_p)
{ {
ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout); ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout);
ClutterBoxLayoutPrivate *priv = self->priv;
get_preferred_height (self, CLUTTER_ACTOR (container), for_width, if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
min_height_p, {
natural_height_p); if (for_width < 0)
get_base_size_for_opposite_orientation (self, CLUTTER_ACTOR (container),
min_height_p, natural_height_p);
else
get_preferred_size_for_opposite_orientation (self, CLUTTER_ACTOR (container), for_width,
min_height_p, natural_height_p);
}
else
get_preferred_size_for_orientation (self, CLUTTER_ACTOR (container), for_width,
min_height_p, natural_height_p);
} }
static void static void
@ -712,14 +852,6 @@ count_expand_children (ClutterLayoutManager *layout,
} }
} }
typedef struct _RequestedSize
{
ClutterActor *actor;
gfloat minimum_size;
gfloat natural_size;
} RequestedSize;
/* Pulled from gtksizerequest.c from Gtk+ */ /* Pulled from gtksizerequest.c from Gtk+ */
static gint static gint
compare_gap (gconstpointer p1, compare_gap (gconstpointer p1,