From 32ad63efef8ee92b0bc361b9dea3fd46ba72fbd1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 28 Jun 2010 17:18:35 +0100 Subject: [PATCH] bind-constraint: Add width and height binding Allow using the BindConstraint to bind width and height of a source actor. Also, add a test for the BindConstraint showing all types of usages for this constraint class. --- clutter/clutter-bind-constraint.c | 105 ++++++++++++++++++++++------ clutter/clutter-bind-constraint.h | 8 ++- tests/interactive/.gitignore | 1 + tests/interactive/Makefile.am | 3 +- tests/interactive/test-bind.c | 110 ++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 23 deletions(-) create mode 100644 tests/interactive/test-bind.c diff --git a/clutter/clutter-bind-constraint.c b/clutter/clutter-bind-constraint.c index 5a7674ec4..daee500ff 100644 --- a/clutter/clutter-bind-constraint.c +++ b/clutter/clutter-bind-constraint.c @@ -25,11 +25,56 @@ /** * SECTION:clutter-bind-constraint * @Title: ClutterBindConstraint - * @Short_Description: A constraint binding the position of an actor + * @Short_Description: A constraint binding the position or size of an actor * - * #ClutterBindConstraint is a #ClutterConstraint that binds the position of - * the #ClutterActor to which it is applied to the the position of another - * #ClutterActor. + * #ClutterBindConstraint is a #ClutterConstraint that binds the + * position or the size of the #ClutterActor to which it is applied + * to the the position or the size of another #ClutterActor, or + * "source". + * + * An offset can be applied to the constraint, to avoid overlapping. The offset + * can also be animated. For instance, the following code will set up three + * actors to be bound to the same origin: + * + * |[ + * /* source */ + * rect[0] = clutter_rectangle_new_with_color (&red_color); + * clutter_actor_set_position (rect[0], x_pos, y_pos); + * clutter_actor_set_size (rect[0], 100, 100); + * + * /* second rectangle */ + * rect[1] = clutter_rectangle_new_with_color (&green_color); + * clutter_actor_set_size (rect[1], 100, 100); + * clutter_actor_set_opacity (rect[1], 0); + * + * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_X, 0.0); + * clutter_actor_add_constraint_with_name (rect[1], "green-x", constraint); + * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_Y, 0.0); + * clutter_actor_add_constraint_with_name (rect[1], "green-y", constraint); + * + * /* third rectangle */ + * rect[2] = clutter_rectangle_new_with_color (&blue_color); + * clutter_actor_set_size (rect[2], 100, 100); + * clutter_actor_set_opacity (rect[2], 0); + * + * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_X, 0.0); + * clutter_actor_add_constraint_with_name (rect[2], "blue-x", constraint); + * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_Y, 0.0); + * ]| + * + * The following code animates the second and third rectangles to "expand" + * them horizontally from underneath the first rectangle: + * + * |[ + * clutter_actor_animate (rect[1], CLUTTER_EASE_OUT_CUBIC, 250, + * "@constraints.green-x.offset", 100.0, + * "opacity", 255, + * NULL); + * clutter_actor_animate (rect[2], CLUTTER_EASE_OUT_CUBIC, 250, + * "@constraints.blue-x.offset", 200.0, + * "opacity", 255, + * NULL); + * ]| * * #ClutterBindConstraint is available since Clutter 1.4 */ @@ -79,8 +124,9 @@ G_DEFINE_TYPE (ClutterBindConstraint, CLUTTER_TYPE_CONSTRAINT); static void -update_actor_position (ClutterBindConstraint *bind) +update_actor_coords (ClutterBindConstraint *bind) { + gfloat source_width, source_height; ClutterVertex source_position; ClutterActor *actor; @@ -97,6 +143,7 @@ update_actor_position (ClutterBindConstraint *bind) source_position.x = clutter_actor_get_x (bind->source); source_position.y = clutter_actor_get_y (bind->source); source_position.z = clutter_actor_get_depth (bind->source); + clutter_actor_get_size (bind->source, &source_width, &source_height); switch (bind->coordinate) { @@ -111,20 +158,32 @@ update_actor_position (ClutterBindConstraint *bind) case CLUTTER_BIND_Z: clutter_actor_set_depth (actor, source_position.z + bind->offset); break; + + case CLUTTER_BIND_WIDTH: + clutter_actor_set_width (actor, source_width + bind->offset); + break; + + case CLUTTER_BIND_HEIGHT: + clutter_actor_set_height (actor, source_height + bind->offset); + break; } } static void -source_position_changed (GObject *gobject, - GParamSpec *pspec, - ClutterBindConstraint *bind) +source_allocation_changed (ClutterActor *source, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags, + ClutterBindConstraint *bind) { - if (strcmp (pspec->name, "x") == 0 || - strcmp (pspec->name, "y") == 0 || - strcmp (pspec->name, "depth") == 0) - { - update_actor_position (bind); - } + update_actor_coords (bind); +} + +static void +source_depth_changed (GObject *gobject, + GParamSpec *pspec, + ClutterBindConstraint *bind) +{ + update_actor_coords (bind); } static void @@ -308,21 +367,27 @@ clutter_bind_constraint_set_source (ClutterBindConstraint *constraint, G_CALLBACK (source_destroyed), constraint); g_signal_handlers_disconnect_by_func (old_source, - G_CALLBACK (source_position_changed), + G_CALLBACK (source_allocation_changed), + constraint); + g_signal_handlers_disconnect_by_func (old_source, + G_CALLBACK (source_depth_changed), constraint); } constraint->source = source; if (constraint->source != NULL) { - g_signal_connect (constraint->source, "notify", - G_CALLBACK (source_position_changed), + g_signal_connect (constraint->source, "allocation-changed", + G_CALLBACK (source_allocation_changed), + constraint); + g_signal_connect (constraint->source, "notify::depth", + G_CALLBACK (source_depth_changed), constraint); g_signal_connect (constraint->source, "destroy", G_CALLBACK (source_destroyed), constraint); - update_actor_position (constraint); + update_actor_coords (constraint); } g_object_notify (G_OBJECT (constraint), "source"); @@ -366,7 +431,7 @@ clutter_bind_constraint_set_coordinate (ClutterBindConstraint *constraint, constraint->coordinate = coordinate; - update_actor_position (constraint); + update_actor_coords (constraint); g_object_notify (G_OBJECT (constraint), "coordinate"); } @@ -410,7 +475,7 @@ clutter_bind_constraint_set_offset (ClutterBindConstraint *constraint, constraint->offset = offset; - update_actor_position (constraint); + update_actor_coords (constraint); g_object_notify (G_OBJECT (constraint), "offset"); } diff --git a/clutter/clutter-bind-constraint.h b/clutter/clutter-bind-constraint.h index 15aa161ec..387e3b5bf 100644 --- a/clutter/clutter-bind-constraint.h +++ b/clutter/clutter-bind-constraint.h @@ -52,15 +52,19 @@ typedef struct _ClutterBindConstraint ClutterBindConstraint; * @CLUTTER_BIND_X: Bind the X coordinate * @CLUTTER_BIND_Y: Bind the Y coordinate * @CLUTTER_BIND_Z: Bind the Z coordinate + * @CLUTTER_BIND_WIDTH: Bind the width + * @CLUTTER_BIND_HEIGHT: Bidng the height * - * Specifies which coordinate should be used in a binding + * Specifies which property should be used in a binding * * Since: 1.4 */ typedef enum { /*< prefix=CLUTTER_BIND >*/ CLUTTER_BIND_X, CLUTTER_BIND_Y, - CLUTTER_BIND_Z + CLUTTER_BIND_Z, + CLUTTER_BIND_WIDTH, + CLUTTER_BIND_HEIGHT } ClutterBindCoordinate; GType clutter_bind_constraint_get_type (void) G_GNUC_CONST; diff --git a/tests/interactive/.gitignore b/tests/interactive/.gitignore index 3b0fd8a54..20a77c212 100644 --- a/tests/interactive/.gitignore +++ b/tests/interactive/.gitignore @@ -67,3 +67,4 @@ /test-state /test-state-animator /test-scrolling +/test-bind diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index a28063671..80b77565f 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -53,7 +53,8 @@ UNIT_TESTS = \ test-stage-sizing.c \ test-drag.c \ test-constraints.c \ - test-scrolling.c + test-scrolling.c \ + test-bind.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-bind.c b/tests/interactive/test-bind.c new file mode 100644 index 000000000..cd170bffe --- /dev/null +++ b/tests/interactive/test-bind.c @@ -0,0 +1,110 @@ +#include +#include +#include + +#define RECT_SIZE 128.0 + +#define N_COLORS 3 + +static ClutterActor *rects[N_COLORS] = { NULL, }; +static const gchar *colors[N_COLORS] = { + "#cc0000", "#3465a4", "#73d216" +}; +static gboolean is_expanded = FALSE; + +static void +on_click (ClutterClickAction *action, + ClutterActor *actor) +{ + if (!is_expanded) + { + clutter_actor_animate (rects[1], CLUTTER_EASE_OUT_CUBIC, 250, + "@constraints.green-x.offset", RECT_SIZE, + "opacity", 255, + NULL); + clutter_actor_animate (rects[2], CLUTTER_EASE_OUT_CUBIC, 500, + "@constraints.blue-x.offset", (RECT_SIZE * 2.0 + 0.5), + "opacity", 255, + NULL); + } + else + { + clutter_actor_animate (rects[1], CLUTTER_EASE_OUT_CUBIC, 250, + "@constraints.green-x.offset", 0.0, + "opacity", 0, + NULL); + clutter_actor_animate (rects[2], CLUTTER_EASE_OUT_CUBIC, 250, + "@constraints.blue-x.offset", 0.0, + "opacity", 0, + NULL); + } + + is_expanded = !is_expanded; +} + +G_MODULE_EXPORT int +test_bind_main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterConstraint *constraint; + ClutterAction *action; + ClutterColor color; + + clutter_init (&argc, &argv); + + stage = clutter_stage_new (); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Bind Constraint"); + clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE); + clutter_actor_set_size (stage, RECT_SIZE * 4.0, RECT_SIZE * 3.0); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + + /* main rectangle */ + clutter_color_from_string (&color, colors[0]); + rects[0] = clutter_rectangle_new_with_color (&color); + clutter_actor_set_size (rects[0], RECT_SIZE, RECT_SIZE); + + /* center it on the stage */ + clutter_actor_add_constraint (rects[0], clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.1)); + clutter_actor_add_constraint (rects[0], clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5)); + + /* make it clickable */ + action = clutter_click_action_new (); + clutter_actor_add_action (rects[0], action); + clutter_actor_set_reactive (rects[0], TRUE); + g_signal_connect (action, "clicked", G_CALLBACK (on_click), NULL); + + /* second rectangle */ + clutter_color_from_string (&color, colors[1]); + rects[1] = clutter_rectangle_new_with_color (&color); + clutter_actor_set_opacity (rects[1], 0); + clutter_actor_add_constraint (rects[1], clutter_bind_constraint_new (rects[0], CLUTTER_BIND_WIDTH, 0.0)); + clutter_actor_add_constraint (rects[1], clutter_bind_constraint_new (rects[0], CLUTTER_BIND_HEIGHT, 0.0)); + constraint = clutter_bind_constraint_new (rects[0], CLUTTER_BIND_X, 0.0); + clutter_actor_add_constraint_with_name (rects[1], "green-x", constraint); + constraint = clutter_bind_constraint_new (rects[0], CLUTTER_BIND_Y, 0.0); + clutter_actor_add_constraint_with_name (rects[1], "green-y", constraint); + + /* third rectangle */ + clutter_color_from_string (&color, colors[2]); + rects[2] = clutter_rectangle_new_with_color (&color); + clutter_actor_set_opacity (rects[2], 0); + clutter_actor_add_constraint (rects[2], clutter_bind_constraint_new (rects[0], CLUTTER_BIND_WIDTH, 0.0)); + clutter_actor_add_constraint (rects[2], clutter_bind_constraint_new (rects[0], CLUTTER_BIND_HEIGHT, 0.0)); + constraint = clutter_bind_constraint_new (rects[0], CLUTTER_BIND_X, 0.0); + clutter_actor_add_constraint_with_name (rects[2], "blue-x", constraint); + constraint = clutter_bind_constraint_new (rects[0], CLUTTER_BIND_Y, 0.0); + clutter_actor_add_constraint_with_name (rects[2], "blue-y", constraint); + + /* add everything to the stage */ + clutter_container_add (CLUTTER_CONTAINER (stage), + rects[2], + rects[1], + rects[0], + NULL); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +}