The following example demonstrates the implementation of a box container that lays its child actors out horizontally. A real container should probably allow optional padding around the container and spacing between the child actors. You might also want to allow some child actors to expand to fill the available space, or align differently inside the container.
File: examplebox.h
#ifndef __EXAMPLE_BOX_H__
#define __EXAMPLE_BOX_H__
#include <clutter/clutter-actor.h>
#include <clutter/clutter-types.h>
G_BEGIN_DECLS
#define EXAMPLE_TYPE_BOX (example_box_get_type ())
#define EXAMPLE_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_BOX, ExampleBox))
#define EXAMPLE_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_BOX))
#define EXAMPLE_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_BOX, ExampleBoxClass))
#define EXAMPLE_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_BOX))
#define EXAMPLE_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_BOX, ExampleBoxClass))
typedef struct _ExampleBoxChild ExampleBoxChild;
typedef struct _ExampleBox ExampleBox;
typedef struct _ExampleBoxClass ExampleBoxClass;
struct _ExampleBox
{
/*< private >*/
ClutterActor parent_instance;
/* List of ExampleBoxChild structures */
GList *children;
};
struct _ExampleBoxClass
{
/*< private >*/
ClutterActorClass parent_class;
};
GType example_box_get_type (void) G_GNUC_CONST;
ClutterActor *example_box_new (void);
void example_box_pack (ExampleBox*box, ClutterActor *actor);
void example_box_remove_all (ExampleBox *box);
G_END_DECLS
#endif /* __EXAMPLE_BOX_H__ */
File: main.c
#include <clutter/clutter.h>
#include "examplebox.h"
#include <stdlib.h>
int main(int argc, char *argv[])
{
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
ClutterColor actor_color = { 0xff, 0xff, 0xff, 0x99 };
ClutterColor actor_color2 = { 0x10, 0x40, 0x90, 0xff };
clutter_init (&argc, &argv);
/* Get the stage and set its size and color: */
ClutterActor *stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 200, 200);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
/* Add our custom container to the stage: */
ClutterActor *box = example_box_new ();
/* Set the size to the preferred size of the container: */
clutter_actor_set_size (box, -1, -1);
clutter_actor_set_position (box, 20, 20);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
clutter_actor_show (box);
/* Add some actors to our container: */
ClutterActor *actor = clutter_rectangle_new_with_color (&actor_color);
clutter_actor_set_size (actor, 75, 75);
clutter_container_add_actor (CLUTTER_CONTAINER (box), actor);
clutter_actor_show (actor);
ClutterActor *actor2 = clutter_rectangle_new_with_color (&actor_color2);
clutter_actor_set_size (actor2, 75, 75);
clutter_container_add_actor (CLUTTER_CONTAINER (box), actor2);
clutter_actor_show (actor2);
/* Show the stage: */
clutter_actor_show (stage);
/* Start the main loop, so we can respond to events: */
clutter_main ();
return EXIT_SUCCESS;
}
File: examplebox.c
#include "examplebox.h"
#include <clutter/clutter-container.h>
#include <cogl/cogl.h>
#include <string.h>
/**
* SECTION:example-box
* @short_description: Simple example of a container actor.
*
* #ExampleBox imposes a specific layout on its children,
* unlike #ClutterGroup which is a free-form container.
*
* Specifically, ExampleBox lays out its children along an imaginary
* horizontal line.
*/
static void clutter_container_iface_init (ClutterContainerIface *iface);
G_DEFINE_TYPE_WITH_CODE (ExampleBox,
example_box,
CLUTTER_TYPE_ACTOR,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
clutter_container_iface_init));
/* An implementation for the ClutterContainer::add() vfunc: */
static void
example_box_add (ClutterContainer *container,
ClutterActor *actor)
{
example_box_pack (EXAMPLE_BOX (container), actor);
}
/* An implementation for the ClutterContainer::remove() vfunc: */
static void
example_box_remove (ClutterContainer *container,
ClutterActor *actor)
{
ExampleBox *box = EXAMPLE_BOX (container);
GList *l;
g_object_ref (actor);
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
if (child == actor)
{
clutter_actor_unparent (child);
box->children = g_list_remove_link (box->children, l);
g_list_free (l);
g_signal_emit_by_name (container, "actor-removed", actor);
/* queue a relayout of the container */
clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
break;
}
}
g_object_unref (actor);
}
/* An implementation for the ClutterContainer::foreach() vfunc: */
static void
example_box_foreach (ClutterContainer *container,
ClutterCallback callback,
gpointer user_data)
{
ExampleBox *box = EXAMPLE_BOX (container);
GList *l;
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
(* callback) (child, user_data);
}
}
static void
clutter_container_iface_init (ClutterContainerIface *iface)
{
/* Provide implementations for ClutterContainer vfuncs: */
iface->add = example_box_add;
iface->remove = example_box_remove;
iface->foreach = example_box_foreach;
}
/* An implementation for the ClutterActor::show_all() vfunc,
showing all the child actors: */
static void
example_box_show_all (ClutterActor *actor)
{
ExampleBox *box = EXAMPLE_BOX (actor);
GList *l;
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
clutter_actor_show (child);
}
clutter_actor_show (actor);
}
/* An implementation for the ClutterActor::hide_all() vfunc,
hiding all the child actors: */
static void
example_box_hide_all (ClutterActor *actor)
{
ExampleBox *box = EXAMPLE_BOX (actor);
GList *l;
clutter_actor_hide (actor);
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
clutter_actor_hide (child);
}
}
/* An implementation for the ClutterActor::paint() vfunc,
painting all the child actors: */
static void
example_box_paint (ClutterActor *actor)
{
ExampleBox *box = EXAMPLE_BOX (actor);
GList *l;
cogl_push_matrix ();
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
if (CLUTTER_ACTOR_IS_MAPPED (child))
clutter_actor_paint (child);
}
cogl_pop_matrix ();
}
/* An implementation for the ClutterActor::pick() vfunc,
picking all the child actors: */
static void
example_box_pick (ClutterActor *actor,
const ClutterColor *color)
{
ExampleBox *box = EXAMPLE_BOX (actor);
GList *l;
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
if (CLUTTER_ACTOR_IS_MAPPED (child))
clutter_actor_pick (child, color);
}
}
/* An implementation for the ClutterActor::get_preferred_width() vfunc: */
static void
example_box_get_preferred_width (ClutterActor *actor,
ClutterUnit for_height,
ClutterUnit *min_width_p,
ClutterUnit *natural_width_p)
{
ExampleBox *box = EXAMPLE_BOX (actor);
GList *l;
ClutterUnit min_width = 0, natural_width = 0;
/* For this container, the preferred width is the sum of the widths
* of the children. The preferred width depends on the height provided
* by for_height.
*/
/* Calculate the preferred width for this container,
* based on the preferred width requested by the children: */
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
if (CLUTTER_ACTOR_IS_VISIBLE (child))
{
ClutterUnit child_min_width, child_natural_width;
clutter_actor_get_preferred_width (child, for_height, &child_min_width, &child_natural_width);
min_width += child_min_width;
natural_width += child_natural_width;
}
}
if (min_width_p)
*min_width_p = min_width;
if (natural_width_p)
*natural_width_p = natural_width;
}
/* An implementation for the ClutterActor::get_preferred_height() vfunc: */
static void
example_box_get_preferred_height (ClutterActor *actor,
ClutterUnit for_width,
ClutterUnit *min_height_p,
ClutterUnit *natural_height_p)
{
ExampleBox *box = EXAMPLE_BOX (actor);
GList *l;
ClutterUnit min_height = 0, natural_height = 0;
/* For this container, the preferred height is the maximum height
* of the children. The preferred height is independent of the given width.
*/
/* Calculate the preferred height for this container,
* based on the preferred height requested by the children: */
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
if (CLUTTER_ACTOR_IS_VISIBLE (child))
{
ClutterUnit child_min_height, child_natural_height;
clutter_actor_get_preferred_height (child, -1, &child_min_height, &child_natural_height);
min_height = MAX (min_height, child_min_height);
natural_height = MAX (natural_height, child_natural_height);
}
}
if (min_height_p)
*min_height_p = min_height;
if (natural_height_p)
*natural_height_p = natural_height;
}
/* An implementation for the ClutterActor::allocate() vfunc: */
static void
example_box_allocate (ClutterActor *actor,
const ClutterActorBox *box,
gboolean absolute_origin_changed)
{
ExampleBox *ebox = EXAMPLE_BOX (actor);
/* Look at each child actor: */
ClutterUnit child_x = 0;
GList *l = NULL;
for (l = ebox->children; l; l = l->next)
{
ClutterActor *child = l->data;
/* Discover what size the child wants: */
ClutterUnit child_width, child_height;
clutter_actor_get_preferred_size (child, NULL, NULL, &child_width, &child_height);
/* Calculate the position and size that the child may actually have: */
/* Position the child just after the previous child, horizontally: */
ClutterActorBox child_box = { 0, };
child_box.x1 = child_x;
child_box.x2 = child_x + child_width;
child_x = child_box.x2;
/* Position the child at the top of the container: */
child_box.y1 = 0;
child_box.y2 = child_box.y1 + child_height;
/* Tell the child what position and size it may actually have: */
clutter_actor_allocate (child, &child_box, absolute_origin_changed);
}
CLUTTER_ACTOR_CLASS (example_box_parent_class)->allocate (actor, box, absolute_origin_changed);
}
static void
example_box_dispose (GObject *gobject)
{
/* Destroy each child actor when this container is destroyed: */
ExampleBox *box = EXAMPLE_BOX (gobject);
GList *l;
for (l = box->children; l; l = l->next)
{
ClutterActor *child = l->data;
clutter_actor_destroy (child);
}
g_list_free (box->children);
box->children = NULL;
G_OBJECT_CLASS (example_box_parent_class)->dispose (gobject);
}
static void
example_box_class_init (ExampleBoxClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
gobject_class->dispose = example_box_dispose;
/* Provide implementations for ClutterActor vfuncs: */
actor_class->show_all = example_box_show_all;
actor_class->hide_all = example_box_hide_all;
actor_class->paint = example_box_paint;
actor_class->pick = example_box_pick;
actor_class->get_preferred_width = example_box_get_preferred_width;
actor_class->get_preferred_height = example_box_get_preferred_height;
actor_class->allocate = example_box_allocate;
}
static void
example_box_init (ExampleBox *box)
{
/* The required width depends on a given height in this container */
g_object_set (G_OBJECT (box),
"request-mode", CLUTTER_REQUEST_WIDTH_FOR_HEIGHT,
NULL);
}
/*
* Public API
*/
/**
* example_box_pack:
* @box: a #ExampleBox
* @actor: a #ClutterActor to pack into the box
*
* Packs @actor into @box.
*/
void
example_box_pack (ExampleBox *box,
ClutterActor *actor)
{
g_return_if_fail (EXAMPLE_IS_BOX (box));
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
box->children = g_list_prepend (box->children, actor);
clutter_actor_set_parent (actor, CLUTTER_ACTOR (box));
/* queue a relayout of the container */
clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
}
/**
* example_box_remove_all:
* @box: a #ExampleBox
*
* Removes all child actors from the #ExampleBox
*/
void
example_box_remove_all (ExampleBox *box)
{
GList *children;
g_return_if_fail (EXAMPLE_IS_BOX (box));
children = box->children;
while (children)
{
ClutterActor *child = children->data;
children = children->next;
clutter_container_remove_actor (CLUTTER_CONTAINER (box), child);
}
}
/**
* example_box_new:
*
* Creates a new box.
*
* Return value: the newly created #ExampleBox
*/
ClutterActor *
example_box_new (void)
{
return g_object_new (EXAMPLE_TYPE_BOX, NULL);
}