From b526b765931bcf4e44feac4ab1b72763eb68f264 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 13 Oct 2009 12:14:05 +0100 Subject: [PATCH] layout: Add BoxLayout, a single line layout manager The BoxLayout layout manager implements a layout policy for arranging children on a single line, either alongside the X axis or alongside the Y axis. --- .gitignore | 1 + clutter/Makefile.am | 2 + clutter/clutter-box-layout.c | 1496 +++++++++++++++++++++++++++ clutter/clutter-box-layout.h | 143 +++ clutter/clutter.h | 1 + tests/interactive/Makefile.am | 3 +- tests/interactive/test-box-layout.c | 207 ++++ 7 files changed, 1852 insertions(+), 1 deletion(-) create mode 100644 clutter/clutter-box-layout.c create mode 100644 clutter/clutter-box-layout.h create mode 100644 tests/interactive/test-box-layout.c diff --git a/.gitignore b/.gitignore index 2ebadd2a8..cdf618308 100644 --- a/.gitignore +++ b/.gitignore @@ -135,6 +135,7 @@ TAGS /tests/interactive/test-clutter-cairo-flowers /tests/interactive/test-bin-layout /tests/interactive/test-flow-layout +/tests/interactive/test-box-layout /tests/conform/stamp-test-conformance /tests/conform/test-anchors /tests/conform/test-conformance diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 61a761694..f4e6181b7 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -67,6 +67,7 @@ source_h = \ $(srcdir)/clutter-binding-pool.h \ $(srcdir)/clutter-bin-layout.h \ $(srcdir)/clutter-box.h \ + $(srcdir)/clutter-box-layout.h \ $(srcdir)/clutter-cairo-texture.h \ $(srcdir)/clutter-child-meta.h \ $(srcdir)/clutter-clone.h \ @@ -137,6 +138,7 @@ source_c = \ $(srcdir)/clutter-binding-pool.c \ $(srcdir)/clutter-bin-layout.c \ $(srcdir)/clutter-box.c \ + $(srcdir)/clutter-box-layout.c \ $(srcdir)/clutter-cairo-texture.c \ $(srcdir)/clutter-child-meta.c \ $(srcdir)/clutter-clone.c \ diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c new file mode 100644 index 000000000..c9371f950 --- /dev/null +++ b/clutter/clutter-box-layout.c @@ -0,0 +1,1496 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + * + * Based on the NBTK NbtkBoxLayout actor by: + * Thomas Wood + */ + +/** + * SECTION:clutter-box-layout + * @short_description: A layout manager arranging children on a single line + * + * The #ClutterBoxLayout is a #ClutterLayoutManager implementing the + * following layout policy: + * + * all children are arranged on a single + * line; + * the axis used is controlled by the + * #ClutterBoxLayout:vertical boolean property; + * the order of the packing is determined by the + * #ClutterBoxLayout:pack-start boolean property; + * each child will be allocated to its natural + * size or, if set to expand, the available size; + * if a child is set to fill on either (or both) + * axis, its allocation will match all the available size; the + * fill layout property only makes sense if the expand property is + * also set; + * if a child is set to expand but not to fill then + * it is possible to control the alignment using the X and Y alignment + * layout properties. + * + * + * It is possible to control the spacing between children of a + * #ClutterBoxLayout by using clutter_box_layout_set_spacing(). + * + * In order to set the layout properties when packing an actor inside a + * #ClutterBoxLayout you should use the clutter_box_layout_pack() + * function. + * + * #ClutterBoxLayout is available since Clutter 1.2 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "clutter-box-layout.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-layout-meta.h" +#include "clutter-private.h" +#include "clutter-types.h" + +#define CLUTTER_TYPE_BOX_CHILD (clutter_box_child_get_type ()) +#define CLUTTER_BOX_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX_CHILD, ClutterBoxChild)) +#define CLUTTER_IS_BOX_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX_CHILD)) + +#define CLUTTER_BOX_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutPrivate)) + +typedef struct _ClutterBoxChild ClutterBoxChild; +typedef struct _ClutterLayoutMetaClass ClutterBoxChildClass; + +struct _ClutterBoxLayoutPrivate +{ + ClutterContainer *container; + + guint spacing; + + guint is_vertical : 1; + guint is_pack_start : 1; +}; + +struct _ClutterBoxChild +{ + ClutterLayoutMeta parent_instance; + + ClutterBoxAlignment x_align; + ClutterBoxAlignment y_align; + + guint x_fill : 1; + guint y_fill : 1; + + guint expand : 1; +}; + +enum +{ + PROP_CHILD_0, + + PROP_CHILD_X_ALIGN, + PROP_CHILD_Y_ALIGN, + PROP_CHILD_X_FILL, + PROP_CHILD_Y_FILL, + PROP_CHILD_EXPAND +}; + +enum +{ + PROP_0, + + PROP_SPACING, + PROP_VERTICAL, + PROP_PACK_START +}; + +G_DEFINE_TYPE (ClutterBoxChild, + clutter_box_child, + CLUTTER_TYPE_LAYOUT_META); + +G_DEFINE_TYPE (ClutterBoxLayout, + clutter_box_layout, + CLUTTER_TYPE_LAYOUT_MANAGER); + +/* + * ClutterBoxChild + */ + +static void +box_child_set_align (ClutterBoxChild *self, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align) +{ + gboolean x_changed = FALSE, y_changed = FALSE; + + if (self->x_align != x_align) + { + self->x_align = x_align; + + x_changed = TRUE; + } + + if (self->y_align != y_align) + { + self->y_align = y_align; + + y_changed = TRUE; + } + + if (x_changed || y_changed) + { + ClutterLayoutManager *layout; + + layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_layout_changed (layout); + + if (x_changed) + g_object_notify (G_OBJECT (self), "x-align"); + + if (y_changed) + g_object_notify (G_OBJECT (self), "y-align"); + } +} + +static void +box_child_set_fill (ClutterBoxChild *self, + gboolean x_fill, + gboolean y_fill) +{ + gboolean x_changed = FALSE, y_changed = FALSE; + + if (self->x_fill != x_fill) + { + self->x_fill = x_fill; + + x_changed = TRUE; + } + + if (self->y_fill != y_fill) + { + self->y_fill = y_fill; + + y_changed = TRUE; + } + + if (x_changed || y_changed) + { + ClutterLayoutManager *layout; + + layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_layout_changed (layout); + + if (x_changed) + g_object_notify (G_OBJECT (self), "x-fill"); + + if (y_changed) + g_object_notify (G_OBJECT (self), "y-fill"); + } +} + +static void +box_child_set_expand (ClutterBoxChild *self, + gboolean expand) +{ + if (self->expand != expand) + { + ClutterLayoutManager *layout; + + self->expand = expand; + + layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_layout_changed (layout); + + g_object_notify (G_OBJECT (self), "expand"); + } +} + +static void +clutter_box_child_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject); + + switch (prop_id) + { + case PROP_CHILD_X_ALIGN: + box_child_set_align (self, + g_value_get_enum (value), + self->y_align); + break; + + case PROP_CHILD_Y_ALIGN: + box_child_set_align (self, + self->x_align, + g_value_get_enum (value)); + break; + + case PROP_CHILD_X_FILL: + box_child_set_fill (self, + g_value_get_boolean (value), + self->y_fill); + break; + + case PROP_CHILD_Y_FILL: + box_child_set_fill (self, + self->x_fill, + g_value_get_boolean (value)); + break; + + case PROP_CHILD_EXPAND: + box_child_set_expand (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_box_child_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject); + + switch (prop_id) + { + case PROP_CHILD_X_ALIGN: + g_value_set_enum (value, self->x_align); + break; + + case PROP_CHILD_Y_ALIGN: + g_value_set_enum (value, self->y_align); + break; + + case PROP_CHILD_X_FILL: + g_value_set_boolean (value, self->x_fill); + break; + + case PROP_CHILD_Y_FILL: + g_value_set_boolean (value, self->y_fill); + break; + + case PROP_CHILD_EXPAND: + g_value_set_boolean (value, self->expand); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_box_child_class_init (ClutterBoxChildClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + gobject_class->set_property = clutter_box_child_set_property; + gobject_class->get_property = clutter_box_child_get_property; + + pspec = g_param_spec_boolean ("expand", + "Expand", + "Allocate extra space for the child", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CHILD_EXPAND, pspec); + + pspec = g_param_spec_boolean ("x-fill", + "Horizontal Fill", + "Whether the child should receive priority " + "when the container is allocating spare space " + "on the horizontal axis", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CHILD_X_FILL, pspec); + + pspec = g_param_spec_boolean ("y-fill", + "Vertical Fill", + "Whether the child should receive priority " + "when the container is allocating spare space " + "on the vertical axis", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CHILD_Y_FILL, pspec); + + pspec = g_param_spec_enum ("x-align", + "Horizontal Alignment", + "Horizontal alignment of the actor within " + "the cell", + CLUTTER_TYPE_BOX_ALIGNMENT, + CLUTTER_BOX_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CHILD_X_ALIGN, pspec); + + pspec = g_param_spec_enum ("y-align", + "Vertical Alignment", + "Vertical alignment of the actor within " + "the cell", + CLUTTER_TYPE_BOX_ALIGNMENT, + CLUTTER_BOX_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CHILD_Y_ALIGN, pspec); +} + +static void +clutter_box_child_init (ClutterBoxChild *self) +{ + self->x_align = CLUTTER_BOX_ALIGNMENT_CENTER; + self->y_align = CLUTTER_BOX_ALIGNMENT_CENTER; + + self->x_fill = self->y_fill = FALSE; + + self->expand = FALSE; +} + +static inline void +allocate_fill (ClutterActor *child, + ClutterActorBox *childbox, + ClutterBoxChild *box_child) +{ + gfloat natural_width, natural_height; + gfloat min_width, min_height; + gfloat child_width, child_height; + gfloat available_width, available_height; + ClutterRequestMode request; + ClutterActorBox allocation = { 0, }; + gdouble x_align, y_align; + + if (box_child->x_align == CLUTTER_BOX_ALIGNMENT_START) + x_align = 0.0; + else if (box_child->x_align == CLUTTER_BOX_ALIGNMENT_CENTER) + x_align = 0.5; + else + x_align = 1.0; + + if (box_child->y_align == CLUTTER_BOX_ALIGNMENT_START) + y_align = 0.0; + else if (box_child->y_align == CLUTTER_BOX_ALIGNMENT_CENTER) + y_align = 0.5; + else + y_align = 1.0; + + available_width = childbox->x2 - childbox->x1; + available_height = childbox->y2 - childbox->y1; + + if (available_width < 0) + available_width = 0; + + if (available_height < 0) + available_height = 0; + + if (box_child->x_fill) + { + allocation.x1 = childbox->x1; + allocation.x2 = ceilf (allocation.x1 + available_width); + } + + if (box_child->y_fill) + { + allocation.y1 = childbox->y1; + allocation.y2 = ceilf (allocation.y1 + available_height); + } + + /* if we are filling horizontally and vertically then we're done */ + if (box_child->x_fill && box_child->y_fill) + { + *childbox = allocation; + return; + } + + request = clutter_actor_get_request_mode (child); + if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) + { + clutter_actor_get_preferred_width (child, available_height, + &min_width, + &natural_width); + + child_width = CLAMP (natural_width, min_width, available_width); + + clutter_actor_get_preferred_height (child, child_width, + &min_height, + &natural_height); + + child_height = CLAMP (natural_height, min_height, available_height); + } + else + { + clutter_actor_get_preferred_height (child, available_width, + &min_height, + &natural_height); + + child_height = CLAMP (natural_height, min_height, available_height); + + clutter_actor_get_preferred_width (child, child_height, + &min_width, + &natural_width); + + child_width = CLAMP (natural_width, min_width, available_width); + } + + if (!box_child->x_fill) + { + allocation.x1 = ceilf (childbox->x1 + + ((available_width - child_width) * x_align)); + allocation.x2 = ceilf (allocation.x1 + child_width); + } + + if (!box_child->y_fill) + { + allocation.y1 = ceilf (childbox->y1 + + ((available_height - child_height) * y_align)); + allocation.y2 = ceilf (allocation.y1 + child_height); + } + + *childbox = allocation; +} + +static ClutterLayoutMeta * +clutter_box_layout_create_child_meta (ClutterLayoutManager *layout, + ClutterContainer *container, + ClutterActor *actor) +{ + return g_object_new (CLUTTER_TYPE_BOX_CHILD, + "manager", layout, + "container", container, + "actor", actor, + NULL); +} + +static void +clutter_box_layout_set_container (ClutterLayoutManager *layout, + ClutterContainer *container) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv; + + priv->container = container; + + if (priv->container != NULL) + { + ClutterRequestMode request_mode; + + /* we need to change the :request-mode of the container + * to match the orientation + */ + request_mode = (priv->is_vertical) + ? CLUTTER_REQUEST_WIDTH_FOR_HEIGHT + : CLUTTER_REQUEST_HEIGHT_FOR_WIDTH; + clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), + request_mode); + } +} + +static void +get_preferred_width (ClutterBoxLayout *self, + const GList *children, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + ClutterBoxLayoutPrivate *priv = self->priv; + gint n_children = 0; + const GList *l; + + if (min_width_p) + *min_width_p = 0; + + if (natural_width_p) + *natural_width_p = 0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat child_min = 0, child_nat = 0; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + n_children++; + + clutter_actor_get_preferred_width (child, + (!priv->is_vertical) + ? for_height + : -1, + &child_min, + &child_nat); + + if (priv->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; + + if (natural_width_p) + *natural_width_p += child_nat; + } + } + + + if (!priv->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); + } +} + +static void +get_preferred_height (ClutterBoxLayout *self, + const GList *children, + gfloat for_height, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + ClutterBoxLayoutPrivate *priv = self->priv; + gint n_children = 0; + const GList *l; + + if (min_height_p) + *min_height_p = 0; + + if (natural_height_p) + *natural_height_p = 0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat child_min = 0, child_nat = 0; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + n_children++; + + clutter_actor_get_preferred_height (child, + (priv->is_vertical) + ? for_height + : -1, + &child_min, + &child_nat); + + if (!priv->is_vertical) + { + 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 (priv->is_vertical && n_children > 1) + { + if (min_height_p) + *min_height_p += priv->spacing * (n_children - 1); + + if (natural_height_p) + *natural_height_p += priv->spacing * (n_children - 1); + } +} + +static void +clutter_box_layout_get_preferred_width (ClutterLayoutManager *layout, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout); + GList *children; + + children = clutter_container_get_children (container); + + get_preferred_width (self, children, for_height, + min_width_p, + natural_width_p); + + g_list_free (children); +} + +static void +clutter_box_layout_get_preferred_height (ClutterLayoutManager *layout, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout); + GList *children; + + children = clutter_container_get_children (container); + + get_preferred_height (self, children, for_width, + min_height_p, + natural_height_p); + + g_list_free (children); +} + +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; + GList *children, *l; + gint n_expand_children, extra_space; + gfloat position; + + children = clutter_container_get_children (container); + if (children == NULL) + return; + + clutter_actor_box_get_size (box, &avail_width, &avail_height); + + if (priv->is_vertical) + { + get_preferred_height (CLUTTER_BOX_LAYOUT (layout), + children, avail_width, + NULL, + &pref_height); + + pref_width = avail_width; + } + else + { + get_preferred_width (CLUTTER_BOX_LAYOUT (layout), + children, avail_height, + NULL, + &pref_width); + + pref_height = avail_height; + } + + /* count the number of children with expand set to TRUE */ + n_expand_children = 0; + for (l = children; l; l = l->next) + { + ClutterLayoutMeta *meta; + + meta = clutter_layout_manager_get_child_meta (layout, + container, + l->data); + + if (CLUTTER_BOX_CHILD (meta)->expand) + n_expand_children++; + } + + if (n_expand_children == 0) + { + extra_space = 0; + n_expand_children = 1; + } + else + { + if (priv->is_vertical) + extra_space = (avail_height - pref_height) / n_expand_children; + else + extra_space = (avail_width - pref_width) / n_expand_children; + + /* don't shrink anything */ + if (extra_space < 0) + extra_space = 0; + } + + position = 0; + + for (l = (priv->is_pack_start) ? g_list_last (children) : children; + l != NULL; + l = (priv->is_pack_start) ? l->prev : l->next) + { + ClutterActor *child = l->data; + ClutterActorBox child_box; + ClutterBoxChild *box_child; + ClutterLayoutMeta *meta; + gfloat child_nat; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + meta = clutter_layout_manager_get_child_meta (layout, + container, + child); + box_child = CLUTTER_BOX_CHILD (meta); + + if (priv->is_vertical) + { + clutter_actor_get_preferred_height (child, avail_width, + NULL, &child_nat); + + child_box.y1 = ceilf (position); + + if (box_child->expand) + child_box.y2 = ceilf (position + child_nat + extra_space); + else + child_box.y2 = ceilf (position + child_nat); + + child_box.x1 = 0; + child_box.x2 = ceilf (avail_width); + + allocate_fill (child, &child_box, box_child); + clutter_actor_allocate (child, &child_box, flags); + + if (box_child->expand) + position += (child_nat + priv->spacing + extra_space); + else + position += (child_nat + priv->spacing); + } + else + { + clutter_actor_get_preferred_width (child, avail_height, + NULL, &child_nat); + + child_box.x1 = ceilf (position); + + if (box_child->expand) + child_box.x2 = ceilf (position + child_nat + extra_space); + else + child_box.x2 = ceilf (position + child_nat); + + child_box.y1 = 0; + child_box.y2 = ceilf (avail_height); + + allocate_fill (child, &child_box, box_child); + clutter_actor_allocate (child, &child_box, flags); + + if (box_child->expand) + position += (child_nat + priv->spacing + extra_space); + else + position += (child_nat + priv->spacing); + } + } + + g_list_free (children); +} + +static void +clutter_box_layout_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (gobject); + + switch (prop_id) + { + case PROP_VERTICAL: + clutter_box_layout_set_vertical (self, g_value_get_boolean (value)); + break; + + case PROP_SPACING: + clutter_box_layout_set_spacing (self, g_value_get_uint (value)); + break; + + case PROP_PACK_START: + clutter_box_layout_set_pack_start (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_box_layout_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (gobject)->priv; + + switch (prop_id) + { + case PROP_VERTICAL: + g_value_set_boolean (value, priv->is_vertical); + break; + + case PROP_SPACING: + g_value_set_uint (value, priv->spacing); + break; + + case PROP_PACK_START: + g_value_set_boolean (value, priv->is_pack_start); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterLayoutManagerClass *layout_class; + GParamSpec *pspec; + + layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); + + gobject_class->set_property = clutter_box_layout_set_property; + gobject_class->get_property = clutter_box_layout_get_property; + + layout_class->get_preferred_width = + clutter_box_layout_get_preferred_width; + layout_class->get_preferred_height = + clutter_box_layout_get_preferred_height; + layout_class->allocate = clutter_box_layout_allocate; + layout_class->set_container = clutter_box_layout_set_container; + layout_class->create_child_meta = clutter_box_layout_create_child_meta; + + g_type_class_add_private (klass, sizeof (ClutterBoxLayoutPrivate)); + + /** + * ClutterBoxLayout:vertical: + * + * Whether the #ClutterBoxLayout should arrange its children + * alongside the Y axis, instead of alongside the X axis + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("vertical", + "Vertical", + "Whether the layout should be vertical, rather" + "than horizontal", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_VERTICAL, pspec); + + /** + * ClutterBoxLayout:pack-start: + * + * Whether the #ClutterBoxLayout should pack items at the start + * or append them at the end + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("pack-start", + "Pack Start", + "Whether to pack items at the start of the box", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_PACK_START, pspec); + + /** + * ClutterBoxLayout:spacing: + * + * The spacing between children of the #ClutterBoxLayout, in pixels + * + * Since: 1.2 + */ + pspec = g_param_spec_uint ("spacing", + "Spacing", + "Spacing between children", + 0, G_MAXUINT, 0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SPACING, pspec); +} + +static void +clutter_box_layout_init (ClutterBoxLayout *layout) +{ + ClutterBoxLayoutPrivate *priv; + + layout->priv = priv = CLUTTER_BOX_LAYOUT_GET_PRIVATE (layout); + + priv->is_vertical = FALSE; + priv->is_pack_start = FALSE; + priv->spacing = 0; +} + +/** + * clutter_box_layout_new: + * + * Creates a new #ClutterBoxLayout layout manager + * + * Return value: the newly created #ClutterBoxLayout + * + * Since: 1.2 + */ +ClutterLayoutManager * +clutter_box_layout_new (void) +{ + return g_object_new (CLUTTER_TYPE_BOX_LAYOUT, NULL); +} + +/** + * clutter_box_layout_set_spacing: + * @layout: a #ClutterBoxLayout + * @spacing: the spacing between children of the layout, in pixels + * + * Sets the spacing between children of @layout + * + * Since: 1.2 + */ +void +clutter_box_layout_set_spacing (ClutterBoxLayout *layout, + guint spacing) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->spacing != spacing) + { + ClutterLayoutManager *manager; + + priv->spacing = spacing; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "spacing"); + } +} + +/** + * clutter_box_layout_get_spacing: + * @layout: a #ClutterBoxLayout + * + * Retrieves the spacing set using clutter_box_layout_set_spacing() + * + * Return value: the spacing between children of the #ClutterBoxLayout + * + * Since: 1.2 + */ +guint +clutter_box_layout_get_spacing (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), 0); + + return layout->priv->spacing; +} + +/** + * clutter_box_layout_set_vertical: + * @layout: a #ClutterBoxLayout + * @vertical: %TRUE if the layout should be vertical + * + * Sets whether @layout should arrange its children vertically alongside + * the Y axis, instead of horizontally alongside the X axis + * + * Since: 1.2 + */ +void +clutter_box_layout_set_vertical (ClutterBoxLayout *layout, + gboolean vertical) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->is_vertical != vertical) + { + ClutterLayoutManager *manager; + + priv->is_vertical = vertical ? TRUE : FALSE; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "vertical"); + } +} + +/** + * clutter_box_layout_get_vertical: + * @layout: a #ClutterBoxLayout + * + * Retrieves the orientation of the @layout as set using the + * clutter_box_layout_set_vertical() function + * + * Return value: %TRUE if the #ClutterBoxLayout is arranging its children + * vertically, and %FALSE otherwise + * + * Since: 1.2 + */ +gboolean +clutter_box_layout_get_vertical (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); + + return layout->priv->is_vertical; +} + +/** + * clutter_box_layout_set_pack_start: + * @layout: a #ClutterBoxLayout + * @pack_start: %TRUE if the @layout should pack children at the + * beginning of the layout + * + * Sets whether children of @layout should be layed out by appending + * them or by prepending them + * + * Since: 1.2 + */ +void +clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, + gboolean pack_start) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->is_pack_start != pack_start) + { + ClutterLayoutManager *manager; + + priv->is_pack_start = pack_start ? TRUE : FALSE; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "pack-start"); + } +} + +/** + * clutter_box_layout_get_pack_start: + * @layout: a #ClutterBoxLayout + * + * Retrieves the value set using clutter_box_layout_set_pack_start() + * + * Return value: %TRUE if the #ClutterBoxLayout should pack children + * at the beginning of the layout, and %FALSE otherwise + * + * Since: 1.2 + */ +gboolean +clutter_box_layout_get_pack_start (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); + + return layout->priv->is_pack_start; +} + +/** + * clutter_box_layout_pack: + * @layout: a #ClutterBoxLayout + * @actor: a #ClutterActor + * @expand: whether the @actor should expand + * @x_fill: whether the @actor should fill horizontally + * @y_fill: whether the @actor should fill vertically + * @x_align: the horizontal alignment policy for @actor + * @y_align: the vertical alignment policy for @actor + * + * Packs @actor inside the #ClutterContainer associated to @layout + * and sets the layout properties + * + * Since: 1.2 + */ +void +clutter_box_layout_pack (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand, + gboolean x_fill, + gboolean y_fill, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align) +{ + ClutterBoxLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = layout->priv; + + if (priv->container == NULL) + { + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before adding children", + G_OBJECT_TYPE_NAME (layout)); + return; + } + + clutter_container_add_actor (priv->container, actor); + + manager = CLUTTER_LAYOUT_MANAGER (layout); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + actor); + g_assert (CLUTTER_IS_BOX_CHILD (meta)); + + box_child_set_align (CLUTTER_BOX_CHILD (meta), x_align, y_align); + box_child_set_fill (CLUTTER_BOX_CHILD (meta), x_fill, y_fill); + box_child_set_expand (CLUTTER_BOX_CHILD (meta), expand); +} + +/** + * clutter_box_layout_set_alignment: + * @layout: a #ClutterBoxLayout + * @actor: a #ClutterActor child of @layout + * @x_align: Horizontal alignment policy for @actor + * @y_align: Vertical alignment policy for @actor + * + * Sets the horizontal and vertical alignment policies for @actor + * inside @layout + * + * Since: 1.2 + */ +void +clutter_box_layout_set_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align) +{ + ClutterBoxLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = layout->priv; + + if (priv->container == NULL) + { + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before querying layout " + "properties", + G_OBJECT_TYPE_NAME (layout)); + return; + } + + manager = CLUTTER_LAYOUT_MANAGER (layout); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + actor); + if (meta == NULL) + { + g_warning ("No layout meta found for the child of type '%s' " + "inside the layout manager of type '%s'", + G_OBJECT_TYPE_NAME (actor), + G_OBJECT_TYPE_NAME (manager)); + return; + } + + g_assert (CLUTTER_IS_BOX_CHILD (meta)); + + box_child_set_align (CLUTTER_BOX_CHILD (meta), x_align, y_align); +} + +/** + * clutter_box_layout_get_alignment: + * @layout: a #ClutterBoxLayout + * @actor: a #ClutterActor child of @layout + * @x_align: (out): return location for the horizontal alignment policy + * @y_align: (out): return location for the vertical alignment policy + * + * Retrieves the horizontal and vertical alignment policies for @actor + * as set using clutter_box_layout_pack() or clutter_box_layout_set_alignment() + * + * Since: 1.2 + */ +void +clutter_box_layout_get_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment *x_align, + ClutterBoxAlignment *y_align) +{ + ClutterBoxLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = layout->priv; + + if (priv->container == NULL) + { + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before querying layout " + "properties", + G_OBJECT_TYPE_NAME (layout)); + return; + } + + manager = CLUTTER_LAYOUT_MANAGER (layout); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + actor); + if (meta == NULL) + { + g_warning ("No layout meta found for the child of type '%s' " + "inside the layout manager of type '%s'", + G_OBJECT_TYPE_NAME (actor), + G_OBJECT_TYPE_NAME (manager)); + return; + } + + g_assert (CLUTTER_IS_BOX_CHILD (meta)); + + if (x_align) + *x_align = CLUTTER_BOX_CHILD (meta)->x_align; + + if (y_align) + *y_align = CLUTTER_BOX_CHILD (meta)->y_align; +} + +/** + * clutter_box_layout_set_fill: + * @layout: a #ClutterBoxLayout + * @actor: a #ClutterActor child of @layout + * @x_fill: whether @actor should fill horizontally the allocated space + * @y_fill: whether @actor should fill vertically the allocated space + * + * Sets the horizontal and vertical fill policies for @actor + * inside @layout + * + * Since: 1.2 + */ +void +clutter_box_layout_set_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean x_fill, + gboolean y_fill) +{ + ClutterBoxLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = layout->priv; + + if (priv->container == NULL) + { + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before querying layout " + "properties", + G_OBJECT_TYPE_NAME (layout)); + return; + } + + manager = CLUTTER_LAYOUT_MANAGER (layout); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + actor); + if (meta == NULL) + { + g_warning ("No layout meta found for the child of type '%s' " + "inside the layout manager of type '%s'", + G_OBJECT_TYPE_NAME (actor), + G_OBJECT_TYPE_NAME (manager)); + return; + } + + g_assert (CLUTTER_IS_BOX_CHILD (meta)); + + box_child_set_fill (CLUTTER_BOX_CHILD (meta), x_fill, y_fill); +} + +/** + * clutter_box_layout_get_fill: + * @layout: a #ClutterBoxLayout + * @actor: a #ClutterActor child of @layout + * @x_fill: (out): return location for the horizontal fill policy + * @y_fill: (out): return location for the vertical fill policy + * + * Retrieves the horizontal and vertical fill policies for @actor + * as set using clutter_box_layout_pack() or clutter_box_layout_set_fill() + * + * Since: 1.2 + */ +void +clutter_box_layout_get_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean *x_fill, + gboolean *y_fill) +{ + ClutterBoxLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = layout->priv; + + if (priv->container == NULL) + { + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before querying layout " + "properties", + G_OBJECT_TYPE_NAME (layout)); + return; + } + + manager = CLUTTER_LAYOUT_MANAGER (layout); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + actor); + if (meta == NULL) + { + g_warning ("No layout meta found for the child of type '%s' " + "inside the layout manager of type '%s'", + G_OBJECT_TYPE_NAME (actor), + G_OBJECT_TYPE_NAME (manager)); + return; + } + + g_assert (CLUTTER_IS_BOX_CHILD (meta)); + + if (x_fill) + *x_fill = CLUTTER_BOX_CHILD (meta)->x_fill; + + if (y_fill) + *y_fill = CLUTTER_BOX_CHILD (meta)->y_fill; +} + +/** + * clutter_box_layout_set_expand: + * @layout: a #ClutterBoxLayout + * @actor: a #ClutterActor child of @layout + * @expand: whether @actor should expand + * + * Sets whether @actor should expand inside @layout + * + * Since: 1.2 + */ +void +clutter_box_layout_set_expand (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand) +{ + ClutterBoxLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = layout->priv; + + if (priv->container == NULL) + { + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before querying layout " + "properties", + G_OBJECT_TYPE_NAME (layout)); + return; + } + + manager = CLUTTER_LAYOUT_MANAGER (layout); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + actor); + if (meta == NULL) + { + g_warning ("No layout meta found for the child of type '%s' " + "inside the layout manager of type '%s'", + G_OBJECT_TYPE_NAME (actor), + G_OBJECT_TYPE_NAME (manager)); + return; + } + + g_assert (CLUTTER_IS_BOX_CHILD (meta)); + + box_child_set_expand (CLUTTER_BOX_CHILD (meta), expand); +} + +/** + * clutter_box_layout_get_expand: + * @layout: a #ClutterBoxLayout + * @actor: a #ClutterActor child of @layout + * + * Retrieves whether @actor should expand inside @layout + * + * Return value: %TRUE if the #ClutterActor should expand, %FALSE otherwise + * + * Since: 1.2 + */ +gboolean +clutter_box_layout_get_expand (ClutterBoxLayout *layout, + ClutterActor *actor) +{ + ClutterBoxLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; + + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); + + priv = layout->priv; + + if (priv->container == NULL) + { + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before querying layout " + "properties", + G_OBJECT_TYPE_NAME (layout)); + return FALSE; + } + + manager = CLUTTER_LAYOUT_MANAGER (layout); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + actor); + if (meta == NULL) + { + g_warning ("No layout meta found for the child of type '%s' " + "inside the layout manager of type '%s'", + G_OBJECT_TYPE_NAME (actor), + G_OBJECT_TYPE_NAME (manager)); + return FALSE; + } + + g_assert (CLUTTER_IS_BOX_CHILD (meta)); + + return CLUTTER_BOX_CHILD (meta)->expand; +} diff --git a/clutter/clutter-box-layout.h b/clutter/clutter-box-layout.h new file mode 100644 index 000000000..be2be9130 --- /dev/null +++ b/clutter/clutter-box-layout.h @@ -0,0 +1,143 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + * + * Based on the NBTK NbtkBoxLayout actor by: + * Thomas Wood + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_BOX_LAYOUT_H__ +#define __CLUTTER_BOX_LAYOUT_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BOX_LAYOUT (clutter_box_layout_get_type ()) +#define CLUTTER_BOX_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayout)) +#define CLUTTER_IS_BOX_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX_LAYOUT)) +#define CLUTTER_BOX_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutClass)) +#define CLUTTER_IS_BOX_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BOX_LAYOUT)) +#define CLUTTER_BOX_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutClass)) + +typedef struct _ClutterBoxLayout ClutterBoxLayout; +typedef struct _ClutterBoxLayoutPrivate ClutterBoxLayoutPrivate; +typedef struct _ClutterBoxLayoutClass ClutterBoxLayoutClass; + +/** + * ClutterBoxAlignment: + * @CLUTTER_BOX_ALIGNMENT_START: Align the child to the top or to + * to the left, depending on the used axis + * @CLUTTER_BOX_ALIGNMENT_CENTER: Align the child to the center + * @CLUTTER_BOX_ALIGNMENT_END: Align the child to the bottom or to + * the right, depending on the used axis + * + * The alignment policies available on each axis of the #ClutterBoxLayout + * + * Since: 1.2 + */ +typedef enum { + CLUTTER_BOX_ALIGNMENT_START, + CLUTTER_BOX_ALIGNMENT_END, + CLUTTER_BOX_ALIGNMENT_CENTER +} ClutterBoxAlignment; + +/** + * ClutterBoxLayout: + * + * The #ClutterBoxLayout structure contains only private data + * and should be accessed using the provided API + * + * Since: 1.2 + */ +struct _ClutterBoxLayout +{ + /*< private >*/ + ClutterLayoutManager parent_instance; + + ClutterBoxLayoutPrivate *priv; +}; + +/** + * ClutterBoxLayoutClass: + * + * The #ClutterBoxLayoutClass structure contains only private + * data and should be accessed using the provided API + * + * Since: 1.2 + */ +struct _ClutterBoxLayoutClass +{ + /*< private >*/ + ClutterLayoutManagerClass parent_class; +}; + +GType clutter_box_layout_get_type (void) G_GNUC_CONST; + +ClutterLayoutManager *clutter_box_layout_new (void); + +void clutter_box_layout_set_spacing (ClutterBoxLayout *layout, + guint spacing); +guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout); +void clutter_box_layout_set_vertical (ClutterBoxLayout *layout, + gboolean vertical); +gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout); +void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, + gboolean pack_start); +gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout); + +void clutter_box_layout_pack (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand, + gboolean x_fill, + gboolean y_fill, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align); +void clutter_box_layout_set_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align); +void clutter_box_layout_get_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment *x_align, + ClutterBoxAlignment *y_align); +void clutter_box_layout_set_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean x_fill, + gboolean y_fill); +void clutter_box_layout_get_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean *x_fill, + gboolean *y_fill); +void clutter_box_layout_set_expand (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand); +gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout, + ClutterActor *actor); + +G_END_DECLS + +#endif /* __CLUTTER_BOX_LAYOUT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 5c313179b..087d93d40 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -45,6 +45,7 @@ #include "clutter-binding-pool.h" #include "clutter-bin-layout.h" #include "clutter-box.h" +#include "clutter-box-layout.h" #include "clutter-cairo-texture.h" #include "clutter-child-meta.h" #include "clutter-clone.h" diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index a0af9d1bf..ec9925498 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -45,7 +45,8 @@ UNIT_TESTS = \ test-clutter-cairo-flowers.c \ test-cogl-vertex-buffer.c \ test-bin-layout.c \ - test-flow-layout.c + test-flow-layout.c \ + test-box-layout.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-box-layout.c b/tests/interactive/test-box-layout.c new file mode 100644 index 000000000..9903fcfa9 --- /dev/null +++ b/tests/interactive/test-box-layout.c @@ -0,0 +1,207 @@ +/* + * Copyright 2009 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * Boston, MA 02111-1307, USA. + * + */ +#include +#include + +#include + +#include + +static ClutterActor *hover_actor = NULL; + +static void +enter_event (ClutterActor *actor, + ClutterEvent *event, + gpointer data) +{ + ClutterColor color = { 0x00, 0x00, 0x00, 0xff }; + + clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (actor), 2); + clutter_rectangle_set_border_color (CLUTTER_RECTANGLE (actor), &color); + + hover_actor = actor; +} + +static void +leave_event (ClutterActor *actor, + ClutterEvent *event, + gpointer data) +{ + clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (actor), 0); + + hover_actor = NULL; +} + +static gboolean +button_release_event (ClutterActor *actor, + ClutterEvent *event, + ClutterBoxLayout *box) +{ + gboolean xfill, yfill; + ClutterBoxAlignment xalign, yalign; + gint button; + + button = clutter_event_get_button (event); + + if (button == 1) + { + clutter_box_layout_get_fill (box, actor, &xfill, &yfill); + clutter_box_layout_set_fill (box, actor, + xfill ? FALSE : TRUE, + yfill ? FALSE : TRUE); + } + else + { + clutter_box_layout_get_alignment (box, actor, &xalign, &yalign); + + if (xalign < 2) + xalign += 1; + else + xalign = 0; + + if (yalign < 2) + yalign += 1; + else + yalign = 0; + + clutter_box_layout_set_alignment (box, actor, xalign, yalign); + } + + return TRUE; +} + +static void +add_actor (ClutterBoxLayout *box) +{ + ClutterActor *rect; + ClutterColor color = { 0xff, 0xff, 0xff, 255 }; + static gboolean expand = TRUE; + + clutter_color_from_hls (&color, + g_random_double_range (0.0, 360.0), + 0.5, + 0.5); + + rect = clutter_rectangle_new_with_color (&color); + clutter_actor_set_size (rect, 32, 64); + clutter_box_layout_pack (box, rect, expand, + FALSE, /* x-fill */ + FALSE, /* y-fill */ + CLUTTER_BOX_ALIGNMENT_CENTER, + CLUTTER_BOX_ALIGNMENT_CENTER); + + clutter_actor_set_reactive (rect, TRUE); + g_signal_connect (rect, "enter-event", G_CALLBACK (enter_event), NULL); + g_signal_connect (rect, "leave-event", G_CALLBACK (leave_event), NULL); + g_signal_connect (rect, "button-release-event", + G_CALLBACK (button_release_event), + box); + + expand = !expand; +} + +static gboolean +key_release_cb (ClutterActor *actor, + ClutterEvent *event, + ClutterBoxLayout *layout) +{ + gboolean toggle; + guint spacing; + + switch (clutter_event_get_key_symbol (event)) + { + case CLUTTER_v: + toggle = clutter_box_layout_get_vertical (layout); + clutter_box_layout_set_vertical (layout, !toggle); + break; + + case CLUTTER_p: + toggle = clutter_box_layout_get_pack_start (layout); + clutter_box_layout_set_pack_start (layout, !toggle); + break; + + case CLUTTER_s: + spacing = clutter_box_layout_get_spacing (layout); + + if (spacing > 12) + spacing = 0; + else + spacing++; + + clutter_box_layout_set_spacing (layout, spacing); + break; + + case '+': + add_actor (layout); + break; + + default: + return FALSE; + } + + return TRUE; +} + +static void +stage_size_changed_cb (ClutterActor *stage, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags, + ClutterActor *box) +{ + gfloat width, height; + + clutter_actor_box_get_size (allocation, &width, &height); + clutter_actor_set_size (box, width - 100, height - 100); +} + +G_MODULE_EXPORT int +test_box_layout_main (int argc, char *argv[]) +{ + ClutterActor *stage, *box; + ClutterLayoutManager *layout; + gint i; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Box Layout"); + clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE); + clutter_actor_set_size (stage, 640, 480); + + layout = clutter_box_layout_new (); + + box = clutter_box_new (layout); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); + + for (i = 0; i < 5; i++) + add_actor (CLUTTER_BOX_LAYOUT (layout)); + + g_signal_connect (stage, "key-release-event", + G_CALLBACK (key_release_cb), + layout); + g_signal_connect (stage, "allocation-changed", + G_CALLBACK (stage_size_changed_cb), + box); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +}