1 /*
2 * Clutter.
3 *
4 * An OpenGL based 'interactive canvas' library.
5 *
6 * Authored By Matthew Allum <mallum@openedhand.com>
7 *
8 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
9 * Copyright (C) 2009, 2010, 2011, 2012 Intel Corp
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 /**
26 * SECTION:clutter-actor
27 * @short_description: The basic element of the scene graph
28 *
29 * The ClutterActor class is the basic element of the scene graph in Clutter,
30 * and it encapsulates the position, size, and transformations of a node in
31 * the graph.
32 *
33 * ## Actor transformations ## {#clutter-actor-transformations}
34 *
35 * Each actor can be transformed using methods like clutter_actor_set_scale()
36 * or clutter_actor_set_rotation(). The order in which the transformations are
37 * applied is decided by Clutter and it is the following:
38 *
39 * 1. translation by the origin of the #ClutterActor:allocation property
40 * 2. translation by the actor's #ClutterActor:z-position property
41 * 3. translation by the actor's #ClutterActor:pivot-point property
42 * 4. scaling by the #ClutterActor:scale-x and #ClutterActor:scale-y factors
43 * 5. rotation around the #ClutterActor:rotation-angle-x and #ClutterActor:rotation-center-x
44 * 6. rotation around the #ClutterActor:rotation-angle-y and #ClutterActor:rotation-center-y
45 * 7. rotation around the #ClutterActor:rotation-angle-z and #ClutterActor:rotation-center-z
46 * 8. negative translation by the #ClutterActor:anchor-x and #ClutterActor:anchor-y point.
47 * 9. negative translation by the actor's #ClutterActor:pivot-point
48 *
49 * ## Modifying an actor's geometry ## {#clutter-actor-geometry}
50 *
51 * Each actor has a bounding box, called #ClutterActor:allocation
52 * which is either set by its parent or explicitly through the
53 * clutter_actor_set_position() and clutter_actor_set_size() methods.
54 * Each actor also has an implicit preferred size.
55 *
56 * An actor’s preferred size can be defined by any subclass by
57 * overriding the #ClutterActorClass.get_preferred_width() and the
58 * #ClutterActorClass.get_preferred_height() virtual functions, or it can
59 * be explicitly set by using clutter_actor_set_width() and
60 * clutter_actor_set_height().
61 *
62 * An actor’s position can be set explicitly by using
63 * clutter_actor_set_x() and clutter_actor_set_y(); the coordinates are
64 * relative to the origin of the actor’s parent.
65 *
66 * ## Managing actor children ## {#clutter-actor-children}
67 *
68 * Each actor can have multiple children, by calling
69 * clutter_actor_add_child() to add a new child actor, and
70 * clutter_actor_remove_child() to remove an existing child. #ClutterActor
71 * will hold a reference on each child actor, which will be released when
72 * the child is removed from its parent, or destroyed using
73 * clutter_actor_destroy().
74 *
75 * |[<!-- language="C" -->
76 * ClutterActor *actor = clutter_actor_new ();
77 *
78 * // set the bounding box of the actor
79 * clutter_actor_set_position (actor, 0, 0);
80 * clutter_actor_set_size (actor, 480, 640);
81 *
82 * // set the background color of the actor
83 * clutter_actor_set_background_color (actor, CLUTTER_COLOR_Orange);
84 *
85 * // set the bounding box of the child, relative to the parent
86 * ClutterActor *child = clutter_actor_new ();
87 * clutter_actor_set_position (child, 20, 20);
88 * clutter_actor_set_size (child, 80, 240);
89 *
90 * // set the background color of the child
91 * clutter_actor_set_background_color (child, CLUTTER_COLOR_Blue);
92 *
93 * // add the child to the actor
94 * clutter_actor_add_child (actor, child);
95 * ]|
96 *
97 * Children can be inserted at a given index, or above and below
98 * another child actor. The order of insertion determines the order of the
99 * children when iterating over them. Iterating over children is performed
100 * by using clutter_actor_get_first_child(), clutter_actor_get_previous_sibling(),
101 * clutter_actor_get_next_sibling(), and clutter_actor_get_last_child(). It is
102 * also possible to retrieve a list of children by using
103 * clutter_actor_get_children(), as well as retrieving a specific child at a
104 * given index by using clutter_actor_get_child_at_index().
105 *
106 * If you need to track additions of children to a #ClutterActor, use
107 * the #ClutterContainer::actor-added signal; similarly, to track removals
108 * of children from a ClutterActor, use the #ClutterContainer::actor-removed
109 * signal.
110 *
111 * See [basic-actor.c](https://git.gnome.org/browse/clutter/tree/examples/basic-actor.c?h=clutter-1.18).
112 *
113 * ## Painting an actor ## {#clutter-actor-painting}
114 *
115 * There are three ways to paint an actor:
116 *
117 * - set a delegate #ClutterContent as the value for the #ClutterActor:content property of the actor
118 * - subclass #ClutterActor and override the #ClutterActorClass.paint_node() virtual function
119 * - subclass #ClutterActor and override the #ClutterActorClass.paint() virtual function.
120 *
121 * A #ClutterContent is a delegate object that takes over the painting
122 * operations of one, or more actors. The #ClutterContent painting will
123 * be performed on top of the #ClutterActor:background-color of the actor,
124 * and before calling the actor's own implementation of the
125 * #ClutterActorClass.paint_node() virtual function.
126 *
127 * |[<!-- language="C" -->
128 * ClutterActor *actor = clutter_actor_new ();
129 *
130 * // set the bounding box
131 * clutter_actor_set_position (actor, 50, 50);
132 * clutter_actor_set_size (actor, 100, 100);
133 *
134 * // set the content; the image_content variable is set elsewhere
135 * clutter_actor_set_content (actor, image_content);
136 * ]|
137 *
138 * The #ClutterActorClass.paint_node() virtual function is invoked whenever
139 * an actor needs to be painted. The implementation of the virtual function
140 * must only paint the contents of the actor itself, and not the contents of
141 * its children, if the actor has any.
142 *
143 * The #ClutterPaintNode passed to the virtual function is the local root of
144 * the render tree; any node added to it will be rendered at the correct
145 * position, as defined by the actor's #ClutterActor:allocation.
146 *
147 * |[<!-- language="C" -->
148 * static void
149 * my_actor_paint_node (ClutterActor *actor,
150 * ClutterPaintNode *root)
151 * {
152 * ClutterPaintNode *node;
153 * ClutterActorBox box;
154 *
155 * // where the content of the actor should be painted
156 * clutter_actor_get_allocation_box (actor, &box);
157 *
158 * // the cogl_texture variable is set elsewhere
159 * node = clutter_texture_node_new (cogl_texture, CLUTTER_COLOR_White,
160 * CLUTTER_SCALING_FILTER_TRILINEAR,
161 * CLUTTER_SCALING_FILTER_LINEAR);
162 *
163 * // paint the content of the node using the allocation
164 * clutter_paint_node_add_rectangle (node, &box);
165 *
166 * // add the node, and transfer ownership
167 * clutter_paint_node_add_child (root, node);
168 * clutter_paint_node_unref (node);
169 * }
170 *
171 * The #ClutterActorClass.paint() virtual function is invoked when the
172 * #ClutterActor::paint signal is emitted, and after the other signal
173 * handlers have been invoked. Overriding the paint virtual function
174 * gives total control to the paint sequence of the actor itself,
175 * including the children of the actor, if any.
176 *
177 * It is strongly discouraged to override the #ClutterActorClass.paint()
178 * virtual function, as well as connecting to the #ClutterActor::paint
179 * signal. These hooks into the paint sequence are considered legacy, and
180 * will be removed when the Clutter API changes.
181 *
182 * ## Handling events on an actor ## {#clutter-actor-event-handling}
183 *
184 * A #ClutterActor can receive and handle input device events, for
185 * instance pointer events and key events, as long as its
186 * #ClutterActor:reactive property is set to %TRUE.
187 *
188 * Once an actor has been determined to be the source of an event,
189 * Clutter will traverse the scene graph from the top-level actor towards the
190 * event source, emitting the #ClutterActor::captured-event signal on each
191 * ancestor until it reaches the source; this phase is also called
192 * the "capture" phase. If the event propagation was not stopped, the graph
193 * is walked backwards, from the source actor to the top-level, and the
194 * #ClutterActor::event signal is emitted, alongside eventual event-specific
195 * signals like #ClutterActor::button-press-event or #ClutterActor::motion-event;
196 * this phase is also called the "bubble" phase.
197 *
198 * At any point of the signal emission, signal handlers can stop the propagation
199 * through the scene graph by returning %CLUTTER_EVENT_STOP; otherwise, they can
200 * continue the propagation by returning %CLUTTER_EVENT_PROPAGATE.
201 *
202 * ## Animation ## {#clutter-actor-animation}
203 *
204 * Animation is a core concept of modern user interfaces; Clutter provides a
205 * complete and powerful animation framework that automatically tweens the
206 * actor's state without requiring direct, frame by frame manipulation from
207 * your application code. You have two models at your disposal:
208 *
209 * - an implicit animation model
210 * - an explicit animation model
211 *
212 * The implicit animation model of Clutter assumes that all the
213 * changes in an actor state should be gradual and asynchronous; Clutter
214 * will automatically transition an actor's property change between the
215 * current state and the desired one without manual intervention, if the
216 * property is defined to be animatable in its documentation.
217 *
218 * By default, in the 1.0 API series, the transition happens with a duration
219 * of zero milliseconds, and the implicit animation is an opt in feature to
220 * retain backwards compatibility.
221 *
222 * Implicit animations depend on the current easing state; in order to use
223 * the default easing state for an actor you should call the
224 * clutter_actor_save_easing_state() function:
225 *
226 * |[<!-- language="C" -->
227 * // assume that the actor is currently positioned at (100, 100)
228 *
229 * // store the current easing state and reset the new easing state to
230 * // its default values
231 * clutter_actor_save_easing_state (actor);
232 *
233 * // change the actor's position
234 * clutter_actor_set_position (actor, 500, 500);
235 *
236 * // restore the previously saved easing state
237 * clutter_actor_restore_easing_state (actor);
238 * ]|
239 *
240 * The example above will trigger an implicit animation of the
241 * actor between its current position to a new position.
242 *
243 * Implicit animations use a default duration of 250 milliseconds,
244 * and a default easing mode of %CLUTTER_EASE_OUT_CUBIC, unless you call
245 * clutter_actor_set_easing_mode() and clutter_actor_set_easing_duration()
246 * after changing the easing state of the actor.
247 *
248 * It is possible to animate multiple properties of an actor
249 * at the same time, and you can animate multiple actors at the same
250 * time as well, for instance:
251 *
252 * |[<!-- language="C" -->
253 * clutter_actor_save_easing_state (actor);
254 *
255 * // animate the actor's opacity and depth
256 * clutter_actor_set_opacity (actor, 0);
257 * clutter_actor_set_depth (actor, -100);
258 *
259 * clutter_actor_restore_easing_state (actor);
260 *
261 * clutter_actor_save_easing_state (another_actor);
262 *
263 * // animate another actor's opacity
264 * clutter_actor_set_opacity (another_actor, 255);
265 * clutter_actor_set_depth (another_actor, 100);
266 *
267 * clutter_actor_restore_easing_state (another_actor);
268 * ]|
269 *
270 * Changing the easing state will affect all the following property
271 * transitions, but will not affect existing transitions.
272 *
273 * It is important to note that if you modify the state on an
274 * animatable property while a transition is in flight, the transition's
275 * final value will be updated, as well as its duration and progress
276 * mode by using the current easing state; for instance, in the following
277 * example:
278 *
279 * |[<!-- language="C" -->
280 * clutter_actor_save_easing_state (actor);
281 * clutter_actor_set_easing_duration (actor, 1000);
282 * clutter_actor_set_x (actor, 200);
283 * clutter_actor_restore_easing_state (actor);
284 *
285 * clutter_actor_save_easing_state (actor);
286 * clutter_actor_set_easing_duration (actor, 500);
287 * clutter_actor_set_x (actor, 100);
288 * clutter_actor_restore_easing_state (actor);
289 * ]|
290 *
291 * the first call to clutter_actor_set_x() will begin a transition
292 * of the #ClutterActor:x property from the current value to the value of
293 * 200 over a duration of one second; the second call to clutter_actor_set_x()
294 * will change the transition's final value to 100 and the duration to 500
295 * milliseconds.
296 *
297 * It is possible to receive a notification of the completion of an
298 * implicit transition by using the #ClutterActor::transition-stopped
299 * signal, decorated with the name of the property. In case you want to
300 * know when all the currently in flight transitions are complete, use
301 * the #ClutterActor::transitions-completed signal instead.
302 *
303 * It is possible to retrieve the #ClutterTransition used by the
304 * animatable properties by using clutter_actor_get_transition() and using
305 * the property name as the transition name.
306 *
307 * The explicit animation model supported by Clutter requires that
308 * you create a #ClutterTransition object, and optionally set the initial
309 * and final values. The transition will not start unless you add it to the
310 * #ClutterActor.
311 *
312 * |[<!-- language="C" -->
313 * ClutterTransition *transition;
314 *
315 * transition = clutter_property_transition_new ("opacity");
316 * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 3000);
317 * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2);
318 * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
319 * clutter_transition_set_from (transition, G_TYPE_UINT, 255);
320 * clutter_transition_set_to (transition, G_TYPE_UINT, 0);
321 *
322 * clutter_actor_add_transition (actor, "animate-opacity", transition);
323 * ]|
324 *
325 * The example above will animate the #ClutterActor:opacity property
326 * of an actor between fully opaque and fully transparent, and back, over
327 * a span of 3 seconds. The animation does not begin until it is added to
328 * the actor.
329 *
330 * The explicit animation API applies to all #GObject properties,
331 * as well as the custom properties defined through the #ClutterAnimatable
332 * interface, regardless of whether they are defined as implicitly
333 * animatable or not.
334 *
335 * The explicit animation API should also be used when using custom
336 * animatable properties for #ClutterAction, #ClutterConstraint, and
337 * #ClutterEffect instances associated to an actor; see the section on
338 * custom animatable properties below for an example.
339 *
340 * Finally, explicit animations are useful for creating animations
341 * that run continuously, for instance:
342 *
343 * |[<!-- language="C" -->
344 * // this animation will pulse the actor's opacity continuously
345 * ClutterTransition *transition;
346 * ClutterInterval *interval;
347 *
348 * transition = clutter_property_transition_new ("opacity");
349 *
350 * // we want to animate the opacity between 0 and 255
351 * clutter_transition_set_from (transition, G_TYPE_UINT, 0);
352 * clutter_transition_set_to (transition, G_TYPE_UINT, 255);
353 *
354 * // over a one second duration, running an infinite amount of times
355 * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 1000);
356 * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), -1);
357 *
358 * // we want to fade in and out, so we need to auto-reverse the transition
359 * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
360 *
361 * // and we want to use an easing function that eases both in and out
362 * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
363 * CLUTTER_EASE_IN_OUT_CUBIC);
364 *
365 * // add the transition to the desired actor to start it
366 * clutter_actor_add_transition (actor, "opacityAnimation", transition);
367 * ]|
368 *
369 * ## Implementing an actor ## {#clutter-actor-implementing}
370 *
371 * Careful consideration should be given when deciding to implement
372 * a #ClutterActor sub-class. It is generally recommended to implement a
373 * sub-class of #ClutterActor only for actors that should be used as leaf
374 * nodes of a scene graph.
375 *
376 * If your actor should be painted in a custom way, you should
377 * override the #ClutterActor::paint signal class handler. You can either
378 * opt to chain up to the parent class implementation or decide to fully
379 * override the default paint implementation; Clutter will set up the
380 * transformations and clip regions prior to emitting the #ClutterActor::paint
381 * signal.
382 *
383 * By overriding the #ClutterActorClass.get_preferred_width() and
384 * #ClutterActorClass.get_preferred_height() virtual functions it is
385 * possible to change or provide the preferred size of an actor; similarly,
386 * by overriding the #ClutterActorClass.allocate() virtual function it is
387 * possible to control the layout of the children of an actor. Make sure to
388 * always chain up to the parent implementation of the
389 * #ClutterActorClass.allocate() virtual function.
390 *
391 * In general, it is strongly encouraged to use delegation and composition
392 * instead of direct subclassing.
393 *
394 * ## ClutterActor custom properties for ClutterScript ## {#clutter-actor-custom-script}
395 *
396 * #ClutterActor defines a custom "rotation" property which allows a short-hand
397 * description of the rotations to be applied to an actor.
398 *
399 * The syntax of the "rotation" property is the following:
400 *
401 * |[
402 * "rotation" : [ { "<axis>" : [ <angle>, [ <center-point> ] ] } ]
403 * ]|
404 *
405 * where:
406 *
407 * - axis is the name of an enumeration value of type #ClutterRotateAxis
408 * - angle is a floating point value representing the rotation angle on the given axis in degrees
409 * - center-point is an optional array, and if present it must contain the center of rotation as described by two coordinates:
410 * - Y and Z for "x-axis"
411 * - X and Z for "y-axis"
412 * - X and Y for "z-axis".
413 *
414 * #ClutterActor also defines a scriptable "margin" property which follows the CSS "margin" shorthand.
415 *
416 * |[
417 * // 4 values
418 * "margin" : [ top, right, bottom, left ]
419 * // 3 values
420 * "margin" : [ top, left/right, bottom ]
421 * // 2 values
422 * "margin" : [ top/bottom, left/right ]
423 * // 1 value
424 * "margin" : [ top/right/bottom/left ]
425 * ]|
426 *
427 * #ClutterActor will also parse every positional and dimensional
428 * property defined as a string through clutter_units_from_string(); you
429 * should read the documentation for the #ClutterUnits parser format for
430 * the valid units and syntax.
431 *
432 * ## Custom animatable properties
433 *
434 * #ClutterActor allows accessing properties of #ClutterAction,
435 * #ClutterEffect, and #ClutterConstraint instances associated to an actor
436 * instance for animation purposes.
437 *
438 * In order to access a specific #ClutterAction or a #ClutterConstraint
439 * property it is necessary to set the #ClutterActorMeta:name property on the
440 * given action or constraint.
441 *
442 * The property can be accessed using the following syntax:
443 *
444 * |[
445 * @<section>.<meta-name>.<property-name>
446 * ]|
447 *
448 * - the initial `@` is mandatory
449 * - the `section` fragment can be one between "actions", "constraints" and "effects"
450 * - the `meta-name` fragment is the name of the action, effect, or constraint, as
451 * specified by the #ClutterActorMeta:name property of #ClutterActorMeta
452 * - the `property-name` fragment is the name of the action, effect, or constraint
453 * property to be animated.
454 *
455 * The example below animates a #ClutterBindConstraint applied to an actor
456 * using an explicit transition. The `rect` actor has a binding constraint
457 * on the `origin` actor, and in its initial state is overlapping the actor
458 * to which is bound to.
459 *
460 * |[<!-- language="C" -->
461 * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_X, 0.0);
462 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-x");
463 * clutter_actor_add_constraint (rect, constraint);
464 *
465 * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_Y, 0.0);
466 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-y");
467 * clutter_actor_add_constraint (rect, constraint);
468 *
469 * clutter_actor_set_reactive (origin, TRUE);
470 *
471 * g_signal_connect (origin, "button-press-event",
472 * G_CALLBACK (on_button_press),
473 * rect);
474 * ]|
475 *
476 * On button press, the rectangle "slides" from behind the actor to
477 * which is bound to, using the #ClutterBindConstraint:offset property to
478 * achieve the effect:
479 *
480 * |[<!-- language="C" -->
481 * gboolean
482 * on_button_press (ClutterActor *origin,
483 * ClutterEvent *event,
484 * ClutterActor *rect)
485 * {
486 * ClutterTransition *transition;
487 *
488 * // the offset that we want to apply; this will make the actor
489 * // slide in from behind the origin and rest at the right of
490 * // the origin, plus a padding value
491 * float new_offset = clutter_actor_get_width (origin) + h_padding;
492 *
493 * // the property we wish to animate; the "@constraints" section
494 * // tells Clutter to check inside the constraints associated
495 * // with the actor; the "bind-x" section is the name of the
496 * // constraint; and the "offset" is the name of the property
497 * // on the constraint
498 * const char *prop = "@constraints.bind-x.offset";
499 *
500 * // create a new transition for the given property
501 * transition = clutter_property_transition_new (prop);
502 *
503 * // set the easing mode and duration
504 * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
505 * CLUTTER_EASE_OUT_CUBIC);
506 * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 500);
507 *
508 * // create the interval with the initial and final values
509 * clutter_transition_set_from (transition, G_TYPE_FLOAT, 0.f);
510 * clutter_transition_set_to (transition, G_TYPE_FLOAT, new_offset);
511 *
512 * // add the transition to the actor; this causes the animation
513 * // to start. the name "offsetAnimation" can be used to retrieve
514 * // the transition later
515 * clutter_actor_add_transition (rect, "offsetAnimation", transition);
516 *
517 * // we handled the event
518 * return CLUTTER_EVENT_STOP;
519 * }
520 * ]|
521 */
522
523 /**
524 * CLUTTER_ACTOR_IS_MAPPED:
525 * @a: a #ClutterActor
526 *
527 * Evaluates to %TRUE if the %CLUTTER_ACTOR_MAPPED flag is set.
528 *
529 * The mapped state is set when the actor is visible and all its parents up
530 * to a top-level (e.g. a #ClutterStage) are visible, realized, and mapped.
531 *
532 * This check can be used to see if an actor is going to be painted, as only
533 * actors with the %CLUTTER_ACTOR_MAPPED flag set are going to be painted.
534 *
535 * The %CLUTTER_ACTOR_MAPPED flag is managed by Clutter itself, and it should
536 * not be checked directly; instead, the recommended usage is to connect a
537 * handler on the #GObject::notify signal for the #ClutterActor:mapped
538 * property of #ClutterActor, and check the presence of
539 * the %CLUTTER_ACTOR_MAPPED flag on state changes.
540 *
541 * It is also important to note that Clutter may delay the changes of
542 * the %CLUTTER_ACTOR_MAPPED flag on top-levels due to backend-specific
543 * limitations, or during the reparenting of an actor, to optimize
544 * unnecessary (and potentially expensive) state changes.
545 *
546 * Since: 0.2
547 *
548 * Deprecated: 1.24: Use clutter_actor_is_mapped() or the #ClutterActor:mapped
549 * property instead of this macro.
550 */
551
552 /**
553 * CLUTTER_ACTOR_IS_REALIZED:
554 * @a: a #ClutterActor
555 *
556 * Evaluates to %TRUE if the %CLUTTER_ACTOR_REALIZED flag is set.
557 *
558 * The realized state has an actor-dependant interpretation. If an
559 * actor wants to delay allocating resources until it is attached to a
560 * stage, it may use the realize state to do so. However it is
561 * perfectly acceptable for an actor to allocate Cogl resources before
562 * being realized because there is only one drawing context used by Clutter
563 * so any resources will work on any stage. If an actor is mapped it
564 * must also be realized, but an actor can be realized and unmapped
565 * (this is so hiding an actor temporarily doesn't do an expensive
566 * unrealize/realize).
567 *
568 * To be realized an actor must be inside a stage, and all its parents
569 * must be realized.
570 *
571 * Since: 0.2
572 *
573 * Deprecated: 1.24: Use clutter_actor_is_realized() or the #ClutterActor:realized
574 * property instead of this macro.
575 */
576
577 /**
578 * CLUTTER_ACTOR_IS_VISIBLE:
579 * @a: a #ClutterActor
580 *
581 * Evaluates to %TRUE if the actor has been shown, %FALSE if it's hidden.
582 * Equivalent to the ClutterActor::visible object property.
583 *
584 * Note that an actor is only painted onscreen if it's mapped, which
585 * means it's visible, and all its parents are visible, and one of the
586 * parents is a toplevel stage; see also %CLUTTER_ACTOR_IS_MAPPED.
587 *
588 * Since: 0.2
589 *
590 * Deprecated: 1.24: Use clutter_actor_is_visible() or the #ClutterActor:visible
591 * property instead of this macro.
592 */
593
594 /**
595 * CLUTTER_ACTOR_IS_REACTIVE:
596 * @a: a #ClutterActor
597 *
598 * Evaluates to %TRUE if the %CLUTTER_ACTOR_REACTIVE flag is set.
599 *
600 * Only reactive actors will receive event-related signals.
601 *
602 * Since: 0.6
603 *
604 * Deprecated: 1.24: Use clutter_actor_get_reactive() or the
605 * #ClutterActor:reactive property instead of this macro.
606 */
607
608 #include "config.h"
609
610 #include <math.h>
611
612 #include <gobject/gvaluecollector.h>
613
614 #include <cogl/cogl.h>
615
616 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
617 #define CLUTTER_ENABLE_EXPERIMENTAL_API
618
619 #include "clutter-actor-private.h"
620
621 #include "clutter-action.h"
622 #include "clutter-actor-meta-private.h"
623 #include "clutter-animatable.h"
624 #include "clutter-color-static.h"
625 #include "clutter-color.h"
626 #include "clutter-constraint-private.h"
627 #include "clutter-container.h"
628 #include "clutter-content-private.h"
629 #include "clutter-debug.h"
630 #include "clutter-easing.h"
631 #include "clutter-effect-private.h"
632 #include "clutter-enum-types.h"
633 #include "clutter-fixed-layout.h"
634 #include "clutter-flatten-effect.h"
635 #include "clutter-interval.h"
636 #include "clutter-main.h"
637 #include "clutter-marshal.h"
638 #include "clutter-paint-nodes.h"
639 #include "clutter-paint-node-private.h"
640 #include "clutter-paint-volume-private.h"
641 #include "clutter-private.h"
642 #include "clutter-property-transition.h"
643 #include "clutter-scriptable.h"
644 #include "clutter-script-private.h"
645 #include "clutter-stage-private.h"
646 #include "clutter-timeline.h"
647 #include "clutter-transition.h"
648 #include "clutter-units.h"
649
650 #include "deprecated/clutter-actor.h"
651 #include "deprecated/clutter-behaviour.h"
652 #include "deprecated/clutter-container.h"
653
654 /* Internal enum used to control mapped state update. This is a hint
655 * which indicates when to do something other than just enforce
656 * invariants.
657 */
658 typedef enum {
659 MAP_STATE_CHECK, /* just enforce invariants. */
660 MAP_STATE_MAKE_UNREALIZED, /* force unrealize, ignoring invariants,
661 * used when about to unparent.
662 */
663 MAP_STATE_MAKE_MAPPED, /* set mapped, error if invariants not met;
664 * used to set mapped on toplevels.
665 */
666 MAP_STATE_MAKE_UNMAPPED /* set unmapped, even if parent is mapped,
667 * used just before unmapping parent.
668 */
669 } MapStateChange;
670
671 /* 3 entries should be a good compromise, few layout managers
672 * will ask for 3 different preferred size in each allocation cycle */
673 #define N_CACHED_SIZE_REQUESTS 3
674
675 struct _ClutterActorPrivate
676 {
677 /* request mode */
678 ClutterRequestMode request_mode;
679
680 /* our cached size requests for different width / height */
681 SizeRequest width_requests[N_CACHED_SIZE_REQUESTS];
682 SizeRequest height_requests[N_CACHED_SIZE_REQUESTS];
683
684 /* An age of 0 means the entry is not set */
685 guint cached_height_age;
686 guint cached_width_age;
687
688 /* the bounding box of the actor, relative to the parent's
689 * allocation
690 */
691 ClutterActorBox allocation;
692 ClutterAllocationFlags allocation_flags;
693
694 /* clip, in actor coordinates */
695 ClutterRect clip;
696
697 /* the cached transformation matrix; see apply_transform() */
698 CoglMatrix transform;
699
700 guint8 opacity;
701 gint opacity_override;
702
703 ClutterOffscreenRedirect offscreen_redirect;
704
705 /* This is an internal effect used to implement the
706 offscreen-redirect property */
707 ClutterEffect *flatten_effect;
708
709 /* scene graph */
710 ClutterActor *parent;
711 ClutterActor *prev_sibling;
712 ClutterActor *next_sibling;
713 ClutterActor *first_child;
714 ClutterActor *last_child;
715
716 gint n_children;
717
718 /* tracks whenever the children of an actor are changed; the
719 * age is incremented by 1 whenever an actor is added or
720 * removed. the age is not incremented when the first or the
721 * last child pointers are changed, or when grandchildren of
722 * an actor are changed.
723 */
724 gint age;
725
726 gchar *name; /* a non-unique name, used for debugging */
727
728 gint32 pick_id; /* per-stage unique id, used for picking */
729
730 /* a back-pointer to the Pango context that we can use
731 * to create pre-configured PangoLayout
732 */
733 PangoContext *pango_context;
734
735 /* the text direction configured for this child - either by
736 * application code, or by the actor's parent
737 */
738 ClutterTextDirection text_direction;
739
740 /* a counter used to toggle the CLUTTER_INTERNAL_CHILD flag */
741 gint internal_child;
742
743 /* meta classes */
744 ClutterMetaGroup *actions;
745 ClutterMetaGroup *constraints;
746 ClutterMetaGroup *effects;
747
748 /* delegate object used to allocate the children of this actor */
749 ClutterLayoutManager *layout_manager;
750
751 /* delegate object used to paint the contents of this actor */
752 ClutterContent *content;
753
754 ClutterActorBox content_box;
755 ClutterContentGravity content_gravity;
756 ClutterScalingFilter min_filter;
757 ClutterScalingFilter mag_filter;
758 ClutterContentRepeat content_repeat;
759
760 /* used when painting, to update the paint volume */
761 ClutterEffect *current_effect;
762
763 /* This is used to store an effect which needs to be redrawn. A
764 redraw can be queued to start from a particular effect. This is
765 used by parametrised effects that can cache an image of the
766 actor. If a parameter of the effect changes then it only needs to
767 redraw the cached image, not the actual actor. The pointer is
768 only valid if is_dirty == TRUE. If the pointer is NULL then the
769 whole actor is dirty. */
770 ClutterEffect *effect_to_redraw;
771
772 /* This is used when painting effects to implement the
773 clutter_actor_continue_paint() function. It points to the node in
774 the list of effects that is next in the chain */
775 const GList *next_effect_to_paint;
776
777 ClutterPaintVolume paint_volume;
778
779 /* NB: This volume isn't relative to this actor, it is in eye
780 * coordinates so that it can remain valid after the actor changes.
781 */
782 ClutterPaintVolume last_paint_volume;
783
784 ClutterStageQueueRedrawEntry *queue_redraw_entry;
785
786 ClutterColor bg_color;
787
788 #ifdef CLUTTER_ENABLE_DEBUG
789 /* a string used for debugging messages */
790 gchar *debug_name;
791 #endif
792
793 /* a set of clones of the actor */
794 GHashTable *clones;
795
796 /* whether the actor is inside a cloned branch; this
797 * value is propagated to all the actor's children
798 */
799 gulong in_cloned_branch;
800
801 GListModel *child_model;
802 ClutterActorCreateChildFunc create_child_func;
803 gpointer create_child_data;
804 GDestroyNotify create_child_notify;
805
806 /* bitfields: KEEP AT THE END */
807
808 /* fixed position and sizes */
809 guint position_set : 1;
810 guint min_width_set : 1;
811 guint min_height_set : 1;
812 guint natural_width_set : 1;
813 guint natural_height_set : 1;
814 /* cached request is invalid (implies allocation is too) */
815 guint needs_width_request : 1;
816 /* cached request is invalid (implies allocation is too) */
817 guint needs_height_request : 1;
818 /* cached allocation is invalid (request has changed, probably) */
819 guint needs_allocation : 1;
820 guint show_on_set_parent : 1;
821 guint has_clip : 1;
822 guint clip_to_allocation : 1;
823 guint enable_model_view_transform : 1;
824 guint enable_paint_unmapped : 1;
825 guint has_pointer : 1;
826 guint propagated_one_redraw : 1;
827 guint paint_volume_valid : 1;
828 guint last_paint_volume_valid : 1;
829 guint in_clone_paint : 1;
830 guint transform_valid : 1;
831 /* This is TRUE if anything has queued a redraw since we were last
832 painted. In this case effect_to_redraw will point to an effect
833 the redraw was queued from or it will be NULL if the redraw was
834 queued without an effect. */
835 guint is_dirty : 1;
836 guint bg_color_set : 1;
837 guint content_box_valid : 1;
838 guint x_expand_set : 1;
839 guint y_expand_set : 1;
840 guint needs_compute_expand : 1;
841 guint needs_x_expand : 1;
842 guint needs_y_expand : 1;
843 };
844
845 enum
846 {
847 PROP_0,
848
849 PROP_NAME,
850
851 /* X, Y, WIDTH, HEIGHT are "do what I mean" properties;
852 * when set they force a size request, when gotten they
853 * get the allocation if the allocation is valid, and the
854 * request otherwise
855 */
856 PROP_X,
857 PROP_Y,
858 PROP_WIDTH,
859 PROP_HEIGHT,
860
861 PROP_POSITION,
862 PROP_SIZE,
863
864 /* Then the rest of these size-related properties are the "actual"
865 * underlying properties set or gotten by X, Y, WIDTH, HEIGHT
866 */
867 PROP_FIXED_X,
868 PROP_FIXED_Y,
869
870 PROP_FIXED_POSITION_SET,
871
872 PROP_MIN_WIDTH,
873 PROP_MIN_WIDTH_SET,
874
875 PROP_MIN_HEIGHT,
876 PROP_MIN_HEIGHT_SET,
877
878 PROP_NATURAL_WIDTH,
879 PROP_NATURAL_WIDTH_SET,
880
881 PROP_NATURAL_HEIGHT,
882 PROP_NATURAL_HEIGHT_SET,
883
884 PROP_REQUEST_MODE,
885
886 /* Allocation properties are read-only */
887 PROP_ALLOCATION,
888
889 PROP_DEPTH, /* XXX:2.0 remove */
890 PROP_Z_POSITION,
891
892 PROP_CLIP, /* XXX:2.0 remove */
893 PROP_CLIP_RECT,
894 PROP_HAS_CLIP,
895 PROP_CLIP_TO_ALLOCATION,
896
897 PROP_OPACITY,
898
899 PROP_OFFSCREEN_REDIRECT,
900
901 PROP_VISIBLE,
902 PROP_MAPPED,
903 PROP_REALIZED,
904 PROP_REACTIVE,
905
906 PROP_PIVOT_POINT,
907 PROP_PIVOT_POINT_Z,
908
909 PROP_SCALE_X,
910 PROP_SCALE_Y,
911 PROP_SCALE_Z,
912 PROP_SCALE_CENTER_X, /* XXX:2.0 remove */
913 PROP_SCALE_CENTER_Y, /* XXX:2.0 remove */
914 PROP_SCALE_GRAVITY, /* XXX:2.0 remove */
915
916 PROP_ROTATION_ANGLE_X, /* XXX:2.0 rename to rotation-x */
917 PROP_ROTATION_ANGLE_Y, /* XXX:2.0 rename to rotation-y */
918 PROP_ROTATION_ANGLE_Z, /* XXX:2.0 rename to rotation-z */
919 PROP_ROTATION_CENTER_X, /* XXX:2.0 remove */
920 PROP_ROTATION_CENTER_Y, /* XXX:2.0 remove */
921 PROP_ROTATION_CENTER_Z, /* XXX:2.0 remove */
922 /* This property only makes sense for the z rotation because the
923 others would depend on the actor having a size along the
924 z-axis */
925 PROP_ROTATION_CENTER_Z_GRAVITY, /* XXX:2.0 remove */
926
927 PROP_ANCHOR_X, /* XXX:2.0 remove */
928 PROP_ANCHOR_Y, /* XXX:2.0 remove */
929 PROP_ANCHOR_GRAVITY, /*XXX:2.0 remove */
930
931 PROP_TRANSLATION_X,
932 PROP_TRANSLATION_Y,
933 PROP_TRANSLATION_Z,
934
935 PROP_TRANSFORM,
936 PROP_TRANSFORM_SET,
937 PROP_CHILD_TRANSFORM,
938 PROP_CHILD_TRANSFORM_SET,
939
940 PROP_SHOW_ON_SET_PARENT, /*XXX:2.0 remove */
941
942 PROP_TEXT_DIRECTION,
943 PROP_HAS_POINTER,
944
945 PROP_ACTIONS,
946 PROP_CONSTRAINTS,
947 PROP_EFFECT,
948
949 PROP_LAYOUT_MANAGER,
950
951 PROP_X_EXPAND,
952 PROP_Y_EXPAND,
953 PROP_X_ALIGN,
954 PROP_Y_ALIGN,
955 PROP_MARGIN_TOP,
956 PROP_MARGIN_BOTTOM,
957 PROP_MARGIN_LEFT,
958 PROP_MARGIN_RIGHT,
959
960 PROP_BACKGROUND_COLOR,
961 PROP_BACKGROUND_COLOR_SET,
962
963 PROP_FIRST_CHILD,
964 PROP_LAST_CHILD,
965
966 PROP_CONTENT,
967 PROP_CONTENT_GRAVITY,
968 PROP_CONTENT_BOX,
969 PROP_MINIFICATION_FILTER,
970 PROP_MAGNIFICATION_FILTER,
971 PROP_CONTENT_REPEAT,
972
973 PROP_LAST
974 };
975
976 static GParamSpec *obj_props[PROP_LAST];
977
978 enum
979 {
980 SHOW,
981 HIDE,
982 DESTROY,
983 PARENT_SET,
984 KEY_FOCUS_IN,
985 KEY_FOCUS_OUT,
986 PAINT,
987 PICK,
988 REALIZE,
989 UNREALIZE,
990 QUEUE_REDRAW,
991 QUEUE_RELAYOUT,
992 EVENT,
993 CAPTURED_EVENT,
994 BUTTON_PRESS_EVENT,
995 BUTTON_RELEASE_EVENT,
996 SCROLL_EVENT,
997 KEY_PRESS_EVENT,
998 KEY_RELEASE_EVENT,
999 MOTION_EVENT,
1000 ENTER_EVENT,
1001 LEAVE_EVENT,
1002 ALLOCATION_CHANGED,
1003 TRANSITIONS_COMPLETED,
1004 TOUCH_EVENT,
1005 TRANSITION_STOPPED,
1006
1007 LAST_SIGNAL
1008 };
1009
1010 static guint actor_signals[LAST_SIGNAL] = { 0, };
1011
1012 typedef struct _TransitionClosure
1013 {
1014 ClutterActor *actor;
1015 ClutterTransition *transition;
1016 gchar *name;
1017 gulong completed_id;
1018 } TransitionClosure;
1019
1020 static void clutter_container_iface_init (ClutterContainerIface *iface);
1021 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
1022 static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
1023 static void atk_implementor_iface_init (AtkImplementorIface *iface);
1024
1025 /* These setters are all static for now, maybe they should be in the
1026 * public API, but they are perhaps obscure enough to leave only as
1027 * properties
1028 */
1029 static void clutter_actor_set_min_width (ClutterActor *self,
1030 gfloat min_width);
1031 static void clutter_actor_set_min_height (ClutterActor *self,
1032 gfloat min_height);
1033 static void clutter_actor_set_natural_width (ClutterActor *self,
1034 gfloat natural_width);
1035 static void clutter_actor_set_natural_height (ClutterActor *self,
1036 gfloat natural_height);
1037 static void clutter_actor_set_min_width_set (ClutterActor *self,
1038 gboolean use_min_width);
1039 static void clutter_actor_set_min_height_set (ClutterActor *self,
1040 gboolean use_min_height);
1041 static void clutter_actor_set_natural_width_set (ClutterActor *self,
1042 gboolean use_natural_width);
1043 static void clutter_actor_set_natural_height_set (ClutterActor *self,
1044 gboolean use_natural_height);
1045 static void clutter_actor_update_map_state (ClutterActor *self,
1046 MapStateChange change);
1047 static void clutter_actor_unrealize_not_hiding (ClutterActor *self);
1048
1049 /* Helper routines for managing anchor coords */
1050 static void clutter_anchor_coord_get_units (ClutterActor *self,
1051 const AnchorCoord *coord,
1052 gfloat *x,
1053 gfloat *y,
1054 gfloat *z);
1055 static void clutter_anchor_coord_set_units (AnchorCoord *coord,
1056 gfloat x,
1057 gfloat y,
1058 gfloat z);
1059
1060 static ClutterGravity clutter_anchor_coord_get_gravity (const AnchorCoord *coord);
1061 static void clutter_anchor_coord_set_gravity (AnchorCoord *coord,
1062 ClutterGravity gravity);
1063
1064 static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord);
1065
1066 static void _clutter_actor_get_relative_transformation_matrix (ClutterActor *self,
1067 ClutterActor *ancestor,
1068 CoglMatrix *matrix);
1069
1070 static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self);
1071
1072 static guint8 clutter_actor_get_paint_opacity_internal (ClutterActor *self);
1073
1074 static inline void clutter_actor_set_background_color_internal (ClutterActor *self,
1075 const ClutterColor *color);
1076
1077 static void on_layout_manager_changed (ClutterLayoutManager *manager,
1078 ClutterActor *self);
1079
1080 static inline void clutter_actor_queue_compute_expand (ClutterActor *self);
1081
1082 static inline void clutter_actor_set_margin_internal (ClutterActor *self,
1083 gfloat margin,
1084 GParamSpec *pspec);
1085
1086 static void clutter_actor_set_transform_internal (ClutterActor *self,
1087 const ClutterMatrix *transform);
1088 static void clutter_actor_set_child_transform_internal (ClutterActor *self,
1089 const ClutterMatrix *transform);
1090
1091 static void clutter_actor_realize_internal (ClutterActor *self);
1092 static void clutter_actor_unrealize_internal (ClutterActor *self);
1093
1094 /* Helper macro which translates by the anchor coord, applies the
1095 given transformation and then translates back */
1096 #define TRANSFORM_ABOUT_ANCHOR_COORD(a,m,c,_transform) G_STMT_START { \
1097 gfloat _tx, _ty, _tz; \
1098 clutter_anchor_coord_get_units ((a), (c), &_tx, &_ty, &_tz); \
1099 cogl_matrix_translate ((m), _tx, _ty, _tz); \
1100 { _transform; } \
1101 cogl_matrix_translate ((m), -_tx, -_ty, -_tz); } G_STMT_END
1102
1103 static GQuark quark_shader_data = 0;
1104 static GQuark quark_actor_layout_info = 0;
1105 static GQuark quark_actor_transform_info = 0;
1106 static GQuark quark_actor_animation_info = 0;
1107
1108 G_DEFINE_TYPE_WITH_CODE (ClutterActor,
1109 clutter_actor,
1110 G_TYPE_INITIALLY_UNOWNED,
1111 G_ADD_PRIVATE (ClutterActor)
1112 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
1113 clutter_container_iface_init)
1114 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
1115 clutter_scriptable_iface_init)
1116 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
1117 clutter_animatable_iface_init)
1118 G_IMPLEMENT_INTERFACE (ATK_TYPE_IMPLEMENTOR,
1119 atk_implementor_iface_init));
1120
1121 /*< private >
1122 * clutter_actor_get_debug_name:
1123 * @actor: a #ClutterActor
1124 *
1125 * Retrieves a printable name of @actor for debugging messages
1126 *
1127 * Return value: a string with a printable name
1128 */
1129 const gchar *
_clutter_actor_get_debug_name(ClutterActor * actor)1130 _clutter_actor_get_debug_name (ClutterActor *actor)
1131 {
1132 ClutterActorPrivate *priv = actor->priv;
1133 const gchar *retval;
1134
1135 #ifdef CLUTTER_ENABLE_DEBUG
1136 if (G_UNLIKELY (priv->debug_name == NULL))
1137 {
1138 priv->debug_name = g_strdup_printf ("<%s>[<%s>:%p]",
1139 priv->name != NULL ? priv->name
1140 : "unnamed",
1141 G_OBJECT_TYPE_NAME (actor),
1142 actor);
1143 }
1144
1145 retval = priv->debug_name;
1146 #else
1147 retval = priv->name != NULL
1148 ? priv->name
1149 : G_OBJECT_TYPE_NAME (actor);
1150 #endif
1151
1152 return retval;
1153 }
1154
1155 #ifdef CLUTTER_ENABLE_DEBUG
1156 /* XXX - this is for debugging only, remove once working (or leave
1157 * in only in some debug mode). Should leave it for a little while
1158 * until we're confident in the new map/realize/visible handling.
1159 */
1160 static inline void
clutter_actor_verify_map_state(ClutterActor * self)1161 clutter_actor_verify_map_state (ClutterActor *self)
1162 {
1163 ClutterActorPrivate *priv = self->priv;
1164
1165 if (CLUTTER_ACTOR_IS_REALIZED (self))
1166 {
1167 /* all bets are off during reparent when we're potentially realized,
1168 * but should not be according to invariants
1169 */
1170 if (!CLUTTER_ACTOR_IN_REPARENT (self))
1171 {
1172 if (priv->parent == NULL)
1173 {
1174 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1175 {
1176 }
1177 else
1178 g_warning ("Realized non-toplevel actor '%s' should "
1179 "have a parent",
1180 _clutter_actor_get_debug_name (self));
1181 }
1182 else if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1183 {
1184 g_warning ("Realized actor %s has an unrealized parent %s",
1185 _clutter_actor_get_debug_name (self),
1186 _clutter_actor_get_debug_name (priv->parent));
1187 }
1188 }
1189 }
1190
1191 if (CLUTTER_ACTOR_IS_MAPPED (self))
1192 {
1193 if (!CLUTTER_ACTOR_IS_REALIZED (self))
1194 g_warning ("Actor '%s' is mapped but not realized",
1195 _clutter_actor_get_debug_name (self));
1196
1197 /* remaining bets are off during reparent when we're potentially
1198 * mapped, but should not be according to invariants
1199 */
1200 if (!CLUTTER_ACTOR_IN_REPARENT (self))
1201 {
1202 if (priv->parent == NULL)
1203 {
1204 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1205 {
1206 if (!CLUTTER_ACTOR_IS_VISIBLE (self) &&
1207 !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1208 {
1209 g_warning ("Toplevel actor '%s' is mapped "
1210 "but not visible",
1211 _clutter_actor_get_debug_name (self));
1212 }
1213 }
1214 else
1215 {
1216 g_warning ("Mapped actor '%s' should have a parent",
1217 _clutter_actor_get_debug_name (self));
1218 }
1219 }
1220 else
1221 {
1222 ClutterActor *iter = self;
1223
1224 /* check for the enable_paint_unmapped flag on the actor
1225 * and parents; if the flag is enabled at any point of this
1226 * branch of the scene graph then all the later checks
1227 * become pointless
1228 */
1229 while (iter != NULL)
1230 {
1231 if (iter->priv->enable_paint_unmapped)
1232 return;
1233
1234 iter = iter->priv->parent;
1235 }
1236
1237 if (!CLUTTER_ACTOR_IS_VISIBLE (priv->parent))
1238 {
1239 g_warning ("Actor '%s' should not be mapped if parent '%s'"
1240 "is not visible",
1241 _clutter_actor_get_debug_name (self),
1242 _clutter_actor_get_debug_name (priv->parent));
1243 }
1244
1245 if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1246 {
1247 g_warning ("Actor '%s' should not be mapped if parent '%s'"
1248 "is not realized",
1249 _clutter_actor_get_debug_name (self),
1250 _clutter_actor_get_debug_name (priv->parent));
1251 }
1252
1253 if (!CLUTTER_ACTOR_IS_TOPLEVEL (priv->parent))
1254 {
1255 if (!CLUTTER_ACTOR_IS_MAPPED (priv->parent))
1256 g_warning ("Actor '%s' is mapped but its non-toplevel "
1257 "parent '%s' is not mapped",
1258 _clutter_actor_get_debug_name (self),
1259 _clutter_actor_get_debug_name (priv->parent));
1260 }
1261 }
1262 }
1263 }
1264 }
1265
1266 #endif /* CLUTTER_ENABLE_DEBUG */
1267
1268 static void
clutter_actor_set_mapped(ClutterActor * self,gboolean mapped)1269 clutter_actor_set_mapped (ClutterActor *self,
1270 gboolean mapped)
1271 {
1272 if (CLUTTER_ACTOR_IS_MAPPED (self) == mapped)
1273 return;
1274
1275 if (mapped)
1276 {
1277 CLUTTER_ACTOR_GET_CLASS (self)->map (self);
1278 g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1279 }
1280 else
1281 {
1282 CLUTTER_ACTOR_GET_CLASS (self)->unmap (self);
1283 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1284 }
1285 }
1286
1287 /* this function updates the mapped and realized states according to
1288 * invariants, in the appropriate order.
1289 */
1290 static void
clutter_actor_update_map_state(ClutterActor * self,MapStateChange change)1291 clutter_actor_update_map_state (ClutterActor *self,
1292 MapStateChange change)
1293 {
1294 gboolean was_mapped;
1295
1296 was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
1297
1298 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1299 {
1300 /* the mapped flag on top-level actors must be set by the
1301 * per-backend implementation because it might be asynchronous.
1302 *
1303 * That is, the MAPPED flag on toplevels currently tracks the X
1304 * server mapped-ness of the window, while the expected behavior
1305 * (if used to GTK) may be to track WM_STATE!=WithdrawnState.
1306 * This creates some weird complexity by breaking the invariant
1307 * that if we're visible and all ancestors shown then we are
1308 * also mapped - instead, we are mapped if all ancestors
1309 * _possibly excepting_ the stage are mapped. The stage
1310 * will map/unmap for example when it is minimized or
1311 * moved to another workspace.
1312 *
1313 * So, the only invariant on the stage is that if visible it
1314 * should be realized, and that it has to be visible to be
1315 * mapped.
1316 */
1317 if (CLUTTER_ACTOR_IS_VISIBLE (self))
1318 clutter_actor_realize (self);
1319
1320 switch (change)
1321 {
1322 case MAP_STATE_CHECK:
1323 break;
1324
1325 case MAP_STATE_MAKE_MAPPED:
1326 g_assert (!was_mapped);
1327 clutter_actor_set_mapped (self, TRUE);
1328 break;
1329
1330 case MAP_STATE_MAKE_UNMAPPED:
1331 g_assert (was_mapped);
1332 clutter_actor_set_mapped (self, FALSE);
1333 break;
1334
1335 case MAP_STATE_MAKE_UNREALIZED:
1336 /* we only use MAKE_UNREALIZED in unparent,
1337 * and unparenting a stage isn't possible.
1338 * If someone wants to just unrealize a stage
1339 * then clutter_actor_unrealize() doesn't
1340 * go through this codepath.
1341 */
1342 g_warning ("Trying to force unrealize stage is not allowed");
1343 break;
1344 }
1345
1346 if (CLUTTER_ACTOR_IS_MAPPED (self) &&
1347 !CLUTTER_ACTOR_IS_VISIBLE (self) &&
1348 !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1349 {
1350 g_warning ("Clutter toplevel of type '%s' is not visible, but "
1351 "it is somehow still mapped",
1352 _clutter_actor_get_debug_name (self));
1353 }
1354 }
1355 else
1356 {
1357 ClutterActorPrivate *priv = self->priv;
1358 ClutterActor *parent = priv->parent;
1359 gboolean should_be_mapped;
1360 gboolean may_be_realized;
1361 gboolean must_be_realized;
1362
1363 should_be_mapped = FALSE;
1364 may_be_realized = TRUE;
1365 must_be_realized = FALSE;
1366
1367 if (parent == NULL || change == MAP_STATE_MAKE_UNREALIZED)
1368 {
1369 may_be_realized = FALSE;
1370 }
1371 else
1372 {
1373 /* Maintain invariant that if parent is mapped, and we are
1374 * visible, then we are mapped ... unless parent is a
1375 * stage, in which case we map regardless of parent's map
1376 * state but do require stage to be visible and realized.
1377 *
1378 * If parent is realized, that does not force us to be
1379 * realized; but if parent is unrealized, that does force
1380 * us to be unrealized.
1381 *
1382 * The reason we don't force children to realize with
1383 * parents is _clutter_actor_rerealize(); if we require that
1384 * a realized parent means children are realized, then to
1385 * unrealize an actor we would have to unrealize its
1386 * parents, which would end up meaning unrealizing and
1387 * hiding the entire stage. So we allow unrealizing a
1388 * child (as long as that child is not mapped) while that
1389 * child still has a realized parent.
1390 *
1391 * Also, if we unrealize from leaf nodes to root, and
1392 * realize from root to leaf, the invariants are never
1393 * violated if we allow children to be unrealized
1394 * while parents are realized.
1395 *
1396 * When unmapping, MAP_STATE_MAKE_UNMAPPED is specified
1397 * to force us to unmap, even though parent is still
1398 * mapped. This is because we're unmapping from leaf nodes
1399 * up to root nodes.
1400 */
1401 if (CLUTTER_ACTOR_IS_VISIBLE (self) &&
1402 change != MAP_STATE_MAKE_UNMAPPED)
1403 {
1404 gboolean parent_is_visible_realized_toplevel;
1405
1406 parent_is_visible_realized_toplevel =
1407 (CLUTTER_ACTOR_IS_TOPLEVEL (parent) &&
1408 CLUTTER_ACTOR_IS_VISIBLE (parent) &&
1409 CLUTTER_ACTOR_IS_REALIZED (parent));
1410
1411 if (CLUTTER_ACTOR_IS_MAPPED (parent) ||
1412 parent_is_visible_realized_toplevel)
1413 {
1414 must_be_realized = TRUE;
1415 should_be_mapped = TRUE;
1416 }
1417 }
1418
1419 /* if the actor has been set to be painted even if unmapped
1420 * then we should map it and check for realization as well;
1421 * this is an override for the branch of the scene graph
1422 * which begins with this node
1423 */
1424 if (priv->enable_paint_unmapped)
1425 {
1426 should_be_mapped = TRUE;
1427 must_be_realized = TRUE;
1428 }
1429
1430 if (!CLUTTER_ACTOR_IS_REALIZED (parent))
1431 may_be_realized = FALSE;
1432 }
1433
1434 if (change == MAP_STATE_MAKE_MAPPED && !should_be_mapped)
1435 {
1436 if (parent == NULL)
1437 g_warning ("Attempting to map a child that does not "
1438 "meet the necessary invariants: the actor '%s' "
1439 "has no parent",
1440 _clutter_actor_get_debug_name (self));
1441 else
1442 g_warning ("Attempting to map a child that does not "
1443 "meet the necessary invariants: the actor '%s' "
1444 "is parented to an unmapped actor '%s'",
1445 _clutter_actor_get_debug_name (self),
1446 _clutter_actor_get_debug_name (priv->parent));
1447 }
1448
1449 /* If in reparent, we temporarily suspend unmap and unrealize.
1450 *
1451 * We want to go in the order "realize, map" and "unmap, unrealize"
1452 */
1453
1454 /* Unmap */
1455 if (!should_be_mapped && !CLUTTER_ACTOR_IN_REPARENT (self))
1456 clutter_actor_set_mapped (self, FALSE);
1457
1458 /* Realize */
1459 if (must_be_realized)
1460 clutter_actor_realize (self);
1461
1462 /* if we must be realized then we may be, presumably */
1463 g_assert (!(must_be_realized && !may_be_realized));
1464
1465 /* Unrealize */
1466 if (!may_be_realized && !CLUTTER_ACTOR_IN_REPARENT (self))
1467 clutter_actor_unrealize_not_hiding (self);
1468
1469 /* Map */
1470 if (should_be_mapped)
1471 {
1472 if (!must_be_realized)
1473 g_warning ("Somehow we think actor '%s' should be mapped but "
1474 "not realized, which isn't allowed",
1475 _clutter_actor_get_debug_name (self));
1476
1477 /* realization is allowed to fail (though I don't know what
1478 * an app is supposed to do about that - shouldn't it just
1479 * be a g_error? anyway, we have to avoid mapping if this
1480 * happens)
1481 */
1482 if (CLUTTER_ACTOR_IS_REALIZED (self))
1483 clutter_actor_set_mapped (self, TRUE);
1484 }
1485 }
1486
1487 #ifdef CLUTTER_ENABLE_DEBUG
1488 /* check all invariants were kept */
1489 clutter_actor_verify_map_state (self);
1490 #endif
1491 }
1492
1493 static void
clutter_actor_real_map(ClutterActor * self)1494 clutter_actor_real_map (ClutterActor *self)
1495 {
1496 ClutterActorPrivate *priv = self->priv;
1497 ClutterActor *stage, *iter;
1498
1499 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1500
1501 CLUTTER_NOTE (ACTOR, "Mapping actor '%s'",
1502 _clutter_actor_get_debug_name (self));
1503
1504 CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1505
1506 stage = _clutter_actor_get_stage_internal (self);
1507 priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self);
1508
1509 CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'",
1510 priv->pick_id,
1511 _clutter_actor_get_debug_name (self));
1512
1513 /* notify on parent mapped before potentially mapping
1514 * children, so apps see a top-down notification.
1515 */
1516 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1517
1518 for (iter = self->priv->first_child;
1519 iter != NULL;
1520 iter = iter->priv->next_sibling)
1521 {
1522 clutter_actor_map (iter);
1523 }
1524 }
1525
1526 /**
1527 * clutter_actor_map:
1528 * @self: A #ClutterActor
1529 *
1530 * Sets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly maps
1531 * and realizes its children if they are visible. Does nothing if the
1532 * actor is not visible.
1533 *
1534 * Calling this function is strongly disencouraged: the default
1535 * implementation of #ClutterActorClass.map() will map all the children
1536 * of an actor when mapping its parent.
1537 *
1538 * When overriding map, it is mandatory to chain up to the parent
1539 * implementation.
1540 *
1541 * Since: 1.0
1542 */
1543 void
clutter_actor_map(ClutterActor * self)1544 clutter_actor_map (ClutterActor *self)
1545 {
1546 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1547
1548 if (CLUTTER_ACTOR_IS_MAPPED (self))
1549 return;
1550
1551 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1552 return;
1553
1554 clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
1555 }
1556
1557 /**
1558 * clutter_actor_is_mapped:
1559 * @self: a #ClutterActor
1560 *
1561 * Checks whether a #ClutterActor has been set as mapped.
1562 *
1563 * See also %CLUTTER_ACTOR_IS_MAPPED and #ClutterActor:mapped
1564 *
1565 * Returns: %TRUE if the actor is mapped
1566 *
1567 * Since: 1.24
1568 */
1569 gboolean
clutter_actor_is_mapped(ClutterActor * self)1570 clutter_actor_is_mapped (ClutterActor *self)
1571 {
1572 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1573
1574 return CLUTTER_ACTOR_IS_MAPPED (self);
1575 }
1576
1577 static void
clutter_actor_real_unmap(ClutterActor * self)1578 clutter_actor_real_unmap (ClutterActor *self)
1579 {
1580 ClutterActorPrivate *priv = self->priv;
1581 ClutterActor *iter;
1582
1583 g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1584
1585 CLUTTER_NOTE (ACTOR, "Unmapping actor '%s'",
1586 _clutter_actor_get_debug_name (self));
1587
1588 for (iter = self->priv->first_child;
1589 iter != NULL;
1590 iter = iter->priv->next_sibling)
1591 {
1592 clutter_actor_unmap (iter);
1593 }
1594
1595 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1596
1597 /* clear the contents of the last paint volume, so that hiding + moving +
1598 * showing will not result in the wrong area being repainted
1599 */
1600 _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
1601 priv->last_paint_volume_valid = TRUE;
1602
1603 /* notify on parent mapped after potentially unmapping
1604 * children, so apps see a bottom-up notification.
1605 */
1606 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1607
1608 /* relinquish keyboard focus if we were unmapped while owning it */
1609 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
1610 {
1611 ClutterStage *stage;
1612
1613 stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
1614
1615 if (stage != NULL)
1616 _clutter_stage_release_pick_id (stage, priv->pick_id);
1617
1618 priv->pick_id = -1;
1619
1620 if (stage != NULL &&
1621 clutter_stage_get_key_focus (stage) == self)
1622 {
1623 clutter_stage_set_key_focus (stage, NULL);
1624 }
1625 }
1626 }
1627
1628 /**
1629 * clutter_actor_unmap:
1630 * @self: A #ClutterActor
1631 *
1632 * Unsets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly
1633 * unmaps its children if they were mapped.
1634 *
1635 * Calling this function is not encouraged: the default #ClutterActor
1636 * implementation of #ClutterActorClass.unmap() will also unmap any
1637 * eventual children by default when their parent is unmapped.
1638 *
1639 * When overriding #ClutterActorClass.unmap(), it is mandatory to
1640 * chain up to the parent implementation.
1641 *
1642 * It is important to note that the implementation of the
1643 * #ClutterActorClass.unmap() virtual function may be called after
1644 * the #ClutterActorClass.destroy() or the #GObjectClass.dispose()
1645 * implementation, but it is guaranteed to be called before the
1646 * #GObjectClass.finalize() implementation.
1647 *
1648 * Since: 1.0
1649 */
1650 void
clutter_actor_unmap(ClutterActor * self)1651 clutter_actor_unmap (ClutterActor *self)
1652 {
1653 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1654
1655 if (!CLUTTER_ACTOR_IS_MAPPED (self))
1656 return;
1657
1658 clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNMAPPED);
1659 }
1660
1661 static void
clutter_actor_real_show(ClutterActor * self)1662 clutter_actor_real_show (ClutterActor *self)
1663 {
1664 ClutterActorPrivate *priv = self->priv;
1665
1666 if (CLUTTER_ACTOR_IS_VISIBLE (self))
1667 return;
1668
1669 CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1670
1671 /* we notify on the "visible" flag in the clutter_actor_show()
1672 * wrapper so the entire show signal emission completes first,
1673 * and the branch of the scene graph is in a stable state
1674 */
1675 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1676
1677 /* we queue a relayout unless the actor is inside a
1678 * container that explicitly told us not to
1679 */
1680 if (priv->parent != NULL &&
1681 (!(priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)))
1682 {
1683 /* While an actor is hidden the parent may not have
1684 * allocated/requested so we need to start from scratch
1685 * and avoid the short-circuiting in
1686 * clutter_actor_queue_relayout().
1687 */
1688 priv->needs_width_request = FALSE;
1689 priv->needs_height_request = FALSE;
1690 priv->needs_allocation = FALSE;
1691
1692 clutter_actor_queue_relayout (self);
1693 }
1694 }
1695
1696 static inline void
set_show_on_set_parent(ClutterActor * self,gboolean set_show)1697 set_show_on_set_parent (ClutterActor *self,
1698 gboolean set_show)
1699 {
1700 ClutterActorPrivate *priv = self->priv;
1701
1702 set_show = !!set_show;
1703
1704 if (priv->show_on_set_parent == set_show)
1705 return;
1706
1707 if (priv->parent == NULL)
1708 {
1709 priv->show_on_set_parent = set_show;
1710 g_object_notify_by_pspec (G_OBJECT (self),
1711 obj_props[PROP_SHOW_ON_SET_PARENT]);
1712 }
1713 }
1714
1715 /**
1716 * clutter_actor_show:
1717 * @self: A #ClutterActor
1718 *
1719 * Flags an actor to be displayed. An actor that isn't shown will not
1720 * be rendered on the stage.
1721 *
1722 * Actors are visible by default.
1723 *
1724 * If this function is called on an actor without a parent, the
1725 * #ClutterActor:show-on-set-parent will be set to %TRUE as a side
1726 * effect.
1727 */
1728 void
clutter_actor_show(ClutterActor * self)1729 clutter_actor_show (ClutterActor *self)
1730 {
1731 ClutterActorPrivate *priv;
1732
1733 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1734
1735 /* simple optimization */
1736 if (CLUTTER_ACTOR_IS_VISIBLE (self))
1737 {
1738 /* we still need to set the :show-on-set-parent property, in
1739 * case show() is called on an unparented actor
1740 */
1741 set_show_on_set_parent (self, TRUE);
1742 return;
1743 }
1744
1745 #ifdef CLUTTER_ENABLE_DEBUG
1746 clutter_actor_verify_map_state (self);
1747 #endif
1748
1749 priv = self->priv;
1750
1751 g_object_freeze_notify (G_OBJECT (self));
1752
1753 set_show_on_set_parent (self, TRUE);
1754
1755 /* if we're showing a child that needs to expand, or may
1756 * expand, then we need to recompute the expand flags for
1757 * its parent as well
1758 */
1759 if (priv->needs_compute_expand ||
1760 priv->needs_x_expand ||
1761 priv->needs_y_expand)
1762 {
1763 clutter_actor_queue_compute_expand (self);
1764 }
1765
1766 g_signal_emit (self, actor_signals[SHOW], 0);
1767 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1768
1769 if (priv->parent != NULL)
1770 clutter_actor_queue_redraw (priv->parent);
1771
1772 g_object_thaw_notify (G_OBJECT (self));
1773 }
1774
1775 /**
1776 * clutter_actor_is_visible:
1777 * @self: a #ClutterActor
1778 *
1779 * Checks whether an actor is marked as visible.
1780 *
1781 * See also %CLUTTER_ACTOR_IS_VISIBLE and #ClutterActor:visible.
1782 *
1783 * Returns: %TRUE if the actor visible
1784 *
1785 * Since: 1.24
1786 */
1787 gboolean
clutter_actor_is_visible(ClutterActor * self)1788 clutter_actor_is_visible (ClutterActor *self)
1789 {
1790 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1791
1792 return CLUTTER_ACTOR_IS_VISIBLE (self);
1793 }
1794
1795 /**
1796 * clutter_actor_show_all:
1797 * @self: a #ClutterActor
1798 *
1799 * Calls clutter_actor_show() on all children of an actor (if any).
1800 *
1801 * Since: 0.2
1802 *
1803 * Deprecated: 1.10: Actors are visible by default
1804 */
1805 void
clutter_actor_show_all(ClutterActor * self)1806 clutter_actor_show_all (ClutterActor *self)
1807 {
1808 ClutterActorClass *klass;
1809
1810 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1811
1812 klass = CLUTTER_ACTOR_GET_CLASS (self);
1813 if (klass->show_all)
1814 klass->show_all (self);
1815 }
1816
1817 static void
clutter_actor_real_hide(ClutterActor * self)1818 clutter_actor_real_hide (ClutterActor *self)
1819 {
1820 ClutterActorPrivate *priv = self->priv;
1821
1822 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1823 return;
1824
1825 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1826
1827 /* we notify on the "visible" flag in the clutter_actor_hide()
1828 * wrapper so the entire hide signal emission completes first,
1829 * and the branch of the scene graph is in a stable state
1830 */
1831 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1832
1833 /* we queue a relayout unless the actor is inside a
1834 * container that explicitly told us not to
1835 */
1836 if (priv->parent != NULL &&
1837 (!(priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)))
1838 clutter_actor_queue_relayout (priv->parent);
1839 }
1840
1841 /**
1842 * clutter_actor_hide:
1843 * @self: A #ClutterActor
1844 *
1845 * Flags an actor to be hidden. A hidden actor will not be
1846 * rendered on the stage.
1847 *
1848 * Actors are visible by default.
1849 *
1850 * If this function is called on an actor without a parent, the
1851 * #ClutterActor:show-on-set-parent property will be set to %FALSE
1852 * as a side-effect.
1853 */
1854 void
clutter_actor_hide(ClutterActor * self)1855 clutter_actor_hide (ClutterActor *self)
1856 {
1857 ClutterActorPrivate *priv;
1858
1859 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1860
1861 /* simple optimization */
1862 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1863 {
1864 /* we still need to set the :show-on-set-parent property, in
1865 * case hide() is called on an unparented actor
1866 */
1867 set_show_on_set_parent (self, FALSE);
1868 return;
1869 }
1870
1871 #ifdef CLUTTER_ENABLE_DEBUG
1872 clutter_actor_verify_map_state (self);
1873 #endif
1874
1875 priv = self->priv;
1876
1877 g_object_freeze_notify (G_OBJECT (self));
1878
1879 set_show_on_set_parent (self, FALSE);
1880
1881 /* if we're hiding a child that needs to expand, or may
1882 * expand, then we need to recompute the expand flags for
1883 * its parent as well
1884 */
1885 if (priv->needs_compute_expand ||
1886 priv->needs_x_expand ||
1887 priv->needs_y_expand)
1888 {
1889 clutter_actor_queue_compute_expand (self);
1890 }
1891
1892 g_signal_emit (self, actor_signals[HIDE], 0);
1893 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1894
1895 if (priv->parent != NULL)
1896 clutter_actor_queue_redraw (priv->parent);
1897
1898 g_object_thaw_notify (G_OBJECT (self));
1899 }
1900
1901 /**
1902 * clutter_actor_hide_all:
1903 * @self: a #ClutterActor
1904 *
1905 * Calls clutter_actor_hide() on all child actors (if any).
1906 *
1907 * Since: 0.2
1908 *
1909 * Deprecated: 1.10: Using clutter_actor_hide() on the actor will
1910 * prevent its children from being painted as well.
1911 */
1912 void
clutter_actor_hide_all(ClutterActor * self)1913 clutter_actor_hide_all (ClutterActor *self)
1914 {
1915 ClutterActorClass *klass;
1916
1917 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1918
1919 klass = CLUTTER_ACTOR_GET_CLASS (self);
1920 if (klass->hide_all)
1921 klass->hide_all (self);
1922 }
1923
1924 /**
1925 * clutter_actor_realize:
1926 * @self: A #ClutterActor
1927 *
1928 * Realization informs the actor that it is attached to a stage. It
1929 * can use this to allocate resources if it wanted to delay allocation
1930 * until it would be rendered. However it is perfectly acceptable for
1931 * an actor to create resources before being realized because Clutter
1932 * only ever has a single rendering context so that actor is free to
1933 * be moved from one stage to another.
1934 *
1935 * This function does nothing if the actor is already realized.
1936 *
1937 * Because a realized actor must have realized parent actors, calling
1938 * clutter_actor_realize() will also realize all parents of the actor.
1939 *
1940 * This function does not realize child actors, except in the special
1941 * case that realizing the stage, when the stage is visible, will
1942 * suddenly map (and thus realize) the children of the stage.
1943 *
1944 * Deprecated: 1.16: Actors are automatically realized, and nothing
1945 * requires explicit realization.
1946 */
1947 void
clutter_actor_realize(ClutterActor * self)1948 clutter_actor_realize (ClutterActor *self)
1949 {
1950 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1951
1952 clutter_actor_realize_internal (self);
1953 }
1954
1955 /**
1956 * clutter_actor_is_realized:
1957 * @self: a #ClutterActor
1958 *
1959 * Checks whether a #ClutterActor is realized.
1960 *
1961 * See also %CLUTTER_ACTOR_IS_REALIZED and #ClutterActor:realized.
1962 *
1963 * Returns: %TRUE if the actor is realized
1964 *
1965 * Since: 1.24
1966 */
1967 gboolean
clutter_actor_is_realized(ClutterActor * self)1968 clutter_actor_is_realized (ClutterActor *self)
1969 {
1970 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1971
1972 return CLUTTER_ACTOR_IS_REALIZED (self);
1973 }
1974
1975 static void
clutter_actor_realize_internal(ClutterActor * self)1976 clutter_actor_realize_internal (ClutterActor *self)
1977 {
1978 ClutterActorPrivate *priv = self->priv;
1979
1980 #ifdef CLUTTER_ENABLE_DEBUG
1981 clutter_actor_verify_map_state (self);
1982 #endif
1983
1984 if (CLUTTER_ACTOR_IS_REALIZED (self))
1985 return;
1986
1987 /* To be realized, our parent actors must be realized first.
1988 * This will only succeed if we're inside a toplevel.
1989 */
1990 if (priv->parent != NULL)
1991 clutter_actor_realize (priv->parent);
1992
1993 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1994 {
1995 /* toplevels can be realized at any time */
1996 }
1997 else
1998 {
1999 /* "Fail" the realization if parent is missing or unrealized;
2000 * this should really be a g_warning() not some kind of runtime
2001 * failure; how can an app possibly recover? Instead it's a bug
2002 * in the app and the app should get an explanatory warning so
2003 * someone can fix it. But for now it's too hard to fix this
2004 * because e.g. ClutterTexture needs reworking.
2005 */
2006 if (priv->parent == NULL ||
2007 !CLUTTER_ACTOR_IS_REALIZED (priv->parent))
2008 return;
2009 }
2010
2011 CLUTTER_NOTE (ACTOR, "Realizing actor '%s'", _clutter_actor_get_debug_name (self));
2012
2013 CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2014 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2015
2016 g_signal_emit (self, actor_signals[REALIZE], 0);
2017
2018 /* Stage actor is allowed to unset the realized flag again in its
2019 * default signal handler, though that is a pathological situation.
2020 */
2021
2022 /* If realization "failed" we'll have to update child state. */
2023 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
2024 }
2025
2026 static void
clutter_actor_real_unrealize(ClutterActor * self)2027 clutter_actor_real_unrealize (ClutterActor *self)
2028 {
2029 /* we must be unmapped (implying our children are also unmapped) */
2030 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2031 }
2032
2033 /**
2034 * clutter_actor_unrealize:
2035 * @self: A #ClutterActor
2036 *
2037 * Unrealization informs the actor that it may be being destroyed or
2038 * moved to another stage. The actor may want to destroy any
2039 * underlying graphics resources at this point. However it is
2040 * perfectly acceptable for it to retain the resources until the actor
2041 * is destroyed because Clutter only ever uses a single rendering
2042 * context and all of the graphics resources are valid on any stage.
2043 *
2044 * Because mapped actors must be realized, actors may not be
2045 * unrealized if they are mapped. This function hides the actor to be
2046 * sure it isn't mapped, an application-visible side effect that you
2047 * may not be expecting.
2048 *
2049 * This function should not be called by application code.
2050 *
2051 * This function should not really be in the public API, because
2052 * there isn't a good reason to call it. ClutterActor will already
2053 * unrealize things for you when it's important to do so.
2054 *
2055 * If you were using clutter_actor_unrealize() in a dispose
2056 * implementation, then don't, just chain up to ClutterActor's
2057 * dispose.
2058 *
2059 * If you were using clutter_actor_unrealize() to implement
2060 * unrealizing children of your container, then don't, ClutterActor
2061 * will already take care of that.
2062 *
2063 * Deprecated: 1.16: Actors are automatically unrealized, and nothing
2064 * requires explicit realization.
2065 */
2066 void
clutter_actor_unrealize(ClutterActor * self)2067 clutter_actor_unrealize (ClutterActor *self)
2068 {
2069 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2070 g_return_if_fail (!CLUTTER_ACTOR_IS_MAPPED (self));
2071
2072 clutter_actor_unrealize_internal (self);
2073 }
2074
2075 /* If you were using clutter_actor_unrealize() to re-realize to
2076 * create your resources in a different way, then use
2077 * _clutter_actor_rerealize() (inside Clutter) or just call your
2078 * code that recreates your resources directly (outside Clutter).
2079 */
2080 static void
clutter_actor_unrealize_internal(ClutterActor * self)2081 clutter_actor_unrealize_internal (ClutterActor *self)
2082 {
2083 #ifdef CLUTTER_ENABLE_DEBUG
2084 clutter_actor_verify_map_state (self);
2085 #endif
2086
2087 clutter_actor_hide (self);
2088
2089 clutter_actor_unrealize_not_hiding (self);
2090 }
2091
2092 static ClutterActorTraverseVisitFlags
unrealize_actor_before_children_cb(ClutterActor * self,int depth,void * user_data)2093 unrealize_actor_before_children_cb (ClutterActor *self,
2094 int depth,
2095 void *user_data)
2096 {
2097 /* If an actor is already unrealized we know its children have also
2098 * already been unrealized... */
2099 if (!CLUTTER_ACTOR_IS_REALIZED (self))
2100 return CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN;
2101
2102 g_signal_emit (self, actor_signals[UNREALIZE], 0);
2103
2104 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2105 }
2106
2107 static ClutterActorTraverseVisitFlags
unrealize_actor_after_children_cb(ClutterActor * self,int depth,void * user_data)2108 unrealize_actor_after_children_cb (ClutterActor *self,
2109 int depth,
2110 void *user_data)
2111 {
2112 /* We want to unset the realized flag only _after_
2113 * child actors are unrealized, to maintain invariants.
2114 */
2115 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2116 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2117 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2118 }
2119
2120 /*
2121 * clutter_actor_unrealize_not_hiding:
2122 * @self: A #ClutterActor
2123 *
2124 * Unrealization informs the actor that it may be being destroyed or
2125 * moved to another stage. The actor may want to destroy any
2126 * underlying graphics resources at this point. However it is
2127 * perfectly acceptable for it to retain the resources until the actor
2128 * is destroyed because Clutter only ever uses a single rendering
2129 * context and all of the graphics resources are valid on any stage.
2130 *
2131 * Because mapped actors must be realized, actors may not be
2132 * unrealized if they are mapped. You must hide the actor or one of
2133 * its parents before attempting to unrealize.
2134 *
2135 * This function is separate from clutter_actor_unrealize() because it
2136 * does not automatically hide the actor.
2137 * Actors need not be hidden to be unrealized, they just need to
2138 * be unmapped. In fact we don't want to mess up the application's
2139 * setting of the "visible" flag, so hiding is very undesirable.
2140 *
2141 * clutter_actor_unrealize() does a clutter_actor_hide() just for
2142 * backward compatibility.
2143 */
2144 static void
clutter_actor_unrealize_not_hiding(ClutterActor * self)2145 clutter_actor_unrealize_not_hiding (ClutterActor *self)
2146 {
2147 _clutter_actor_traverse (self,
2148 CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
2149 unrealize_actor_before_children_cb,
2150 unrealize_actor_after_children_cb,
2151 NULL);
2152 }
2153
2154 /*
2155 * _clutter_actor_rerealize:
2156 * @self: A #ClutterActor
2157 * @callback: Function to call while unrealized
2158 * @data: data for callback
2159 *
2160 * If an actor is already unrealized, this just calls the callback.
2161 *
2162 * If it is realized, it unrealizes temporarily, calls the callback,
2163 * and then re-realizes the actor.
2164 *
2165 * As a side effect, leaves all children of the actor unrealized if
2166 * the actor was realized but not showing. This is because when we
2167 * unrealize the actor temporarily we must unrealize its children
2168 * (e.g. children of a stage can't be realized if stage window is
2169 * gone). And we aren't clever enough to save the realization state of
2170 * all children. In most cases this should not matter, because
2171 * the children will automatically realize when they next become mapped.
2172 */
2173 void
_clutter_actor_rerealize(ClutterActor * self,ClutterCallback callback,void * data)2174 _clutter_actor_rerealize (ClutterActor *self,
2175 ClutterCallback callback,
2176 void *data)
2177 {
2178 gboolean was_mapped;
2179 gboolean was_showing;
2180 gboolean was_realized;
2181
2182 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2183
2184 #ifdef CLUTTER_ENABLE_DEBUG
2185 clutter_actor_verify_map_state (self);
2186 #endif
2187
2188 was_realized = CLUTTER_ACTOR_IS_REALIZED (self);
2189 was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
2190 was_showing = CLUTTER_ACTOR_IS_VISIBLE (self);
2191
2192 /* Must be unmapped to unrealize. Note we only have to hide this
2193 * actor if it was mapped (if all parents were showing). If actor
2194 * is merely visible (but not mapped), then that's fine, we can
2195 * leave it visible.
2196 */
2197 if (was_mapped)
2198 clutter_actor_hide (self);
2199
2200 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2201
2202 /* unrealize self and all children */
2203 clutter_actor_unrealize_not_hiding (self);
2204
2205 if (callback != NULL)
2206 {
2207 (* callback) (self, data);
2208 }
2209
2210 if (was_showing)
2211 clutter_actor_show (self); /* will realize only if mapping implies it */
2212 else if (was_realized)
2213 clutter_actor_realize (self); /* realize self and all parents */
2214 }
2215
2216 static void
clutter_actor_real_pick(ClutterActor * self,const ClutterColor * color)2217 clutter_actor_real_pick (ClutterActor *self,
2218 const ClutterColor *color)
2219 {
2220 /* the default implementation is just to paint a rectangle
2221 * with the same size of the actor using the passed color
2222 */
2223 if (clutter_actor_should_pick_paint (self))
2224 {
2225 ClutterActorBox box = { 0, };
2226 float width, height;
2227
2228 clutter_actor_get_allocation_box (self, &box);
2229
2230 width = box.x2 - box.x1;
2231 height = box.y2 - box.y1;
2232
2233 cogl_set_source_color4ub (color->red,
2234 color->green,
2235 color->blue,
2236 color->alpha);
2237
2238 cogl_rectangle (0, 0, width, height);
2239 }
2240
2241 /* XXX - this thoroughly sucks, but we need to maintain compatibility
2242 * with existing container classes that override the pick() virtual
2243 * and chain up to the default implementation - otherwise we'll end up
2244 * painting our children twice.
2245 *
2246 * this has to go away for 2.0; hopefully along the pick() itself.
2247 */
2248 if (CLUTTER_ACTOR_GET_CLASS (self)->pick == clutter_actor_real_pick)
2249 {
2250 ClutterActor *iter;
2251
2252 for (iter = self->priv->first_child;
2253 iter != NULL;
2254 iter = iter->priv->next_sibling)
2255 clutter_actor_paint (iter);
2256 }
2257 }
2258
2259 /**
2260 * clutter_actor_should_pick_paint:
2261 * @self: A #ClutterActor
2262 *
2263 * Should be called inside the implementation of the
2264 * #ClutterActor::pick virtual function in order to check whether
2265 * the actor should paint itself in pick mode or not.
2266 *
2267 * This function should never be called directly by applications.
2268 *
2269 * Return value: %TRUE if the actor should paint its silhouette,
2270 * %FALSE otherwise
2271 */
2272 gboolean
clutter_actor_should_pick_paint(ClutterActor * self)2273 clutter_actor_should_pick_paint (ClutterActor *self)
2274 {
2275 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2276
2277 if (CLUTTER_ACTOR_IS_MAPPED (self) &&
2278 (_clutter_context_get_pick_mode () == CLUTTER_PICK_ALL ||
2279 CLUTTER_ACTOR_IS_REACTIVE (self)))
2280 return TRUE;
2281
2282 return FALSE;
2283 }
2284
2285 static void
clutter_actor_real_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)2286 clutter_actor_real_get_preferred_width (ClutterActor *self,
2287 gfloat for_height,
2288 gfloat *min_width_p,
2289 gfloat *natural_width_p)
2290 {
2291 ClutterActorPrivate *priv = self->priv;
2292
2293 if (priv->n_children != 0 &&
2294 priv->layout_manager != NULL)
2295 {
2296 ClutterContainer *container = CLUTTER_CONTAINER (self);
2297
2298 CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2299 "for the preferred width",
2300 G_OBJECT_TYPE_NAME (priv->layout_manager),
2301 priv->layout_manager);
2302
2303 clutter_layout_manager_get_preferred_width (priv->layout_manager,
2304 container,
2305 for_height,
2306 min_width_p,
2307 natural_width_p);
2308
2309 return;
2310 }
2311
2312 /* Default implementation is always 0x0, usually an actor
2313 * using this default is relying on someone to set the
2314 * request manually
2315 */
2316 CLUTTER_NOTE (LAYOUT, "Default preferred width: 0, 0");
2317
2318 if (min_width_p)
2319 *min_width_p = 0;
2320
2321 if (natural_width_p)
2322 *natural_width_p = 0;
2323 }
2324
2325 static void
clutter_actor_real_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)2326 clutter_actor_real_get_preferred_height (ClutterActor *self,
2327 gfloat for_width,
2328 gfloat *min_height_p,
2329 gfloat *natural_height_p)
2330 {
2331 ClutterActorPrivate *priv = self->priv;
2332
2333 if (priv->n_children != 0 &&
2334 priv->layout_manager != NULL)
2335 {
2336 ClutterContainer *container = CLUTTER_CONTAINER (self);
2337
2338 CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2339 "for the preferred height",
2340 G_OBJECT_TYPE_NAME (priv->layout_manager),
2341 priv->layout_manager);
2342
2343 clutter_layout_manager_get_preferred_height (priv->layout_manager,
2344 container,
2345 for_width,
2346 min_height_p,
2347 natural_height_p);
2348
2349 return;
2350 }
2351 /* Default implementation is always 0x0, usually an actor
2352 * using this default is relying on someone to set the
2353 * request manually
2354 */
2355 CLUTTER_NOTE (LAYOUT, "Default preferred height: 0, 0");
2356
2357 if (min_height_p)
2358 *min_height_p = 0;
2359
2360 if (natural_height_p)
2361 *natural_height_p = 0;
2362 }
2363
2364 static void
clutter_actor_store_old_geometry(ClutterActor * self,ClutterActorBox * box)2365 clutter_actor_store_old_geometry (ClutterActor *self,
2366 ClutterActorBox *box)
2367 {
2368 *box = self->priv->allocation;
2369 }
2370
2371 static inline void
clutter_actor_notify_if_geometry_changed(ClutterActor * self,const ClutterActorBox * old)2372 clutter_actor_notify_if_geometry_changed (ClutterActor *self,
2373 const ClutterActorBox *old)
2374 {
2375 ClutterActorPrivate *priv = self->priv;
2376 GObject *obj = G_OBJECT (self);
2377
2378 g_object_freeze_notify (obj);
2379
2380 /* to avoid excessive requisition or allocation cycles we
2381 * use the cached values.
2382 *
2383 * - if we don't have an allocation we assume that we need
2384 * to notify anyway
2385 * - if we don't have a width or a height request we notify
2386 * width and height
2387 * - if we have a valid allocation then we check the old
2388 * bounding box with the current allocation and we notify
2389 * the changes
2390 */
2391 if (priv->needs_allocation)
2392 {
2393 g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2394 g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2395 g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2396 g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2397 g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2398 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2399 }
2400 else if (priv->needs_width_request || priv->needs_height_request)
2401 {
2402 g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2403 g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2404 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2405 }
2406 else
2407 {
2408 gfloat x, y;
2409 gfloat width, height;
2410
2411 x = priv->allocation.x1;
2412 y = priv->allocation.y1;
2413 width = priv->allocation.x2 - priv->allocation.x1;
2414 height = priv->allocation.y2 - priv->allocation.y1;
2415
2416 if (x != old->x1)
2417 {
2418 g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2419 g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2420 }
2421
2422 if (y != old->y1)
2423 {
2424 g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2425 g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2426 }
2427
2428 if (width != (old->x2 - old->x1))
2429 {
2430 g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2431 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2432 }
2433
2434 if (height != (old->y2 - old->y1))
2435 {
2436 g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2437 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2438 }
2439 }
2440
2441 g_object_thaw_notify (obj);
2442 }
2443
2444 /*< private >
2445 * clutter_actor_set_allocation_internal:
2446 * @self: a #ClutterActor
2447 * @box: a #ClutterActorBox
2448 * @flags: allocation flags
2449 *
2450 * Stores the allocation of @self.
2451 *
2452 * This function only performs basic storage and property notification.
2453 *
2454 * This function should be called by clutter_actor_set_allocation()
2455 * and by the default implementation of #ClutterActorClass.allocate().
2456 *
2457 * Return value: %TRUE if the allocation of the #ClutterActor has been
2458 * changed, and %FALSE otherwise
2459 */
2460 static inline gboolean
clutter_actor_set_allocation_internal(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)2461 clutter_actor_set_allocation_internal (ClutterActor *self,
2462 const ClutterActorBox *box,
2463 ClutterAllocationFlags flags)
2464 {
2465 ClutterActorPrivate *priv = self->priv;
2466 GObject *obj;
2467 gboolean x1_changed, y1_changed, x2_changed, y2_changed;
2468 gboolean retval;
2469 ClutterActorBox old_alloc = { 0, };
2470
2471 obj = G_OBJECT (self);
2472
2473 g_object_freeze_notify (obj);
2474
2475 clutter_actor_store_old_geometry (self, &old_alloc);
2476
2477 x1_changed = priv->allocation.x1 != box->x1;
2478 y1_changed = priv->allocation.y1 != box->y1;
2479 x2_changed = priv->allocation.x2 != box->x2;
2480 y2_changed = priv->allocation.y2 != box->y2;
2481
2482 priv->allocation = *box;
2483 priv->allocation_flags = flags;
2484
2485 /* allocation is authoritative */
2486 priv->needs_width_request = FALSE;
2487 priv->needs_height_request = FALSE;
2488 priv->needs_allocation = FALSE;
2489
2490 if (x1_changed ||
2491 y1_changed ||
2492 x2_changed ||
2493 y2_changed)
2494 {
2495 CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed",
2496 _clutter_actor_get_debug_name (self));
2497
2498 priv->transform_valid = FALSE;
2499
2500 g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
2501
2502 /* if the allocation changes, so does the content box */
2503 if (priv->content != NULL)
2504 {
2505 priv->content_box_valid = FALSE;
2506 g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]);
2507 }
2508
2509 retval = TRUE;
2510 }
2511 else
2512 retval = FALSE;
2513
2514 clutter_actor_notify_if_geometry_changed (self, &old_alloc);
2515
2516 g_object_thaw_notify (obj);
2517
2518 return retval;
2519 }
2520
2521 static void clutter_actor_real_allocate (ClutterActor *self,
2522 const ClutterActorBox *box,
2523 ClutterAllocationFlags flags);
2524
2525 static inline void
clutter_actor_maybe_layout_children(ClutterActor * self,const ClutterActorBox * allocation,ClutterAllocationFlags flags)2526 clutter_actor_maybe_layout_children (ClutterActor *self,
2527 const ClutterActorBox *allocation,
2528 ClutterAllocationFlags flags)
2529 {
2530 ClutterActorPrivate *priv = self->priv;
2531
2532 /* this is going to be a bit hard to follow, so let's put an explanation
2533 * here.
2534 *
2535 * we want ClutterActor to have a default layout manager if the actor was
2536 * created using "g_object_new (CLUTTER_TYPE_ACTOR, NULL)".
2537 *
2538 * we also want any subclass of ClutterActor that does not override the
2539 * ::allocate() virtual function to delegate to a layout manager.
2540 *
2541 * finally, we want to allow people subclassing ClutterActor and overriding
2542 * the ::allocate() vfunc to let Clutter delegate to the layout manager.
2543 *
2544 * on the other hand, we want existing actor subclasses overriding the
2545 * ::allocate() virtual function and chaining up to the parent's
2546 * implementation to continue working without allocating their children
2547 * twice, or without entering an allocation loop.
2548 *
2549 * for the first two points, we check if the class of the actor is
2550 * overridding the ::allocate() virtual function; if it isn't, then we
2551 * follow through with checking whether we have children and a layout
2552 * manager, and eventually calling clutter_layout_manager_allocate().
2553 *
2554 * for the third point, we check the CLUTTER_DELEGATE_LAYOUT flag in the
2555 * allocation flags that we got passed, and if it is present, we continue
2556 * with the check above.
2557 *
2558 * if neither of these two checks yields a positive result, we just
2559 * assume that the ::allocate() virtual function that resulted in this
2560 * function being called will also allocate the children of the actor.
2561 */
2562
2563 if (CLUTTER_ACTOR_GET_CLASS (self)->allocate == clutter_actor_real_allocate)
2564 goto check_layout;
2565
2566 if ((flags & CLUTTER_DELEGATE_LAYOUT) != 0)
2567 goto check_layout;
2568
2569 return;
2570
2571 check_layout:
2572 if (priv->n_children != 0 &&
2573 priv->layout_manager != NULL)
2574 {
2575 ClutterContainer *container = CLUTTER_CONTAINER (self);
2576 ClutterAllocationFlags children_flags;
2577 ClutterActorBox children_box;
2578
2579 /* normalize the box passed to the layout manager */
2580 children_box.x1 = children_box.y1 = 0.f;
2581 children_box.x2 = (allocation->x2 - allocation->x1);
2582 children_box.y2 = (allocation->y2 - allocation->y1);
2583
2584 /* remove the DELEGATE_LAYOUT flag; this won't be passed to
2585 * the actor's children, since it refers only to the current
2586 * actor's allocation.
2587 */
2588 children_flags = flags;
2589 children_flags &= ~CLUTTER_DELEGATE_LAYOUT;
2590
2591 CLUTTER_NOTE (LAYOUT,
2592 "Allocating %d children of %s "
2593 "at { %.2f, %.2f - %.2f x %.2f } "
2594 "using %s",
2595 priv->n_children,
2596 _clutter_actor_get_debug_name (self),
2597 allocation->x1,
2598 allocation->y1,
2599 (allocation->x2 - allocation->x1),
2600 (allocation->y2 - allocation->y1),
2601 G_OBJECT_TYPE_NAME (priv->layout_manager));
2602
2603 clutter_layout_manager_allocate (priv->layout_manager,
2604 container,
2605 &children_box,
2606 children_flags);
2607 }
2608 }
2609
2610 static void
clutter_actor_real_allocate(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)2611 clutter_actor_real_allocate (ClutterActor *self,
2612 const ClutterActorBox *box,
2613 ClutterAllocationFlags flags)
2614 {
2615 ClutterActorPrivate *priv = self->priv;
2616 gboolean changed;
2617
2618 g_object_freeze_notify (G_OBJECT (self));
2619
2620 changed = clutter_actor_set_allocation_internal (self, box, flags);
2621
2622 /* we allocate our children before we notify changes in our geometry,
2623 * so that people connecting to properties will be able to get valid
2624 * data out of the sub-tree of the scene graph that has this actor at
2625 * the root.
2626 */
2627 clutter_actor_maybe_layout_children (self, box, flags);
2628
2629 if (changed)
2630 {
2631 ClutterActorBox signal_box = priv->allocation;
2632 ClutterAllocationFlags signal_flags = priv->allocation_flags;
2633
2634 g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
2635 &signal_box,
2636 signal_flags);
2637 }
2638
2639 g_object_thaw_notify (G_OBJECT (self));
2640 }
2641
2642 static void
_clutter_actor_signal_queue_redraw(ClutterActor * self,ClutterActor * origin)2643 _clutter_actor_signal_queue_redraw (ClutterActor *self,
2644 ClutterActor *origin)
2645 {
2646 /* no point in queuing a redraw on a destroyed actor */
2647 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2648 return;
2649
2650 /* NB: We can't bail out early here if the actor is hidden in case
2651 * the actor bas been cloned. In this case the clone will need to
2652 * receive the signal so it can queue its own redraw.
2653 */
2654
2655 _clutter_actor_queue_redraw_on_clones (self);
2656
2657 /* calls klass->queue_redraw in default handler */
2658 g_signal_emit (self, actor_signals[QUEUE_REDRAW], 0, origin);
2659 }
2660
2661 static void
clutter_actor_real_queue_redraw(ClutterActor * self,ClutterActor * origin)2662 clutter_actor_real_queue_redraw (ClutterActor *self,
2663 ClutterActor *origin)
2664 {
2665 ClutterActor *parent;
2666
2667 CLUTTER_NOTE (PAINT, "Redraw queued on '%s' (from: '%s')",
2668 _clutter_actor_get_debug_name (self),
2669 origin != NULL ? _clutter_actor_get_debug_name (origin)
2670 : "same actor");
2671
2672 /* no point in queuing a redraw on a destroyed actor */
2673 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2674 return;
2675
2676 /* If the queue redraw is coming from a child then the actor has
2677 become dirty and any queued effect is no longer valid */
2678 if (self != origin)
2679 {
2680 self->priv->is_dirty = TRUE;
2681 self->priv->effect_to_redraw = NULL;
2682 }
2683
2684 /* If the actor isn't visible, we still had to emit the signal
2685 * to allow for a ClutterClone, but the appearance of the parent
2686 * won't change so we don't have to propagate up the hierarchy.
2687 */
2688 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
2689 return;
2690
2691 /* Although we could determine here that a full stage redraw
2692 * has already been queued and immediately bail out, we actually
2693 * guarantee that we will propagate a queue-redraw signal to our
2694 * parent at least once so that it's possible to implement a
2695 * container that tracks which of its children have queued a
2696 * redraw.
2697 */
2698 if (self->priv->propagated_one_redraw)
2699 {
2700 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2701 if (stage != NULL &&
2702 _clutter_stage_has_full_redraw_queued (CLUTTER_STAGE (stage)))
2703 return;
2704 }
2705
2706 self->priv->propagated_one_redraw = TRUE;
2707
2708 /* notify parents, if they are all visible eventually we'll
2709 * queue redraw on the stage, which queues the redraw idle.
2710 */
2711 parent = clutter_actor_get_parent (self);
2712 if (parent != NULL)
2713 {
2714 /* this will go up recursively */
2715 _clutter_actor_signal_queue_redraw (parent, origin);
2716 }
2717 }
2718
2719 static void
clutter_actor_real_queue_relayout(ClutterActor * self)2720 clutter_actor_real_queue_relayout (ClutterActor *self)
2721 {
2722 ClutterActorPrivate *priv = self->priv;
2723
2724 /* no point in queueing a redraw on a destroyed actor */
2725 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2726 return;
2727
2728 priv->needs_width_request = TRUE;
2729 priv->needs_height_request = TRUE;
2730 priv->needs_allocation = TRUE;
2731
2732 /* reset the cached size requests */
2733 memset (priv->width_requests, 0,
2734 N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2735 memset (priv->height_requests, 0,
2736 N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2737
2738 /* We need to go all the way up the hierarchy */
2739 if (priv->parent != NULL)
2740 _clutter_actor_queue_only_relayout (priv->parent);
2741 }
2742
2743 /**
2744 * clutter_actor_apply_relative_transform_to_point:
2745 * @self: A #ClutterActor
2746 * @ancestor: (allow-none): A #ClutterActor ancestor, or %NULL to use the
2747 * default #ClutterStage
2748 * @point: A point as #ClutterVertex
2749 * @vertex: (out caller-allocates): The translated #ClutterVertex
2750 *
2751 * Transforms @point in coordinates relative to the actor into
2752 * ancestor-relative coordinates using the relevant transform
2753 * stack (i.e. scale, rotation, etc).
2754 *
2755 * If @ancestor is %NULL the ancestor will be the #ClutterStage. In
2756 * this case, the coordinates returned will be the coordinates on
2757 * the stage before the projection is applied. This is different from
2758 * the behaviour of clutter_actor_apply_transform_to_point().
2759 *
2760 * Since: 0.6
2761 */
2762 void
clutter_actor_apply_relative_transform_to_point(ClutterActor * self,ClutterActor * ancestor,const ClutterVertex * point,ClutterVertex * vertex)2763 clutter_actor_apply_relative_transform_to_point (ClutterActor *self,
2764 ClutterActor *ancestor,
2765 const ClutterVertex *point,
2766 ClutterVertex *vertex)
2767 {
2768 gfloat w;
2769 CoglMatrix matrix;
2770
2771 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2772 g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
2773 g_return_if_fail (point != NULL);
2774 g_return_if_fail (vertex != NULL);
2775
2776 *vertex = *point;
2777 w = 1.0;
2778
2779 if (ancestor == NULL)
2780 ancestor = _clutter_actor_get_stage_internal (self);
2781
2782 if (ancestor == NULL)
2783 {
2784 *vertex = *point;
2785 return;
2786 }
2787
2788 _clutter_actor_get_relative_transformation_matrix (self, ancestor, &matrix);
2789 cogl_matrix_transform_point (&matrix, &vertex->x, &vertex->y, &vertex->z, &w);
2790 }
2791
2792 static gboolean
_clutter_actor_fully_transform_vertices(ClutterActor * self,const ClutterVertex * vertices_in,ClutterVertex * vertices_out,int n_vertices)2793 _clutter_actor_fully_transform_vertices (ClutterActor *self,
2794 const ClutterVertex *vertices_in,
2795 ClutterVertex *vertices_out,
2796 int n_vertices)
2797 {
2798 ClutterActor *stage;
2799 CoglMatrix modelview;
2800 CoglMatrix projection;
2801 float viewport[4];
2802
2803 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2804
2805 stage = _clutter_actor_get_stage_internal (self);
2806
2807 /* We really can't do anything meaningful in this case so don't try
2808 * to do any transform */
2809 if (stage == NULL)
2810 return FALSE;
2811
2812 /* Note: we pass NULL as the ancestor because we don't just want the modelview
2813 * that gets us to stage coordinates, we want to go all the way to eye
2814 * coordinates */
2815 _clutter_actor_apply_relative_transformation_matrix (self, NULL, &modelview);
2816
2817 /* Fetch the projection and viewport */
2818 _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
2819 _clutter_stage_get_viewport (CLUTTER_STAGE (stage),
2820 &viewport[0],
2821 &viewport[1],
2822 &viewport[2],
2823 &viewport[3]);
2824
2825 _clutter_util_fully_transform_vertices (&modelview,
2826 &projection,
2827 viewport,
2828 vertices_in,
2829 vertices_out,
2830 n_vertices);
2831
2832 return TRUE;
2833 }
2834
2835 /**
2836 * clutter_actor_apply_transform_to_point:
2837 * @self: A #ClutterActor
2838 * @point: A point as #ClutterVertex
2839 * @vertex: (out caller-allocates): The translated #ClutterVertex
2840 *
2841 * Transforms @point in coordinates relative to the actor
2842 * into screen-relative coordinates with the current actor
2843 * transformation (i.e. scale, rotation, etc)
2844 *
2845 * Since: 0.4
2846 **/
2847 void
clutter_actor_apply_transform_to_point(ClutterActor * self,const ClutterVertex * point,ClutterVertex * vertex)2848 clutter_actor_apply_transform_to_point (ClutterActor *self,
2849 const ClutterVertex *point,
2850 ClutterVertex *vertex)
2851 {
2852 g_return_if_fail (point != NULL);
2853 g_return_if_fail (vertex != NULL);
2854 _clutter_actor_fully_transform_vertices (self, point, vertex, 1);
2855 }
2856
2857 /*
2858 * _clutter_actor_get_relative_transformation_matrix:
2859 * @self: The actor whose coordinate space you want to transform from.
2860 * @ancestor: The ancestor actor whose coordinate space you want to transform too
2861 * or %NULL if you want to transform all the way to eye coordinates.
2862 * @matrix: A #CoglMatrix to store the transformation
2863 *
2864 * This gets a transformation @matrix that will transform coordinates from the
2865 * coordinate space of @self into the coordinate space of @ancestor.
2866 *
2867 * For example if you need a matrix that can transform the local actor
2868 * coordinates of @self into stage coordinates you would pass the actor's stage
2869 * pointer as the @ancestor.
2870 *
2871 * If you pass %NULL then the transformation will take you all the way through
2872 * to eye coordinates. This can be useful if you want to extract the entire
2873 * modelview transform that Clutter applies before applying the projection
2874 * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
2875 * using cogl_set_modelview_matrix() for example then you would want a matrix
2876 * that transforms into eye coordinates.
2877 *
2878 * Note: This function explicitly initializes the given @matrix. If you just
2879 * want clutter to multiply a relative transformation with an existing matrix
2880 * you can use clutter_actor_apply_relative_transformation_matrix()
2881 * instead.
2882 *
2883 */
2884 /* XXX: We should consider caching the stage relative modelview along with
2885 * the actor itself */
2886 static void
_clutter_actor_get_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,CoglMatrix * matrix)2887 _clutter_actor_get_relative_transformation_matrix (ClutterActor *self,
2888 ClutterActor *ancestor,
2889 CoglMatrix *matrix)
2890 {
2891 cogl_matrix_init_identity (matrix);
2892
2893 _clutter_actor_apply_relative_transformation_matrix (self, ancestor, matrix);
2894 }
2895
2896 /* Project the given @box into stage window coordinates, writing the
2897 * transformed vertices to @verts[]. */
2898 static gboolean
_clutter_actor_transform_and_project_box(ClutterActor * self,const ClutterActorBox * box,ClutterVertex verts[])2899 _clutter_actor_transform_and_project_box (ClutterActor *self,
2900 const ClutterActorBox *box,
2901 ClutterVertex verts[])
2902 {
2903 ClutterVertex box_vertices[4];
2904
2905 box_vertices[0].x = box->x1;
2906 box_vertices[0].y = box->y1;
2907 box_vertices[0].z = 0;
2908 box_vertices[1].x = box->x2;
2909 box_vertices[1].y = box->y1;
2910 box_vertices[1].z = 0;
2911 box_vertices[2].x = box->x1;
2912 box_vertices[2].y = box->y2;
2913 box_vertices[2].z = 0;
2914 box_vertices[3].x = box->x2;
2915 box_vertices[3].y = box->y2;
2916 box_vertices[3].z = 0;
2917
2918 return
2919 _clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4);
2920 }
2921
2922 /**
2923 * clutter_actor_get_allocation_vertices:
2924 * @self: A #ClutterActor
2925 * @ancestor: (allow-none): A #ClutterActor to calculate the vertices
2926 * against, or %NULL to use the #ClutterStage
2927 * @verts: (out) (array fixed-size=4) (element-type Clutter.Vertex): return
2928 * location for an array of 4 #ClutterVertex in which to store the result
2929 *
2930 * Calculates the transformed coordinates of the four corners of the
2931 * actor in the plane of @ancestor. The returned vertices relate to
2932 * the #ClutterActorBox coordinates as follows:
2933 *
2934 * - @verts[0] contains (x1, y1)
2935 * - @verts[1] contains (x2, y1)
2936 * - @verts[2] contains (x1, y2)
2937 * - @verts[3] contains (x2, y2)
2938 *
2939 * If @ancestor is %NULL the ancestor will be the #ClutterStage. In
2940 * this case, the coordinates returned will be the coordinates on
2941 * the stage before the projection is applied. This is different from
2942 * the behaviour of clutter_actor_get_abs_allocation_vertices().
2943 *
2944 * Since: 0.6
2945 */
2946 void
clutter_actor_get_allocation_vertices(ClutterActor * self,ClutterActor * ancestor,ClutterVertex verts[])2947 clutter_actor_get_allocation_vertices (ClutterActor *self,
2948 ClutterActor *ancestor,
2949 ClutterVertex verts[])
2950 {
2951 ClutterActorPrivate *priv;
2952 ClutterActorBox box;
2953 ClutterVertex vertices[4];
2954 CoglMatrix modelview;
2955
2956 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2957 g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
2958
2959 if (ancestor == NULL)
2960 ancestor = _clutter_actor_get_stage_internal (self);
2961
2962 /* Fallback to a NOP transform if the actor isn't parented under a
2963 * stage. */
2964 if (ancestor == NULL)
2965 ancestor = self;
2966
2967 priv = self->priv;
2968
2969 /* if the actor needs to be allocated we force a relayout, so that
2970 * we will have valid values to use in the transformations */
2971 if (priv->needs_allocation)
2972 {
2973 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2974 if (stage)
2975 _clutter_stage_maybe_relayout (stage);
2976 else
2977 {
2978 box.x1 = box.y1 = 0;
2979 /* The result isn't really meaningful in this case but at
2980 * least try to do something *vaguely* reasonable... */
2981 clutter_actor_get_size (self, &box.x2, &box.y2);
2982 }
2983 }
2984
2985 clutter_actor_get_allocation_box (self, &box);
2986
2987 vertices[0].x = box.x1;
2988 vertices[0].y = box.y1;
2989 vertices[0].z = 0;
2990 vertices[1].x = box.x2;
2991 vertices[1].y = box.y1;
2992 vertices[1].z = 0;
2993 vertices[2].x = box.x1;
2994 vertices[2].y = box.y2;
2995 vertices[2].z = 0;
2996 vertices[3].x = box.x2;
2997 vertices[3].y = box.y2;
2998 vertices[3].z = 0;
2999
3000 _clutter_actor_get_relative_transformation_matrix (self, ancestor,
3001 &modelview);
3002
3003 cogl_matrix_transform_points (&modelview,
3004 3,
3005 sizeof (ClutterVertex),
3006 vertices,
3007 sizeof (ClutterVertex),
3008 vertices,
3009 4);
3010 }
3011
3012 /**
3013 * clutter_actor_get_abs_allocation_vertices:
3014 * @self: A #ClutterActor
3015 * @verts: (out) (array fixed-size=4): Pointer to a location of an array
3016 * of 4 #ClutterVertex where to store the result.
3017 *
3018 * Calculates the transformed screen coordinates of the four corners of
3019 * the actor; the returned vertices relate to the #ClutterActorBox
3020 * coordinates as follows:
3021 *
3022 * - v[0] contains (x1, y1)
3023 * - v[1] contains (x2, y1)
3024 * - v[2] contains (x1, y2)
3025 * - v[3] contains (x2, y2)
3026 *
3027 * Since: 0.4
3028 */
3029 void
clutter_actor_get_abs_allocation_vertices(ClutterActor * self,ClutterVertex verts[])3030 clutter_actor_get_abs_allocation_vertices (ClutterActor *self,
3031 ClutterVertex verts[])
3032 {
3033 ClutterActorPrivate *priv;
3034 ClutterActorBox actor_space_allocation;
3035
3036 g_return_if_fail (CLUTTER_IS_ACTOR (self));
3037
3038 priv = self->priv;
3039
3040 /* if the actor needs to be allocated we force a relayout, so that
3041 * the actor allocation box will be valid for
3042 * _clutter_actor_transform_and_project_box()
3043 */
3044 if (priv->needs_allocation)
3045 {
3046 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
3047 /* There's nothing meaningful we can do now */
3048 if (!stage)
3049 return;
3050
3051 _clutter_stage_maybe_relayout (stage);
3052 }
3053
3054 /* NB: _clutter_actor_transform_and_project_box expects a box in the actor's
3055 * own coordinate space... */
3056 actor_space_allocation.x1 = 0;
3057 actor_space_allocation.y1 = 0;
3058 actor_space_allocation.x2 = priv->allocation.x2 - priv->allocation.x1;
3059 actor_space_allocation.y2 = priv->allocation.y2 - priv->allocation.y1;
3060 _clutter_actor_transform_and_project_box (self,
3061 &actor_space_allocation,
3062 verts);
3063 }
3064
3065 static void
clutter_actor_real_apply_transform(ClutterActor * self,ClutterMatrix * matrix)3066 clutter_actor_real_apply_transform (ClutterActor *self,
3067 ClutterMatrix *matrix)
3068 {
3069 ClutterActorPrivate *priv = self->priv;
3070 CoglMatrix *transform = &priv->transform;
3071 const ClutterTransformInfo *info;
3072 float pivot_x = 0.f, pivot_y = 0.f;
3073
3074 /* we already have a cached transformation */
3075 if (priv->transform_valid)
3076 goto multiply_and_return;
3077
3078 info = _clutter_actor_get_transform_info_or_defaults (self);
3079
3080 /* compute the pivot point given the allocated size */
3081 pivot_x = (priv->allocation.x2 - priv->allocation.x1)
3082 * info->pivot.x;
3083 pivot_y = (priv->allocation.y2 - priv->allocation.y1)
3084 * info->pivot.y;
3085
3086 CLUTTER_NOTE (PAINT,
3087 "Allocation: (%.2f, %2.f), "
3088 "pivot: (%.2f, %.2f), "
3089 "translation: (%.2f, %.2f) -> "
3090 "new origin: (%.2f, %.2f)",
3091 priv->allocation.x1, priv->allocation.y1,
3092 info->pivot.x, info->pivot.y,
3093 info->translation.x, info->translation.y,
3094 priv->allocation.x1 + pivot_x + info->translation.x,
3095 priv->allocation.y1 + pivot_y + info->translation.y);
3096
3097 /* we apply the :child-transform from the parent actor, if we have one */
3098 if (priv->parent != NULL)
3099 {
3100 const ClutterTransformInfo *parent_info;
3101
3102 parent_info = _clutter_actor_get_transform_info_or_defaults (priv->parent);
3103 clutter_matrix_init_from_matrix (transform, &(parent_info->child_transform));
3104 }
3105 else
3106 clutter_matrix_init_identity (transform);
3107
3108 /* if we have an overriding transformation, we use that, and get out */
3109 if (info->transform_set)
3110 {
3111 /* we still need to apply the :allocation's origin and :pivot-point
3112 * translations, since :transform is relative to the actor's coordinate
3113 * space, and to the pivot point
3114 */
3115 cogl_matrix_translate (transform,
3116 priv->allocation.x1 + pivot_x,
3117 priv->allocation.y1 + pivot_y,
3118 info->pivot_z);
3119 cogl_matrix_multiply (transform, transform, &info->transform);
3120 goto roll_back_pivot;
3121 }
3122
3123 /* basic translation: :allocation's origin and :z-position; instead
3124 * of decomposing the pivot and translation info separate operations,
3125 * we just compose everything into a single translation
3126 */
3127 cogl_matrix_translate (transform,
3128 priv->allocation.x1 + pivot_x + info->translation.x,
3129 priv->allocation.y1 + pivot_y + info->translation.y,
3130 info->z_position + info->pivot_z + info->translation.z);
3131
3132 /* because the rotation involves translations, we must scale
3133 * before applying the rotations (if we apply the scale after
3134 * the rotations, the translations included in the rotation are
3135 * not scaled and so the entire object will move on the screen
3136 * as a result of rotating it).
3137 *
3138 * XXX:2.0 the comment has to be reworded once we remove the
3139 * per-transformation centers; we also may want to apply rotation
3140 * first and scaling after, to match the matrix decomposition
3141 * code we use when interpolating transformations
3142 */
3143 if (info->scale_x != 1.0 || info->scale_y != 1.0 || info->scale_z != 1.0)
3144 {
3145 /* XXX:2.0 remove anchor coord */
3146 TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3147 &info->scale_center,
3148 cogl_matrix_scale (transform,
3149 info->scale_x,
3150 info->scale_y,
3151 info->scale_z));
3152 }
3153
3154 if (info->rz_angle)
3155 {
3156 /* XXX:2.0 remove anchor coord */
3157 TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3158 &info->rz_center,
3159 cogl_matrix_rotate (transform,
3160 info->rz_angle,
3161 0, 0, 1.0));
3162 }
3163
3164 if (info->ry_angle)
3165 {
3166 /* XXX:2.0 remove anchor coord */
3167 TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3168 &info->ry_center,
3169 cogl_matrix_rotate (transform,
3170 info->ry_angle,
3171 0, 1.0, 0));
3172 }
3173
3174 if (info->rx_angle)
3175 {
3176 /* XXX:2.0 remove anchor coord */
3177 TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3178 &info->rx_center,
3179 cogl_matrix_rotate (transform,
3180 info->rx_angle,
3181 1.0, 0, 0));
3182 }
3183
3184 /* XXX:2.0 remove anchor point translation */
3185 if (!clutter_anchor_coord_is_zero (&info->anchor))
3186 {
3187 gfloat x, y, z;
3188
3189 clutter_anchor_coord_get_units (self, &info->anchor, &x, &y, &z);
3190 cogl_matrix_translate (transform, -x, -y, -z);
3191 }
3192
3193 roll_back_pivot:
3194 /* roll back the pivot translation */
3195 if (pivot_x != 0.f || pivot_y != 0.f || info->pivot_z != 0.f)
3196 cogl_matrix_translate (transform, -pivot_x, -pivot_y, -info->pivot_z);
3197
3198 /* we have a valid modelview */
3199 priv->transform_valid = TRUE;
3200
3201 multiply_and_return:
3202 cogl_matrix_multiply (matrix, matrix, &priv->transform);
3203 }
3204
3205 /* Applies the transforms associated with this actor to the given
3206 * matrix. */
3207 void
_clutter_actor_apply_modelview_transform(ClutterActor * self,ClutterMatrix * matrix)3208 _clutter_actor_apply_modelview_transform (ClutterActor *self,
3209 ClutterMatrix *matrix)
3210 {
3211 CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix);
3212 }
3213
3214 /*
3215 * clutter_actor_apply_relative_transformation_matrix:
3216 * @self: The actor whose coordinate space you want to transform from.
3217 * @ancestor: The ancestor actor whose coordinate space you want to transform too
3218 * or %NULL if you want to transform all the way to eye coordinates.
3219 * @matrix: A #ClutterMatrix to apply the transformation too.
3220 *
3221 * This multiplies a transform with @matrix that will transform coordinates
3222 * from the coordinate space of @self into the coordinate space of @ancestor.
3223 *
3224 * For example if you need a matrix that can transform the local actor
3225 * coordinates of @self into stage coordinates you would pass the actor's stage
3226 * pointer as the @ancestor.
3227 *
3228 * If you pass %NULL then the transformation will take you all the way through
3229 * to eye coordinates. This can be useful if you want to extract the entire
3230 * modelview transform that Clutter applies before applying the projection
3231 * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
3232 * using cogl_set_modelview_matrix() for example then you would want a matrix
3233 * that transforms into eye coordinates.
3234 *
3235 * This function doesn't initialize the given @matrix, it simply
3236 * multiplies the requested transformation matrix with the existing contents of
3237 * @matrix. You can use cogl_matrix_init_identity() to initialize the @matrix
3238 * before calling this function, or you can use
3239 * clutter_actor_get_relative_transformation_matrix() instead.
3240 */
3241 void
_clutter_actor_apply_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,CoglMatrix * matrix)3242 _clutter_actor_apply_relative_transformation_matrix (ClutterActor *self,
3243 ClutterActor *ancestor,
3244 CoglMatrix *matrix)
3245 {
3246 ClutterActor *parent;
3247
3248 /* Note we terminate before ever calling stage->apply_transform()
3249 * since that would conceptually be relative to the underlying
3250 * window OpenGL coordinates so we'd need a special @ancestor
3251 * value to represent the fake parent of the stage. */
3252 if (self == ancestor)
3253 return;
3254
3255 parent = clutter_actor_get_parent (self);
3256
3257 if (parent != NULL)
3258 _clutter_actor_apply_relative_transformation_matrix (parent, ancestor,
3259 matrix);
3260
3261 _clutter_actor_apply_modelview_transform (self, matrix);
3262 }
3263
3264 static void
_clutter_actor_draw_paint_volume_full(ClutterActor * self,ClutterPaintVolume * pv,const char * label,const CoglColor * color)3265 _clutter_actor_draw_paint_volume_full (ClutterActor *self,
3266 ClutterPaintVolume *pv,
3267 const char *label,
3268 const CoglColor *color)
3269 {
3270 static CoglPipeline *outline = NULL;
3271 CoglPrimitive *prim;
3272 ClutterVertex line_ends[12 * 2];
3273 int n_vertices;
3274 CoglContext *ctx =
3275 clutter_backend_get_cogl_context (clutter_get_default_backend ());
3276 /* XXX: at some point we'll query this from the stage but we can't
3277 * do that until the osx backend uses Cogl natively. */
3278 CoglFramebuffer *fb = cogl_get_draw_framebuffer ();
3279
3280 if (outline == NULL)
3281 outline = cogl_pipeline_new (ctx);
3282
3283 _clutter_paint_volume_complete (pv);
3284
3285 n_vertices = pv->is_2d ? 4 * 2 : 12 * 2;
3286
3287 /* Front face */
3288 line_ends[0] = pv->vertices[0]; line_ends[1] = pv->vertices[1];
3289 line_ends[2] = pv->vertices[1]; line_ends[3] = pv->vertices[2];
3290 line_ends[4] = pv->vertices[2]; line_ends[5] = pv->vertices[3];
3291 line_ends[6] = pv->vertices[3]; line_ends[7] = pv->vertices[0];
3292
3293 if (!pv->is_2d)
3294 {
3295 /* Back face */
3296 line_ends[8] = pv->vertices[4]; line_ends[9] = pv->vertices[5];
3297 line_ends[10] = pv->vertices[5]; line_ends[11] = pv->vertices[6];
3298 line_ends[12] = pv->vertices[6]; line_ends[13] = pv->vertices[7];
3299 line_ends[14] = pv->vertices[7]; line_ends[15] = pv->vertices[4];
3300
3301 /* Lines connecting front face to back face */
3302 line_ends[16] = pv->vertices[0]; line_ends[17] = pv->vertices[4];
3303 line_ends[18] = pv->vertices[1]; line_ends[19] = pv->vertices[5];
3304 line_ends[20] = pv->vertices[2]; line_ends[21] = pv->vertices[6];
3305 line_ends[22] = pv->vertices[3]; line_ends[23] = pv->vertices[7];
3306 }
3307
3308 prim = cogl_primitive_new_p3 (ctx, COGL_VERTICES_MODE_LINES,
3309 n_vertices,
3310 (CoglVertexP3 *)line_ends);
3311
3312 cogl_pipeline_set_color (outline, color);
3313 cogl_framebuffer_draw_primitive (fb, outline, prim);
3314 cogl_object_unref (prim);
3315
3316 if (label)
3317 {
3318 PangoLayout *layout;
3319 layout = pango_layout_new (clutter_actor_get_pango_context (self));
3320 pango_layout_set_text (layout, label, -1);
3321 cogl_pango_render_layout (layout,
3322 pv->vertices[0].x,
3323 pv->vertices[0].y,
3324 color,
3325 0);
3326 g_object_unref (layout);
3327 }
3328 }
3329
3330 static void
_clutter_actor_draw_paint_volume(ClutterActor * self)3331 _clutter_actor_draw_paint_volume (ClutterActor *self)
3332 {
3333 ClutterPaintVolume *pv;
3334 CoglColor color;
3335
3336 pv = _clutter_actor_get_paint_volume_mutable (self);
3337 if (!pv)
3338 {
3339 gfloat width, height;
3340 ClutterPaintVolume fake_pv;
3341
3342 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
3343 _clutter_paint_volume_init_static (&fake_pv, stage);
3344
3345 clutter_actor_get_size (self, &width, &height);
3346 clutter_paint_volume_set_width (&fake_pv, width);
3347 clutter_paint_volume_set_height (&fake_pv, height);
3348
3349 cogl_color_init_from_4f (&color, 0, 0, 1, 1);
3350 _clutter_actor_draw_paint_volume_full (self, &fake_pv,
3351 _clutter_actor_get_debug_name (self),
3352 &color);
3353
3354 clutter_paint_volume_free (&fake_pv);
3355 }
3356 else
3357 {
3358 cogl_color_init_from_4f (&color, 0, 1, 0, 1);
3359 _clutter_actor_draw_paint_volume_full (self, pv,
3360 _clutter_actor_get_debug_name (self),
3361 &color);
3362 }
3363 }
3364
3365 static void
_clutter_actor_paint_cull_result(ClutterActor * self,gboolean success,ClutterCullResult result)3366 _clutter_actor_paint_cull_result (ClutterActor *self,
3367 gboolean success,
3368 ClutterCullResult result)
3369 {
3370 ClutterPaintVolume *pv;
3371 CoglColor color;
3372
3373 if (success)
3374 {
3375 if (result == CLUTTER_CULL_RESULT_IN)
3376 cogl_color_init_from_4f (&color, 0, 1, 0, 1);
3377 else if (result == CLUTTER_CULL_RESULT_OUT)
3378 cogl_color_init_from_4f (&color, 0, 0, 1, 1);
3379 else
3380 cogl_color_init_from_4f (&color, 0, 1, 1, 1);
3381 }
3382 else
3383 cogl_color_init_from_4f (&color, 1, 1, 1, 1);
3384
3385 if (success && (pv = _clutter_actor_get_paint_volume_mutable (self)))
3386 _clutter_actor_draw_paint_volume_full (self, pv,
3387 _clutter_actor_get_debug_name (self),
3388 &color);
3389 else
3390 {
3391 PangoLayout *layout;
3392 char *label =
3393 g_strdup_printf ("CULL FAILURE: %s", _clutter_actor_get_debug_name (self));
3394 cogl_color_init_from_4f (&color, 1, 1, 1, 1);
3395 cogl_set_source_color (&color);
3396
3397 layout = pango_layout_new (clutter_actor_get_pango_context (self));
3398 pango_layout_set_text (layout, label, -1);
3399 cogl_pango_render_layout (layout,
3400 0,
3401 0,
3402 &color,
3403 0);
3404 g_free (label);
3405 g_object_unref (layout);
3406 }
3407 }
3408
3409 static int clone_paint_level = 0;
3410
3411 void
_clutter_actor_push_clone_paint(void)3412 _clutter_actor_push_clone_paint (void)
3413 {
3414 clone_paint_level++;
3415 }
3416
3417 void
_clutter_actor_pop_clone_paint(void)3418 _clutter_actor_pop_clone_paint (void)
3419 {
3420 clone_paint_level--;
3421 }
3422
3423 static gboolean
in_clone_paint(void)3424 in_clone_paint (void)
3425 {
3426 return clone_paint_level > 0;
3427 }
3428
3429 /* Returns TRUE if the actor can be ignored */
3430 /* FIXME: we should return a ClutterCullResult, and
3431 * clutter_actor_paint should understand that a CLUTTER_CULL_RESULT_IN
3432 * means there's no point in trying to cull descendants of the current
3433 * node. */
3434 static gboolean
cull_actor(ClutterActor * self,ClutterCullResult * result_out)3435 cull_actor (ClutterActor *self,
3436 ClutterCullResult *result_out)
3437 {
3438 ClutterActorPrivate *priv = self->priv;
3439 ClutterStage *stage;
3440 const ClutterPlane *stage_clip;
3441
3442 if (!priv->last_paint_volume_valid)
3443 {
3444 CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3445 "->last_paint_volume_valid == FALSE",
3446 _clutter_actor_get_debug_name (self));
3447 return FALSE;
3448 }
3449
3450 if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING))
3451 return FALSE;
3452
3453 stage = (ClutterStage *) _clutter_actor_get_stage_internal (self);
3454 stage_clip = _clutter_stage_get_clip (stage);
3455 if (G_UNLIKELY (!stage_clip))
3456 {
3457 CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3458 "No stage clip set",
3459 _clutter_actor_get_debug_name (self));
3460 return FALSE;
3461 }
3462
3463 if (cogl_get_draw_framebuffer () != _clutter_stage_get_active_framebuffer (stage))
3464 {
3465 CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3466 "Current framebuffer doesn't correspond to stage",
3467 _clutter_actor_get_debug_name (self));
3468 return FALSE;
3469 }
3470
3471 *result_out =
3472 _clutter_paint_volume_cull (&priv->last_paint_volume, stage_clip);
3473
3474 return TRUE;
3475 }
3476
3477 static void
_clutter_actor_update_last_paint_volume(ClutterActor * self)3478 _clutter_actor_update_last_paint_volume (ClutterActor *self)
3479 {
3480 ClutterActorPrivate *priv = self->priv;
3481 const ClutterPaintVolume *pv;
3482
3483 if (priv->last_paint_volume_valid)
3484 {
3485 clutter_paint_volume_free (&priv->last_paint_volume);
3486 priv->last_paint_volume_valid = FALSE;
3487 }
3488
3489 pv = clutter_actor_get_paint_volume (self);
3490 if (!pv)
3491 {
3492 CLUTTER_NOTE (CLIPPING, "Bail from update_last_paint_volume (%s): "
3493 "Actor failed to report a paint volume",
3494 _clutter_actor_get_debug_name (self));
3495 return;
3496 }
3497
3498 _clutter_paint_volume_copy_static (pv, &priv->last_paint_volume);
3499
3500 _clutter_paint_volume_transform_relative (&priv->last_paint_volume,
3501 NULL); /* eye coordinates */
3502
3503 priv->last_paint_volume_valid = TRUE;
3504 }
3505
3506 static inline gboolean
actor_has_shader_data(ClutterActor * self)3507 actor_has_shader_data (ClutterActor *self)
3508 {
3509 return g_object_get_qdata (G_OBJECT (self), quark_shader_data) != NULL;
3510 }
3511
3512 guint32
_clutter_actor_get_pick_id(ClutterActor * self)3513 _clutter_actor_get_pick_id (ClutterActor *self)
3514 {
3515 if (self->priv->pick_id < 0)
3516 return 0;
3517
3518 return self->priv->pick_id;
3519 }
3520
3521 /* This is the same as clutter_actor_add_effect except that it doesn't
3522 queue a redraw and it doesn't notify on the effect property */
3523 static void
_clutter_actor_add_effect_internal(ClutterActor * self,ClutterEffect * effect)3524 _clutter_actor_add_effect_internal (ClutterActor *self,
3525 ClutterEffect *effect)
3526 {
3527 ClutterActorPrivate *priv = self->priv;
3528
3529 if (priv->effects == NULL)
3530 {
3531 priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
3532 priv->effects->actor = self;
3533 }
3534
3535 _clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3536 }
3537
3538 /* This is the same as clutter_actor_remove_effect except that it doesn't
3539 queue a redraw and it doesn't notify on the effect property */
3540 static void
_clutter_actor_remove_effect_internal(ClutterActor * self,ClutterEffect * effect)3541 _clutter_actor_remove_effect_internal (ClutterActor *self,
3542 ClutterEffect *effect)
3543 {
3544 ClutterActorPrivate *priv = self->priv;
3545
3546 if (priv->effects == NULL)
3547 return;
3548
3549 _clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3550
3551 if (_clutter_meta_group_peek_metas (priv->effects) == NULL)
3552 g_clear_object (&priv->effects);
3553 }
3554
3555 static gboolean
needs_flatten_effect(ClutterActor * self)3556 needs_flatten_effect (ClutterActor *self)
3557 {
3558 ClutterActorPrivate *priv = self->priv;
3559
3560 if (G_UNLIKELY (clutter_paint_debug_flags &
3561 CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT))
3562 return FALSE;
3563
3564 if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS)
3565 return TRUE;
3566 else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY)
3567 {
3568 if (clutter_actor_get_paint_opacity (self) < 255 &&
3569 clutter_actor_has_overlaps (self))
3570 return TRUE;
3571 }
3572
3573 return FALSE;
3574 }
3575
3576 static void
add_or_remove_flatten_effect(ClutterActor * self)3577 add_or_remove_flatten_effect (ClutterActor *self)
3578 {
3579 ClutterActorPrivate *priv = self->priv;
3580
3581 /* Add or remove the flatten effect depending on the
3582 offscreen-redirect property. */
3583 if (needs_flatten_effect (self))
3584 {
3585 if (priv->flatten_effect == NULL)
3586 {
3587 ClutterActorMeta *actor_meta;
3588 gint priority;
3589
3590 priv->flatten_effect = _clutter_flatten_effect_new ();
3591 /* Keep a reference to the effect so that we can queue
3592 redraws from it */
3593 g_object_ref_sink (priv->flatten_effect);
3594
3595 /* Set the priority of the effect to high so that it will
3596 always be applied to the actor first. It uses an internal
3597 priority so that it won't be visible to applications */
3598 actor_meta = CLUTTER_ACTOR_META (priv->flatten_effect);
3599 priority = CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH;
3600 _clutter_actor_meta_set_priority (actor_meta, priority);
3601
3602 /* This will add the effect without queueing a redraw */
3603 _clutter_actor_add_effect_internal (self, priv->flatten_effect);
3604 }
3605 }
3606 else
3607 {
3608 if (priv->flatten_effect != NULL)
3609 {
3610 /* Destroy the effect so that it will lose its fbo cache of
3611 the actor */
3612 _clutter_actor_remove_effect_internal (self, priv->flatten_effect);
3613 g_clear_object (&priv->flatten_effect);
3614 }
3615 }
3616 }
3617
3618 static void
clutter_actor_real_paint(ClutterActor * actor)3619 clutter_actor_real_paint (ClutterActor *actor)
3620 {
3621 ClutterActorPrivate *priv = actor->priv;
3622 ClutterActor *iter;
3623
3624 for (iter = priv->first_child;
3625 iter != NULL;
3626 iter = iter->priv->next_sibling)
3627 {
3628 CLUTTER_NOTE (PAINT, "Painting %s, child of %s, at { %.2f, %.2f - %.2f x %.2f }",
3629 _clutter_actor_get_debug_name (iter),
3630 _clutter_actor_get_debug_name (actor),
3631 iter->priv->allocation.x1,
3632 iter->priv->allocation.y1,
3633 iter->priv->allocation.x2 - iter->priv->allocation.x1,
3634 iter->priv->allocation.y2 - iter->priv->allocation.y1);
3635
3636 clutter_actor_paint (iter);
3637 }
3638 }
3639
3640 static gboolean
clutter_actor_paint_node(ClutterActor * actor,ClutterPaintNode * root)3641 clutter_actor_paint_node (ClutterActor *actor,
3642 ClutterPaintNode *root)
3643 {
3644 ClutterActorPrivate *priv = actor->priv;
3645 ClutterActorBox box;
3646 ClutterColor bg_color;
3647
3648 if (root == NULL)
3649 return FALSE;
3650
3651 box.x1 = 0.f;
3652 box.y1 = 0.f;
3653 box.x2 = clutter_actor_box_get_width (&priv->allocation);
3654 box.y2 = clutter_actor_box_get_height (&priv->allocation);
3655
3656 bg_color = priv->bg_color;
3657
3658 if (CLUTTER_ACTOR_IS_TOPLEVEL (actor))
3659 {
3660 ClutterPaintNode *node;
3661 CoglFramebuffer *fb;
3662 CoglBufferBit clear_flags;
3663
3664 fb = _clutter_stage_get_active_framebuffer (CLUTTER_STAGE (actor));
3665
3666 if (clutter_stage_get_use_alpha (CLUTTER_STAGE (actor)))
3667 {
3668 bg_color.alpha = priv->opacity
3669 * priv->bg_color.alpha
3670 / 255;
3671 }
3672 else
3673 bg_color.alpha = 255;
3674
3675 CLUTTER_NOTE (PAINT, "Stage clear color: (%d, %d, %d, %d)",
3676 bg_color.red,
3677 bg_color.green,
3678 bg_color.blue,
3679 bg_color.alpha);
3680
3681 clear_flags = COGL_BUFFER_BIT_DEPTH;
3682 if (!clutter_stage_get_no_clear_hint (CLUTTER_STAGE (actor)))
3683 clear_flags |= COGL_BUFFER_BIT_COLOR;
3684
3685 node = _clutter_root_node_new (fb, &bg_color, clear_flags);
3686 clutter_paint_node_set_name (node, "stageClear");
3687 clutter_paint_node_add_rectangle (node, &box);
3688 clutter_paint_node_add_child (root, node);
3689 clutter_paint_node_unref (node);
3690 }
3691 else if (priv->bg_color_set &&
3692 !clutter_color_equal (&priv->bg_color, CLUTTER_COLOR_Transparent))
3693 {
3694 ClutterPaintNode *node;
3695
3696 bg_color.alpha = clutter_actor_get_paint_opacity_internal (actor)
3697 * priv->bg_color.alpha
3698 / 255;
3699
3700 node = clutter_color_node_new (&bg_color);
3701 clutter_paint_node_set_name (node, "backgroundColor");
3702 clutter_paint_node_add_rectangle (node, &box);
3703 clutter_paint_node_add_child (root, node);
3704 clutter_paint_node_unref (node);
3705 }
3706
3707 if (priv->content != NULL)
3708 _clutter_content_paint_content (priv->content, actor, root);
3709
3710 if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL)
3711 CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root);
3712
3713 if (clutter_paint_node_get_n_children (root) == 0)
3714 return FALSE;
3715
3716 #ifdef CLUTTER_ENABLE_DEBUG
3717 if (CLUTTER_HAS_DEBUG (PAINT))
3718 {
3719 /* dump the tree only if we have one */
3720 _clutter_paint_node_dump_tree (root);
3721 }
3722 #endif /* CLUTTER_ENABLE_DEBUG */
3723
3724 _clutter_paint_node_paint (root);
3725
3726 return TRUE;
3727 }
3728
3729 /**
3730 * clutter_actor_paint:
3731 * @self: A #ClutterActor
3732 *
3733 * Renders the actor to display.
3734 *
3735 * This function should not be called directly by applications.
3736 * Call clutter_actor_queue_redraw() to queue paints, instead.
3737 *
3738 * This function is context-aware, and will either cause a
3739 * regular paint or a pick paint.
3740 *
3741 * This function will emit the #ClutterActor::paint signal or
3742 * the #ClutterActor::pick signal, depending on the context.
3743 *
3744 * This function does not paint the actor if the actor is set to 0,
3745 * unless it is performing a pick paint.
3746 */
3747 void
clutter_actor_paint(ClutterActor * self)3748 clutter_actor_paint (ClutterActor *self)
3749 {
3750 ClutterActorPrivate *priv;
3751 ClutterPickMode pick_mode;
3752 gboolean clip_set = FALSE;
3753 gboolean shader_applied = FALSE;
3754 ClutterStage *stage;
3755
3756 g_return_if_fail (CLUTTER_IS_ACTOR (self));
3757
3758 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
3759 return;
3760
3761 priv = self->priv;
3762
3763 pick_mode = _clutter_context_get_pick_mode ();
3764
3765 if (pick_mode == CLUTTER_PICK_NONE)
3766 priv->propagated_one_redraw = FALSE;
3767
3768 /* It's an important optimization that we consider painting of
3769 * actors with 0 opacity to be a NOP... */
3770 if (pick_mode == CLUTTER_PICK_NONE &&
3771 /* ignore top-levels, since they might be transparent */
3772 !CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
3773 /* Use the override opacity if its been set */
3774 ((priv->opacity_override >= 0) ?
3775 priv->opacity_override : priv->opacity) == 0)
3776 return;
3777
3778 /* if we aren't paintable (not in a toplevel with all
3779 * parents paintable) then do nothing.
3780 */
3781 if (!CLUTTER_ACTOR_IS_MAPPED (self))
3782 return;
3783
3784 stage = (ClutterStage *) _clutter_actor_get_stage_internal (self);
3785
3786 /* mark that we are in the paint process */
3787 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT);
3788
3789 cogl_push_matrix ();
3790
3791 if (priv->enable_model_view_transform)
3792 {
3793 CoglMatrix matrix;
3794
3795 /* XXX: It could be better to cache the modelview with the actor
3796 * instead of progressively building up the transformations on
3797 * the matrix stack every time we paint. */
3798 cogl_get_modelview_matrix (&matrix);
3799 _clutter_actor_apply_modelview_transform (self, &matrix);
3800
3801 #ifdef CLUTTER_ENABLE_DEBUG
3802 /* Catch when out-of-band transforms have been made by actors not as part
3803 * of an apply_transform vfunc... */
3804 if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_OOB_TRANSFORMS))
3805 {
3806 CoglMatrix expected_matrix;
3807
3808 _clutter_actor_get_relative_transformation_matrix (self, NULL,
3809 &expected_matrix);
3810
3811 if (!cogl_matrix_equal (&matrix, &expected_matrix))
3812 {
3813 GString *buf = g_string_sized_new (1024);
3814 ClutterActor *parent;
3815
3816 parent = self;
3817 while (parent != NULL)
3818 {
3819 g_string_append (buf, _clutter_actor_get_debug_name (parent));
3820
3821 if (parent->priv->parent != NULL)
3822 g_string_append (buf, "->");
3823
3824 parent = parent->priv->parent;
3825 }
3826
3827 g_warning ("Unexpected transform found when painting actor "
3828 "\"%s\". This will be caused by one of the actor's "
3829 "ancestors (%s) using the Cogl API directly to transform "
3830 "children instead of using ::apply_transform().",
3831 _clutter_actor_get_debug_name (self),
3832 buf->str);
3833
3834 g_string_free (buf, TRUE);
3835 }
3836 }
3837 #endif /* CLUTTER_ENABLE_DEBUG */
3838
3839 cogl_set_modelview_matrix (&matrix);
3840 }
3841
3842 if (priv->has_clip)
3843 {
3844 CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
3845 cogl_framebuffer_push_rectangle_clip (fb,
3846 priv->clip.origin.x,
3847 priv->clip.origin.y,
3848 priv->clip.origin.x + priv->clip.size.width,
3849 priv->clip.origin.y + priv->clip.size.height);
3850 clip_set = TRUE;
3851 }
3852 else if (priv->clip_to_allocation)
3853 {
3854 CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
3855 gfloat width, height;
3856
3857 width = priv->allocation.x2 - priv->allocation.x1;
3858 height = priv->allocation.y2 - priv->allocation.y1;
3859
3860 cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height);
3861 clip_set = TRUE;
3862 }
3863
3864 if (pick_mode == CLUTTER_PICK_NONE)
3865 {
3866 /* We check whether we need to add the flatten effect before
3867 each paint so that we can avoid having a mechanism for
3868 applications to notify when the value of the
3869 has_overlaps virtual changes. */
3870 add_or_remove_flatten_effect (self);
3871 }
3872
3873 /* We save the current paint volume so that the next time the
3874 * actor queues a redraw we can constrain the redraw to just
3875 * cover the union of the new bounding box and the old.
3876 *
3877 * We also fetch the current paint volume to perform culling so
3878 * we can avoid painting actors outside the current clip region.
3879 *
3880 * If we are painting inside a clone, we should neither update
3881 * the paint volume or use it to cull painting, since the paint
3882 * box represents the location of the source actor on the
3883 * screen.
3884 *
3885 * XXX: We are starting to do a lot of vertex transforms on
3886 * the CPU in a typical paint, so at some point we should
3887 * audit these and consider caching some things.
3888 *
3889 * NB: We don't perform culling while picking at this point because
3890 * clutter-stage.c doesn't setup the clipping planes appropriately.
3891 *
3892 * NB: We don't want to update the last-paint-volume during picking
3893 * because the last-paint-volume is used to determine the old screen
3894 * space location of an actor that has moved so we can know the
3895 * minimal region to redraw to clear an old view of the actor. If we
3896 * update this during picking then by the time we come around to
3897 * paint then the last-paint-volume would likely represent the new
3898 * actor position not the old.
3899 */
3900 if (!in_clone_paint () && pick_mode == CLUTTER_PICK_NONE)
3901 {
3902 gboolean success;
3903 /* annoyingly gcc warns if uninitialized even though
3904 * the initialization is redundant :-( */
3905 ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
3906
3907 if (G_LIKELY ((clutter_paint_debug_flags &
3908 (CLUTTER_DEBUG_DISABLE_CULLING |
3909 CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
3910 (CLUTTER_DEBUG_DISABLE_CULLING |
3911 CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
3912 _clutter_actor_update_last_paint_volume (self);
3913
3914 success = cull_actor (self, &result);
3915
3916 if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))
3917 _clutter_actor_paint_cull_result (self, success, result);
3918 else if (result == CLUTTER_CULL_RESULT_OUT && success)
3919 goto done;
3920 }
3921
3922 if (priv->effects == NULL)
3923 {
3924 if (pick_mode == CLUTTER_PICK_NONE &&
3925 actor_has_shader_data (self))
3926 {
3927 _clutter_actor_shader_pre_paint (self, FALSE);
3928 shader_applied = TRUE;
3929 }
3930
3931 priv->next_effect_to_paint = NULL;
3932 }
3933 else
3934 priv->next_effect_to_paint =
3935 _clutter_meta_group_peek_metas (priv->effects);
3936
3937 clutter_actor_continue_paint (self);
3938
3939 if (shader_applied)
3940 _clutter_actor_shader_post_paint (self);
3941
3942 if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES &&
3943 pick_mode == CLUTTER_PICK_NONE))
3944 _clutter_actor_draw_paint_volume (self);
3945
3946 done:
3947 /* If we make it here then the actor has run through a complete
3948 paint run including all the effects so it's no longer dirty */
3949 if (pick_mode == CLUTTER_PICK_NONE)
3950 priv->is_dirty = FALSE;
3951
3952 if (clip_set)
3953 {
3954 CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
3955
3956 cogl_framebuffer_pop_clip (fb);
3957 }
3958
3959 cogl_pop_matrix ();
3960
3961 /* paint sequence complete */
3962 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT);
3963 }
3964
3965 /**
3966 * clutter_actor_continue_paint:
3967 * @self: A #ClutterActor
3968 *
3969 * Run the next stage of the paint sequence. This function should only
3970 * be called within the implementation of the ‘run’ virtual of a
3971 * #ClutterEffect. It will cause the run method of the next effect to
3972 * be applied, or it will paint the actual actor if the current effect
3973 * is the last effect in the chain.
3974 *
3975 * Since: 1.8
3976 */
3977 void
clutter_actor_continue_paint(ClutterActor * self)3978 clutter_actor_continue_paint (ClutterActor *self)
3979 {
3980 ClutterActorPrivate *priv;
3981
3982 g_return_if_fail (CLUTTER_IS_ACTOR (self));
3983 /* This should only be called from with in the ‘run’ implementation
3984 of a ClutterEffect */
3985 g_return_if_fail (CLUTTER_ACTOR_IN_PAINT (self));
3986
3987 priv = self->priv;
3988
3989 /* Skip any effects that are disabled */
3990 while (priv->next_effect_to_paint &&
3991 !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data))
3992 priv->next_effect_to_paint = priv->next_effect_to_paint->next;
3993
3994 /* If this has come from the last effect then we'll just paint the
3995 actual actor */
3996 if (priv->next_effect_to_paint == NULL)
3997 {
3998 if (_clutter_context_get_pick_mode () == CLUTTER_PICK_NONE)
3999 {
4000 ClutterPaintNode *dummy;
4001
4002 /* XXX - this will go away in 2.0, when we can get rid of this
4003 * stuff and switch to a pure retained render tree of PaintNodes
4004 * for the entire frame, starting from the Stage; the paint()
4005 * virtual function can then be called directly.
4006 */
4007 dummy = _clutter_dummy_node_new (self);
4008 clutter_paint_node_set_name (dummy, "Root");
4009
4010 /* XXX - for 1.12, we use the return value of paint_node() to
4011 * decide whether we should emit the ::paint signal.
4012 */
4013 clutter_actor_paint_node (self, dummy);
4014 clutter_paint_node_unref (dummy);
4015
4016 /* XXX:2.0 - Call the paint() virtual directly */
4017 g_signal_emit (self, actor_signals[PAINT], 0);
4018 }
4019 else
4020 {
4021 ClutterColor col = { 0, };
4022
4023 _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col);
4024
4025 /* Actor will then paint silhouette of itself in supplied
4026 * color. See clutter_stage_get_actor_at_pos() for where
4027 * picking is enabled.
4028 *
4029 * XXX:2.0 - Call the pick() virtual directly
4030 */
4031 g_signal_emit (self, actor_signals[PICK], 0, &col);
4032 }
4033 }
4034 else
4035 {
4036 ClutterEffect *old_current_effect;
4037 ClutterEffectPaintFlags run_flags = 0;
4038
4039 /* Cache the current effect so that we can put it back before
4040 returning */
4041 old_current_effect = priv->current_effect;
4042
4043 priv->current_effect = priv->next_effect_to_paint->data;
4044 priv->next_effect_to_paint = priv->next_effect_to_paint->next;
4045
4046 if (_clutter_context_get_pick_mode () == CLUTTER_PICK_NONE)
4047 {
4048 if (priv->is_dirty)
4049 {
4050 /* If there's an effect queued with this redraw then all
4051 effects up to that one will be considered dirty. It
4052 is expected the queued effect will paint the cached
4053 image and not call clutter_actor_continue_paint again
4054 (although it should work ok if it does) */
4055 if (priv->effect_to_redraw == NULL ||
4056 priv->current_effect != priv->effect_to_redraw)
4057 run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
4058 }
4059
4060 _clutter_effect_paint (priv->current_effect, run_flags);
4061 }
4062 else
4063 {
4064 /* We can't determine when an actor has been modified since
4065 its last pick so lets just assume it has always been
4066 modified */
4067 run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
4068
4069 _clutter_effect_pick (priv->current_effect, run_flags);
4070 }
4071
4072 priv->current_effect = old_current_effect;
4073 }
4074 }
4075
4076 static void
_clutter_actor_stop_transitions(ClutterActor * self)4077 _clutter_actor_stop_transitions (ClutterActor *self)
4078 {
4079 const ClutterAnimationInfo *info;
4080 GHashTableIter iter;
4081 gpointer value;
4082
4083 info = _clutter_actor_get_animation_info_or_defaults (self);
4084 if (info->transitions == NULL)
4085 return;
4086
4087 g_hash_table_iter_init (&iter, info->transitions);
4088 while (g_hash_table_iter_next (&iter, NULL, &value))
4089 {
4090 TransitionClosure *closure = value;
4091
4092 if (clutter_transition_get_remove_on_complete (closure->transition))
4093 {
4094 g_hash_table_iter_remove (&iter);
4095 }
4096 else
4097 {
4098 /* otherwise we stop it, and the transition will be removed
4099 * later, either by the actor's destruction or by explicit
4100 * removal
4101 */
4102 clutter_timeline_stop (CLUTTER_TIMELINE (closure->transition));
4103 }
4104 }
4105 }
4106
4107 static ClutterActorTraverseVisitFlags
invalidate_queue_redraw_entry(ClutterActor * self,int depth,gpointer user_data)4108 invalidate_queue_redraw_entry (ClutterActor *self,
4109 int depth,
4110 gpointer user_data)
4111 {
4112 ClutterActorPrivate *priv = self->priv;
4113
4114 if (priv->queue_redraw_entry != NULL)
4115 {
4116 _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
4117 priv->queue_redraw_entry = NULL;
4118 }
4119
4120 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
4121 }
4122
4123 static inline void
remove_child(ClutterActor * self,ClutterActor * child)4124 remove_child (ClutterActor *self,
4125 ClutterActor *child)
4126 {
4127 ClutterActor *prev_sibling, *next_sibling;
4128
4129 prev_sibling = child->priv->prev_sibling;
4130 next_sibling = child->priv->next_sibling;
4131
4132 if (prev_sibling != NULL)
4133 prev_sibling->priv->next_sibling = next_sibling;
4134
4135 if (next_sibling != NULL)
4136 next_sibling->priv->prev_sibling = prev_sibling;
4137
4138 if (self->priv->first_child == child)
4139 self->priv->first_child = next_sibling;
4140
4141 if (self->priv->last_child == child)
4142 self->priv->last_child = prev_sibling;
4143
4144 child->priv->parent = NULL;
4145 child->priv->prev_sibling = NULL;
4146 child->priv->next_sibling = NULL;
4147 }
4148
4149 typedef enum {
4150 REMOVE_CHILD_DESTROY_META = 1 << 0,
4151 REMOVE_CHILD_EMIT_PARENT_SET = 1 << 1,
4152 REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
4153 REMOVE_CHILD_CHECK_STATE = 1 << 3,
4154 REMOVE_CHILD_FLUSH_QUEUE = 1 << 4,
4155 REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5,
4156 REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6,
4157
4158 /* default flags for public API */
4159 REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
4160 REMOVE_CHILD_DESTROY_META |
4161 REMOVE_CHILD_EMIT_PARENT_SET |
4162 REMOVE_CHILD_EMIT_ACTOR_REMOVED |
4163 REMOVE_CHILD_CHECK_STATE |
4164 REMOVE_CHILD_FLUSH_QUEUE |
4165 REMOVE_CHILD_NOTIFY_FIRST_LAST,
4166
4167 /* flags for legacy/deprecated API */
4168 REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
4169 REMOVE_CHILD_CHECK_STATE |
4170 REMOVE_CHILD_FLUSH_QUEUE |
4171 REMOVE_CHILD_EMIT_PARENT_SET |
4172 REMOVE_CHILD_NOTIFY_FIRST_LAST
4173 } ClutterActorRemoveChildFlags;
4174
4175 /*< private >
4176 * clutter_actor_remove_child_internal:
4177 * @self: a #ClutterActor
4178 * @child: the child of @self that has to be removed
4179 * @flags: control the removal operations
4180 *
4181 * Removes @child from the list of children of @self.
4182 */
4183 static void
clutter_actor_remove_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorRemoveChildFlags flags)4184 clutter_actor_remove_child_internal (ClutterActor *self,
4185 ClutterActor *child,
4186 ClutterActorRemoveChildFlags flags)
4187 {
4188 ClutterActor *old_first, *old_last;
4189 gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
4190 gboolean flush_queue;
4191 gboolean notify_first_last;
4192 gboolean was_mapped;
4193 gboolean stop_transitions;
4194 GObject *obj;
4195
4196 if (self == child)
4197 {
4198 g_warning ("Cannot remove actor '%s' from itself.",
4199 _clutter_actor_get_debug_name (self));
4200 return;
4201 }
4202
4203 destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
4204 emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
4205 emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
4206 check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
4207 flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
4208 notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
4209 stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
4210
4211 obj = G_OBJECT (self);
4212 g_object_freeze_notify (obj);
4213
4214 if (stop_transitions)
4215 _clutter_actor_stop_transitions (child);
4216
4217 if (destroy_meta)
4218 clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
4219
4220 if (check_state)
4221 {
4222 was_mapped = CLUTTER_ACTOR_IS_MAPPED (child);
4223
4224 /* we need to unrealize *before* we set parent_actor to NULL,
4225 * because in an unrealize method actors are dissociating from the
4226 * stage, which means they need to be able to
4227 * clutter_actor_get_stage().
4228 *
4229 * yhis should unmap and unrealize, unless we're reparenting.
4230 */
4231 clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
4232 }
4233 else
4234 was_mapped = FALSE;
4235
4236 if (flush_queue)
4237 {
4238 /* We take this opportunity to invalidate any queue redraw entry
4239 * associated with the actor and descendants since we won't be able to
4240 * determine the appropriate stage after this.
4241 *
4242 * we do this after we updated the mapped state because actors might
4243 * end up queueing redraws inside their mapped/unmapped virtual
4244 * functions, and if we invalidate the redraw entry we could end up
4245 * with an inconsistent state and weird memory corruption. see
4246 * bugs:
4247 *
4248 * http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
4249 * https://bugzilla.gnome.org/show_bug.cgi?id=652036
4250 */
4251 _clutter_actor_traverse (child,
4252 0,
4253 invalidate_queue_redraw_entry,
4254 NULL,
4255 NULL);
4256 }
4257
4258 old_first = self->priv->first_child;
4259 old_last = self->priv->last_child;
4260
4261 remove_child (self, child);
4262
4263 self->priv->n_children -= 1;
4264
4265 self->priv->age += 1;
4266
4267 /* if the child that got removed was visible and set to
4268 * expand then we want to reset the parent's state in
4269 * case the child was the only thing that was making it
4270 * expand.
4271 */
4272 if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
4273 (child->priv->needs_compute_expand ||
4274 child->priv->needs_x_expand ||
4275 child->priv->needs_y_expand))
4276 {
4277 clutter_actor_queue_compute_expand (self);
4278 }
4279
4280 /* clutter_actor_reparent() will emit ::parent-set for us */
4281 if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
4282 g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
4283
4284 /* if the child was mapped then we need to relayout ourselves to account
4285 * for the removed child
4286 */
4287 if (was_mapped)
4288 clutter_actor_queue_relayout (self);
4289
4290 /* we need to emit the signal before dropping the reference */
4291 if (emit_actor_removed)
4292 g_signal_emit_by_name (self, "actor-removed", child);
4293
4294 if (notify_first_last)
4295 {
4296 if (old_first != self->priv->first_child)
4297 g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
4298
4299 if (old_last != self->priv->last_child)
4300 g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
4301 }
4302
4303 g_object_thaw_notify (obj);
4304
4305 /* remove the reference we acquired in clutter_actor_add_child() */
4306 g_object_unref (child);
4307 }
4308
4309 static const ClutterTransformInfo default_transform_info = {
4310 0.0, { 0, }, /* rotation-x */
4311 0.0, { 0, }, /* rotation-y */
4312 0.0, { 0, }, /* rotation-z */
4313
4314 1.0, 1.0, 1.0, { 0, }, /* scale */
4315
4316 { 0, }, /* anchor XXX:2.0 - remove*/
4317
4318 CLUTTER_VERTEX_INIT_ZERO, /* translation */
4319
4320 0.f, /* z-position */
4321
4322 CLUTTER_POINT_INIT_ZERO, /* pivot */
4323 0.f, /* pivot-z */
4324
4325 CLUTTER_MATRIX_INIT_IDENTITY,
4326 FALSE, /* transform */
4327 CLUTTER_MATRIX_INIT_IDENTITY,
4328 FALSE, /* child-transform */
4329 };
4330
4331 /*< private >
4332 * _clutter_actor_get_transform_info_or_defaults:
4333 * @self: a #ClutterActor
4334 *
4335 * Retrieves the ClutterTransformInfo structure associated to an actor.
4336 *
4337 * If the actor does not have a ClutterTransformInfo structure associated
4338 * to it, then the default structure will be returned.
4339 *
4340 * This function should only be used for getters.
4341 *
4342 * Return value: a const pointer to the ClutterTransformInfo structure
4343 */
4344 const ClutterTransformInfo *
_clutter_actor_get_transform_info_or_defaults(ClutterActor * self)4345 _clutter_actor_get_transform_info_or_defaults (ClutterActor *self)
4346 {
4347 ClutterTransformInfo *info;
4348
4349 info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4350 if (info != NULL)
4351 return info;
4352
4353 return &default_transform_info;
4354 }
4355
4356 static void
clutter_transform_info_free(gpointer data)4357 clutter_transform_info_free (gpointer data)
4358 {
4359 if (data != NULL)
4360 g_slice_free (ClutterTransformInfo, data);
4361 }
4362
4363 /*< private >
4364 * _clutter_actor_get_transform_info:
4365 * @self: a #ClutterActor
4366 *
4367 * Retrieves a pointer to the ClutterTransformInfo structure.
4368 *
4369 * If the actor does not have a ClutterTransformInfo associated to it, one
4370 * will be created and initialized to the default values.
4371 *
4372 * This function should be used for setters.
4373 *
4374 * For getters, you should use _clutter_actor_get_transform_info_or_defaults()
4375 * instead.
4376 *
4377 * Return value: (transfer none): a pointer to the ClutterTransformInfo
4378 * structure
4379 */
4380 ClutterTransformInfo *
_clutter_actor_get_transform_info(ClutterActor * self)4381 _clutter_actor_get_transform_info (ClutterActor *self)
4382 {
4383 ClutterTransformInfo *info;
4384
4385 info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4386 if (info == NULL)
4387 {
4388 info = g_slice_new (ClutterTransformInfo);
4389
4390 *info = default_transform_info;
4391
4392 g_object_set_qdata_full (G_OBJECT (self), quark_actor_transform_info,
4393 info,
4394 clutter_transform_info_free);
4395 }
4396
4397 return info;
4398 }
4399
4400 static inline void
clutter_actor_set_pivot_point_internal(ClutterActor * self,const ClutterPoint * pivot)4401 clutter_actor_set_pivot_point_internal (ClutterActor *self,
4402 const ClutterPoint *pivot)
4403 {
4404 ClutterTransformInfo *info;
4405
4406 info = _clutter_actor_get_transform_info (self);
4407 info->pivot = *pivot;
4408
4409 self->priv->transform_valid = FALSE;
4410
4411 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT]);
4412
4413 clutter_actor_queue_redraw (self);
4414 }
4415
4416 static inline void
clutter_actor_set_pivot_point_z_internal(ClutterActor * self,float pivot_z)4417 clutter_actor_set_pivot_point_z_internal (ClutterActor *self,
4418 float pivot_z)
4419 {
4420 ClutterTransformInfo *info;
4421
4422 info = _clutter_actor_get_transform_info (self);
4423 info->pivot_z = pivot_z;
4424
4425 self->priv->transform_valid = FALSE;
4426
4427 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT_Z]);
4428
4429 clutter_actor_queue_redraw (self);
4430 }
4431
4432 /*< private >
4433 * clutter_actor_set_translation_internal:
4434 * @self: a #ClutterActor
4435 * @axis: the axis of the translation to change
4436 * @angle: the translation as a value along @axis
4437 *
4438 * Sets the translation on the given @axis
4439 */
4440 static void
clutter_actor_set_translation_internal(ClutterActor * self,gfloat value,GParamSpec * pspec)4441 clutter_actor_set_translation_internal (ClutterActor *self,
4442 gfloat value,
4443 GParamSpec *pspec)
4444 {
4445 GObject *obj = G_OBJECT (self);
4446 ClutterTransformInfo *info;
4447
4448 info = _clutter_actor_get_transform_info (self);
4449
4450 if (pspec == obj_props[PROP_TRANSLATION_X])
4451 info->translation.x = value;
4452 else if (pspec == obj_props[PROP_TRANSLATION_Y])
4453 info->translation.y = value;
4454 else if (pspec == obj_props[PROP_TRANSLATION_Z])
4455 info->translation.z = value;
4456 else
4457 g_assert_not_reached ();
4458
4459 self->priv->transform_valid = FALSE;
4460 clutter_actor_queue_redraw (self);
4461 g_object_notify_by_pspec (obj, pspec);
4462 }
4463
4464 static inline void
clutter_actor_set_translation_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble value)4465 clutter_actor_set_translation_factor (ClutterActor *self,
4466 ClutterRotateAxis axis,
4467 gdouble value)
4468 {
4469 const ClutterTransformInfo *info;
4470 const float *translate_p = NULL;
4471 GParamSpec *pspec = NULL;
4472
4473 info = _clutter_actor_get_transform_info_or_defaults (self);
4474
4475 switch (axis)
4476 {
4477 case CLUTTER_X_AXIS:
4478 pspec = obj_props[PROP_TRANSLATION_X];
4479 translate_p = &info->translation.x;
4480 break;
4481
4482 case CLUTTER_Y_AXIS:
4483 pspec = obj_props[PROP_TRANSLATION_Y];
4484 translate_p = &info->translation.y;
4485 break;
4486
4487 case CLUTTER_Z_AXIS:
4488 pspec = obj_props[PROP_TRANSLATION_Z];
4489 translate_p = &info->translation.z;
4490 break;
4491 }
4492
4493 g_assert (pspec != NULL);
4494 g_assert (translate_p != NULL);
4495
4496 _clutter_actor_create_transition (self, pspec, *translate_p, value);
4497 }
4498
4499 /**
4500 * clutter_actor_set_translation:
4501 * @self: a #ClutterActor
4502 * @translate_x: the translation along the X axis
4503 * @translate_y: the translation along the Y axis
4504 * @translate_z: the translation along the Z axis
4505 *
4506 * Sets an additional translation transformation on a #ClutterActor,
4507 * relative to the #ClutterActor:pivot-point.
4508 *
4509 * Since: 1.12
4510 */
4511 void
clutter_actor_set_translation(ClutterActor * self,gfloat translate_x,gfloat translate_y,gfloat translate_z)4512 clutter_actor_set_translation (ClutterActor *self,
4513 gfloat translate_x,
4514 gfloat translate_y,
4515 gfloat translate_z)
4516 {
4517 g_return_if_fail (CLUTTER_IS_ACTOR (self));
4518
4519 g_object_freeze_notify (G_OBJECT (self));
4520
4521 clutter_actor_set_translation_factor (self, CLUTTER_X_AXIS, translate_x);
4522 clutter_actor_set_translation_factor (self, CLUTTER_Y_AXIS, translate_y);
4523 clutter_actor_set_translation_factor (self, CLUTTER_Z_AXIS, translate_z);
4524
4525 g_object_thaw_notify (G_OBJECT (self));
4526 }
4527
4528 /**
4529 * clutter_actor_get_translation:
4530 * @self: a #ClutterActor
4531 * @translate_x: (out) (allow-none): return location for the X component
4532 * of the translation, or %NULL
4533 * @translate_y: (out) (allow-none): return location for the Y component
4534 * of the translation, or %NULL
4535 * @translate_z: (out) (allow-none): return location for the Z component
4536 * of the translation, or %NULL
4537 *
4538 * Retrieves the translation set using clutter_actor_set_translation().
4539 *
4540 * Since: 1.12
4541 */
4542 void
clutter_actor_get_translation(ClutterActor * self,gfloat * translate_x,gfloat * translate_y,gfloat * translate_z)4543 clutter_actor_get_translation (ClutterActor *self,
4544 gfloat *translate_x,
4545 gfloat *translate_y,
4546 gfloat *translate_z)
4547 {
4548 const ClutterTransformInfo *info;
4549
4550 g_return_if_fail (CLUTTER_IS_ACTOR (self));
4551
4552 info = _clutter_actor_get_transform_info_or_defaults (self);
4553
4554 if (translate_x != NULL)
4555 *translate_x = info->translation.x;
4556
4557 if (translate_y != NULL)
4558 *translate_y = info->translation.y;
4559
4560 if (translate_z != NULL)
4561 *translate_z = info->translation.z;
4562 }
4563
4564 /*< private >
4565 * clutter_actor_set_rotation_angle_internal:
4566 * @self: a #ClutterActor
4567 * @angle: the angle of rotation
4568 * @pspec: the #GParamSpec of the property
4569 *
4570 * Sets the rotation angle on the given axis without affecting the
4571 * rotation center point.
4572 */
4573 static inline void
clutter_actor_set_rotation_angle_internal(ClutterActor * self,gdouble angle,GParamSpec * pspec)4574 clutter_actor_set_rotation_angle_internal (ClutterActor *self,
4575 gdouble angle,
4576 GParamSpec *pspec)
4577 {
4578 ClutterTransformInfo *info;
4579
4580 info = _clutter_actor_get_transform_info (self);
4581
4582 if (pspec == obj_props[PROP_ROTATION_ANGLE_X])
4583 info->rx_angle = angle;
4584 else if (pspec == obj_props[PROP_ROTATION_ANGLE_Y])
4585 info->ry_angle = angle;
4586 else if (pspec == obj_props[PROP_ROTATION_ANGLE_Z])
4587 info->rz_angle = angle;
4588 else
4589 g_assert_not_reached ();
4590
4591 self->priv->transform_valid = FALSE;
4592
4593 clutter_actor_queue_redraw (self);
4594
4595 g_object_notify_by_pspec (G_OBJECT (self), pspec);
4596 }
4597
4598 /**
4599 * clutter_actor_set_rotation_angle:
4600 * @self: a #ClutterActor
4601 * @axis: the axis to set the angle one
4602 * @angle: the angle of rotation, in degrees
4603 *
4604 * Sets the @angle of rotation of a #ClutterActor on the given @axis.
4605 *
4606 * This function is a convenience for setting the rotation properties
4607 * #ClutterActor:rotation-angle-x, #ClutterActor:rotation-angle-y,
4608 * and #ClutterActor:rotation-angle-z.
4609 *
4610 * The center of rotation is established by the #ClutterActor:pivot-point
4611 * property.
4612 *
4613 * Since: 1.12
4614 */
4615 void
clutter_actor_set_rotation_angle(ClutterActor * self,ClutterRotateAxis axis,gdouble angle)4616 clutter_actor_set_rotation_angle (ClutterActor *self,
4617 ClutterRotateAxis axis,
4618 gdouble angle)
4619 {
4620 const ClutterTransformInfo *info;
4621 const double *cur_angle_p = NULL;
4622 GParamSpec *pspec = NULL;
4623
4624 g_return_if_fail (CLUTTER_IS_ACTOR (self));
4625
4626 info = _clutter_actor_get_transform_info_or_defaults (self);
4627
4628 switch (axis)
4629 {
4630 case CLUTTER_X_AXIS:
4631 cur_angle_p = &info->rx_angle;
4632 pspec = obj_props[PROP_ROTATION_ANGLE_X];
4633 break;
4634
4635 case CLUTTER_Y_AXIS:
4636 cur_angle_p = &info->ry_angle;
4637 pspec = obj_props[PROP_ROTATION_ANGLE_Y];
4638 break;
4639
4640 case CLUTTER_Z_AXIS:
4641 cur_angle_p = &info->rz_angle;
4642 pspec = obj_props[PROP_ROTATION_ANGLE_Z];
4643 break;
4644 }
4645
4646 g_assert (pspec != NULL);
4647 g_assert (cur_angle_p != NULL);
4648
4649 _clutter_actor_create_transition (self, pspec, *cur_angle_p, angle);
4650 }
4651
4652 /**
4653 * clutter_actor_get_rotation_angle:
4654 * @self: a #ClutterActor
4655 * @axis: the axis of the rotation
4656 *
4657 * Retrieves the angle of rotation set by clutter_actor_set_rotation_angle().
4658 *
4659 * Return value: the angle of rotation, in degrees
4660 *
4661 * Since: 1.12
4662 */
4663 gdouble
clutter_actor_get_rotation_angle(ClutterActor * self,ClutterRotateAxis axis)4664 clutter_actor_get_rotation_angle (ClutterActor *self,
4665 ClutterRotateAxis axis)
4666 {
4667 const ClutterTransformInfo *info;
4668 gdouble retval;
4669
4670 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0);
4671
4672 info = _clutter_actor_get_transform_info_or_defaults (self);
4673
4674 switch (axis)
4675 {
4676 case CLUTTER_X_AXIS:
4677 retval = info->rx_angle;
4678 break;
4679
4680 case CLUTTER_Y_AXIS:
4681 retval = info->ry_angle;
4682 break;
4683
4684 case CLUTTER_Z_AXIS:
4685 retval = info->rz_angle;
4686 break;
4687
4688 default:
4689 g_warn_if_reached ();
4690 retval = 0.0;
4691 break;
4692 }
4693
4694 return retval;
4695 }
4696
4697 /*< private >
4698 * clutter_actor_set_rotation_center_internal:
4699 * @self: a #ClutterActor
4700 * @axis: the axis of the center to change
4701 * @center: the coordinates of the rotation center
4702 *
4703 * Sets the rotation center on the given axis without affecting the
4704 * rotation angle.
4705 */
4706 static inline void
clutter_actor_set_rotation_center_internal(ClutterActor * self,ClutterRotateAxis axis,const ClutterVertex * center)4707 clutter_actor_set_rotation_center_internal (ClutterActor *self,
4708 ClutterRotateAxis axis,
4709 const ClutterVertex *center)
4710 {
4711 ClutterVertex v = CLUTTER_VERTEX_INIT_ZERO;
4712 GObject *obj = G_OBJECT (self);
4713 ClutterTransformInfo *info;
4714
4715 info = _clutter_actor_get_transform_info (self);
4716
4717 if (center != NULL)
4718 v = *center;
4719
4720 g_object_freeze_notify (obj);
4721
4722 switch (axis)
4723 {
4724 case CLUTTER_X_AXIS:
4725 clutter_anchor_coord_set_units (&info->rx_center, v.x, v.y, v.z);
4726 g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_X]);
4727 break;
4728
4729 case CLUTTER_Y_AXIS:
4730 clutter_anchor_coord_set_units (&info->ry_center, v.x, v.y, v.z);
4731 g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Y]);
4732 break;
4733
4734 case CLUTTER_Z_AXIS:
4735 /* if the previously set rotation center was fractional, then
4736 * setting explicit coordinates will have to notify the
4737 * :rotation-center-z-gravity property as well
4738 */
4739 if (info->rz_center.is_fractional)
4740 g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]);
4741
4742 clutter_anchor_coord_set_units (&info->rz_center, v.x, v.y, v.z);
4743 g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z]);
4744 break;
4745 }
4746
4747 self->priv->transform_valid = FALSE;
4748
4749 g_object_thaw_notify (obj);
4750
4751 clutter_actor_queue_redraw (self);
4752 }
4753
4754 static void
clutter_actor_set_scale_factor_internal(ClutterActor * self,double factor,GParamSpec * pspec)4755 clutter_actor_set_scale_factor_internal (ClutterActor *self,
4756 double factor,
4757 GParamSpec *pspec)
4758 {
4759 GObject *obj = G_OBJECT (self);
4760 ClutterTransformInfo *info;
4761
4762 info = _clutter_actor_get_transform_info (self);
4763
4764 if (pspec == obj_props[PROP_SCALE_X])
4765 info->scale_x = factor;
4766 else if (pspec == obj_props[PROP_SCALE_Y])
4767 info->scale_y = factor;
4768 else if (pspec == obj_props[PROP_SCALE_Z])
4769 info->scale_z = factor;
4770 else
4771 g_assert_not_reached ();
4772
4773 self->priv->transform_valid = FALSE;
4774 clutter_actor_queue_redraw (self);
4775 g_object_notify_by_pspec (obj, pspec);
4776 }
4777
4778 static inline void
clutter_actor_set_scale_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble factor)4779 clutter_actor_set_scale_factor (ClutterActor *self,
4780 ClutterRotateAxis axis,
4781 gdouble factor)
4782 {
4783 const ClutterTransformInfo *info;
4784 const double *scale_p = NULL;
4785 GParamSpec *pspec = NULL;
4786
4787 info = _clutter_actor_get_transform_info_or_defaults (self);
4788
4789 switch (axis)
4790 {
4791 case CLUTTER_X_AXIS:
4792 pspec = obj_props[PROP_SCALE_X];
4793 scale_p = &info->scale_x;
4794 break;
4795
4796 case CLUTTER_Y_AXIS:
4797 pspec = obj_props[PROP_SCALE_Y];
4798 scale_p = &info->scale_y;
4799 break;
4800
4801 case CLUTTER_Z_AXIS:
4802 pspec = obj_props[PROP_SCALE_Z];
4803 scale_p = &info->scale_z;
4804 break;
4805 }
4806
4807 g_assert (pspec != NULL);
4808 g_assert (scale_p != NULL);
4809
4810 _clutter_actor_create_transition (self, pspec, *scale_p, factor);
4811 }
4812
4813 static inline void
clutter_actor_set_scale_center(ClutterActor * self,ClutterRotateAxis axis,gfloat coord)4814 clutter_actor_set_scale_center (ClutterActor *self,
4815 ClutterRotateAxis axis,
4816 gfloat coord)
4817 {
4818 GObject *obj = G_OBJECT (self);
4819 ClutterTransformInfo *info;
4820 gfloat center_x, center_y;
4821
4822 info = _clutter_actor_get_transform_info (self);
4823
4824 g_object_freeze_notify (obj);
4825
4826 /* get the current scale center coordinates */
4827 clutter_anchor_coord_get_units (self, &info->scale_center,
4828 ¢er_x,
4829 ¢er_y,
4830 NULL);
4831
4832 /* we need to notify this too, because setting explicit coordinates will
4833 * change the gravity as a side effect
4834 */
4835 if (info->scale_center.is_fractional)
4836 g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]);
4837
4838 switch (axis)
4839 {
4840 case CLUTTER_X_AXIS:
4841 clutter_anchor_coord_set_units (&info->scale_center, coord, center_y, 0);
4842 g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]);
4843 break;
4844
4845 case CLUTTER_Y_AXIS:
4846 clutter_anchor_coord_set_units (&info->scale_center, center_x, coord, 0);
4847 g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]);
4848 break;
4849
4850 default:
4851 g_assert_not_reached ();
4852 }
4853
4854 self->priv->transform_valid = FALSE;
4855
4856 clutter_actor_queue_redraw (self);
4857
4858 g_object_thaw_notify (obj);
4859 }
4860
4861 static inline void
clutter_actor_set_scale_gravity(ClutterActor * self,ClutterGravity gravity)4862 clutter_actor_set_scale_gravity (ClutterActor *self,
4863 ClutterGravity gravity)
4864 {
4865 ClutterTransformInfo *info;
4866 GObject *obj;
4867
4868 info = _clutter_actor_get_transform_info (self);
4869 obj = G_OBJECT (self);
4870
4871 if (gravity == CLUTTER_GRAVITY_NONE)
4872 clutter_anchor_coord_set_units (&info->scale_center, 0, 0, 0);
4873 else
4874 clutter_anchor_coord_set_gravity (&info->scale_center, gravity);
4875
4876 self->priv->transform_valid = FALSE;
4877
4878 g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]);
4879 g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]);
4880 g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]);
4881
4882 clutter_actor_queue_redraw (self);
4883 }
4884
4885 /* XXX:2.0 - remove */
4886 static inline void
clutter_actor_set_anchor_coord(ClutterActor * self,ClutterRotateAxis axis,gfloat coord)4887 clutter_actor_set_anchor_coord (ClutterActor *self,
4888 ClutterRotateAxis axis,
4889 gfloat coord)
4890 {
4891 GObject *obj = G_OBJECT (self);
4892 ClutterTransformInfo *info;
4893 gfloat anchor_x, anchor_y;
4894
4895 info = _clutter_actor_get_transform_info (self);
4896
4897 g_object_freeze_notify (obj);
4898
4899 clutter_anchor_coord_get_units (self, &info->anchor,
4900 &anchor_x,
4901 &anchor_y,
4902 NULL);
4903
4904 if (info->anchor.is_fractional)
4905 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]);
4906
4907 switch (axis)
4908 {
4909 case CLUTTER_X_AXIS:
4910 clutter_anchor_coord_set_units (&info->anchor,
4911 coord,
4912 anchor_y,
4913 0.0);
4914 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]);
4915 break;
4916
4917 case CLUTTER_Y_AXIS:
4918 clutter_anchor_coord_set_units (&info->anchor,
4919 anchor_x,
4920 coord,
4921 0.0);
4922 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]);
4923 break;
4924
4925 default:
4926 g_assert_not_reached ();
4927 }
4928
4929 self->priv->transform_valid = FALSE;
4930
4931 clutter_actor_queue_redraw (self);
4932
4933 g_object_thaw_notify (obj);
4934 }
4935
4936 static void
clutter_actor_set_clip_rect(ClutterActor * self,const ClutterRect * clip)4937 clutter_actor_set_clip_rect (ClutterActor *self,
4938 const ClutterRect *clip)
4939 {
4940 ClutterActorPrivate *priv = self->priv;
4941 GObject *obj = G_OBJECT (self);
4942
4943 if (clip != NULL)
4944 {
4945 priv->clip = *clip;
4946 priv->has_clip = TRUE;
4947 }
4948 else
4949 priv->has_clip = FALSE;
4950
4951 clutter_actor_queue_redraw (self);
4952
4953 g_object_notify_by_pspec (obj, obj_props[PROP_CLIP]); /* XXX:2.0 - remove */
4954 g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
4955 g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
4956 }
4957
4958 static void
clutter_actor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4959 clutter_actor_set_property (GObject *object,
4960 guint prop_id,
4961 const GValue *value,
4962 GParamSpec *pspec)
4963 {
4964 ClutterActor *actor = CLUTTER_ACTOR (object);
4965 ClutterActorPrivate *priv = actor->priv;
4966
4967 switch (prop_id)
4968 {
4969 case PROP_X:
4970 clutter_actor_set_x (actor, g_value_get_float (value));
4971 break;
4972
4973 case PROP_Y:
4974 clutter_actor_set_y (actor, g_value_get_float (value));
4975 break;
4976
4977 case PROP_POSITION:
4978 {
4979 const ClutterPoint *pos = g_value_get_boxed (value);
4980
4981 if (pos != NULL)
4982 clutter_actor_set_position (actor, pos->x, pos->y);
4983 else
4984 clutter_actor_set_fixed_position_set (actor, FALSE);
4985 }
4986 break;
4987
4988 case PROP_WIDTH:
4989 clutter_actor_set_width (actor, g_value_get_float (value));
4990 break;
4991
4992 case PROP_HEIGHT:
4993 clutter_actor_set_height (actor, g_value_get_float (value));
4994 break;
4995
4996 case PROP_SIZE:
4997 {
4998 const ClutterSize *size = g_value_get_boxed (value);
4999
5000 if (size != NULL)
5001 clutter_actor_set_size (actor, size->width, size->height);
5002 else
5003 clutter_actor_set_size (actor, -1, -1);
5004 }
5005 break;
5006
5007 case PROP_FIXED_X:
5008 clutter_actor_set_x (actor, g_value_get_float (value));
5009 break;
5010
5011 case PROP_FIXED_Y:
5012 clutter_actor_set_y (actor, g_value_get_float (value));
5013 break;
5014
5015 case PROP_FIXED_POSITION_SET:
5016 clutter_actor_set_fixed_position_set (actor, g_value_get_boolean (value));
5017 break;
5018
5019 case PROP_MIN_WIDTH:
5020 clutter_actor_set_min_width (actor, g_value_get_float (value));
5021 break;
5022
5023 case PROP_MIN_HEIGHT:
5024 clutter_actor_set_min_height (actor, g_value_get_float (value));
5025 break;
5026
5027 case PROP_NATURAL_WIDTH:
5028 clutter_actor_set_natural_width (actor, g_value_get_float (value));
5029 break;
5030
5031 case PROP_NATURAL_HEIGHT:
5032 clutter_actor_set_natural_height (actor, g_value_get_float (value));
5033 break;
5034
5035 case PROP_MIN_WIDTH_SET:
5036 clutter_actor_set_min_width_set (actor, g_value_get_boolean (value));
5037 break;
5038
5039 case PROP_MIN_HEIGHT_SET:
5040 clutter_actor_set_min_height_set (actor, g_value_get_boolean (value));
5041 break;
5042
5043 case PROP_NATURAL_WIDTH_SET:
5044 clutter_actor_set_natural_width_set (actor, g_value_get_boolean (value));
5045 break;
5046
5047 case PROP_NATURAL_HEIGHT_SET:
5048 clutter_actor_set_natural_height_set (actor, g_value_get_boolean (value));
5049 break;
5050
5051 case PROP_REQUEST_MODE:
5052 clutter_actor_set_request_mode (actor, g_value_get_enum (value));
5053 break;
5054
5055 case PROP_DEPTH: /* XXX:2.0 - remove */
5056 clutter_actor_set_depth (actor, g_value_get_float (value));
5057 break;
5058
5059 case PROP_Z_POSITION:
5060 clutter_actor_set_z_position (actor, g_value_get_float (value));
5061 break;
5062
5063 case PROP_OPACITY:
5064 clutter_actor_set_opacity (actor, g_value_get_uint (value));
5065 break;
5066
5067 case PROP_OFFSCREEN_REDIRECT:
5068 clutter_actor_set_offscreen_redirect (actor, g_value_get_enum (value));
5069 break;
5070
5071 case PROP_NAME:
5072 clutter_actor_set_name (actor, g_value_get_string (value));
5073 break;
5074
5075 case PROP_VISIBLE:
5076 if (g_value_get_boolean (value) == TRUE)
5077 clutter_actor_show (actor);
5078 else
5079 clutter_actor_hide (actor);
5080 break;
5081
5082 case PROP_PIVOT_POINT:
5083 {
5084 const ClutterPoint *pivot = g_value_get_boxed (value);
5085
5086 if (pivot == NULL)
5087 pivot = clutter_point_zero ();
5088
5089 clutter_actor_set_pivot_point (actor, pivot->x, pivot->y);
5090 }
5091 break;
5092
5093 case PROP_PIVOT_POINT_Z:
5094 clutter_actor_set_pivot_point_z (actor, g_value_get_float (value));
5095 break;
5096
5097 case PROP_TRANSLATION_X:
5098 clutter_actor_set_translation_factor (actor, CLUTTER_X_AXIS,
5099 g_value_get_float (value));
5100 break;
5101
5102 case PROP_TRANSLATION_Y:
5103 clutter_actor_set_translation_factor (actor, CLUTTER_Y_AXIS,
5104 g_value_get_float (value));
5105 break;
5106
5107 case PROP_TRANSLATION_Z:
5108 clutter_actor_set_translation_factor (actor, CLUTTER_Z_AXIS,
5109 g_value_get_float (value));
5110 break;
5111
5112 case PROP_SCALE_X:
5113 clutter_actor_set_scale_factor (actor, CLUTTER_X_AXIS,
5114 g_value_get_double (value));
5115 break;
5116
5117 case PROP_SCALE_Y:
5118 clutter_actor_set_scale_factor (actor, CLUTTER_Y_AXIS,
5119 g_value_get_double (value));
5120 break;
5121
5122 case PROP_SCALE_Z:
5123 clutter_actor_set_scale_factor (actor, CLUTTER_Z_AXIS,
5124 g_value_get_double (value));
5125 break;
5126
5127 case PROP_SCALE_CENTER_X: /* XXX:2.0 - remove */
5128 clutter_actor_set_scale_center (actor, CLUTTER_X_AXIS,
5129 g_value_get_float (value));
5130 break;
5131
5132 case PROP_SCALE_CENTER_Y: /* XXX:2.0 - remove */
5133 clutter_actor_set_scale_center (actor, CLUTTER_Y_AXIS,
5134 g_value_get_float (value));
5135 break;
5136
5137 case PROP_SCALE_GRAVITY: /* XXX:2.0 - remove */
5138 clutter_actor_set_scale_gravity (actor, g_value_get_enum (value));
5139 break;
5140
5141 case PROP_CLIP: /* XXX:2.0 - remove */
5142 {
5143 const ClutterGeometry *geom = g_value_get_boxed (value);
5144
5145 clutter_actor_set_clip (actor,
5146 geom->x, geom->y,
5147 geom->width, geom->height);
5148 }
5149 break;
5150
5151 case PROP_CLIP_RECT:
5152 clutter_actor_set_clip_rect (actor, g_value_get_boxed (value));
5153 break;
5154
5155 case PROP_CLIP_TO_ALLOCATION:
5156 clutter_actor_set_clip_to_allocation (actor, g_value_get_boolean (value));
5157 break;
5158
5159 case PROP_REACTIVE:
5160 clutter_actor_set_reactive (actor, g_value_get_boolean (value));
5161 break;
5162
5163 case PROP_ROTATION_ANGLE_X:
5164 clutter_actor_set_rotation_angle (actor,
5165 CLUTTER_X_AXIS,
5166 g_value_get_double (value));
5167 break;
5168
5169 case PROP_ROTATION_ANGLE_Y:
5170 clutter_actor_set_rotation_angle (actor,
5171 CLUTTER_Y_AXIS,
5172 g_value_get_double (value));
5173 break;
5174
5175 case PROP_ROTATION_ANGLE_Z:
5176 clutter_actor_set_rotation_angle (actor,
5177 CLUTTER_Z_AXIS,
5178 g_value_get_double (value));
5179 break;
5180
5181 case PROP_ROTATION_CENTER_X: /* XXX:2.0 - remove */
5182 clutter_actor_set_rotation_center_internal (actor,
5183 CLUTTER_X_AXIS,
5184 g_value_get_boxed (value));
5185 break;
5186
5187 case PROP_ROTATION_CENTER_Y: /* XXX:2.0 - remove */
5188 clutter_actor_set_rotation_center_internal (actor,
5189 CLUTTER_Y_AXIS,
5190 g_value_get_boxed (value));
5191 break;
5192
5193 case PROP_ROTATION_CENTER_Z: /* XXX:2.0 - remove */
5194 clutter_actor_set_rotation_center_internal (actor,
5195 CLUTTER_Z_AXIS,
5196 g_value_get_boxed (value));
5197 break;
5198
5199 case PROP_ROTATION_CENTER_Z_GRAVITY: /* XXX:2.0 - remove */
5200 {
5201 const ClutterTransformInfo *info;
5202
5203 info = _clutter_actor_get_transform_info_or_defaults (actor);
5204 clutter_actor_set_z_rotation_from_gravity (actor, info->rz_angle,
5205 g_value_get_enum (value));
5206 }
5207 break;
5208
5209 case PROP_ANCHOR_X: /* XXX:2.0 - remove */
5210 clutter_actor_set_anchor_coord (actor, CLUTTER_X_AXIS,
5211 g_value_get_float (value));
5212 break;
5213
5214 case PROP_ANCHOR_Y: /* XXX:2.0 - remove */
5215 clutter_actor_set_anchor_coord (actor, CLUTTER_Y_AXIS,
5216 g_value_get_float (value));
5217 break;
5218
5219 case PROP_ANCHOR_GRAVITY: /* XXX:2.0 - remove */
5220 clutter_actor_set_anchor_point_from_gravity (actor,
5221 g_value_get_enum (value));
5222 break;
5223
5224 case PROP_TRANSFORM:
5225 clutter_actor_set_transform (actor, g_value_get_boxed (value));
5226 break;
5227
5228 case PROP_CHILD_TRANSFORM:
5229 clutter_actor_set_child_transform (actor, g_value_get_boxed (value));
5230 break;
5231
5232 case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
5233 priv->show_on_set_parent = g_value_get_boolean (value);
5234 break;
5235
5236 case PROP_TEXT_DIRECTION:
5237 clutter_actor_set_text_direction (actor, g_value_get_enum (value));
5238 break;
5239
5240 case PROP_ACTIONS:
5241 clutter_actor_add_action (actor, g_value_get_object (value));
5242 break;
5243
5244 case PROP_CONSTRAINTS:
5245 clutter_actor_add_constraint (actor, g_value_get_object (value));
5246 break;
5247
5248 case PROP_EFFECT:
5249 clutter_actor_add_effect (actor, g_value_get_object (value));
5250 break;
5251
5252 case PROP_LAYOUT_MANAGER:
5253 clutter_actor_set_layout_manager (actor, g_value_get_object (value));
5254 break;
5255
5256 case PROP_X_EXPAND:
5257 clutter_actor_set_x_expand (actor, g_value_get_boolean (value));
5258 break;
5259
5260 case PROP_Y_EXPAND:
5261 clutter_actor_set_y_expand (actor, g_value_get_boolean (value));
5262 break;
5263
5264 case PROP_X_ALIGN:
5265 clutter_actor_set_x_align (actor, g_value_get_enum (value));
5266 break;
5267
5268 case PROP_Y_ALIGN:
5269 clutter_actor_set_y_align (actor, g_value_get_enum (value));
5270 break;
5271
5272 case PROP_MARGIN_TOP:
5273 clutter_actor_set_margin_top (actor, g_value_get_float (value));
5274 break;
5275
5276 case PROP_MARGIN_BOTTOM:
5277 clutter_actor_set_margin_bottom (actor, g_value_get_float (value));
5278 break;
5279
5280 case PROP_MARGIN_LEFT:
5281 clutter_actor_set_margin_left (actor, g_value_get_float (value));
5282 break;
5283
5284 case PROP_MARGIN_RIGHT:
5285 clutter_actor_set_margin_right (actor, g_value_get_float (value));
5286 break;
5287
5288 case PROP_BACKGROUND_COLOR:
5289 clutter_actor_set_background_color (actor, g_value_get_boxed (value));
5290 break;
5291
5292 case PROP_CONTENT:
5293 clutter_actor_set_content (actor, g_value_get_object (value));
5294 break;
5295
5296 case PROP_CONTENT_GRAVITY:
5297 clutter_actor_set_content_gravity (actor, g_value_get_enum (value));
5298 break;
5299
5300 case PROP_MINIFICATION_FILTER:
5301 clutter_actor_set_content_scaling_filters (actor,
5302 g_value_get_enum (value),
5303 actor->priv->mag_filter);
5304 break;
5305
5306 case PROP_MAGNIFICATION_FILTER:
5307 clutter_actor_set_content_scaling_filters (actor,
5308 actor->priv->min_filter,
5309 g_value_get_enum (value));
5310 break;
5311
5312 case PROP_CONTENT_REPEAT:
5313 clutter_actor_set_content_repeat (actor, g_value_get_flags (value));
5314 break;
5315
5316 default:
5317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5318 break;
5319 }
5320 }
5321
5322 static void
clutter_actor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)5323 clutter_actor_get_property (GObject *object,
5324 guint prop_id,
5325 GValue *value,
5326 GParamSpec *pspec)
5327 {
5328 ClutterActor *actor = CLUTTER_ACTOR (object);
5329 ClutterActorPrivate *priv = actor->priv;
5330
5331 switch (prop_id)
5332 {
5333 case PROP_X:
5334 g_value_set_float (value, clutter_actor_get_x (actor));
5335 break;
5336
5337 case PROP_Y:
5338 g_value_set_float (value, clutter_actor_get_y (actor));
5339 break;
5340
5341 case PROP_POSITION:
5342 {
5343 ClutterPoint position;
5344
5345 clutter_point_init (&position,
5346 clutter_actor_get_x (actor),
5347 clutter_actor_get_y (actor));
5348 g_value_set_boxed (value, &position);
5349 }
5350 break;
5351
5352 case PROP_WIDTH:
5353 g_value_set_float (value, clutter_actor_get_width (actor));
5354 break;
5355
5356 case PROP_HEIGHT:
5357 g_value_set_float (value, clutter_actor_get_height (actor));
5358 break;
5359
5360 case PROP_SIZE:
5361 {
5362 ClutterSize size;
5363
5364 clutter_size_init (&size,
5365 clutter_actor_get_width (actor),
5366 clutter_actor_get_height (actor));
5367 g_value_set_boxed (value, &size);
5368 }
5369 break;
5370
5371 case PROP_FIXED_X:
5372 {
5373 const ClutterLayoutInfo *info;
5374
5375 info = _clutter_actor_get_layout_info_or_defaults (actor);
5376 g_value_set_float (value, info->fixed_pos.x);
5377 }
5378 break;
5379
5380 case PROP_FIXED_Y:
5381 {
5382 const ClutterLayoutInfo *info;
5383
5384 info = _clutter_actor_get_layout_info_or_defaults (actor);
5385 g_value_set_float (value, info->fixed_pos.y);
5386 }
5387 break;
5388
5389 case PROP_FIXED_POSITION_SET:
5390 g_value_set_boolean (value, priv->position_set);
5391 break;
5392
5393 case PROP_MIN_WIDTH:
5394 {
5395 const ClutterLayoutInfo *info;
5396
5397 info = _clutter_actor_get_layout_info_or_defaults (actor);
5398 g_value_set_float (value, info->minimum.width);
5399 }
5400 break;
5401
5402 case PROP_MIN_HEIGHT:
5403 {
5404 const ClutterLayoutInfo *info;
5405
5406 info = _clutter_actor_get_layout_info_or_defaults (actor);
5407 g_value_set_float (value, info->minimum.height);
5408 }
5409 break;
5410
5411 case PROP_NATURAL_WIDTH:
5412 {
5413 const ClutterLayoutInfo *info;
5414
5415 info = _clutter_actor_get_layout_info_or_defaults (actor);
5416 g_value_set_float (value, info->natural.width);
5417 }
5418 break;
5419
5420 case PROP_NATURAL_HEIGHT:
5421 {
5422 const ClutterLayoutInfo *info;
5423
5424 info = _clutter_actor_get_layout_info_or_defaults (actor);
5425 g_value_set_float (value, info->natural.height);
5426 }
5427 break;
5428
5429 case PROP_MIN_WIDTH_SET:
5430 g_value_set_boolean (value, priv->min_width_set);
5431 break;
5432
5433 case PROP_MIN_HEIGHT_SET:
5434 g_value_set_boolean (value, priv->min_height_set);
5435 break;
5436
5437 case PROP_NATURAL_WIDTH_SET:
5438 g_value_set_boolean (value, priv->natural_width_set);
5439 break;
5440
5441 case PROP_NATURAL_HEIGHT_SET:
5442 g_value_set_boolean (value, priv->natural_height_set);
5443 break;
5444
5445 case PROP_REQUEST_MODE:
5446 g_value_set_enum (value, priv->request_mode);
5447 break;
5448
5449 case PROP_ALLOCATION:
5450 g_value_set_boxed (value, &priv->allocation);
5451 break;
5452
5453 case PROP_DEPTH: /* XXX:2.0 - remove */
5454 g_value_set_float (value, clutter_actor_get_depth (actor));
5455 break;
5456
5457 case PROP_Z_POSITION:
5458 g_value_set_float (value, clutter_actor_get_z_position (actor));
5459 break;
5460
5461 case PROP_OPACITY:
5462 g_value_set_uint (value, priv->opacity);
5463 break;
5464
5465 case PROP_OFFSCREEN_REDIRECT:
5466 g_value_set_flags (value, priv->offscreen_redirect);
5467 break;
5468
5469 case PROP_NAME:
5470 g_value_set_string (value, priv->name);
5471 break;
5472
5473 case PROP_VISIBLE:
5474 g_value_set_boolean (value, CLUTTER_ACTOR_IS_VISIBLE (actor));
5475 break;
5476
5477 case PROP_MAPPED:
5478 g_value_set_boolean (value, CLUTTER_ACTOR_IS_MAPPED (actor));
5479 break;
5480
5481 case PROP_REALIZED:
5482 g_value_set_boolean (value, CLUTTER_ACTOR_IS_REALIZED (actor));
5483 break;
5484
5485 case PROP_HAS_CLIP:
5486 g_value_set_boolean (value, priv->has_clip);
5487 break;
5488
5489 case PROP_CLIP: /* XXX:2.0 - remove */
5490 {
5491 ClutterGeometry clip;
5492
5493 clip.x = CLUTTER_NEARBYINT (priv->clip.origin.x);
5494 clip.y = CLUTTER_NEARBYINT (priv->clip.origin.y);
5495 clip.width = CLUTTER_NEARBYINT (priv->clip.size.width);
5496 clip.height = CLUTTER_NEARBYINT (priv->clip.size.height);
5497
5498 g_value_set_boxed (value, &clip);
5499 }
5500 break;
5501
5502 case PROP_CLIP_RECT:
5503 g_value_set_boxed (value, &priv->clip);
5504 break;
5505
5506 case PROP_CLIP_TO_ALLOCATION:
5507 g_value_set_boolean (value, priv->clip_to_allocation);
5508 break;
5509
5510 case PROP_PIVOT_POINT:
5511 {
5512 const ClutterTransformInfo *info;
5513
5514 info = _clutter_actor_get_transform_info_or_defaults (actor);
5515 g_value_set_boxed (value, &info->pivot);
5516 }
5517 break;
5518
5519 case PROP_PIVOT_POINT_Z:
5520 {
5521 const ClutterTransformInfo *info;
5522
5523 info = _clutter_actor_get_transform_info_or_defaults (actor);
5524 g_value_set_float (value, info->pivot_z);
5525 }
5526 break;
5527
5528 case PROP_TRANSLATION_X:
5529 {
5530 const ClutterTransformInfo *info;
5531
5532 info = _clutter_actor_get_transform_info_or_defaults (actor);
5533 g_value_set_float (value, info->translation.x);
5534 }
5535 break;
5536
5537 case PROP_TRANSLATION_Y:
5538 {
5539 const ClutterTransformInfo *info;
5540
5541 info = _clutter_actor_get_transform_info_or_defaults (actor);
5542 g_value_set_float (value, info->translation.y);
5543 }
5544 break;
5545
5546 case PROP_TRANSLATION_Z:
5547 {
5548 const ClutterTransformInfo *info;
5549
5550 info = _clutter_actor_get_transform_info_or_defaults (actor);
5551 g_value_set_float (value, info->translation.z);
5552 }
5553 break;
5554
5555 case PROP_SCALE_X:
5556 {
5557 const ClutterTransformInfo *info;
5558
5559 info = _clutter_actor_get_transform_info_or_defaults (actor);
5560 g_value_set_double (value, info->scale_x);
5561 }
5562 break;
5563
5564 case PROP_SCALE_Y:
5565 {
5566 const ClutterTransformInfo *info;
5567
5568 info = _clutter_actor_get_transform_info_or_defaults (actor);
5569 g_value_set_double (value, info->scale_y);
5570 }
5571 break;
5572
5573 case PROP_SCALE_Z:
5574 {
5575 const ClutterTransformInfo *info;
5576
5577 info = _clutter_actor_get_transform_info_or_defaults (actor);
5578 g_value_set_double (value, info->scale_z);
5579 }
5580 break;
5581
5582 case PROP_SCALE_CENTER_X: /* XXX:2.0 - remove */
5583 {
5584 gfloat center;
5585
5586 clutter_actor_get_scale_center (actor, ¢er, NULL);
5587
5588 g_value_set_float (value, center);
5589 }
5590 break;
5591
5592 case PROP_SCALE_CENTER_Y: /* XXX:2.0 - remove */
5593 {
5594 gfloat center;
5595
5596 clutter_actor_get_scale_center (actor, NULL, ¢er);
5597
5598 g_value_set_float (value, center);
5599 }
5600 break;
5601
5602 case PROP_SCALE_GRAVITY: /* XXX:2.0 - remove */
5603 g_value_set_enum (value, clutter_actor_get_scale_gravity (actor));
5604 break;
5605
5606 case PROP_REACTIVE:
5607 g_value_set_boolean (value, clutter_actor_get_reactive (actor));
5608 break;
5609
5610 case PROP_ROTATION_ANGLE_X:
5611 {
5612 const ClutterTransformInfo *info;
5613
5614 info = _clutter_actor_get_transform_info_or_defaults (actor);
5615 g_value_set_double (value, info->rx_angle);
5616 }
5617 break;
5618
5619 case PROP_ROTATION_ANGLE_Y:
5620 {
5621 const ClutterTransformInfo *info;
5622
5623 info = _clutter_actor_get_transform_info_or_defaults (actor);
5624 g_value_set_double (value, info->ry_angle);
5625 }
5626 break;
5627
5628 case PROP_ROTATION_ANGLE_Z:
5629 {
5630 const ClutterTransformInfo *info;
5631
5632 info = _clutter_actor_get_transform_info_or_defaults (actor);
5633 g_value_set_double (value, info->rz_angle);
5634 }
5635 break;
5636
5637 case PROP_ROTATION_CENTER_X: /* XXX:2.0 - remove */
5638 {
5639 ClutterVertex center;
5640
5641 clutter_actor_get_rotation (actor, CLUTTER_X_AXIS,
5642 ¢er.x,
5643 ¢er.y,
5644 ¢er.z);
5645
5646 g_value_set_boxed (value, ¢er);
5647 }
5648 break;
5649
5650 case PROP_ROTATION_CENTER_Y: /* XXX:2.0 - remove */
5651 {
5652 ClutterVertex center;
5653
5654 clutter_actor_get_rotation (actor, CLUTTER_Y_AXIS,
5655 ¢er.x,
5656 ¢er.y,
5657 ¢er.z);
5658
5659 g_value_set_boxed (value, ¢er);
5660 }
5661 break;
5662
5663 case PROP_ROTATION_CENTER_Z: /* XXX:2.0 - remove */
5664 {
5665 ClutterVertex center;
5666
5667 clutter_actor_get_rotation (actor, CLUTTER_Z_AXIS,
5668 ¢er.x,
5669 ¢er.y,
5670 ¢er.z);
5671
5672 g_value_set_boxed (value, ¢er);
5673 }
5674 break;
5675
5676 case PROP_ROTATION_CENTER_Z_GRAVITY: /* XXX:2.0 - remove */
5677 g_value_set_enum (value, clutter_actor_get_z_rotation_gravity (actor));
5678 break;
5679
5680 case PROP_ANCHOR_X: /* XXX:2.0 - remove */
5681 {
5682 const ClutterTransformInfo *info;
5683 gfloat anchor_x;
5684
5685 info = _clutter_actor_get_transform_info_or_defaults (actor);
5686 clutter_anchor_coord_get_units (actor, &info->anchor,
5687 &anchor_x,
5688 NULL,
5689 NULL);
5690 g_value_set_float (value, anchor_x);
5691 }
5692 break;
5693
5694 case PROP_ANCHOR_Y: /* XXX:2.0 - remove */
5695 {
5696 const ClutterTransformInfo *info;
5697 gfloat anchor_y;
5698
5699 info = _clutter_actor_get_transform_info_or_defaults (actor);
5700 clutter_anchor_coord_get_units (actor, &info->anchor,
5701 NULL,
5702 &anchor_y,
5703 NULL);
5704 g_value_set_float (value, anchor_y);
5705 }
5706 break;
5707
5708 case PROP_ANCHOR_GRAVITY: /* XXX:2.0 - remove */
5709 g_value_set_enum (value, clutter_actor_get_anchor_point_gravity (actor));
5710 break;
5711
5712 case PROP_TRANSFORM:
5713 {
5714 ClutterMatrix m;
5715
5716 clutter_actor_get_transform (actor, &m);
5717 g_value_set_boxed (value, &m);
5718 }
5719 break;
5720
5721 case PROP_TRANSFORM_SET:
5722 {
5723 const ClutterTransformInfo *info;
5724
5725 info = _clutter_actor_get_transform_info_or_defaults (actor);
5726 g_value_set_boolean (value, info->transform_set);
5727 }
5728 break;
5729
5730 case PROP_CHILD_TRANSFORM:
5731 {
5732 ClutterMatrix m;
5733
5734 clutter_actor_get_child_transform (actor, &m);
5735 g_value_set_boxed (value, &m);
5736 }
5737 break;
5738
5739 case PROP_CHILD_TRANSFORM_SET:
5740 {
5741 const ClutterTransformInfo *info;
5742
5743 info = _clutter_actor_get_transform_info_or_defaults (actor);
5744 g_value_set_boolean (value, info->child_transform_set);
5745 }
5746 break;
5747
5748 case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
5749 g_value_set_boolean (value, priv->show_on_set_parent);
5750 break;
5751
5752 case PROP_TEXT_DIRECTION:
5753 g_value_set_enum (value, priv->text_direction);
5754 break;
5755
5756 case PROP_HAS_POINTER:
5757 g_value_set_boolean (value, priv->has_pointer);
5758 break;
5759
5760 case PROP_LAYOUT_MANAGER:
5761 g_value_set_object (value, priv->layout_manager);
5762 break;
5763
5764 case PROP_X_EXPAND:
5765 {
5766 const ClutterLayoutInfo *info;
5767
5768 info = _clutter_actor_get_layout_info_or_defaults (actor);
5769 g_value_set_boolean (value, info->x_expand);
5770 }
5771 break;
5772
5773 case PROP_Y_EXPAND:
5774 {
5775 const ClutterLayoutInfo *info;
5776
5777 info = _clutter_actor_get_layout_info_or_defaults (actor);
5778 g_value_set_boolean (value, info->y_expand);
5779 }
5780 break;
5781
5782 case PROP_X_ALIGN:
5783 {
5784 const ClutterLayoutInfo *info;
5785
5786 info = _clutter_actor_get_layout_info_or_defaults (actor);
5787 g_value_set_enum (value, info->x_align);
5788 }
5789 break;
5790
5791 case PROP_Y_ALIGN:
5792 {
5793 const ClutterLayoutInfo *info;
5794
5795 info = _clutter_actor_get_layout_info_or_defaults (actor);
5796 g_value_set_enum (value, info->y_align);
5797 }
5798 break;
5799
5800 case PROP_MARGIN_TOP:
5801 {
5802 const ClutterLayoutInfo *info;
5803
5804 info = _clutter_actor_get_layout_info_or_defaults (actor);
5805 g_value_set_float (value, info->margin.top);
5806 }
5807 break;
5808
5809 case PROP_MARGIN_BOTTOM:
5810 {
5811 const ClutterLayoutInfo *info;
5812
5813 info = _clutter_actor_get_layout_info_or_defaults (actor);
5814 g_value_set_float (value, info->margin.bottom);
5815 }
5816 break;
5817
5818 case PROP_MARGIN_LEFT:
5819 {
5820 const ClutterLayoutInfo *info;
5821
5822 info = _clutter_actor_get_layout_info_or_defaults (actor);
5823 g_value_set_float (value, info->margin.left);
5824 }
5825 break;
5826
5827 case PROP_MARGIN_RIGHT:
5828 {
5829 const ClutterLayoutInfo *info;
5830
5831 info = _clutter_actor_get_layout_info_or_defaults (actor);
5832 g_value_set_float (value, info->margin.right);
5833 }
5834 break;
5835
5836 case PROP_BACKGROUND_COLOR_SET:
5837 g_value_set_boolean (value, priv->bg_color_set);
5838 break;
5839
5840 case PROP_BACKGROUND_COLOR:
5841 g_value_set_boxed (value, &priv->bg_color);
5842 break;
5843
5844 case PROP_FIRST_CHILD:
5845 g_value_set_object (value, priv->first_child);
5846 break;
5847
5848 case PROP_LAST_CHILD:
5849 g_value_set_object (value, priv->last_child);
5850 break;
5851
5852 case PROP_CONTENT:
5853 g_value_set_object (value, priv->content);
5854 break;
5855
5856 case PROP_CONTENT_GRAVITY:
5857 g_value_set_enum (value, priv->content_gravity);
5858 break;
5859
5860 case PROP_CONTENT_BOX:
5861 {
5862 ClutterActorBox box = { 0, };
5863
5864 clutter_actor_get_content_box (actor, &box);
5865 g_value_set_boxed (value, &box);
5866 }
5867 break;
5868
5869 case PROP_MINIFICATION_FILTER:
5870 g_value_set_enum (value, priv->min_filter);
5871 break;
5872
5873 case PROP_MAGNIFICATION_FILTER:
5874 g_value_set_enum (value, priv->mag_filter);
5875 break;
5876
5877 case PROP_CONTENT_REPEAT:
5878 g_value_set_flags (value, priv->content_repeat);
5879 break;
5880
5881 default:
5882 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5883 break;
5884 }
5885 }
5886
5887 static void
clutter_actor_dispose(GObject * object)5888 clutter_actor_dispose (GObject *object)
5889 {
5890 ClutterActor *self = CLUTTER_ACTOR (object);
5891 ClutterActorPrivate *priv = self->priv;
5892
5893 CLUTTER_NOTE (MISC, "Dispose actor (name='%s', ref_count:%d) of type '%s'",
5894 _clutter_actor_get_debug_name (self),
5895 object->ref_count,
5896 g_type_name (G_OBJECT_TYPE (self)));
5897
5898 g_signal_emit (self, actor_signals[DESTROY], 0);
5899
5900 /* avoid recursing when called from clutter_actor_destroy() */
5901 if (priv->parent != NULL)
5902 {
5903 ClutterActor *parent = priv->parent;
5904
5905 /* go through the Container implementation unless this
5906 * is an internal child and has been marked as such.
5907 *
5908 * removing the actor from its parent will reset the
5909 * realized and mapped states.
5910 */
5911 if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
5912 clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self);
5913 else
5914 clutter_actor_remove_child_internal (parent, self,
5915 REMOVE_CHILD_LEGACY_FLAGS);
5916 }
5917
5918 /* parent must be gone at this point */
5919 g_assert (priv->parent == NULL);
5920
5921 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
5922 {
5923 /* can't be mapped or realized with no parent */
5924 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
5925 g_assert (!CLUTTER_ACTOR_IS_REALIZED (self));
5926 }
5927
5928 g_clear_object (&priv->pango_context);
5929 g_clear_object (&priv->actions);
5930 g_clear_object (&priv->constraints);
5931 g_clear_object (&priv->effects);
5932 g_clear_object (&priv->flatten_effect);
5933
5934 if (priv->child_model != NULL)
5935 {
5936 if (priv->create_child_notify != NULL)
5937 priv->create_child_notify (priv->create_child_data);
5938
5939 priv->create_child_func = NULL;
5940 priv->create_child_data = NULL;
5941 priv->create_child_notify = NULL;
5942
5943 g_clear_object (&priv->child_model);
5944 }
5945
5946 if (priv->layout_manager != NULL)
5947 {
5948 clutter_layout_manager_set_container (priv->layout_manager, NULL);
5949 g_clear_object (&priv->layout_manager);
5950 }
5951
5952 if (priv->content != NULL)
5953 {
5954 _clutter_content_detached (priv->content, self);
5955 g_clear_object (&priv->content);
5956 }
5957
5958 if (priv->clones != NULL)
5959 {
5960 g_hash_table_unref (priv->clones);
5961 priv->clones = NULL;
5962 }
5963
5964 G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
5965 }
5966
5967 static void
clutter_actor_finalize(GObject * object)5968 clutter_actor_finalize (GObject *object)
5969 {
5970 ClutterActorPrivate *priv = CLUTTER_ACTOR (object)->priv;
5971
5972 CLUTTER_NOTE (MISC, "Finalize actor (name='%s') of type '%s'",
5973 _clutter_actor_get_debug_name ((ClutterActor *) object),
5974 g_type_name (G_OBJECT_TYPE (object)));
5975
5976 g_free (priv->name);
5977
5978 #ifdef CLUTTER_ENABLE_DEBUG
5979 g_free (priv->debug_name);
5980 #endif
5981
5982 G_OBJECT_CLASS (clutter_actor_parent_class)->finalize (object);
5983 }
5984
5985
5986 /**
5987 * clutter_actor_get_accessible:
5988 * @self: a #ClutterActor
5989 *
5990 * Returns the accessible object that describes the actor to an
5991 * assistive technology.
5992 *
5993 * If no class-specific #AtkObject implementation is available for the
5994 * actor instance in question, it will inherit an #AtkObject
5995 * implementation from the first ancestor class for which such an
5996 * implementation is defined.
5997 *
5998 * The documentation of the <ulink
5999 * url="http://developer.gnome.org/doc/API/2.0/atk/index.html">ATK</ulink>
6000 * library contains more information about accessible objects and
6001 * their uses.
6002 *
6003 * Returns: (transfer none): the #AtkObject associated with @actor
6004 */
6005 AtkObject *
clutter_actor_get_accessible(ClutterActor * self)6006 clutter_actor_get_accessible (ClutterActor *self)
6007 {
6008 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
6009
6010 return CLUTTER_ACTOR_GET_CLASS (self)->get_accessible (self);
6011 }
6012
6013 static AtkObject *
clutter_actor_real_get_accessible(ClutterActor * actor)6014 clutter_actor_real_get_accessible (ClutterActor *actor)
6015 {
6016 return atk_gobject_accessible_for_object (G_OBJECT (actor));
6017 }
6018
6019 static AtkObject *
_clutter_actor_ref_accessible(AtkImplementor * implementor)6020 _clutter_actor_ref_accessible (AtkImplementor *implementor)
6021 {
6022 AtkObject *accessible;
6023
6024 accessible = clutter_actor_get_accessible (CLUTTER_ACTOR (implementor));
6025 if (accessible != NULL)
6026 g_object_ref (accessible);
6027
6028 return accessible;
6029 }
6030
6031 static void
atk_implementor_iface_init(AtkImplementorIface * iface)6032 atk_implementor_iface_init (AtkImplementorIface *iface)
6033 {
6034 iface->ref_accessible = _clutter_actor_ref_accessible;
6035 }
6036
6037 static gboolean
clutter_actor_update_default_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)6038 clutter_actor_update_default_paint_volume (ClutterActor *self,
6039 ClutterPaintVolume *volume)
6040 {
6041 ClutterActorPrivate *priv = self->priv;
6042 gboolean res = TRUE;
6043
6044 /* this should be checked before we call this function, but it's a
6045 * good idea to be explicit when it costs us nothing
6046 */
6047 if (priv->needs_allocation)
6048 return FALSE;
6049
6050 /* we start from the allocation */
6051 clutter_paint_volume_set_width (volume,
6052 priv->allocation.x2 - priv->allocation.x1);
6053 clutter_paint_volume_set_height (volume,
6054 priv->allocation.y2 - priv->allocation.y1);
6055
6056 /* if the actor has a clip set then we have a pretty definite
6057 * size for the paint volume: the actor cannot possibly paint
6058 * outside the clip region.
6059 */
6060 if (priv->clip_to_allocation)
6061 {
6062 /* the allocation has already been set, so we just flip the
6063 * return value
6064 */
6065 res = TRUE;
6066 }
6067 else
6068 {
6069 ClutterActor *child;
6070
6071 if (priv->has_clip &&
6072 priv->clip.size.width >= 0 &&
6073 priv->clip.size.height >= 0)
6074 {
6075 ClutterVertex origin;
6076
6077 origin.x = priv->clip.origin.x;
6078 origin.y = priv->clip.origin.y;
6079 origin.z = 0;
6080
6081 clutter_paint_volume_set_origin (volume, &origin);
6082 clutter_paint_volume_set_width (volume, priv->clip.size.width);
6083 clutter_paint_volume_set_height (volume, priv->clip.size.height);
6084
6085 res = TRUE;
6086 }
6087
6088 /* if we don't have children we just bail out here... */
6089 if (priv->n_children == 0)
6090 return res;
6091
6092 /* ...but if we have children then we ask for their paint volume in
6093 * our coordinates. if any of our children replies that it doesn't
6094 * have a paint volume, we bail out
6095 */
6096 for (child = priv->first_child;
6097 child != NULL;
6098 child = child->priv->next_sibling)
6099 {
6100 const ClutterPaintVolume *child_volume;
6101
6102 /* we ignore unmapped children, since they won't be painted.
6103 *
6104 * XXX: we also have to ignore mapped children without a valid
6105 * allocation, because apparently some code above Clutter allows
6106 * them.
6107 */
6108 if (!CLUTTER_ACTOR_IS_MAPPED (child) || !clutter_actor_has_allocation (child))
6109 continue;
6110
6111 child_volume = clutter_actor_get_transformed_paint_volume (child, self);
6112 if (child_volume == NULL)
6113 {
6114 res = FALSE;
6115 break;
6116 }
6117
6118 clutter_paint_volume_union (volume, child_volume);
6119 res = TRUE;
6120 }
6121 }
6122
6123 return res;
6124
6125 }
6126
6127 static gboolean
clutter_actor_real_get_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)6128 clutter_actor_real_get_paint_volume (ClutterActor *self,
6129 ClutterPaintVolume *volume)
6130 {
6131 ClutterActorClass *klass;
6132 gboolean res;
6133
6134 klass = CLUTTER_ACTOR_GET_CLASS (self);
6135
6136 /* XXX - this thoroughly sucks, but we don't want to penalize users
6137 * who use ClutterActor as a "new ClutterGroup" by forcing a full-stage
6138 * redraw. This should go away in 2.0.
6139 */
6140 if (klass->paint == clutter_actor_real_paint &&
6141 klass->get_paint_volume == clutter_actor_real_get_paint_volume)
6142 {
6143 res = TRUE;
6144 }
6145 else
6146 {
6147 /* this is the default return value: we cannot know if a class
6148 * is going to paint outside its allocation, so we take the
6149 * conservative approach.
6150 */
6151 res = FALSE;
6152 }
6153
6154 /* update_default_paint_volume() should only fail if one of the children
6155 * reported an invalid, or no, paint volume
6156 */
6157 if (!clutter_actor_update_default_paint_volume (self, volume))
6158 return FALSE;
6159
6160 return res;
6161 }
6162
6163 /**
6164 * clutter_actor_get_default_paint_volume:
6165 * @self: a #ClutterActor
6166 *
6167 * Retrieves the default paint volume for @self.
6168 *
6169 * This function provides the same #ClutterPaintVolume that would be
6170 * computed by the default implementation inside #ClutterActor of the
6171 * #ClutterActorClass.get_paint_volume() virtual function.
6172 *
6173 * This function should only be used by #ClutterActor subclasses that
6174 * cannot chain up to the parent implementation when computing their
6175 * paint volume.
6176 *
6177 * Return value: (transfer none): a pointer to the default
6178 * #ClutterPaintVolume, relative to the #ClutterActor, or %NULL if
6179 * the actor could not compute a valid paint volume. The returned value
6180 * is not guaranteed to be stable across multiple frames, so if you
6181 * want to retain it, you will need to copy it using
6182 * clutter_paint_volume_copy().
6183 *
6184 * Since: 1.10
6185 */
6186 const ClutterPaintVolume *
clutter_actor_get_default_paint_volume(ClutterActor * self)6187 clutter_actor_get_default_paint_volume (ClutterActor *self)
6188 {
6189 ClutterPaintVolume volume;
6190 ClutterPaintVolume *res;
6191
6192 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
6193
6194 res = NULL;
6195 _clutter_paint_volume_init_static (&volume, self);
6196 if (clutter_actor_update_default_paint_volume (self, &volume))
6197 {
6198 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
6199
6200 if (stage != NULL)
6201 {
6202 res = _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
6203 _clutter_paint_volume_copy_static (&volume, res);
6204 }
6205 }
6206
6207 clutter_paint_volume_free (&volume);
6208
6209 return res;
6210 }
6211
6212 static gboolean
clutter_actor_real_has_overlaps(ClutterActor * self)6213 clutter_actor_real_has_overlaps (ClutterActor *self)
6214 {
6215 /* By default we'll assume that all actors need an offscreen redirect to get
6216 * the correct opacity. Actors such as ClutterTexture that would never need
6217 * an offscreen redirect can override this to return FALSE. */
6218 return TRUE;
6219 }
6220
6221 static void
clutter_actor_real_destroy(ClutterActor * actor)6222 clutter_actor_real_destroy (ClutterActor *actor)
6223 {
6224 ClutterActorIter iter;
6225
6226 g_object_freeze_notify (G_OBJECT (actor));
6227
6228 clutter_actor_iter_init (&iter, actor);
6229 while (clutter_actor_iter_next (&iter, NULL))
6230 clutter_actor_iter_destroy (&iter);
6231
6232 g_object_thaw_notify (G_OBJECT (actor));
6233 }
6234
6235 static GObject *
clutter_actor_constructor(GType gtype,guint n_props,GObjectConstructParam * props)6236 clutter_actor_constructor (GType gtype,
6237 guint n_props,
6238 GObjectConstructParam *props)
6239 {
6240 GObjectClass *gobject_class;
6241 ClutterActor *self;
6242 GObject *retval;
6243
6244 gobject_class = G_OBJECT_CLASS (clutter_actor_parent_class);
6245 retval = gobject_class->constructor (gtype, n_props, props);
6246 self = CLUTTER_ACTOR (retval);
6247
6248 if (self->priv->layout_manager == NULL)
6249 {
6250 ClutterLayoutManager *default_layout;
6251
6252 CLUTTER_NOTE (LAYOUT, "Creating default layout manager");
6253
6254 default_layout = clutter_fixed_layout_new ();
6255 clutter_actor_set_layout_manager (self, default_layout);
6256 }
6257
6258 return retval;
6259 }
6260
6261 static void
clutter_actor_class_init(ClutterActorClass * klass)6262 clutter_actor_class_init (ClutterActorClass *klass)
6263 {
6264 GObjectClass *object_class = G_OBJECT_CLASS (klass);
6265
6266 quark_shader_data = g_quark_from_static_string ("-clutter-actor-shader-data");
6267 quark_actor_layout_info = g_quark_from_static_string ("-clutter-actor-layout-info");
6268 quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
6269 quark_actor_animation_info = g_quark_from_static_string ("-clutter-actor-animation-info");
6270
6271 object_class->constructor = clutter_actor_constructor;
6272 object_class->set_property = clutter_actor_set_property;
6273 object_class->get_property = clutter_actor_get_property;
6274 object_class->dispose = clutter_actor_dispose;
6275 object_class->finalize = clutter_actor_finalize;
6276
6277 klass->show = clutter_actor_real_show;
6278 klass->show_all = clutter_actor_show;
6279 klass->hide = clutter_actor_real_hide;
6280 klass->hide_all = clutter_actor_hide;
6281 klass->map = clutter_actor_real_map;
6282 klass->unmap = clutter_actor_real_unmap;
6283 klass->unrealize = clutter_actor_real_unrealize;
6284 klass->pick = clutter_actor_real_pick;
6285 klass->get_preferred_width = clutter_actor_real_get_preferred_width;
6286 klass->get_preferred_height = clutter_actor_real_get_preferred_height;
6287 klass->allocate = clutter_actor_real_allocate;
6288 klass->queue_redraw = clutter_actor_real_queue_redraw;
6289 klass->queue_relayout = clutter_actor_real_queue_relayout;
6290 klass->apply_transform = clutter_actor_real_apply_transform;
6291 klass->get_accessible = clutter_actor_real_get_accessible;
6292 klass->get_paint_volume = clutter_actor_real_get_paint_volume;
6293 klass->has_overlaps = clutter_actor_real_has_overlaps;
6294 klass->paint = clutter_actor_real_paint;
6295 klass->destroy = clutter_actor_real_destroy;
6296
6297 /**
6298 * ClutterActor:x:
6299 *
6300 * X coordinate of the actor in pixels. If written, forces a fixed
6301 * position for the actor. If read, returns the fixed position if any,
6302 * otherwise the allocation if available, otherwise 0.
6303 *
6304 * The #ClutterActor:x property is animatable.
6305 */
6306 obj_props[PROP_X] =
6307 g_param_spec_float ("x",
6308 P_("X coordinate"),
6309 P_("X coordinate of the actor"),
6310 -G_MAXFLOAT, G_MAXFLOAT,
6311 0.0,
6312 G_PARAM_READWRITE |
6313 G_PARAM_STATIC_STRINGS |
6314 CLUTTER_PARAM_ANIMATABLE);
6315
6316 /**
6317 * ClutterActor:y:
6318 *
6319 * Y coordinate of the actor in pixels. If written, forces a fixed
6320 * position for the actor. If read, returns the fixed position if
6321 * any, otherwise the allocation if available, otherwise 0.
6322 *
6323 * The #ClutterActor:y property is animatable.
6324 */
6325 obj_props[PROP_Y] =
6326 g_param_spec_float ("y",
6327 P_("Y coordinate"),
6328 P_("Y coordinate of the actor"),
6329 -G_MAXFLOAT, G_MAXFLOAT,
6330 0.0,
6331 G_PARAM_READWRITE |
6332 G_PARAM_STATIC_STRINGS |
6333 CLUTTER_PARAM_ANIMATABLE);
6334
6335 /**
6336 * ClutterActor:position:
6337 *
6338 * The position of the origin of the actor.
6339 *
6340 * This property is a shorthand for setting and getting the
6341 * #ClutterActor:x and #ClutterActor:y properties at the same
6342 * time.
6343 *
6344 * The #ClutterActor:position property is animatable.
6345 *
6346 * Since: 1.12
6347 */
6348 obj_props[PROP_POSITION] =
6349 g_param_spec_boxed ("position",
6350 P_("Position"),
6351 P_("The position of the origin of the actor"),
6352 CLUTTER_TYPE_POINT,
6353 G_PARAM_READWRITE |
6354 G_PARAM_STATIC_STRINGS |
6355 CLUTTER_PARAM_ANIMATABLE);
6356
6357 /**
6358 * ClutterActor:width:
6359 *
6360 * Width of the actor (in pixels). If written, forces the minimum and
6361 * natural size request of the actor to the given width. If read, returns
6362 * the allocated width if available, otherwise the width request.
6363 *
6364 * The #ClutterActor:width property is animatable.
6365 */
6366 obj_props[PROP_WIDTH] =
6367 g_param_spec_float ("width",
6368 P_("Width"),
6369 P_("Width of the actor"),
6370 0.0, G_MAXFLOAT,
6371 0.0,
6372 G_PARAM_READWRITE |
6373 G_PARAM_STATIC_STRINGS |
6374 CLUTTER_PARAM_ANIMATABLE);
6375
6376 /**
6377 * ClutterActor:height:
6378 *
6379 * Height of the actor (in pixels). If written, forces the minimum and
6380 * natural size request of the actor to the given height. If read, returns
6381 * the allocated height if available, otherwise the height request.
6382 *
6383 * The #ClutterActor:height property is animatable.
6384 */
6385 obj_props[PROP_HEIGHT] =
6386 g_param_spec_float ("height",
6387 P_("Height"),
6388 P_("Height of the actor"),
6389 0.0, G_MAXFLOAT,
6390 0.0,
6391 G_PARAM_READWRITE |
6392 G_PARAM_STATIC_STRINGS |
6393 CLUTTER_PARAM_ANIMATABLE);
6394
6395 /**
6396 * ClutterActor:size:
6397 *
6398 * The size of the actor.
6399 *
6400 * This property is a shorthand for setting and getting the
6401 * #ClutterActor:width and #ClutterActor:height at the same time.
6402 *
6403 * The #ClutterActor:size property is animatable.
6404 *
6405 * Since: 1.12
6406 */
6407 obj_props[PROP_SIZE] =
6408 g_param_spec_boxed ("size",
6409 P_("Size"),
6410 P_("The size of the actor"),
6411 CLUTTER_TYPE_SIZE,
6412 G_PARAM_READWRITE |
6413 G_PARAM_STATIC_STRINGS |
6414 CLUTTER_PARAM_ANIMATABLE);
6415
6416 /**
6417 * ClutterActor:fixed-x:
6418 *
6419 * The fixed X position of the actor in pixels.
6420 *
6421 * Writing this property sets #ClutterActor:fixed-position-set
6422 * property as well, as a side effect
6423 *
6424 * Since: 0.8
6425 */
6426 obj_props[PROP_FIXED_X] =
6427 g_param_spec_float ("fixed-x",
6428 P_("Fixed X"),
6429 P_("Forced X position of the actor"),
6430 -G_MAXFLOAT, G_MAXFLOAT,
6431 0.0,
6432 CLUTTER_PARAM_READWRITE);
6433
6434 /**
6435 * ClutterActor:fixed-y:
6436 *
6437 * The fixed Y position of the actor in pixels.
6438 *
6439 * Writing this property sets the #ClutterActor:fixed-position-set
6440 * property as well, as a side effect
6441 *
6442 * Since: 0.8
6443 */
6444 obj_props[PROP_FIXED_Y] =
6445 g_param_spec_float ("fixed-y",
6446 P_("Fixed Y"),
6447 P_("Forced Y position of the actor"),
6448 -G_MAXFLOAT, G_MAXFLOAT,
6449 0,
6450 CLUTTER_PARAM_READWRITE);
6451
6452 /**
6453 * ClutterActor:fixed-position-set:
6454 *
6455 * This flag controls whether the #ClutterActor:fixed-x and
6456 * #ClutterActor:fixed-y properties are used
6457 *
6458 * Since: 0.8
6459 */
6460 obj_props[PROP_FIXED_POSITION_SET] =
6461 g_param_spec_boolean ("fixed-position-set",
6462 P_("Fixed position set"),
6463 P_("Whether to use fixed positioning for the actor"),
6464 FALSE,
6465 CLUTTER_PARAM_READWRITE);
6466
6467 /**
6468 * ClutterActor:min-width:
6469 *
6470 * A forced minimum width request for the actor, in pixels
6471 *
6472 * Writing this property sets the #ClutterActor:min-width-set property
6473 * as well, as a side effect.
6474 *
6475 *This property overrides the usual width request of the actor.
6476 *
6477 * Since: 0.8
6478 */
6479 obj_props[PROP_MIN_WIDTH] =
6480 g_param_spec_float ("min-width",
6481 P_("Min Width"),
6482 P_("Forced minimum width request for the actor"),
6483 0.0, G_MAXFLOAT,
6484 0.0,
6485 CLUTTER_PARAM_READWRITE);
6486
6487 /**
6488 * ClutterActor:min-height:
6489 *
6490 * A forced minimum height request for the actor, in pixels
6491 *
6492 * Writing this property sets the #ClutterActor:min-height-set property
6493 * as well, as a side effect. This property overrides the usual height
6494 * request of the actor.
6495 *
6496 * Since: 0.8
6497 */
6498 obj_props[PROP_MIN_HEIGHT] =
6499 g_param_spec_float ("min-height",
6500 P_("Min Height"),
6501 P_("Forced minimum height request for the actor"),
6502 0.0, G_MAXFLOAT,
6503 0.0,
6504 CLUTTER_PARAM_READWRITE);
6505
6506 /**
6507 * ClutterActor:natural-width:
6508 *
6509 * A forced natural width request for the actor, in pixels
6510 *
6511 * Writing this property sets the #ClutterActor:natural-width-set
6512 * property as well, as a side effect. This property overrides the
6513 * usual width request of the actor
6514 *
6515 * Since: 0.8
6516 */
6517 obj_props[PROP_NATURAL_WIDTH] =
6518 g_param_spec_float ("natural-width",
6519 P_("Natural Width"),
6520 P_("Forced natural width request for the actor"),
6521 0.0, G_MAXFLOAT,
6522 0.0,
6523 CLUTTER_PARAM_READWRITE);
6524
6525 /**
6526 * ClutterActor:natural-height:
6527 *
6528 * A forced natural height request for the actor, in pixels
6529 *
6530 * Writing this property sets the #ClutterActor:natural-height-set
6531 * property as well, as a side effect. This property overrides the
6532 * usual height request of the actor
6533 *
6534 * Since: 0.8
6535 */
6536 obj_props[PROP_NATURAL_HEIGHT] =
6537 g_param_spec_float ("natural-height",
6538 P_("Natural Height"),
6539 P_("Forced natural height request for the actor"),
6540 0.0, G_MAXFLOAT,
6541 0.0,
6542 CLUTTER_PARAM_READWRITE);
6543
6544 /**
6545 * ClutterActor:min-width-set:
6546 *
6547 * This flag controls whether the #ClutterActor:min-width property
6548 * is used
6549 *
6550 * Since: 0.8
6551 */
6552 obj_props[PROP_MIN_WIDTH_SET] =
6553 g_param_spec_boolean ("min-width-set",
6554 P_("Minimum width set"),
6555 P_("Whether to use the min-width property"),
6556 FALSE,
6557 CLUTTER_PARAM_READWRITE);
6558
6559 /**
6560 * ClutterActor:min-height-set:
6561 *
6562 * This flag controls whether the #ClutterActor:min-height property
6563 * is used
6564 *
6565 * Since: 0.8
6566 */
6567 obj_props[PROP_MIN_HEIGHT_SET] =
6568 g_param_spec_boolean ("min-height-set",
6569 P_("Minimum height set"),
6570 P_("Whether to use the min-height property"),
6571 FALSE,
6572 CLUTTER_PARAM_READWRITE);
6573
6574 /**
6575 * ClutterActor:natural-width-set:
6576 *
6577 * This flag controls whether the #ClutterActor:natural-width property
6578 * is used
6579 *
6580 * Since: 0.8
6581 */
6582 obj_props[PROP_NATURAL_WIDTH_SET] =
6583 g_param_spec_boolean ("natural-width-set",
6584 P_("Natural width set"),
6585 P_("Whether to use the natural-width property"),
6586 FALSE,
6587 CLUTTER_PARAM_READWRITE);
6588
6589 /**
6590 * ClutterActor:natural-height-set:
6591 *
6592 * This flag controls whether the #ClutterActor:natural-height property
6593 * is used
6594 *
6595 * Since: 0.8
6596 */
6597 obj_props[PROP_NATURAL_HEIGHT_SET] =
6598 g_param_spec_boolean ("natural-height-set",
6599 P_("Natural height set"),
6600 P_("Whether to use the natural-height property"),
6601 FALSE,
6602 CLUTTER_PARAM_READWRITE);
6603
6604 /**
6605 * ClutterActor:allocation:
6606 *
6607 * The allocation for the actor, in pixels
6608 *
6609 * This is property is read-only, but you might monitor it to know when an
6610 * actor moves or resizes
6611 *
6612 * Since: 0.8
6613 */
6614 obj_props[PROP_ALLOCATION] =
6615 g_param_spec_boxed ("allocation",
6616 P_("Allocation"),
6617 P_("The actor’s allocation"),
6618 CLUTTER_TYPE_ACTOR_BOX,
6619 G_PARAM_READABLE |
6620 G_PARAM_STATIC_STRINGS |
6621 CLUTTER_PARAM_ANIMATABLE);
6622
6623 /**
6624 * ClutterActor:request-mode:
6625 *
6626 * Request mode for the #ClutterActor. The request mode determines the
6627 * type of geometry management used by the actor, either height for width
6628 * (the default) or width for height.
6629 *
6630 * For actors implementing height for width, the parent container should get
6631 * the preferred width first, and then the preferred height for that width.
6632 *
6633 * For actors implementing width for height, the parent container should get
6634 * the preferred height first, and then the preferred width for that height.
6635 *
6636 * For instance:
6637 *
6638 * |[<!-- language="C" -->
6639 * ClutterRequestMode mode;
6640 * gfloat natural_width, min_width;
6641 * gfloat natural_height, min_height;
6642 *
6643 * mode = clutter_actor_get_request_mode (child);
6644 * if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
6645 * {
6646 * clutter_actor_get_preferred_width (child, -1,
6647 * &min_width,
6648 * &natural_width);
6649 * clutter_actor_get_preferred_height (child, natural_width,
6650 * &min_height,
6651 * &natural_height);
6652 * }
6653 * else if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
6654 * {
6655 * clutter_actor_get_preferred_height (child, -1,
6656 * &min_height,
6657 * &natural_height);
6658 * clutter_actor_get_preferred_width (child, natural_height,
6659 * &min_width,
6660 * &natural_width);
6661 * }
6662 * else if (mode == CLUTTER_REQUEST_CONTENT_SIZE)
6663 * {
6664 * ClutterContent *content = clutter_actor_get_content (child);
6665 *
6666 * min_width, min_height = 0;
6667 * natural_width = natural_height = 0;
6668 *
6669 * if (content != NULL)
6670 * clutter_content_get_preferred_size (content, &natural_width, &natural_height);
6671 * }
6672 * ]|
6673 *
6674 * will retrieve the minimum and natural width and height depending on the
6675 * preferred request mode of the #ClutterActor "child".
6676 *
6677 * The clutter_actor_get_preferred_size() function will implement this
6678 * check for you.
6679 *
6680 * Since: 0.8
6681 */
6682 obj_props[PROP_REQUEST_MODE] =
6683 g_param_spec_enum ("request-mode",
6684 P_("Request Mode"),
6685 P_("The actor’s request mode"),
6686 CLUTTER_TYPE_REQUEST_MODE,
6687 CLUTTER_REQUEST_HEIGHT_FOR_WIDTH,
6688 CLUTTER_PARAM_READWRITE);
6689
6690 /**
6691 * ClutterActor:depth:
6692 *
6693 * The position of the actor on the Z axis.
6694 *
6695 * The #ClutterActor:depth property is relative to the parent's
6696 * modelview matrix.
6697 *
6698 * Setting this property will call #ClutterContainerIface.sort_depth_order()
6699 * which is usually a no-op, and it's most likely not what you want.
6700 *
6701 * The #ClutterActor:depth property is animatable.
6702 *
6703 * Since: 0.6
6704 *
6705 * Deprecated: 1.12: Use #ClutterActor:z-position instead.
6706 */
6707 obj_props[PROP_DEPTH] =
6708 g_param_spec_float ("depth",
6709 P_("Depth"),
6710 P_("Position on the Z axis"),
6711 -G_MAXFLOAT, G_MAXFLOAT,
6712 0.0,
6713 G_PARAM_READWRITE |
6714 G_PARAM_STATIC_STRINGS |
6715 G_PARAM_DEPRECATED |
6716 CLUTTER_PARAM_ANIMATABLE);
6717
6718 /**
6719 * ClutterActor:z-position:
6720 *
6721 * The actor's position on the Z axis, relative to the parent's
6722 * transformations.
6723 *
6724 * Positive values will bring the actor's position nearer to the user,
6725 * whereas negative values will bring the actor's position farther from
6726 * the user.
6727 *
6728 * The #ClutterActor:z-position does not affect the paint or allocation
6729 * order.
6730 *
6731 * The #ClutterActor:z-position property is animatable.
6732 *
6733 * Since: 1.12
6734 */
6735 obj_props[PROP_Z_POSITION] =
6736 g_param_spec_float ("z-position",
6737 P_("Z Position"),
6738 P_("The actor’s position on the Z axis"),
6739 -G_MAXFLOAT, G_MAXFLOAT,
6740 0.0f,
6741 G_PARAM_READWRITE |
6742 G_PARAM_STATIC_STRINGS |
6743 CLUTTER_PARAM_ANIMATABLE);
6744
6745 /**
6746 * ClutterActor:opacity:
6747 *
6748 * Opacity of an actor, between 0 (fully transparent) and
6749 * 255 (fully opaque)
6750 *
6751 * The #ClutterActor:opacity property is animatable.
6752 */
6753 obj_props[PROP_OPACITY] =
6754 g_param_spec_uint ("opacity",
6755 P_("Opacity"),
6756 P_("Opacity of an actor"),
6757 0, 255,
6758 255,
6759 G_PARAM_READWRITE |
6760 G_PARAM_STATIC_STRINGS |
6761 CLUTTER_PARAM_ANIMATABLE);
6762
6763 /**
6764 * ClutterActor:offscreen-redirect:
6765 *
6766 * Determines the conditions in which the actor will be redirected
6767 * to an offscreen framebuffer while being painted. For example this
6768 * can be used to cache an actor in a framebuffer or for improved
6769 * handling of transparent actors. See
6770 * clutter_actor_set_offscreen_redirect() for details.
6771 *
6772 * Since: 1.8
6773 */
6774 obj_props[PROP_OFFSCREEN_REDIRECT] =
6775 g_param_spec_flags ("offscreen-redirect",
6776 P_("Offscreen redirect"),
6777 P_("Flags controlling when to flatten the actor into a single image"),
6778 CLUTTER_TYPE_OFFSCREEN_REDIRECT,
6779 0,
6780 CLUTTER_PARAM_READWRITE);
6781
6782 /**
6783 * ClutterActor:visible:
6784 *
6785 * Whether the actor is set to be visible or not
6786 *
6787 * See also #ClutterActor:mapped
6788 */
6789 obj_props[PROP_VISIBLE] =
6790 g_param_spec_boolean ("visible",
6791 P_("Visible"),
6792 P_("Whether the actor is visible or not"),
6793 FALSE,
6794 CLUTTER_PARAM_READWRITE);
6795
6796 /**
6797 * ClutterActor:mapped:
6798 *
6799 * Whether the actor is mapped (will be painted when the stage
6800 * to which it belongs is mapped)
6801 *
6802 * Since: 1.0
6803 */
6804 obj_props[PROP_MAPPED] =
6805 g_param_spec_boolean ("mapped",
6806 P_("Mapped"),
6807 P_("Whether the actor will be painted"),
6808 FALSE,
6809 CLUTTER_PARAM_READABLE);
6810
6811 /**
6812 * ClutterActor:realized:
6813 *
6814 * Whether the actor has been realized
6815 *
6816 * Since: 1.0
6817 */
6818 obj_props[PROP_REALIZED] =
6819 g_param_spec_boolean ("realized",
6820 P_("Realized"),
6821 P_("Whether the actor has been realized"),
6822 FALSE,
6823 CLUTTER_PARAM_READABLE);
6824
6825 /**
6826 * ClutterActor:reactive:
6827 *
6828 * Whether the actor is reactive to events or not
6829 *
6830 * Only reactive actors will emit event-related signals
6831 *
6832 * Since: 0.6
6833 */
6834 obj_props[PROP_REACTIVE] =
6835 g_param_spec_boolean ("reactive",
6836 P_("Reactive"),
6837 P_("Whether the actor is reactive to events"),
6838 FALSE,
6839 CLUTTER_PARAM_READWRITE);
6840
6841 /**
6842 * ClutterActor:has-clip:
6843 *
6844 * Whether the actor has the #ClutterActor:clip property set or not
6845 */
6846 obj_props[PROP_HAS_CLIP] =
6847 g_param_spec_boolean ("has-clip",
6848 P_("Has Clip"),
6849 P_("Whether the actor has a clip set"),
6850 FALSE,
6851 CLUTTER_PARAM_READABLE);
6852
6853 /**
6854 * ClutterActor:clip:
6855 *
6856 * The visible region of the actor, in actor-relative coordinates
6857 *
6858 * Deprecated: 1.12: Use #ClutterActor:clip-rect instead.
6859 */
6860 obj_props[PROP_CLIP] = /* XXX:2.0 - remove */
6861 g_param_spec_boxed ("clip",
6862 P_("Clip"),
6863 P_("The clip region for the actor"),
6864 CLUTTER_TYPE_GEOMETRY,
6865 CLUTTER_PARAM_READWRITE);
6866
6867 /**
6868 * ClutterActor:clip-rect:
6869 *
6870 * The visible region of the actor, in actor-relative coordinates,
6871 * expressed as a #ClutterRect.
6872 *
6873 * Setting this property to %NULL will unset the existing clip.
6874 *
6875 * Setting this property will change the #ClutterActor:has-clip
6876 * property as a side effect.
6877 *
6878 * Since: 1.12
6879 */
6880 obj_props[PROP_CLIP_RECT] =
6881 g_param_spec_boxed ("clip-rect",
6882 P_("Clip Rectangle"),
6883 P_("The visible region of the actor"),
6884 CLUTTER_TYPE_RECT,
6885 G_PARAM_READWRITE |
6886 G_PARAM_STATIC_STRINGS);
6887
6888 /**
6889 * ClutterActor:name:
6890 *
6891 * The name of the actor
6892 *
6893 * Since: 0.2
6894 */
6895 obj_props[PROP_NAME] =
6896 g_param_spec_string ("name",
6897 P_("Name"),
6898 P_("Name of the actor"),
6899 NULL,
6900 CLUTTER_PARAM_READWRITE);
6901
6902 /**
6903 * ClutterActor:pivot-point:
6904 *
6905 * The point around which the scaling and rotation transformations occur.
6906 *
6907 * The pivot point is expressed in normalized coordinates space, with (0, 0)
6908 * being the top left corner of the actor and (1, 1) the bottom right corner
6909 * of the actor.
6910 *
6911 * The default pivot point is located at (0, 0).
6912 *
6913 * The #ClutterActor:pivot-point property is animatable.
6914 *
6915 * Since: 1.12
6916 */
6917 obj_props[PROP_PIVOT_POINT] =
6918 g_param_spec_boxed ("pivot-point",
6919 P_("Pivot Point"),
6920 P_("The point around which the scaling and rotation occur"),
6921 CLUTTER_TYPE_POINT,
6922 G_PARAM_READWRITE |
6923 G_PARAM_STATIC_STRINGS |
6924 CLUTTER_PARAM_ANIMATABLE);
6925
6926 /**
6927 * ClutterActor:pivot-point-z:
6928 *
6929 * The Z component of the #ClutterActor:pivot-point, expressed as a value
6930 * along the Z axis.
6931 *
6932 * The #ClutterActor:pivot-point-z property is animatable.
6933 *
6934 * Since: 1.12
6935 */
6936 obj_props[PROP_PIVOT_POINT_Z] =
6937 g_param_spec_float ("pivot-point-z",
6938 P_("Pivot Point Z"),
6939 P_("Z component of the pivot point"),
6940 -G_MAXFLOAT, G_MAXFLOAT,
6941 0.f,
6942 G_PARAM_READWRITE |
6943 G_PARAM_STATIC_STRINGS |
6944 CLUTTER_PARAM_ANIMATABLE);
6945
6946 /**
6947 * ClutterActor:scale-x:
6948 *
6949 * The horizontal scale of the actor.
6950 *
6951 * The #ClutterActor:scale-x property is animatable.
6952 *
6953 * Since: 0.6
6954 */
6955 obj_props[PROP_SCALE_X] =
6956 g_param_spec_double ("scale-x",
6957 P_("Scale X"),
6958 P_("Scale factor on the X axis"),
6959 -G_MAXDOUBLE, G_MAXDOUBLE,
6960 1.0,
6961 G_PARAM_READWRITE |
6962 G_PARAM_STATIC_STRINGS |
6963 CLUTTER_PARAM_ANIMATABLE);
6964
6965 /**
6966 * ClutterActor:scale-y:
6967 *
6968 * The vertical scale of the actor.
6969 *
6970 * The #ClutterActor:scale-y property is animatable.
6971 *
6972 * Since: 0.6
6973 */
6974 obj_props[PROP_SCALE_Y] =
6975 g_param_spec_double ("scale-y",
6976 P_("Scale Y"),
6977 P_("Scale factor on the Y axis"),
6978 -G_MAXDOUBLE, G_MAXDOUBLE,
6979 1.0,
6980 G_PARAM_READWRITE |
6981 G_PARAM_STATIC_STRINGS |
6982 CLUTTER_PARAM_ANIMATABLE);
6983
6984 /**
6985 * ClutterActor:scale-z:
6986 *
6987 * The scale factor of the actor along the Z axis.
6988 *
6989 * The #ClutterActor:scale-y property is animatable.
6990 *
6991 * Since: 1.12
6992 */
6993 obj_props[PROP_SCALE_Z] =
6994 g_param_spec_double ("scale-z",
6995 P_("Scale Z"),
6996 P_("Scale factor on the Z axis"),
6997 -G_MAXDOUBLE, G_MAXDOUBLE,
6998 1.0,
6999 G_PARAM_READWRITE |
7000 G_PARAM_STATIC_STRINGS |
7001 CLUTTER_PARAM_ANIMATABLE);
7002
7003 /**
7004 * ClutterActor:scale-center-x:
7005 *
7006 * The horizontal center point for scaling
7007 *
7008 * Since: 1.0
7009 *
7010 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7011 */
7012 obj_props[PROP_SCALE_CENTER_X] = /* XXX:2.0 - remove */
7013 g_param_spec_float ("scale-center-x",
7014 P_("Scale Center X"),
7015 P_("Horizontal scale center"),
7016 -G_MAXFLOAT, G_MAXFLOAT,
7017 0.0,
7018 G_PARAM_READWRITE |
7019 G_PARAM_STATIC_STRINGS |
7020 G_PARAM_DEPRECATED);
7021
7022 /**
7023 * ClutterActor:scale-center-y:
7024 *
7025 * The vertical center point for scaling
7026 *
7027 * Since: 1.0
7028 *
7029 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7030 */
7031 obj_props[PROP_SCALE_CENTER_Y] = /* XXX:2.0 - remove */
7032 g_param_spec_float ("scale-center-y",
7033 P_("Scale Center Y"),
7034 P_("Vertical scale center"),
7035 -G_MAXFLOAT, G_MAXFLOAT,
7036 0.0,
7037 G_PARAM_READWRITE |
7038 G_PARAM_STATIC_STRINGS |
7039 G_PARAM_DEPRECATED);
7040
7041 /**
7042 * ClutterActor:scale-gravity:
7043 *
7044 * The center point for scaling expressed as a #ClutterGravity
7045 *
7046 * Since: 1.0
7047 *
7048 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7049 */
7050 obj_props[PROP_SCALE_GRAVITY] = /* XXX:2.0 - remove */
7051 g_param_spec_enum ("scale-gravity",
7052 P_("Scale Gravity"),
7053 P_("The center of scaling"),
7054 CLUTTER_TYPE_GRAVITY,
7055 CLUTTER_GRAVITY_NONE,
7056 G_PARAM_READWRITE |
7057 G_PARAM_STATIC_STRINGS |
7058 G_PARAM_DEPRECATED);
7059
7060 /**
7061 * ClutterActor:rotation-angle-x:
7062 *
7063 * The rotation angle on the X axis.
7064 *
7065 * The #ClutterActor:rotation-angle-x property is animatable.
7066 *
7067 * Since: 0.6
7068 */
7069 obj_props[PROP_ROTATION_ANGLE_X] =
7070 g_param_spec_double ("rotation-angle-x",
7071 P_("Rotation Angle X"),
7072 P_("The rotation angle on the X axis"),
7073 -G_MAXDOUBLE, G_MAXDOUBLE,
7074 0.0,
7075 G_PARAM_READWRITE |
7076 G_PARAM_STATIC_STRINGS |
7077 CLUTTER_PARAM_ANIMATABLE);
7078
7079 /**
7080 * ClutterActor:rotation-angle-y:
7081 *
7082 * The rotation angle on the Y axis
7083 *
7084 * The #ClutterActor:rotation-angle-y property is animatable.
7085 *
7086 * Since: 0.6
7087 */
7088 obj_props[PROP_ROTATION_ANGLE_Y] =
7089 g_param_spec_double ("rotation-angle-y",
7090 P_("Rotation Angle Y"),
7091 P_("The rotation angle on the Y axis"),
7092 -G_MAXDOUBLE, G_MAXDOUBLE,
7093 0.0,
7094 G_PARAM_READWRITE |
7095 G_PARAM_STATIC_STRINGS |
7096 CLUTTER_PARAM_ANIMATABLE);
7097
7098 /**
7099 * ClutterActor:rotation-angle-z:
7100 *
7101 * The rotation angle on the Z axis
7102 *
7103 * The #ClutterActor:rotation-angle-z property is animatable.
7104 *
7105 * Since: 0.6
7106 */
7107 obj_props[PROP_ROTATION_ANGLE_Z] =
7108 g_param_spec_double ("rotation-angle-z",
7109 P_("Rotation Angle Z"),
7110 P_("The rotation angle on the Z axis"),
7111 -G_MAXDOUBLE, G_MAXDOUBLE,
7112 0.0,
7113 G_PARAM_READWRITE |
7114 G_PARAM_STATIC_STRINGS |
7115 CLUTTER_PARAM_ANIMATABLE);
7116
7117 /**
7118 * ClutterActor:rotation-center-x:
7119 *
7120 * The rotation center on the X axis.
7121 *
7122 * Since: 0.6
7123 *
7124 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7125 */
7126 obj_props[PROP_ROTATION_CENTER_X] = /* XXX:2.0 - remove */
7127 g_param_spec_boxed ("rotation-center-x",
7128 P_("Rotation Center X"),
7129 P_("The rotation center on the X axis"),
7130 CLUTTER_TYPE_VERTEX,
7131 G_PARAM_READWRITE |
7132 G_PARAM_STATIC_STRINGS |
7133 G_PARAM_DEPRECATED);
7134
7135 /**
7136 * ClutterActor:rotation-center-y:
7137 *
7138 * The rotation center on the Y axis.
7139 *
7140 * Since: 0.6
7141 *
7142 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7143 */
7144 obj_props[PROP_ROTATION_CENTER_Y] = /* XXX:2.0 - remove */
7145 g_param_spec_boxed ("rotation-center-y",
7146 P_("Rotation Center Y"),
7147 P_("The rotation center on the Y axis"),
7148 CLUTTER_TYPE_VERTEX,
7149 G_PARAM_READWRITE |
7150 G_PARAM_STATIC_STRINGS |
7151 G_PARAM_DEPRECATED);
7152
7153 /**
7154 * ClutterActor:rotation-center-z:
7155 *
7156 * The rotation center on the Z axis.
7157 *
7158 * Since: 0.6
7159 *
7160 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7161 */
7162 obj_props[PROP_ROTATION_CENTER_Z] = /* XXX:2.0 - remove */
7163 g_param_spec_boxed ("rotation-center-z",
7164 P_("Rotation Center Z"),
7165 P_("The rotation center on the Z axis"),
7166 CLUTTER_TYPE_VERTEX,
7167 G_PARAM_READWRITE |
7168 G_PARAM_STATIC_STRINGS |
7169 G_PARAM_DEPRECATED);
7170
7171 /**
7172 * ClutterActor:rotation-center-z-gravity:
7173 *
7174 * The rotation center on the Z axis expressed as a #ClutterGravity.
7175 *
7176 * Since: 1.0
7177 *
7178 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7179 */
7180 obj_props[PROP_ROTATION_CENTER_Z_GRAVITY] = /* XXX:2.0 - remove */
7181 g_param_spec_enum ("rotation-center-z-gravity",
7182 P_("Rotation Center Z Gravity"),
7183 P_("Center point for rotation around the Z axis"),
7184 CLUTTER_TYPE_GRAVITY,
7185 CLUTTER_GRAVITY_NONE,
7186 G_PARAM_READWRITE |
7187 G_PARAM_STATIC_STRINGS |
7188 G_PARAM_DEPRECATED);
7189
7190 /**
7191 * ClutterActor:anchor-x:
7192 *
7193 * The X coordinate of an actor's anchor point, relative to
7194 * the actor coordinate space, in pixels.
7195 *
7196 * It is highly recommended not to use #ClutterActor:anchor-x,
7197 * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly
7198 * written code; the anchor point adds an additional translation that
7199 * will affect the actor's relative position with regards to its
7200 * parent, as well as the position of its children. This change needs
7201 * to always be taken into account when positioning the actor. It is
7202 * recommended to use the #ClutterActor:pivot-point property instead,
7203 * as it will affect only the transformations.
7204 *
7205 * Since: 0.8
7206 *
7207 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7208 */
7209 obj_props[PROP_ANCHOR_X] = /* XXX:2.0 - remove */
7210 g_param_spec_float ("anchor-x",
7211 P_("Anchor X"),
7212 P_("X coordinate of the anchor point"),
7213 -G_MAXFLOAT, G_MAXFLOAT,
7214 0,
7215 G_PARAM_READWRITE |
7216 G_PARAM_STATIC_STRINGS |
7217 G_PARAM_DEPRECATED);
7218
7219 /**
7220 * ClutterActor:anchor-y:
7221 *
7222 * The Y coordinate of an actor's anchor point, relative to
7223 * the actor coordinate space, in pixels
7224 *
7225 * It is highly recommended not to use #ClutterActor:anchor-x,
7226 * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly
7227 * written code; the anchor point adds an additional translation that
7228 * will affect the actor's relative position with regards to its
7229 * parent, as well as the position of its children. This change needs
7230 * to always be taken into account when positioning the actor. It is
7231 * recommended to use the #ClutterActor:pivot-point property instead,
7232 * as it will affect only the transformations.
7233 *
7234 * Since: 0.8
7235 *
7236 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7237 */
7238 obj_props[PROP_ANCHOR_Y] = /* XXX:2.0 - remove */
7239 g_param_spec_float ("anchor-y",
7240 P_("Anchor Y"),
7241 P_("Y coordinate of the anchor point"),
7242 -G_MAXFLOAT, G_MAXFLOAT,
7243 0,
7244 G_PARAM_READWRITE |
7245 G_PARAM_STATIC_STRINGS |
7246 G_PARAM_DEPRECATED);
7247
7248 /**
7249 * ClutterActor:anchor-gravity:
7250 *
7251 * The anchor point expressed as a #ClutterGravity
7252 *
7253 * It is highly recommended not to use #ClutterActor:anchor-x,
7254 * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly
7255 * written code; the anchor point adds an additional translation that
7256 * will affect the actor's relative position with regards to its
7257 * parent, as well as the position of its children. This change needs
7258 * to always be taken into account when positioning the actor. It is
7259 * recommended to use the #ClutterActor:pivot-point property instead,
7260 * as it will affect only the transformations.
7261 *
7262 * Since: 1.0
7263 *
7264 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7265 */
7266 obj_props[PROP_ANCHOR_GRAVITY] = /* XXX:2.0 - remove */
7267 g_param_spec_enum ("anchor-gravity",
7268 P_("Anchor Gravity"),
7269 P_("The anchor point as a ClutterGravity"),
7270 CLUTTER_TYPE_GRAVITY,
7271 CLUTTER_GRAVITY_NONE,
7272 G_PARAM_READWRITE |
7273 G_PARAM_STATIC_STRINGS |
7274 G_PARAM_DEPRECATED);
7275
7276 /**
7277 * ClutterActor:translation-x:
7278 *
7279 * An additional translation applied along the X axis, relative
7280 * to the actor's #ClutterActor:pivot-point.
7281 *
7282 * The #ClutterActor:translation-x property is animatable.
7283 *
7284 * Since: 1.12
7285 */
7286 obj_props[PROP_TRANSLATION_X] =
7287 g_param_spec_float ("translation-x",
7288 P_("Translation X"),
7289 P_("Translation along the X axis"),
7290 -G_MAXFLOAT, G_MAXFLOAT,
7291 0.f,
7292 G_PARAM_READWRITE |
7293 G_PARAM_STATIC_STRINGS |
7294 CLUTTER_PARAM_ANIMATABLE);
7295
7296 /**
7297 * ClutterActor:translation-y:
7298 *
7299 * An additional translation applied along the Y axis, relative
7300 * to the actor's #ClutterActor:pivot-point.
7301 *
7302 * The #ClutterActor:translation-y property is animatable.
7303 *
7304 * Since: 1.12
7305 */
7306 obj_props[PROP_TRANSLATION_Y] =
7307 g_param_spec_float ("translation-y",
7308 P_("Translation Y"),
7309 P_("Translation along the Y axis"),
7310 -G_MAXFLOAT, G_MAXFLOAT,
7311 0.f,
7312 G_PARAM_READWRITE |
7313 G_PARAM_STATIC_STRINGS |
7314 CLUTTER_PARAM_ANIMATABLE);
7315
7316 /**
7317 * ClutterActor:translation-z:
7318 *
7319 * An additional translation applied along the Z axis, relative
7320 * to the actor's #ClutterActor:pivot-point.
7321 *
7322 * The #ClutterActor:translation-z property is animatable.
7323 *
7324 * Since: 1.12
7325 */
7326 obj_props[PROP_TRANSLATION_Z] =
7327 g_param_spec_float ("translation-z",
7328 P_("Translation Z"),
7329 P_("Translation along the Z axis"),
7330 -G_MAXFLOAT, G_MAXFLOAT,
7331 0.f,
7332 G_PARAM_READWRITE |
7333 G_PARAM_STATIC_STRINGS |
7334 CLUTTER_PARAM_ANIMATABLE);
7335
7336 /**
7337 * ClutterActor:transform:
7338 *
7339 * Overrides the transformations of a #ClutterActor with a custom
7340 * matrix.
7341 *
7342 * The matrix specified by the #ClutterActor:transform property is
7343 * applied to the actor and its children relative to the actor's
7344 * #ClutterActor:allocation and #ClutterActor:pivot-point.
7345 *
7346 * Application code should rarely need to use this function directly.
7347 *
7348 * Setting this property with a #ClutterMatrix will set the
7349 * #ClutterActor:transform-set property to %TRUE as a side effect;
7350 * setting this property with %NULL will set the
7351 * #ClutterActor:transform-set property to %FALSE.
7352 *
7353 * The #ClutterActor:transform property is animatable.
7354 *
7355 * Since: 1.12
7356 */
7357 obj_props[PROP_TRANSFORM] =
7358 g_param_spec_boxed ("transform",
7359 P_("Transform"),
7360 P_("Transformation matrix"),
7361 CLUTTER_TYPE_MATRIX,
7362 G_PARAM_READWRITE |
7363 G_PARAM_STATIC_STRINGS |
7364 CLUTTER_PARAM_ANIMATABLE);
7365
7366 /**
7367 * ClutterActor:transform-set:
7368 *
7369 * Whether the #ClutterActor:transform property is set.
7370 *
7371 * Since: 1.12
7372 */
7373 obj_props[PROP_TRANSFORM_SET] =
7374 g_param_spec_boolean ("transform-set",
7375 P_("Transform Set"),
7376 P_("Whether the transform property is set"),
7377 FALSE,
7378 G_PARAM_READABLE |
7379 G_PARAM_STATIC_STRINGS);
7380
7381 /**
7382 * ClutterActor:child-transform:
7383 *
7384 * Applies a transformation matrix on each child of an actor.
7385 *
7386 * Setting this property with a #ClutterMatrix will set the
7387 * #ClutterActor:child-transform-set property to %TRUE as a side effect;
7388 * setting this property with %NULL will set the
7389 * #ClutterActor:child-transform-set property to %FALSE.
7390 *
7391 * The #ClutterActor:child-transform property is animatable.
7392 *
7393 * Since: 1.12
7394 */
7395 obj_props[PROP_CHILD_TRANSFORM] =
7396 g_param_spec_boxed ("child-transform",
7397 P_("Child Transform"),
7398 P_("Children transformation matrix"),
7399 CLUTTER_TYPE_MATRIX,
7400 G_PARAM_READWRITE |
7401 G_PARAM_STATIC_STRINGS |
7402 CLUTTER_PARAM_ANIMATABLE);
7403
7404 /**
7405 * ClutterActor:child-transform-set:
7406 *
7407 * Whether the #ClutterActor:child-transform property is set.
7408 *
7409 * Since: 1.12
7410 */
7411 obj_props[PROP_CHILD_TRANSFORM_SET] =
7412 g_param_spec_boolean ("child-transform-set",
7413 P_("Child Transform Set"),
7414 P_("Whether the child-transform property is set"),
7415 FALSE,
7416 G_PARAM_READABLE |
7417 G_PARAM_STATIC_STRINGS);
7418
7419 /**
7420 * ClutterActor:show-on-set-parent:
7421 *
7422 * If %TRUE, the actor is automatically shown when parented.
7423 *
7424 * Calling clutter_actor_hide() on an actor which has not been
7425 * parented will set this property to %FALSE as a side effect.
7426 *
7427 * Since: 0.8
7428 */
7429 obj_props[PROP_SHOW_ON_SET_PARENT] = /* XXX:2.0 - remove */
7430 g_param_spec_boolean ("show-on-set-parent",
7431 P_("Show on set parent"),
7432 P_("Whether the actor is shown when parented"),
7433 TRUE,
7434 CLUTTER_PARAM_READWRITE);
7435
7436 /**
7437 * ClutterActor:clip-to-allocation:
7438 *
7439 * Whether the clip region should track the allocated area
7440 * of the actor.
7441 *
7442 * This property is ignored if a clip area has been explicitly
7443 * set using clutter_actor_set_clip().
7444 *
7445 * Since: 1.0
7446 */
7447 obj_props[PROP_CLIP_TO_ALLOCATION] =
7448 g_param_spec_boolean ("clip-to-allocation",
7449 P_("Clip to Allocation"),
7450 P_("Sets the clip region to track the actor’s allocation"),
7451 FALSE,
7452 CLUTTER_PARAM_READWRITE);
7453
7454 /**
7455 * ClutterActor:text-direction:
7456 *
7457 * The direction of the text inside a #ClutterActor.
7458 *
7459 * Since: 1.0
7460 */
7461 obj_props[PROP_TEXT_DIRECTION] =
7462 g_param_spec_enum ("text-direction",
7463 P_("Text Direction"),
7464 P_("Direction of the text"),
7465 CLUTTER_TYPE_TEXT_DIRECTION,
7466 CLUTTER_TEXT_DIRECTION_LTR,
7467 CLUTTER_PARAM_READWRITE);
7468
7469 /**
7470 * ClutterActor:has-pointer:
7471 *
7472 * Whether the actor contains the pointer of a #ClutterInputDevice
7473 * or not.
7474 *
7475 * Since: 1.2
7476 */
7477 obj_props[PROP_HAS_POINTER] =
7478 g_param_spec_boolean ("has-pointer",
7479 P_("Has Pointer"),
7480 P_("Whether the actor contains the pointer of an input device"),
7481 FALSE,
7482 CLUTTER_PARAM_READABLE);
7483
7484 /**
7485 * ClutterActor:actions:
7486 *
7487 * Adds a #ClutterAction to the actor
7488 *
7489 * Since: 1.4
7490 */
7491 obj_props[PROP_ACTIONS] =
7492 g_param_spec_object ("actions",
7493 P_("Actions"),
7494 P_("Adds an action to the actor"),
7495 CLUTTER_TYPE_ACTION,
7496 CLUTTER_PARAM_WRITABLE);
7497
7498 /**
7499 * ClutterActor:constraints:
7500 *
7501 * Adds a #ClutterConstraint to the actor
7502 *
7503 * Since: 1.4
7504 */
7505 obj_props[PROP_CONSTRAINTS] =
7506 g_param_spec_object ("constraints",
7507 P_("Constraints"),
7508 P_("Adds a constraint to the actor"),
7509 CLUTTER_TYPE_CONSTRAINT,
7510 CLUTTER_PARAM_WRITABLE);
7511
7512 /**
7513 * ClutterActor:effect:
7514 *
7515 * Adds #ClutterEffect to the list of effects be applied on a #ClutterActor
7516 *
7517 * Since: 1.4
7518 */
7519 obj_props[PROP_EFFECT] =
7520 g_param_spec_object ("effect",
7521 P_("Effect"),
7522 P_("Add an effect to be applied on the actor"),
7523 CLUTTER_TYPE_EFFECT,
7524 CLUTTER_PARAM_WRITABLE);
7525
7526 /**
7527 * ClutterActor:layout-manager:
7528 *
7529 * A delegate object for controlling the layout of the children of
7530 * an actor.
7531 *
7532 * Since: 1.10
7533 */
7534 obj_props[PROP_LAYOUT_MANAGER] =
7535 g_param_spec_object ("layout-manager",
7536 P_("Layout Manager"),
7537 P_("The object controlling the layout of an actor’s children"),
7538 CLUTTER_TYPE_LAYOUT_MANAGER,
7539 CLUTTER_PARAM_READWRITE);
7540
7541 /**
7542 * ClutterActor:x-expand:
7543 *
7544 * Whether a layout manager should assign more space to the actor on
7545 * the X axis.
7546 *
7547 * Since: 1.12
7548 */
7549 obj_props[PROP_X_EXPAND] =
7550 g_param_spec_boolean ("x-expand",
7551 P_("X Expand"),
7552 P_("Whether extra horizontal space should be assigned to the actor"),
7553 FALSE,
7554 G_PARAM_READWRITE |
7555 G_PARAM_STATIC_STRINGS);
7556
7557 /**
7558 * ClutterActor:y-expand:
7559 *
7560 * Whether a layout manager should assign more space to the actor on
7561 * the Y axis.
7562 *
7563 * Since: 1.12
7564 */
7565 obj_props[PROP_Y_EXPAND] =
7566 g_param_spec_boolean ("y-expand",
7567 P_("Y Expand"),
7568 P_("Whether extra vertical space should be assigned to the actor"),
7569 FALSE,
7570 G_PARAM_READWRITE |
7571 G_PARAM_STATIC_STRINGS);
7572
7573 /**
7574 * ClutterActor:x-align:
7575 *
7576 * The alignment of an actor on the X axis, if the actor has been given
7577 * extra space for its allocation. See also the #ClutterActor:x-expand
7578 * property.
7579 *
7580 * Since: 1.10
7581 */
7582 obj_props[PROP_X_ALIGN] =
7583 g_param_spec_enum ("x-align",
7584 P_("X Alignment"),
7585 P_("The alignment of the actor on the X axis within its allocation"),
7586 CLUTTER_TYPE_ACTOR_ALIGN,
7587 CLUTTER_ACTOR_ALIGN_FILL,
7588 CLUTTER_PARAM_READWRITE);
7589
7590 /**
7591 * ClutterActor:y-align:
7592 *
7593 * The alignment of an actor on the Y axis, if the actor has been given
7594 * extra space for its allocation.
7595 *
7596 * Since: 1.10
7597 */
7598 obj_props[PROP_Y_ALIGN] =
7599 g_param_spec_enum ("y-align",
7600 P_("Y Alignment"),
7601 P_("The alignment of the actor on the Y axis within its allocation"),
7602 CLUTTER_TYPE_ACTOR_ALIGN,
7603 CLUTTER_ACTOR_ALIGN_FILL,
7604 CLUTTER_PARAM_READWRITE);
7605
7606 /**
7607 * ClutterActor:margin-top:
7608 *
7609 * The margin (in pixels) from the top of the actor.
7610 *
7611 * This property adds a margin to the actor's preferred size; the margin
7612 * will be automatically taken into account when allocating the actor.
7613 *
7614 * The #ClutterActor:margin-top property is animatable.
7615 *
7616 * Since: 1.10
7617 */
7618 obj_props[PROP_MARGIN_TOP] =
7619 g_param_spec_float ("margin-top",
7620 P_("Margin Top"),
7621 P_("Extra space at the top"),
7622 0.0, G_MAXFLOAT,
7623 0.0,
7624 G_PARAM_READWRITE |
7625 G_PARAM_STATIC_STRINGS |
7626 CLUTTER_PARAM_ANIMATABLE);
7627
7628 /**
7629 * ClutterActor:margin-bottom:
7630 *
7631 * The margin (in pixels) from the bottom of the actor.
7632 *
7633 * This property adds a margin to the actor's preferred size; the margin
7634 * will be automatically taken into account when allocating the actor.
7635 *
7636 * The #ClutterActor:margin-bottom property is animatable.
7637 *
7638 * Since: 1.10
7639 */
7640 obj_props[PROP_MARGIN_BOTTOM] =
7641 g_param_spec_float ("margin-bottom",
7642 P_("Margin Bottom"),
7643 P_("Extra space at the bottom"),
7644 0.0, G_MAXFLOAT,
7645 0.0,
7646 G_PARAM_READWRITE |
7647 G_PARAM_STATIC_STRINGS |
7648 CLUTTER_PARAM_ANIMATABLE);
7649
7650 /**
7651 * ClutterActor:margin-left:
7652 *
7653 * The margin (in pixels) from the left of the actor.
7654 *
7655 * This property adds a margin to the actor's preferred size; the margin
7656 * will be automatically taken into account when allocating the actor.
7657 *
7658 * The #ClutterActor:margin-left property is animatable.
7659 *
7660 * Since: 1.10
7661 */
7662 obj_props[PROP_MARGIN_LEFT] =
7663 g_param_spec_float ("margin-left",
7664 P_("Margin Left"),
7665 P_("Extra space at the left"),
7666 0.0, G_MAXFLOAT,
7667 0.0,
7668 G_PARAM_READWRITE |
7669 G_PARAM_STATIC_STRINGS |
7670 CLUTTER_PARAM_ANIMATABLE);
7671
7672 /**
7673 * ClutterActor:margin-right:
7674 *
7675 * The margin (in pixels) from the right of the actor.
7676 *
7677 * This property adds a margin to the actor's preferred size; the margin
7678 * will be automatically taken into account when allocating the actor.
7679 *
7680 * The #ClutterActor:margin-right property is animatable.
7681 *
7682 * Since: 1.10
7683 */
7684 obj_props[PROP_MARGIN_RIGHT] =
7685 g_param_spec_float ("margin-right",
7686 P_("Margin Right"),
7687 P_("Extra space at the right"),
7688 0.0, G_MAXFLOAT,
7689 0.0,
7690 G_PARAM_READWRITE |
7691 G_PARAM_STATIC_STRINGS |
7692 CLUTTER_PARAM_ANIMATABLE);
7693
7694 /**
7695 * ClutterActor:background-color-set:
7696 *
7697 * Whether the #ClutterActor:background-color property has been set.
7698 *
7699 * Since: 1.10
7700 */
7701 obj_props[PROP_BACKGROUND_COLOR_SET] =
7702 g_param_spec_boolean ("background-color-set",
7703 P_("Background Color Set"),
7704 P_("Whether the background color is set"),
7705 FALSE,
7706 CLUTTER_PARAM_READABLE);
7707
7708 /**
7709 * ClutterActor:background-color:
7710 *
7711 * Paints a solid fill of the actor's allocation using the specified
7712 * color.
7713 *
7714 * The #ClutterActor:background-color property is animatable.
7715 *
7716 * Since: 1.10
7717 */
7718 obj_props[PROP_BACKGROUND_COLOR] =
7719 clutter_param_spec_color ("background-color",
7720 P_("Background color"),
7721 P_("The actor’s background color"),
7722 CLUTTER_COLOR_Transparent,
7723 G_PARAM_READWRITE |
7724 G_PARAM_STATIC_STRINGS |
7725 CLUTTER_PARAM_ANIMATABLE);
7726
7727 /**
7728 * ClutterActor:first-child:
7729 *
7730 * The actor's first child.
7731 *
7732 * Since: 1.10
7733 */
7734 obj_props[PROP_FIRST_CHILD] =
7735 g_param_spec_object ("first-child",
7736 P_("First Child"),
7737 P_("The actor’s first child"),
7738 CLUTTER_TYPE_ACTOR,
7739 CLUTTER_PARAM_READABLE);
7740
7741 /**
7742 * ClutterActor:last-child:
7743 *
7744 * The actor's last child.
7745 *
7746 * Since: 1.10
7747 */
7748 obj_props[PROP_LAST_CHILD] =
7749 g_param_spec_object ("last-child",
7750 P_("Last Child"),
7751 P_("The actor’s last child"),
7752 CLUTTER_TYPE_ACTOR,
7753 CLUTTER_PARAM_READABLE);
7754
7755 /**
7756 * ClutterActor:content:
7757 *
7758 * The #ClutterContent implementation that controls the content
7759 * of the actor.
7760 *
7761 * Since: 1.10
7762 */
7763 obj_props[PROP_CONTENT] =
7764 g_param_spec_object ("content",
7765 P_("Content"),
7766 P_("Delegate object for painting the actor’s content"),
7767 CLUTTER_TYPE_CONTENT,
7768 CLUTTER_PARAM_READWRITE);
7769
7770 /**
7771 * ClutterActor:content-gravity:
7772 *
7773 * The alignment that should be honoured by the #ClutterContent
7774 * set with the #ClutterActor:content property.
7775 *
7776 * Changing the value of this property will change the bounding box of
7777 * the content; you can use the #ClutterActor:content-box property to
7778 * get the position and size of the content within the actor's
7779 * allocation.
7780 *
7781 * This property is meaningful only for #ClutterContent implementations
7782 * that have a preferred size, and if the preferred size is smaller than
7783 * the actor's allocation.
7784 *
7785 * The #ClutterActor:content-gravity property is animatable.
7786 *
7787 * Since: 1.10
7788 */
7789 obj_props[PROP_CONTENT_GRAVITY] =
7790 g_param_spec_enum ("content-gravity",
7791 P_("Content Gravity"),
7792 P_("Alignment of the actor’s content"),
7793 CLUTTER_TYPE_CONTENT_GRAVITY,
7794 CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
7795 CLUTTER_PARAM_READWRITE);
7796
7797 /**
7798 * ClutterActor:content-box:
7799 *
7800 * The bounding box for the #ClutterContent used by the actor.
7801 *
7802 * The value of this property is controlled by the #ClutterActor:allocation
7803 * and #ClutterActor:content-gravity properties of #ClutterActor.
7804 *
7805 * The bounding box for the content is guaranteed to never exceed the
7806 * allocation's of the actor.
7807 *
7808 * Since: 1.10
7809 */
7810 obj_props[PROP_CONTENT_BOX] =
7811 g_param_spec_boxed ("content-box",
7812 P_("Content Box"),
7813 P_("The bounding box of the actor’s content"),
7814 CLUTTER_TYPE_ACTOR_BOX,
7815 G_PARAM_READABLE |
7816 G_PARAM_STATIC_STRINGS |
7817 CLUTTER_PARAM_ANIMATABLE);
7818
7819 obj_props[PROP_MINIFICATION_FILTER] =
7820 g_param_spec_enum ("minification-filter",
7821 P_("Minification Filter"),
7822 P_("The filter used when reducing the size of the content"),
7823 CLUTTER_TYPE_SCALING_FILTER,
7824 CLUTTER_SCALING_FILTER_LINEAR,
7825 CLUTTER_PARAM_READWRITE);
7826
7827 obj_props[PROP_MAGNIFICATION_FILTER] =
7828 g_param_spec_enum ("magnification-filter",
7829 P_("Magnification Filter"),
7830 P_("The filter used when increasing the size of the content"),
7831 CLUTTER_TYPE_SCALING_FILTER,
7832 CLUTTER_SCALING_FILTER_LINEAR,
7833 CLUTTER_PARAM_READWRITE);
7834
7835 /**
7836 * ClutterActor:content-repeat:
7837 *
7838 * The repeat policy for the actor's #ClutterActor:content.
7839 *
7840 * Since: 1.12
7841 */
7842 obj_props[PROP_CONTENT_REPEAT] =
7843 g_param_spec_flags ("content-repeat",
7844 P_("Content Repeat"),
7845 P_("The repeat policy for the actor’s content"),
7846 CLUTTER_TYPE_CONTENT_REPEAT,
7847 CLUTTER_REPEAT_NONE,
7848 G_PARAM_READWRITE |
7849 G_PARAM_STATIC_STRINGS);
7850
7851 g_object_class_install_properties (object_class, PROP_LAST, obj_props);
7852
7853 /**
7854 * ClutterActor::destroy:
7855 * @actor: the #ClutterActor which emitted the signal
7856 *
7857 * The ::destroy signal notifies that all references held on the
7858 * actor which emitted it should be released.
7859 *
7860 * The ::destroy signal should be used by all holders of a reference
7861 * on @actor.
7862 *
7863 * This signal might result in the finalization of the #ClutterActor
7864 * if all references are released.
7865 *
7866 * Composite actors and actors implementing the #ClutterContainer
7867 * interface should override the default implementation of the
7868 * class handler of this signal and call clutter_actor_destroy() on
7869 * their children. When overriding the default class handler, it is
7870 * required to chain up to the parent's implementation.
7871 *
7872 * Since: 0.2
7873 */
7874 actor_signals[DESTROY] =
7875 g_signal_new (I_("destroy"),
7876 G_TYPE_FROM_CLASS (object_class),
7877 G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
7878 G_STRUCT_OFFSET (ClutterActorClass, destroy),
7879 NULL, NULL,
7880 _clutter_marshal_VOID__VOID,
7881 G_TYPE_NONE, 0);
7882 /**
7883 * ClutterActor::show:
7884 * @actor: the object which received the signal
7885 *
7886 * The ::show signal is emitted when an actor is visible and
7887 * rendered on the stage.
7888 *
7889 * Since: 0.2
7890 */
7891 actor_signals[SHOW] =
7892 g_signal_new (I_("show"),
7893 G_TYPE_FROM_CLASS (object_class),
7894 G_SIGNAL_RUN_FIRST,
7895 G_STRUCT_OFFSET (ClutterActorClass, show),
7896 NULL, NULL,
7897 _clutter_marshal_VOID__VOID,
7898 G_TYPE_NONE, 0);
7899 /**
7900 * ClutterActor::hide:
7901 * @actor: the object which received the signal
7902 *
7903 * The ::hide signal is emitted when an actor is no longer rendered
7904 * on the stage.
7905 *
7906 * Since: 0.2
7907 */
7908 actor_signals[HIDE] =
7909 g_signal_new (I_("hide"),
7910 G_TYPE_FROM_CLASS (object_class),
7911 G_SIGNAL_RUN_FIRST,
7912 G_STRUCT_OFFSET (ClutterActorClass, hide),
7913 NULL, NULL,
7914 _clutter_marshal_VOID__VOID,
7915 G_TYPE_NONE, 0);
7916 /**
7917 * ClutterActor::parent-set:
7918 * @actor: the object which received the signal
7919 * @old_parent: (allow-none): the previous parent of the actor, or %NULL
7920 *
7921 * This signal is emitted when the parent of the actor changes.
7922 *
7923 * Since: 0.2
7924 */
7925 actor_signals[PARENT_SET] =
7926 g_signal_new (I_("parent-set"),
7927 G_TYPE_FROM_CLASS (object_class),
7928 G_SIGNAL_RUN_LAST,
7929 G_STRUCT_OFFSET (ClutterActorClass, parent_set),
7930 NULL, NULL,
7931 _clutter_marshal_VOID__OBJECT,
7932 G_TYPE_NONE, 1,
7933 CLUTTER_TYPE_ACTOR);
7934
7935 /**
7936 * ClutterActor::queue-redraw:
7937 * @actor: the actor we're bubbling the redraw request through
7938 * @origin: the actor which initiated the redraw request
7939 *
7940 * The ::queue_redraw signal is emitted when clutter_actor_queue_redraw()
7941 * is called on @origin.
7942 *
7943 * The default implementation for #ClutterActor chains up to the
7944 * parent actor and queues a redraw on the parent, thus "bubbling"
7945 * the redraw queue up through the actor graph. The default
7946 * implementation for #ClutterStage queues a clutter_stage_ensure_redraw()
7947 * in a main loop idle handler.
7948 *
7949 * Note that the @origin actor may be the stage, or a container; it
7950 * does not have to be a leaf node in the actor graph.
7951 *
7952 * Toolkits embedding a #ClutterStage which require a redraw and
7953 * relayout cycle can stop the emission of this signal using the
7954 * GSignal API, redraw the UI and then call clutter_stage_ensure_redraw()
7955 * themselves, like:
7956 *
7957 * |[<!-- language="C" -->
7958 * static void
7959 * on_redraw_complete (gpointer data)
7960 * {
7961 * ClutterStage *stage = data;
7962 *
7963 * // execute the Clutter drawing pipeline
7964 * clutter_stage_ensure_redraw (stage);
7965 * }
7966 *
7967 * static void
7968 * on_stage_queue_redraw (ClutterStage *stage)
7969 * {
7970 * // this prevents the default handler to run
7971 * g_signal_stop_emission_by_name (stage, "queue-redraw");
7972 *
7973 * // queue a redraw with the host toolkit and call
7974 * // a function when the redraw has been completed
7975 * queue_a_redraw (G_CALLBACK (on_redraw_complete), stage);
7976 * }
7977 * ]|
7978 *
7979 * Note: This signal is emitted before the Clutter paint
7980 * pipeline is executed. If you want to know when the pipeline has
7981 * been completed you should use clutter_threads_add_repaint_func()
7982 * or clutter_threads_add_repaint_func_full().
7983 *
7984 * Since: 1.0
7985 */
7986 actor_signals[QUEUE_REDRAW] =
7987 g_signal_new (I_("queue-redraw"),
7988 G_TYPE_FROM_CLASS (object_class),
7989 G_SIGNAL_RUN_LAST |
7990 G_SIGNAL_NO_HOOKS,
7991 G_STRUCT_OFFSET (ClutterActorClass, queue_redraw),
7992 NULL, NULL,
7993 _clutter_marshal_VOID__OBJECT,
7994 G_TYPE_NONE, 1,
7995 CLUTTER_TYPE_ACTOR);
7996
7997 /**
7998 * ClutterActor::queue-relayout:
7999 * @actor: the actor being queued for relayout
8000 *
8001 * The ::queue_layout signal is emitted when clutter_actor_queue_relayout()
8002 * is called on an actor.
8003 *
8004 * The default implementation for #ClutterActor chains up to the
8005 * parent actor and queues a relayout on the parent, thus "bubbling"
8006 * the relayout queue up through the actor graph.
8007 *
8008 * The main purpose of this signal is to allow relayout to be propagated
8009 * properly in the presence of #ClutterClone actors. Applications will
8010 * not normally need to connect to this signal.
8011 *
8012 * Since: 1.2
8013 */
8014 actor_signals[QUEUE_RELAYOUT] =
8015 g_signal_new (I_("queue-relayout"),
8016 G_TYPE_FROM_CLASS (object_class),
8017 G_SIGNAL_RUN_LAST |
8018 G_SIGNAL_NO_HOOKS,
8019 G_STRUCT_OFFSET (ClutterActorClass, queue_relayout),
8020 NULL, NULL,
8021 _clutter_marshal_VOID__VOID,
8022 G_TYPE_NONE, 0);
8023
8024 /**
8025 * ClutterActor::event:
8026 * @actor: the actor which received the event
8027 * @event: a #ClutterEvent
8028 *
8029 * The ::event signal is emitted each time an event is received
8030 * by the @actor. This signal will be emitted on every actor,
8031 * following the hierarchy chain, until it reaches the top-level
8032 * container (the #ClutterStage).
8033 *
8034 * Return value: %TRUE if the event has been handled by the actor,
8035 * or %FALSE to continue the emission.
8036 *
8037 * Since: 0.6
8038 */
8039 actor_signals[EVENT] =
8040 g_signal_new (I_("event"),
8041 G_TYPE_FROM_CLASS (object_class),
8042 G_SIGNAL_RUN_LAST,
8043 G_STRUCT_OFFSET (ClutterActorClass, event),
8044 _clutter_boolean_handled_accumulator, NULL,
8045 _clutter_marshal_BOOLEAN__BOXED,
8046 G_TYPE_BOOLEAN, 1,
8047 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8048 /**
8049 * ClutterActor::button-press-event:
8050 * @actor: the actor which received the event
8051 * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
8052 *
8053 * The ::button-press-event signal is emitted each time a mouse button
8054 * is pressed on @actor.
8055 *
8056 * Return value: %TRUE if the event has been handled by the actor,
8057 * or %FALSE to continue the emission.
8058 *
8059 * Since: 0.6
8060 */
8061 actor_signals[BUTTON_PRESS_EVENT] =
8062 g_signal_new (I_("button-press-event"),
8063 G_TYPE_FROM_CLASS (object_class),
8064 G_SIGNAL_RUN_LAST,
8065 G_STRUCT_OFFSET (ClutterActorClass, button_press_event),
8066 _clutter_boolean_handled_accumulator, NULL,
8067 _clutter_marshal_BOOLEAN__BOXED,
8068 G_TYPE_BOOLEAN, 1,
8069 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8070 /**
8071 * ClutterActor::button-release-event:
8072 * @actor: the actor which received the event
8073 * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
8074 *
8075 * The ::button-release-event signal is emitted each time a mouse button
8076 * is released on @actor.
8077 *
8078 * Return value: %TRUE if the event has been handled by the actor,
8079 * or %FALSE to continue the emission.
8080 *
8081 * Since: 0.6
8082 */
8083 actor_signals[BUTTON_RELEASE_EVENT] =
8084 g_signal_new (I_("button-release-event"),
8085 G_TYPE_FROM_CLASS (object_class),
8086 G_SIGNAL_RUN_LAST,
8087 G_STRUCT_OFFSET (ClutterActorClass, button_release_event),
8088 _clutter_boolean_handled_accumulator, NULL,
8089 _clutter_marshal_BOOLEAN__BOXED,
8090 G_TYPE_BOOLEAN, 1,
8091 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8092 /**
8093 * ClutterActor::scroll-event:
8094 * @actor: the actor which received the event
8095 * @event: (type ClutterScrollEvent): a #ClutterScrollEvent
8096 *
8097 * The ::scroll-event signal is emitted each time the mouse is
8098 * scrolled on @actor
8099 *
8100 * Return value: %TRUE if the event has been handled by the actor,
8101 * or %FALSE to continue the emission.
8102 *
8103 * Since: 0.6
8104 */
8105 actor_signals[SCROLL_EVENT] =
8106 g_signal_new (I_("scroll-event"),
8107 G_TYPE_FROM_CLASS (object_class),
8108 G_SIGNAL_RUN_LAST,
8109 G_STRUCT_OFFSET (ClutterActorClass, scroll_event),
8110 _clutter_boolean_handled_accumulator, NULL,
8111 _clutter_marshal_BOOLEAN__BOXED,
8112 G_TYPE_BOOLEAN, 1,
8113 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8114 /**
8115 * ClutterActor::key-press-event:
8116 * @actor: the actor which received the event
8117 * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
8118 *
8119 * The ::key-press-event signal is emitted each time a keyboard button
8120 * is pressed while @actor has key focus (see clutter_stage_set_key_focus()).
8121 *
8122 * Return value: %TRUE if the event has been handled by the actor,
8123 * or %FALSE to continue the emission.
8124 *
8125 * Since: 0.6
8126 */
8127 actor_signals[KEY_PRESS_EVENT] =
8128 g_signal_new (I_("key-press-event"),
8129 G_TYPE_FROM_CLASS (object_class),
8130 G_SIGNAL_RUN_LAST,
8131 G_STRUCT_OFFSET (ClutterActorClass, key_press_event),
8132 _clutter_boolean_handled_accumulator, NULL,
8133 _clutter_marshal_BOOLEAN__BOXED,
8134 G_TYPE_BOOLEAN, 1,
8135 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8136 /**
8137 * ClutterActor::key-release-event:
8138 * @actor: the actor which received the event
8139 * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
8140 *
8141 * The ::key-release-event signal is emitted each time a keyboard button
8142 * is released while @actor has key focus (see
8143 * clutter_stage_set_key_focus()).
8144 *
8145 * Return value: %TRUE if the event has been handled by the actor,
8146 * or %FALSE to continue the emission.
8147 *
8148 * Since: 0.6
8149 */
8150 actor_signals[KEY_RELEASE_EVENT] =
8151 g_signal_new (I_("key-release-event"),
8152 G_TYPE_FROM_CLASS (object_class),
8153 G_SIGNAL_RUN_LAST,
8154 G_STRUCT_OFFSET (ClutterActorClass, key_release_event),
8155 _clutter_boolean_handled_accumulator, NULL,
8156 _clutter_marshal_BOOLEAN__BOXED,
8157 G_TYPE_BOOLEAN, 1,
8158 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8159 /**
8160 * ClutterActor::motion-event:
8161 * @actor: the actor which received the event
8162 * @event: (type ClutterMotionEvent): a #ClutterMotionEvent
8163 *
8164 * The ::motion-event signal is emitted each time the mouse pointer is
8165 * moved over @actor.
8166 *
8167 * Return value: %TRUE if the event has been handled by the actor,
8168 * or %FALSE to continue the emission.
8169 *
8170 * Since: 0.6
8171 */
8172 actor_signals[MOTION_EVENT] =
8173 g_signal_new (I_("motion-event"),
8174 G_TYPE_FROM_CLASS (object_class),
8175 G_SIGNAL_RUN_LAST,
8176 G_STRUCT_OFFSET (ClutterActorClass, motion_event),
8177 _clutter_boolean_handled_accumulator, NULL,
8178 _clutter_marshal_BOOLEAN__BOXED,
8179 G_TYPE_BOOLEAN, 1,
8180 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8181
8182 /**
8183 * ClutterActor::key-focus-in:
8184 * @actor: the actor which now has key focus
8185 *
8186 * The ::key-focus-in signal is emitted when @actor receives key focus.
8187 *
8188 * Since: 0.6
8189 */
8190 actor_signals[KEY_FOCUS_IN] =
8191 g_signal_new (I_("key-focus-in"),
8192 G_TYPE_FROM_CLASS (object_class),
8193 G_SIGNAL_RUN_LAST,
8194 G_STRUCT_OFFSET (ClutterActorClass, key_focus_in),
8195 NULL, NULL,
8196 _clutter_marshal_VOID__VOID,
8197 G_TYPE_NONE, 0);
8198
8199 /**
8200 * ClutterActor::key-focus-out:
8201 * @actor: the actor which now has key focus
8202 *
8203 * The ::key-focus-out signal is emitted when @actor loses key focus.
8204 *
8205 * Since: 0.6
8206 */
8207 actor_signals[KEY_FOCUS_OUT] =
8208 g_signal_new (I_("key-focus-out"),
8209 G_TYPE_FROM_CLASS (object_class),
8210 G_SIGNAL_RUN_LAST,
8211 G_STRUCT_OFFSET (ClutterActorClass, key_focus_out),
8212 NULL, NULL,
8213 _clutter_marshal_VOID__VOID,
8214 G_TYPE_NONE, 0);
8215
8216 /**
8217 * ClutterActor::enter-event:
8218 * @actor: the actor which the pointer has entered.
8219 * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
8220 *
8221 * The ::enter-event signal is emitted when the pointer enters the @actor
8222 *
8223 * Return value: %TRUE if the event has been handled by the actor,
8224 * or %FALSE to continue the emission.
8225 *
8226 * Since: 0.6
8227 */
8228 actor_signals[ENTER_EVENT] =
8229 g_signal_new (I_("enter-event"),
8230 G_TYPE_FROM_CLASS (object_class),
8231 G_SIGNAL_RUN_LAST,
8232 G_STRUCT_OFFSET (ClutterActorClass, enter_event),
8233 _clutter_boolean_handled_accumulator, NULL,
8234 _clutter_marshal_BOOLEAN__BOXED,
8235 G_TYPE_BOOLEAN, 1,
8236 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8237
8238 /**
8239 * ClutterActor::leave-event:
8240 * @actor: the actor which the pointer has left
8241 * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
8242 *
8243 * The ::leave-event signal is emitted when the pointer leaves the @actor.
8244 *
8245 * Return value: %TRUE if the event has been handled by the actor,
8246 * or %FALSE to continue the emission.
8247 *
8248 * Since: 0.6
8249 */
8250 actor_signals[LEAVE_EVENT] =
8251 g_signal_new (I_("leave-event"),
8252 G_TYPE_FROM_CLASS (object_class),
8253 G_SIGNAL_RUN_LAST,
8254 G_STRUCT_OFFSET (ClutterActorClass, leave_event),
8255 _clutter_boolean_handled_accumulator, NULL,
8256 _clutter_marshal_BOOLEAN__BOXED,
8257 G_TYPE_BOOLEAN, 1,
8258 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8259
8260 /**
8261 * ClutterActor::captured-event:
8262 * @actor: the actor which received the signal
8263 * @event: a #ClutterEvent
8264 *
8265 * The ::captured-event signal is emitted when an event is captured
8266 * by Clutter. This signal will be emitted starting from the top-level
8267 * container (the #ClutterStage) to the actor which received the event
8268 * going down the hierarchy. This signal can be used to intercept every
8269 * event before the specialized events (like
8270 * ClutterActor::button-press-event or ::key-released-event) are
8271 * emitted.
8272 *
8273 * Return value: %TRUE if the event has been handled by the actor,
8274 * or %FALSE to continue the emission.
8275 *
8276 * Since: 0.6
8277 */
8278 actor_signals[CAPTURED_EVENT] =
8279 g_signal_new (I_("captured-event"),
8280 G_TYPE_FROM_CLASS (object_class),
8281 G_SIGNAL_RUN_LAST,
8282 G_STRUCT_OFFSET (ClutterActorClass, captured_event),
8283 _clutter_boolean_handled_accumulator, NULL,
8284 _clutter_marshal_BOOLEAN__BOXED,
8285 G_TYPE_BOOLEAN, 1,
8286 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8287
8288 /**
8289 * ClutterActor::paint:
8290 * @actor: the #ClutterActor that received the signal
8291 *
8292 * The ::paint signal is emitted each time an actor is being painted.
8293 *
8294 * Subclasses of #ClutterActor should override the #ClutterActorClass.paint
8295 * virtual function paint themselves in that function.
8296 *
8297 * It is strongly discouraged to connect a signal handler to
8298 * the #ClutterActor::paint signal; if you want to change the paint
8299 * sequence of an existing #ClutterActor instance, either create a new
8300 * #ClutterActor class and override the #ClutterActorClass.paint virtual
8301 * function, or use a #ClutterEffect. The #ClutterActor::paint signal
8302 * will be removed in a future version of Clutter.
8303 *
8304 * Since: 0.8
8305 *
8306 * Deprecated: 1.12: Override the #ClutterActorClass.paint virtual
8307 * function, use a #ClutterContent implementation, or a #ClutterEffect
8308 * instead of connecting to this signal.
8309 */
8310 actor_signals[PAINT] =
8311 g_signal_new (I_("paint"),
8312 G_TYPE_FROM_CLASS (object_class),
8313 G_SIGNAL_RUN_LAST |
8314 G_SIGNAL_NO_HOOKS |
8315 G_SIGNAL_DEPRECATED,
8316 G_STRUCT_OFFSET (ClutterActorClass, paint),
8317 NULL, NULL,
8318 _clutter_marshal_VOID__VOID,
8319 G_TYPE_NONE, 0);
8320 /**
8321 * ClutterActor::realize:
8322 * @actor: the #ClutterActor that received the signal
8323 *
8324 * The ::realize signal is emitted each time an actor is being
8325 * realized.
8326 *
8327 * Since: 0.8
8328 *
8329 * Deprecated: 1.16: The signal should not be used in newly
8330 * written code
8331 */
8332 actor_signals[REALIZE] =
8333 g_signal_new (I_("realize"),
8334 G_TYPE_FROM_CLASS (object_class),
8335 G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
8336 G_STRUCT_OFFSET (ClutterActorClass, realize),
8337 NULL, NULL,
8338 _clutter_marshal_VOID__VOID,
8339 G_TYPE_NONE, 0);
8340 /**
8341 * ClutterActor::unrealize:
8342 * @actor: the #ClutterActor that received the signal
8343 *
8344 * The ::unrealize signal is emitted each time an actor is being
8345 * unrealized.
8346 *
8347 * Since: 0.8
8348 *
8349 * Deprecated: 1.16: The signal should not be used in newly
8350 * written code
8351 */
8352 actor_signals[UNREALIZE] =
8353 g_signal_new (I_("unrealize"),
8354 G_TYPE_FROM_CLASS (object_class),
8355 G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
8356 G_STRUCT_OFFSET (ClutterActorClass, unrealize),
8357 NULL, NULL,
8358 _clutter_marshal_VOID__VOID,
8359 G_TYPE_NONE, 0);
8360
8361 /**
8362 * ClutterActor::pick:
8363 * @actor: the #ClutterActor that received the signal
8364 * @color: the #ClutterColor to be used when picking
8365 *
8366 * The ::pick signal is emitted each time an actor is being painted
8367 * in "pick mode". The pick mode is used to identify the actor during
8368 * the event handling phase, or by clutter_stage_get_actor_at_pos().
8369 * The actor should paint its shape using the passed @pick_color.
8370 *
8371 * Subclasses of #ClutterActor should override the class signal handler
8372 * and paint themselves in that function.
8373 *
8374 * It is possible to connect a handler to the ::pick signal in order
8375 * to set up some custom aspect of a paint in pick mode.
8376 *
8377 * Since: 1.0
8378 * Deprecated: 1.12: Override the #ClutterActorClass.pick virtual function
8379 * instead.
8380 */
8381 actor_signals[PICK] =
8382 g_signal_new (I_("pick"),
8383 G_TYPE_FROM_CLASS (object_class),
8384 G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
8385 G_STRUCT_OFFSET (ClutterActorClass, pick),
8386 NULL, NULL,
8387 _clutter_marshal_VOID__BOXED,
8388 G_TYPE_NONE, 1,
8389 CLUTTER_TYPE_COLOR | G_SIGNAL_TYPE_STATIC_SCOPE);
8390
8391 /**
8392 * ClutterActor::allocation-changed:
8393 * @actor: the #ClutterActor that emitted the signal
8394 * @box: a #ClutterActorBox with the new allocation
8395 * @flags: #ClutterAllocationFlags for the allocation
8396 *
8397 * The ::allocation-changed signal is emitted when the
8398 * #ClutterActor:allocation property changes. Usually, application
8399 * code should just use the notifications for the :allocation property
8400 * but if you want to track the allocation flags as well, for instance
8401 * to know whether the absolute origin of @actor changed, then you might
8402 * want use this signal instead.
8403 *
8404 * Since: 1.0
8405 */
8406 actor_signals[ALLOCATION_CHANGED] =
8407 g_signal_new (I_("allocation-changed"),
8408 G_TYPE_FROM_CLASS (object_class),
8409 G_SIGNAL_RUN_LAST,
8410 0,
8411 NULL, NULL,
8412 _clutter_marshal_VOID__BOXED_FLAGS,
8413 G_TYPE_NONE, 2,
8414 CLUTTER_TYPE_ACTOR_BOX | G_SIGNAL_TYPE_STATIC_SCOPE,
8415 CLUTTER_TYPE_ALLOCATION_FLAGS);
8416
8417 /**
8418 * ClutterActor::transitions-completed:
8419 * @actor: a #ClutterActor
8420 *
8421 * The ::transitions-completed signal is emitted once all transitions
8422 * involving @actor are complete.
8423 *
8424 * Since: 1.10
8425 */
8426 actor_signals[TRANSITIONS_COMPLETED] =
8427 g_signal_new (I_("transitions-completed"),
8428 G_TYPE_FROM_CLASS (object_class),
8429 G_SIGNAL_RUN_LAST,
8430 0,
8431 NULL, NULL,
8432 _clutter_marshal_VOID__VOID,
8433 G_TYPE_NONE, 0);
8434
8435 /**
8436 * ClutterActor::transition-stopped:
8437 * @actor: a #ClutterActor
8438 * @name: the name of the transition
8439 * @is_finished: whether the transition was finished, or stopped
8440 *
8441 * The ::transition-stopped signal is emitted once a transition
8442 * is stopped; a transition is stopped once it reached its total
8443 * duration (including eventual repeats), it has been stopped
8444 * using clutter_timeline_stop(), or it has been removed from the
8445 * transitions applied on @actor, using clutter_actor_remove_transition().
8446 *
8447 * Since: 1.12
8448 */
8449 actor_signals[TRANSITION_STOPPED] =
8450 g_signal_new (I_("transition-stopped"),
8451 G_TYPE_FROM_CLASS (object_class),
8452 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
8453 G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED,
8454 0,
8455 NULL, NULL,
8456 _clutter_marshal_VOID__STRING_BOOLEAN,
8457 G_TYPE_NONE, 2,
8458 G_TYPE_STRING,
8459 G_TYPE_BOOLEAN);
8460
8461 /**
8462 * ClutterActor::touch-event:
8463 * @actor: a #ClutterActor
8464 * @event: a #ClutterEvent
8465 *
8466 * The ::touch-event signal is emitted each time a touch
8467 * begin/end/update/cancel event.
8468 *
8469 * Return value: %CLUTTER_EVENT_STOP if the event has been handled by
8470 * the actor, or %CLUTTER_EVENT_PROPAGATE to continue the emission.
8471 *
8472 * Since: 1.12
8473 */
8474 actor_signals[TOUCH_EVENT] =
8475 g_signal_new (I_("touch-event"),
8476 G_TYPE_FROM_CLASS (object_class),
8477 G_SIGNAL_RUN_LAST,
8478 G_STRUCT_OFFSET (ClutterActorClass, touch_event),
8479 _clutter_boolean_handled_accumulator, NULL,
8480 _clutter_marshal_BOOLEAN__BOXED,
8481 G_TYPE_BOOLEAN, 1,
8482 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8483 }
8484
8485 static void
clutter_actor_init(ClutterActor * self)8486 clutter_actor_init (ClutterActor *self)
8487 {
8488 ClutterActorPrivate *priv;
8489
8490 self->priv = priv = clutter_actor_get_instance_private (self);
8491
8492 priv->pick_id = -1;
8493
8494 priv->opacity = 0xff;
8495 priv->show_on_set_parent = TRUE;
8496
8497 priv->needs_width_request = TRUE;
8498 priv->needs_height_request = TRUE;
8499 priv->needs_allocation = TRUE;
8500
8501 priv->cached_width_age = 1;
8502 priv->cached_height_age = 1;
8503
8504 priv->opacity_override = -1;
8505 priv->enable_model_view_transform = TRUE;
8506
8507 /* Initialize an empty paint volume to start with */
8508 _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
8509 priv->last_paint_volume_valid = TRUE;
8510
8511 priv->transform_valid = FALSE;
8512
8513 /* the default is to stretch the content, to match the
8514 * current behaviour of basically all actors. also, it's
8515 * the easiest thing to compute.
8516 */
8517 priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL;
8518 priv->min_filter = CLUTTER_SCALING_FILTER_LINEAR;
8519 priv->mag_filter = CLUTTER_SCALING_FILTER_LINEAR;
8520
8521 /* this flag will be set to TRUE if the actor gets a child
8522 * or if the [xy]-expand flags are explicitly set; until
8523 * then, the actor does not need to expand.
8524 *
8525 * this also allows us to avoid computing the expand flag
8526 * when building up a scene.
8527 */
8528 priv->needs_compute_expand = FALSE;
8529
8530 /* we start with an easing state with duration forcibly set
8531 * to 0, for backward compatibility.
8532 */
8533 clutter_actor_save_easing_state (self);
8534 clutter_actor_set_easing_duration (self, 0);
8535 }
8536
8537 /**
8538 * clutter_actor_new:
8539 *
8540 * Creates a new #ClutterActor.
8541 *
8542 * A newly created actor has a floating reference, which will be sunk
8543 * when it is added to another actor.
8544 *
8545 * Return value: the newly created #ClutterActor
8546 *
8547 * Since: 1.10
8548 */
8549 ClutterActor *
clutter_actor_new(void)8550 clutter_actor_new (void)
8551 {
8552 return g_object_new (CLUTTER_TYPE_ACTOR, NULL);
8553 }
8554
8555 /**
8556 * clutter_actor_destroy:
8557 * @self: a #ClutterActor
8558 *
8559 * Destroys an actor. When an actor is destroyed, it will break any
8560 * references it holds to other objects. If the actor is inside a
8561 * container, the actor will be removed.
8562 *
8563 * When you destroy a container, its children will be destroyed as well.
8564 *
8565 * Note: you cannot destroy the #ClutterStage returned by
8566 * clutter_stage_get_default().
8567 */
8568 void
clutter_actor_destroy(ClutterActor * self)8569 clutter_actor_destroy (ClutterActor *self)
8570 {
8571 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8572
8573 g_object_ref (self);
8574
8575 /* avoid recursion while destroying */
8576 if (!CLUTTER_ACTOR_IN_DESTRUCTION (self))
8577 {
8578 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
8579
8580 g_object_run_dispose (G_OBJECT (self));
8581
8582 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
8583 }
8584
8585 g_object_unref (self);
8586 }
8587
8588 void
_clutter_actor_finish_queue_redraw(ClutterActor * self,ClutterPaintVolume * clip)8589 _clutter_actor_finish_queue_redraw (ClutterActor *self,
8590 ClutterPaintVolume *clip)
8591 {
8592 ClutterActorPrivate *priv = self->priv;
8593 ClutterPaintVolume *pv;
8594 gboolean clipped;
8595
8596 /* Remove queue entry early in the process, otherwise a new
8597 queue_redraw() during signal handling could put back this
8598 object in the stage redraw list (but the entry is freed as
8599 soon as we return from this function, causing a segfault
8600 later)
8601 */
8602 priv->queue_redraw_entry = NULL;
8603
8604 /* If we've been explicitly passed a clip volume then there's
8605 * nothing more to calculate, but otherwise the only thing we know
8606 * is that the change is constrained to the given actor.
8607 *
8608 * The idea is that if we know the paint volume for where the actor
8609 * was last drawn (in eye coordinates) and we also have the paint
8610 * volume for where it will be drawn next (in actor coordinates)
8611 * then if we queue a redraw for both these volumes that will cover
8612 * everything that needs to be redrawn to clear the old view and
8613 * show the latest view of the actor.
8614 *
8615 * Don't clip this redraw if we don't know what position we had for
8616 * the previous redraw since we don't know where to set the clip so
8617 * it will clear the actor as it is currently.
8618 */
8619 if (clip)
8620 {
8621 _clutter_actor_set_queue_redraw_clip (self, clip);
8622 clipped = TRUE;
8623 }
8624 else if (G_LIKELY (priv->last_paint_volume_valid))
8625 {
8626 pv = _clutter_actor_get_paint_volume_mutable (self);
8627 if (pv)
8628 {
8629 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
8630
8631 /* make sure we redraw the actors old position... */
8632 _clutter_actor_set_queue_redraw_clip (stage,
8633 &priv->last_paint_volume);
8634 _clutter_actor_signal_queue_redraw (stage, stage);
8635 _clutter_actor_set_queue_redraw_clip (stage, NULL);
8636
8637 /* XXX: Ideally the redraw signal would take a clip volume
8638 * argument, but that would be an ABI break. Until we can
8639 * break the ABI we pass the argument out-of-band
8640 */
8641
8642 /* setup the clip for the actors new position... */
8643 _clutter_actor_set_queue_redraw_clip (self, pv);
8644 clipped = TRUE;
8645 }
8646 else
8647 clipped = FALSE;
8648 }
8649 else
8650 clipped = FALSE;
8651
8652 _clutter_actor_signal_queue_redraw (self, self);
8653
8654 /* Just in case anyone is manually firing redraw signals without
8655 * using the public queue_redraw() API we are careful to ensure that
8656 * our out-of-band clip member is cleared before returning...
8657 *
8658 * Note: A NULL clip denotes a full-stage, un-clipped redraw
8659 */
8660 if (G_LIKELY (clipped))
8661 _clutter_actor_set_queue_redraw_clip (self, NULL);
8662 }
8663
8664 static void
_clutter_actor_get_allocation_clip(ClutterActor * self,ClutterActorBox * clip)8665 _clutter_actor_get_allocation_clip (ClutterActor *self,
8666 ClutterActorBox *clip)
8667 {
8668 ClutterActorBox allocation;
8669
8670 /* XXX: we don't care if we get an out of date allocation here
8671 * because clutter_actor_queue_redraw_with_clip knows to ignore
8672 * the clip if the actor's allocation is invalid.
8673 *
8674 * This is noted because clutter_actor_get_allocation_box does some
8675 * unnecessary work to support buggy code with a comment suggesting
8676 * that it could be changed later which would be good for this use
8677 * case!
8678 */
8679 clutter_actor_get_allocation_box (self, &allocation);
8680
8681 /* NB: clutter_actor_queue_redraw_with_clip expects a box in the
8682 * actor's own coordinate space but the allocation is in parent
8683 * coordinates */
8684 clip->x1 = 0;
8685 clip->y1 = 0;
8686 clip->x2 = allocation.x2 - allocation.x1;
8687 clip->y2 = allocation.y2 - allocation.y1;
8688 }
8689
8690 void
_clutter_actor_queue_redraw_full(ClutterActor * self,ClutterRedrawFlags flags,ClutterPaintVolume * volume,ClutterEffect * effect)8691 _clutter_actor_queue_redraw_full (ClutterActor *self,
8692 ClutterRedrawFlags flags,
8693 ClutterPaintVolume *volume,
8694 ClutterEffect *effect)
8695 {
8696 ClutterActorPrivate *priv = self->priv;
8697 ClutterPaintVolume allocation_pv;
8698 ClutterPaintVolume *pv;
8699 gboolean should_free_pv;
8700 ClutterActor *stage;
8701
8702 /* Here's an outline of the actor queue redraw mechanism:
8703 *
8704 * The process starts in one of the following two functions which
8705 * are wrappers for this function:
8706 *
8707 * clutter_actor_queue_redraw()
8708 * _clutter_actor_queue_redraw_with_clip()
8709 *
8710 * additionally, an effect can queue a redraw by wrapping this
8711 * function in clutter_effect_queue_repaint().
8712 *
8713 * This functions queues an entry in a list associated with the
8714 * stage which is a list of actors that queued a redraw while
8715 * updating the timelines, performing layouting and processing other
8716 * mainloop sources before the next paint starts.
8717 *
8718 * We aim to minimize the processing done at this point because
8719 * there is a good chance other events will happen while updating
8720 * the scenegraph that would invalidate any expensive work we might
8721 * otherwise try to do here. For example we don't try and resolve
8722 * the screen space bounding box of an actor at this stage so as to
8723 * minimize how much of the screen redraw because it's possible
8724 * something else will happen which will force a full redraw anyway.
8725 *
8726 * When all updates are complete and we come to paint the stage then
8727 * we iterate this list and actually emit the "queue-redraw" signals
8728 * for each of the listed actors which will bubble up to the stage
8729 * for each actor and at that point we will transform the actors
8730 * paint volume into screen coordinates to determine the clip region
8731 * for what needs to be redrawn in the next paint.
8732 *
8733 * Besides minimizing redundant work another reason for this
8734 * deferred design is that it's more likely we will be able to
8735 * determine the paint volume of an actor once we've finished
8736 * updating the scenegraph because its allocation should be up to
8737 * date. NB: If we can't determine an actors paint volume then we
8738 * can't automatically queue a clipped redraw which can make a big
8739 * difference to performance.
8740 *
8741 * So the control flow goes like this:
8742 * One of clutter_actor_queue_redraw(),
8743 * _clutter_actor_queue_redraw_with_clip(),
8744 * or clutter_effect_queue_repaint()
8745 *
8746 * then control moves to:
8747 * _clutter_stage_queue_actor_redraw()
8748 *
8749 * later during _clutter_stage_do_update(), once relayouting is done
8750 * and the scenegraph has been updated we will call:
8751 * _clutter_stage_finish_queue_redraws().
8752 *
8753 * _clutter_stage_finish_queue_redraws() will call
8754 * _clutter_actor_finish_queue_redraw() for each listed actor.
8755 *
8756 * Note: actors *are* allowed to queue further redraws during this
8757 * process (considering clone actors or texture_new_from_actor which
8758 * respond to their source queueing a redraw by queuing a redraw
8759 * themselves). We repeat the process until the list is empty.
8760 *
8761 * This will result in the "queue-redraw" signal being fired for
8762 * each actor which will pass control to the default signal handler:
8763 * clutter_actor_real_queue_redraw()
8764 *
8765 * This will bubble up to the stages handler:
8766 * clutter_stage_real_queue_redraw()
8767 *
8768 * clutter_stage_real_queue_redraw() will transform the actors paint
8769 * volume into screen space and add it as a clip region for the next
8770 * paint.
8771 */
8772
8773 /* ignore queueing a redraw for actors being destroyed */
8774 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8775 return;
8776
8777 /* we can ignore unmapped actors, unless they have at least one
8778 * mapped clone or they are inside a cloned branch of the scene
8779 * graph, as unmapped actors will simply be left unpainted.
8780 *
8781 * this allows us to ignore redraws queued on leaf nodes when one
8782 * of their parents has been hidden
8783 */
8784 if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
8785 self->priv->in_cloned_branch == 0 &&
8786 !clutter_actor_has_mapped_clones (self))
8787 {
8788 CLUTTER_NOTE (PAINT,
8789 "Skipping queue_redraw('%s'): mapped=%s, "
8790 "mapped_clones=%s, "
8791 "in_cloned_branch=%s",
8792 _clutter_actor_get_debug_name (self),
8793 CLUTTER_ACTOR_IS_MAPPED (self) ? "yes" : "no",
8794 clutter_actor_has_mapped_clones (self) ? "yes" : "no",
8795 self->priv->in_cloned_branch != 0 ? "yes" : "no");
8796 return;
8797 }
8798
8799 /* given the check above we could end up queueing a redraw on an
8800 * unmapped actor with mapped clones, so we cannot assume that
8801 * get_stage() will return a Stage
8802 */
8803 stage = _clutter_actor_get_stage_internal (self);
8804 if (stage == NULL)
8805 return;
8806
8807 /* ignore queueing a redraw on stages that are being destroyed */
8808 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
8809 return;
8810
8811 if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION)
8812 {
8813 ClutterActorBox allocation_clip;
8814 ClutterVertex origin;
8815
8816 /* If the actor doesn't have a valid allocation then we will
8817 * queue a full stage redraw. */
8818 if (priv->needs_allocation)
8819 {
8820 /* NB: NULL denotes an undefined clip which will result in a
8821 * full redraw... */
8822 _clutter_actor_set_queue_redraw_clip (self, NULL);
8823 _clutter_actor_signal_queue_redraw (self, self);
8824 return;
8825 }
8826
8827 _clutter_paint_volume_init_static (&allocation_pv, self);
8828 pv = &allocation_pv;
8829
8830 _clutter_actor_get_allocation_clip (self, &allocation_clip);
8831
8832 origin.x = allocation_clip.x1;
8833 origin.y = allocation_clip.y1;
8834 origin.z = 0;
8835 clutter_paint_volume_set_origin (pv, &origin);
8836 clutter_paint_volume_set_width (pv,
8837 allocation_clip.x2 - allocation_clip.x1);
8838 clutter_paint_volume_set_height (pv,
8839 allocation_clip.y2 -
8840 allocation_clip.y1);
8841 should_free_pv = TRUE;
8842 }
8843 else
8844 {
8845 pv = volume;
8846 should_free_pv = FALSE;
8847 }
8848
8849 self->priv->queue_redraw_entry =
8850 _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
8851 priv->queue_redraw_entry,
8852 self,
8853 pv);
8854
8855 if (should_free_pv)
8856 clutter_paint_volume_free (pv);
8857
8858 /* If this is the first redraw queued then we can directly use the
8859 effect parameter */
8860 if (!priv->is_dirty)
8861 priv->effect_to_redraw = effect;
8862 /* Otherwise we need to merge it with the existing effect parameter */
8863 else if (effect != NULL)
8864 {
8865 /* If there's already an effect then we need to use whichever is
8866 later in the chain of actors. Otherwise a full redraw has
8867 already been queued on the actor so we need to ignore the
8868 effect parameter */
8869 if (priv->effect_to_redraw != NULL)
8870 {
8871 if (priv->effects == NULL)
8872 g_warning ("Redraw queued with an effect that is "
8873 "not applied to the actor");
8874 else
8875 {
8876 const GList *l;
8877
8878 for (l = _clutter_meta_group_peek_metas (priv->effects);
8879 l != NULL;
8880 l = l->next)
8881 {
8882 if (l->data == priv->effect_to_redraw ||
8883 l->data == effect)
8884 priv->effect_to_redraw = l->data;
8885 }
8886 }
8887 }
8888 }
8889 else
8890 {
8891 /* If no effect is specified then we need to redraw the whole
8892 actor */
8893 priv->effect_to_redraw = NULL;
8894 }
8895
8896 priv->is_dirty = TRUE;
8897 }
8898
8899 /**
8900 * clutter_actor_queue_redraw:
8901 * @self: A #ClutterActor
8902 *
8903 * Queues up a redraw of an actor and any children. The redraw occurs
8904 * once the main loop becomes idle (after the current batch of events
8905 * has been processed, roughly).
8906 *
8907 * Applications rarely need to call this, as redraws are handled
8908 * automatically by modification functions.
8909 *
8910 * This function will not do anything if @self is not visible, or
8911 * if the actor is inside an invisible part of the scenegraph.
8912 *
8913 * Also be aware that painting is a NOP for actors with an opacity of
8914 * 0
8915 *
8916 * When you are implementing a custom actor you must queue a redraw
8917 * whenever some private state changes that will affect painting or
8918 * picking of your actor.
8919 */
8920 void
clutter_actor_queue_redraw(ClutterActor * self)8921 clutter_actor_queue_redraw (ClutterActor *self)
8922 {
8923 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8924
8925 _clutter_actor_queue_redraw_full (self,
8926 0, /* flags */
8927 NULL, /* clip volume */
8928 NULL /* effect */);
8929 }
8930
8931 /*< private >
8932 * _clutter_actor_queue_redraw_with_clip:
8933 * @self: A #ClutterActor
8934 * @flags: A mask of #ClutterRedrawFlags controlling the behaviour of
8935 * this queue redraw.
8936 * @volume: A #ClutterPaintVolume describing the bounds of what needs to be
8937 * redrawn or %NULL if you are just using a @flag to state your
8938 * desired clipping.
8939 *
8940 * Queues up a clipped redraw of an actor and any children. The redraw
8941 * occurs once the main loop becomes idle (after the current batch of
8942 * events has been processed, roughly).
8943 *
8944 * If no flags are given the clip volume is defined by @volume
8945 * specified in actor coordinates and tells Clutter that only content
8946 * within this volume has been changed so Clutter can optionally
8947 * optimize the redraw.
8948 *
8949 * If the %CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION @flag is used, @volume
8950 * should be %NULL and this tells Clutter to use the actor's current
8951 * allocation as a clip box. This flag can only be used for 2D actors,
8952 * because any actor with depth may be projected outside its
8953 * allocation.
8954 *
8955 * Applications rarely need to call this, as redraws are handled
8956 * automatically by modification functions.
8957 *
8958 * This function will not do anything if @self is not visible, or if
8959 * the actor is inside an invisible part of the scenegraph.
8960 *
8961 * Also be aware that painting is a NOP for actors with an opacity of
8962 * 0
8963 *
8964 * When you are implementing a custom actor you must queue a redraw
8965 * whenever some private state changes that will affect painting or
8966 * picking of your actor.
8967 */
8968 void
_clutter_actor_queue_redraw_with_clip(ClutterActor * self,ClutterRedrawFlags flags,ClutterPaintVolume * volume)8969 _clutter_actor_queue_redraw_with_clip (ClutterActor *self,
8970 ClutterRedrawFlags flags,
8971 ClutterPaintVolume *volume)
8972 {
8973 _clutter_actor_queue_redraw_full (self,
8974 flags, /* flags */
8975 volume, /* clip volume */
8976 NULL /* effect */);
8977 }
8978
8979 void
_clutter_actor_queue_only_relayout(ClutterActor * self)8980 _clutter_actor_queue_only_relayout (ClutterActor *self)
8981 {
8982 ClutterActorPrivate *priv = self->priv;
8983
8984 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8985 return;
8986
8987 if (priv->needs_width_request &&
8988 priv->needs_height_request &&
8989 priv->needs_allocation)
8990 return; /* save some cpu cycles */
8991
8992 #if CLUTTER_ENABLE_DEBUG
8993 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) && CLUTTER_ACTOR_IN_RELAYOUT (self))
8994 {
8995 g_warning ("The actor '%s' is currently inside an allocation "
8996 "cycle; calling clutter_actor_queue_relayout() is "
8997 "not recommended",
8998 _clutter_actor_get_debug_name (self));
8999 }
9000 #endif /* CLUTTER_ENABLE_DEBUG */
9001
9002 _clutter_actor_queue_relayout_on_clones (self);
9003
9004 g_signal_emit (self, actor_signals[QUEUE_RELAYOUT], 0);
9005 }
9006
9007 /**
9008 * clutter_actor_queue_redraw_with_clip:
9009 * @self: a #ClutterActor
9010 * @clip: (allow-none): a rectangular clip region, or %NULL
9011 *
9012 * Queues a redraw on @self limited to a specific, actor-relative
9013 * rectangular area.
9014 *
9015 * If @clip is %NULL this function is equivalent to
9016 * clutter_actor_queue_redraw().
9017 *
9018 * Since: 1.10
9019 */
9020 void
clutter_actor_queue_redraw_with_clip(ClutterActor * self,const cairo_rectangle_int_t * clip)9021 clutter_actor_queue_redraw_with_clip (ClutterActor *self,
9022 const cairo_rectangle_int_t *clip)
9023 {
9024 ClutterPaintVolume volume;
9025 ClutterVertex origin;
9026
9027 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9028
9029 if (clip == NULL)
9030 {
9031 clutter_actor_queue_redraw (self);
9032 return;
9033 }
9034
9035 _clutter_paint_volume_init_static (&volume, self);
9036
9037 origin.x = clip->x;
9038 origin.y = clip->y;
9039 origin.z = 0.0f;
9040
9041 clutter_paint_volume_set_origin (&volume, &origin);
9042 clutter_paint_volume_set_width (&volume, clip->width);
9043 clutter_paint_volume_set_height (&volume, clip->height);
9044
9045 _clutter_actor_queue_redraw_full (self, 0, &volume, NULL);
9046
9047 clutter_paint_volume_free (&volume);
9048 }
9049
9050 /**
9051 * clutter_actor_queue_relayout:
9052 * @self: A #ClutterActor
9053 *
9054 * Indicates that the actor's size request or other layout-affecting
9055 * properties may have changed. This function is used inside #ClutterActor
9056 * subclass implementations, not by applications directly.
9057 *
9058 * Queueing a new layout automatically queues a redraw as well.
9059 *
9060 * Since: 0.8
9061 */
9062 void
clutter_actor_queue_relayout(ClutterActor * self)9063 clutter_actor_queue_relayout (ClutterActor *self)
9064 {
9065 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9066
9067 _clutter_actor_queue_only_relayout (self);
9068 clutter_actor_queue_redraw (self);
9069 }
9070
9071 /**
9072 * clutter_actor_get_preferred_size:
9073 * @self: a #ClutterActor
9074 * @min_width_p: (out) (allow-none): return location for the minimum
9075 * width, or %NULL
9076 * @min_height_p: (out) (allow-none): return location for the minimum
9077 * height, or %NULL
9078 * @natural_width_p: (out) (allow-none): return location for the natural
9079 * width, or %NULL
9080 * @natural_height_p: (out) (allow-none): return location for the natural
9081 * height, or %NULL
9082 *
9083 * Computes the preferred minimum and natural size of an actor, taking into
9084 * account the actor's geometry management (either height-for-width
9085 * or width-for-height).
9086 *
9087 * The width and height used to compute the preferred height and preferred
9088 * width are the actor's natural ones.
9089 *
9090 * If you need to control the height for the preferred width, or the width for
9091 * the preferred height, you should use clutter_actor_get_preferred_width()
9092 * and clutter_actor_get_preferred_height(), and check the actor's preferred
9093 * geometry management using the #ClutterActor:request-mode property.
9094 *
9095 * Since: 0.8
9096 */
9097 void
clutter_actor_get_preferred_size(ClutterActor * self,gfloat * min_width_p,gfloat * min_height_p,gfloat * natural_width_p,gfloat * natural_height_p)9098 clutter_actor_get_preferred_size (ClutterActor *self,
9099 gfloat *min_width_p,
9100 gfloat *min_height_p,
9101 gfloat *natural_width_p,
9102 gfloat *natural_height_p)
9103 {
9104 ClutterActorPrivate *priv;
9105 gfloat min_width, min_height;
9106 gfloat natural_width, natural_height;
9107
9108 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9109
9110 priv = self->priv;
9111
9112 min_width = min_height = 0;
9113 natural_width = natural_height = 0;
9114
9115 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
9116 {
9117 CLUTTER_NOTE (LAYOUT, "Preferred size (height-for-width)");
9118 clutter_actor_get_preferred_width (self, -1,
9119 &min_width,
9120 &natural_width);
9121 clutter_actor_get_preferred_height (self, natural_width,
9122 &min_height,
9123 &natural_height);
9124 }
9125 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
9126 {
9127 CLUTTER_NOTE (LAYOUT, "Preferred size (width-for-height)");
9128 clutter_actor_get_preferred_height (self, -1,
9129 &min_height,
9130 &natural_height);
9131 clutter_actor_get_preferred_width (self, natural_height,
9132 &min_width,
9133 &natural_width);
9134 }
9135 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
9136 {
9137 CLUTTER_NOTE (LAYOUT, "Preferred size (content-size)");
9138
9139 if (priv->content != NULL)
9140 clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
9141 }
9142 else
9143 {
9144 CLUTTER_NOTE (LAYOUT, "Unknown request mode");
9145 }
9146
9147 if (min_width_p)
9148 *min_width_p = min_width;
9149
9150 if (min_height_p)
9151 *min_height_p = min_height;
9152
9153 if (natural_width_p)
9154 *natural_width_p = natural_width;
9155
9156 if (natural_height_p)
9157 *natural_height_p = natural_height;
9158 }
9159
9160 /*< private >
9161 * effective_align:
9162 * @align: a #ClutterActorAlign
9163 * @direction: a #ClutterTextDirection
9164 *
9165 * Retrieves the correct alignment depending on the text direction
9166 *
9167 * Return value: the effective alignment
9168 */
9169 static ClutterActorAlign
effective_align(ClutterActorAlign align,ClutterTextDirection direction)9170 effective_align (ClutterActorAlign align,
9171 ClutterTextDirection direction)
9172 {
9173 ClutterActorAlign res;
9174
9175 switch (align)
9176 {
9177 case CLUTTER_ACTOR_ALIGN_START:
9178 res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
9179 ? CLUTTER_ACTOR_ALIGN_END
9180 : CLUTTER_ACTOR_ALIGN_START;
9181 break;
9182
9183 case CLUTTER_ACTOR_ALIGN_END:
9184 res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
9185 ? CLUTTER_ACTOR_ALIGN_START
9186 : CLUTTER_ACTOR_ALIGN_END;
9187 break;
9188
9189 default:
9190 res = align;
9191 break;
9192 }
9193
9194 return res;
9195 }
9196
9197 /*< private >
9198 * _clutter_actor_get_effective_x_align:
9199 * @self: a #ClutterActor
9200 *
9201 * Retrieves the effective horizontal alignment, taking into
9202 * consideration the text direction of @self.
9203 *
9204 * Return value: the effective horizontal alignment
9205 */
9206 ClutterActorAlign
_clutter_actor_get_effective_x_align(ClutterActor * self)9207 _clutter_actor_get_effective_x_align (ClutterActor *self)
9208 {
9209 return effective_align (clutter_actor_get_x_align (self),
9210 clutter_actor_get_text_direction (self));
9211 }
9212
9213 static inline void
adjust_for_margin(float margin_start,float margin_end,float * minimum_size,float * natural_size,float * allocated_start,float * allocated_end)9214 adjust_for_margin (float margin_start,
9215 float margin_end,
9216 float *minimum_size,
9217 float *natural_size,
9218 float *allocated_start,
9219 float *allocated_end)
9220 {
9221 float min_size = *minimum_size;
9222 float nat_size = *natural_size;
9223 float start = *allocated_start;
9224 float end = *allocated_end;
9225
9226 min_size = MAX (min_size - (margin_start + margin_end), 0);
9227 nat_size = MAX (nat_size - (margin_start + margin_end), 0);
9228
9229 *minimum_size = min_size;
9230 *natural_size = nat_size;
9231
9232 start += margin_start;
9233 end -= margin_end;
9234
9235 if (end - start >= 0)
9236 {
9237 *allocated_start = start;
9238 *allocated_end = end;
9239 }
9240 }
9241
9242 static inline void
adjust_for_alignment(ClutterActorAlign alignment,float natural_size,float * allocated_start,float * allocated_end)9243 adjust_for_alignment (ClutterActorAlign alignment,
9244 float natural_size,
9245 float *allocated_start,
9246 float *allocated_end)
9247 {
9248 float allocated_size = *allocated_end - *allocated_start;
9249
9250 if (allocated_size <= 0.f)
9251 return;
9252
9253 switch (alignment)
9254 {
9255 case CLUTTER_ACTOR_ALIGN_FILL:
9256 /* do nothing */
9257 break;
9258
9259 case CLUTTER_ACTOR_ALIGN_START:
9260 /* keep start */
9261 *allocated_end = *allocated_start + MIN (natural_size, allocated_size);
9262 break;
9263
9264 case CLUTTER_ACTOR_ALIGN_END:
9265 if (allocated_size > natural_size)
9266 {
9267 *allocated_start += (allocated_size - natural_size);
9268 *allocated_end = *allocated_start + natural_size;
9269 }
9270 break;
9271
9272 case CLUTTER_ACTOR_ALIGN_CENTER:
9273 if (allocated_size > natural_size)
9274 {
9275 *allocated_start += floorf ((allocated_size - natural_size) / 2);
9276 *allocated_end = *allocated_start + MIN (allocated_size, natural_size);
9277 }
9278 break;
9279 }
9280 }
9281
9282 /*< private >
9283 * clutter_actor_adjust_width:
9284 * @self: a #ClutterActor
9285 * @minimum_width: (inout): the actor's preferred minimum width, which
9286 * will be adjusted depending on the margin
9287 * @natural_width: (inout): the actor's preferred natural width, which
9288 * will be adjusted depending on the margin
9289 * @adjusted_x1: (out): the adjusted x1 for the actor's bounding box
9290 * @adjusted_x2: (out): the adjusted x2 for the actor's bounding box
9291 *
9292 * Adjusts the preferred and allocated position and size of an actor,
9293 * depending on the margin and alignment properties.
9294 */
9295 static void
clutter_actor_adjust_width(ClutterActor * self,gfloat * minimum_width,gfloat * natural_width,gfloat * adjusted_x1,gfloat * adjusted_x2)9296 clutter_actor_adjust_width (ClutterActor *self,
9297 gfloat *minimum_width,
9298 gfloat *natural_width,
9299 gfloat *adjusted_x1,
9300 gfloat *adjusted_x2)
9301 {
9302 ClutterTextDirection text_dir;
9303 const ClutterLayoutInfo *info;
9304
9305 info = _clutter_actor_get_layout_info_or_defaults (self);
9306 text_dir = clutter_actor_get_text_direction (self);
9307
9308 CLUTTER_NOTE (LAYOUT, "Adjusting allocated X and width");
9309
9310 /* this will tweak natural_width to remove the margin, so that
9311 * adjust_for_alignment() will use the correct size
9312 */
9313 adjust_for_margin (info->margin.left, info->margin.right,
9314 minimum_width, natural_width,
9315 adjusted_x1, adjusted_x2);
9316
9317 adjust_for_alignment (effective_align (info->x_align, text_dir),
9318 *natural_width,
9319 adjusted_x1, adjusted_x2);
9320 }
9321
9322 /*< private >
9323 * clutter_actor_adjust_height:
9324 * @self: a #ClutterActor
9325 * @minimum_height: (inout): the actor's preferred minimum height, which
9326 * will be adjusted depending on the margin
9327 * @natural_height: (inout): the actor's preferred natural height, which
9328 * will be adjusted depending on the margin
9329 * @adjusted_y1: (out): the adjusted y1 for the actor's bounding box
9330 * @adjusted_y2: (out): the adjusted y2 for the actor's bounding box
9331 *
9332 * Adjusts the preferred and allocated position and size of an actor,
9333 * depending on the margin and alignment properties.
9334 */
9335 static void
clutter_actor_adjust_height(ClutterActor * self,gfloat * minimum_height,gfloat * natural_height,gfloat * adjusted_y1,gfloat * adjusted_y2)9336 clutter_actor_adjust_height (ClutterActor *self,
9337 gfloat *minimum_height,
9338 gfloat *natural_height,
9339 gfloat *adjusted_y1,
9340 gfloat *adjusted_y2)
9341 {
9342 const ClutterLayoutInfo *info;
9343
9344 info = _clutter_actor_get_layout_info_or_defaults (self);
9345
9346 CLUTTER_NOTE (LAYOUT, "Adjusting allocated Y and height");
9347
9348 /* this will tweak natural_height to remove the margin, so that
9349 * adjust_for_alignment() will use the correct size
9350 */
9351 adjust_for_margin (info->margin.top, info->margin.bottom,
9352 minimum_height, natural_height,
9353 adjusted_y1,
9354 adjusted_y2);
9355
9356 /* we don't use effective_align() here, because text direction
9357 * only affects the horizontal axis
9358 */
9359 adjust_for_alignment (info->y_align,
9360 *natural_height,
9361 adjusted_y1,
9362 adjusted_y2);
9363
9364 }
9365
9366 /* looks for a cached size request for this for_size. If not
9367 * found, returns the oldest entry so it can be overwritten */
9368 static gboolean
_clutter_actor_get_cached_size_request(gfloat for_size,SizeRequest * cached_size_requests,SizeRequest ** result)9369 _clutter_actor_get_cached_size_request (gfloat for_size,
9370 SizeRequest *cached_size_requests,
9371 SizeRequest **result)
9372 {
9373 guint i;
9374
9375 *result = &cached_size_requests[0];
9376
9377 for (i = 0; i < N_CACHED_SIZE_REQUESTS; i++)
9378 {
9379 SizeRequest *sr;
9380
9381 sr = &cached_size_requests[i];
9382
9383 if (sr->age > 0 &&
9384 sr->for_size == for_size)
9385 {
9386 CLUTTER_NOTE (LAYOUT, "Size cache hit for size: %.2f", for_size);
9387 *result = sr;
9388 return TRUE;
9389 }
9390 else if (sr->age < (*result)->age)
9391 {
9392 *result = sr;
9393 }
9394 }
9395
9396 CLUTTER_NOTE (LAYOUT, "Size cache miss for size: %.2f", for_size);
9397
9398 return FALSE;
9399 }
9400
9401 static void
clutter_actor_update_preferred_size_for_constraints(ClutterActor * self,ClutterOrientation direction,float for_size,float * minimum_size,float * natural_size)9402 clutter_actor_update_preferred_size_for_constraints (ClutterActor *self,
9403 ClutterOrientation direction,
9404 float for_size,
9405 float *minimum_size,
9406 float *natural_size)
9407 {
9408 ClutterActorPrivate *priv = self->priv;
9409 const GList *constraints, *l;
9410
9411 if (priv->constraints == NULL)
9412 return;
9413
9414 constraints = _clutter_meta_group_peek_metas (priv->constraints);
9415 for (l = constraints; l != NULL; l = l->next)
9416 {
9417 ClutterConstraint *constraint = l->data;
9418 ClutterActorMeta *meta = l->data;
9419
9420 if (!clutter_actor_meta_get_enabled (meta))
9421 continue;
9422
9423 clutter_constraint_update_preferred_size (constraint, self,
9424 direction,
9425 for_size,
9426 minimum_size,
9427 natural_size);
9428
9429 CLUTTER_NOTE (LAYOUT,
9430 "Preferred %s of '%s' after constraint '%s': "
9431 "{ min:%.2f, nat:%.2f }",
9432 direction == CLUTTER_ORIENTATION_HORIZONTAL
9433 ? "width"
9434 : "height",
9435 _clutter_actor_get_debug_name (self),
9436 _clutter_actor_meta_get_debug_name (meta),
9437 *minimum_size, *natural_size);
9438 }
9439 }
9440
9441 /**
9442 * clutter_actor_get_preferred_width:
9443 * @self: A #ClutterActor
9444 * @for_height: available height when computing the preferred width,
9445 * or a negative value to indicate that no height is defined
9446 * @min_width_p: (out) (allow-none): return location for minimum width,
9447 * or %NULL
9448 * @natural_width_p: (out) (allow-none): return location for the natural
9449 * width, or %NULL
9450 *
9451 * Computes the requested minimum and natural widths for an actor,
9452 * optionally depending on the specified height, or if they are
9453 * already computed, returns the cached values.
9454 *
9455 * An actor may not get its request - depending on the layout
9456 * manager that's in effect.
9457 *
9458 * A request should not incorporate the actor's scale or anchor point;
9459 * those transformations do not affect layout, only rendering.
9460 *
9461 * Since: 0.8
9462 */
9463 void
clutter_actor_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)9464 clutter_actor_get_preferred_width (ClutterActor *self,
9465 gfloat for_height,
9466 gfloat *min_width_p,
9467 gfloat *natural_width_p)
9468 {
9469 float request_min_width, request_natural_width;
9470 SizeRequest *cached_size_request;
9471 const ClutterLayoutInfo *info;
9472 ClutterActorPrivate *priv;
9473 gboolean found_in_cache;
9474
9475 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9476
9477 priv = self->priv;
9478
9479 info = _clutter_actor_get_layout_info_or_defaults (self);
9480
9481 /* we shortcircuit the case of a fixed size set using set_width() */
9482 if (priv->min_width_set && priv->natural_width_set)
9483 {
9484 if (min_width_p != NULL)
9485 *min_width_p = info->minimum.width + (info->margin.left + info->margin.right);
9486
9487 if (natural_width_p != NULL)
9488 *natural_width_p = info->natural.width + (info->margin.left + info->margin.right);
9489
9490 return;
9491 }
9492
9493 /* the remaining cases are:
9494 *
9495 * - either min_width or natural_width have been set
9496 * - neither min_width or natural_width have been set
9497 *
9498 * in both cases, we go through the cache (and through the actor in case
9499 * of cache misses) and determine the authoritative value depending on
9500 * the *_set flags.
9501 */
9502
9503 if (!priv->needs_width_request)
9504 {
9505 found_in_cache =
9506 _clutter_actor_get_cached_size_request (for_height,
9507 priv->width_requests,
9508 &cached_size_request);
9509 }
9510 else
9511 {
9512 /* if the actor needs a width request we use the first slot */
9513 found_in_cache = FALSE;
9514 cached_size_request = &priv->width_requests[0];
9515 }
9516
9517 if (!found_in_cache)
9518 {
9519 gfloat minimum_width, natural_width;
9520 ClutterActorClass *klass;
9521
9522 minimum_width = natural_width = 0;
9523
9524 /* adjust for the margin */
9525 if (for_height >= 0)
9526 {
9527 for_height -= (info->margin.top + info->margin.bottom);
9528 if (for_height < 0)
9529 for_height = 0;
9530 }
9531
9532 CLUTTER_NOTE (LAYOUT, "Width request for %.2f px", for_height);
9533
9534 klass = CLUTTER_ACTOR_GET_CLASS (self);
9535 klass->get_preferred_width (self, for_height,
9536 &minimum_width,
9537 &natural_width);
9538
9539 /* adjust for constraints */
9540 clutter_actor_update_preferred_size_for_constraints (self,
9541 CLUTTER_ORIENTATION_HORIZONTAL,
9542 for_height,
9543 &minimum_width,
9544 &natural_width);
9545
9546 /* adjust for the margin */
9547 minimum_width += (info->margin.left + info->margin.right);
9548 natural_width += (info->margin.left + info->margin.right);
9549
9550 /* Due to accumulated float errors, it's better not to warn
9551 * on this, but just fix it.
9552 */
9553 if (natural_width < minimum_width)
9554 natural_width = minimum_width;
9555
9556 cached_size_request->min_size = minimum_width;
9557 cached_size_request->natural_size = natural_width;
9558 cached_size_request->for_size = for_height;
9559 cached_size_request->age = priv->cached_width_age;
9560
9561 priv->cached_width_age += 1;
9562 priv->needs_width_request = FALSE;
9563 }
9564
9565 if (!priv->min_width_set)
9566 request_min_width = cached_size_request->min_size;
9567 else
9568 request_min_width = info->margin.left
9569 + info->minimum.width
9570 + info->margin.right;
9571
9572 if (!priv->natural_width_set)
9573 request_natural_width = cached_size_request->natural_size;
9574 else
9575 request_natural_width = info->margin.left
9576 + info->natural.width
9577 + info->margin.right;
9578
9579 if (min_width_p)
9580 *min_width_p = request_min_width;
9581
9582 if (natural_width_p)
9583 *natural_width_p = request_natural_width;
9584 }
9585
9586 /**
9587 * clutter_actor_get_preferred_height:
9588 * @self: A #ClutterActor
9589 * @for_width: available width to assume in computing desired height,
9590 * or a negative value to indicate that no width is defined
9591 * @min_height_p: (out) (allow-none): return location for minimum height,
9592 * or %NULL
9593 * @natural_height_p: (out) (allow-none): return location for natural
9594 * height, or %NULL
9595 *
9596 * Computes the requested minimum and natural heights for an actor,
9597 * or if they are already computed, returns the cached values.
9598 *
9599 * An actor may not get its request - depending on the layout
9600 * manager that's in effect.
9601 *
9602 * A request should not incorporate the actor's scale or anchor point;
9603 * those transformations do not affect layout, only rendering.
9604 *
9605 * Since: 0.8
9606 */
9607 void
clutter_actor_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)9608 clutter_actor_get_preferred_height (ClutterActor *self,
9609 gfloat for_width,
9610 gfloat *min_height_p,
9611 gfloat *natural_height_p)
9612 {
9613 float request_min_height, request_natural_height;
9614 SizeRequest *cached_size_request;
9615 const ClutterLayoutInfo *info;
9616 ClutterActorPrivate *priv;
9617 gboolean found_in_cache;
9618
9619 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9620
9621 priv = self->priv;
9622
9623 info = _clutter_actor_get_layout_info_or_defaults (self);
9624
9625 /* we shortcircuit the case of a fixed size set using set_height() */
9626 if (priv->min_height_set && priv->natural_height_set)
9627 {
9628 if (min_height_p != NULL)
9629 *min_height_p = info->minimum.height + (info->margin.top + info->margin.bottom);
9630
9631 if (natural_height_p != NULL)
9632 *natural_height_p = info->natural.height + (info->margin.top + info->margin.bottom);
9633
9634 return;
9635 }
9636
9637 /* the remaining cases are:
9638 *
9639 * - either min_height or natural_height have been set
9640 * - neither min_height or natural_height have been set
9641 *
9642 * in both cases, we go through the cache (and through the actor in case
9643 * of cache misses) and determine the authoritative value depending on
9644 * the *_set flags.
9645 */
9646
9647 if (!priv->needs_height_request)
9648 {
9649 found_in_cache =
9650 _clutter_actor_get_cached_size_request (for_width,
9651 priv->height_requests,
9652 &cached_size_request);
9653 }
9654 else
9655 {
9656 found_in_cache = FALSE;
9657 cached_size_request = &priv->height_requests[0];
9658 }
9659
9660 if (!found_in_cache)
9661 {
9662 gfloat minimum_height, natural_height;
9663 ClutterActorClass *klass;
9664
9665 minimum_height = natural_height = 0;
9666
9667 CLUTTER_NOTE (LAYOUT, "Height request for %.2f px", for_width);
9668
9669 /* adjust for margin */
9670 if (for_width >= 0)
9671 {
9672 for_width -= (info->margin.left + info->margin.right);
9673 if (for_width < 0)
9674 for_width = 0;
9675 }
9676
9677 klass = CLUTTER_ACTOR_GET_CLASS (self);
9678 klass->get_preferred_height (self, for_width,
9679 &minimum_height,
9680 &natural_height);
9681
9682 /* adjust for constraints */
9683 clutter_actor_update_preferred_size_for_constraints (self,
9684 CLUTTER_ORIENTATION_VERTICAL,
9685 for_width,
9686 &minimum_height,
9687 &natural_height);
9688
9689 /* adjust for margin */
9690 minimum_height += (info->margin.top + info->margin.bottom);
9691 natural_height += (info->margin.top + info->margin.bottom);
9692
9693 /* Due to accumulated float errors, it's better not to warn
9694 * on this, but just fix it.
9695 */
9696 if (natural_height < minimum_height)
9697 natural_height = minimum_height;
9698
9699 cached_size_request->min_size = minimum_height;
9700 cached_size_request->natural_size = natural_height;
9701 cached_size_request->for_size = for_width;
9702 cached_size_request->age = priv->cached_height_age;
9703
9704 priv->cached_height_age += 1;
9705 priv->needs_height_request = FALSE;
9706 }
9707
9708 if (!priv->min_height_set)
9709 request_min_height = cached_size_request->min_size;
9710 else
9711 request_min_height = info->margin.top
9712 + info->minimum.height
9713 + info->margin.bottom;
9714
9715 if (!priv->natural_height_set)
9716 request_natural_height = cached_size_request->natural_size;
9717 else
9718 request_natural_height = info->margin.top
9719 + info->natural.height
9720 + info->margin.bottom;
9721
9722 if (min_height_p)
9723 *min_height_p = request_min_height;
9724
9725 if (natural_height_p)
9726 *natural_height_p = request_natural_height;
9727 }
9728
9729 /**
9730 * clutter_actor_get_allocation_box:
9731 * @self: A #ClutterActor
9732 * @box: (out): the function fills this in with the actor's allocation
9733 *
9734 * Gets the layout box an actor has been assigned. The allocation can
9735 * only be assumed valid inside a paint() method; anywhere else, it
9736 * may be out-of-date.
9737 *
9738 * An allocation does not incorporate the actor's scale or anchor point;
9739 * those transformations do not affect layout, only rendering.
9740 *
9741 * Do not call any of the clutter_actor_get_allocation_*() family
9742 * of functions inside the implementation of the get_preferred_width()
9743 * or get_preferred_height() virtual functions.
9744 *
9745 * Since: 0.8
9746 */
9747 void
clutter_actor_get_allocation_box(ClutterActor * self,ClutterActorBox * box)9748 clutter_actor_get_allocation_box (ClutterActor *self,
9749 ClutterActorBox *box)
9750 {
9751 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9752
9753 /* XXX - if needs_allocation=TRUE, we can either 1) g_return_if_fail,
9754 * which limits calling get_allocation to inside paint() basically; or
9755 * we can 2) force a layout, which could be expensive if someone calls
9756 * get_allocation somewhere silly; or we can 3) just return the latest
9757 * value, allowing it to be out-of-date, and assume people know what
9758 * they are doing.
9759 *
9760 * The least-surprises approach that keeps existing code working is
9761 * likely to be 2). People can end up doing some inefficient things,
9762 * though, and in general code that requires 2) is probably broken.
9763 */
9764
9765 /* this implements 2) */
9766 if (G_UNLIKELY (self->priv->needs_allocation))
9767 {
9768 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
9769
9770 /* do not queue a relayout on an unparented actor */
9771 if (stage)
9772 _clutter_stage_maybe_relayout (stage);
9773 }
9774
9775 /* commenting out the code above and just keeping this assigment
9776 * implements 3)
9777 */
9778 *box = self->priv->allocation;
9779 }
9780
9781 static void
clutter_actor_update_constraints(ClutterActor * self,ClutterActorBox * allocation)9782 clutter_actor_update_constraints (ClutterActor *self,
9783 ClutterActorBox *allocation)
9784 {
9785 ClutterActorPrivate *priv = self->priv;
9786 const GList *constraints, *l;
9787
9788 if (priv->constraints == NULL)
9789 return;
9790
9791 constraints = _clutter_meta_group_peek_metas (priv->constraints);
9792 for (l = constraints; l != NULL; l = l->next)
9793 {
9794 ClutterConstraint *constraint = l->data;
9795 ClutterActorMeta *meta = l->data;
9796 gboolean changed = FALSE;
9797
9798 if (clutter_actor_meta_get_enabled (meta))
9799 {
9800 changed |=
9801 clutter_constraint_update_allocation (constraint,
9802 self,
9803 allocation);
9804
9805 CLUTTER_NOTE (LAYOUT,
9806 "Allocation of '%s' after constraint '%s': "
9807 "{ %.2f, %.2f, %.2f, %.2f } (changed:%s)",
9808 _clutter_actor_get_debug_name (self),
9809 _clutter_actor_meta_get_debug_name (meta),
9810 allocation->x1,
9811 allocation->y1,
9812 allocation->x2,
9813 allocation->y2,
9814 changed ? "yes" : "no");
9815 }
9816 }
9817 }
9818
9819 /*< private >
9820 * clutter_actor_adjust_allocation:
9821 * @self: a #ClutterActor
9822 * @allocation: (inout): the allocation to adjust
9823 *
9824 * Adjusts the passed allocation box taking into account the actor's
9825 * layout information, like alignment, expansion, and margin.
9826 */
9827 static void
clutter_actor_adjust_allocation(ClutterActor * self,ClutterActorBox * allocation)9828 clutter_actor_adjust_allocation (ClutterActor *self,
9829 ClutterActorBox *allocation)
9830 {
9831 ClutterActorBox adj_allocation;
9832 float alloc_width, alloc_height;
9833 float min_width, min_height;
9834 float nat_width, nat_height;
9835 ClutterRequestMode req_mode;
9836
9837 adj_allocation = *allocation;
9838
9839 clutter_actor_box_get_size (allocation, &alloc_width, &alloc_height);
9840
9841 /* There's no point in trying to adjust a zero-sized actor */
9842 if (alloc_width == 0.f && alloc_height == 0.f)
9843 return;
9844
9845 /* we want to hit the cache, so we use the public API */
9846 req_mode = clutter_actor_get_request_mode (self);
9847
9848 if (req_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
9849 {
9850 clutter_actor_get_preferred_width (self, -1,
9851 &min_width,
9852 &nat_width);
9853 clutter_actor_get_preferred_height (self, alloc_width,
9854 &min_height,
9855 &nat_height);
9856 }
9857 else if (req_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
9858 {
9859 clutter_actor_get_preferred_height (self, -1,
9860 &min_height,
9861 &nat_height);
9862 clutter_actor_get_preferred_width (self, alloc_height,
9863 &min_width,
9864 &nat_width);
9865 }
9866 else if (req_mode == CLUTTER_REQUEST_CONTENT_SIZE)
9867 {
9868 min_width = min_height = 0;
9869 nat_width = nat_height = 0;
9870
9871 if (self->priv->content != NULL)
9872 clutter_content_get_preferred_size (self->priv->content, &nat_width, &nat_height);
9873 }
9874
9875 #ifdef CLUTTER_ENABLE_DEBUG
9876 /* warn about underallocations */
9877 if (_clutter_diagnostic_enabled () &&
9878 (floorf (min_width - alloc_width) > 0 ||
9879 floorf (min_height - alloc_height) > 0))
9880 {
9881 ClutterActor *parent = clutter_actor_get_parent (self);
9882
9883 /* the only actors that are allowed to be underallocated are the Stage,
9884 * as it doesn't have an implicit size, and Actors that specifically
9885 * told us that they want to opt-out from layout control mechanisms
9886 * through the NO_LAYOUT escape hatch.
9887 */
9888 if (parent != NULL &&
9889 !(self->flags & CLUTTER_ACTOR_NO_LAYOUT) != 0)
9890 {
9891 g_warning (G_STRLOC ": The actor '%s' is getting an allocation "
9892 "of %.2f x %.2f from its parent actor '%s', but its "
9893 "requested minimum size is of %.2f x %.2f",
9894 _clutter_actor_get_debug_name (self),
9895 alloc_width, alloc_height,
9896 _clutter_actor_get_debug_name (parent),
9897 min_width, min_height);
9898 }
9899 }
9900 #endif
9901
9902 clutter_actor_adjust_width (self,
9903 &min_width,
9904 &nat_width,
9905 &adj_allocation.x1,
9906 &adj_allocation.x2);
9907
9908 clutter_actor_adjust_height (self,
9909 &min_height,
9910 &nat_height,
9911 &adj_allocation.y1,
9912 &adj_allocation.y2);
9913
9914 /* we maintain the invariant that an allocation cannot be adjusted
9915 * to be outside the parent-given box
9916 */
9917 if (adj_allocation.x1 < allocation->x1 ||
9918 adj_allocation.y1 < allocation->y1 ||
9919 adj_allocation.x2 > allocation->x2 ||
9920 adj_allocation.y2 > allocation->y2)
9921 {
9922 g_warning (G_STRLOC ": The actor '%s' tried to adjust its allocation "
9923 "to { %.2f, %.2f, %.2f, %.2f }, which is outside of its "
9924 "original allocation of { %.2f, %.2f, %.2f, %.2f }",
9925 _clutter_actor_get_debug_name (self),
9926 adj_allocation.x1, adj_allocation.y1,
9927 adj_allocation.x2 - adj_allocation.x1,
9928 adj_allocation.y2 - adj_allocation.y1,
9929 allocation->x1, allocation->y1,
9930 allocation->x2 - allocation->x1,
9931 allocation->y2 - allocation->y1);
9932 return;
9933 }
9934
9935 *allocation = adj_allocation;
9936 }
9937
9938 static void
clutter_actor_allocate_internal(ClutterActor * self,const ClutterActorBox * allocation,ClutterAllocationFlags flags)9939 clutter_actor_allocate_internal (ClutterActor *self,
9940 const ClutterActorBox *allocation,
9941 ClutterAllocationFlags flags)
9942 {
9943 ClutterActorClass *klass;
9944
9945 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9946
9947 CLUTTER_NOTE (LAYOUT, "Calling %s::allocate()",
9948 _clutter_actor_get_debug_name (self));
9949
9950 klass = CLUTTER_ACTOR_GET_CLASS (self);
9951 klass->allocate (self, allocation, flags);
9952
9953 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9954
9955 /* Caller should call clutter_actor_queue_redraw() if needed
9956 * for that particular case.
9957 */
9958 }
9959
9960 /**
9961 * clutter_actor_allocate:
9962 * @self: A #ClutterActor
9963 * @box: new allocation of the actor, in parent-relative coordinates
9964 * @flags: flags that control the allocation
9965 *
9966 * Assigns the size of a #ClutterActor from the given @box.
9967 *
9968 * This function should only be called on the children of an actor when
9969 * overriding the #ClutterActorClass.allocate() virtual function.
9970 *
9971 * This function will adjust the stored allocation to take into account
9972 * the alignment flags set in the #ClutterActor:x-align and
9973 * #ClutterActor:y-align properties, as well as the margin values set in
9974 * the #ClutterActor:margin-top, #ClutterActor:margin-right,
9975 * #ClutterActor:margin-bottom, and #ClutterActor:margin-left properties.
9976 *
9977 * This function will respect the easing state of the #ClutterActor and
9978 * interpolate between the current allocation and the new one if the
9979 * easing state duration is a positive value.
9980 *
9981 * Actors can know from their allocation box whether they have moved
9982 * with respect to their parent actor. The @flags parameter describes
9983 * additional information about the allocation, for instance whether
9984 * the parent has moved with respect to the stage, for example because
9985 * a grandparent's origin has moved.
9986 *
9987 * Since: 0.8
9988 */
9989 void
clutter_actor_allocate(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)9990 clutter_actor_allocate (ClutterActor *self,
9991 const ClutterActorBox *box,
9992 ClutterAllocationFlags flags)
9993 {
9994 ClutterActorBox old_allocation, real_allocation;
9995 gboolean origin_changed, child_moved, size_changed;
9996 gboolean stage_allocation_changed;
9997 ClutterActorPrivate *priv;
9998
9999 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10000 if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL))
10001 {
10002 g_warning ("Spurious clutter_actor_allocate called for actor %p/%s "
10003 "which isn't a descendent of the stage!\n",
10004 self, _clutter_actor_get_debug_name (self));
10005 return;
10006 }
10007
10008 priv = self->priv;
10009
10010 old_allocation = priv->allocation;
10011 real_allocation = *box;
10012
10013 /* constraints are allowed to modify the allocation only here; we do
10014 * this prior to all the other checks so that we can bail out if the
10015 * allocation did not change
10016 */
10017 clutter_actor_update_constraints (self, &real_allocation);
10018
10019 /* adjust the allocation depending on the align/margin properties */
10020 clutter_actor_adjust_allocation (self, &real_allocation);
10021
10022 if (real_allocation.x2 < real_allocation.x1 ||
10023 real_allocation.y2 < real_allocation.y1)
10024 {
10025 g_warning (G_STRLOC ": Actor '%s' tried to allocate a size of %.2f x %.2f",
10026 _clutter_actor_get_debug_name (self),
10027 real_allocation.x2 - real_allocation.x1,
10028 real_allocation.y2 - real_allocation.y1);
10029 }
10030
10031 /* we allow 0-sized actors, but not negative-sized ones */
10032 real_allocation.x2 = MAX (real_allocation.x2, real_allocation.x1);
10033 real_allocation.y2 = MAX (real_allocation.y2, real_allocation.y1);
10034
10035 origin_changed = (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED);
10036
10037 child_moved = (real_allocation.x1 != old_allocation.x1 ||
10038 real_allocation.y1 != old_allocation.y1);
10039
10040 size_changed = (real_allocation.x2 != old_allocation.x2 ||
10041 real_allocation.y2 != old_allocation.y2);
10042
10043 if (origin_changed || child_moved || size_changed)
10044 stage_allocation_changed = TRUE;
10045 else
10046 stage_allocation_changed = FALSE;
10047
10048 /* If we get an allocation "out of the blue"
10049 * (we did not queue relayout), then we want to
10050 * ignore it. But if we have needs_allocation set,
10051 * we want to guarantee that allocate() virtual
10052 * method is always called, i.e. that queue_relayout()
10053 * always results in an allocate() invocation on
10054 * an actor.
10055 *
10056 * The optimization here is to avoid re-allocating
10057 * actors that did not queue relayout and were
10058 * not moved.
10059 */
10060 if (!priv->needs_allocation && !stage_allocation_changed)
10061 {
10062 CLUTTER_NOTE (LAYOUT, "No allocation needed");
10063 return;
10064 }
10065
10066 if (!stage_allocation_changed)
10067 {
10068 /* If the actor didn't move but needs_allocation is set, we just
10069 * need to allocate the children */
10070 clutter_actor_allocate_internal (self, &real_allocation, flags);
10071 return;
10072 }
10073
10074 /* When ABSOLUTE_ORIGIN_CHANGED is passed in to
10075 * clutter_actor_allocate(), it indicates whether the parent has its
10076 * absolute origin moved; when passed in to ClutterActor::allocate()
10077 * virtual method though, it indicates whether the child has its
10078 * absolute origin moved. So we set it when child_moved is TRUE
10079 */
10080 if (child_moved)
10081 flags |= CLUTTER_ABSOLUTE_ORIGIN_CHANGED;
10082
10083 /* store the flags here, so that they can be propagated by the
10084 * transition code
10085 */
10086 self->priv->allocation_flags = flags;
10087
10088 _clutter_actor_create_transition (self, obj_props[PROP_ALLOCATION],
10089 &priv->allocation,
10090 &real_allocation);
10091 }
10092
10093 /**
10094 * clutter_actor_set_allocation:
10095 * @self: a #ClutterActor
10096 * @box: a #ClutterActorBox
10097 * @flags: allocation flags
10098 *
10099 * Stores the allocation of @self as defined by @box.
10100 *
10101 * This function can only be called from within the implementation of
10102 * the #ClutterActorClass.allocate() virtual function.
10103 *
10104 * The allocation should have been adjusted to take into account constraints,
10105 * alignment, and margin properties. If you are implementing a #ClutterActor
10106 * subclass that provides its own layout management policy for its children
10107 * instead of using a #ClutterLayoutManager delegate, you should not call
10108 * this function on the children of @self; instead, you should call
10109 * clutter_actor_allocate(), which will adjust the allocation box for
10110 * you.
10111 *
10112 * This function should only be used by subclasses of #ClutterActor
10113 * that wish to store their allocation but cannot chain up to the
10114 * parent's implementation; the default implementation of the
10115 * #ClutterActorClass.allocate() virtual function will call this
10116 * function.
10117 *
10118 * It is important to note that, while chaining up was the recommended
10119 * behaviour for #ClutterActor subclasses prior to the introduction of
10120 * this function, it is recommended to call clutter_actor_set_allocation()
10121 * instead.
10122 *
10123 * If the #ClutterActor is using a #ClutterLayoutManager delegate object
10124 * to handle the allocation of its children, this function will call
10125 * the clutter_layout_manager_allocate() function only if the
10126 * %CLUTTER_DELEGATE_LAYOUT flag is set on @flags, otherwise it is
10127 * expected that the subclass will call clutter_layout_manager_allocate()
10128 * by itself. For instance, the following code:
10129 *
10130 * |[<!-- language="C" -->
10131 * static void
10132 * my_actor_allocate (ClutterActor *actor,
10133 * const ClutterActorBox *allocation,
10134 * ClutterAllocationFlags flags)
10135 * {
10136 * ClutterActorBox new_alloc;
10137 * ClutterAllocationFlags new_flags;
10138 *
10139 * adjust_allocation (allocation, &new_alloc);
10140 *
10141 * new_flags = flags | CLUTTER_DELEGATE_LAYOUT;
10142 *
10143 * // this will use the layout manager set on the actor
10144 * clutter_actor_set_allocation (actor, &new_alloc, new_flags);
10145 * }
10146 * ]|
10147 *
10148 * is equivalent to this:
10149 *
10150 * |[<!-- language="C" -->
10151 * static void
10152 * my_actor_allocate (ClutterActor *actor,
10153 * const ClutterActorBox *allocation,
10154 * ClutterAllocationFlags flags)
10155 * {
10156 * ClutterLayoutManager *layout;
10157 * ClutterActorBox new_alloc;
10158 *
10159 * adjust_allocation (allocation, &new_alloc);
10160 *
10161 * clutter_actor_set_allocation (actor, &new_alloc, flags);
10162 *
10163 * layout = clutter_actor_get_layout_manager (actor);
10164 * clutter_layout_manager_allocate (layout,
10165 * CLUTTER_CONTAINER (actor),
10166 * &new_alloc,
10167 * flags);
10168 * }
10169 * ]|
10170 *
10171 * Since: 1.10
10172 */
10173 void
clutter_actor_set_allocation(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)10174 clutter_actor_set_allocation (ClutterActor *self,
10175 const ClutterActorBox *box,
10176 ClutterAllocationFlags flags)
10177 {
10178 ClutterActorPrivate *priv;
10179 gboolean changed;
10180
10181 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10182 g_return_if_fail (box != NULL);
10183
10184 if (G_UNLIKELY (!CLUTTER_ACTOR_IN_RELAYOUT (self)))
10185 {
10186 g_critical (G_STRLOC ": The clutter_actor_set_allocation() function "
10187 "can only be called from within the implementation of "
10188 "the ClutterActor::allocate() virtual function.");
10189 return;
10190 }
10191
10192 priv = self->priv;
10193
10194 g_object_freeze_notify (G_OBJECT (self));
10195
10196 changed = clutter_actor_set_allocation_internal (self, box, flags);
10197
10198 /* we allocate our children before we notify changes in our geometry,
10199 * so that people connecting to properties will be able to get valid
10200 * data out of the sub-tree of the scene graph that has this actor at
10201 * the root.
10202 */
10203 clutter_actor_maybe_layout_children (self, box, flags);
10204
10205 if (changed)
10206 {
10207 ClutterActorBox signal_box = priv->allocation;
10208 ClutterAllocationFlags signal_flags = priv->allocation_flags;
10209
10210 g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
10211 &signal_box,
10212 signal_flags);
10213 }
10214
10215 g_object_thaw_notify (G_OBJECT (self));
10216 }
10217
10218 /**
10219 * clutter_actor_set_position:
10220 * @self: A #ClutterActor
10221 * @x: New left position of actor in pixels.
10222 * @y: New top position of actor in pixels.
10223 *
10224 * Sets the actor's fixed position in pixels relative to any parent
10225 * actor.
10226 *
10227 * If a layout manager is in use, this position will override the
10228 * layout manager and force a fixed position.
10229 */
10230 void
clutter_actor_set_position(ClutterActor * self,gfloat x,gfloat y)10231 clutter_actor_set_position (ClutterActor *self,
10232 gfloat x,
10233 gfloat y)
10234 {
10235 ClutterPoint new_position;
10236 ClutterPoint cur_position;
10237
10238 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10239
10240 clutter_point_init (&new_position, x, y);
10241
10242 cur_position.x = clutter_actor_get_x (self);
10243 cur_position.y = clutter_actor_get_y (self);
10244
10245 _clutter_actor_create_transition (self, obj_props[PROP_POSITION],
10246 &cur_position,
10247 &new_position);
10248 }
10249
10250 /**
10251 * clutter_actor_get_fixed_position_set:
10252 * @self: A #ClutterActor
10253 *
10254 * Checks whether an actor has a fixed position set (and will thus be
10255 * unaffected by any layout manager).
10256 *
10257 * Return value: %TRUE if the fixed position is set on the actor
10258 *
10259 * Since: 0.8
10260 */
10261 gboolean
clutter_actor_get_fixed_position_set(ClutterActor * self)10262 clutter_actor_get_fixed_position_set (ClutterActor *self)
10263 {
10264 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
10265
10266 return self->priv->position_set;
10267 }
10268
10269 /**
10270 * clutter_actor_set_fixed_position_set:
10271 * @self: A #ClutterActor
10272 * @is_set: whether to use fixed position
10273 *
10274 * Sets whether an actor has a fixed position set (and will thus be
10275 * unaffected by any layout manager).
10276 *
10277 * Since: 0.8
10278 */
10279 void
clutter_actor_set_fixed_position_set(ClutterActor * self,gboolean is_set)10280 clutter_actor_set_fixed_position_set (ClutterActor *self,
10281 gboolean is_set)
10282 {
10283 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10284
10285 if (self->priv->position_set == (is_set != FALSE))
10286 return;
10287
10288 if (!is_set)
10289 {
10290 ClutterLayoutInfo *info;
10291
10292 /* Ensure we set back the default fixed position of 0,0 so that setting
10293 just one of x/y always atomically gets 0 for the other */
10294 info = _clutter_actor_peek_layout_info (self);
10295 if (info != NULL)
10296 {
10297 info->fixed_pos.x = 0;
10298 info->fixed_pos.y = 0;
10299 }
10300 }
10301
10302 self->priv->position_set = is_set != FALSE;
10303 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIXED_POSITION_SET]);
10304
10305 clutter_actor_queue_relayout (self);
10306 }
10307
10308 /**
10309 * clutter_actor_move_by:
10310 * @self: A #ClutterActor
10311 * @dx: Distance to move Actor on X axis.
10312 * @dy: Distance to move Actor on Y axis.
10313 *
10314 * Moves an actor by the specified distance relative to its current
10315 * position in pixels.
10316 *
10317 * This function modifies the fixed position of an actor and thus removes
10318 * it from any layout management. Another way to move an actor is with an
10319 * anchor point, see clutter_actor_set_anchor_point(), or with an additional
10320 * translation, using clutter_actor_set_translation().
10321 *
10322 * Since: 0.2
10323 */
10324 void
clutter_actor_move_by(ClutterActor * self,gfloat dx,gfloat dy)10325 clutter_actor_move_by (ClutterActor *self,
10326 gfloat dx,
10327 gfloat dy)
10328 {
10329 const ClutterLayoutInfo *info;
10330 gfloat x, y;
10331
10332 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10333
10334 info = _clutter_actor_get_layout_info_or_defaults (self);
10335 x = info->fixed_pos.x;
10336 y = info->fixed_pos.y;
10337
10338 clutter_actor_set_position (self, x + dx, y + dy);
10339 }
10340
10341 static void
clutter_actor_set_min_width(ClutterActor * self,gfloat min_width)10342 clutter_actor_set_min_width (ClutterActor *self,
10343 gfloat min_width)
10344 {
10345 ClutterActorPrivate *priv = self->priv;
10346 ClutterActorBox old = { 0, };
10347 ClutterLayoutInfo *info;
10348
10349 /* if we are setting the size on a top-level actor and the
10350 * backend only supports static top-levels (e.g. framebuffers)
10351 * then we ignore the passed value and we override it with
10352 * the stage implementation's preferred size.
10353 */
10354 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10355 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10356 return;
10357
10358 info = _clutter_actor_get_layout_info (self);
10359
10360 if (priv->min_width_set && min_width == info->minimum.width)
10361 return;
10362
10363 g_object_freeze_notify (G_OBJECT (self));
10364
10365 clutter_actor_store_old_geometry (self, &old);
10366
10367 info->minimum.width = min_width;
10368 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH]);
10369 clutter_actor_set_min_width_set (self, TRUE);
10370
10371 clutter_actor_notify_if_geometry_changed (self, &old);
10372
10373 g_object_thaw_notify (G_OBJECT (self));
10374
10375 clutter_actor_queue_relayout (self);
10376 }
10377
10378 static void
clutter_actor_set_min_height(ClutterActor * self,gfloat min_height)10379 clutter_actor_set_min_height (ClutterActor *self,
10380 gfloat min_height)
10381
10382 {
10383 ClutterActorPrivate *priv = self->priv;
10384 ClutterActorBox old = { 0, };
10385 ClutterLayoutInfo *info;
10386
10387 /* if we are setting the size on a top-level actor and the
10388 * backend only supports static top-levels (e.g. framebuffers)
10389 * then we ignore the passed value and we override it with
10390 * the stage implementation's preferred size.
10391 */
10392 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10393 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10394 return;
10395
10396 info = _clutter_actor_get_layout_info (self);
10397
10398 if (priv->min_height_set && min_height == info->minimum.height)
10399 return;
10400
10401 g_object_freeze_notify (G_OBJECT (self));
10402
10403 clutter_actor_store_old_geometry (self, &old);
10404
10405 info->minimum.height = min_height;
10406 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT]);
10407 clutter_actor_set_min_height_set (self, TRUE);
10408
10409 clutter_actor_notify_if_geometry_changed (self, &old);
10410
10411 g_object_thaw_notify (G_OBJECT (self));
10412
10413 clutter_actor_queue_relayout (self);
10414 }
10415
10416 static void
clutter_actor_set_natural_width(ClutterActor * self,gfloat natural_width)10417 clutter_actor_set_natural_width (ClutterActor *self,
10418 gfloat natural_width)
10419 {
10420 ClutterActorPrivate *priv = self->priv;
10421 ClutterActorBox old = { 0, };
10422 ClutterLayoutInfo *info;
10423
10424 /* if we are setting the size on a top-level actor and the
10425 * backend only supports static top-levels (e.g. framebuffers)
10426 * then we ignore the passed value and we override it with
10427 * the stage implementation's preferred size.
10428 */
10429 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10430 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10431 return;
10432
10433 info = _clutter_actor_get_layout_info (self);
10434
10435 if (priv->natural_width_set && natural_width == info->natural.width)
10436 return;
10437
10438 g_object_freeze_notify (G_OBJECT (self));
10439
10440 clutter_actor_store_old_geometry (self, &old);
10441
10442 info->natural.width = natural_width;
10443 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH]);
10444 clutter_actor_set_natural_width_set (self, TRUE);
10445
10446 clutter_actor_notify_if_geometry_changed (self, &old);
10447
10448 g_object_thaw_notify (G_OBJECT (self));
10449
10450 clutter_actor_queue_relayout (self);
10451 }
10452
10453 static void
clutter_actor_set_natural_height(ClutterActor * self,gfloat natural_height)10454 clutter_actor_set_natural_height (ClutterActor *self,
10455 gfloat natural_height)
10456 {
10457 ClutterActorPrivate *priv = self->priv;
10458 ClutterActorBox old = { 0, };
10459 ClutterLayoutInfo *info;
10460
10461 /* if we are setting the size on a top-level actor and the
10462 * backend only supports static top-levels (e.g. framebuffers)
10463 * then we ignore the passed value and we override it with
10464 * the stage implementation's preferred size.
10465 */
10466 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10467 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10468 return;
10469
10470 info = _clutter_actor_get_layout_info (self);
10471
10472 if (priv->natural_height_set && natural_height == info->natural.height)
10473 return;
10474
10475 g_object_freeze_notify (G_OBJECT (self));
10476
10477 clutter_actor_store_old_geometry (self, &old);
10478
10479 info->natural.height = natural_height;
10480 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT]);
10481 clutter_actor_set_natural_height_set (self, TRUE);
10482
10483 clutter_actor_notify_if_geometry_changed (self, &old);
10484
10485 g_object_thaw_notify (G_OBJECT (self));
10486
10487 clutter_actor_queue_relayout (self);
10488 }
10489
10490 static void
clutter_actor_set_min_width_set(ClutterActor * self,gboolean use_min_width)10491 clutter_actor_set_min_width_set (ClutterActor *self,
10492 gboolean use_min_width)
10493 {
10494 ClutterActorPrivate *priv = self->priv;
10495 ClutterActorBox old = { 0, };
10496
10497 if (priv->min_width_set == (use_min_width != FALSE))
10498 return;
10499
10500 clutter_actor_store_old_geometry (self, &old);
10501
10502 priv->min_width_set = use_min_width != FALSE;
10503 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH_SET]);
10504
10505 clutter_actor_notify_if_geometry_changed (self, &old);
10506
10507 clutter_actor_queue_relayout (self);
10508 }
10509
10510 static void
clutter_actor_set_min_height_set(ClutterActor * self,gboolean use_min_height)10511 clutter_actor_set_min_height_set (ClutterActor *self,
10512 gboolean use_min_height)
10513 {
10514 ClutterActorPrivate *priv = self->priv;
10515 ClutterActorBox old = { 0, };
10516
10517 if (priv->min_height_set == (use_min_height != FALSE))
10518 return;
10519
10520 clutter_actor_store_old_geometry (self, &old);
10521
10522 priv->min_height_set = use_min_height != FALSE;
10523 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT_SET]);
10524
10525 clutter_actor_notify_if_geometry_changed (self, &old);
10526
10527 clutter_actor_queue_relayout (self);
10528 }
10529
10530 static void
clutter_actor_set_natural_width_set(ClutterActor * self,gboolean use_natural_width)10531 clutter_actor_set_natural_width_set (ClutterActor *self,
10532 gboolean use_natural_width)
10533 {
10534 ClutterActorPrivate *priv = self->priv;
10535 ClutterActorBox old = { 0, };
10536
10537 if (priv->natural_width_set == (use_natural_width != FALSE))
10538 return;
10539
10540 clutter_actor_store_old_geometry (self, &old);
10541
10542 priv->natural_width_set = use_natural_width != FALSE;
10543 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH_SET]);
10544
10545 clutter_actor_notify_if_geometry_changed (self, &old);
10546
10547 clutter_actor_queue_relayout (self);
10548 }
10549
10550 static void
clutter_actor_set_natural_height_set(ClutterActor * self,gboolean use_natural_height)10551 clutter_actor_set_natural_height_set (ClutterActor *self,
10552 gboolean use_natural_height)
10553 {
10554 ClutterActorPrivate *priv = self->priv;
10555 ClutterActorBox old = { 0, };
10556
10557 if (priv->natural_height_set == (use_natural_height != FALSE))
10558 return;
10559
10560 clutter_actor_store_old_geometry (self, &old);
10561
10562 priv->natural_height_set = use_natural_height != FALSE;
10563 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT_SET]);
10564
10565 clutter_actor_notify_if_geometry_changed (self, &old);
10566
10567 clutter_actor_queue_relayout (self);
10568 }
10569
10570 /**
10571 * clutter_actor_set_request_mode:
10572 * @self: a #ClutterActor
10573 * @mode: the request mode
10574 *
10575 * Sets the geometry request mode of @self.
10576 *
10577 * The @mode determines the order for invoking
10578 * clutter_actor_get_preferred_width() and
10579 * clutter_actor_get_preferred_height()
10580 *
10581 * Since: 1.2
10582 */
10583 void
clutter_actor_set_request_mode(ClutterActor * self,ClutterRequestMode mode)10584 clutter_actor_set_request_mode (ClutterActor *self,
10585 ClutterRequestMode mode)
10586 {
10587 ClutterActorPrivate *priv;
10588
10589 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10590
10591 priv = self->priv;
10592
10593 if (priv->request_mode == mode)
10594 return;
10595
10596 priv->request_mode = mode;
10597
10598 priv->needs_width_request = TRUE;
10599 priv->needs_height_request = TRUE;
10600
10601 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REQUEST_MODE]);
10602
10603 clutter_actor_queue_relayout (self);
10604 }
10605
10606 /**
10607 * clutter_actor_get_request_mode:
10608 * @self: a #ClutterActor
10609 *
10610 * Retrieves the geometry request mode of @self
10611 *
10612 * Return value: the request mode for the actor
10613 *
10614 * Since: 1.2
10615 */
10616 ClutterRequestMode
clutter_actor_get_request_mode(ClutterActor * self)10617 clutter_actor_get_request_mode (ClutterActor *self)
10618 {
10619 g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
10620 CLUTTER_REQUEST_HEIGHT_FOR_WIDTH);
10621
10622 return self->priv->request_mode;
10623 }
10624
10625 /* variant of set_width() without checks and without notification
10626 * freeze+thaw, for internal usage only
10627 */
10628 static inline void
clutter_actor_set_width_internal(ClutterActor * self,gfloat width)10629 clutter_actor_set_width_internal (ClutterActor *self,
10630 gfloat width)
10631 {
10632 if (width >= 0)
10633 {
10634 /* the Stage will use the :min-width to control the minimum
10635 * width to be resized to, so we should not be setting it
10636 * along with the :natural-width
10637 */
10638 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10639 clutter_actor_set_min_width (self, width);
10640
10641 clutter_actor_set_natural_width (self, width);
10642 }
10643 else
10644 {
10645 /* we only unset the :natural-width for the Stage */
10646 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10647 clutter_actor_set_min_width_set (self, FALSE);
10648
10649 clutter_actor_set_natural_width_set (self, FALSE);
10650 }
10651 }
10652
10653 /* variant of set_height() without checks and without notification
10654 * freeze+thaw, for internal usage only
10655 */
10656 static inline void
clutter_actor_set_height_internal(ClutterActor * self,gfloat height)10657 clutter_actor_set_height_internal (ClutterActor *self,
10658 gfloat height)
10659 {
10660 if (height >= 0)
10661 {
10662 /* see the comment above in set_width_internal() */
10663 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10664 clutter_actor_set_min_height (self, height);
10665
10666 clutter_actor_set_natural_height (self, height);
10667 }
10668 else
10669 {
10670 /* see the comment above in set_width_internal() */
10671 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10672 clutter_actor_set_min_height_set (self, FALSE);
10673
10674 clutter_actor_set_natural_height_set (self, FALSE);
10675 }
10676 }
10677
10678 static void
clutter_actor_set_size_internal(ClutterActor * self,const ClutterSize * size)10679 clutter_actor_set_size_internal (ClutterActor *self,
10680 const ClutterSize *size)
10681 {
10682 if (size != NULL)
10683 {
10684 clutter_actor_set_width_internal (self, size->width);
10685 clutter_actor_set_height_internal (self, size->height);
10686 }
10687 else
10688 {
10689 clutter_actor_set_width_internal (self, -1);
10690 clutter_actor_set_height_internal (self, -1);
10691 }
10692 }
10693
10694 /**
10695 * clutter_actor_set_size:
10696 * @self: A #ClutterActor
10697 * @width: New width of actor in pixels, or -1
10698 * @height: New height of actor in pixels, or -1
10699 *
10700 * Sets the actor's size request in pixels. This overrides any
10701 * "normal" size request the actor would have. For example
10702 * a text actor might normally request the size of the text;
10703 * this function would force a specific size instead.
10704 *
10705 * If @width and/or @height are -1 the actor will use its
10706 * "normal" size request instead of overriding it, i.e.
10707 * you can "unset" the size with -1.
10708 *
10709 * This function sets or unsets both the minimum and natural size.
10710 */
10711 void
clutter_actor_set_size(ClutterActor * self,gfloat width,gfloat height)10712 clutter_actor_set_size (ClutterActor *self,
10713 gfloat width,
10714 gfloat height)
10715 {
10716 ClutterSize new_size;
10717
10718 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10719
10720 clutter_size_init (&new_size, width, height);
10721
10722 /* minor optimization: if we don't have a duration then we can
10723 * skip the get_size() below, to avoid the chance of going through
10724 * get_preferred_width() and get_preferred_height() just to jump to
10725 * a new desired size
10726 */
10727 if (clutter_actor_get_easing_duration (self) == 0)
10728 {
10729 g_object_freeze_notify (G_OBJECT (self));
10730
10731 clutter_actor_set_size_internal (self, &new_size);
10732
10733 g_object_thaw_notify (G_OBJECT (self));
10734
10735 return;
10736 }
10737 else
10738 {
10739 ClutterSize cur_size;
10740
10741 clutter_size_init (&cur_size,
10742 clutter_actor_get_width (self),
10743 clutter_actor_get_height (self));
10744
10745 _clutter_actor_create_transition (self,
10746 obj_props[PROP_SIZE],
10747 &cur_size,
10748 &new_size);
10749 }
10750 }
10751
10752 /**
10753 * clutter_actor_get_size:
10754 * @self: A #ClutterActor
10755 * @width: (out) (allow-none): return location for the width, or %NULL.
10756 * @height: (out) (allow-none): return location for the height, or %NULL.
10757 *
10758 * This function tries to "do what you mean" and return
10759 * the size an actor will have. If the actor has a valid
10760 * allocation, the allocation will be returned; otherwise,
10761 * the actors natural size request will be returned.
10762 *
10763 * If you care whether you get the request vs. the allocation, you
10764 * should probably call a different function like
10765 * clutter_actor_get_allocation_box() or
10766 * clutter_actor_get_preferred_width().
10767 *
10768 * Since: 0.2
10769 */
10770 void
clutter_actor_get_size(ClutterActor * self,gfloat * width,gfloat * height)10771 clutter_actor_get_size (ClutterActor *self,
10772 gfloat *width,
10773 gfloat *height)
10774 {
10775 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10776
10777 if (width)
10778 *width = clutter_actor_get_width (self);
10779
10780 if (height)
10781 *height = clutter_actor_get_height (self);
10782 }
10783
10784 /**
10785 * clutter_actor_get_position:
10786 * @self: a #ClutterActor
10787 * @x: (out) (allow-none): return location for the X coordinate, or %NULL
10788 * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
10789 *
10790 * This function tries to "do what you mean" and tell you where the
10791 * actor is, prior to any transformations. Retrieves the fixed
10792 * position of an actor in pixels, if one has been set; otherwise, if
10793 * the allocation is valid, returns the actor's allocated position;
10794 * otherwise, returns 0,0.
10795 *
10796 * The returned position is in pixels.
10797 *
10798 * Since: 0.6
10799 */
10800 void
clutter_actor_get_position(ClutterActor * self,gfloat * x,gfloat * y)10801 clutter_actor_get_position (ClutterActor *self,
10802 gfloat *x,
10803 gfloat *y)
10804 {
10805 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10806
10807 if (x)
10808 *x = clutter_actor_get_x (self);
10809
10810 if (y)
10811 *y = clutter_actor_get_y (self);
10812 }
10813
10814 /**
10815 * clutter_actor_get_transformed_position:
10816 * @self: A #ClutterActor
10817 * @x: (out) (allow-none): return location for the X coordinate, or %NULL
10818 * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
10819 *
10820 * Gets the absolute position of an actor, in pixels relative to the stage.
10821 *
10822 * Since: 0.8
10823 */
10824 void
clutter_actor_get_transformed_position(ClutterActor * self,gfloat * x,gfloat * y)10825 clutter_actor_get_transformed_position (ClutterActor *self,
10826 gfloat *x,
10827 gfloat *y)
10828 {
10829 ClutterVertex v1;
10830 ClutterVertex v2;
10831
10832 v1.x = v1.y = v1.z = 0;
10833 clutter_actor_apply_transform_to_point (self, &v1, &v2);
10834
10835 if (x)
10836 *x = v2.x;
10837
10838 if (y)
10839 *y = v2.y;
10840 }
10841
10842 /**
10843 * clutter_actor_get_transformed_size:
10844 * @self: A #ClutterActor
10845 * @width: (out) (allow-none): return location for the width, or %NULL
10846 * @height: (out) (allow-none): return location for the height, or %NULL
10847 *
10848 * Gets the absolute size of an actor in pixels, taking into account the
10849 * scaling factors.
10850 *
10851 * If the actor has a valid allocation, the allocated size will be used.
10852 * If the actor has not a valid allocation then the preferred size will
10853 * be transformed and returned.
10854 *
10855 * If you want the transformed allocation, see
10856 * clutter_actor_get_abs_allocation_vertices() instead.
10857 *
10858 * When the actor (or one of its ancestors) is rotated around the
10859 * X or Y axis, it no longer appears as on the stage as a rectangle, but
10860 * as a generic quadrangle; in that case this function returns the size
10861 * of the smallest rectangle that encapsulates the entire quad. Please
10862 * note that in this case no assumptions can be made about the relative
10863 * position of this envelope to the absolute position of the actor, as
10864 * returned by clutter_actor_get_transformed_position(); if you need this
10865 * information, you need to use clutter_actor_get_abs_allocation_vertices()
10866 * to get the coords of the actual quadrangle.
10867 *
10868 * Since: 0.8
10869 */
10870 void
clutter_actor_get_transformed_size(ClutterActor * self,gfloat * width,gfloat * height)10871 clutter_actor_get_transformed_size (ClutterActor *self,
10872 gfloat *width,
10873 gfloat *height)
10874 {
10875 ClutterActorPrivate *priv;
10876 ClutterVertex v[4];
10877 gfloat x_min, x_max, y_min, y_max;
10878 gint i;
10879
10880 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10881
10882 priv = self->priv;
10883
10884 /* if the actor hasn't been allocated yet, get the preferred
10885 * size and transform that
10886 */
10887 if (priv->needs_allocation)
10888 {
10889 gfloat natural_width, natural_height;
10890 ClutterActorBox box;
10891
10892 /* Make a fake allocation to transform.
10893 *
10894 * NB: _clutter_actor_transform_and_project_box expects a box in
10895 * the actor's coordinate space... */
10896
10897 box.x1 = 0;
10898 box.y1 = 0;
10899
10900 natural_width = natural_height = 0;
10901 clutter_actor_get_preferred_size (self, NULL, NULL,
10902 &natural_width,
10903 &natural_height);
10904
10905 box.x2 = natural_width;
10906 box.y2 = natural_height;
10907
10908 _clutter_actor_transform_and_project_box (self, &box, v);
10909 }
10910 else
10911 clutter_actor_get_abs_allocation_vertices (self, v);
10912
10913 x_min = x_max = v[0].x;
10914 y_min = y_max = v[0].y;
10915
10916 for (i = 1; i < G_N_ELEMENTS (v); ++i)
10917 {
10918 if (v[i].x < x_min)
10919 x_min = v[i].x;
10920
10921 if (v[i].x > x_max)
10922 x_max = v[i].x;
10923
10924 if (v[i].y < y_min)
10925 y_min = v[i].y;
10926
10927 if (v[i].y > y_max)
10928 y_max = v[i].y;
10929 }
10930
10931 if (width)
10932 *width = x_max - x_min;
10933
10934 if (height)
10935 *height = y_max - y_min;
10936 }
10937
10938 /**
10939 * clutter_actor_get_width:
10940 * @self: A #ClutterActor
10941 *
10942 * Retrieves the width of a #ClutterActor.
10943 *
10944 * If the actor has a valid allocation, this function will return the
10945 * width of the allocated area given to the actor.
10946 *
10947 * If the actor does not have a valid allocation, this function will
10948 * return the actor's natural width, that is the preferred width of
10949 * the actor.
10950 *
10951 * If you care whether you get the preferred width or the width that
10952 * has been assigned to the actor, you should probably call a different
10953 * function like clutter_actor_get_allocation_box() to retrieve the
10954 * allocated size or clutter_actor_get_preferred_width() to retrieve the
10955 * preferred width.
10956 *
10957 * If an actor has a fixed width, for instance a width that has been
10958 * assigned using clutter_actor_set_width(), the width returned will
10959 * be the same value.
10960 *
10961 * Return value: the width of the actor, in pixels
10962 */
10963 gfloat
clutter_actor_get_width(ClutterActor * self)10964 clutter_actor_get_width (ClutterActor *self)
10965 {
10966 ClutterActorPrivate *priv;
10967
10968 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10969
10970 priv = self->priv;
10971
10972 if (priv->needs_allocation)
10973 {
10974 gfloat natural_width = 0;
10975
10976 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
10977 {
10978 clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
10979 }
10980 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
10981 {
10982 gfloat natural_height = 0;
10983
10984 clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
10985 clutter_actor_get_preferred_width (self, natural_height,
10986 NULL,
10987 &natural_width);
10988 }
10989 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
10990 {
10991 clutter_content_get_preferred_size (priv->content, &natural_width, NULL);
10992 }
10993
10994 return natural_width;
10995 }
10996 else
10997 return priv->allocation.x2 - priv->allocation.x1;
10998 }
10999
11000 /**
11001 * clutter_actor_get_height:
11002 * @self: A #ClutterActor
11003 *
11004 * Retrieves the height of a #ClutterActor.
11005 *
11006 * If the actor has a valid allocation, this function will return the
11007 * height of the allocated area given to the actor.
11008 *
11009 * If the actor does not have a valid allocation, this function will
11010 * return the actor's natural height, that is the preferred height of
11011 * the actor.
11012 *
11013 * If you care whether you get the preferred height or the height that
11014 * has been assigned to the actor, you should probably call a different
11015 * function like clutter_actor_get_allocation_box() to retrieve the
11016 * allocated size or clutter_actor_get_preferred_height() to retrieve the
11017 * preferred height.
11018 *
11019 * If an actor has a fixed height, for instance a height that has been
11020 * assigned using clutter_actor_set_height(), the height returned will
11021 * be the same value.
11022 *
11023 * Return value: the height of the actor, in pixels
11024 */
11025 gfloat
clutter_actor_get_height(ClutterActor * self)11026 clutter_actor_get_height (ClutterActor *self)
11027 {
11028 ClutterActorPrivate *priv;
11029
11030 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11031
11032 priv = self->priv;
11033
11034 if (priv->needs_allocation)
11035 {
11036 gfloat natural_height = 0;
11037
11038 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
11039 {
11040 gfloat natural_width = 0;
11041
11042 clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
11043 clutter_actor_get_preferred_height (self, natural_width,
11044 NULL, &natural_height);
11045 }
11046 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
11047 {
11048 clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
11049 }
11050 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
11051 {
11052 clutter_content_get_preferred_size (priv->content, NULL, &natural_height);
11053 }
11054
11055 return natural_height;
11056 }
11057 else
11058 return priv->allocation.y2 - priv->allocation.y1;
11059 }
11060
11061 /**
11062 * clutter_actor_set_width:
11063 * @self: A #ClutterActor
11064 * @width: Requested new width for the actor, in pixels, or -1
11065 *
11066 * Forces a width on an actor, causing the actor's preferred width
11067 * and height (if any) to be ignored.
11068 *
11069 * If @width is -1 the actor will use its preferred width request
11070 * instead of overriding it, i.e. you can "unset" the width with -1.
11071 *
11072 * This function sets both the minimum and natural size of the actor.
11073 *
11074 * since: 0.2
11075 */
11076 void
clutter_actor_set_width(ClutterActor * self,gfloat width)11077 clutter_actor_set_width (ClutterActor *self,
11078 gfloat width)
11079 {
11080 float cur_size;
11081
11082 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11083
11084 /* minor optimization: if we don't have a duration
11085 * then we can skip the get_width() below, to avoid
11086 * the chance of going through get_preferred_width()
11087 * just to jump to a new desired width.
11088 */
11089 if (clutter_actor_get_easing_duration (self) == 0)
11090 {
11091 g_object_freeze_notify (G_OBJECT (self));
11092
11093 clutter_actor_set_width_internal (self, width);
11094
11095 g_object_thaw_notify (G_OBJECT (self));
11096
11097 return;
11098 }
11099 else
11100 cur_size = clutter_actor_get_width (self);
11101
11102 _clutter_actor_create_transition (self,
11103 obj_props[PROP_WIDTH],
11104 cur_size,
11105 width);
11106 }
11107
11108 /**
11109 * clutter_actor_set_height:
11110 * @self: A #ClutterActor
11111 * @height: Requested new height for the actor, in pixels, or -1
11112 *
11113 * Forces a height on an actor, causing the actor's preferred width
11114 * and height (if any) to be ignored.
11115 *
11116 * If @height is -1 the actor will use its preferred height instead of
11117 * overriding it, i.e. you can "unset" the height with -1.
11118 *
11119 * This function sets both the minimum and natural size of the actor.
11120 *
11121 * since: 0.2
11122 */
11123 void
clutter_actor_set_height(ClutterActor * self,gfloat height)11124 clutter_actor_set_height (ClutterActor *self,
11125 gfloat height)
11126 {
11127 float cur_size;
11128
11129 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11130
11131 /* see the comment in clutter_actor_set_width() above */
11132 if (clutter_actor_get_easing_duration (self) == 0)
11133 {
11134 g_object_freeze_notify (G_OBJECT (self));
11135
11136 clutter_actor_set_height_internal (self, height);
11137
11138 g_object_thaw_notify (G_OBJECT (self));
11139
11140 return;
11141 }
11142 else
11143 cur_size = clutter_actor_get_height (self);
11144
11145 _clutter_actor_create_transition (self,
11146 obj_props[PROP_HEIGHT],
11147 cur_size,
11148 height);
11149 }
11150
11151 static inline void
clutter_actor_set_x_internal(ClutterActor * self,float x)11152 clutter_actor_set_x_internal (ClutterActor *self,
11153 float x)
11154 {
11155 ClutterActorPrivate *priv = self->priv;
11156 ClutterLayoutInfo *linfo;
11157 ClutterActorBox old = { 0, };
11158
11159 linfo = _clutter_actor_get_layout_info (self);
11160
11161 if (priv->position_set && linfo->fixed_pos.x == x)
11162 return;
11163
11164 clutter_actor_store_old_geometry (self, &old);
11165
11166 linfo->fixed_pos.x = x;
11167 clutter_actor_set_fixed_position_set (self, TRUE);
11168
11169 clutter_actor_notify_if_geometry_changed (self, &old);
11170
11171 clutter_actor_queue_relayout (self);
11172 }
11173
11174 static inline void
clutter_actor_set_y_internal(ClutterActor * self,float y)11175 clutter_actor_set_y_internal (ClutterActor *self,
11176 float y)
11177 {
11178 ClutterActorPrivate *priv = self->priv;
11179 ClutterLayoutInfo *linfo;
11180 ClutterActorBox old = { 0, };
11181
11182 linfo = _clutter_actor_get_layout_info (self);
11183
11184 if (priv->position_set && linfo->fixed_pos.y == y)
11185 return;
11186
11187 clutter_actor_store_old_geometry (self, &old);
11188
11189 linfo->fixed_pos.y = y;
11190 clutter_actor_set_fixed_position_set (self, TRUE);
11191
11192 clutter_actor_notify_if_geometry_changed (self, &old);
11193
11194 clutter_actor_queue_relayout (self);
11195 }
11196
11197 static void
clutter_actor_set_position_internal(ClutterActor * self,const ClutterPoint * position)11198 clutter_actor_set_position_internal (ClutterActor *self,
11199 const ClutterPoint *position)
11200 {
11201 ClutterActorPrivate *priv = self->priv;
11202 ClutterLayoutInfo *linfo;
11203 ClutterActorBox old = { 0, };
11204
11205 linfo = _clutter_actor_get_layout_info (self);
11206
11207 if (priv->position_set &&
11208 clutter_point_equals (position, &linfo->fixed_pos))
11209 return;
11210
11211 clutter_actor_store_old_geometry (self, &old);
11212
11213 if (position != NULL)
11214 {
11215 linfo->fixed_pos = *position;
11216 clutter_actor_set_fixed_position_set (self, TRUE);
11217 }
11218 else
11219 clutter_actor_set_fixed_position_set (self, FALSE);
11220
11221 clutter_actor_notify_if_geometry_changed (self, &old);
11222
11223 clutter_actor_queue_relayout (self);
11224 }
11225
11226 /**
11227 * clutter_actor_set_x:
11228 * @self: a #ClutterActor
11229 * @x: the actor's position on the X axis
11230 *
11231 * Sets the actor's X coordinate, relative to its parent, in pixels.
11232 *
11233 * Overrides any layout manager and forces a fixed position for
11234 * the actor.
11235 *
11236 * The #ClutterActor:x property is animatable.
11237 *
11238 * Since: 0.6
11239 */
11240 void
clutter_actor_set_x(ClutterActor * self,gfloat x)11241 clutter_actor_set_x (ClutterActor *self,
11242 gfloat x)
11243 {
11244 float cur_position = clutter_actor_get_x (self);
11245
11246 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11247
11248 _clutter_actor_create_transition (self, obj_props[PROP_X],
11249 cur_position,
11250 x);
11251 }
11252
11253 /**
11254 * clutter_actor_set_y:
11255 * @self: a #ClutterActor
11256 * @y: the actor's position on the Y axis
11257 *
11258 * Sets the actor's Y coordinate, relative to its parent, in pixels.#
11259 *
11260 * Overrides any layout manager and forces a fixed position for
11261 * the actor.
11262 *
11263 * The #ClutterActor:y property is animatable.
11264 *
11265 * Since: 0.6
11266 */
11267 void
clutter_actor_set_y(ClutterActor * self,gfloat y)11268 clutter_actor_set_y (ClutterActor *self,
11269 gfloat y)
11270 {
11271 float cur_position = clutter_actor_get_y (self);
11272
11273 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11274
11275 _clutter_actor_create_transition (self, obj_props[PROP_Y],
11276 cur_position,
11277 y);
11278 }
11279
11280 /**
11281 * clutter_actor_get_x:
11282 * @self: A #ClutterActor
11283 *
11284 * Retrieves the X coordinate of a #ClutterActor.
11285 *
11286 * This function tries to "do what you mean", by returning the
11287 * correct value depending on the actor's state.
11288 *
11289 * If the actor has a valid allocation, this function will return
11290 * the X coordinate of the origin of the allocation box.
11291 *
11292 * If the actor has any fixed coordinate set using clutter_actor_set_x(),
11293 * clutter_actor_set_position() or clutter_actor_set_geometry(), this
11294 * function will return that coordinate.
11295 *
11296 * If both the allocation and a fixed position are missing, this function
11297 * will return 0.
11298 *
11299 * Return value: the X coordinate, in pixels, ignoring any
11300 * transformation (i.e. scaling, rotation)
11301 */
11302 gfloat
clutter_actor_get_x(ClutterActor * self)11303 clutter_actor_get_x (ClutterActor *self)
11304 {
11305 ClutterActorPrivate *priv;
11306
11307 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11308
11309 priv = self->priv;
11310
11311 if (priv->needs_allocation)
11312 {
11313 if (priv->position_set)
11314 {
11315 const ClutterLayoutInfo *info;
11316
11317 info = _clutter_actor_get_layout_info_or_defaults (self);
11318
11319 return info->fixed_pos.x;
11320 }
11321 else
11322 return 0;
11323 }
11324 else
11325 return priv->allocation.x1;
11326 }
11327
11328 /**
11329 * clutter_actor_get_y:
11330 * @self: A #ClutterActor
11331 *
11332 * Retrieves the Y coordinate of a #ClutterActor.
11333 *
11334 * This function tries to "do what you mean", by returning the
11335 * correct value depending on the actor's state.
11336 *
11337 * If the actor has a valid allocation, this function will return
11338 * the Y coordinate of the origin of the allocation box.
11339 *
11340 * If the actor has any fixed coordinate set using clutter_actor_set_y(),
11341 * clutter_actor_set_position() or clutter_actor_set_geometry(), this
11342 * function will return that coordinate.
11343 *
11344 * If both the allocation and a fixed position are missing, this function
11345 * will return 0.
11346 *
11347 * Return value: the Y coordinate, in pixels, ignoring any
11348 * transformation (i.e. scaling, rotation)
11349 */
11350 gfloat
clutter_actor_get_y(ClutterActor * self)11351 clutter_actor_get_y (ClutterActor *self)
11352 {
11353 ClutterActorPrivate *priv;
11354
11355 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11356
11357 priv = self->priv;
11358
11359 if (priv->needs_allocation)
11360 {
11361 if (priv->position_set)
11362 {
11363 const ClutterLayoutInfo *info;
11364
11365 info = _clutter_actor_get_layout_info_or_defaults (self);
11366
11367 return info->fixed_pos.y;
11368 }
11369 else
11370 return 0;
11371 }
11372 else
11373 return priv->allocation.y1;
11374 }
11375
11376 /**
11377 * clutter_actor_set_scale:
11378 * @self: A #ClutterActor
11379 * @scale_x: double factor to scale actor by horizontally.
11380 * @scale_y: double factor to scale actor by vertically.
11381 *
11382 * Scales an actor with the given factors.
11383 *
11384 * The scale transformation is relative the the #ClutterActor:pivot-point.
11385 *
11386 * The #ClutterActor:scale-x and #ClutterActor:scale-y properties are
11387 * animatable.
11388 *
11389 * Since: 0.2
11390 */
11391 void
clutter_actor_set_scale(ClutterActor * self,gdouble scale_x,gdouble scale_y)11392 clutter_actor_set_scale (ClutterActor *self,
11393 gdouble scale_x,
11394 gdouble scale_y)
11395 {
11396 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11397
11398 g_object_freeze_notify (G_OBJECT (self));
11399
11400 clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
11401 clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
11402
11403 g_object_thaw_notify (G_OBJECT (self));
11404 }
11405
11406 /**
11407 * clutter_actor_set_scale_z:
11408 * @self: a #ClutterActor
11409 * @scale_z: the scaling factor along the Z axis
11410 *
11411 * Scales an actor on the Z axis by the given @scale_z factor.
11412 *
11413 * The scale transformation is relative the the #ClutterActor:pivot-point.
11414 *
11415 * The #ClutterActor:scale-z property is animatable.
11416 *
11417 * Since: 1.12
11418 */
11419 void
clutter_actor_set_scale_z(ClutterActor * self,gdouble scale_z)11420 clutter_actor_set_scale_z (ClutterActor *self,
11421 gdouble scale_z)
11422 {
11423 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11424
11425 clutter_actor_set_scale_factor (self, CLUTTER_Z_AXIS, scale_z);
11426 }
11427
11428 /**
11429 * clutter_actor_set_scale_full:
11430 * @self: A #ClutterActor
11431 * @scale_x: double factor to scale actor by horizontally.
11432 * @scale_y: double factor to scale actor by vertically.
11433 * @center_x: X coordinate of the center of the scaling
11434 * @center_y: Y coordinate of the center of the scaling
11435 *
11436 * Scales an actor with the given factors around the given center
11437 * point. The center point is specified in pixels relative to the
11438 * anchor point (usually the top left corner of the actor).
11439 *
11440 * The #ClutterActor:scale-x and #ClutterActor:scale-y properties
11441 * are animatable.
11442 *
11443 * Since: 1.0
11444 *
11445 * Deprecated: 1.12: Use clutter_actor_set_pivot_point() to control
11446 * the scale center
11447 */
11448 void
clutter_actor_set_scale_full(ClutterActor * self,gdouble scale_x,gdouble scale_y,gfloat center_x,gfloat center_y)11449 clutter_actor_set_scale_full (ClutterActor *self,
11450 gdouble scale_x,
11451 gdouble scale_y,
11452 gfloat center_x,
11453 gfloat center_y)
11454 {
11455 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11456
11457 g_object_freeze_notify (G_OBJECT (self));
11458
11459 clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
11460 clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
11461 clutter_actor_set_scale_center (self, CLUTTER_X_AXIS, center_x);
11462 clutter_actor_set_scale_center (self, CLUTTER_Y_AXIS, center_y);
11463
11464 g_object_thaw_notify (G_OBJECT (self));
11465 }
11466
11467 /**
11468 * clutter_actor_set_scale_with_gravity:
11469 * @self: A #ClutterActor
11470 * @scale_x: double factor to scale actor by horizontally.
11471 * @scale_y: double factor to scale actor by vertically.
11472 * @gravity: the location of the scale center expressed as a compass
11473 * direction.
11474 *
11475 * Scales an actor with the given factors around the given
11476 * center point. The center point is specified as one of the compass
11477 * directions in #ClutterGravity. For example, setting it to north
11478 * will cause the top of the actor to remain unchanged and the rest of
11479 * the actor to expand left, right and downwards.
11480 *
11481 * The #ClutterActor:scale-x and #ClutterActor:scale-y properties are
11482 * animatable.
11483 *
11484 * Since: 1.0
11485 *
11486 * Deprecated: 1.12: Use clutter_actor_set_pivot_point() to set the
11487 * scale center using normalized coordinates instead.
11488 */
11489 void
clutter_actor_set_scale_with_gravity(ClutterActor * self,gdouble scale_x,gdouble scale_y,ClutterGravity gravity)11490 clutter_actor_set_scale_with_gravity (ClutterActor *self,
11491 gdouble scale_x,
11492 gdouble scale_y,
11493 ClutterGravity gravity)
11494 {
11495 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11496
11497 g_object_freeze_notify (G_OBJECT (self));
11498
11499 clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
11500 clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
11501 clutter_actor_set_scale_gravity (self, gravity);
11502
11503 g_object_thaw_notify (G_OBJECT (self));
11504 }
11505
11506 /**
11507 * clutter_actor_get_scale:
11508 * @self: A #ClutterActor
11509 * @scale_x: (out) (allow-none): Location to store horizonal
11510 * scale factor, or %NULL.
11511 * @scale_y: (out) (allow-none): Location to store vertical
11512 * scale factor, or %NULL.
11513 *
11514 * Retrieves an actors scale factors.
11515 *
11516 * Since: 0.2
11517 */
11518 void
clutter_actor_get_scale(ClutterActor * self,gdouble * scale_x,gdouble * scale_y)11519 clutter_actor_get_scale (ClutterActor *self,
11520 gdouble *scale_x,
11521 gdouble *scale_y)
11522 {
11523 const ClutterTransformInfo *info;
11524
11525 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11526
11527 info = _clutter_actor_get_transform_info_or_defaults (self);
11528
11529 if (scale_x)
11530 *scale_x = info->scale_x;
11531
11532 if (scale_y)
11533 *scale_y = info->scale_y;
11534 }
11535
11536 /**
11537 * clutter_actor_get_scale_z:
11538 * @self: A #ClutterActor
11539 *
11540 * Retrieves the scaling factor along the Z axis, as set using
11541 * clutter_actor_set_scale_z().
11542 *
11543 * Return value: the scaling factor along the Z axis
11544 *
11545 * Since: 1.12
11546 */
11547 gdouble
clutter_actor_get_scale_z(ClutterActor * self)11548 clutter_actor_get_scale_z (ClutterActor *self)
11549 {
11550 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.0);
11551
11552 return _clutter_actor_get_transform_info_or_defaults (self)->scale_z;
11553 }
11554
11555 /**
11556 * clutter_actor_get_scale_center:
11557 * @self: A #ClutterActor
11558 * @center_x: (out) (allow-none): Location to store the X position
11559 * of the scale center, or %NULL.
11560 * @center_y: (out) (allow-none): Location to store the Y position
11561 * of the scale center, or %NULL.
11562 *
11563 * Retrieves the scale center coordinate in pixels relative to the top
11564 * left corner of the actor. If the scale center was specified using a
11565 * #ClutterGravity this will calculate the pixel offset using the
11566 * current size of the actor.
11567 *
11568 * Since: 1.0
11569 *
11570 * Deprecated: 1.12: Use clutter_actor_get_pivot_point() instead.
11571 */
11572 void
clutter_actor_get_scale_center(ClutterActor * self,gfloat * center_x,gfloat * center_y)11573 clutter_actor_get_scale_center (ClutterActor *self,
11574 gfloat *center_x,
11575 gfloat *center_y)
11576 {
11577 const ClutterTransformInfo *info;
11578
11579 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11580
11581 info = _clutter_actor_get_transform_info_or_defaults (self);
11582
11583 clutter_anchor_coord_get_units (self, &info->scale_center,
11584 center_x,
11585 center_y,
11586 NULL);
11587 }
11588
11589 /**
11590 * clutter_actor_get_scale_gravity:
11591 * @self: A #ClutterActor
11592 *
11593 * Retrieves the scale center as a compass direction. If the scale
11594 * center was specified in pixels or units this will return
11595 * %CLUTTER_GRAVITY_NONE.
11596 *
11597 * Return value: the scale gravity
11598 *
11599 * Since: 1.0
11600 *
11601 * Deprecated: 1.12: Use clutter_actor_get_pivot_point() instead.
11602 */
11603 ClutterGravity
clutter_actor_get_scale_gravity(ClutterActor * self)11604 clutter_actor_get_scale_gravity (ClutterActor *self)
11605 {
11606 const ClutterTransformInfo *info;
11607
11608 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
11609
11610 info = _clutter_actor_get_transform_info_or_defaults (self);
11611
11612 return clutter_anchor_coord_get_gravity (&info->scale_center);
11613 }
11614
11615 static inline void
clutter_actor_set_opacity_internal(ClutterActor * self,guint8 opacity)11616 clutter_actor_set_opacity_internal (ClutterActor *self,
11617 guint8 opacity)
11618 {
11619 ClutterActorPrivate *priv = self->priv;
11620
11621 if (priv->opacity != opacity)
11622 {
11623 priv->opacity = opacity;
11624
11625 /* Queue a redraw from the flatten effect so that it can use
11626 its cached image if available instead of having to redraw the
11627 actual actor. If it doesn't end up using the FBO then the
11628 effect is still able to continue the paint anyway. If there
11629 is no flatten effect yet then this is equivalent to queueing
11630 a full redraw */
11631 _clutter_actor_queue_redraw_full (self,
11632 0, /* flags */
11633 NULL, /* clip */
11634 priv->flatten_effect);
11635
11636 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OPACITY]);
11637 }
11638 }
11639
11640 /**
11641 * clutter_actor_set_opacity:
11642 * @self: A #ClutterActor
11643 * @opacity: New opacity value for the actor.
11644 *
11645 * Sets the actor's opacity, with zero being completely transparent and
11646 * 255 (0xff) being fully opaque.
11647 *
11648 * The #ClutterActor:opacity property is animatable.
11649 */
11650 void
clutter_actor_set_opacity(ClutterActor * self,guint8 opacity)11651 clutter_actor_set_opacity (ClutterActor *self,
11652 guint8 opacity)
11653 {
11654 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11655
11656 _clutter_actor_create_transition (self, obj_props[PROP_OPACITY],
11657 self->priv->opacity,
11658 opacity);
11659 }
11660
11661 /*
11662 * clutter_actor_get_paint_opacity_internal:
11663 * @self: a #ClutterActor
11664 *
11665 * Retrieves the absolute opacity of the actor, as it appears on the stage
11666 *
11667 * This function does not do type checks
11668 *
11669 * Return value: the absolute opacity of the actor
11670 */
11671 static guint8
clutter_actor_get_paint_opacity_internal(ClutterActor * self)11672 clutter_actor_get_paint_opacity_internal (ClutterActor *self)
11673 {
11674 ClutterActorPrivate *priv = self->priv;
11675 ClutterActor *parent;
11676
11677 /* override the top-level opacity to always be 255; even in
11678 * case of ClutterStage:use-alpha being TRUE we want the rest
11679 * of the scene to be painted
11680 */
11681 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
11682 return 255;
11683
11684 if (priv->opacity_override >= 0)
11685 return priv->opacity_override;
11686
11687 parent = priv->parent;
11688
11689 /* Factor in the actual actors opacity with parents */
11690 if (parent != NULL)
11691 {
11692 guint8 opacity = clutter_actor_get_paint_opacity_internal (parent);
11693
11694 if (opacity != 0xff)
11695 return (opacity * priv->opacity) / 0xff;
11696 }
11697
11698 return priv->opacity;
11699
11700 }
11701
11702 /**
11703 * clutter_actor_get_paint_opacity:
11704 * @self: A #ClutterActor
11705 *
11706 * Retrieves the absolute opacity of the actor, as it appears on the stage.
11707 *
11708 * This function traverses the hierarchy chain and composites the opacity of
11709 * the actor with that of its parents.
11710 *
11711 * This function is intended for subclasses to use in the paint virtual
11712 * function, to paint themselves with the correct opacity.
11713 *
11714 * Return value: The actor opacity value.
11715 *
11716 * Since: 0.8
11717 */
11718 guint8
clutter_actor_get_paint_opacity(ClutterActor * self)11719 clutter_actor_get_paint_opacity (ClutterActor *self)
11720 {
11721 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11722
11723 return clutter_actor_get_paint_opacity_internal (self);
11724 }
11725
11726 /**
11727 * clutter_actor_get_opacity:
11728 * @self: a #ClutterActor
11729 *
11730 * Retrieves the opacity value of an actor, as set by
11731 * clutter_actor_set_opacity().
11732 *
11733 * For retrieving the absolute opacity of the actor inside a paint
11734 * virtual function, see clutter_actor_get_paint_opacity().
11735 *
11736 * Return value: the opacity of the actor
11737 */
11738 guint8
clutter_actor_get_opacity(ClutterActor * self)11739 clutter_actor_get_opacity (ClutterActor *self)
11740 {
11741 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11742
11743 return self->priv->opacity;
11744 }
11745
11746 /**
11747 * clutter_actor_set_offscreen_redirect:
11748 * @self: A #ClutterActor
11749 * @redirect: New offscreen redirect flags for the actor.
11750 *
11751 * Defines the circumstances where the actor should be redirected into
11752 * an offscreen image. The offscreen image is used to flatten the
11753 * actor into a single image while painting for two main reasons.
11754 * Firstly, when the actor is painted a second time without any of its
11755 * contents changing it can simply repaint the cached image without
11756 * descending further down the actor hierarchy. Secondly, it will make
11757 * the opacity look correct even if there are overlapping primitives
11758 * in the actor.
11759 *
11760 * Caching the actor could in some cases be a performance win and in
11761 * some cases be a performance lose so it is important to determine
11762 * which value is right for an actor before modifying this value. For
11763 * example, there is never any reason to flatten an actor that is just
11764 * a single texture (such as a #ClutterTexture) because it is
11765 * effectively already cached in an image so the offscreen would be
11766 * redundant. Also if the actor contains primitives that are far apart
11767 * with a large transparent area in the middle (such as a large
11768 * CluterGroup with a small actor in the top left and a small actor in
11769 * the bottom right) then the cached image will contain the entire
11770 * image of the large area and the paint will waste time blending all
11771 * of the transparent pixels in the middle.
11772 *
11773 * The default method of implementing opacity on a container simply
11774 * forwards on the opacity to all of the children. If the children are
11775 * overlapping then it will appear as if they are two separate glassy
11776 * objects and there will be a break in the color where they
11777 * overlap. By redirecting to an offscreen buffer it will be as if the
11778 * two opaque objects are combined into one and then made transparent
11779 * which is usually what is expected.
11780 *
11781 * The image below demonstrates the difference between redirecting and
11782 * not. The image shows two Clutter groups, each containing a red and
11783 * a green rectangle which overlap. The opacity on the group is set to
11784 * 128 (which is 50%). When the offscreen redirect is not used, the
11785 * red rectangle can be seen through the blue rectangle as if the two
11786 * rectangles were separately transparent. When the redirect is used
11787 * the group as a whole is transparent instead so the red rectangle is
11788 * not visible where they overlap.
11789 *
11790 * <figure id="offscreen-redirect">
11791 * <title>Sample of using an offscreen redirect for transparency</title>
11792 * <graphic fileref="offscreen-redirect.png" format="PNG"/>
11793 * </figure>
11794 *
11795 * The default value for this property is 0, so we effectively will
11796 * never redirect an actor offscreen by default. This means that there
11797 * are times that transparent actors may look glassy as described
11798 * above. The reason this is the default is because there is a
11799 * performance trade off between quality and performance here. In many
11800 * cases the default form of glassy opacity looks good enough, but if
11801 * it's not you will need to set the
11802 * %CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY flag to enable
11803 * redirection for opacity.
11804 *
11805 * Custom actors that don't contain any overlapping primitives are
11806 * recommended to override the has_overlaps() virtual to return %FALSE
11807 * for maximum efficiency.
11808 *
11809 * Since: 1.8
11810 */
11811 void
clutter_actor_set_offscreen_redirect(ClutterActor * self,ClutterOffscreenRedirect redirect)11812 clutter_actor_set_offscreen_redirect (ClutterActor *self,
11813 ClutterOffscreenRedirect redirect)
11814 {
11815 ClutterActorPrivate *priv;
11816
11817 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11818
11819 priv = self->priv;
11820
11821 if (priv->offscreen_redirect != redirect)
11822 {
11823 priv->offscreen_redirect = redirect;
11824
11825 /* Queue a redraw from the effect so that it can use its cached
11826 image if available instead of having to redraw the actual
11827 actor. If it doesn't end up using the FBO then the effect is
11828 still able to continue the paint anyway. If there is no
11829 effect then this is equivalent to queuing a full redraw */
11830 _clutter_actor_queue_redraw_full (self,
11831 0, /* flags */
11832 NULL, /* clip */
11833 priv->flatten_effect);
11834
11835 g_object_notify_by_pspec (G_OBJECT (self),
11836 obj_props[PROP_OFFSCREEN_REDIRECT]);
11837 }
11838 }
11839
11840 /**
11841 * clutter_actor_get_offscreen_redirect:
11842 * @self: a #ClutterActor
11843 *
11844 * Retrieves whether to redirect the actor to an offscreen buffer, as
11845 * set by clutter_actor_set_offscreen_redirect().
11846 *
11847 * Return value: the value of the offscreen-redirect property of the actor
11848 *
11849 * Since: 1.8
11850 */
11851 ClutterOffscreenRedirect
clutter_actor_get_offscreen_redirect(ClutterActor * self)11852 clutter_actor_get_offscreen_redirect (ClutterActor *self)
11853 {
11854 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11855
11856 return self->priv->offscreen_redirect;
11857 }
11858
11859 /**
11860 * clutter_actor_set_name:
11861 * @self: A #ClutterActor
11862 * @name: Textual tag to apply to actor
11863 *
11864 * Sets the given name to @self. The name can be used to identify
11865 * a #ClutterActor.
11866 */
11867 void
clutter_actor_set_name(ClutterActor * self,const gchar * name)11868 clutter_actor_set_name (ClutterActor *self,
11869 const gchar *name)
11870 {
11871 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11872
11873 g_free (self->priv->name);
11874 self->priv->name = g_strdup (name);
11875
11876 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
11877 }
11878
11879 /**
11880 * clutter_actor_get_name:
11881 * @self: A #ClutterActor
11882 *
11883 * Retrieves the name of @self.
11884 *
11885 * Return value: the name of the actor, or %NULL. The returned string is
11886 * owned by the actor and should not be modified or freed.
11887 */
11888 const gchar *
clutter_actor_get_name(ClutterActor * self)11889 clutter_actor_get_name (ClutterActor *self)
11890 {
11891 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
11892
11893 return self->priv->name;
11894 }
11895
11896 /**
11897 * clutter_actor_get_gid:
11898 * @self: A #ClutterActor
11899 *
11900 * Retrieves the unique id for @self.
11901 *
11902 * Return value: Globally unique value for this object instance.
11903 *
11904 * Since: 0.6
11905 *
11906 * Deprecated: 1.8: The id is not used any longer, and this function
11907 * always returns 0.
11908 */
11909 guint32
clutter_actor_get_gid(ClutterActor * self)11910 clutter_actor_get_gid (ClutterActor *self)
11911 {
11912 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11913
11914 return 0;
11915 }
11916
11917 static inline void
clutter_actor_set_depth_internal(ClutterActor * self,float depth)11918 clutter_actor_set_depth_internal (ClutterActor *self,
11919 float depth)
11920 {
11921 ClutterTransformInfo *info;
11922
11923 info = _clutter_actor_get_transform_info (self);
11924
11925 if (info->z_position != depth)
11926 {
11927 /* Sets Z value - XXX 2.0: should we invert? */
11928 info->z_position = depth;
11929
11930 self->priv->transform_valid = FALSE;
11931
11932 /* FIXME - remove this crap; sadly, there are still containers
11933 * in Clutter that depend on this utter brain damage
11934 */
11935 clutter_container_sort_depth_order (CLUTTER_CONTAINER (self));
11936
11937 clutter_actor_queue_redraw (self);
11938
11939 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DEPTH]);
11940 }
11941 }
11942
11943 static inline void
clutter_actor_set_z_position_internal(ClutterActor * self,float z_position)11944 clutter_actor_set_z_position_internal (ClutterActor *self,
11945 float z_position)
11946 {
11947 ClutterTransformInfo *info;
11948
11949 info = _clutter_actor_get_transform_info (self);
11950
11951 if (memcmp (&info->z_position, &z_position, sizeof (float)) != 0)
11952 {
11953 info->z_position = z_position;
11954
11955 self->priv->transform_valid = FALSE;
11956
11957 clutter_actor_queue_redraw (self);
11958
11959 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Z_POSITION]);
11960 }
11961 }
11962
11963 /**
11964 * clutter_actor_set_z_position:
11965 * @self: a #ClutterActor
11966 * @z_position: the position on the Z axis
11967 *
11968 * Sets the actor's position on the Z axis.
11969 *
11970 * See #ClutterActor:z-position.
11971 *
11972 * Since: 1.12
11973 */
11974 void
clutter_actor_set_z_position(ClutterActor * self,gfloat z_position)11975 clutter_actor_set_z_position (ClutterActor *self,
11976 gfloat z_position)
11977 {
11978 const ClutterTransformInfo *info;
11979
11980 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11981
11982 info = _clutter_actor_get_transform_info_or_defaults (self);
11983
11984 _clutter_actor_create_transition (self, obj_props[PROP_Z_POSITION],
11985 info->z_position,
11986 z_position);
11987 }
11988
11989 /**
11990 * clutter_actor_get_z_position:
11991 * @self: a #ClutterActor
11992 *
11993 * Retrieves the actor's position on the Z axis.
11994 *
11995 * Return value: the position on the Z axis.
11996 *
11997 * Since: 1.12
11998 */
11999 gfloat
clutter_actor_get_z_position(ClutterActor * self)12000 clutter_actor_get_z_position (ClutterActor *self)
12001 {
12002 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
12003
12004 return _clutter_actor_get_transform_info_or_defaults (self)->z_position;
12005 }
12006
12007 /**
12008 * clutter_actor_set_pivot_point:
12009 * @self: a #ClutterActor
12010 * @pivot_x: the normalized X coordinate of the pivot point
12011 * @pivot_y: the normalized Y coordinate of the pivot point
12012 *
12013 * Sets the position of the #ClutterActor:pivot-point around which the
12014 * scaling and rotation transformations occur.
12015 *
12016 * The pivot point's coordinates are in normalized space, with the (0, 0)
12017 * point being the top left corner of the actor, and the (1, 1) point being
12018 * the bottom right corner.
12019 *
12020 * Since: 1.12
12021 */
12022 void
clutter_actor_set_pivot_point(ClutterActor * self,gfloat pivot_x,gfloat pivot_y)12023 clutter_actor_set_pivot_point (ClutterActor *self,
12024 gfloat pivot_x,
12025 gfloat pivot_y)
12026 {
12027 ClutterPoint pivot = CLUTTER_POINT_INIT (pivot_x, pivot_y);
12028 const ClutterTransformInfo *info;
12029
12030 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12031
12032 info = _clutter_actor_get_transform_info_or_defaults (self);
12033 _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT],
12034 &info->pivot,
12035 &pivot);
12036 }
12037
12038 /**
12039 * clutter_actor_get_pivot_point:
12040 * @self: a #ClutterActor
12041 * @pivot_x: (out) (allow-none): return location for the normalized X
12042 * coordinate of the pivot point, or %NULL
12043 * @pivot_y: (out) (allow-none): return location for the normalized Y
12044 * coordinate of the pivot point, or %NULL
12045 *
12046 * Retrieves the coordinates of the #ClutterActor:pivot-point.
12047 *
12048 * Since: 1.12
12049 */
12050 void
clutter_actor_get_pivot_point(ClutterActor * self,gfloat * pivot_x,gfloat * pivot_y)12051 clutter_actor_get_pivot_point (ClutterActor *self,
12052 gfloat *pivot_x,
12053 gfloat *pivot_y)
12054 {
12055 const ClutterTransformInfo *info;
12056
12057 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12058
12059 info = _clutter_actor_get_transform_info_or_defaults (self);
12060
12061 if (pivot_x != NULL)
12062 *pivot_x = info->pivot.x;
12063
12064 if (pivot_y != NULL)
12065 *pivot_y = info->pivot.y;
12066 }
12067
12068 /**
12069 * clutter_actor_set_pivot_point_z:
12070 * @self: a #ClutterActor
12071 * @pivot_z: the Z coordinate of the actor's pivot point
12072 *
12073 * Sets the component on the Z axis of the #ClutterActor:pivot-point around
12074 * which the scaling and rotation transformations occur.
12075 *
12076 * The @pivot_z value is expressed as a distance along the Z axis.
12077 *
12078 * Since: 1.12
12079 */
12080 void
clutter_actor_set_pivot_point_z(ClutterActor * self,gfloat pivot_z)12081 clutter_actor_set_pivot_point_z (ClutterActor *self,
12082 gfloat pivot_z)
12083 {
12084 const ClutterTransformInfo *info;
12085
12086 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12087
12088 info = _clutter_actor_get_transform_info_or_defaults (self);
12089 _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT_Z],
12090 info->pivot_z,
12091 pivot_z);
12092 }
12093
12094 /**
12095 * clutter_actor_get_pivot_point_z:
12096 * @self: a #ClutterActor
12097 *
12098 * Retrieves the Z component of the #ClutterActor:pivot-point.
12099 *
12100 * Since: 1.12
12101 */
12102 gfloat
clutter_actor_get_pivot_point_z(ClutterActor * self)12103 clutter_actor_get_pivot_point_z (ClutterActor *self)
12104 {
12105 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
12106
12107 return _clutter_actor_get_transform_info_or_defaults (self)->pivot_z;
12108 }
12109
12110 /**
12111 * clutter_actor_set_depth:
12112 * @self: a #ClutterActor
12113 * @depth: Z co-ord
12114 *
12115 * Sets the Z coordinate of @self to @depth.
12116 *
12117 * The unit used by @depth is dependant on the perspective setup. See
12118 * also clutter_stage_set_perspective().
12119 *
12120 * Deprecated: 1.12: Use clutter_actor_set_z_position() instead.
12121 */
12122 void
clutter_actor_set_depth(ClutterActor * self,gfloat depth)12123 clutter_actor_set_depth (ClutterActor *self,
12124 gfloat depth)
12125 {
12126 const ClutterTransformInfo *info;
12127
12128 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12129
12130 info = _clutter_actor_get_transform_info_or_defaults (self);
12131 _clutter_actor_create_transition (self, obj_props[PROP_DEPTH],
12132 info->z_position,
12133 depth);
12134 }
12135
12136 /**
12137 * clutter_actor_get_depth:
12138 * @self: a #ClutterActor
12139 *
12140 * Retrieves the depth of @self.
12141 *
12142 * Return value: the depth of the actor
12143 *
12144 * Deprecated: 1.12: Use clutter_actor_get_z_position() instead.
12145 */
12146 gfloat
clutter_actor_get_depth(ClutterActor * self)12147 clutter_actor_get_depth (ClutterActor *self)
12148 {
12149 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0);
12150
12151 return _clutter_actor_get_transform_info_or_defaults (self)->z_position;
12152 }
12153
12154 /**
12155 * clutter_actor_set_rotation:
12156 * @self: a #ClutterActor
12157 * @axis: the axis of rotation
12158 * @angle: the angle of rotation
12159 * @x: X coordinate of the rotation center
12160 * @y: Y coordinate of the rotation center
12161 * @z: Z coordinate of the rotation center
12162 *
12163 * Sets the rotation angle of @self around the given axis.
12164 *
12165 * The rotation center coordinates used depend on the value of @axis:
12166 *
12167 * - %CLUTTER_X_AXIS requires @y and @z
12168 * - %CLUTTER_Y_AXIS requires @x and @z
12169 * - %CLUTTER_Z_AXIS requires @x and @y
12170 *
12171 * The rotation coordinates are relative to the anchor point of the
12172 * actor, set using clutter_actor_set_anchor_point(). If no anchor
12173 * point is set, the upper left corner is assumed as the origin.
12174 *
12175 * Since: 0.8
12176 *
12177 * Deprecated: 1.12: Use clutter_actor_set_rotation_angle() and
12178 * clutter_actor_set_pivot_point() instead.
12179 */
12180 void
clutter_actor_set_rotation(ClutterActor * self,ClutterRotateAxis axis,gdouble angle,gfloat x,gfloat y,gfloat z)12181 clutter_actor_set_rotation (ClutterActor *self,
12182 ClutterRotateAxis axis,
12183 gdouble angle,
12184 gfloat x,
12185 gfloat y,
12186 gfloat z)
12187 {
12188 ClutterVertex v;
12189
12190 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12191
12192 v.x = x;
12193 v.y = y;
12194 v.z = z;
12195
12196 g_object_freeze_notify (G_OBJECT (self));
12197
12198 clutter_actor_set_rotation_angle (self, axis, angle);
12199 clutter_actor_set_rotation_center_internal (self, axis, &v);
12200
12201 g_object_thaw_notify (G_OBJECT (self));
12202 }
12203
12204 /**
12205 * clutter_actor_set_z_rotation_from_gravity:
12206 * @self: a #ClutterActor
12207 * @angle: the angle of rotation
12208 * @gravity: the center point of the rotation
12209 *
12210 * Sets the rotation angle of @self around the Z axis using the center
12211 * point specified as a compass point. For example to rotate such that
12212 * the center of the actor remains static you can use
12213 * %CLUTTER_GRAVITY_CENTER. If the actor changes size the center point
12214 * will move accordingly.
12215 *
12216 * Since: 1.0
12217 *
12218 * Deprecated: 1.12: Use clutter_actor_set_rotation_angle() and
12219 * clutter_actor_set_pivot_point() instead.
12220 */
12221 void
clutter_actor_set_z_rotation_from_gravity(ClutterActor * self,gdouble angle,ClutterGravity gravity)12222 clutter_actor_set_z_rotation_from_gravity (ClutterActor *self,
12223 gdouble angle,
12224 ClutterGravity gravity)
12225 {
12226 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12227
12228 if (gravity == CLUTTER_GRAVITY_NONE)
12229 clutter_actor_set_rotation (self, CLUTTER_Z_AXIS, angle, 0, 0, 0);
12230 else
12231 {
12232 GObject *obj = G_OBJECT (self);
12233 ClutterTransformInfo *info;
12234 GParamSpec *pspec;
12235
12236 pspec = obj_props[PROP_ROTATION_ANGLE_Z];
12237 info = _clutter_actor_get_transform_info (self);
12238
12239 g_object_freeze_notify (obj);
12240
12241 clutter_actor_set_rotation_angle_internal (self, angle, pspec);
12242
12243 clutter_anchor_coord_set_gravity (&info->rz_center, gravity);
12244 g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]);
12245 g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z]);
12246
12247 g_object_thaw_notify (obj);
12248 }
12249 }
12250
12251 /**
12252 * clutter_actor_get_rotation:
12253 * @self: a #ClutterActor
12254 * @axis: the axis of rotation
12255 * @x: (out): return value for the X coordinate of the center of rotation
12256 * @y: (out): return value for the Y coordinate of the center of rotation
12257 * @z: (out): return value for the Z coordinate of the center of rotation
12258 *
12259 * Retrieves the angle and center of rotation on the given axis,
12260 * set using clutter_actor_set_rotation().
12261 *
12262 * Return value: the angle of rotation
12263 *
12264 * Since: 0.8
12265 *
12266 * Deprecated: 1.12: Use clutter_actor_get_rotation_angle() and
12267 * clutter_actor_get_pivot_point() instead.
12268 */
12269 gdouble
clutter_actor_get_rotation(ClutterActor * self,ClutterRotateAxis axis,gfloat * x,gfloat * y,gfloat * z)12270 clutter_actor_get_rotation (ClutterActor *self,
12271 ClutterRotateAxis axis,
12272 gfloat *x,
12273 gfloat *y,
12274 gfloat *z)
12275 {
12276 const ClutterTransformInfo *info;
12277 const AnchorCoord *anchor_coord;
12278 gdouble retval = 0;
12279
12280 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
12281
12282 info = _clutter_actor_get_transform_info_or_defaults (self);
12283
12284 switch (axis)
12285 {
12286 case CLUTTER_X_AXIS:
12287 anchor_coord = &info->rx_center;
12288 retval = info->rx_angle;
12289 break;
12290
12291 case CLUTTER_Y_AXIS:
12292 anchor_coord = &info->ry_center;
12293 retval = info->ry_angle;
12294 break;
12295
12296 case CLUTTER_Z_AXIS:
12297 anchor_coord = &info->rz_center;
12298 retval = info->rz_angle;
12299 break;
12300
12301 default:
12302 anchor_coord = NULL;
12303 retval = 0.0;
12304 break;
12305 }
12306
12307 clutter_anchor_coord_get_units (self, anchor_coord, x, y, z);
12308
12309 return retval;
12310 }
12311
12312 /**
12313 * clutter_actor_get_z_rotation_gravity:
12314 * @self: A #ClutterActor
12315 *
12316 * Retrieves the center for the rotation around the Z axis as a
12317 * compass direction. If the center was specified in pixels or units
12318 * this will return %CLUTTER_GRAVITY_NONE.
12319 *
12320 * Return value: the Z rotation center
12321 *
12322 * Since: 1.0
12323 *
12324 * Deprecated: 1.12: Use the #ClutterActor:pivot-point instead of
12325 * a #ClutterGravity
12326 */
12327 ClutterGravity
clutter_actor_get_z_rotation_gravity(ClutterActor * self)12328 clutter_actor_get_z_rotation_gravity (ClutterActor *self)
12329 {
12330 const ClutterTransformInfo *info;
12331
12332 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
12333
12334 info = _clutter_actor_get_transform_info_or_defaults (self);
12335
12336 return clutter_anchor_coord_get_gravity (&info->rz_center);
12337 }
12338
12339 /**
12340 * clutter_actor_set_clip:
12341 * @self: A #ClutterActor
12342 * @xoff: X offset of the clip rectangle
12343 * @yoff: Y offset of the clip rectangle
12344 * @width: Width of the clip rectangle
12345 * @height: Height of the clip rectangle
12346 *
12347 * Sets clip area for @self. The clip area is always computed from the
12348 * upper left corner of the actor, even if the anchor point is set
12349 * otherwise.
12350 *
12351 * Since: 0.6
12352 */
12353 void
clutter_actor_set_clip(ClutterActor * self,gfloat xoff,gfloat yoff,gfloat width,gfloat height)12354 clutter_actor_set_clip (ClutterActor *self,
12355 gfloat xoff,
12356 gfloat yoff,
12357 gfloat width,
12358 gfloat height)
12359 {
12360 ClutterActorPrivate *priv;
12361 GObject *obj;
12362
12363 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12364
12365 priv = self->priv;
12366
12367 if (priv->has_clip &&
12368 priv->clip.origin.x == xoff &&
12369 priv->clip.origin.y == yoff &&
12370 priv->clip.size.width == width &&
12371 priv->clip.size.height == height)
12372 return;
12373
12374 obj = G_OBJECT (self);
12375
12376 priv->clip.origin.x = xoff;
12377 priv->clip.origin.y = yoff;
12378 priv->clip.size.width = width;
12379 priv->clip.size.height = height;
12380
12381 priv->has_clip = TRUE;
12382
12383 clutter_actor_queue_redraw (self);
12384
12385 g_object_notify_by_pspec (obj, obj_props[PROP_CLIP]);
12386 g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
12387 g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
12388 }
12389
12390 /**
12391 * clutter_actor_remove_clip:
12392 * @self: A #ClutterActor
12393 *
12394 * Removes clip area from @self.
12395 */
12396 void
clutter_actor_remove_clip(ClutterActor * self)12397 clutter_actor_remove_clip (ClutterActor *self)
12398 {
12399 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12400
12401 if (!self->priv->has_clip)
12402 return;
12403
12404 self->priv->has_clip = FALSE;
12405
12406 clutter_actor_queue_redraw (self);
12407
12408 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
12409 }
12410
12411 /**
12412 * clutter_actor_has_clip:
12413 * @self: a #ClutterActor
12414 *
12415 * Determines whether the actor has a clip area set or not.
12416 *
12417 * Return value: %TRUE if the actor has a clip area set.
12418 *
12419 * Since: 0.2
12420 */
12421 gboolean
clutter_actor_has_clip(ClutterActor * self)12422 clutter_actor_has_clip (ClutterActor *self)
12423 {
12424 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
12425
12426 return self->priv->has_clip;
12427 }
12428
12429 /**
12430 * clutter_actor_get_clip:
12431 * @self: a #ClutterActor
12432 * @xoff: (out) (allow-none): return location for the X offset of
12433 * the clip rectangle, or %NULL
12434 * @yoff: (out) (allow-none): return location for the Y offset of
12435 * the clip rectangle, or %NULL
12436 * @width: (out) (allow-none): return location for the width of
12437 * the clip rectangle, or %NULL
12438 * @height: (out) (allow-none): return location for the height of
12439 * the clip rectangle, or %NULL
12440 *
12441 * Gets the clip area for @self, if any is set.
12442 *
12443 * Since: 0.6
12444 */
12445 void
clutter_actor_get_clip(ClutterActor * self,gfloat * xoff,gfloat * yoff,gfloat * width,gfloat * height)12446 clutter_actor_get_clip (ClutterActor *self,
12447 gfloat *xoff,
12448 gfloat *yoff,
12449 gfloat *width,
12450 gfloat *height)
12451 {
12452 ClutterActorPrivate *priv;
12453
12454 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12455
12456 priv = self->priv;
12457
12458 if (!priv->has_clip)
12459 return;
12460
12461 if (xoff != NULL)
12462 *xoff = priv->clip.origin.x;
12463
12464 if (yoff != NULL)
12465 *yoff = priv->clip.origin.y;
12466
12467 if (width != NULL)
12468 *width = priv->clip.size.width;
12469
12470 if (height != NULL)
12471 *height = priv->clip.size.height;
12472 }
12473
12474 /**
12475 * clutter_actor_get_children:
12476 * @self: a #ClutterActor
12477 *
12478 * Retrieves the list of children of @self.
12479 *
12480 * Return value: (transfer container) (element-type ClutterActor): A newly
12481 * allocated #GList of #ClutterActor<!-- -->s. Use g_list_free() when
12482 * done.
12483 *
12484 * Since: 1.10
12485 */
12486 GList *
clutter_actor_get_children(ClutterActor * self)12487 clutter_actor_get_children (ClutterActor *self)
12488 {
12489 ClutterActor *iter;
12490 GList *res;
12491
12492 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
12493
12494 /* we walk the list backward so that we can use prepend(),
12495 * which is O(1)
12496 */
12497 for (iter = self->priv->last_child, res = NULL;
12498 iter != NULL;
12499 iter = iter->priv->prev_sibling)
12500 {
12501 res = g_list_prepend (res, iter);
12502 }
12503
12504 return res;
12505 }
12506
12507 /*< private >
12508 * insert_child_at_depth:
12509 * @self: a #ClutterActor
12510 * @child: a #ClutterActor
12511 *
12512 * Inserts @child inside the list of children held by @self, using
12513 * the depth as the insertion criteria.
12514 *
12515 * This sadly makes the insertion not O(1), but we can keep the
12516 * list sorted so that the painters algorithm we use for painting
12517 * the children will work correctly.
12518 */
12519 static void
insert_child_at_depth(ClutterActor * self,ClutterActor * child,gpointer dummy G_GNUC_UNUSED)12520 insert_child_at_depth (ClutterActor *self,
12521 ClutterActor *child,
12522 gpointer dummy G_GNUC_UNUSED)
12523 {
12524 ClutterActor *iter;
12525 float child_depth;
12526
12527 child->priv->parent = self;
12528
12529 child_depth =
12530 _clutter_actor_get_transform_info_or_defaults (child)->z_position;
12531
12532 /* special-case the first child */
12533 if (self->priv->n_children == 0)
12534 {
12535 self->priv->first_child = child;
12536 self->priv->last_child = child;
12537
12538 child->priv->next_sibling = NULL;
12539 child->priv->prev_sibling = NULL;
12540
12541 return;
12542 }
12543
12544 /* Find the right place to insert the child so that it will still be
12545 sorted and the child will be after all of the actors at the same
12546 dept */
12547 for (iter = self->priv->first_child;
12548 iter != NULL;
12549 iter = iter->priv->next_sibling)
12550 {
12551 float iter_depth;
12552
12553 iter_depth =
12554 _clutter_actor_get_transform_info_or_defaults (iter)->z_position;
12555
12556 if (iter_depth > child_depth)
12557 break;
12558 }
12559
12560 if (iter != NULL)
12561 {
12562 ClutterActor *tmp = iter->priv->prev_sibling;
12563
12564 if (tmp != NULL)
12565 tmp->priv->next_sibling = child;
12566
12567 /* Insert the node before the found one */
12568 child->priv->prev_sibling = iter->priv->prev_sibling;
12569 child->priv->next_sibling = iter;
12570 iter->priv->prev_sibling = child;
12571 }
12572 else
12573 {
12574 ClutterActor *tmp = self->priv->last_child;
12575
12576 if (tmp != NULL)
12577 tmp->priv->next_sibling = child;
12578
12579 /* insert the node at the end of the list */
12580 child->priv->prev_sibling = self->priv->last_child;
12581 child->priv->next_sibling = NULL;
12582 }
12583
12584 if (child->priv->prev_sibling == NULL)
12585 self->priv->first_child = child;
12586
12587 if (child->priv->next_sibling == NULL)
12588 self->priv->last_child = child;
12589 }
12590
12591 static void
insert_child_at_index(ClutterActor * self,ClutterActor * child,gpointer data_)12592 insert_child_at_index (ClutterActor *self,
12593 ClutterActor *child,
12594 gpointer data_)
12595 {
12596 gint index_ = GPOINTER_TO_INT (data_);
12597
12598 child->priv->parent = self;
12599
12600 if (index_ == 0)
12601 {
12602 ClutterActor *tmp = self->priv->first_child;
12603
12604 if (tmp != NULL)
12605 tmp->priv->prev_sibling = child;
12606
12607 child->priv->prev_sibling = NULL;
12608 child->priv->next_sibling = tmp;
12609 }
12610 else if (index_ < 0 || index_ >= self->priv->n_children)
12611 {
12612 ClutterActor *tmp = self->priv->last_child;
12613
12614 if (tmp != NULL)
12615 tmp->priv->next_sibling = child;
12616
12617 child->priv->prev_sibling = tmp;
12618 child->priv->next_sibling = NULL;
12619 }
12620 else
12621 {
12622 ClutterActor *iter;
12623 int i;
12624
12625 for (iter = self->priv->first_child, i = 0;
12626 iter != NULL;
12627 iter = iter->priv->next_sibling, i += 1)
12628 {
12629 if (index_ == i)
12630 {
12631 ClutterActor *tmp = iter->priv->prev_sibling;
12632
12633 child->priv->prev_sibling = tmp;
12634 child->priv->next_sibling = iter;
12635
12636 iter->priv->prev_sibling = child;
12637
12638 if (tmp != NULL)
12639 tmp->priv->next_sibling = child;
12640
12641 break;
12642 }
12643 }
12644 }
12645
12646 if (child->priv->prev_sibling == NULL)
12647 self->priv->first_child = child;
12648
12649 if (child->priv->next_sibling == NULL)
12650 self->priv->last_child = child;
12651 }
12652
12653 static void
insert_child_above(ClutterActor * self,ClutterActor * child,gpointer data)12654 insert_child_above (ClutterActor *self,
12655 ClutterActor *child,
12656 gpointer data)
12657 {
12658 ClutterActor *sibling = data;
12659
12660 child->priv->parent = self;
12661
12662 if (sibling == NULL)
12663 sibling = self->priv->last_child;
12664
12665 child->priv->prev_sibling = sibling;
12666
12667 if (sibling != NULL)
12668 {
12669 ClutterActor *tmp = sibling->priv->next_sibling;
12670
12671 child->priv->next_sibling = tmp;
12672
12673 if (tmp != NULL)
12674 tmp->priv->prev_sibling = child;
12675
12676 sibling->priv->next_sibling = child;
12677 }
12678 else
12679 child->priv->next_sibling = NULL;
12680
12681 if (child->priv->prev_sibling == NULL)
12682 self->priv->first_child = child;
12683
12684 if (child->priv->next_sibling == NULL)
12685 self->priv->last_child = child;
12686 }
12687
12688 static void
insert_child_below(ClutterActor * self,ClutterActor * child,gpointer data)12689 insert_child_below (ClutterActor *self,
12690 ClutterActor *child,
12691 gpointer data)
12692 {
12693 ClutterActor *sibling = data;
12694
12695 child->priv->parent = self;
12696
12697 if (sibling == NULL)
12698 sibling = self->priv->first_child;
12699
12700 child->priv->next_sibling = sibling;
12701
12702 if (sibling != NULL)
12703 {
12704 ClutterActor *tmp = sibling->priv->prev_sibling;
12705
12706 child->priv->prev_sibling = tmp;
12707
12708 if (tmp != NULL)
12709 tmp->priv->next_sibling = child;
12710
12711 sibling->priv->prev_sibling = child;
12712 }
12713 else
12714 child->priv->prev_sibling = NULL;
12715
12716 if (child->priv->prev_sibling == NULL)
12717 self->priv->first_child = child;
12718
12719 if (child->priv->next_sibling == NULL)
12720 self->priv->last_child = child;
12721 }
12722
12723 typedef void (* ClutterActorAddChildFunc) (ClutterActor *parent,
12724 ClutterActor *child,
12725 gpointer data);
12726
12727 typedef enum {
12728 ADD_CHILD_CREATE_META = 1 << 0,
12729 ADD_CHILD_EMIT_PARENT_SET = 1 << 1,
12730 ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2,
12731 ADD_CHILD_CHECK_STATE = 1 << 3,
12732 ADD_CHILD_NOTIFY_FIRST_LAST = 1 << 4,
12733 ADD_CHILD_SHOW_ON_SET_PARENT = 1 << 5,
12734
12735 /* default flags for public API */
12736 ADD_CHILD_DEFAULT_FLAGS = ADD_CHILD_CREATE_META |
12737 ADD_CHILD_EMIT_PARENT_SET |
12738 ADD_CHILD_EMIT_ACTOR_ADDED |
12739 ADD_CHILD_CHECK_STATE |
12740 ADD_CHILD_NOTIFY_FIRST_LAST |
12741 ADD_CHILD_SHOW_ON_SET_PARENT,
12742
12743 /* flags for legacy/deprecated API */
12744 ADD_CHILD_LEGACY_FLAGS = ADD_CHILD_EMIT_PARENT_SET |
12745 ADD_CHILD_CHECK_STATE |
12746 ADD_CHILD_NOTIFY_FIRST_LAST |
12747 ADD_CHILD_SHOW_ON_SET_PARENT
12748 } ClutterActorAddChildFlags;
12749
12750 /*< private >
12751 * clutter_actor_add_child_internal:
12752 * @self: a #ClutterActor
12753 * @child: a #ClutterActor
12754 * @flags: control flags for actions
12755 * @add_func: delegate function
12756 * @data: (closure): data to pass to @add_func
12757 *
12758 * Adds @child to the list of children of @self.
12759 *
12760 * The actual insertion inside the list is delegated to @add_func: this
12761 * function will just set up the state, perform basic checks, and emit
12762 * signals.
12763 *
12764 * The @flags argument is used to perform additional operations.
12765 */
12766 static inline void
clutter_actor_add_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorAddChildFlags flags,ClutterActorAddChildFunc add_func,gpointer data)12767 clutter_actor_add_child_internal (ClutterActor *self,
12768 ClutterActor *child,
12769 ClutterActorAddChildFlags flags,
12770 ClutterActorAddChildFunc add_func,
12771 gpointer data)
12772 {
12773 ClutterTextDirection text_dir;
12774 gboolean create_meta;
12775 gboolean emit_parent_set, emit_actor_added;
12776 gboolean check_state;
12777 gboolean notify_first_last;
12778 gboolean show_on_set_parent;
12779 ClutterActor *old_first_child, *old_last_child;
12780 GObject *obj;
12781
12782 if (self == child)
12783 {
12784 g_warning ("Cannot add the actor '%s' to itself.",
12785 _clutter_actor_get_debug_name (self));
12786 return;
12787 }
12788
12789 if (child->priv->parent != NULL)
12790 {
12791 g_warning ("The actor '%s' already has a parent, '%s'. You must "
12792 "use clutter_actor_remove_child() first.",
12793 _clutter_actor_get_debug_name (child),
12794 _clutter_actor_get_debug_name (child->priv->parent));
12795 return;
12796 }
12797
12798 if (CLUTTER_ACTOR_IS_TOPLEVEL (child))
12799 {
12800 g_warning ("The actor '%s' is a top-level actor, and cannot be "
12801 "a child of another actor.",
12802 _clutter_actor_get_debug_name (child));
12803 return;
12804 }
12805
12806 /* the following check disallows calling methods that change the stacking
12807 * order within the destruction sequence, by triggering a critical
12808 * warning first, and leaving the actor in an undefined state, which
12809 * then ends up being caught by an assertion.
12810 *
12811 * the reproducible sequence is:
12812 *
12813 * - actor gets destroyed;
12814 * - another actor, linked to the first, will try to change the
12815 * stacking order of the first actor;
12816 * - changing the stacking order is a composite operation composed
12817 * by the following steps:
12818 * 1. ref() the child;
12819 * 2. remove_child_internal(), which removes the reference;
12820 * 3. add_child_internal(), which adds a reference;
12821 * - the state of the actor is not changed between (2) and (3), as
12822 * it could be an expensive recomputation;
12823 * - if (3) bails out, then the actor is in an undefined state, but
12824 * still alive;
12825 * - the destruction sequence terminates, but the actor is unparented
12826 * while its state indicates being parented instead.
12827 * - assertion failure.
12828 *
12829 * the obvious fix would be to decompose each set_child_*_sibling()
12830 * method into proper remove_child()/add_child(), with state validation;
12831 * this may cause excessive work, though, and trigger a cascade of other
12832 * bugs in code that assumes that a change in the stacking order is an
12833 * atomic operation.
12834 *
12835 * another potential fix is to just remove this check here, and let
12836 * code doing stacking order changes inside the destruction sequence
12837 * of an actor continue doing the stacking changes as before; this
12838 * option still performs more work than necessary.
12839 *
12840 * the third fix is to silently bail out early from every
12841 * set_child_*_sibling() and set_child_at_index() method, and avoid
12842 * doing stack changes altogether; Clutter implements this last option.
12843 *
12844 * see bug: https://bugzilla.gnome.org/show_bug.cgi?id=670647
12845 */
12846 if (CLUTTER_ACTOR_IN_DESTRUCTION (child))
12847 {
12848 g_warning ("The actor '%s' is currently being destroyed, and "
12849 "cannot be added as a child of another actor.",
12850 _clutter_actor_get_debug_name (child));
12851 return;
12852 }
12853
12854 create_meta = (flags & ADD_CHILD_CREATE_META) != 0;
12855 emit_parent_set = (flags & ADD_CHILD_EMIT_PARENT_SET) != 0;
12856 emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0;
12857 check_state = (flags & ADD_CHILD_CHECK_STATE) != 0;
12858 notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0;
12859 show_on_set_parent = (flags & ADD_CHILD_SHOW_ON_SET_PARENT) != 0;
12860
12861 old_first_child = self->priv->first_child;
12862 old_last_child = self->priv->last_child;
12863
12864 obj = G_OBJECT (self);
12865 g_object_freeze_notify (obj);
12866
12867 if (create_meta)
12868 clutter_container_create_child_meta (CLUTTER_CONTAINER (self), child);
12869
12870 g_object_ref_sink (child);
12871 child->priv->parent = NULL;
12872 child->priv->next_sibling = NULL;
12873 child->priv->prev_sibling = NULL;
12874
12875 /* delegate the actual insertion */
12876 add_func (self, child, data);
12877
12878 g_assert (child->priv->parent == self);
12879
12880 self->priv->n_children += 1;
12881
12882 self->priv->age += 1;
12883
12884 /* if push_internal() has been called then we automatically set
12885 * the flag on the actor
12886 */
12887 if (self->priv->internal_child)
12888 CLUTTER_SET_PRIVATE_FLAGS (child, CLUTTER_INTERNAL_CHILD);
12889
12890 /* children may cause their parent to expand, if they are set
12891 * to expand; if a child is not expanded then it cannot change
12892 * its parent's state. any further change later on will queue
12893 * an expand state check.
12894 *
12895 * this check, with the initial state of the needs_compute_expand
12896 * flag set to FALSE, should avoid recomputing the expand flags
12897 * state while building the actor tree.
12898 */
12899 if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
12900 (child->priv->needs_compute_expand ||
12901 child->priv->needs_x_expand ||
12902 child->priv->needs_y_expand))
12903 {
12904 clutter_actor_queue_compute_expand (self);
12905 }
12906
12907 /* clutter_actor_reparent() will emit ::parent-set for us */
12908 if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
12909 g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
12910
12911 if (check_state)
12912 {
12913 /* If parent is mapped or realized, we need to also be mapped or
12914 * realized once we're inside the parent.
12915 */
12916 clutter_actor_update_map_state (child, MAP_STATE_CHECK);
12917
12918 /* propagate the parent's text direction to the child */
12919 text_dir = clutter_actor_get_text_direction (self);
12920 clutter_actor_set_text_direction (child, text_dir);
12921 }
12922
12923 /* this may end up queueing a redraw, in case the actor is
12924 * not visible but the show-on-set-parent property is still
12925 * set.
12926 *
12927 * XXX:2.0 - remove this check and unconditionally show() the
12928 * actor once we remove the show-on-set-parent property
12929 */
12930 if (show_on_set_parent && child->priv->show_on_set_parent)
12931 clutter_actor_show (child);
12932
12933 /* on the other hand, this will catch any other case where
12934 * the actor is supposed to be visible when it's added
12935 */
12936 if (CLUTTER_ACTOR_IS_MAPPED (child))
12937 clutter_actor_queue_redraw (child);
12938
12939 /* maintain the invariant that if an actor needs layout,
12940 * its parents do as well
12941 */
12942 if (child->priv->needs_width_request ||
12943 child->priv->needs_height_request ||
12944 child->priv->needs_allocation)
12945 {
12946 /* we work around the short-circuiting we do
12947 * in clutter_actor_queue_relayout() since we
12948 * want to force a relayout
12949 */
12950 child->priv->needs_width_request = TRUE;
12951 child->priv->needs_height_request = TRUE;
12952 child->priv->needs_allocation = TRUE;
12953
12954 /* we only queue a relayout here, because any possible
12955 * redraw has already been queued either by show() or
12956 * by our call to queue_redraw() above
12957 */
12958 _clutter_actor_queue_only_relayout (child->priv->parent);
12959 }
12960
12961 if (emit_actor_added)
12962 g_signal_emit_by_name (self, "actor-added", child);
12963
12964 if (notify_first_last)
12965 {
12966 if (old_first_child != self->priv->first_child)
12967 g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
12968
12969 if (old_last_child != self->priv->last_child)
12970 g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
12971 }
12972
12973 g_object_thaw_notify (obj);
12974 }
12975
12976 /**
12977 * clutter_actor_add_child:
12978 * @self: a #ClutterActor
12979 * @child: a #ClutterActor
12980 *
12981 * Adds @child to the children of @self.
12982 *
12983 * This function will acquire a reference on @child that will only
12984 * be released when calling clutter_actor_remove_child().
12985 *
12986 * This function will take into consideration the #ClutterActor:depth
12987 * of @child, and will keep the list of children sorted.
12988 *
12989 * This function will emit the #ClutterContainer::actor-added signal
12990 * on @self.
12991 *
12992 * Since: 1.10
12993 */
12994 void
clutter_actor_add_child(ClutterActor * self,ClutterActor * child)12995 clutter_actor_add_child (ClutterActor *self,
12996 ClutterActor *child)
12997 {
12998 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12999 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13000 g_return_if_fail (self != child);
13001 g_return_if_fail (child->priv->parent == NULL);
13002
13003 clutter_actor_add_child_internal (self, child,
13004 ADD_CHILD_DEFAULT_FLAGS,
13005 insert_child_at_depth,
13006 NULL);
13007 }
13008
13009 /**
13010 * clutter_actor_insert_child_at_index:
13011 * @self: a #ClutterActor
13012 * @child: a #ClutterActor
13013 * @index_: the index
13014 *
13015 * Inserts @child into the list of children of @self, using the
13016 * given @index_. If @index_ is greater than the number of children
13017 * in @self, or is less than 0, then the new child is added at the end.
13018 *
13019 * This function will acquire a reference on @child that will only
13020 * be released when calling clutter_actor_remove_child().
13021 *
13022 * This function will not take into consideration the #ClutterActor:depth
13023 * of @child.
13024 *
13025 * This function will emit the #ClutterContainer::actor-added signal
13026 * on @self.
13027 *
13028 * Since: 1.10
13029 */
13030 void
clutter_actor_insert_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)13031 clutter_actor_insert_child_at_index (ClutterActor *self,
13032 ClutterActor *child,
13033 gint index_)
13034 {
13035 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13036 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13037 g_return_if_fail (self != child);
13038 g_return_if_fail (child->priv->parent == NULL);
13039
13040 clutter_actor_add_child_internal (self, child,
13041 ADD_CHILD_DEFAULT_FLAGS,
13042 insert_child_at_index,
13043 GINT_TO_POINTER (index_));
13044 }
13045
13046 /**
13047 * clutter_actor_insert_child_above:
13048 * @self: a #ClutterActor
13049 * @child: a #ClutterActor
13050 * @sibling: (allow-none): a child of @self, or %NULL
13051 *
13052 * Inserts @child into the list of children of @self, above another
13053 * child of @self or, if @sibling is %NULL, above all the children
13054 * of @self.
13055 *
13056 * This function will acquire a reference on @child that will only
13057 * be released when calling clutter_actor_remove_child().
13058 *
13059 * This function will not take into consideration the #ClutterActor:depth
13060 * of @child.
13061 *
13062 * This function will emit the #ClutterContainer::actor-added signal
13063 * on @self.
13064 *
13065 * Since: 1.10
13066 */
13067 void
clutter_actor_insert_child_above(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13068 clutter_actor_insert_child_above (ClutterActor *self,
13069 ClutterActor *child,
13070 ClutterActor *sibling)
13071 {
13072 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13073 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13074 g_return_if_fail (self != child);
13075 g_return_if_fail (child != sibling);
13076 g_return_if_fail (child->priv->parent == NULL);
13077 g_return_if_fail (sibling == NULL ||
13078 (CLUTTER_IS_ACTOR (sibling) &&
13079 sibling->priv->parent == self));
13080
13081 clutter_actor_add_child_internal (self, child,
13082 ADD_CHILD_DEFAULT_FLAGS,
13083 insert_child_above,
13084 sibling);
13085 }
13086
13087 /**
13088 * clutter_actor_insert_child_below:
13089 * @self: a #ClutterActor
13090 * @child: a #ClutterActor
13091 * @sibling: (allow-none): a child of @self, or %NULL
13092 *
13093 * Inserts @child into the list of children of @self, below another
13094 * child of @self or, if @sibling is %NULL, below all the children
13095 * of @self.
13096 *
13097 * This function will acquire a reference on @child that will only
13098 * be released when calling clutter_actor_remove_child().
13099 *
13100 * This function will not take into consideration the #ClutterActor:depth
13101 * of @child.
13102 *
13103 * This function will emit the #ClutterContainer::actor-added signal
13104 * on @self.
13105 *
13106 * Since: 1.10
13107 */
13108 void
clutter_actor_insert_child_below(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13109 clutter_actor_insert_child_below (ClutterActor *self,
13110 ClutterActor *child,
13111 ClutterActor *sibling)
13112 {
13113 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13114 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13115 g_return_if_fail (self != child);
13116 g_return_if_fail (child != sibling);
13117 g_return_if_fail (child->priv->parent == NULL);
13118 g_return_if_fail (sibling == NULL ||
13119 (CLUTTER_IS_ACTOR (sibling) &&
13120 sibling->priv->parent == self));
13121
13122 clutter_actor_add_child_internal (self, child,
13123 ADD_CHILD_DEFAULT_FLAGS,
13124 insert_child_below,
13125 sibling);
13126 }
13127
13128 /**
13129 * clutter_actor_set_parent:
13130 * @self: A #ClutterActor
13131 * @parent: A new #ClutterActor parent
13132 *
13133 * Sets the parent of @self to @parent.
13134 *
13135 * This function will result in @parent acquiring a reference on @self,
13136 * eventually by sinking its floating reference first. The reference
13137 * will be released by clutter_actor_unparent().
13138 *
13139 * This function should only be called by legacy #ClutterActor<!-- -->s
13140 * implementing the #ClutterContainer interface.
13141 *
13142 * Deprecated: 1.10: Use clutter_actor_add_child() instead.
13143 */
13144 void
clutter_actor_set_parent(ClutterActor * self,ClutterActor * parent)13145 clutter_actor_set_parent (ClutterActor *self,
13146 ClutterActor *parent)
13147 {
13148 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13149 g_return_if_fail (CLUTTER_IS_ACTOR (parent));
13150 g_return_if_fail (self != parent);
13151 g_return_if_fail (self->priv->parent == NULL);
13152
13153 /* as this function will be called inside ClutterContainer::add
13154 * implementations or when building up a composite actor, we have
13155 * to preserve the old behaviour, and not create child meta or
13156 * emit the ::actor-added signal, to avoid recursion or double
13157 * emissions
13158 */
13159 clutter_actor_add_child_internal (parent, self,
13160 ADD_CHILD_LEGACY_FLAGS,
13161 insert_child_at_depth,
13162 NULL);
13163 }
13164
13165 /**
13166 * clutter_actor_get_parent:
13167 * @self: A #ClutterActor
13168 *
13169 * Retrieves the parent of @self.
13170 *
13171 * Return Value: (transfer none): The #ClutterActor parent, or %NULL
13172 * if no parent is set
13173 */
13174 ClutterActor *
clutter_actor_get_parent(ClutterActor * self)13175 clutter_actor_get_parent (ClutterActor *self)
13176 {
13177 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
13178
13179 return self->priv->parent;
13180 }
13181
13182 /**
13183 * clutter_actor_get_paint_visibility:
13184 * @self: A #ClutterActor
13185 *
13186 * Retrieves the 'paint' visibility of an actor recursively checking for non
13187 * visible parents.
13188 *
13189 * This is by definition the same as %CLUTTER_ACTOR_IS_MAPPED.
13190 *
13191 * Return Value: %TRUE if the actor is visibile and will be painted.
13192 *
13193 * Since: 0.8
13194 */
13195 gboolean
clutter_actor_get_paint_visibility(ClutterActor * actor)13196 clutter_actor_get_paint_visibility (ClutterActor *actor)
13197 {
13198 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
13199
13200 return CLUTTER_ACTOR_IS_MAPPED (actor);
13201 }
13202
13203 /**
13204 * clutter_actor_remove_child:
13205 * @self: a #ClutterActor
13206 * @child: a #ClutterActor
13207 *
13208 * Removes @child from the children of @self.
13209 *
13210 * This function will release the reference added by
13211 * clutter_actor_add_child(), so if you want to keep using @child
13212 * you will have to acquire a referenced on it before calling this
13213 * function.
13214 *
13215 * This function will emit the #ClutterContainer::actor-removed
13216 * signal on @self.
13217 *
13218 * Since: 1.10
13219 */
13220 void
clutter_actor_remove_child(ClutterActor * self,ClutterActor * child)13221 clutter_actor_remove_child (ClutterActor *self,
13222 ClutterActor *child)
13223 {
13224 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13225 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13226 g_return_if_fail (self != child);
13227 g_return_if_fail (child->priv->parent != NULL);
13228 g_return_if_fail (child->priv->parent == self);
13229
13230 clutter_actor_remove_child_internal (self, child,
13231 REMOVE_CHILD_DEFAULT_FLAGS);
13232 }
13233
13234 /**
13235 * clutter_actor_remove_all_children:
13236 * @self: a #ClutterActor
13237 *
13238 * Removes all children of @self.
13239 *
13240 * This function releases the reference added by inserting a child actor
13241 * in the list of children of @self.
13242 *
13243 * If the reference count of a child drops to zero, the child will be
13244 * destroyed. If you want to ensure the destruction of all the children
13245 * of @self, use clutter_actor_destroy_all_children().
13246 *
13247 * Since: 1.10
13248 */
13249 void
clutter_actor_remove_all_children(ClutterActor * self)13250 clutter_actor_remove_all_children (ClutterActor *self)
13251 {
13252 ClutterActorIter iter;
13253
13254 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13255
13256 if (self->priv->n_children == 0)
13257 return;
13258
13259 g_object_freeze_notify (G_OBJECT (self));
13260
13261 clutter_actor_iter_init (&iter, self);
13262 while (clutter_actor_iter_next (&iter, NULL))
13263 clutter_actor_iter_remove (&iter);
13264
13265 g_object_thaw_notify (G_OBJECT (self));
13266
13267 /* sanity check */
13268 g_assert (self->priv->first_child == NULL);
13269 g_assert (self->priv->last_child == NULL);
13270 g_assert (self->priv->n_children == 0);
13271 }
13272
13273 /**
13274 * clutter_actor_destroy_all_children:
13275 * @self: a #ClutterActor
13276 *
13277 * Destroys all children of @self.
13278 *
13279 * This function releases the reference added by inserting a child
13280 * actor in the list of children of @self, and ensures that the
13281 * #ClutterActor::destroy signal is emitted on each child of the
13282 * actor.
13283 *
13284 * By default, #ClutterActor will emit the #ClutterActor::destroy signal
13285 * when its reference count drops to 0; the default handler of the
13286 * #ClutterActor::destroy signal will destroy all the children of an
13287 * actor. This function ensures that all children are destroyed, instead
13288 * of just removed from @self, unlike clutter_actor_remove_all_children()
13289 * which will merely release the reference and remove each child.
13290 *
13291 * Unless you acquired an additional reference on each child of @self
13292 * prior to calling clutter_actor_remove_all_children() and want to reuse
13293 * the actors, you should use clutter_actor_destroy_all_children() in
13294 * order to make sure that children are destroyed and signal handlers
13295 * are disconnected even in cases where circular references prevent this
13296 * from automatically happening through reference counting alone.
13297 *
13298 * Since: 1.10
13299 */
13300 void
clutter_actor_destroy_all_children(ClutterActor * self)13301 clutter_actor_destroy_all_children (ClutterActor *self)
13302 {
13303 ClutterActorIter iter;
13304
13305 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13306
13307 if (self->priv->n_children == 0)
13308 return;
13309
13310 g_object_freeze_notify (G_OBJECT (self));
13311
13312 clutter_actor_iter_init (&iter, self);
13313 while (clutter_actor_iter_next (&iter, NULL))
13314 clutter_actor_iter_destroy (&iter);
13315
13316 g_object_thaw_notify (G_OBJECT (self));
13317
13318 /* sanity check */
13319 g_assert (self->priv->first_child == NULL);
13320 g_assert (self->priv->last_child == NULL);
13321 g_assert (self->priv->n_children == 0);
13322 }
13323
13324 typedef struct _InsertBetweenData {
13325 ClutterActor *prev_sibling;
13326 ClutterActor *next_sibling;
13327 } InsertBetweenData;
13328
13329 static void
insert_child_between(ClutterActor * self,ClutterActor * child,gpointer data_)13330 insert_child_between (ClutterActor *self,
13331 ClutterActor *child,
13332 gpointer data_)
13333 {
13334 InsertBetweenData *data = data_;
13335 ClutterActor *prev_sibling = data->prev_sibling;
13336 ClutterActor *next_sibling = data->next_sibling;
13337
13338 child->priv->parent = self;
13339 child->priv->prev_sibling = prev_sibling;
13340 child->priv->next_sibling = next_sibling;
13341
13342 if (prev_sibling != NULL)
13343 prev_sibling->priv->next_sibling = child;
13344
13345 if (next_sibling != NULL)
13346 next_sibling->priv->prev_sibling = child;
13347
13348 if (child->priv->prev_sibling == NULL)
13349 self->priv->first_child = child;
13350
13351 if (child->priv->next_sibling == NULL)
13352 self->priv->last_child = child;
13353 }
13354
13355 /**
13356 * clutter_actor_replace_child:
13357 * @self: a #ClutterActor
13358 * @old_child: the child of @self to replace
13359 * @new_child: the #ClutterActor to replace @old_child
13360 *
13361 * Replaces @old_child with @new_child in the list of children of @self.
13362 *
13363 * Since: 1.10
13364 */
13365 void
clutter_actor_replace_child(ClutterActor * self,ClutterActor * old_child,ClutterActor * new_child)13366 clutter_actor_replace_child (ClutterActor *self,
13367 ClutterActor *old_child,
13368 ClutterActor *new_child)
13369 {
13370 ClutterActor *prev_sibling, *next_sibling;
13371 InsertBetweenData clos;
13372
13373 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13374 g_return_if_fail (CLUTTER_IS_ACTOR (old_child));
13375 g_return_if_fail (old_child->priv->parent == self);
13376 g_return_if_fail (CLUTTER_IS_ACTOR (new_child));
13377 g_return_if_fail (old_child != new_child);
13378 g_return_if_fail (new_child != self);
13379 g_return_if_fail (new_child->priv->parent == NULL);
13380
13381 prev_sibling = old_child->priv->prev_sibling;
13382 next_sibling = old_child->priv->next_sibling;
13383 clutter_actor_remove_child_internal (self, old_child,
13384 REMOVE_CHILD_DEFAULT_FLAGS);
13385
13386 clos.prev_sibling = prev_sibling;
13387 clos.next_sibling = next_sibling;
13388 clutter_actor_add_child_internal (self, new_child,
13389 ADD_CHILD_DEFAULT_FLAGS,
13390 insert_child_between,
13391 &clos);
13392 }
13393
13394 /**
13395 * clutter_actor_unparent:
13396 * @self: a #ClutterActor
13397 *
13398 * Removes the parent of @self.
13399 *
13400 * This will cause the parent of @self to release the reference
13401 * acquired when calling clutter_actor_set_parent(), so if you
13402 * want to keep @self you will have to acquire a reference of
13403 * your own, through g_object_ref().
13404 *
13405 * This function should only be called by legacy #ClutterActor<!-- -->s
13406 * implementing the #ClutterContainer interface.
13407 *
13408 * Since: 0.2
13409 *
13410 * Deprecated: 1.10: Use clutter_actor_remove_child() instead.
13411 */
13412 void
clutter_actor_unparent(ClutterActor * self)13413 clutter_actor_unparent (ClutterActor *self)
13414 {
13415 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13416
13417 if (self->priv->parent == NULL)
13418 return;
13419
13420 clutter_actor_remove_child_internal (self->priv->parent, self,
13421 REMOVE_CHILD_LEGACY_FLAGS);
13422 }
13423
13424 /**
13425 * clutter_actor_reparent:
13426 * @self: a #ClutterActor
13427 * @new_parent: the new #ClutterActor parent
13428 *
13429 * Resets the parent actor of @self.
13430 *
13431 * This function is logically equivalent to calling clutter_actor_unparent()
13432 * and clutter_actor_set_parent(), but more efficiently implemented, as it
13433 * ensures the child is not finalized when unparented, and emits the
13434 * #ClutterActor::parent-set signal only once.
13435 *
13436 * In reality, calling this function is less useful than it sounds, as some
13437 * application code may rely on changes in the intermediate state between
13438 * removal and addition of the actor from its old parent to the @new_parent.
13439 * Thus, it is strongly encouraged to avoid using this function in application
13440 * code.
13441 *
13442 * Since: 0.2
13443 *
13444 * Deprecated: 1.10: Use clutter_actor_remove_child() and
13445 * clutter_actor_add_child() instead; remember to take a reference on
13446 * the actor being removed before calling clutter_actor_remove_child()
13447 * to avoid the reference count dropping to zero and the actor being
13448 * destroyed.
13449 */
13450 void
clutter_actor_reparent(ClutterActor * self,ClutterActor * new_parent)13451 clutter_actor_reparent (ClutterActor *self,
13452 ClutterActor *new_parent)
13453 {
13454 ClutterActorPrivate *priv;
13455
13456 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13457 g_return_if_fail (CLUTTER_IS_ACTOR (new_parent));
13458 g_return_if_fail (self != new_parent);
13459
13460 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
13461 {
13462 g_warning ("Cannot set a parent on a toplevel actor");
13463 return;
13464 }
13465
13466 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
13467 {
13468 g_warning ("Cannot set a parent currently being destroyed");
13469 return;
13470 }
13471
13472 priv = self->priv;
13473
13474 if (priv->parent != new_parent)
13475 {
13476 ClutterActor *old_parent;
13477
13478 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_REPARENT);
13479
13480 old_parent = priv->parent;
13481
13482 g_object_ref (self);
13483
13484 if (old_parent != NULL)
13485 {
13486 /* go through the Container implementation if this is a regular
13487 * child and not an internal one
13488 */
13489 if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
13490 {
13491 ClutterContainer *parent = CLUTTER_CONTAINER (old_parent);
13492
13493 /* this will have to call unparent() */
13494 clutter_container_remove_actor (parent, self);
13495 }
13496 else
13497 clutter_actor_remove_child_internal (old_parent, self,
13498 REMOVE_CHILD_LEGACY_FLAGS);
13499 }
13500
13501 /* Note, will call set_parent() */
13502 if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
13503 clutter_container_add_actor (CLUTTER_CONTAINER (new_parent), self);
13504 else
13505 clutter_actor_add_child_internal (new_parent, self,
13506 ADD_CHILD_LEGACY_FLAGS,
13507 insert_child_at_depth,
13508 NULL);
13509
13510 /* we emit the ::parent-set signal once */
13511 g_signal_emit (self, actor_signals[PARENT_SET], 0, old_parent);
13512
13513 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_REPARENT);
13514
13515 /* the IN_REPARENT flag suspends state updates */
13516 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
13517
13518 g_object_unref (self);
13519 }
13520 }
13521
13522 /**
13523 * clutter_actor_contains:
13524 * @self: A #ClutterActor
13525 * @descendant: A #ClutterActor, possibly contained in @self
13526 *
13527 * Determines if @descendant is contained inside @self (either as an
13528 * immediate child, or as a deeper descendant). If @self and
13529 * @descendant point to the same actor then it will also return %TRUE.
13530 *
13531 * Return value: whether @descendent is contained within @self
13532 *
13533 * Since: 1.4
13534 */
13535 gboolean
clutter_actor_contains(ClutterActor * self,ClutterActor * descendant)13536 clutter_actor_contains (ClutterActor *self,
13537 ClutterActor *descendant)
13538 {
13539 ClutterActor *actor;
13540
13541 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13542 g_return_val_if_fail (CLUTTER_IS_ACTOR (descendant), FALSE);
13543
13544 for (actor = descendant; actor; actor = actor->priv->parent)
13545 if (actor == self)
13546 return TRUE;
13547
13548 return FALSE;
13549 }
13550
13551 /**
13552 * clutter_actor_set_child_above_sibling:
13553 * @self: a #ClutterActor
13554 * @child: a #ClutterActor child of @self
13555 * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
13556 *
13557 * Sets @child to be above @sibling in the list of children of @self.
13558 *
13559 * If @sibling is %NULL, @child will be the new last child of @self.
13560 *
13561 * This function is logically equivalent to removing @child and using
13562 * clutter_actor_insert_child_above(), but it will not emit signals
13563 * or change state on @child.
13564 *
13565 * Since: 1.10
13566 */
13567 void
clutter_actor_set_child_above_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13568 clutter_actor_set_child_above_sibling (ClutterActor *self,
13569 ClutterActor *child,
13570 ClutterActor *sibling)
13571 {
13572 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13573 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13574 g_return_if_fail (child->priv->parent == self);
13575 g_return_if_fail (child != sibling);
13576 g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
13577
13578 if (sibling != NULL)
13579 g_return_if_fail (sibling->priv->parent == self);
13580
13581 if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
13582 CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
13583 (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
13584 return;
13585
13586 /* we don't want to change the state of child, or emit signals, or
13587 * regenerate ChildMeta instances here, but we still want to follow
13588 * the correct sequence of steps encoded in remove_child() and
13589 * add_child(), so that correctness is ensured, and we only go
13590 * through one known code path.
13591 */
13592 g_object_ref (child);
13593 clutter_actor_remove_child_internal (self, child, 0);
13594 clutter_actor_add_child_internal (self, child,
13595 ADD_CHILD_NOTIFY_FIRST_LAST,
13596 insert_child_above,
13597 sibling);
13598 g_object_unref(child);
13599
13600 clutter_actor_queue_relayout (self);
13601 }
13602
13603 /**
13604 * clutter_actor_set_child_below_sibling:
13605 * @self: a #ClutterActor
13606 * @child: a #ClutterActor child of @self
13607 * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
13608 *
13609 * Sets @child to be below @sibling in the list of children of @self.
13610 *
13611 * If @sibling is %NULL, @child will be the new first child of @self.
13612 *
13613 * This function is logically equivalent to removing @self and using
13614 * clutter_actor_insert_child_below(), but it will not emit signals
13615 * or change state on @child.
13616 *
13617 * Since: 1.10
13618 */
13619 void
clutter_actor_set_child_below_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13620 clutter_actor_set_child_below_sibling (ClutterActor *self,
13621 ClutterActor *child,
13622 ClutterActor *sibling)
13623 {
13624 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13625 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13626 g_return_if_fail (child->priv->parent == self);
13627 g_return_if_fail (child != sibling);
13628 g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
13629
13630 if (sibling != NULL)
13631 g_return_if_fail (sibling->priv->parent == self);
13632
13633 if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
13634 CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
13635 (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
13636 return;
13637
13638 /* see the comment in set_child_above_sibling() */
13639 g_object_ref (child);
13640 clutter_actor_remove_child_internal (self, child, 0);
13641 clutter_actor_add_child_internal (self, child,
13642 ADD_CHILD_NOTIFY_FIRST_LAST,
13643 insert_child_below,
13644 sibling);
13645 g_object_unref(child);
13646
13647 clutter_actor_queue_relayout (self);
13648 }
13649
13650 /**
13651 * clutter_actor_set_child_at_index:
13652 * @self: a #ClutterActor
13653 * @child: a #ClutterActor child of @self
13654 * @index_: the new index for @child
13655 *
13656 * Changes the index of @child in the list of children of @self.
13657 *
13658 * This function is logically equivalent to removing @child and
13659 * calling clutter_actor_insert_child_at_index(), but it will not
13660 * emit signals or change state on @child.
13661 *
13662 * Since: 1.10
13663 */
13664 void
clutter_actor_set_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)13665 clutter_actor_set_child_at_index (ClutterActor *self,
13666 ClutterActor *child,
13667 gint index_)
13668 {
13669 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13670 g_return_if_fail (CLUTTER_IS_ACTOR (child));
13671 g_return_if_fail (child->priv->parent == self);
13672 g_return_if_fail (index_ <= self->priv->n_children);
13673
13674 if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
13675 CLUTTER_ACTOR_IN_DESTRUCTION (child))
13676 return;
13677
13678 g_object_ref (child);
13679 clutter_actor_remove_child_internal (self, child, 0);
13680 clutter_actor_add_child_internal (self, child,
13681 ADD_CHILD_NOTIFY_FIRST_LAST,
13682 insert_child_at_index,
13683 GINT_TO_POINTER (index_));
13684 g_object_unref (child);
13685
13686 clutter_actor_queue_relayout (self);
13687 }
13688
13689 /**
13690 * clutter_actor_raise:
13691 * @self: A #ClutterActor
13692 * @below: (allow-none): A #ClutterActor to raise above.
13693 *
13694 * Puts @self above @below.
13695 *
13696 * Both actors must have the same parent, and the parent must implement
13697 * the #ClutterContainer interface
13698 *
13699 * This function calls clutter_container_raise_child() internally.
13700 *
13701 * Deprecated: 1.10: Use clutter_actor_set_child_above_sibling() instead.
13702 */
13703 void
clutter_actor_raise(ClutterActor * self,ClutterActor * below)13704 clutter_actor_raise (ClutterActor *self,
13705 ClutterActor *below)
13706 {
13707 ClutterActor *parent;
13708
13709 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13710
13711 parent = clutter_actor_get_parent (self);
13712 if (parent == NULL)
13713 {
13714 g_warning ("%s: Actor '%s' is not inside a container",
13715 G_STRFUNC,
13716 _clutter_actor_get_debug_name (self));
13717 return;
13718 }
13719
13720 if (below != NULL)
13721 {
13722 if (parent != clutter_actor_get_parent (below))
13723 {
13724 g_warning ("%s Actor '%s' is not in the same container as "
13725 "actor '%s'",
13726 G_STRFUNC,
13727 _clutter_actor_get_debug_name (self),
13728 _clutter_actor_get_debug_name (below));
13729 return;
13730 }
13731 }
13732
13733 clutter_container_raise_child (CLUTTER_CONTAINER (parent), self, below);
13734 }
13735
13736 /**
13737 * clutter_actor_lower:
13738 * @self: A #ClutterActor
13739 * @above: (allow-none): A #ClutterActor to lower below
13740 *
13741 * Puts @self below @above.
13742 *
13743 * Both actors must have the same parent, and the parent must implement
13744 * the #ClutterContainer interface.
13745 *
13746 * This function calls clutter_container_lower_child() internally.
13747 *
13748 * Deprecated: 1.10: Use clutter_actor_set_child_below_sibling() instead.
13749 */
13750 void
clutter_actor_lower(ClutterActor * self,ClutterActor * above)13751 clutter_actor_lower (ClutterActor *self,
13752 ClutterActor *above)
13753 {
13754 ClutterActor *parent;
13755
13756 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13757
13758 parent = clutter_actor_get_parent (self);
13759 if (parent == NULL)
13760 {
13761 g_warning ("%s: Actor of type %s is not inside a container",
13762 G_STRFUNC,
13763 _clutter_actor_get_debug_name (self));
13764 return;
13765 }
13766
13767 if (above)
13768 {
13769 if (parent != clutter_actor_get_parent (above))
13770 {
13771 g_warning ("%s: Actor '%s' is not in the same container as "
13772 "actor '%s'",
13773 G_STRFUNC,
13774 _clutter_actor_get_debug_name (self),
13775 _clutter_actor_get_debug_name (above));
13776 return;
13777 }
13778 }
13779
13780 clutter_container_lower_child (CLUTTER_CONTAINER (parent), self, above);
13781 }
13782
13783 /**
13784 * clutter_actor_raise_top:
13785 * @self: A #ClutterActor
13786 *
13787 * Raises @self to the top.
13788 *
13789 * This function calls clutter_actor_raise() internally.
13790 *
13791 * Deprecated: 1.10: Use clutter_actor_set_child_above_sibling() with
13792 * a %NULL sibling, instead.
13793 */
13794 void
clutter_actor_raise_top(ClutterActor * self)13795 clutter_actor_raise_top (ClutterActor *self)
13796 {
13797 clutter_actor_raise (self, NULL);
13798 }
13799
13800 /**
13801 * clutter_actor_lower_bottom:
13802 * @self: A #ClutterActor
13803 *
13804 * Lowers @self to the bottom.
13805 *
13806 * This function calls clutter_actor_lower() internally.
13807 *
13808 * Deprecated: 1.10: Use clutter_actor_set_child_below_sibling() with
13809 * a %NULL sibling, instead.
13810 */
13811 void
clutter_actor_lower_bottom(ClutterActor * self)13812 clutter_actor_lower_bottom (ClutterActor *self)
13813 {
13814 clutter_actor_lower (self, NULL);
13815 }
13816
13817 /*
13818 * Event handling
13819 */
13820
13821 /**
13822 * clutter_actor_event:
13823 * @actor: a #ClutterActor
13824 * @event: a #ClutterEvent
13825 * @capture: %TRUE if event in in capture phase, %FALSE otherwise.
13826 *
13827 * This function is used to emit an event on the main stage.
13828 * You should rarely need to use this function, except for
13829 * synthetising events.
13830 *
13831 * Return value: the return value from the signal emission: %TRUE
13832 * if the actor handled the event, or %FALSE if the event was
13833 * not handled
13834 *
13835 * Since: 0.6
13836 */
13837 gboolean
clutter_actor_event(ClutterActor * actor,const ClutterEvent * event,gboolean capture)13838 clutter_actor_event (ClutterActor *actor,
13839 const ClutterEvent *event,
13840 gboolean capture)
13841 {
13842 gboolean retval = FALSE;
13843 gint signal_num = -1;
13844
13845 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
13846 g_return_val_if_fail (event != NULL, FALSE);
13847
13848 g_object_ref (actor);
13849
13850 if (capture)
13851 {
13852 g_signal_emit (actor, actor_signals[CAPTURED_EVENT], 0,
13853 event,
13854 &retval);
13855 goto out;
13856 }
13857
13858 g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
13859
13860 if (!retval)
13861 {
13862 switch (event->type)
13863 {
13864 case CLUTTER_NOTHING:
13865 break;
13866 case CLUTTER_BUTTON_PRESS:
13867 signal_num = BUTTON_PRESS_EVENT;
13868 break;
13869 case CLUTTER_BUTTON_RELEASE:
13870 signal_num = BUTTON_RELEASE_EVENT;
13871 break;
13872 case CLUTTER_SCROLL:
13873 signal_num = SCROLL_EVENT;
13874 break;
13875 case CLUTTER_KEY_PRESS:
13876 signal_num = KEY_PRESS_EVENT;
13877 break;
13878 case CLUTTER_KEY_RELEASE:
13879 signal_num = KEY_RELEASE_EVENT;
13880 break;
13881 case CLUTTER_MOTION:
13882 signal_num = MOTION_EVENT;
13883 break;
13884 case CLUTTER_ENTER:
13885 signal_num = ENTER_EVENT;
13886 break;
13887 case CLUTTER_LEAVE:
13888 signal_num = LEAVE_EVENT;
13889 break;
13890 case CLUTTER_TOUCH_BEGIN:
13891 case CLUTTER_TOUCH_END:
13892 case CLUTTER_TOUCH_UPDATE:
13893 case CLUTTER_TOUCH_CANCEL:
13894 signal_num = TOUCH_EVENT;
13895 break;
13896 case CLUTTER_DELETE:
13897 case CLUTTER_DESTROY_NOTIFY:
13898 case CLUTTER_CLIENT_MESSAGE:
13899 default:
13900 signal_num = -1;
13901 break;
13902 }
13903
13904 if (signal_num != -1)
13905 g_signal_emit (actor, actor_signals[signal_num], 0,
13906 event, &retval);
13907 }
13908
13909 out:
13910 g_object_unref (actor);
13911
13912 return retval;
13913 }
13914
13915 /**
13916 * clutter_actor_set_reactive:
13917 * @actor: a #ClutterActor
13918 * @reactive: whether the actor should be reactive to events
13919 *
13920 * Sets @actor as reactive. Reactive actors will receive events.
13921 *
13922 * Since: 0.6
13923 */
13924 void
clutter_actor_set_reactive(ClutterActor * actor,gboolean reactive)13925 clutter_actor_set_reactive (ClutterActor *actor,
13926 gboolean reactive)
13927 {
13928 g_return_if_fail (CLUTTER_IS_ACTOR (actor));
13929
13930 if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor))
13931 return;
13932
13933 if (reactive)
13934 CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
13935 else
13936 CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
13937
13938 g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]);
13939 }
13940
13941 /**
13942 * clutter_actor_get_reactive:
13943 * @actor: a #ClutterActor
13944 *
13945 * Checks whether @actor is marked as reactive.
13946 *
13947 * Return value: %TRUE if the actor is reactive
13948 *
13949 * Since: 0.6
13950 */
13951 gboolean
clutter_actor_get_reactive(ClutterActor * actor)13952 clutter_actor_get_reactive (ClutterActor *actor)
13953 {
13954 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
13955
13956 return CLUTTER_ACTOR_IS_REACTIVE (actor) ? TRUE : FALSE;
13957 }
13958
13959 /**
13960 * clutter_actor_get_anchor_point:
13961 * @self: a #ClutterActor
13962 * @anchor_x: (out): return location for the X coordinate of the anchor point
13963 * @anchor_y: (out): return location for the Y coordinate of the anchor point
13964 *
13965 * Gets the current anchor point of the @actor in pixels.
13966 *
13967 * Since: 0.6
13968 *
13969 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
13970 */
13971 void
clutter_actor_get_anchor_point(ClutterActor * self,gfloat * anchor_x,gfloat * anchor_y)13972 clutter_actor_get_anchor_point (ClutterActor *self,
13973 gfloat *anchor_x,
13974 gfloat *anchor_y)
13975 {
13976 const ClutterTransformInfo *info;
13977
13978 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13979
13980 info = _clutter_actor_get_transform_info_or_defaults (self);
13981 clutter_anchor_coord_get_units (self, &info->anchor,
13982 anchor_x,
13983 anchor_y,
13984 NULL);
13985 }
13986
13987 /**
13988 * clutter_actor_set_anchor_point:
13989 * @self: a #ClutterActor
13990 * @anchor_x: X coordinate of the anchor point
13991 * @anchor_y: Y coordinate of the anchor point
13992 *
13993 * Sets an anchor point for @self. The anchor point is a point in the
13994 * coordinate space of an actor to which the actor position within its
13995 * parent is relative; the default is (0, 0), i.e. the top-left corner
13996 * of the actor.
13997 *
13998 * Since: 0.6
13999 *
14000 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead.
14001 */
14002 void
clutter_actor_set_anchor_point(ClutterActor * self,gfloat anchor_x,gfloat anchor_y)14003 clutter_actor_set_anchor_point (ClutterActor *self,
14004 gfloat anchor_x,
14005 gfloat anchor_y)
14006 {
14007 ClutterTransformInfo *info;
14008 ClutterActorPrivate *priv;
14009 gboolean changed = FALSE;
14010 gfloat old_anchor_x, old_anchor_y;
14011 GObject *obj;
14012
14013 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14014
14015 obj = G_OBJECT (self);
14016 priv = self->priv;
14017 info = _clutter_actor_get_transform_info (self);
14018
14019 g_object_freeze_notify (obj);
14020
14021 clutter_anchor_coord_get_units (self, &info->anchor,
14022 &old_anchor_x,
14023 &old_anchor_y,
14024 NULL);
14025
14026 if (info->anchor.is_fractional)
14027 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]);
14028
14029 if (old_anchor_x != anchor_x)
14030 {
14031 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]);
14032 changed = TRUE;
14033 }
14034
14035 if (old_anchor_y != anchor_y)
14036 {
14037 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]);
14038 changed = TRUE;
14039 }
14040
14041 clutter_anchor_coord_set_units (&info->anchor, anchor_x, anchor_y, 0);
14042
14043 if (changed)
14044 {
14045 priv->transform_valid = FALSE;
14046 clutter_actor_queue_redraw (self);
14047 }
14048
14049 g_object_thaw_notify (obj);
14050 }
14051
14052 /**
14053 * clutter_actor_get_anchor_point_gravity:
14054 * @self: a #ClutterActor
14055 *
14056 * Retrieves the anchor position expressed as a #ClutterGravity. If
14057 * the anchor point was specified using pixels or units this will
14058 * return %CLUTTER_GRAVITY_NONE.
14059 *
14060 * Return value: the #ClutterGravity used by the anchor point
14061 *
14062 * Since: 1.0
14063 *
14064 * Deprecated: 1.12: Use #ClutterActor:pivot-point instead.
14065 */
14066 ClutterGravity
clutter_actor_get_anchor_point_gravity(ClutterActor * self)14067 clutter_actor_get_anchor_point_gravity (ClutterActor *self)
14068 {
14069 const ClutterTransformInfo *info;
14070
14071 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
14072
14073 info = _clutter_actor_get_transform_info_or_defaults (self);
14074
14075 return clutter_anchor_coord_get_gravity (&info->anchor);
14076 }
14077
14078 /**
14079 * clutter_actor_move_anchor_point:
14080 * @self: a #ClutterActor
14081 * @anchor_x: X coordinate of the anchor point
14082 * @anchor_y: Y coordinate of the anchor point
14083 *
14084 * Sets an anchor point for the actor, and adjusts the actor postion so that
14085 * the relative position of the actor toward its parent remains the same.
14086 *
14087 * Since: 0.6
14088 *
14089 * Deprecated: 1.12: Use #ClutterActor:pivot-point and
14090 * clutter_actor_set_translation() instead.
14091 */
14092 void
clutter_actor_move_anchor_point(ClutterActor * self,gfloat anchor_x,gfloat anchor_y)14093 clutter_actor_move_anchor_point (ClutterActor *self,
14094 gfloat anchor_x,
14095 gfloat anchor_y)
14096 {
14097 gfloat old_anchor_x, old_anchor_y;
14098 const ClutterTransformInfo *info;
14099
14100 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14101
14102 info = _clutter_actor_get_transform_info (self);
14103 clutter_anchor_coord_get_units (self, &info->anchor,
14104 &old_anchor_x,
14105 &old_anchor_y,
14106 NULL);
14107
14108 g_object_freeze_notify (G_OBJECT (self));
14109
14110 clutter_actor_set_anchor_point (self, anchor_x, anchor_y);
14111
14112 if (self->priv->position_set)
14113 clutter_actor_move_by (self,
14114 anchor_x - old_anchor_x,
14115 anchor_y - old_anchor_y);
14116
14117 g_object_thaw_notify (G_OBJECT (self));
14118 }
14119
14120 /**
14121 * clutter_actor_move_anchor_point_from_gravity:
14122 * @self: a #ClutterActor
14123 * @gravity: #ClutterGravity.
14124 *
14125 * Sets an anchor point on the actor based on the given gravity, adjusting the
14126 * actor postion so that its relative position within its parent remains
14127 * unchanged.
14128 *
14129 * Since version 1.0 the anchor point will be stored as a gravity so
14130 * that if the actor changes size then the anchor point will move. For
14131 * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST
14132 * and later double the size of the actor, the anchor point will move
14133 * to the bottom right.
14134 *
14135 * Since: 0.6
14136 *
14137 * Deprecated: 1.12: Use #ClutterActor:pivot-point and
14138 * clutter_actor_set_translation() instead.
14139 */
14140 void
clutter_actor_move_anchor_point_from_gravity(ClutterActor * self,ClutterGravity gravity)14141 clutter_actor_move_anchor_point_from_gravity (ClutterActor *self,
14142 ClutterGravity gravity)
14143 {
14144 gfloat old_anchor_x, old_anchor_y, new_anchor_x, new_anchor_y;
14145 const ClutterTransformInfo *info;
14146 ClutterActorPrivate *priv;
14147
14148 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14149
14150 priv = self->priv;
14151 info = _clutter_actor_get_transform_info (self);
14152
14153 g_object_freeze_notify (G_OBJECT (self));
14154
14155 clutter_anchor_coord_get_units (self, &info->anchor,
14156 &old_anchor_x,
14157 &old_anchor_y,
14158 NULL);
14159 clutter_actor_set_anchor_point_from_gravity (self, gravity);
14160 clutter_anchor_coord_get_units (self, &info->anchor,
14161 &new_anchor_x,
14162 &new_anchor_y,
14163 NULL);
14164
14165 if (priv->position_set)
14166 clutter_actor_move_by (self,
14167 new_anchor_x - old_anchor_x,
14168 new_anchor_y - old_anchor_y);
14169
14170 g_object_thaw_notify (G_OBJECT (self));
14171 }
14172
14173 /**
14174 * clutter_actor_set_anchor_point_from_gravity:
14175 * @self: a #ClutterActor
14176 * @gravity: #ClutterGravity.
14177 *
14178 * Sets an anchor point on the actor, based on the given gravity (this is a
14179 * convenience function wrapping clutter_actor_set_anchor_point()).
14180 *
14181 * Since version 1.0 the anchor point will be stored as a gravity so
14182 * that if the actor changes size then the anchor point will move. For
14183 * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST
14184 * and later double the size of the actor, the anchor point will move
14185 * to the bottom right.
14186 *
14187 * Since: 0.6
14188 *
14189 * Deprecated: 1.12: Use #ClutterActor:pivot-point and
14190 * clutter_actor_set_translation() instead. E.g. For %CLUTTER_GRAVITY_CENTER set
14191 * pivot_point to (0.5,0.5) and the translation to (width/2,height/2).
14192 */
14193 void
clutter_actor_set_anchor_point_from_gravity(ClutterActor * self,ClutterGravity gravity)14194 clutter_actor_set_anchor_point_from_gravity (ClutterActor *self,
14195 ClutterGravity gravity)
14196 {
14197 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14198
14199 if (gravity == CLUTTER_GRAVITY_NONE)
14200 clutter_actor_set_anchor_point (self, 0, 0);
14201 else
14202 {
14203 GObject *obj = G_OBJECT (self);
14204 ClutterTransformInfo *info;
14205
14206 g_object_freeze_notify (obj);
14207
14208 info = _clutter_actor_get_transform_info (self);
14209 clutter_anchor_coord_set_gravity (&info->anchor, gravity);
14210
14211 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]);
14212 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]);
14213 g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]);
14214
14215 self->priv->transform_valid = FALSE;
14216
14217 clutter_actor_queue_redraw (self);
14218
14219 g_object_thaw_notify (obj);
14220 }
14221 }
14222
14223 static void
clutter_actor_store_content_box(ClutterActor * self,const ClutterActorBox * box)14224 clutter_actor_store_content_box (ClutterActor *self,
14225 const ClutterActorBox *box)
14226 {
14227 if (box != NULL)
14228 {
14229 self->priv->content_box = *box;
14230 self->priv->content_box_valid = TRUE;
14231 }
14232 else
14233 self->priv->content_box_valid = FALSE;
14234
14235 clutter_actor_queue_redraw (self);
14236
14237 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
14238 }
14239
14240 static void
clutter_container_iface_init(ClutterContainerIface * iface)14241 clutter_container_iface_init (ClutterContainerIface *iface)
14242 {
14243 /* we don't override anything, as ClutterContainer already has a default
14244 * implementation that we can use, and which calls into our own API.
14245 */
14246 }
14247
14248 typedef enum
14249 {
14250 PARSE_X,
14251 PARSE_Y,
14252 PARSE_WIDTH,
14253 PARSE_HEIGHT,
14254 PARSE_ANCHOR_X,
14255 PARSE_ANCHOR_Y
14256 } ParseDimension;
14257
14258 static gfloat
parse_units(ClutterActor * self,ParseDimension dimension,JsonNode * node)14259 parse_units (ClutterActor *self,
14260 ParseDimension dimension,
14261 JsonNode *node)
14262 {
14263 GValue value = G_VALUE_INIT;
14264 gfloat retval = 0;
14265
14266 if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
14267 return 0;
14268
14269 json_node_get_value (node, &value);
14270
14271 if (G_VALUE_HOLDS (&value, G_TYPE_INT64))
14272 {
14273 retval = (gfloat) g_value_get_int64 (&value);
14274 }
14275 else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE))
14276 {
14277 retval = g_value_get_double (&value);
14278 }
14279 else if (G_VALUE_HOLDS (&value, G_TYPE_STRING))
14280 {
14281 ClutterUnits units;
14282 gboolean res;
14283
14284 res = clutter_units_from_string (&units, g_value_get_string (&value));
14285 if (res)
14286 retval = clutter_units_to_pixels (&units);
14287 else
14288 {
14289 g_warning ("Invalid value '%s': integers, strings or floating point "
14290 "values can be used for the x, y, width and height "
14291 "properties. Valid modifiers for strings are 'px', 'mm', "
14292 "'pt' and 'em'.",
14293 g_value_get_string (&value));
14294 retval = 0;
14295 }
14296 }
14297 else
14298 {
14299 g_warning ("Invalid value of type '%s': integers, strings of floating "
14300 "point values can be used for the x, y, width, height "
14301 "anchor-x and anchor-y properties.",
14302 g_type_name (G_VALUE_TYPE (&value)));
14303 }
14304
14305 g_value_unset (&value);
14306
14307 return retval;
14308 }
14309
14310 typedef struct {
14311 ClutterRotateAxis axis;
14312
14313 gdouble angle;
14314
14315 gfloat center_x;
14316 gfloat center_y;
14317 gfloat center_z;
14318 } RotationInfo;
14319
14320 static inline gboolean
parse_rotation_array(ClutterActor * actor,JsonArray * array,RotationInfo * info)14321 parse_rotation_array (ClutterActor *actor,
14322 JsonArray *array,
14323 RotationInfo *info)
14324 {
14325 JsonNode *element;
14326
14327 if (json_array_get_length (array) != 2)
14328 return FALSE;
14329
14330 /* angle */
14331 element = json_array_get_element (array, 0);
14332 if (JSON_NODE_TYPE (element) == JSON_NODE_VALUE)
14333 info->angle = json_node_get_double (element);
14334 else
14335 return FALSE;
14336
14337 /* center */
14338 element = json_array_get_element (array, 1);
14339 if (JSON_NODE_TYPE (element) == JSON_NODE_ARRAY)
14340 {
14341 JsonArray *center = json_node_get_array (element);
14342
14343 if (json_array_get_length (center) != 2)
14344 return FALSE;
14345
14346 switch (info->axis)
14347 {
14348 case CLUTTER_X_AXIS:
14349 info->center_y = parse_units (actor, PARSE_Y,
14350 json_array_get_element (center, 0));
14351 info->center_z = parse_units (actor, PARSE_Y,
14352 json_array_get_element (center, 1));
14353 return TRUE;
14354
14355 case CLUTTER_Y_AXIS:
14356 info->center_x = parse_units (actor, PARSE_X,
14357 json_array_get_element (center, 0));
14358 info->center_z = parse_units (actor, PARSE_X,
14359 json_array_get_element (center, 1));
14360 return TRUE;
14361
14362 case CLUTTER_Z_AXIS:
14363 info->center_x = parse_units (actor, PARSE_X,
14364 json_array_get_element (center, 0));
14365 info->center_y = parse_units (actor, PARSE_Y,
14366 json_array_get_element (center, 1));
14367 return TRUE;
14368 }
14369 }
14370
14371 return FALSE;
14372 }
14373
14374 static gboolean
parse_rotation(ClutterActor * actor,JsonNode * node,RotationInfo * info)14375 parse_rotation (ClutterActor *actor,
14376 JsonNode *node,
14377 RotationInfo *info)
14378 {
14379 JsonArray *array;
14380 guint len, i;
14381 gboolean retval = FALSE;
14382
14383 if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
14384 {
14385 g_warning ("Invalid node of type '%s' found, expecting an array",
14386 json_node_type_name (node));
14387 return FALSE;
14388 }
14389
14390 array = json_node_get_array (node);
14391 len = json_array_get_length (array);
14392
14393 for (i = 0; i < len; i++)
14394 {
14395 JsonNode *element = json_array_get_element (array, i);
14396 JsonObject *object;
14397 JsonNode *member;
14398
14399 if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
14400 {
14401 g_warning ("Invalid node of type '%s' found, expecting an object",
14402 json_node_type_name (element));
14403 return FALSE;
14404 }
14405
14406 object = json_node_get_object (element);
14407
14408 if (json_object_has_member (object, "x-axis"))
14409 {
14410 member = json_object_get_member (object, "x-axis");
14411
14412 info->axis = CLUTTER_X_AXIS;
14413
14414 if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
14415 {
14416 info->angle = json_node_get_double (member);
14417 retval = TRUE;
14418 }
14419 else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
14420 retval = parse_rotation_array (actor,
14421 json_node_get_array (member),
14422 info);
14423 else
14424 retval = FALSE;
14425 }
14426 else if (json_object_has_member (object, "y-axis"))
14427 {
14428 member = json_object_get_member (object, "y-axis");
14429
14430 info->axis = CLUTTER_Y_AXIS;
14431
14432 if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
14433 {
14434 info->angle = json_node_get_double (member);
14435 retval = TRUE;
14436 }
14437 else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
14438 retval = parse_rotation_array (actor,
14439 json_node_get_array (member),
14440 info);
14441 else
14442 retval = FALSE;
14443 }
14444 else if (json_object_has_member (object, "z-axis"))
14445 {
14446 member = json_object_get_member (object, "z-axis");
14447
14448 info->axis = CLUTTER_Z_AXIS;
14449
14450 if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
14451 {
14452 info->angle = json_node_get_double (member);
14453 retval = TRUE;
14454 }
14455 else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
14456 retval = parse_rotation_array (actor,
14457 json_node_get_array (member),
14458 info);
14459 else
14460 retval = FALSE;
14461 }
14462 }
14463
14464 return retval;
14465 }
14466
14467 static GSList *
parse_actor_metas(ClutterScript * script,ClutterActor * actor,JsonNode * node)14468 parse_actor_metas (ClutterScript *script,
14469 ClutterActor *actor,
14470 JsonNode *node)
14471 {
14472 GList *elements, *l;
14473 GSList *retval = NULL;
14474
14475 if (!JSON_NODE_HOLDS_ARRAY (node))
14476 return NULL;
14477
14478 elements = json_array_get_elements (json_node_get_array (node));
14479
14480 for (l = elements; l != NULL; l = l->next)
14481 {
14482 JsonNode *element = l->data;
14483 const gchar *id_ = _clutter_script_get_id_from_node (element);
14484 GObject *meta;
14485
14486 if (id_ == NULL || *id_ == '\0')
14487 continue;
14488
14489 meta = clutter_script_get_object (script, id_);
14490 if (meta == NULL)
14491 continue;
14492
14493 retval = g_slist_prepend (retval, meta);
14494 }
14495
14496 g_list_free (elements);
14497
14498 return g_slist_reverse (retval);
14499 }
14500
14501 static GSList *
parse_behaviours(ClutterScript * script,ClutterActor * actor,JsonNode * node)14502 parse_behaviours (ClutterScript *script,
14503 ClutterActor *actor,
14504 JsonNode *node)
14505 {
14506 GList *elements, *l;
14507 GSList *retval = NULL;
14508
14509 if (!JSON_NODE_HOLDS_ARRAY (node))
14510 return NULL;
14511
14512 elements = json_array_get_elements (json_node_get_array (node));
14513
14514 for (l = elements; l != NULL; l = l->next)
14515 {
14516 JsonNode *element = l->data;
14517 const gchar *id_ = _clutter_script_get_id_from_node (element);
14518 GObject *behaviour;
14519
14520 if (id_ == NULL || *id_ == '\0')
14521 continue;
14522
14523 behaviour = clutter_script_get_object (script, id_);
14524 if (behaviour == NULL)
14525 continue;
14526
14527 retval = g_slist_prepend (retval, behaviour);
14528 }
14529
14530 g_list_free (elements);
14531
14532 return g_slist_reverse (retval);
14533 }
14534
14535 static ClutterMargin *
parse_margin(ClutterActor * self,JsonNode * node)14536 parse_margin (ClutterActor *self,
14537 JsonNode *node)
14538 {
14539 ClutterMargin *margin;
14540 JsonArray *array;
14541
14542 if (!JSON_NODE_HOLDS_ARRAY (node))
14543 {
14544 g_warning ("The margin property must be an array of 1 to 4 elements");
14545 return NULL;
14546 }
14547
14548 margin = clutter_margin_new ();
14549 array = json_node_get_array (node);
14550 switch (json_array_get_length (array))
14551 {
14552 case 1:
14553 margin->top = margin->right = margin->bottom = margin->left =
14554 parse_units (self, 0, json_array_get_element (array, 0));
14555 break;
14556
14557 case 2:
14558 margin->top = margin->bottom =
14559 parse_units (self, 0, json_array_get_element (array, 0));
14560 margin->right = margin->left =
14561 parse_units (self, 0, json_array_get_element (array, 1));
14562 break;
14563
14564 case 3:
14565 margin->top =
14566 parse_units (self, 0, json_array_get_element (array, 0));
14567 margin->right = margin->left =
14568 parse_units (self, 0, json_array_get_element (array, 1));
14569 margin->bottom =
14570 parse_units (self, 0, json_array_get_element (array, 2));
14571 break;
14572
14573 case 4:
14574 margin->top =
14575 parse_units (self, 0, json_array_get_element (array, 0));
14576 margin->right =
14577 parse_units (self, 0, json_array_get_element (array, 1));
14578 margin->bottom =
14579 parse_units (self, 0, json_array_get_element (array, 2));
14580 margin->left =
14581 parse_units (self, 0, json_array_get_element (array, 3));
14582 break;
14583
14584 default:
14585 g_warning ("The margin property must be an array of 1 to 4 elements");
14586 clutter_margin_free (margin);
14587 return NULL;
14588 }
14589 return margin;
14590 }
14591
14592 static gboolean
clutter_actor_parse_custom_node(ClutterScriptable * scriptable,ClutterScript * script,GValue * value,const gchar * name,JsonNode * node)14593 clutter_actor_parse_custom_node (ClutterScriptable *scriptable,
14594 ClutterScript *script,
14595 GValue *value,
14596 const gchar *name,
14597 JsonNode *node)
14598 {
14599 ClutterActor *actor = CLUTTER_ACTOR (scriptable);
14600 gboolean retval = FALSE;
14601
14602 if ((name[0] == 'x' && name[1] == '\0') ||
14603 (name[0] == 'y' && name[1] == '\0') ||
14604 (strcmp (name, "width") == 0) ||
14605 (strcmp (name, "height") == 0) ||
14606 (strcmp (name, "anchor_x") == 0) ||
14607 (strcmp (name, "anchor_y") == 0))
14608 {
14609 ParseDimension dimension;
14610 gfloat units;
14611
14612 if (name[0] == 'x')
14613 dimension = PARSE_X;
14614 else if (name[0] == 'y')
14615 dimension = PARSE_Y;
14616 else if (name[0] == 'w')
14617 dimension = PARSE_WIDTH;
14618 else if (name[0] == 'h')
14619 dimension = PARSE_HEIGHT;
14620 else if (name[0] == 'a' && name[7] == 'x')
14621 dimension = PARSE_ANCHOR_X;
14622 else if (name[0] == 'a' && name[7] == 'y')
14623 dimension = PARSE_ANCHOR_Y;
14624 else
14625 return FALSE;
14626
14627 units = parse_units (actor, dimension, node);
14628
14629 /* convert back to pixels: all properties are pixel-based */
14630 g_value_init (value, G_TYPE_FLOAT);
14631 g_value_set_float (value, units);
14632
14633 retval = TRUE;
14634 }
14635 else if (strcmp (name, "rotation") == 0)
14636 {
14637 RotationInfo *info;
14638
14639 info = g_slice_new0 (RotationInfo);
14640 retval = parse_rotation (actor, node, info);
14641
14642 if (retval)
14643 {
14644 g_value_init (value, G_TYPE_POINTER);
14645 g_value_set_pointer (value, info);
14646 }
14647 else
14648 g_slice_free (RotationInfo, info);
14649 }
14650 else if (strcmp (name, "behaviours") == 0)
14651 {
14652 GSList *l;
14653
14654 #ifdef CLUTTER_ENABLE_DEBUG
14655 if (G_UNLIKELY (_clutter_diagnostic_enabled ()))
14656 _clutter_diagnostic_message ("The 'behaviours' key is deprecated "
14657 "and it should not be used in newly "
14658 "written ClutterScript definitions.");
14659 #endif
14660
14661 l = parse_behaviours (script, actor, node);
14662
14663 g_value_init (value, G_TYPE_POINTER);
14664 g_value_set_pointer (value, l);
14665
14666 retval = TRUE;
14667 }
14668 else if (strcmp (name, "actions") == 0 ||
14669 strcmp (name, "constraints") == 0 ||
14670 strcmp (name, "effects") == 0)
14671 {
14672 GSList *l;
14673
14674 l = parse_actor_metas (script, actor, node);
14675
14676 g_value_init (value, G_TYPE_POINTER);
14677 g_value_set_pointer (value, l);
14678
14679 retval = TRUE;
14680 }
14681 else if (strcmp (name, "margin") == 0)
14682 {
14683 ClutterMargin *margin = parse_margin (actor, node);
14684
14685 if (margin)
14686 {
14687 g_value_init (value, CLUTTER_TYPE_MARGIN);
14688 g_value_set_boxed (value, margin);
14689 retval = TRUE;
14690 }
14691 }
14692
14693 return retval;
14694 }
14695
14696 static void
clutter_actor_set_custom_property(ClutterScriptable * scriptable,ClutterScript * script,const gchar * name,const GValue * value)14697 clutter_actor_set_custom_property (ClutterScriptable *scriptable,
14698 ClutterScript *script,
14699 const gchar *name,
14700 const GValue *value)
14701 {
14702 ClutterActor *actor = CLUTTER_ACTOR (scriptable);
14703
14704 #ifdef CLUTTER_ENABLE_DEBUG
14705 if (G_UNLIKELY (CLUTTER_HAS_DEBUG (SCRIPT)))
14706 {
14707 gchar *tmp = g_strdup_value_contents (value);
14708
14709 CLUTTER_NOTE (SCRIPT,
14710 "in ClutterActor::set_custom_property('%s') = %s",
14711 name,
14712 tmp);
14713
14714 g_free (tmp);
14715 }
14716 #endif /* CLUTTER_ENABLE_DEBUG */
14717
14718 if (strcmp (name, "rotation") == 0)
14719 {
14720 RotationInfo *info;
14721
14722 if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
14723 return;
14724
14725 info = g_value_get_pointer (value);
14726
14727 clutter_actor_set_rotation (actor,
14728 info->axis, info->angle,
14729 info->center_x,
14730 info->center_y,
14731 info->center_z);
14732
14733 g_slice_free (RotationInfo, info);
14734
14735 return;
14736 }
14737
14738 if (strcmp (name, "behaviours") == 0)
14739 {
14740 GSList *behaviours, *l;
14741
14742 if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
14743 return;
14744
14745 behaviours = g_value_get_pointer (value);
14746 for (l = behaviours; l != NULL; l = l->next)
14747 {
14748 ClutterBehaviour *behaviour = l->data;
14749
14750 clutter_behaviour_apply (behaviour, actor);
14751 }
14752
14753 g_slist_free (behaviours);
14754
14755 return;
14756 }
14757
14758 if (strcmp (name, "actions") == 0 ||
14759 strcmp (name, "constraints") == 0 ||
14760 strcmp (name, "effects") == 0)
14761 {
14762 GSList *metas, *l;
14763
14764 if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
14765 return;
14766
14767 metas = g_value_get_pointer (value);
14768 for (l = metas; l != NULL; l = l->next)
14769 {
14770 if (name[0] == 'a')
14771 clutter_actor_add_action (actor, l->data);
14772
14773 if (name[0] == 'c')
14774 clutter_actor_add_constraint (actor, l->data);
14775
14776 if (name[0] == 'e')
14777 clutter_actor_add_effect (actor, l->data);
14778 }
14779
14780 g_slist_free (metas);
14781
14782 return;
14783 }
14784 if (strcmp (name, "margin") == 0)
14785 {
14786 clutter_actor_set_margin (actor, g_value_get_boxed (value));
14787 return;
14788 }
14789
14790 g_object_set_property (G_OBJECT (scriptable), name, value);
14791 }
14792
14793 static void
clutter_scriptable_iface_init(ClutterScriptableIface * iface)14794 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
14795 {
14796 iface->parse_custom_node = clutter_actor_parse_custom_node;
14797 iface->set_custom_property = clutter_actor_set_custom_property;
14798 }
14799
14800 static ClutterActorMeta *
get_meta_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)14801 get_meta_from_animation_property (ClutterActor *actor,
14802 const gchar *name,
14803 gchar **name_p)
14804 {
14805 ClutterActorPrivate *priv = actor->priv;
14806 ClutterActorMeta *meta = NULL;
14807 gchar **tokens;
14808
14809 /* if this is not a special property, fall through */
14810 if (name[0] != '@')
14811 return NULL;
14812
14813 /* detect the properties named using the following spec:
14814 *
14815 * @<section>.<meta-name>.<property-name>
14816 *
14817 * where <section> can be one of the following:
14818 *
14819 * - actions
14820 * - constraints
14821 * - effects
14822 *
14823 * and <meta-name> is the name set on a specific ActorMeta
14824 */
14825
14826 tokens = g_strsplit (name + 1, ".", -1);
14827 if (tokens == NULL || g_strv_length (tokens) != 3)
14828 {
14829 CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
14830 name + 1);
14831 g_strfreev (tokens);
14832 return NULL;
14833 }
14834
14835 if (strcmp (tokens[0], "actions") == 0)
14836 meta = _clutter_meta_group_get_meta (priv->actions, tokens[1]);
14837
14838 if (strcmp (tokens[0], "constraints") == 0)
14839 meta = _clutter_meta_group_get_meta (priv->constraints, tokens[1]);
14840
14841 if (strcmp (tokens[0], "effects") == 0)
14842 meta = _clutter_meta_group_get_meta (priv->effects, tokens[1]);
14843
14844 if (name_p != NULL)
14845 *name_p = g_strdup (tokens[2]);
14846
14847 CLUTTER_NOTE (ANIMATION,
14848 "Looking for property '%s' of object '%s' in section '%s'",
14849 tokens[2],
14850 tokens[1],
14851 tokens[0]);
14852
14853 g_strfreev (tokens);
14854
14855 return meta;
14856 }
14857
14858 static GParamSpec *
clutter_actor_find_property(ClutterAnimatable * animatable,const gchar * property_name)14859 clutter_actor_find_property (ClutterAnimatable *animatable,
14860 const gchar *property_name)
14861 {
14862 ClutterActorMeta *meta = NULL;
14863 GObjectClass *klass = NULL;
14864 GParamSpec *pspec = NULL;
14865 gchar *p_name = NULL;
14866
14867 meta = get_meta_from_animation_property (CLUTTER_ACTOR (animatable),
14868 property_name,
14869 &p_name);
14870
14871 if (meta != NULL)
14872 {
14873 klass = G_OBJECT_GET_CLASS (meta);
14874
14875 pspec = g_object_class_find_property (klass, p_name);
14876 }
14877 else
14878 {
14879 klass = G_OBJECT_GET_CLASS (animatable);
14880
14881 pspec = g_object_class_find_property (klass, property_name);
14882 }
14883
14884 g_free (p_name);
14885
14886 return pspec;
14887 }
14888
14889 static void
clutter_actor_get_initial_state(ClutterAnimatable * animatable,const gchar * property_name,GValue * initial)14890 clutter_actor_get_initial_state (ClutterAnimatable *animatable,
14891 const gchar *property_name,
14892 GValue *initial)
14893 {
14894 ClutterActorMeta *meta = NULL;
14895 gchar *p_name = NULL;
14896
14897 meta = get_meta_from_animation_property (CLUTTER_ACTOR (animatable),
14898 property_name,
14899 &p_name);
14900
14901 if (meta != NULL)
14902 g_object_get_property (G_OBJECT (meta), p_name, initial);
14903 else
14904 g_object_get_property (G_OBJECT (animatable), property_name, initial);
14905
14906 g_free (p_name);
14907 }
14908
14909 /*
14910 * clutter_actor_set_animatable_property:
14911 * @actor: a #ClutterActor
14912 * @prop_id: the paramspec id
14913 * @value: the value to set
14914 * @pspec: the paramspec
14915 *
14916 * Sets values of animatable properties.
14917 *
14918 * This is a variant of clutter_actor_set_property() that gets called
14919 * by the #ClutterAnimatable implementation of #ClutterActor for the
14920 * properties with the %CLUTTER_PARAM_ANIMATABLE flag set on their
14921 * #GParamSpec.
14922 *
14923 * Unlike the implementation of #GObjectClass.set_property(), this
14924 * function will not update the interval if a transition involving an
14925 * animatable property is in progress - this avoids cycles with the
14926 * transition API calling the public API.
14927 */
14928 static void
clutter_actor_set_animatable_property(ClutterActor * actor,guint prop_id,const GValue * value,GParamSpec * pspec)14929 clutter_actor_set_animatable_property (ClutterActor *actor,
14930 guint prop_id,
14931 const GValue *value,
14932 GParamSpec *pspec)
14933 {
14934 GObject *obj = G_OBJECT (actor);
14935
14936 g_object_freeze_notify (obj);
14937
14938 switch (prop_id)
14939 {
14940 case PROP_X:
14941 clutter_actor_set_x_internal (actor, g_value_get_float (value));
14942 break;
14943
14944 case PROP_Y:
14945 clutter_actor_set_y_internal (actor, g_value_get_float (value));
14946 break;
14947
14948 case PROP_POSITION:
14949 clutter_actor_set_position_internal (actor, g_value_get_boxed (value));
14950 break;
14951
14952 case PROP_WIDTH:
14953 clutter_actor_set_width_internal (actor, g_value_get_float (value));
14954 break;
14955
14956 case PROP_HEIGHT:
14957 clutter_actor_set_height_internal (actor, g_value_get_float (value));
14958 break;
14959
14960 case PROP_SIZE:
14961 clutter_actor_set_size_internal (actor, g_value_get_boxed (value));
14962 break;
14963
14964 case PROP_ALLOCATION:
14965 clutter_actor_allocate_internal (actor,
14966 g_value_get_boxed (value),
14967 actor->priv->allocation_flags);
14968 clutter_actor_queue_redraw (actor);
14969 break;
14970
14971 case PROP_DEPTH:
14972 clutter_actor_set_depth_internal (actor, g_value_get_float (value));
14973 break;
14974
14975 case PROP_Z_POSITION:
14976 clutter_actor_set_z_position_internal (actor, g_value_get_float (value));
14977 break;
14978
14979 case PROP_OPACITY:
14980 clutter_actor_set_opacity_internal (actor, g_value_get_uint (value));
14981 break;
14982
14983 case PROP_BACKGROUND_COLOR:
14984 clutter_actor_set_background_color_internal (actor, clutter_value_get_color (value));
14985 break;
14986
14987 case PROP_PIVOT_POINT:
14988 clutter_actor_set_pivot_point_internal (actor, g_value_get_boxed (value));
14989 break;
14990
14991 case PROP_PIVOT_POINT_Z:
14992 clutter_actor_set_pivot_point_z_internal (actor, g_value_get_float (value));
14993 break;
14994
14995 case PROP_TRANSLATION_X:
14996 case PROP_TRANSLATION_Y:
14997 case PROP_TRANSLATION_Z:
14998 clutter_actor_set_translation_internal (actor,
14999 g_value_get_float (value),
15000 pspec);
15001 break;
15002
15003 case PROP_SCALE_X:
15004 case PROP_SCALE_Y:
15005 case PROP_SCALE_Z:
15006 clutter_actor_set_scale_factor_internal (actor,
15007 g_value_get_double (value),
15008 pspec);
15009 break;
15010
15011 case PROP_ROTATION_ANGLE_X:
15012 case PROP_ROTATION_ANGLE_Y:
15013 case PROP_ROTATION_ANGLE_Z:
15014 clutter_actor_set_rotation_angle_internal (actor,
15015 g_value_get_double (value),
15016 pspec);
15017 break;
15018
15019 case PROP_CONTENT_BOX:
15020 clutter_actor_store_content_box (actor, g_value_get_boxed (value));
15021 break;
15022
15023 case PROP_MARGIN_TOP:
15024 case PROP_MARGIN_BOTTOM:
15025 case PROP_MARGIN_LEFT:
15026 case PROP_MARGIN_RIGHT:
15027 clutter_actor_set_margin_internal (actor, g_value_get_float (value),
15028 pspec);
15029 break;
15030
15031 case PROP_TRANSFORM:
15032 clutter_actor_set_transform_internal (actor, g_value_get_boxed (value));
15033 break;
15034
15035 case PROP_CHILD_TRANSFORM:
15036 clutter_actor_set_child_transform_internal (actor, g_value_get_boxed (value));
15037 break;
15038
15039 default:
15040 g_object_set_property (obj, pspec->name, value);
15041 break;
15042 }
15043
15044 g_object_thaw_notify (obj);
15045 }
15046
15047 static void
clutter_actor_set_final_state(ClutterAnimatable * animatable,const gchar * property_name,const GValue * final)15048 clutter_actor_set_final_state (ClutterAnimatable *animatable,
15049 const gchar *property_name,
15050 const GValue *final)
15051 {
15052 ClutterActor *actor = CLUTTER_ACTOR (animatable);
15053 ClutterActorMeta *meta = NULL;
15054 gchar *p_name = NULL;
15055
15056 meta = get_meta_from_animation_property (actor,
15057 property_name,
15058 &p_name);
15059 if (meta != NULL)
15060 g_object_set_property (G_OBJECT (meta), p_name, final);
15061 else
15062 {
15063 GObjectClass *obj_class = G_OBJECT_GET_CLASS (animatable);
15064 GParamSpec *pspec;
15065
15066 pspec = g_object_class_find_property (obj_class, property_name);
15067
15068 if (pspec != NULL)
15069 {
15070 if ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0)
15071 {
15072 /* XXX - I'm going to the special hell for this */
15073 clutter_actor_set_animatable_property (actor, pspec->param_id, final, pspec);
15074 }
15075 else
15076 g_object_set_property (G_OBJECT (animatable), pspec->name, final);
15077 }
15078 }
15079
15080 g_free (p_name);
15081 }
15082
15083 static void
clutter_animatable_iface_init(ClutterAnimatableIface * iface)15084 clutter_animatable_iface_init (ClutterAnimatableIface *iface)
15085 {
15086 iface->find_property = clutter_actor_find_property;
15087 iface->get_initial_state = clutter_actor_get_initial_state;
15088 iface->set_final_state = clutter_actor_set_final_state;
15089 }
15090
15091 /**
15092 * clutter_actor_transform_stage_point:
15093 * @self: A #ClutterActor
15094 * @x: (in): x screen coordinate of the point to unproject
15095 * @y: (in): y screen coordinate of the point to unproject
15096 * @x_out: (out): return location for the unprojected x coordinance
15097 * @y_out: (out): return location for the unprojected y coordinance
15098 *
15099 * This function translates screen coordinates (@x, @y) to
15100 * coordinates relative to the actor. For example, it can be used to translate
15101 * screen events from global screen coordinates into actor-local coordinates.
15102 *
15103 * The conversion can fail, notably if the transform stack results in the
15104 * actor being projected on the screen as a mere line.
15105 *
15106 * The conversion should not be expected to be pixel-perfect due to the
15107 * nature of the operation. In general the error grows when the skewing
15108 * of the actor rectangle on screen increases.
15109 *
15110 * This function can be computationally intensive.
15111 *
15112 * This function only works when the allocation is up-to-date, i.e. inside of
15113 * the #ClutterActorClass.paint() implementation
15114 *
15115 * Return value: %TRUE if conversion was successful.
15116 *
15117 * Since: 0.6
15118 */
15119 gboolean
clutter_actor_transform_stage_point(ClutterActor * self,gfloat x,gfloat y,gfloat * x_out,gfloat * y_out)15120 clutter_actor_transform_stage_point (ClutterActor *self,
15121 gfloat x,
15122 gfloat y,
15123 gfloat *x_out,
15124 gfloat *y_out)
15125 {
15126 ClutterVertex v[4];
15127 double ST[3][3];
15128 double RQ[3][3];
15129 int du, dv;
15130 double px, py;
15131 double det;
15132 float xf, yf, wf;
15133 ClutterActorPrivate *priv;
15134
15135 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15136
15137 priv = self->priv;
15138
15139 /* This implementation is based on the quad -> quad projection algorithm
15140 * described by Paul Heckbert in:
15141 *
15142 * http://www.cs.cmu.edu/~ph/texfund/texfund.pdf
15143 *
15144 * and the sample implementation at:
15145 *
15146 * http://www.cs.cmu.edu/~ph/src/texfund/
15147 *
15148 * Our texture is a rectangle with origin [0, 0], so we are mapping from
15149 * quad to rectangle only, which significantly simplifies things; the
15150 * function calls have been unrolled, and most of the math is done in fixed
15151 * point.
15152 */
15153 clutter_actor_get_abs_allocation_vertices (self, v);
15154
15155 /* Keeping these as ints simplifies the multiplication (no significant
15156 * loss of precision here).
15157 */
15158 du = ceilf (priv->allocation.x2 - priv->allocation.x1);
15159 dv = ceilf (priv->allocation.y2 - priv->allocation.y1);
15160
15161 if (du == 0 || dv == 0)
15162 return FALSE;
15163
15164 #define DET(a,b,c,d) (((a) * (d)) - ((b) * (c)))
15165
15166 /* First, find mapping from unit uv square to xy quadrilateral; this
15167 * equivalent to the pmap_square_quad() functions in the sample
15168 * implementation, which we can simplify, since our target is always
15169 * a rectangle.
15170 */
15171 px = v[0].x - v[1].x + v[3].x - v[2].x;
15172 py = v[0].y - v[1].y + v[3].y - v[2].y;
15173
15174 if ((int) px == 0 && (int) py == 0)
15175 {
15176 /* affine transform */
15177 RQ[0][0] = v[1].x - v[0].x;
15178 RQ[1][0] = v[3].x - v[1].x;
15179 RQ[2][0] = v[0].x;
15180 RQ[0][1] = v[1].y - v[0].y;
15181 RQ[1][1] = v[3].y - v[1].y;
15182 RQ[2][1] = v[0].y;
15183 RQ[0][2] = 0.0;
15184 RQ[1][2] = 0.0;
15185 RQ[2][2] = 1.0;
15186 }
15187 else
15188 {
15189 /* projective transform */
15190 double dx1, dx2, dy1, dy2;
15191
15192 dx1 = v[1].x - v[3].x;
15193 dx2 = v[2].x - v[3].x;
15194 dy1 = v[1].y - v[3].y;
15195 dy2 = v[2].y - v[3].y;
15196
15197 det = DET (dx1, dx2, dy1, dy2);
15198 if (fabs (det) <= DBL_EPSILON)
15199 return FALSE;
15200
15201 RQ[0][2] = DET (px, dx2, py, dy2) / det;
15202 RQ[1][2] = DET (dx1, px, dy1, py) / det;
15203 RQ[1][2] = DET (dx1, px, dy1, py) / det;
15204 RQ[2][2] = 1.0;
15205 RQ[0][0] = v[1].x - v[0].x + (RQ[0][2] * v[1].x);
15206 RQ[1][0] = v[2].x - v[0].x + (RQ[1][2] * v[2].x);
15207 RQ[2][0] = v[0].x;
15208 RQ[0][1] = v[1].y - v[0].y + (RQ[0][2] * v[1].y);
15209 RQ[1][1] = v[2].y - v[0].y + (RQ[1][2] * v[2].y);
15210 RQ[2][1] = v[0].y;
15211 }
15212
15213 /*
15214 * Now combine with transform from our rectangle (u0,v0,u1,v1) to unit
15215 * square. Since our rectangle is based at 0,0 we only need to scale.
15216 */
15217 RQ[0][0] /= du;
15218 RQ[1][0] /= dv;
15219 RQ[0][1] /= du;
15220 RQ[1][1] /= dv;
15221 RQ[0][2] /= du;
15222 RQ[1][2] /= dv;
15223
15224 /*
15225 * Now RQ is transform from uv rectangle to xy quadrilateral; we need an
15226 * inverse of that.
15227 */
15228 ST[0][0] = DET (RQ[1][1], RQ[1][2], RQ[2][1], RQ[2][2]);
15229 ST[1][0] = DET (RQ[1][2], RQ[1][0], RQ[2][2], RQ[2][0]);
15230 ST[2][0] = DET (RQ[1][0], RQ[1][1], RQ[2][0], RQ[2][1]);
15231 ST[0][1] = DET (RQ[2][1], RQ[2][2], RQ[0][1], RQ[0][2]);
15232 ST[1][1] = DET (RQ[2][2], RQ[2][0], RQ[0][2], RQ[0][0]);
15233 ST[2][1] = DET (RQ[2][0], RQ[2][1], RQ[0][0], RQ[0][1]);
15234 ST[0][2] = DET (RQ[0][1], RQ[0][2], RQ[1][1], RQ[1][2]);
15235 ST[1][2] = DET (RQ[0][2], RQ[0][0], RQ[1][2], RQ[1][0]);
15236 ST[2][2] = DET (RQ[0][0], RQ[0][1], RQ[1][0], RQ[1][1]);
15237
15238 /*
15239 * Check the resulting matrix is OK.
15240 */
15241 det = (RQ[0][0] * ST[0][0])
15242 + (RQ[0][1] * ST[0][1])
15243 + (RQ[0][2] * ST[0][2]);
15244 if (fabs (det) <= DBL_EPSILON)
15245 return FALSE;
15246
15247 /*
15248 * Now transform our point with the ST matrix; the notional w
15249 * coordinate is 1, hence the last part is simply added.
15250 */
15251 xf = x * ST[0][0] + y * ST[1][0] + ST[2][0];
15252 yf = x * ST[0][1] + y * ST[1][1] + ST[2][1];
15253 wf = x * ST[0][2] + y * ST[1][2] + ST[2][2];
15254
15255 if (x_out)
15256 *x_out = xf / wf;
15257
15258 if (y_out)
15259 *y_out = yf / wf;
15260
15261 #undef DET
15262
15263 return TRUE;
15264 }
15265
15266 /**
15267 * clutter_actor_is_rotated:
15268 * @self: a #ClutterActor
15269 *
15270 * Checks whether any rotation is applied to the actor.
15271 *
15272 * Return value: %TRUE if the actor is rotated.
15273 *
15274 * Since: 0.6
15275 */
15276 gboolean
clutter_actor_is_rotated(ClutterActor * self)15277 clutter_actor_is_rotated (ClutterActor *self)
15278 {
15279 const ClutterTransformInfo *info;
15280
15281 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15282
15283 info = _clutter_actor_get_transform_info_or_defaults (self);
15284
15285 if (info->rx_angle || info->ry_angle || info->rz_angle)
15286 return TRUE;
15287
15288 return FALSE;
15289 }
15290
15291 /**
15292 * clutter_actor_is_scaled:
15293 * @self: a #ClutterActor
15294 *
15295 * Checks whether the actor is scaled in either dimension.
15296 *
15297 * Return value: %TRUE if the actor is scaled.
15298 *
15299 * Since: 0.6
15300 */
15301 gboolean
clutter_actor_is_scaled(ClutterActor * self)15302 clutter_actor_is_scaled (ClutterActor *self)
15303 {
15304 const ClutterTransformInfo *info;
15305
15306 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15307
15308 info = _clutter_actor_get_transform_info_or_defaults (self);
15309
15310 if (info->scale_x != 1.0 || info->scale_y != 1.0)
15311 return TRUE;
15312
15313 return FALSE;
15314 }
15315
15316 ClutterActor *
_clutter_actor_get_stage_internal(ClutterActor * actor)15317 _clutter_actor_get_stage_internal (ClutterActor *actor)
15318 {
15319 while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor))
15320 actor = actor->priv->parent;
15321
15322 return actor;
15323 }
15324
15325 /**
15326 * clutter_actor_get_stage:
15327 * @actor: a #ClutterActor
15328 *
15329 * Retrieves the #ClutterStage where @actor is contained.
15330 *
15331 * Return value: (transfer none) (type Clutter.Stage): the stage
15332 * containing the actor, or %NULL
15333 *
15334 * Since: 0.8
15335 */
15336 ClutterActor *
clutter_actor_get_stage(ClutterActor * actor)15337 clutter_actor_get_stage (ClutterActor *actor)
15338 {
15339 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
15340
15341 return _clutter_actor_get_stage_internal (actor);
15342 }
15343
15344 /**
15345 * clutter_actor_allocate_available_size:
15346 * @self: a #ClutterActor
15347 * @x: the actor's X coordinate
15348 * @y: the actor's Y coordinate
15349 * @available_width: the maximum available width, or -1 to use the
15350 * actor's natural width
15351 * @available_height: the maximum available height, or -1 to use the
15352 * actor's natural height
15353 * @flags: flags controlling the allocation
15354 *
15355 * Allocates @self taking into account the #ClutterActor's
15356 * preferred size, but limiting it to the maximum available width
15357 * and height provided.
15358 *
15359 * This function will do the right thing when dealing with the
15360 * actor's request mode.
15361 *
15362 * The implementation of this function is equivalent to:
15363 *
15364 * |[<!-- language="C" -->
15365 * if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
15366 * {
15367 * clutter_actor_get_preferred_width (self, available_height,
15368 * &min_width,
15369 * &natural_width);
15370 * width = CLAMP (natural_width, min_width, available_width);
15371 *
15372 * clutter_actor_get_preferred_height (self, width,
15373 * &min_height,
15374 * &natural_height);
15375 * height = CLAMP (natural_height, min_height, available_height);
15376 * }
15377 * else if (request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
15378 * {
15379 * clutter_actor_get_preferred_height (self, available_width,
15380 * &min_height,
15381 * &natural_height);
15382 * height = CLAMP (natural_height, min_height, available_height);
15383 *
15384 * clutter_actor_get_preferred_width (self, height,
15385 * &min_width,
15386 * &natural_width);
15387 * width = CLAMP (natural_width, min_width, available_width);
15388 * }
15389 * else if (request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
15390 * {
15391 * clutter_content_get_preferred_size (content, &natural_width, &natural_height);
15392 *
15393 * width = CLAMP (natural_width, 0, available_width);
15394 * height = CLAMP (natural_height, 0, available_height);
15395 * }
15396 *
15397 * box.x1 = x; box.y1 = y;
15398 * box.x2 = box.x1 + available_width;
15399 * box.y2 = box.y1 + available_height;
15400 * clutter_actor_allocate (self, &box, flags);
15401 * ]|
15402 *
15403 * This function can be used by fluid layout managers to allocate
15404 * an actor's preferred size without making it bigger than the area
15405 * available for the container.
15406 *
15407 * Since: 1.0
15408 */
15409 void
clutter_actor_allocate_available_size(ClutterActor * self,gfloat x,gfloat y,gfloat available_width,gfloat available_height,ClutterAllocationFlags flags)15410 clutter_actor_allocate_available_size (ClutterActor *self,
15411 gfloat x,
15412 gfloat y,
15413 gfloat available_width,
15414 gfloat available_height,
15415 ClutterAllocationFlags flags)
15416 {
15417 ClutterActorPrivate *priv;
15418 gfloat width, height;
15419 gfloat min_width, min_height;
15420 gfloat natural_width, natural_height;
15421 ClutterActorBox box;
15422
15423 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15424
15425 priv = self->priv;
15426
15427 width = height = 0.0;
15428
15429 switch (priv->request_mode)
15430 {
15431 case CLUTTER_REQUEST_HEIGHT_FOR_WIDTH:
15432 clutter_actor_get_preferred_width (self, available_height,
15433 &min_width,
15434 &natural_width);
15435 width = CLAMP (natural_width, min_width, available_width);
15436
15437 clutter_actor_get_preferred_height (self, width,
15438 &min_height,
15439 &natural_height);
15440 height = CLAMP (natural_height, min_height, available_height);
15441 break;
15442
15443 case CLUTTER_REQUEST_WIDTH_FOR_HEIGHT:
15444 clutter_actor_get_preferred_height (self, available_width,
15445 &min_height,
15446 &natural_height);
15447 height = CLAMP (natural_height, min_height, available_height);
15448
15449 clutter_actor_get_preferred_width (self, height,
15450 &min_width,
15451 &natural_width);
15452 width = CLAMP (natural_width, min_width, available_width);
15453 break;
15454
15455 case CLUTTER_REQUEST_CONTENT_SIZE:
15456 if (priv->content != NULL)
15457 {
15458 clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
15459
15460 width = CLAMP (natural_width, 0, available_width);
15461 height = CLAMP (natural_height, 0, available_height);
15462 }
15463 break;
15464 }
15465
15466
15467 box.x1 = x;
15468 box.y1 = y;
15469 box.x2 = box.x1 + width;
15470 box.y2 = box.y1 + height;
15471 clutter_actor_allocate (self, &box, flags);
15472 }
15473
15474 /**
15475 * clutter_actor_allocate_preferred_size:
15476 * @self: a #ClutterActor
15477 * @flags: flags controlling the allocation
15478 *
15479 * Allocates the natural size of @self.
15480 *
15481 * This function is a utility call for #ClutterActor implementations
15482 * that allocates the actor's preferred natural size. It can be used
15483 * by fixed layout managers (like #ClutterGroup or so called
15484 * 'composite actors') inside the ClutterActor::allocate
15485 * implementation to give each child exactly how much space it
15486 * requires, regardless of the size of the parent.
15487 *
15488 * This function is not meant to be used by applications. It is also
15489 * not meant to be used outside the implementation of the
15490 * #ClutterActorClass.allocate virtual function.
15491 *
15492 * Since: 0.8
15493 */
15494 void
clutter_actor_allocate_preferred_size(ClutterActor * self,ClutterAllocationFlags flags)15495 clutter_actor_allocate_preferred_size (ClutterActor *self,
15496 ClutterAllocationFlags flags)
15497 {
15498 gfloat actor_x, actor_y;
15499 gfloat natural_width, natural_height;
15500 ClutterActorBox actor_box;
15501 ClutterActorPrivate *priv;
15502 const ClutterLayoutInfo *info;
15503
15504 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15505
15506 priv = self->priv;
15507
15508 if (priv->position_set)
15509 {
15510 info = _clutter_actor_get_layout_info_or_defaults (self);
15511 actor_x = info->fixed_pos.x;
15512 actor_y = info->fixed_pos.y;
15513 }
15514 else
15515 {
15516 actor_x = 0;
15517 actor_y = 0;
15518 }
15519
15520 clutter_actor_get_preferred_size (self,
15521 NULL, NULL,
15522 &natural_width,
15523 &natural_height);
15524
15525 actor_box.x1 = actor_x;
15526 actor_box.y1 = actor_y;
15527 actor_box.x2 = actor_box.x1 + natural_width;
15528 actor_box.y2 = actor_box.y1 + natural_height;
15529
15530 clutter_actor_allocate (self, &actor_box, flags);
15531 }
15532
15533 /**
15534 * clutter_actor_allocate_align_fill:
15535 * @self: a #ClutterActor
15536 * @box: a #ClutterActorBox, containing the available width and height
15537 * @x_align: the horizontal alignment, between 0 and 1
15538 * @y_align: the vertical alignment, between 0 and 1
15539 * @x_fill: whether the actor should fill horizontally
15540 * @y_fill: whether the actor should fill vertically
15541 * @flags: allocation flags to be passed to clutter_actor_allocate()
15542 *
15543 * Allocates @self by taking into consideration the available allocation
15544 * area; an alignment factor on either axis; and whether the actor should
15545 * fill the allocation on either axis.
15546 *
15547 * The @box should contain the available allocation width and height;
15548 * if the x1 and y1 members of #ClutterActorBox are not set to 0, the
15549 * allocation will be offset by their value.
15550 *
15551 * This function takes into consideration the geometry request specified by
15552 * the #ClutterActor:request-mode property, and the text direction.
15553 *
15554 * This function is useful for fluid layout managers using legacy alignment
15555 * flags. Newly written layout managers should use the #ClutterActor:x-align
15556 * and #ClutterActor:y-align properties, instead, and just call
15557 * clutter_actor_allocate() inside their #ClutterActorClass.allocate()
15558 * implementation.
15559 *
15560 * Since: 1.4
15561 */
15562 void
clutter_actor_allocate_align_fill(ClutterActor * self,const ClutterActorBox * box,gdouble x_align,gdouble y_align,gboolean x_fill,gboolean y_fill,ClutterAllocationFlags flags)15563 clutter_actor_allocate_align_fill (ClutterActor *self,
15564 const ClutterActorBox *box,
15565 gdouble x_align,
15566 gdouble y_align,
15567 gboolean x_fill,
15568 gboolean y_fill,
15569 ClutterAllocationFlags flags)
15570 {
15571 ClutterActorPrivate *priv;
15572 ClutterActorBox allocation = CLUTTER_ACTOR_BOX_INIT_ZERO;
15573 gfloat x_offset, y_offset;
15574 gfloat available_width, available_height;
15575 gfloat child_width = 0.f, child_height = 0.f;
15576
15577 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15578 g_return_if_fail (box != NULL);
15579 g_return_if_fail (x_align >= 0.0 && x_align <= 1.0);
15580 g_return_if_fail (y_align >= 0.0 && y_align <= 1.0);
15581
15582 priv = self->priv;
15583
15584 clutter_actor_box_get_origin (box, &x_offset, &y_offset);
15585 clutter_actor_box_get_size (box, &available_width, &available_height);
15586
15587 if (available_width <= 0)
15588 available_width = 0.f;
15589
15590 if (available_height <= 0)
15591 available_height = 0.f;
15592
15593 allocation.x1 = x_offset;
15594 allocation.y1 = y_offset;
15595
15596 if (available_width == 0.f && available_height == 0.f)
15597 goto out;
15598
15599 if (x_fill)
15600 child_width = available_width;
15601
15602 if (y_fill)
15603 child_height = available_height;
15604
15605 /* if we are filling horizontally and vertically then we're done */
15606 if (x_fill && y_fill)
15607 goto out;
15608
15609 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
15610 {
15611 gfloat min_width, natural_width;
15612 gfloat min_height, natural_height;
15613
15614 if (!x_fill)
15615 {
15616 clutter_actor_get_preferred_width (self, available_height,
15617 &min_width,
15618 &natural_width);
15619
15620 child_width = CLAMP (natural_width, min_width, available_width);
15621 }
15622
15623 if (!y_fill)
15624 {
15625 clutter_actor_get_preferred_height (self, child_width,
15626 &min_height,
15627 &natural_height);
15628
15629 child_height = CLAMP (natural_height, min_height, available_height);
15630 }
15631 }
15632 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
15633 {
15634 gfloat min_width, natural_width;
15635 gfloat min_height, natural_height;
15636
15637 if (!y_fill)
15638 {
15639 clutter_actor_get_preferred_height (self, available_width,
15640 &min_height,
15641 &natural_height);
15642
15643 child_height = CLAMP (natural_height, min_height, available_height);
15644 }
15645
15646 if (!x_fill)
15647 {
15648 clutter_actor_get_preferred_width (self, child_height,
15649 &min_width,
15650 &natural_width);
15651
15652 child_width = CLAMP (natural_width, min_width, available_width);
15653 }
15654 }
15655 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
15656 {
15657 gfloat natural_width, natural_height;
15658
15659 clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
15660
15661 if (!x_fill)
15662 child_width = CLAMP (natural_width, 0, available_width);
15663
15664 if (!y_fill)
15665 child_height = CLAMP (natural_height, 0, available_height);
15666 }
15667
15668 /* invert the horizontal alignment for RTL languages */
15669 if (priv->text_direction == CLUTTER_TEXT_DIRECTION_RTL)
15670 x_align = 1.0 - x_align;
15671
15672 if (!x_fill)
15673 allocation.x1 += ((available_width - child_width) * x_align);
15674
15675 if (!y_fill)
15676 allocation.y1 += ((available_height - child_height) * y_align);
15677
15678 out:
15679
15680 allocation.x1 = floorf (allocation.x1);
15681 allocation.y1 = floorf (allocation.y1);
15682 allocation.x2 = ceilf (allocation.x1 + MAX (child_width, 0));
15683 allocation.y2 = ceilf (allocation.y1 + MAX (child_height, 0));
15684
15685 clutter_actor_allocate (self, &allocation, flags);
15686 }
15687
15688 /**
15689 * clutter_actor_grab_key_focus:
15690 * @self: a #ClutterActor
15691 *
15692 * Sets the key focus of the #ClutterStage including @self
15693 * to this #ClutterActor.
15694 *
15695 * Since: 1.0
15696 */
15697 void
clutter_actor_grab_key_focus(ClutterActor * self)15698 clutter_actor_grab_key_focus (ClutterActor *self)
15699 {
15700 ClutterActor *stage;
15701
15702 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15703
15704 stage = _clutter_actor_get_stage_internal (self);
15705 if (stage != NULL)
15706 clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self);
15707 }
15708
15709 static void
update_pango_context(ClutterBackend * backend,PangoContext * context)15710 update_pango_context (ClutterBackend *backend,
15711 PangoContext *context)
15712 {
15713 ClutterSettings *settings;
15714 PangoFontDescription *font_desc;
15715 const cairo_font_options_t *font_options;
15716 gchar *font_name;
15717 PangoDirection pango_dir;
15718 gdouble resolution;
15719
15720 settings = clutter_settings_get_default ();
15721
15722 /* update the text direction */
15723 if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
15724 pango_dir = PANGO_DIRECTION_RTL;
15725 else
15726 pango_dir = PANGO_DIRECTION_LTR;
15727
15728 pango_context_set_base_dir (context, pango_dir);
15729
15730 g_object_get (settings, "font-name", &font_name, NULL);
15731
15732 /* get the configuration for the PangoContext from the backend */
15733 font_options = clutter_backend_get_font_options (backend);
15734 resolution = clutter_backend_get_resolution (backend);
15735
15736 font_desc = pango_font_description_from_string (font_name);
15737
15738 if (resolution < 0)
15739 resolution = 96.0; /* fall back */
15740
15741 pango_context_set_font_description (context, font_desc);
15742 pango_cairo_context_set_font_options (context, font_options);
15743 pango_cairo_context_set_resolution (context, resolution);
15744
15745 pango_font_description_free (font_desc);
15746 g_free (font_name);
15747 }
15748
15749 /**
15750 * clutter_actor_get_pango_context:
15751 * @self: a #ClutterActor
15752 *
15753 * Retrieves the #PangoContext for @self. The actor's #PangoContext
15754 * is already configured using the appropriate font map, resolution
15755 * and font options.
15756 *
15757 * Unlike clutter_actor_create_pango_context(), this context is owend
15758 * by the #ClutterActor and it will be updated each time the options
15759 * stored by the #ClutterBackend change.
15760 *
15761 * You can use the returned #PangoContext to create a #PangoLayout
15762 * and render text using cogl_pango_render_layout() to reuse the
15763 * glyphs cache also used by Clutter.
15764 *
15765 * Return value: (transfer none): the #PangoContext for a #ClutterActor.
15766 * The returned #PangoContext is owned by the actor and should not be
15767 * unreferenced by the application code
15768 *
15769 * Since: 1.0
15770 */
15771 PangoContext *
clutter_actor_get_pango_context(ClutterActor * self)15772 clutter_actor_get_pango_context (ClutterActor *self)
15773 {
15774 ClutterActorPrivate *priv;
15775 ClutterBackend *backend = clutter_get_default_backend ();
15776
15777 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15778
15779 priv = self->priv;
15780
15781 if (G_UNLIKELY (priv->pango_context == NULL))
15782 {
15783 priv->pango_context = clutter_actor_create_pango_context (self);
15784
15785 g_signal_connect_object (backend, "resolution-changed",
15786 G_CALLBACK (update_pango_context), priv->pango_context, 0);
15787 g_signal_connect_object (backend, "font-changed",
15788 G_CALLBACK (update_pango_context), priv->pango_context, 0);
15789 }
15790 else
15791 update_pango_context (backend, priv->pango_context);
15792
15793 return priv->pango_context;
15794 }
15795
15796 /**
15797 * clutter_actor_create_pango_context:
15798 * @self: a #ClutterActor
15799 *
15800 * Creates a #PangoContext for the given actor. The #PangoContext
15801 * is already configured using the appropriate font map, resolution
15802 * and font options.
15803 *
15804 * See also clutter_actor_get_pango_context().
15805 *
15806 * Return value: (transfer full): the newly created #PangoContext.
15807 * Use g_object_unref() on the returned value to deallocate its
15808 * resources
15809 *
15810 * Since: 1.0
15811 */
15812 PangoContext *
clutter_actor_create_pango_context(ClutterActor * self)15813 clutter_actor_create_pango_context (ClutterActor *self)
15814 {
15815 CoglPangoFontMap *font_map;
15816 PangoContext *context;
15817
15818 font_map = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
15819
15820 context = cogl_pango_font_map_create_context (font_map);
15821 update_pango_context (clutter_get_default_backend (), context);
15822 pango_context_set_language (context, pango_language_get_default ());
15823
15824 return context;
15825 }
15826
15827 /**
15828 * clutter_actor_create_pango_layout:
15829 * @self: a #ClutterActor
15830 * @text: (allow-none): the text to set on the #PangoLayout, or %NULL
15831 *
15832 * Creates a new #PangoLayout from the same #PangoContext used
15833 * by the #ClutterActor. The #PangoLayout is already configured
15834 * with the font map, resolution and font options, and the
15835 * given @text.
15836 *
15837 * If you want to keep around a #PangoLayout created by this
15838 * function you will have to connect to the #ClutterBackend::font-changed
15839 * and #ClutterBackend::resolution-changed signals, and call
15840 * pango_layout_context_changed() in response to them.
15841 *
15842 * Return value: (transfer full): the newly created #PangoLayout.
15843 * Use g_object_unref() when done
15844 *
15845 * Since: 1.0
15846 */
15847 PangoLayout *
clutter_actor_create_pango_layout(ClutterActor * self,const gchar * text)15848 clutter_actor_create_pango_layout (ClutterActor *self,
15849 const gchar *text)
15850 {
15851 PangoContext *context;
15852 PangoLayout *layout;
15853
15854 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15855
15856 context = clutter_actor_get_pango_context (self);
15857 layout = pango_layout_new (context);
15858
15859 if (text)
15860 pango_layout_set_text (layout, text, -1);
15861
15862 return layout;
15863 }
15864
15865 /**
15866 * clutter_actor_set_opacity_override:
15867 * @self: a #ClutterActor
15868 * @opacity: the override opacity value, or -1 to reset
15869 *
15870 * Allows overriding the calculated paint opacity (as returned by
15871 * clutter_actor_get_paint_opacity()). This is used internally by
15872 * ClutterClone and ClutterOffscreenEffect, and should be used by
15873 * actors that need to mimick those.
15874 *
15875 * In almost all cases this should not used by applications.
15876 *
15877 * Stability: unstable
15878 */
15879 void
clutter_actor_set_opacity_override(ClutterActor * self,gint opacity)15880 clutter_actor_set_opacity_override (ClutterActor *self,
15881 gint opacity)
15882 {
15883 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15884
15885 /* ensure bounds */
15886 if (opacity >= 0)
15887 opacity = CLAMP (opacity, 0, 255);
15888 else
15889 opacity = -1;
15890
15891 self->priv->opacity_override = opacity;
15892 }
15893
15894 /**
15895 * clutter_actor_get_opacity_override:
15896 * @self: a #ClutterActor
15897 *
15898 * See clutter_actor_set_opacity_override()
15899 *
15900 * Returns: the override value for the actor's opacity, or -1 if no override
15901 * is set.
15902 *
15903 * Since: 1.22
15904 *
15905 * Stability: unstable
15906 */
15907 gint
clutter_actor_get_opacity_override(ClutterActor * self)15908 clutter_actor_get_opacity_override (ClutterActor *self)
15909 {
15910 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), -1);
15911
15912 return self->priv->opacity_override;
15913 }
15914
15915 /* Allows you to disable applying the actors model view transform during
15916 * a paint. Used by ClutterClone. */
15917 void
_clutter_actor_set_enable_model_view_transform(ClutterActor * self,gboolean enable)15918 _clutter_actor_set_enable_model_view_transform (ClutterActor *self,
15919 gboolean enable)
15920 {
15921 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15922
15923 self->priv->enable_model_view_transform = enable;
15924 }
15925
15926 void
_clutter_actor_set_enable_paint_unmapped(ClutterActor * self,gboolean enable)15927 _clutter_actor_set_enable_paint_unmapped (ClutterActor *self,
15928 gboolean enable)
15929 {
15930 ClutterActorPrivate *priv;
15931
15932 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15933
15934 priv = self->priv;
15935
15936 priv->enable_paint_unmapped = enable;
15937
15938 if (priv->enable_paint_unmapped)
15939 {
15940 /* Make sure that the parents of the widget are realized first;
15941 * otherwise checks in clutter_actor_update_map_state() will
15942 * fail.
15943 */
15944 clutter_actor_realize (self);
15945
15946 /* If the actor isn't ultimately connected to a toplevel, it can't be
15947 * realized or painted.
15948 */
15949 if (CLUTTER_ACTOR_IS_REALIZED (self))
15950 clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
15951 }
15952 else
15953 {
15954 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
15955 }
15956 }
15957
15958 static void
clutter_anchor_coord_get_units(ClutterActor * self,const AnchorCoord * coord,gfloat * x,gfloat * y,gfloat * z)15959 clutter_anchor_coord_get_units (ClutterActor *self,
15960 const AnchorCoord *coord,
15961 gfloat *x,
15962 gfloat *y,
15963 gfloat *z)
15964 {
15965 if (coord->is_fractional)
15966 {
15967 gfloat actor_width, actor_height;
15968
15969 clutter_actor_get_size (self, &actor_width, &actor_height);
15970
15971 if (x)
15972 *x = actor_width * coord->v.fraction.x;
15973
15974 if (y)
15975 *y = actor_height * coord->v.fraction.y;
15976
15977 if (z)
15978 *z = 0;
15979 }
15980 else
15981 {
15982 if (x)
15983 *x = coord->v.units.x;
15984
15985 if (y)
15986 *y = coord->v.units.y;
15987
15988 if (z)
15989 *z = coord->v.units.z;
15990 }
15991 }
15992
15993 static void
clutter_anchor_coord_set_units(AnchorCoord * coord,gfloat x,gfloat y,gfloat z)15994 clutter_anchor_coord_set_units (AnchorCoord *coord,
15995 gfloat x,
15996 gfloat y,
15997 gfloat z)
15998 {
15999 coord->is_fractional = FALSE;
16000 coord->v.units.x = x;
16001 coord->v.units.y = y;
16002 coord->v.units.z = z;
16003 }
16004
16005 static ClutterGravity
clutter_anchor_coord_get_gravity(const AnchorCoord * coord)16006 clutter_anchor_coord_get_gravity (const AnchorCoord *coord)
16007 {
16008 if (coord->is_fractional)
16009 {
16010 if (coord->v.fraction.x == 0.0)
16011 {
16012 if (coord->v.fraction.y == 0.0)
16013 return CLUTTER_GRAVITY_NORTH_WEST;
16014 else if (coord->v.fraction.y == 0.5)
16015 return CLUTTER_GRAVITY_WEST;
16016 else if (coord->v.fraction.y == 1.0)
16017 return CLUTTER_GRAVITY_SOUTH_WEST;
16018 else
16019 return CLUTTER_GRAVITY_NONE;
16020 }
16021 else if (coord->v.fraction.x == 0.5)
16022 {
16023 if (coord->v.fraction.y == 0.0)
16024 return CLUTTER_GRAVITY_NORTH;
16025 else if (coord->v.fraction.y == 0.5)
16026 return CLUTTER_GRAVITY_CENTER;
16027 else if (coord->v.fraction.y == 1.0)
16028 return CLUTTER_GRAVITY_SOUTH;
16029 else
16030 return CLUTTER_GRAVITY_NONE;
16031 }
16032 else if (coord->v.fraction.x == 1.0)
16033 {
16034 if (coord->v.fraction.y == 0.0)
16035 return CLUTTER_GRAVITY_NORTH_EAST;
16036 else if (coord->v.fraction.y == 0.5)
16037 return CLUTTER_GRAVITY_EAST;
16038 else if (coord->v.fraction.y == 1.0)
16039 return CLUTTER_GRAVITY_SOUTH_EAST;
16040 else
16041 return CLUTTER_GRAVITY_NONE;
16042 }
16043 else
16044 return CLUTTER_GRAVITY_NONE;
16045 }
16046 else
16047 return CLUTTER_GRAVITY_NONE;
16048 }
16049
16050 static void
clutter_anchor_coord_set_gravity(AnchorCoord * coord,ClutterGravity gravity)16051 clutter_anchor_coord_set_gravity (AnchorCoord *coord,
16052 ClutterGravity gravity)
16053 {
16054 switch (gravity)
16055 {
16056 case CLUTTER_GRAVITY_NORTH:
16057 coord->v.fraction.x = 0.5;
16058 coord->v.fraction.y = 0.0;
16059 break;
16060
16061 case CLUTTER_GRAVITY_NORTH_EAST:
16062 coord->v.fraction.x = 1.0;
16063 coord->v.fraction.y = 0.0;
16064 break;
16065
16066 case CLUTTER_GRAVITY_EAST:
16067 coord->v.fraction.x = 1.0;
16068 coord->v.fraction.y = 0.5;
16069 break;
16070
16071 case CLUTTER_GRAVITY_SOUTH_EAST:
16072 coord->v.fraction.x = 1.0;
16073 coord->v.fraction.y = 1.0;
16074 break;
16075
16076 case CLUTTER_GRAVITY_SOUTH:
16077 coord->v.fraction.x = 0.5;
16078 coord->v.fraction.y = 1.0;
16079 break;
16080
16081 case CLUTTER_GRAVITY_SOUTH_WEST:
16082 coord->v.fraction.x = 0.0;
16083 coord->v.fraction.y = 1.0;
16084 break;
16085
16086 case CLUTTER_GRAVITY_WEST:
16087 coord->v.fraction.x = 0.0;
16088 coord->v.fraction.y = 0.5;
16089 break;
16090
16091 case CLUTTER_GRAVITY_NORTH_WEST:
16092 coord->v.fraction.x = 0.0;
16093 coord->v.fraction.y = 0.0;
16094 break;
16095
16096 case CLUTTER_GRAVITY_CENTER:
16097 coord->v.fraction.x = 0.5;
16098 coord->v.fraction.y = 0.5;
16099 break;
16100
16101 default:
16102 coord->v.fraction.x = 0.0;
16103 coord->v.fraction.y = 0.0;
16104 break;
16105 }
16106
16107 coord->is_fractional = TRUE;
16108 }
16109
16110 static gboolean
clutter_anchor_coord_is_zero(const AnchorCoord * coord)16111 clutter_anchor_coord_is_zero (const AnchorCoord *coord)
16112 {
16113 if (coord->is_fractional)
16114 return coord->v.fraction.x == 0.0 && coord->v.fraction.y == 0.0;
16115 else
16116 return (coord->v.units.x == 0.0
16117 && coord->v.units.y == 0.0
16118 && coord->v.units.z == 0.0);
16119 }
16120
16121 /**
16122 * clutter_actor_get_flags:
16123 * @self: a #ClutterActor
16124 *
16125 * Retrieves the flags set on @self
16126 *
16127 * Return value: a bitwise or of #ClutterActorFlags or 0
16128 *
16129 * Since: 1.0
16130 */
16131 ClutterActorFlags
clutter_actor_get_flags(ClutterActor * self)16132 clutter_actor_get_flags (ClutterActor *self)
16133 {
16134 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
16135
16136 return self->flags;
16137 }
16138
16139 /**
16140 * clutter_actor_set_flags:
16141 * @self: a #ClutterActor
16142 * @flags: the flags to set
16143 *
16144 * Sets @flags on @self
16145 *
16146 * This function will emit notifications for the changed properties
16147 *
16148 * Since: 1.0
16149 */
16150 void
clutter_actor_set_flags(ClutterActor * self,ClutterActorFlags flags)16151 clutter_actor_set_flags (ClutterActor *self,
16152 ClutterActorFlags flags)
16153 {
16154 ClutterActorFlags old_flags;
16155 GObject *obj;
16156 gboolean was_reactive_set, reactive_set;
16157 gboolean was_realized_set, realized_set;
16158 gboolean was_mapped_set, mapped_set;
16159 gboolean was_visible_set, visible_set;
16160
16161 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16162
16163 if (self->flags == flags)
16164 return;
16165
16166 obj = G_OBJECT (self);
16167 g_object_ref (obj);
16168 g_object_freeze_notify (obj);
16169
16170 old_flags = self->flags;
16171
16172 was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
16173 was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
16174 was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0);
16175 was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0);
16176
16177 self->flags |= flags;
16178
16179 reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
16180 realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
16181 mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0);
16182 visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0);
16183
16184 if (reactive_set != was_reactive_set)
16185 g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
16186
16187 if (realized_set != was_realized_set)
16188 g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
16189
16190 if (mapped_set != was_mapped_set)
16191 g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
16192
16193 if (visible_set != was_visible_set)
16194 g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
16195
16196 g_object_thaw_notify (obj);
16197 g_object_unref (obj);
16198 }
16199
16200 /**
16201 * clutter_actor_unset_flags:
16202 * @self: a #ClutterActor
16203 * @flags: the flags to unset
16204 *
16205 * Unsets @flags on @self
16206 *
16207 * This function will emit notifications for the changed properties
16208 *
16209 * Since: 1.0
16210 */
16211 void
clutter_actor_unset_flags(ClutterActor * self,ClutterActorFlags flags)16212 clutter_actor_unset_flags (ClutterActor *self,
16213 ClutterActorFlags flags)
16214 {
16215 ClutterActorFlags old_flags;
16216 GObject *obj;
16217 gboolean was_reactive_set, reactive_set;
16218 gboolean was_realized_set, realized_set;
16219 gboolean was_mapped_set, mapped_set;
16220 gboolean was_visible_set, visible_set;
16221
16222 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16223
16224 obj = G_OBJECT (self);
16225 g_object_freeze_notify (obj);
16226
16227 old_flags = self->flags;
16228
16229 was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
16230 was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
16231 was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0);
16232 was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0);
16233
16234 self->flags &= ~flags;
16235
16236 if (self->flags == old_flags)
16237 return;
16238
16239 reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
16240 realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
16241 mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0);
16242 visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0);
16243
16244 if (reactive_set != was_reactive_set)
16245 g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
16246
16247 if (realized_set != was_realized_set)
16248 g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
16249
16250 if (mapped_set != was_mapped_set)
16251 g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
16252
16253 if (visible_set != was_visible_set)
16254 g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
16255
16256 g_object_thaw_notify (obj);
16257 }
16258
16259 /**
16260 * clutter_actor_get_transformation_matrix:
16261 * @self: a #ClutterActor
16262 * @matrix: (out caller-allocates): the return location for a #ClutterMatrix
16263 *
16264 * Retrieves the transformations applied to @self relative to its
16265 * parent.
16266 *
16267 * Since: 1.0
16268 *
16269 * Deprecated: 1.12: Use clutter_actor_get_transform() instead
16270 */
16271 void
clutter_actor_get_transformation_matrix(ClutterActor * self,ClutterMatrix * matrix)16272 clutter_actor_get_transformation_matrix (ClutterActor *self,
16273 ClutterMatrix *matrix)
16274 {
16275 clutter_actor_get_transform (self, matrix);
16276 }
16277
16278 static void
clutter_actor_set_transform_internal(ClutterActor * self,const ClutterMatrix * transform)16279 clutter_actor_set_transform_internal (ClutterActor *self,
16280 const ClutterMatrix *transform)
16281 {
16282 ClutterTransformInfo *info;
16283 gboolean was_set;
16284 GObject *obj;
16285
16286 obj = G_OBJECT (self);
16287
16288 info = _clutter_actor_get_transform_info (self);
16289
16290 was_set = info->transform_set;
16291
16292 info->transform = *transform;
16293 info->transform_set = !cogl_matrix_is_identity (&info->transform);
16294
16295 self->priv->transform_valid = FALSE;
16296
16297 clutter_actor_queue_redraw (self);
16298
16299 g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM]);
16300
16301 if (was_set != info->transform_set)
16302 g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM_SET]);
16303 }
16304
16305 /**
16306 * clutter_actor_set_transform:
16307 * @self: a #ClutterActor
16308 * @transform: (allow-none): a #ClutterMatrix, or %NULL to
16309 * unset a custom transformation
16310 *
16311 * Overrides the transformations of a #ClutterActor with a custom
16312 * matrix, which will be applied relative to the origin of the
16313 * actor's allocation and to the actor's pivot point.
16314 *
16315 * The #ClutterActor:transform property is animatable.
16316 *
16317 * Since: 1.12
16318 */
16319 void
clutter_actor_set_transform(ClutterActor * self,const ClutterMatrix * transform)16320 clutter_actor_set_transform (ClutterActor *self,
16321 const ClutterMatrix *transform)
16322 {
16323 const ClutterTransformInfo *info;
16324 ClutterMatrix new_transform;
16325
16326 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16327
16328 info = _clutter_actor_get_transform_info_or_defaults (self);
16329
16330 if (transform != NULL)
16331 clutter_matrix_init_from_matrix (&new_transform, transform);
16332 else
16333 clutter_matrix_init_identity (&new_transform);
16334
16335 _clutter_actor_create_transition (self, obj_props[PROP_TRANSFORM],
16336 &info->transform,
16337 &new_transform);
16338 }
16339
16340 /**
16341 * clutter_actor_get_transform:
16342 * @self: a #ClutterActor
16343 * @transform: (out caller-allocates): a #ClutterMatrix
16344 *
16345 * Retrieves the current transformation matrix of a #ClutterActor.
16346 *
16347 * Since: 1.12
16348 */
16349 void
clutter_actor_get_transform(ClutterActor * self,ClutterMatrix * transform)16350 clutter_actor_get_transform (ClutterActor *self,
16351 ClutterMatrix *transform)
16352 {
16353 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16354 g_return_if_fail (transform != NULL);
16355
16356 cogl_matrix_init_identity (transform);
16357 _clutter_actor_apply_modelview_transform (self, transform);
16358 }
16359
16360 void
_clutter_actor_set_in_clone_paint(ClutterActor * self,gboolean is_in_clone_paint)16361 _clutter_actor_set_in_clone_paint (ClutterActor *self,
16362 gboolean is_in_clone_paint)
16363 {
16364 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16365 self->priv->in_clone_paint = is_in_clone_paint;
16366 }
16367
16368 /**
16369 * clutter_actor_is_in_clone_paint:
16370 * @self: a #ClutterActor
16371 *
16372 * Checks whether @self is being currently painted by a #ClutterClone
16373 *
16374 * This function is useful only inside the ::paint virtual function
16375 * implementations or within handlers for the #ClutterActor::paint
16376 * signal
16377 *
16378 * This function should not be used by applications
16379 *
16380 * Return value: %TRUE if the #ClutterActor is currently being painted
16381 * by a #ClutterClone, and %FALSE otherwise
16382 *
16383 * Since: 1.0
16384 */
16385 gboolean
clutter_actor_is_in_clone_paint(ClutterActor * self)16386 clutter_actor_is_in_clone_paint (ClutterActor *self)
16387 {
16388 ClutterActor *parent;
16389
16390 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16391
16392 if (self->priv->in_clone_paint)
16393 return TRUE;
16394
16395 if (self->priv->in_cloned_branch == 0)
16396 return FALSE;
16397
16398 parent = self->priv->parent;
16399 while (parent != NULL)
16400 {
16401 if (parent->priv->in_cloned_branch == 0)
16402 break;
16403
16404 if (parent->priv->in_clone_paint)
16405 return TRUE;
16406
16407 parent = parent->priv->parent;
16408 }
16409
16410 return FALSE;
16411 }
16412
16413 static gboolean
set_direction_recursive(ClutterActor * actor,gpointer user_data)16414 set_direction_recursive (ClutterActor *actor,
16415 gpointer user_data)
16416 {
16417 ClutterTextDirection text_dir = GPOINTER_TO_INT (user_data);
16418
16419 clutter_actor_set_text_direction (actor, text_dir);
16420
16421 return TRUE;
16422 }
16423
16424 /**
16425 * clutter_actor_set_text_direction:
16426 * @self: a #ClutterActor
16427 * @text_dir: the text direction for @self
16428 *
16429 * Sets the #ClutterTextDirection for an actor
16430 *
16431 * The passed text direction must not be %CLUTTER_TEXT_DIRECTION_DEFAULT
16432 *
16433 * If @self implements #ClutterContainer then this function will recurse
16434 * inside all the children of @self (including the internal ones).
16435 *
16436 * Composite actors not implementing #ClutterContainer, or actors requiring
16437 * special handling when the text direction changes, should connect to
16438 * the #GObject::notify signal for the #ClutterActor:text-direction property
16439 *
16440 * Since: 1.2
16441 */
16442 void
clutter_actor_set_text_direction(ClutterActor * self,ClutterTextDirection text_dir)16443 clutter_actor_set_text_direction (ClutterActor *self,
16444 ClutterTextDirection text_dir)
16445 {
16446 ClutterActorPrivate *priv;
16447
16448 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16449 g_return_if_fail (text_dir != CLUTTER_TEXT_DIRECTION_DEFAULT);
16450
16451 priv = self->priv;
16452
16453 if (priv->text_direction != text_dir)
16454 {
16455 priv->text_direction = text_dir;
16456
16457 /* we need to emit the notify::text-direction first, so that
16458 * the sub-classes can catch that and do specific handling of
16459 * the text direction; see clutter_text_direction_changed_cb()
16460 * inside clutter-text.c
16461 */
16462 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT_DIRECTION]);
16463
16464 _clutter_actor_foreach_child (self, set_direction_recursive,
16465 GINT_TO_POINTER (text_dir));
16466
16467 clutter_actor_queue_relayout (self);
16468 }
16469 }
16470
16471 void
_clutter_actor_set_has_pointer(ClutterActor * self,gboolean has_pointer)16472 _clutter_actor_set_has_pointer (ClutterActor *self,
16473 gboolean has_pointer)
16474 {
16475 ClutterActorPrivate *priv = self->priv;
16476
16477 if (priv->has_pointer != has_pointer)
16478 {
16479 priv->has_pointer = has_pointer;
16480
16481 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_POINTER]);
16482 }
16483 }
16484
16485 /**
16486 * clutter_actor_get_text_direction:
16487 * @self: a #ClutterActor
16488 *
16489 * Retrieves the value set using clutter_actor_set_text_direction()
16490 *
16491 * If no text direction has been previously set, the default text
16492 * direction, as returned by clutter_get_default_text_direction(), will
16493 * be returned instead
16494 *
16495 * Return value: the #ClutterTextDirection for the actor
16496 *
16497 * Since: 1.2
16498 */
16499 ClutterTextDirection
clutter_actor_get_text_direction(ClutterActor * self)16500 clutter_actor_get_text_direction (ClutterActor *self)
16501 {
16502 ClutterActorPrivate *priv;
16503
16504 g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
16505 CLUTTER_TEXT_DIRECTION_LTR);
16506
16507 priv = self->priv;
16508
16509 /* if no direction has been set yet use the default */
16510 if (priv->text_direction == CLUTTER_TEXT_DIRECTION_DEFAULT)
16511 priv->text_direction = clutter_get_default_text_direction ();
16512
16513 return priv->text_direction;
16514 }
16515
16516 /**
16517 * clutter_actor_push_internal:
16518 * @self: a #ClutterActor
16519 *
16520 * Should be used by actors implementing the #ClutterContainer and with
16521 * internal children added through clutter_actor_set_parent(), for instance:
16522 *
16523 * |[<!-- language="C" -->
16524 * static void
16525 * my_actor_init (MyActor *self)
16526 * {
16527 * self->priv = my_actor_get_instance_private (self);
16528 *
16529 * clutter_actor_push_internal (CLUTTER_ACTOR (self));
16530 *
16531 * // calling clutter_actor_set_parent() now will result in
16532 * // the internal flag being set on a child of MyActor
16533 *
16534 * // internal child - a background texture
16535 * self->priv->background_tex = clutter_texture_new ();
16536 * clutter_actor_set_parent (self->priv->background_tex,
16537 * CLUTTER_ACTOR (self));
16538 *
16539 * // internal child - a label
16540 * self->priv->label = clutter_text_new ();
16541 * clutter_actor_set_parent (self->priv->label,
16542 * CLUTTER_ACTOR (self));
16543 *
16544 * clutter_actor_pop_internal (CLUTTER_ACTOR (self));
16545 *
16546 * // calling clutter_actor_set_parent() now will not result in
16547 * // the internal flag being set on a child of MyActor
16548 * }
16549 * ]|
16550 *
16551 * This function will be used by Clutter to toggle an "internal child"
16552 * flag whenever clutter_actor_set_parent() is called; internal children
16553 * are handled differently by Clutter, specifically when destroying their
16554 * parent.
16555 *
16556 * Call clutter_actor_pop_internal() when you finished adding internal
16557 * children.
16558 *
16559 * Nested calls to clutter_actor_push_internal() are allowed, but each
16560 * one must by followed by a clutter_actor_pop_internal() call.
16561 *
16562 * Since: 1.2
16563 *
16564 * Deprecated: 1.10: All children of an actor are accessible through
16565 * the #ClutterActor API, and #ClutterActor implements the
16566 * #ClutterContainer interface, so this function is only useful
16567 * for legacy containers overriding the default implementation.
16568 */
16569 void
clutter_actor_push_internal(ClutterActor * self)16570 clutter_actor_push_internal (ClutterActor *self)
16571 {
16572 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16573
16574 self->priv->internal_child += 1;
16575 }
16576
16577 /**
16578 * clutter_actor_pop_internal:
16579 * @self: a #ClutterActor
16580 *
16581 * Disables the effects of clutter_actor_push_internal().
16582 *
16583 * Since: 1.2
16584 *
16585 * Deprecated: 1.10: All children of an actor are accessible through
16586 * the #ClutterActor API. This function is only useful for legacy
16587 * containers overriding the default implementation of the
16588 * #ClutterContainer interface.
16589 */
16590 void
clutter_actor_pop_internal(ClutterActor * self)16591 clutter_actor_pop_internal (ClutterActor *self)
16592 {
16593 ClutterActorPrivate *priv;
16594
16595 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16596
16597 priv = self->priv;
16598
16599 if (priv->internal_child == 0)
16600 {
16601 g_warning ("Mismatched %s: you need to call "
16602 "clutter_actor_push_composite() at least once before "
16603 "calling this function", G_STRFUNC);
16604 return;
16605 }
16606
16607 priv->internal_child -= 1;
16608 }
16609
16610 /**
16611 * clutter_actor_has_pointer:
16612 * @self: a #ClutterActor
16613 *
16614 * Checks whether an actor contains the pointer of a
16615 * #ClutterInputDevice
16616 *
16617 * Return value: %TRUE if the actor contains the pointer, and
16618 * %FALSE otherwise
16619 *
16620 * Since: 1.2
16621 */
16622 gboolean
clutter_actor_has_pointer(ClutterActor * self)16623 clutter_actor_has_pointer (ClutterActor *self)
16624 {
16625 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16626
16627 return self->priv->has_pointer;
16628 }
16629
16630 /* XXX: This is a workaround for not being able to break the ABI of
16631 * the QUEUE_REDRAW signal. It is an out-of-band argument. See
16632 * clutter_actor_queue_clipped_redraw() for details.
16633 */
16634 ClutterPaintVolume *
_clutter_actor_get_queue_redraw_clip(ClutterActor * self)16635 _clutter_actor_get_queue_redraw_clip (ClutterActor *self)
16636 {
16637 return g_object_get_data (G_OBJECT (self),
16638 "-clutter-actor-queue-redraw-clip");
16639 }
16640
16641 void
_clutter_actor_set_queue_redraw_clip(ClutterActor * self,ClutterPaintVolume * clip)16642 _clutter_actor_set_queue_redraw_clip (ClutterActor *self,
16643 ClutterPaintVolume *clip)
16644 {
16645 g_object_set_data (G_OBJECT (self),
16646 "-clutter-actor-queue-redraw-clip",
16647 clip);
16648 }
16649
16650 /**
16651 * clutter_actor_has_allocation:
16652 * @self: a #ClutterActor
16653 *
16654 * Checks if the actor has an up-to-date allocation assigned to
16655 * it. This means that the actor should have an allocation: it's
16656 * visible and has a parent. It also means that there is no
16657 * outstanding relayout request in progress for the actor or its
16658 * children (There might be other outstanding layout requests in
16659 * progress that will cause the actor to get a new allocation
16660 * when the stage is laid out, however).
16661 *
16662 * If this function returns %FALSE, then the actor will normally
16663 * be allocated before it is next drawn on the screen.
16664 *
16665 * Return value: %TRUE if the actor has an up-to-date allocation
16666 *
16667 * Since: 1.4
16668 */
16669 gboolean
clutter_actor_has_allocation(ClutterActor * self)16670 clutter_actor_has_allocation (ClutterActor *self)
16671 {
16672 ClutterActorPrivate *priv;
16673
16674 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16675
16676 priv = self->priv;
16677
16678 return priv->parent != NULL &&
16679 CLUTTER_ACTOR_IS_VISIBLE (self) &&
16680 !priv->needs_allocation;
16681 }
16682
16683 /**
16684 * clutter_actor_add_action:
16685 * @self: a #ClutterActor
16686 * @action: a #ClutterAction
16687 *
16688 * Adds @action to the list of actions applied to @self
16689 *
16690 * A #ClutterAction can only belong to one actor at a time
16691 *
16692 * The #ClutterActor will hold a reference on @action until either
16693 * clutter_actor_remove_action() or clutter_actor_clear_actions()
16694 * is called
16695 *
16696 * Since: 1.4
16697 */
16698 void
clutter_actor_add_action(ClutterActor * self,ClutterAction * action)16699 clutter_actor_add_action (ClutterActor *self,
16700 ClutterAction *action)
16701 {
16702 ClutterActorPrivate *priv;
16703
16704 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16705 g_return_if_fail (CLUTTER_IS_ACTION (action));
16706
16707 priv = self->priv;
16708
16709 if (priv->actions == NULL)
16710 {
16711 priv->actions = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
16712 priv->actions->actor = self;
16713 }
16714
16715 _clutter_meta_group_add_meta (priv->actions, CLUTTER_ACTOR_META (action));
16716
16717 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
16718 }
16719
16720 /**
16721 * clutter_actor_add_action_with_name:
16722 * @self: a #ClutterActor
16723 * @name: the name to set on the action
16724 * @action: a #ClutterAction
16725 *
16726 * A convenience function for setting the name of a #ClutterAction
16727 * while adding it to the list of actions applied to @self
16728 *
16729 * This function is the logical equivalent of:
16730 *
16731 * |[<!-- language="C" -->
16732 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
16733 * clutter_actor_add_action (self, action);
16734 * ]|
16735 *
16736 * Since: 1.4
16737 */
16738 void
clutter_actor_add_action_with_name(ClutterActor * self,const gchar * name,ClutterAction * action)16739 clutter_actor_add_action_with_name (ClutterActor *self,
16740 const gchar *name,
16741 ClutterAction *action)
16742 {
16743 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16744 g_return_if_fail (name != NULL);
16745 g_return_if_fail (CLUTTER_IS_ACTION (action));
16746
16747 clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
16748 clutter_actor_add_action (self, action);
16749 }
16750
16751 /**
16752 * clutter_actor_remove_action:
16753 * @self: a #ClutterActor
16754 * @action: a #ClutterAction
16755 *
16756 * Removes @action from the list of actions applied to @self
16757 *
16758 * The reference held by @self on the #ClutterAction will be released
16759 *
16760 * Since: 1.4
16761 */
16762 void
clutter_actor_remove_action(ClutterActor * self,ClutterAction * action)16763 clutter_actor_remove_action (ClutterActor *self,
16764 ClutterAction *action)
16765 {
16766 ClutterActorPrivate *priv;
16767
16768 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16769 g_return_if_fail (CLUTTER_IS_ACTION (action));
16770
16771 priv = self->priv;
16772
16773 if (priv->actions == NULL)
16774 return;
16775
16776 _clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action));
16777
16778 if (_clutter_meta_group_peek_metas (priv->actions) == NULL)
16779 g_clear_object (&priv->actions);
16780
16781 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
16782 }
16783
16784 /**
16785 * clutter_actor_remove_action_by_name:
16786 * @self: a #ClutterActor
16787 * @name: the name of the action to remove
16788 *
16789 * Removes the #ClutterAction with the given name from the list
16790 * of actions applied to @self
16791 *
16792 * Since: 1.4
16793 */
16794 void
clutter_actor_remove_action_by_name(ClutterActor * self,const gchar * name)16795 clutter_actor_remove_action_by_name (ClutterActor *self,
16796 const gchar *name)
16797 {
16798 ClutterActorPrivate *priv;
16799 ClutterActorMeta *meta;
16800
16801 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16802 g_return_if_fail (name != NULL);
16803
16804 priv = self->priv;
16805
16806 if (priv->actions == NULL)
16807 return;
16808
16809 meta = _clutter_meta_group_get_meta (priv->actions, name);
16810 if (meta == NULL)
16811 return;
16812
16813 _clutter_meta_group_remove_meta (priv->actions, meta);
16814
16815 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
16816 }
16817
16818 /**
16819 * clutter_actor_get_actions:
16820 * @self: a #ClutterActor
16821 *
16822 * Retrieves the list of actions applied to @self
16823 *
16824 * Return value: (transfer container) (element-type Clutter.Action): a copy
16825 * of the list of #ClutterAction<!-- -->s. The contents of the list are
16826 * owned by the #ClutterActor. Use g_list_free() to free the resources
16827 * allocated by the returned #GList
16828 *
16829 * Since: 1.4
16830 */
16831 GList *
clutter_actor_get_actions(ClutterActor * self)16832 clutter_actor_get_actions (ClutterActor *self)
16833 {
16834 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16835
16836 if (self->priv->actions == NULL)
16837 return NULL;
16838
16839 return _clutter_meta_group_get_metas_no_internal (self->priv->actions);
16840 }
16841
16842 /**
16843 * clutter_actor_get_action:
16844 * @self: a #ClutterActor
16845 * @name: the name of the action to retrieve
16846 *
16847 * Retrieves the #ClutterAction with the given name in the list
16848 * of actions applied to @self
16849 *
16850 * Return value: (transfer none): a #ClutterAction for the given
16851 * name, or %NULL. The returned #ClutterAction is owned by the
16852 * actor and it should not be unreferenced directly
16853 *
16854 * Since: 1.4
16855 */
16856 ClutterAction *
clutter_actor_get_action(ClutterActor * self,const gchar * name)16857 clutter_actor_get_action (ClutterActor *self,
16858 const gchar *name)
16859 {
16860 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16861 g_return_val_if_fail (name != NULL, NULL);
16862
16863 if (self->priv->actions == NULL)
16864 return NULL;
16865
16866 return CLUTTER_ACTION (_clutter_meta_group_get_meta (self->priv->actions, name));
16867 }
16868
16869 /**
16870 * clutter_actor_clear_actions:
16871 * @self: a #ClutterActor
16872 *
16873 * Clears the list of actions applied to @self
16874 *
16875 * Since: 1.4
16876 */
16877 void
clutter_actor_clear_actions(ClutterActor * self)16878 clutter_actor_clear_actions (ClutterActor *self)
16879 {
16880 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16881
16882 if (self->priv->actions == NULL)
16883 return;
16884
16885 _clutter_meta_group_clear_metas_no_internal (self->priv->actions);
16886 }
16887
16888 /**
16889 * clutter_actor_add_constraint:
16890 * @self: a #ClutterActor
16891 * @constraint: a #ClutterConstraint
16892 *
16893 * Adds @constraint to the list of #ClutterConstraint<!-- -->s applied
16894 * to @self
16895 *
16896 * The #ClutterActor will hold a reference on the @constraint until
16897 * either clutter_actor_remove_constraint() or
16898 * clutter_actor_clear_constraints() is called.
16899 *
16900 * Since: 1.4
16901 */
16902 void
clutter_actor_add_constraint(ClutterActor * self,ClutterConstraint * constraint)16903 clutter_actor_add_constraint (ClutterActor *self,
16904 ClutterConstraint *constraint)
16905 {
16906 ClutterActorPrivate *priv;
16907
16908 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16909 g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
16910
16911 priv = self->priv;
16912
16913 if (priv->constraints == NULL)
16914 {
16915 priv->constraints = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
16916 priv->constraints->actor = self;
16917 }
16918
16919 _clutter_meta_group_add_meta (priv->constraints,
16920 CLUTTER_ACTOR_META (constraint));
16921 clutter_actor_queue_relayout (self);
16922
16923 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
16924 }
16925
16926 /**
16927 * clutter_actor_add_constraint_with_name:
16928 * @self: a #ClutterActor
16929 * @name: the name to set on the constraint
16930 * @constraint: a #ClutterConstraint
16931 *
16932 * A convenience function for setting the name of a #ClutterConstraint
16933 * while adding it to the list of constraints applied to @self
16934 *
16935 * This function is the logical equivalent of:
16936 *
16937 * |[<!-- language="C" -->
16938 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
16939 * clutter_actor_add_constraint (self, constraint);
16940 * ]|
16941 *
16942 * Since: 1.4
16943 */
16944 void
clutter_actor_add_constraint_with_name(ClutterActor * self,const gchar * name,ClutterConstraint * constraint)16945 clutter_actor_add_constraint_with_name (ClutterActor *self,
16946 const gchar *name,
16947 ClutterConstraint *constraint)
16948 {
16949 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16950 g_return_if_fail (name != NULL);
16951 g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
16952
16953 clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
16954 clutter_actor_add_constraint (self, constraint);
16955 }
16956
16957 /**
16958 * clutter_actor_remove_constraint:
16959 * @self: a #ClutterActor
16960 * @constraint: a #ClutterConstraint
16961 *
16962 * Removes @constraint from the list of constraints applied to @self
16963 *
16964 * The reference held by @self on the #ClutterConstraint will be released
16965 *
16966 * Since: 1.4
16967 */
16968 void
clutter_actor_remove_constraint(ClutterActor * self,ClutterConstraint * constraint)16969 clutter_actor_remove_constraint (ClutterActor *self,
16970 ClutterConstraint *constraint)
16971 {
16972 ClutterActorPrivate *priv;
16973
16974 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16975 g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
16976
16977 priv = self->priv;
16978
16979 if (priv->constraints == NULL)
16980 return;
16981
16982 _clutter_meta_group_remove_meta (priv->constraints,
16983 CLUTTER_ACTOR_META (constraint));
16984
16985 if (_clutter_meta_group_peek_metas (priv->constraints) == NULL)
16986 g_clear_object (&priv->constraints);
16987
16988 clutter_actor_queue_relayout (self);
16989
16990 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
16991 }
16992
16993 /**
16994 * clutter_actor_remove_constraint_by_name:
16995 * @self: a #ClutterActor
16996 * @name: the name of the constraint to remove
16997 *
16998 * Removes the #ClutterConstraint with the given name from the list
16999 * of constraints applied to @self
17000 *
17001 * Since: 1.4
17002 */
17003 void
clutter_actor_remove_constraint_by_name(ClutterActor * self,const gchar * name)17004 clutter_actor_remove_constraint_by_name (ClutterActor *self,
17005 const gchar *name)
17006 {
17007 ClutterActorPrivate *priv;
17008 ClutterActorMeta *meta;
17009
17010 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17011 g_return_if_fail (name != NULL);
17012
17013 priv = self->priv;
17014
17015 if (priv->constraints == NULL)
17016 return;
17017
17018 meta = _clutter_meta_group_get_meta (priv->constraints, name);
17019 if (meta == NULL)
17020 return;
17021
17022 _clutter_meta_group_remove_meta (priv->constraints, meta);
17023 clutter_actor_queue_relayout (self);
17024 }
17025
17026 /**
17027 * clutter_actor_get_constraints:
17028 * @self: a #ClutterActor
17029 *
17030 * Retrieves the list of constraints applied to @self
17031 *
17032 * Return value: (transfer container) (element-type Clutter.Constraint): a copy
17033 * of the list of #ClutterConstraint<!-- -->s. The contents of the list are
17034 * owned by the #ClutterActor. Use g_list_free() to free the resources
17035 * allocated by the returned #GList
17036 *
17037 * Since: 1.4
17038 */
17039 GList *
clutter_actor_get_constraints(ClutterActor * self)17040 clutter_actor_get_constraints (ClutterActor *self)
17041 {
17042 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17043
17044 if (self->priv->constraints == NULL)
17045 return NULL;
17046
17047 return _clutter_meta_group_get_metas_no_internal (self->priv->constraints);
17048 }
17049
17050 /**
17051 * clutter_actor_get_constraint:
17052 * @self: a #ClutterActor
17053 * @name: the name of the constraint to retrieve
17054 *
17055 * Retrieves the #ClutterConstraint with the given name in the list
17056 * of constraints applied to @self
17057 *
17058 * Return value: (transfer none): a #ClutterConstraint for the given
17059 * name, or %NULL. The returned #ClutterConstraint is owned by the
17060 * actor and it should not be unreferenced directly
17061 *
17062 * Since: 1.4
17063 */
17064 ClutterConstraint *
clutter_actor_get_constraint(ClutterActor * self,const gchar * name)17065 clutter_actor_get_constraint (ClutterActor *self,
17066 const gchar *name)
17067 {
17068 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17069 g_return_val_if_fail (name != NULL, NULL);
17070
17071 if (self->priv->constraints == NULL)
17072 return NULL;
17073
17074 return CLUTTER_CONSTRAINT (_clutter_meta_group_get_meta (self->priv->constraints, name));
17075 }
17076
17077 /**
17078 * clutter_actor_clear_constraints:
17079 * @self: a #ClutterActor
17080 *
17081 * Clears the list of constraints applied to @self
17082 *
17083 * Since: 1.4
17084 */
17085 void
clutter_actor_clear_constraints(ClutterActor * self)17086 clutter_actor_clear_constraints (ClutterActor *self)
17087 {
17088 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17089
17090 if (self->priv->constraints == NULL)
17091 return;
17092
17093 _clutter_meta_group_clear_metas_no_internal (self->priv->constraints);
17094
17095 clutter_actor_queue_relayout (self);
17096 }
17097
17098 /**
17099 * clutter_actor_set_clip_to_allocation:
17100 * @self: a #ClutterActor
17101 * @clip_set: %TRUE to apply a clip tracking the allocation
17102 *
17103 * Sets whether @self should be clipped to the same size as its
17104 * allocation
17105 *
17106 * Since: 1.4
17107 */
17108 void
clutter_actor_set_clip_to_allocation(ClutterActor * self,gboolean clip_set)17109 clutter_actor_set_clip_to_allocation (ClutterActor *self,
17110 gboolean clip_set)
17111 {
17112 ClutterActorPrivate *priv;
17113
17114 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17115
17116 clip_set = !!clip_set;
17117
17118 priv = self->priv;
17119
17120 if (priv->clip_to_allocation != clip_set)
17121 {
17122 priv->clip_to_allocation = clip_set;
17123
17124 clutter_actor_queue_redraw (self);
17125
17126 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]);
17127 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
17128 }
17129 }
17130
17131 /**
17132 * clutter_actor_get_clip_to_allocation:
17133 * @self: a #ClutterActor
17134 *
17135 * Retrieves the value set using clutter_actor_set_clip_to_allocation()
17136 *
17137 * Return value: %TRUE if the #ClutterActor is clipped to its allocation
17138 *
17139 * Since: 1.4
17140 */
17141 gboolean
clutter_actor_get_clip_to_allocation(ClutterActor * self)17142 clutter_actor_get_clip_to_allocation (ClutterActor *self)
17143 {
17144 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17145
17146 return self->priv->clip_to_allocation;
17147 }
17148
17149 /**
17150 * clutter_actor_add_effect:
17151 * @self: a #ClutterActor
17152 * @effect: a #ClutterEffect
17153 *
17154 * Adds @effect to the list of #ClutterEffect<!-- -->s applied to @self
17155 *
17156 * The #ClutterActor will hold a reference on the @effect until either
17157 * clutter_actor_remove_effect() or clutter_actor_clear_effects() is
17158 * called.
17159 *
17160 * Note that as #ClutterEffect is initially unowned,
17161 * clutter_actor_add_effect() will sink any floating reference on @effect.
17162 *
17163 * Since: 1.4
17164 */
17165 void
clutter_actor_add_effect(ClutterActor * self,ClutterEffect * effect)17166 clutter_actor_add_effect (ClutterActor *self,
17167 ClutterEffect *effect)
17168 {
17169 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17170 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
17171
17172 _clutter_actor_add_effect_internal (self, effect);
17173
17174 clutter_actor_queue_redraw (self);
17175
17176 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
17177 }
17178
17179 /**
17180 * clutter_actor_add_effect_with_name:
17181 * @self: a #ClutterActor
17182 * @name: the name to set on the effect
17183 * @effect: a #ClutterEffect
17184 *
17185 * A convenience function for setting the name of a #ClutterEffect
17186 * while adding it to the list of effects applied to @self.
17187 *
17188 * Note that as #ClutterEffect is initially unowned,
17189 * clutter_actor_add_effect_with_name() will sink any floating
17190 * reference on @effect.
17191 *
17192 * This function is the logical equivalent of:
17193 *
17194 * |[<!-- language="C" -->
17195 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
17196 * clutter_actor_add_effect (self, effect);
17197 * ]|
17198 *
17199 * Since: 1.4
17200 */
17201 void
clutter_actor_add_effect_with_name(ClutterActor * self,const gchar * name,ClutterEffect * effect)17202 clutter_actor_add_effect_with_name (ClutterActor *self,
17203 const gchar *name,
17204 ClutterEffect *effect)
17205 {
17206 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17207 g_return_if_fail (name != NULL);
17208 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
17209
17210 clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
17211 clutter_actor_add_effect (self, effect);
17212 }
17213
17214 /**
17215 * clutter_actor_remove_effect:
17216 * @self: a #ClutterActor
17217 * @effect: a #ClutterEffect
17218 *
17219 * Removes @effect from the list of effects applied to @self
17220 *
17221 * The reference held by @self on the #ClutterEffect will be released
17222 *
17223 * Since: 1.4
17224 */
17225 void
clutter_actor_remove_effect(ClutterActor * self,ClutterEffect * effect)17226 clutter_actor_remove_effect (ClutterActor *self,
17227 ClutterEffect *effect)
17228 {
17229 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17230 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
17231
17232 _clutter_actor_remove_effect_internal (self, effect);
17233
17234 clutter_actor_queue_redraw (self);
17235
17236 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
17237 }
17238
17239 /**
17240 * clutter_actor_remove_effect_by_name:
17241 * @self: a #ClutterActor
17242 * @name: the name of the effect to remove
17243 *
17244 * Removes the #ClutterEffect with the given name from the list
17245 * of effects applied to @self
17246 *
17247 * Since: 1.4
17248 */
17249 void
clutter_actor_remove_effect_by_name(ClutterActor * self,const gchar * name)17250 clutter_actor_remove_effect_by_name (ClutterActor *self,
17251 const gchar *name)
17252 {
17253 ClutterActorPrivate *priv;
17254 ClutterActorMeta *meta;
17255
17256 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17257 g_return_if_fail (name != NULL);
17258
17259 priv = self->priv;
17260
17261 if (priv->effects == NULL)
17262 return;
17263
17264 meta = _clutter_meta_group_get_meta (priv->effects, name);
17265 if (meta == NULL)
17266 return;
17267
17268 clutter_actor_remove_effect (self, CLUTTER_EFFECT (meta));
17269 }
17270
17271 /**
17272 * clutter_actor_get_effects:
17273 * @self: a #ClutterActor
17274 *
17275 * Retrieves the #ClutterEffect<!-- -->s applied on @self, if any
17276 *
17277 * Return value: (transfer container) (element-type Clutter.Effect): a list
17278 * of #ClutterEffect<!-- -->s, or %NULL. The elements of the returned
17279 * list are owned by Clutter and they should not be freed. You should
17280 * free the returned list using g_list_free() when done
17281 *
17282 * Since: 1.4
17283 */
17284 GList *
clutter_actor_get_effects(ClutterActor * self)17285 clutter_actor_get_effects (ClutterActor *self)
17286 {
17287 ClutterActorPrivate *priv;
17288
17289 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17290
17291 priv = self->priv;
17292
17293 if (priv->effects == NULL)
17294 return NULL;
17295
17296 return _clutter_meta_group_get_metas_no_internal (priv->effects);
17297 }
17298
17299 /**
17300 * clutter_actor_get_effect:
17301 * @self: a #ClutterActor
17302 * @name: the name of the effect to retrieve
17303 *
17304 * Retrieves the #ClutterEffect with the given name in the list
17305 * of effects applied to @self
17306 *
17307 * Return value: (transfer none): a #ClutterEffect for the given
17308 * name, or %NULL. The returned #ClutterEffect is owned by the
17309 * actor and it should not be unreferenced directly
17310 *
17311 * Since: 1.4
17312 */
17313 ClutterEffect *
clutter_actor_get_effect(ClutterActor * self,const gchar * name)17314 clutter_actor_get_effect (ClutterActor *self,
17315 const gchar *name)
17316 {
17317 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17318 g_return_val_if_fail (name != NULL, NULL);
17319
17320 if (self->priv->effects == NULL)
17321 return NULL;
17322
17323 return CLUTTER_EFFECT (_clutter_meta_group_get_meta (self->priv->effects, name));
17324 }
17325
17326 /**
17327 * clutter_actor_clear_effects:
17328 * @self: a #ClutterActor
17329 *
17330 * Clears the list of effects applied to @self
17331 *
17332 * Since: 1.4
17333 */
17334 void
clutter_actor_clear_effects(ClutterActor * self)17335 clutter_actor_clear_effects (ClutterActor *self)
17336 {
17337 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17338
17339 if (self->priv->effects == NULL)
17340 return;
17341
17342 _clutter_meta_group_clear_metas_no_internal (self->priv->effects);
17343
17344 clutter_actor_queue_redraw (self);
17345 }
17346
17347 /**
17348 * clutter_actor_has_key_focus:
17349 * @self: a #ClutterActor
17350 *
17351 * Checks whether @self is the #ClutterActor that has key focus
17352 *
17353 * Return value: %TRUE if the actor has key focus, and %FALSE otherwise
17354 *
17355 * Since: 1.4
17356 */
17357 gboolean
clutter_actor_has_key_focus(ClutterActor * self)17358 clutter_actor_has_key_focus (ClutterActor *self)
17359 {
17360 ClutterActor *stage;
17361
17362 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17363
17364 stage = _clutter_actor_get_stage_internal (self);
17365 if (stage == NULL)
17366 return FALSE;
17367
17368 return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == self;
17369 }
17370
17371 static gboolean
_clutter_actor_get_paint_volume_real(ClutterActor * self,ClutterPaintVolume * pv)17372 _clutter_actor_get_paint_volume_real (ClutterActor *self,
17373 ClutterPaintVolume *pv)
17374 {
17375 ClutterActorPrivate *priv = self->priv;
17376
17377 /* Actors are only expected to report a valid paint volume
17378 * while they have a valid allocation. */
17379 if (G_UNLIKELY (priv->needs_allocation))
17380 {
17381 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17382 "Actor needs allocation",
17383 _clutter_actor_get_debug_name (self));
17384 return FALSE;
17385 }
17386
17387 /* Check if there are any handlers connected to the paint
17388 * signal. If there are then all bets are off for what the paint
17389 * volume for this actor might possibly be!
17390 *
17391 * XXX: It's expected that this is going to end up being quite a
17392 * costly check to have to do here, but we haven't come up with
17393 * another solution that can reliably catch paint signal handlers at
17394 * the right time to either avoid artefacts due to invalid stage
17395 * clipping or due to incorrect culling.
17396 *
17397 * Previously we checked in clutter_actor_paint(), but at that time
17398 * we may already be using a stage clip that could be derived from
17399 * an invalid paint-volume. We used to try and handle that by
17400 * queuing a follow up, unclipped, redraw but still the previous
17401 * checking wasn't enough to catch invalid volumes involved in
17402 * culling (considering that containers may derive their volume from
17403 * children that haven't yet been painted)
17404 *
17405 * Longer term, improved solutions could be:
17406 * - Disallow painting in the paint signal, only allow using it
17407 * for tracking when paints happen. We can add another API that
17408 * allows monkey patching the paint of arbitrary actors but in a
17409 * more controlled way and that also supports modifying the
17410 * paint-volume.
17411 * - If we could be notified somehow when signal handlers are
17412 * connected we wouldn't have to poll for handlers like this.
17413 *
17414 * XXX:2.0 - Remove when we remove the paint signal
17415 */
17416 if (g_signal_has_handler_pending (self,
17417 actor_signals[PAINT],
17418 0,
17419 TRUE))
17420 {
17421 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17422 "Actor has \"paint\" signal handlers",
17423 _clutter_actor_get_debug_name (self));
17424 return FALSE;
17425 }
17426
17427 _clutter_paint_volume_init_static (pv, self);
17428
17429 if (!CLUTTER_ACTOR_GET_CLASS (self)->get_paint_volume (self, pv))
17430 {
17431 clutter_paint_volume_free (pv);
17432 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17433 "Actor failed to report a volume",
17434 _clutter_actor_get_debug_name (self));
17435 return FALSE;
17436 }
17437
17438 /* since effects can modify the paint volume, we allow them to actually
17439 * do this by making get_paint_volume() "context sensitive"
17440 */
17441 if (priv->effects != NULL)
17442 {
17443 if (priv->current_effect != NULL)
17444 {
17445 const GList *effects, *l;
17446
17447 /* if we are being called from within the paint sequence of
17448 * an actor, get the paint volume up to the current effect
17449 */
17450 effects = _clutter_meta_group_peek_metas (priv->effects);
17451 for (l = effects;
17452 l != NULL || (l != NULL && l->data != priv->current_effect);
17453 l = l->next)
17454 {
17455 if (!_clutter_effect_get_paint_volume (l->data, pv))
17456 {
17457 clutter_paint_volume_free (pv);
17458 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17459 "Effect (%s) failed to report a volume",
17460 _clutter_actor_get_debug_name (self),
17461 _clutter_actor_meta_get_debug_name (l->data));
17462 return FALSE;
17463 }
17464 }
17465 }
17466 else
17467 {
17468 const GList *effects, *l;
17469
17470 /* otherwise, get the cumulative volume */
17471 effects = _clutter_meta_group_peek_metas (priv->effects);
17472 for (l = effects; l != NULL; l = l->next)
17473 if (!_clutter_effect_get_paint_volume (l->data, pv))
17474 {
17475 clutter_paint_volume_free (pv);
17476 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17477 "Effect (%s) failed to report a volume",
17478 _clutter_actor_get_debug_name (self),
17479 _clutter_actor_meta_get_debug_name (l->data));
17480 return FALSE;
17481 }
17482 }
17483 }
17484
17485 return TRUE;
17486 }
17487
17488 /* The public clutter_actor_get_paint_volume API returns a const
17489 * pointer since we return a pointer directly to the cached
17490 * PaintVolume associated with the actor and don't want the user to
17491 * inadvertently modify it, but for internal uses we sometimes need
17492 * access to the same PaintVolume but need to apply some book-keeping
17493 * modifications to it so we don't want a const pointer.
17494 */
17495 static ClutterPaintVolume *
_clutter_actor_get_paint_volume_mutable(ClutterActor * self)17496 _clutter_actor_get_paint_volume_mutable (ClutterActor *self)
17497 {
17498 ClutterActorPrivate *priv;
17499
17500 priv = self->priv;
17501
17502 if (priv->paint_volume_valid)
17503 clutter_paint_volume_free (&priv->paint_volume);
17504
17505 if (_clutter_actor_get_paint_volume_real (self, &priv->paint_volume))
17506 {
17507 priv->paint_volume_valid = TRUE;
17508 return &priv->paint_volume;
17509 }
17510 else
17511 {
17512 priv->paint_volume_valid = FALSE;
17513 return NULL;
17514 }
17515 }
17516
17517 /**
17518 * clutter_actor_get_paint_volume:
17519 * @self: a #ClutterActor
17520 *
17521 * Retrieves the paint volume of the passed #ClutterActor, or %NULL
17522 * when a paint volume can't be determined.
17523 *
17524 * The paint volume is defined as the 3D space occupied by an actor
17525 * when being painted.
17526 *
17527 * This function will call the #ClutterActorClass.get_paint_volume()
17528 * virtual function of the #ClutterActor class. Sub-classes of #ClutterActor
17529 * should not usually care about overriding the default implementation,
17530 * unless they are, for instance: painting outside their allocation, or
17531 * actors with a depth factor (not in terms of #ClutterActor:depth but real
17532 * 3D depth).
17533 *
17534 * Note: 2D actors overriding #ClutterActorClass.get_paint_volume()
17535 * should ensure that their volume has a depth of 0. (This will be true
17536 * as long as you don't call clutter_paint_volume_set_depth().)
17537 *
17538 * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
17539 * or %NULL if no volume could be determined. The returned pointer
17540 * is not guaranteed to be valid across multiple frames; if you want
17541 * to keep it, you will need to copy it using clutter_paint_volume_copy().
17542 *
17543 * Since: 1.6
17544 */
17545 const ClutterPaintVolume *
clutter_actor_get_paint_volume(ClutterActor * self)17546 clutter_actor_get_paint_volume (ClutterActor *self)
17547 {
17548 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17549
17550 return _clutter_actor_get_paint_volume_mutable (self);
17551 }
17552
17553 /**
17554 * clutter_actor_get_transformed_paint_volume:
17555 * @self: a #ClutterActor
17556 * @relative_to_ancestor: A #ClutterActor that is an ancestor of @self
17557 * (or %NULL for the stage)
17558 *
17559 * Retrieves the 3D paint volume of an actor like
17560 * clutter_actor_get_paint_volume() does (Please refer to the
17561 * documentation of clutter_actor_get_paint_volume() for more
17562 * details.) and it additionally transforms the paint volume into the
17563 * coordinate space of @relative_to_ancestor. (Or the stage if %NULL
17564 * is passed for @relative_to_ancestor)
17565 *
17566 * This can be used by containers that base their paint volume on
17567 * the volume of their children. Such containers can query the
17568 * transformed paint volume of all of its children and union them
17569 * together using clutter_paint_volume_union().
17570 *
17571 * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
17572 * or %NULL if no volume could be determined. The returned pointer is
17573 * not guaranteed to be valid across multiple frames; if you wish to
17574 * keep it, you will have to copy it using clutter_paint_volume_copy().
17575 *
17576 * Since: 1.6
17577 */
17578 const ClutterPaintVolume *
clutter_actor_get_transformed_paint_volume(ClutterActor * self,ClutterActor * relative_to_ancestor)17579 clutter_actor_get_transformed_paint_volume (ClutterActor *self,
17580 ClutterActor *relative_to_ancestor)
17581 {
17582 const ClutterPaintVolume *volume;
17583 ClutterActor *stage;
17584 ClutterPaintVolume *transformed_volume;
17585
17586 stage = _clutter_actor_get_stage_internal (self);
17587 if (G_UNLIKELY (stage == NULL))
17588 return NULL;
17589
17590 if (relative_to_ancestor == NULL)
17591 relative_to_ancestor = stage;
17592
17593 volume = clutter_actor_get_paint_volume (self);
17594 if (volume == NULL)
17595 return NULL;
17596
17597 transformed_volume =
17598 _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
17599
17600 _clutter_paint_volume_copy_static (volume, transformed_volume);
17601
17602 _clutter_paint_volume_transform_relative (transformed_volume,
17603 relative_to_ancestor);
17604
17605 return transformed_volume;
17606 }
17607
17608 /**
17609 * clutter_actor_get_paint_box:
17610 * @self: a #ClutterActor
17611 * @box: (out): return location for a #ClutterActorBox
17612 *
17613 * Retrieves the paint volume of the passed #ClutterActor, and
17614 * transforms it into a 2D bounding box in stage coordinates.
17615 *
17616 * This function is useful to determine the on screen area occupied by
17617 * the actor. The box is only an approximation and may often be
17618 * considerably larger due to the optimizations used to calculate the
17619 * box. The box is never smaller though, so it can reliably be used
17620 * for culling.
17621 *
17622 * There are times when a 2D paint box can't be determined, e.g.
17623 * because the actor isn't yet parented under a stage or because
17624 * the actor is unable to determine a paint volume.
17625 *
17626 * Return value: %TRUE if a 2D paint box could be determined, else
17627 * %FALSE.
17628 *
17629 * Since: 1.6
17630 */
17631 gboolean
clutter_actor_get_paint_box(ClutterActor * self,ClutterActorBox * box)17632 clutter_actor_get_paint_box (ClutterActor *self,
17633 ClutterActorBox *box)
17634 {
17635 ClutterActor *stage;
17636 ClutterPaintVolume *pv;
17637
17638 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17639 g_return_val_if_fail (box != NULL, FALSE);
17640
17641 stage = _clutter_actor_get_stage_internal (self);
17642 if (G_UNLIKELY (!stage))
17643 return FALSE;
17644
17645 pv = _clutter_actor_get_paint_volume_mutable (self);
17646 if (G_UNLIKELY (!pv))
17647 return FALSE;
17648
17649 _clutter_paint_volume_get_stage_paint_box (pv, CLUTTER_STAGE (stage), box);
17650
17651 return TRUE;
17652 }
17653
17654 /**
17655 * clutter_actor_has_overlaps:
17656 * @self: A #ClutterActor
17657 *
17658 * Asks the actor's implementation whether it may contain overlapping
17659 * primitives.
17660 *
17661 * For example; Clutter may use this to determine whether the painting
17662 * should be redirected to an offscreen buffer to correctly implement
17663 * the opacity property.
17664 *
17665 * Custom actors can override the default response by implementing the
17666 * #ClutterActorClass.has_overlaps() virtual function. See
17667 * clutter_actor_set_offscreen_redirect() for more information.
17668 *
17669 * Return value: %TRUE if the actor may have overlapping primitives, and
17670 * %FALSE otherwise
17671 *
17672 * Since: 1.8
17673 */
17674 gboolean
clutter_actor_has_overlaps(ClutterActor * self)17675 clutter_actor_has_overlaps (ClutterActor *self)
17676 {
17677 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
17678
17679 return CLUTTER_ACTOR_GET_CLASS (self)->has_overlaps (self);
17680 }
17681
17682 /**
17683 * clutter_actor_has_effects:
17684 * @self: A #ClutterActor
17685 *
17686 * Returns whether the actor has any effects applied.
17687 *
17688 * Return value: %TRUE if the actor has any effects,
17689 * %FALSE otherwise
17690 *
17691 * Since: 1.10
17692 */
17693 gboolean
clutter_actor_has_effects(ClutterActor * self)17694 clutter_actor_has_effects (ClutterActor *self)
17695 {
17696 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17697
17698 if (self->priv->effects == NULL)
17699 return FALSE;
17700
17701 return _clutter_meta_group_has_metas_no_internal (self->priv->effects);
17702 }
17703
17704 /**
17705 * clutter_actor_has_constraints:
17706 * @self: A #ClutterActor
17707 *
17708 * Returns whether the actor has any constraints applied.
17709 *
17710 * Return value: %TRUE if the actor has any constraints,
17711 * %FALSE otherwise
17712 *
17713 * Since: 1.10
17714 */
17715 gboolean
clutter_actor_has_constraints(ClutterActor * self)17716 clutter_actor_has_constraints (ClutterActor *self)
17717 {
17718 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17719
17720 if (self->priv->constraints == NULL)
17721 return FALSE;
17722
17723 return _clutter_meta_group_has_metas_no_internal (self->priv->constraints);
17724 }
17725
17726 /**
17727 * clutter_actor_has_actions:
17728 * @self: A #ClutterActor
17729 *
17730 * Returns whether the actor has any actions applied.
17731 *
17732 * Return value: %TRUE if the actor has any actions,
17733 * %FALSE otherwise
17734 *
17735 * Since: 1.10
17736 */
17737 gboolean
clutter_actor_has_actions(ClutterActor * self)17738 clutter_actor_has_actions (ClutterActor *self)
17739 {
17740 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17741
17742 if (self->priv->actions == NULL)
17743 return FALSE;
17744
17745 return _clutter_meta_group_has_metas_no_internal (self->priv->actions);
17746 }
17747
17748 /**
17749 * clutter_actor_get_n_children:
17750 * @self: a #ClutterActor
17751 *
17752 * Retrieves the number of children of @self.
17753 *
17754 * Return value: the number of children of an actor
17755 *
17756 * Since: 1.10
17757 */
17758 gint
clutter_actor_get_n_children(ClutterActor * self)17759 clutter_actor_get_n_children (ClutterActor *self)
17760 {
17761 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
17762
17763 return self->priv->n_children;
17764 }
17765
17766 /**
17767 * clutter_actor_get_child_at_index:
17768 * @self: a #ClutterActor
17769 * @index_: the position in the list of children
17770 *
17771 * Retrieves the actor at the given @index_ inside the list of
17772 * children of @self.
17773 *
17774 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17775 *
17776 * Since: 1.10
17777 */
17778 ClutterActor *
clutter_actor_get_child_at_index(ClutterActor * self,gint index_)17779 clutter_actor_get_child_at_index (ClutterActor *self,
17780 gint index_)
17781 {
17782 ClutterActor *iter;
17783 int i;
17784
17785 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17786 g_return_val_if_fail (index_ <= self->priv->n_children, NULL);
17787
17788 for (iter = self->priv->first_child, i = 0;
17789 iter != NULL && i < index_;
17790 iter = iter->priv->next_sibling, i += 1)
17791 ;
17792
17793 return iter;
17794 }
17795
17796 /*< private >
17797 * _clutter_actor_foreach_child:
17798 * @actor: The actor whos children you want to iterate
17799 * @callback: The function to call for each child
17800 * @user_data: Private data to pass to @callback
17801 *
17802 * Calls a given @callback once for each child of the specified @actor and
17803 * passing the @user_data pointer each time.
17804 *
17805 * Return value: returns %TRUE if all children were iterated, else
17806 * %FALSE if a callback broke out of iteration early.
17807 */
17808 gboolean
_clutter_actor_foreach_child(ClutterActor * self,ClutterForeachCallback callback,gpointer user_data)17809 _clutter_actor_foreach_child (ClutterActor *self,
17810 ClutterForeachCallback callback,
17811 gpointer user_data)
17812 {
17813 ClutterActor *iter;
17814 gboolean cont;
17815
17816 if (self->priv->first_child == NULL)
17817 return TRUE;
17818
17819 cont = TRUE;
17820 iter = self->priv->first_child;
17821
17822 /* we use this form so that it's safe to change the children
17823 * list while iterating it
17824 */
17825 while (cont && iter != NULL)
17826 {
17827 ClutterActor *next = iter->priv->next_sibling;
17828
17829 cont = callback (iter, user_data);
17830
17831 iter = next;
17832 }
17833
17834 return cont;
17835 }
17836
17837 #if 0
17838 /* For debugging purposes this gives us a simple way to print out
17839 * the scenegraph e.g in gdb using:
17840 * [|
17841 * _clutter_actor_traverse (stage,
17842 * 0,
17843 * clutter_debug_print_actor_cb,
17844 * NULL,
17845 * NULL);
17846 * |]
17847 */
17848 static ClutterActorTraverseVisitFlags
17849 clutter_debug_print_actor_cb (ClutterActor *actor,
17850 int depth,
17851 void *user_data)
17852 {
17853 g_print ("%*s%s:%p\n",
17854 depth * 2, "",
17855 _clutter_actor_get_debug_name (actor),
17856 actor);
17857
17858 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
17859 }
17860 #endif
17861
17862 static void
_clutter_actor_traverse_breadth(ClutterActor * actor,ClutterTraverseCallback callback,gpointer user_data)17863 _clutter_actor_traverse_breadth (ClutterActor *actor,
17864 ClutterTraverseCallback callback,
17865 gpointer user_data)
17866 {
17867 GQueue *queue = g_queue_new ();
17868 ClutterActor dummy;
17869 int current_depth = 0;
17870
17871 g_queue_push_tail (queue, actor);
17872 g_queue_push_tail (queue, &dummy); /* use to delimit depth changes */
17873
17874 while ((actor = g_queue_pop_head (queue)))
17875 {
17876 ClutterActorTraverseVisitFlags flags;
17877
17878 if (actor == &dummy)
17879 {
17880 current_depth++;
17881 g_queue_push_tail (queue, &dummy);
17882 continue;
17883 }
17884
17885 flags = callback (actor, current_depth, user_data);
17886 if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
17887 break;
17888 else if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
17889 {
17890 ClutterActor *iter;
17891
17892 for (iter = actor->priv->first_child;
17893 iter != NULL;
17894 iter = iter->priv->next_sibling)
17895 {
17896 g_queue_push_tail (queue, iter);
17897 }
17898 }
17899 }
17900
17901 g_queue_free (queue);
17902 }
17903
17904 static ClutterActorTraverseVisitFlags
_clutter_actor_traverse_depth(ClutterActor * actor,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,int current_depth,gpointer user_data)17905 _clutter_actor_traverse_depth (ClutterActor *actor,
17906 ClutterTraverseCallback before_children_callback,
17907 ClutterTraverseCallback after_children_callback,
17908 int current_depth,
17909 gpointer user_data)
17910 {
17911 ClutterActorTraverseVisitFlags flags;
17912
17913 flags = before_children_callback (actor, current_depth, user_data);
17914 if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
17915 return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
17916
17917 if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
17918 {
17919 ClutterActor *iter;
17920
17921 for (iter = actor->priv->first_child;
17922 iter != NULL;
17923 iter = iter->priv->next_sibling)
17924 {
17925 flags = _clutter_actor_traverse_depth (iter,
17926 before_children_callback,
17927 after_children_callback,
17928 current_depth + 1,
17929 user_data);
17930
17931 if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
17932 return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
17933 }
17934 }
17935
17936 if (after_children_callback)
17937 return after_children_callback (actor, current_depth, user_data);
17938 else
17939 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
17940 }
17941
17942 /* _clutter_actor_traverse:
17943 * @actor: The actor to start traversing the graph from
17944 * @flags: These flags may affect how the traversal is done
17945 * @before_children_callback: A function to call before visiting the
17946 * children of the current actor.
17947 * @after_children_callback: A function to call after visiting the
17948 * children of the current actor. (Ignored if
17949 * %CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST is passed to @flags.)
17950 * @user_data: The private data to pass to the callbacks
17951 *
17952 * Traverses the scenegraph starting at the specified @actor and
17953 * descending through all its children and its children's children.
17954 * For each actor traversed @before_children_callback and
17955 * @after_children_callback are called with the specified
17956 * @user_data, before and after visiting that actor's children.
17957 *
17958 * The callbacks can return flags that affect the ongoing traversal
17959 * such as by skipping over an actors children or bailing out of
17960 * any further traversing.
17961 */
17962 void
_clutter_actor_traverse(ClutterActor * actor,ClutterActorTraverseFlags flags,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,gpointer user_data)17963 _clutter_actor_traverse (ClutterActor *actor,
17964 ClutterActorTraverseFlags flags,
17965 ClutterTraverseCallback before_children_callback,
17966 ClutterTraverseCallback after_children_callback,
17967 gpointer user_data)
17968 {
17969 if (flags & CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST)
17970 _clutter_actor_traverse_breadth (actor,
17971 before_children_callback,
17972 user_data);
17973 else /* DEPTH_FIRST */
17974 _clutter_actor_traverse_depth (actor,
17975 before_children_callback,
17976 after_children_callback,
17977 0, /* start depth */
17978 user_data);
17979 }
17980
17981 static void
on_layout_manager_changed(ClutterLayoutManager * manager,ClutterActor * self)17982 on_layout_manager_changed (ClutterLayoutManager *manager,
17983 ClutterActor *self)
17984 {
17985 clutter_actor_queue_relayout (self);
17986 }
17987
17988 /**
17989 * clutter_actor_set_layout_manager:
17990 * @self: a #ClutterActor
17991 * @manager: (allow-none): a #ClutterLayoutManager, or %NULL to unset it
17992 *
17993 * Sets the #ClutterLayoutManager delegate object that will be used to
17994 * lay out the children of @self.
17995 *
17996 * The #ClutterActor will take a reference on the passed @manager which
17997 * will be released either when the layout manager is removed, or when
17998 * the actor is destroyed.
17999 *
18000 * Since: 1.10
18001 */
18002 void
clutter_actor_set_layout_manager(ClutterActor * self,ClutterLayoutManager * manager)18003 clutter_actor_set_layout_manager (ClutterActor *self,
18004 ClutterLayoutManager *manager)
18005 {
18006 ClutterActorPrivate *priv;
18007
18008 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18009 g_return_if_fail (manager == NULL || CLUTTER_IS_LAYOUT_MANAGER (manager));
18010
18011 priv = self->priv;
18012
18013 if (priv->layout_manager != NULL)
18014 {
18015 g_signal_handlers_disconnect_by_func (priv->layout_manager,
18016 G_CALLBACK (on_layout_manager_changed),
18017 self);
18018 clutter_layout_manager_set_container (priv->layout_manager, NULL);
18019 g_clear_object (&priv->layout_manager);
18020 }
18021
18022 priv->layout_manager = manager;
18023
18024 if (priv->layout_manager != NULL)
18025 {
18026 g_object_ref_sink (priv->layout_manager);
18027 clutter_layout_manager_set_container (priv->layout_manager,
18028 CLUTTER_CONTAINER (self));
18029 g_signal_connect (priv->layout_manager, "layout-changed",
18030 G_CALLBACK (on_layout_manager_changed),
18031 self);
18032 }
18033
18034 clutter_actor_queue_relayout (self);
18035
18036 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAYOUT_MANAGER]);
18037 }
18038
18039 /**
18040 * clutter_actor_get_layout_manager:
18041 * @self: a #ClutterActor
18042 *
18043 * Retrieves the #ClutterLayoutManager used by @self.
18044 *
18045 * Return value: (transfer none): a pointer to the #ClutterLayoutManager,
18046 * or %NULL
18047 *
18048 * Since: 1.10
18049 */
18050 ClutterLayoutManager *
clutter_actor_get_layout_manager(ClutterActor * self)18051 clutter_actor_get_layout_manager (ClutterActor *self)
18052 {
18053 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18054
18055 return self->priv->layout_manager;
18056 }
18057
18058 static const ClutterLayoutInfo default_layout_info = {
18059 CLUTTER_POINT_INIT_ZERO, /* fixed-pos */
18060 { 0, 0, 0, 0 }, /* margin */
18061 CLUTTER_ACTOR_ALIGN_FILL, /* x-align */
18062 CLUTTER_ACTOR_ALIGN_FILL, /* y-align */
18063 FALSE, FALSE, /* expand */
18064 CLUTTER_SIZE_INIT_ZERO, /* minimum */
18065 CLUTTER_SIZE_INIT_ZERO, /* natural */
18066 };
18067
18068 static void
layout_info_free(gpointer data)18069 layout_info_free (gpointer data)
18070 {
18071 if (G_LIKELY (data != NULL))
18072 g_slice_free (ClutterLayoutInfo, data);
18073 }
18074
18075 /*< private >
18076 * _clutter_actor_peek_layout_info:
18077 * @self: a #ClutterActor
18078 *
18079 * Retrieves a pointer to the ClutterLayoutInfo structure.
18080 *
18081 * If the actor does not have a ClutterLayoutInfo associated to it, %NULL is returned.
18082 *
18083 * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
18084 */
18085 ClutterLayoutInfo *
_clutter_actor_peek_layout_info(ClutterActor * self)18086 _clutter_actor_peek_layout_info (ClutterActor *self)
18087 {
18088 return g_object_get_qdata (G_OBJECT (self), quark_actor_layout_info);
18089 }
18090
18091 /*< private >
18092 * _clutter_actor_get_layout_info:
18093 * @self: a #ClutterActor
18094 *
18095 * Retrieves a pointer to the ClutterLayoutInfo structure.
18096 *
18097 * If the actor does not have a ClutterLayoutInfo associated to it, one
18098 * will be created and initialized to the default values.
18099 *
18100 * This function should be used for setters.
18101 *
18102 * For getters, you should use _clutter_actor_get_layout_info_or_defaults()
18103 * instead.
18104 *
18105 * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
18106 */
18107 ClutterLayoutInfo *
_clutter_actor_get_layout_info(ClutterActor * self)18108 _clutter_actor_get_layout_info (ClutterActor *self)
18109 {
18110 ClutterLayoutInfo *retval;
18111
18112 retval = _clutter_actor_peek_layout_info (self);
18113 if (retval == NULL)
18114 {
18115 retval = g_slice_new (ClutterLayoutInfo);
18116
18117 *retval = default_layout_info;
18118
18119 g_object_set_qdata_full (G_OBJECT (self), quark_actor_layout_info,
18120 retval,
18121 layout_info_free);
18122 }
18123
18124 return retval;
18125 }
18126
18127 /*< private >
18128 * _clutter_actor_get_layout_info_or_defaults:
18129 * @self: a #ClutterActor
18130 *
18131 * Retrieves the ClutterLayoutInfo structure associated to an actor.
18132 *
18133 * If the actor does not have a ClutterLayoutInfo structure associated to it,
18134 * then the default structure will be returned.
18135 *
18136 * This function should only be used for getters.
18137 *
18138 * Return value: a const pointer to the ClutterLayoutInfo structure
18139 */
18140 const ClutterLayoutInfo *
_clutter_actor_get_layout_info_or_defaults(ClutterActor * self)18141 _clutter_actor_get_layout_info_or_defaults (ClutterActor *self)
18142 {
18143 const ClutterLayoutInfo *info;
18144
18145 info = _clutter_actor_peek_layout_info (self);
18146 if (info == NULL)
18147 return &default_layout_info;
18148
18149 return info;
18150 }
18151
18152 /**
18153 * clutter_actor_set_x_align:
18154 * @self: a #ClutterActor
18155 * @x_align: the horizontal alignment policy
18156 *
18157 * Sets the horizontal alignment policy of a #ClutterActor, in case the
18158 * actor received extra horizontal space.
18159 *
18160 * See also the #ClutterActor:x-align property.
18161 *
18162 * Since: 1.10
18163 */
18164 void
clutter_actor_set_x_align(ClutterActor * self,ClutterActorAlign x_align)18165 clutter_actor_set_x_align (ClutterActor *self,
18166 ClutterActorAlign x_align)
18167 {
18168 ClutterLayoutInfo *info;
18169
18170 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18171
18172 info = _clutter_actor_get_layout_info (self);
18173
18174 if (info->x_align != x_align)
18175 {
18176 info->x_align = x_align;
18177
18178 clutter_actor_queue_relayout (self);
18179
18180 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_X_ALIGN]);
18181 }
18182 }
18183
18184 /**
18185 * clutter_actor_get_x_align:
18186 * @self: a #ClutterActor
18187 *
18188 * Retrieves the horizontal alignment policy set using
18189 * clutter_actor_set_x_align().
18190 *
18191 * Return value: the horizontal alignment policy.
18192 *
18193 * Since: 1.10
18194 */
18195 ClutterActorAlign
clutter_actor_get_x_align(ClutterActor * self)18196 clutter_actor_get_x_align (ClutterActor *self)
18197 {
18198 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
18199
18200 return _clutter_actor_get_layout_info_or_defaults (self)->x_align;
18201 }
18202
18203 /**
18204 * clutter_actor_set_y_align:
18205 * @self: a #ClutterActor
18206 * @y_align: the vertical alignment policy
18207 *
18208 * Sets the vertical alignment policy of a #ClutterActor, in case the
18209 * actor received extra vertical space.
18210 *
18211 * See also the #ClutterActor:y-align property.
18212 *
18213 * Since: 1.10
18214 */
18215 void
clutter_actor_set_y_align(ClutterActor * self,ClutterActorAlign y_align)18216 clutter_actor_set_y_align (ClutterActor *self,
18217 ClutterActorAlign y_align)
18218 {
18219 ClutterLayoutInfo *info;
18220
18221 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18222
18223 info = _clutter_actor_get_layout_info (self);
18224
18225 if (info->y_align != y_align)
18226 {
18227 info->y_align = y_align;
18228
18229 clutter_actor_queue_relayout (self);
18230
18231 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Y_ALIGN]);
18232 }
18233 }
18234
18235 /**
18236 * clutter_actor_get_y_align:
18237 * @self: a #ClutterActor
18238 *
18239 * Retrieves the vertical alignment policy set using
18240 * clutter_actor_set_y_align().
18241 *
18242 * Return value: the vertical alignment policy.
18243 *
18244 * Since: 1.10
18245 */
18246 ClutterActorAlign
clutter_actor_get_y_align(ClutterActor * self)18247 clutter_actor_get_y_align (ClutterActor *self)
18248 {
18249 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
18250
18251 return _clutter_actor_get_layout_info_or_defaults (self)->y_align;
18252 }
18253
18254 static inline void
clutter_actor_set_margin_internal(ClutterActor * self,gfloat margin,GParamSpec * pspec)18255 clutter_actor_set_margin_internal (ClutterActor *self,
18256 gfloat margin,
18257 GParamSpec *pspec)
18258 {
18259 ClutterLayoutInfo *info;
18260
18261 info = _clutter_actor_get_layout_info (self);
18262
18263 if (pspec == obj_props[PROP_MARGIN_TOP])
18264 info->margin.top = margin;
18265 else if (pspec == obj_props[PROP_MARGIN_RIGHT])
18266 info->margin.right = margin;
18267 else if (pspec == obj_props[PROP_MARGIN_BOTTOM])
18268 info->margin.bottom = margin;
18269 else
18270 info->margin.left = margin;
18271
18272 clutter_actor_queue_relayout (self);
18273 g_object_notify_by_pspec (G_OBJECT (self), pspec);
18274 }
18275
18276 /**
18277 * clutter_actor_set_margin:
18278 * @self: a #ClutterActor
18279 * @margin: a #ClutterMargin
18280 *
18281 * Sets all the components of the margin of a #ClutterActor.
18282 *
18283 * Since: 1.10
18284 */
18285 void
clutter_actor_set_margin(ClutterActor * self,const ClutterMargin * margin)18286 clutter_actor_set_margin (ClutterActor *self,
18287 const ClutterMargin *margin)
18288 {
18289 ClutterLayoutInfo *info;
18290
18291 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18292 g_return_if_fail (margin != NULL);
18293
18294 info = _clutter_actor_get_layout_info (self);
18295
18296 if (info->margin.top != margin->top)
18297 clutter_actor_set_margin_top (self, margin->top);
18298
18299 if (info->margin.right != margin->right)
18300 clutter_actor_set_margin_right (self, margin->right);
18301
18302 if (info->margin.bottom != margin->bottom)
18303 clutter_actor_set_margin_bottom (self, margin->bottom);
18304
18305 if (info->margin.left != margin->left)
18306 clutter_actor_set_margin_left (self, margin->left);
18307 }
18308
18309 /**
18310 * clutter_actor_get_margin:
18311 * @self: a #ClutterActor
18312 * @margin: (out caller-allocates): return location for a #ClutterMargin
18313 *
18314 * Retrieves all the components of the margin of a #ClutterActor.
18315 *
18316 * Since: 1.10
18317 */
18318 void
clutter_actor_get_margin(ClutterActor * self,ClutterMargin * margin)18319 clutter_actor_get_margin (ClutterActor *self,
18320 ClutterMargin *margin)
18321 {
18322 const ClutterLayoutInfo *info;
18323
18324 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18325 g_return_if_fail (margin != NULL);
18326
18327 info = _clutter_actor_get_layout_info_or_defaults (self);
18328
18329 *margin = info->margin;
18330 }
18331
18332 /**
18333 * clutter_actor_set_margin_top:
18334 * @self: a #ClutterActor
18335 * @margin: the top margin
18336 *
18337 * Sets the margin from the top of a #ClutterActor.
18338 *
18339 * The #ClutterActor:margin-top property is animatable.
18340 *
18341 * Since: 1.10
18342 */
18343 void
clutter_actor_set_margin_top(ClutterActor * self,gfloat margin)18344 clutter_actor_set_margin_top (ClutterActor *self,
18345 gfloat margin)
18346 {
18347 const ClutterLayoutInfo *info;
18348
18349 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18350 g_return_if_fail (margin >= 0.f);
18351
18352 info = _clutter_actor_get_layout_info_or_defaults (self);
18353 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_TOP],
18354 info->margin.top,
18355 margin);
18356 }
18357
18358 /**
18359 * clutter_actor_get_margin_top:
18360 * @self: a #ClutterActor
18361 *
18362 * Retrieves the top margin of a #ClutterActor.
18363 *
18364 * Return value: the top margin
18365 *
18366 * Since: 1.10
18367 */
18368 gfloat
clutter_actor_get_margin_top(ClutterActor * self)18369 clutter_actor_get_margin_top (ClutterActor *self)
18370 {
18371 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18372
18373 return _clutter_actor_get_layout_info_or_defaults (self)->margin.top;
18374 }
18375
18376 /**
18377 * clutter_actor_set_margin_bottom:
18378 * @self: a #ClutterActor
18379 * @margin: the bottom margin
18380 *
18381 * Sets the margin from the bottom of a #ClutterActor.
18382 *
18383 * The #ClutterActor:margin-bottom property is animatable.
18384 *
18385 * Since: 1.10
18386 */
18387 void
clutter_actor_set_margin_bottom(ClutterActor * self,gfloat margin)18388 clutter_actor_set_margin_bottom (ClutterActor *self,
18389 gfloat margin)
18390 {
18391 const ClutterLayoutInfo *info;
18392
18393 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18394 g_return_if_fail (margin >= 0.f);
18395
18396 info = _clutter_actor_get_layout_info_or_defaults (self);
18397 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_BOTTOM],
18398 info->margin.bottom,
18399 margin);
18400 }
18401
18402 /**
18403 * clutter_actor_get_margin_bottom:
18404 * @self: a #ClutterActor
18405 *
18406 * Retrieves the bottom margin of a #ClutterActor.
18407 *
18408 * Return value: the bottom margin
18409 *
18410 * Since: 1.10
18411 */
18412 gfloat
clutter_actor_get_margin_bottom(ClutterActor * self)18413 clutter_actor_get_margin_bottom (ClutterActor *self)
18414 {
18415 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18416
18417 return _clutter_actor_get_layout_info_or_defaults (self)->margin.bottom;
18418 }
18419
18420 /**
18421 * clutter_actor_set_margin_left:
18422 * @self: a #ClutterActor
18423 * @margin: the left margin
18424 *
18425 * Sets the margin from the left of a #ClutterActor.
18426 *
18427 * The #ClutterActor:margin-left property is animatable.
18428 *
18429 * Since: 1.10
18430 */
18431 void
clutter_actor_set_margin_left(ClutterActor * self,gfloat margin)18432 clutter_actor_set_margin_left (ClutterActor *self,
18433 gfloat margin)
18434 {
18435 const ClutterLayoutInfo *info;
18436
18437 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18438 g_return_if_fail (margin >= 0.f);
18439
18440 info = _clutter_actor_get_layout_info_or_defaults (self);
18441 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_LEFT],
18442 info->margin.left,
18443 margin);
18444 }
18445
18446 /**
18447 * clutter_actor_get_margin_left:
18448 * @self: a #ClutterActor
18449 *
18450 * Retrieves the left margin of a #ClutterActor.
18451 *
18452 * Return value: the left margin
18453 *
18454 * Since: 1.10
18455 */
18456 gfloat
clutter_actor_get_margin_left(ClutterActor * self)18457 clutter_actor_get_margin_left (ClutterActor *self)
18458 {
18459 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18460
18461 return _clutter_actor_get_layout_info_or_defaults (self)->margin.left;
18462 }
18463
18464 /**
18465 * clutter_actor_set_margin_right:
18466 * @self: a #ClutterActor
18467 * @margin: the right margin
18468 *
18469 * Sets the margin from the right of a #ClutterActor.
18470 *
18471 * The #ClutterActor:margin-right property is animatable.
18472 *
18473 * Since: 1.10
18474 */
18475 void
clutter_actor_set_margin_right(ClutterActor * self,gfloat margin)18476 clutter_actor_set_margin_right (ClutterActor *self,
18477 gfloat margin)
18478 {
18479 const ClutterLayoutInfo *info;
18480
18481 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18482 g_return_if_fail (margin >= 0.f);
18483
18484 info = _clutter_actor_get_layout_info_or_defaults (self);
18485 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_RIGHT],
18486 info->margin.right,
18487 margin);
18488 }
18489
18490 /**
18491 * clutter_actor_get_margin_right:
18492 * @self: a #ClutterActor
18493 *
18494 * Retrieves the right margin of a #ClutterActor.
18495 *
18496 * Return value: the right margin
18497 *
18498 * Since: 1.10
18499 */
18500 gfloat
clutter_actor_get_margin_right(ClutterActor * self)18501 clutter_actor_get_margin_right (ClutterActor *self)
18502 {
18503 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18504
18505 return _clutter_actor_get_layout_info_or_defaults (self)->margin.right;
18506 }
18507
18508 static inline void
clutter_actor_set_background_color_internal(ClutterActor * self,const ClutterColor * color)18509 clutter_actor_set_background_color_internal (ClutterActor *self,
18510 const ClutterColor *color)
18511 {
18512 ClutterActorPrivate *priv = self->priv;
18513 GObject *obj;
18514
18515 if (priv->bg_color_set && clutter_color_equal (color, &priv->bg_color))
18516 return;
18517
18518 obj = G_OBJECT (self);
18519
18520 priv->bg_color = *color;
18521 priv->bg_color_set = TRUE;
18522
18523 clutter_actor_queue_redraw (self);
18524
18525 g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
18526 g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR]);
18527 }
18528
18529 /**
18530 * clutter_actor_set_background_color:
18531 * @self: a #ClutterActor
18532 * @color: (allow-none): a #ClutterColor, or %NULL to unset a previously
18533 * set color
18534 *
18535 * Sets the background color of a #ClutterActor.
18536 *
18537 * The background color will be used to cover the whole allocation of the
18538 * actor. The default background color of an actor is transparent.
18539 *
18540 * To check whether an actor has a background color, you can use the
18541 * #ClutterActor:background-color-set actor property.
18542 *
18543 * The #ClutterActor:background-color property is animatable.
18544 *
18545 * Since: 1.10
18546 */
18547 void
clutter_actor_set_background_color(ClutterActor * self,const ClutterColor * color)18548 clutter_actor_set_background_color (ClutterActor *self,
18549 const ClutterColor *color)
18550 {
18551 ClutterActorPrivate *priv;
18552
18553 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18554
18555 priv = self->priv;
18556
18557 if (color == NULL)
18558 {
18559 GObject *obj = G_OBJECT (self);
18560
18561 priv->bg_color_set = FALSE;
18562
18563 clutter_actor_queue_redraw (self);
18564
18565 g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
18566 }
18567 else
18568 _clutter_actor_create_transition (self,
18569 obj_props[PROP_BACKGROUND_COLOR],
18570 &priv->bg_color,
18571 color);
18572 }
18573
18574 /**
18575 * clutter_actor_get_background_color:
18576 * @self: a #ClutterActor
18577 * @color: (out caller-allocates): return location for a #ClutterColor
18578 *
18579 * Retrieves the color set using clutter_actor_set_background_color().
18580 *
18581 * Since: 1.10
18582 */
18583 void
clutter_actor_get_background_color(ClutterActor * self,ClutterColor * color)18584 clutter_actor_get_background_color (ClutterActor *self,
18585 ClutterColor *color)
18586 {
18587 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18588 g_return_if_fail (color != NULL);
18589
18590 *color = self->priv->bg_color;
18591 }
18592
18593 /**
18594 * clutter_actor_get_previous_sibling:
18595 * @self: a #ClutterActor
18596 *
18597 * Retrieves the sibling of @self that comes before it in the list
18598 * of children of @self's parent.
18599 *
18600 * The returned pointer is only valid until the scene graph changes; it
18601 * is not safe to modify the list of children of @self while iterating
18602 * it.
18603 *
18604 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18605 *
18606 * Since: 1.10
18607 */
18608 ClutterActor *
clutter_actor_get_previous_sibling(ClutterActor * self)18609 clutter_actor_get_previous_sibling (ClutterActor *self)
18610 {
18611 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18612
18613 return self->priv->prev_sibling;
18614 }
18615
18616 /**
18617 * clutter_actor_get_next_sibling:
18618 * @self: a #ClutterActor
18619 *
18620 * Retrieves the sibling of @self that comes after it in the list
18621 * of children of @self's parent.
18622 *
18623 * The returned pointer is only valid until the scene graph changes; it
18624 * is not safe to modify the list of children of @self while iterating
18625 * it.
18626 *
18627 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18628 *
18629 * Since: 1.10
18630 */
18631 ClutterActor *
clutter_actor_get_next_sibling(ClutterActor * self)18632 clutter_actor_get_next_sibling (ClutterActor *self)
18633 {
18634 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18635
18636 return self->priv->next_sibling;
18637 }
18638
18639 /**
18640 * clutter_actor_get_first_child:
18641 * @self: a #ClutterActor
18642 *
18643 * Retrieves the first child of @self.
18644 *
18645 * The returned pointer is only valid until the scene graph changes; it
18646 * is not safe to modify the list of children of @self while iterating
18647 * it.
18648 *
18649 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18650 *
18651 * Since: 1.10
18652 */
18653 ClutterActor *
clutter_actor_get_first_child(ClutterActor * self)18654 clutter_actor_get_first_child (ClutterActor *self)
18655 {
18656 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18657
18658 return self->priv->first_child;
18659 }
18660
18661 /**
18662 * clutter_actor_get_last_child:
18663 * @self: a #ClutterActor
18664 *
18665 * Retrieves the last child of @self.
18666 *
18667 * The returned pointer is only valid until the scene graph changes; it
18668 * is not safe to modify the list of children of @self while iterating
18669 * it.
18670 *
18671 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18672 *
18673 * Since: 1.10
18674 */
18675 ClutterActor *
clutter_actor_get_last_child(ClutterActor * self)18676 clutter_actor_get_last_child (ClutterActor *self)
18677 {
18678 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18679
18680 return self->priv->last_child;
18681 }
18682
18683 /* easy way to have properly named fields instead of the dummy ones
18684 * we use in the public structure
18685 */
18686 typedef struct _RealActorIter
18687 {
18688 ClutterActor *root; /* dummy1 */
18689 ClutterActor *current; /* dummy2 */
18690 gpointer padding_1; /* dummy3 */
18691 gint age; /* dummy4 */
18692 gpointer padding_2; /* dummy5 */
18693 } RealActorIter;
18694
18695 /**
18696 * clutter_actor_iter_init:
18697 * @iter: a #ClutterActorIter
18698 * @root: a #ClutterActor
18699 *
18700 * Initializes a #ClutterActorIter, which can then be used to iterate
18701 * efficiently over a section of the scene graph, and associates it
18702 * with @root.
18703 *
18704 * Modifying the scene graph section that contains @root will invalidate
18705 * the iterator.
18706 *
18707 * |[<!-- language="C" -->
18708 * ClutterActorIter iter;
18709 * ClutterActor *child;
18710 *
18711 * clutter_actor_iter_init (&iter, container);
18712 * while (clutter_actor_iter_next (&iter, &child))
18713 * {
18714 * // do something with child
18715 * }
18716 * ]|
18717 *
18718 * Since: 1.10
18719 */
18720 void
clutter_actor_iter_init(ClutterActorIter * iter,ClutterActor * root)18721 clutter_actor_iter_init (ClutterActorIter *iter,
18722 ClutterActor *root)
18723 {
18724 RealActorIter *ri = (RealActorIter *) iter;
18725
18726 g_return_if_fail (iter != NULL);
18727 g_return_if_fail (CLUTTER_IS_ACTOR (root));
18728
18729 ri->root = root;
18730 ri->current = NULL;
18731 ri->age = root->priv->age;
18732 }
18733
18734 /**
18735 * clutter_actor_iter_is_valid:
18736 * @iter: a #ClutterActorIter
18737 *
18738 * Checks whether a #ClutterActorIter is still valid.
18739 *
18740 * An iterator is considered valid if it has been initialized, and
18741 * if the #ClutterActor that it refers to hasn't been modified after
18742 * the initialization.
18743 *
18744 * Return value: %TRUE if the iterator is valid, and %FALSE otherwise
18745 *
18746 * Since: 1.12
18747 */
18748 gboolean
clutter_actor_iter_is_valid(const ClutterActorIter * iter)18749 clutter_actor_iter_is_valid (const ClutterActorIter *iter)
18750 {
18751 RealActorIter *ri = (RealActorIter *) iter;
18752
18753 g_return_val_if_fail (iter != NULL, FALSE);
18754
18755 if (ri->root == NULL)
18756 return FALSE;
18757
18758 return ri->root->priv->age == ri->age;
18759 }
18760
18761 /**
18762 * clutter_actor_iter_next:
18763 * @iter: a #ClutterActorIter
18764 * @child: (out) (transfer none): return location for a #ClutterActor
18765 *
18766 * Advances the @iter and retrieves the next child of the root #ClutterActor
18767 * that was used to initialize the #ClutterActorIterator.
18768 *
18769 * If the iterator can advance, this function returns %TRUE and sets the
18770 * @child argument.
18771 *
18772 * If the iterator cannot advance, this function returns %FALSE, and
18773 * the contents of @child are undefined.
18774 *
18775 * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
18776 *
18777 * Since: 1.10
18778 */
18779 gboolean
clutter_actor_iter_next(ClutterActorIter * iter,ClutterActor ** child)18780 clutter_actor_iter_next (ClutterActorIter *iter,
18781 ClutterActor **child)
18782 {
18783 RealActorIter *ri = (RealActorIter *) iter;
18784
18785 g_return_val_if_fail (iter != NULL, FALSE);
18786 g_return_val_if_fail (ri->root != NULL, FALSE);
18787 #ifndef G_DISABLE_ASSERT
18788 g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
18789 #endif
18790
18791 if (ri->current == NULL)
18792 ri->current = ri->root->priv->first_child;
18793 else
18794 ri->current = ri->current->priv->next_sibling;
18795
18796 if (child != NULL)
18797 *child = ri->current;
18798
18799 return ri->current != NULL;
18800 }
18801
18802 /**
18803 * clutter_actor_iter_prev:
18804 * @iter: a #ClutterActorIter
18805 * @child: (out) (transfer none): return location for a #ClutterActor
18806 *
18807 * Advances the @iter and retrieves the previous child of the root
18808 * #ClutterActor that was used to initialize the #ClutterActorIterator.
18809 *
18810 * If the iterator can advance, this function returns %TRUE and sets the
18811 * @child argument.
18812 *
18813 * If the iterator cannot advance, this function returns %FALSE, and
18814 * the contents of @child are undefined.
18815 *
18816 * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
18817 *
18818 * Since: 1.10
18819 */
18820 gboolean
clutter_actor_iter_prev(ClutterActorIter * iter,ClutterActor ** child)18821 clutter_actor_iter_prev (ClutterActorIter *iter,
18822 ClutterActor **child)
18823 {
18824 RealActorIter *ri = (RealActorIter *) iter;
18825
18826 g_return_val_if_fail (iter != NULL, FALSE);
18827 g_return_val_if_fail (ri->root != NULL, FALSE);
18828 #ifndef G_DISABLE_ASSERT
18829 g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
18830 #endif
18831
18832 if (ri->current == NULL)
18833 ri->current = ri->root->priv->last_child;
18834 else
18835 ri->current = ri->current->priv->prev_sibling;
18836
18837 if (child != NULL)
18838 *child = ri->current;
18839
18840 return ri->current != NULL;
18841 }
18842
18843 /**
18844 * clutter_actor_iter_remove:
18845 * @iter: a #ClutterActorIter
18846 *
18847 * Safely removes the #ClutterActor currently pointer to by the iterator
18848 * from its parent.
18849 *
18850 * This function can only be called after clutter_actor_iter_next() or
18851 * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
18852 * than once for the same actor.
18853 *
18854 * This function will call clutter_actor_remove_child() internally.
18855 *
18856 * Since: 1.10
18857 */
18858 void
clutter_actor_iter_remove(ClutterActorIter * iter)18859 clutter_actor_iter_remove (ClutterActorIter *iter)
18860 {
18861 RealActorIter *ri = (RealActorIter *) iter;
18862 ClutterActor *cur;
18863
18864 g_return_if_fail (iter != NULL);
18865 g_return_if_fail (ri->root != NULL);
18866 #ifndef G_DISABLE_ASSERT
18867 g_return_if_fail (ri->age == ri->root->priv->age);
18868 #endif
18869 g_return_if_fail (ri->current != NULL);
18870
18871 cur = ri->current;
18872
18873 if (cur != NULL)
18874 {
18875 ri->current = cur->priv->prev_sibling;
18876
18877 clutter_actor_remove_child_internal (ri->root, cur,
18878 REMOVE_CHILD_DEFAULT_FLAGS);
18879
18880 ri->age += 1;
18881 }
18882 }
18883
18884 /**
18885 * clutter_actor_iter_destroy:
18886 * @iter: a #ClutterActorIter
18887 *
18888 * Safely destroys the #ClutterActor currently pointer to by the iterator
18889 * from its parent.
18890 *
18891 * This function can only be called after clutter_actor_iter_next() or
18892 * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
18893 * than once for the same actor.
18894 *
18895 * This function will call clutter_actor_destroy() internally.
18896 *
18897 * Since: 1.10
18898 */
18899 void
clutter_actor_iter_destroy(ClutterActorIter * iter)18900 clutter_actor_iter_destroy (ClutterActorIter *iter)
18901 {
18902 RealActorIter *ri = (RealActorIter *) iter;
18903 ClutterActor *cur;
18904
18905 g_return_if_fail (iter != NULL);
18906 g_return_if_fail (ri->root != NULL);
18907 #ifndef G_DISABLE_ASSERT
18908 g_return_if_fail (ri->age == ri->root->priv->age);
18909 #endif
18910 g_return_if_fail (ri->current != NULL);
18911
18912 cur = ri->current;
18913
18914 if (cur != NULL)
18915 {
18916 ri->current = cur->priv->prev_sibling;
18917
18918 clutter_actor_destroy (cur);
18919
18920 ri->age += 1;
18921 }
18922 }
18923
18924 static const ClutterAnimationInfo default_animation_info = {
18925 NULL, /* transitions */
18926 NULL, /* states */
18927 NULL, /* cur_state */
18928 };
18929
18930 static void
clutter_animation_info_free(gpointer data)18931 clutter_animation_info_free (gpointer data)
18932 {
18933 if (data != NULL)
18934 {
18935 ClutterAnimationInfo *info = data;
18936
18937 if (info->transitions != NULL)
18938 g_hash_table_unref (info->transitions);
18939
18940 if (info->states != NULL)
18941 g_array_unref (info->states);
18942
18943 g_slice_free (ClutterAnimationInfo, info);
18944 }
18945 }
18946
18947 const ClutterAnimationInfo *
_clutter_actor_get_animation_info_or_defaults(ClutterActor * self)18948 _clutter_actor_get_animation_info_or_defaults (ClutterActor *self)
18949 {
18950 const ClutterAnimationInfo *res;
18951 GObject *obj = G_OBJECT (self);
18952
18953 res = g_object_get_qdata (obj, quark_actor_animation_info);
18954 if (res != NULL)
18955 return res;
18956
18957 return &default_animation_info;
18958 }
18959
18960 ClutterAnimationInfo *
_clutter_actor_get_animation_info(ClutterActor * self)18961 _clutter_actor_get_animation_info (ClutterActor *self)
18962 {
18963 GObject *obj = G_OBJECT (self);
18964 ClutterAnimationInfo *res;
18965
18966 res = g_object_get_qdata (obj, quark_actor_animation_info);
18967 if (res == NULL)
18968 {
18969 res = g_slice_new (ClutterAnimationInfo);
18970
18971 *res = default_animation_info;
18972
18973 g_object_set_qdata_full (obj, quark_actor_animation_info,
18974 res,
18975 clutter_animation_info_free);
18976 }
18977
18978 return res;
18979 }
18980
18981 ClutterTransition *
_clutter_actor_get_transition(ClutterActor * actor,GParamSpec * pspec)18982 _clutter_actor_get_transition (ClutterActor *actor,
18983 GParamSpec *pspec)
18984 {
18985 const ClutterAnimationInfo *info;
18986
18987 info = _clutter_actor_get_animation_info_or_defaults (actor);
18988
18989 if (info->transitions == NULL)
18990 return NULL;
18991
18992 return g_hash_table_lookup (info->transitions, pspec->name);
18993 }
18994
18995 static void
transition_closure_free(gpointer data)18996 transition_closure_free (gpointer data)
18997 {
18998 if (G_LIKELY (data != NULL))
18999 {
19000 TransitionClosure *clos = data;
19001 ClutterTimeline *timeline;
19002
19003 timeline = CLUTTER_TIMELINE (clos->transition);
19004
19005 /* we disconnect the signal handler before stopping the timeline,
19006 * so that we don't end up inside on_transition_stopped() from
19007 * a call to g_hash_table_remove().
19008 */
19009 g_signal_handler_disconnect (clos->transition, clos->completed_id);
19010
19011 if (clutter_timeline_is_playing (timeline))
19012 clutter_timeline_stop (timeline);
19013
19014 /* remove the reference added in add_transition_internal() */
19015 g_object_unref (clos->transition);
19016
19017 g_free (clos->name);
19018
19019 g_slice_free (TransitionClosure, clos);
19020 }
19021 }
19022
19023 static void
on_transition_stopped(ClutterTransition * transition,gboolean is_finished,TransitionClosure * clos)19024 on_transition_stopped (ClutterTransition *transition,
19025 gboolean is_finished,
19026 TransitionClosure *clos)
19027 {
19028 ClutterActor *actor = clos->actor;
19029 ClutterAnimationInfo *info;
19030 GQuark t_quark;
19031 gchar *t_name;
19032
19033 if (clos->name == NULL)
19034 return;
19035
19036 /* reset the caches used by animations */
19037 clutter_actor_store_content_box (actor, NULL);
19038
19039 info = _clutter_actor_get_animation_info (actor);
19040
19041 /* we need copies because we emit the signal after the
19042 * TransitionClosure data structure has been freed
19043 */
19044 t_quark = g_quark_from_string (clos->name);
19045 t_name = g_strdup (clos->name);
19046
19047 if (clutter_transition_get_remove_on_complete (transition))
19048 {
19049 /* this is safe, because the timeline has now stopped,
19050 * so we won't recurse; the reference on the Animatable
19051 * will be dropped by the ::stopped signal closure in
19052 * ClutterTransition, which is RUN_LAST, and thus will
19053 * be called after this handler
19054 */
19055 g_hash_table_remove (info->transitions, clos->name);
19056 }
19057
19058 /* we emit the ::transition-stopped after removing the
19059 * transition, so that we can chain up new transitions
19060 * without interfering with the one that just finished
19061 */
19062 g_signal_emit (actor, actor_signals[TRANSITION_STOPPED], t_quark,
19063 t_name,
19064 is_finished);
19065
19066 g_free (t_name);
19067
19068 /* if it's the last transition then we clean up */
19069 if (g_hash_table_size (info->transitions) == 0)
19070 {
19071 g_hash_table_unref (info->transitions);
19072 info->transitions = NULL;
19073
19074 CLUTTER_NOTE (ANIMATION, "Transitions for '%s' completed",
19075 _clutter_actor_get_debug_name (actor));
19076
19077 g_signal_emit (actor, actor_signals[TRANSITIONS_COMPLETED], 0);
19078 }
19079 }
19080
19081 static void
clutter_actor_add_transition_internal(ClutterActor * self,const gchar * name,ClutterTransition * transition)19082 clutter_actor_add_transition_internal (ClutterActor *self,
19083 const gchar *name,
19084 ClutterTransition *transition)
19085 {
19086 ClutterTimeline *timeline;
19087 TransitionClosure *clos;
19088 ClutterAnimationInfo *info;
19089
19090 info = _clutter_actor_get_animation_info (self);
19091
19092 if (info->transitions == NULL)
19093 info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
19094 NULL,
19095 transition_closure_free);
19096
19097 if (g_hash_table_lookup (info->transitions, name) != NULL)
19098 {
19099 g_warning ("A transition with name '%s' already exists for "
19100 "the actor '%s'",
19101 name,
19102 _clutter_actor_get_debug_name (self));
19103 return;
19104 }
19105
19106 clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (self));
19107
19108 timeline = CLUTTER_TIMELINE (transition);
19109
19110 clos = g_slice_new (TransitionClosure);
19111 clos->actor = self;
19112 clos->transition = g_object_ref (transition);
19113 clos->name = g_strdup (name);
19114 clos->completed_id = g_signal_connect (timeline, "stopped",
19115 G_CALLBACK (on_transition_stopped),
19116 clos);
19117
19118 CLUTTER_NOTE (ANIMATION,
19119 "Adding transition '%s' [%p] to actor '%s'",
19120 clos->name,
19121 clos->transition,
19122 _clutter_actor_get_debug_name (self));
19123
19124 g_hash_table_insert (info->transitions, clos->name, clos);
19125 clutter_timeline_start (timeline);
19126 }
19127
19128 static gboolean
should_skip_implicit_transition(ClutterActor * self,GParamSpec * pspec)19129 should_skip_implicit_transition (ClutterActor *self,
19130 GParamSpec *pspec)
19131 {
19132 ClutterActorPrivate *priv = self->priv;
19133 const ClutterAnimationInfo *info;
19134
19135 /* this function is called from _clutter_actor_create_transition() which
19136 * calls _clutter_actor_get_animation_info() first, so we're guaranteed
19137 * to have the correct ClutterAnimationInfo pointer
19138 */
19139 info = _clutter_actor_get_animation_info_or_defaults (self);
19140
19141 /* if the easing state has a non-zero duration we always want an
19142 * implicit transition to occur
19143 */
19144 if (info->cur_state->easing_duration == 0)
19145 return TRUE;
19146
19147 /* on the other hand, if the actor hasn't been allocated yet, we want to
19148 * skip all transitions on the :allocation, to avoid actors "flying in"
19149 * into their new position and size
19150 */
19151 if (pspec == obj_props[PROP_ALLOCATION] && priv->needs_allocation)
19152 return TRUE;
19153
19154 /* if the actor is not mapped and is not part of a branch of the scene
19155 * graph that is being cloned, then we always skip implicit transitions
19156 * on the account of the fact that the actor is not going to be visible
19157 * when those transitions happen
19158 */
19159 if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
19160 priv->in_cloned_branch == 0 &&
19161 !clutter_actor_has_mapped_clones (self))
19162 return TRUE;
19163
19164 return FALSE;
19165 }
19166
19167 /*< private >*
19168 * _clutter_actor_create_transition:
19169 * @actor: a #ClutterActor
19170 * @pspec: the property used for the transition
19171 * @...: initial and final state
19172 *
19173 * Creates a #ClutterTransition for the property represented by @pspec.
19174 *
19175 * Return value: a #ClutterTransition
19176 */
19177 ClutterTransition *
_clutter_actor_create_transition(ClutterActor * actor,GParamSpec * pspec,...)19178 _clutter_actor_create_transition (ClutterActor *actor,
19179 GParamSpec *pspec,
19180 ...)
19181 {
19182 ClutterTimeline *timeline;
19183 ClutterInterval *interval;
19184 ClutterAnimationInfo *info;
19185 ClutterTransition *res = NULL;
19186 gboolean call_restore = FALSE;
19187 TransitionClosure *clos;
19188 va_list var_args;
19189 GValue initial = G_VALUE_INIT;
19190 GValue final = G_VALUE_INIT;
19191 GType ptype;
19192 char *error;
19193
19194 g_assert (pspec != NULL);
19195 g_assert ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0);
19196
19197 info = _clutter_actor_get_animation_info (actor);
19198
19199 /* XXX - this will go away in 2.0
19200 *
19201 * if no state has been pushed, we assume that the easing state is
19202 * in "compatibility mode": all transitions have a duration of 0
19203 * msecs, which means that they happen immediately. in Clutter 2.0
19204 * this will turn into a g_assert(info->states != NULL), as every
19205 * actor will start with a predefined easing state
19206 */
19207 if (info->states == NULL)
19208 {
19209 clutter_actor_save_easing_state (actor);
19210 clutter_actor_set_easing_duration (actor, 0);
19211 call_restore = TRUE;
19212 }
19213
19214 if (info->transitions == NULL)
19215 info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
19216 NULL,
19217 transition_closure_free);
19218
19219 va_start (var_args, pspec);
19220
19221 ptype = G_PARAM_SPEC_VALUE_TYPE (pspec);
19222
19223 G_VALUE_COLLECT_INIT (&initial, ptype,
19224 var_args, 0,
19225 &error);
19226 if (error != NULL)
19227 {
19228 g_critical ("%s: %s", G_STRLOC, error);
19229 g_free (error);
19230 goto out;
19231 }
19232
19233 G_VALUE_COLLECT_INIT (&final, ptype,
19234 var_args, 0,
19235 &error);
19236 if (error != NULL)
19237 {
19238 g_critical ("%s: %s", G_STRLOC, error);
19239 g_value_unset (&initial);
19240 g_free (error);
19241 goto out;
19242 }
19243
19244 if (should_skip_implicit_transition (actor, pspec))
19245 {
19246 CLUTTER_NOTE (ANIMATION, "Skipping implicit transition for '%s::%s'",
19247 _clutter_actor_get_debug_name (actor),
19248 pspec->name);
19249
19250 /* remove a transition, if one exists */
19251 clutter_actor_remove_transition (actor, pspec->name);
19252
19253 /* we don't go through the Animatable interface because we
19254 * already know we got here through an animatable property.
19255 */
19256 clutter_actor_set_animatable_property (actor,
19257 pspec->param_id,
19258 &final,
19259 pspec);
19260
19261 g_value_unset (&initial);
19262 g_value_unset (&final);
19263
19264 goto out;
19265 }
19266
19267 clos = g_hash_table_lookup (info->transitions, pspec->name);
19268 if (clos == NULL)
19269 {
19270 res = clutter_property_transition_new (pspec->name);
19271
19272 clutter_transition_set_remove_on_complete (res, TRUE);
19273
19274 interval = clutter_interval_new_with_values (ptype, &initial, &final);
19275 clutter_transition_set_interval (res, interval);
19276
19277 timeline = CLUTTER_TIMELINE (res);
19278 clutter_timeline_set_delay (timeline, info->cur_state->easing_delay);
19279 clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
19280 clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
19281
19282 #ifdef CLUTTER_ENABLE_DEBUG
19283 if (CLUTTER_HAS_DEBUG (ANIMATION))
19284 {
19285 gchar *initial_v, *final_v;
19286
19287 initial_v = g_strdup_value_contents (&initial);
19288 final_v = g_strdup_value_contents (&final);
19289
19290 CLUTTER_NOTE (ANIMATION,
19291 "Created transition for %s:%s "
19292 "(len:%u, mode:%s, delay:%u) "
19293 "initial:%s, final:%s",
19294 _clutter_actor_get_debug_name (actor),
19295 pspec->name,
19296 info->cur_state->easing_duration,
19297 clutter_get_easing_name_for_mode (info->cur_state->easing_mode),
19298 info->cur_state->easing_delay,
19299 initial_v, final_v);
19300
19301 g_free (initial_v);
19302 g_free (final_v);
19303 }
19304 #endif /* CLUTTER_ENABLE_DEBUG */
19305
19306 /* this will start the transition as well */
19307 clutter_actor_add_transition_internal (actor, pspec->name, res);
19308
19309 /* the actor now owns the transition */
19310 g_object_unref (res);
19311
19312 g_value_unset (&initial);
19313 g_value_unset (&final);
19314 }
19315 else
19316 {
19317 ClutterAnimationMode cur_mode;
19318 guint cur_duration;
19319
19320 CLUTTER_NOTE (ANIMATION, "Existing transition for %s:%s",
19321 _clutter_actor_get_debug_name (actor),
19322 pspec->name);
19323
19324 timeline = CLUTTER_TIMELINE (clos->transition);
19325
19326 cur_duration = clutter_timeline_get_duration (timeline);
19327 if (cur_duration != info->cur_state->easing_duration)
19328 clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
19329
19330 cur_mode = clutter_timeline_get_progress_mode (timeline);
19331 if (cur_mode != info->cur_state->easing_mode)
19332 clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
19333
19334 clutter_timeline_rewind (timeline);
19335
19336 interval = clutter_transition_get_interval (clos->transition);
19337 clutter_interval_set_initial_value (interval, &initial);
19338 clutter_interval_set_final_value (interval, &final);
19339
19340 res = clos->transition;
19341 }
19342
19343 out:
19344 if (call_restore)
19345 clutter_actor_restore_easing_state (actor);
19346
19347 va_end (var_args);
19348
19349 return res;
19350 }
19351
19352 /**
19353 * clutter_actor_add_transition:
19354 * @self: a #ClutterActor
19355 * @name: the name of the transition to add
19356 * @transition: the #ClutterTransition to add
19357 *
19358 * Adds a @transition to the #ClutterActor's list of animations.
19359 *
19360 * The @name string is a per-actor unique identifier of the @transition: only
19361 * one #ClutterTransition can be associated to the specified @name.
19362 *
19363 * The @transition will be started once added.
19364 *
19365 * This function will take a reference on the @transition.
19366 *
19367 * This function is usually called implicitly when modifying an animatable
19368 * property.
19369 *
19370 * Since: 1.10
19371 */
19372 void
clutter_actor_add_transition(ClutterActor * self,const char * name,ClutterTransition * transition)19373 clutter_actor_add_transition (ClutterActor *self,
19374 const char *name,
19375 ClutterTransition *transition)
19376 {
19377 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19378 g_return_if_fail (name != NULL);
19379 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
19380
19381 clutter_actor_add_transition_internal (self, name, transition);
19382 }
19383
19384 /**
19385 * clutter_actor_remove_transition:
19386 * @self: a #ClutterActor
19387 * @name: the name of the transition to remove
19388 *
19389 * Removes the transition stored inside a #ClutterActor using @name
19390 * identifier.
19391 *
19392 * If the transition is currently in progress, it will be stopped.
19393 *
19394 * This function releases the reference acquired when the transition
19395 * was added to the #ClutterActor.
19396 *
19397 * Since: 1.10
19398 */
19399 void
clutter_actor_remove_transition(ClutterActor * self,const char * name)19400 clutter_actor_remove_transition (ClutterActor *self,
19401 const char *name)
19402 {
19403 const ClutterAnimationInfo *info;
19404 TransitionClosure *clos;
19405 gboolean was_playing;
19406 GQuark t_quark;
19407 gchar *t_name;
19408
19409 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19410 g_return_if_fail (name != NULL);
19411
19412 info = _clutter_actor_get_animation_info_or_defaults (self);
19413
19414 if (info->transitions == NULL)
19415 return;
19416
19417 clos = g_hash_table_lookup (info->transitions, name);
19418 if (clos == NULL)
19419 return;
19420
19421 was_playing =
19422 clutter_timeline_is_playing (CLUTTER_TIMELINE (clos->transition));
19423 t_quark = g_quark_from_string (clos->name);
19424 t_name = g_strdup (clos->name);
19425
19426 g_hash_table_remove (info->transitions, name);
19427
19428 /* we want to maintain the invariant that ::transition-stopped is
19429 * emitted after the transition has been removed, to allow replacing
19430 * or chaining; removing the transition from the hash table will
19431 * stop it, but transition_closure_free() will disconnect the signal
19432 * handler we install in add_transition_internal(), to avoid loops
19433 * or segfaults.
19434 *
19435 * since we know already that a transition will stop once it's removed
19436 * from an actor, we can simply emit the ::transition-stopped here
19437 * ourselves, if the timeline was playing (if it wasn't, then the
19438 * signal was already emitted at least once).
19439 */
19440 if (was_playing)
19441 {
19442 g_signal_emit (self, actor_signals[TRANSITION_STOPPED],
19443 t_quark,
19444 t_name,
19445 FALSE);
19446 }
19447
19448 g_free (t_name);
19449 }
19450
19451 /**
19452 * clutter_actor_remove_all_transitions:
19453 * @self: a #ClutterActor
19454 *
19455 * Removes all transitions associated to @self.
19456 *
19457 * Since: 1.10
19458 */
19459 void
clutter_actor_remove_all_transitions(ClutterActor * self)19460 clutter_actor_remove_all_transitions (ClutterActor *self)
19461 {
19462 const ClutterAnimationInfo *info;
19463
19464 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19465
19466 info = _clutter_actor_get_animation_info_or_defaults (self);
19467 if (info->transitions == NULL)
19468 return;
19469
19470 g_hash_table_remove_all (info->transitions);
19471 }
19472
19473 /**
19474 * clutter_actor_set_easing_duration:
19475 * @self: a #ClutterActor
19476 * @msecs: the duration of the easing, or %NULL
19477 *
19478 * Sets the duration of the tweening for animatable properties
19479 * of @self for the current easing state.
19480 *
19481 * Since: 1.10
19482 */
19483 void
clutter_actor_set_easing_duration(ClutterActor * self,guint msecs)19484 clutter_actor_set_easing_duration (ClutterActor *self,
19485 guint msecs)
19486 {
19487 ClutterAnimationInfo *info;
19488
19489 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19490
19491 info = _clutter_actor_get_animation_info (self);
19492
19493 if (info->cur_state == NULL)
19494 {
19495 g_warning ("You must call clutter_actor_save_easing_state() prior "
19496 "to calling clutter_actor_set_easing_duration().");
19497 return;
19498 }
19499
19500 if (info->cur_state->easing_duration != msecs)
19501 info->cur_state->easing_duration = msecs;
19502 }
19503
19504 /**
19505 * clutter_actor_get_easing_duration:
19506 * @self: a #ClutterActor
19507 *
19508 * Retrieves the duration of the tweening for animatable
19509 * properties of @self for the current easing state.
19510 *
19511 * Return value: the duration of the tweening, in milliseconds
19512 *
19513 * Since: 1.10
19514 */
19515 guint
clutter_actor_get_easing_duration(ClutterActor * self)19516 clutter_actor_get_easing_duration (ClutterActor *self)
19517 {
19518 const ClutterAnimationInfo *info;
19519
19520 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
19521
19522 info = _clutter_actor_get_animation_info_or_defaults (self);
19523
19524 if (info->cur_state != NULL)
19525 return info->cur_state->easing_duration;
19526
19527 return 0;
19528 }
19529
19530 /**
19531 * clutter_actor_set_easing_mode:
19532 * @self: a #ClutterActor
19533 * @mode: an easing mode, excluding %CLUTTER_CUSTOM_MODE
19534 *
19535 * Sets the easing mode for the tweening of animatable properties
19536 * of @self.
19537 *
19538 * Since: 1.10
19539 */
19540 void
clutter_actor_set_easing_mode(ClutterActor * self,ClutterAnimationMode mode)19541 clutter_actor_set_easing_mode (ClutterActor *self,
19542 ClutterAnimationMode mode)
19543 {
19544 ClutterAnimationInfo *info;
19545
19546 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19547 g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
19548 g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
19549
19550 info = _clutter_actor_get_animation_info (self);
19551
19552 if (info->cur_state == NULL)
19553 {
19554 g_warning ("You must call clutter_actor_save_easing_state() prior "
19555 "to calling clutter_actor_set_easing_mode().");
19556 return;
19557 }
19558
19559 if (info->cur_state->easing_mode != mode)
19560 info->cur_state->easing_mode = mode;
19561 }
19562
19563 /**
19564 * clutter_actor_get_easing_mode:
19565 * @self: a #ClutterActor
19566 *
19567 * Retrieves the easing mode for the tweening of animatable properties
19568 * of @self for the current easing state.
19569 *
19570 * Return value: an easing mode
19571 *
19572 * Since: 1.10
19573 */
19574 ClutterAnimationMode
clutter_actor_get_easing_mode(ClutterActor * self)19575 clutter_actor_get_easing_mode (ClutterActor *self)
19576 {
19577 const ClutterAnimationInfo *info;
19578
19579 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_EASE_OUT_CUBIC);
19580
19581 info = _clutter_actor_get_animation_info_or_defaults (self);
19582
19583 if (info->cur_state != NULL)
19584 return info->cur_state->easing_mode;
19585
19586 return CLUTTER_EASE_OUT_CUBIC;
19587 }
19588
19589 /**
19590 * clutter_actor_set_easing_delay:
19591 * @self: a #ClutterActor
19592 * @msecs: the delay before the start of the tweening, in milliseconds
19593 *
19594 * Sets the delay that should be applied before tweening animatable
19595 * properties.
19596 *
19597 * Since: 1.10
19598 */
19599 void
clutter_actor_set_easing_delay(ClutterActor * self,guint msecs)19600 clutter_actor_set_easing_delay (ClutterActor *self,
19601 guint msecs)
19602 {
19603 ClutterAnimationInfo *info;
19604
19605 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19606
19607 info = _clutter_actor_get_animation_info (self);
19608
19609 if (info->cur_state == NULL)
19610 {
19611 g_warning ("You must call clutter_actor_save_easing_state() prior "
19612 "to calling clutter_actor_set_easing_delay().");
19613 return;
19614 }
19615
19616 if (info->cur_state->easing_delay != msecs)
19617 info->cur_state->easing_delay = msecs;
19618 }
19619
19620 /**
19621 * clutter_actor_get_easing_delay:
19622 * @self: a #ClutterActor
19623 *
19624 * Retrieves the delay that should be applied when tweening animatable
19625 * properties.
19626 *
19627 * Return value: a delay, in milliseconds
19628 *
19629 * Since: 1.10
19630 */
19631 guint
clutter_actor_get_easing_delay(ClutterActor * self)19632 clutter_actor_get_easing_delay (ClutterActor *self)
19633 {
19634 const ClutterAnimationInfo *info;
19635
19636 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
19637
19638 info = _clutter_actor_get_animation_info_or_defaults (self);
19639
19640 if (info->cur_state != NULL)
19641 return info->cur_state->easing_delay;
19642
19643 return 0;
19644 }
19645
19646 /**
19647 * clutter_actor_get_transition:
19648 * @self: a #ClutterActor
19649 * @name: the name of the transition
19650 *
19651 * Retrieves the #ClutterTransition of a #ClutterActor by using the
19652 * transition @name.
19653 *
19654 * Transitions created for animatable properties use the name of the
19655 * property itself, for instance the code below:
19656 *
19657 * |[<!-- language="C" -->
19658 * clutter_actor_set_easing_duration (actor, 1000);
19659 * clutter_actor_set_rotation (actor, CLUTTER_Y_AXIS, 360.0, x, y, z);
19660 *
19661 * transition = clutter_actor_get_transition (actor, "rotation-angle-y");
19662 * g_signal_connect (transition, "stopped",
19663 * G_CALLBACK (on_transition_stopped),
19664 * actor);
19665 * ]|
19666 *
19667 * will call the `on_transition_stopped` callback when the transition
19668 * is finished.
19669 *
19670 * If you just want to get notifications of the completion of a transition,
19671 * you should use the #ClutterActor::transition-stopped signal, using the
19672 * transition name as the signal detail.
19673 *
19674 * Return value: (transfer none): a #ClutterTransition, or %NULL is none
19675 * was found to match the passed name; the returned instance is owned
19676 * by Clutter and it should not be freed
19677 *
19678 * Since: 1.10
19679 */
19680 ClutterTransition *
clutter_actor_get_transition(ClutterActor * self,const char * name)19681 clutter_actor_get_transition (ClutterActor *self,
19682 const char *name)
19683 {
19684 TransitionClosure *clos;
19685 const ClutterAnimationInfo *info;
19686
19687 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
19688 g_return_val_if_fail (name != NULL, NULL);
19689
19690 info = _clutter_actor_get_animation_info_or_defaults (self);
19691 if (info->transitions == NULL)
19692 return NULL;
19693
19694 clos = g_hash_table_lookup (info->transitions, name);
19695 if (clos == NULL)
19696 return NULL;
19697
19698 return clos->transition;
19699 }
19700
19701 /**
19702 * clutter_actor_save_easing_state:
19703 * @self: a #ClutterActor
19704 *
19705 * Saves the current easing state for animatable properties, and creates
19706 * a new state with the default values for easing mode and duration.
19707 *
19708 * New transitions created after calling this function will inherit the
19709 * duration, easing mode, and delay of the new easing state; this also
19710 * applies to transitions modified in flight.
19711 *
19712 * Since: 1.10
19713 */
19714 void
clutter_actor_save_easing_state(ClutterActor * self)19715 clutter_actor_save_easing_state (ClutterActor *self)
19716 {
19717 ClutterAnimationInfo *info;
19718 AState new_state;
19719
19720 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19721
19722 info = _clutter_actor_get_animation_info (self);
19723
19724 if (info->states == NULL)
19725 info->states = g_array_new (FALSE, FALSE, sizeof (AState));
19726
19727 new_state.easing_mode = CLUTTER_EASE_OUT_CUBIC;
19728 new_state.easing_duration = 250;
19729 new_state.easing_delay = 0;
19730
19731 g_array_append_val (info->states, new_state);
19732
19733 info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
19734 }
19735
19736 /**
19737 * clutter_actor_restore_easing_state:
19738 * @self: a #ClutterActor
19739 *
19740 * Restores the easing state as it was prior to a call to
19741 * clutter_actor_save_easing_state().
19742 *
19743 * Since: 1.10
19744 */
19745 void
clutter_actor_restore_easing_state(ClutterActor * self)19746 clutter_actor_restore_easing_state (ClutterActor *self)
19747 {
19748 ClutterAnimationInfo *info;
19749
19750 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19751
19752 info = _clutter_actor_get_animation_info (self);
19753
19754 if (info->states == NULL)
19755 {
19756 g_critical ("The function clutter_actor_restore_easing_state() has "
19757 "been called without a previous call to "
19758 "clutter_actor_save_easing_state().");
19759 return;
19760 }
19761
19762 g_array_remove_index (info->states, info->states->len - 1);
19763
19764 if (info->states->len > 0)
19765 info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
19766 else
19767 {
19768 g_array_unref (info->states);
19769 info->states = NULL;
19770 info->cur_state = NULL;
19771 }
19772 }
19773
19774 /**
19775 * clutter_actor_set_content:
19776 * @self: a #ClutterActor
19777 * @content: (allow-none): a #ClutterContent, or %NULL
19778 *
19779 * Sets the contents of a #ClutterActor.
19780 *
19781 * Since: 1.10
19782 */
19783 void
clutter_actor_set_content(ClutterActor * self,ClutterContent * content)19784 clutter_actor_set_content (ClutterActor *self,
19785 ClutterContent *content)
19786 {
19787 ClutterActorPrivate *priv;
19788
19789 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19790 g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content));
19791
19792 priv = self->priv;
19793
19794 if (priv->content == content)
19795 return;
19796
19797 if (priv->content != NULL)
19798 {
19799 _clutter_content_detached (priv->content, self);
19800 g_clear_object (&priv->content);
19801 }
19802
19803 priv->content = content;
19804
19805 if (priv->content != NULL)
19806 {
19807 g_object_ref (priv->content);
19808 _clutter_content_attached (priv->content, self);
19809 }
19810
19811 /* if the actor's preferred size is the content's preferred size,
19812 * then we need to conditionally queue a relayout here...
19813 */
19814 if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
19815 _clutter_actor_queue_only_relayout (self);
19816
19817 clutter_actor_queue_redraw (self);
19818
19819 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]);
19820
19821 /* if the content gravity is not resize-fill, and the new content has a
19822 * different preferred size than the previous one, then the content box
19823 * may have been changed. since we compute that lazily, we just notify
19824 * here, and let whomever watches :content-box do whatever they need to
19825 * do.
19826 */
19827 if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
19828 {
19829 if (priv->content_box_valid)
19830 {
19831 ClutterActorBox from_box, to_box;
19832
19833 clutter_actor_get_content_box (self, &from_box);
19834
19835 /* invalidate the cached content box */
19836 priv->content_box_valid = FALSE;
19837 clutter_actor_get_content_box (self, &to_box);
19838
19839 if (!clutter_actor_box_equal (&from_box, &to_box))
19840 _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
19841 &from_box,
19842 &to_box);
19843 }
19844
19845 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
19846 }
19847 }
19848
19849 /**
19850 * clutter_actor_get_content:
19851 * @self: a #ClutterActor
19852 *
19853 * Retrieves the contents of @self.
19854 *
19855 * Return value: (transfer none): a pointer to the #ClutterContent instance,
19856 * or %NULL if none was set
19857 *
19858 * Since: 1.10
19859 */
19860 ClutterContent *
clutter_actor_get_content(ClutterActor * self)19861 clutter_actor_get_content (ClutterActor *self)
19862 {
19863 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
19864
19865 return self->priv->content;
19866 }
19867
19868 /**
19869 * clutter_actor_set_content_gravity:
19870 * @self: a #ClutterActor
19871 * @gravity: the #ClutterContentGravity
19872 *
19873 * Sets the gravity of the #ClutterContent used by @self.
19874 *
19875 * See the description of the #ClutterActor:content-gravity property for
19876 * more information.
19877 *
19878 * The #ClutterActor:content-gravity property is animatable.
19879 *
19880 * Since: 1.10
19881 */
19882 void
clutter_actor_set_content_gravity(ClutterActor * self,ClutterContentGravity gravity)19883 clutter_actor_set_content_gravity (ClutterActor *self,
19884 ClutterContentGravity gravity)
19885 {
19886 ClutterActorPrivate *priv;
19887 ClutterActorBox from_box, to_box;
19888
19889 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19890
19891 priv = self->priv;
19892
19893 if (priv->content_gravity == gravity)
19894 return;
19895
19896 priv->content_box_valid = FALSE;
19897
19898 clutter_actor_get_content_box (self, &from_box);
19899
19900 priv->content_gravity = gravity;
19901
19902 clutter_actor_get_content_box (self, &to_box);
19903
19904 _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
19905 &from_box,
19906 &to_box);
19907
19908 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]);
19909 }
19910
19911 /**
19912 * clutter_actor_get_content_gravity:
19913 * @self: a #ClutterActor
19914 *
19915 * Retrieves the content gravity as set using
19916 * clutter_actor_set_content_gravity().
19917 *
19918 * Return value: the content gravity
19919 *
19920 * Since: 1.10
19921 */
19922 ClutterContentGravity
clutter_actor_get_content_gravity(ClutterActor * self)19923 clutter_actor_get_content_gravity (ClutterActor *self)
19924 {
19925 g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
19926 CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
19927
19928 return self->priv->content_gravity;
19929 }
19930
19931 /**
19932 * clutter_actor_get_content_box:
19933 * @self: a #ClutterActor
19934 * @box: (out caller-allocates): the return location for the bounding
19935 * box for the #ClutterContent
19936 *
19937 * Retrieves the bounding box for the #ClutterContent of @self.
19938 *
19939 * The bounding box is relative to the actor's allocation.
19940 *
19941 * If no #ClutterContent is set for @self, or if @self has not been
19942 * allocated yet, then the result is undefined.
19943 *
19944 * The content box is guaranteed to be, at most, as big as the allocation
19945 * of the #ClutterActor.
19946 *
19947 * If the #ClutterContent used by the actor has a preferred size, then
19948 * it is possible to modify the content box by using the
19949 * #ClutterActor:content-gravity property.
19950 *
19951 * Since: 1.10
19952 */
19953 void
clutter_actor_get_content_box(ClutterActor * self,ClutterActorBox * box)19954 clutter_actor_get_content_box (ClutterActor *self,
19955 ClutterActorBox *box)
19956 {
19957 ClutterActorPrivate *priv;
19958 gfloat content_w, content_h;
19959 gfloat alloc_w, alloc_h;
19960
19961 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19962 g_return_if_fail (box != NULL);
19963
19964 priv = self->priv;
19965
19966 box->x1 = 0.f;
19967 box->y1 = 0.f;
19968 box->x2 = priv->allocation.x2 - priv->allocation.x1;
19969 box->y2 = priv->allocation.y2 - priv->allocation.y1;
19970
19971 if (priv->content_box_valid)
19972 {
19973 *box = priv->content_box;
19974 return;
19975 }
19976
19977 /* no need to do any more work */
19978 if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
19979 return;
19980
19981 if (priv->content == NULL)
19982 return;
19983
19984 /* if the content does not have a preferred size then there is
19985 * no point in computing the content box
19986 */
19987 if (!clutter_content_get_preferred_size (priv->content,
19988 &content_w,
19989 &content_h))
19990 return;
19991
19992 alloc_w = box->x2;
19993 alloc_h = box->y2;
19994
19995 switch (priv->content_gravity)
19996 {
19997 case CLUTTER_CONTENT_GRAVITY_TOP_LEFT:
19998 box->x2 = box->x1 + MIN (content_w, alloc_w);
19999 box->y2 = box->y1 + MIN (content_h, alloc_h);
20000 break;
20001
20002 case CLUTTER_CONTENT_GRAVITY_TOP:
20003 if (alloc_w > content_w)
20004 {
20005 box->x1 += ceilf ((alloc_w - content_w) / 2.0);
20006 box->x2 = box->x1 + content_w;
20007 }
20008 box->y2 = box->y1 + MIN (content_h, alloc_h);
20009 break;
20010
20011 case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT:
20012 if (alloc_w > content_w)
20013 {
20014 box->x1 += (alloc_w - content_w);
20015 box->x2 = box->x1 + content_w;
20016 }
20017 box->y2 = box->y1 + MIN (content_h, alloc_h);
20018 break;
20019
20020 case CLUTTER_CONTENT_GRAVITY_LEFT:
20021 box->x2 = box->x1 + MIN (content_w, alloc_w);
20022 if (alloc_h > content_h)
20023 {
20024 box->y1 += ceilf ((alloc_h - content_h) / 2.0);
20025 box->y2 = box->y1 + content_h;
20026 }
20027 break;
20028
20029 case CLUTTER_CONTENT_GRAVITY_CENTER:
20030 if (alloc_w > content_w)
20031 {
20032 box->x1 += ceilf ((alloc_w - content_w) / 2.0);
20033 box->x2 = box->x1 + content_w;
20034 }
20035 if (alloc_h > content_h)
20036 {
20037 box->y1 += ceilf ((alloc_h - content_h) / 2.0);
20038 box->y2 = box->y1 + content_h;
20039 }
20040 break;
20041
20042 case CLUTTER_CONTENT_GRAVITY_RIGHT:
20043 if (alloc_w > content_w)
20044 {
20045 box->x1 += (alloc_w - content_w);
20046 box->x2 = box->x1 + content_w;
20047 }
20048 if (alloc_h > content_h)
20049 {
20050 box->y1 += ceilf ((alloc_h - content_h) / 2.0);
20051 box->y2 = box->y1 + content_h;
20052 }
20053 break;
20054
20055 case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT:
20056 box->x2 = box->x1 + MIN (content_w, alloc_w);
20057 if (alloc_h > content_h)
20058 {
20059 box->y1 += (alloc_h - content_h);
20060 box->y2 = box->y1 + content_h;
20061 }
20062 break;
20063
20064 case CLUTTER_CONTENT_GRAVITY_BOTTOM:
20065 if (alloc_w > content_w)
20066 {
20067 box->x1 += ceilf ((alloc_w - content_w) / 2.0);
20068 box->x2 = box->x1 + content_w;
20069 }
20070 if (alloc_h > content_h)
20071 {
20072 box->y1 += (alloc_h - content_h);
20073 box->y2 = box->y1 + content_h;
20074 }
20075 break;
20076
20077 case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT:
20078 if (alloc_w > content_w)
20079 {
20080 box->x1 += (alloc_w - content_w);
20081 box->x2 = box->x1 + content_w;
20082 }
20083 if (alloc_h > content_h)
20084 {
20085 box->y1 += (alloc_h - content_h);
20086 box->y2 = box->y1 + content_h;
20087 }
20088 break;
20089
20090 case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL:
20091 g_assert_not_reached ();
20092 break;
20093
20094 case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT:
20095 {
20096 double r_c = content_w / content_h;
20097
20098 if ((alloc_w / r_c) > alloc_h)
20099 {
20100 box->y1 = 0.f;
20101 box->y2 = alloc_h;
20102
20103 box->x1 = (alloc_w - (alloc_h * r_c)) / 2.0f;
20104 box->x2 = box->x1 + (alloc_h * r_c);
20105 }
20106 else
20107 {
20108 box->x1 = 0.f;
20109 box->x2 = alloc_w;
20110
20111 box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f;
20112 box->y2 = box->y1 + (alloc_w / r_c);
20113 }
20114
20115 CLUTTER_NOTE (LAYOUT,
20116 "r_c: %.3f, r_a: %.3f\t"
20117 "a: [%.2fx%.2f], c: [%.2fx%.2f]\t"
20118 "b: [%.2f, %.2f, %.2f, %.2f]",
20119 r_c, alloc_w / alloc_h,
20120 alloc_w, alloc_h,
20121 content_w, content_h,
20122 box->x1, box->y1, box->x2, box->y2);
20123 }
20124 break;
20125 }
20126 }
20127
20128 /**
20129 * clutter_actor_set_content_scaling_filters:
20130 * @self: a #ClutterActor
20131 * @min_filter: the minification filter for the content
20132 * @mag_filter: the magnification filter for the content
20133 *
20134 * Sets the minification and magnification filter to be applied when
20135 * scaling the #ClutterActor:content of a #ClutterActor.
20136 *
20137 * The #ClutterActor:minification-filter will be used when reducing
20138 * the size of the content; the #ClutterActor:magnification-filter
20139 * will be used when increasing the size of the content.
20140 *
20141 * Since: 1.10
20142 */
20143 void
clutter_actor_set_content_scaling_filters(ClutterActor * self,ClutterScalingFilter min_filter,ClutterScalingFilter mag_filter)20144 clutter_actor_set_content_scaling_filters (ClutterActor *self,
20145 ClutterScalingFilter min_filter,
20146 ClutterScalingFilter mag_filter)
20147 {
20148 ClutterActorPrivate *priv;
20149 gboolean changed;
20150 GObject *obj;
20151
20152 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20153
20154 priv = self->priv;
20155 obj = G_OBJECT (self);
20156
20157 g_object_freeze_notify (obj);
20158
20159 changed = FALSE;
20160
20161 if (priv->min_filter != min_filter)
20162 {
20163 priv->min_filter = min_filter;
20164 changed = TRUE;
20165
20166 g_object_notify_by_pspec (obj, obj_props[PROP_MINIFICATION_FILTER]);
20167 }
20168
20169 if (priv->mag_filter != mag_filter)
20170 {
20171 priv->mag_filter = mag_filter;
20172 changed = TRUE;
20173
20174 g_object_notify_by_pspec (obj, obj_props[PROP_MAGNIFICATION_FILTER]);
20175 }
20176
20177 if (changed)
20178 clutter_actor_queue_redraw (self);
20179
20180 g_object_thaw_notify (obj);
20181 }
20182
20183 /**
20184 * clutter_actor_get_content_scaling_filters:
20185 * @self: a #ClutterActor
20186 * @min_filter: (out) (allow-none): return location for the minification
20187 * filter, or %NULL
20188 * @mag_filter: (out) (allow-none): return location for the magnification
20189 * filter, or %NULL
20190 *
20191 * Retrieves the values set using clutter_actor_set_content_scaling_filters().
20192 *
20193 * Since: 1.10
20194 */
20195 void
clutter_actor_get_content_scaling_filters(ClutterActor * self,ClutterScalingFilter * min_filter,ClutterScalingFilter * mag_filter)20196 clutter_actor_get_content_scaling_filters (ClutterActor *self,
20197 ClutterScalingFilter *min_filter,
20198 ClutterScalingFilter *mag_filter)
20199 {
20200 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20201
20202 if (min_filter != NULL)
20203 *min_filter = self->priv->min_filter;
20204
20205 if (mag_filter != NULL)
20206 *mag_filter = self->priv->mag_filter;
20207 }
20208
20209 /*
20210 * clutter_actor_queue_compute_expand:
20211 * @self: a #ClutterActor
20212 *
20213 * Invalidates the needs_x_expand and needs_y_expand flags on @self
20214 * and its parents up to the top-level actor.
20215 *
20216 * This function also queues a relayout if anything changed.
20217 */
20218 static inline void
clutter_actor_queue_compute_expand(ClutterActor * self)20219 clutter_actor_queue_compute_expand (ClutterActor *self)
20220 {
20221 ClutterActor *parent;
20222 gboolean changed;
20223
20224 if (self->priv->needs_compute_expand)
20225 return;
20226
20227 changed = FALSE;
20228 parent = self;
20229 while (parent != NULL)
20230 {
20231 if (!parent->priv->needs_compute_expand)
20232 {
20233 parent->priv->needs_compute_expand = TRUE;
20234 changed = TRUE;
20235 }
20236
20237 parent = parent->priv->parent;
20238 }
20239
20240 if (changed)
20241 clutter_actor_queue_relayout (self);
20242 }
20243
20244 /**
20245 * clutter_actor_set_x_expand:
20246 * @self: a #ClutterActor
20247 * @expand: whether the actor should expand horizontally
20248 *
20249 * Sets whether a #ClutterActor should expand horizontally; this means
20250 * that layout manager should allocate extra space for the actor, if
20251 * possible.
20252 *
20253 * Setting an actor to expand will also make all its parent expand, so
20254 * that it's possible to build an actor tree and only set this flag on
20255 * its leaves and not on every single actor.
20256 *
20257 * Since: 1.12
20258 */
20259 void
clutter_actor_set_x_expand(ClutterActor * self,gboolean expand)20260 clutter_actor_set_x_expand (ClutterActor *self,
20261 gboolean expand)
20262 {
20263 ClutterLayoutInfo *info;
20264
20265 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20266
20267 expand = !!expand;
20268
20269 info = _clutter_actor_get_layout_info (self);
20270 if (info->x_expand != expand)
20271 {
20272 info->x_expand = expand;
20273
20274 self->priv->x_expand_set = TRUE;
20275
20276 clutter_actor_queue_compute_expand (self);
20277
20278 g_object_notify_by_pspec (G_OBJECT (self),
20279 obj_props[PROP_X_EXPAND]);
20280 }
20281 }
20282
20283 /**
20284 * clutter_actor_get_x_expand:
20285 * @self: a #ClutterActor
20286 *
20287 * Retrieves the value set with clutter_actor_set_x_expand().
20288 *
20289 * See also: clutter_actor_needs_expand()
20290 *
20291 * Return value: %TRUE if the actor has been set to expand
20292 *
20293 * Since: 1.12
20294 */
20295 gboolean
clutter_actor_get_x_expand(ClutterActor * self)20296 clutter_actor_get_x_expand (ClutterActor *self)
20297 {
20298 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20299
20300 return _clutter_actor_get_layout_info_or_defaults (self)->x_expand;
20301 }
20302
20303 /**
20304 * clutter_actor_set_y_expand:
20305 * @self: a #ClutterActor
20306 * @expand: whether the actor should expand vertically
20307 *
20308 * Sets whether a #ClutterActor should expand horizontally; this means
20309 * that layout manager should allocate extra space for the actor, if
20310 * possible.
20311 *
20312 * Setting an actor to expand will also make all its parent expand, so
20313 * that it's possible to build an actor tree and only set this flag on
20314 * its leaves and not on every single actor.
20315 *
20316 * Since: 1.12
20317 */
20318 void
clutter_actor_set_y_expand(ClutterActor * self,gboolean expand)20319 clutter_actor_set_y_expand (ClutterActor *self,
20320 gboolean expand)
20321 {
20322 ClutterLayoutInfo *info;
20323
20324 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20325
20326 expand = !!expand;
20327
20328 info = _clutter_actor_get_layout_info (self);
20329 if (info->y_expand != expand)
20330 {
20331 info->y_expand = expand;
20332
20333 self->priv->y_expand_set = TRUE;
20334
20335 clutter_actor_queue_compute_expand (self);
20336
20337 g_object_notify_by_pspec (G_OBJECT (self),
20338 obj_props[PROP_Y_EXPAND]);
20339 }
20340 }
20341
20342 /**
20343 * clutter_actor_get_y_expand:
20344 * @self: a #ClutterActor
20345 *
20346 * Retrieves the value set with clutter_actor_set_y_expand().
20347 *
20348 * See also: clutter_actor_needs_expand()
20349 *
20350 * Return value: %TRUE if the actor has been set to expand
20351 *
20352 * Since: 1.12
20353 */
20354 gboolean
clutter_actor_get_y_expand(ClutterActor * self)20355 clutter_actor_get_y_expand (ClutterActor *self)
20356 {
20357 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20358
20359 return _clutter_actor_get_layout_info_or_defaults (self)->y_expand;
20360 }
20361
20362 static void
clutter_actor_compute_expand_recursive(ClutterActor * self,gboolean * x_expand_p,gboolean * y_expand_p)20363 clutter_actor_compute_expand_recursive (ClutterActor *self,
20364 gboolean *x_expand_p,
20365 gboolean *y_expand_p)
20366 {
20367 ClutterActorIter iter;
20368 ClutterActor *child;
20369 gboolean x_expand, y_expand;
20370
20371 x_expand = y_expand = FALSE;
20372
20373 /* note that we don't recurse into children if we're already set to expand;
20374 * this avoids traversing the whole actor tree, even if it may lead to some
20375 * child left with the needs_compute_expand flag set.
20376 */
20377 clutter_actor_iter_init (&iter, self);
20378 while (clutter_actor_iter_next (&iter, &child))
20379 {
20380 x_expand = x_expand ||
20381 clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL);
20382
20383 y_expand = y_expand ||
20384 clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL);
20385 }
20386
20387 *x_expand_p = x_expand;
20388 *y_expand_p = y_expand;
20389 }
20390
20391 static void
clutter_actor_compute_expand(ClutterActor * self)20392 clutter_actor_compute_expand (ClutterActor *self)
20393 {
20394 if (self->priv->needs_compute_expand)
20395 {
20396 const ClutterLayoutInfo *info;
20397 gboolean x_expand, y_expand;
20398
20399 info = _clutter_actor_get_layout_info_or_defaults (self);
20400
20401 if (self->priv->x_expand_set)
20402 x_expand = info->x_expand;
20403 else
20404 x_expand = FALSE;
20405
20406 if (self->priv->y_expand_set)
20407 y_expand = info->y_expand;
20408 else
20409 y_expand = FALSE;
20410
20411 /* we don't need to recurse down to the children if the
20412 * actor has been forcibly set to expand
20413 */
20414 if (!(self->priv->x_expand_set && self->priv->y_expand_set))
20415 {
20416 if (self->priv->n_children != 0)
20417 {
20418 gboolean *x_expand_p, *y_expand_p;
20419 gboolean ignored = FALSE;
20420
20421 x_expand_p = self->priv->x_expand_set ? &ignored : &x_expand;
20422 y_expand_p = self->priv->y_expand_set ? &ignored : &y_expand;
20423
20424 clutter_actor_compute_expand_recursive (self,
20425 x_expand_p,
20426 y_expand_p);
20427 }
20428 }
20429
20430 self->priv->needs_compute_expand = FALSE;
20431 self->priv->needs_x_expand = (x_expand != FALSE);
20432 self->priv->needs_y_expand = (y_expand != FALSE);
20433 }
20434 }
20435
20436 /**
20437 * clutter_actor_needs_expand:
20438 * @self: a #ClutterActor
20439 * @orientation: the direction of expansion
20440 *
20441 * Checks whether an actor, or any of its children, is set to expand
20442 * horizontally or vertically.
20443 *
20444 * This function should only be called by layout managers that can
20445 * assign extra space to their children.
20446 *
20447 * If you want to know whether the actor was explicitly set to expand,
20448 * use clutter_actor_get_x_expand() or clutter_actor_get_y_expand().
20449 *
20450 * Return value: %TRUE if the actor should expand
20451 *
20452 * Since: 1.12
20453 */
20454 gboolean
clutter_actor_needs_expand(ClutterActor * self,ClutterOrientation orientation)20455 clutter_actor_needs_expand (ClutterActor *self,
20456 ClutterOrientation orientation)
20457 {
20458 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20459
20460 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
20461 return FALSE;
20462
20463 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
20464 return FALSE;
20465
20466 clutter_actor_compute_expand (self);
20467
20468 switch (orientation)
20469 {
20470 case CLUTTER_ORIENTATION_HORIZONTAL:
20471 return self->priv->needs_x_expand;
20472
20473 case CLUTTER_ORIENTATION_VERTICAL:
20474 return self->priv->needs_y_expand;
20475 }
20476
20477 return FALSE;
20478 }
20479
20480 /**
20481 * clutter_actor_set_content_repeat:
20482 * @self: a #ClutterActor
20483 * @repeat: the repeat policy
20484 *
20485 * Sets the policy for repeating the #ClutterActor:content of a
20486 * #ClutterActor. The behaviour is deferred to the #ClutterContent
20487 * implementation.
20488 *
20489 * Since: 1.12
20490 */
20491 void
clutter_actor_set_content_repeat(ClutterActor * self,ClutterContentRepeat repeat)20492 clutter_actor_set_content_repeat (ClutterActor *self,
20493 ClutterContentRepeat repeat)
20494 {
20495 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20496
20497 if (self->priv->content_repeat == repeat)
20498 return;
20499
20500 self->priv->content_repeat = repeat;
20501
20502 clutter_actor_queue_redraw (self);
20503 }
20504
20505 /**
20506 * clutter_actor_get_content_repeat:
20507 * @self: a #ClutterActor
20508 *
20509 * Retrieves the repeat policy for a #ClutterActor set by
20510 * clutter_actor_set_content_repeat().
20511 *
20512 * Return value: the content repeat policy
20513 *
20514 * Since: 1.12
20515 */
20516 ClutterContentRepeat
clutter_actor_get_content_repeat(ClutterActor * self)20517 clutter_actor_get_content_repeat (ClutterActor *self)
20518 {
20519 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_REPEAT_NONE);
20520
20521 return self->priv->content_repeat;
20522 }
20523
20524 void
_clutter_actor_handle_event(ClutterActor * self,const ClutterEvent * event)20525 _clutter_actor_handle_event (ClutterActor *self,
20526 const ClutterEvent *event)
20527 {
20528 GPtrArray *event_tree;
20529 ClutterActor *iter;
20530 gboolean is_key_event;
20531 gint i = 0;
20532
20533 /* XXX - for historical reasons that are now lost in the mists of time,
20534 * key events are delivered regardless of whether an actor is set as
20535 * reactive; this should be changed for 2.0.
20536 */
20537 is_key_event = event->type == CLUTTER_KEY_PRESS ||
20538 event->type == CLUTTER_KEY_RELEASE;
20539
20540 event_tree = g_ptr_array_sized_new (64);
20541 g_ptr_array_set_free_func (event_tree, (GDestroyNotify) g_object_unref);
20542
20543 /* build the list of of emitters for the event */
20544 iter = self;
20545 while (iter != NULL)
20546 {
20547 ClutterActor *parent = iter->priv->parent;
20548
20549 if (CLUTTER_ACTOR_IS_REACTIVE (iter) || /* an actor must be reactive */
20550 parent == NULL || /* unless it's the stage */
20551 is_key_event) /* or this is a key event */
20552 {
20553 /* keep a reference on the actor, so that it remains valid
20554 * for the duration of the signal emission
20555 */
20556 g_ptr_array_add (event_tree, g_object_ref (iter));
20557 }
20558
20559 iter = parent;
20560 }
20561
20562 /* Capture: from top-level downwards */
20563 for (i = event_tree->len - 1; i >= 0; i--)
20564 if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE))
20565 goto done;
20566
20567 /* Bubble: from source upwards */
20568 for (i = 0; i < event_tree->len; i++)
20569 if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE))
20570 goto done;
20571
20572 done:
20573 g_ptr_array_free (event_tree, TRUE);
20574 }
20575
20576 static void
clutter_actor_set_child_transform_internal(ClutterActor * self,const ClutterMatrix * transform)20577 clutter_actor_set_child_transform_internal (ClutterActor *self,
20578 const ClutterMatrix *transform)
20579 {
20580 ClutterTransformInfo *info = _clutter_actor_get_transform_info (self);
20581 ClutterActorIter iter;
20582 ClutterActor *child;
20583 GObject *obj;
20584 gboolean was_set = info->child_transform_set;
20585
20586 clutter_matrix_init_from_matrix (&info->child_transform, transform);
20587
20588 /* if it's the identity matrix, we need to toggle the boolean flag */
20589 info->child_transform_set = !cogl_matrix_is_identity (transform);
20590
20591 /* we need to reset the transform_valid flag on each child */
20592 clutter_actor_iter_init (&iter, self);
20593 while (clutter_actor_iter_next (&iter, &child))
20594 child->priv->transform_valid = FALSE;
20595
20596 clutter_actor_queue_redraw (self);
20597
20598 obj = G_OBJECT (self);
20599 g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM]);
20600
20601 if (was_set != info->child_transform_set)
20602 g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM_SET]);
20603 }
20604
20605 /**
20606 * clutter_actor_set_child_transform:
20607 * @self: a #ClutterActor
20608 * @transform: (allow-none): a #ClutterMatrix, or %NULL
20609 *
20610 * Sets the transformation matrix to be applied to all the children
20611 * of @self prior to their own transformations. The default child
20612 * transformation is the identity matrix.
20613 *
20614 * If @transform is %NULL, the child transform will be unset.
20615 *
20616 * The #ClutterActor:child-transform property is animatable.
20617 *
20618 * Since: 1.12
20619 */
20620 void
clutter_actor_set_child_transform(ClutterActor * self,const ClutterMatrix * transform)20621 clutter_actor_set_child_transform (ClutterActor *self,
20622 const ClutterMatrix *transform)
20623 {
20624 const ClutterTransformInfo *info;
20625 ClutterMatrix new_transform;
20626
20627 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20628
20629 info = _clutter_actor_get_transform_info_or_defaults (self);
20630
20631 if (transform != NULL)
20632 clutter_matrix_init_from_matrix (&new_transform, transform);
20633 else
20634 clutter_matrix_init_identity (&new_transform);
20635
20636 _clutter_actor_create_transition (self, obj_props[PROP_CHILD_TRANSFORM],
20637 &info->child_transform,
20638 &new_transform);
20639 }
20640
20641 /**
20642 * clutter_actor_get_child_transform:
20643 * @self: a #ClutterActor
20644 * @transform: (out caller-allocates): a #ClutterMatrix
20645 *
20646 * Retrieves the child transformation matrix set using
20647 * clutter_actor_set_child_transform(); if none is currently set,
20648 * the @transform matrix will be initialized to the identity matrix.
20649 *
20650 * Since: 1.12
20651 */
20652 void
clutter_actor_get_child_transform(ClutterActor * self,ClutterMatrix * transform)20653 clutter_actor_get_child_transform (ClutterActor *self,
20654 ClutterMatrix *transform)
20655 {
20656 const ClutterTransformInfo *info;
20657
20658 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20659 g_return_if_fail (transform != NULL);
20660
20661 info = _clutter_actor_get_transform_info_or_defaults (self);
20662
20663 if (info->child_transform_set)
20664 clutter_matrix_init_from_matrix (transform, &info->child_transform);
20665 else
20666 clutter_matrix_init_identity (transform);
20667 }
20668
20669 static void
clutter_actor_push_in_cloned_branch(ClutterActor * self)20670 clutter_actor_push_in_cloned_branch (ClutterActor *self)
20671 {
20672 ClutterActor *iter;
20673
20674 for (iter = self->priv->first_child;
20675 iter != NULL;
20676 iter = iter->priv->next_sibling)
20677 clutter_actor_push_in_cloned_branch (iter);
20678
20679 self->priv->in_cloned_branch += 1;
20680 }
20681
20682 static void
clutter_actor_pop_in_cloned_branch(ClutterActor * self)20683 clutter_actor_pop_in_cloned_branch (ClutterActor *self)
20684 {
20685 ClutterActor *iter;
20686
20687 self->priv->in_cloned_branch -= 1;
20688
20689 for (iter = self->priv->first_child;
20690 iter != NULL;
20691 iter = iter->priv->next_sibling)
20692 clutter_actor_pop_in_cloned_branch (iter);
20693 }
20694
20695 void
_clutter_actor_attach_clone(ClutterActor * actor,ClutterActor * clone)20696 _clutter_actor_attach_clone (ClutterActor *actor,
20697 ClutterActor *clone)
20698 {
20699 ClutterActorPrivate *priv = actor->priv;
20700
20701 g_assert (clone != NULL);
20702
20703 if (priv->clones == NULL)
20704 priv->clones = g_hash_table_new (NULL, NULL);
20705
20706 g_hash_table_add (priv->clones, clone);
20707
20708 clutter_actor_push_in_cloned_branch (actor);
20709 }
20710
20711 void
_clutter_actor_detach_clone(ClutterActor * actor,ClutterActor * clone)20712 _clutter_actor_detach_clone (ClutterActor *actor,
20713 ClutterActor *clone)
20714 {
20715 ClutterActorPrivate *priv = actor->priv;
20716
20717 g_assert (clone != NULL);
20718
20719 if (priv->clones == NULL ||
20720 g_hash_table_lookup (priv->clones, clone) == NULL)
20721 return;
20722
20723 clutter_actor_pop_in_cloned_branch (actor);
20724
20725 g_hash_table_remove (priv->clones, clone);
20726
20727 if (g_hash_table_size (priv->clones) == 0)
20728 {
20729 g_hash_table_unref (priv->clones);
20730 priv->clones = NULL;
20731 }
20732 }
20733
20734 void
_clutter_actor_queue_redraw_on_clones(ClutterActor * self)20735 _clutter_actor_queue_redraw_on_clones (ClutterActor *self)
20736 {
20737 ClutterActorPrivate *priv = self->priv;
20738 GHashTableIter iter;
20739 gpointer key;
20740
20741 if (priv->clones == NULL)
20742 return;
20743
20744 g_hash_table_iter_init (&iter, priv->clones);
20745 while (g_hash_table_iter_next (&iter, &key, NULL))
20746 clutter_actor_queue_redraw (key);
20747 }
20748
20749 void
_clutter_actor_queue_relayout_on_clones(ClutterActor * self)20750 _clutter_actor_queue_relayout_on_clones (ClutterActor *self)
20751 {
20752 ClutterActorPrivate *priv = self->priv;
20753 GHashTableIter iter;
20754 gpointer key;
20755
20756 if (priv->clones == NULL)
20757 return;
20758
20759 g_hash_table_iter_init (&iter, priv->clones);
20760 while (g_hash_table_iter_next (&iter, &key, NULL))
20761 clutter_actor_queue_relayout (key);
20762 }
20763
20764 /**
20765 * clutter_actor_has_mapped_clones:
20766 * @self: a #ClutterActor
20767 *
20768 * Returns whether a #ClutterActor has any mapped clones.
20769 *
20770 * Return: %TRUE if the actor has mapped clones, and %FALSE otherwise
20771 *
20772 * Since: 1.16
20773 */
20774 gboolean
clutter_actor_has_mapped_clones(ClutterActor * self)20775 clutter_actor_has_mapped_clones (ClutterActor *self)
20776 {
20777 ClutterActorPrivate *priv;
20778 GHashTableIter iter;
20779 gpointer key;
20780
20781 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20782
20783 priv = self->priv;
20784
20785 if (priv->clones == NULL)
20786 return FALSE;
20787
20788 g_hash_table_iter_init (&iter, priv->clones);
20789 while (g_hash_table_iter_next (&iter, &key, NULL))
20790 {
20791 if (CLUTTER_ACTOR_IS_MAPPED (key))
20792 return TRUE;
20793 }
20794
20795 return FALSE;
20796 }
20797
20798 CoglFramebuffer *
_clutter_actor_get_active_framebuffer(ClutterActor * self)20799 _clutter_actor_get_active_framebuffer (ClutterActor *self)
20800 {
20801 ClutterStage *stage;
20802
20803 if (!CLUTTER_ACTOR_IN_PAINT (self))
20804 {
20805 g_critical ("The active framebuffer of actor '%s' can only be "
20806 "retrieved during the paint sequence. Please, check "
20807 "your code.",
20808 _clutter_actor_get_debug_name (self));
20809 return NULL;
20810 }
20811
20812 stage = (ClutterStage *) _clutter_actor_get_stage_internal (self);
20813 if (stage == NULL)
20814 {
20815 g_critical ("The active framebuffer of actor '%s' is only available "
20816 "if the actor is associated to a ClutterStage.",
20817 _clutter_actor_get_debug_name (self));
20818 return NULL;
20819 }
20820
20821 return _clutter_stage_get_active_framebuffer (stage);
20822 }
20823
20824 static void
clutter_actor_child_model__items_changed(GListModel * model,guint position,guint removed,guint added,gpointer user_data)20825 clutter_actor_child_model__items_changed (GListModel *model,
20826 guint position,
20827 guint removed,
20828 guint added,
20829 gpointer user_data)
20830 {
20831 ClutterActor *parent = user_data;
20832 ClutterActorPrivate *priv = parent->priv;
20833 guint i;
20834
20835 while (removed--)
20836 {
20837 ClutterActor *child = clutter_actor_get_child_at_index (parent, position);
20838 clutter_actor_destroy (child);
20839 }
20840
20841 for (i = 0; i < added; i++)
20842 {
20843 GObject *item = g_list_model_get_item (model, position + i);
20844 ClutterActor *child = priv->create_child_func (item, priv->create_child_data);
20845
20846 /* The actor returned by the function can have a floating reference,
20847 * if the implementation is in pure C, or have a full reference, usually
20848 * the case for language bindings. To avoid leaking references, we
20849 * try to assume ownership of the instance, and release the reference
20850 * at the end unconditionally, leaving the only reference to the actor
20851 * itself.
20852 */
20853 if (g_object_is_floating (child))
20854 g_object_ref_sink (child);
20855
20856 clutter_actor_insert_child_at_index (parent, child, position + i);
20857
20858 g_object_unref (child);
20859 g_object_unref (item);
20860 }
20861 }
20862
20863 /**
20864 * clutter_actor_bind_model:
20865 * @self: a #ClutterActor
20866 * @model: (nullable): a #GListModel
20867 * @create_child_func: a function that creates #ClutterActor instances
20868 * from the contents of the @model
20869 * @user_data: user data passed to @create_child_func
20870 * @notify: function called when unsetting the @model
20871 *
20872 * Binds a #GListModel to a #ClutterActor.
20873 *
20874 * If the #ClutterActor was already bound to a #GListModel, the previous
20875 * binding is destroyed.
20876 *
20877 * The existing children of #ClutterActor are destroyed when setting a
20878 * model, and new children are created and added, representing the contents
20879 * of the @model. The #ClutterActor is updated whenever the @model changes.
20880 * If @model is %NULL, the #ClutterActor is left empty.
20881 *
20882 * When a #ClutterActor is bound to a model, adding and removing children
20883 * directly is undefined behaviour.
20884 *
20885 * Since: 1.24
20886 */
20887 void
clutter_actor_bind_model(ClutterActor * self,GListModel * model,ClutterActorCreateChildFunc create_child_func,gpointer user_data,GDestroyNotify notify)20888 clutter_actor_bind_model (ClutterActor *self,
20889 GListModel *model,
20890 ClutterActorCreateChildFunc create_child_func,
20891 gpointer user_data,
20892 GDestroyNotify notify)
20893 {
20894 ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
20895
20896 g_return_if_fail (CLUTTER_IS_ACTOR (self));
20897 g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
20898 g_return_if_fail (model == NULL || create_child_func != NULL);
20899
20900 if (priv->child_model != NULL)
20901 {
20902 if (priv->create_child_notify != NULL)
20903 priv->create_child_notify (priv->create_child_data);
20904
20905 g_signal_handlers_disconnect_by_func (priv->child_model,
20906 clutter_actor_child_model__items_changed,
20907 self);
20908 g_clear_object (&priv->child_model);
20909 priv->create_child_func = NULL;
20910 priv->create_child_data = NULL;
20911 priv->create_child_notify = NULL;
20912 }
20913
20914 clutter_actor_destroy_all_children (self);
20915
20916 if (model == NULL)
20917 return;
20918
20919 priv->child_model = g_object_ref (model);
20920 priv->create_child_func = create_child_func;
20921 priv->create_child_data = user_data;
20922 priv->create_child_notify = notify;
20923
20924 g_signal_connect (priv->child_model, "items-changed",
20925 G_CALLBACK (clutter_actor_child_model__items_changed),
20926 self);
20927
20928 clutter_actor_child_model__items_changed (priv->child_model,
20929 0,
20930 0,
20931 g_list_model_get_n_items (priv->child_model),
20932 self);
20933 }
20934
20935 typedef struct {
20936 GType child_type;
20937 GArray *props;
20938 } BindClosure;
20939
20940 typedef struct {
20941 const char *model_property;
20942 const char *child_property;
20943 GBindingFlags flags;
20944 } BindProperty;
20945
20946 static void
bind_closure_free(gpointer data_)20947 bind_closure_free (gpointer data_)
20948 {
20949 BindClosure *data = data_;
20950
20951 if (data == NULL)
20952 return;
20953
20954 g_array_unref (data->props);
20955 g_slice_free (BindClosure, data);
20956 }
20957
20958 static ClutterActor *
bind_child_with_properties(gpointer item,gpointer data_)20959 bind_child_with_properties (gpointer item,
20960 gpointer data_)
20961 {
20962 BindClosure *data = data_;
20963 ClutterActor *res;
20964 guint i;
20965
20966 res = g_object_new (data->child_type, NULL);
20967
20968 for (i = 0; i < data->props->len; i++)
20969 {
20970 const BindProperty *prop = &g_array_index (data->props, BindProperty, i);
20971
20972 g_object_bind_property (item, prop->model_property,
20973 res, prop->child_property,
20974 prop->flags);
20975 }
20976
20977 return res;
20978 }
20979
20980 /**
20981 * clutter_actor_bind_model_with_properties:
20982 * @self: a #ClutterActor
20983 * @model: a #GListModel
20984 * @child_type: the type of #ClutterActor to use when creating
20985 * children mapping to items inside the @model
20986 * @first_model_property: the first property of @model to bind
20987 * @...: tuples of property names on the @model, on the child, and the
20988 * #GBindingFlags used to bind them, terminated by %NULL
20989 *
20990 * Binds a #GListModel to a #ClutterActor.
20991 *
20992 * Unlike clutter_actor_bind_model(), this function automatically creates
20993 * a child #ClutterActor of type @child_type, and binds properties on the
20994 * items inside the @model to the corresponding properties on the child,
20995 * for instance:
20996 *
20997 * |[<!-- language="C" -->
20998 * clutter_actor_bind_model_with_properties (actor, model,
20999 * MY_TYPE_CHILD_VIEW,
21000 * "label", "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
21001 * "icon", "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
21002 * "selected", "selected", G_BINDING_BIDIRECTIONAL,
21003 * "active", "active", G_BINDING_BIDIRECTIONAL,
21004 * NULL);
21005 * ]|
21006 *
21007 * is the equivalent of calling clutter_actor_bind_model() with a
21008 * #ClutterActorCreateChildFunc of:
21009 *
21010 * |[<!-- language="C" -->
21011 * ClutterActor *res = g_object_new (MY_TYPE_CHILD_VIEW, NULL);
21012 *
21013 * g_object_bind_property (item, "label", res, "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
21014 * g_object_bind_property (item, "icon", res, "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
21015 * g_object_bind_property (item, "selected", res, "selected", G_BINDING_BIDIRECTIONAL);
21016 * g_object_bind_property (item, "active", res, "active", G_BINDING_BIDIRECTIONAL);
21017 *
21018 * return res;
21019 * ]|
21020 *
21021 * If the #ClutterActor was already bound to a #GListModel, the previous
21022 * binding is destroyed.
21023 *
21024 * When a #ClutterActor is bound to a model, adding and removing children
21025 * directly is undefined behaviour.
21026 *
21027 * See also: clutter_actor_bind_model()
21028 *
21029 * Since: 1.24
21030 */
21031 void
clutter_actor_bind_model_with_properties(ClutterActor * self,GListModel * model,GType child_type,const char * first_model_property,...)21032 clutter_actor_bind_model_with_properties (ClutterActor *self,
21033 GListModel *model,
21034 GType child_type,
21035 const char *first_model_property,
21036 ...)
21037 {
21038 va_list args;
21039 BindClosure *clos;
21040 const char *model_property;
21041
21042 g_return_if_fail (CLUTTER_IS_ACTOR (self));
21043 g_return_if_fail (G_IS_LIST_MODEL (model));
21044 g_return_if_fail (g_type_is_a (child_type, CLUTTER_TYPE_ACTOR));
21045
21046 clos = g_slice_new0 (BindClosure);
21047 clos->child_type = child_type;
21048 clos->props = g_array_new (FALSE, FALSE, sizeof (BindProperty));
21049
21050 va_start (args, first_model_property);
21051 model_property = first_model_property;
21052 while (model_property != NULL)
21053 {
21054 const char *child_property = va_arg (args, char *);
21055 GBindingFlags binding_flags = va_arg (args, guint);
21056 BindProperty bind;
21057
21058 bind.model_property = g_intern_string (model_property);
21059 bind.child_property = g_intern_string (child_property);
21060 bind.flags = binding_flags;
21061
21062 g_array_append_val (clos->props, bind);
21063
21064 model_property = va_arg (args, char *);
21065 }
21066
21067 clutter_actor_bind_model (self, model, bind_child_with_properties, clos, bind_closure_free);
21068 }
21069
21070 /*< private >
21071 * clutter_actor_create_texture_paint_node:
21072 * @self: a #ClutterActor
21073 * @texture: a #CoglTexture
21074 *
21075 * Creates a #ClutterPaintNode initialized using the state of the
21076 * given #ClutterActor, ready to be used inside the implementation
21077 * of the #ClutterActorClass.paint_node virtual function.
21078 *
21079 * The returned paint node has the geometry set to the size of the
21080 * #ClutterActor:content-box property; it uses the filters specified
21081 * in the #ClutterActor:minification-filter and #ClutterActor:magnification-filter
21082 * properties; and respects the #ClutterActor:content-repeat property.
21083 *
21084 * Returns: (transfer full): The newly created #ClutterPaintNode
21085 *
21086 * Since: 1.24
21087 */
21088 ClutterPaintNode *
clutter_actor_create_texture_paint_node(ClutterActor * self,CoglTexture * texture)21089 clutter_actor_create_texture_paint_node (ClutterActor *self,
21090 CoglTexture *texture)
21091 {
21092 ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
21093 ClutterPaintNode *node;
21094 ClutterActorBox box;
21095 ClutterColor color;
21096
21097 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
21098 g_return_val_if_fail (texture != NULL, NULL);
21099
21100 clutter_actor_get_content_box (self, &box);
21101
21102 /* ClutterTextureNode will premultiply the blend color, so we
21103 * want it to be white with the paint opacity
21104 */
21105 color.red = 255;
21106 color.green = 255;
21107 color.blue = 255;
21108 color.alpha = clutter_actor_get_paint_opacity_internal (self);
21109
21110 node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter);
21111 clutter_paint_node_set_name (node, "Texture");
21112
21113 if (priv->content_repeat == CLUTTER_REPEAT_NONE)
21114 clutter_paint_node_add_rectangle (node, &box);
21115 else
21116 {
21117 float t_w = 1.f, t_h = 1.f;
21118
21119 if ((priv->content_repeat & CLUTTER_REPEAT_X_AXIS) != FALSE)
21120 t_w = (box.x2 - box.x1) / cogl_texture_get_width (texture);
21121
21122 if ((priv->content_repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE)
21123 t_h = (box.y2 - box.y1) / cogl_texture_get_height (texture);
21124
21125 clutter_paint_node_add_texture_rectangle (node, &box,
21126 0.f, 0.f,
21127 t_w, t_h);
21128 }
21129
21130 return node;
21131 }
21132