You want to lay out several actors so that they are in layers on top of each other (e.g. to create a button widget composed from a rectangle with text on top of it).
The most flexible approach is to use a ClutterBinLayout associated with a ClutterActor:
/* define some colors */ const ClutterColor background_color = { 0xaa, 0x99, 0x00, 0xff }; const ClutterColor text_color = { 0xff, 0xff, 0xff, 0xff }; ClutterLayoutManager *layout; ClutterActor *box; ClutterActor *background; ClutterActor *text; /* * create a layout, setting the default x and y alignment; * actors fill the whole allocation of the layout's container * by default */ layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_FILL, CLUTTER_BIN_ALIGNMENT_FILL); /* create the box whose children the layout will manage */ box = clutter_box_new (layout); /* * fill doesn't have much effect here * unless the container has height and/or width */ clutter_actor_set_size (box, 100, 30); /* * background for the button; could equally be a texture * with an image loaded into it or any other ClutterActor */ background = clutter_rectangle_new_with_color (&background_color); /* * add the background to the container; * as it should use the default alignment, it can be added * direct to the container, rather than via the layout */ clutter_actor_add_child (box, background); /* text for the button */ text = clutter_text_new_full ("Sans 15px", "Click me", &text_color); /* * the text requires a different alignment from the background * (centered on the box) * so we add it via the layout so the default * alignment can be overridden */ clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), text, CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_BIN_ALIGNMENT_CENTER); /* * ensure the actors are arranged in the correct depth order; * in this case, the text is on top * (NB this is not strictly necesary here as text is added after * background) */ clutter_actor_raise_top (text);
This section covers some other aspects of using a ClutterBinLayout.
Alignment is the only
layout
property available for ClutterBinLayout. Each
actor can have a different setting for its alignment in one or both
of the x
or y
axes. However, as shown in the
solution above, alignment can also be used to expand an actor to
fill the container (CLUTTER_BIN_ALIGNMENT_FILL
)
in one or both axes.
Setting alignment does not have any effect if the container is the same size as all of the actors inside it: in this case, every alignment produces the same layout. But if the container associated with the layout is larger than the actor being aligned, alignment will have an effect; see this section for more details.
Changing an actor's alignment after it has been added
to a ClutterBinLayout may make the actor "jump"
(without animation) to a new position and/or change its size.
The exception is changing from some other alignment to
CLUTTER_BIN_ALIGNMENT_FIXED
:
in this case, the actor will retain the position and size it
had before its alignment was fixed.
A container with a ClutterBinLayout will by default request the width of the widest actor in it, and the height of the tallest. If you add actors smaller than those dimensions, they will be aligned inside the container according to the layout's policies. Here's an example where a ClutterBinLayout requests a size to encompass the tallest (light grey rectangle) and widest (dark grey rectangle) actors inside it, with other actors aligned within those bounds:
Note
The screenshot also shows the 9 possible combinations
of start, center and end alignments on the x
and
y
axes. See
the sample
code for more details.
The white space is the stage visible behind the ClutterActor holding the coloured rectangles. Notice that the layout is the width of the widest actor within it and the height of the tallest.
You can also manually set a size on the container associated with a layout to override the automatically-computed size requisition.
Another important consideration is the
depth ordering of actors inside a
ClutterBinLayout. By default, the depth ordering
mirrors the order in which actors are added to the layout: the
earlier an actor is added, the lower down in the depth order it
is. If this isn't what you want, you can fix the depth ordering using
clutter_actor_set_child_above_sibling()
,
clutter_actor_set_child_below_sibling()
and
their relatives.
ClutterBinLayout makes it simple to lay out large numbers of actors in a stack and align them to the container; see the example below which shows layering of many actors on top of each other.
However, if you have a small number of actors and you need some simple alignment, an alternative is to use manual positioning inside a ClutterFixedLayout, possibly combined with ClutterConstraints to align actors with each other and bind their widths and heights together. See this section for more details.
Note
By default, ClutterActor uses a ClutterFixedLayout as its layout manager.
Example 7.1. ClutterBinLayout, with actors in 9 combinations of start, center and end alignment combinations
#include <clutter/clutter.h> static const ClutterColor dark_grey = { 0x66, 0x66, 0x66, 0xff }; static const ClutterColor light_grey = { 0xcc, 0xcc, 0xcc, 0xff }; int main (int argc, char *argv[]) { ClutterActor *stage; ClutterLayoutManager *layout; ClutterActor *box; ClutterActor *rect1, *rect2; guint align_x, align_y, diff_x, diff_y; ClutterColor *color; ClutterActor *rect; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_actor_set_size (stage, 400, 400); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_START, CLUTTER_BIN_ALIGNMENT_START); box = clutter_box_new (layout); rect1 = clutter_rectangle_new_with_color (&dark_grey); clutter_actor_set_size (rect1, 400, 200); rect2 = clutter_rectangle_new_with_color (&light_grey); clutter_actor_set_size (rect2, 200, 400); clutter_container_add (CLUTTER_CONTAINER (box), rect1, rect2, NULL); /* * 2 = CLUTTER_BIN_ALIGNMENT_START * 3 = CLUTTER_BIN_ALIGNMENT_END * 4 = CLUTTER_BIN_ALIGNMENT_CENTER */ for (align_x = 2; align_x < 5; align_x++) { for (align_y = 2; align_y < 5; align_y++) { diff_x = align_x - 1; if (align_x == 3) diff_x = 3; else if (align_x == 4) diff_x = 2; diff_y = align_y - 1; if (align_y == 3) diff_y = 3; else if (align_y == 4) diff_y = 2; color = clutter_color_new (255 - diff_x * 50, 100 + diff_y * 50, 0, 255); rect = clutter_rectangle_new_with_color (color); clutter_actor_set_size (rect, 100, 100); clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), rect, align_x, align_y); clutter_container_add_actor (CLUTTER_CONTAINER (box), rect); } } clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); clutter_actor_show (stage); clutter_main (); return 0; }
Example 7.2. Layering multiple textures on top of each other inside a ClutterBinLayout
/* * Display multiple rotated copies of an image on top of each other * * Invoke with the path to a file to load a custom image */ #include <clutter/clutter.h> #define STAGE_SIDE 512 static const ClutterColor box_color = { 0x33, 0x33, 0x55, 0xff }; int main (int argc, char *argv[]) { ClutterLayoutManager *layout; ClutterActor *box; ClutterActor *stage; ClutterActor *texture; CoglHandle *cogl_texture; GError *error = NULL; gfloat width; const gchar *filename = "redhand.png"; if (argc > 1) filename = argv[1]; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_actor_set_size (stage, STAGE_SIDE, STAGE_SIDE); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_BIN_ALIGNMENT_CENTER); box = clutter_actor_new (); clutter_actor_set_layout_manager (box, layout); clutter_actor_set_background_color (box, &box_color); texture = clutter_texture_new_from_file (filename, &error); if (error != NULL) g_error ("Error loading file %s; message was:\n%s", filename, error->message); /* * get a reference to the underlying Cogl texture * for copying onto each Clutter texture placed into the layout */ cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture)); /* * add gradually turning and shrinking textures, * smallest one last; each actor ends up on top * of the one added just before it */ for (width = STAGE_SIDE * 0.75; width >= STAGE_SIDE * 0.0625; width -= STAGE_SIDE * 0.0625) { ClutterActor *texture_copy = clutter_texture_new (); clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (texture_copy), cogl_texture); clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture_copy), TRUE); clutter_actor_set_z_rotation_from_gravity (texture_copy, (gfloat)(width * 0.5) - (STAGE_SIDE * 0.03125), CLUTTER_GRAVITY_CENTER); clutter_actor_set_width (texture_copy, width); clutter_actor_add_child (box, texture_copy); } clutter_actor_add_constraint (box, clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5)); clutter_actor_add_child (stage, box); clutter_actor_show (stage); clutter_main (); return 0; }