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-snap-constraint
27 * @Title: ClutterSnapConstraint
28 * @Short_Description: A constraint snapping two actors together
30 * #ClutterSnapConstraint is a constraint the snaps the edges of two
31 * actors together, expanding the actor's allocation if necessary.
33 * An offset can be applied to the constraint, to provide spacing.
35 * #ClutterSnapConstraint is available since Clutter 1.6
44 #include "clutter-snap-constraint.h"
46 #include "clutter-actor-private.h"
47 #include "clutter-constraint.h"
48 #include "clutter-debug.h"
49 #include "clutter-enum-types.h"
50 #include "clutter-private.h"
52 #define CLUTTER_SNAP_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SNAP_CONSTRAINT, ClutterSnapConstraintClass))
53 #define CLUTTER_IS_SNAP_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SNAP_CONSTRAINT))
54 #define CLUTTER_SNAP_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SNAP_CONSTRAINT, ClutterSnapConstraintClass))
56 struct _ClutterSnapConstraint
58 ClutterConstraint parent_instance;
63 ClutterSnapEdge from_edge;
64 ClutterSnapEdge to_edge;
69 struct _ClutterSnapConstraintClass
71 ClutterConstraintClass parent_class;
86 G_DEFINE_TYPE (ClutterSnapConstraint,
87 clutter_snap_constraint,
88 CLUTTER_TYPE_CONSTRAINT);
90 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
93 source_queue_relayout (ClutterActor *source,
94 ClutterSnapConstraint *constraint)
96 if (constraint->actor != NULL)
97 clutter_actor_queue_relayout (constraint->actor);
101 source_destroyed (ClutterActor *actor,
102 ClutterSnapConstraint *constraint)
104 constraint->source = NULL;
108 warn_horizontal_edge (const gchar *edge,
110 ClutterActor *source)
112 g_warning (G_STRLOC ": the %s edge of actor '%s' can only be snapped "
113 "to either the right or the left edge of actor '%s'",
115 _clutter_actor_get_debug_name (actor),
116 _clutter_actor_get_debug_name (source));
120 warn_vertical_edge (const gchar *edge,
122 ClutterActor *source)
124 g_warning (G_STRLOC ": the %s edge of actor '%s' can only "
125 "be snapped to the top or bottom edge of actor '%s'",
127 _clutter_actor_get_debug_name (actor),
128 _clutter_actor_get_debug_name (source));
132 clutter_snap_constraint_update_allocation (ClutterConstraint *constraint,
134 ClutterActorBox *allocation)
136 ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (constraint);
137 gfloat source_width, source_height;
138 gfloat source_x, source_y;
139 gfloat actor_width, actor_height;
141 if (self->source == NULL)
144 clutter_actor_get_position (self->source, &source_x, &source_y);
145 clutter_actor_get_size (self->source, &source_width, &source_height);
147 clutter_actor_box_get_size (allocation, &actor_width, &actor_height);
149 switch (self->to_edge)
151 case CLUTTER_SNAP_EDGE_LEFT:
152 if (self->from_edge == CLUTTER_SNAP_EDGE_LEFT)
153 allocation->x1 = source_x + self->offset;
154 else if (self->from_edge == CLUTTER_SNAP_EDGE_RIGHT)
155 allocation->x2 = source_x + self->offset;
157 warn_horizontal_edge ("left", self->actor, self->source);
160 case CLUTTER_SNAP_EDGE_RIGHT:
161 if (self->from_edge == CLUTTER_SNAP_EDGE_RIGHT)
162 allocation->x2 = source_x + source_width + self->offset;
163 else if (self->from_edge == CLUTTER_SNAP_EDGE_LEFT)
164 allocation->x1 = source_x + source_width + self->offset;
166 warn_horizontal_edge ("right", self->actor, self->source);
171 case CLUTTER_SNAP_EDGE_TOP:
172 if (self->from_edge == CLUTTER_SNAP_EDGE_TOP)
173 allocation->y1 = source_y + self->offset;
174 else if (self->from_edge == CLUTTER_SNAP_EDGE_BOTTOM)
175 allocation->y2 = source_y + self->offset;
177 warn_vertical_edge ("top", self->actor, self->source);
180 case CLUTTER_SNAP_EDGE_BOTTOM:
181 if (self->from_edge == CLUTTER_SNAP_EDGE_BOTTOM)
182 allocation->y2 = source_y + source_height + self->offset;
183 else if (self->from_edge == CLUTTER_SNAP_EDGE_TOP)
184 allocation->y1 = source_y + source_height + self->offset;
186 warn_vertical_edge ("bottom", self->actor, self->source);
190 g_assert_not_reached ();
194 if (allocation->x2 - allocation->x1 < 0)
195 allocation->x2 = allocation->x1;
197 if (allocation->y2 - allocation->y1 < 0)
198 allocation->y2 = allocation->y1;
202 clutter_snap_constraint_set_actor (ClutterActorMeta *meta,
203 ClutterActor *new_actor)
205 ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (meta);
206 ClutterActorMetaClass *parent;
208 /* store the pointer to the actor, for later use */
209 self->actor = new_actor;
211 parent = CLUTTER_ACTOR_META_CLASS (clutter_snap_constraint_parent_class);
212 parent->set_actor (meta, new_actor);
216 clutter_snap_constraint_dispose (GObject *gobject)
218 ClutterSnapConstraint *snap = CLUTTER_SNAP_CONSTRAINT (gobject);
220 if (snap->source != NULL)
222 g_signal_handlers_disconnect_by_func (snap->source,
223 G_CALLBACK (source_destroyed),
225 g_signal_handlers_disconnect_by_func (snap->source,
226 G_CALLBACK (source_queue_relayout),
231 G_OBJECT_CLASS (clutter_snap_constraint_parent_class)->dispose (gobject);
235 clutter_snap_constraint_set_property (GObject *gobject,
240 ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (gobject);
245 clutter_snap_constraint_set_source (self, g_value_get_object (value));
249 clutter_snap_constraint_set_edges (self,
250 g_value_get_enum (value),
255 clutter_snap_constraint_set_edges (self,
257 g_value_get_enum (value));
261 clutter_snap_constraint_set_offset (self, g_value_get_float (value));
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
271 clutter_snap_constraint_get_property (GObject *gobject,
276 ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (gobject);
281 g_value_set_object (value, self->source);
285 g_value_set_enum (value, self->from_edge);
289 g_value_set_enum (value, self->to_edge);
293 g_value_set_float (value, self->offset);
297 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
303 clutter_snap_constraint_class_init (ClutterSnapConstraintClass *klass)
305 ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
306 ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass);
307 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
309 meta_class->set_actor = clutter_snap_constraint_set_actor;
311 constraint_class->update_allocation = clutter_snap_constraint_update_allocation;
313 * ClutterSnapConstraint:source:
315 * The #ClutterActor used as the source for the constraint
319 obj_props[PROP_SOURCE] =
320 g_param_spec_object ("source",
322 P_("The source of the constraint"),
324 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
327 * ClutterSnapConstraint:from-edge:
329 * The edge of the #ClutterActor that should be snapped
333 obj_props[PROP_FROM_EDGE] =
334 g_param_spec_enum ("from-edge",
336 P_("The edge of the actor that should be snapped"),
337 CLUTTER_TYPE_SNAP_EDGE,
338 CLUTTER_SNAP_EDGE_RIGHT,
339 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
342 * ClutterSnapConstraint:to-edge:
344 * The edge of the #ClutterSnapConstraint:source that should be snapped
348 obj_props[PROP_TO_EDGE] =
349 g_param_spec_enum ("to-edge",
351 P_("The edge of the source that should be snapped"),
352 CLUTTER_TYPE_SNAP_EDGE,
353 CLUTTER_SNAP_EDGE_RIGHT,
354 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
357 * ClutterSnapConstraint:offset:
359 * The offset, in pixels, between #ClutterSnapConstraint:from-edge
360 * and #ClutterSnapConstraint:to-edge
364 obj_props[PROP_OFFSET] =
365 g_param_spec_float ("offset",
367 P_("The offset in pixels to apply to the constraint"),
368 -G_MAXFLOAT, G_MAXFLOAT,
370 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
372 gobject_class->dispose = clutter_snap_constraint_dispose;
373 gobject_class->set_property = clutter_snap_constraint_set_property;
374 gobject_class->get_property = clutter_snap_constraint_get_property;
375 g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
379 clutter_snap_constraint_init (ClutterSnapConstraint *self)
384 self->from_edge = CLUTTER_SNAP_EDGE_RIGHT;
385 self->to_edge = CLUTTER_SNAP_EDGE_RIGHT;
391 * clutter_snap_constraint_new:
392 * @source: (allow-none): the #ClutterActor to use as the source of
393 * the constraint, or %NULL
394 * @from_edge: the edge of the actor to use in the constraint
395 * @to_edge: the edge of @source to use in the constraint
396 * @offset: the offset to apply to the constraint, in pixels
398 * Creates a new #ClutterSnapConstraint that will snap a #ClutterActor
399 * to the @edge of @source, with the given @offset.
401 * Return value: the newly created #ClutterSnapConstraint
406 clutter_snap_constraint_new (ClutterActor *source,
407 ClutterSnapEdge from_edge,
408 ClutterSnapEdge to_edge,
411 g_return_val_if_fail (source == NULL || CLUTTER_IS_ACTOR (source), NULL);
413 return g_object_new (CLUTTER_TYPE_SNAP_CONSTRAINT,
415 "from-edge", from_edge,
422 * clutter_snap_constraint_set_source:
423 * @constraint: a #ClutterSnapConstraint
424 * @source: (allow-none): a #ClutterActor, or %NULL to unset the source
426 * Sets the source #ClutterActor for the constraint
431 clutter_snap_constraint_set_source (ClutterSnapConstraint *constraint,
432 ClutterActor *source)
434 ClutterActor *old_source;
436 g_return_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint));
437 g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source));
439 if (constraint->source == source)
442 old_source = constraint->source;
443 if (old_source != NULL)
445 g_signal_handlers_disconnect_by_func (old_source,
446 G_CALLBACK (source_destroyed),
448 g_signal_handlers_disconnect_by_func (old_source,
449 G_CALLBACK (source_queue_relayout),
453 constraint->source = source;
454 if (constraint->source != NULL)
456 g_signal_connect (constraint->source, "queue-relayout",
457 G_CALLBACK (source_queue_relayout),
459 g_signal_connect (constraint->source, "destroy",
460 G_CALLBACK (source_destroyed),
463 if (constraint->actor != NULL)
464 clutter_actor_queue_relayout (constraint->actor);
467 g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_SOURCE]);
471 * clutter_snap_constraint_get_source:
472 * @constraint: a #ClutterSnapConstraint
474 * Retrieves the #ClutterActor set using clutter_snap_constraint_set_source()
476 * Return value: (transfer none): a pointer to the source actor
481 clutter_snap_constraint_get_source (ClutterSnapConstraint *constraint)
483 g_return_val_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint), NULL);
485 return constraint->source;
489 * clutter_snap_constraint_set_edges:
490 * @constraint: a #ClutterSnapConstraint
491 * @from_edge: the edge on the actor
492 * @to_edge: the edge on the source
494 * Sets the edges to be used by the @constraint
496 * The @from_edge is the edge on the #ClutterActor to which @constraint
497 * has been added. The @to_edge is the edge of the #ClutterActor inside
498 * the #ClutterSnapConstraint:source property.
503 clutter_snap_constraint_set_edges (ClutterSnapConstraint *constraint,
504 ClutterSnapEdge from_edge,
505 ClutterSnapEdge to_edge)
507 gboolean from_changed = FALSE, to_changed = FALSE;
509 g_return_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint));
511 g_object_freeze_notify (G_OBJECT (constraint));
513 if (constraint->from_edge != from_edge)
515 constraint->from_edge = from_edge;
516 g_object_notify_by_pspec (G_OBJECT (constraint),
517 obj_props[PROP_FROM_EDGE]);
521 if (constraint->to_edge != to_edge)
523 constraint->to_edge = to_edge;
524 g_object_notify_by_pspec (G_OBJECT (constraint),
525 obj_props[PROP_TO_EDGE]);
529 if ((from_changed || to_changed) &&
530 constraint->actor != NULL)
532 clutter_actor_queue_relayout (constraint->actor);
535 g_object_thaw_notify (G_OBJECT (constraint));
539 * clutter_snap_constraint_get_edges:
540 * @constraint: a #ClutterSnapConstraint
541 * @from_edge: (out): return location for the actor's edge, or %NULL
542 * @to_edge: (out): return location for the source's edge, or %NULL
544 * Retrieves the edges used by the @constraint
549 clutter_snap_constraint_get_edges (ClutterSnapConstraint *constraint,
550 ClutterSnapEdge *from_edge,
551 ClutterSnapEdge *to_edge)
553 g_return_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint));
556 *from_edge = constraint->from_edge;
559 *to_edge = constraint->to_edge;
563 * clutter_snap_constraint_set_offset:
564 * @constraint: a #ClutterSnapConstraint
565 * @offset: the offset to apply, in pixels
567 * Sets the offset to be applied to the constraint
572 clutter_snap_constraint_set_offset (ClutterSnapConstraint *constraint,
575 g_return_if_fail (CLUTTER_IS_SNAP_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_snap_constraint_get_offset:
590 * @constraint: a #ClutterSnapConstraint
592 * Retrieves the offset set using clutter_snap_constraint_set_offset()
594 * Return value: the offset, in pixels
599 clutter_snap_constraint_get_offset (ClutterSnapConstraint *constraint)
601 g_return_val_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint), 0.0);
603 return constraint->offset;