Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-align-constraint.c
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 "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
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
99 source_destroyed (ClutterActor           *actor,
100                   ClutterAlignConstraint *align)
101 {
102   align->source = NULL;
103 }
104
105 static void
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
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->x1 = floorf (allocation->x1 + 0.5);
156       allocation->x2 = allocation->x1 + actor_width;
157       break;
158
159     case CLUTTER_ALIGN_Y_AXIS:
160       allocation->y1 = ((source_height - actor_height) * align->factor)
161                      + source_y;
162       allocation->y1 = floorf (allocation->y1 + 0.5);
163       allocation->y2 = allocation->y1 + actor_height;
164       break;
165
166     case CLUTTER_ALIGN_BOTH:
167       allocation->x1 = ((source_width - actor_width) * align->factor)
168                      + source_x;
169       allocation->y1 = ((source_height - actor_height) * align->factor)
170                      + source_y;
171       allocation->x1 = floorf (allocation->x1 + 0.5f);
172       allocation->y1 = floorf (allocation->y1 + 0.5f);
173       allocation->x2 = allocation->x1 + actor_width;
174       allocation->y2 = allocation->y1 + actor_height;
175       break;
176
177     default:
178       g_assert_not_reached ();
179       break;
180     }
181 }
182
183 static void
184 clutter_align_constraint_dispose (GObject *gobject)
185 {
186   ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject);
187
188   if (align->source != NULL)
189     {
190       g_signal_handlers_disconnect_by_func (align->source,
191                                             G_CALLBACK (source_destroyed),
192                                             align);
193       g_signal_handlers_disconnect_by_func (align->source,
194                                             G_CALLBACK (source_position_changed),
195                                             align);
196       align->source = NULL;
197     }
198
199   G_OBJECT_CLASS (clutter_align_constraint_parent_class)->dispose (gobject);
200 }
201
202 static void
203 clutter_align_constraint_set_property (GObject      *gobject,
204                                        guint         prop_id,
205                                        const GValue *value,
206                                        GParamSpec   *pspec)
207 {
208   ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject);
209
210   switch (prop_id)
211     {
212     case PROP_SOURCE:
213       clutter_align_constraint_set_source (align, g_value_get_object (value));
214       break;
215
216     case PROP_ALIGN_AXIS:
217       clutter_align_constraint_set_align_axis (align, g_value_get_enum (value));
218       break;
219
220     case PROP_FACTOR:
221       clutter_align_constraint_set_factor (align, g_value_get_float (value));
222       break;
223
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
226       break;
227     }
228 }
229
230 static void
231 clutter_align_constraint_get_property (GObject    *gobject,
232                                        guint       prop_id,
233                                        GValue     *value,
234                                        GParamSpec *pspec)
235 {
236   ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject);
237
238   switch (prop_id)
239     {
240     case PROP_SOURCE:
241       g_value_set_object (value, align->source);
242       break;
243
244     case PROP_ALIGN_AXIS:
245       g_value_set_enum (value, align->align_axis);
246       break;
247
248     case PROP_FACTOR:
249       g_value_set_float (value, align->factor);
250       break;
251
252     default:
253       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
254       break;
255     }
256 }
257
258 static void
259 clutter_align_constraint_class_init (ClutterAlignConstraintClass *klass)
260 {
261   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
262   ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
263   ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass);
264
265   meta_class->set_actor = clutter_align_constraint_set_actor;
266
267   constraint_class->update_allocation = clutter_align_constraint_update_allocation;
268
269   /**
270    * ClutterAlignConstraint:source:
271    *
272    * The #ClutterActor used as the source for the alignment.
273    *
274    * The #ClutterActor must not be a child or a grandchild of the actor
275    * using the constraint.
276    *
277    * Since: 1.4
278    */
279   obj_props[PROP_SOURCE] =
280     g_param_spec_object ("source",
281                            P_("Source"),
282                            P_("The source of the alignment"),
283                            CLUTTER_TYPE_ACTOR,
284                            CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
285
286   /**
287    * ClutterAlignConstraint:align-axis:
288    *
289    * The axis to be used to compute the alignment
290    *
291    * Since: 1.4
292    */
293   obj_props[PROP_ALIGN_AXIS] =
294     g_param_spec_enum ("align-axis",
295                        P_("Align Axis"),
296                        P_("The axis to align the position to"),
297                        CLUTTER_TYPE_ALIGN_AXIS,
298                        CLUTTER_ALIGN_X_AXIS,
299                        CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
300
301   /**
302    * ClutterAlignConstraint:factor:
303    *
304    * The alignment factor, as a normalized value between 0.0 and 1.0
305    *
306    * The factor depends on the #ClutterAlignConstraint:align-axis property:
307    * with an align-axis value of %CLUTTER_ALIGN_X_AXIS, 0.0 means left and
308    * 1.0 means right; with a value of %CLUTTER_ALIGN_Y_AXIS, 0.0 means top
309    * and 1.0 means bottom.
310    *
311    * Since: 1.4
312    */
313   obj_props[PROP_FACTOR] =
314     g_param_spec_float ("factor",
315                         P_("Factor"),
316                         P_("The alignment factor, between 0.0 and 1.0"),
317                         0.0, 1.0,
318                         0.0,
319                         CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT);
320
321   gobject_class->dispose = clutter_align_constraint_dispose;
322   gobject_class->set_property = clutter_align_constraint_set_property;
323   gobject_class->get_property = clutter_align_constraint_get_property;
324   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
325 }
326
327 static void
328 clutter_align_constraint_init (ClutterAlignConstraint *self)
329 {
330   self->actor = NULL;
331   self->source = NULL;
332   self->align_axis = CLUTTER_ALIGN_X_AXIS;
333   self->factor = 0.0f;
334 }
335
336 /**
337  * clutter_align_constraint_new:
338  * @source: (allow-none): the #ClutterActor to use as the source of the
339  *   alignment, or %NULL
340  * @axis: the axis to be used to compute the alignment
341  * @factor: the alignment factor, between 0.0 and 1.0
342  *
343  * Creates a new constraint, aligning a #ClutterActor's position with
344  * regards of the size of the actor to @source, with the given
345  * alignment @factor
346  *
347  * Return value: the newly created #ClutterAlignConstraint
348  *
349  * Since: 1.4
350  */
351 ClutterConstraint *
352 clutter_align_constraint_new (ClutterActor     *source,
353                               ClutterAlignAxis  axis,
354                               gfloat            factor)
355 {
356   g_return_val_if_fail (source == NULL || CLUTTER_IS_ACTOR (source), NULL);
357
358   return g_object_new (CLUTTER_TYPE_ALIGN_CONSTRAINT,
359                        "source", source,
360                        "align-axis", axis,
361                        "factor", factor,
362                        NULL);
363 }
364
365 /**
366  * clutter_align_constraint_set_source:
367  * @align: a #ClutterAlignConstraint
368  * @source: (allow-none): a #ClutterActor, or %NULL to unset the source
369  *
370  * Sets the source of the alignment constraint
371  *
372  * Since: 1.4
373  */
374 void
375 clutter_align_constraint_set_source (ClutterAlignConstraint *align,
376                                      ClutterActor           *source)
377 {
378   ClutterActor *old_source, *actor;
379   ClutterActorMeta *meta;
380
381   g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align));
382   g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source));
383
384   if (align->source == source)
385     return;
386
387   meta = CLUTTER_ACTOR_META (align);
388   actor = clutter_actor_meta_get_actor (meta);
389   if (actor != NULL && source != NULL)
390     {
391       if (clutter_actor_contains (actor, source))
392         {
393           g_warning (G_STRLOC ": The source actor '%s' is contained "
394                      "by the actor '%s' associated to the constraint "
395                      "'%s'",
396                      _clutter_actor_get_debug_name (source),
397                      _clutter_actor_get_debug_name (actor),
398                      _clutter_actor_meta_get_debug_name (meta));
399           return;
400         }
401     }
402
403   old_source = align->source;
404   if (old_source != NULL)
405     {
406       g_signal_handlers_disconnect_by_func (old_source,
407                                             G_CALLBACK (source_destroyed),
408                                             align);
409       g_signal_handlers_disconnect_by_func (old_source,
410                                             G_CALLBACK (source_position_changed),
411                                             align);
412     }
413
414   align->source = source;
415   if (align->source != NULL)
416     {
417       g_signal_connect (align->source, "allocation-changed",
418                         G_CALLBACK (source_position_changed),
419                         align);
420       g_signal_connect (align->source, "destroy",
421                         G_CALLBACK (source_destroyed),
422                         align);
423
424       if (align->actor != NULL)
425         clutter_actor_queue_relayout (align->actor);
426     }
427
428   g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_SOURCE]);
429 }
430
431 /**
432  * clutter_align_constraint_get_source:
433  * @align: a #ClutterAlignConstraint
434  *
435  * Retrieves the source of the alignment
436  *
437  * Return value: (transfer none): the #ClutterActor used as the source
438  *   of the alignment
439  *
440  * Since: 1.4
441  */
442 ClutterActor *
443 clutter_align_constraint_get_source (ClutterAlignConstraint *align)
444 {
445   g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align), NULL);
446
447   return align->source;
448 }
449
450 /**
451  * clutter_align_constraint_set_align_axis:
452  * @align: a #ClutterAlignConstraint
453  * @axis: the axis to which the alignment refers to
454  *
455  * Sets the axis to which the alignment refers to
456  *
457  * Since: 1.4
458  */
459 void
460 clutter_align_constraint_set_align_axis (ClutterAlignConstraint *align,
461                                          ClutterAlignAxis        axis)
462 {
463   g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align));
464
465   if (align->align_axis == axis)
466     return;
467
468   align->align_axis = axis;
469
470   if (align->actor != NULL)
471     clutter_actor_queue_relayout (align->actor);
472
473   g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_ALIGN_AXIS]);
474 }
475
476 /**
477  * clutter_align_constraint_get_align_axis:
478  * @align: a #ClutterAlignConstraint
479  *
480  * Retrieves the value set using clutter_align_constraint_set_align_axis()
481  *
482  * Return value: the alignment axis
483  *
484  * Since: 1.4
485  */
486 ClutterAlignAxis
487 clutter_align_constraint_get_align_axis (ClutterAlignConstraint *align)
488 {
489   g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align),
490                         CLUTTER_ALIGN_X_AXIS);
491
492   return align->align_axis;
493 }
494
495 /**
496  * clutter_align_constraint_set_factor:
497  * @align: a #ClutterAlignConstraint
498  * @factor: the alignment factor, between 0.0 and 1.0
499  *
500  * Sets the alignment factor of the constraint
501  *
502  * The factor depends on the #ClutterAlignConstraint:align-axis property
503  * and it is a value between 0.0 (meaning left, when
504  * #ClutterAlignConstraint:align-axis is set to %CLUTTER_ALIGN_X_AXIS; or
505  * meaning top, when #ClutterAlignConstraint:align-axis is set to
506  * %CLUTTER_ALIGN_Y_AXIS) and 1.0 (meaning right, when
507  * #ClutterAlignConstraint:align-axis is set to %CLUTTER_ALIGN_X_AXIS; or
508  * meaning bottom, when #ClutterAlignConstraint:align-axis is set to
509  * %CLUTTER_ALIGN_Y_AXIS). A value of 0.5 aligns in the middle in either
510  * cases
511  *
512  * Since: 1.4
513  */
514 void
515 clutter_align_constraint_set_factor (ClutterAlignConstraint *align,
516                                      gfloat                  factor)
517 {
518   g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align));
519
520   align->factor = CLAMP (factor, 0.0, 1.0);
521
522   if (align->actor != NULL)
523     clutter_actor_queue_relayout (align->actor);
524
525   g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_FACTOR]);
526 }
527
528 /**
529  * clutter_align_constraint_get_factor:
530  * @align: a #ClutterAlignConstraint
531  *
532  * Retrieves the factor set using clutter_align_constraint_set_factor()
533  *
534  * Return value: the alignment factor
535  *
536  * Since: 1.4
537  */
538 gfloat
539 clutter_align_constraint_get_factor (ClutterAlignConstraint *align)
540 {
541   g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align), 0.0);
542
543   return align->factor;
544 }