From d6183e95e51d1e3807ed5150768de2426b6b8e65 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 1 Sep 2009 16:34:28 +0100 Subject: [PATCH 01/50] [layout] Add initial implementation of LayoutManager The LayoutManager class is an abstract proxy for the size requesition and size allocation process in ClutterActor. A ClutterLayoutManager sub-class must implement get_preferred_width(), get_preferred_height() and allocate(); a ClutterContainer using the LayoutManager API will then proxy the corresponding Actor virtual functions to the LayoutManager instance. This allows having a generic "blank" ClutterActor sub-class, implementing the ClutterContainer interface, which leaves only the layout management implementation to the application developers. --- clutter/Makefile.am | 2 + clutter/clutter-layout-manager.c | 180 +++++++++++++++++++++++++++++++ clutter/clutter-layout-manager.h | 92 ++++++++++++++++ clutter/clutter.h | 1 + 4 files changed, 275 insertions(+) create mode 100644 clutter/clutter-layout-manager.c create mode 100644 clutter/clutter-layout-manager.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 05336cc2e..34fb51841 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -78,6 +78,7 @@ source_h = \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-interval.h \ $(srcdir)/clutter-keysyms.h \ + $(srcdir)/clutter-layout-manager.h \ $(srcdir)/clutter-list-model.h \ $(srcdir)/clutter-main.h \ $(srcdir)/clutter-media.h \ @@ -142,6 +143,7 @@ source_c = \ $(srcdir)/clutter-group.c \ $(srcdir)/clutter-id-pool.c \ $(srcdir)/clutter-interval.c \ + $(srcdir)/clutter-layout-manager.c \ $(srcdir)/clutter-list-model.c \ $(srcdir)/clutter-main.c \ clutter-marshal.c \ diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c new file mode 100644 index 000000000..f9e59087f --- /dev/null +++ b/clutter/clutter-layout-manager.c @@ -0,0 +1,180 @@ +/** + * SECTION:clutter-layout-manager + * @short_description: Layout managers base class + * + * #ClutterLayoutManager is FIXME + * + * #ClutterLayoutManager is available since Clutter 1.2 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-debug.h" +#include "clutter-layout-manager.h" +#include "clutter-marshal.h" +#include "clutter-private.h" + +#define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \ + GObject *_obj = G_OBJECT (m); \ + g_warning ("Layout managers of type %s do not implement " \ + "the ClutterLayoutManager::%s method", \ + G_OBJECT_TYPE_NAME (_obj), \ + (method)); } G_STMT_END + +G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_OBJECT); + +static void +layout_manager_real_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p) +{ + LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_width"); + + if (min_width_p) + *min_width_p = 0.0; + + if (nat_width_p) + *nat_width_p = 0.0; +} + +static void +layout_manager_real_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p) +{ + LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_height"); + + if (min_height_p) + *min_height_p = 0.0; + + if (nat_height_p) + *nat_height_p = 0.0; +} + +static void +layout_manager_real_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate"); +} + +static void +clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass) +{ + klass->get_preferred_width = layout_manager_real_get_preferred_width; + klass->get_preferred_height = layout_manager_real_get_preferred_height; + klass->allocate = layout_manager_real_allocate; +} + +static void +clutter_layout_manager_init (ClutterLayoutManager *manager) +{ +} + +/** + * clutter_layout_manager_get_preferred_width: + * @manager: a #ClutterLayoutManager + * @container: the #ClutterContainer using @manager + * @for_height: the height for which the width should be computed, or -1 + * @min_width_p: (out) (allow-none): return location for the minimum width + * of the layout, or %NULL + * @nat_width_p: (out) (allow-none): return location for the natural width + * of the layout, or %NULL + * + * Computes the minimum and natural widths of the @container according + * to @manager. + * + * See also clutter_actor_get_preferred_width() + * + * Since: 1.2 + */ +void +clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p) +{ + ClutterLayoutManagerClass *klass; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + klass->get_preferred_width (manager, container, for_height, + min_width_p, + nat_width_p); +} + +/** + * clutter_layout_manager_get_preferred_height: + * @manager: a #ClutterLayoutManager + * @container: the #ClutterContainer using @manager + * @for_width: the width for which the height should be computed, or -1 + * @min_height_p: (out) (allow-none): return location for the minimum height + * of the layout, or %NULL + * @nat_height_p: (out) (allow-none): return location for the natural height + * of the layout, or %NULL + * + * Computes the minimum and natural heights of the @container according + * to @manager. + * + * See also clutter_actor_get_preferred_height() + * + * Since: 1.2 + */ +void +clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p) +{ + ClutterLayoutManagerClass *klass; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + klass->get_preferred_height (manager, container, for_width, + min_height_p, + nat_height_p); +} + +/** + * clutter_layout_manager_allocate: + * @manager: a #ClutterLayoutManager + * @container: the #ClutterContainer using @manager + * @allocation: the #ClutterActorBox containing the allocated area + * of @container + * @flags: the allocation flags + * + * Allocates the children of @container given an area + * + * See also clutter_actor_allocate() + * + * Since: 1.2 + */ +void +clutter_layout_manager_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + ClutterLayoutManagerClass *klass; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (allocation != NULL); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + klass->allocate (manager, container, allocation, flags); +} diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h new file mode 100644 index 000000000..f93e68a02 --- /dev/null +++ b/clutter/clutter-layout-manager.h @@ -0,0 +1,92 @@ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only <clutter/clutter.h> can be included directly." +#endif + +#ifndef __CLUTTER_LAYOUT_MANAGER_H__ +#define __CLUTTER_LAYOUT_MANAGER_H__ + +#include <clutter/clutter-actor.h> +#include <clutter/clutter-container.h> + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_LAYOUT_MANAGER (clutter_layout_manager_get_type ()) +#define CLUTTER_LAYOUT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManager)) +#define CLUTTER_IS_LAYOUT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYOUT_MANAGER)) +#define CLUTTER_LAYOUT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManagerClass)) +#define CLUTTER_IS_LAYOUT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_LAYOUT_MANAGER)) +#define CLUTTER_LAYOUT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManagerClass)) + +typedef struct _ClutterLayoutManager ClutterLayoutManager; +typedef struct _ClutterLayoutManagerClass ClutterLayoutManagerClass; + +/** + * ClutterLayoutManager: + * + * The #ClutterLayoutManager structure contains only private data + * and should be accessed using the provided API + * + * Since: 1.2 + */ +struct _ClutterLayoutManager +{ + GObject parent_instance; +}; + +/** + * ClutterLayoutManagerClass: + * + * The #ClutterLayoutManagerClass structure contains only private + * data and should be accessed using the provided API + * + * Since: 1.2 + */ +struct _ClutterLayoutManagerClass +{ + GObjectClass parent_class; + + void (* get_preferred_width) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *minimum_width_p, + gfloat *natural_width_p); + void (* get_preferred_height) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *minimum_height_p, + gfloat *natural_height_p); + void (* allocate) (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); + + void (* _clutter_padding_1) (void); + void (* _clutter_padding_2) (void); + void (* _clutter_padding_3) (void); + void (* _clutter_padding_4) (void); + void (* _clutter_padding_5) (void); + void (* _clutter_padding_6) (void); + void (* _clutter_padding_7) (void); + void (* _clutter_padding_8) (void); +}; + +GType clutter_layout_manager_get_type (void) G_GNUC_CONST; + +void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p); +void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p); +void clutter_layout_manager_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); + +G_END_DECLS + +#endif /* __CLUTTER_LAYOUT_MANAGER_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 701cf3176..95e35c252 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -54,6 +54,7 @@ #include "clutter-group.h" #include "clutter-interval.h" #include "clutter-keysyms.h" +#include "clutter-layout-manager.h" #include "clutter-list-model.h" #include "clutter-main.h" #include "clutter-media.h" From 0340f656346d80696305218897720e28c88f367a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 1 Sep 2009 17:42:50 +0100 Subject: [PATCH 02/50] [box] Add ClutterBox ClutterBox is an actor with no layout management. It relies on a ClutterLayoutManager to perform size requisition and allocation of its children. --- clutter/Makefile.am | 2 + clutter/clutter-box.c | 368 ++++++++++++++++++++++++++++++++++++++++++ clutter/clutter-box.h | 43 +++++ clutter/clutter.h | 1 + 4 files changed, 414 insertions(+) create mode 100644 clutter/clutter-box.c create mode 100644 clutter/clutter-box.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 34fb51841..fdd5c7293 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -65,6 +65,7 @@ source_h = \ $(srcdir)/clutter-behaviour-rotate.h \ $(srcdir)/clutter-behaviour-scale.h \ $(srcdir)/clutter-binding-pool.h \ + $(srcdir)/clutter-box.h \ $(srcdir)/clutter-cairo-texture.h \ $(srcdir)/clutter-child-meta.h \ $(srcdir)/clutter-clone.h \ @@ -130,6 +131,7 @@ source_c = \ $(srcdir)/clutter-behaviour-scale.c \ $(srcdir)/clutter-bezier.c \ $(srcdir)/clutter-binding-pool.c \ + $(srcdir)/clutter-box.c \ $(srcdir)/clutter-cairo-texture.c \ $(srcdir)/clutter-child-meta.c \ $(srcdir)/clutter-clone.c \ diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c new file mode 100644 index 000000000..6b8253eac --- /dev/null +++ b/clutter/clutter-box.c @@ -0,0 +1,368 @@ +/** + * SECTION:clutter-box + * @short_description: A Generic layout container + * + * #ClutterBox is a FIXME + * + * #ClutterBox is available since Clutter 1.2 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-box.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-marshal.h" +#include "clutter-private.h" + +#define CLUTTER_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BOX, ClutterBoxPrivate)) + +struct _ClutterBoxPrivate +{ + ClutterLayoutManager *manager; + + GList *children; +}; + +enum +{ + PROP_0, + + PROP_LAYOUT_MANAGER +}; + +static void clutter_container_iface_init (ClutterContainerIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ClutterBox, clutter_box, CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, + clutter_container_iface_init)); + +static gint +sort_by_depth (gconstpointer a, + gconstpointer b) +{ + gfloat depth_a = clutter_actor_get_depth ((ClutterActor *) a); + gfloat depth_b = clutter_actor_get_depth ((ClutterActor *) b); + + if (depth_a < depth_b) + return -1; + + if (depth_a > depth_b) + return 1; + + return 0; +} + +static void +clutter_box_real_add (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv; + + g_object_ref (actor); + + priv->children = g_list_insert_sorted (priv->children, + actor, + sort_by_depth); + + clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); + clutter_actor_queue_relayout (actor); + + g_signal_emit_by_name (container, "actor-added", actor); + + g_object_unref (actor); +} + +static void +clutter_box_real_remove (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv; + + g_object_ref (actor); + + priv->children = g_list_remove (priv->children, actor); + clutter_actor_unparent (actor); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); + + g_signal_emit_by_name (container, "actor-removed", actor); + + g_object_unref (actor); +} + +static void +clutter_box_real_foreach (ClutterContainer *container, + ClutterCallback callback, + gpointer user_data) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv; + GList *l; + + for (l = priv->children; l != NULL; l = l->next) + (* callback) (l->data, user_data); +} + +static void +clutter_box_real_raise (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv; + + priv->children = g_list_remove (priv->children, actor); + + if (sibling == NULL) + priv->children = g_list_append (priv->children, actor); + else + { + gint index_ = g_list_index (priv->children, sibling) + 1; + + priv->children = g_list_insert (priv->children, actor, index_); + } + + clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); +} + +static void +clutter_box_real_lower (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv; + + priv->children = g_list_remove (priv->children, actor); + + if (sibling == NULL) + priv->children = g_list_prepend (priv->children, actor); + else + { + gint index_ = g_list_index (priv->children, sibling); + + priv->children = g_list_insert (priv->children, actor, index_); + } + + clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); +} + +static void +clutter_box_real_sort_depth_order (ClutterContainer *container) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv; + + priv->children = g_list_sort (priv->children, sort_by_depth); + + clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); +} + +static void +clutter_container_iface_init (ClutterContainerIface *iface) +{ + iface->add = clutter_box_real_add; + iface->remove = clutter_box_real_remove; + iface->foreach = clutter_box_real_foreach; + iface->raise = clutter_box_real_raise; + iface->lower = clutter_box_real_lower; + iface->sort_depth_order = clutter_box_real_sort_depth_order; +} + +static void +clutter_box_real_paint (ClutterActor *actor) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + + g_list_foreach (priv->children, (GFunc) clutter_actor_paint, NULL); +} + +static void +clutter_box_real_pick (ClutterActor *actor, + const ClutterColor *pick) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + + CLUTTER_ACTOR_CLASS (clutter_box_parent_class)->pick (actor, pick); + + g_list_foreach (priv->children, (GFunc) clutter_actor_paint, NULL); +} + +static void +clutter_box_real_get_preferred_width (ClutterActor *actor, + gfloat for_height, + gfloat *min_width, + gfloat *natural_width) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + + if (priv->children == NULL) + { + if (min_width) + *min_width = 0.0; + + if (natural_width) + *natural_width = 0.0; + + return; + } + + clutter_layout_manager_get_preferred_width (priv->manager, + CLUTTER_CONTAINER (actor), + for_height, + min_width, natural_width); +} + +static void +clutter_box_real_get_preferred_height (ClutterActor *actor, + gfloat for_width, + gfloat *min_height, + gfloat *natural_height) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + + if (priv->children == NULL) + { + if (min_height) + *min_height = 0.0; + + if (natural_height) + *natural_height = 0.0; + + return; + } + + clutter_layout_manager_get_preferred_height (priv->manager, + CLUTTER_CONTAINER (actor), + for_width, + min_height, natural_height); +} + +static void +clutter_box_real_allocate (ClutterActor *actor, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + ClutterActorClass *klass; + + klass = CLUTTER_ACTOR_CLASS (clutter_box_parent_class); + klass->allocate (actor, allocation, flags); + + if (priv->children == NULL) + return; + + clutter_layout_manager_allocate (priv->manager, + CLUTTER_CONTAINER (actor), + allocation, flags); +} + +static void +clutter_box_dispose (GObject *gobject) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (gobject)->priv; + + if (priv->manager != NULL) + { + g_object_unref (priv->manager); + priv->manager = NULL; + } + + G_OBJECT_CLASS (clutter_box_parent_class)->dispose (gobject); +} + +static void +clutter_box_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (gobject)->priv; + + switch (prop_id) + { + case PROP_LAYOUT_MANAGER: + priv->manager = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_box_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterBoxPrivate *priv = CLUTTER_BOX (gobject)->priv; + + switch (prop_id) + { + case PROP_LAYOUT_MANAGER: + g_value_set_object (value, priv->manager); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_box_class_init (ClutterBoxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (ClutterBoxPrivate)); + + actor_class->get_preferred_width = clutter_box_real_get_preferred_width; + actor_class->get_preferred_height = clutter_box_real_get_preferred_height; + actor_class->allocate = clutter_box_real_allocate; + actor_class->paint = clutter_box_real_paint; + actor_class->pick = clutter_box_real_pick; + + gobject_class->set_property = clutter_box_set_property; + gobject_class->get_property = clutter_box_get_property; + gobject_class->dispose = clutter_box_dispose; + + pspec = g_param_spec_object ("layout-manager", + "Layout Manager", + "The layout manager used by the box", + CLUTTER_TYPE_LAYOUT_MANAGER, + CLUTTER_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_LAYOUT_MANAGER, + pspec); +} + +static void +clutter_box_init (ClutterBox *self) +{ + self->priv = CLUTTER_BOX_GET_PRIVATE (self); +} + +/** + * clutter_box_new: + * @manager: a #ClutterLayoutManager + * + * Creates a new #ClutterBox. The children of the box will be layed + * out by the passed @manager + * + * Return value: the newly created #ClutterBox actor + * + * Since: 1.0 + */ +ClutterActor * +clutter_box_new (ClutterLayoutManager *manager) +{ + g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL); + + return g_object_new (CLUTTER_TYPE_BOX, + "layout-manager", manager, + NULL); +} diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h new file mode 100644 index 000000000..6793ee265 --- /dev/null +++ b/clutter/clutter-box.h @@ -0,0 +1,43 @@ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only <clutter/clutter.h> can be included directly." +#endif + +#ifndef __CLUTTER_BOX_H__ +#define __CLUTTER_BOX_H__ + +#include <clutter/clutter-actor.h> +#include <clutter/clutter-container.h> +#include <clutter/clutter-layout-manager.h> + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BOX (clutter_box_get_type ()) +#define CLUTTER_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX, ClutterBox)) +#define CLUTTER_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX)) +#define CLUTTER_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BOX, ClutterBoxClass)) +#define CLUTTER_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BOX)) +#define CLUTTER_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BOX, ClutterBoxClass)) + +typedef struct _ClutterBox ClutterBox; +typedef struct _ClutterBoxPrivate ClutterBoxPrivate; +typedef struct _ClutterBoxClass ClutterBoxClass; + +struct _ClutterBox +{ + ClutterActor parent_instance; + + ClutterBoxPrivate *priv; +}; + +struct _ClutterBoxClass +{ + ClutterActorClass parent_class; +}; + +GType clutter_box_get_type (void) G_GNUC_CONST; + +ClutterActor *clutter_box_new (ClutterLayoutManager *manager); + +G_END_DECLS + +#endif /* __CLUTTER_BOX_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 95e35c252..0ffb6cca5 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -43,6 +43,7 @@ #include "clutter-behaviour-rotate.h" #include "clutter-behaviour-scale.h" #include "clutter-binding-pool.h" +#include "clutter-box.h" #include "clutter-cairo-texture.h" #include "clutter-child-meta.h" #include "clutter-clone.h" From 141a1556908749236c90e69fc2388f4fc40c6d8f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 2 Sep 2009 11:55:22 +0100 Subject: [PATCH 03/50] [layout] Make LayoutManager a floating object A layout manager instance makes only sense if it's owned by a container. For this reason, it should have a floating reference instead of a full reference on construction; this allows constructing Boxes like: box = clutter_box_new (clutter_fixed_layout_new ()); without leaking the layout manager instance. --- clutter/clutter-box.c | 3 ++- clutter/clutter-layout-manager.c | 4 +++- clutter/clutter-layout-manager.h | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index 6b8253eac..f3c0648ab 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -281,7 +281,8 @@ clutter_box_set_property (GObject *gobject, switch (prop_id) { case PROP_LAYOUT_MANAGER: - priv->manager = g_value_dup_object (value); + priv->manager = g_value_get_object (value); + g_object_ref_sink (priv->manager); break; default: diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index f9e59087f..912ccd2d3 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -23,7 +23,9 @@ G_OBJECT_TYPE_NAME (_obj), \ (method)); } G_STMT_END -G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_OBJECT); +G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, + clutter_layout_manager, + G_TYPE_INITIALLY_UNOWNED); static void layout_manager_real_get_preferred_width (ClutterLayoutManager *manager, diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index f93e68a02..e053c658e 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -30,7 +30,7 @@ typedef struct _ClutterLayoutManagerClass ClutterLayoutManagerClass; */ struct _ClutterLayoutManager { - GObject parent_instance; + GInitiallyUnowned parent_instance; }; /** @@ -43,7 +43,7 @@ struct _ClutterLayoutManager */ struct _ClutterLayoutManagerClass { - GObjectClass parent_class; + GInitiallyUnownedClass parent_class; void (* get_preferred_width) (ClutterLayoutManager *manager, ClutterContainer *container, From 6d4cc13f7c7047a609dd8055ce63048da294af39 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 2 Sep 2009 12:37:16 +0100 Subject: [PATCH 04/50] [layout] Add Fixed layout manager The FixedLayout layout manager object implements the same layout policy of ClutterGroup. --- clutter/Makefile.am | 2 + clutter/clutter-fixed-layout.c | 217 +++++++++++++++++++++++++++++++++ clutter/clutter-fixed-layout.h | 38 ++++++ clutter/clutter.h | 1 + 4 files changed, 258 insertions(+) create mode 100644 clutter/clutter-fixed-layout.c create mode 100644 clutter/clutter-fixed-layout.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index fdd5c7293..59128c6d8 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -75,6 +75,7 @@ source_h = \ $(srcdir)/clutter-event.h \ $(srcdir)/clutter-feature.h \ $(srcdir)/clutter-fixed.h \ + $(srcdir)/clutter-fixed-layout.h \ $(srcdir)/clutter-frame-source.h \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-interval.h \ @@ -141,6 +142,7 @@ source_c = \ $(srcdir)/clutter-event.c \ $(srcdir)/clutter-feature.c \ $(srcdir)/clutter-fixed.c \ + $(srcdir)/clutter-fixed-layout.c \ $(srcdir)/clutter-frame-source.c \ $(srcdir)/clutter-group.c \ $(srcdir)/clutter-id-pool.c \ diff --git a/clutter/clutter-fixed-layout.c b/clutter/clutter-fixed-layout.c new file mode 100644 index 000000000..f3e3d385d --- /dev/null +++ b/clutter/clutter-fixed-layout.c @@ -0,0 +1,217 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-debug.h" +#include "clutter-fixed-layout.h" +#include "clutter-private.h" + +G_DEFINE_TYPE (ClutterFixedLayout, + clutter_fixed_layout, + CLUTTER_TYPE_LAYOUT_MANAGER); + +static void +clutter_fixed_layout_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p) +{ + GList *children, *l; + gdouble min_left, min_right; + gdouble natural_left, natural_right; + + min_left = 0; + min_right = 0; + natural_left = 0; + natural_right = 0; + + children = clutter_container_get_children (container); + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat child_x, child_min, child_natural; + + child_x = clutter_actor_get_x (child); + + clutter_actor_get_preferred_size (child, + &child_min, NULL, + &child_natural, NULL); + + if (l == children) + { + /* First child */ + min_left = child_x; + natural_left = child_x; + min_right = min_left + child_min; + natural_right = natural_left + child_natural; + } + else + { + /* Union of extents with previous children */ + if (child_x < min_left) + min_left = child_x; + + if (child_x < natural_left) + natural_left = child_x; + + if (child_x + child_min > min_right) + min_right = child_x + child_min; + + if (child_x + child_natural > natural_right) + natural_right = child_x + child_natural; + } + } + + g_list_free (children); + + /* The preferred size is defined as the width and height we want starting + * from our origin, since our allocation will set the origin; so we now + * need to remove any part of the request that is to the left of the origin. + */ + if (min_left < 0) + min_left = 0; + + if (natural_left < 0) + natural_left = 0; + + if (min_right < 0) + min_right = 0; + + if (natural_right < 0) + natural_right = 0; + + g_assert (min_right >= min_left); + g_assert (natural_right >= natural_left); + + if (min_width_p) + *min_width_p = min_right - min_left; + + if (nat_width_p) + *nat_width_p = natural_right - min_left; +} + +static void +clutter_fixed_layout_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p) +{ + GList *children, *l; + gdouble min_top, min_bottom; + gdouble natural_top, natural_bottom; + + min_top = 0; + min_bottom = 0; + natural_top = 0; + natural_bottom = 0; + + children = clutter_container_get_children (container); + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat child_y, child_min, child_natural; + + child_y = clutter_actor_get_y (child); + + clutter_actor_get_preferred_size (child, + NULL, &child_min, + NULL, &child_natural); + + if (l == children) + { + /* First child */ + min_top = child_y; + natural_top = child_y; + min_bottom = min_top + child_min; + natural_bottom = natural_top + child_natural; + } + else + { + /* Union of extents with previous children */ + if (child_y < min_top) + min_top = child_y; + + if (child_y < natural_top) + natural_top = child_y; + + if (child_y + child_min > min_bottom) + min_bottom = child_y + child_min; + + if (child_y + child_natural > natural_bottom) + natural_bottom = child_y + child_natural; + } + } + + g_list_free (children); + + /* The preferred size is defined as the width and height we want starting + * from our origin, since our allocation will set the origin; so we now + * need to remove any part of the request that is above the origin. + */ + if (min_top < 0) + min_top = 0; + + if (natural_top < 0) + natural_top = 0; + + if (min_bottom < 0) + min_bottom = 0; + + if (natural_bottom < 0) + natural_bottom = 0; + + g_assert (min_bottom >= min_top); + g_assert (natural_bottom >= natural_top); + + if (min_height_p) + *min_height_p = min_bottom - min_top; + + if (nat_height_p) + *nat_height_p = natural_bottom - min_top; +} + +static void +clutter_fixed_layout_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + GList *children, *l; + + children = clutter_container_get_children (container); + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + + clutter_actor_allocate_preferred_size (child, flags); + } + + g_list_free (children); +} + +static void +clutter_fixed_layout_class_init (ClutterFixedLayoutClass *klass) +{ + ClutterLayoutManagerClass *manager_class = + CLUTTER_LAYOUT_MANAGER_CLASS (klass); + + manager_class->get_preferred_width = + clutter_fixed_layout_get_preferred_width; + manager_class->get_preferred_height = + clutter_fixed_layout_get_preferred_height; + manager_class->allocate = clutter_fixed_layout_allocate; +} + +static void +clutter_fixed_layout_init (ClutterFixedLayout *self) +{ +} + +ClutterLayoutManager * +clutter_fixed_layout_new (void) +{ + return g_object_new (CLUTTER_TYPE_FIXED_LAYOUT, NULL); +} diff --git a/clutter/clutter-fixed-layout.h b/clutter/clutter-fixed-layout.h new file mode 100644 index 000000000..b8075172a --- /dev/null +++ b/clutter/clutter-fixed-layout.h @@ -0,0 +1,38 @@ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only <clutter/clutter.h> can be included directly." +#endif + +#ifndef __CLUTTER_FIXED_LAYOUT_H__ +#define __CLUTTER_FIXED_LAYOUT_H__ + +#include <clutter/clutter-layout-manager.h> + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_FIXED_LAYOUT (clutter_fixed_layout_get_type ()) +#define CLUTTER_FIXED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayout)) +#define CLUTTER_IS_FIXED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_FIXED_LAYOUT)) +#define CLUTTER_FIXED_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayoutClass)) +#define CLUTTER_IS_FIXED_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_FIXED_LAYOUT)) +#define CLUTTER_FIXED_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayoutClass)) + +typedef struct _ClutterFixedLayout ClutterFixedLayout; +typedef struct _ClutterFixedLayoutClass ClutterFixedLayoutClass; + +struct _ClutterFixedLayout +{ + ClutterLayoutManager parent_instance; +}; + +struct _ClutterFixedLayoutClass +{ + ClutterLayoutManagerClass parent_class; +}; + +GType clutter_fixed_layout_get_type (void) G_GNUC_CONST; + +ClutterLayoutManager *clutter_fixed_layout_new (void); + +G_END_DECLS + +#endif /* __CLUTTER_FIXED_LAYOUT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 0ffb6cca5..5c5edd261 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -51,6 +51,7 @@ #include "clutter-container.h" #include "clutter-event.h" #include "clutter-feature.h" +#include "clutter-fixed-layout.h" #include "clutter-frame-source.h" #include "clutter-group.h" #include "clutter-interval.h" From 1061ebeac90a5b03a172aa649aa5a8b8cfb5b293 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Fri, 11 Sep 2009 13:51:23 +0100 Subject: [PATCH 05/50] [layout] Add BinLayout A BinLayout is a simple layout manager that allocates a single cell, providing alignment on both the horizontal and vertical axis. If the container associated to the BinLayout has more than one child, the preferred size returned by the layout manager will be as big as the maximum of the children preferred sizes; the allocation will be applied to all children - but it will still depend on each child preferred size and the BinLayout horizontal and vertical alignment properties. The supported alignment properties are: * center: align the child by centering it * start: align the child at the top or left border of the layout * end: align the child at the bottom or right border of the layout * fill: expand the child to fill the size of the layout * fixed: let the child position itself --- clutter/Makefile.am | 2 + clutter/clutter-bin-layout.c | 397 +++++++++++++++++++++++++++++++++++ clutter/clutter-bin-layout.h | 50 +++++ clutter/clutter.h | 1 + 4 files changed, 450 insertions(+) create mode 100644 clutter/clutter-bin-layout.c create mode 100644 clutter/clutter-bin-layout.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 59128c6d8..7cc3ddab3 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -65,6 +65,7 @@ source_h = \ $(srcdir)/clutter-behaviour-rotate.h \ $(srcdir)/clutter-behaviour-scale.h \ $(srcdir)/clutter-binding-pool.h \ + $(srcdir)/clutter-bin-layout.h \ $(srcdir)/clutter-box.h \ $(srcdir)/clutter-cairo-texture.h \ $(srcdir)/clutter-child-meta.h \ @@ -132,6 +133,7 @@ source_c = \ $(srcdir)/clutter-behaviour-scale.c \ $(srcdir)/clutter-bezier.c \ $(srcdir)/clutter-binding-pool.c \ + $(srcdir)/clutter-bin-layout.c \ $(srcdir)/clutter-box.c \ $(srcdir)/clutter-cairo-texture.c \ $(srcdir)/clutter-child-meta.c \ diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c new file mode 100644 index 000000000..81a9cef39 --- /dev/null +++ b/clutter/clutter-bin-layout.c @@ -0,0 +1,397 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-actor.h" +#include "clutter-animatable.h" +#include "clutter-bin-layout.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-private.h" + +#define CLUTTER_BIN_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutPrivate)) + +struct _ClutterBinLayoutPrivate +{ + ClutterBinAlignment x_align; + ClutterBinAlignment y_align; + + gdouble x_factor; + gdouble y_factor; +}; + +enum +{ + PROP_0, + + PROP_X_ALIGN, + PROP_Y_ALIGN +}; + +G_DEFINE_TYPE (ClutterBinLayout, + clutter_bin_layout, + CLUTTER_TYPE_LAYOUT_MANAGER); + + +static void +set_x_align (ClutterBinLayout *self, + ClutterBinAlignment alignment) +{ + ClutterBinLayoutPrivate *priv = self->priv; + + if (priv->x_align != alignment) + { + priv->x_align = alignment; + + g_object_notify (G_OBJECT (self), "x-align"); + } +} + +static void +set_y_align (ClutterBinLayout *self, + ClutterBinAlignment alignment) +{ + ClutterBinLayoutPrivate *priv = self->priv; + + if (priv->y_align != alignment) + { + priv->y_align = alignment; + + g_object_notify (G_OBJECT (self), "y-align"); + } +} + +static void +clutter_bin_layout_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p) +{ + GList *children = clutter_container_get_children (container); + GList *l; + gfloat min_width, nat_width; + + min_width = nat_width = 0.0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat minimum, natural; + + clutter_actor_get_preferred_width (child, for_height, + &minimum, + &natural); + + min_width = MAX (min_width, minimum); + nat_width = MAX (nat_width, natural); + } + + if (min_width_p) + *min_width_p = min_width; + + if (nat_width_p) + *nat_width_p = nat_width; +} + +static void +clutter_bin_layout_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p) +{ + GList *children = clutter_container_get_children (container); + GList *l; + gfloat min_height, nat_height; + + min_height = nat_height = 0.0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat minimum, natural; + + clutter_actor_get_preferred_height (child, for_width, + &minimum, + &natural); + + min_height = MAX (min_height, minimum); + nat_height = MAX (nat_height, natural); + } + + if (min_height_p) + *min_height_p = min_height; + + if (nat_height_p) + *nat_height_p = nat_height; +} + +static gdouble +get_bin_alignment_factor (ClutterBinAlignment alignment) +{ + switch (alignment) + { + case CLUTTER_BIN_ALIGNMENT_CENTER: + return 0.5; + + case CLUTTER_BIN_ALIGNMENT_START: + return 0.0; + + case CLUTTER_BIN_ALIGNMENT_END: + return 1.0; + + case CLUTTER_BIN_ALIGNMENT_FIXED: + case CLUTTER_BIN_ALIGNMENT_FILL: + return 0.0; + } + + return 0.0; +} + +static void +clutter_bin_layout_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + ClutterBinLayoutPrivate *priv = CLUTTER_BIN_LAYOUT (manager)->priv; + GList *children = clutter_container_get_children (container); + GList *l; + gfloat available_w, available_h; + + available_w = clutter_actor_box_get_width (allocation); + available_h = clutter_actor_box_get_height (allocation); + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + ClutterActorBox child_alloc = { 0, }; + gfloat child_width, child_height; + ClutterRequestMode request; + + if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.x1 = (int) 0; + child_alloc.x2 = (int) available_w; + } + + if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.y1 = (int) 0; + child_alloc.y2 = (int) available_h; + } + + /* if we are filling horizontally and vertically then we + * can break here because we already have a full allocation + */ + if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL && + priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL) + { + clutter_actor_allocate (child, &child_alloc, flags); + break; + } + + request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH; + g_object_get (G_OBJECT (child), "request-mode", &request, NULL); + if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) + { + gfloat min_width, nat_width; + gfloat min_height, nat_height; + + clutter_actor_get_preferred_width (child, available_h, + &min_width, + &nat_width); + child_width = CLAMP (nat_width, min_width, available_w); + + clutter_actor_get_preferred_height (child, child_width, + &min_height, + &nat_height); + child_height = CLAMP (nat_height, min_height, available_h); + } + else if (request == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) + { + gfloat min_width, nat_width; + gfloat min_height, nat_height; + + clutter_actor_get_preferred_height (child, available_w, + &min_height, + &nat_height); + child_height = CLAMP (nat_height, min_height, available_h); + + clutter_actor_get_preferred_width (child, child_height, + &min_width, + &nat_width); + child_width = CLAMP (nat_width, min_width, available_w); + } + + if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) + { + child_alloc.x1 = (int) clutter_actor_get_x (child); + child_alloc.x2 = (int) child_alloc.x1 + child_width; + } + else + { + gdouble x_align = get_bin_alignment_factor (priv->x_align); + + if (priv->x_align != CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.x1 = (int) ((available_w - child_width) * x_align); + child_alloc.x2 = (int) child_alloc.x1 + child_width; + } + } + + if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) + { + child_alloc.y1 = (int) clutter_actor_get_y (child); + child_alloc.y2 = (int) child_alloc.y1 + child_height; + } + else + { + gdouble y_align = get_bin_alignment_factor (priv->y_align); + + if (priv->y_align != CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.y1 = (int) ((available_h - child_height) * y_align); + child_alloc.y2 = (int) child_alloc.y1 + child_height; + } + } + + clutter_actor_allocate (child, &child_alloc, flags); + } + + g_list_free (children); +} + +static void +clutter_bin_layout_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_X_ALIGN: + set_x_align (CLUTTER_BIN_LAYOUT (gobject), + g_value_get_enum (value)); + break; + + case PROP_Y_ALIGN: + set_y_align (CLUTTER_BIN_LAYOUT (gobject), + g_value_get_enum (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_bin_layout_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterBinLayoutPrivate *priv; + + priv = CLUTTER_BIN_LAYOUT (gobject)->priv; + + switch (prop_id) + { + case PROP_X_ALIGN: + g_value_set_enum (value, priv->x_align); + break; + + case PROP_Y_ALIGN: + g_value_set_enum (value, priv->y_align); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterLayoutManagerClass *layout_class = + CLUTTER_LAYOUT_MANAGER_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (ClutterBinLayoutPrivate)); + + gobject_class->set_property = clutter_bin_layout_set_property; + gobject_class->get_property = clutter_bin_layout_get_property; + + pspec = g_param_spec_enum ("x-align", + "X Align", + "Horizontal alignment for the actors " + "inside the layout manager", + CLUTTER_TYPE_BIN_ALIGNMENT, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec); + + pspec = g_param_spec_enum ("y-align", + "Y Align", + "Vertical alignment for the actors " + "inside the layout manager", + CLUTTER_TYPE_BIN_ALIGNMENT, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec); + + layout_class->get_preferred_width = + clutter_bin_layout_get_preferred_width; + layout_class->get_preferred_height = + clutter_bin_layout_get_preferred_height; + layout_class->allocate = + clutter_bin_layout_allocate; +} + +static void +clutter_bin_layout_init (ClutterBinLayout *self) +{ + self->priv = CLUTTER_BIN_LAYOUT_GET_PRIVATE (self); + + self->priv->x_align = CLUTTER_BIN_ALIGNMENT_CENTER; + self->priv->y_align = CLUTTER_BIN_ALIGNMENT_CENTER; +} + +ClutterLayoutManager * +clutter_bin_layout_new (ClutterBinAlignment x_align, + ClutterBinAlignment y_align) +{ + return g_object_new (CLUTTER_TYPE_BIN_LAYOUT, + "x-align", x_align, + "y-align", y_align, + NULL); +} + +void +clutter_bin_layout_set_alignment (ClutterBinLayout *self, + ClutterBinAlignment x_align, + ClutterBinAlignment y_align) +{ + g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + + set_x_align (self, x_align); + set_y_align (self, y_align); +} + +void +clutter_bin_layout_get_alignment (ClutterBinLayout *self, + ClutterBinAlignment *x_align, + ClutterBinAlignment *y_align) +{ + g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + + if (x_align) + *x_align = self->priv->x_align; + + if (y_align) + *y_align = self->priv->y_align; +} diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h new file mode 100644 index 000000000..dd360deef --- /dev/null +++ b/clutter/clutter-bin-layout.h @@ -0,0 +1,50 @@ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only <clutter/clutter.h> can be included directly." +#endif + +#ifndef __CLUTTER_BIN_LAYOUT_H__ +#define __CLUTTER_BIN_LAYOUT_H__ + +#include <clutter/clutter-layout-manager.h> + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BIN_LAYOUT (clutter_bin_layout_get_type ()) +#define CLUTTER_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayout)) +#define CLUTTER_IS_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYOUT)) +#define CLUTTER_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass)) +#define CLUTTER_IS_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BIN_LAYOUT)) +#define CLUTTER_BIN_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass)) + +typedef struct _ClutterBinLayout ClutterBinLayout; +typedef struct _ClutterBinLayoutPrivate ClutterBinLayoutPrivate; +typedef struct _ClutterBinLayoutClass ClutterBinLayoutClass; + +typedef enum { + CLUTTER_BIN_ALIGNMENT_FIXED, + CLUTTER_BIN_ALIGNMENT_FILL, + CLUTTER_BIN_ALIGNMENT_START, + CLUTTER_BIN_ALIGNMENT_END, + CLUTTER_BIN_ALIGNMENT_CENTER +} ClutterBinAlignment; + +struct _ClutterBinLayout +{ + ClutterLayoutManager parent_instance; + + ClutterBinLayoutPrivate *priv; +}; + +struct _ClutterBinLayoutClass +{ + ClutterLayoutManagerClass parent_class; +}; + +GType clutter_bin_layout_get_type (void) G_GNUC_CONST; + +ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment align_x, + ClutterBinAlignment align_y); + +G_END_DECLS + +#endif /* __CLUTTER_BIN_LAYOUT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 5c5edd261..ef042ff85 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -43,6 +43,7 @@ #include "clutter-behaviour-rotate.h" #include "clutter-behaviour-scale.h" #include "clutter-binding-pool.h" +#include "clutter-bin-layout.h" #include "clutter-box.h" #include "clutter-cairo-texture.h" #include "clutter-child-meta.h" From a1853892bae7c54d28a86e253e04c3753d65e159 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Fri, 11 Sep 2009 15:34:13 +0100 Subject: [PATCH 06/50] [tests] Add a Box interactive test --- .gitignore | 1 + tests/interactive/Makefile.am | 3 ++- tests/interactive/test-box.c | 47 +++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/interactive/test-box.c diff --git a/.gitignore b/.gitignore index 1ca16fe5a..e492b15a7 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,7 @@ TAGS /tests/interactive/redhand_alpha.png /tests/interactive/test-script.json /tests/interactive/test-clutter-cairo-flowers +/tests/interactive/test-box /tests/conform/stamp-test-conformance /tests/conform/test-anchors /tests/conform/test-conformance diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 9187b1e5d..3a2215f65 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -43,7 +43,8 @@ UNIT_TESTS = \ test-text.c \ test-text-field.c \ test-clutter-cairo-flowers.c \ - test-cogl-vertex-buffer.c + test-cogl-vertex-buffer.c \ + test-box.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-box.c b/tests/interactive/test-box.c new file mode 100644 index 000000000..ecfa07a97 --- /dev/null +++ b/tests/interactive/test-box.c @@ -0,0 +1,47 @@ +#include <stdlib.h> +#include <gmodule.h> +#include <clutter/clutter.h> + +G_MODULE_EXPORT int +test_box_main (int argc, char *argv[]) +{ + ClutterActor *stage, *box, *rect; + ClutterLayoutManager *layout; + ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x99 }; + ClutterColor *color; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Box test"); + clutter_actor_set_size (stage, 320, 200); + + layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_BIN_ALIGNMENT_CENTER); + + box = clutter_box_new (layout); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); + clutter_actor_set_anchor_point_from_gravity (box, CLUTTER_GRAVITY_CENTER); + clutter_actor_set_position (box, 160, 100); + + rect = clutter_rectangle_new_with_color (&bg_color); + clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); + clutter_actor_set_size (rect, 100, 100); + + color = clutter_color_new (g_random_int_range (0, 255), + g_random_int_range (0, 255), + g_random_int_range (0, 255), + 255); + + rect = clutter_rectangle_new_with_color (color); + clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); + clutter_actor_set_size (rect, 50, 50); + + clutter_actor_show_all (stage); + + clutter_main (); + + clutter_color_free (color); + + return EXIT_SUCCESS; +} From d096a3c791ab5b105d55a19ab316cd8b2fff878b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Mon, 14 Sep 2009 11:04:11 +0100 Subject: [PATCH 07/50] [layout] Add LayoutManager::layout-changed signal If a sub-class of LayoutManager wishes to implement a parametrized layout policy it also needs a way to notify the container using the layout manager that the layout has changed. We cannot do it directly and automatically from the LayoutManager because a) it has no back link to the actor that it is using it and b) it can be attached to multiple actors. This is a job for <cue raising dramatic music> signals! By adding ClutterLayoutManager::layout-changed (and its relative emitted function) we can notify actors using the layout manager that the layout parameters have been changed, and thus they should queue a relayout. --- clutter/clutter-layout-manager.c | 103 ++++++++++++++++++++++++++++++- clutter/clutter-layout-manager.h | 44 +++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 912ccd2d3..f27b8cc81 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -1,8 +1,42 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + /** * SECTION:clutter-layout-manager * @short_description: Layout managers base class * - * #ClutterLayoutManager is FIXME + * #ClutterLayoutManager is a base abstract class for layout managers. A + * layout manager implements the layouting policy for a composite or a + * container actor: it controls the preferred size of the actor to which + * it has been paired, and it controls the allocation of its children. + * + * Any composite or container #ClutterActor subclass can delegate the + * layouting of its children to a #ClutterLayoutManager. Clutter provides + * a generic container using #ClutterLayoutManager called #ClutterBox. + * + * Clutter provides some simple #ClutterLayoutManager sub-classes, like + * #ClutterFixedLayout and #ClutterBinLayout. * * #ClutterLayoutManager is available since Clutter 1.2 */ @@ -23,10 +57,19 @@ G_OBJECT_TYPE_NAME (_obj), \ (method)); } G_STMT_END +enum +{ + LAYOUT_CHANGED, + + LAST_SIGNAL +}; + G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_INITIALLY_UNOWNED); +static guint manager_signals[LAST_SIGNAL] = { 0, }; + static void layout_manager_real_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, @@ -74,6 +117,45 @@ clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass) klass->get_preferred_width = layout_manager_real_get_preferred_width; klass->get_preferred_height = layout_manager_real_get_preferred_height; klass->allocate = layout_manager_real_allocate; + + /** + * ClutterLayoutManager::layout-changed: + * @manager: the #ClutterLayoutManager that emitted the signal + * + * The ::layout-changed signal is emitted each time a layout manager + * has been changed. Every #ClutterActor using the @manager instance + * as a layout manager should connect a handler to the ::layout-changed + * signal and queue a relayout on themselves: + * + * |[ + * static void layout_changed (ClutterLayoutManager *manager, + * ClutterActor *self) + * { + * clutter_actor_queue_relayout (self); + * } + * ... + * self->manager = g_object_ref_sink (manager); + * g_signal_connect (self->manager, "layout-changed", + * G_CALLBACK (layout_changed), + * self); + * ]| + * + * Sub-classes of #ClutterLayoutManager that implement a layout that + * can be controlled or changed using parameters should emit the + * ::layout-changed signal whenever one of the parameters changes, + * by using clutter_layout_manager_layout_changed(). + * + * Since: 1.2 + */ + manager_signals[LAYOUT_CHANGED] = + g_signal_new (I_("layout-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterLayoutManagerClass, + layout_changed), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -180,3 +262,22 @@ clutter_layout_manager_allocate (ClutterLayoutManager *manager, klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); klass->allocate (manager, container, allocation, flags); } + +/** + * clutter_layout_manager_layout_changed: + * @manager: a #ClutterLayoutManager + * + * Emits the #ClutterLayoutManager::layout-changed signal on @manager + * + * This function should only be called by implementations of the + * #ClutterLayoutManager class + * + * Since: 1.2 + */ +void +clutter_layout_manager_layout_changed (ClutterLayoutManager *manager) +{ + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + + g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0); +} diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index e053c658e..b979430c0 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -1,3 +1,27 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only <clutter/clutter.h> can be included directly." #endif @@ -30,11 +54,23 @@ typedef struct _ClutterLayoutManagerClass ClutterLayoutManagerClass; */ struct _ClutterLayoutManager { + /*< private >*/ GInitiallyUnowned parent_instance; }; /** * ClutterLayoutManagerClass: + * @get_preferred_width: virtual function; override to provide a preferred + * width for the layout manager. See also the get_preferred_width() + * virtual function in #ClutterActor + * @get_preferred_height: virtual function; override to provide a preferred + * height for the layout manager. See also the get_preferred_height() + * virtual function in #ClutterActor + * @allocate: virtual function; override to allocate the children of the + * layout manager. See also the allocate() virtual function in + * #ClutterActor + * @layout_changed: class handler for the #ClutterLayoutManager::layout-changed + * signal * * The #ClutterLayoutManagerClass structure contains only private * data and should be accessed using the provided API @@ -43,8 +79,10 @@ struct _ClutterLayoutManager */ struct _ClutterLayoutManagerClass { + /*< private >*/ GInitiallyUnownedClass parent_class; + /*< public >*/ void (* get_preferred_width) (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, @@ -60,6 +98,10 @@ struct _ClutterLayoutManagerClass const ClutterActorBox *allocation, ClutterAllocationFlags flags); + void (* layout_changed) (ClutterLayoutManager *manager); + + /*< private >*/ + /* padding for future expansion */ void (* _clutter_padding_1) (void); void (* _clutter_padding_2) (void); void (* _clutter_padding_3) (void); @@ -87,6 +129,8 @@ void clutter_layout_manager_allocate (ClutterLayoutManager *manage const ClutterActorBox *allocation, ClutterAllocationFlags flags); +void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager); + G_END_DECLS #endif /* __CLUTTER_LAYOUT_MANAGER_H__ */ From 4e8d8bbc15f26e95adfd59469306b7098f9fd491 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Mon, 14 Sep 2009 11:28:34 +0100 Subject: [PATCH 08/50] [layout] Update Box * Use ::layout-changed to queue a relayout when the layout changes * Destroy the Box children when destroying the Box * Allow getting the layout manager from the Box --- clutter/clutter-box.c | 72 ++++++++++++++++++++++++++++++++++++++++--- clutter/clutter-box.h | 4 ++- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index f3c0648ab..394c78886 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -24,6 +24,8 @@ struct _ClutterBoxPrivate ClutterLayoutManager *manager; GList *children; + + guint changed_id; }; enum @@ -257,16 +259,58 @@ clutter_box_real_allocate (ClutterActor *actor, } static void -clutter_box_dispose (GObject *gobject) +clutter_box_destroy (ClutterActor *actor) { - ClutterBoxPrivate *priv = CLUTTER_BOX (gobject)->priv; + ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + GList *l; + + for (l = priv->children; l != NULL; l = l->next) + clutter_actor_destroy (l->data); + + CLUTTER_ACTOR_CLASS (clutter_box_parent_class)->destroy (actor); +} + +static void +on_layout_changed (ClutterLayoutManager *manager, + ClutterActor *self) +{ + clutter_actor_queue_relayout (self); +} + +static void +set_layout_manager (ClutterBox *self, + ClutterLayoutManager *manager) +{ + ClutterBoxPrivate *priv = self->priv; if (priv->manager != NULL) { + if (priv->changed_id != 0) + g_signal_handler_disconnect (priv->manager, priv->changed_id); + g_object_unref (priv->manager); + priv->manager = NULL; + priv->changed_id = 0; } + if (manager != NULL) + { + priv->manager = g_object_ref_sink (manager); + priv->changed_id = + g_signal_connect (priv->manager, "layout-changed", + G_CALLBACK (on_layout_changed), + self); + } +} + +static void +clutter_box_dispose (GObject *gobject) +{ + ClutterBox *self = CLUTTER_BOX (gobject); + + set_layout_manager (self, NULL); + G_OBJECT_CLASS (clutter_box_parent_class)->dispose (gobject); } @@ -276,13 +320,12 @@ clutter_box_set_property (GObject *gobject, const GValue *value, GParamSpec *pspec) { - ClutterBoxPrivate *priv = CLUTTER_BOX (gobject)->priv; + ClutterBox *self = CLUTTER_BOX (gobject); switch (prop_id) { case PROP_LAYOUT_MANAGER: - priv->manager = g_value_get_object (value); - g_object_ref_sink (priv->manager); + set_layout_manager (self, g_value_get_object (value)); break; default: @@ -325,6 +368,7 @@ clutter_box_class_init (ClutterBoxClass *klass) actor_class->allocate = clutter_box_real_allocate; actor_class->paint = clutter_box_real_paint; actor_class->pick = clutter_box_real_pick; + actor_class->destroy = clutter_box_destroy; gobject_class->set_property = clutter_box_set_property; gobject_class->get_property = clutter_box_get_property; @@ -367,3 +411,21 @@ clutter_box_new (ClutterLayoutManager *manager) "layout-manager", manager, NULL); } + +/** + * clutter_box_get_layout_manager: + * @box: a #ClutterBox + * + * Retrieves the #ClutterLayoutManager instance used by @box + * + * Return value: a #ClutterLayoutManager + * + * Since: 1.2 + */ +ClutterLayoutManager * +clutter_box_get_layout_manager (ClutterBox *box) +{ + g_return_val_if_fail (CLUTTER_IS_BOX (box), NULL); + + return box->priv->manager; +} diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h index 6793ee265..40d359c89 100644 --- a/clutter/clutter-box.h +++ b/clutter/clutter-box.h @@ -36,7 +36,9 @@ struct _ClutterBoxClass GType clutter_box_get_type (void) G_GNUC_CONST; -ClutterActor *clutter_box_new (ClutterLayoutManager *manager); +ClutterActor * clutter_box_new (ClutterLayoutManager *manager); + +ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box); G_END_DECLS From b06a3293fe3d065369d9f97ad8bfa77051414fec Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Mon, 14 Sep 2009 12:03:38 +0100 Subject: [PATCH 09/50] [layout] Notify of alignment changes in BinLayout Emit the ::layout-changed when the BinLayout alignment policies change. This will result in a queue_relayout() on the containers using the BinLayout layout manager. --- clutter/clutter-bin-layout.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index 81a9cef39..be544abf3 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -41,8 +41,13 @@ set_x_align (ClutterBinLayout *self, if (priv->x_align != alignment) { + ClutterLayoutManager *manager; + priv->x_align = alignment; + manager = CLUTTER_LAYOUT_MANAGER (self); + clutter_layout_manager_layout_changed (manager); + g_object_notify (G_OBJECT (self), "x-align"); } } @@ -55,8 +60,13 @@ set_y_align (ClutterBinLayout *self, if (priv->y_align != alignment) { + ClutterLayoutManager *manager; + priv->y_align = alignment; + manager = CLUTTER_LAYOUT_MANAGER (self); + clutter_layout_manager_layout_changed (manager); + g_object_notify (G_OBJECT (self), "y-align"); } } From 83a4e9626772453804c61d7aa022eacf540bf0ce Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Mon, 14 Sep 2009 12:04:42 +0100 Subject: [PATCH 10/50] [layout] Document BinLayout --- clutter/clutter-bin-layout.c | 93 ++++++++++++++++++++++++++++++++++++ clutter/clutter-bin-layout.h | 46 +++++++++++++++++- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index be544abf3..c922319c6 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -1,3 +1,38 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + +/** + * SECTION:clutter-bin-layout + * @short_description: A simple layout manager + * + * #ClutterBinLayout is a layout manager which implements the following + * policy: + * + * + * #ClutterBinLayout is available since Clutter 1.2 + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -336,6 +371,14 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) gobject_class->set_property = clutter_bin_layout_set_property; gobject_class->get_property = clutter_bin_layout_get_property; + /** + * ClutterBinLayout:x-align: + * + * The horizontal alignment policy for actors managed by the + * #ClutterBinLayout + * + * Since: 1.2 + */ pspec = g_param_spec_enum ("x-align", "X Align", "Horizontal alignment for the actors " @@ -345,6 +388,14 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec); + /** + * ClutterBinLayout:y-align: + * + * The vertical alignment policy for actors managed by the + * #ClutterBinLayout + * + * Since: 1.2 + */ pspec = g_param_spec_enum ("y-align", "Y Align", "Vertical alignment for the actors " @@ -371,6 +422,19 @@ clutter_bin_layout_init (ClutterBinLayout *self) self->priv->y_align = CLUTTER_BIN_ALIGNMENT_CENTER; } +/** + * clutter_bin_layout_new: + * @x_align: the #ClutterBinAlignment policy to be used on the + * horizontal axis + * @y_align: the #ClutterBinAlignment policy to be used on the + * vertical axis + * + * Creates a new #ClutterBinLayout layout manager + * + * Return value: the newly created layout manager + * + * Since: 1.2 + */ ClutterLayoutManager * clutter_bin_layout_new (ClutterBinAlignment x_align, ClutterBinAlignment y_align) @@ -381,6 +445,19 @@ clutter_bin_layout_new (ClutterBinAlignment x_align, NULL); } +/** + * clutter_bin_layout_set_alignment: + * @self: a #ClutterBinLayout + * @x_align: the #ClutterBinAlignment policy to be used on the + * horizontal axis + * @y_align: the #ClutterBinAlignment policy to be used on the + * vertical axis + * + * Sets the alignment policies on the horizontal and vertical + * axis for @self + * + * Since: 1.2 + */ void clutter_bin_layout_set_alignment (ClutterBinLayout *self, ClutterBinAlignment x_align, @@ -388,10 +465,26 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self, { g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + g_object_freeze_notify (G_OBJECT (self)); + set_x_align (self, x_align); set_y_align (self, y_align); + + g_object_thaw_notify (G_OBJECT (self)); } +/** + * clutter_bin_layout_get_alignment: + * @self: a #ClutterBinLayout + * @x_align: (out) (allow-none): return location for the horizontal + * alignment policy + * @y_align: (out) (allow-none): return location for the vertical + * alignment policy + * + * Retrieves the horizontal and vertical alignment policies for @self + * + * Since: 1.2 + */ void clutter_bin_layout_get_alignment (ClutterBinLayout *self, ClutterBinAlignment *x_align, diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h index dd360deef..09449a1f6 100644 --- a/clutter/clutter-bin-layout.h +++ b/clutter/clutter-bin-layout.h @@ -20,6 +20,23 @@ typedef struct _ClutterBinLayout ClutterBinLayout; typedef struct _ClutterBinLayoutPrivate ClutterBinLayoutPrivate; typedef struct _ClutterBinLayoutClass ClutterBinLayoutClass; +/** + * ClutterBinAlignment: + * @CLUTTER_BIN_ALIGNMENT_FIXED: Fixed position alignment; the + * #ClutterBinLayout will honour the fixed position provided + * by the actors themselves when allocating them + * @CLUTTER_BIN_ALIGNMENT_FILL: Fill the allocation size + * @CLUTTER_BIN_ALIGNMENT_START: Position the actors at the top + * or left side of the container, depending on the axis + * @CLUTTER_BIN_ALIGNMENT_END: Position the actors at the bottom + * or right side of the container, depending on the axis + * @CLUTTER_BIN_ALIGNMENT_CENTER: Position the actors at the + * center of the container, depending on the axis + * + * The alignment policies available on each axis for #ClutterBinLayout + * + * Since: 1.2 + */ typedef enum { CLUTTER_BIN_ALIGNMENT_FIXED, CLUTTER_BIN_ALIGNMENT_FILL, @@ -28,22 +45,47 @@ typedef enum { CLUTTER_BIN_ALIGNMENT_CENTER } ClutterBinAlignment; +/** + * ClutterBinLayout: + * + * The #ClutterBinLayout structure contains only private data + * and should be accessed using the provided API + * + * Since: 1.2 + */ struct _ClutterBinLayout { + /*< private >*/ ClutterLayoutManager parent_instance; ClutterBinLayoutPrivate *priv; }; +/** + * ClutterBinLayoutClass: + * + * The #ClutterBinLayoutClass structure contains only private + * data and should be accessed using the provided API + * + * Since: 1.2 + */ struct _ClutterBinLayoutClass { + /*< private >*/ ClutterLayoutManagerClass parent_class; }; GType clutter_bin_layout_get_type (void) G_GNUC_CONST; -ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment align_x, - ClutterBinAlignment align_y); +ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment align_x, + ClutterBinAlignment align_y); + +void clutter_bin_layout_set_alignment (ClutterBinLayout *self, + ClutterBinAlignment x_align, + ClutterBinAlignment y_align); +void clutter_bin_layout_get_alignment (ClutterBinLayout *self, + ClutterBinAlignment *x_align, + ClutterBinAlignment *y_align); G_END_DECLS From 7051fe275d9d052ce42e8237080844aa18ee927c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Mon, 14 Sep 2009 21:48:06 +0100 Subject: [PATCH 11/50] [layout] Bind ChildMeta to LayoutManager The ChildMeta object is a storage for child-container properties, that is properties that exist only when an actor is inside a specific container. The LayoutManager delegate class should also have layout-specific properties -- so, for this job, we can "recycle" ChildMeta as the storage. --- clutter/clutter-layout-manager.c | 372 +++++++++++++++++++++++++++++++ clutter/clutter-layout-manager.h | 88 +++++--- 2 files changed, 430 insertions(+), 30 deletions(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index f27b8cc81..7d6e7dd6d 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -45,6 +45,9 @@ #include "config.h" #endif +#include <glib-object.h> +#include <gobject/gvaluecollector.h> + #include "clutter-debug.h" #include "clutter-layout-manager.h" #include "clutter-marshal.h" @@ -68,6 +71,7 @@ G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_INITIALLY_UNOWNED); +static GQuark quark_layout_meta = 0; static guint manager_signals[LAST_SIGNAL] = { 0, }; static void @@ -111,12 +115,24 @@ layout_manager_real_allocate (ClutterLayoutManager *manager, LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate"); } +static ClutterChildMeta * +layout_manager_real_create_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor) +{ + return NULL; +} + static void clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass) { + quark_layout_meta = + g_quark_from_static_string ("clutter-layout-manager-child-meta"); + klass->get_preferred_width = layout_manager_real_get_preferred_width; klass->get_preferred_height = layout_manager_real_get_preferred_height; klass->allocate = layout_manager_real_allocate; + klass->create_child_meta = layout_manager_real_create_child_meta; /** * ClutterLayoutManager::layout-changed: @@ -281,3 +297,359 @@ clutter_layout_manager_layout_changed (ClutterLayoutManager *manager) g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0); } + +static inline ClutterChildMeta * +create_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor) +{ + ClutterLayoutManagerClass *klass; + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + + return klass->create_child_meta (manager, container, actor); +} + +static inline ClutterChildMeta * +get_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor) +{ + ClutterChildMeta *meta; + + meta = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta); + if (meta != NULL && + meta->container == container && + meta->actor == actor) + return meta; + + return NULL; +} + +/** + * clutter_layout_manager_get_child_meta: + * @manager: a #ClutterLayoutManager + * @container: a #ClutterContainer using @manager + * @actor: a #ClutterActor child of @container + * + * Retrieves the #ClutterChildMeta that the layout @manager associated + * to the @actor child of @container + * + * Return value: a #ClutterChildMeta or %NULL + * + * Since: 1.0 + */ +ClutterChildMeta * +clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor) +{ + g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL); + g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL); + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); + + return get_child_meta (manager, container, actor); +} + +void +clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor) +{ + ClutterChildMeta *meta; + + meta = create_child_meta (manager, container, actor); + if (meta == NULL) + return; + + g_object_set_qdata_full (G_OBJECT (actor), quark_layout_meta, + meta, + (GDestroyNotify) g_object_unref); +} + +void +clutter_layout_manager_remove_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor) +{ + ClutterChildMeta *meta; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + if (get_child_meta (manager, container, actor)) + g_object_set_qdata (G_OBJECT (actor), quark_layout_meta, NULL); +} + +static inline gboolean +layout_set_property_internal (ClutterLayoutManager *manager, + GObject *gobject, + GParamSpec *pspec, + const GValue *value) +{ + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) + { + g_warning ("%s: Child property '%s' of the layout manager of " + "type '%s' is constructor-only", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); + return FALSE; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("%s: Child property '%s' of the layout manager of " + "type '%s' is not writable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); + return FALSE; + } + + g_object_set_property (gobject, pspec->name, value); + + return TRUE; +} + +static inline gboolean +layout_get_property_internal (ClutterLayoutManager *manager, + GObject *gobject, + GParamSpec *pspec, + GValue *value) +{ + if (!(pspec->flags & G_PARAM_READABLE)) + { + g_warning ("%s: Child property '%s' of the layout manager of " + "type '%s' is not readable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); + return FALSE; + } + + g_object_get_property (gobject, pspec->name, value); + + return TRUE; +} + +void +clutter_layout_manager_child_set (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *first_property, + ...) +{ + ClutterChildMeta *meta; + GObjectClass *klass; + const gchar *pname; + va_list var_args; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (first_property != NULL); + + meta = get_child_meta (manager, container, actor); + if (meta == NULL) + { + g_warning ("Layout managers of type '%s' do not support " + "child metadata", + g_type_name (G_OBJECT_TYPE (manager))); + return; + } + + klass = G_OBJECT_GET_CLASS (meta); + + va_start (var_args, first_property); + + pname = first_property; + while (pname) + { + GValue value = { 0, }; + GParamSpec *pspec; + gchar *error; + gboolean res; + + pspec = g_object_class_find_property (klass, pname); + if (pspec == NULL) + { + g_warning ("%s: Layout managers of type '%s' have no child " + "property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname); + break; + } + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + G_VALUE_COLLECT (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + break; + } + + res = layout_set_property_internal (manager, G_OBJECT (meta), + pspec, + &value); + + g_value_unset (&value); + + if (!res) + break; + + pname = va_arg (var_args, gchar*); + } + + va_end (var_args); +} + +void +clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *property_name, + const GValue *value) +{ + ClutterChildMeta *meta; + GObjectClass *klass; + GParamSpec *pspec; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (property_name != NULL); + g_return_if_fail (value != NULL); + + meta = get_child_meta (manager, container, actor); + if (meta == NULL) + { + g_warning ("Layout managers of type '%s' do not support " + "child metadata", + g_type_name (G_OBJECT_TYPE (manager))); + return; + } + + klass = G_OBJECT_GET_CLASS (meta); + + pspec = g_object_class_find_property (klass, property_name); + if (pspec == NULL) + { + g_warning ("%s: Layout managers of type '%s' have no child " + "property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name); + return; + } + + layout_set_property_internal (manager, G_OBJECT (meta), pspec, value); +} + +void +clutter_layout_manager_child_get (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *first_property, + ...) +{ + ClutterChildMeta *meta; + GObjectClass *klass; + const gchar *pname; + va_list var_args; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (first_property != NULL); + + meta = get_child_meta (manager, container, actor); + if (meta == NULL) + { + g_warning ("Layout managers of type '%s' do not support " + "child metadata", + g_type_name (G_OBJECT_TYPE (manager))); + return; + } + + klass = G_OBJECT_GET_CLASS (meta); + + va_start (var_args, first_property); + + pname = first_property; + while (pname) + { + GValue value = { 0, }; + GParamSpec *pspec; + gchar *error; + gboolean res; + + pspec = g_object_class_find_property (klass, pname); + if (pspec == NULL) + { + g_warning ("%s: Layout managers of type '%s' have no child " + "property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname); + break; + } + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + res = layout_get_property_internal (manager, G_OBJECT (meta), + pspec, + &value); + if (!res) + { + g_value_unset (&value); + break; + } + + G_VALUE_LCOPY (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + g_value_unset (&value); + break; + } + + g_value_unset (&value); + + pname = va_arg (var_args, gchar*); + } + + va_end (var_args); +} + +void +clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *property_name, + GValue *value) +{ + ClutterChildMeta *meta; + GObjectClass *klass; + GParamSpec *pspec; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (property_name != NULL); + g_return_if_fail (value != NULL); + + meta = get_child_meta (manager, container, actor); + if (meta == NULL) + { + g_warning ("Layout managers of type %s do not support " + "child metadata", + g_type_name (G_OBJECT_TYPE (manager))); + return; + } + + klass = G_OBJECT_GET_CLASS (meta); + + pspec = g_object_class_find_property (klass, property_name); + if (pspec == NULL) + { + g_warning ("%s: Layout managers of type '%s' have no child " + "property named '%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name); + return; + } + + layout_get_property_internal (manager, G_OBJECT (meta), pspec, value); +} diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index b979430c0..4acd26447 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -69,6 +69,9 @@ struct _ClutterLayoutManager * @allocate: virtual function; override to allocate the children of the * layout manager. See also the allocate() virtual function in * #ClutterActor + * @get_child_meta: virtual function; override to create a #ClutterChildMeta + * instance associated to a #ClutterContainer and a child #ClutterActor, + * used to maintain layout manager specific properties * @layout_changed: class handler for the #ClutterLayoutManager::layout-changed * signal * @@ -83,22 +86,26 @@ struct _ClutterLayoutManagerClass GInitiallyUnownedClass parent_class; /*< public >*/ - void (* get_preferred_width) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_height, - gfloat *minimum_width_p, - gfloat *natural_width_p); - void (* get_preferred_height) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_width, - gfloat *minimum_height_p, - gfloat *natural_height_p); - void (* allocate) (ClutterLayoutManager *manager, - ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); + void (* get_preferred_width) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *minimum_width_p, + gfloat *natural_width_p); + void (* get_preferred_height) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *minimum_height_p, + gfloat *natural_height_p); + void (* allocate) (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); - void (* layout_changed) (ClutterLayoutManager *manager); + ClutterChildMeta *(* create_child_meta) (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); + + void (* layout_changed) (ClutterLayoutManager *manager); /*< private >*/ /* padding for future expansion */ @@ -114,22 +121,43 @@ struct _ClutterLayoutManagerClass GType clutter_layout_manager_get_type (void) G_GNUC_CONST; -void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_height, - gfloat *min_width_p, - gfloat *nat_width_p); -void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_width, - gfloat *min_height_p, - gfloat *nat_height_p); -void clutter_layout_manager_allocate (ClutterLayoutManager *manager, - ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); +void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p); +void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p); +void clutter_layout_manager_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); -void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager); +void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager); + +ClutterChildMeta *clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); +void clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); +void clutter_layout_manager_remove_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); + +void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *property_name, + const GValue *value); +void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *property_name, + GValue *value); G_END_DECLS From 98474076a1310494fec252fb6bfce8001bca78da Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Mon, 14 Sep 2009 21:50:20 +0100 Subject: [PATCH 12/50] [layout] Bind the layout ChildMeta inside Box The ClutterBox container actor should add and remove ChildMeta to each actor that has been added and removed to the list of children, respectively. --- clutter/clutter-box.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index 394c78886..c7ec8c453 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -70,6 +70,11 @@ clutter_box_real_add (ClutterContainer *container, sort_by_depth); clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); + + clutter_layout_manager_add_child_meta (priv->manager, + container, + actor); + clutter_actor_queue_relayout (actor); g_signal_emit_by_name (container, "actor-added", actor); @@ -88,6 +93,10 @@ clutter_box_real_remove (ClutterContainer *container, priv->children = g_list_remove (priv->children, actor); clutter_actor_unparent (actor); + clutter_layout_manager_remove_child_meta (priv->manager, + container, + actor); + clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); g_signal_emit_by_name (container, "actor-removed", actor); From 9cccff504a40706e70b11878572d7e7db7430792 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Mon, 14 Sep 2009 21:51:49 +0100 Subject: [PATCH 13/50] [layout] Add layers to BinLayout Each actor managed by a BinLayout policy should reside inside its own "layer", with horizontal and vertical alignment. The :x-align and :y-align properties of the BinLayout are the default alignment policies, which are copied to each new "layer" when it is created. The set_alignment() and get_alignment() methods of BinLayout can be changed to operate on a specific "layer". The whole machinery uses the new ChildMeta support inside the LayoutManager base abstract class. --- clutter/clutter-bin-layout.c | 265 +++++++++++++++++++++++++++++++---- clutter/clutter-bin-layout.h | 4 + tests/interactive/test-box.c | 147 ++++++++++++++++++- 3 files changed, 385 insertions(+), 31 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index c922319c6..8b92db69e 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -40,12 +40,20 @@ #include "clutter-actor.h" #include "clutter-animatable.h" #include "clutter-bin-layout.h" +#include "clutter-child-meta.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-private.h" +#define CLUTTER_TYPE_BIN_LAYER (clutter_bin_layer_get_type ()) +#define CLUTTER_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYER, ClutterBinLayer)) +#define CLUTTER_IS_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYER)) + #define CLUTTER_BIN_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutPrivate)) +typedef struct _ClutterBinLayer ClutterBinLayer; +typedef struct _ClutterChildMetaClass ClutterBinLayerClass; + struct _ClutterBinLayoutPrivate { ClutterBinAlignment x_align; @@ -55,6 +63,24 @@ struct _ClutterBinLayoutPrivate gdouble y_factor; }; +struct _ClutterBinLayer +{ + ClutterChildMeta parent_instance; + + ClutterBinLayout *layout; + + ClutterBinAlignment x_align; + ClutterBinAlignment y_align; +}; + +enum +{ + PROP_LAYER_0, + + PROP_LAYER_X_ALIGN, + PROP_LAYER_Y_ALIGN +}; + enum { PROP_0, @@ -63,11 +89,146 @@ enum PROP_Y_ALIGN }; +G_DEFINE_TYPE (ClutterBinLayer, + clutter_bin_layer, + CLUTTER_TYPE_CHILD_META); + G_DEFINE_TYPE (ClutterBinLayout, clutter_bin_layout, CLUTTER_TYPE_LAYOUT_MANAGER); +/* + * ClutterBinLayer + */ + +static void +set_layer_x_align (ClutterBinLayer *self, + ClutterBinAlignment alignment) +{ + ClutterLayoutManager *manager; + + if (self->x_align == alignment) + return; + + self->x_align = alignment; + + g_assert (self->layout != NULL); + manager = CLUTTER_LAYOUT_MANAGER (self->layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (self), "x-align"); +} + +static void +set_layer_y_align (ClutterBinLayer *self, + ClutterBinAlignment alignment) +{ + ClutterLayoutManager *manager; + + if (self->y_align == alignment) + return; + + self->y_align = alignment; + + g_assert (self->layout != NULL); + manager = CLUTTER_LAYOUT_MANAGER (self->layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (self), "y-align"); +} + +static void +clutter_bin_layer_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject); + + switch (prop_id) + { + case PROP_LAYER_X_ALIGN: + set_layer_x_align (layer, g_value_get_enum (value)); + break; + + case PROP_LAYER_Y_ALIGN: + set_layer_y_align (layer, g_value_get_enum (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_bin_layer_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject); + + switch (prop_id) + { + case PROP_LAYER_X_ALIGN: + g_value_set_enum (value, layer->x_align); + break; + + case PROP_LAYER_Y_ALIGN: + g_value_set_enum (value, layer->y_align); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_bin_layer_class_init (ClutterBinLayerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + gobject_class->set_property = clutter_bin_layer_set_property; + gobject_class->get_property = clutter_bin_layer_get_property; + + pspec = g_param_spec_enum ("x-align", + "X Align", + "Horizontal alignment for the actor " + "inside the layer", + CLUTTER_TYPE_BIN_ALIGNMENT, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_LAYER_X_ALIGN, + pspec); + + pspec = g_param_spec_enum ("y-align", + "Y Align", + "Vertical alignment for the actor " + "inside the layer manager", + CLUTTER_TYPE_BIN_ALIGNMENT, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_LAYER_Y_ALIGN, + pspec); +} + +static void +clutter_bin_layer_init (ClutterBinLayer *layer) +{ + layer->x_align = CLUTTER_BIN_ALIGNMENT_CENTER; + layer->y_align = CLUTTER_BIN_ALIGNMENT_CENTER; +} + +/* + * ClutterBinLayout + */ + static void set_x_align (ClutterBinLayout *self, ClutterBinAlignment alignment) @@ -200,7 +361,6 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { - ClutterBinLayoutPrivate *priv = CLUTTER_BIN_LAYOUT (manager)->priv; GList *children = clutter_container_get_children (container); GList *l; gfloat available_w, available_h; @@ -211,17 +371,24 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; + ClutterChildMeta *meta; + ClutterBinLayer *layer; ClutterActorBox child_alloc = { 0, }; gfloat child_width, child_height; ClutterRequestMode request; - if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL) + meta = clutter_layout_manager_get_child_meta (manager, + container, + child); + layer = CLUTTER_BIN_LAYER (meta); + + if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL) { child_alloc.x1 = (int) 0; child_alloc.x2 = (int) available_w; } - if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL) + if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL) { child_alloc.y1 = (int) 0; child_alloc.y2 = (int) available_h; @@ -230,11 +397,11 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, /* if we are filling horizontally and vertically then we * can break here because we already have a full allocation */ - if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL && - priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL) + if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL && + layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL) { clutter_actor_allocate (child, &child_alloc, flags); - break; + continue; } request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH; @@ -270,32 +437,32 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, child_width = CLAMP (nat_width, min_width, available_w); } - if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) + if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) { child_alloc.x1 = (int) clutter_actor_get_x (child); child_alloc.x2 = (int) child_alloc.x1 + child_width; } else { - gdouble x_align = get_bin_alignment_factor (priv->x_align); + gdouble x_align = get_bin_alignment_factor (layer->x_align); - if (priv->x_align != CLUTTER_BIN_ALIGNMENT_FILL) + if (layer->x_align != CLUTTER_BIN_ALIGNMENT_FILL) { child_alloc.x1 = (int) ((available_w - child_width) * x_align); child_alloc.x2 = (int) child_alloc.x1 + child_width; } } - if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) + if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) { child_alloc.y1 = (int) clutter_actor_get_y (child); child_alloc.y2 = (int) child_alloc.y1 + child_height; } else { - gdouble y_align = get_bin_alignment_factor (priv->y_align); + gdouble y_align = get_bin_alignment_factor (layer->y_align); - if (priv->y_align != CLUTTER_BIN_ALIGNMENT_FILL) + if (layer->y_align != CLUTTER_BIN_ALIGNMENT_FILL) { child_alloc.y1 = (int) ((available_h - child_height) * y_align); child_alloc.y2 = (int) child_alloc.y1 + child_height; @@ -308,6 +475,28 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, g_list_free (children); } +static ClutterChildMeta * +clutter_bin_layout_create_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor) +{ + ClutterBinLayoutPrivate *priv; + ClutterChildMeta *meta; + + priv = CLUTTER_BIN_LAYOUT (manager)->priv; + + meta = g_object_new (CLUTTER_TYPE_BIN_LAYER, + "container", container, + "actor", actor, + "x-align", priv->x_align, + "y_align", priv->y_align, + NULL); + + CLUTTER_BIN_LAYER (meta)->layout = CLUTTER_BIN_LAYOUT (manager); + + return meta; +} + static void clutter_bin_layout_set_property (GObject *gobject, guint prop_id, @@ -411,6 +600,8 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) clutter_bin_layout_get_preferred_height; layout_class->allocate = clutter_bin_layout_allocate; + layout_class->create_child_meta = + clutter_bin_layout_create_child_meta; } static void @@ -424,9 +615,9 @@ clutter_bin_layout_init (ClutterBinLayout *self) /** * clutter_bin_layout_new: - * @x_align: the #ClutterBinAlignment policy to be used on the + * @x_align: the default alignment policy to be used on the * horizontal axis - * @y_align: the #ClutterBinAlignment policy to be used on the + * @y_align: the default alignment policy to be used on the * vertical axis * * Creates a new #ClutterBinLayout layout manager @@ -448,29 +639,39 @@ clutter_bin_layout_new (ClutterBinAlignment x_align, /** * clutter_bin_layout_set_alignment: * @self: a #ClutterBinLayout - * @x_align: the #ClutterBinAlignment policy to be used on the - * horizontal axis - * @y_align: the #ClutterBinAlignment policy to be used on the - * vertical axis + * @container: a #ClutterContainer with a layout managed by @self + * @child: a #ClutterActor child of @container + * @x_align: the horizontal alignment policy to be used for the @child + * inside @container + * @y_align: the vertical aligment policy to be used on the @child + * inside @container * - * Sets the alignment policies on the horizontal and vertical - * axis for @self + * Sets the horizontal and vertical alignment policies to be applied + * to the @child of @container * * Since: 1.2 */ void clutter_bin_layout_set_alignment (ClutterBinLayout *self, + ClutterContainer *container, + ClutterActor *child, ClutterBinAlignment x_align, ClutterBinAlignment y_align) { + ClutterLayoutManager *manager; + ClutterChildMeta *meta; + ClutterBinLayer *layer; + g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); - g_object_freeze_notify (G_OBJECT (self)); + manager = CLUTTER_LAYOUT_MANAGER (self); + meta = clutter_layout_manager_get_child_meta (manager, container, child); + g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta)); - set_x_align (self, x_align); - set_y_align (self, y_align); + layer = CLUTTER_BIN_LAYER (meta); - g_object_thaw_notify (G_OBJECT (self)); + set_layer_x_align (layer, x_align); + set_layer_y_align (layer, y_align); } /** @@ -487,14 +688,26 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self, */ void clutter_bin_layout_get_alignment (ClutterBinLayout *self, + ClutterContainer *container, + ClutterActor *child, ClutterBinAlignment *x_align, ClutterBinAlignment *y_align) { + ClutterLayoutManager *manager; + ClutterChildMeta *meta; + ClutterBinLayer *layer; + g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + manager = CLUTTER_LAYOUT_MANAGER (self); + meta = clutter_layout_manager_get_child_meta (manager, container, child); + g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta)); + + layer = CLUTTER_BIN_LAYER (meta); + if (x_align) - *x_align = self->priv->x_align; + *x_align = layer->x_align; if (y_align) - *y_align = self->priv->y_align; + *y_align = layer->y_align; } diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h index 09449a1f6..5eb4617d6 100644 --- a/clutter/clutter-bin-layout.h +++ b/clutter/clutter-bin-layout.h @@ -81,9 +81,13 @@ ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment ali ClutterBinAlignment align_y); void clutter_bin_layout_set_alignment (ClutterBinLayout *self, + ClutterContainer *container, + ClutterActor *child, ClutterBinAlignment x_align, ClutterBinAlignment y_align); void clutter_bin_layout_get_alignment (ClutterBinLayout *self, + ClutterContainer *container, + ClutterActor *child, ClutterBinAlignment *x_align, ClutterBinAlignment *y_align); diff --git a/tests/interactive/test-box.c b/tests/interactive/test-box.c index ecfa07a97..2c3d50ed2 100644 --- a/tests/interactive/test-box.c +++ b/tests/interactive/test-box.c @@ -1,12 +1,101 @@ #include <stdlib.h> #include <gmodule.h> +#include <cairo/cairo.h> #include <clutter/clutter.h> +static ClutterActor * +make_background (const ClutterColor *color, + gfloat width, + gfloat height) +{ + ClutterActor *tex = clutter_cairo_texture_new (width, height); + cairo_t *cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (tex)); + cairo_pattern_t *pat; + gfloat x, y; + +#define BG_ROUND_RADIUS 12 + + x = y = 0; + + cairo_move_to (cr, BG_ROUND_RADIUS, y); + cairo_line_to (cr, width - BG_ROUND_RADIUS, y); + cairo_curve_to (cr, width, y, width, y, width, BG_ROUND_RADIUS); + cairo_line_to (cr, width, height - BG_ROUND_RADIUS); + cairo_curve_to (cr, width, height, width, height, width - BG_ROUND_RADIUS, height); + cairo_line_to (cr, BG_ROUND_RADIUS, height); + cairo_curve_to (cr, x, height, x, height, x, height - BG_ROUND_RADIUS); + cairo_line_to (cr, x, BG_ROUND_RADIUS); + cairo_curve_to (cr, x, y, x, y, BG_ROUND_RADIUS, y); + + cairo_close_path (cr); + + clutter_cairo_set_source_color (cr, color); + cairo_stroke (cr); + + x += 4; + y += 4; + width -= 4; + height -= 4; + + cairo_move_to (cr, BG_ROUND_RADIUS, y); + cairo_line_to (cr, width - BG_ROUND_RADIUS, y); + cairo_curve_to (cr, width, y, width, y, width, BG_ROUND_RADIUS); + cairo_line_to (cr, width, height - BG_ROUND_RADIUS); + cairo_curve_to (cr, width, height, width, height, width - BG_ROUND_RADIUS, height); + cairo_line_to (cr, BG_ROUND_RADIUS, height); + cairo_curve_to (cr, x, height, x, height, x, height - BG_ROUND_RADIUS); + cairo_line_to (cr, x, BG_ROUND_RADIUS); + cairo_curve_to (cr, x, y, x, y, BG_ROUND_RADIUS, y); + + cairo_close_path (cr); + + pat = cairo_pattern_create_linear (0, 0, 0, height); + cairo_pattern_add_color_stop_rgba (pat, 1, .85, .85, .85, 1); + cairo_pattern_add_color_stop_rgba (pat, .95, 1, 1, 1, 1); + cairo_pattern_add_color_stop_rgba (pat, .05, 1, 1, 1, 1); + cairo_pattern_add_color_stop_rgba (pat, 0, .85, .85, .85, 1); + + cairo_set_source (cr, pat); + cairo_fill (cr); + + cairo_pattern_destroy (pat); + cairo_destroy (cr); + +#undef BG_ROUND_RADIUS + + return tex; +} + +static gboolean +on_box_enter (ClutterActor *box, + ClutterEvent *event, + ClutterActor *emblem) +{ + clutter_actor_animate (emblem, CLUTTER_LINEAR, 150, + "opacity", 255, + NULL); + + return TRUE; +} + +static gboolean +on_box_leave (ClutterActor *box, + ClutterEvent *event, + ClutterActor *emblem) +{ + clutter_actor_animate (emblem, CLUTTER_LINEAR, 150, + "opacity", 0, + NULL); + + return TRUE; +} + G_MODULE_EXPORT int test_box_main (int argc, char *argv[]) { ClutterActor *stage, *box, *rect; ClutterLayoutManager *layout; + ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff }; ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x99 }; ClutterColor *color; @@ -14,7 +103,8 @@ test_box_main (int argc, char *argv[]) stage = clutter_stage_get_default (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Box test"); - clutter_actor_set_size (stage, 320, 200); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, 640, 480); layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_BIN_ALIGNMENT_CENTER); @@ -22,20 +112,67 @@ test_box_main (int argc, char *argv[]) box = clutter_box_new (layout); clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); clutter_actor_set_anchor_point_from_gravity (box, CLUTTER_GRAVITY_CENTER); - clutter_actor_set_position (box, 160, 100); + clutter_actor_set_position (box, 320, 240); + clutter_actor_set_reactive (box, TRUE); + clutter_actor_set_name (box, "box"); - rect = clutter_rectangle_new_with_color (&bg_color); + rect = make_background (&bg_color, 200, 200); clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); - clutter_actor_set_size (rect, 100, 100); + clutter_actor_lower_bottom (rect); + clutter_actor_set_name (rect, "background"); + + clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), + CLUTTER_CONTAINER (box), + rect, + CLUTTER_BIN_ALIGNMENT_FILL, + CLUTTER_BIN_ALIGNMENT_FILL); + + { + ClutterActor *tex; + GError *error; + + error = NULL; + tex = clutter_texture_new_from_file ("redhand.png", &error); + if (error) + g_error ("Unable to create texture: %s", error->message); + + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (tex), TRUE); + clutter_container_add_actor (CLUTTER_CONTAINER (box), tex); + clutter_actor_raise (tex, rect); + clutter_actor_set_width (tex, 175); + clutter_actor_set_name (tex, "texture"); + + clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), + CLUTTER_CONTAINER (box), + tex, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_BIN_ALIGNMENT_CENTER); + } color = clutter_color_new (g_random_int_range (0, 255), g_random_int_range (0, 255), g_random_int_range (0, 255), - 255); + 224); rect = clutter_rectangle_new_with_color (color); clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); clutter_actor_set_size (rect, 50, 50); + clutter_actor_set_opacity (rect, 0); + clutter_actor_raise_top (rect); + clutter_actor_set_name (rect, "emblem"); + + clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), + CLUTTER_CONTAINER (box), + rect, + CLUTTER_BIN_ALIGNMENT_END, + CLUTTER_BIN_ALIGNMENT_END); + + g_signal_connect (box, + "enter-event", G_CALLBACK (on_box_enter), + rect); + g_signal_connect (box, + "leave-event", G_CALLBACK (on_box_leave), + rect); clutter_actor_show_all (stage); From 899db6f226864614350d6aa07d8313dccf7d652f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 15 Sep 2009 16:24:47 +0100 Subject: [PATCH 14/50] [layout, docs] Add layout managers sections Add LayoutManager and its subclasses, and the Box actor to the gtk-doc machinery needed to generate the API reference. --- clutter/clutter-bin-layout.c | 6 +- clutter/clutter-bin-layout.h | 4 +- clutter/clutter-layout-manager.c | 145 ++++++++++++++++++++- clutter/clutter-layout-manager.h | 16 ++- doc/reference/clutter/clutter-docs.xml.in | 9 ++ doc/reference/clutter/clutter-sections.txt | 94 +++++++++++++ doc/reference/clutter/clutter.types | 4 + 7 files changed, 269 insertions(+), 9 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index 8b92db69e..bca6f9e39 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -639,8 +639,8 @@ clutter_bin_layout_new (ClutterBinAlignment x_align, /** * clutter_bin_layout_set_alignment: * @self: a #ClutterBinLayout - * @container: a #ClutterContainer with a layout managed by @self - * @child: a #ClutterActor child of @container + * @container: a #ClutterContainer using the #ClutterBinLayout + * @child: a child of @container * @x_align: the horizontal alignment policy to be used for the @child * inside @container * @y_align: the vertical aligment policy to be used on the @child @@ -677,6 +677,8 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self, /** * clutter_bin_layout_get_alignment: * @self: a #ClutterBinLayout + * @container: a #ClutterContainer using the #ClutterBinLayout + * @child: a child of @container * @x_align: (out) (allow-none): return location for the horizontal * alignment policy * @y_align: (out) (allow-none): return location for the vertical diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h index 5eb4617d6..032d73794 100644 --- a/clutter/clutter-bin-layout.h +++ b/clutter/clutter-bin-layout.h @@ -77,8 +77,8 @@ struct _ClutterBinLayoutClass GType clutter_bin_layout_get_type (void) G_GNUC_CONST; -ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment align_x, - ClutterBinAlignment align_y); +ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment x_align, + ClutterBinAlignment y_align); void clutter_bin_layout_set_alignment (ClutterBinLayout *self, ClutterContainer *container, diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 7d6e7dd6d..c62c0567e 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -351,6 +351,46 @@ clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, return get_child_meta (manager, container, actor); } +/** + * clutter_layout_manager_add_child_meta: + * @manager: a #ClutterLayoutManager + * @container: a #ClutterContainer using @manager + * @actor: a #ClutterActor child of @container + * + * Creates and binds a #ClutterChildMeta for @manager to + * a child of @container + * + * This function should only be used when implementing containers + * using #ClutterLayoutManager and not by application code + * + * Typically, containers should bind a #ClutterChildMeta created + * by a #ClutterLayoutManager when adding a new child, e.g.: + * + * |[ + * static void + * my_container_add (ClutterContainer *container, + * ClutterActor *actor) + * { + * MyContainer *self = MY_CONTAINER (container); + * + * self->children = g_slist_append (self->children, actor); + * clutter_actor_set_parent (actor, CLUTTER_ACTOR (self)); + * + * clutter_layout_manager_add_child_meta (self->layout, + * container, + * actor); + * + * clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + * + * g_signal_emit_by_name (container, "actor-added"); + * } + * ]| + * + * The #ClutterChildMeta should be removed when removing an + * actor; see clutter_layout_manager_remove_child_meta() + * + * Since: 1.2 + */ void clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, @@ -367,13 +407,54 @@ clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, (GDestroyNotify) g_object_unref); } +/** + * clutter_layout_manager_remove_child_meta: + * @manager: a #ClutterLayoutManager + * @container: a #ClutterContainer using @manager + * @actor: a #ClutterActor child of @container + * + * Unbinds and unrefs a #ClutterChildMeta for @manager from + * a child of @container + * + * This function should only be used when implementing containers + * using #ClutterLayoutManager and not by application code + * + * Typically, containers should remove a #ClutterChildMeta created + * by a #ClutterLayoutManager when removing a child, e.g.: + * + * |[ + * static void + * my_container_remove (ClutterContainer *container, + * ClutterActor *actor) + * { + * MyContainer *self = MY_CONTAINER (container); + * + * g_object_ref (actor); + * + * self->children = g_slist_remove (self->children, actor); + * clutter_actor_unparent (actor); + * + * clutter_layout_manager_remove_child_meta (self->layout, + * container, + * actor); + * + * clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + * + * g_signal_emit_by_name (container, "actor-removed"); + * + * g_object_unref (actor); + * } + * ]| + * + * See also clutter_layout_manager_add_child_meta() + * + * Since: 1.2 + */ void clutter_layout_manager_remove_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { - ClutterChildMeta *meta; - g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); @@ -428,6 +509,22 @@ layout_get_property_internal (ClutterLayoutManager *manager, return TRUE; } +/** + * clutter_layout_manager_child_set: + * @manager: a #ClutterLayoutManager + * @container: a #ClutterContainer using @manager + * @actor: a #ClutterActor child of @container + * @first_property: the first property name + * @Varargs: a list of property name and value pairs + * + * Sets a list of properties and their values on the #ClutterChildMeta + * associated by @manager to a child of @container + * + * Languages bindings should use clutter_layout_manager_child_set_property() + * instead + * + * Since: 1.2 + */ void clutter_layout_manager_child_set (ClutterLayoutManager *manager, ClutterContainer *container, @@ -499,6 +596,19 @@ clutter_layout_manager_child_set (ClutterLayoutManager *manager, va_end (var_args); } +/** + * clutter_layout_manager_child_set_property: + * @manager: a #ClutterLayoutManager + * @container: a #ClutterContainer using @manager + * @actor: a #ClutterActor child of @container + * @property_name: the name of the property to set + * @value: a #GValue with the value of the property to set + * + * Sets a property on the #ClutterChildMeta created by @manager and + * attached to a child of @container + * + * Since: 1.2 + */ void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, ClutterContainer *container, @@ -539,6 +649,20 @@ clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, layout_set_property_internal (manager, G_OBJECT (meta), pspec, value); } +/** + * clutter_layout_manager_child_get: + * @manager: a #ClutterLayoutManager + * @container: a #ClutterContainer using @manager + * @actor: a #ClutterActor child of @container + * @first_property: the name of the first property + * @Varargs: a list of property name and return location for the value pairs + * + * Retrieves the values for a list of properties out of the + * #ClutterChildMeta created by @manager and attached to the + * child of a @container + * + * Since: 1.2 + */ void clutter_layout_manager_child_get (ClutterLayoutManager *manager, ClutterContainer *container, @@ -614,6 +738,23 @@ clutter_layout_manager_child_get (ClutterLayoutManager *manager, va_end (var_args); } +/** + * clutter_layout_manager_child_get_property: + * @manager: a #ClutterLayoutManager + * @container: a #ClutterContainer using @manager + * @actor: a #ClutterActor child of @container + * @property_name: the name of the property to get + * @value: a #GValue with the value of the property to get + * + * Gets a property on the #ClutterChildMeta created by @manager and + * attached to a child of @container + * + * The #GValue must already be initialized to the type of the property + * and has to be unset with g_value_unset() after extracting the real + * value out of it + * + * Since: 1.2 + */ void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, ClutterContainer *container, diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index 4acd26447..429be2006 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -69,9 +69,9 @@ struct _ClutterLayoutManager * @allocate: virtual function; override to allocate the children of the * layout manager. See also the allocate() virtual function in * #ClutterActor - * @get_child_meta: virtual function; override to create a #ClutterChildMeta - * instance associated to a #ClutterContainer and a child #ClutterActor, - * used to maintain layout manager specific properties + * @create_child_meta: virtual function; override to create a + * #ClutterChildMeta instance associated to a #ClutterContainer and a + * child #ClutterActor, used to maintain layout manager specific properties * @layout_changed: class handler for the #ClutterLayoutManager::layout-changed * signal * @@ -148,6 +148,16 @@ void clutter_layout_manager_remove_child_meta (ClutterLayoutMana ClutterContainer *container, ClutterActor *actor); +void clutter_layout_manager_child_set (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *first_property, + ...) G_GNUC_NULL_TERMINATED; +void clutter_layout_manager_child_get (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *first_property, + ...) G_GNUC_NULL_TERMINATED; void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index 1a6409105..7b1be959f 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -55,6 +55,7 @@ <xi:include href="xml/clutter-container.xml"/> <xi:include href="xml/clutter-child-meta.xml"/> <xi:include href="xml/clutter-media.xml"/> + <xi:include href="xml/clutter-layout-manager.xml"/> </chapter> <chapter> @@ -71,6 +72,14 @@ <xi:include href="xml/clutter-group.xml"/> <xi:include href="xml/clutter-stage.xml"/> + <xi:include href="xml/clutter-box.xml"/> + </chapter> + + <chapter> + <title>Layout managers</title> + + <xi:include href="xml/clutter-fixed-layout.xml"/> + <xi:include href="xml/clutter-bin-layout.xml"/> </chapter> </part> diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 1ba2c6868..34bcae8d2 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1725,3 +1725,97 @@ CLUTTER_STAGE_MANAGER_GET_CLASS <SUBSECTION Private> clutter_stage_manager_get_type </SECTION> + +<SECTION> +<TITLE>Layout Managers</TITLE> +<FILE>clutter-layout-manager</FILE> +ClutterLayoutManager +ClutterLayoutManagerClass +clutter_layout_manager_get_preferred_width +clutter_layout_manager_get_preferred_height +clutter_layout_manager_allocate +clutter_layout_manager_layout_changed + +<SUBSECTION> +clutter_layout_manager_add_child_meta +clutter_layout_manager_remove_child_meta +clutter_layout_manager_get_child_meta +clutter_layout_manager_child_set +clutter_layout_manager_child_set_property +clutter_layout_manager_child_get +clutter_layout_manager_child_get_property + +<SUBSECTION Standard> +CLUTTER_TYPE_LAYOUT_MANAGER +CLUTTER_LAYOUT_MANAGER +CLUTTER_LAYOUT_MANAGER_CLASS +CLUTTER_IS_LAYOUT_MANAGER +CLUTTER_IS_LAYOUT_MANAGER_CLASS +CLUTTER_LAYOUT_MANAGER_GET_CLASS + +<SUBSECTION Private> +clutter_layout_manager_get_type +</SECTION> + +<SECTION> +<TITLE>ClutterFixedLayout</TITLE> +<FILE>clutter-fixed-layout</FILE> +ClutterFixedLayout +ClutterFixedLayoutClass +clutter_fixed_layout_new + +<SUBSECTION Standard> +CLUTTER_TYPE_FIXED_LAYOUT +CLUTTER_FIXED_LAYOUT +CLUTTER_FIXED_LAYOUT_CLASS +CLUTTER_IS_FIXED_LAYOUT +CLUTTER_IS_FIXED_LAYOUT_CLASS +CLUTTER_FIXED_LAYOUT_GET_CLASS + +<SUBSECTION Private> +clutter_fixed_layout_get_type +</SECTION> + +<SECTION> +<TITLE>ClutterBinLayout</TITLE> +<FILE>clutter-bin-layout</FILE> +ClutterBinAlignment +ClutterBinLayout +ClutterBinLayoutClass +clutter_bin_layout_new +clutter_bin_layout_set_alignment +clutter_bin_layout_get_alignment + +<SUBSECTION Standard> +CLUTTER_TYPE_BIN_LAYOUT +CLUTTER_BIN_LAYOUT +CLUTTER_BIN_LAYOUT_CLASS +CLUTTER_IS_BIN_LAYOUT +CLUTTER_IS_BIN_LAYOUT_CLASS +CLUTTER_BIN_LAYOUT_GET_CLASS + +<SUBSECTION Private> +ClutterBinLayoutPrivate +clutter_bin_layout_get_type +</SECTION> + +<SECTION> +<TITLE>ClutterBox</TITLE> +<FILE>clutter-box</FILE> +ClutterBox +ClutterBoxClass +clutter_box_new +clutter_box_get_layout_manager + +<SUBSECTION Standard> +CLUTTER_TYPE_BOX +CLUTTER_BOX +CLUTTER_BOX_CLASS +CLUTTER_IS_BOX +CLUTTER_IS_BOX_CLASS +CLUTTER_BOX_GET_CLASS + +<SUBSECTION Private> +ClutterBoxPrivate +clutter_box_get_type +</SECTION> diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 5a243947f..f4d0ac375 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -33,3 +33,7 @@ clutter_animation_get_type clutter_interval_get_type clutter_stage_manager_get_type clutter_binding_pool_get_type +clutter_box_get_type +clutter_layout_manager_get_type +clutter_fixed_layout_get_type +clutter_bin_layout_get_type From a2086f1178fa3aed7fdc6f94d09efbc0e1bc2c11 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 15 Sep 2009 17:37:11 +0100 Subject: [PATCH 15/50] [layout] Add LayoutMeta Instead of overloading ClutterChildMeta with both container and layout metadata and delegate to every LayoutManager implementation to keep a backpointer to the layout manager instance, we can simply subclass ChildMeta into LayoutMeta and presto! everything works out pretty well for everyone. --- clutter/Makefile.am | 2 + clutter/clutter-bin-layout.c | 35 +++-- clutter/clutter-layout-manager.c | 58 +++++---- clutter/clutter-layout-manager.h | 123 +++++++++--------- clutter/clutter-layout-meta.c | 142 +++++++++++++++++++++ clutter/clutter-layout-meta.h | 85 ++++++++++++ clutter/clutter-types.h | 1 + clutter/clutter.h | 1 + doc/reference/clutter/clutter-docs.xml.in | 5 +- doc/reference/clutter/clutter-sections.txt | 19 +++ doc/reference/clutter/clutter.types | 1 + 11 files changed, 364 insertions(+), 108 deletions(-) create mode 100644 clutter/clutter-layout-meta.c create mode 100644 clutter/clutter-layout-meta.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 7cc3ddab3..6c8983add 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -82,6 +82,7 @@ source_h = \ $(srcdir)/clutter-interval.h \ $(srcdir)/clutter-keysyms.h \ $(srcdir)/clutter-layout-manager.h \ + $(srcdir)/clutter-layout-meta.h \ $(srcdir)/clutter-list-model.h \ $(srcdir)/clutter-main.h \ $(srcdir)/clutter-media.h \ @@ -150,6 +151,7 @@ source_c = \ $(srcdir)/clutter-id-pool.c \ $(srcdir)/clutter-interval.c \ $(srcdir)/clutter-layout-manager.c \ + $(srcdir)/clutter-layout-meta.c \ $(srcdir)/clutter-list-model.c \ $(srcdir)/clutter-main.c \ clutter-marshal.c \ diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index bca6f9e39..a00d9f17a 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -43,6 +43,7 @@ #include "clutter-child-meta.h" #include "clutter-debug.h" #include "clutter-enum-types.h" +#include "clutter-layout-meta.h" #include "clutter-private.h" #define CLUTTER_TYPE_BIN_LAYER (clutter_bin_layer_get_type ()) @@ -52,7 +53,7 @@ #define CLUTTER_BIN_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutPrivate)) typedef struct _ClutterBinLayer ClutterBinLayer; -typedef struct _ClutterChildMetaClass ClutterBinLayerClass; +typedef struct _ClutterLayoutMetaClass ClutterBinLayerClass; struct _ClutterBinLayoutPrivate { @@ -65,9 +66,7 @@ struct _ClutterBinLayoutPrivate struct _ClutterBinLayer { - ClutterChildMeta parent_instance; - - ClutterBinLayout *layout; + ClutterLayoutMeta parent_instance; ClutterBinAlignment x_align; ClutterBinAlignment y_align; @@ -91,7 +90,7 @@ enum G_DEFINE_TYPE (ClutterBinLayer, clutter_bin_layer, - CLUTTER_TYPE_CHILD_META); + CLUTTER_TYPE_LAYOUT_META); G_DEFINE_TYPE (ClutterBinLayout, clutter_bin_layout, @@ -107,14 +106,15 @@ set_layer_x_align (ClutterBinLayer *self, ClutterBinAlignment alignment) { ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; if (self->x_align == alignment) return; self->x_align = alignment; - g_assert (self->layout != NULL); - manager = CLUTTER_LAYOUT_MANAGER (self->layout); + meta = CLUTTER_LAYOUT_META (self); + manager = clutter_layout_meta_get_manager (meta); clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (self), "x-align"); @@ -125,14 +125,15 @@ set_layer_y_align (ClutterBinLayer *self, ClutterBinAlignment alignment) { ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; if (self->y_align == alignment) return; self->y_align = alignment; - g_assert (self->layout != NULL); - manager = CLUTTER_LAYOUT_MANAGER (self->layout); + meta = CLUTTER_LAYOUT_META (self); + manager = clutter_layout_meta_get_manager (meta); clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (self), "y-align"); @@ -371,7 +372,7 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; ClutterBinLayer *layer; ClutterActorBox child_alloc = { 0, }; gfloat child_width, child_height; @@ -475,26 +476,22 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, g_list_free (children); } -static ClutterChildMeta * +static ClutterLayoutMeta * clutter_bin_layout_create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterBinLayoutPrivate *priv; - ClutterChildMeta *meta; priv = CLUTTER_BIN_LAYOUT (manager)->priv; - meta = g_object_new (CLUTTER_TYPE_BIN_LAYER, + return g_object_new (CLUTTER_TYPE_BIN_LAYER, "container", container, "actor", actor, + "manager", manager, "x-align", priv->x_align, "y_align", priv->y_align, NULL); - - CLUTTER_BIN_LAYER (meta)->layout = CLUTTER_BIN_LAYOUT (manager); - - return meta; } static void @@ -659,7 +656,7 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self, ClutterBinAlignment y_align) { ClutterLayoutManager *manager; - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; ClutterBinLayer *layer; g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); @@ -696,7 +693,7 @@ clutter_bin_layout_get_alignment (ClutterBinLayout *self, ClutterBinAlignment *y_align) { ClutterLayoutManager *manager; - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; ClutterBinLayer *layer; g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index c62c0567e..2a2db6878 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -50,6 +50,7 @@ #include "clutter-debug.h" #include "clutter-layout-manager.h" +#include "clutter-layout-meta.h" #include "clutter-marshal.h" #include "clutter-private.h" @@ -115,7 +116,7 @@ layout_manager_real_allocate (ClutterLayoutManager *manager, LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate"); } -static ClutterChildMeta * +static ClutterLayoutMeta * layout_manager_real_create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) @@ -298,7 +299,7 @@ clutter_layout_manager_layout_changed (ClutterLayoutManager *manager) g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0); } -static inline ClutterChildMeta * +static inline ClutterLayoutMeta * create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) @@ -310,18 +311,23 @@ create_child_meta (ClutterLayoutManager *manager, return klass->create_child_meta (manager, container, actor); } -static inline ClutterChildMeta * +static inline ClutterLayoutMeta * get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { - ClutterChildMeta *meta; + ClutterLayoutMeta *layout; - meta = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta); - if (meta != NULL && - meta->container == container && - meta->actor == actor) - return meta; + layout = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta); + if (layout != NULL) + { + ClutterChildMeta *child = CLUTTER_CHILD_META (layout); + + if (layout->manager == manager && + child->container == container && + child->actor == actor) + return layout; + } return NULL; } @@ -332,14 +338,14 @@ get_child_meta (ClutterLayoutManager *manager, * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * - * Retrieves the #ClutterChildMeta that the layout @manager associated + * Retrieves the #ClutterLayoutMeta that the layout @manager associated * to the @actor child of @container * - * Return value: a #ClutterChildMeta or %NULL + * Return value: a #ClutterLayoutMeta or %NULL * * Since: 1.0 */ -ClutterChildMeta * +ClutterLayoutMeta * clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) @@ -357,13 +363,13 @@ clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * - * Creates and binds a #ClutterChildMeta for @manager to + * Creates and binds a #ClutterLayoutMeta for @manager to * a child of @container * * This function should only be used when implementing containers * using #ClutterLayoutManager and not by application code * - * Typically, containers should bind a #ClutterChildMeta created + * Typically, containers should bind a #ClutterLayoutMeta created * by a #ClutterLayoutManager when adding a new child, e.g.: * * |[ @@ -386,7 +392,7 @@ clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, * } * ]| * - * The #ClutterChildMeta should be removed when removing an + * The #ClutterLayoutMeta should be removed when removing an * actor; see clutter_layout_manager_remove_child_meta() * * Since: 1.2 @@ -396,7 +402,7 @@ clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; meta = create_child_meta (manager, container, actor); if (meta == NULL) @@ -413,13 +419,13 @@ clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * - * Unbinds and unrefs a #ClutterChildMeta for @manager from + * Unbinds and unrefs a #ClutterLayoutMeta for @manager from * a child of @container * * This function should only be used when implementing containers * using #ClutterLayoutManager and not by application code * - * Typically, containers should remove a #ClutterChildMeta created + * Typically, containers should remove a #ClutterLayoutMeta created * by a #ClutterLayoutManager when removing a child, e.g.: * * |[ @@ -517,7 +523,7 @@ layout_get_property_internal (ClutterLayoutManager *manager, * @first_property: the first property name * @Varargs: a list of property name and value pairs * - * Sets a list of properties and their values on the #ClutterChildMeta + * Sets a list of properties and their values on the #ClutterLayoutMeta * associated by @manager to a child of @container * * Languages bindings should use clutter_layout_manager_child_set_property() @@ -532,7 +538,7 @@ clutter_layout_manager_child_set (ClutterLayoutManager *manager, const gchar *first_property, ...) { - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; va_list var_args; @@ -604,7 +610,7 @@ clutter_layout_manager_child_set (ClutterLayoutManager *manager, * @property_name: the name of the property to set * @value: a #GValue with the value of the property to set * - * Sets a property on the #ClutterChildMeta created by @manager and + * Sets a property on the #ClutterLayoutMeta created by @manager and * attached to a child of @container * * Since: 1.2 @@ -616,7 +622,7 @@ clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, const gchar *property_name, const GValue *value) { - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; GObjectClass *klass; GParamSpec *pspec; @@ -658,7 +664,7 @@ clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, * @Varargs: a list of property name and return location for the value pairs * * Retrieves the values for a list of properties out of the - * #ClutterChildMeta created by @manager and attached to the + * #ClutterLayoutMeta created by @manager and attached to the * child of a @container * * Since: 1.2 @@ -670,7 +676,7 @@ clutter_layout_manager_child_get (ClutterLayoutManager *manager, const gchar *first_property, ...) { - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; va_list var_args; @@ -746,7 +752,7 @@ clutter_layout_manager_child_get (ClutterLayoutManager *manager, * @property_name: the name of the property to get * @value: a #GValue with the value of the property to get * - * Gets a property on the #ClutterChildMeta created by @manager and + * Gets a property on the #ClutterLayoutMeta created by @manager and * attached to a child of @container * * The #GValue must already be initialized to the type of the property @@ -762,7 +768,7 @@ clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, const gchar *property_name, GValue *value) { - ClutterChildMeta *meta; + ClutterLayoutMeta *meta; GObjectClass *klass; GParamSpec *pspec; diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index 429be2006..aabeabca9 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -31,6 +31,7 @@ #include <clutter/clutter-actor.h> #include <clutter/clutter-container.h> +#include <clutter/clutter-types.h> G_BEGIN_DECLS @@ -86,26 +87,26 @@ struct _ClutterLayoutManagerClass GInitiallyUnownedClass parent_class; /*< public >*/ - void (* get_preferred_width) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_height, - gfloat *minimum_width_p, - gfloat *natural_width_p); - void (* get_preferred_height) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_width, - gfloat *minimum_height_p, - gfloat *natural_height_p); - void (* allocate) (ClutterLayoutManager *manager, - ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); + void (* get_preferred_width) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *minimum_width_p, + gfloat *natural_width_p); + void (* get_preferred_height) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *minimum_height_p, + gfloat *natural_height_p); + void (* allocate) (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); - ClutterChildMeta *(* create_child_meta) (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor); + ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); - void (* layout_changed) (ClutterLayoutManager *manager); + void (* layout_changed) (ClutterLayoutManager *manager); /*< private >*/ /* padding for future expansion */ @@ -121,53 +122,53 @@ struct _ClutterLayoutManagerClass GType clutter_layout_manager_get_type (void) G_GNUC_CONST; -void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_height, - gfloat *min_width_p, - gfloat *nat_width_p); -void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_width, - gfloat *min_height_p, - gfloat *nat_height_p); -void clutter_layout_manager_allocate (ClutterLayoutManager *manager, - ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); +void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p); +void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p); +void clutter_layout_manager_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); -void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager); +void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager); -ClutterChildMeta *clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor); -void clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor); -void clutter_layout_manager_remove_child_meta (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor); +ClutterLayoutMeta *clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); +void clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); +void clutter_layout_manager_remove_child_meta (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); -void clutter_layout_manager_child_set (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor, - const gchar *first_property, +void clutter_layout_manager_child_set (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *first_property, + ...) G_GNUC_NULL_TERMINATED; +void clutter_layout_manager_child_get (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *first_property, ...) G_GNUC_NULL_TERMINATED; -void clutter_layout_manager_child_get (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor, - const gchar *first_property, - ...) G_GNUC_NULL_TERMINATED; -void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor, - const gchar *property_name, - const GValue *value); -void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor, - const gchar *property_name, - GValue *value); +void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *property_name, + const GValue *value); +void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor, + const gchar *property_name, + GValue *value); G_END_DECLS diff --git a/clutter/clutter-layout-meta.c b/clutter/clutter-layout-meta.c new file mode 100644 index 000000000..f7bf423f0 --- /dev/null +++ b/clutter/clutter-layout-meta.c @@ -0,0 +1,142 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + +/** + * SECTION:clutter-layout-meta + * @short_description: Wrapper for actors inside a layout manager + * + * #ClutterLayoutMeta is a wrapper object created by #ClutterLayoutManager + * implementations in order to store child-specific data and properties. + * + * A #ClutterLayoutMeta wraps a #ClutterActor inside a #ClutterContainer + * using a #ClutterLayoutManager. + * + * #ClutterLayoutMeta is available since Clutter 1.2 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-layout-meta.h" +#include "clutter-debug.h" +#include "clutter-private.h" + +G_DEFINE_ABSTRACT_TYPE (ClutterLayoutMeta, + clutter_layout_meta, + CLUTTER_TYPE_CHILD_META); + +enum +{ + PROP_0, + + PROP_MANAGER +}; + +static void +clutter_layout_meta_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterLayoutMeta *layout_meta = CLUTTER_LAYOUT_META (object); + + switch (prop_id) + { + case PROP_MANAGER: + layout_meta->manager = g_value_get_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clutter_layout_meta_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterLayoutMeta *layout_meta = CLUTTER_LAYOUT_META (object); + + switch (prop_id) + { + case PROP_MANAGER: + g_value_set_object (value, layout_meta->manager); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clutter_layout_meta_class_init (ClutterLayoutMetaClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + gobject_class->set_property = clutter_layout_meta_set_property; + gobject_class->get_property = clutter_layout_meta_get_property; + + /** + * ClutterLayoutMeta:manager: + * + * The #ClutterLayoutManager that created this #ClutterLayoutMeta. + * + * Since: 1.2 + */ + pspec = g_param_spec_object ("manager", + "Manager", + "The manager that created this data", + CLUTTER_TYPE_LAYOUT_MANAGER, + G_PARAM_CONSTRUCT_ONLY | + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_MANAGER, pspec); +} + +static void +clutter_layout_meta_init (ClutterLayoutMeta *self) +{ +} + +/** + * clutter_layout_meta_get_manager: + * @data: a #ClutterLayoutMeta + * + * Retrieves the actor wrapped by @data + * + * Return value: (transfer none): a #ClutterLayoutManager + * + * Since: 1.2 + */ +ClutterLayoutManager * +clutter_layout_meta_get_manager (ClutterLayoutMeta *data) +{ + g_return_val_if_fail (CLUTTER_IS_LAYOUT_META (data), NULL); + + return data->manager; +} diff --git a/clutter/clutter-layout-meta.h b/clutter/clutter-layout-meta.h new file mode 100644 index 000000000..ac34daeea --- /dev/null +++ b/clutter/clutter-layout-meta.h @@ -0,0 +1,85 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only <clutter/clutter.h> can be included directly." +#endif + +#ifndef __CLUTTER_LAYOUT_META_H__ +#define __CLUTTER_LAYOUT_META_H__ + +#include <clutter/clutter-types.h> +#include <clutter/clutter-child-meta.h> +#include <clutter/clutter-layout-manager.h> + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_LAYOUT_META (clutter_layout_meta_get_type ()) +#define CLUTTER_LAYOUT_META(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMeta)) +#define CLUTTER_IS_LAYOUT_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYOUT_META)) +#define CLUTTER_LAYOUT_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMetaClass)) +#define CLUTTER_IS_LAYOUT_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_LAYOUT_META)) +#define CLUTTER_LAYOUT_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMetaClass)) + +/* ClutterLayoutMeta is defined in clutter-types.h */ + +typedef struct _ClutterLayoutMetaClass ClutterLayoutMetaClass; + +/** + * ClutterLayoutMeta + * @manager: the layout manager handling this data + * + * Sub-class of #ClutterChildMeta specific for layout managers + * + * A #ClutterLayoutManager sub-class should create a #ClutterLayoutMeta + * instance by overriding the #ClutterLayoutManager::create_child_meta() + * virtual function + * + * Since: 1.2 + */ +struct _ClutterLayoutMeta +{ + /*< private >*/ + ClutterChildMeta parent_instance; + + /*< public >*/ + ClutterLayoutManager *manager; + + /*< private >*/ + /* padding */ + gpointer dummy; +}; + +struct _ClutterLayoutMetaClass +{ + ClutterChildMetaClass parent_class; +}; + +GType clutter_layout_meta_get_type (void) G_GNUC_CONST; + +ClutterLayoutManager *clutter_layout_meta_get_manager (ClutterLayoutMeta *data); + +G_END_DECLS + +#endif /* __CLUTTER_LAYOUT_META_H__ */ diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index 69d6aa129..13f120307 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -43,6 +43,7 @@ typedef struct _ClutterActor ClutterActor; typedef struct _ClutterStage ClutterStage; typedef struct _ClutterContainer ClutterContainer; /* dummy */ typedef struct _ClutterChildMeta ClutterChildMeta; +typedef struct _ClutterLayoutMeta ClutterLayoutMeta; /** * ClutterGravity: diff --git a/clutter/clutter.h b/clutter/clutter.h index ef042ff85..169626264 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -58,6 +58,7 @@ #include "clutter-interval.h" #include "clutter-keysyms.h" #include "clutter-layout-manager.h" +#include "clutter-layout-meta.h" #include "clutter-list-model.h" #include "clutter-main.h" #include "clutter-media.h" diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index 7b1be959f..ed019ead0 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -56,6 +56,7 @@ <xi:include href="xml/clutter-child-meta.xml"/> <xi:include href="xml/clutter-media.xml"/> <xi:include href="xml/clutter-layout-manager.xml"/> + <xi:include href="xml/clutter-layout-meta.xml"/> </chapter> <chapter> @@ -207,13 +208,13 @@ <chapter id="clutterobjecthierarchy"> <title>Object Hierarchy</title> - <xi:include href="xml/tree_index.sgml"/> + <xi:include href="xml/tree_index.sgml"><xi:fallback /></xi:include> </chapter> <chapter id="clutterobjectindex"> <title>Object Index</title> - <xi:include href="xml/object_index.sgml"/> + <xi:include href="xml/object_index.sgml"><xi:fallback /></xi:include> </chapter> </part> diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 34bcae8d2..63c090d49 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1819,3 +1819,22 @@ CLUTTER_BOX_GET_CLASS ClutterBoxPrivate clutter_box_get_type </SECTION> + +<SECTION> +<TITLE>ClutterLayoutMeta</TITLE> +<FILE>clutter-layout-meta</FILE> +ClutterLayoutMeta +ClutterLayoutMetaClass +clutter_layout_meta_get_manager + +<SUBSECTION Standard> +CLUTTER_TYPE_LAYOUT_META +CLUTTER_LAYOUT_META +CLUTTER_LAYOUT_META_CLASS +CLUTTER_IS_LAYOUT_META +CLUTTER_IS_LAYOUT_META_CLASS +CLUTTER_LAYOUT_META_GET_CLASS + +<SUBSECTION Private> +clutter_layout_meta_get_type +</SECTION> diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index f4d0ac375..82869b92a 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -35,5 +35,6 @@ clutter_stage_manager_get_type clutter_binding_pool_get_type clutter_box_get_type clutter_layout_manager_get_type +clutter_layout_meta_get_type clutter_fixed_layout_get_type clutter_bin_layout_get_type From aaae60e17837d6f0b330c04345f1e2c2be9f24b9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 15 Sep 2009 23:20:51 +0100 Subject: [PATCH 16/50] [layout] Implement ClutterBox::add The ClutterBox::add method is a simple wrapper around the Container add_actor() method and the LayoutManager layout properties API. It allows adding an actor to a Box and setting the layout properties in one call. If the LayoutManager used by the Box does not support layout properties then the add() method short-circuits out. Along with the varargs version of the method there's also a vector-based variant, for language bindings to use. --- clutter/clutter-box.c | 186 +++++++++++++++++++++ clutter/clutter-box.h | 10 ++ doc/reference/clutter/clutter-sections.txt | 2 + tests/interactive/test-box.c | 31 ++-- 4 files changed, 210 insertions(+), 19 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index c7ec8c453..6d55d7f56 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -11,6 +11,9 @@ #include "config.h" #endif +#include <glib-object.h> +#include <gobject/gvaluecollector.h> + #include "clutter-box.h" #include "clutter-debug.h" #include "clutter-enum-types.h" @@ -438,3 +441,186 @@ clutter_box_get_layout_manager (ClutterBox *box) return box->priv->manager; } + +/** + * clutter_box_addv: + * @box: a #ClutterBox + * @actor: a #ClutterActor + * @n_properties: the number of properties to set + * @properties: (array length=n_properties) (element-type utf8): a vector + * containing the property names to set + * @values: (array length=n_properties): a vector containing the property + * values to set + * + * Vector-based variant of clutter_box_add(), intended for language + * bindings to use + * + * Since: 1.2 + */ +void +clutter_box_addv (ClutterBox *box, + ClutterActor *actor, + guint n_properties, + const gchar * const properties[], + const GValue *values) +{ + ClutterContainer *container; + ClutterBoxPrivate *priv; + ClutterLayoutMeta *meta; + GObjectClass *klass; + gint i; + + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + container = CLUTTER_CONTAINER (box); + clutter_container_add_actor (container, actor); + + priv = box->priv; + + meta = clutter_layout_manager_get_child_meta (priv->manager, + container, + actor); + + if (meta == NULL) + return; + + klass = G_OBJECT_GET_CLASS (meta); + + for (i = 0; i < n_properties; i++) + { + const gchar *pname = properties[i]; + GParamSpec *pspec; + + pspec = g_object_class_find_property (klass, pname); + if (pspec == NULL) + { + g_warning ("%s: the layout property '%s' for managers " + "of type '%s' (meta type '%s') does not exist", + G_STRLOC, + pname, + G_OBJECT_TYPE_NAME (priv->manager), + G_OBJECT_TYPE_NAME (meta)); + break; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("%s: the layout property '%s' for managers " + "of type '%s' (meta type '%s') is not writable", + G_STRLOC, + pspec->name, + G_OBJECT_TYPE_NAME (priv->manager), + G_OBJECT_TYPE_NAME (meta)); + break; + } + + clutter_layout_manager_child_set_property (priv->manager, + container, actor, + pname, &values[i]); + } +} + +/** + * clutter_box_add: + * @box: a #ClutterBox + * @actor: a #ClutterActor + * @first_property: the name of the first property to set, or %NULL + * @Varargs: a list of property name and value pairs, terminated by %NULL + * + * Adds @actor to @box and sets layout properties at the same time, + * if the #ClutterLayoutManager used by @box has them + * + * This function is a wrapper around clutter_container_add_actor() + * and clutter_layout_manager_child_set() + * + * Language bindings should use the vector-based clutter_box_addv() + * variant instead + * + * Since: 1.2 + */ +void +clutter_box_add (ClutterBox *box, + ClutterActor *actor, + const gchar *first_property, + ...) +{ + ClutterBoxPrivate *priv; + ClutterContainer *container; + ClutterLayoutMeta *meta; + GObjectClass *klass; + const gchar *pname; + va_list var_args; + + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + container = CLUTTER_CONTAINER (box); + clutter_container_add_actor (container, actor); + + if (first_property == NULL || *first_property == '\0') + return; + + priv = box->priv; + + meta = clutter_layout_manager_get_child_meta (priv->manager, + container, + actor); + + if (meta == NULL) + return; + + klass = G_OBJECT_GET_CLASS (meta); + + va_start (var_args, first_property); + + pname = first_property; + while (pname) + { + GValue value = { 0, }; + GParamSpec *pspec; + gchar *error; + + pspec = g_object_class_find_property (klass, pname); + if (pspec == NULL) + { + g_warning ("%s: the layout property '%s' for managers " + "of type '%s' (meta type '%s') does not exist", + G_STRLOC, + pname, + G_OBJECT_TYPE_NAME (priv->manager), + G_OBJECT_TYPE_NAME (meta)); + break; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("%s: the layout property '%s' for managers " + "of type '%s' (meta type '%s') is not writable", + G_STRLOC, + pspec->name, + G_OBJECT_TYPE_NAME (priv->manager), + G_OBJECT_TYPE_NAME (meta)); + break; + } + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + G_VALUE_COLLECT (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + break; + } + + clutter_layout_manager_child_set_property (priv->manager, + container, actor, + pspec->name, &value); + + g_value_unset (&value); + + pname = va_arg (var_args, gchar*); + } + + va_end (var_args); +} diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h index 40d359c89..3d473655f 100644 --- a/clutter/clutter-box.h +++ b/clutter/clutter-box.h @@ -40,6 +40,16 @@ ClutterActor * clutter_box_new (ClutterLayoutManager *mana ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box); +void clutter_box_add (ClutterBox *box, + ClutterActor *actor, + const gchar *first_property, + ...) G_GNUC_NULL_TERMINATED; +void clutter_box_addv (ClutterBox *box, + ClutterActor *actor, + guint n_properties, + const gchar * const properties[], + const GValue *values); + G_END_DECLS #endif /* __CLUTTER_BOX_H__ */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 63c090d49..d28f45316 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1806,6 +1806,8 @@ ClutterBox ClutterBoxClass clutter_box_new clutter_box_get_layout_manager +clutter_box_add +clutter_box_addv <SUBSECTION Standard> CLUTTER_TYPE_BOX diff --git a/tests/interactive/test-box.c b/tests/interactive/test-box.c index 2c3d50ed2..1a51c84a9 100644 --- a/tests/interactive/test-box.c +++ b/tests/interactive/test-box.c @@ -117,15 +117,13 @@ test_box_main (int argc, char *argv[]) clutter_actor_set_name (box, "box"); rect = make_background (&bg_color, 200, 200); - clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); + clutter_box_add (CLUTTER_BOX (box), rect, + "x-align", CLUTTER_BIN_ALIGNMENT_FILL, + "y-align", CLUTTER_BIN_ALIGNMENT_FILL, + NULL); clutter_actor_lower_bottom (rect); clutter_actor_set_name (rect, "background"); - clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), - CLUTTER_CONTAINER (box), - rect, - CLUTTER_BIN_ALIGNMENT_FILL, - CLUTTER_BIN_ALIGNMENT_FILL); { ClutterActor *tex; @@ -137,16 +135,13 @@ test_box_main (int argc, char *argv[]) g_error ("Unable to create texture: %s", error->message); clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (tex), TRUE); - clutter_container_add_actor (CLUTTER_CONTAINER (box), tex); + clutter_box_add (CLUTTER_BOX (box), tex, + "x-align", CLUTTER_BIN_ALIGNMENT_CENTER, + "y-align", CLUTTER_BIN_ALIGNMENT_CENTER, + NULL); clutter_actor_raise (tex, rect); clutter_actor_set_width (tex, 175); clutter_actor_set_name (tex, "texture"); - - clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), - CLUTTER_CONTAINER (box), - tex, - CLUTTER_BIN_ALIGNMENT_CENTER, - CLUTTER_BIN_ALIGNMENT_CENTER); } color = clutter_color_new (g_random_int_range (0, 255), @@ -155,17 +150,15 @@ test_box_main (int argc, char *argv[]) 224); rect = clutter_rectangle_new_with_color (color); - clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); + clutter_box_add (CLUTTER_BOX (box), rect, + "x-align", CLUTTER_BIN_ALIGNMENT_END, + "y-align", CLUTTER_BIN_ALIGNMENT_END, + NULL); clutter_actor_set_size (rect, 50, 50); clutter_actor_set_opacity (rect, 0); clutter_actor_raise_top (rect); clutter_actor_set_name (rect, "emblem"); - clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), - CLUTTER_CONTAINER (box), - rect, - CLUTTER_BIN_ALIGNMENT_END, - CLUTTER_BIN_ALIGNMENT_END); g_signal_connect (box, "enter-event", G_CALLBACK (on_box_enter), From c6f67bf872ec2b51951a554e0351e6d7615d28ce Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 16 Sep 2009 11:10:38 +0100 Subject: [PATCH 17/50] [layout, docs] Document FixedLayout --- clutter/clutter-fixed-layout.c | 45 ++++++++++++++++++++++++++++++++++ clutter/clutter-fixed-layout.h | 42 +++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/clutter/clutter-fixed-layout.c b/clutter/clutter-fixed-layout.c index f3e3d385d..f558c132d 100644 --- a/clutter/clutter-fixed-layout.c +++ b/clutter/clutter-fixed-layout.c @@ -1,3 +1,39 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + * + * Based on the fixed layout code inside clutter-group.c + */ + +/** + * SECTION:clutter-fixed-layout + * @short_description: A fixed layout manager + * + * #ClutterFixedLayout is a layout manager implementing the same + * layout policies as #ClutterGroup. + * + * #ClutterFixedLayout is available since Clutter 1.2 + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -210,6 +246,15 @@ clutter_fixed_layout_init (ClutterFixedLayout *self) { } +/** + * clutter_fixed_layout_new: + * + * Creates a new #ClutterFixedLayout + * + * Return value: the newly created #ClutterFixedLayout + * + * Since: 1.2 + */ ClutterLayoutManager * clutter_fixed_layout_new (void) { diff --git a/clutter/clutter-fixed-layout.h b/clutter/clutter-fixed-layout.h index b8075172a..32273bc00 100644 --- a/clutter/clutter-fixed-layout.h +++ b/clutter/clutter-fixed-layout.h @@ -1,3 +1,27 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only <clutter/clutter.h> can be included directly." #endif @@ -19,13 +43,31 @@ G_BEGIN_DECLS typedef struct _ClutterFixedLayout ClutterFixedLayout; typedef struct _ClutterFixedLayoutClass ClutterFixedLayoutClass; +/** + * ClutterFixedLayout: + * + * The #ClutterFixedLayout structure contains only private data and + * it should be accessed using the provided API + * + * Since: 1.2 + */ struct _ClutterFixedLayout { + /*< private >*/ ClutterLayoutManager parent_instance; }; +/** + * ClutterFixedLayoutClass: + * + * The #ClutterFixedLayoutClass structure contains only private data + * and it should be accessed using the provided API + * + * Since: 1.2 + */ struct _ClutterFixedLayoutClass { + /*< private >*/ ClutterLayoutManagerClass parent_class; }; From f58bdbad15206f4369589f50789b9db9585f6e03 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 16 Sep 2009 11:10:45 +0100 Subject: [PATCH 18/50] [layout] Rename Box::add to Box::pack Since ClutterBox is a ClutterContainer we should avoid naming collisions between methods. --- clutter/clutter-box.c | 24 +++++++++++----------- clutter/clutter-box.h | 4 ++-- doc/reference/clutter/clutter-sections.txt | 4 ++-- tests/interactive/test-box.c | 24 +++++++++++----------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index 6d55d7f56..3c4bac846 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -443,7 +443,7 @@ clutter_box_get_layout_manager (ClutterBox *box) } /** - * clutter_box_addv: + * clutter_box_packv: * @box: a #ClutterBox * @actor: a #ClutterActor * @n_properties: the number of properties to set @@ -452,17 +452,17 @@ clutter_box_get_layout_manager (ClutterBox *box) * @values: (array length=n_properties): a vector containing the property * values to set * - * Vector-based variant of clutter_box_add(), intended for language + * Vector-based variant of clutter_box_pack(), intended for language * bindings to use * * Since: 1.2 */ void -clutter_box_addv (ClutterBox *box, - ClutterActor *actor, - guint n_properties, - const gchar * const properties[], - const GValue *values) +clutter_box_packv (ClutterBox *box, + ClutterActor *actor, + guint n_properties, + const gchar * const properties[], + const GValue *values) { ClutterContainer *container; ClutterBoxPrivate *priv; @@ -522,7 +522,7 @@ clutter_box_addv (ClutterBox *box, } /** - * clutter_box_add: + * clutter_box_pack: * @box: a #ClutterBox * @actor: a #ClutterActor * @first_property: the name of the first property to set, or %NULL @@ -540,10 +540,10 @@ clutter_box_addv (ClutterBox *box, * Since: 1.2 */ void -clutter_box_add (ClutterBox *box, - ClutterActor *actor, - const gchar *first_property, - ...) +clutter_box_pack (ClutterBox *box, + ClutterActor *actor, + const gchar *first_property, + ...) { ClutterBoxPrivate *priv; ClutterContainer *container; diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h index 3d473655f..9b8ad19b5 100644 --- a/clutter/clutter-box.h +++ b/clutter/clutter-box.h @@ -40,11 +40,11 @@ ClutterActor * clutter_box_new (ClutterLayoutManager *mana ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box); -void clutter_box_add (ClutterBox *box, +void clutter_box_pack (ClutterBox *box, ClutterActor *actor, const gchar *first_property, ...) G_GNUC_NULL_TERMINATED; -void clutter_box_addv (ClutterBox *box, +void clutter_box_packv (ClutterBox *box, ClutterActor *actor, guint n_properties, const gchar * const properties[], diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index d28f45316..31864a961 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1806,8 +1806,8 @@ ClutterBox ClutterBoxClass clutter_box_new clutter_box_get_layout_manager -clutter_box_add -clutter_box_addv +clutter_box_pack +clutter_box_packv <SUBSECTION Standard> CLUTTER_TYPE_BOX diff --git a/tests/interactive/test-box.c b/tests/interactive/test-box.c index 1a51c84a9..d2d6c5bf3 100644 --- a/tests/interactive/test-box.c +++ b/tests/interactive/test-box.c @@ -117,10 +117,10 @@ test_box_main (int argc, char *argv[]) clutter_actor_set_name (box, "box"); rect = make_background (&bg_color, 200, 200); - clutter_box_add (CLUTTER_BOX (box), rect, - "x-align", CLUTTER_BIN_ALIGNMENT_FILL, - "y-align", CLUTTER_BIN_ALIGNMENT_FILL, - NULL); + clutter_box_pack (CLUTTER_BOX (box), rect, + "x-align", CLUTTER_BIN_ALIGNMENT_FILL, + "y-align", CLUTTER_BIN_ALIGNMENT_FILL, + NULL); clutter_actor_lower_bottom (rect); clutter_actor_set_name (rect, "background"); @@ -135,10 +135,10 @@ test_box_main (int argc, char *argv[]) g_error ("Unable to create texture: %s", error->message); clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (tex), TRUE); - clutter_box_add (CLUTTER_BOX (box), tex, - "x-align", CLUTTER_BIN_ALIGNMENT_CENTER, - "y-align", CLUTTER_BIN_ALIGNMENT_CENTER, - NULL); + clutter_box_pack (CLUTTER_BOX (box), tex, + "x-align", CLUTTER_BIN_ALIGNMENT_CENTER, + "y-align", CLUTTER_BIN_ALIGNMENT_CENTER, + NULL); clutter_actor_raise (tex, rect); clutter_actor_set_width (tex, 175); clutter_actor_set_name (tex, "texture"); @@ -150,10 +150,10 @@ test_box_main (int argc, char *argv[]) 224); rect = clutter_rectangle_new_with_color (color); - clutter_box_add (CLUTTER_BOX (box), rect, - "x-align", CLUTTER_BIN_ALIGNMENT_END, - "y-align", CLUTTER_BIN_ALIGNMENT_END, - NULL); + clutter_box_pack (CLUTTER_BOX (box), rect, + "x-align", CLUTTER_BIN_ALIGNMENT_END, + "y-align", CLUTTER_BIN_ALIGNMENT_END, + NULL); clutter_actor_set_size (rect, 50, 50); clutter_actor_set_opacity (rect, 0); clutter_actor_raise_top (rect); From 9117ee205691a5bb1a3bb65e1963ed5b8826b79f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 16 Sep 2009 14:58:30 +0100 Subject: [PATCH 19/50] [layout] Allow taking a back pointer to the Container The LayoutManager implementation might opt to take a back pointer to the Container that is using the layout instance; this allows direct access to the container itself from within the implementation. --- clutter/clutter-layout-manager.c | 28 ++++++++++++++++++++++ clutter/clutter-layout-manager.h | 12 ++++++++++ doc/reference/clutter/clutter-sections.txt | 1 + 3 files changed, 41 insertions(+) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 2a2db6878..b46641265 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -299,6 +299,34 @@ clutter_layout_manager_layout_changed (ClutterLayoutManager *manager) g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0); } +/** + * clutter_layout_manager_set_container: + * @manager: a #ClutterLayoutManager + * @container: (allow-none): a #ClutterContainer using @manager + * + * If the #ClutterLayoutManager sub-class allows it, allow + * adding a weak reference of the @container using @manager + * from within the layout manager + * + * The layout manager should not increase the reference + * count of the @container + * + * Since: 1.2 + */ +void +clutter_layout_manager_set_container (ClutterLayoutManager *manager, + ClutterContainer *container) +{ + ClutterLayoutManagerClass *klass; + + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (container == NULL || CLUTTER_IS_CONTAINER (container)); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + if (klass->set_container) + klass->set_container (manager, container); +} + static inline ClutterLayoutMeta * create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index aabeabca9..d3333c7cb 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -57,6 +57,9 @@ struct _ClutterLayoutManager { /*< private >*/ GInitiallyUnowned parent_instance; + + /* padding for future expansion */ + gpointer dummy; }; /** @@ -70,6 +73,10 @@ struct _ClutterLayoutManager * @allocate: virtual function; override to allocate the children of the * layout manager. See also the allocate() virtual function in * #ClutterActor + * @set_container: virtual function; override to set a back pointer + * on the #ClutterContainer using the layout manager. The implementation + * should not take a reference on the container, but just take a weak + * reference, to avoid potential leaks due to reference cycles * @create_child_meta: virtual function; override to create a * #ClutterChildMeta instance associated to a #ClutterContainer and a * child #ClutterActor, used to maintain layout manager specific properties @@ -102,6 +109,9 @@ struct _ClutterLayoutManagerClass const ClutterActorBox *allocation, ClutterAllocationFlags flags); + void (* set_container) (ClutterLayoutManager *manager, + ClutterContainer *container); + ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor); @@ -137,6 +147,8 @@ void clutter_layout_manager_allocate (ClutterLayoutMan const ClutterActorBox *allocation, ClutterAllocationFlags flags); +void clutter_layout_manager_set_container (ClutterLayoutManager *manager, + ClutterContainer *container); void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager); ClutterLayoutMeta *clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 31864a961..21d9c51c3 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1735,6 +1735,7 @@ clutter_layout_manager_get_preferred_width clutter_layout_manager_get_preferred_height clutter_layout_manager_allocate clutter_layout_manager_layout_changed +clutter_layout_manager_set_container <SUBSECTION> clutter_layout_manager_add_child_meta From 22bb243ec24821cd18e7dba791e793884ffe2a63 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 16 Sep 2009 15:47:26 +0100 Subject: [PATCH 20/50] [layout] Replace stale LayoutMeta If a LayoutMeta references a different container and/or layout manager then we should simply replace it and discard the previous one. --- clutter/clutter-layout-manager.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index b46641265..56b5c5993 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -344,7 +344,7 @@ get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { - ClutterLayoutMeta *layout; + ClutterLayoutMeta *layout = NULL; layout = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta); if (layout != NULL) @@ -355,6 +355,21 @@ get_child_meta (ClutterLayoutManager *manager, child->container == container && child->actor == actor) return layout; + + /* if the LayoutMeta referenced is not attached to the + * layout manager then we simply ask the layout manager + * to replace it with the right one + */ + layout = create_child_meta (manager, container, actor); + if (layout != NULL) + { + g_assert (CLUTTER_IS_LAYOUT_META (layout)); + g_object_set_qdata_full (G_OBJECT (actor), quark_layout_meta, + layout, + (GDestroyNotify) g_object_unref); + } + + return layout; } return NULL; From 755896664f7efc4bbfce0bfb6ea3249b6c526a59 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 16 Sep 2009 15:48:28 +0100 Subject: [PATCH 21/50] [layout] Set a back pointer to Box inside the layout Use the LayoutManager API to set a back pointer to the Box actor inside the LayoutManager used by the box. This also allows us to replace the LayoutManager on a Box, since the LayoutManager will be able to replace all the metadata if needed. --- clutter/clutter-box.c | 37 ++++++++++++++++++++-- clutter/clutter-box.h | 2 ++ doc/reference/clutter/clutter-sections.txt | 1 + 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index 3c4bac846..f51070bde 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -289,17 +289,21 @@ on_layout_changed (ClutterLayoutManager *manager, clutter_actor_queue_relayout (self); } -static void +static inline void set_layout_manager (ClutterBox *self, ClutterLayoutManager *manager) { ClutterBoxPrivate *priv = self->priv; + if (priv->manager == manager) + return; + if (priv->manager != NULL) { if (priv->changed_id != 0) g_signal_handler_disconnect (priv->manager, priv->changed_id); + clutter_layout_manager_set_container (priv->manager, NULL); g_object_unref (priv->manager); priv->manager = NULL; @@ -309,11 +313,18 @@ set_layout_manager (ClutterBox *self, if (manager != NULL) { priv->manager = g_object_ref_sink (manager); + clutter_layout_manager_set_container (manager, + CLUTTER_CONTAINER (self)); + priv->changed_id = g_signal_connect (priv->manager, "layout-changed", G_CALLBACK (on_layout_changed), self); } + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "layout-manager"); } static void @@ -391,7 +402,7 @@ clutter_box_class_init (ClutterBoxClass *klass) "The layout manager used by the box", CLUTTER_TYPE_LAYOUT_MANAGER, CLUTTER_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); + G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_LAYOUT_MANAGER, pspec); @@ -424,6 +435,28 @@ clutter_box_new (ClutterLayoutManager *manager) NULL); } +/** + * clutter_box_set_layout_manager: + * @box: a #ClutterBox + * @manager: a #ClutterLayoutManager + * + * Sets the #ClutterLayoutManager for @box + * + * A #ClutterLayoutManager is a delegate object that controls the + * layout of the children of @box + * + * Since: 1.2 + */ +void +clutter_box_set_layout_manager (ClutterBox *box, + ClutterLayoutManager *manager) +{ + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (manager == NULL || CLUTTER_IS_LAYOUT_MANAGER (manager)); + + set_layout_manager (box, manager); +} + /** * clutter_box_get_layout_manager: * @box: a #ClutterBox diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h index 9b8ad19b5..4e9ea127a 100644 --- a/clutter/clutter-box.h +++ b/clutter/clutter-box.h @@ -38,6 +38,8 @@ GType clutter_box_get_type (void) G_GNUC_CONST; ClutterActor * clutter_box_new (ClutterLayoutManager *manager); +void clutter_box_set_layout_manager (ClutterBox *box, + ClutterLayoutManager *manger); ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box); void clutter_box_pack (ClutterBox *box, diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 21d9c51c3..0b0823364 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1806,6 +1806,7 @@ clutter_bin_layout_get_type ClutterBox ClutterBoxClass clutter_box_new +clutter_box_set_layout_manager clutter_box_get_layout_manager clutter_box_pack clutter_box_packv From 431a63d04adc40dfd719563c4f9978324f618b4e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 16 Sep 2009 15:51:13 +0100 Subject: [PATCH 22/50] [layout] Store and use the container inside BinLayout The BinLayout should store a pointer to the Container that it is using it as the layout manager. This allows us to fix the API and drop the additional Container arguments from set_alignment() and get_alignment(). This also allows us to add a ClutterBinLayout::add() method which adds an actor and sets the alignment policies without dealing with variadic arguments functions and GValue (de)marshalling. --- clutter/clutter-bin-layout.c | 120 ++++++++++++++++++--- clutter/clutter-bin-layout.h | 7 +- doc/reference/clutter/clutter-sections.txt | 1 + 3 files changed, 114 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index a00d9f17a..956e3467f 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -60,8 +60,7 @@ struct _ClutterBinLayoutPrivate ClutterBinAlignment x_align; ClutterBinAlignment y_align; - gdouble x_factor; - gdouble y_factor; + ClutterContainer *container; }; struct _ClutterBinLayer @@ -494,6 +493,16 @@ clutter_bin_layout_create_child_meta (ClutterLayoutManager *manager, NULL); } +static void +clutter_bin_layout_set_container (ClutterLayoutManager *manager, + ClutterContainer *container) +{ + ClutterBinLayoutPrivate *priv; + + priv = CLUTTER_BIN_LAYOUT (manager)->priv; + priv->container = container; +} + static void clutter_bin_layout_set_property (GObject *gobject, guint prop_id, @@ -599,6 +608,8 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) clutter_bin_layout_allocate; layout_class->create_child_meta = clutter_bin_layout_create_child_meta; + layout_class->set_container = + clutter_bin_layout_set_container; } static void @@ -636,33 +647,47 @@ clutter_bin_layout_new (ClutterBinAlignment x_align, /** * clutter_bin_layout_set_alignment: * @self: a #ClutterBinLayout - * @container: a #ClutterContainer using the #ClutterBinLayout - * @child: a child of @container + * @child: (allow-none): a child of @container * @x_align: the horizontal alignment policy to be used for the @child * inside @container * @y_align: the vertical aligment policy to be used on the @child * inside @container * * Sets the horizontal and vertical alignment policies to be applied - * to the @child of @container + * to a @child of @self + * + * If @child is %NULL then the @x_align and @y_align values will + * be set as the default alignment policies * * Since: 1.2 */ void clutter_bin_layout_set_alignment (ClutterBinLayout *self, - ClutterContainer *container, ClutterActor *child, ClutterBinAlignment x_align, ClutterBinAlignment y_align) { + ClutterBinLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; ClutterBinLayer *layer; g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + priv = self->priv; + + if (child == NULL || priv->container == NULL) + { + set_x_align (self, x_align); + set_y_align (self, y_align); + + return; + } + manager = CLUTTER_LAYOUT_MANAGER (self); - meta = clutter_layout_manager_get_child_meta (manager, container, child); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + child); g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta)); layer = CLUTTER_BIN_LAYER (meta); @@ -674,32 +699,50 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self, /** * clutter_bin_layout_get_alignment: * @self: a #ClutterBinLayout - * @container: a #ClutterContainer using the #ClutterBinLayout - * @child: a child of @container + * @child: (allow-none): a child of @container * @x_align: (out) (allow-none): return location for the horizontal * alignment policy * @y_align: (out) (allow-none): return location for the vertical * alignment policy * - * Retrieves the horizontal and vertical alignment policies for @self + * Retrieves the horizontal and vertical alignment policies for + * a child of @self + * + * If @child is %NULL the default alignment policies will be returned + * instead * * Since: 1.2 */ void clutter_bin_layout_get_alignment (ClutterBinLayout *self, - ClutterContainer *container, ClutterActor *child, ClutterBinAlignment *x_align, ClutterBinAlignment *y_align) { + ClutterBinLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; ClutterBinLayer *layer; g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + priv = self->priv; + + if (child == NULL || priv->container == NULL) + { + if (x_align) + *x_align = priv->x_align; + + if (y_align) + *y_align = priv->y_align; + + return; + } + manager = CLUTTER_LAYOUT_MANAGER (self); - meta = clutter_layout_manager_get_child_meta (manager, container, child); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + child); g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta)); layer = CLUTTER_BIN_LAYER (meta); @@ -710,3 +753,56 @@ clutter_bin_layout_get_alignment (ClutterBinLayout *self, if (y_align) *y_align = layer->y_align; } + +/** + * clutter_bin_layout_add: + * @self: a #ClutterBinLayout + * @child: a #ClutterActor + * @x_align: horizontal alignment policy for @child + * @y_align: vertical alignment policy for @child + * + * Adds a #ClutterActor to the container using @self and + * sets the alignment policies for it + * + * This function is equivalent to clutter_container_add_actor() + * and clutter_layout_manager_get_child_meta() but it does not + * require the #ClutterContainer + * + * Since: 1.2 + */ +void +clutter_bin_layout_add (ClutterBinLayout *self, + ClutterActor *child, + ClutterBinAlignment x_align, + ClutterBinAlignment y_align) +{ + ClutterBinLayer *layer; + ClutterLayoutMeta *meta; + ClutterLayoutManager *manager; + ClutterBinLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + g_return_if_fail (CLUTTER_IS_ACTOR (child)); + + priv = self->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 (self)); + return; + } + + clutter_container_add_actor (priv->container, child); + + manager = CLUTTER_LAYOUT_MANAGER (self); + meta = clutter_layout_manager_get_child_meta (manager, + priv->container, + child); + g_assert (CLUTTER_IS_BIN_LAYER (meta)); + + layer = CLUTTER_BIN_LAYER (meta); + set_layer_x_align (layer, x_align); + set_layer_y_align (layer, y_align); +} diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h index 032d73794..0ce9587ae 100644 --- a/clutter/clutter-bin-layout.h +++ b/clutter/clutter-bin-layout.h @@ -81,16 +81,19 @@ ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment x_a ClutterBinAlignment y_align); void clutter_bin_layout_set_alignment (ClutterBinLayout *self, - ClutterContainer *container, ClutterActor *child, ClutterBinAlignment x_align, ClutterBinAlignment y_align); void clutter_bin_layout_get_alignment (ClutterBinLayout *self, - ClutterContainer *container, ClutterActor *child, ClutterBinAlignment *x_align, ClutterBinAlignment *y_align); +void clutter_bin_layout_add (ClutterBinLayout *self, + ClutterActor *child, + ClutterBinAlignment x_align, + ClutterBinAlignment y_align); + G_END_DECLS #endif /* __CLUTTER_BIN_LAYOUT_H__ */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 0b0823364..178693de3 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1786,6 +1786,7 @@ ClutterBinLayoutClass clutter_bin_layout_new clutter_bin_layout_set_alignment clutter_bin_layout_get_alignment +clutter_bin_layout_add <SUBSECTION Standard> CLUTTER_TYPE_BIN_LAYOUT From 8b2088a91787562c01d5b8184f1f2b2f1d225371 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Wed, 16 Sep 2009 15:55:30 +0100 Subject: [PATCH 23/50] [layout, tests] Use variants for child packing in Box There are three potential variants to add a child inside a Box with a BinLayout: - clutter_box_pack(), a variadic argument function which allows passing arbitrary LayoutMeta properties and values; - clutter_bin_layout_add(), which uses the backpointer to the container from the LayoutManager and sets the layout properties directly without GValue (de)marshalling - clutter_container_add_actor() and clutter_bin_layout_set_alignment(), similar to the clutter_bin_layout_add() function above, but split in two The test-box interactive test should exercise all three variants. --- tests/interactive/test-box.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/interactive/test-box.c b/tests/interactive/test-box.c index d2d6c5bf3..777c8e28b 100644 --- a/tests/interactive/test-box.c +++ b/tests/interactive/test-box.c @@ -117,10 +117,13 @@ test_box_main (int argc, char *argv[]) clutter_actor_set_name (box, "box"); rect = make_background (&bg_color, 200, 200); + + /* first method: use clutter_box_pack() */ clutter_box_pack (CLUTTER_BOX (box), rect, "x-align", CLUTTER_BIN_ALIGNMENT_FILL, "y-align", CLUTTER_BIN_ALIGNMENT_FILL, NULL); + clutter_actor_lower_bottom (rect); clutter_actor_set_name (rect, "background"); @@ -135,10 +138,12 @@ test_box_main (int argc, char *argv[]) g_error ("Unable to create texture: %s", error->message); clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (tex), TRUE); - clutter_box_pack (CLUTTER_BOX (box), tex, - "x-align", CLUTTER_BIN_ALIGNMENT_CENTER, - "y-align", CLUTTER_BIN_ALIGNMENT_CENTER, - NULL); + + /* second method: use clutter_bin_layout_add() */ + clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), tex, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_BIN_ALIGNMENT_CENTER); + clutter_actor_raise (tex, rect); clutter_actor_set_width (tex, 175); clutter_actor_set_name (tex, "texture"); @@ -150,10 +155,13 @@ test_box_main (int argc, char *argv[]) 224); rect = clutter_rectangle_new_with_color (color); - clutter_box_pack (CLUTTER_BOX (box), rect, - "x-align", CLUTTER_BIN_ALIGNMENT_END, - "y-align", CLUTTER_BIN_ALIGNMENT_END, - NULL); + + /* third method: container_add() and set_alignment() */ + clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); + clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), rect, + CLUTTER_BIN_ALIGNMENT_END, + CLUTTER_BIN_ALIGNMENT_END); + clutter_actor_set_size (rect, 50, 50); clutter_actor_set_opacity (rect, 0); clutter_actor_raise_top (rect); From df6ca3d171a6a8d1a18cbf0fe70f4e4572e54479 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Thu, 17 Sep 2009 11:38:43 +0100 Subject: [PATCH 24/50] [layout, docs] Clean up BinLayout documentation Documentation and code style fixes for BinLayout. --- clutter/clutter-bin-layout.c | 120 +++++++++++++++++++++++++---------- clutter/clutter-bin-layout.h | 24 +++++++ 2 files changed, 112 insertions(+), 32 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index 956e3467f..7c1c55ff1 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -29,6 +29,50 @@ * #ClutterBinLayout is a layout manager which implements the following * policy: * + * <itemizedlist> + * <listitem><simpara>the preferred size is the maximum preferred size + * between all the children of the container using the + * layout;</simpara></listitem> + * <listitem><simpara>each child is allocated in "layers", on on top + * of the other;</simpara></listitem> + * <listitem><simpara>for each layer there are horizontal and vertical + * alignment policies.</simpara></listitem> + * </itemizedlist> + * + * <example id="example-clutter-bin-layout"> + * <title>How to pack actors inside a BinLayout</title> + * <para>The following code shows how to build a composite actor with + * a texture and a background, and add controls overlayed on top. The + * background is set to fill the whole allocation, whilst the texture + * is centered; there is a control in the top right corner and a label + * in the bottom, filling out the whole allocated width.</para> + * <programlisting> + * ClutterLayoutManager *manager; + * ClutterActor *box; + * + * /* create the layout first */ + * layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, + * CLUTTER_BIN_ALIGNMENT_CENTER); + * box = clutter_box_new (layout); /* then the container */ + * + * /* we can use the layout object to add actors */ + * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), background, + * CLUTTER_BIN_ALIGNMENT_FILL, + * CLUTTER_BIN_ALIGNMENT_FILL); + * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), icon, + * CLUTTER_BIN_ALIGNMENT_CENTER, + * CLUTTER_BIN_ALIGNMENT_CENTER); + * + * /* align to the bottom left */ + * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), label, + * CLUTTER_BIN_ALIGNMENT_START, + * CLUTTER_BIN_ALIGNMENT_END); + * /* align to the top right */ + * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), button, + * CLUTTER_BIN_ALIGNMENT_END, + * CLUTTER_BIN_ALIGNMENT_START); + * </programlisting> + * </example> * * #ClutterBinLayout is available since Clutter 1.2 */ @@ -95,7 +139,6 @@ G_DEFINE_TYPE (ClutterBinLayout, clutter_bin_layout, CLUTTER_TYPE_LAYOUT_MANAGER); - /* * ClutterBinLayer */ @@ -509,16 +552,16 @@ clutter_bin_layout_set_property (GObject *gobject, const GValue *value, GParamSpec *pspec) { + ClutterBinLayout *layout = CLUTTER_BIN_LAYOUT (gobject); + switch (prop_id) { case PROP_X_ALIGN: - set_x_align (CLUTTER_BIN_LAYOUT (gobject), - g_value_get_enum (value)); + set_x_align (layout, g_value_get_enum (value)); break; case PROP_Y_ALIGN: - set_y_align (CLUTTER_BIN_LAYOUT (gobject), - g_value_get_enum (value)); + set_y_align (layout, g_value_get_enum (value)); break; default: @@ -569,8 +612,8 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) /** * ClutterBinLayout:x-align: * - * The horizontal alignment policy for actors managed by the - * #ClutterBinLayout + * The default horizontal alignment policy for actors managed + * by the #ClutterBinLayout * * Since: 1.2 */ @@ -586,8 +629,8 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) /** * ClutterBinLayout:y-align: * - * The vertical alignment policy for actors managed by the - * #ClutterBinLayout + * The default vertical alignment policy for actors managed + * by the #ClutterBinLayout * * Since: 1.2 */ @@ -670,16 +713,24 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self, ClutterBinLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; - ClutterBinLayer *layer; g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child)); priv = self->priv; - if (child == NULL || priv->container == NULL) + if (priv->container == NULL) { - set_x_align (self, x_align); - set_y_align (self, y_align); + if (child == NULL) + { + set_x_align (self, x_align); + set_y_align (self, y_align); + } + else + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before setting the alignment " + "on its children", + G_OBJECT_TYPE_NAME (self)); return; } @@ -688,12 +739,10 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self, meta = clutter_layout_manager_get_child_meta (manager, priv->container, child); - g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta)); + g_assert (CLUTTER_IS_BIN_LAYER (meta)); - layer = CLUTTER_BIN_LAYER (meta); - - set_layer_x_align (layer, x_align); - set_layer_y_align (layer, y_align); + set_layer_x_align (CLUTTER_BIN_LAYER (meta), x_align); + set_layer_y_align (CLUTTER_BIN_LAYER (meta), y_align); } /** @@ -728,13 +777,21 @@ clutter_bin_layout_get_alignment (ClutterBinLayout *self, priv = self->priv; - if (child == NULL || priv->container == NULL) + if (priv->container == NULL) { - if (x_align) - *x_align = priv->x_align; + if (child == NULL) + { + if (x_align) + *x_align = priv->x_align; - if (y_align) - *y_align = priv->y_align; + if (y_align) + *y_align = priv->y_align; + } + else + g_warning ("The layout of type '%s' must be associated to " + "a ClutterContainer before getting the alignment " + "of its children", + G_OBJECT_TYPE_NAME (self)); return; } @@ -743,7 +800,7 @@ clutter_bin_layout_get_alignment (ClutterBinLayout *self, meta = clutter_layout_manager_get_child_meta (manager, priv->container, child); - g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta)); + g_assert (CLUTTER_IS_BIN_LAYER (meta)); layer = CLUTTER_BIN_LAYER (meta); @@ -765,8 +822,9 @@ clutter_bin_layout_get_alignment (ClutterBinLayout *self, * sets the alignment policies for it * * This function is equivalent to clutter_container_add_actor() - * and clutter_layout_manager_get_child_meta() but it does not - * require the #ClutterContainer + * and clutter_layout_manager_child_set_property() but it does not + * require a pointer to the #ClutterContainer associated to the + * #ClutterBinLayout * * Since: 1.2 */ @@ -776,10 +834,9 @@ clutter_bin_layout_add (ClutterBinLayout *self, ClutterBinAlignment x_align, ClutterBinAlignment y_align) { - ClutterBinLayer *layer; - ClutterLayoutMeta *meta; - ClutterLayoutManager *manager; ClutterBinLayoutPrivate *priv; + ClutterLayoutManager *manager; + ClutterLayoutMeta *meta; g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); @@ -802,7 +859,6 @@ clutter_bin_layout_add (ClutterBinLayout *self, child); g_assert (CLUTTER_IS_BIN_LAYER (meta)); - layer = CLUTTER_BIN_LAYER (meta); - set_layer_x_align (layer, x_align); - set_layer_y_align (layer, y_align); + set_layer_x_align (CLUTTER_BIN_LAYER (meta), x_align); + set_layer_y_align (CLUTTER_BIN_LAYER (meta), y_align); } diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h index 0ce9587ae..169af0774 100644 --- a/clutter/clutter-bin-layout.h +++ b/clutter/clutter-bin-layout.h @@ -1,3 +1,27 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only <clutter/clutter.h> can be included directly." #endif From c98388bb0166fedd4e72bca42b99782a9b2fb52c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Thu, 17 Sep 2009 18:21:51 +0100 Subject: [PATCH 25/50] [layout, box] Add Box:color Allow setting the background color of a ClutterBox --- clutter/clutter-box.c | 136 ++++++++++++++++++++- clutter/clutter-box.h | 5 + doc/reference/clutter/clutter-sections.txt | 4 + 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index f51070bde..95f638b9c 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -29,15 +29,22 @@ struct _ClutterBoxPrivate GList *children; guint changed_id; + + ClutterColor color; + guint color_set : 1; }; enum { PROP_0, - PROP_LAYOUT_MANAGER + PROP_LAYOUT_MANAGER, + PROP_COLOR, + PROP_COLOR_SET }; +static const ClutterColor default_box_color = { 255, 255, 255, 255 }; + static void clutter_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterBox, clutter_box, CLUTTER_TYPE_ACTOR, @@ -187,6 +194,27 @@ clutter_box_real_paint (ClutterActor *actor) { ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + if (priv->color_set) + { + ClutterActorBox box = { 0, }; + gfloat width, height; + guint8 tmp_alpha; + + clutter_actor_get_allocation_box (actor, &box); + clutter_actor_box_get_size (&box, &width, &height); + + tmp_alpha = clutter_actor_get_paint_opacity (actor) + * priv->color.alpha + / 255; + + cogl_set_source_color4ub (priv->color.red, + priv->color.green, + priv->color.blue, + tmp_alpha); + + cogl_rectangle (0, 0, width, height); + } + g_list_foreach (priv->children, (GFunc) clutter_actor_paint, NULL); } @@ -351,6 +379,10 @@ clutter_box_set_property (GObject *gobject, set_layout_manager (self, g_value_get_object (value)); break; + case PROP_COLOR: + clutter_box_set_color (self, clutter_value_get_color (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -371,6 +403,14 @@ clutter_box_get_property (GObject *gobject, g_value_set_object (value, priv->manager); break; + case PROP_COLOR: + clutter_value_set_color (value, &priv->color); + break; + + case PROP_COLOR_SET: + g_value_set_boolean (value, priv->color_set); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -397,6 +437,13 @@ clutter_box_class_init (ClutterBoxClass *klass) gobject_class->get_property = clutter_box_get_property; gobject_class->dispose = clutter_box_dispose; + /** + * ClutterBox:layout-manager: + * + * The #ClutterLayoutManager used by the #ClutterBox + * + * Since: 1.2 + */ pspec = g_param_spec_object ("layout-manager", "Layout Manager", "The layout manager used by the box", @@ -406,12 +453,44 @@ clutter_box_class_init (ClutterBoxClass *klass) g_object_class_install_property (gobject_class, PROP_LAYOUT_MANAGER, pspec); + + /** + * ClutterBox:color: + * + * The color to be used to paint the background of the + * #ClutterBox. Setting this property will set the + * #ClutterBox:color-set property as a side effect + * + * Since: 1.2 + */ + pspec = clutter_param_spec_color ("color", + "Color", + "The background color of the box", + &default_box_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_COLOR, pspec); + + /** + * ClutterBox:color-set: + * + * Whether the #ClutterBox:color property has been set + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("color-set", + "Color Set", + "Whether the background color is set", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_COLOR_SET, pspec); } static void clutter_box_init (ClutterBox *self) { self->priv = CLUTTER_BOX_GET_PRIVATE (self); + + self->priv->color = default_box_color; } /** @@ -657,3 +736,58 @@ clutter_box_pack (ClutterBox *box, va_end (var_args); } + +/** + * clutter_box_set_color: + * @box: a #ClutterBox + * @color: (allow-none): the background color, or %NULL to unset + * + * Sets (or unsets) the background color for @box + * + * Since: 1.2 + */ +void +clutter_box_set_color (ClutterBox *box, + const ClutterColor *color) +{ + ClutterBoxPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX (box)); + + priv = box->priv; + + if (color) + { + priv->color = *color; + priv->color_set = TRUE; + } + else + priv->color_set = FALSE; + + clutter_actor_queue_redraw (CLUTTER_ACTOR (box)); + + g_object_notify (G_OBJECT (box), "color-set"); + g_object_notify (G_OBJECT (box), "color"); +} + +/** + * clutter_box_get_color: + * @box: a #ClutterBox + * @color: (out): return location for a #ClutterColor + * + * Retrieves the background color of @box + * + * If the #ClutterBox:color-set property is set to %FALSE the + * returned #ClutterColor is undefined + * + * Since: 1.2 + */ +void +clutter_box_get_color (ClutterBox *box, + ClutterColor *color) +{ + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (color != NULL); + + *color = box->priv->color; +} diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h index 4e9ea127a..bd60f8256 100644 --- a/clutter/clutter-box.h +++ b/clutter/clutter-box.h @@ -42,6 +42,11 @@ void clutter_box_set_layout_manager (ClutterBox *box, ClutterLayoutManager *manger); ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box); +void clutter_box_set_color (ClutterBox *box, + const ClutterColor *color); +void clutter_box_get_color (ClutterBox *box, + ClutterColor *color); + void clutter_box_pack (ClutterBox *box, ClutterActor *actor, const gchar *first_property, diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 178693de3..cd1bba45a 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1809,6 +1809,10 @@ ClutterBoxClass clutter_box_new clutter_box_set_layout_manager clutter_box_get_layout_manager +clutter_box_set_color +clutter_box_get_color + +<SUBSECTION> clutter_box_pack clutter_box_packv From 4663552a0017f85c818a664b3528fab617e6e15c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Fri, 18 Sep 2009 15:29:09 +0100 Subject: [PATCH 26/50] [layout] Typo and whitespace fixes --- clutter/clutter-box.c | 2 +- clutter/clutter-box.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index 95f638b9c..7bcd879f6 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -527,7 +527,7 @@ clutter_box_new (ClutterLayoutManager *manager) * Since: 1.2 */ void -clutter_box_set_layout_manager (ClutterBox *box, +clutter_box_set_layout_manager (ClutterBox *box, ClutterLayoutManager *manager) { g_return_if_fail (CLUTTER_IS_BOX (box)); diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h index bd60f8256..a44c274c1 100644 --- a/clutter/clutter-box.h +++ b/clutter/clutter-box.h @@ -39,7 +39,7 @@ GType clutter_box_get_type (void) G_GNUC_CONST; ClutterActor * clutter_box_new (ClutterLayoutManager *manager); void clutter_box_set_layout_manager (ClutterBox *box, - ClutterLayoutManager *manger); + ClutterLayoutManager *manager); ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box); void clutter_box_set_color (ClutterBox *box, From 62db72cf4106f498a041d02d67de72460130acdd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Mon, 5 Oct 2009 17:09:04 +0100 Subject: [PATCH 27/50] [layout] Update FixedLayout The behaviour of ClutterGroup has been fixed with regards to the preferred size request; the fixed layout manager should use the same behaviour. --- clutter/clutter-fixed-layout.c | 112 ++++++--------------------------- 1 file changed, 18 insertions(+), 94 deletions(-) diff --git a/clutter/clutter-fixed-layout.c b/clutter/clutter-fixed-layout.c index f558c132d..5ec56fa3d 100644 --- a/clutter/clutter-fixed-layout.c +++ b/clutter/clutter-fixed-layout.c @@ -54,15 +54,14 @@ clutter_fixed_layout_get_preferred_width (ClutterLayoutManager *manager, gfloat *nat_width_p) { GList *children, *l; - gdouble min_left, min_right; - gdouble natural_left, natural_right; + gdouble min_right; + gdouble natural_right; - min_left = 0; min_right = 0; - natural_left = 0; natural_right = 0; children = clutter_container_get_children (container); + for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; @@ -74,57 +73,20 @@ clutter_fixed_layout_get_preferred_width (ClutterLayoutManager *manager, &child_min, NULL, &child_natural, NULL); - if (l == children) - { - /* First child */ - min_left = child_x; - natural_left = child_x; - min_right = min_left + child_min; - natural_right = natural_left + child_natural; - } - else - { - /* Union of extents with previous children */ - if (child_x < min_left) - min_left = child_x; + if (child_x + child_min > min_right) + min_right = child_x + child_min; - if (child_x < natural_left) - natural_left = child_x; - - if (child_x + child_min > min_right) - min_right = child_x + child_min; - - if (child_x + child_natural > natural_right) - natural_right = child_x + child_natural; - } + if (child_x + child_natural > natural_right) + natural_right = child_x + child_natural; } g_list_free (children); - /* The preferred size is defined as the width and height we want starting - * from our origin, since our allocation will set the origin; so we now - * need to remove any part of the request that is to the left of the origin. - */ - if (min_left < 0) - min_left = 0; - - if (natural_left < 0) - natural_left = 0; - - if (min_right < 0) - min_right = 0; - - if (natural_right < 0) - natural_right = 0; - - g_assert (min_right >= min_left); - g_assert (natural_right >= natural_left); - if (min_width_p) - *min_width_p = min_right - min_left; + *min_width_p = min_right; if (nat_width_p) - *nat_width_p = natural_right - min_left; + *nat_width_p = natural_right; } static void @@ -135,15 +97,14 @@ clutter_fixed_layout_get_preferred_height (ClutterLayoutManager *manager, gfloat *nat_height_p) { GList *children, *l; - gdouble min_top, min_bottom; - gdouble natural_top, natural_bottom; + gdouble min_bottom; + gdouble natural_bottom; - min_top = 0; min_bottom = 0; - natural_top = 0; natural_bottom = 0; children = clutter_container_get_children (container); + for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; @@ -155,57 +116,20 @@ clutter_fixed_layout_get_preferred_height (ClutterLayoutManager *manager, NULL, &child_min, NULL, &child_natural); - if (l == children) - { - /* First child */ - min_top = child_y; - natural_top = child_y; - min_bottom = min_top + child_min; - natural_bottom = natural_top + child_natural; - } - else - { - /* Union of extents with previous children */ - if (child_y < min_top) - min_top = child_y; + if (child_y + child_min > min_bottom) + min_bottom = child_y + child_min; - if (child_y < natural_top) - natural_top = child_y; - - if (child_y + child_min > min_bottom) - min_bottom = child_y + child_min; - - if (child_y + child_natural > natural_bottom) - natural_bottom = child_y + child_natural; - } + if (child_y + child_natural > natural_bottom) + natural_bottom = child_y + child_natural; } g_list_free (children); - /* The preferred size is defined as the width and height we want starting - * from our origin, since our allocation will set the origin; so we now - * need to remove any part of the request that is above the origin. - */ - if (min_top < 0) - min_top = 0; - - if (natural_top < 0) - natural_top = 0; - - if (min_bottom < 0) - min_bottom = 0; - - if (natural_bottom < 0) - natural_bottom = 0; - - g_assert (min_bottom >= min_top); - g_assert (natural_bottom >= natural_top); - if (min_height_p) - *min_height_p = min_bottom - min_top; + *min_height_p = min_bottom; if (nat_height_p) - *nat_height_p = natural_bottom - min_top; + *nat_height_p = natural_bottom; } static void From 857b0239e906b0e5eb1a2fec8703eb675a70675f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Mon, 5 Oct 2009 17:21:41 +0100 Subject: [PATCH 28/50] [layout] Use FixedLayout inside Group The Group actor should use the FixedLayout layout manager object to avoid duplicating code. --- clutter/clutter-group.c | 138 +++++++++------------------------------- 1 file changed, 29 insertions(+), 109 deletions(-) diff --git a/clutter/clutter-group.c b/clutter/clutter-group.c index 918aa5ecd..94dfdddf0 100644 --- a/clutter/clutter-group.c +++ b/clutter/clutter-group.c @@ -45,6 +45,7 @@ #include "clutter-group.h" #include "clutter-container.h" +#include "clutter-fixed-layout.h" #include "clutter-main.h" #include "clutter-private.h" #include "clutter-debug.h" @@ -75,6 +76,8 @@ G_DEFINE_TYPE_WITH_CODE (ClutterGroup, struct _ClutterGroupPrivate { GList *children; + + ClutterLayoutManager *layout; }; @@ -126,113 +129,19 @@ clutter_group_pick (ClutterActor *actor, } } -static void -clutter_fixed_layout_get_preferred_width (GList *children, - gfloat *min_width_p, - gfloat *natural_width_p) -{ - GList *l; - gdouble min_right, natural_right; - - /* We will always be at least 0 sized (ie, if all of the actors are - to the left of the origin we won't return a negative size) */ - min_right = 0; - natural_right = 0; - - for (l = children; l != NULL; l = l->next) - { - ClutterActor *child = l->data; - gfloat child_x, child_min, child_natural; - - child_x = clutter_actor_get_x (child); - - clutter_actor_get_preferred_size (child, - &child_min, NULL, - &child_natural, NULL); - - /* Track the rightmost edge */ - if (child_x + child_min > min_right) - min_right = child_x + child_min; - - if (child_x + child_natural > natural_right) - natural_right = child_x + child_natural; - } - - /* The size is defined as the distance from the origin to the - right-hand edge of the rightmost actor */ - if (min_width_p) - *min_width_p = min_right; - - if (natural_width_p) - *natural_width_p = natural_right; -} - -static void -clutter_fixed_layout_get_preferred_height (GList *children, - gfloat *min_height_p, - gfloat *natural_height_p) -{ - GList *l; - gdouble min_bottom, natural_bottom; - - /* We will always be at least 0 sized (ie, if all of the actors are - above the origin we won't return a negative size) */ - min_bottom = 0; - natural_bottom = 0; - - for (l = children; l != NULL; l = l->next) - { - ClutterActor *child = l->data; - gfloat child_y, child_min, child_natural; - - child_y = clutter_actor_get_y (child); - - clutter_actor_get_preferred_size (child, - NULL, &child_min, - NULL, &child_natural); - - /* Track the bottommost edge */ - if (child_y + child_min > min_bottom) - min_bottom = child_y + child_min; - - if (child_y + child_natural > natural_bottom) - natural_bottom = child_y + child_natural; - } - - /* The size is defined as the distance from the origin to the bottom - edge of the bottommost actor */ - if (min_height_p) - *min_height_p = min_bottom; - - if (natural_height_p) - *natural_height_p = natural_bottom; -} - -static void -clutter_fixed_layout_allocate (GList *children, - ClutterAllocationFlags flags) -{ - GList *l; - - for (l = children; l != NULL; l = l->next) - { - ClutterActor *child = l->data; - clutter_actor_allocate_preferred_size (child, flags); - } -} - static void clutter_group_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { + ClutterContainer *container = CLUTTER_CONTAINER (self); ClutterGroupPrivate *priv = CLUTTER_GROUP (self)->priv; - /* for_height is irrelevant to the fixed layout, so it's not used */ - clutter_fixed_layout_get_preferred_width (priv->children, - min_width_p, - natural_width_p); + clutter_layout_manager_get_preferred_width (priv->layout, container, + for_height, + min_width_p, + natural_width_p); } static void @@ -241,12 +150,13 @@ clutter_group_get_preferred_height (ClutterActor *self, gfloat *min_height_p, gfloat *natural_height_p) { + ClutterContainer *container = CLUTTER_CONTAINER (self); ClutterGroupPrivate *priv = CLUTTER_GROUP (self)->priv; - /* for_width is irrelevant to the fixed layout, so it's not used */ - clutter_fixed_layout_get_preferred_height (priv->children, - min_height_p, - natural_height_p); + clutter_layout_manager_get_preferred_width (priv->layout, container, + for_width, + min_height_p, + natural_height_p); } static void @@ -254,17 +164,16 @@ clutter_group_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { + ClutterContainer *container = CLUTTER_CONTAINER (self); ClutterGroupPrivate *priv = CLUTTER_GROUP (self)->priv; /* chain up to set actor->allocation */ CLUTTER_ACTOR_CLASS (clutter_group_parent_class)->allocate (self, box, flags); - /* Note that fixed-layout allocation of children does not care what - * allocation the container received, so "box" is not passed in - * here. We do not require that children's allocations are completely - * contained by our own. - */ - clutter_fixed_layout_allocate (priv->children, flags); + if (priv->children == NULL) + return; + + clutter_layout_manager_allocate (priv->layout, container, box, flags); } static void @@ -281,6 +190,12 @@ clutter_group_dispose (GObject *object) priv->children = NULL; } + if (priv->layout) + { + g_object_unref (priv->layout); + priv->layout = NULL; + } + G_OBJECT_CLASS (clutter_group_parent_class)->dispose (object); } @@ -468,8 +383,10 @@ sort_z_order (gconstpointer a, if (depth_a < depth_b) return -1; + if (depth_a > depth_b) return 1; + return 0; } @@ -521,6 +438,9 @@ static void clutter_group_init (ClutterGroup *self) { self->priv = CLUTTER_GROUP_GET_PRIVATE (self); + + self->priv->layout = clutter_fixed_layout_new (); + g_object_ref_sink (self->priv->layout); } /** From 5737cf869f162f5539ae00f86db7923502c38dab Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Fri, 18 Sep 2009 17:28:02 +0100 Subject: [PATCH 29/50] [layout] Initial implementation of FlowLayout FlowLayout is a layout manager that arranges its children in a reflowing line; the orientation controls the major axis for the layout: horizontal, for reflow on the Y axis, and vertical, for reflow on the X axis. --- .gitignore | 1 + clutter/Makefile.am | 2 + clutter/clutter-flow-layout.c | 855 +++++++++++++++++++++ clutter/clutter-flow-layout.h | 127 +++ clutter/clutter.h | 1 + doc/reference/clutter/clutter-docs.xml.in | 1 + doc/reference/clutter/clutter-sections.txt | 34 + doc/reference/clutter/clutter.types | 1 + tests/interactive/Makefile.am | 3 +- tests/interactive/test-flow.c | 117 +++ 10 files changed, 1141 insertions(+), 1 deletion(-) create mode 100644 clutter/clutter-flow-layout.c create mode 100644 clutter/clutter-flow-layout.h create mode 100644 tests/interactive/test-flow.c diff --git a/.gitignore b/.gitignore index e492b15a7..48bf770b7 100644 --- a/.gitignore +++ b/.gitignore @@ -134,6 +134,7 @@ TAGS /tests/interactive/test-script.json /tests/interactive/test-clutter-cairo-flowers /tests/interactive/test-box +/tests/interactive/test-flow /tests/conform/stamp-test-conformance /tests/conform/test-anchors /tests/conform/test-conformance diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 6c8983add..61a761694 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -77,6 +77,7 @@ source_h = \ $(srcdir)/clutter-feature.h \ $(srcdir)/clutter-fixed.h \ $(srcdir)/clutter-fixed-layout.h \ + $(srcdir)/clutter-flow-layout.h \ $(srcdir)/clutter-frame-source.h \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-interval.h \ @@ -146,6 +147,7 @@ source_c = \ $(srcdir)/clutter-feature.c \ $(srcdir)/clutter-fixed.c \ $(srcdir)/clutter-fixed-layout.c \ + $(srcdir)/clutter-flow-layout.c \ $(srcdir)/clutter-frame-source.c \ $(srcdir)/clutter-group.c \ $(srcdir)/clutter-id-pool.c \ diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c new file mode 100644 index 000000000..54e9a17db --- /dev/null +++ b/clutter/clutter-flow-layout.c @@ -0,0 +1,855 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + +/** + * SECTION:clutter-flow-layout + * @short_description: A reflowing layout manager + * + * #ClutterFlowLayout is a layout manager which implements the following + * policy: + * + * <itemizedlist> + * </itemizedlist> + * + * #ClutterFlowLayout is available since Clutter 1.2 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> + +#include "clutter-actor.h" +#include "clutter-animatable.h" +#include "clutter-child-meta.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-flow-layout.h" +#include "clutter-layout-meta.h" +#include "clutter-private.h" + +#define CLUTTER_FLOW_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutPrivate)) + +struct _ClutterFlowLayoutPrivate +{ + ClutterContainer *container; + + ClutterFlowOrientation orientation; + + gfloat col_spacing; + gfloat row_spacing; + + gfloat min_col_width; + gfloat max_col_width; + gfloat col_width; + + gfloat min_row_height; + gfloat max_row_height; + gfloat row_height; + + /* cache the preferred size; this way, if we get what we asked + * or more then we don't have to recompute the layout + */ + gfloat request_width; + gfloat request_height; + + gint max_row_items; + + guint layout_wrap : 1; +}; + +enum +{ + PROP_0, + + PROP_ORIENTATION, + + PROP_COLUMN_SPACING, + PROP_ROW_SPACING, + + PROP_MIN_COLUMN_WIDTH, + PROP_MAX_COLUMN_WIDTH, + PROP_MIN_ROW_HEGHT, + PROP_MAX_ROW_HEIGHT, + + PROP_WRAP +}; + +G_DEFINE_TYPE (ClutterFlowLayout, + clutter_flow_layout, + CLUTTER_TYPE_LAYOUT_MANAGER); + +static void +clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p) +{ + ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; + GList *l, *children = clutter_container_get_children (container); + gfloat max_child_min_width, max_child_natural_width; + gfloat row_natural_width; + + max_child_min_width = max_child_natural_width = 0; + row_natural_width = 0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat child_min, child_natural; + + clutter_actor_get_preferred_width (child, for_height, + &child_min, + &child_natural); + + max_child_min_width = MAX (max_child_min_width, child_min); + max_child_natural_width = MAX (max_child_natural_width, child_natural); + + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) + row_natural_width += (child_natural + priv->col_spacing); + else + row_natural_width = MAX (row_natural_width, max_child_natural_width); + } + + g_list_free (children); + + priv->request_width = row_natural_width; + priv->col_width = max_child_natural_width; + + if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width) + priv->col_width = MAX (priv->max_col_width, max_child_min_width); + + if (priv->col_width < priv->min_col_width) + priv->col_width = priv->min_col_width; + + if (min_width_p) + *min_width_p = ceilf (max_child_min_width); + + if (nat_width_p) + *nat_width_p = ceilf (row_natural_width); +} + +static void +clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p) +{ + ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; + GList *l, *children = clutter_container_get_children (container); + gfloat max_child_min_height, max_child_natural_height; + gfloat col_natural_height; + + max_child_min_height = max_child_natural_height = 0; + col_natural_height = 0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat child_min, child_natural; + + clutter_actor_get_preferred_height (child, for_width, + &child_min, + &child_natural); + + max_child_min_height = MAX (max_child_min_height, child_min); + max_child_natural_height = MAX (max_child_natural_height, child_natural); + + if (priv->orientation == CLUTTER_FLOW_VERTICAL) + col_natural_height += (child_natural + priv->row_spacing); + else + col_natural_height = MAX (col_natural_height, max_child_natural_height); + } + + g_list_free (children); + + priv->request_height = col_natural_height; + priv->row_height = max_child_natural_height; + + if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height) + priv->row_height = MAX (priv->max_row_height, max_child_min_height); + + if (priv->row_height < priv->min_row_height) + priv->row_height = priv->min_row_height; + + if (min_height_p) + *min_height_p = ceilf (max_child_min_height); + + if (nat_height_p) + *nat_height_p = ceilf (col_natural_height); +} + +static gint +compute_lines (ClutterFlowLayout *self, + const GList *children, + gfloat avail_width, + gfloat avail_height) +{ + ClutterFlowLayoutPrivate *priv = self->priv; + gint items_per_line; + + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) + items_per_line = avail_width / (priv->col_width + priv->col_spacing); + else + items_per_line = avail_height / (priv->row_height + priv->row_spacing); + + return items_per_line; +} + +static void +clutter_flow_layout_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; + GList *l, *children = clutter_container_get_children (container); + gfloat avail_width, avail_height; + gfloat item_x, item_y; + gint line_items_count; + gint items_per_line; + gint line_index; + + if (children == NULL) + return; + + clutter_actor_box_get_size (allocation, &avail_width, &avail_height); + + items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager), + children, + avail_width, avail_height); + + item_x = item_y = 0; + + line_items_count = 0; + line_index = 0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + ClutterActorBox child_alloc; + gfloat item_width, item_height; + gfloat child_min, child_natural; + + if (line_items_count == items_per_line) + { + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) + { + item_x = 0; + item_y += priv->row_height + priv->row_spacing; + } + else + { + item_x += priv->col_width + priv->col_spacing; + item_y = 0; + } + + line_items_count = 0; + line_index += 1; + } + + clutter_actor_get_preferred_width (child, priv->row_height, + &child_min, + &child_natural); + item_width = MIN (child_natural, priv->col_width); + + clutter_actor_get_preferred_height (child, item_width, + &child_min, + &child_natural); + item_height = MIN (child_natural, priv->row_height); + + CLUTTER_NOTE (LAYOUT, + "flow[line:%d, item:%d/%d] = { %.2f, %.2f, %.2f, %.2f }", + line_index, line_items_count, items_per_line, + item_x, item_y, item_width, item_height); + + child_alloc.x1 = ceil (item_x); + child_alloc.y1 = ceil (item_y); + child_alloc.x2 = ceil (child_alloc.x1 + item_width); + child_alloc.y2 = ceil (child_alloc.y1 + item_height); + clutter_actor_allocate (child, &child_alloc, flags); + + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) + item_x += item_width; + else + item_y += item_height; + + line_items_count += 1; + } + + g_list_free (children); +} + +static void +clutter_flow_layout_set_container (ClutterLayoutManager *manager, + ClutterContainer *container) +{ + ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; + + priv->container = container; +} + +static void +clutter_flow_layout_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterFlowLayout *self = CLUTTER_FLOW_LAYOUT (gobject); + + switch (prop_id) + { + case PROP_ORIENTATION: + clutter_flow_layout_set_orientation (self, g_value_get_enum (value)); + break; + + case PROP_WRAP: + clutter_flow_layout_set_wrap (self, g_value_get_boolean (value)); + break; + + case PROP_COLUMN_SPACING: + clutter_flow_layout_set_column_spacing (self, g_value_get_float (value)); + break; + + case PROP_ROW_SPACING: + clutter_flow_layout_set_row_spacing (self, g_value_get_float (value)); + break; + + case PROP_MIN_COLUMN_WIDTH: + clutter_flow_layout_set_column_width (self, + g_value_get_float (value), + self->priv->max_col_width); + break; + + case PROP_MAX_COLUMN_WIDTH: + clutter_flow_layout_set_column_width (self, + self->priv->min_col_width, + g_value_get_float (value)); + break; + + case PROP_MIN_ROW_HEGHT: + clutter_flow_layout_set_row_height (self, + g_value_get_float (value), + self->priv->max_row_height); + break; + + case PROP_MAX_ROW_HEIGHT: + clutter_flow_layout_set_row_height (self, + self->priv->min_row_height, + g_value_get_float (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_flow_layout_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (gobject)->priv; + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, priv->orientation); + break; + + case PROP_WRAP: + g_value_set_boolean (value, priv->layout_wrap); + break; + + case PROP_COLUMN_SPACING: + g_value_set_float (value, priv->col_spacing); + break; + + case PROP_ROW_SPACING: + g_value_set_float (value, priv->row_spacing); + break; + + case PROP_MIN_COLUMN_WIDTH: + g_value_set_float (value, priv->min_col_width); + break; + + case PROP_MAX_COLUMN_WIDTH: + g_value_set_float (value, priv->max_col_width); + break; + + case PROP_MIN_ROW_HEGHT: + g_value_set_float (value, priv->min_row_height); + break; + + case PROP_MAX_ROW_HEIGHT: + g_value_set_float (value, priv->max_row_height); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) +{ + GObjectClass *gobject_class; + ClutterLayoutManagerClass *layout_class; + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (ClutterFlowLayoutPrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); + + gobject_class->set_property = clutter_flow_layout_set_property; + gobject_class->get_property = clutter_flow_layout_get_property; + + layout_class->get_preferred_width = + clutter_flow_layout_get_preferred_width; + layout_class->get_preferred_height = + clutter_flow_layout_get_preferred_height; + layout_class->allocate = clutter_flow_layout_allocate; + layout_class->set_container = clutter_flow_layout_set_container; + + /** + * ClutterFlowLayout:orientation: + * + * The orientation of the #ClutterFlowLayout. The children + * of the layout will be layed out following the orientation. + * If #ClutterFlowLayout:wrap is set to %TRUE then this property + * will control the primary direction of the layout before + * wrapping takes place + * + * Since: 1.2 + */ + pspec = g_param_spec_enum ("orientation", + "Orientation", + "The orientation of the layout", + CLUTTER_TYPE_FLOW_ORIENTATION, + CLUTTER_FLOW_HORIZONTAL, + CLUTTER_PARAM_READWRITE | + G_PARAM_CONSTRUCT); + g_object_class_install_property (gobject_class, PROP_ORIENTATION, pspec); + + /** + * ClutterFlowLayout:wrap: + * + * Whether the layout should wrap the children to fit them + * in the allocation. A non-wrapping layout has a preferred + * size of the biggest child in the direction opposite to the + * #ClutterFlowLayout:orientation property, and the sum of + * the preferred sizes (taking into account spacing) of the + * children in the direction of the orientation + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("wrap", + "Wrap", + "Whether the layout should wrap", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_WRAP, pspec); + + /** + * ClutterFlowLayout:column-spacing: + * + * The spacing between columns, in pixels; the value of this + * property is honoured by horizontal non-wrapping layouts and + * by vertical wrapping layouts + * + * Since: 1.2 + */ + pspec = g_param_spec_float ("column-spacing", + "Column Spacing", + "The spacing between columns", + 0.0, G_MAXFLOAT, + 0.0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_COLUMN_SPACING, + pspec); + + /** + * ClutterFlowLayout:row-spacing: + * + * The spacing between rows, in pixels; the value of this + * property is honoured by vertical non-wrapping layouts and + * by horizontal wrapping layouts + * + * Since: 1.2 + */ + pspec = g_param_spec_float ("row-spacing", + "Row Spacing", + "The spacing between rows", + 0.0, G_MAXFLOAT, + 0.0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_ROW_SPACING, + pspec); + + /** + * ClutterFlowLayout:min-column-width: + * + * Minimum width for each column in the layout, in pixels + * + * Since: 1.2 + */ + pspec = g_param_spec_float ("min-column-width", + "Minimum Column Width", + "Minimum width for each column", + 0.0, G_MAXFLOAT, + 0.0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_MIN_COLUMN_WIDTH, + pspec); + + /** + * ClutterFlowLayout:max-column-width: + * + * Maximum width for each column in the layout, in pixels. If + * set to -1 the width will be the maximum child width + * + * Since: 1.2 + */ + pspec = g_param_spec_float ("max-column-width", + "Maximum Column Width", + "Maximum width for each column", + -1.0, G_MAXFLOAT, + -1.0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_MAX_COLUMN_WIDTH, + pspec); + + /** + * ClutterFlowLayout:min-row-height: + * + * Minimum height for each row in the layout, in pixels + * + * Since: 1.2 + */ + pspec = g_param_spec_float ("min-row-height", + "Minimum Row Height", + "Minimum height for each row", + 0.0, G_MAXFLOAT, + 0.0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_MIN_ROW_HEGHT, + pspec); + + /** + * ClutterFlowLayout:max-row-height: + * + * Maximum height for each row in the layout, in pixels. If + * set to -1 the width will be the maximum child height + * + * Since: 1.2 + */ + pspec = g_param_spec_float ("max-row-height", + "Maximum Row Height", + "Maximum height for each row", + -1.0, G_MAXFLOAT, + -1.0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_MAX_ROW_HEIGHT, + pspec); +} + +static void +clutter_flow_layout_init (ClutterFlowLayout *self) +{ + ClutterFlowLayoutPrivate *priv; + + self->priv = priv = CLUTTER_FLOW_LAYOUT_GET_PRIVATE (self); + + priv->orientation = CLUTTER_FLOW_HORIZONTAL; + + priv->col_spacing = 0; + priv->row_spacing = 0; + + priv->min_col_width = priv->min_row_height = 0; + priv->max_col_width = priv->max_row_height = -1; +} + +/** + * clutter_flow_layout_new: + * @orientation: the orientation of the flow layout + * + * Creates a new #ClutterFlowLayout with the given @orientation + * + * Return value: the newly created #ClutterFlowLayout + * + * Since: 1.2 + */ +ClutterLayoutManager * +clutter_flow_layout_new (ClutterFlowOrientation orientation) +{ + return g_object_new (CLUTTER_TYPE_FLOW_LAYOUT, + "orientation", orientation, + NULL); +} + +void +clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, + ClutterFlowOrientation orientation) +{ + ClutterFlowLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->orientation != orientation) + { + ClutterLayoutManager *manager; + + priv->orientation = orientation; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "orientation"); + } +} + +ClutterFlowOrientation +clutter_flow_layout_get_orientation (ClutterFlowLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), + CLUTTER_FLOW_HORIZONTAL); + + return layout->priv->orientation; +} + +void +clutter_flow_layout_set_wrap (ClutterFlowLayout *layout, + gboolean wrap) +{ + ClutterFlowLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->layout_wrap != wrap) + { + ClutterLayoutManager *manager; + + priv->layout_wrap = wrap; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "wrap"); + } +} + +gboolean +clutter_flow_layout_get_wrap (ClutterFlowLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), FALSE); + + return layout->priv->layout_wrap; +} + +void +clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, + gfloat spacing) +{ + ClutterFlowLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->col_spacing != spacing) + { + ClutterLayoutManager *manager; + + priv->col_spacing = spacing; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "column-spacing"); + } +} + +gfloat +clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), 0.0); + + return layout->priv->col_spacing; +} + +void +clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout, + gfloat spacing) +{ + ClutterFlowLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->row_spacing != spacing) + { + ClutterLayoutManager *manager; + + priv->row_spacing = spacing; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "row-spacing"); + } +} + +gfloat +clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), 0.0); + + return layout->priv->row_spacing; +} + +void +clutter_flow_layout_set_column_width (ClutterFlowLayout *layout, + gfloat min_width, + gfloat max_width) +{ + ClutterFlowLayoutPrivate *priv; + gboolean notify_min = FALSE, notify_max = FALSE; + + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->min_col_width != min_width) + { + priv->min_col_width = min_width; + + notify_min = TRUE; + } + + if (priv->max_col_width != max_width) + { + priv->max_col_width = max_width; + + notify_max = TRUE; + } + + if (notify_min || notify_max) + { + ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout); + + clutter_layout_manager_layout_changed (manager); + } + + if (notify_min) + g_object_notify (G_OBJECT (layout), "min-column-width"); + + if (notify_max) + g_object_notify (G_OBJECT (layout), "max-column-width"); +} + +void +clutter_flow_layout_get_column_width (ClutterFlowLayout *layout, + gfloat *min_width, + gfloat *max_width) +{ + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + if (min_width) + *min_width = layout->priv->min_col_width; + + if (max_width) + *max_width = layout->priv->max_col_width; +} + +void +clutter_flow_layout_set_row_height (ClutterFlowLayout *layout, + gfloat min_height, + gfloat max_height) +{ + ClutterFlowLayoutPrivate *priv; + gboolean notify_min = FALSE, notify_max = FALSE; + + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->min_row_height != min_height) + { + priv->min_row_height = min_height; + + notify_min = TRUE; + } + + if (priv->max_row_height != max_height) + { + priv->max_row_height = max_height; + + notify_max = TRUE; + } + + if (notify_min || notify_max) + { + ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout); + + clutter_layout_manager_layout_changed (manager); + } + + if (notify_min) + g_object_notify (G_OBJECT (layout), "min-row-height"); + + if (notify_max) + g_object_notify (G_OBJECT (layout), "max-row-height"); +} + +void +clutter_flow_layout_get_row_height (ClutterFlowLayout *layout, + gfloat *min_height, + gfloat *max_height) +{ + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + if (min_height) + *min_height = layout->priv->min_row_height; + + if (max_height) + *max_height = layout->priv->max_row_height; +} diff --git a/clutter/clutter-flow-layout.h b/clutter/clutter-flow-layout.h new file mode 100644 index 000000000..95aee0978 --- /dev/null +++ b/clutter/clutter-flow-layout.h @@ -0,0 +1,127 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only <clutter/clutter.h> can be included directly." +#endif + +#ifndef __CLUTTER_FLOW_LAYOUT_H__ +#define __CLUTTER_FLOW_LAYOUT_H__ + +#include <clutter/clutter-layout-manager.h> + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_FLOW_LAYOUT (clutter_flow_layout_get_type ()) +#define CLUTTER_FLOW_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayout)) +#define CLUTTER_IS_FLOW_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_FLOW_LAYOUT)) +#define CLUTTER_FLOW_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutClass)) +#define CLUTTER_IS_FLOW_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_FLOW_LAYOUT)) +#define CLUTTER_FLOW_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutClass)) + +typedef struct _ClutterFlowLayout ClutterFlowLayout; +typedef struct _ClutterFlowLayoutPrivate ClutterFlowLayoutPrivate; +typedef struct _ClutterFlowLayoutClass ClutterFlowLayoutClass; + +/** + * ClutterFlowOrientation: + * @CLUTTER_FLOW_HORIZONTAL: Arrange the children of the flow layout + * horizontally first + * @CLUTTER_FLOW_VERTICAL: Arrange the children of the flow layout + * vertically first + * + * The direction of the arrangement of the children inside + * a #ClutterFlowLayout + * + * Since: 1.2 + */ +typedef enum { /*< prefix=CLUTTER_FLOW >*/ + CLUTTER_FLOW_HORIZONTAL, + CLUTTER_FLOW_VERTICAL +} ClutterFlowOrientation; + +/** + * ClutterFlowLayout: + * + * The #ClutterFlowLayout structure contains only private data + * and should be accessed using the provided API + * + * Since: 1.2 + */ +struct _ClutterFlowLayout +{ + /*< private >*/ + ClutterLayoutManager parent_instance; + + ClutterFlowLayoutPrivate *priv; +}; + +/** + * ClutterFlowLayoutClass: + * + * The #ClutterFlowLayoutClass structure contains only private data + * and should be accessed using the provided API + * + * Since: 1.2 + */ +struct _ClutterFlowLayoutClass +{ + /*< private >*/ + ClutterLayoutManagerClass parent_class; +}; + +GType clutter_flow_layout_get_type (void) G_GNUC_CONST; + +ClutterLayoutManager * clutter_flow_layout_new (ClutterFlowOrientation orientation); + +void clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, + ClutterFlowOrientation orientation); +ClutterFlowOrientation clutter_flow_layout_get_orientation (ClutterFlowLayout *layout); +void clutter_flow_layout_set_wrap (ClutterFlowLayout *layout, + gboolean wrap); +gboolean clutter_flow_layout_get_wrap (ClutterFlowLayout *layout); + +void clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, + gfloat spacing); +gfloat clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout); +void clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout, + gfloat spacing); +gfloat clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout); + +void clutter_flow_layout_set_column_width (ClutterFlowLayout *layout, + gfloat min_width, + gfloat max_width); +void clutter_flow_layout_get_column_width (ClutterFlowLayout *layout, + gfloat *min_width, + gfloat *max_width); +void clutter_flow_layout_set_row_height (ClutterFlowLayout *layout, + gfloat min_height, + gfloat max_height); +void clutter_flow_layout_get_row_height (ClutterFlowLayout *layout, + gfloat *min_height, + gfloat *max_height); + +G_END_DECLS + +#endif /* __CLUTTER_FLOW_LAYOUT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 169626264..5c313179b 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -53,6 +53,7 @@ #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-fixed-layout.h" +#include "clutter-flow-layout.h" #include "clutter-frame-source.h" #include "clutter-group.h" #include "clutter-interval.h" diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index ed019ead0..5771a97fc 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -81,6 +81,7 @@ <xi:include href="xml/clutter-fixed-layout.xml"/> <xi:include href="xml/clutter-bin-layout.xml"/> + <xi:include href="xml/clutter-flow-layout.xml"/> </chapter> </part> diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index cd1bba45a..68c3f750f 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1847,3 +1847,37 @@ CLUTTER_LAYOUT_META_GET_CLASS <SUBSECTION Private> clutter_layout_meta_get_type </SECTION> + +<SECTION> +<FILE>clutter-flow-layout</FILE> +<TITLE>ClutterFlowLayout</TITLE> +ClutterFlowOrientation +ClutterFlowLayout +ClutterFlowLayoutClass +clutter_flow_layout_new +clutter_flow_layout_set_orientation +clutter_flow_layout_get_orientation +clutter_flow_layout_set_wrap +clutter_flow_layout_get_wrap + +<SUBSECTION> +clutter_flow_layout_set_column_spacing +clutter_flow_layout_get_column_spacing +clutter_flow_layout_set_row_spacing +clutter_flow_layout_get_row_spacing +clutter_flow_layout_set_column_width +clutter_flow_layout_get_column_width +clutter_flow_layout_set_row_height +clutter_flow_layout_get_row_height + +<SUBSECTION Standard> +CLUTTER_TYPE_FLOW_LAYOUT +CLUTTER_FLOW_LAYOUT +CLUTTER_FLOW_LAYOUT_CLASS +CLUTTER_IS_FLOW_LAYOUT +CLUTTER_IS_FLOW_LAYOUT_CLASS +CLUTTER_FLOW_LAYOUT_GET_CLASS +<SUBSECTION Private> +ClutterFlowLayoutPrivate +clutter_flow_layout_get_type +</SECTION> diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 82869b92a..07164a443 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -38,3 +38,4 @@ clutter_layout_manager_get_type clutter_layout_meta_get_type clutter_fixed_layout_get_type clutter_bin_layout_get_type +clutter_flow_layout_get_type diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 3a2215f65..193e1130f 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -44,7 +44,8 @@ UNIT_TESTS = \ test-text-field.c \ test-clutter-cairo-flowers.c \ test-cogl-vertex-buffer.c \ - test-box.c + test-box.c \ + test-flow.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-flow.c b/tests/interactive/test-flow.c new file mode 100644 index 000000000..838a5ec67 --- /dev/null +++ b/tests/interactive/test-flow.c @@ -0,0 +1,117 @@ +#include <stdlib.h> +#include <gmodule.h> +#include <cairo/cairo.h> +#include <clutter/clutter.h> + +#define N_RECTS 20 + +static gboolean vertical = FALSE; +static gboolean random_size = FALSE; +static gint n_rects = N_RECTS; + +static GOptionEntry entries[] = { + { + "random-size", 'r', + 0, + G_OPTION_ARG_NONE, + &random_size, + "Randomly size the rectangles", NULL + }, + { + "num-rects", 'n', + 0, + G_OPTION_ARG_INT, + &n_rects, + "Number of rectangles", "RECTS" + }, + { + "vertical", 'v', + 0, + G_OPTION_ARG_NONE, + &vertical, + "Set vertical orientation", NULL + }, + { NULL } +}; + +G_MODULE_EXPORT int +test_flow_main (int argc, char *argv[]) +{ + ClutterActor *stage, *box; + ClutterLayoutManager *layout; + ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff }; + GError *error; + gint i; + + error = NULL; + clutter_init_with_args (&argc, &argv, + NULL, + entries, + NULL, + &error); + if (error) + { + g_print ("Unable to run test-flow: %s", error->message); + g_error_free (error); + + return EXIT_FAILURE; + } + + stage = clutter_stage_get_default (); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Flow Layout"); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, 640, 480); + + layout = clutter_flow_layout_new (vertical ? CLUTTER_FLOW_VERTICAL + : CLUTTER_FLOW_HORIZONTAL); + + box = clutter_box_new (layout); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); + clutter_actor_set_position (box, 0, 0); + + if (vertical) + clutter_actor_set_height (box, 480); + else + clutter_actor_set_width (box, 640); + + clutter_actor_set_name (box, "box"); + + for (i = 0; i < n_rects; i++) + { + ClutterColor color = { 255, 255, 255, 255 }; + ClutterActor *rect; + gchar *name; + gfloat width, height; + + name = g_strdup_printf ("rect%02d", i); + + clutter_color_from_hls (&color, + 360.0 / n_rects * i, + 0.5, + 0.8); + rect = clutter_rectangle_new_with_color (&color); + + clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); + + if (random_size) + { + width = g_random_int_range (50, 100); + height = g_random_int_range (50, 100); + } + else + { + width = height = 50; + } + + clutter_actor_set_size (rect, width, height); + clutter_actor_set_name (rect, name); + + g_free (name); + } + + clutter_actor_show_all (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} From e5a074fd9e487444db9114c2970a4d83c1661c7f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Tue, 6 Oct 2009 16:17:16 +0100 Subject: [PATCH 30/50] [layout] Add :homogeneous to FlowLayout --- clutter/clutter-flow-layout.c | 88 ++++++++++++++++++++++++++++++----- clutter/clutter-flow-layout.h | 3 ++ tests/interactive/test-flow.c | 37 +++++++++++++-- 3 files changed, 114 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index 54e9a17db..d6d92b9fd 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -77,7 +77,8 @@ struct _ClutterFlowLayoutPrivate gint max_row_items; - guint layout_wrap : 1; + guint layout_wrap : 1; + guint is_homogeneous : 1; }; enum @@ -86,6 +87,8 @@ enum PROP_ORIENTATION, + PROP_HOMOGENEOUS, + PROP_COLUMN_SPACING, PROP_ROW_SPACING, @@ -272,15 +275,23 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, line_index += 1; } - clutter_actor_get_preferred_width (child, priv->row_height, - &child_min, - &child_natural); - item_width = MIN (child_natural, priv->col_width); + if (priv->is_homogeneous) + { + item_width = priv->col_width; + item_height = priv->row_height; + } + else + { + clutter_actor_get_preferred_width (child, priv->row_height, + &child_min, + &child_natural); + item_width = MIN (child_natural, priv->col_width); - clutter_actor_get_preferred_height (child, item_width, - &child_min, - &child_natural); - item_height = MIN (child_natural, priv->row_height); + clutter_actor_get_preferred_height (child, item_width, + &child_min, + &child_natural); + item_height = MIN (child_natural, priv->row_height); + } CLUTTER_NOTE (LAYOUT, "flow[line:%d, item:%d/%d] = { %.2f, %.2f, %.2f, %.2f }", @@ -294,9 +305,9 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, clutter_actor_allocate (child, &child_alloc, flags); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) - item_x += item_width; + item_x += (item_width + priv->col_spacing); else - item_y += item_height; + item_y += (item_height + priv->row_spacing); line_items_count += 1; } @@ -331,6 +342,10 @@ clutter_flow_layout_set_property (GObject *gobject, clutter_flow_layout_set_wrap (self, g_value_get_boolean (value)); break; + case PROP_HOMOGENEOUS: + clutter_flow_layout_set_homogeneous (self, g_value_get_boolean (value)); + break; + case PROP_COLUMN_SPACING: clutter_flow_layout_set_column_spacing (self, g_value_get_float (value)); break; @@ -387,6 +402,10 @@ clutter_flow_layout_get_property (GObject *gobject, g_value_set_boolean (value, priv->layout_wrap); break; + case PROP_HOMOGENEOUS: + g_value_set_boolean (value, priv->is_homogeneous); + break; + case PROP_COLUMN_SPACING: g_value_set_float (value, priv->col_spacing); break; @@ -459,6 +478,22 @@ clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_ORIENTATION, pspec); + /** + * ClutterFlowLayout:homogeneous: + * + * Whether each child inside the #ClutterFlowLayout should receive + * the same allocation + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("homogeneous", + "Homogeneous", + "Whether each item should receive the " + "same allocation", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_HOMOGENEOUS, pspec); + /** * ClutterFlowLayout:wrap: * @@ -684,6 +719,37 @@ clutter_flow_layout_get_wrap (ClutterFlowLayout *layout) return layout->priv->layout_wrap; } +void +clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout, + gboolean homogeneous) +{ + ClutterFlowLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->is_homogeneous != homogeneous) + { + ClutterLayoutManager *manager; + + priv->is_homogeneous = homogeneous; + + manager = CLUTTER_LAYOUT_MANAGER (layout); + clutter_layout_manager_layout_changed (manager); + + g_object_notify (G_OBJECT (layout), "homogeneous"); + } +} + +gboolean +clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), FALSE); + + return layout->priv->is_homogeneous; +} + void clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, gfloat spacing) diff --git a/clutter/clutter-flow-layout.h b/clutter/clutter-flow-layout.h index 95aee0978..530b2f134 100644 --- a/clutter/clutter-flow-layout.h +++ b/clutter/clutter-flow-layout.h @@ -101,6 +101,9 @@ ClutterFlowOrientation clutter_flow_layout_get_orientation (ClutterFlowLayout void clutter_flow_layout_set_wrap (ClutterFlowLayout *layout, gboolean wrap); gboolean clutter_flow_layout_get_wrap (ClutterFlowLayout *layout); +void clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout, + gboolean homogeneous); +gboolean clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout); void clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, gfloat spacing); diff --git a/tests/interactive/test-flow.c b/tests/interactive/test-flow.c index 838a5ec67..00348e7d7 100644 --- a/tests/interactive/test-flow.c +++ b/tests/interactive/test-flow.c @@ -5,9 +5,13 @@ #define N_RECTS 20 -static gboolean vertical = FALSE; -static gboolean random_size = FALSE; -static gint n_rects = N_RECTS; +static gboolean is_homogeneous = FALSE; +static gboolean vertical = FALSE; +static gboolean random_size = FALSE; + +static gint n_rects = N_RECTS; +static gint x_spacing = 0; +static gint y_spacing = 0; static GOptionEntry entries[] = { { @@ -31,6 +35,27 @@ static GOptionEntry entries[] = { &vertical, "Set vertical orientation", NULL }, + { + "homogeneous", 'h', + 0, + G_OPTION_ARG_NONE, + &is_homogeneous, + "Whether the layout should be homogeneous", NULL + }, + { + "x-spacing", 0, + 0, + G_OPTION_ARG_INT, + &x_spacing, + "Horizontal spacing between elements", "PX" + }, + { + "y-spacing", 0, + 0, + G_OPTION_ARG_INT, + &y_spacing, + "Vertical spacing between elements", "PX" + }, { NULL } }; @@ -64,6 +89,12 @@ test_flow_main (int argc, char *argv[]) layout = clutter_flow_layout_new (vertical ? CLUTTER_FLOW_VERTICAL : CLUTTER_FLOW_HORIZONTAL); + clutter_flow_layout_set_homogeneous (CLUTTER_FLOW_LAYOUT (layout), + is_homogeneous); + clutter_flow_layout_set_column_spacing (CLUTTER_FLOW_LAYOUT (layout), + x_spacing); + clutter_flow_layout_set_row_spacing (CLUTTER_FLOW_LAYOUT (layout), + y_spacing); box = clutter_box_new (layout); clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); From db3ef971705485b1000def688635daaac99f79cd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Tue, 6 Oct 2009 17:30:49 +0100 Subject: [PATCH 31/50] [layout] Snap children of FlowLayout to column/row Use the column and row size to align each child; with :homogeneous set to TRUE, or with children with the same size, the FlowLayout will behave like a reflowing grid. --- clutter/clutter-flow-layout.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index d6d92b9fd..3a70669e5 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -216,9 +216,15 @@ compute_lines (ClutterFlowLayout *self, gint items_per_line; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) - items_per_line = avail_width / (priv->col_width + priv->col_spacing); + { + items_per_line = (avail_width - priv->col_spacing) + / (priv->col_width + priv->col_spacing); + } else - items_per_line = avail_height / (priv->row_height + priv->row_spacing); + { + items_per_line = (avail_height - priv->row_spacing) + / (priv->row_height + priv->row_spacing); + } return items_per_line; } @@ -305,9 +311,9 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, clutter_actor_allocate (child, &child_alloc, flags); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) - item_x += (item_width + priv->col_spacing); + item_x += (priv->col_width + priv->col_spacing); else - item_y += (item_height + priv->row_spacing); + item_y += (priv->row_height + priv->row_spacing); line_items_count += 1; } From b1bae4d66afc8420209f72dabcbf2fe81b461ff3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 11:08:51 +0100 Subject: [PATCH 32/50] [layout] Clean up and document FlowLayout --- clutter/clutter-flow-layout.c | 177 ++++++++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 9 deletions(-) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index 3a70669e5..44e14f418 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -69,12 +69,6 @@ struct _ClutterFlowLayoutPrivate gfloat max_row_height; gfloat row_height; - /* cache the preferred size; this way, if we get what we asked - * or more then we don't have to recompute the layout - */ - gfloat request_width; - gfloat request_height; - gint max_row_items; guint layout_wrap : 1; @@ -139,9 +133,12 @@ clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager, g_list_free (children); - priv->request_width = row_natural_width; priv->col_width = max_child_natural_width; + /* if we get a maximum value for the column width we only apply + * it if there isn't a child whose minimum width is bigger than + * the requested maximum width + */ if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width) priv->col_width = MAX (priv->max_col_width, max_child_min_width); @@ -190,9 +187,11 @@ clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager, g_list_free (children); - priv->request_height = col_natural_height; priv->row_height = max_child_natural_height; + /* similarly to max-col-width, we only apply max-row-height if there + * is no child with a bigger minimum height + */ if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height) priv->row_height = MAX (priv->max_row_height, max_child_min_height); @@ -301,7 +300,7 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, CLUTTER_NOTE (LAYOUT, "flow[line:%d, item:%d/%d] = { %.2f, %.2f, %.2f, %.2f }", - line_index, line_items_count, items_per_line, + line_index, line_items_count + 1, items_per_line, item_x, item_y, item_width, item_height); child_alloc.x1 = ceil (item_x); @@ -510,6 +509,11 @@ clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) * the preferred sizes (taking into account spacing) of the * children in the direction of the orientation * + * If a wrapping #ClutterFlowLayout is allocated less than the + * preferred size in the direction of orientation then it will + * not allocate and paint the children falling out of the + * allocation box + * * Since: 1.2 */ pspec = g_param_spec_boolean ("wrap", @@ -662,6 +666,20 @@ clutter_flow_layout_new (ClutterFlowOrientation orientation) NULL); } +/** + * clutter_flow_layout_set_orientation: + * @layout: a #ClutterFlowLayout + * @orientation: the orientation of the layout + * + * Sets the orientation of the flow layout + * + * The orientation controls the direction used to allocate + * the children: either horizontally or vertically. The + * orientation also controls the direction of the wrapping + * in case #ClutterFlowLayout:wrap is set to %TRUE + * + * Since: 1.2 + */ void clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, ClutterFlowOrientation orientation) @@ -685,6 +703,16 @@ clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, } } +/** + * clutter_flow_layout_get_orientation: + * @layout: a #ClutterFlowLayout + * + * Retrieves the orientation of the @layout + * + * Return value: the orientation of the #ClutterFlowLayout + * + * Since: 1.2 + */ ClutterFlowOrientation clutter_flow_layout_get_orientation (ClutterFlowLayout *layout) { @@ -694,6 +722,19 @@ clutter_flow_layout_get_orientation (ClutterFlowLayout *layout) return layout->priv->orientation; } +/** + * clutter_flow_layout_set_wrap: + * @layout: a #ClutterFlowLayout + * @wrap: whether the layout should wrap + * + * Sets whether the @layout should wrap its children when + * allocating them + * + * The direction of the wrapping is controlled by the + * #ClutterFlowLayout:orientation property + * + * Since: 1.2 + */ void clutter_flow_layout_set_wrap (ClutterFlowLayout *layout, gboolean wrap) @@ -717,6 +758,16 @@ clutter_flow_layout_set_wrap (ClutterFlowLayout *layout, } } +/** + * clutter_flow_layout_get_wrap: + * @layout: a #ClutterFlowLayout + * + * Gets whether @layout should wrap + * + * Return value: %TRUE if the #ClutterFlowLayout is wrapping + * + * Since: 1.2 + */ gboolean clutter_flow_layout_get_wrap (ClutterFlowLayout *layout) { @@ -725,6 +776,16 @@ clutter_flow_layout_get_wrap (ClutterFlowLayout *layout) return layout->priv->layout_wrap; } +/** + * clutter_flow_layout_set_homogeneous: + * @layout: a #ClutterFlowLayout + * @homogeneous: whether the layout should be homogeneous or not + * + * Sets whether the @layout should allocate the same space for + * each child + * + * Since: 1.2 + */ void clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout, gboolean homogeneous) @@ -748,6 +809,16 @@ clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout, } } +/** + * clutter_flow_layout_get_homogeneous: + * @layout: a #ClutterFlowLayout + * + * Retrieves whether the @layout is homogeneous + * + * Return value: %TRUE if the #ClutterFlowLayout is homogeneous + * + * Since: 1.2 + */ gboolean clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout) { @@ -756,6 +827,15 @@ clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout) return layout->priv->is_homogeneous; } +/** + * clutter_flow_layout_set_column_spacing: + * @layout: a #ClutterFlowLayout + * @spacing: the space between columns + * + * Sets the space between columns, in pixels + * + * Since: 1.2 + */ void clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, gfloat spacing) @@ -779,6 +859,17 @@ clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, } } +/** + * clutter_flow_layout_get_column_spacing: + * @layout: a #ClutterFlowLayout + * + * Retrieves the spacing between columns + * + * Return value: the spacing between columns of the #ClutterFlowLayout, + * in pixels + * + * Since: 1.2 + */ gfloat clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout) { @@ -787,6 +878,15 @@ clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout) return layout->priv->col_spacing; } +/** + * clutter_flow_layout_set_row_spacing: + * @layout: a #ClutterFlowLayout + * @spacing: the space between rows + * + * Sets the spacing between rows, in pixels + * + * Since: 1.2 + */ void clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout, gfloat spacing) @@ -810,6 +910,17 @@ clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout, } } +/** + * clutter_flow_layout_get_row_spacing: + * @layout: a #ClutterFlowLayout + * + * Retrieves the spacing between rows + * + * Return value: the spacing between rows of the #ClutterFlowLayout, + * in pixels + * + * Since: 1.2 + */ gfloat clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout) { @@ -818,6 +929,16 @@ clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout) return layout->priv->row_spacing; } +/** + * clutter_flow_layout_set_column_width: + * @layout: a #ClutterFlowLayout + * @min_width: minimum width of a column + * @max_width: maximum width of a column + * + * Sets the minimum and maximum widths that a column can have + * + * Since: 1.2 + */ void clutter_flow_layout_set_column_width (ClutterFlowLayout *layout, gfloat min_width, @@ -844,6 +965,8 @@ clutter_flow_layout_set_column_width (ClutterFlowLayout *layout, notify_max = TRUE; } + g_object_freeze_notify (G_OBJECT (layout)); + if (notify_min || notify_max) { ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout); @@ -856,8 +979,20 @@ clutter_flow_layout_set_column_width (ClutterFlowLayout *layout, if (notify_max) g_object_notify (G_OBJECT (layout), "max-column-width"); + + g_object_thaw_notify (G_OBJECT (layout)); } +/** + * clutter_flow_layout_get_column_width: + * @layout: a #ClutterFlowLayout + * @min_width: (out): return location for the minimum column width, or %NULL + * @max_width: (out): return location for the maximum column width, or %NULL + * + * Retrieves the minimum and maximum column widths + * + * Since: 1.2 + */ void clutter_flow_layout_get_column_width (ClutterFlowLayout *layout, gfloat *min_width, @@ -872,6 +1007,16 @@ clutter_flow_layout_get_column_width (ClutterFlowLayout *layout, *max_width = layout->priv->max_col_width; } +/** + * clutter_flow_layout_set_row_height: + * @layout: a #ClutterFlowLayout + * @min_height: the minimum height of a row + * @max_height: the maximum height of a row + * + * Sets the minimum and maximum heights that a row can have + * + * Since: 1.2 + */ void clutter_flow_layout_set_row_height (ClutterFlowLayout *layout, gfloat min_height, @@ -898,6 +1043,8 @@ clutter_flow_layout_set_row_height (ClutterFlowLayout *layout, notify_max = TRUE; } + g_object_freeze_notify (G_OBJECT (layout)); + if (notify_min || notify_max) { ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout); @@ -910,8 +1057,20 @@ clutter_flow_layout_set_row_height (ClutterFlowLayout *layout, if (notify_max) g_object_notify (G_OBJECT (layout), "max-row-height"); + + g_object_thaw_notify (G_OBJECT (layout)); } +/** + * clutter_flow_layout_get_row_height: + * @layout: a #ClutterFlowLayout + * @min_height: (out): return location for the minimum row height, or %NULL + * @max_height: (out): return location for the maximum row height, or %NULL + * + * Retrieves the minimum and maximum row heights + * + * Since: 1.2 + */ void clutter_flow_layout_get_row_height (ClutterFlowLayout *layout, gfloat *min_height, From 4ea57bc685abeea9273b2b8427a9199980cc2f40 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 11:39:18 +0100 Subject: [PATCH 33/50] [layout] Skip invisible children in FlowLayout Skip hidden actors when computing the preferred size and when allocating. --- clutter/clutter-flow-layout.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index 44e14f418..289e6f840 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -118,6 +118,9 @@ clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterActor *child = l->data; gfloat child_min, child_natural; + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + clutter_actor_get_preferred_width (child, for_height, &child_min, &child_natural); @@ -172,6 +175,9 @@ clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager, ClutterActor *child = l->data; gfloat child_min, child_natural; + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + clutter_actor_get_preferred_height (child, for_width, &child_min, &child_natural); @@ -263,6 +269,9 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, gfloat item_width, item_height; gfloat child_min, child_natural; + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + if (line_items_count == items_per_line) { if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) From 6d954ec0742bad823c8c474873a21ab990a56c7f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 11:42:09 +0100 Subject: [PATCH 34/50] [layout] Rename BinLayout and FlowLayout interactive tests The BinLayout and FlowLayout interactive tests should be named more explicitly. --- .gitignore | 4 ++-- tests/interactive/Makefile.am | 4 ++-- tests/interactive/{test-box.c => test-bin-layout.c} | 2 +- tests/interactive/{test-flow.c => test-flow-layout.c} | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename tests/interactive/{test-box.c => test-bin-layout.c} (99%) rename tests/interactive/{test-flow.c => test-flow-layout.c} (98%) diff --git a/.gitignore b/.gitignore index 48bf770b7..2ebadd2a8 100644 --- a/.gitignore +++ b/.gitignore @@ -133,8 +133,8 @@ TAGS /tests/interactive/redhand_alpha.png /tests/interactive/test-script.json /tests/interactive/test-clutter-cairo-flowers -/tests/interactive/test-box -/tests/interactive/test-flow +/tests/interactive/test-bin-layout +/tests/interactive/test-flow-layout /tests/conform/stamp-test-conformance /tests/conform/test-anchors /tests/conform/test-conformance diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 193e1130f..a0af9d1bf 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -44,8 +44,8 @@ UNIT_TESTS = \ test-text-field.c \ test-clutter-cairo-flowers.c \ test-cogl-vertex-buffer.c \ - test-box.c \ - test-flow.c + test-bin-layout.c \ + test-flow-layout.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-box.c b/tests/interactive/test-bin-layout.c similarity index 99% rename from tests/interactive/test-box.c rename to tests/interactive/test-bin-layout.c index 777c8e28b..8fcc0515c 100644 --- a/tests/interactive/test-box.c +++ b/tests/interactive/test-bin-layout.c @@ -91,7 +91,7 @@ on_box_leave (ClutterActor *box, } G_MODULE_EXPORT int -test_box_main (int argc, char *argv[]) +test_bin_layout_main (int argc, char *argv[]) { ClutterActor *stage, *box, *rect; ClutterLayoutManager *layout; diff --git a/tests/interactive/test-flow.c b/tests/interactive/test-flow-layout.c similarity index 98% rename from tests/interactive/test-flow.c rename to tests/interactive/test-flow-layout.c index 00348e7d7..ccca5040f 100644 --- a/tests/interactive/test-flow.c +++ b/tests/interactive/test-flow-layout.c @@ -60,7 +60,7 @@ static GOptionEntry entries[] = { }; G_MODULE_EXPORT int -test_flow_main (int argc, char *argv[]) +test_flow_layout_main (int argc, char *argv[]) { ClutterActor *stage, *box; ClutterLayoutManager *layout; From 19317520b57461b505751411cf9d9f39bbf12ad5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 12:35:39 +0100 Subject: [PATCH 35/50] [layout] Remove FlowLayout:wrap The :wrap property is not implemented, and mostly useless: the FlowLayout is a reflowing grid. This means that if it receives less than the preferred width or height in the flow direction then it should always reflow. --- clutter/clutter-flow-layout.c | 107 +++------------------------------- clutter/clutter-flow-layout.h | 3 - 2 files changed, 8 insertions(+), 102 deletions(-) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index 289e6f840..960585016 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -71,7 +71,6 @@ struct _ClutterFlowLayoutPrivate gint max_row_items; - guint layout_wrap : 1; guint is_homogeneous : 1; }; @@ -89,9 +88,7 @@ enum PROP_MIN_COLUMN_WIDTH, PROP_MAX_COLUMN_WIDTH, PROP_MIN_ROW_HEGHT, - PROP_MAX_ROW_HEIGHT, - - PROP_WRAP + PROP_MAX_ROW_HEIGHT }; G_DEFINE_TYPE (ClutterFlowLayout, @@ -352,10 +349,6 @@ clutter_flow_layout_set_property (GObject *gobject, clutter_flow_layout_set_orientation (self, g_value_get_enum (value)); break; - case PROP_WRAP: - clutter_flow_layout_set_wrap (self, g_value_get_boolean (value)); - break; - case PROP_HOMOGENEOUS: clutter_flow_layout_set_homogeneous (self, g_value_get_boolean (value)); break; @@ -412,10 +405,6 @@ clutter_flow_layout_get_property (GObject *gobject, g_value_set_enum (value, priv->orientation); break; - case PROP_WRAP: - g_value_set_boolean (value, priv->layout_wrap); - break; - case PROP_HOMOGENEOUS: g_value_set_boolean (value, priv->is_homogeneous); break; @@ -477,9 +466,8 @@ clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) * * The orientation of the #ClutterFlowLayout. The children * of the layout will be layed out following the orientation. - * If #ClutterFlowLayout:wrap is set to %TRUE then this property - * will control the primary direction of the layout before - * wrapping takes place + * + * This property also controls the overflowing directions * * Since: 1.2 */ @@ -508,36 +496,12 @@ clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_HOMOGENEOUS, pspec); - /** - * ClutterFlowLayout:wrap: - * - * Whether the layout should wrap the children to fit them - * in the allocation. A non-wrapping layout has a preferred - * size of the biggest child in the direction opposite to the - * #ClutterFlowLayout:orientation property, and the sum of - * the preferred sizes (taking into account spacing) of the - * children in the direction of the orientation - * - * If a wrapping #ClutterFlowLayout is allocated less than the - * preferred size in the direction of orientation then it will - * not allocate and paint the children falling out of the - * allocation box - * - * Since: 1.2 - */ - pspec = g_param_spec_boolean ("wrap", - "Wrap", - "Whether the layout should wrap", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_WRAP, pspec); - /** * ClutterFlowLayout:column-spacing: * * The spacing between columns, in pixels; the value of this - * property is honoured by horizontal non-wrapping layouts and - * by vertical wrapping layouts + * property is honoured by horizontal non-overflowing layouts + * and by vertical overflowing layouts * * Since: 1.2 */ @@ -555,8 +519,8 @@ clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) * ClutterFlowLayout:row-spacing: * * The spacing between rows, in pixels; the value of this - * property is honoured by vertical non-wrapping layouts and - * by horizontal wrapping layouts + * property is honoured by vertical non-overflowing layouts and + * by horizontal overflowing layouts * * Since: 1.2 */ @@ -684,8 +648,7 @@ clutter_flow_layout_new (ClutterFlowOrientation orientation) * * The orientation controls the direction used to allocate * the children: either horizontally or vertically. The - * orientation also controls the direction of the wrapping - * in case #ClutterFlowLayout:wrap is set to %TRUE + * orientation also controls the direction of the overflowing * * Since: 1.2 */ @@ -731,60 +694,6 @@ clutter_flow_layout_get_orientation (ClutterFlowLayout *layout) return layout->priv->orientation; } -/** - * clutter_flow_layout_set_wrap: - * @layout: a #ClutterFlowLayout - * @wrap: whether the layout should wrap - * - * Sets whether the @layout should wrap its children when - * allocating them - * - * The direction of the wrapping is controlled by the - * #ClutterFlowLayout:orientation property - * - * Since: 1.2 - */ -void -clutter_flow_layout_set_wrap (ClutterFlowLayout *layout, - gboolean wrap) -{ - ClutterFlowLayoutPrivate *priv; - - g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); - - priv = layout->priv; - - if (priv->layout_wrap != wrap) - { - ClutterLayoutManager *manager; - - priv->layout_wrap = wrap; - - manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_layout_changed (manager); - - g_object_notify (G_OBJECT (layout), "wrap"); - } -} - -/** - * clutter_flow_layout_get_wrap: - * @layout: a #ClutterFlowLayout - * - * Gets whether @layout should wrap - * - * Return value: %TRUE if the #ClutterFlowLayout is wrapping - * - * Since: 1.2 - */ -gboolean -clutter_flow_layout_get_wrap (ClutterFlowLayout *layout) -{ - g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), FALSE); - - return layout->priv->layout_wrap; -} - /** * clutter_flow_layout_set_homogeneous: * @layout: a #ClutterFlowLayout diff --git a/clutter/clutter-flow-layout.h b/clutter/clutter-flow-layout.h index 530b2f134..5cbbcd359 100644 --- a/clutter/clutter-flow-layout.h +++ b/clutter/clutter-flow-layout.h @@ -98,9 +98,6 @@ ClutterLayoutManager * clutter_flow_layout_new (ClutterFlowOrient void clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, ClutterFlowOrientation orientation); ClutterFlowOrientation clutter_flow_layout_get_orientation (ClutterFlowLayout *layout); -void clutter_flow_layout_set_wrap (ClutterFlowLayout *layout, - gboolean wrap); -gboolean clutter_flow_layout_get_wrap (ClutterFlowLayout *layout); void clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout, gboolean homogeneous); gboolean clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout); From b5895335ab2d6975665426693fb1b96d7ca86f74 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 15:15:02 +0100 Subject: [PATCH 36/50] actor: Add set_request_mode() method We should not require the use g_object_set()/_get() for accessing the :request-mode property. A proper accessors pair should be preferred. --- clutter/clutter-actor.c | 48 ++++++++++++++++++---- clutter/clutter-actor.h | 3 ++ doc/reference/clutter/clutter-sections.txt | 2 + 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 652365a80..6d75c3c5e 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -473,8 +473,6 @@ static void clutter_actor_set_natural_width_set (ClutterActor *self, gboolean use_natural_width); static void clutter_actor_set_natural_height_set (ClutterActor *self, gboolean use_natural_height); -static void clutter_actor_set_request_mode (ClutterActor *self, - ClutterRequestMode mode); static void clutter_actor_update_map_state (ClutterActor *self, MapStateChange change); static void clutter_actor_unrealize_not_hiding (ClutterActor *self); @@ -3297,7 +3295,7 @@ clutter_actor_class_init (ClutterActorClass *klass) * gfloat natural_width, min_width; * gfloat natural_height, min_height; * - * g_object_get (G_OBJECT (child), "request-mode", &mode, NULL); + * mode = clutter_actor_get_request_mode (child); * if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) * { * clutter_actor_get_preferred_width (child, -1, @@ -5184,11 +5182,28 @@ clutter_actor_set_natural_height_set (ClutterActor *self, clutter_actor_queue_relayout (self); } -static void -clutter_actor_set_request_mode (ClutterActor *self, - ClutterRequestMode mode) +/** + * clutter_actor_set_request_mode: + * @self: a #ClutterActor + * @mode: the request mode + * + * Sets the geometry request mode of @self. + * + * The @mode determines the order for invoking + * clutter_actor_get_preferred_width() and + * clutter_actor_get_preferred_height() + * + * Since: 1.2 + */ +void +clutter_actor_set_request_mode (ClutterActor *self, + ClutterRequestMode mode) { - ClutterActorPrivate *priv = self->priv; + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + priv = self->priv; if (priv->request_mode == mode) return; @@ -5203,6 +5218,25 @@ clutter_actor_set_request_mode (ClutterActor *self, clutter_actor_queue_relayout (self); } +/** + * clutter_actor_get_request_mode: + * @self: a #ClutterActor + * + * Retrieves the geometry request mode of @self + * + * Return value: the request mode for the actor + * + * Since: 1.2 + */ +ClutterRequestMode +clutter_actor_get_request_mode (ClutterActor *self) +{ + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), + CLUTTER_REQUEST_HEIGHT_FOR_WIDTH); + + return self->priv->request_mode; +} + /* variant of set_width() without checks and without notification * freeze+thaw, for internal usage only */ diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index d3e28a91b..4a5a38319 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -301,6 +301,9 @@ void clutter_actor_queue_relayout (ClutterActor void clutter_actor_destroy (ClutterActor *self); /* size negotiation */ +void clutter_actor_set_request_mode (ClutterActor *self, + ClutterRequestMode mode); +ClutterRequestMode clutter_actor_get_request_mode (ClutterActor *self); void clutter_actor_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 68c3f750f..cf95aa386 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -305,6 +305,8 @@ clutter_actor_get_preferred_width clutter_actor_get_preferred_height clutter_actor_set_fixed_position_set clutter_actor_get_fixed_position_set +clutter_actor_set_request_mode +clutter_actor_get_request_mode <SUBSECTION> clutter_actor_set_geometry From eb40e856e1a687c27d047ee6069b6ec84e1e1237 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 15:28:01 +0100 Subject: [PATCH 37/50] layout: Change the request-mode along with the orientation When changing the orientation of a FlowLayout, the associated container should also change its request mode. A horizontally flowing layout has a height depending on the width, since it will reflow vertically; similarly, a vertically reflowing layout will have a width depending on the height. --- clutter/clutter-flow-layout.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index 960585016..027221f1b 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -69,8 +69,6 @@ struct _ClutterFlowLayoutPrivate gfloat max_row_height; gfloat row_height; - gint max_row_items; - guint is_homogeneous : 1; }; @@ -333,6 +331,20 @@ clutter_flow_layout_set_container (ClutterLayoutManager *manager, ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->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->orientation == CLUTTER_FLOW_HORIZONTAL) + ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH + : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT; + clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), + request_mode); + } } static void @@ -668,6 +680,20 @@ clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, priv->orientation = orientation; + if (priv->container != NULL) + { + ClutterRequestMode request_mode; + + /* we need to change the :request-mode of the container + * to match the orientation + */ + request_mode = (priv->orientation == CLUTTER_FLOW_HORIZONTAL) + ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH + : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT; + clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), + request_mode); + } + manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); From 0876575a9549acd54fbc3c70b4e7178e9e4e1e65 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 15:29:47 +0100 Subject: [PATCH 38/50] layout: Use the get_request_mode() getter in BinLayout Instead of using g_object_get(child, "request-mode", ...). --- clutter/clutter-bin-layout.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index 7c1c55ff1..61484caec 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -447,8 +447,7 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, continue; } - request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH; - g_object_get (G_OBJECT (child), "request-mode", &request, NULL); + request = clutter_actor_get_request_mode (child); if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { gfloat min_width, nat_width; From 6f19666b13ac9bde44996620df714484dbe4bd67 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Wed, 7 Oct 2009 15:30:29 +0100 Subject: [PATCH 39/50] layout: Resizing the stage resizes the FlowLayout box Add some user interaction to verify the dynamic reflowing. --- tests/interactive/test-flow-layout.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/interactive/test-flow-layout.c b/tests/interactive/test-flow-layout.c index ccca5040f..51fab1da3 100644 --- a/tests/interactive/test-flow-layout.c +++ b/tests/interactive/test-flow-layout.c @@ -59,6 +59,22 @@ static GOptionEntry entries[] = { { NULL } }; +static void +on_stage_resize (ClutterActor *stage, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags, + ClutterActor *box) +{ + gfloat width, height; + + clutter_actor_box_get_size (allocation, &width, &height); + + if (vertical) + clutter_actor_set_height (box, height); + else + clutter_actor_set_width (box, width); +} + G_MODULE_EXPORT int test_flow_layout_main (int argc, char *argv[]) { @@ -85,6 +101,7 @@ test_flow_layout_main (int argc, char *argv[]) stage = clutter_stage_get_default (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Flow Layout"); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE); clutter_actor_set_size (stage, 640, 480); layout = clutter_flow_layout_new (vertical ? CLUTTER_FLOW_VERTICAL @@ -140,6 +157,10 @@ test_flow_layout_main (int argc, char *argv[]) g_free (name); } + g_signal_connect (stage, + "allocation-changed", G_CALLBACK (on_stage_resize), + box); + clutter_actor_show_all (stage); clutter_main (); From c4b2d4ce797b5ac4c61865d4249badcfb85657f0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Thu, 8 Oct 2009 15:45:29 +0100 Subject: [PATCH 40/50] layout: Report the correct size of FlowLayout FlowLayout should compute the correct height for the assigned width when in horizontal flow, and the correct width for the assigned height when in vertical flow. This means pre-computing the number of lines inside the get_preferred_width() and get_preferred_height(). We can then cache the computed column width and row height, cache them inside the layout and then use them when allocating the children. --- clutter/clutter-flow-layout.c | 463 +++++++++++++++++++++------ tests/interactive/test-flow-layout.c | 4 +- 2 files changed, 372 insertions(+), 95 deletions(-) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index 027221f1b..bec7ab068 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -69,6 +69,12 @@ struct _ClutterFlowLayoutPrivate gfloat max_row_height; gfloat row_height; + /* per-line size */ + GArray *line_min; + GArray *line_natural; + + guint line_count; + guint is_homogeneous : 1; }; @@ -93,6 +99,63 @@ G_DEFINE_TYPE (ClutterFlowLayout, clutter_flow_layout, CLUTTER_TYPE_LAYOUT_MANAGER); +static gint +get_columns (ClutterFlowLayout *self, + gfloat for_width) +{ + ClutterFlowLayoutPrivate *priv = self->priv; + gint n_columns; + + if (for_width < 0) + return 1; + + if (priv->col_width == 0) + return 1; + + n_columns = (gint) (for_width + priv->col_spacing) + / (priv->col_width + priv->col_spacing); + + if (n_columns == 0) + return 1; + + return n_columns; +} + +static gint +get_rows (ClutterFlowLayout *self, + gfloat for_height) +{ + ClutterFlowLayoutPrivate *priv = self->priv; + gint n_rows; + + if (for_height < 0) + return 1; + + if (priv->row_height == 0) + return 1; + + n_rows = (gint) (for_height + priv->row_spacing) + / (priv->row_height + priv->row_spacing); + + if (n_rows == 0) + return 1; + + return n_rows; +} + +static gint +compute_lines (ClutterFlowLayout *self, + gfloat avail_width, + gfloat avail_height) +{ + ClutterFlowLayoutPrivate *priv = self->priv; + + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) + return get_columns (self, avail_width); + else + return get_rows (self, avail_height); +} + static void clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, @@ -102,52 +165,141 @@ clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager, { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; GList *l, *children = clutter_container_get_children (container); - gfloat max_child_min_width, max_child_natural_width; - gfloat row_natural_width; + gint n_rows, line_item_count, line_count; + gfloat total_min_width, total_natural_width; + gfloat line_min_width, line_natural_width; + gfloat max_min_width, max_natural_width; + gfloat item_y; - max_child_min_width = max_child_natural_width = 0; - row_natural_width = 0; + n_rows = get_rows (CLUTTER_FLOW_LAYOUT (manager), for_height); + + total_min_width = 0; + total_natural_width = 0; + + line_min_width = 0; + line_natural_width = 0; + + line_item_count = 0; + line_count = 0; + + item_y = 0; + + /* clear the line width arrays */ + if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0) + { + if (priv->line_min != NULL) + g_array_free (priv->line_min, TRUE); + + if (priv->line_natural != NULL) + g_array_free (priv->line_natural, TRUE); + + priv->line_min = g_array_sized_new (FALSE, FALSE, + sizeof (gfloat), + 16); + priv->line_natural = g_array_sized_new (FALSE, FALSE, + sizeof (gfloat), + 16); + } for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat child_min, child_natural; + gfloat new_y, item_height; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; - clutter_actor_get_preferred_width (child, for_height, - &child_min, - &child_natural); + if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0) + { + if (line_item_count == n_rows) + { + total_min_width += line_min_width; + total_natural_width += line_natural_width; - max_child_min_width = MAX (max_child_min_width, child_min); - max_child_natural_width = MAX (max_child_natural_width, child_natural); + g_array_append_val (priv->line_min, + line_min_width); + g_array_append_val (priv->line_natural, + line_natural_width); - if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) - row_natural_width += (child_natural + priv->col_spacing); + line_min_width = line_natural_width = 0; + + line_item_count = 0; + line_count += 1; + item_y = 0; + } + + new_y = ((line_item_count + 1) * (for_height + priv->row_spacing)) + / n_rows; + item_height = new_y - item_y - priv->row_spacing; + + clutter_actor_get_preferred_width (child, item_height, + &child_min, + &child_natural); + + line_min_width = MAX (line_min_width, child_min); + line_natural_width = MAX (line_natural_width, child_natural); + + item_y = new_y; + line_item_count += 1; + + max_min_width = MAX (max_min_width, line_min_width); + max_natural_width = MAX (max_natural_width, line_natural_width); + } else - row_natural_width = MAX (row_natural_width, max_child_natural_width); + { + clutter_actor_get_preferred_width (child, for_height, + &child_min, + &child_natural); + + max_min_width = MAX (max_min_width, child_min); + max_natural_width = MAX (max_natural_width, child_natural); + + line_count += 1; + } } g_list_free (children); - priv->col_width = max_child_natural_width; + priv->col_width = max_natural_width; - /* if we get a maximum value for the column width we only apply - * it if there isn't a child whose minimum width is bigger than - * the requested maximum width - */ if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width) - priv->col_width = MAX (priv->max_col_width, max_child_min_width); + priv->col_width = MAX (priv->max_col_width, max_min_width); if (priv->col_width < priv->min_col_width) priv->col_width = priv->min_col_width; + if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0) + { + /* if we have a non-full row we need to add it */ + if (line_item_count > 0) + { + total_min_width += line_min_width; + total_natural_width += line_natural_width; + + g_array_append_val (priv->line_min, + line_min_width); + g_array_append_val (priv->line_natural, + line_natural_width); + } + + priv->line_count = line_count; + if (priv->line_count > 0) + { + gfloat total_spacing; + + total_spacing = priv->col_spacing * (priv->line_count - 1); + + total_min_width += total_spacing; + total_natural_width += total_spacing; + } + } + if (min_width_p) - *min_width_p = ceilf (max_child_min_width); + *min_width_p = total_min_width; if (nat_width_p) - *nat_width_p = ceilf (row_natural_width); + *nat_width_p = total_natural_width; } static void @@ -159,74 +311,141 @@ clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager, { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; GList *l, *children = clutter_container_get_children (container); - gfloat max_child_min_height, max_child_natural_height; - gfloat col_natural_height; + gint n_columns, line_item_count, line_count; + gfloat total_min_height, total_natural_height; + gfloat line_min_height, line_natural_height; + gfloat max_min_height, max_natural_height; + gfloat item_x; - max_child_min_height = max_child_natural_height = 0; - col_natural_height = 0; + n_columns = get_columns (CLUTTER_FLOW_LAYOUT (manager), for_width); + + total_min_height = 0; + total_natural_height = 0; + + line_min_height = 0; + line_natural_height = 0; + + line_item_count = 0; + line_count = 0; + + item_x = 0; + + /* clear the line height arrays */ + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0) + { + if (priv->line_min != NULL) + g_array_free (priv->line_min, TRUE); + + if (priv->line_natural != NULL) + g_array_free (priv->line_natural, TRUE); + + priv->line_min = g_array_sized_new (FALSE, FALSE, + sizeof (gfloat), + 16); + priv->line_natural = g_array_sized_new (FALSE, FALSE, + sizeof (gfloat), + 16); + } for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat child_min, child_natural; + gfloat new_x, item_width; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; - clutter_actor_get_preferred_height (child, for_width, - &child_min, - &child_natural); + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0) + { + if (line_item_count == n_columns) + { + total_min_height += line_min_height; + total_natural_height += line_natural_height; - max_child_min_height = MAX (max_child_min_height, child_min); - max_child_natural_height = MAX (max_child_natural_height, child_natural); + g_array_append_val (priv->line_min, + line_min_height); + g_array_append_val (priv->line_natural, + line_natural_height); - if (priv->orientation == CLUTTER_FLOW_VERTICAL) - col_natural_height += (child_natural + priv->row_spacing); + line_min_height = line_natural_height = 0; + + line_item_count = 0; + line_count += 1; + item_x = 0; + } + + new_x = ((line_item_count + 1) * (for_width + priv->col_spacing)) + / n_columns; + item_width = new_x - item_x - priv->col_spacing; + + clutter_actor_get_preferred_height (child, item_width, + &child_min, + &child_natural); + + line_min_height = MAX (line_min_height, child_min); + line_natural_height = MAX (line_natural_height, child_natural); + + item_x = new_x; + line_item_count += 1; + + max_min_height = MAX (max_min_height, line_min_height); + max_natural_height = MAX (max_natural_height, line_natural_height); + } else - col_natural_height = MAX (col_natural_height, max_child_natural_height); + { + clutter_actor_get_preferred_height (child, for_width, + &child_min, + &child_natural); + + max_min_height = MAX (max_min_height, child_min); + max_natural_height = MAX (max_natural_height, child_natural); + + line_count += 1; + } } g_list_free (children); - priv->row_height = max_child_natural_height; + priv->row_height = max_natural_height; - /* similarly to max-col-width, we only apply max-row-height if there - * is no child with a bigger minimum height - */ if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height) - priv->row_height = MAX (priv->max_row_height, max_child_min_height); + priv->row_height = MAX (priv->max_row_height, max_min_height); if (priv->row_height < priv->min_row_height) priv->row_height = priv->min_row_height; + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0) + { + /* if we have a non-full row we need to add it */ + if (line_item_count > 0) + { + total_min_height += line_min_height; + total_natural_height += line_natural_height; + + g_array_append_val (priv->line_min, + line_min_height); + g_array_append_val (priv->line_natural, + line_natural_height); + } + + priv->line_count = line_count; + if (priv->line_count > 0) + { + gfloat total_spacing; + + total_spacing = priv->row_spacing * (priv->line_count - 1); + + total_min_height += total_spacing; + total_natural_height += total_spacing; + } + } + if (min_height_p) - *min_height_p = ceilf (max_child_min_height); + *min_height_p = total_min_height; if (nat_height_p) - *nat_height_p = ceilf (col_natural_height); -} - -static gint -compute_lines (ClutterFlowLayout *self, - const GList *children, - gfloat avail_width, - gfloat avail_height) -{ - ClutterFlowLayoutPrivate *priv = self->priv; - gint items_per_line; - - if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) - { - items_per_line = (avail_width - priv->col_spacing) - / (priv->col_width + priv->col_spacing); - } - else - { - items_per_line = (avail_height - priv->row_spacing) - / (priv->row_height + priv->row_spacing); - } - - return items_per_line; + *nat_height_p = total_natural_height; } static void @@ -239,7 +458,7 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, GList *l, *children = clutter_container_get_children (container); gfloat avail_width, avail_height; gfloat item_x, item_y; - gint line_items_count; + gint line_item_count; gint items_per_line; gint line_index; @@ -249,12 +468,11 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, clutter_actor_box_get_size (allocation, &avail_width, &avail_height); items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager), - children, avail_width, avail_height); item_x = item_y = 0; - line_items_count = 0; + line_item_count = 0; line_index = 0; for (l = children; l != NULL; l = l->next) @@ -262,49 +480,88 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, ClutterActor *child = l->data; ClutterActorBox child_alloc; gfloat item_width, item_height; - gfloat child_min, child_natural; + gfloat new_x, new_y; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; - if (line_items_count == items_per_line) + if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) { - if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) + if (line_item_count == items_per_line && line_item_count > 0) { + item_y += g_array_index (priv->line_natural, + gfloat, + line_index); + + line_item_count = 0; + line_index += 1; + item_x = 0; - item_y += priv->row_height + priv->row_spacing; } - else + + new_x = ((line_item_count + 1) * (avail_width + priv->col_spacing)) + / items_per_line; + item_width = new_x - item_x - priv->col_spacing; + item_height = g_array_index (priv->line_natural, + gfloat, + line_index); + + if (!priv->is_homogeneous) { - item_x += priv->col_width + priv->col_spacing; - item_y = 0; + gfloat child_min, child_natural; + + clutter_actor_get_preferred_width (child, item_height, + &child_min, + &child_natural); + item_width = MIN (item_width, child_min); + + clutter_actor_get_preferred_height (child, item_width, + &child_min, + &child_natural); + item_height = MIN (item_height, child_natural); } - - line_items_count = 0; - line_index += 1; - } - - if (priv->is_homogeneous) - { - item_width = priv->col_width; - item_height = priv->row_height; } else { - clutter_actor_get_preferred_width (child, priv->row_height, - &child_min, - &child_natural); - item_width = MIN (child_natural, priv->col_width); + if (line_item_count == items_per_line && line_item_count > 0) + { + item_x += g_array_index (priv->line_natural, + gfloat, + line_index); - clutter_actor_get_preferred_height (child, item_width, - &child_min, - &child_natural); - item_height = MIN (child_natural, priv->row_height); + line_item_count = 0; + line_index += 1; + + item_y = 0; + } + + new_y = ((line_item_count + 1) * (avail_height + priv->row_spacing)) + / items_per_line; + item_height = new_y - item_y - priv->row_spacing; + item_width = g_array_index (priv->line_natural, + gfloat, + line_index); + + if (!priv->is_homogeneous) + { + gfloat child_min, child_natural; + + clutter_actor_get_preferred_width (child, item_height, + &child_min, + &child_natural); + item_width = MIN (item_width, child_min); + + clutter_actor_get_preferred_height (child, item_width, + &child_min, + &child_natural); + item_height = MIN (item_height, child_natural); + } } CLUTTER_NOTE (LAYOUT, - "flow[line:%d, item:%d/%d] = { %.2f, %.2f, %.2f, %.2f }", - line_index, line_items_count + 1, items_per_line, + "flow[line:%d, item:%d/%d] =" + "{ %.2f, %.2f, %.2f, %.2f }", + line_index, line_item_count + 1, items_per_line, item_x, item_y, item_width, item_height); child_alloc.x1 = ceil (item_x); @@ -314,11 +571,11 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, clutter_actor_allocate (child, &child_alloc, flags); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) - item_x += (priv->col_width + priv->col_spacing); + item_x = new_x; else - item_y += (priv->row_height + priv->row_spacing); + item_y = new_y; - line_items_count += 1; + line_item_count += 1; } g_list_free (children); @@ -451,6 +708,20 @@ clutter_flow_layout_get_property (GObject *gobject, } } +static void +clutter_flow_layout_finalize (GObject *gobject) +{ + ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (gobject)->priv; + + if (priv->line_min != NULL) + g_array_free (priv->line_min, TRUE); + + if (priv->line_natural != NULL) + g_array_free (priv->line_natural, TRUE); + + G_OBJECT_CLASS (clutter_flow_layout_parent_class)->finalize (gobject); +} + static void clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) { @@ -465,6 +736,7 @@ clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) gobject_class->set_property = clutter_flow_layout_set_property; gobject_class->get_property = clutter_flow_layout_get_property; + gobject_class->finalize = clutter_flow_layout_finalize; layout_class->get_preferred_width = clutter_flow_layout_get_preferred_width; @@ -631,6 +903,9 @@ clutter_flow_layout_init (ClutterFlowLayout *self) priv->min_col_width = priv->min_row_height = 0; priv->max_col_width = priv->max_row_height = -1; + + priv->line_min = NULL; + priv->line_natural = NULL; } /** diff --git a/tests/interactive/test-flow-layout.c b/tests/interactive/test-flow-layout.c index 51fab1da3..f8131ee3e 100644 --- a/tests/interactive/test-flow-layout.c +++ b/tests/interactive/test-flow-layout.c @@ -81,6 +81,7 @@ test_flow_layout_main (int argc, char *argv[]) ClutterActor *stage, *box; ClutterLayoutManager *layout; ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff }; + ClutterColor box_color = { 255, 255, 255, 255 }; GError *error; gint i; @@ -114,6 +115,7 @@ test_flow_layout_main (int argc, char *argv[]) y_spacing); box = clutter_box_new (layout); + clutter_box_set_color (CLUTTER_BOX (box), &box_color); clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); clutter_actor_set_position (box, 0, 0); @@ -126,7 +128,7 @@ test_flow_layout_main (int argc, char *argv[]) for (i = 0; i < n_rects; i++) { - ClutterColor color = { 255, 255, 255, 255 }; + ClutterColor color = { 255, 255, 255, 224 }; ClutterActor *rect; gchar *name; gfloat width, height; From b526b765931bcf4e44feac4ab1b72763eb68f264 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 13 Oct 2009 12:14:05 +0100 Subject: [PATCH 41/50] 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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + * + * Based on the NBTK NbtkBoxLayout actor by: + * Thomas Wood <thomas.wood@intel.com> + */ + +/** + * 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: + * <itemizedlist> + * <listitem><para>all children are arranged on a single + * line;</para></listitem> + * <listitem><para>the axis used is controlled by the + * #ClutterBoxLayout:vertical boolean property;</para></listitem> + * <listitem><para>the order of the packing is determined by the + * #ClutterBoxLayout:pack-start boolean property;</para></listitem> + * <listitem><para>each child will be allocated to its natural + * size or, if set to expand, the available size;</para></listitem> + * <listitem><para>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;</para></listitem> + * <listitem><para>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.</para></listitem> + * </itemizedlist> + * + * 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 <math.h> + +#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 <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + * + * Based on the NBTK NbtkBoxLayout actor by: + * Thomas Wood <thomas.wood@intel.com> + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only <clutter/clutter.h> can be included directly." +#endif + +#ifndef __CLUTTER_BOX_LAYOUT_H__ +#define __CLUTTER_BOX_LAYOUT_H__ + +#include <clutter/clutter-layout-manager.h> + +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 <stdio.h> +#include <stdlib.h> + +#include <gmodule.h> + +#include <clutter/clutter.h> + +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; +} From cd3dce1d5d957046934062780d21558e182ceb1a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 13 Oct 2009 12:15:25 +0100 Subject: [PATCH 42/50] layout: Document BoxLayout Add BoxLayout to the API reference. --- doc/reference/clutter/clutter-docs.xml.in | 1 + doc/reference/clutter/clutter-sections.txt | 38 ++++++++++++++++++++++ doc/reference/clutter/clutter.types | 1 + 3 files changed, 40 insertions(+) diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index 5771a97fc..d00ec1d5c 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -82,6 +82,7 @@ <xi:include href="xml/clutter-fixed-layout.xml"/> <xi:include href="xml/clutter-bin-layout.xml"/> <xi:include href="xml/clutter-flow-layout.xml"/> + <xi:include href="xml/clutter-box-layout.xml"/> </chapter> </part> diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index cf95aa386..74d5be5f9 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1883,3 +1883,41 @@ CLUTTER_FLOW_LAYOUT_GET_CLASS ClutterFlowLayoutPrivate clutter_flow_layout_get_type </SECTION> + +<SECTION> +<TITLE>ClutterBoxLayout</TITLE> +<FILE>clutter-box-layout</FILE> +ClutterBoxAlignment +ClutterBoxLayout +ClutterBoxLayoutClass +clutter_box_layout_new +clutter_box_layout_set_pack_start +clutter_box_layout_get_pack_start +clutter_box_layout_set_spacing +clutter_box_layout_get_spacing +clutter_box_layout_set_vertical +clutter_box_layout_get_vertical + +<SUBSECTION> +clutter_box_layout_pack + +<SUBSECTION> +clutter_box_layout_set_alignment +clutter_box_layout_get_alignment +clutter_box_layout_set_expand +clutter_box_layout_get_expand +clutter_box_layout_set_fill +clutter_box_layout_get_fill + +<SUBSECTION Standard> +CLUTTER_TYPE_BOX_LAYOUT +CLUTTER_BOX_LAYOUT +CLUTTER_BOX_LAYOUT_CLASS +CLUTTER_IS_BOX_LAYOUT +CLUTTER_IS_BOX_LAYOUT_CLASS +CLUTTER_BOX_LAYOUT_GET_CLASS + +<SUBSECTION Private> +ClutterBoxLayoutPrivate +clutter_box_layout_get_type +</SECTION> diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 07164a443..9c62cb757 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -39,3 +39,4 @@ clutter_layout_meta_get_type clutter_fixed_layout_get_type clutter_bin_layout_get_type clutter_flow_layout_get_type +clutter_box_layout_get_type From 9f06f726d409d0ac948179b9771350147c5e699b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@linux.intel.com> Date: Tue, 13 Oct 2009 16:52:57 +0100 Subject: [PATCH 43/50] layout, docs: Remove unused functions The :wrap property and its accessor functions were removed from ClutterFlowLayout. --- doc/reference/clutter/clutter-sections.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 74d5be5f9..0ea236d24 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1859,8 +1859,6 @@ ClutterFlowLayoutClass clutter_flow_layout_new clutter_flow_layout_set_orientation clutter_flow_layout_get_orientation -clutter_flow_layout_set_wrap -clutter_flow_layout_get_wrap <SUBSECTION> clutter_flow_layout_set_column_spacing From adca939101e3a25309c29e6a263c1c7ac7b0d877 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Thu, 15 Oct 2009 12:04:50 +0100 Subject: [PATCH 44/50] layout, box: Write long description for Box Also have an example of how to create a Box with a layout manager and how to use the pack() method. --- clutter/clutter-box.c | 45 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index 7bcd879f6..42ba18ec7 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -2,7 +2,44 @@ * SECTION:clutter-box * @short_description: A Generic layout container * - * #ClutterBox is a FIXME + * #ClutterBox is a #ClutterActor sub-class implementing the #ClutterContainer + * interface. A Box delegates the whole size requisition and size allocation to + * a #ClutterLayoutManager instance. + * + * <example id="example-clutter-box"> + * <title>Using ClutterBox</title> + * <para>The following code shows how to create a #ClutterBox with + * a #ClutterLayoutManager sub-class, and how to add children to + * it via clutter_box_pack().</para> + * <programlisting> + * ClutterActor *box; + * ClutterLayoutManager *layout; + * + * /* Create the layout manager first */ + * layout = clutter_box_layout_new (); + * clutter_box_layout_set_homogeneous (CLUTTER_BOX_LAYOUT (layout), TRUE); + * clutter_box_layout_set_spacing (CLUTTER_BOX_LAYOUT (layout), 12); + * + * /* Then create the ClutterBox actor. The Box will take + * * ownership of the ClutterLayoutManager instance by sinking + * * its floating reference + * */ + * box = clutter_box_new (layout); + * + * /* Now add children to the Box using the variadic arguments + * * function clutter_box_pack() to set layout properties + * */ + * clutter_box_pack (CLUTTER_BOX (box), actor, + * "x-align", CLUTTER_BOX_ALIGNMENT_CENTER, + * "y-align", CLUTTER_BOX_ALIGNMENT_END, + * "expand", TRUE, + * NULL); + * </programlisting> + * </example> + * + * #ClutterBox<!-- -->'s clutter_box_pack() wraps the generic + * clutter_container_add_actor() function, but it also allows setting + * layout properties while adding the new child to the box. * * #ClutterBox is available since Clutter 1.2 */ @@ -237,6 +274,9 @@ clutter_box_real_get_preferred_width (ClutterActor *actor, { ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + /* if we don't have any children don't bother proxying the + * call to the layout manager instance + */ if (priv->children == NULL) { if (min_width) @@ -262,6 +302,9 @@ clutter_box_real_get_preferred_height (ClutterActor *actor, { ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; + /* if we don't have any children don't bother proxying the + * call to the layout manager instance + */ if (priv->children == NULL) { if (min_height) From 4d153e4507fdd8fc135998f771e1826a9b02e658 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Thu, 15 Oct 2009 12:15:49 +0100 Subject: [PATCH 45/50] layout, box: Clean up * Use g_list_foreach() instead of iterating over the list inside the destruction sequence, since we are causing the widgets to be implicitly removed from the list via the destroy() call. * Use g_signal_connect_swapped() and spare us from a callback. --- clutter/clutter-box.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index 42ba18ec7..131327551 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -345,21 +345,13 @@ static void clutter_box_destroy (ClutterActor *actor) { ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv; - GList *l; - for (l = priv->children; l != NULL; l = l->next) - clutter_actor_destroy (l->data); + /* destroy all our children */ + g_list_foreach (priv->children, (GFunc) clutter_actor_destroy, NULL); CLUTTER_ACTOR_CLASS (clutter_box_parent_class)->destroy (actor); } -static void -on_layout_changed (ClutterLayoutManager *manager, - ClutterActor *self) -{ - clutter_actor_queue_relayout (self); -} - static inline void set_layout_manager (ClutterBox *self, ClutterLayoutManager *manager) @@ -388,9 +380,9 @@ set_layout_manager (ClutterBox *self, CLUTTER_CONTAINER (self)); priv->changed_id = - g_signal_connect (priv->manager, "layout-changed", - G_CALLBACK (on_layout_changed), - self); + g_signal_connect_swapped (priv->manager, "layout-changed", + G_CALLBACK (clutter_actor_queue_relayout), + self); } clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); @@ -574,7 +566,7 @@ clutter_box_set_layout_manager (ClutterBox *box, ClutterLayoutManager *manager) { g_return_if_fail (CLUTTER_IS_BOX (box)); - g_return_if_fail (manager == NULL || CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); set_layout_manager (box, manager); } From 308c930f37c7c00eb6791d53fced7d1c2ac9600e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Thu, 15 Oct 2009 14:11:36 +0100 Subject: [PATCH 46/50] layout, docs: Add long description for FlowLayout Add the full description of the layout policy --- clutter/clutter-flow-layout.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index bec7ab068..bc81a679d 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -30,6 +30,22 @@ * policy: * * <itemizedlist> + * <listitem><para>the preferred natural size depends on the value + * of the #ClutterFlowLayout:orientation property; the layout will try + * to maintain all its children on a single row or + * column;</para></listitem> + * <listitem><para>if either the width or the height allocated are + * smaller than the preferred ones, the layout will wrap; in this case, + * the preferred height or width, respectively, will take into account + * the amount of columns and rows;</para></listitem> + * <listitem><para>each line (either column or row) in reflowing will + * have the size of the biggest cell on that line; if the + * #ClutterFlowLayout:homogeneous property is set to %FALSE the actor + * will be allocated within that area, and if set to %TRUE instead the + * actor will be given exactly that area;</para></listitem> + * <listitem><para>the size of the columns or rows can be controlled + * for both minimum and maximum; the spacing can also be controlled + * in both columns and rows.</para></listitem> * </itemizedlist> * * #ClutterFlowLayout is available since Clutter 1.2 From 852abbb138898467fb6497508e99fb1a92203c78 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Thu, 15 Oct 2009 14:12:37 +0100 Subject: [PATCH 47/50] layout, bin: Use ceilf() instead of casting to int Casting a float to int to truncate it before assigning the value to a float again is wrong. We should use ceilf() instead which does what we want to achieve (rounding up the size to avoid sub-pixel positioning of children). --- clutter/clutter-bin-layout.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index 61484caec..acc515b26 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -81,6 +81,8 @@ #include "config.h" #endif +#include <math.h> + #include "clutter-actor.h" #include "clutter-animatable.h" #include "clutter-bin-layout.h" @@ -427,14 +429,14 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL) { - child_alloc.x1 = (int) 0; - child_alloc.x2 = (int) available_w; + child_alloc.x1 = 0; + child_alloc.x2 = ceilf (available_w); } if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL) { - child_alloc.y1 = (int) 0; - child_alloc.y2 = (int) available_h; + child_alloc.y1 = 0; + child_alloc.y2 = ceilf (available_h); } /* if we are filling horizontally and vertically then we @@ -481,8 +483,8 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) { - child_alloc.x1 = (int) clutter_actor_get_x (child); - child_alloc.x2 = (int) child_alloc.x1 + child_width; + child_alloc.x1 = ceilf (clutter_actor_get_x (child)); + child_alloc.x2 = ceilf (child_alloc.x1 + child_width); } else { @@ -490,15 +492,15 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, if (layer->x_align != CLUTTER_BIN_ALIGNMENT_FILL) { - child_alloc.x1 = (int) ((available_w - child_width) * x_align); - child_alloc.x2 = (int) child_alloc.x1 + child_width; + child_alloc.x1 = ceilf ((available_w - child_width) * x_align); + child_alloc.x2 = ceilf (child_alloc.x1 + child_width); } } if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) { - child_alloc.y1 = (int) clutter_actor_get_y (child); - child_alloc.y2 = (int) child_alloc.y1 + child_height; + child_alloc.y1 = ceilf (clutter_actor_get_y (child)); + child_alloc.y2 = ceilf (child_alloc.y1 + child_height); } else { @@ -506,8 +508,8 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, if (layer->y_align != CLUTTER_BIN_ALIGNMENT_FILL) { - child_alloc.y1 = (int) ((available_h - child_height) * y_align); - child_alloc.y2 = (int) child_alloc.y1 + child_height; + child_alloc.y1 = ceilf ((available_h - child_height) * y_align); + child_alloc.y2 = ceilf (child_alloc.y1 + child_height); } } From 87f0b94df7524400b233cd1acca871fa6f8f0a02 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Thu, 15 Oct 2009 14:20:44 +0100 Subject: [PATCH 48/50] layout, docs: Fix description of Bin properties The BinLayer and BinLayout properties name and blurb for introspection should be slightly more descriptive. --- clutter/clutter-bin-layout.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c index acc515b26..80100feda 100644 --- a/clutter/clutter-bin-layout.c +++ b/clutter/clutter-bin-layout.c @@ -241,7 +241,7 @@ clutter_bin_layer_class_init (ClutterBinLayerClass *klass) gobject_class->get_property = clutter_bin_layer_get_property; pspec = g_param_spec_enum ("x-align", - "X Align", + "Horizontal Alignment", "Horizontal alignment for the actor " "inside the layer", CLUTTER_TYPE_BIN_ALIGNMENT, @@ -252,7 +252,7 @@ clutter_bin_layer_class_init (ClutterBinLayerClass *klass) pspec); pspec = g_param_spec_enum ("y-align", - "Y Align", + "Vertical Alignment", "Vertical alignment for the actor " "inside the layer manager", CLUTTER_TYPE_BIN_ALIGNMENT, @@ -619,8 +619,8 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) * Since: 1.2 */ pspec = g_param_spec_enum ("x-align", - "X Align", - "Horizontal alignment for the actors " + "Horizontal Alignment", + "Default horizontal alignment for the actors " "inside the layout manager", CLUTTER_TYPE_BIN_ALIGNMENT, CLUTTER_BIN_ALIGNMENT_CENTER, @@ -636,8 +636,8 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) * Since: 1.2 */ pspec = g_param_spec_enum ("y-align", - "Y Align", - "Vertical alignment for the actors " + "Vertical Alignment", + "Default vertical alignment for the actors " "inside the layout manager", CLUTTER_TYPE_BIN_ALIGNMENT, CLUTTER_BIN_ALIGNMENT_CENTER, From cd3c85f7ba9094a250ec03f0380428fadafef0df Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Mon, 19 Oct 2009 11:00:23 +0100 Subject: [PATCH 49/50] layout, docs: Add more documentation to LayoutManager The layout manager reference should have some documentation on how to use a LayoutManager object inside a container and how to implement a LayoutManager sub-class correctly. --- clutter/clutter-layout-manager.c | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 56b5c5993..b8806a60b 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -38,6 +38,47 @@ * Clutter provides some simple #ClutterLayoutManager sub-classes, like * #ClutterFixedLayout and #ClutterBinLayout. * + * <refsect2 id="ClutterLayoutManager-use-in-Actor"> + * <title>Using ClutterLayoutManager inside an Actor</title> + * <para>In order to use a #ClutterLayoutManager inside a #ClutterActor + * sub-class you should invoke clutter_layout_manager_get_preferred_width() + * inside the <structname>ClutterActor</structname>::get_preferred_width() + * virtual function and clutter_layout_manager_get_preferred_height() + * inside the <structname>ClutterActor</structname>::get_preferred_height() + * virtual function implementations. You should also call + * clutter_layout_manager_allocate() inside the implementation of the + * <structname>ClutterActor</structname>::allocate() virtual + * function.</para> + * <para>In order to receive notifications for changes in the layout + * manager policies you should also connect to the + * #ClutterLayoutManager::layout-changed signal and queue a relayout + * on your actor. The following code should be enough if the actor + * does not need to perform specific operations whenever a layout + * manager changes:</para> + * <informalexample><programlisting> + * g_signal_connect_swapped (layout_manager, + * "layout-changed", + * G_CALLBACK (clutter_actor_queue_relayout), + * actor); + * </programlisting></informalexample> + * </refsect2> + * + * <refsect2 id="ClutterLayoutManager-implementation"> + * <title>Implementing a ClutterLayoutManager</title> + * <para>The implementation of a layout manager does not differ from + * the implementation of the size requisition and allocation bits of + * #ClutterActor, so you should read the relative documentation + * <link linkend="clutter-subclassing-ClutterActor">here</link>.</para> + * <para>The layout manager implementation can hold a back reference + * to the #ClutterContainer by implementing the set_container() + * virtual function. The layout manager should not hold a reference + * on the container actor, to avoid reference cycles.</para> + * <para>If the layout manager has properties affecting the layout + * policies then it should emit the #ClutterLayoutManager::layout-changed + * signal on itself by using the clutter_layout_manager_layout_changed() + * function.</para> + * </refsect2> + * * #ClutterLayoutManager is available since Clutter 1.2 */ From 8ce8b91383390482efe56d982d945a384766ca8c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi <ebassi@gnome.org> Date: Mon, 19 Oct 2009 11:44:29 +0100 Subject: [PATCH 50/50] docs: Reword a link --- clutter/clutter-layout-manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index b8806a60b..899c10305 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -68,7 +68,8 @@ * <para>The implementation of a layout manager does not differ from * the implementation of the size requisition and allocation bits of * #ClutterActor, so you should read the relative documentation - * <link linkend="clutter-subclassing-ClutterActor">here</link>.</para> + * <link linkend="clutter-subclassing-ClutterActor">for subclassing + * ClutterActor</link>.</para> * <para>The layout manager implementation can hold a back reference * to the #ClutterContainer by implementing the set_container() * virtual function. The layout manager should not hold a reference