diff --git a/doc/cookbook/Makefile.am b/doc/cookbook/Makefile.am index e2956317d..d77f4cdc5 100644 --- a/doc/cookbook/Makefile.am +++ b/doc/cookbook/Makefile.am @@ -12,6 +12,7 @@ XML_FILES = \ introduction.xml \ text.xml \ textures.xml \ + layouts.xml \ $(NULL) XSLTOPTS = \ @@ -36,6 +37,7 @@ IMAGE_FILES = \ images/actors-opacity-container-affects-opacity.png \ images/text-shadow.png \ images/textures-sub-texture.png \ + images/layouts-stacking-diff-actor-sizes.png \ $(NULL) VIDEO_FILES = \ videos/animations-fading-out.ogv \ diff --git a/doc/cookbook/clutter-cookbook.xml.in b/doc/cookbook/clutter-cookbook.xml.in index ade193535..3bd710821 100644 --- a/doc/cookbook/clutter-cookbook.xml.in +++ b/doc/cookbook/clutter-cookbook.xml.in @@ -44,6 +44,7 @@ + Contributing to this document diff --git a/doc/cookbook/examples/.gitignore b/doc/cookbook/examples/.gitignore index af4ba9caf..29c14b0f1 100644 --- a/doc/cookbook/examples/.gitignore +++ b/doc/cookbook/examples/.gitignore @@ -3,3 +3,5 @@ /textures-reflection /textures-split-go /textures-sub-texture +/layouts-stacking +/layouts-stacking-diff-sized-actors diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am index 662786ead..2369e7a0f 100644 --- a/doc/cookbook/examples/Makefile.am +++ b/doc/cookbook/examples/Makefile.am @@ -8,6 +8,8 @@ noinst_PROGRAMS = \ textures-reflection \ textures-split-go \ textures-sub-texture \ + layouts-stacking \ + layouts-stacking-diff-sized-actors \ $(NULL) INCLUDES = \ @@ -29,8 +31,10 @@ AM_CFLAGS = \ AM_LDFLAGS = $(CLUTTER_LIBS) -animations_rotating_SOURCES = animations-rotating.c -text_shadow_SOURCES = text-shadow.c -textures_reflection_SOURCES = textures-reflection.c -textures_split_go_SOURCES = textures-split-go.c -textures_sub_texture_SOURCES = textures-sub-texture.c +animations_rotating_SOURCES = animations-rotating.c +text_shadow_SOURCES = text-shadow.c +textures_reflection_SOURCES = textures-reflection.c +textures_split_go_SOURCES = textures-split-go.c +textures_sub_texture_SOURCES = textures-sub-texture.c +layouts_stacking_SOURCES = layouts-stacking.c +layouts_stacking_diff_sized_actors_SOURCES = layouts-stacking-diff-sized-actors.c diff --git a/doc/cookbook/examples/layouts-stacking-diff-sized-actors.c b/doc/cookbook/examples/layouts-stacking-diff-sized-actors.c new file mode 100644 index 000000000..6030eeeda --- /dev/null +++ b/doc/cookbook/examples/layouts-stacking-diff-sized-actors.c @@ -0,0 +1,78 @@ +#include + +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; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + + 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; + + ClutterColor *color = clutter_color_new (255 - diff_x * 50, + 100 + diff_y * 50, + 0, + 255); + ClutterActor *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; +} diff --git a/doc/cookbook/examples/layouts-stacking.c b/doc/cookbook/examples/layouts-stacking.c new file mode 100644 index 000000000..8e9f05381 --- /dev/null +++ b/doc/cookbook/examples/layouts-stacking.c @@ -0,0 +1,80 @@ +/* + * 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 + +#define STAGE_SIDE 512 + +static const ClutterColor box_color = { 0x33, 0x33, 0x55, 0xff }; + +int +main (int argc, char *argv[]) +{ + gchar *filename = TESTS_DATA_DIR "/redhand.png"; + + if (argc > 1) + filename = argv[1]; + + ClutterLayoutManager *layout; + ClutterActor *box; + ClutterActor *stage; + ClutterActor *texture; + CoglHandle *cogl_texture; + GError *error = NULL; + gfloat width; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, STAGE_SIDE, STAGE_SIDE); + + layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_BIN_ALIGNMENT_CENTER); + + box = clutter_box_new (layout); + clutter_box_set_color (CLUTTER_BOX (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_container_add_actor (CLUTTER_CONTAINER (box), texture_copy); + } + + clutter_actor_add_constraint (box, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5)); + clutter_actor_add_constraint (box, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5)); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); + + clutter_actor_show (stage); + + clutter_main (); + + return 0; +} diff --git a/doc/cookbook/images/layouts-stacking-diff-actor-sizes.png b/doc/cookbook/images/layouts-stacking-diff-actor-sizes.png new file mode 100644 index 000000000..54c59f1c7 Binary files /dev/null and b/doc/cookbook/images/layouts-stacking-diff-actor-sizes.png differ diff --git a/doc/cookbook/layouts.xml b/doc/cookbook/layouts.xml new file mode 100644 index 000000000..618f9dcf9 --- /dev/null +++ b/doc/cookbook/layouts.xml @@ -0,0 +1,590 @@ + + + + + Layout management + + + Abigail Adams, wife of John Adams, in a letter to John + Thaxter (1778-09-29) + If we do not lay out ourselves in the service of mankind, + whom should we serve? + + +
+ Introduction + + Layout management in Clutter controls how an actor and + children "inside" that actor are sized and positioned. More + specifically, layouts are managed by associating a parent with a + ClutterLayoutManager; the parent is usually either a + composite ClutterActor (composed of several + ClutterActors) or a ClutterContainer + (containing child ClutterActors). The + ClutterLayoutManager then manages: + + + + The size requisition + (determination of the desired height and width) of the + parent. + + + The allocation (size and position) + assigned to each composed or child ClutterActor. + + + + + To make this more concrete, imagine you have a sheet of + paper and some coloured squares to place on it. Someone stands + next to you telling you how big the piece of paper should be, + how big the squares should be, and where to put each square on the + piece of paper. + The sheet of paper is analogous to the container or + composite actor; the squares are analogous to the child + ClutterActors; and the person giving you instructions + is analogous to the layout manager. + + + The following sections give an overview of how layout + management works in Clutter. + +
+ Using layouts + + Although Clutter provides plenty of flexibility in how you + can use layout management, the simplest way to get started is to + use the built-in ClutterBox class with one of the + provided ClutterLayoutManager implementations. + + The pattern for doing this is: + + + + Create an instance of one of the + ClutterLayoutManager implementations (see + the + following section). + + + Configure the layout manager's default policies + (e.g. how actors are aligned by default, whether to pack + actors horizontally or vertically, spacing between actors + in the layout). + + + Create a ClutterBox, setting its layout + manager to the one you just created. + + + Pack actors into the ClutterBox, + setting layout properties (if required) as each is added. + + + Modify layout properties of child actors using + clutter_layout_manager_child_set() + (if required). + + + + Individual recipes in this section give more examples of + how to make use of the different layout manager + implementations. + + + It is not possible to use a layout manager with an arbitrary + ClutterContainer: you must use a ClutterActor + subclass which can delegate its layout to a layout manager (either + use ClutterBox or write your own). + + +
+ +
+ Types of layout manager + + Clutter provides a range of layout managers suitable + for different use cases: + + + + ClutterFixedLayout arranges actors + at fixed positions on the stage. No alignment options are + available, so you have to manually compute and manage the + coordinates (or use ClutterConstraints) which + will align actors how you want them. + + + ClutterBinLayout arranges actors in a + depth-ordered stack on top of each other, aligned to the container. + This is useful for arranging actors inside composites (e.g. + creating a button widget from a ClutterTexture + with a ClutterText on top of it). + + + ClutterBoxLayout arranges actors in a + single horizontal row or vertical column. This type of layout is + common in UI elements like toolbars and menus. + + + ClutterFlowLayout arranges actors + in reflowing columns and rows. If the container's allocation + changes, the child actors are rearranged to fit inside its + new allocation. This can be useful for arranging actors + where you're not sure how many there might be; or where + new ones are going to be added into the UI, perhaps displacing + others. An example might be a photo viewer or an + RSS feed display. + + + +
+ +
+ Layout properties + + How actors are sized and positioned inside a container + associated with a layout manager depends on two things: + + + + + Properties which apply to all actors added to the layout + There will be one setting at the layout level which can't + be overridden per actor. This includes properties like spacing + between rows and columns, whether the layout is homogenous + (each actor gets the same allocation), etc. + + + + + Properties for each actor added to the layout + These are properties of the relationship between the + layout, the container associated with the layout, and the + children of the container. Each layout/container/actor + combination can have different settings for each of these + properties. + + + + + Each layout manager implementation supports a subset of the + following layout properties; different managers may have different + names or functions for setting them, but the functionality remains + the same. Individual recipes give more details about which + properties can be set for each layout manager implementation. + + + + + Alignment + How an actor aligns to the container's axes, e.g. + aligned to the container's left, right, or center. For some + layouts (like ClutterBinLayout) alignment + is also used to set expand and fill properties. + + + + + Horizontal/vertical orientation + Whether actors are arranged in a horizontal row or + vertical column. + + + + + Homogenous rows and columns + Grid-like layouts (e.g. ClutterFlowLayout) + can be configured to have uniform rows and/or columns, + expanding to fit the largest actor they contain. + + + + + Row height and column width + Grid-like layouts arranged in rows and columns + can be configured with maximum and minimum row height and + column width. + + + + + Row and column spacing + Grid-like layouts enable you to define a space (in pixels) + between rows and columns. + + + + + Expand + Some layouts can be configured to minimize their size request + to fit the actors they contain (expand is FALSE); + or to increase the allocation of actors they contain so + that all available space in the layout is used + (expand is TRUE). In the latter case, you'd + also need to set a size for the container associated with + the layout, otherwise the container will just fit itself to the + actors inside it. + + + + + Fill + This property only has an effect when + expand is on. The fill + setting controls whether actors are resized to fill their + allocation (fill is TRUE); or if the + space around the actor is increased (fill is + FALSE). + + + + + Pack at start/end + This controls whether actors at prepended or appended + to the layout. + + + If the orientation is vertical, prepended + actors are added to the top of the layout and appended + actors to the bottom. + + + If the orientation is horizontal, prepended + actors are added at the left of the layout and appended actors + on the right. + + + + + + +
+ Setting layout properties + + Layout properties can be set in one or more of the following ways + (depending on the type of property and the layout manager): + + + + By setting a default value for the property on the + layout manager (e.g. using + clutter_bin_layout_set_alignment(), + clutter_box_layout_set_expand()). Any + actor added to the layout gets this value for the property, + unless it is overridden for that actor. + + + When adding an actor to a ClutterBox container + using clutter_box_pack(), you can set + properties on the actor which you're adding. + + + When adding an actor to a layout you can use a function + which enables setting properties simultaneously (e.g. + clutter_box_layout_pack(), + clutter_bin_layout_add()). + + + By using + clutter_layout_manager_child_set() on + the child of a layout. + + + +
+ +
+ +
+ Not using layout managers + + It is perfectly possible to arrange ClutterActors + without using layout managers; however, you may have to do + more of your own calculations about actor sizes and positions. + + There are two (not mutually-exclusive) approaches you can + take to do this, described below. + +
+ Manual positioning and alignment + + This basically means using the ClutterActor + bounding box mechanism (see the ClutterActor + documentation for details) to set actor sizes and positions. + This is the approach you will see in a lot of older Clutter + code (written before layout managers were available). + + This approach is simplest where the UI is relatively static + and is composed of a few known actors. It will work in larger, + more complex scenarios, but in those sorts of cases it is better + to make use of layout managers and constraints (see below) instead. +
+ +
+ Using <type>ClutterConstraint</type> + + Constraints provide mechanisms for: + + + + Aligning actors with each other + (ClutterAlignConstraint). For example, you + can align the top, bottom or center of one actor with the + top, bottom or center of another (on the y + axis). Similarly, you can align one actor to another + on the x axis. + + + Binding properties of one actor to those of + another. For example, you could ensure that two actors + always remain the same width; or you could specify + that two actors always have the same x + coordinate. In both these cases and others, you can + specify that the properties should be the same, or the same + +/- some offset. + + + + + ClutterConstraints can be used in combination + with some layout managers, but you need to be careful that + constraints don't fight with the layout manager policies. + Unpredictable results could ensue. + + +
+ +
+ +
+ +
+ Stacking actors on top of each other + +
+ Problem + + 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). +
+ +
+ Solution + + The most flexible approach is to use a ClutterBinLayout + associated with a ClutterBox: + + + + + + +
+ +
+ Discussion + + This section covers some other aspects of using a + ClutterBinLayout. + +
+ Setting and changing alignment + + 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. +
+ +
+ Size requisitioning + + 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: + + + + + + + + Size requisition in a ClutterBinLayout + + + + + + 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 + ClutterBox 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. + +
+ +
+ Depth ordering + + 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_raise(), + clutter_actor_lower() and their relatives. +
+ +
+ Other ways to stack actors + + 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 + (or even a ClutterGroup), possibly combined with + ClutterConstraints to align actors with each other + and bind their widths and heights together. See + this + section for more details. +
+ +
+ +
+ Full examples + + + <type>ClutterBinLayout</type>, with actors in 9 + combinations of start, center and end alignment combinations + + + a code sample should be here... but isn't + + + + + + Layering multiple textures on top of each other + inside a <type>ClutterBinLayout</type> + + + a code sample should be here... but isn't + + + + +
+ +
+ +