[clutter] Sync allocation in ClutterBoxLayout with the one in GtkBox
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [clutter] Sync allocation in ClutterBoxLayout with the one in GtkBox
- Date: Tue, 7 Jun 2011 13:44:21 +0000 (UTC)
commit e636a0bbce2b24fdb4e99dcf2d4385eb25d0247a
Author: Tomeu Vizoso <tomeu vizoso collabora co uk>
Date: Wed May 18 16:10:18 2011 +0200
Sync allocation in ClutterBoxLayout with the one in GtkBox
https://bugzilla.gnome.org/show_bug.cgi?id=650487
Signed-off-by: Emmanuele Bassi <ebassi linux intel com>
clutter/clutter-box-layout.c | 512 ++++++++++++++++++++++++++++++------------
1 files changed, 370 insertions(+), 142 deletions(-)
---
diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c
index e4ed96a..ba71599 100644
--- a/clutter/clutter-box-layout.c
+++ b/clutter/clutter-box-layout.c
@@ -631,66 +631,20 @@ static void
allocate_box_child (ClutterBoxLayout *self,
ClutterContainer *container,
ClutterActor *child,
- gfloat *position,
- gfloat avail_width,
- gfloat avail_height,
- gfloat extra_space,
+ ClutterActorBox *child_box,
ClutterAllocationFlags flags)
{
ClutterBoxLayoutPrivate *priv = self->priv;
- ClutterActorBox child_box;
- ClutterBoxChild *box_child;
- ClutterLayoutMeta *meta;
- gfloat child_nat;
-
- if (!CLUTTER_ACTOR_IS_VISIBLE (child))
- return;
+ ClutterBoxChild *box_child;
+ ClutterLayoutMeta *meta;
+ ClutterActorBox final_child_box;
meta = clutter_layout_manager_get_child_meta (CLUTTER_LAYOUT_MANAGER (self),
container,
child);
box_child = CLUTTER_BOX_CHILD (meta);
- if (priv->is_vertical)
- {
- clutter_actor_get_preferred_height (child, avail_width,
- NULL, &child_nat);
-
- child_nat = MIN (child_nat, avail_height);
-
- child_box.y1 = floorf (*position + 0.5);
-
- if (priv->is_homogeneous)
- child_box.y2 = floorf (*position + extra_space + 0.5);
- else if (box_child->expand)
- child_box.y2 = floorf (*position + child_nat + extra_space + 0.5);
- else
- child_box.y2 = floorf (*position + child_nat + 0.5);
-
- child_box.x1 = 0;
- child_box.x2 = floorf (avail_width + 0.5);
- }
- else
- {
- clutter_actor_get_preferred_width (child, avail_height,
- NULL, &child_nat);
-
- child_nat = MIN (child_nat, avail_width);
-
- child_box.x1 = floorf (*position + 0.5);
-
- if (priv->is_homogeneous)
- child_box.x2 = floorf (*position + extra_space + 0.5);
- else if (box_child->expand)
- child_box.x2 = floorf (*position + child_nat + extra_space + 0.5);
- else
- child_box.x2 = floorf (*position + child_nat + 0.5);
-
- child_box.y1 = 0;
- child_box.y2 = floorf (avail_height + 0.5);
- }
-
- clutter_actor_allocate_align_fill (child, &child_box,
+ clutter_actor_allocate_align_fill (child, child_box,
get_box_alignment_factor (box_child->x_align),
get_box_alignment_factor (box_child->y_align),
box_child->x_fill,
@@ -700,7 +654,7 @@ allocate_box_child (ClutterBoxLayout *self,
/* retrieve the allocation computed and set by allocate_align_fill();
* since we call this *after* allocate(), it's just a cheap copy
*/
- clutter_actor_get_allocation_box (child, &child_box);
+ clutter_actor_get_allocation_box (child, &final_child_box);
if (priv->use_animations && priv->is_animating)
{
@@ -716,17 +670,17 @@ allocate_box_child (ClutterBoxLayout *self,
* been added to the container; we put it in the final state
* and store its allocation for later
*/
- box_child->last_allocation = child_box;
+ box_child->last_allocation = final_child_box;
box_child->has_last_allocation = TRUE;
goto do_allocate;
}
start = &box_child->last_allocation;
- end = child_box;
+ end = final_child_box;
/* interpolate between the initial and final values */
- clutter_actor_box_interpolate (start, &end, p, &child_box);
+ clutter_actor_box_interpolate (start, &end, p, &final_child_box);
CLUTTER_NOTE (ANIMATION,
"Animate { %.1f, %.1f, %.1f, %.1f }\t"
@@ -735,27 +689,20 @@ allocate_box_child (ClutterBoxLayout *self,
start->x1, start->y1,
start->x2, start->y2,
p,
- child_box.x1, child_box.y1,
- child_box.x2, child_box.y2,
+ final_child_box.x1, final_child_box.y1,
+ final_child_box.x2, final_child_box.y2,
end.x1, end.y1,
end.x2, end.y2);
}
else
{
/* store the allocation for later animations */
- box_child->last_allocation = child_box;
+ box_child->last_allocation = final_child_box;
box_child->has_last_allocation = TRUE;
}
do_allocate:
- clutter_actor_allocate (child, &child_box, flags);
-
- if (priv->is_homogeneous)
- *position += (priv->spacing + extra_space);
- else if (box_child->expand)
- *position += (child_nat + priv->spacing + extra_space);
- else
- *position += (child_nat + priv->spacing);
+ clutter_actor_allocate (child, &final_child_box, flags);
}
static void
@@ -797,88 +744,268 @@ clutter_box_layout_get_preferred_height (ClutterLayoutManager *layout,
}
static void
+count_expand_children (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ gint *visible_children,
+ gint *expand_children)
+{
+ GList *children;
+ ClutterActor *child;
+
+ *visible_children = *expand_children = 0;
+
+ for (children = clutter_container_get_children (container);
+ children;
+ children = children->next)
+ {
+ child = children->data;
+
+ if (CLUTTER_ACTOR_IS_VISIBLE (child))
+ {
+ ClutterLayoutMeta *meta;
+
+ *visible_children += 1;
+
+ meta = clutter_layout_manager_get_child_meta (layout,
+ container,
+ child);
+
+ if (CLUTTER_BOX_CHILD (meta)->expand)
+ *expand_children += 1;
+ }
+ }
+ g_list_free (children);
+}
+
+struct _ClutterRequestedSize
+{
+ ClutterActor *actor;
+
+ gfloat minimum_size;
+ gfloat natural_size;
+};
+
+/* Pulled from gtksizerequest.c from Gtk+ */
+
+static gint
+compare_gap (gconstpointer p1,
+ gconstpointer p2,
+ gpointer data)
+{
+ struct _ClutterRequestedSize *sizes = data;
+ const guint *c1 = p1;
+ const guint *c2 = p2;
+
+ const gint d1 = MAX (sizes[*c1].natural_size -
+ sizes[*c1].minimum_size,
+ 0);
+ const gint d2 = MAX (sizes[*c2].natural_size -
+ sizes[*c2].minimum_size,
+ 0);
+
+ gint delta = (d2 - d1);
+
+ if (0 == delta)
+ delta = (*c2 - *c1);
+
+ return delta;
+}
+
+/*
+ * distribute_natural_allocation:
+ * @extra_space: Extra space to redistribute among children after subtracting
+ * minimum sizes and any child padding from the overall allocation
+ * @n_requested_sizes: Number of requests to fit into the allocation
+ * @sizes: An array of structs with a client pointer and a minimum/natural size
+ * in the orientation of the allocation.
+ *
+ * Distributes @extra_space to child @sizes by bringing smaller
+ * children up to natural size first.
+ *
+ * The remaining space will be added to the @minimum_size member of the
+ * GtkRequestedSize struct. If all sizes reach their natural size then
+ * the remaining space is returned.
+ *
+ * Returns: The remainder of @extra_space after redistributing space
+ * to @sizes.
+ *
+ * Pulled from gtksizerequest.c from Gtk+
+ */
+static gint
+distribute_natural_allocation (gint extra_space,
+ guint n_requested_sizes,
+ struct _ClutterRequestedSize *sizes)
+{
+ guint *spreading;
+ gint i;
+
+ g_return_val_if_fail (extra_space >= 0, 0);
+
+ spreading = g_newa (guint, n_requested_sizes);
+
+ for (i = 0; i < n_requested_sizes; i++)
+ spreading[i] = i;
+
+ /* Distribute the container's extra space c_gap. We want to assign
+ * this space such that the sum of extra space assigned to children
+ * (c^i_gap) is equal to c_cap. The case that there's not enough
+ * space for all children to take their natural size needs some
+ * attention. The goals we want to achieve are:
+ *
+ * a) Maximize number of children taking their natural size.
+ * b) The allocated size of children should be a continuous
+ * function of c_gap. That is, increasing the container size by
+ * one pixel should never make drastic changes in the distribution.
+ * c) If child i takes its natural size and child j doesn't,
+ * child j should have received at least as much gap as child i.
+ *
+ * The following code distributes the additional space by following
+ * these rules.
+ */
+
+ /* Sort descending by gap and position. */
+ g_qsort_with_data (spreading,
+ n_requested_sizes, sizeof (guint),
+ compare_gap, sizes);
+
+ /* Distribute available space.
+ * This master piece of a loop was conceived by Behdad Esfahbod.
+ */
+ for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i)
+ {
+ /* Divide remaining space by number of remaining children.
+ * Sort order and reducing remaining space by assigned space
+ * ensures that space is distributed equally.
+ */
+ gint glue = (extra_space + i) / (i + 1);
+ gint gap = sizes[(spreading[i])].natural_size
+ - sizes[(spreading[i])].minimum_size;
+
+ gint extra = MIN (glue, gap);
+
+ sizes[spreading[i]].minimum_size += extra;
+
+ extra_space -= extra;
+ }
+
+ return extra_space;
+}
+
+/* Pulled from gtkbox.c from Gtk+ */
+
+static void
clutter_box_layout_allocate (ClutterLayoutManager *layout,
ClutterContainer *container,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv;
- gfloat avail_width, avail_height, pref_width, pref_height;
- gint n_expand_children, n_children, extra_space;
- GList *children, *l;
- gfloat position;
+ ClutterActor *child;
+ GList *children;
+ gint nvis_children;
+ gint nexpand_children;
gboolean is_rtl;
- children = clutter_container_get_children (container);
- if (children == NULL)
+ ClutterActorBox child_allocation;
+ struct _ClutterRequestedSize *sizes;
+
+ gint size;
+ gint extra;
+ gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
+ gint x = 0, y = 0, i;
+ gint child_size;
+
+ count_expand_children (layout, container, &nvis_children, &nexpand_children);
+
+ /* If there is no visible child, simply return. */
+ if (nvis_children <= 0)
return;
- clutter_actor_box_get_size (box, &avail_width, &avail_height);
+ sizes = g_newa (struct _ClutterRequestedSize, nvis_children);
if (priv->is_vertical)
- {
- get_preferred_height (CLUTTER_BOX_LAYOUT (layout),
- container,
- children, avail_width,
- NULL,
- &pref_height);
-
- pref_width = avail_width;
- }
+ size = box->y2 - box->y1 - (nvis_children - 1) * priv->spacing;
else
- {
- get_preferred_width (CLUTTER_BOX_LAYOUT (layout),
- container,
- children, avail_height,
- NULL,
- &pref_width);
-
- pref_height = avail_height;
- }
+ size = box->x2 - box->x1 - (nvis_children - 1) * priv->spacing;
- /* count the number of children with expand set to TRUE */
- n_children = n_expand_children = 0;
- for (l = children; l; l = l->next)
+ /* Retrieve desired size for visible children. */
+ for (i = 0, children = clutter_container_get_children (container);
+ children;
+ children = children->next)
{
- ClutterLayoutMeta *meta;
-
- meta = clutter_layout_manager_get_child_meta (layout,
- container,
- l->data);
+ child = children->data;
- if (CLUTTER_BOX_CHILD (meta)->expand)
- n_expand_children++;
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
- n_children++;
+ if (priv->is_vertical)
+ clutter_actor_get_preferred_height (child,
+ box->x2 - box->x1,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+ else
+ clutter_actor_get_preferred_width (child,
+ box->y2 - box->y1,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+
+
+ /* Assert the api is working properly */
+ if (sizes[i].minimum_size < 0)
+ g_error ("GtkBox child %s minimum %s: %f < 0 for %s %f",
+ clutter_actor_get_name (child),
+ priv->is_vertical ? "height" : "width",
+ sizes[i].minimum_size,
+ priv->is_vertical ? "width" : "height",
+ priv->is_vertical ? box->x2 - box->x1 : box->y2 - box->y1);
+
+ if (sizes[i].natural_size < sizes[i].minimum_size)
+ g_error ("GtkBox child %s natural %s: %f < minimum %f for %s %f",
+ clutter_actor_get_name (child),
+ priv->is_vertical ? "height" : "width",
+ sizes[i].natural_size,
+ sizes[i].minimum_size,
+ priv->is_vertical ? "width" : "height",
+ priv->is_vertical ? box->x2 - box->x1 : box->y2 - box->y1);
+
+ size -= sizes[i].minimum_size;
+
+ sizes[i].actor = child;
+
+ i++;
}
+ g_list_free (children);
if (priv->is_homogeneous)
{
+ /* If were homogenous we still need to run the above loop to get the
+ * minimum sizes for children that are not going to fill
+ */
if (priv->is_vertical)
- extra_space = (avail_height - (n_children - 1) * priv->spacing)
- / n_children;
+ size = box->y2 - box->y1 - (nvis_children - 1) * priv->spacing;
else
- extra_space = (avail_width - (n_children - 1) * priv->spacing)
- / n_children;
- }
- else if (n_expand_children == 0)
- {
- extra_space = 0;
+ size = box->x2 - box->x1 - (nvis_children - 1) * priv->spacing;
+
+ extra = size / nvis_children;
+ n_extra_widgets = size % nvis_children;
}
else
{
- if (priv->is_vertical)
- extra_space = (avail_height - pref_height) / n_expand_children;
- else
- extra_space = (avail_width - pref_width) / n_expand_children;
+ /* Bring children up to size first */
+ size = distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
- /* don't shrink anything */
- if (extra_space < 0)
- extra_space = 0;
+ /* Calculate space which hasn't distributed yet,
+ * and is available for expanding children.
+ */
+ if (nexpand_children > 0)
+ {
+ extra = size / nexpand_children;
+ n_extra_widgets = size % nexpand_children;
+ }
+ else
+ extra = 0;
}
- position = 0;
-
if (!priv->is_vertical)
{
ClutterTextDirection text_dir;
@@ -889,37 +1016,138 @@ clutter_box_layout_allocate (ClutterLayoutManager *layout,
else
is_rtl = FALSE;
- if (is_rtl)
+ /* Allocate child positions. */
+ if (priv->is_vertical)
{
- for (l = (priv->is_pack_start) ? children : g_list_last (children);
- l != NULL;
- l = (priv->is_pack_start) ? l->next : l->prev)
- {
- ClutterActor *child = l->data;
-
- allocate_box_child (CLUTTER_BOX_LAYOUT (layout), container, child,
- &position,
- avail_width,
- avail_height,
- extra_space, flags);
- }
+ child_allocation.x1 = 0.0;
+ child_allocation.x2 = MAX (1.0, box->x2 - box->x1);
+ if (priv->is_pack_start)
+ y = 0.0;
+ else
+ y = box->y2 - box->y1;
}
else
{
- for (l = (priv->is_pack_start) ? g_list_last (children) : children;
- l != NULL;
- l = (priv->is_pack_start) ? l->prev : l->next)
+ child_allocation.y1 = 0.0;
+ child_allocation.y2 = MAX (1.0, box->y2 - box->y1);
+ if (priv->is_pack_start)
+ x = 0.0;
+ else
+ x = 0.0 + box->x2 - box->x1;
+ }
+
+ children = clutter_container_get_children (container);
+ for (i = g_list_length (children) - 1, children = g_list_last (children);
+ children;
+ children = children->prev, i--)
+ {
+ ClutterLayoutMeta *meta;
+ ClutterBoxChild *box_child;
+
+ child = children->data;
+ meta = clutter_layout_manager_get_child_meta (layout,
+ container,
+ child);
+ box_child = CLUTTER_BOX_CHILD (meta);
+
+ /* If widget is not visible, skip it. */
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ /* Assign the child's size. */
+ if (priv->is_homogeneous)
{
- ClutterActor *child = l->data;
+ child_size = extra;
- allocate_box_child (CLUTTER_BOX_LAYOUT (layout), container, child,
- &position,
- avail_width,
- avail_height,
- extra_space, flags);
+ if (n_extra_widgets > 0)
+ {
+ child_size++;
+ n_extra_widgets--;
+ }
+ }
+ else
+ {
+ child_size = sizes[i].minimum_size;
+
+ if (box_child->expand)
+ {
+ child_size += extra;
+
+ if (n_extra_widgets > 0)
+ {
+ child_size++;
+ n_extra_widgets--;
+ }
+ }
}
- }
+ /* Assign the child's position. */
+ if (priv->is_vertical)
+ {
+ if (box_child->y_fill)
+ {
+ child_allocation.y1 = y;
+ child_allocation.y2 = child_allocation.y1 + MAX (1.0, child_size);
+ }
+ else
+ {
+ child_allocation.y1 = y + (child_size - sizes[i].minimum_size) / 2;
+ child_allocation.y2 = child_allocation.y1 + sizes[i].minimum_size;
+ }
+
+ if (priv->is_pack_start)
+ {
+ y += child_size + priv->spacing;
+ }
+ else
+ {
+ y -= child_size + priv->spacing;
+
+ child_allocation.y1 -= child_size;
+ child_allocation.y2 -= child_size;
+ }
+ }
+ else /* !priv->is_vertical */
+ {
+ if (box_child->x_fill)
+ {
+ child_allocation.x1 = x;
+ child_allocation.x2 = child_allocation.x1 + MAX (1, child_size);
+ }
+ else
+ {
+ child_allocation.x1 = x + (child_size - sizes[i].minimum_size) / 2;
+ child_allocation.x2 = child_allocation.x1 + sizes[i].minimum_size;
+ }
+
+ if (priv->is_pack_start)
+ {
+ x += child_size + priv->spacing;
+ }
+ else
+ {
+ x -= child_size + priv->spacing;
+
+ child_allocation.x1 -= child_size;
+ child_allocation.x2 -= child_size;
+ }
+
+ if (is_rtl)
+ {
+ gfloat width = child_allocation.x2 - child_allocation.x1;
+
+ child_allocation.x1 = box->x2 - box->x1 - child_allocation.x1 - (child_allocation.x2 - child_allocation.x1);
+ child_allocation.x2 = child_allocation.x1 + width;
+ }
+
+ }
+
+ allocate_box_child (CLUTTER_BOX_LAYOUT (layout),
+ container,
+ child,
+ &child_allocation,
+ flags);
+ }
g_list_free (children);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]