4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2010 Intel Corporation.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 * Emmanuele Bassi <ebassi@linux.intel.com>
26 * SECTION:clutter-bind-constraint
27 * @Title: ClutterBindConstraint
28 * @Short_Description: A constraint binding the position or size of an actor
30 * #ClutterBindConstraint is a #ClutterConstraint that binds the
31 * position or the size of the #ClutterActor to which it is applied
32 * to the the position or the size of another #ClutterActor, or
35 * An offset can be applied to the constraint, to avoid overlapping. The offset
36 * can also be animated. For instance, the following code will set up three
37 * actors to be bound to the same origin:
40 * /* source */
41 * rect[0] = clutter_rectangle_new_with_color (&red_color);
42 * clutter_actor_set_position (rect[0], x_pos, y_pos);
43 * clutter_actor_set_size (rect[0], 100, 100);
45 * /* second rectangle */
46 * rect[1] = clutter_rectangle_new_with_color (&green_color);
47 * clutter_actor_set_size (rect[1], 100, 100);
48 * clutter_actor_set_opacity (rect[1], 0);
50 * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_X, 0.0);
51 * clutter_actor_add_constraint_with_name (rect[1], "green-x", constraint);
52 * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_Y, 0.0);
53 * clutter_actor_add_constraint_with_name (rect[1], "green-y", constraint);
55 * /* third rectangle */
56 * rect[2] = clutter_rectangle_new_with_color (&blue_color);
57 * clutter_actor_set_size (rect[2], 100, 100);
58 * clutter_actor_set_opacity (rect[2], 0);
60 * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_X, 0.0);
61 * clutter_actor_add_constraint_with_name (rect[2], "blue-x", constraint);
62 * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_Y, 0.0);
63 * clutter_actor_add_constraint_with_name (rect[2], "blue-y", constraint);
66 * The following code animates the second and third rectangles to "expand"
67 * them horizontally from underneath the first rectangle:
70 * clutter_actor_animate (rect[1], CLUTTER_EASE_OUT_CUBIC, 250,
71 * "@constraints.green-x.offset", 100.0,
74 * clutter_actor_animate (rect[2], CLUTTER_EASE_OUT_CUBIC, 250,
75 * "@constraints.blue-x.offset", 200.0,
80 * <example id="bind-constraint-example">
81 * <title>Animating the offset property of ClutterBindConstraint</title>
83 * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/bind-constraint.c">
84 * <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
87 * <para>The example above creates eight rectangles and binds them to a
88 * rectangle positioned in the center of the stage; when the user presses
89 * the center rectangle, the #ClutterBindConstraint:offset property is
90 * animated through the clutter_actor_animate() function to lay out the
91 * eight rectangles around the center one. Pressing one of the outer
92 * rectangles will animate the offset back to 0.</para>
95 * #ClutterBindConstraint is available since Clutter 1.4
104 #include "clutter-bind-constraint.h"
106 #include "clutter-actor-meta-private.h"
107 #include "clutter-actor-private.h"
108 #include "clutter-constraint.h"
109 #include "clutter-debug.h"
110 #include "clutter-enum-types.h"
111 #include "clutter-private.h"
113 #define CLUTTER_BIND_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BIND_CONSTRAINT, ClutterBindConstraintClass))
114 #define CLUTTER_IS_BIND_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BIND_CONSTRAINT))
115 #define CLUTTER_BIND_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BIND_CONSTRAINT, ClutterBindConstraintClass))
117 struct _ClutterBindConstraint
119 ClutterConstraint parent_instance;
122 ClutterActor *source;
123 ClutterBindCoordinate coordinate;
127 struct _ClutterBindConstraintClass
129 ClutterConstraintClass parent_class;
143 static GParamSpec *obj_props[PROP_LAST];
145 G_DEFINE_TYPE (ClutterBindConstraint,
146 clutter_bind_constraint,
147 CLUTTER_TYPE_CONSTRAINT);
150 source_queue_relayout (ClutterActor *source,
151 ClutterBindConstraint *bind)
153 if (bind->actor != NULL)
154 clutter_actor_queue_relayout (bind->actor);
158 source_destroyed (ClutterActor *actor,
159 ClutterBindConstraint *bind)
165 clutter_bind_constraint_update_allocation (ClutterConstraint *constraint,
167 ClutterActorBox *allocation)
169 ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (constraint);
170 gfloat source_width, source_height;
171 gfloat actor_width, actor_height;
172 ClutterVertex source_position = { 0., };
174 if (bind->source == NULL)
177 source_position.x = clutter_actor_get_x (bind->source);
178 source_position.y = clutter_actor_get_y (bind->source);
179 clutter_actor_get_size (bind->source, &source_width, &source_height);
181 clutter_actor_box_get_size (allocation, &actor_width, &actor_height);
183 switch (bind->coordinate)
186 allocation->x1 = source_position.x + bind->offset;
187 allocation->x2 = allocation->x1 + actor_width;
191 allocation->y1 = source_position.y + bind->offset;
192 allocation->y2 = allocation->y1 + actor_height;
195 case CLUTTER_BIND_POSITION:
196 allocation->x1 = source_position.x + bind->offset;
197 allocation->y1 = source_position.y + bind->offset;
198 allocation->x2 = allocation->x1 + actor_width;
199 allocation->y2 = allocation->y1 + actor_height;
202 case CLUTTER_BIND_WIDTH:
203 allocation->x2 = allocation->x1 + source_width + bind->offset;
206 case CLUTTER_BIND_HEIGHT:
207 allocation->y2 = allocation->y1 + source_height + bind->offset;
210 case CLUTTER_BIND_SIZE:
211 allocation->x2 = allocation->x1 + source_width + bind->offset;
212 allocation->y2 = allocation->y1 + source_height + bind->offset;
215 case CLUTTER_BIND_ALL:
216 allocation->x1 = source_position.x + bind->offset;
217 allocation->y1 = source_position.y + bind->offset;
218 allocation->x2 = allocation->x1 + source_width + bind->offset;
219 allocation->y2 = allocation->y1 + source_height + bind->offset;
223 g_assert_not_reached ();
229 clutter_bind_constraint_set_actor (ClutterActorMeta *meta,
230 ClutterActor *new_actor)
232 ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (meta);
233 ClutterActorMetaClass *parent;
235 if (new_actor != NULL &&
236 bind->source != NULL &&
237 clutter_actor_contains (new_actor, bind->source))
239 g_warning (G_STRLOC ": The source actor '%s' is contained "
240 "by the actor '%s' associated to the constraint "
242 _clutter_actor_get_debug_name (bind->source),
243 _clutter_actor_get_debug_name (new_actor),
244 _clutter_actor_meta_get_debug_name (meta));
248 /* store the pointer to the actor, for later use */
249 bind->actor = new_actor;
251 parent = CLUTTER_ACTOR_META_CLASS (clutter_bind_constraint_parent_class);
252 parent->set_actor (meta, new_actor);
256 clutter_bind_constraint_dispose (GObject *gobject)
258 ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (gobject);
260 if (bind->source != NULL)
262 g_signal_handlers_disconnect_by_func (bind->source,
263 G_CALLBACK (source_destroyed),
265 g_signal_handlers_disconnect_by_func (bind->source,
266 G_CALLBACK (source_queue_relayout),
271 G_OBJECT_CLASS (clutter_bind_constraint_parent_class)->dispose (gobject);
275 clutter_bind_constraint_set_property (GObject *gobject,
280 ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (gobject);
285 clutter_bind_constraint_set_source (bind, g_value_get_object (value));
288 case PROP_COORDINATE:
289 clutter_bind_constraint_set_coordinate (bind, g_value_get_enum (value));
293 clutter_bind_constraint_set_offset (bind, g_value_get_float (value));
297 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
303 clutter_bind_constraint_get_property (GObject *gobject,
308 ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (gobject);
313 g_value_set_object (value, bind->source);
316 case PROP_COORDINATE:
317 g_value_set_enum (value, bind->coordinate);
321 g_value_set_float (value, bind->offset);
325 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
331 clutter_bind_constraint_class_init (ClutterBindConstraintClass *klass)
333 ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
334 ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass);
335 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
337 gobject_class->set_property = clutter_bind_constraint_set_property;
338 gobject_class->get_property = clutter_bind_constraint_get_property;
339 gobject_class->dispose = clutter_bind_constraint_dispose;
341 meta_class->set_actor = clutter_bind_constraint_set_actor;
343 constraint_class->update_allocation = clutter_bind_constraint_update_allocation;
345 * ClutterBindConstraint:source:
347 * The #ClutterActor used as the source for the binding.
349 * The #ClutterActor must not be contained inside the actor associated
354 obj_props[PROP_SOURCE] =
355 g_param_spec_object ("source",
357 P_("The source of the binding"),
359 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
362 * ClutterBindConstraint:coordinate:
364 * The coordinate to be bound
368 obj_props[PROP_COORDINATE] =
369 g_param_spec_enum ("coordinate",
371 P_("The coordinate to bind"),
372 CLUTTER_TYPE_BIND_COORDINATE,
374 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
377 * ClutterBindConstraint:offset:
379 * The offset, in pixels, to be applied to the binding
383 obj_props[PROP_OFFSET] =
384 g_param_spec_float ("offset",
386 P_("The offset in pixels to apply to the binding"),
387 -G_MAXFLOAT, G_MAXFLOAT,
389 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
391 g_object_class_install_properties (gobject_class,
397 clutter_bind_constraint_init (ClutterBindConstraint *self)
401 self->coordinate = CLUTTER_BIND_X;
406 * clutter_bind_constraint_new:
407 * @source: (allow-none): the #ClutterActor to use as the source of
408 * the binding, or %NULL
409 * @coordinate: the coordinate to bind
410 * @offset: the offset to apply to the binding, in pixels
412 * Creates a new constraint, binding a #ClutterActor's position to
413 * the given @coordinate of the position of @source
415 * Return value: the newly created #ClutterBindConstraint
420 clutter_bind_constraint_new (ClutterActor *source,
421 ClutterBindCoordinate coordinate,
424 g_return_val_if_fail (source == NULL || CLUTTER_IS_ACTOR (source), NULL);
426 return g_object_new (CLUTTER_TYPE_BIND_CONSTRAINT,
428 "coordinate", coordinate,
434 * clutter_bind_constraint_set_source:
435 * @constraint: a #ClutterBindConstraint
436 * @source: (allow-none): a #ClutterActor, or %NULL to unset the source
438 * Sets the source #ClutterActor for the constraint
443 clutter_bind_constraint_set_source (ClutterBindConstraint *constraint,
444 ClutterActor *source)
446 ClutterActor *old_source, *actor;
447 ClutterActorMeta *meta;
449 g_return_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint));
450 g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source));
452 if (constraint->source == source)
455 meta = CLUTTER_ACTOR_META (constraint);
456 actor = clutter_actor_meta_get_actor (meta);
457 if (source != NULL && actor != NULL)
459 if (clutter_actor_contains (actor, source))
461 g_warning (G_STRLOC ": The source actor '%s' is contained "
462 "by the actor '%s' associated to the constraint "
464 _clutter_actor_get_debug_name (source),
465 _clutter_actor_get_debug_name (actor),
466 _clutter_actor_meta_get_debug_name (meta));
471 old_source = constraint->source;
472 if (old_source != NULL)
474 g_signal_handlers_disconnect_by_func (old_source,
475 G_CALLBACK (source_destroyed),
477 g_signal_handlers_disconnect_by_func (old_source,
478 G_CALLBACK (source_queue_relayout),
482 constraint->source = source;
483 if (constraint->source != NULL)
485 g_signal_connect (constraint->source, "queue-relayout",
486 G_CALLBACK (source_queue_relayout),
488 g_signal_connect (constraint->source, "destroy",
489 G_CALLBACK (source_destroyed),
492 if (constraint->actor != NULL)
493 clutter_actor_queue_relayout (constraint->actor);
496 g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_SOURCE]);
500 * clutter_bind_constraint_get_source:
501 * @constraint: a #ClutterBindConstraint
503 * Retrieves the #ClutterActor set using clutter_bind_constraint_set_source()
505 * Return value: (transfer none): a pointer to the source actor
510 clutter_bind_constraint_get_source (ClutterBindConstraint *constraint)
512 g_return_val_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint), NULL);
514 return constraint->source;
518 * clutter_bind_constraint_set_coordinate:
519 * @constraint: a #ClutterBindConstraint
520 * @coordinate: the coordinate to bind
522 * Sets the coordinate to bind in the constraint
527 clutter_bind_constraint_set_coordinate (ClutterBindConstraint *constraint,
528 ClutterBindCoordinate coordinate)
530 g_return_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint));
532 if (constraint->coordinate == coordinate)
535 constraint->coordinate = coordinate;
537 if (constraint->actor != NULL)
538 clutter_actor_queue_relayout (constraint->actor);
540 g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_COORDINATE]);
544 * clutter_bind_constraint_get_coordinate:
545 * @constraint: a #ClutterBindConstraint
547 * Retrieves the bound coordinate of the constraint
549 * Return value: the bound coordinate
553 ClutterBindCoordinate
554 clutter_bind_constraint_get_coordinate (ClutterBindConstraint *constraint)
556 g_return_val_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint),
559 return constraint->coordinate;
563 * clutter_bind_constraint_set_offset:
564 * @constraint: a #ClutterBindConstraint
565 * @offset: the offset to apply, in pixels
567 * Sets the offset to be applied to the constraint
572 clutter_bind_constraint_set_offset (ClutterBindConstraint *constraint,
575 g_return_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint));
577 if (fabs (constraint->offset - offset) < 0.00001f)
580 constraint->offset = offset;
582 if (constraint->actor != NULL)
583 clutter_actor_queue_relayout (constraint->actor);
585 g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_OFFSET]);
589 * clutter_bind_constraint_get_offset:
590 * @constraint: a #ClutterBindConstraint
592 * Retrieves the offset set using clutter_bind_constraint_set_offset()
594 * Return value: the offset, in pixels
599 clutter_bind_constraint_get_offset (ClutterBindConstraint *bind)
601 g_return_val_if_fail (CLUTTER_IS_BIND_CONSTRAINT (bind), 0.0);