summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Van Berkom <tristan.van.berkom@gmail.com>2012-07-31 12:29:49 -0400
committerEmmanuele Bassi <ebassi@gnome.org>2012-08-20 17:52:38 +0100
commitd037890fc4a4d488a521af666ddcb3945fe64aff (patch)
tree6c2a985daafb7b1a82e63b054d95988e38b33a77
parent8536314dbff5212e7afac29343ef67c46dfb30b2 (diff)
downloadclutter-d037890fc4a4d488a521af666ddcb3945fe64aff.tar.gz
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
-rw-r--r--clutter/clutter-box-layout.c362
1 files changed, 247 insertions, 115 deletions
diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c
index b585c628f..a87b32bf2 100644
--- a/clutter/clutter-box-layout.c
+++ b/clutter/clutter-box-layout.c
@@ -159,6 +159,23 @@ G_DEFINE_TYPE (ClutterBoxLayout,
clutter_box_layout,
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
*/
@@ -451,158 +468,261 @@ clutter_box_layout_set_container (ClutterLayoutManager *layout,
}
static void
-get_preferred_width (ClutterBoxLayout *self,
- ClutterActor *container,
- gfloat for_height,
- gfloat *min_width_p,
- gfloat *natural_width_p)
+get_child_size (ClutterActor *actor,
+ ClutterOrientation orientation,
+ gfloat for_size,
+ gfloat *min_size_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;
+ ClutterActorIter iter;
ClutterActor *child;
gint n_children = 0;
- gboolean is_rtl, is_vertical;
-
- if (min_width_p)
- *min_width_p = 0;
+ gfloat minimum, natural;
- if (natural_width_p)
- *natural_width_p = 0;
+ minimum = natural = 0;
- if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
+ clutter_actor_iter_init (&iter, container);
+ while (clutter_actor_iter_next (&iter, &child))
{
- ClutterTextDirection text_dir;
+ gfloat child_min = 0, child_nat = 0;
- text_dir = clutter_actor_get_text_direction (container);
- is_rtl = (text_dir == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE;
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ n_children++;
+
+ get_child_size (child, priv->orientation,
+ for_size, &child_min, &child_nat);
+
+ minimum += child_min;
+ natural += child_nat;
}
- else
- is_rtl = FALSE;
- is_vertical = priv->orientation == CLUTTER_ORIENTATION_VERTICAL;
+ if (n_children > 1)
+ {
+ minimum += priv->spacing * (n_children - 1);
+ natural += priv->spacing * (n_children - 1);
+ }
- 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))
+ if (min_size_p)
+ *min_size_p = minimum;
+
+ if (natural_size_p)
+ *natural_size_p = natural;
+}
+
+static void
+get_base_size_for_opposite_orientation (ClutterBoxLayout *self,
+ ClutterActor *container,
+ gfloat *min_size_p,
+ gfloat *natural_size_p)
+{
+ ClutterBoxLayoutPrivate *priv = self->priv;
+ ClutterActorIter iter;
+ ClutterActor *child;
+ gint n_children = 0;
+ gfloat minimum, natural;
+ ClutterOrientation opposite_orientation =
+ priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL
+ ? CLUTTER_ORIENTATION_VERTICAL
+ : CLUTTER_ORIENTATION_HORIZONTAL;
+
+ minimum = natural = 0;
+
+ clutter_actor_iter_init (&iter, container);
+ while (clutter_actor_iter_next (&iter, &child))
{
gfloat child_min = 0, child_nat = 0;
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
- continue;
+ continue;
n_children++;
- clutter_actor_get_preferred_width (child,
- !is_vertical ? for_height : -1,
- &child_min,
- &child_nat);
-
- if (is_vertical)
- {
- 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;
+ get_child_size (child, opposite_orientation, -1, &child_min, &child_nat);
- if (natural_width_p)
- *natural_width_p += child_nat;
- }
+ minimum = MAX (minimum, child_min);
+ natural = MAX (natural, child_nat);
}
+ if (min_size_p)
+ *min_size_p = minimum;
- if (!is_vertical && n_children > 1)
- {
- if (min_width_p)
- *min_width_p += priv->spacing * (n_children - 1);
-
- if (natural_width_p)
- *natural_width_p += priv->spacing * (n_children - 1);
- }
+ 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_height (ClutterBoxLayout *self,
- ClutterActor *container,
- gfloat for_width,
- gfloat *min_height_p,
- gfloat *natural_height_p)
+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;
- gint n_children = 0;
- gboolean is_rtl, is_vertical;
+ 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;
- if (min_height_p)
- *min_height_p = 0;
+ minimum = natural = 0;
- if (natural_height_p)
- *natural_height_p = 0;
+ count_expand_children (layout, real_container,
+ &nvis_children, &nexpand_children);
- if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
+ if (nvis_children < 1)
{
- ClutterTextDirection text_dir;
+ if (min_size_p)
+ *min_size_p = 0;
- text_dir = clutter_actor_get_text_direction (container);
- is_rtl = (text_dir == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE;
+ 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
- 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)
- : clutter_actor_get_first_child (container);
- child != NULL;
- child = (is_rtl) ? clutter_actor_get_previous_sibling (child)
- : clutter_actor_get_next_sibling (child))
+ /* Distribute expand space to children */
+ i = 0;
+ clutter_actor_iter_init (&iter, container);
+ while (clutter_actor_iter_next (&iter, &child))
{
- gfloat child_min = 0, child_nat = 0;
+ ClutterLayoutMeta *meta;
+ ClutterBoxChild *box_child;
+ /* If widget is not visible, skip it. */
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
continue;
- n_children++;
-
- clutter_actor_get_preferred_height (child,
- is_vertical ? for_width : -1,
- &child_min,
- &child_nat);
+ meta = clutter_layout_manager_get_child_meta (layout, real_container, child);
+ box_child = CLUTTER_BOX_CHILD (meta);
- if (!is_vertical)
- {
- if (min_height_p)
- *min_height_p = MAX (child_min, *min_height_p);
+ if (priv->is_homogeneous)
+ {
+ sizes[i].minimum_size = extra;
- if (natural_height_p)
- *natural_height_p = MAX (child_nat, *natural_height_p);
- }
+ if (n_extra_widgets > 0)
+ {
+ sizes[i].minimum_size++;
+ n_extra_widgets--;
+ }
+ }
else
- {
- if (min_height_p)
- *min_height_p += child_min;
+ {
+ if (clutter_actor_needs_expand (child, priv->orientation) || box_child->expand)
+ {
+ sizes[i].minimum_size += extra;
- if (natural_height_p)
- *natural_height_p += child_nat;
- }
+ if (n_extra_widgets > 0)
+ {
+ sizes[i].minimum_size++;
+ n_extra_widgets--;
+ }
+ }
+ }
+ i++;
}
- if (is_vertical && n_children > 1)
+ /* 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))
{
- if (min_height_p)
- *min_height_p += priv->spacing * (n_children - 1);
+ gfloat child_min = 0, child_nat = 0;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ get_child_size (child, opposite_orientation,
+ sizes[i].minimum_size,
+ &child_min, &child_nat);
- if (natural_height_p)
- *natural_height_p += priv->spacing * (n_children - 1);
+ minimum = MAX (minimum, child_min);
+ natural = MAX (natural, child_nat);
+
+ i++;
}
+
+ if (min_size_p)
+ *min_size_p = minimum;
+
+ if (natural_size_p)
+ *natural_size_p = natural;
}
+
static void
allocate_box_child (ClutterBoxLayout *self,
ClutterContainer *container,
@@ -657,11 +777,21 @@ clutter_box_layout_get_preferred_width (ClutterLayoutManager *layout,
gfloat *min_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,
- min_width_p,
- natural_width_p);
+ if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL)
+ {
+ 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
@@ -671,11 +801,21 @@ clutter_box_layout_get_preferred_height (ClutterLayoutManager *layout,
gfloat *min_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,
- min_height_p,
- natural_height_p);
+ if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
+ {
+ 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
@@ -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+ */
static gint
compare_gap (gconstpointer p1,