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.
This commit is contained in:
Emmanuele Bassi 2010-06-28 17:18:35 +01:00
parent b3ffe602a2
commit 32ad63efef
5 changed files with 204 additions and 23 deletions

View File

@ -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");
}

View File

@ -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;

View File

@ -67,3 +67,4 @@
/test-state
/test-state-animator
/test-scrolling
/test-bind

View File

@ -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

View File

@ -0,0 +1,110 @@
#include <stdlib.h>
#include <gmodule.h>
#include <clutter/clutter.h>
#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;
}