summaryrefslogtreecommitdiff
path: root/clutter/clutter-constraint.c
blob: 2e7eb7659a51d7a9d865a271782f788ef2550703 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/*
 * Clutter.
 *
 * An OpenGL based 'interactive canvas' library.
 *
 * Copyright (C) 2010  Intel Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author:
 *   Emmanuele Bassi <ebassi@linux.intel.com>
 */

/**
 * SECTION:clutter-constraint
 * @Title: ClutterConstraint
 * @Short_Description: Abstract class for constraints on position or size
 * @See_Also: #ClutterAction
 *
 * #ClutterConstraint is a base abstract class for modifiers of a #ClutterActor
 * position or size.
 *
 * A #ClutterConstraint sub-class should contain the logic for modifying
 * the position or size of the #ClutterActor to which it is applied, by
 * updating the actor's allocation. Each #ClutterConstraint can change the
 * allocation of the actor to which they are applied by overriding the
 * #ClutterConstraintClass.update_allocation() virtual function.
 *
 * #ClutterConstraint is available since Clutter 1.4
 *
 * ## Using Constraints
 *
 * Constraints can be used with fixed layout managers, like
 * #ClutterFixedLayout, or with actors implicitly using a fixed layout
 * manager, like #ClutterGroup and #ClutterStage.
 *
 * Constraints provide a way to build user interfaces by using
 * relations between #ClutterActors, without explicit fixed
 * positioning and sizing, similarly to how fluid layout managers like
 * #ClutterBoxLayout and #ClutterTableLayout lay out their children.
 *
 * Constraints are attached to a #ClutterActor, and are available
 * for inspection using clutter_actor_get_constraints().
 *
 * Clutter provides different implementation of the #ClutterConstraint
 * abstract class, for instance:
 *
 *  - #ClutterAlignConstraint, a constraint that can be used to align
 *  an actor to another one on either the horizontal or the vertical
 *  axis, using a normalized value between 0 and 1.
 *  - #ClutterBindConstraint, a constraint binds the X, Y, width or height
 *  of an actor to the corresponding position or size of a source actor,
 *  with or without an offset.
 *  - #ClutterSnapConstraint, a constraint that "snaps" together the edges
 *  of two #ClutterActors; if an actor uses two constraints on both its
 *  horizontal or vertical edges then it can also expand to fit the empty
 *  space.
 *
 * The [constraints example](https://git.gnome.org/browse/clutter/tree/examples/constraints.c?h=clutter-1.18)
 * uses various types of #ClutterConstraints to lay out three actors on a
 * resizable stage. Only the central actor has an explicit size, and no
 * actor has an explicit position.
 *
 *  - The #ClutterActor with #ClutterActor:name `layerA` is explicitly
 *  sized to 100 pixels by 25 pixels, and it's added to the #ClutterStage
 *  - two #ClutterAlignConstraints are used to anchor `layerA` to the
 *  center of the stage, by using 0.5 as the alignment #ClutterAlignConstraint:factor on
 *  both the X and Y axis
 *  - the #ClutterActor with #ClutterActor:name `layerB` is added to the
 *  #ClutterStage with no explicit size
 *  - the #ClutterActor:x and #ClutterActor:width of `layerB` are bound
 *  to the same properties of `layerA` using two #ClutterBindConstraint
 *  objects, thus keeping `layerB` aligned to `layerA`
 *  - the top edge of `layerB` is snapped together with the bottom edge
 *  of `layerA`; the bottom edge of `layerB` is also snapped together with
 *  the bottom edge of the #ClutterStage; an offset is  given to the two
 *  #ClutterSnapConstraintss to allow for some padding; since `layerB` is
 *  snapped between two different #ClutterActors, its height is stretched
 *  to match the gap
 *  - the #ClutterActor with #ClutterActor:name `layerC` mirrors `layerB`,
 *  snapping the top edge of the #ClutterStage to the top edge of `layerC`
 *  and the top edge of `layerA` to the bottom edge of `layerC`
 *
 * You can try resizing interactively the #ClutterStage and verify
 * that the three #ClutterActors maintain the same position and
 * size relative to each other, and to the #ClutterStage.
 *
 * It is important to note that Clutter does not avoid loops or
 * competing constraints; if two or more #ClutterConstraints
 * are operating on the same positional or dimensional attributes of an
 * actor, or if the constraints on two different actors depend on each
 * other, then the behavior is undefined.
 *
 * ## Implementing a ClutterConstraint
 *
 * Creating a sub-class of #ClutterConstraint requires the
 * implementation of the #ClutterConstraintClass.update_allocation()
 * virtual function.
 *
 * The `update_allocation()` virtual function is called during the
 * allocation sequence of a #ClutterActor, and allows any #ClutterConstraint
 * attached to that actor to modify the allocation before it is passed to
 * the actor's #ClutterActorClass.allocate() implementation.
 *
 * The #ClutterActorBox passed to the `update_allocation()` implementation
 * contains the original allocation of the #ClutterActor, plus the eventual
 * modifications applied by the other #ClutterConstraints, in the same order
 * the constraints have been applied to the actor.
 *
 * It is not necessary for a #ClutterConstraint sub-class to chain
 * up to the parent's implementation.
 *
 * If a #ClutterConstraint is parametrized - i.e. if it contains
 * properties that affect the way the constraint is implemented - it should
 * call clutter_actor_queue_relayout() on the actor to which it is attached
 * to whenever any parameter is changed. The actor to which it is attached
 * can be recovered at any point using clutter_actor_meta_get_actor().
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include "clutter-constraint-private.h"

#include "clutter-actor.h"
#include "clutter-actor-meta-private.h"
#include "clutter-private.h"

G_DEFINE_ABSTRACT_TYPE (ClutterConstraint,
                        clutter_constraint,
                        CLUTTER_TYPE_ACTOR_META);

static void
constraint_update_allocation (ClutterConstraint *constraint,
                              ClutterActor      *actor,
                              ClutterActorBox   *allocation)
{
}

static void
constraint_update_preferred_size (ClutterConstraint  *constraint,
                                  ClutterActor       *actor,
                                  ClutterOrientation  direction,
                                  float               for_size,
                                  float              *minimum_size,
                                  float              *natural_size)
{
}

static void
clutter_constraint_notify (GObject    *gobject,
                           GParamSpec *pspec)
{
  if (strcmp (pspec->name, "enabled") == 0)
    {
      ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject);
      ClutterActor *actor = clutter_actor_meta_get_actor (meta);

      if (actor != NULL)
        clutter_actor_queue_relayout (actor);
    }

  if (G_OBJECT_CLASS (clutter_constraint_parent_class)->notify != NULL)
    G_OBJECT_CLASS (clutter_constraint_parent_class)->notify (gobject, pspec);
}

static void
clutter_constraint_class_init (ClutterConstraintClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->notify = clutter_constraint_notify;

  klass->update_allocation = constraint_update_allocation;
  klass->update_preferred_size = constraint_update_preferred_size;
}

static void
clutter_constraint_init (ClutterConstraint *self)
{
}

/*< private >
 * clutter_constraint_update_allocation:
 * @constraint: a #ClutterConstraint
 * @actor: a #ClutterActor
 * @allocation: (inout): the allocation to modify
 *
 * Asks the @constraint to update the @allocation of a #ClutterActor.
 *
 * Returns: %TRUE if the allocation was updated
 */
gboolean
clutter_constraint_update_allocation (ClutterConstraint *constraint,
                                      ClutterActor      *actor,
                                      ClutterActorBox   *allocation)
{
  ClutterActorBox old_alloc;

  g_return_val_if_fail (CLUTTER_IS_CONSTRAINT (constraint), FALSE);
  g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
  g_return_val_if_fail (allocation != NULL, FALSE);

  old_alloc = *allocation;

  CLUTTER_CONSTRAINT_GET_CLASS (constraint)->update_allocation (constraint,
                                                                actor,
                                                                allocation);

  return !clutter_actor_box_equal (allocation, &old_alloc);
}

void
clutter_constraint_update_preferred_size (ClutterConstraint  *constraint,
                                          ClutterActor       *actor,
                                          ClutterOrientation  direction,
                                          float               for_size,
                                          float              *minimum_size,
                                          float              *natural_size)
{
  g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
  g_return_if_fail (CLUTTER_IS_ACTOR (actor));

  CLUTTER_CONSTRAINT_GET_CLASS (constraint)->update_preferred_size (constraint, actor,
                                                                    direction,
                                                                    for_size,
                                                                    minimum_size,
                                                                    natural_size);
}