1 /*
2 * Clutter.
3 *
4 * An OpenGL based 'interactive canvas' library.
5 *
6 * Copyright (C) 2010 Intel Corporation.
7 *
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.
12 *
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.
17 *
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/>.
20 *
21 * Author:
22 * Emmanuele Bassi <ebassi@linux.intel.com>
23 */
24
25 /**
26 * SECTION:clutter-align-constraint
27 * @Title: ClutterAlignConstraint
28 * @Short_Description: A constraint aligning the position of an actor
29 *
30 * #ClutterAlignConstraint is a #ClutterConstraint that aligns the position
31 * of the #ClutterActor to which it is applied to the size of another
32 * #ClutterActor using an alignment factor
33 *
34 * #ClutterAlignConstraint is available since Clutter 1.4
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include "clutter-build-config.h"
39 #endif
40
41 #include "clutter-align-constraint.h"
42
43 #include "clutter-actor-meta-private.h"
44 #include "clutter-actor-private.h"
45 #include "clutter-constraint.h"
46 #include "clutter-debug.h"
47 #include "clutter-enum-types.h"
48 #include "clutter-private.h"
49
50 #include <math.h>
51
52 #define CLUTTER_ALIGN_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ALIGN_CONSTRAINT, ClutterAlignConstraintClass))
53 #define CLUTTER_IS_ALIGN_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ALIGN_CONSTRAINT))
54 #define CLUTTER_ALIGN_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ALIGN_CONSTRAINT, ClutterAlignConstraintClass))
55
56 struct _ClutterAlignConstraint
57 {
58 ClutterConstraint parent_instance;
59
60 ClutterActor *actor;
61 ClutterActor *source;
62 ClutterAlignAxis align_axis;
63 gfloat factor;
64 };
65
66 struct _ClutterAlignConstraintClass
67 {
68 ClutterConstraintClass parent_class;
69 };
70
71 enum
72 {
73 PROP_0,
74
75 PROP_SOURCE,
76 PROP_ALIGN_AXIS,
77 PROP_FACTOR,
78
79 PROP_LAST
80 };
81
82 static GParamSpec *obj_props[PROP_LAST];
83
84 G_DEFINE_TYPE (ClutterAlignConstraint,
85 clutter_align_constraint,
86 CLUTTER_TYPE_CONSTRAINT);
87
88 static void
source_position_changed(ClutterActor * actor,const ClutterActorBox * allocation,ClutterAllocationFlags flags,ClutterAlignConstraint * align)89 source_position_changed (ClutterActor *actor,
90 const ClutterActorBox *allocation,
91 ClutterAllocationFlags flags,
92 ClutterAlignConstraint *align)
93 {
94 if (align->actor != NULL)
95 clutter_actor_queue_relayout (align->actor);
96 }
97
98 static void
source_destroyed(ClutterActor * actor,ClutterAlignConstraint * align)99 source_destroyed (ClutterActor *actor,
100 ClutterAlignConstraint *align)
101 {
102 align->source = NULL;
103 }
104
105 static void
clutter_align_constraint_set_actor(ClutterActorMeta * meta,ClutterActor * new_actor)106 clutter_align_constraint_set_actor (ClutterActorMeta *meta,
107 ClutterActor *new_actor)
108 {
109 ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (meta);
110 ClutterActorMetaClass *parent;
111
112 if (new_actor != NULL &&
113 align->source != NULL &&
114 clutter_actor_contains (new_actor, align->source))
115 {
116 g_warning (G_STRLOC ": The source actor '%s' is contained "
117 "by the actor '%s' associated to the constraint "
118 "'%s'",
119 _clutter_actor_get_debug_name (align->source),
120 _clutter_actor_get_debug_name (new_actor),
121 _clutter_actor_meta_get_debug_name (meta));
122 return;
123 }
124
125 /* store the pointer to the actor, for later use */
126 align->actor = new_actor;
127
128 parent = CLUTTER_ACTOR_META_CLASS (clutter_align_constraint_parent_class);
129 parent->set_actor (meta, new_actor);
130 }
131
132 static void
clutter_align_constraint_update_allocation(ClutterConstraint * constraint,ClutterActor * actor,ClutterActorBox * allocation)133 clutter_align_constraint_update_allocation (ClutterConstraint *constraint,
134 ClutterActor *actor,
135 ClutterActorBox *allocation)
136 {
137 ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (constraint);
138 gfloat source_width, source_height;
139 gfloat actor_width, actor_height;
140 gfloat source_x, source_y;
141
142 if (align->source == NULL)
143 return;
144
145 clutter_actor_box_get_size (allocation, &actor_width, &actor_height);
146
147 clutter_actor_get_position (align->source, &source_x, &source_y);
148 clutter_actor_get_size (align->source, &source_width, &source_height);
149
150 switch (align->align_axis)
151 {
152 case CLUTTER_ALIGN_X_AXIS:
153 allocation->x1 = ((source_width - actor_width) * align->factor)
154 + source_x;
155 allocation->x2 = allocation->x1 + actor_width;
156 break;
157
158 case CLUTTER_ALIGN_Y_AXIS:
159 allocation->y1 = ((source_height - actor_height) * align->factor)
160 + source_y;
161 allocation->y2 = allocation->y1 + actor_height;
162 break;
163
164 case CLUTTER_ALIGN_BOTH:
165 allocation->x1 = ((source_width - actor_width) * align->factor)
166 + source_x;
167 allocation->y1 = ((source_height - actor_height) * align->factor)
168 + source_y;
169 allocation->x2 = allocation->x1 + actor_width;
170 allocation->y2 = allocation->y1 + actor_height;
171 break;
172
173 default:
174 g_assert_not_reached ();
175 break;
176 }
177
178 clutter_actor_box_clamp_to_pixel (allocation);
179 }
180
181 static void
clutter_align_constraint_dispose(GObject * gobject)182 clutter_align_constraint_dispose (GObject *gobject)
183 {
184 ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject);
185
186 if (align->source != NULL)
187 {
188 g_signal_handlers_disconnect_by_func (align->source,
189 G_CALLBACK (source_destroyed),
190 align);
191 g_signal_handlers_disconnect_by_func (align->source,
192 G_CALLBACK (source_position_changed),
193 align);
194 align->source = NULL;
195 }
196
197 G_OBJECT_CLASS (clutter_align_constraint_parent_class)->dispose (gobject);
198 }
199
200 static void
clutter_align_constraint_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)201 clutter_align_constraint_set_property (GObject *gobject,
202 guint prop_id,
203 const GValue *value,
204 GParamSpec *pspec)
205 {
206 ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject);
207
208 switch (prop_id)
209 {
210 case PROP_SOURCE:
211 clutter_align_constraint_set_source (align, g_value_get_object (value));
212 break;
213
214 case PROP_ALIGN_AXIS:
215 clutter_align_constraint_set_align_axis (align, g_value_get_enum (value));
216 break;
217
218 case PROP_FACTOR:
219 clutter_align_constraint_set_factor (align, g_value_get_float (value));
220 break;
221
222 default:
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
224 break;
225 }
226 }
227
228 static void
clutter_align_constraint_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)229 clutter_align_constraint_get_property (GObject *gobject,
230 guint prop_id,
231 GValue *value,
232 GParamSpec *pspec)
233 {
234 ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject);
235
236 switch (prop_id)
237 {
238 case PROP_SOURCE:
239 g_value_set_object (value, align->source);
240 break;
241
242 case PROP_ALIGN_AXIS:
243 g_value_set_enum (value, align->align_axis);
244 break;
245
246 case PROP_FACTOR:
247 g_value_set_float (value, align->factor);
248 break;
249
250 default:
251 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
252 break;
253 }
254 }
255
256 static void
clutter_align_constraint_class_init(ClutterAlignConstraintClass * klass)257 clutter_align_constraint_class_init (ClutterAlignConstraintClass *klass)
258 {
259 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
260 ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
261 ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass);
262
263 meta_class->set_actor = clutter_align_constraint_set_actor;
264
265 constraint_class->update_allocation = clutter_align_constraint_update_allocation;
266
267 /**
268 * ClutterAlignConstraint:source:
269 *
270 * The #ClutterActor used as the source for the alignment.
271 *
272 * The #ClutterActor must not be a child or a grandchild of the actor
273 * using the constraint.
274 *
275 * Since: 1.4
276 */
277 obj_props[PROP_SOURCE] =
278 g_param_spec_object ("source",
279 P_("Source"),
280 P_("The source of the alignment"),
281 CLUTTER_TYPE_ACTOR,
282 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
283
284 /**
285 * ClutterAlignConstraint:align-axis:
286 *
287 * The axis to be used to compute the alignment
288 *
289 * Since: 1.4
290 */
291 obj_props[PROP_ALIGN_AXIS] =
292 g_param_spec_enum ("align-axis",
293 P_("Align Axis"),
294 P_("The axis to align the position to"),
295 CLUTTER_TYPE_ALIGN_AXIS,
296 CLUTTER_ALIGN_X_AXIS,
297 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
298
299 /**
300 * ClutterAlignConstraint:factor:
301 *
302 * The alignment factor, as a normalized value between 0.0 and 1.0
303 *
304 * The factor depends on the #ClutterAlignConstraint:align-axis property:
305 * with an align-axis value of %CLUTTER_ALIGN_X_AXIS, 0.0 means left and
306 * 1.0 means right; with a value of %CLUTTER_ALIGN_Y_AXIS, 0.0 means top
307 * and 1.0 means bottom.
308 *
309 * Since: 1.4
310 */
311 obj_props[PROP_FACTOR] =
312 g_param_spec_float ("factor",
313 P_("Factor"),
314 P_("The alignment factor, between 0.0 and 1.0"),
315 0.0, 1.0,
316 0.0,
317 CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
318
319 gobject_class->dispose = clutter_align_constraint_dispose;
320 gobject_class->set_property = clutter_align_constraint_set_property;
321 gobject_class->get_property = clutter_align_constraint_get_property;
322 g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
323 }
324
325 static void
clutter_align_constraint_init(ClutterAlignConstraint * self)326 clutter_align_constraint_init (ClutterAlignConstraint *self)
327 {
328 self->actor = NULL;
329 self->source = NULL;
330 self->align_axis = CLUTTER_ALIGN_X_AXIS;
331 self->factor = 0.0f;
332 }
333
334 /**
335 * clutter_align_constraint_new:
336 * @source: (allow-none): the #ClutterActor to use as the source of the
337 * alignment, or %NULL
338 * @axis: the axis to be used to compute the alignment
339 * @factor: the alignment factor, between 0.0 and 1.0
340 *
341 * Creates a new constraint, aligning a #ClutterActor's position with
342 * regards of the size of the actor to @source, with the given
343 * alignment @factor
344 *
345 * Return value: the newly created #ClutterAlignConstraint
346 *
347 * Since: 1.4
348 */
349 ClutterConstraint *
clutter_align_constraint_new(ClutterActor * source,ClutterAlignAxis axis,gfloat factor)350 clutter_align_constraint_new (ClutterActor *source,
351 ClutterAlignAxis axis,
352 gfloat factor)
353 {
354 g_return_val_if_fail (source == NULL || CLUTTER_IS_ACTOR (source), NULL);
355
356 return g_object_new (CLUTTER_TYPE_ALIGN_CONSTRAINT,
357 "source", source,
358 "align-axis", axis,
359 "factor", factor,
360 NULL);
361 }
362
363 /**
364 * clutter_align_constraint_set_source:
365 * @align: a #ClutterAlignConstraint
366 * @source: (allow-none): a #ClutterActor, or %NULL to unset the source
367 *
368 * Sets the source of the alignment constraint
369 *
370 * Since: 1.4
371 */
372 void
clutter_align_constraint_set_source(ClutterAlignConstraint * align,ClutterActor * source)373 clutter_align_constraint_set_source (ClutterAlignConstraint *align,
374 ClutterActor *source)
375 {
376 ClutterActor *old_source, *actor;
377 ClutterActorMeta *meta;
378
379 g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align));
380 g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source));
381
382 if (align->source == source)
383 return;
384
385 meta = CLUTTER_ACTOR_META (align);
386 actor = clutter_actor_meta_get_actor (meta);
387 if (actor != NULL && source != NULL)
388 {
389 if (clutter_actor_contains (actor, source))
390 {
391 g_warning (G_STRLOC ": The source actor '%s' is contained "
392 "by the actor '%s' associated to the constraint "
393 "'%s'",
394 _clutter_actor_get_debug_name (source),
395 _clutter_actor_get_debug_name (actor),
396 _clutter_actor_meta_get_debug_name (meta));
397 return;
398 }
399 }
400
401 old_source = align->source;
402 if (old_source != NULL)
403 {
404 g_signal_handlers_disconnect_by_func (old_source,
405 G_CALLBACK (source_destroyed),
406 align);
407 g_signal_handlers_disconnect_by_func (old_source,
408 G_CALLBACK (source_position_changed),
409 align);
410 }
411
412 align->source = source;
413 if (align->source != NULL)
414 {
415 g_signal_connect (align->source, "allocation-changed",
416 G_CALLBACK (source_position_changed),
417 align);
418 g_signal_connect (align->source, "destroy",
419 G_CALLBACK (source_destroyed),
420 align);
421
422 if (align->actor != NULL)
423 clutter_actor_queue_relayout (align->actor);
424 }
425
426 g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_SOURCE]);
427 }
428
429 /**
430 * clutter_align_constraint_get_source:
431 * @align: a #ClutterAlignConstraint
432 *
433 * Retrieves the source of the alignment
434 *
435 * Return value: (transfer none): the #ClutterActor used as the source
436 * of the alignment
437 *
438 * Since: 1.4
439 */
440 ClutterActor *
clutter_align_constraint_get_source(ClutterAlignConstraint * align)441 clutter_align_constraint_get_source (ClutterAlignConstraint *align)
442 {
443 g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align), NULL);
444
445 return align->source;
446 }
447
448 /**
449 * clutter_align_constraint_set_align_axis:
450 * @align: a #ClutterAlignConstraint
451 * @axis: the axis to which the alignment refers to
452 *
453 * Sets the axis to which the alignment refers to
454 *
455 * Since: 1.4
456 */
457 void
clutter_align_constraint_set_align_axis(ClutterAlignConstraint * align,ClutterAlignAxis axis)458 clutter_align_constraint_set_align_axis (ClutterAlignConstraint *align,
459 ClutterAlignAxis axis)
460 {
461 g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align));
462
463 if (align->align_axis == axis)
464 return;
465
466 align->align_axis = axis;
467
468 if (align->actor != NULL)
469 clutter_actor_queue_relayout (align->actor);
470
471 g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_ALIGN_AXIS]);
472 }
473
474 /**
475 * clutter_align_constraint_get_align_axis:
476 * @align: a #ClutterAlignConstraint
477 *
478 * Retrieves the value set using clutter_align_constraint_set_align_axis()
479 *
480 * Return value: the alignment axis
481 *
482 * Since: 1.4
483 */
484 ClutterAlignAxis
clutter_align_constraint_get_align_axis(ClutterAlignConstraint * align)485 clutter_align_constraint_get_align_axis (ClutterAlignConstraint *align)
486 {
487 g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align),
488 CLUTTER_ALIGN_X_AXIS);
489
490 return align->align_axis;
491 }
492
493 /**
494 * clutter_align_constraint_set_factor:
495 * @align: a #ClutterAlignConstraint
496 * @factor: the alignment factor, between 0.0 and 1.0
497 *
498 * Sets the alignment factor of the constraint
499 *
500 * The factor depends on the #ClutterAlignConstraint:align-axis property
501 * and it is a value between 0.0 (meaning left, when
502 * #ClutterAlignConstraint:align-axis is set to %CLUTTER_ALIGN_X_AXIS; or
503 * meaning top, when #ClutterAlignConstraint:align-axis is set to
504 * %CLUTTER_ALIGN_Y_AXIS) and 1.0 (meaning right, when
505 * #ClutterAlignConstraint:align-axis is set to %CLUTTER_ALIGN_X_AXIS; or
506 * meaning bottom, when #ClutterAlignConstraint:align-axis is set to
507 * %CLUTTER_ALIGN_Y_AXIS). A value of 0.5 aligns in the middle in either
508 * cases
509 *
510 * Since: 1.4
511 */
512 void
clutter_align_constraint_set_factor(ClutterAlignConstraint * align,gfloat factor)513 clutter_align_constraint_set_factor (ClutterAlignConstraint *align,
514 gfloat factor)
515 {
516 g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align));
517
518 align->factor = CLAMP (factor, 0.0, 1.0);
519
520 if (align->actor != NULL)
521 clutter_actor_queue_relayout (align->actor);
522
523 g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_FACTOR]);
524 }
525
526 /**
527 * clutter_align_constraint_get_factor:
528 * @align: a #ClutterAlignConstraint
529 *
530 * Retrieves the factor set using clutter_align_constraint_set_factor()
531 *
532 * Return value: the alignment factor
533 *
534 * Since: 1.4
535 */
536 gfloat
clutter_align_constraint_get_factor(ClutterAlignConstraint * align)537 clutter_align_constraint_get_factor (ClutterAlignConstraint *align)
538 {
539 g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align), 0.0);
540
541 return align->factor;
542 }
543