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 actor's #ClutterActor:pivot-point
47 *
48 * ## Modifying an actor's geometry ## {#clutter-actor-geometry}
49 *
50 * Each actor has a bounding box, called #ClutterActor:allocation
51 * which is either set by its parent or explicitly through the
52 * clutter_actor_set_position() and clutter_actor_set_size() methods.
53 * Each actor also has an implicit preferred size.
54 *
55 * An actor’s preferred size can be defined by any subclass by
56 * overriding the #ClutterActorClass.get_preferred_width() and the
57 * #ClutterActorClass.get_preferred_height() virtual functions, or it can
58 * be explicitly set by using clutter_actor_set_width() and
59 * clutter_actor_set_height().
60 *
61 * An actor’s position can be set explicitly by using
62 * clutter_actor_set_x() and clutter_actor_set_y(); the coordinates are
63 * relative to the origin of the actor’s parent.
64 *
65 * ## Managing actor children ## {#clutter-actor-children}
66 *
67 * Each actor can have multiple children, by calling
68 * clutter_actor_add_child() to add a new child actor, and
69 * clutter_actor_remove_child() to remove an existing child. #ClutterActor
70 * will hold a reference on each child actor, which will be released when
71 * the child is removed from its parent, or destroyed using
72 * clutter_actor_destroy().
73 *
74 * |[<!-- language="C" -->
75 * ClutterActor *actor = clutter_actor_new ();
76 *
77 * // set the bounding box of the actor
78 * clutter_actor_set_position (actor, 0, 0);
79 * clutter_actor_set_size (actor, 480, 640);
80 *
81 * // set the background color of the actor
82 * clutter_actor_set_background_color (actor, CLUTTER_COLOR_Orange);
83 *
84 * // set the bounding box of the child, relative to the parent
85 * ClutterActor *child = clutter_actor_new ();
86 * clutter_actor_set_position (child, 20, 20);
87 * clutter_actor_set_size (child, 80, 240);
88 *
89 * // set the background color of the child
90 * clutter_actor_set_background_color (child, CLUTTER_COLOR_Blue);
91 *
92 * // add the child to the actor
93 * clutter_actor_add_child (actor, child);
94 * ]|
95 *
96 * Children can be inserted at a given index, or above and below
97 * another child actor. The order of insertion determines the order of the
98 * children when iterating over them. Iterating over children is performed
99 * by using clutter_actor_get_first_child(), clutter_actor_get_previous_sibling(),
100 * clutter_actor_get_next_sibling(), and clutter_actor_get_last_child(). It is
101 * also possible to retrieve a list of children by using
102 * clutter_actor_get_children(), as well as retrieving a specific child at a
103 * given index by using clutter_actor_get_child_at_index().
104 *
105 * If you need to track additions of children to a #ClutterActor, use
106 * the #ClutterContainer::actor-added signal; similarly, to track removals
107 * of children from a ClutterActor, use the #ClutterContainer::actor-removed
108 * signal.
109 *
110 * See [basic-actor.c](https://git.gnome.org/browse/clutter/tree/examples/basic-actor.c?h=clutter-1.18).
111 *
112 * ## Painting an actor ## {#clutter-actor-painting}
113 *
114 * There are three ways to paint an actor:
115 *
116 * - set a delegate #ClutterContent as the value for the #ClutterActor:content property of the actor
117 * - subclass #ClutterActor and override the #ClutterActorClass.paint_node() virtual function
118 * - subclass #ClutterActor and override the #ClutterActorClass.paint() virtual function.
119 *
120 * A #ClutterContent is a delegate object that takes over the painting
121 * operations of one, or more actors. The #ClutterContent painting will
122 * be performed on top of the #ClutterActor:background-color of the actor,
123 * and before calling the actor's own implementation of the
124 * #ClutterActorClass.paint_node() virtual function.
125 *
126 * |[<!-- language="C" -->
127 * ClutterActor *actor = clutter_actor_new ();
128 *
129 * // set the bounding box
130 * clutter_actor_set_position (actor, 50, 50);
131 * clutter_actor_set_size (actor, 100, 100);
132 *
133 * // set the content; the image_content variable is set elsewhere
134 * clutter_actor_set_content (actor, image_content);
135 * ]|
136 *
137 * The #ClutterActorClass.paint_node() virtual function is invoked whenever
138 * an actor needs to be painted. The implementation of the virtual function
139 * must only paint the contents of the actor itself, and not the contents of
140 * its children, if the actor has any.
141 *
142 * The #ClutterPaintNode passed to the virtual function is the local root of
143 * the render tree; any node added to it will be rendered at the correct
144 * position, as defined by the actor's #ClutterActor:allocation.
145 *
146 * |[<!-- language="C" -->
147 * static void
148 * my_actor_paint_node (ClutterActor *actor,
149 * ClutterPaintNode *root)
150 * {
151 * ClutterPaintNode *node;
152 * ClutterActorBox box;
153 *
154 * // where the content of the actor should be painted
155 * clutter_actor_get_allocation_box (actor, &box);
156 *
157 * // the cogl_texture variable is set elsewhere
158 * node = clutter_texture_node_new (cogl_texture, CLUTTER_COLOR_White,
159 * CLUTTER_SCALING_FILTER_TRILINEAR,
160 * CLUTTER_SCALING_FILTER_LINEAR);
161 *
162 * // paint the content of the node using the allocation
163 * clutter_paint_node_add_rectangle (node, &box);
164 *
165 * // add the node, and transfer ownership
166 * clutter_paint_node_add_child (root, node);
167 * clutter_paint_node_unref (node);
168 * }
169 *
170 * The #ClutterActorClass.paint() virtual function function gives total
171 * control to the paint sequence of the actor itself, including the
172 * children of the actor, if any. It is strongly discouraged to override
173 * the #ClutterActorClass.paint() virtual function and it will be removed
174 * when the Clutter API changes.
175 *
176 * ## Handling events on an actor ## {#clutter-actor-event-handling}
177 *
178 * A #ClutterActor can receive and handle input device events, for
179 * instance pointer events and key events, as long as its
180 * #ClutterActor:reactive property is set to %TRUE.
181 *
182 * Once an actor has been determined to be the source of an event,
183 * Clutter will traverse the scene graph from the top-level actor towards the
184 * event source, emitting the #ClutterActor::captured-event signal on each
185 * ancestor until it reaches the source; this phase is also called
186 * the "capture" phase. If the event propagation was not stopped, the graph
187 * is walked backwards, from the source actor to the top-level, and the
188 * #ClutterActor::event signal is emitted, alongside eventual event-specific
189 * signals like #ClutterActor::button-press-event or #ClutterActor::motion-event;
190 * this phase is also called the "bubble" phase.
191 *
192 * At any point of the signal emission, signal handlers can stop the propagation
193 * through the scene graph by returning %CLUTTER_EVENT_STOP; otherwise, they can
194 * continue the propagation by returning %CLUTTER_EVENT_PROPAGATE.
195 *
196 * ## Animation ## {#clutter-actor-animation}
197 *
198 * Animation is a core concept of modern user interfaces; Clutter provides a
199 * complete and powerful animation framework that automatically tweens the
200 * actor's state without requiring direct, frame by frame manipulation from
201 * your application code. You have two models at your disposal:
202 *
203 * - an implicit animation model
204 * - an explicit animation model
205 *
206 * The implicit animation model of Clutter assumes that all the
207 * changes in an actor state should be gradual and asynchronous; Clutter
208 * will automatically transition an actor's property change between the
209 * current state and the desired one without manual intervention, if the
210 * property is defined to be animatable in its documentation.
211 *
212 * By default, in the 1.0 API series, the transition happens with a duration
213 * of zero milliseconds, and the implicit animation is an opt in feature to
214 * retain backwards compatibility.
215 *
216 * Implicit animations depend on the current easing state; in order to use
217 * the default easing state for an actor you should call the
218 * clutter_actor_save_easing_state() function:
219 *
220 * |[<!-- language="C" -->
221 * // assume that the actor is currently positioned at (100, 100)
222 *
223 * // store the current easing state and reset the new easing state to
224 * // its default values
225 * clutter_actor_save_easing_state (actor);
226 *
227 * // change the actor's position
228 * clutter_actor_set_position (actor, 500, 500);
229 *
230 * // restore the previously saved easing state
231 * clutter_actor_restore_easing_state (actor);
232 * ]|
233 *
234 * The example above will trigger an implicit animation of the
235 * actor between its current position to a new position.
236 *
237 * Implicit animations use a default duration of 250 milliseconds,
238 * and a default easing mode of %CLUTTER_EASE_OUT_CUBIC, unless you call
239 * clutter_actor_set_easing_mode() and clutter_actor_set_easing_duration()
240 * after changing the easing state of the actor.
241 *
242 * It is possible to animate multiple properties of an actor
243 * at the same time, and you can animate multiple actors at the same
244 * time as well, for instance:
245 *
246 * |[<!-- language="C" -->
247 * clutter_actor_save_easing_state (actor);
248 *
249 * // animate the actor's opacity and depth
250 * clutter_actor_set_opacity (actor, 0);
251 * clutter_actor_set_z_position (actor, -100);
252 *
253 * clutter_actor_restore_easing_state (actor);
254 *
255 * clutter_actor_save_easing_state (another_actor);
256 *
257 * // animate another actor's opacity
258 * clutter_actor_set_opacity (another_actor, 255);
259 * clutter_actor_set_z_position (another_actor, 100);
260 *
261 * clutter_actor_restore_easing_state (another_actor);
262 * ]|
263 *
264 * Changing the easing state will affect all the following property
265 * transitions, but will not affect existing transitions.
266 *
267 * It is important to note that if you modify the state on an
268 * animatable property while a transition is in flight, the transition's
269 * final value will be updated, as well as its duration and progress
270 * mode by using the current easing state; for instance, in the following
271 * example:
272 *
273 * |[<!-- language="C" -->
274 * clutter_actor_save_easing_state (actor);
275 * clutter_actor_set_easing_duration (actor, 1000);
276 * clutter_actor_set_x (actor, 200);
277 * clutter_actor_restore_easing_state (actor);
278 *
279 * clutter_actor_save_easing_state (actor);
280 * clutter_actor_set_easing_duration (actor, 500);
281 * clutter_actor_set_x (actor, 100);
282 * clutter_actor_restore_easing_state (actor);
283 * ]|
284 *
285 * the first call to clutter_actor_set_x() will begin a transition
286 * of the #ClutterActor:x property from the current value to the value of
287 * 200 over a duration of one second; the second call to clutter_actor_set_x()
288 * will change the transition's final value to 100 and the duration to 500
289 * milliseconds.
290 *
291 * It is possible to receive a notification of the completion of an
292 * implicit transition by using the #ClutterActor::transition-stopped
293 * signal, decorated with the name of the property. In case you want to
294 * know when all the currently in flight transitions are complete, use
295 * the #ClutterActor::transitions-completed signal instead.
296 *
297 * It is possible to retrieve the #ClutterTransition used by the
298 * animatable properties by using clutter_actor_get_transition() and using
299 * the property name as the transition name.
300 *
301 * The explicit animation model supported by Clutter requires that
302 * you create a #ClutterTransition object, and optionally set the initial
303 * and final values. The transition will not start unless you add it to the
304 * #ClutterActor.
305 *
306 * |[<!-- language="C" -->
307 * ClutterTransition *transition;
308 *
309 * transition = clutter_property_transition_new_for_actor (actor, "opacity");
310 * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 3000);
311 * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2);
312 * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
313 * clutter_transition_set_from (transition, G_TYPE_UINT, 255);
314 * clutter_transition_set_to (transition, G_TYPE_UINT, 0);
315 *
316 * clutter_actor_add_transition (actor, "animate-opacity", transition);
317 * ]|
318 *
319 * The example above will animate the #ClutterActor:opacity property
320 * of an actor between fully opaque and fully transparent, and back, over
321 * a span of 3 seconds. The animation does not begin until it is added to
322 * the actor.
323 *
324 * The explicit animation API applies to all #GObject properties,
325 * as well as the custom properties defined through the #ClutterAnimatable
326 * interface, regardless of whether they are defined as implicitly
327 * animatable or not.
328 *
329 * The explicit animation API should also be used when using custom
330 * animatable properties for #ClutterAction, #ClutterConstraint, and
331 * #ClutterEffect instances associated to an actor; see the section on
332 * custom animatable properties below for an example.
333 *
334 * Finally, explicit animations are useful for creating animations
335 * that run continuously, for instance:
336 *
337 * |[<!-- language="C" -->
338 * // this animation will pulse the actor's opacity continuously
339 * ClutterTransition *transition;
340 * ClutterInterval *interval;
341 *
342 * transition = clutter_property_transition_new_for_actor (actor, "opacity");
343 *
344 * // we want to animate the opacity between 0 and 255
345 * clutter_transition_set_from (transition, G_TYPE_UINT, 0);
346 * clutter_transition_set_to (transition, G_TYPE_UINT, 255);
347 *
348 * // over a one second duration, running an infinite amount of times
349 * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 1000);
350 * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), -1);
351 *
352 * // we want to fade in and out, so we need to auto-reverse the transition
353 * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
354 *
355 * // and we want to use an easing function that eases both in and out
356 * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
357 * CLUTTER_EASE_IN_OUT_CUBIC);
358 *
359 * // add the transition to the desired actor to start it
360 * clutter_actor_add_transition (actor, "opacityAnimation", transition);
361 * ]|
362 *
363 * ## Implementing an actor ## {#clutter-actor-implementing}
364 *
365 * Careful consideration should be given when deciding to implement
366 * a #ClutterActor sub-class. It is generally recommended to implement a
367 * sub-class of #ClutterActor only for actors that should be used as leaf
368 * nodes of a scene graph.
369 *
370 * By overriding the #ClutterActorClass.get_preferred_width() and
371 * #ClutterActorClass.get_preferred_height() virtual functions it is
372 * possible to change or provide the preferred size of an actor; similarly,
373 * by overriding the #ClutterActorClass.allocate() virtual function it is
374 * possible to control the layout of the children of an actor. Make sure to
375 * always chain up to the parent implementation of the
376 * #ClutterActorClass.allocate() virtual function.
377 *
378 * In general, it is strongly encouraged to use delegation and composition
379 * instead of direct subclassing.
380 *
381 * ## ClutterActor custom properties for ClutterScript ## {#clutter-actor-custom-script}
382 *
383 * #ClutterActor defines a custom "rotation" property which allows a short-hand
384 * description of the rotations to be applied to an actor.
385 *
386 * The syntax of the "rotation" property is the following:
387 *
388 * |[
389 * "rotation" : [ { "<axis>" : [ <angle>, [ <center-point> ] ] } ]
390 * ]|
391 *
392 * where:
393 *
394 * - axis is the name of an enumeration value of type #ClutterRotateAxis
395 * - angle is a floating point value representing the rotation angle on the given axis in degrees
396 * - center-point is an optional array, and if present it must contain the center of rotation as described by two coordinates:
397 * - Y and Z for "x-axis"
398 * - X and Z for "y-axis"
399 * - X and Y for "z-axis".
400 *
401 * #ClutterActor also defines a scriptable "margin" property which follows the CSS "margin" shorthand.
402 *
403 * |[
404 * // 4 values
405 * "margin" : [ top, right, bottom, left ]
406 * // 3 values
407 * "margin" : [ top, left/right, bottom ]
408 * // 2 values
409 * "margin" : [ top/bottom, left/right ]
410 * // 1 value
411 * "margin" : [ top/right/bottom/left ]
412 * ]|
413 *
414 * #ClutterActor will also parse every positional and dimensional
415 * property defined as a string through clutter_units_from_string(); you
416 * should read the documentation for the #ClutterUnits parser format for
417 * the valid units and syntax.
418 *
419 * ## Custom animatable properties
420 *
421 * #ClutterActor allows accessing properties of #ClutterAction,
422 * #ClutterEffect, and #ClutterConstraint instances associated to an actor
423 * instance for animation purposes, as well as its #ClutterLayoutManager.
424 *
425 * In order to access a specific #ClutterAction or a #ClutterConstraint
426 * property it is necessary to set the #ClutterActorMeta:name property on the
427 * given action or constraint.
428 *
429 * The property can be accessed using the following syntax:
430 *
431 * |[
432 * @<section>.<meta-name>.<property-name>
433 * ]|
434 *
435 * - the initial `@` is mandatory
436 * - the `section` fragment can be one between "actions", "constraints", "content",
437 * and "effects"
438 * - the `meta-name` fragment is the name of the action, effect, or constraint, as
439 * specified by the #ClutterActorMeta:name property of #ClutterActorMeta
440 * - the `property-name` fragment is the name of the action, effect, or constraint
441 * property to be animated.
442 *
443 * The example below animates a #ClutterBindConstraint applied to an actor
444 * using an explicit transition. The `rect` actor has a binding constraint
445 * on the `origin` actor, and in its initial state is overlapping the actor
446 * to which is bound to.
447 *
448 * As the actor has only one #ClutterLayoutManager, the syntax for accessing its
449 * properties is simpler:
450 *
451 * |[
452 * @layout.<property-name>
453 * ]|
454 *
455 * |[<!-- language="C" -->
456 * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_X, 0.0);
457 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-x");
458 * clutter_actor_add_constraint (rect, constraint);
459 *
460 * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_Y, 0.0);
461 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-y");
462 * clutter_actor_add_constraint (rect, constraint);
463 *
464 * clutter_actor_set_reactive (origin, TRUE);
465 *
466 * g_signal_connect (origin, "button-press-event",
467 * G_CALLBACK (on_button_press),
468 * rect);
469 * ]|
470 *
471 * On button press, the rectangle "slides" from behind the actor to
472 * which is bound to, using the #ClutterBindConstraint:offset property to
473 * achieve the effect:
474 *
475 * |[<!-- language="C" -->
476 * gboolean
477 * on_button_press (ClutterActor *origin,
478 * ClutterEvent *event,
479 * ClutterActor *rect)
480 * {
481 * ClutterTransition *transition;
482 *
483 * // the offset that we want to apply; this will make the actor
484 * // slide in from behind the origin and rest at the right of
485 * // the origin, plus a padding value
486 * float new_offset = clutter_actor_get_width (origin) + h_padding;
487 *
488 * // the property we wish to animate; the "@constraints" section
489 * // tells Clutter to check inside the constraints associated
490 * // with the actor; the "bind-x" section is the name of the
491 * // constraint; and the "offset" is the name of the property
492 * // on the constraint
493 * const char *prop = "@constraints.bind-x.offset";
494 *
495 * // create a new transition for the given property
496 * transition = clutter_property_transition_new_for_actor (rect, prop);
497 *
498 * // set the easing mode and duration
499 * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
500 * CLUTTER_EASE_OUT_CUBIC);
501 * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 500);
502 *
503 * // create the interval with the initial and final values
504 * clutter_transition_set_from (transition, G_TYPE_FLOAT, 0.f);
505 * clutter_transition_set_to (transition, G_TYPE_FLOAT, new_offset);
506 *
507 * // add the transition to the actor; this causes the animation
508 * // to start. the name "offsetAnimation" can be used to retrieve
509 * // the transition later
510 * clutter_actor_add_transition (rect, "offsetAnimation", transition);
511 *
512 * // we handled the event
513 * return CLUTTER_EVENT_STOP;
514 * }
515 * ]|
516 */
517
518 /**
519 * CLUTTER_ACTOR_IS_MAPPED:
520 * @a: a #ClutterActor
521 *
522 * Evaluates to %TRUE if the %CLUTTER_ACTOR_MAPPED flag is set.
523 *
524 * The mapped state is set when the actor is visible and all its parents up
525 * to a top-level (e.g. a #ClutterStage) are visible, realized, and mapped.
526 *
527 * This check can be used to see if an actor is going to be painted, as only
528 * actors with the %CLUTTER_ACTOR_MAPPED flag set are going to be painted.
529 *
530 * The %CLUTTER_ACTOR_MAPPED flag is managed by Clutter itself, and it should
531 * not be checked directly; instead, the recommended usage is to connect a
532 * handler on the #GObject::notify signal for the #ClutterActor:mapped
533 * property of #ClutterActor, and check the presence of
534 * the %CLUTTER_ACTOR_MAPPED flag on state changes.
535 *
536 * It is also important to note that Clutter may delay the changes of
537 * the %CLUTTER_ACTOR_MAPPED flag on top-levels due to backend-specific
538 * limitations, or during the reparenting of an actor, to optimize
539 * unnecessary (and potentially expensive) state changes.
540 *
541 * Since: 0.2
542 *
543 * Deprecated: 1.24: Use clutter_actor_is_mapped() or the #ClutterActor:mapped
544 * property instead of this macro.
545 */
546
547 /**
548 * CLUTTER_ACTOR_IS_REALIZED:
549 * @a: a #ClutterActor
550 *
551 * Evaluates to %TRUE if the %CLUTTER_ACTOR_REALIZED flag is set.
552 *
553 * The realized state has an actor-dependant interpretation. If an
554 * actor wants to delay allocating resources until it is attached to a
555 * stage, it may use the realize state to do so. However it is
556 * perfectly acceptable for an actor to allocate Cogl resources before
557 * being realized because there is only one drawing context used by Clutter
558 * so any resources will work on any stage. If an actor is mapped it
559 * must also be realized, but an actor can be realized and unmapped
560 * (this is so hiding an actor temporarily doesn't do an expensive
561 * unrealize/realize).
562 *
563 * To be realized an actor must be inside a stage, and all its parents
564 * must be realized.
565 *
566 * Since: 0.2
567 *
568 * Deprecated: 1.24: Use clutter_actor_is_realized() or the #ClutterActor:realized
569 * property instead of this macro.
570 */
571
572 /**
573 * CLUTTER_ACTOR_IS_VISIBLE:
574 * @a: a #ClutterActor
575 *
576 * Evaluates to %TRUE if the actor has been shown, %FALSE if it's hidden.
577 * Equivalent to the ClutterActor::visible object property.
578 *
579 * Note that an actor is only painted onscreen if it's mapped, which
580 * means it's visible, and all its parents are visible, and one of the
581 * parents is a toplevel stage; see also %CLUTTER_ACTOR_IS_MAPPED.
582 *
583 * Since: 0.2
584 *
585 * Deprecated: 1.24: Use clutter_actor_is_visible() or the #ClutterActor:visible
586 * property instead of this macro.
587 */
588
589 /**
590 * CLUTTER_ACTOR_IS_REACTIVE:
591 * @a: a #ClutterActor
592 *
593 * Evaluates to %TRUE if the %CLUTTER_ACTOR_REACTIVE flag is set.
594 *
595 * Only reactive actors will receive event-related signals.
596 *
597 * Since: 0.6
598 *
599 * Deprecated: 1.24: Use clutter_actor_get_reactive() or the
600 * #ClutterActor:reactive property instead of this macro.
601 */
602
603 #include "clutter-build-config.h"
604
605 #include <math.h>
606
607 #include <gobject/gvaluecollector.h>
608
609 #include <cogl/cogl.h>
610
611 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
612
613 #include "clutter-actor-private.h"
614
615 #include "clutter-action.h"
616 #include "clutter-actor-meta-private.h"
617 #include "clutter-animatable.h"
618 #include "clutter-color-static.h"
619 #include "clutter-color.h"
620 #include "clutter-constraint-private.h"
621 #include "clutter-container-private.h"
622 #include "clutter-content-private.h"
623 #include "clutter-debug.h"
624 #include "clutter-easing.h"
625 #include "clutter-effect-private.h"
626 #include "clutter-enum-types.h"
627 #include "clutter-fixed-layout.h"
628 #include "clutter-flatten-effect.h"
629 #include "clutter-interval.h"
630 #include "clutter-main.h"
631 #include "clutter-marshal.h"
632 #include "clutter-mutter.h"
633 #include "clutter-paint-context-private.h"
634 #include "clutter-paint-nodes.h"
635 #include "clutter-paint-node-private.h"
636 #include "clutter-paint-volume-private.h"
637 #include "clutter-pick-context-private.h"
638 #include "clutter-private.h"
639 #include "clutter-property-transition.h"
640 #include "clutter-scriptable.h"
641 #include "clutter-script-private.h"
642 #include "clutter-stage-private.h"
643 #include "clutter-stage-view-private.h"
644 #include "clutter-timeline.h"
645 #include "clutter-transition.h"
646 #include "clutter-units.h"
647
648 #include "deprecated/clutter-container.h"
649
650 /* Internal enum used to control mapped state update. This is a hint
651 * which indicates when to do something other than just enforce
652 * invariants.
653 */
654 typedef enum
655 {
656 MAP_STATE_CHECK, /* just enforce invariants. */
657 MAP_STATE_MAKE_UNREALIZED, /* force unrealize, ignoring invariants,
658 * used when about to unparent.
659 */
660 MAP_STATE_MAKE_MAPPED, /* set mapped, error if invariants not met;
661 * used to set mapped on toplevels.
662 */
663 MAP_STATE_MAKE_UNMAPPED /* set unmapped, even if parent is mapped,
664 * used just before unmapping parent.
665 */
666 } MapStateChange;
667
668 /* 3 entries should be a good compromise, few layout managers
669 * will ask for 3 different preferred size in each allocation cycle */
670 #define N_CACHED_SIZE_REQUESTS 3
671
672 struct _ClutterActorPrivate
673 {
674 /* request mode */
675 ClutterRequestMode request_mode;
676
677 /* our cached size requests for different width / height */
678 SizeRequest width_requests[N_CACHED_SIZE_REQUESTS];
679 SizeRequest height_requests[N_CACHED_SIZE_REQUESTS];
680
681 /* An age of 0 means the entry is not set */
682 guint cached_height_age;
683 guint cached_width_age;
684
685 /* the bounding box of the actor, relative to the parent's
686 * allocation
687 */
688 ClutterActorBox allocation;
689
690 /* clip, in actor coordinates */
691 graphene_rect_t clip;
692
693 /* the cached transformation matrix; see apply_transform() */
694 graphene_matrix_t transform;
695
696 float resource_scale;
697
698 guint8 opacity;
699 gint opacity_override;
700 unsigned int inhibit_culling_counter;
701
702 ClutterOffscreenRedirect offscreen_redirect;
703
704 /* This is an internal effect used to implement the
705 offscreen-redirect property */
706 ClutterEffect *flatten_effect;
707
708 /* scene graph */
709 ClutterActor *parent;
710 ClutterActor *prev_sibling;
711 ClutterActor *next_sibling;
712 ClutterActor *first_child;
713 ClutterActor *last_child;
714
715 gint n_children;
716
717 /* tracks whenever the children of an actor are changed; the
718 * age is incremented by 1 whenever an actor is added or
719 * removed. the age is not incremented when the first or the
720 * last child pointers are changed, or when grandchildren of
721 * an actor are changed.
722 */
723 gint age;
724
725 gchar *name; /* a non-unique name, used for debugging */
726
727 /* a back-pointer to the Pango context that we can use
728 * to create pre-configured PangoLayout
729 */
730 PangoContext *pango_context;
731
732 /* the text direction configured for this child - either by
733 * application code, or by the actor's parent
734 */
735 ClutterTextDirection text_direction;
736
737 /* meta classes */
738 ClutterMetaGroup *actions;
739 ClutterMetaGroup *constraints;
740 ClutterMetaGroup *effects;
741
742 /* delegate object used to allocate the children of this actor */
743 ClutterLayoutManager *layout_manager;
744
745 /* delegate object used to paint the contents of this actor */
746 ClutterContent *content;
747
748 ClutterActorBox content_box;
749 ClutterContentGravity content_gravity;
750 ClutterScalingFilter min_filter;
751 ClutterScalingFilter mag_filter;
752 ClutterContentRepeat content_repeat;
753
754 /* used when painting, to update the paint volume */
755 ClutterEffect *current_effect;
756
757 /* This is used to store an effect which needs to be redrawn. A
758 redraw can be queued to start from a particular effect. This is
759 used by parametrised effects that can cache an image of the
760 actor. If a parameter of the effect changes then it only needs to
761 redraw the cached image, not the actual actor. The pointer is
762 only valid if is_dirty == TRUE. If the pointer is NULL then the
763 whole actor is dirty. */
764 ClutterEffect *effect_to_redraw;
765
766 /* This is used when painting effects to implement the
767 clutter_actor_continue_paint() function. It points to the node in
768 the list of effects that is next in the chain */
769 const GList *next_effect_to_paint;
770
771 ClutterPaintVolume paint_volume;
772
773 /* NB: This volume isn't relative to this actor, it is in eye
774 * coordinates so that it can remain valid after the actor changes.
775 */
776 ClutterPaintVolume last_paint_volume;
777
778 ClutterColor bg_color;
779
780 /* a string used for debugging messages */
781 char *debug_name;
782
783 /* a set of clones of the actor */
784 GHashTable *clones;
785
786 /* whether the actor is inside a cloned branch; this
787 * value is propagated to all the actor's children
788 */
789 gulong in_cloned_branch;
790
791 guint unmapped_paint_branch_counter;
792
793 GListModel *child_model;
794 ClutterActorCreateChildFunc create_child_func;
795 gpointer create_child_data;
796 GDestroyNotify create_child_notify;
797
798 gulong resolution_changed_id;
799 gulong font_changed_id;
800 gulong layout_changed_id;
801
802 GList *stage_views;
803
804 /* bitfields: KEEP AT THE END */
805
806 /* fixed position and sizes */
807 guint position_set : 1;
808 guint min_width_set : 1;
809 guint min_height_set : 1;
810 guint natural_width_set : 1;
811 guint natural_height_set : 1;
812 /* cached request is invalid (implies allocation is too) */
813 guint needs_width_request : 1;
814 /* cached request is invalid (implies allocation is too) */
815 guint needs_height_request : 1;
816 /* cached allocation is invalid (request has changed, probably) */
817 guint needs_allocation : 1;
818 guint show_on_set_parent : 1;
819 guint has_clip : 1;
820 guint clip_to_allocation : 1;
821 guint enable_model_view_transform : 1;
822 guint enable_paint_unmapped : 1;
823 guint has_pointer : 1;
824 guint has_key_focus : 1;
825 guint propagated_one_redraw : 1;
826 guint paint_volume_valid : 1;
827 guint last_paint_volume_valid : 1;
828 guint in_clone_paint : 1;
829 guint transform_valid : 1;
830 /* This is TRUE if anything has queued a redraw since we were last
831 painted. In this case effect_to_redraw will point to an effect
832 the redraw was queued from or it will be NULL if the redraw was
833 queued without an effect. */
834 guint is_dirty : 1;
835 guint bg_color_set : 1;
836 guint content_box_valid : 1;
837 guint x_expand_set : 1;
838 guint y_expand_set : 1;
839 guint needs_compute_expand : 1;
840 guint needs_x_expand : 1;
841 guint needs_y_expand : 1;
842 guint needs_paint_volume_update : 1;
843 guint had_effects_on_last_paint_volume_update : 1;
844 guint needs_update_stage_views : 1;
845 };
846
847 enum
848 {
849 PROP_0,
850
851 PROP_NAME,
852
853 /* X, Y, WIDTH, HEIGHT are "do what I mean" properties;
854 * when set they force a size request, when gotten they
855 * get the allocation if the allocation is valid, and the
856 * request otherwise
857 */
858 PROP_X,
859 PROP_Y,
860 PROP_WIDTH,
861 PROP_HEIGHT,
862
863 PROP_POSITION,
864 PROP_SIZE,
865
866 /* Then the rest of these size-related properties are the "actual"
867 * underlying properties set or gotten by X, Y, WIDTH, HEIGHT
868 */
869 PROP_FIXED_X,
870 PROP_FIXED_Y,
871
872 PROP_FIXED_POSITION_SET,
873
874 PROP_MIN_WIDTH,
875 PROP_MIN_WIDTH_SET,
876
877 PROP_MIN_HEIGHT,
878 PROP_MIN_HEIGHT_SET,
879
880 PROP_NATURAL_WIDTH,
881 PROP_NATURAL_WIDTH_SET,
882
883 PROP_NATURAL_HEIGHT,
884 PROP_NATURAL_HEIGHT_SET,
885
886 PROP_REQUEST_MODE,
887
888 /* Allocation properties are read-only */
889 PROP_ALLOCATION,
890
891 PROP_Z_POSITION,
892
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
913 PROP_ROTATION_ANGLE_X, /* XXX:2.0 rename to rotation-x */
914 PROP_ROTATION_ANGLE_Y, /* XXX:2.0 rename to rotation-y */
915 PROP_ROTATION_ANGLE_Z, /* XXX:2.0 rename to rotation-z */
916
917 PROP_TRANSLATION_X,
918 PROP_TRANSLATION_Y,
919 PROP_TRANSLATION_Z,
920
921 PROP_TRANSFORM,
922 PROP_TRANSFORM_SET,
923 PROP_CHILD_TRANSFORM,
924 PROP_CHILD_TRANSFORM_SET,
925
926 PROP_SHOW_ON_SET_PARENT, /*XXX:2.0 remove */
927
928 PROP_TEXT_DIRECTION,
929 PROP_HAS_POINTER,
930
931 PROP_ACTIONS,
932 PROP_CONSTRAINTS,
933 PROP_EFFECT,
934
935 PROP_LAYOUT_MANAGER,
936
937 PROP_X_EXPAND,
938 PROP_Y_EXPAND,
939 PROP_X_ALIGN,
940 PROP_Y_ALIGN,
941 PROP_MARGIN_TOP,
942 PROP_MARGIN_BOTTOM,
943 PROP_MARGIN_LEFT,
944 PROP_MARGIN_RIGHT,
945
946 PROP_BACKGROUND_COLOR,
947 PROP_BACKGROUND_COLOR_SET,
948
949 PROP_FIRST_CHILD,
950 PROP_LAST_CHILD,
951
952 PROP_CONTENT,
953 PROP_CONTENT_GRAVITY,
954 PROP_CONTENT_BOX,
955 PROP_MINIFICATION_FILTER,
956 PROP_MAGNIFICATION_FILTER,
957 PROP_CONTENT_REPEAT,
958
959 PROP_LAST
960 };
961
962 static GParamSpec *obj_props[PROP_LAST];
963
964 enum
965 {
966 SHOW,
967 HIDE,
968 DESTROY,
969 PARENT_SET,
970 KEY_FOCUS_IN,
971 KEY_FOCUS_OUT,
972 PAINT,
973 PICK,
974 REALIZE,
975 UNREALIZE,
976 QUEUE_RELAYOUT,
977 EVENT,
978 CAPTURED_EVENT,
979 BUTTON_PRESS_EVENT,
980 BUTTON_RELEASE_EVENT,
981 SCROLL_EVENT,
982 KEY_PRESS_EVENT,
983 KEY_RELEASE_EVENT,
984 MOTION_EVENT,
985 ENTER_EVENT,
986 LEAVE_EVENT,
987 TRANSITIONS_COMPLETED,
988 TOUCH_EVENT,
989 TRANSITION_STOPPED,
990 STAGE_VIEWS_CHANGED,
991 RESOURCE_SCALE_CHANGED,
992
993 LAST_SIGNAL
994 };
995
996 static guint actor_signals[LAST_SIGNAL] = { 0, };
997
998 typedef struct _TransitionClosure
999 {
1000 ClutterActor *actor;
1001 ClutterTransition *transition;
1002 gchar *name;
1003 gulong completed_id;
1004 } TransitionClosure;
1005
1006 static void clutter_container_iface_init (ClutterContainerIface *iface);
1007 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
1008 static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface);
1009 static void atk_implementor_iface_init (AtkImplementorIface *iface);
1010
1011 /* These setters are all static for now, maybe they should be in the
1012 * public API, but they are perhaps obscure enough to leave only as
1013 * properties
1014 */
1015 static void clutter_actor_set_min_width (ClutterActor *self,
1016 gfloat min_width);
1017 static void clutter_actor_set_min_height (ClutterActor *self,
1018 gfloat min_height);
1019 static void clutter_actor_set_natural_width (ClutterActor *self,
1020 gfloat natural_width);
1021 static void clutter_actor_set_natural_height (ClutterActor *self,
1022 gfloat natural_height);
1023 static void clutter_actor_set_min_width_set (ClutterActor *self,
1024 gboolean use_min_width);
1025 static void clutter_actor_set_min_height_set (ClutterActor *self,
1026 gboolean use_min_height);
1027 static void clutter_actor_set_natural_width_set (ClutterActor *self,
1028 gboolean use_natural_width);
1029 static void clutter_actor_set_natural_height_set (ClutterActor *self,
1030 gboolean use_natural_height);
1031 static void clutter_actor_update_map_state (ClutterActor *self,
1032 MapStateChange change);
1033 static void clutter_actor_unrealize_not_hiding (ClutterActor *self);
1034
1035 static void _clutter_actor_get_relative_transformation_matrix (ClutterActor *self,
1036 ClutterActor *ancestor,
1037 graphene_matrix_t *matrix);
1038
1039 static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self);
1040
1041 static guint8 clutter_actor_get_paint_opacity_internal (ClutterActor *self);
1042
1043 static inline void clutter_actor_set_background_color_internal (ClutterActor *self,
1044 const ClutterColor *color);
1045
1046 static void on_layout_manager_changed (ClutterLayoutManager *manager,
1047 ClutterActor *self);
1048
1049 static inline void clutter_actor_queue_compute_expand (ClutterActor *self);
1050
1051 static inline void clutter_actor_set_margin_internal (ClutterActor *self,
1052 gfloat margin,
1053 GParamSpec *pspec);
1054
1055 static void clutter_actor_set_transform_internal (ClutterActor *self,
1056 const graphene_matrix_t *transform);
1057 static void clutter_actor_set_child_transform_internal (ClutterActor *self,
1058 const graphene_matrix_t *transform);
1059
1060 static void clutter_actor_realize_internal (ClutterActor *self);
1061 static void clutter_actor_unrealize_internal (ClutterActor *self);
1062
1063 static void clutter_actor_push_in_cloned_branch (ClutterActor *self,
1064 gulong count);
1065 static void clutter_actor_pop_in_cloned_branch (ClutterActor *self,
1066 gulong count);
1067 static void ensure_valid_actor_transform (ClutterActor *actor);
1068
1069 static void push_in_paint_unmapped_branch (ClutterActor *self,
1070 guint count);
1071 static void pop_in_paint_unmapped_branch (ClutterActor *self,
1072 guint count);
1073
1074 static GQuark quark_actor_layout_info = 0;
1075 static GQuark quark_actor_transform_info = 0;
1076 static GQuark quark_actor_animation_info = 0;
1077
1078 static GQuark quark_key = 0;
1079 static GQuark quark_motion = 0;
1080 static GQuark quark_pointer_focus = 0;
1081 static GQuark quark_button = 0;
1082 static GQuark quark_scroll = 0;
1083 static GQuark quark_stage = 0;
1084 static GQuark quark_touch = 0;
1085 static GQuark quark_touchpad = 0;
1086 static GQuark quark_proximity = 0;
1087 static GQuark quark_pad = 0;
1088 static GQuark quark_im = 0;
1089
1090 G_DEFINE_TYPE_WITH_CODE (ClutterActor,
1091 clutter_actor,
1092 G_TYPE_INITIALLY_UNOWNED,
1093 G_ADD_PRIVATE (ClutterActor)
1094 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
1095 clutter_container_iface_init)
1096 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
1097 clutter_scriptable_iface_init)
1098 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
1099 clutter_animatable_iface_init)
1100 G_IMPLEMENT_INTERFACE (ATK_TYPE_IMPLEMENTOR,
1101 atk_implementor_iface_init));
1102
1103 /*< private >
1104 * clutter_actor_get_debug_name:
1105 * @actor: a #ClutterActor
1106 *
1107 * Retrieves a printable name of @actor for debugging messages
1108 *
1109 * Return value: a string with a printable name
1110 */
1111 const char *
_clutter_actor_get_debug_name(ClutterActor * actor)1112 _clutter_actor_get_debug_name (ClutterActor *actor)
1113 {
1114 ClutterActorPrivate *priv = actor->priv;
1115 const char *retval;
1116
1117 if (G_UNLIKELY (priv->debug_name == NULL))
1118 {
1119 priv->debug_name = g_strdup_printf ("<%s>[<%s>:%p]",
1120 priv->name != NULL ? priv->name
1121 : "unnamed",
1122 G_OBJECT_TYPE_NAME (actor),
1123 actor);
1124 }
1125
1126 retval = priv->debug_name;
1127
1128 return retval;
1129 }
1130
1131 #ifdef CLUTTER_ENABLE_DEBUG
1132 /* XXX - this is for debugging only, remove once working (or leave
1133 * in only in some debug mode). Should leave it for a little while
1134 * until we're confident in the new map/realize/visible handling.
1135 */
1136 static inline void
clutter_actor_verify_map_state(ClutterActor * self)1137 clutter_actor_verify_map_state (ClutterActor *self)
1138 {
1139 ClutterActorPrivate *priv = self->priv;
1140
1141 if (CLUTTER_ACTOR_IS_REALIZED (self))
1142 {
1143 if (priv->parent == NULL)
1144 {
1145 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
1146 {
1147 g_warning ("Realized non-toplevel actor '%s' should "
1148 "have a parent",
1149 _clutter_actor_get_debug_name (self));
1150 }
1151 }
1152 else if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1153 {
1154 g_warning ("Realized actor %s has an unrealized parent %s",
1155 _clutter_actor_get_debug_name (self),
1156 _clutter_actor_get_debug_name (priv->parent));
1157 }
1158 }
1159
1160 if (CLUTTER_ACTOR_IS_MAPPED (self))
1161 {
1162 if (!CLUTTER_ACTOR_IS_REALIZED (self))
1163 g_warning ("Actor '%s' is mapped but not realized",
1164 _clutter_actor_get_debug_name (self));
1165
1166 if (priv->parent == NULL)
1167 {
1168 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1169 {
1170 if (!CLUTTER_ACTOR_IS_VISIBLE (self) &&
1171 !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1172 {
1173 g_warning ("Toplevel actor '%s' is mapped "
1174 "but not visible",
1175 _clutter_actor_get_debug_name (self));
1176 }
1177 }
1178 else
1179 {
1180 g_warning ("Mapped actor '%s' should have a parent",
1181 _clutter_actor_get_debug_name (self));
1182 }
1183 }
1184 else
1185 {
1186 ClutterActor *iter = self;
1187
1188 /* check for the enable_paint_unmapped flag on the actor
1189 * and parents; if the flag is enabled at any point of this
1190 * branch of the scene graph then all the later checks
1191 * become pointless
1192 */
1193 while (iter != NULL)
1194 {
1195 if (iter->priv->enable_paint_unmapped)
1196 return;
1197
1198 iter = iter->priv->parent;
1199 }
1200
1201 if (!CLUTTER_ACTOR_IS_VISIBLE (priv->parent))
1202 {
1203 g_warning ("Actor '%s' should not be mapped if parent '%s'"
1204 "is not visible",
1205 _clutter_actor_get_debug_name (self),
1206 _clutter_actor_get_debug_name (priv->parent));
1207 }
1208
1209 if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1210 {
1211 g_warning ("Actor '%s' should not be mapped if parent '%s'"
1212 "is not realized",
1213 _clutter_actor_get_debug_name (self),
1214 _clutter_actor_get_debug_name (priv->parent));
1215 }
1216
1217 if (!CLUTTER_ACTOR_IS_TOPLEVEL (priv->parent))
1218 {
1219 if (!CLUTTER_ACTOR_IS_MAPPED (priv->parent))
1220 g_warning ("Actor '%s' is mapped but its non-toplevel "
1221 "parent '%s' is not mapped",
1222 _clutter_actor_get_debug_name (self),
1223 _clutter_actor_get_debug_name (priv->parent));
1224 }
1225 }
1226 }
1227 }
1228
1229 #endif /* CLUTTER_ENABLE_DEBUG */
1230
1231 /**
1232 * clutter_actor_pick_box:
1233 * @self: The #ClutterActor being "pick" painted.
1234 * @pick_context: The #ClutterPickContext
1235 * @box: A rectangle in the actor's own local coordinates.
1236 *
1237 * Logs (does a virtual paint of) a rectangle for picking. Note that @box is
1238 * in the actor's own local coordinates, so is usually {0,0,width,height}
1239 * to include the whole actor. That is unless the actor has a shaped input
1240 * region in which case you may wish to log the (multiple) smaller rectangles
1241 * that make up the input region.
1242 */
1243 void
clutter_actor_pick_box(ClutterActor * self,ClutterPickContext * pick_context,const ClutterActorBox * box)1244 clutter_actor_pick_box (ClutterActor *self,
1245 ClutterPickContext *pick_context,
1246 const ClutterActorBox *box)
1247 {
1248 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1249 g_return_if_fail (box != NULL);
1250
1251 if (box->x1 >= box->x2 || box->y1 >= box->y2)
1252 return;
1253
1254 clutter_pick_context_log_pick (pick_context, box, self);
1255 }
1256
1257 static void
clutter_actor_set_mapped(ClutterActor * self,gboolean mapped)1258 clutter_actor_set_mapped (ClutterActor *self,
1259 gboolean mapped)
1260 {
1261 if (CLUTTER_ACTOR_IS_MAPPED (self) == mapped)
1262 return;
1263
1264 if (mapped)
1265 {
1266 CLUTTER_ACTOR_GET_CLASS (self)->map (self);
1267 g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1268 }
1269 else
1270 {
1271 CLUTTER_ACTOR_GET_CLASS (self)->unmap (self);
1272 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1273 }
1274 }
1275
1276 /* this function updates the mapped and realized states according to
1277 * invariants, in the appropriate order.
1278 */
1279 static void
clutter_actor_update_map_state(ClutterActor * self,MapStateChange change)1280 clutter_actor_update_map_state (ClutterActor *self,
1281 MapStateChange change)
1282 {
1283 gboolean was_mapped;
1284
1285 was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
1286
1287 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1288 {
1289 /* the mapped flag on top-level actors must be set by the
1290 * per-backend implementation because it might be asynchronous.
1291 *
1292 * That is, the MAPPED flag on toplevels currently tracks the X
1293 * server mapped-ness of the window, while the expected behavior
1294 * (if used to GTK) may be to track WM_STATE!=WithdrawnState.
1295 * This creates some weird complexity by breaking the invariant
1296 * that if we're visible and all ancestors shown then we are
1297 * also mapped - instead, we are mapped if all ancestors
1298 * _possibly excepting_ the stage are mapped. The stage
1299 * will map/unmap for example when it is minimized or
1300 * moved to another workspace.
1301 *
1302 * So, the only invariant on the stage is that if visible it
1303 * should be realized, and that it has to be visible to be
1304 * mapped.
1305 */
1306 if (CLUTTER_ACTOR_IS_VISIBLE (self))
1307 clutter_actor_realize (self);
1308
1309 switch (change)
1310 {
1311 case MAP_STATE_CHECK:
1312 break;
1313
1314 case MAP_STATE_MAKE_MAPPED:
1315 g_assert (!was_mapped);
1316 clutter_actor_set_mapped (self, TRUE);
1317 break;
1318
1319 case MAP_STATE_MAKE_UNMAPPED:
1320 g_assert (was_mapped);
1321 clutter_actor_set_mapped (self, FALSE);
1322 break;
1323
1324 case MAP_STATE_MAKE_UNREALIZED:
1325 /* we only use MAKE_UNREALIZED in unparent,
1326 * and unparenting a stage isn't possible.
1327 * If someone wants to just unrealize a stage
1328 * then clutter_actor_unrealize() doesn't
1329 * go through this codepath.
1330 */
1331 g_warning ("Trying to force unrealize stage is not allowed");
1332 break;
1333 }
1334
1335 if (CLUTTER_ACTOR_IS_MAPPED (self) &&
1336 !CLUTTER_ACTOR_IS_VISIBLE (self) &&
1337 !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1338 {
1339 g_warning ("Clutter toplevel of type '%s' is not visible, but "
1340 "it is somehow still mapped",
1341 _clutter_actor_get_debug_name (self));
1342 }
1343 }
1344 else
1345 {
1346 ClutterActorPrivate *priv = self->priv;
1347 ClutterActor *parent = priv->parent;
1348 gboolean should_be_mapped;
1349 gboolean may_be_realized;
1350 gboolean must_be_realized;
1351
1352 should_be_mapped = FALSE;
1353 may_be_realized = TRUE;
1354 must_be_realized = FALSE;
1355
1356 if (parent == NULL || change == MAP_STATE_MAKE_UNREALIZED)
1357 {
1358 may_be_realized = FALSE;
1359 }
1360 else
1361 {
1362 /* Maintain invariant that if parent is mapped, and we are
1363 * visible, then we are mapped ... unless parent is a
1364 * stage, in which case we map regardless of parent's map
1365 * state but do require stage to be visible and realized.
1366 *
1367 * If parent is realized, that does not force us to be
1368 * realized; but if parent is unrealized, that does force
1369 * us to be unrealized.
1370 *
1371 * The reason we don't force children to realize with
1372 * parents is _clutter_actor_rerealize(); if we require that
1373 * a realized parent means children are realized, then to
1374 * unrealize an actor we would have to unrealize its
1375 * parents, which would end up meaning unrealizing and
1376 * hiding the entire stage. So we allow unrealizing a
1377 * child (as long as that child is not mapped) while that
1378 * child still has a realized parent.
1379 *
1380 * Also, if we unrealize from leaf nodes to root, and
1381 * realize from root to leaf, the invariants are never
1382 * violated if we allow children to be unrealized
1383 * while parents are realized.
1384 *
1385 * When unmapping, MAP_STATE_MAKE_UNMAPPED is specified
1386 * to force us to unmap, even though parent is still
1387 * mapped. This is because we're unmapping from leaf nodes
1388 * up to root nodes.
1389 */
1390 if (CLUTTER_ACTOR_IS_VISIBLE (self) &&
1391 change != MAP_STATE_MAKE_UNMAPPED)
1392 {
1393 gboolean parent_is_visible_realized_toplevel;
1394
1395 parent_is_visible_realized_toplevel =
1396 (CLUTTER_ACTOR_IS_TOPLEVEL (parent) &&
1397 CLUTTER_ACTOR_IS_VISIBLE (parent) &&
1398 CLUTTER_ACTOR_IS_REALIZED (parent));
1399
1400 if (CLUTTER_ACTOR_IS_MAPPED (parent) ||
1401 parent_is_visible_realized_toplevel)
1402 {
1403 must_be_realized = TRUE;
1404 should_be_mapped = TRUE;
1405 }
1406 }
1407
1408 /* if the actor has been set to be painted even if unmapped
1409 * then we should map it and check for realization as well;
1410 * this is an override for the branch of the scene graph
1411 * which begins with this node
1412 */
1413 if (priv->enable_paint_unmapped)
1414 {
1415 should_be_mapped = TRUE;
1416 must_be_realized = TRUE;
1417 }
1418
1419 if (!CLUTTER_ACTOR_IS_REALIZED (parent))
1420 may_be_realized = FALSE;
1421 }
1422
1423 if (change == MAP_STATE_MAKE_MAPPED && !should_be_mapped)
1424 {
1425 if (parent == NULL)
1426 g_warning ("Attempting to map a child that does not "
1427 "meet the necessary invariants: the actor '%s' "
1428 "has no parent",
1429 _clutter_actor_get_debug_name (self));
1430 else
1431 g_warning ("Attempting to map a child that does not "
1432 "meet the necessary invariants: the actor '%s' "
1433 "is parented to an unmapped actor '%s'",
1434 _clutter_actor_get_debug_name (self),
1435 _clutter_actor_get_debug_name (priv->parent));
1436 }
1437
1438 /* We want to go in the order "realize, map" and "unmap, unrealize" */
1439
1440 /* Unmap */
1441 if (!should_be_mapped)
1442 clutter_actor_set_mapped (self, FALSE);
1443
1444 /* Realize */
1445 if (must_be_realized)
1446 clutter_actor_realize (self);
1447
1448 /* if we must be realized then we may be, presumably */
1449 g_assert (!(must_be_realized && !may_be_realized));
1450
1451 /* Unrealize */
1452 if (!may_be_realized)
1453 clutter_actor_unrealize_not_hiding (self);
1454
1455 /* Map */
1456 if (should_be_mapped)
1457 {
1458 if (!must_be_realized)
1459 g_warning ("Somehow we think actor '%s' should be mapped but "
1460 "not realized, which isn't allowed",
1461 _clutter_actor_get_debug_name (self));
1462
1463 /* realization is allowed to fail (though I don't know what
1464 * an app is supposed to do about that - shouldn't it just
1465 * be a g_error? anyway, we have to avoid mapping if this
1466 * happens)
1467 */
1468 if (CLUTTER_ACTOR_IS_REALIZED (self))
1469 clutter_actor_set_mapped (self, TRUE);
1470 }
1471 }
1472
1473 #ifdef CLUTTER_ENABLE_DEBUG
1474 /* check all invariants were kept */
1475 clutter_actor_verify_map_state (self);
1476 #endif
1477 }
1478
1479 static void
queue_update_stage_views(ClutterActor * actor)1480 queue_update_stage_views (ClutterActor *actor)
1481 {
1482 while (actor && !actor->priv->needs_update_stage_views)
1483 {
1484 actor->priv->needs_update_stage_views = TRUE;
1485
1486 /* We don't really need to update the stage-views of the actors up the
1487 * hierarchy, we set the flag anyway though so we can avoid traversing
1488 * the whole scenegraph when looking for actors which need an update
1489 * in clutter_actor_finish_layout().
1490 */
1491 actor = actor->priv->parent;
1492 }
1493 }
1494
1495 static void queue_update_paint_volume (ClutterActor *actor);
1496
1497 static void
queue_update_paint_volume_on_clones(ClutterActor * self)1498 queue_update_paint_volume_on_clones (ClutterActor *self)
1499 {
1500 ClutterActorPrivate *priv = self->priv;
1501 GHashTableIter iter;
1502 gpointer key;
1503
1504 if (priv->clones == NULL)
1505 return;
1506
1507 g_hash_table_iter_init (&iter, priv->clones);
1508 while (g_hash_table_iter_next (&iter, &key, NULL))
1509 queue_update_paint_volume (key);
1510 }
1511
1512 void
queue_update_paint_volume(ClutterActor * actor)1513 queue_update_paint_volume (ClutterActor *actor)
1514 {
1515 queue_update_paint_volume_on_clones (actor);
1516
1517 while (actor)
1518 {
1519 actor->priv->needs_paint_volume_update = TRUE;
1520 actor = actor->priv->parent;
1521 }
1522 }
1523
1524 static void
clutter_actor_real_map(ClutterActor * self)1525 clutter_actor_real_map (ClutterActor *self)
1526 {
1527 ClutterActorPrivate *priv = self->priv;
1528 ClutterActor *iter;
1529
1530 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1531
1532 CLUTTER_NOTE (ACTOR, "Mapping actor '%s'",
1533 _clutter_actor_get_debug_name (self));
1534
1535 CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1536
1537 if (priv->unmapped_paint_branch_counter == 0)
1538 {
1539 /* We skip unmapped actors when updating the stage-views list, so if
1540 * an actors list got invalidated while it was unmapped make sure to
1541 * set priv->needs_update_stage_views to TRUE for all actors up the
1542 * hierarchy now.
1543 */
1544 if (priv->needs_update_stage_views)
1545 {
1546 /* Avoid the early return in queue_update_stage_views() */
1547 priv->needs_update_stage_views = FALSE;
1548 queue_update_stage_views (self);
1549 }
1550
1551 /* Avoid the early return in clutter_actor_queue_relayout() */
1552 priv->needs_width_request = FALSE;
1553 priv->needs_height_request = FALSE;
1554 priv->needs_allocation = FALSE;
1555
1556 clutter_actor_queue_relayout (self);
1557 }
1558
1559 /* notify on parent mapped before potentially mapping
1560 * children, so apps see a top-down notification.
1561 */
1562 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1563
1564 for (iter = priv->first_child;
1565 iter != NULL;
1566 iter = iter->priv->next_sibling)
1567 {
1568 clutter_actor_map (iter);
1569 }
1570 }
1571
1572 /**
1573 * clutter_actor_map:
1574 * @self: A #ClutterActor
1575 *
1576 * Sets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly maps
1577 * and realizes its children if they are visible. Does nothing if the
1578 * actor is not visible.
1579 *
1580 * Calling this function is strongly discouraged: the default
1581 * implementation of #ClutterActorClass.map() will map all the children
1582 * of an actor when mapping its parent.
1583 *
1584 * When overriding map, it is mandatory to chain up to the parent
1585 * implementation.
1586 *
1587 * Since: 1.0
1588 */
1589 void
clutter_actor_map(ClutterActor * self)1590 clutter_actor_map (ClutterActor *self)
1591 {
1592 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1593
1594 if (CLUTTER_ACTOR_IS_MAPPED (self))
1595 return;
1596
1597 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1598 return;
1599
1600 clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
1601 }
1602
1603 /**
1604 * clutter_actor_is_mapped:
1605 * @self: a #ClutterActor
1606 *
1607 * Checks whether a #ClutterActor has been set as mapped.
1608 *
1609 * See also %CLUTTER_ACTOR_IS_MAPPED and #ClutterActor:mapped
1610 *
1611 * Returns: %TRUE if the actor is mapped
1612 *
1613 * Since: 1.24
1614 */
1615 gboolean
clutter_actor_is_mapped(ClutterActor * self)1616 clutter_actor_is_mapped (ClutterActor *self)
1617 {
1618 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1619
1620 return CLUTTER_ACTOR_IS_MAPPED (self);
1621 }
1622
1623 static void
maybe_unset_key_focus(ClutterActor * self)1624 maybe_unset_key_focus (ClutterActor *self)
1625 {
1626 ClutterActor *stage;
1627
1628 if (!self->priv->has_key_focus)
1629 return;
1630
1631 stage = _clutter_actor_get_stage_internal (self);
1632
1633 if (stage)
1634 clutter_stage_set_key_focus (CLUTTER_STAGE (stage), NULL);
1635 }
1636
1637 static void
clutter_actor_real_unmap(ClutterActor * self)1638 clutter_actor_real_unmap (ClutterActor *self)
1639 {
1640 ClutterActorPrivate *priv = self->priv;
1641 ClutterActor *iter;
1642
1643 g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1644
1645 CLUTTER_NOTE (ACTOR, "Unmapping actor '%s'",
1646 _clutter_actor_get_debug_name (self));
1647
1648 for (iter = priv->first_child;
1649 iter != NULL;
1650 iter = iter->priv->next_sibling)
1651 {
1652 clutter_actor_unmap (iter);
1653 }
1654
1655 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1656
1657 if (priv->unmapped_paint_branch_counter == 0)
1658 {
1659 /* clear the contents of the last paint volume, so that hiding + moving +
1660 * showing will not result in the wrong area being repainted
1661 */
1662 _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
1663 priv->last_paint_volume_valid = TRUE;
1664
1665 if (priv->parent && !CLUTTER_ACTOR_IN_DESTRUCTION (priv->parent))
1666 {
1667 if (G_UNLIKELY (priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT))
1668 clutter_actor_queue_redraw (priv->parent);
1669 else
1670 clutter_actor_queue_relayout (priv->parent);
1671 }
1672 }
1673
1674 /* notify on parent mapped after potentially unmapping
1675 * children, so apps see a bottom-up notification.
1676 */
1677 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1678
1679 /* relinquish keyboard focus if we were unmapped while owning it */
1680 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
1681 maybe_unset_key_focus (self);
1682 }
1683
1684 /**
1685 * clutter_actor_unmap:
1686 * @self: A #ClutterActor
1687 *
1688 * Unsets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly
1689 * unmaps its children if they were mapped.
1690 *
1691 * Calling this function is not encouraged: the default #ClutterActor
1692 * implementation of #ClutterActorClass.unmap() will also unmap any
1693 * eventual children by default when their parent is unmapped.
1694 *
1695 * When overriding #ClutterActorClass.unmap(), it is mandatory to
1696 * chain up to the parent implementation.
1697 *
1698 * It is important to note that the implementation of the
1699 * #ClutterActorClass.unmap() virtual function may be called after
1700 * the #ClutterActorClass.destroy() or the #GObjectClass.dispose()
1701 * implementation, but it is guaranteed to be called before the
1702 * #GObjectClass.finalize() implementation.
1703 *
1704 * Since: 1.0
1705 */
1706 void
clutter_actor_unmap(ClutterActor * self)1707 clutter_actor_unmap (ClutterActor *self)
1708 {
1709 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1710
1711 if (!CLUTTER_ACTOR_IS_MAPPED (self))
1712 return;
1713
1714 clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNMAPPED);
1715 }
1716
1717 static void
clutter_actor_queue_shallow_relayout(ClutterActor * self)1718 clutter_actor_queue_shallow_relayout (ClutterActor *self)
1719 {
1720 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
1721
1722 if (stage != NULL)
1723 clutter_stage_queue_actor_relayout (CLUTTER_STAGE (stage), self);
1724 }
1725
1726 static void
clutter_actor_real_show(ClutterActor * self)1727 clutter_actor_real_show (ClutterActor *self)
1728 {
1729 if (CLUTTER_ACTOR_IS_VISIBLE (self))
1730 return;
1731
1732 CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1733
1734 /* we notify on the "visible" flag in the clutter_actor_show()
1735 * wrapper so the entire show signal emission completes first,
1736 * and the branch of the scene graph is in a stable state
1737 */
1738 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1739 }
1740
1741 static inline void
set_show_on_set_parent(ClutterActor * self,gboolean set_show)1742 set_show_on_set_parent (ClutterActor *self,
1743 gboolean set_show)
1744 {
1745 ClutterActorPrivate *priv = self->priv;
1746
1747 set_show = !!set_show;
1748
1749 if (priv->show_on_set_parent == set_show)
1750 return;
1751
1752 if (priv->parent == NULL)
1753 {
1754 priv->show_on_set_parent = set_show;
1755 g_object_notify_by_pspec (G_OBJECT (self),
1756 obj_props[PROP_SHOW_ON_SET_PARENT]);
1757 }
1758 }
1759
1760 static void
clutter_actor_queue_redraw_on_parent(ClutterActor * self)1761 clutter_actor_queue_redraw_on_parent (ClutterActor *self)
1762 {
1763 const ClutterPaintVolume *pv;
1764
1765 if (!self->priv->parent)
1766 return;
1767
1768 /* A relayout/redraw is underway */
1769 if (self->priv->needs_allocation)
1770 return;
1771
1772 pv = clutter_actor_get_transformed_paint_volume (self, self->priv->parent);
1773 _clutter_actor_queue_redraw_full (self->priv->parent, pv, NULL);
1774 }
1775
1776 /**
1777 * clutter_actor_show:
1778 * @self: A #ClutterActor
1779 *
1780 * Flags an actor to be displayed. An actor that isn't shown will not
1781 * be rendered on the stage.
1782 *
1783 * Actors are visible by default.
1784 *
1785 * If this function is called on an actor without a parent, the
1786 * #ClutterActor:show-on-set-parent will be set to %TRUE as a side
1787 * effect.
1788 */
1789 void
clutter_actor_show(ClutterActor * self)1790 clutter_actor_show (ClutterActor *self)
1791 {
1792 ClutterActorPrivate *priv;
1793
1794 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1795
1796 /* simple optimization */
1797 if (CLUTTER_ACTOR_IS_VISIBLE (self))
1798 {
1799 /* we still need to set the :show-on-set-parent property, in
1800 * case show() is called on an unparented actor
1801 */
1802 set_show_on_set_parent (self, TRUE);
1803 return;
1804 }
1805
1806 #ifdef CLUTTER_ENABLE_DEBUG
1807 clutter_actor_verify_map_state (self);
1808 #endif
1809
1810 priv = self->priv;
1811
1812 g_object_freeze_notify (G_OBJECT (self));
1813
1814 set_show_on_set_parent (self, TRUE);
1815
1816 /* if we're showing a child that needs to expand, or may
1817 * expand, then we need to recompute the expand flags for
1818 * its parent as well
1819 */
1820 if (priv->needs_compute_expand ||
1821 priv->needs_x_expand ||
1822 priv->needs_y_expand)
1823 {
1824 clutter_actor_queue_compute_expand (self);
1825 }
1826
1827 g_signal_emit (self, actor_signals[SHOW], 0);
1828 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1829
1830 if (priv->parent != NULL)
1831 clutter_actor_queue_redraw (self);
1832
1833 g_object_thaw_notify (G_OBJECT (self));
1834 }
1835
1836 /**
1837 * clutter_actor_is_visible:
1838 * @self: a #ClutterActor
1839 *
1840 * Checks whether an actor is marked as visible.
1841 *
1842 * See also %CLUTTER_ACTOR_IS_VISIBLE and #ClutterActor:visible.
1843 *
1844 * Returns: %TRUE if the actor visible
1845 *
1846 * Since: 1.24
1847 */
1848 gboolean
clutter_actor_is_visible(ClutterActor * self)1849 clutter_actor_is_visible (ClutterActor *self)
1850 {
1851 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1852
1853 return CLUTTER_ACTOR_IS_VISIBLE (self);
1854 }
1855
1856 static void
clutter_actor_real_hide(ClutterActor * self)1857 clutter_actor_real_hide (ClutterActor *self)
1858 {
1859 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1860 return;
1861
1862 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1863
1864 /* we notify on the "visible" flag in the clutter_actor_hide()
1865 * wrapper so the entire hide signal emission completes first,
1866 * and the branch of the scene graph is in a stable state
1867 */
1868 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1869 }
1870
1871 /**
1872 * clutter_actor_hide:
1873 * @self: A #ClutterActor
1874 *
1875 * Flags an actor to be hidden. A hidden actor will not be
1876 * rendered on the stage.
1877 *
1878 * Actors are visible by default.
1879 *
1880 * If this function is called on an actor without a parent, the
1881 * #ClutterActor:show-on-set-parent property will be set to %FALSE
1882 * as a side-effect.
1883 */
1884 void
clutter_actor_hide(ClutterActor * self)1885 clutter_actor_hide (ClutterActor *self)
1886 {
1887 ClutterActorPrivate *priv;
1888
1889 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1890
1891 /* simple optimization */
1892 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1893 {
1894 /* we still need to set the :show-on-set-parent property, in
1895 * case hide() is called on an unparented actor
1896 */
1897 set_show_on_set_parent (self, FALSE);
1898 return;
1899 }
1900
1901 #ifdef CLUTTER_ENABLE_DEBUG
1902 clutter_actor_verify_map_state (self);
1903 #endif
1904
1905 priv = self->priv;
1906
1907 g_object_freeze_notify (G_OBJECT (self));
1908
1909 set_show_on_set_parent (self, FALSE);
1910
1911 /* if we're hiding a child that needs to expand, or may
1912 * expand, then we need to recompute the expand flags for
1913 * its parent as well
1914 */
1915 if (priv->needs_compute_expand ||
1916 priv->needs_x_expand ||
1917 priv->needs_y_expand)
1918 {
1919 clutter_actor_queue_compute_expand (self);
1920 }
1921
1922 g_signal_emit (self, actor_signals[HIDE], 0);
1923 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1924
1925 if (priv->parent != NULL && priv->needs_allocation)
1926 clutter_actor_queue_redraw (priv->parent);
1927 else
1928 clutter_actor_queue_redraw_on_parent (self);
1929
1930 g_object_thaw_notify (G_OBJECT (self));
1931 }
1932
1933 /**
1934 * clutter_actor_realize:
1935 * @self: A #ClutterActor
1936 *
1937 * Realization informs the actor that it is attached to a stage. It
1938 * can use this to allocate resources if it wanted to delay allocation
1939 * until it would be rendered. However it is perfectly acceptable for
1940 * an actor to create resources before being realized because Clutter
1941 * only ever has a single rendering context so that actor is free to
1942 * be moved from one stage to another.
1943 *
1944 * This function does nothing if the actor is already realized.
1945 *
1946 * Because a realized actor must have realized parent actors, calling
1947 * clutter_actor_realize() will also realize all parents of the actor.
1948 *
1949 * This function does not realize child actors, except in the special
1950 * case that realizing the stage, when the stage is visible, will
1951 * suddenly map (and thus realize) the children of the stage.
1952 *
1953 * Deprecated: 1.16: Actors are automatically realized, and nothing
1954 * requires explicit realization.
1955 */
1956 void
clutter_actor_realize(ClutterActor * self)1957 clutter_actor_realize (ClutterActor *self)
1958 {
1959 g_return_if_fail (CLUTTER_IS_ACTOR (self));
1960
1961 clutter_actor_realize_internal (self);
1962 }
1963
1964 /**
1965 * clutter_actor_is_realized:
1966 * @self: a #ClutterActor
1967 *
1968 * Checks whether a #ClutterActor is realized.
1969 *
1970 * See also %CLUTTER_ACTOR_IS_REALIZED and #ClutterActor:realized.
1971 *
1972 * Returns: %TRUE if the actor is realized
1973 *
1974 * Since: 1.24
1975 */
1976 gboolean
clutter_actor_is_realized(ClutterActor * self)1977 clutter_actor_is_realized (ClutterActor *self)
1978 {
1979 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1980
1981 return CLUTTER_ACTOR_IS_REALIZED (self);
1982 }
1983
1984 static void
clutter_actor_realize_internal(ClutterActor * self)1985 clutter_actor_realize_internal (ClutterActor *self)
1986 {
1987 ClutterActorPrivate *priv = self->priv;
1988
1989 #ifdef CLUTTER_ENABLE_DEBUG
1990 clutter_actor_verify_map_state (self);
1991 #endif
1992
1993 if (CLUTTER_ACTOR_IS_REALIZED (self))
1994 return;
1995
1996 /* To be realized, our parent actors must be realized first.
1997 * This will only succeed if we're inside a toplevel.
1998 */
1999 if (priv->parent != NULL)
2000 clutter_actor_realize (priv->parent);
2001
2002 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
2003 {
2004 /* toplevels can be realized at any time */
2005 }
2006 else
2007 {
2008 /* "Fail" the realization if parent is missing or unrealized;
2009 * this should really be a g_warning() not some kind of runtime
2010 * failure; how can an app possibly recover? Instead it's a bug
2011 * in the app and the app should get an explanatory warning so
2012 * someone can fix it. But for now it's too hard to fix this
2013 * because e.g. ClutterTexture needs reworking.
2014 */
2015 if (priv->parent == NULL ||
2016 !CLUTTER_ACTOR_IS_REALIZED (priv->parent))
2017 return;
2018 }
2019
2020 CLUTTER_NOTE (ACTOR, "Realizing actor '%s'", _clutter_actor_get_debug_name (self));
2021
2022 CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2023 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2024
2025 g_signal_emit (self, actor_signals[REALIZE], 0);
2026
2027 /* Stage actor is allowed to unset the realized flag again in its
2028 * default signal handler, though that is a pathological situation.
2029 */
2030
2031 /* If realization "failed" we'll have to update child state. */
2032 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
2033 }
2034
2035 static void
clutter_actor_real_unrealize(ClutterActor * self)2036 clutter_actor_real_unrealize (ClutterActor *self)
2037 {
2038 /* we must be unmapped (implying our children are also unmapped) */
2039 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2040 }
2041
2042 /**
2043 * clutter_actor_unrealize:
2044 * @self: A #ClutterActor
2045 *
2046 * Unrealization informs the actor that it may be being destroyed or
2047 * moved to another stage. The actor may want to destroy any
2048 * underlying graphics resources at this point. However it is
2049 * perfectly acceptable for it to retain the resources until the actor
2050 * is destroyed because Clutter only ever uses a single rendering
2051 * context and all of the graphics resources are valid on any stage.
2052 *
2053 * Because mapped actors must be realized, actors may not be
2054 * unrealized if they are mapped. This function hides the actor to be
2055 * sure it isn't mapped, an application-visible side effect that you
2056 * may not be expecting.
2057 *
2058 * This function should not be called by application code.
2059 *
2060 * This function should not really be in the public API, because
2061 * there isn't a good reason to call it. ClutterActor will already
2062 * unrealize things for you when it's important to do so.
2063 *
2064 * If you were using clutter_actor_unrealize() in a dispose
2065 * implementation, then don't, just chain up to ClutterActor's
2066 * dispose.
2067 *
2068 * If you were using clutter_actor_unrealize() to implement
2069 * unrealizing children of your container, then don't, ClutterActor
2070 * will already take care of that.
2071 *
2072 * Deprecated: 1.16: Actors are automatically unrealized, and nothing
2073 * requires explicit realization.
2074 */
2075 void
clutter_actor_unrealize(ClutterActor * self)2076 clutter_actor_unrealize (ClutterActor *self)
2077 {
2078 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2079 g_return_if_fail (!CLUTTER_ACTOR_IS_MAPPED (self));
2080
2081 clutter_actor_unrealize_internal (self);
2082 }
2083
2084 /* If you were using clutter_actor_unrealize() to re-realize to
2085 * create your resources in a different way, then use
2086 * _clutter_actor_rerealize() (inside Clutter) or just call your
2087 * code that recreates your resources directly (outside Clutter).
2088 */
2089 static void
clutter_actor_unrealize_internal(ClutterActor * self)2090 clutter_actor_unrealize_internal (ClutterActor *self)
2091 {
2092 #ifdef CLUTTER_ENABLE_DEBUG
2093 clutter_actor_verify_map_state (self);
2094 #endif
2095
2096 clutter_actor_hide (self);
2097
2098 clutter_actor_unrealize_not_hiding (self);
2099 }
2100
2101 static ClutterActorTraverseVisitFlags
unrealize_actor_before_children_cb(ClutterActor * self,int depth,void * user_data)2102 unrealize_actor_before_children_cb (ClutterActor *self,
2103 int depth,
2104 void *user_data)
2105 {
2106 /* If an actor is already unrealized we know its children have also
2107 * already been unrealized... */
2108 if (!CLUTTER_ACTOR_IS_REALIZED (self))
2109 return CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN;
2110
2111 g_signal_emit (self, actor_signals[UNREALIZE], 0);
2112
2113 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2114 }
2115
2116 static ClutterActorTraverseVisitFlags
unrealize_actor_after_children_cb(ClutterActor * self,int depth,void * user_data)2117 unrealize_actor_after_children_cb (ClutterActor *self,
2118 int depth,
2119 void *user_data)
2120 {
2121 ClutterActorPrivate *priv = self->priv;
2122 ClutterActor *stage = user_data;
2123
2124 /* We want to unset the realized flag only _after_
2125 * child actors are unrealized, to maintain invariants.
2126 */
2127 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2128 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2129
2130 if (stage != NULL &&
2131 priv->parent != NULL &&
2132 priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)
2133 clutter_stage_dequeue_actor_relayout (CLUTTER_STAGE (stage), self);
2134
2135 if (stage != NULL)
2136 clutter_stage_dequeue_actor_redraw (CLUTTER_STAGE (stage), self);
2137
2138 if (priv->unmapped_paint_branch_counter == 0)
2139 priv->allocation = (ClutterActorBox) CLUTTER_ACTOR_BOX_UNINITIALIZED;
2140
2141 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2142 }
2143
2144 /*
2145 * clutter_actor_unrealize_not_hiding:
2146 * @self: A #ClutterActor
2147 *
2148 * Unrealization informs the actor that it may be being destroyed or
2149 * moved to another stage. The actor may want to destroy any
2150 * underlying graphics resources at this point. However it is
2151 * perfectly acceptable for it to retain the resources until the actor
2152 * is destroyed because Clutter only ever uses a single rendering
2153 * context and all of the graphics resources are valid on any stage.
2154 *
2155 * Because mapped actors must be realized, actors may not be
2156 * unrealized if they are mapped. You must hide the actor or one of
2157 * its parents before attempting to unrealize.
2158 *
2159 * This function is separate from clutter_actor_unrealize() because it
2160 * does not automatically hide the actor.
2161 * Actors need not be hidden to be unrealized, they just need to
2162 * be unmapped. In fact we don't want to mess up the application's
2163 * setting of the "visible" flag, so hiding is very undesirable.
2164 *
2165 * clutter_actor_unrealize() does a clutter_actor_hide() just for
2166 * backward compatibility.
2167 */
2168 static void
clutter_actor_unrealize_not_hiding(ClutterActor * self)2169 clutter_actor_unrealize_not_hiding (ClutterActor *self)
2170 {
2171 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2172
2173 _clutter_actor_traverse (self,
2174 CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
2175 unrealize_actor_before_children_cb,
2176 unrealize_actor_after_children_cb,
2177 stage);
2178 }
2179
2180 /*
2181 * _clutter_actor_rerealize:
2182 * @self: A #ClutterActor
2183 * @callback: Function to call while unrealized
2184 * @data: data for callback
2185 *
2186 * If an actor is already unrealized, this just calls the callback.
2187 *
2188 * If it is realized, it unrealizes temporarily, calls the callback,
2189 * and then re-realizes the actor.
2190 *
2191 * As a side effect, leaves all children of the actor unrealized if
2192 * the actor was realized but not showing. This is because when we
2193 * unrealize the actor temporarily we must unrealize its children
2194 * (e.g. children of a stage can't be realized if stage window is
2195 * gone). And we aren't clever enough to save the realization state of
2196 * all children. In most cases this should not matter, because
2197 * the children will automatically realize when they next become mapped.
2198 */
2199 void
_clutter_actor_rerealize(ClutterActor * self,ClutterCallback callback,void * data)2200 _clutter_actor_rerealize (ClutterActor *self,
2201 ClutterCallback callback,
2202 void *data)
2203 {
2204 gboolean was_mapped;
2205 gboolean was_showing;
2206 gboolean was_realized;
2207
2208 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2209
2210 #ifdef CLUTTER_ENABLE_DEBUG
2211 clutter_actor_verify_map_state (self);
2212 #endif
2213
2214 was_realized = CLUTTER_ACTOR_IS_REALIZED (self);
2215 was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
2216 was_showing = CLUTTER_ACTOR_IS_VISIBLE (self);
2217
2218 /* Must be unmapped to unrealize. Note we only have to hide this
2219 * actor if it was mapped (if all parents were showing). If actor
2220 * is merely visible (but not mapped), then that's fine, we can
2221 * leave it visible.
2222 */
2223 if (was_mapped)
2224 clutter_actor_hide (self);
2225
2226 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2227
2228 /* unrealize self and all children */
2229 clutter_actor_unrealize_not_hiding (self);
2230
2231 if (callback != NULL)
2232 {
2233 (* callback) (self, data);
2234 }
2235
2236 if (was_showing)
2237 clutter_actor_show (self); /* will realize only if mapping implies it */
2238 else if (was_realized)
2239 clutter_actor_realize (self); /* realize self and all parents */
2240 }
2241
2242 static void
clutter_actor_real_pick(ClutterActor * self,ClutterPickContext * pick_context)2243 clutter_actor_real_pick (ClutterActor *self,
2244 ClutterPickContext *pick_context)
2245 {
2246 ClutterActorPrivate *priv = self->priv;
2247
2248 if (clutter_actor_should_pick (self, pick_context))
2249 {
2250 ClutterActorBox box = {
2251 .x1 = 0,
2252 .y1 = 0,
2253 .x2 = priv->allocation.x2 - priv->allocation.x1,
2254 .y2 = priv->allocation.y2 - priv->allocation.y1,
2255 };
2256
2257 clutter_actor_pick_box (self, pick_context, &box);
2258 }
2259
2260 /* XXX - this thoroughly sucks, but we need to maintain compatibility
2261 * with existing container classes that override the pick() virtual
2262 * and chain up to the default implementation - otherwise we'll end up
2263 * painting our children twice.
2264 *
2265 * this has to go away for 2.0; hopefully along the pick() itself.
2266 */
2267 if (CLUTTER_ACTOR_GET_CLASS (self)->pick == clutter_actor_real_pick)
2268 {
2269 ClutterActor *iter;
2270
2271 for (iter = self->priv->first_child;
2272 iter != NULL;
2273 iter = iter->priv->next_sibling)
2274 clutter_actor_pick (iter, pick_context);
2275 }
2276 }
2277
2278 /**
2279 * clutter_actor_should_pick:
2280 * @self: A #ClutterActor
2281 * @pick_context: a #ClutterPickContext
2282 *
2283 * Should be called inside the implementation of the
2284 * #ClutterActor::pick virtual function in order to check whether
2285 * the actor should be picked or not.
2286 *
2287 * This function should never be called directly by applications.
2288 *
2289 * Return value: %TRUE if the actor should be picked, %FALSE otherwise
2290 */
2291 gboolean
clutter_actor_should_pick(ClutterActor * self,ClutterPickContext * pick_context)2292 clutter_actor_should_pick (ClutterActor *self,
2293 ClutterPickContext *pick_context)
2294 {
2295 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2296
2297 if (CLUTTER_ACTOR_IS_MAPPED (self) &&
2298 clutter_actor_box_is_initialized (&self->priv->allocation) &&
2299 (clutter_pick_context_get_mode (pick_context) == CLUTTER_PICK_ALL ||
2300 CLUTTER_ACTOR_IS_REACTIVE (self)))
2301 return TRUE;
2302
2303 return FALSE;
2304 }
2305
2306 static void
clutter_actor_real_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)2307 clutter_actor_real_get_preferred_width (ClutterActor *self,
2308 gfloat for_height,
2309 gfloat *min_width_p,
2310 gfloat *natural_width_p)
2311 {
2312 ClutterActorPrivate *priv = self->priv;
2313
2314 if (priv->layout_manager != NULL)
2315 {
2316 ClutterContainer *container = CLUTTER_CONTAINER (self);
2317
2318 CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2319 "for the preferred width",
2320 G_OBJECT_TYPE_NAME (priv->layout_manager),
2321 priv->layout_manager);
2322
2323 clutter_layout_manager_get_preferred_width (priv->layout_manager,
2324 container,
2325 for_height,
2326 min_width_p,
2327 natural_width_p);
2328
2329 return;
2330 }
2331
2332 /* Default implementation is always 0x0, usually an actor
2333 * using this default is relying on someone to set the
2334 * request manually
2335 */
2336 CLUTTER_NOTE (LAYOUT, "Default preferred width: 0, 0");
2337
2338 if (min_width_p)
2339 *min_width_p = 0;
2340
2341 if (natural_width_p)
2342 *natural_width_p = 0;
2343 }
2344
2345 static void
clutter_actor_real_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)2346 clutter_actor_real_get_preferred_height (ClutterActor *self,
2347 gfloat for_width,
2348 gfloat *min_height_p,
2349 gfloat *natural_height_p)
2350 {
2351 ClutterActorPrivate *priv = self->priv;
2352
2353 if (priv->layout_manager != NULL)
2354 {
2355 ClutterContainer *container = CLUTTER_CONTAINER (self);
2356
2357 CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2358 "for the preferred height",
2359 G_OBJECT_TYPE_NAME (priv->layout_manager),
2360 priv->layout_manager);
2361
2362 clutter_layout_manager_get_preferred_height (priv->layout_manager,
2363 container,
2364 for_width,
2365 min_height_p,
2366 natural_height_p);
2367
2368 return;
2369 }
2370 /* Default implementation is always 0x0, usually an actor
2371 * using this default is relying on someone to set the
2372 * request manually
2373 */
2374 CLUTTER_NOTE (LAYOUT, "Default preferred height: 0, 0");
2375
2376 if (min_height_p)
2377 *min_height_p = 0;
2378
2379 if (natural_height_p)
2380 *natural_height_p = 0;
2381 }
2382
2383 static void
clutter_actor_store_old_geometry(ClutterActor * self,ClutterActorBox * box)2384 clutter_actor_store_old_geometry (ClutterActor *self,
2385 ClutterActorBox *box)
2386 {
2387 *box = self->priv->allocation;
2388 }
2389
2390 static inline void
clutter_actor_notify_if_geometry_changed(ClutterActor * self,const ClutterActorBox * old)2391 clutter_actor_notify_if_geometry_changed (ClutterActor *self,
2392 const ClutterActorBox *old)
2393 {
2394 ClutterActorPrivate *priv = self->priv;
2395 GObject *obj = G_OBJECT (self);
2396
2397 g_object_freeze_notify (obj);
2398
2399 /* to avoid excessive requisition or allocation cycles we
2400 * use the cached values.
2401 *
2402 * - if we don't have an allocation we assume that we need
2403 * to notify anyway
2404 * - if we don't have a width or a height request we notify
2405 * width and height
2406 * - if we have a valid allocation then we check the old
2407 * bounding box with the current allocation and we notify
2408 * the changes
2409 */
2410 if (priv->needs_allocation)
2411 {
2412 g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2413 g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2414 g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2415 g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2416 g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2417 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2418 }
2419 else if (priv->needs_width_request || priv->needs_height_request)
2420 {
2421 g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2422 g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2423 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2424 }
2425 else
2426 {
2427 gfloat x, y;
2428 gfloat width, height;
2429
2430 x = priv->allocation.x1;
2431 y = priv->allocation.y1;
2432 width = priv->allocation.x2 - priv->allocation.x1;
2433 height = priv->allocation.y2 - priv->allocation.y1;
2434
2435 if (x != old->x1)
2436 {
2437 g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2438 g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2439 }
2440
2441 if (y != old->y1)
2442 {
2443 g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2444 g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2445 }
2446
2447 if (width != (old->x2 - old->x1))
2448 {
2449 g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2450 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2451 }
2452
2453 if (height != (old->y2 - old->y1))
2454 {
2455 g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2456 g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2457 }
2458 }
2459
2460 g_object_thaw_notify (obj);
2461 }
2462
2463 static void
absolute_geometry_changed(ClutterActor * actor)2464 absolute_geometry_changed (ClutterActor *actor)
2465 {
2466 queue_update_stage_views (actor);
2467 }
2468
2469 static ClutterActorTraverseVisitFlags
absolute_geometry_changed_cb(ClutterActor * actor,int depth,gpointer user_data)2470 absolute_geometry_changed_cb (ClutterActor *actor,
2471 int depth,
2472 gpointer user_data)
2473 {
2474 absolute_geometry_changed (actor);
2475
2476 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2477 }
2478
2479 static void
transform_changed(ClutterActor * actor)2480 transform_changed (ClutterActor *actor)
2481 {
2482 actor->priv->transform_valid = FALSE;
2483
2484 if (actor->priv->parent)
2485 queue_update_paint_volume (actor->priv->parent);
2486
2487 _clutter_actor_traverse (actor,
2488 CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
2489 absolute_geometry_changed_cb,
2490 NULL,
2491 NULL);
2492 }
2493
2494 /*< private >
2495 * clutter_actor_set_allocation_internal:
2496 * @self: a #ClutterActor
2497 * @box: a #ClutterActorBox
2498 * @flags: allocation flags
2499 *
2500 * Stores the allocation of @self.
2501 *
2502 * This function only performs basic storage and property notification.
2503 *
2504 * This function should be called by clutter_actor_set_allocation()
2505 * and by the default implementation of #ClutterActorClass.allocate().
2506 *
2507 * Return value: %TRUE if the allocation of the #ClutterActor has been
2508 * changed, and %FALSE otherwise
2509 */
2510 static inline void
clutter_actor_set_allocation_internal(ClutterActor * self,const ClutterActorBox * box)2511 clutter_actor_set_allocation_internal (ClutterActor *self,
2512 const ClutterActorBox *box)
2513 {
2514 ClutterActorPrivate *priv = self->priv;
2515 GObject *obj;
2516 gboolean origin_changed, size_changed;
2517 ClutterActorBox old_alloc = { 0, };
2518
2519 g_return_if_fail (!isnan (box->x1) && !isnan (box->x2) &&
2520 !isnan (box->y1) && !isnan (box->y2));
2521
2522 obj = G_OBJECT (self);
2523
2524 g_object_freeze_notify (obj);
2525
2526 clutter_actor_store_old_geometry (self, &old_alloc);
2527
2528 origin_changed =
2529 priv->allocation.x1 != box->x1 || priv->allocation.y1 != box->y1;
2530 size_changed =
2531 priv->allocation.x2 - priv->allocation.x1 != box->x2 - box->x1 ||
2532 priv->allocation.y2 - priv->allocation.y1 != box->y2 - box->y1;
2533
2534 priv->allocation = *box;
2535
2536 /* allocation is authoritative */
2537 priv->needs_width_request = FALSE;
2538 priv->needs_height_request = FALSE;
2539 priv->needs_allocation = FALSE;
2540
2541 if (origin_changed || size_changed)
2542 {
2543 CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed",
2544 _clutter_actor_get_debug_name (self));
2545
2546 /* This will also call absolute_geometry_changed() on the subtree */
2547 transform_changed (self);
2548
2549 if (size_changed)
2550 queue_update_paint_volume (self);
2551
2552 g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
2553
2554 /* if the allocation changes, so does the content box */
2555 if (priv->content != NULL)
2556 {
2557 priv->content_box_valid = FALSE;
2558 g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]);
2559 }
2560 }
2561
2562 clutter_actor_notify_if_geometry_changed (self, &old_alloc);
2563
2564 g_object_thaw_notify (obj);
2565 }
2566
2567 static void
clutter_actor_real_allocate(ClutterActor * self,const ClutterActorBox * box)2568 clutter_actor_real_allocate (ClutterActor *self,
2569 const ClutterActorBox *box)
2570 {
2571 ClutterActorPrivate *priv = self->priv;
2572
2573 g_object_freeze_notify (G_OBJECT (self));
2574
2575 clutter_actor_set_allocation_internal (self, box);
2576
2577 /* we allocate our children before we notify changes in our geometry,
2578 * so that people connecting to properties will be able to get valid
2579 * data out of the sub-tree of the scene graph that has this actor at
2580 * the root.
2581 */
2582 if (priv->n_children != 0 &&
2583 priv->layout_manager != NULL)
2584 {
2585 ClutterActorBox children_box;
2586
2587 /* normalize the box passed to the layout manager */
2588 children_box.x1 = children_box.y1 = 0.f;
2589 children_box.x2 = box->x2 - box->x1;
2590 children_box.y2 = box->y2 - box->y1;
2591
2592 CLUTTER_NOTE (LAYOUT,
2593 "Allocating %d children of %s "
2594 "at { %.2f, %.2f - %.2f x %.2f } "
2595 "using %s",
2596 priv->n_children,
2597 _clutter_actor_get_debug_name (self),
2598 box->x1,
2599 box->y1,
2600 (box->x2 - box->x1),
2601 (box->y2 - box->y1),
2602 G_OBJECT_TYPE_NAME (priv->layout_manager));
2603
2604 clutter_layout_manager_allocate (priv->layout_manager,
2605 CLUTTER_CONTAINER (self),
2606 &children_box);
2607 }
2608
2609 g_object_thaw_notify (G_OBJECT (self));
2610 }
2611
2612 static void
_clutter_actor_queue_redraw_on_clones(ClutterActor * self)2613 _clutter_actor_queue_redraw_on_clones (ClutterActor *self)
2614 {
2615 ClutterActorPrivate *priv = self->priv;
2616 GHashTableIter iter;
2617 gpointer key;
2618
2619 if (priv->clones == NULL)
2620 return;
2621
2622 g_hash_table_iter_init (&iter, priv->clones);
2623 while (g_hash_table_iter_next (&iter, &key, NULL))
2624 clutter_actor_queue_redraw (key);
2625 }
2626
2627 static void
_clutter_actor_propagate_queue_redraw(ClutterActor * self,ClutterActor * origin)2628 _clutter_actor_propagate_queue_redraw (ClutterActor *self,
2629 ClutterActor *origin)
2630 {
2631 while (self)
2632 {
2633 /* no point in queuing a redraw on a destroyed actor */
2634 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2635 break;
2636
2637 _clutter_actor_queue_redraw_on_clones (self);
2638
2639 /* If the queue redraw is coming from a child then the actor has
2640 become dirty and any queued effect is no longer valid */
2641 if (self != origin)
2642 {
2643 self->priv->is_dirty = TRUE;
2644 self->priv->effect_to_redraw = NULL;
2645 }
2646
2647 /* If the actor isn't visible, we still had to emit the signal
2648 * to allow for a ClutterClone, but the appearance of the parent
2649 * won't change so we don't have to propagate up the hierarchy.
2650 */
2651 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
2652 break;
2653
2654 /* We guarantee that we will propagate a queue-redraw up the tree
2655 * at least once so that all clones can get notified.
2656 */
2657 if (self->priv->propagated_one_redraw)
2658 break;
2659
2660 self->priv->propagated_one_redraw = TRUE;
2661
2662 self = self->priv->parent;
2663 }
2664 }
2665
2666 static inline gboolean
clutter_actor_needs_relayout(ClutterActor * self)2667 clutter_actor_needs_relayout (ClutterActor *self)
2668 {
2669 ClutterActorPrivate *priv = self->priv;
2670
2671 return (priv->needs_width_request ||
2672 priv->needs_height_request ||
2673 priv->needs_allocation);
2674 }
2675
2676 static void
clutter_actor_real_queue_relayout(ClutterActor * self)2677 clutter_actor_real_queue_relayout (ClutterActor *self)
2678 {
2679 ClutterActorPrivate *priv = self->priv;
2680
2681 /* no point in queueing a redraw on a destroyed actor */
2682 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2683 return;
2684
2685 priv->needs_width_request = TRUE;
2686 priv->needs_height_request = TRUE;
2687 priv->needs_allocation = TRUE;
2688
2689 /* reset the cached size requests */
2690 memset (priv->width_requests, 0,
2691 N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2692 memset (priv->height_requests, 0,
2693 N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2694
2695 /* We may need to go all the way up the hierarchy */
2696 if (priv->parent != NULL)
2697 {
2698 if (priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)
2699 clutter_actor_queue_shallow_relayout (self);
2700 else
2701 _clutter_actor_queue_only_relayout (priv->parent);
2702 }
2703 }
2704
2705 /**
2706 * clutter_actor_apply_relative_transform_to_point:
2707 * @self: A #ClutterActor
2708 * @ancestor: (allow-none): A #ClutterActor ancestor, or %NULL to use the
2709 * default #ClutterStage
2710 * @point: A point as #graphene_point3d_t
2711 * @vertex: (out caller-allocates): The translated #graphene_point3d_t
2712 *
2713 * Transforms @point in coordinates relative to the actor into
2714 * ancestor-relative coordinates using the relevant transform
2715 * stack (i.e. scale, rotation, etc).
2716 *
2717 * If @ancestor is %NULL the ancestor will be the #ClutterStage. In
2718 * this case, the coordinates returned will be the coordinates on
2719 * the stage before the projection is applied. This is different from
2720 * the behaviour of clutter_actor_apply_transform_to_point().
2721 *
2722 * Since: 0.6
2723 */
2724 void
clutter_actor_apply_relative_transform_to_point(ClutterActor * self,ClutterActor * ancestor,const graphene_point3d_t * point,graphene_point3d_t * vertex)2725 clutter_actor_apply_relative_transform_to_point (ClutterActor *self,
2726 ClutterActor *ancestor,
2727 const graphene_point3d_t *point,
2728 graphene_point3d_t *vertex)
2729 {
2730 gfloat w;
2731 graphene_matrix_t matrix;
2732
2733 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2734 g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
2735 g_return_if_fail (point != NULL);
2736 g_return_if_fail (vertex != NULL);
2737
2738 *vertex = *point;
2739 w = 1.0;
2740
2741 if (ancestor == NULL)
2742 ancestor = _clutter_actor_get_stage_internal (self);
2743
2744 if (ancestor == NULL)
2745 {
2746 *vertex = *point;
2747 return;
2748 }
2749
2750 _clutter_actor_get_relative_transformation_matrix (self, ancestor, &matrix);
2751 cogl_graphene_matrix_project_point (&matrix,
2752 &vertex->x,
2753 &vertex->y,
2754 &vertex->z,
2755 &w);
2756 }
2757
2758 static gboolean
_clutter_actor_fully_transform_vertices(ClutterActor * self,const graphene_point3d_t * vertices_in,graphene_point3d_t * vertices_out,int n_vertices)2759 _clutter_actor_fully_transform_vertices (ClutterActor *self,
2760 const graphene_point3d_t *vertices_in,
2761 graphene_point3d_t *vertices_out,
2762 int n_vertices)
2763 {
2764 ClutterActor *stage;
2765 graphene_matrix_t modelview;
2766 graphene_matrix_t projection;
2767 float viewport[4];
2768
2769 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2770
2771 stage = _clutter_actor_get_stage_internal (self);
2772
2773 /* We really can't do anything meaningful in this case so don't try
2774 * to do any transform */
2775 if (stage == NULL)
2776 return FALSE;
2777
2778 /* Note: we pass NULL as the ancestor because we don't just want the modelview
2779 * that gets us to stage coordinates, we want to go all the way to eye
2780 * coordinates */
2781 _clutter_actor_get_relative_transformation_matrix (self, NULL, &modelview);
2782
2783 /* Fetch the projection and viewport */
2784 _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
2785 _clutter_stage_get_viewport (CLUTTER_STAGE (stage),
2786 &viewport[0],
2787 &viewport[1],
2788 &viewport[2],
2789 &viewport[3]);
2790
2791 _clutter_util_fully_transform_vertices (&modelview,
2792 &projection,
2793 viewport,
2794 vertices_in,
2795 vertices_out,
2796 n_vertices);
2797
2798 return TRUE;
2799 }
2800
2801 /**
2802 * clutter_actor_apply_transform_to_point:
2803 * @self: A #ClutterActor
2804 * @point: A point as #graphene_point3d_t
2805 * @vertex: (out caller-allocates): The translated #graphene_point3d_t
2806 *
2807 * Transforms @point in coordinates relative to the actor
2808 * into screen-relative coordinates with the current actor
2809 * transformation (i.e. scale, rotation, etc)
2810 *
2811 * Since: 0.4
2812 **/
2813 void
clutter_actor_apply_transform_to_point(ClutterActor * self,const graphene_point3d_t * point,graphene_point3d_t * vertex)2814 clutter_actor_apply_transform_to_point (ClutterActor *self,
2815 const graphene_point3d_t *point,
2816 graphene_point3d_t *vertex)
2817 {
2818 g_return_if_fail (point != NULL);
2819 g_return_if_fail (vertex != NULL);
2820 _clutter_actor_fully_transform_vertices (self, point, vertex, 1);
2821 }
2822
2823 /*
2824 * _clutter_actor_get_relative_transformation_matrix:
2825 * @self: The actor whose coordinate space you want to transform from.
2826 * @ancestor: The ancestor actor whose coordinate space you want to transform too
2827 * or %NULL if you want to transform all the way to eye coordinates.
2828 * @matrix: A #graphene_matrix_t to store the transformation
2829 *
2830 * This gets a transformation @matrix that will transform coordinates from the
2831 * coordinate space of @self into the coordinate space of @ancestor.
2832 *
2833 * For example if you need a matrix that can transform the local actor
2834 * coordinates of @self into stage coordinates you would pass the actor's stage
2835 * pointer as the @ancestor.
2836 *
2837 * If you pass %NULL then the transformation will take you all the way through
2838 * to eye coordinates. This can be useful if you want to extract the entire
2839 * modelview transform that Clutter applies before applying the projection
2840 * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
2841 * using cogl_set_modelview_matrix() for example then you would want a matrix
2842 * that transforms into eye coordinates.
2843 *
2844 * Note: This function explicitly initializes the given @matrix. If you just
2845 * want clutter to multiply a relative transformation with an existing matrix
2846 * you can use clutter_actor_apply_relative_transformation_matrix()
2847 * instead.
2848 *
2849 */
2850 /* XXX: We should consider caching the stage relative modelview along with
2851 * the actor itself */
2852 static void
_clutter_actor_get_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,graphene_matrix_t * matrix)2853 _clutter_actor_get_relative_transformation_matrix (ClutterActor *self,
2854 ClutterActor *ancestor,
2855 graphene_matrix_t *matrix)
2856 {
2857 graphene_matrix_init_identity (matrix);
2858
2859 _clutter_actor_apply_relative_transformation_matrix (self, ancestor, matrix);
2860 }
2861
2862 /* Project the given @box into stage window coordinates, writing the
2863 * transformed vertices to @verts[]. */
2864 static gboolean
_clutter_actor_transform_and_project_box(ClutterActor * self,const ClutterActorBox * box,graphene_point3d_t * verts)2865 _clutter_actor_transform_and_project_box (ClutterActor *self,
2866 const ClutterActorBox *box,
2867 graphene_point3d_t *verts)
2868 {
2869 graphene_point3d_t box_vertices[4];
2870
2871 box_vertices[0].x = box->x1;
2872 box_vertices[0].y = box->y1;
2873 box_vertices[0].z = 0;
2874 box_vertices[1].x = box->x2;
2875 box_vertices[1].y = box->y1;
2876 box_vertices[1].z = 0;
2877 box_vertices[2].x = box->x1;
2878 box_vertices[2].y = box->y2;
2879 box_vertices[2].z = 0;
2880 box_vertices[3].x = box->x2;
2881 box_vertices[3].y = box->y2;
2882 box_vertices[3].z = 0;
2883
2884 return
2885 _clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4);
2886 }
2887
2888 /**
2889 * clutter_actor_get_abs_allocation_vertices:
2890 * @self: A #ClutterActor
2891 * @verts: (out) (array fixed-size=4): Pointer to a location of an array
2892 * of 4 #graphene_point3d_t where to store the result.
2893 *
2894 * Calculates the transformed screen coordinates of the four corners of
2895 * the actor; the returned vertices relate to the #ClutterActorBox
2896 * coordinates as follows:
2897 *
2898 * - v[0] contains (x1, y1)
2899 * - v[1] contains (x2, y1)
2900 * - v[2] contains (x1, y2)
2901 * - v[3] contains (x2, y2)
2902 *
2903 * Since: 0.4
2904 */
2905 void
clutter_actor_get_abs_allocation_vertices(ClutterActor * self,graphene_point3d_t * verts)2906 clutter_actor_get_abs_allocation_vertices (ClutterActor *self,
2907 graphene_point3d_t *verts)
2908 {
2909 ClutterActorPrivate *priv;
2910 ClutterActorBox actor_space_allocation;
2911
2912 g_return_if_fail (CLUTTER_IS_ACTOR (self));
2913
2914 priv = self->priv;
2915
2916 /* if the actor needs to be allocated we force a relayout, so that
2917 * the actor allocation box will be valid for
2918 * _clutter_actor_transform_and_project_box()
2919 */
2920 if (priv->needs_allocation)
2921 {
2922 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2923 /* There's nothing meaningful we can do now */
2924 if (!stage)
2925 return;
2926
2927 clutter_stage_maybe_relayout (stage);
2928 }
2929
2930 /* NB: _clutter_actor_transform_and_project_box expects a box in the actor's
2931 * own coordinate space... */
2932 actor_space_allocation.x1 = 0;
2933 actor_space_allocation.y1 = 0;
2934 actor_space_allocation.x2 = priv->allocation.x2 - priv->allocation.x1;
2935 actor_space_allocation.y2 = priv->allocation.y2 - priv->allocation.y1;
2936 _clutter_actor_transform_and_project_box (self,
2937 &actor_space_allocation,
2938 verts);
2939 }
2940
2941 static void
clutter_actor_real_apply_transform(ClutterActor * self,graphene_matrix_t * matrix)2942 clutter_actor_real_apply_transform (ClutterActor *self,
2943 graphene_matrix_t *matrix)
2944 {
2945 ClutterActorPrivate *priv = self->priv;
2946 const ClutterTransformInfo *info;
2947 graphene_point3d_t p;
2948 float pivot_x = 0.f, pivot_y = 0.f;
2949
2950 info = _clutter_actor_get_transform_info_or_defaults (self);
2951
2952 /* compute the pivot point given the allocated size */
2953 pivot_x = (priv->allocation.x2 - priv->allocation.x1)
2954 * info->pivot.x;
2955 pivot_y = (priv->allocation.y2 - priv->allocation.y1)
2956 * info->pivot.y;
2957
2958 CLUTTER_NOTE (PAINT,
2959 "Allocation: (%.2f, %2.f), "
2960 "pivot: (%.2f, %.2f), "
2961 "translation: (%.2f, %.2f) -> "
2962 "new origin: (%.2f, %.2f)",
2963 priv->allocation.x1, priv->allocation.y1,
2964 info->pivot.x, info->pivot.y,
2965 info->translation.x, info->translation.y,
2966 priv->allocation.x1 + pivot_x + info->translation.x,
2967 priv->allocation.y1 + pivot_y + info->translation.y);
2968
2969 /* roll back the pivot translation */
2970 if (pivot_x != 0.f || pivot_y != 0.f || info->pivot_z != 0.f)
2971 {
2972 graphene_point3d_init (&p, -pivot_x, -pivot_y, -info->pivot_z);
2973 graphene_matrix_translate (matrix, &p);
2974 }
2975
2976 /* if we have an overriding transformation, we use that, and get out */
2977 if (info->transform_set)
2978 {
2979 graphene_matrix_multiply (matrix, &info->transform, matrix);
2980
2981 /* we still need to apply the :allocation's origin and :pivot-point
2982 * translations, since :transform is relative to the actor's coordinate
2983 * space, and to the pivot point
2984 */
2985 graphene_point3d_init (&p,
2986 priv->allocation.x1 + pivot_x,
2987 priv->allocation.y1 + pivot_y,
2988 info->pivot_z);
2989 graphene_matrix_translate (matrix, &p);
2990 goto roll_back;
2991 }
2992
2993 if (info->rx_angle)
2994 graphene_matrix_rotate (matrix, info->rx_angle, graphene_vec3_x_axis ());
2995
2996 if (info->ry_angle)
2997 graphene_matrix_rotate (matrix, info->ry_angle, graphene_vec3_y_axis ());
2998
2999 if (info->rz_angle)
3000 graphene_matrix_rotate (matrix, info->rz_angle, graphene_vec3_z_axis ());
3001
3002 if (info->scale_x != 1.0 || info->scale_y != 1.0 || info->scale_z != 1.0)
3003 graphene_matrix_scale (matrix, info->scale_x, info->scale_y, info->scale_z);
3004
3005 /* basic translation: :allocation's origin and :z-position; instead
3006 * of decomposing the pivot and translation info separate operations,
3007 * we just compose everything into a single translation
3008 */
3009 graphene_point3d_init (&p,
3010 priv->allocation.x1 + pivot_x + info->translation.x,
3011 priv->allocation.y1 + pivot_y + info->translation.y,
3012 info->z_position + info->pivot_z + info->translation.z);
3013 graphene_matrix_translate (matrix, &p);
3014
3015 roll_back:
3016 /* we apply the :child-transform from the parent actor, if we have one */
3017 if (priv->parent != NULL)
3018 {
3019 const ClutterTransformInfo *parent_info;
3020
3021 parent_info = _clutter_actor_get_transform_info_or_defaults (priv->parent);
3022 graphene_matrix_multiply (matrix, &parent_info->child_transform, matrix);
3023 }
3024 }
3025
3026 /* Applies the transforms associated with this actor to the given
3027 * matrix. */
3028
3029 static void
ensure_valid_actor_transform(ClutterActor * actor)3030 ensure_valid_actor_transform (ClutterActor *actor)
3031 {
3032 ClutterActorPrivate *priv = actor->priv;
3033
3034 if (priv->transform_valid)
3035 return;
3036
3037 graphene_matrix_init_identity (&priv->transform);
3038
3039 CLUTTER_ACTOR_GET_CLASS (actor)->apply_transform (actor, &priv->transform);
3040
3041 priv->transform_valid = TRUE;
3042 }
3043
3044 void
_clutter_actor_apply_modelview_transform(ClutterActor * self,graphene_matrix_t * matrix)3045 _clutter_actor_apply_modelview_transform (ClutterActor *self,
3046 graphene_matrix_t *matrix)
3047 {
3048 ClutterActorPrivate *priv = self->priv;
3049
3050 ensure_valid_actor_transform (self);
3051 graphene_matrix_multiply (&priv->transform, matrix, matrix);
3052 }
3053
3054 /*
3055 * clutter_actor_apply_relative_transformation_matrix:
3056 * @self: The actor whose coordinate space you want to transform from.
3057 * @ancestor: The ancestor actor whose coordinate space you want to transform too
3058 * or %NULL if you want to transform all the way to eye coordinates.
3059 * @matrix: A #graphene_matrix_t to apply the transformation too.
3060 *
3061 * This multiplies a transform with @matrix that will transform coordinates
3062 * from the coordinate space of @self into the coordinate space of @ancestor.
3063 *
3064 * For example if you need a matrix that can transform the local actor
3065 * coordinates of @self into stage coordinates you would pass the actor's stage
3066 * pointer as the @ancestor.
3067 *
3068 * If you pass %NULL then the transformation will take you all the way through
3069 * to eye coordinates. This can be useful if you want to extract the entire
3070 * modelview transform that Clutter applies before applying the projection
3071 * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
3072 * using cogl_set_modelview_matrix() for example then you would want a matrix
3073 * that transforms into eye coordinates.
3074 *
3075 * This function doesn't initialize the given @matrix, it simply
3076 * multiplies the requested transformation matrix with the existing contents of
3077 * @matrix. You can use graphene_matrix_init_identity() to initialize the @matrix
3078 * before calling this function, or you can use
3079 * clutter_actor_get_relative_transformation_matrix() instead.
3080 */
3081 void
_clutter_actor_apply_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,graphene_matrix_t * matrix)3082 _clutter_actor_apply_relative_transformation_matrix (ClutterActor *self,
3083 ClutterActor *ancestor,
3084 graphene_matrix_t *matrix)
3085 {
3086 /* Note we terminate before ever calling stage->apply_transform()
3087 * since that would conceptually be relative to the underlying
3088 * window OpenGL coordinates so we'd need a special @ancestor
3089 * value to represent the fake parent of the stage. */
3090 if (self == ancestor)
3091 return;
3092
3093 if (self->priv->parent != NULL)
3094 _clutter_actor_apply_relative_transformation_matrix (self->priv->parent,
3095 ancestor,
3096 matrix);
3097
3098 _clutter_actor_apply_modelview_transform (self, matrix);
3099 }
3100
3101 static void
_clutter_actor_draw_paint_volume_full(ClutterActor * self,ClutterPaintVolume * pv,const char * label,const ClutterColor * color,ClutterPaintNode * node)3102 _clutter_actor_draw_paint_volume_full (ClutterActor *self,
3103 ClutterPaintVolume *pv,
3104 const char *label,
3105 const ClutterColor *color,
3106 ClutterPaintNode *node)
3107 {
3108 g_autoptr (ClutterPaintNode) pipeline_node = NULL;
3109 static CoglPipeline *outline = NULL;
3110 CoglPrimitive *prim;
3111 graphene_point3d_t line_ends[12 * 2];
3112 int n_vertices;
3113 CoglContext *ctx =
3114 clutter_backend_get_cogl_context (clutter_get_default_backend ());
3115 CoglColor cogl_color;
3116
3117 if (outline == NULL)
3118 outline = cogl_pipeline_new (ctx);
3119
3120 _clutter_paint_volume_complete (pv);
3121
3122 n_vertices = pv->is_2d ? 4 * 2 : 12 * 2;
3123
3124 /* Front face */
3125 line_ends[0] = pv->vertices[0]; line_ends[1] = pv->vertices[1];
3126 line_ends[2] = pv->vertices[1]; line_ends[3] = pv->vertices[2];
3127 line_ends[4] = pv->vertices[2]; line_ends[5] = pv->vertices[3];
3128 line_ends[6] = pv->vertices[3]; line_ends[7] = pv->vertices[0];
3129
3130 if (!pv->is_2d)
3131 {
3132 /* Back face */
3133 line_ends[8] = pv->vertices[4]; line_ends[9] = pv->vertices[5];
3134 line_ends[10] = pv->vertices[5]; line_ends[11] = pv->vertices[6];
3135 line_ends[12] = pv->vertices[6]; line_ends[13] = pv->vertices[7];
3136 line_ends[14] = pv->vertices[7]; line_ends[15] = pv->vertices[4];
3137
3138 /* Lines connecting front face to back face */
3139 line_ends[16] = pv->vertices[0]; line_ends[17] = pv->vertices[4];
3140 line_ends[18] = pv->vertices[1]; line_ends[19] = pv->vertices[5];
3141 line_ends[20] = pv->vertices[2]; line_ends[21] = pv->vertices[6];
3142 line_ends[22] = pv->vertices[3]; line_ends[23] = pv->vertices[7];
3143 }
3144
3145 prim = cogl_primitive_new_p3 (ctx, COGL_VERTICES_MODE_LINES,
3146 n_vertices,
3147 (CoglVertexP3 *)line_ends);
3148
3149 cogl_color_init_from_4ub (&cogl_color,
3150 color->red,
3151 color->green,
3152 color->blue,
3153 color->alpha);
3154 cogl_pipeline_set_color (outline, &cogl_color);
3155
3156 pipeline_node = clutter_pipeline_node_new (outline);
3157 clutter_paint_node_set_static_name (pipeline_node,
3158 "ClutterActor (paint volume outline)");
3159 clutter_paint_node_add_primitive (pipeline_node, prim);
3160 clutter_paint_node_add_child (node, pipeline_node);
3161 cogl_object_unref (prim);
3162
3163 if (label)
3164 {
3165 g_autoptr (ClutterPaintNode) text_node = NULL;
3166 PangoLayout *layout;
3167
3168 layout = pango_layout_new (clutter_actor_get_pango_context (self));
3169 pango_layout_set_text (layout, label, -1);
3170
3171 text_node = clutter_text_node_new (layout, color);
3172 clutter_paint_node_set_static_name (text_node,
3173 "ClutterActor (paint volume label)");
3174 clutter_paint_node_add_rectangle (text_node,
3175 &(ClutterActorBox) {
3176 .x1 = pv->vertices[0].x,
3177 .y1 = pv->vertices[0].y,
3178 .x2 = pv->vertices[2].x,
3179 .y2 = pv->vertices[2].y,
3180 });
3181 clutter_paint_node_add_child (node, text_node);
3182
3183 g_object_unref (layout);
3184 }
3185 }
3186
3187 static void
_clutter_actor_draw_paint_volume(ClutterActor * self,ClutterPaintNode * node)3188 _clutter_actor_draw_paint_volume (ClutterActor *self,
3189 ClutterPaintNode *node)
3190 {
3191 ClutterPaintVolume *pv;
3192 ClutterColor color;
3193
3194 pv = _clutter_actor_get_paint_volume_mutable (self);
3195 if (!pv)
3196 {
3197 gfloat width, height;
3198 ClutterPaintVolume fake_pv;
3199
3200 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
3201 _clutter_paint_volume_init_static (&fake_pv, stage);
3202
3203 clutter_actor_get_size (self, &width, &height);
3204 clutter_paint_volume_set_width (&fake_pv, width);
3205 clutter_paint_volume_set_height (&fake_pv, height);
3206
3207 clutter_color_init (&color, 0, 0, 255, 255);
3208 _clutter_actor_draw_paint_volume_full (self, &fake_pv,
3209 _clutter_actor_get_debug_name (self),
3210 &color,
3211 node);
3212
3213 clutter_paint_volume_free (&fake_pv);
3214 }
3215 else
3216 {
3217 clutter_color_init (&color, 0, 255, 0, 255);
3218 _clutter_actor_draw_paint_volume_full (self, pv,
3219 _clutter_actor_get_debug_name (self),
3220 &color,
3221 node);
3222 }
3223 }
3224
3225 static void
_clutter_actor_paint_cull_result(ClutterActor * self,gboolean success,ClutterCullResult result,ClutterPaintNode * node)3226 _clutter_actor_paint_cull_result (ClutterActor *self,
3227 gboolean success,
3228 ClutterCullResult result,
3229 ClutterPaintNode *node)
3230 {
3231 ClutterActorPrivate *priv = self->priv;
3232 ClutterPaintVolume *pv;
3233 ClutterColor color;
3234
3235 if (success)
3236 {
3237 switch (result)
3238 {
3239 case CLUTTER_CULL_RESULT_IN:
3240 clutter_color_init (&color, 0, 255, 0, 255);
3241 break;
3242 case CLUTTER_CULL_RESULT_OUT:
3243 clutter_color_init (&color, 0, 0, 255, 255);
3244 break;
3245 default:
3246 clutter_color_init (&color, 0, 255, 255, 255);
3247 break;
3248 }
3249 }
3250 else
3251 clutter_color_init (&color, 255, 255, 255, 255);
3252
3253 if (success && (pv = _clutter_actor_get_paint_volume_mutable (self)))
3254 _clutter_actor_draw_paint_volume_full (self, pv,
3255 _clutter_actor_get_debug_name (self),
3256 &color,
3257 node);
3258 else
3259 {
3260 g_autoptr (ClutterPaintNode) text_node = NULL;
3261 PangoLayout *layout;
3262 float width;
3263 float height;
3264 char *label =
3265 g_strdup_printf ("CULL FAILURE: %s", _clutter_actor_get_debug_name (self));
3266 clutter_color_init (&color, 255, 255, 255, 255);
3267
3268 width = clutter_actor_box_get_width (&priv->allocation);
3269 height = clutter_actor_box_get_height (&priv->allocation);
3270
3271 layout = pango_layout_new (clutter_actor_get_pango_context (self));
3272 pango_layout_set_text (layout, label, -1);
3273
3274 text_node = clutter_text_node_new (layout, &color);
3275 clutter_paint_node_set_static_name (text_node,
3276 "ClutterActor (paint volume text)");
3277 clutter_paint_node_add_rectangle (text_node,
3278 &(ClutterActorBox) {
3279 .x1 = 0.f,
3280 .y1 = 0.f,
3281 .x2 = width,
3282 .y2 = height,
3283 });
3284 clutter_paint_node_add_child (node, text_node);
3285
3286 g_free (label);
3287 g_object_unref (layout);
3288 }
3289 }
3290
3291 static int clone_paint_level = 0;
3292
3293 void
_clutter_actor_push_clone_paint(void)3294 _clutter_actor_push_clone_paint (void)
3295 {
3296 clone_paint_level++;
3297 }
3298
3299 void
_clutter_actor_pop_clone_paint(void)3300 _clutter_actor_pop_clone_paint (void)
3301 {
3302 clone_paint_level--;
3303 }
3304
3305 static gboolean
in_clone_paint(void)3306 in_clone_paint (void)
3307 {
3308 return clone_paint_level > 0;
3309 }
3310
3311 /* Returns TRUE if the actor can be ignored */
3312 /* FIXME: we should return a ClutterCullResult, and
3313 * clutter_actor_paint should understand that a CLUTTER_CULL_RESULT_IN
3314 * means there's no point in trying to cull descendants of the current
3315 * node. */
3316 static gboolean
cull_actor(ClutterActor * self,ClutterPaintContext * paint_context,ClutterCullResult * result_out)3317 cull_actor (ClutterActor *self,
3318 ClutterPaintContext *paint_context,
3319 ClutterCullResult *result_out)
3320 {
3321 ClutterActorPrivate *priv = self->priv;
3322 const GArray *clip_frusta;
3323 ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
3324 int i;
3325
3326 if (!priv->last_paint_volume_valid)
3327 {
3328 CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3329 "->last_paint_volume_valid == FALSE",
3330 _clutter_actor_get_debug_name (self));
3331 return FALSE;
3332 }
3333
3334 if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING))
3335 return FALSE;
3336
3337 if (clutter_paint_context_is_drawing_off_stage (paint_context))
3338 {
3339 CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3340 "Drawing off stage",
3341 _clutter_actor_get_debug_name (self));
3342 return FALSE;
3343 }
3344
3345 clip_frusta = clutter_paint_context_get_clip_frusta (paint_context);
3346 if (!clip_frusta)
3347 {
3348 *result_out = result;
3349 return TRUE;
3350 }
3351
3352 for (i = 0; i < clip_frusta->len; i++)
3353 {
3354 const graphene_frustum_t *clip_frustum =
3355 &g_array_index (clip_frusta, graphene_frustum_t, i);
3356
3357 result = _clutter_paint_volume_cull (&priv->last_paint_volume,
3358 clip_frustum);
3359
3360 if (result != CLUTTER_CULL_RESULT_OUT)
3361 break;
3362 }
3363
3364 *result_out = result;
3365
3366 return TRUE;
3367 }
3368
3369 static void
_clutter_actor_update_last_paint_volume(ClutterActor * self)3370 _clutter_actor_update_last_paint_volume (ClutterActor *self)
3371 {
3372 ClutterActorPrivate *priv = self->priv;
3373 const ClutterPaintVolume *pv;
3374
3375 if (priv->last_paint_volume_valid)
3376 {
3377 clutter_paint_volume_free (&priv->last_paint_volume);
3378 priv->last_paint_volume_valid = FALSE;
3379 }
3380
3381 pv = clutter_actor_get_paint_volume (self);
3382 if (!pv)
3383 {
3384 CLUTTER_NOTE (CLIPPING, "Bail from update_last_paint_volume (%s): "
3385 "Actor failed to report a paint volume",
3386 _clutter_actor_get_debug_name (self));
3387 return;
3388 }
3389
3390 _clutter_paint_volume_copy_static (pv, &priv->last_paint_volume);
3391
3392 _clutter_paint_volume_transform_relative (&priv->last_paint_volume,
3393 NULL); /* eye coordinates */
3394
3395 priv->last_paint_volume_valid = TRUE;
3396 }
3397
3398 /* This is the same as clutter_actor_add_effect except that it doesn't
3399 queue a redraw and it doesn't notify on the effect property */
3400 static void
_clutter_actor_add_effect_internal(ClutterActor * self,ClutterEffect * effect)3401 _clutter_actor_add_effect_internal (ClutterActor *self,
3402 ClutterEffect *effect)
3403 {
3404 ClutterActorPrivate *priv = self->priv;
3405
3406 if (priv->effects == NULL)
3407 {
3408 priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
3409 priv->effects->actor = self;
3410 }
3411
3412 _clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3413 }
3414
3415 /* This is the same as clutter_actor_remove_effect except that it doesn't
3416 queue a redraw and it doesn't notify on the effect property */
3417 static void
_clutter_actor_remove_effect_internal(ClutterActor * self,ClutterEffect * effect)3418 _clutter_actor_remove_effect_internal (ClutterActor *self,
3419 ClutterEffect *effect)
3420 {
3421 ClutterActorPrivate *priv = self->priv;
3422
3423 if (priv->effects == NULL)
3424 return;
3425
3426 _clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3427
3428 if (_clutter_meta_group_peek_metas (priv->effects) == NULL)
3429 g_clear_object (&priv->effects);
3430 }
3431
3432 static gboolean
needs_flatten_effect(ClutterActor * self)3433 needs_flatten_effect (ClutterActor *self)
3434 {
3435 ClutterActorPrivate *priv = self->priv;
3436
3437 if (G_UNLIKELY (clutter_paint_debug_flags &
3438 CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT))
3439 return FALSE;
3440
3441 /* We need to enable the effect immediately even in ON_IDLE because that can
3442 * only be implemented efficiently within the effect itself.
3443 * If it was implemented here using just priv->is_dirty then we would lose
3444 * the ability to animate opacity without repaints.
3445 */
3446 if ((priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS) ||
3447 (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE))
3448 return TRUE;
3449 else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY)
3450 {
3451 if (clutter_actor_get_paint_opacity (self) < 255 &&
3452 clutter_actor_has_overlaps (self))
3453 return TRUE;
3454 }
3455
3456 return FALSE;
3457 }
3458
3459 static void
add_or_remove_flatten_effect(ClutterActor * self)3460 add_or_remove_flatten_effect (ClutterActor *self)
3461 {
3462 ClutterActorPrivate *priv = self->priv;
3463
3464 /* Add or remove the flatten effect depending on the
3465 offscreen-redirect property. */
3466 if (needs_flatten_effect (self))
3467 {
3468 if (priv->flatten_effect == NULL)
3469 {
3470 ClutterActorMeta *actor_meta;
3471 gint priority;
3472
3473 priv->flatten_effect = _clutter_flatten_effect_new ();
3474 /* Keep a reference to the effect so that we can queue
3475 redraws from it */
3476 g_object_ref_sink (priv->flatten_effect);
3477
3478 /* Set the priority of the effect to high so that it will
3479 always be applied to the actor first. It uses an internal
3480 priority so that it won't be visible to applications */
3481 actor_meta = CLUTTER_ACTOR_META (priv->flatten_effect);
3482 priority = CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH;
3483 _clutter_actor_meta_set_priority (actor_meta, priority);
3484
3485 /* This will add the effect without queueing a redraw */
3486 _clutter_actor_add_effect_internal (self, priv->flatten_effect);
3487 }
3488 }
3489 else
3490 {
3491 if (priv->flatten_effect != NULL)
3492 {
3493 /* Destroy the effect so that it will lose its fbo cache of
3494 the actor */
3495 _clutter_actor_remove_effect_internal (self, priv->flatten_effect);
3496 g_clear_object (&priv->flatten_effect);
3497 }
3498 }
3499 }
3500
3501 static void
clutter_actor_real_paint(ClutterActor * actor,ClutterPaintContext * paint_context)3502 clutter_actor_real_paint (ClutterActor *actor,
3503 ClutterPaintContext *paint_context)
3504 {
3505 ClutterActorPrivate *priv = actor->priv;
3506 ClutterActor *iter;
3507
3508 for (iter = priv->first_child;
3509 iter != NULL;
3510 iter = iter->priv->next_sibling)
3511 {
3512 CLUTTER_NOTE (PAINT, "Painting %s, child of %s, at { %.2f, %.2f - %.2f x %.2f }",
3513 _clutter_actor_get_debug_name (iter),
3514 _clutter_actor_get_debug_name (actor),
3515 iter->priv->allocation.x1,
3516 iter->priv->allocation.y1,
3517 iter->priv->allocation.x2 - iter->priv->allocation.x1,
3518 iter->priv->allocation.y2 - iter->priv->allocation.y1);
3519
3520 clutter_actor_paint (iter, paint_context);
3521 }
3522 }
3523
3524 static gboolean
clutter_actor_paint_node(ClutterActor * actor,ClutterPaintNode * root,ClutterPaintContext * paint_context)3525 clutter_actor_paint_node (ClutterActor *actor,
3526 ClutterPaintNode *root,
3527 ClutterPaintContext *paint_context)
3528 {
3529 ClutterActorPrivate *priv = actor->priv;
3530 ClutterActorBox box;
3531 ClutterColor bg_color;
3532
3533 box.x1 = 0.f;
3534 box.y1 = 0.f;
3535 box.x2 = clutter_actor_box_get_width (&priv->allocation);
3536 box.y2 = clutter_actor_box_get_height (&priv->allocation);
3537
3538 bg_color = priv->bg_color;
3539
3540 if (CLUTTER_ACTOR_IS_TOPLEVEL (actor))
3541 {
3542 ClutterPaintNode *node;
3543 CoglFramebuffer *fb;
3544 CoglBufferBit clear_flags;
3545
3546 fb = clutter_paint_context_get_base_framebuffer (paint_context);
3547
3548 bg_color.alpha = 255;
3549
3550 CLUTTER_NOTE (PAINT, "Stage clear color: (%d, %d, %d, %d)",
3551 bg_color.red,
3552 bg_color.green,
3553 bg_color.blue,
3554 bg_color.alpha);
3555
3556 clear_flags = COGL_BUFFER_BIT_DEPTH;
3557
3558 node = clutter_root_node_new (fb, &bg_color, clear_flags);
3559 clutter_paint_node_set_static_name (node, "stageClear");
3560 clutter_paint_node_add_rectangle (node, &box);
3561 clutter_paint_node_add_child (root, node);
3562 clutter_paint_node_unref (node);
3563 }
3564 else if (priv->bg_color_set &&
3565 !clutter_color_equal (&priv->bg_color, CLUTTER_COLOR_Transparent))
3566 {
3567 ClutterPaintNode *node;
3568
3569 bg_color.alpha = clutter_actor_get_paint_opacity_internal (actor)
3570 * priv->bg_color.alpha
3571 / 255;
3572
3573 node = clutter_color_node_new (&bg_color);
3574 clutter_paint_node_set_static_name (node, "backgroundColor");
3575 clutter_paint_node_add_rectangle (node, &box);
3576 clutter_paint_node_add_child (root, node);
3577 clutter_paint_node_unref (node);
3578 }
3579
3580 if (priv->content != NULL)
3581 _clutter_content_paint_content (priv->content, actor, root, paint_context);
3582
3583 if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL)
3584 CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root);
3585
3586 if (clutter_paint_node_get_n_children (root) == 0)
3587 return FALSE;
3588
3589 #ifdef CLUTTER_ENABLE_DEBUG
3590 if (CLUTTER_HAS_DEBUG (PAINT))
3591 {
3592 /* dump the tree only if we have one */
3593 _clutter_paint_node_dump_tree (root);
3594 }
3595 #endif /* CLUTTER_ENABLE_DEBUG */
3596
3597 clutter_paint_node_paint (root, paint_context);
3598
3599 return TRUE;
3600 }
3601
3602 /**
3603 * clutter_actor_paint:
3604 * @self: A #ClutterActor
3605 *
3606 * Renders the actor to display.
3607 *
3608 * This function should not be called directly by applications.
3609 * Call clutter_actor_queue_redraw() to queue paints, instead.
3610 *
3611 * This function is context-aware, and will either cause a
3612 * regular paint or a pick paint.
3613 *
3614 * This function will call the #ClutterActorClass.paint() virtual
3615 * function.
3616 *
3617 * This function does not paint the actor if the actor is set to 0,
3618 * unless it is performing a pick paint.
3619 */
3620 void
clutter_actor_paint(ClutterActor * self,ClutterPaintContext * paint_context)3621 clutter_actor_paint (ClutterActor *self,
3622 ClutterPaintContext *paint_context)
3623 {
3624 g_autoptr (ClutterPaintNode) actor_node = NULL;
3625 g_autoptr (ClutterPaintNode) root_node = NULL;
3626 ClutterActorPrivate *priv;
3627 ClutterActorBox clip;
3628 gboolean culling_inhibited;
3629 gboolean clip_set = FALSE;
3630
3631 g_return_if_fail (CLUTTER_IS_ACTOR (self));
3632
3633 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
3634 return;
3635
3636 priv = self->priv;
3637 priv->propagated_one_redraw = FALSE;
3638
3639 /* It's an important optimization that we consider painting of
3640 * actors with 0 opacity to be a NOP... */
3641 if (/* ignore top-levels, since they might be transparent */
3642 !CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
3643 /* Use the override opacity if its been set */
3644 ((priv->opacity_override >= 0) ?
3645 priv->opacity_override : priv->opacity) == 0)
3646 return;
3647
3648 /* if we aren't paintable (not in a toplevel with all
3649 * parents paintable) then do nothing.
3650 */
3651 if (!CLUTTER_ACTOR_IS_MAPPED (self))
3652 return;
3653
3654 #ifdef COGL_HAS_TRACING
3655 COGL_TRACE_SCOPED_ANCHOR (ClutterActorPaint);
3656
3657 if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DETAILED_TRACE))
3658 {
3659 COGL_TRACE_BEGIN_ANCHORED (ClutterActorPaint,
3660 "ClutterActor (paint)");
3661 COGL_TRACE_DESCRIBE (ClutterActorPaint,
3662 _clutter_actor_get_debug_name (self));
3663 }
3664 #endif
3665
3666 actor_node = clutter_actor_node_new (self, -1);
3667 root_node = clutter_paint_node_ref (actor_node);
3668
3669 if (priv->has_clip)
3670 {
3671 clip.x1 = priv->clip.origin.x;
3672 clip.y1 = priv->clip.origin.y;
3673 clip.x2 = priv->clip.origin.x + priv->clip.size.width;
3674 clip.y2 = priv->clip.origin.y + priv->clip.size.height;
3675 clip_set = TRUE;
3676 }
3677 else if (priv->clip_to_allocation)
3678 {
3679 clip.x1 = 0.f;
3680 clip.y1 = 0.f;
3681 clip.x2 = priv->allocation.x2 - priv->allocation.x1;
3682 clip.y2 = priv->allocation.y2 - priv->allocation.y1;
3683 clip_set = TRUE;
3684 }
3685
3686 if (clip_set)
3687 {
3688 ClutterPaintNode *clip_node;
3689
3690 clip_node = clutter_clip_node_new ();
3691 clutter_paint_node_add_rectangle (clip_node, &clip);
3692 clutter_paint_node_add_child (clip_node, root_node);
3693 clutter_paint_node_unref (root_node);
3694
3695 root_node = g_steal_pointer (&clip_node);
3696 }
3697
3698 if (priv->enable_model_view_transform)
3699 {
3700 ClutterPaintNode *transform_node;
3701 graphene_matrix_t transform;
3702
3703 clutter_actor_get_transform (self, &transform);
3704
3705 if (!graphene_matrix_is_identity (&transform))
3706 {
3707 transform_node = clutter_transform_node_new (&transform);
3708 clutter_paint_node_add_child (transform_node, root_node);
3709 clutter_paint_node_unref (root_node);
3710
3711 root_node = g_steal_pointer (&transform_node);
3712 }
3713
3714 #ifdef CLUTTER_ENABLE_DEBUG
3715 /* Catch when out-of-band transforms have been made by actors not as part
3716 * of an apply_transform vfunc... */
3717 if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_OOB_TRANSFORMS))
3718 {
3719 graphene_matrix_t expected_matrix;
3720
3721 _clutter_actor_get_relative_transformation_matrix (self, NULL,
3722 &expected_matrix);
3723
3724 if (!graphene_matrix_equal_fast (&transform, &expected_matrix))
3725 {
3726 GString *buf = g_string_sized_new (1024);
3727 ClutterActor *parent;
3728
3729 parent = self;
3730 while (parent != NULL)
3731 {
3732 g_string_append (buf, _clutter_actor_get_debug_name (parent));
3733
3734 if (parent->priv->parent != NULL)
3735 g_string_append (buf, "->");
3736
3737 parent = parent->priv->parent;
3738 }
3739
3740 g_warning ("Unexpected transform found when painting actor "
3741 "\"%s\". This will be caused by one of the actor's "
3742 "ancestors (%s) using the Cogl API directly to transform "
3743 "children instead of using ::apply_transform().",
3744 _clutter_actor_get_debug_name (self),
3745 buf->str);
3746
3747 g_string_free (buf, TRUE);
3748 }
3749 }
3750 #endif /* CLUTTER_ENABLE_DEBUG */
3751 }
3752
3753 /* We check whether we need to add the flatten effect before
3754 * each paint so that we can avoid having a mechanism for
3755 * applications to notify when the value of the
3756 * has_overlaps virtual changes.
3757 */
3758 add_or_remove_flatten_effect (self);
3759
3760 /* We save the current paint volume so that the next time the
3761 * actor queues a redraw we can constrain the redraw to just
3762 * cover the union of the new bounding box and the old.
3763 *
3764 * We also fetch the current paint volume to perform culling so
3765 * we can avoid painting actors outside the current clip region.
3766 *
3767 * If we are painting inside a clone, we should neither update
3768 * the paint volume or use it to cull painting, since the paint
3769 * box represents the location of the source actor on the
3770 * screen.
3771 *
3772 * XXX: We are starting to do a lot of vertex transforms on
3773 * the CPU in a typical paint, so at some point we should
3774 * audit these and consider caching some things.
3775 *
3776 * NB: We don't perform culling while picking at this point because
3777 * clutter-stage.c doesn't setup the clipping planes appropriately.
3778 *
3779 * NB: We don't want to update the last-paint-volume during picking
3780 * because the last-paint-volume is used to determine the old screen
3781 * space location of an actor that has moved so we can know the
3782 * minimal region to redraw to clear an old view of the actor. If we
3783 * update this during picking then by the time we come around to
3784 * paint then the last-paint-volume would likely represent the new
3785 * actor position not the old.
3786 */
3787 culling_inhibited = priv->inhibit_culling_counter > 0;
3788 if (!culling_inhibited && !in_clone_paint ())
3789 {
3790 gboolean success;
3791 gboolean should_cull_out = (clutter_paint_debug_flags &
3792 (CLUTTER_DEBUG_DISABLE_CULLING |
3793 CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
3794 (CLUTTER_DEBUG_DISABLE_CULLING |
3795 CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS);
3796 /* annoyingly gcc warns if uninitialized even though
3797 * the initialization is redundant :-( */
3798 ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
3799
3800 success = should_cull_out
3801 ? cull_actor (self, paint_context, &result)
3802 : FALSE;
3803
3804 if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))
3805 _clutter_actor_paint_cull_result (self, success, result, actor_node);
3806 else if (result == CLUTTER_CULL_RESULT_OUT && success)
3807 return;
3808 }
3809
3810 if (priv->effects == NULL)
3811 priv->next_effect_to_paint = NULL;
3812 else
3813 priv->next_effect_to_paint =
3814 _clutter_meta_group_peek_metas (priv->effects);
3815
3816 if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES))
3817 _clutter_actor_draw_paint_volume (self, actor_node);
3818
3819 clutter_paint_node_paint (root_node, paint_context);
3820
3821 /* If we make it here then the actor has run through a complete
3822 paint run including all the effects so it's no longer dirty */
3823 priv->is_dirty = FALSE;
3824 }
3825
3826 /**
3827 * clutter_actor_continue_paint:
3828 * @self: A #ClutterActor
3829 *
3830 * Run the next stage of the paint sequence. This function should only
3831 * be called within the implementation of the ‘run’ virtual of a
3832 * #ClutterEffect. It will cause the run method of the next effect to
3833 * be applied, or it will paint the actual actor if the current effect
3834 * is the last effect in the chain.
3835 *
3836 * Since: 1.8
3837 */
3838 void
clutter_actor_continue_paint(ClutterActor * self,ClutterPaintContext * paint_context)3839 clutter_actor_continue_paint (ClutterActor *self,
3840 ClutterPaintContext *paint_context)
3841 {
3842 ClutterActorPrivate *priv;
3843
3844 g_return_if_fail (CLUTTER_IS_ACTOR (self));
3845 /* This should only be called from with in the ‘run’ implementation
3846 of a ClutterEffect */
3847 g_return_if_fail (CLUTTER_ACTOR_IN_PAINT (self));
3848
3849 priv = self->priv;
3850
3851 /* Skip any effects that are disabled */
3852 while (priv->next_effect_to_paint &&
3853 !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data))
3854 priv->next_effect_to_paint = priv->next_effect_to_paint->next;
3855
3856 /* If this has come from the last effect then we'll just paint the
3857 actual actor */
3858 if (priv->next_effect_to_paint == NULL)
3859 {
3860 CoglFramebuffer *framebuffer;
3861 ClutterPaintNode *dummy;
3862
3863 /* XXX - this will go away in 2.0, when we can get rid of this
3864 * stuff and switch to a pure retained render tree of PaintNodes
3865 * for the entire frame, starting from the Stage; the paint()
3866 * virtual function can then be called directly.
3867 */
3868 framebuffer = clutter_paint_context_get_base_framebuffer (paint_context);
3869 dummy = _clutter_dummy_node_new (self, framebuffer);
3870 clutter_paint_node_set_static_name (dummy, "Root");
3871
3872 /* XXX - for 1.12, we use the return value of paint_node() to
3873 * decide whether we should call the paint() vfunc.
3874 */
3875 clutter_actor_paint_node (self, dummy, paint_context);
3876 clutter_paint_node_unref (dummy);
3877
3878 CLUTTER_ACTOR_GET_CLASS (self)->paint (self, paint_context);
3879 }
3880 else
3881 {
3882 g_autoptr (ClutterPaintNode) effect_node = NULL;
3883 ClutterEffect *old_current_effect;
3884 ClutterEffectPaintFlags run_flags = 0;
3885
3886 /* Cache the current effect so that we can put it back before
3887 returning */
3888 old_current_effect = priv->current_effect;
3889
3890 priv->current_effect = priv->next_effect_to_paint->data;
3891 priv->next_effect_to_paint = priv->next_effect_to_paint->next;
3892
3893 if (priv->is_dirty)
3894 {
3895 /* If there's an effect queued with this redraw then all
3896 * effects up to that one will be considered dirty. It
3897 * is expected the queued effect will paint the cached
3898 * image and not call clutter_actor_continue_paint again
3899 * (although it should work ok if it does)
3900 */
3901 if (priv->effect_to_redraw == NULL ||
3902 priv->current_effect != priv->effect_to_redraw)
3903 run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
3904 }
3905
3906 if (priv->current_effect == priv->flatten_effect &&
3907 priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE &&
3908 run_flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY)
3909 run_flags |= CLUTTER_EFFECT_PAINT_BYPASS_EFFECT;
3910
3911 effect_node = clutter_effect_node_new (priv->current_effect);
3912
3913 _clutter_effect_paint (priv->current_effect,
3914 effect_node,
3915 paint_context,
3916 run_flags);
3917
3918 clutter_paint_node_paint (effect_node, paint_context);
3919
3920 priv->current_effect = old_current_effect;
3921 }
3922 }
3923
3924 /**
3925 * clutter_actor_pick:
3926 * @actor: A #ClutterActor
3927 *
3928 * Asks @actor to perform a pick.
3929 */
3930 void
clutter_actor_pick(ClutterActor * actor,ClutterPickContext * pick_context)3931 clutter_actor_pick (ClutterActor *actor,
3932 ClutterPickContext *pick_context)
3933 {
3934 ClutterActorPrivate *priv;
3935 ClutterActorBox clip;
3936 gboolean transform_pushed = FALSE;
3937 gboolean clip_set = FALSE;
3938 gboolean should_cull = (clutter_paint_debug_flags &
3939 (CLUTTER_DEBUG_DISABLE_CULLING |
3940 CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
3941 (CLUTTER_DEBUG_DISABLE_CULLING |
3942 CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS);
3943
3944 if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
3945 return;
3946
3947 priv = actor->priv;
3948
3949 /* if we aren't paintable (not in a toplevel with all
3950 * parents paintable) then do nothing.
3951 */
3952 if (!CLUTTER_ACTOR_IS_MAPPED (actor))
3953 return;
3954
3955 /* mark that we are in the paint process */
3956 CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK);
3957
3958 if (should_cull && priv->paint_volume_valid && priv->last_paint_volume_valid)
3959 {
3960 graphene_box_t box;
3961
3962 clutter_paint_volume_to_box (&priv->last_paint_volume, &box);
3963 if (!clutter_pick_context_intersects_box (pick_context, &box))
3964 goto out;
3965 }
3966
3967 if (priv->enable_model_view_transform)
3968 {
3969 graphene_matrix_t matrix;
3970
3971 graphene_matrix_init_identity (&matrix);
3972 _clutter_actor_apply_modelview_transform (actor, &matrix);
3973 if (!graphene_matrix_is_identity (&matrix))
3974 {
3975 clutter_pick_context_push_transform (pick_context, &matrix);
3976 transform_pushed = TRUE;
3977 }
3978 }
3979
3980 if (priv->has_clip)
3981 {
3982 clip.x1 = priv->clip.origin.x;
3983 clip.y1 = priv->clip.origin.y;
3984 clip.x2 = priv->clip.origin.x + priv->clip.size.width;
3985 clip.y2 = priv->clip.origin.y + priv->clip.size.height;
3986 clip_set = TRUE;
3987 }
3988 else if (priv->clip_to_allocation)
3989 {
3990 clip.x1 = 0.f;
3991 clip.y1 = 0.f;
3992 clip.x2 = priv->allocation.x2 - priv->allocation.x1;
3993 clip.y2 = priv->allocation.y2 - priv->allocation.y1;
3994 clip_set = TRUE;
3995 }
3996
3997 if (clip_set)
3998 clutter_pick_context_push_clip (pick_context, &clip);
3999
4000 priv->next_effect_to_paint = NULL;
4001 if (priv->effects)
4002 {
4003 priv->next_effect_to_paint =
4004 _clutter_meta_group_peek_metas (priv->effects);
4005 }
4006
4007 clutter_actor_continue_pick (actor, pick_context);
4008
4009 if (clip_set)
4010 clutter_pick_context_pop_clip (pick_context);
4011
4012 if (transform_pushed)
4013 clutter_pick_context_pop_transform (pick_context);
4014
4015 out:
4016 /* paint sequence complete */
4017 CLUTTER_UNSET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK);
4018 }
4019
4020 /**
4021 * clutter_actor_continue_pick:
4022 * @actor: A #ClutterActor
4023 *
4024 * Run the next stage of the pick sequence. This function should only
4025 * be called within the implementation of the ‘pick’ virtual of a
4026 * #ClutterEffect. It will cause the run method of the next effect to
4027 * be applied, or it will pick the actual actor if the current effect
4028 * is the last effect in the chain.
4029 */
4030 void
clutter_actor_continue_pick(ClutterActor * actor,ClutterPickContext * pick_context)4031 clutter_actor_continue_pick (ClutterActor *actor,
4032 ClutterPickContext *pick_context)
4033 {
4034 ClutterActorPrivate *priv;
4035
4036 g_return_if_fail (CLUTTER_IS_ACTOR (actor));
4037
4038 g_return_if_fail (CLUTTER_ACTOR_IN_PICK (actor));
4039
4040 priv = actor->priv;
4041
4042 /* Skip any effects that are disabled */
4043 while (priv->next_effect_to_paint &&
4044 !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data))
4045 priv->next_effect_to_paint = priv->next_effect_to_paint->next;
4046
4047 /* If this has come from the last effect then we'll just pick the
4048 * actual actor.
4049 */
4050 if (priv->next_effect_to_paint == NULL)
4051 {
4052 /* The actor will log a silhouette of itself to the stage pick log.
4053 *
4054 * XXX:2.0 - Call the pick() virtual directly
4055 */
4056 if (g_signal_has_handler_pending (actor, actor_signals[PICK],
4057 0, TRUE))
4058 g_signal_emit (actor, actor_signals[PICK], 0, pick_context);
4059 else
4060 CLUTTER_ACTOR_GET_CLASS (actor)->pick (actor, pick_context);
4061 }
4062 else
4063 {
4064 ClutterEffect *old_current_effect;
4065
4066 /* Cache the current effect so that we can put it back before
4067 * returning.
4068 */
4069 old_current_effect = priv->current_effect;
4070
4071 priv->current_effect = priv->next_effect_to_paint->data;
4072 priv->next_effect_to_paint = priv->next_effect_to_paint->next;
4073
4074 _clutter_effect_pick (priv->current_effect, pick_context);
4075
4076 priv->current_effect = old_current_effect;
4077 }
4078 }
4079
4080 static void
_clutter_actor_stop_transitions(ClutterActor * self)4081 _clutter_actor_stop_transitions (ClutterActor *self)
4082 {
4083 const ClutterAnimationInfo *info;
4084 GHashTableIter iter;
4085 gpointer value;
4086
4087 info = _clutter_actor_get_animation_info_or_defaults (self);
4088 if (info->transitions == NULL)
4089 return;
4090
4091 g_hash_table_iter_init (&iter, info->transitions);
4092 while (g_hash_table_iter_next (&iter, NULL, &value))
4093 {
4094 TransitionClosure *closure = value;
4095
4096 if (clutter_transition_get_remove_on_complete (closure->transition))
4097 {
4098 g_hash_table_iter_remove (&iter);
4099 }
4100 else
4101 {
4102 /* otherwise we stop it, and the transition will be removed
4103 * later, either by the actor's destruction or by explicit
4104 * removal
4105 */
4106 clutter_timeline_stop (CLUTTER_TIMELINE (closure->transition));
4107 }
4108 }
4109 }
4110
4111 static inline void
remove_child(ClutterActor * self,ClutterActor * child)4112 remove_child (ClutterActor *self,
4113 ClutterActor *child)
4114 {
4115 ClutterActor *prev_sibling, *next_sibling;
4116
4117 prev_sibling = child->priv->prev_sibling;
4118 next_sibling = child->priv->next_sibling;
4119
4120 if (prev_sibling != NULL)
4121 prev_sibling->priv->next_sibling = next_sibling;
4122
4123 if (next_sibling != NULL)
4124 next_sibling->priv->prev_sibling = prev_sibling;
4125
4126 if (self->priv->first_child == child)
4127 self->priv->first_child = next_sibling;
4128
4129 if (self->priv->last_child == child)
4130 self->priv->last_child = prev_sibling;
4131
4132 child->priv->parent = NULL;
4133 child->priv->prev_sibling = NULL;
4134 child->priv->next_sibling = NULL;
4135 }
4136
4137 typedef enum
4138 {
4139 REMOVE_CHILD_DESTROY_META = 1 << 0,
4140 REMOVE_CHILD_EMIT_PARENT_SET = 1 << 1,
4141 REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
4142 REMOVE_CHILD_CHECK_STATE = 1 << 3,
4143 REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 4,
4144 REMOVE_CHILD_STOP_TRANSITIONS = 1 << 5,
4145 REMOVE_CHILD_CLEAR_STAGE_VIEWS = 1 << 6,
4146
4147 /* default flags for public API */
4148 REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
4149 REMOVE_CHILD_DESTROY_META |
4150 REMOVE_CHILD_EMIT_PARENT_SET |
4151 REMOVE_CHILD_EMIT_ACTOR_REMOVED |
4152 REMOVE_CHILD_CHECK_STATE |
4153 REMOVE_CHILD_NOTIFY_FIRST_LAST |
4154 REMOVE_CHILD_CLEAR_STAGE_VIEWS,
4155 } ClutterActorRemoveChildFlags;
4156
4157 /*< private >
4158 * clutter_actor_remove_child_internal:
4159 * @self: a #ClutterActor
4160 * @child: the child of @self that has to be removed
4161 * @flags: control the removal operations
4162 *
4163 * Removes @child from the list of children of @self.
4164 */
4165 static void
clutter_actor_remove_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorRemoveChildFlags flags)4166 clutter_actor_remove_child_internal (ClutterActor *self,
4167 ClutterActor *child,
4168 ClutterActorRemoveChildFlags flags)
4169 {
4170 ClutterActor *old_first, *old_last;
4171 gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
4172 gboolean notify_first_last;
4173 gboolean stop_transitions;
4174 gboolean clear_stage_views;
4175 GObject *obj;
4176
4177 if (self == child)
4178 {
4179 g_warning ("Cannot remove actor '%s' from itself.",
4180 _clutter_actor_get_debug_name (self));
4181 return;
4182 }
4183
4184 destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
4185 emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
4186 emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
4187 check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
4188 notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
4189 stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
4190 clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0;
4191
4192 obj = G_OBJECT (self);
4193 g_object_freeze_notify (obj);
4194
4195 if (stop_transitions)
4196 _clutter_actor_stop_transitions (child);
4197
4198 if (destroy_meta)
4199 clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
4200
4201 if (check_state)
4202 {
4203 /* we need to unrealize *before* we set parent_actor to NULL,
4204 * because in an unrealize method actors are dissociating from the
4205 * stage, which means they need to be able to
4206 * clutter_actor_get_stage().
4207 *
4208 * yhis should unmap and unrealize, unless we're reparenting.
4209 */
4210 clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
4211 }
4212
4213 old_first = self->priv->first_child;
4214 old_last = self->priv->last_child;
4215
4216 remove_child (self, child);
4217
4218 self->priv->n_children -= 1;
4219
4220 self->priv->age += 1;
4221
4222 if (self->priv->in_cloned_branch)
4223 clutter_actor_pop_in_cloned_branch (child, self->priv->in_cloned_branch);
4224
4225 if (self->priv->unmapped_paint_branch_counter)
4226 pop_in_paint_unmapped_branch (child, self->priv->unmapped_paint_branch_counter);
4227
4228 /* if the child that got removed was visible and set to
4229 * expand then we want to reset the parent's state in
4230 * case the child was the only thing that was making it
4231 * expand.
4232 */
4233 if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
4234 (child->priv->needs_compute_expand ||
4235 child->priv->needs_x_expand ||
4236 child->priv->needs_y_expand))
4237 {
4238 clutter_actor_queue_compute_expand (self);
4239 }
4240
4241 /* Only actors which are attached to a stage get notified about changes
4242 * to the stage views, so make sure all the stage-views lists are
4243 * cleared as the child and its children leave the actor tree.
4244 */
4245 if (clear_stage_views && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
4246 clutter_actor_clear_stage_views_recursive (child);
4247
4248 if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
4249 g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
4250
4251 /* we need to emit the signal before dropping the reference */
4252 if (emit_actor_removed)
4253 _clutter_container_emit_actor_removed (CLUTTER_CONTAINER (self), child);
4254
4255 if (notify_first_last)
4256 {
4257 if (old_first != self->priv->first_child)
4258 g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
4259
4260 if (old_last != self->priv->last_child)
4261 g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
4262 }
4263
4264 g_object_thaw_notify (obj);
4265
4266 /* remove the reference we acquired in clutter_actor_add_child() */
4267 g_object_unref (child);
4268 }
4269
4270 static ClutterTransformInfo default_transform_info = {
4271 0.0, /* rotation-x */
4272 0.0, /* rotation-y */
4273 0.0, /* rotation-z */
4274
4275 1.0, 1.0, 1.0, /* scale */
4276
4277 GRAPHENE_POINT3D_INIT_ZERO, /* translation */
4278
4279 0.f, /* z-position */
4280
4281 GRAPHENE_POINT_INIT_ZERO, /* pivot */
4282 0.f, /* pivot-z */
4283
4284 { },
4285 FALSE, /* transform */
4286 { },
4287 FALSE, /* child-transform */
4288 };
4289
4290 static inline const ClutterTransformInfo *
get_default_transform_info(void)4291 get_default_transform_info (void)
4292 {
4293 static gsize initialized = FALSE;
4294
4295 if (G_UNLIKELY (g_once_init_enter (&initialized)))
4296 {
4297 graphene_matrix_init_identity (&default_transform_info.transform);
4298 graphene_matrix_init_identity (&default_transform_info.child_transform);
4299 g_once_init_leave (&initialized, TRUE);
4300 }
4301
4302 return &default_transform_info;
4303 }
4304
4305 /*< private >
4306 * _clutter_actor_get_transform_info_or_defaults:
4307 * @self: a #ClutterActor
4308 *
4309 * Retrieves the ClutterTransformInfo structure associated to an actor.
4310 *
4311 * If the actor does not have a ClutterTransformInfo structure associated
4312 * to it, then the default structure will be returned.
4313 *
4314 * This function should only be used for getters.
4315 *
4316 * Return value: a const pointer to the ClutterTransformInfo structure
4317 */
4318 const ClutterTransformInfo *
_clutter_actor_get_transform_info_or_defaults(ClutterActor * self)4319 _clutter_actor_get_transform_info_or_defaults (ClutterActor *self)
4320 {
4321 ClutterTransformInfo *info;
4322
4323 info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4324 if (info != NULL)
4325 return info;
4326
4327 return get_default_transform_info ();
4328 }
4329
4330 static void
clutter_transform_info_free(gpointer data)4331 clutter_transform_info_free (gpointer data)
4332 {
4333 if (data != NULL)
4334 g_free (data);
4335 }
4336
4337 /*< private >
4338 * _clutter_actor_get_transform_info:
4339 * @self: a #ClutterActor
4340 *
4341 * Retrieves a pointer to the ClutterTransformInfo structure.
4342 *
4343 * If the actor does not have a ClutterTransformInfo associated to it, one
4344 * will be created and initialized to the default values.
4345 *
4346 * This function should be used for setters.
4347 *
4348 * For getters, you should use _clutter_actor_get_transform_info_or_defaults()
4349 * instead.
4350 *
4351 * Return value: (transfer none): a pointer to the ClutterTransformInfo
4352 * structure
4353 */
4354 ClutterTransformInfo *
_clutter_actor_get_transform_info(ClutterActor * self)4355 _clutter_actor_get_transform_info (ClutterActor *self)
4356 {
4357 ClutterTransformInfo *info;
4358
4359 info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4360 if (info == NULL)
4361 {
4362 info = g_new0 (ClutterTransformInfo, 1);
4363
4364 *info = *get_default_transform_info ();
4365
4366 g_object_set_qdata_full (G_OBJECT (self), quark_actor_transform_info,
4367 info,
4368 clutter_transform_info_free);
4369 }
4370
4371 return info;
4372 }
4373
4374 static inline void
clutter_actor_set_pivot_point_internal(ClutterActor * self,const graphene_point_t * pivot)4375 clutter_actor_set_pivot_point_internal (ClutterActor *self,
4376 const graphene_point_t *pivot)
4377 {
4378 ClutterTransformInfo *info;
4379
4380 info = _clutter_actor_get_transform_info (self);
4381 info->pivot = *pivot;
4382
4383 transform_changed (self);
4384
4385 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT]);
4386
4387 clutter_actor_queue_redraw (self);
4388 }
4389
4390 static inline void
clutter_actor_set_pivot_point_z_internal(ClutterActor * self,float pivot_z)4391 clutter_actor_set_pivot_point_z_internal (ClutterActor *self,
4392 float pivot_z)
4393 {
4394 ClutterTransformInfo *info;
4395
4396 info = _clutter_actor_get_transform_info (self);
4397 info->pivot_z = pivot_z;
4398
4399 transform_changed (self);
4400
4401 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT_Z]);
4402
4403 clutter_actor_queue_redraw (self);
4404 }
4405
4406 /*< private >
4407 * clutter_actor_set_translation_internal:
4408 * @self: a #ClutterActor
4409 * @axis: the axis of the translation to change
4410 * @angle: the translation as a value along @axis
4411 *
4412 * Sets the translation on the given @axis
4413 */
4414 static void
clutter_actor_set_translation_internal(ClutterActor * self,gfloat value,GParamSpec * pspec)4415 clutter_actor_set_translation_internal (ClutterActor *self,
4416 gfloat value,
4417 GParamSpec *pspec)
4418 {
4419 GObject *obj = G_OBJECT (self);
4420 ClutterTransformInfo *info;
4421
4422 info = _clutter_actor_get_transform_info (self);
4423
4424 if (pspec == obj_props[PROP_TRANSLATION_X])
4425 info->translation.x = value;
4426 else if (pspec == obj_props[PROP_TRANSLATION_Y])
4427 info->translation.y = value;
4428 else if (pspec == obj_props[PROP_TRANSLATION_Z])
4429 info->translation.z = value;
4430 else
4431 g_assert_not_reached ();
4432
4433 transform_changed (self);
4434
4435 clutter_actor_queue_redraw (self);
4436 g_object_notify_by_pspec (obj, pspec);
4437 }
4438
4439 static inline void
clutter_actor_set_translation_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble value)4440 clutter_actor_set_translation_factor (ClutterActor *self,
4441 ClutterRotateAxis axis,
4442 gdouble value)
4443 {
4444 const ClutterTransformInfo *info;
4445 const float *translate_p = NULL;
4446 GParamSpec *pspec = NULL;
4447
4448 info = _clutter_actor_get_transform_info_or_defaults (self);
4449
4450 switch (axis)
4451 {
4452 case CLUTTER_X_AXIS:
4453 pspec = obj_props[PROP_TRANSLATION_X];
4454 translate_p = &info->translation.x;
4455 break;
4456
4457 case CLUTTER_Y_AXIS:
4458 pspec = obj_props[PROP_TRANSLATION_Y];
4459 translate_p = &info->translation.y;
4460 break;
4461
4462 case CLUTTER_Z_AXIS:
4463 pspec = obj_props[PROP_TRANSLATION_Z];
4464 translate_p = &info->translation.z;
4465 break;
4466 }
4467
4468 g_assert (pspec != NULL);
4469 g_assert (translate_p != NULL);
4470
4471 _clutter_actor_create_transition (self, pspec, *translate_p, value);
4472 }
4473
4474 /**
4475 * clutter_actor_set_translation:
4476 * @self: a #ClutterActor
4477 * @translate_x: the translation along the X axis
4478 * @translate_y: the translation along the Y axis
4479 * @translate_z: the translation along the Z axis
4480 *
4481 * Sets an additional translation transformation on a #ClutterActor,
4482 * relative to the #ClutterActor:pivot-point.
4483 *
4484 * Since: 1.12
4485 */
4486 void
clutter_actor_set_translation(ClutterActor * self,gfloat translate_x,gfloat translate_y,gfloat translate_z)4487 clutter_actor_set_translation (ClutterActor *self,
4488 gfloat translate_x,
4489 gfloat translate_y,
4490 gfloat translate_z)
4491 {
4492 g_return_if_fail (CLUTTER_IS_ACTOR (self));
4493
4494 g_object_freeze_notify (G_OBJECT (self));
4495
4496 clutter_actor_set_translation_factor (self, CLUTTER_X_AXIS, translate_x);
4497 clutter_actor_set_translation_factor (self, CLUTTER_Y_AXIS, translate_y);
4498 clutter_actor_set_translation_factor (self, CLUTTER_Z_AXIS, translate_z);
4499
4500 g_object_thaw_notify (G_OBJECT (self));
4501 }
4502
4503 /**
4504 * clutter_actor_get_translation:
4505 * @self: a #ClutterActor
4506 * @translate_x: (out) (allow-none): return location for the X component
4507 * of the translation, or %NULL
4508 * @translate_y: (out) (allow-none): return location for the Y component
4509 * of the translation, or %NULL
4510 * @translate_z: (out) (allow-none): return location for the Z component
4511 * of the translation, or %NULL
4512 *
4513 * Retrieves the translation set using clutter_actor_set_translation().
4514 *
4515 * Since: 1.12
4516 */
4517 void
clutter_actor_get_translation(ClutterActor * self,gfloat * translate_x,gfloat * translate_y,gfloat * translate_z)4518 clutter_actor_get_translation (ClutterActor *self,
4519 gfloat *translate_x,
4520 gfloat *translate_y,
4521 gfloat *translate_z)
4522 {
4523 const ClutterTransformInfo *info;
4524
4525 g_return_if_fail (CLUTTER_IS_ACTOR (self));
4526
4527 info = _clutter_actor_get_transform_info_or_defaults (self);
4528
4529 if (translate_x != NULL)
4530 *translate_x = info->translation.x;
4531
4532 if (translate_y != NULL)
4533 *translate_y = info->translation.y;
4534
4535 if (translate_z != NULL)
4536 *translate_z = info->translation.z;
4537 }
4538
4539 /*< private >
4540 * clutter_actor_set_rotation_angle_internal:
4541 * @self: a #ClutterActor
4542 * @angle: the angle of rotation
4543 * @pspec: the #GParamSpec of the property
4544 *
4545 * Sets the rotation angle on the given axis without affecting the
4546 * rotation center point.
4547 */
4548 static inline void
clutter_actor_set_rotation_angle_internal(ClutterActor * self,gdouble angle,GParamSpec * pspec)4549 clutter_actor_set_rotation_angle_internal (ClutterActor *self,
4550 gdouble angle,
4551 GParamSpec *pspec)
4552 {
4553 ClutterTransformInfo *info;
4554
4555 info = _clutter_actor_get_transform_info (self);
4556
4557 if (pspec == obj_props[PROP_ROTATION_ANGLE_X])
4558 info->rx_angle = angle;
4559 else if (pspec == obj_props[PROP_ROTATION_ANGLE_Y])
4560 info->ry_angle = angle;
4561 else if (pspec == obj_props[PROP_ROTATION_ANGLE_Z])
4562 info->rz_angle = angle;
4563 else
4564 g_assert_not_reached ();
4565
4566 transform_changed (self);
4567
4568 clutter_actor_queue_redraw (self);
4569
4570 g_object_notify_by_pspec (G_OBJECT (self), pspec);
4571 }
4572
4573 /**
4574 * clutter_actor_set_rotation_angle:
4575 * @self: a #ClutterActor
4576 * @axis: the axis to set the angle one
4577 * @angle: the angle of rotation, in degrees
4578 *
4579 * Sets the @angle of rotation of a #ClutterActor on the given @axis.
4580 *
4581 * This function is a convenience for setting the rotation properties
4582 * #ClutterActor:rotation-angle-x, #ClutterActor:rotation-angle-y,
4583 * and #ClutterActor:rotation-angle-z.
4584 *
4585 * The center of rotation is established by the #ClutterActor:pivot-point
4586 * property.
4587 *
4588 * Since: 1.12
4589 */
4590 void
clutter_actor_set_rotation_angle(ClutterActor * self,ClutterRotateAxis axis,gdouble angle)4591 clutter_actor_set_rotation_angle (ClutterActor *self,
4592 ClutterRotateAxis axis,
4593 gdouble angle)
4594 {
4595 const ClutterTransformInfo *info;
4596 const double *cur_angle_p = NULL;
4597 GParamSpec *pspec = NULL;
4598
4599 g_return_if_fail (CLUTTER_IS_ACTOR (self));
4600
4601 info = _clutter_actor_get_transform_info_or_defaults (self);
4602
4603 switch (axis)
4604 {
4605 case CLUTTER_X_AXIS:
4606 cur_angle_p = &info->rx_angle;
4607 pspec = obj_props[PROP_ROTATION_ANGLE_X];
4608 break;
4609
4610 case CLUTTER_Y_AXIS:
4611 cur_angle_p = &info->ry_angle;
4612 pspec = obj_props[PROP_ROTATION_ANGLE_Y];
4613 break;
4614
4615 case CLUTTER_Z_AXIS:
4616 cur_angle_p = &info->rz_angle;
4617 pspec = obj_props[PROP_ROTATION_ANGLE_Z];
4618 break;
4619 }
4620
4621 g_assert (pspec != NULL);
4622 g_assert (cur_angle_p != NULL);
4623
4624 _clutter_actor_create_transition (self, pspec, *cur_angle_p, angle);
4625 }
4626
4627 /**
4628 * clutter_actor_get_rotation_angle:
4629 * @self: a #ClutterActor
4630 * @axis: the axis of the rotation
4631 *
4632 * Retrieves the angle of rotation set by clutter_actor_set_rotation_angle().
4633 *
4634 * Return value: the angle of rotation, in degrees
4635 *
4636 * Since: 1.12
4637 */
4638 gdouble
clutter_actor_get_rotation_angle(ClutterActor * self,ClutterRotateAxis axis)4639 clutter_actor_get_rotation_angle (ClutterActor *self,
4640 ClutterRotateAxis axis)
4641 {
4642 const ClutterTransformInfo *info;
4643 gdouble retval;
4644
4645 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0);
4646
4647 info = _clutter_actor_get_transform_info_or_defaults (self);
4648
4649 switch (axis)
4650 {
4651 case CLUTTER_X_AXIS:
4652 retval = info->rx_angle;
4653 break;
4654
4655 case CLUTTER_Y_AXIS:
4656 retval = info->ry_angle;
4657 break;
4658
4659 case CLUTTER_Z_AXIS:
4660 retval = info->rz_angle;
4661 break;
4662
4663 default:
4664 g_warn_if_reached ();
4665 retval = 0.0;
4666 break;
4667 }
4668
4669 return retval;
4670 }
4671
4672 static void
clutter_actor_set_scale_factor_internal(ClutterActor * self,double factor,GParamSpec * pspec)4673 clutter_actor_set_scale_factor_internal (ClutterActor *self,
4674 double factor,
4675 GParamSpec *pspec)
4676 {
4677 GObject *obj = G_OBJECT (self);
4678 ClutterTransformInfo *info;
4679
4680 info = _clutter_actor_get_transform_info (self);
4681
4682 if (pspec == obj_props[PROP_SCALE_X])
4683 info->scale_x = factor;
4684 else if (pspec == obj_props[PROP_SCALE_Y])
4685 info->scale_y = factor;
4686 else if (pspec == obj_props[PROP_SCALE_Z])
4687 info->scale_z = factor;
4688 else
4689 g_assert_not_reached ();
4690
4691 transform_changed (self);
4692
4693 clutter_actor_queue_redraw (self);
4694 g_object_notify_by_pspec (obj, pspec);
4695 }
4696
4697 static inline void
clutter_actor_set_scale_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble factor)4698 clutter_actor_set_scale_factor (ClutterActor *self,
4699 ClutterRotateAxis axis,
4700 gdouble factor)
4701 {
4702 const ClutterTransformInfo *info;
4703 const double *scale_p = NULL;
4704 GParamSpec *pspec = NULL;
4705
4706 info = _clutter_actor_get_transform_info_or_defaults (self);
4707
4708 switch (axis)
4709 {
4710 case CLUTTER_X_AXIS:
4711 pspec = obj_props[PROP_SCALE_X];
4712 scale_p = &info->scale_x;
4713 break;
4714
4715 case CLUTTER_Y_AXIS:
4716 pspec = obj_props[PROP_SCALE_Y];
4717 scale_p = &info->scale_y;
4718 break;
4719
4720 case CLUTTER_Z_AXIS:
4721 pspec = obj_props[PROP_SCALE_Z];
4722 scale_p = &info->scale_z;
4723 break;
4724 }
4725
4726 g_assert (pspec != NULL);
4727 g_assert (scale_p != NULL);
4728
4729 if (*scale_p != factor)
4730 _clutter_actor_create_transition (self, pspec, *scale_p, factor);
4731 }
4732
4733 static void
clutter_actor_set_clip_rect(ClutterActor * self,const graphene_rect_t * clip)4734 clutter_actor_set_clip_rect (ClutterActor *self,
4735 const graphene_rect_t *clip)
4736 {
4737 ClutterActorPrivate *priv = self->priv;
4738 GObject *obj = G_OBJECT (self);
4739
4740 if (clip != NULL)
4741 {
4742 priv->clip = *clip;
4743 priv->has_clip = TRUE;
4744 }
4745 else
4746 priv->has_clip = FALSE;
4747
4748 queue_update_paint_volume (self);
4749 clutter_actor_queue_redraw (self);
4750
4751 g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
4752 g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
4753 }
4754
4755 static void
clutter_actor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4756 clutter_actor_set_property (GObject *object,
4757 guint prop_id,
4758 const GValue *value,
4759 GParamSpec *pspec)
4760 {
4761 ClutterActor *actor = CLUTTER_ACTOR (object);
4762 ClutterActorPrivate *priv = actor->priv;
4763
4764 switch (prop_id)
4765 {
4766 case PROP_X:
4767 clutter_actor_set_x (actor, g_value_get_float (value));
4768 break;
4769
4770 case PROP_Y:
4771 clutter_actor_set_y (actor, g_value_get_float (value));
4772 break;
4773
4774 case PROP_POSITION:
4775 {
4776 const graphene_point_t *pos = g_value_get_boxed (value);
4777
4778 if (pos != NULL)
4779 clutter_actor_set_position (actor, pos->x, pos->y);
4780 else
4781 clutter_actor_set_fixed_position_set (actor, FALSE);
4782 }
4783 break;
4784
4785 case PROP_WIDTH:
4786 clutter_actor_set_width (actor, g_value_get_float (value));
4787 break;
4788
4789 case PROP_HEIGHT:
4790 clutter_actor_set_height (actor, g_value_get_float (value));
4791 break;
4792
4793 case PROP_SIZE:
4794 {
4795 const graphene_size_t *size = g_value_get_boxed (value);
4796
4797 if (size != NULL)
4798 clutter_actor_set_size (actor, size->width, size->height);
4799 else
4800 clutter_actor_set_size (actor, -1, -1);
4801 }
4802 break;
4803
4804 case PROP_FIXED_X:
4805 clutter_actor_set_x (actor, g_value_get_float (value));
4806 break;
4807
4808 case PROP_FIXED_Y:
4809 clutter_actor_set_y (actor, g_value_get_float (value));
4810 break;
4811
4812 case PROP_FIXED_POSITION_SET:
4813 clutter_actor_set_fixed_position_set (actor, g_value_get_boolean (value));
4814 break;
4815
4816 case PROP_MIN_WIDTH:
4817 clutter_actor_set_min_width (actor, g_value_get_float (value));
4818 break;
4819
4820 case PROP_MIN_HEIGHT:
4821 clutter_actor_set_min_height (actor, g_value_get_float (value));
4822 break;
4823
4824 case PROP_NATURAL_WIDTH:
4825 clutter_actor_set_natural_width (actor, g_value_get_float (value));
4826 break;
4827
4828 case PROP_NATURAL_HEIGHT:
4829 clutter_actor_set_natural_height (actor, g_value_get_float (value));
4830 break;
4831
4832 case PROP_MIN_WIDTH_SET:
4833 clutter_actor_set_min_width_set (actor, g_value_get_boolean (value));
4834 break;
4835
4836 case PROP_MIN_HEIGHT_SET:
4837 clutter_actor_set_min_height_set (actor, g_value_get_boolean (value));
4838 break;
4839
4840 case PROP_NATURAL_WIDTH_SET:
4841 clutter_actor_set_natural_width_set (actor, g_value_get_boolean (value));
4842 break;
4843
4844 case PROP_NATURAL_HEIGHT_SET:
4845 clutter_actor_set_natural_height_set (actor, g_value_get_boolean (value));
4846 break;
4847
4848 case PROP_REQUEST_MODE:
4849 clutter_actor_set_request_mode (actor, g_value_get_enum (value));
4850 break;
4851
4852 case PROP_Z_POSITION:
4853 clutter_actor_set_z_position (actor, g_value_get_float (value));
4854 break;
4855
4856 case PROP_OPACITY:
4857 clutter_actor_set_opacity (actor, g_value_get_uint (value));
4858 break;
4859
4860 case PROP_OFFSCREEN_REDIRECT:
4861 clutter_actor_set_offscreen_redirect (actor, g_value_get_flags (value));
4862 break;
4863
4864 case PROP_NAME:
4865 clutter_actor_set_name (actor, g_value_get_string (value));
4866 break;
4867
4868 case PROP_VISIBLE:
4869 if (g_value_get_boolean (value) == TRUE)
4870 clutter_actor_show (actor);
4871 else
4872 clutter_actor_hide (actor);
4873 break;
4874
4875 case PROP_PIVOT_POINT:
4876 {
4877 const graphene_point_t *pivot = g_value_get_boxed (value);
4878
4879 if (pivot == NULL)
4880 pivot = graphene_point_zero ();
4881
4882 clutter_actor_set_pivot_point (actor, pivot->x, pivot->y);
4883 }
4884 break;
4885
4886 case PROP_PIVOT_POINT_Z:
4887 clutter_actor_set_pivot_point_z (actor, g_value_get_float (value));
4888 break;
4889
4890 case PROP_TRANSLATION_X:
4891 clutter_actor_set_translation_factor (actor, CLUTTER_X_AXIS,
4892 g_value_get_float (value));
4893 break;
4894
4895 case PROP_TRANSLATION_Y:
4896 clutter_actor_set_translation_factor (actor, CLUTTER_Y_AXIS,
4897 g_value_get_float (value));
4898 break;
4899
4900 case PROP_TRANSLATION_Z:
4901 clutter_actor_set_translation_factor (actor, CLUTTER_Z_AXIS,
4902 g_value_get_float (value));
4903 break;
4904
4905 case PROP_SCALE_X:
4906 clutter_actor_set_scale_factor (actor, CLUTTER_X_AXIS,
4907 g_value_get_double (value));
4908 break;
4909
4910 case PROP_SCALE_Y:
4911 clutter_actor_set_scale_factor (actor, CLUTTER_Y_AXIS,
4912 g_value_get_double (value));
4913 break;
4914
4915 case PROP_SCALE_Z:
4916 clutter_actor_set_scale_factor (actor, CLUTTER_Z_AXIS,
4917 g_value_get_double (value));
4918 break;
4919
4920 case PROP_CLIP_RECT:
4921 clutter_actor_set_clip_rect (actor, g_value_get_boxed (value));
4922 break;
4923
4924 case PROP_CLIP_TO_ALLOCATION:
4925 clutter_actor_set_clip_to_allocation (actor, g_value_get_boolean (value));
4926 break;
4927
4928 case PROP_REACTIVE:
4929 clutter_actor_set_reactive (actor, g_value_get_boolean (value));
4930 break;
4931
4932 case PROP_ROTATION_ANGLE_X:
4933 clutter_actor_set_rotation_angle (actor,
4934 CLUTTER_X_AXIS,
4935 g_value_get_double (value));
4936 break;
4937
4938 case PROP_ROTATION_ANGLE_Y:
4939 clutter_actor_set_rotation_angle (actor,
4940 CLUTTER_Y_AXIS,
4941 g_value_get_double (value));
4942 break;
4943
4944 case PROP_ROTATION_ANGLE_Z:
4945 clutter_actor_set_rotation_angle (actor,
4946 CLUTTER_Z_AXIS,
4947 g_value_get_double (value));
4948 break;
4949
4950 case PROP_TRANSFORM:
4951 clutter_actor_set_transform (actor, g_value_get_boxed (value));
4952 break;
4953
4954 case PROP_CHILD_TRANSFORM:
4955 clutter_actor_set_child_transform (actor, g_value_get_boxed (value));
4956 break;
4957
4958 case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
4959 priv->show_on_set_parent = g_value_get_boolean (value);
4960 break;
4961
4962 case PROP_TEXT_DIRECTION:
4963 clutter_actor_set_text_direction (actor, g_value_get_enum (value));
4964 break;
4965
4966 case PROP_ACTIONS:
4967 clutter_actor_add_action (actor, g_value_get_object (value));
4968 break;
4969
4970 case PROP_CONSTRAINTS:
4971 clutter_actor_add_constraint (actor, g_value_get_object (value));
4972 break;
4973
4974 case PROP_EFFECT:
4975 clutter_actor_add_effect (actor, g_value_get_object (value));
4976 break;
4977
4978 case PROP_LAYOUT_MANAGER:
4979 clutter_actor_set_layout_manager (actor, g_value_get_object (value));
4980 break;
4981
4982 case PROP_X_EXPAND:
4983 clutter_actor_set_x_expand (actor, g_value_get_boolean (value));
4984 break;
4985
4986 case PROP_Y_EXPAND:
4987 clutter_actor_set_y_expand (actor, g_value_get_boolean (value));
4988 break;
4989
4990 case PROP_X_ALIGN:
4991 clutter_actor_set_x_align (actor, g_value_get_enum (value));
4992 break;
4993
4994 case PROP_Y_ALIGN:
4995 clutter_actor_set_y_align (actor, g_value_get_enum (value));
4996 break;
4997
4998 case PROP_MARGIN_TOP:
4999 clutter_actor_set_margin_top (actor, g_value_get_float (value));
5000 break;
5001
5002 case PROP_MARGIN_BOTTOM:
5003 clutter_actor_set_margin_bottom (actor, g_value_get_float (value));
5004 break;
5005
5006 case PROP_MARGIN_LEFT:
5007 clutter_actor_set_margin_left (actor, g_value_get_float (value));
5008 break;
5009
5010 case PROP_MARGIN_RIGHT:
5011 clutter_actor_set_margin_right (actor, g_value_get_float (value));
5012 break;
5013
5014 case PROP_BACKGROUND_COLOR:
5015 clutter_actor_set_background_color (actor, g_value_get_boxed (value));
5016 break;
5017
5018 case PROP_CONTENT:
5019 clutter_actor_set_content (actor, g_value_get_object (value));
5020 break;
5021
5022 case PROP_CONTENT_GRAVITY:
5023 clutter_actor_set_content_gravity (actor, g_value_get_enum (value));
5024 break;
5025
5026 case PROP_MINIFICATION_FILTER:
5027 clutter_actor_set_content_scaling_filters (actor,
5028 g_value_get_enum (value),
5029 actor->priv->mag_filter);
5030 break;
5031
5032 case PROP_MAGNIFICATION_FILTER:
5033 clutter_actor_set_content_scaling_filters (actor,
5034 actor->priv->min_filter,
5035 g_value_get_enum (value));
5036 break;
5037
5038 case PROP_CONTENT_REPEAT:
5039 clutter_actor_set_content_repeat (actor, g_value_get_flags (value));
5040 break;
5041
5042 default:
5043 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5044 break;
5045 }
5046 }
5047
5048 static void
clutter_actor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)5049 clutter_actor_get_property (GObject *object,
5050 guint prop_id,
5051 GValue *value,
5052 GParamSpec *pspec)
5053 {
5054 ClutterActor *actor = CLUTTER_ACTOR (object);
5055 ClutterActorPrivate *priv = actor->priv;
5056
5057 switch (prop_id)
5058 {
5059 case PROP_X:
5060 g_value_set_float (value, clutter_actor_get_x (actor));
5061 break;
5062
5063 case PROP_Y:
5064 g_value_set_float (value, clutter_actor_get_y (actor));
5065 break;
5066
5067 case PROP_POSITION:
5068 {
5069 graphene_point_t position;
5070
5071 graphene_point_init (&position,
5072 clutter_actor_get_x (actor),
5073 clutter_actor_get_y (actor));
5074 g_value_set_boxed (value, &position);
5075 }
5076 break;
5077
5078 case PROP_WIDTH:
5079 g_value_set_float (value, clutter_actor_get_width (actor));
5080 break;
5081
5082 case PROP_HEIGHT:
5083 g_value_set_float (value, clutter_actor_get_height (actor));
5084 break;
5085
5086 case PROP_SIZE:
5087 {
5088 graphene_size_t size;
5089
5090 graphene_size_init (&size,
5091 clutter_actor_get_width (actor),
5092 clutter_actor_get_height (actor));
5093 g_value_set_boxed (value, &size);
5094 }
5095 break;
5096
5097 case PROP_FIXED_X:
5098 {
5099 const ClutterLayoutInfo *info;
5100
5101 info = _clutter_actor_get_layout_info_or_defaults (actor);
5102 g_value_set_float (value, info->fixed_pos.x);
5103 }
5104 break;
5105
5106 case PROP_FIXED_Y:
5107 {
5108 const ClutterLayoutInfo *info;
5109
5110 info = _clutter_actor_get_layout_info_or_defaults (actor);
5111 g_value_set_float (value, info->fixed_pos.y);
5112 }
5113 break;
5114
5115 case PROP_FIXED_POSITION_SET:
5116 g_value_set_boolean (value, priv->position_set);
5117 break;
5118
5119 case PROP_MIN_WIDTH:
5120 {
5121 const ClutterLayoutInfo *info;
5122
5123 info = _clutter_actor_get_layout_info_or_defaults (actor);
5124 g_value_set_float (value, info->minimum.width);
5125 }
5126 break;
5127
5128 case PROP_MIN_HEIGHT:
5129 {
5130 const ClutterLayoutInfo *info;
5131
5132 info = _clutter_actor_get_layout_info_or_defaults (actor);
5133 g_value_set_float (value, info->minimum.height);
5134 }
5135 break;
5136
5137 case PROP_NATURAL_WIDTH:
5138 {
5139 const ClutterLayoutInfo *info;
5140
5141 info = _clutter_actor_get_layout_info_or_defaults (actor);
5142 g_value_set_float (value, info->natural.width);
5143 }
5144 break;
5145
5146 case PROP_NATURAL_HEIGHT:
5147 {
5148 const ClutterLayoutInfo *info;
5149
5150 info = _clutter_actor_get_layout_info_or_defaults (actor);
5151 g_value_set_float (value, info->natural.height);
5152 }
5153 break;
5154
5155 case PROP_MIN_WIDTH_SET:
5156 g_value_set_boolean (value, priv->min_width_set);
5157 break;
5158
5159 case PROP_MIN_HEIGHT_SET:
5160 g_value_set_boolean (value, priv->min_height_set);
5161 break;
5162
5163 case PROP_NATURAL_WIDTH_SET:
5164 g_value_set_boolean (value, priv->natural_width_set);
5165 break;
5166
5167 case PROP_NATURAL_HEIGHT_SET:
5168 g_value_set_boolean (value, priv->natural_height_set);
5169 break;
5170
5171 case PROP_REQUEST_MODE:
5172 g_value_set_enum (value, priv->request_mode);
5173 break;
5174
5175 case PROP_ALLOCATION:
5176 g_value_set_boxed (value, &priv->allocation);
5177 break;
5178
5179 case PROP_Z_POSITION:
5180 g_value_set_float (value, clutter_actor_get_z_position (actor));
5181 break;
5182
5183 case PROP_OPACITY:
5184 g_value_set_uint (value, priv->opacity);
5185 break;
5186
5187 case PROP_OFFSCREEN_REDIRECT:
5188 g_value_set_flags (value, priv->offscreen_redirect);
5189 break;
5190
5191 case PROP_NAME:
5192 g_value_set_string (value, priv->name);
5193 break;
5194
5195 case PROP_VISIBLE:
5196 g_value_set_boolean (value, CLUTTER_ACTOR_IS_VISIBLE (actor));
5197 break;
5198
5199 case PROP_MAPPED:
5200 g_value_set_boolean (value, CLUTTER_ACTOR_IS_MAPPED (actor));
5201 break;
5202
5203 case PROP_REALIZED:
5204 g_value_set_boolean (value, CLUTTER_ACTOR_IS_REALIZED (actor));
5205 break;
5206
5207 case PROP_HAS_CLIP:
5208 g_value_set_boolean (value, priv->has_clip);
5209 break;
5210
5211 case PROP_CLIP_RECT:
5212 g_value_set_boxed (value, &priv->clip);
5213 break;
5214
5215 case PROP_CLIP_TO_ALLOCATION:
5216 g_value_set_boolean (value, priv->clip_to_allocation);
5217 break;
5218
5219 case PROP_PIVOT_POINT:
5220 {
5221 const ClutterTransformInfo *info;
5222
5223 info = _clutter_actor_get_transform_info_or_defaults (actor);
5224 g_value_set_boxed (value, &info->pivot);
5225 }
5226 break;
5227
5228 case PROP_PIVOT_POINT_Z:
5229 {
5230 const ClutterTransformInfo *info;
5231
5232 info = _clutter_actor_get_transform_info_or_defaults (actor);
5233 g_value_set_float (value, info->pivot_z);
5234 }
5235 break;
5236
5237 case PROP_TRANSLATION_X:
5238 {
5239 const ClutterTransformInfo *info;
5240
5241 info = _clutter_actor_get_transform_info_or_defaults (actor);
5242 g_value_set_float (value, info->translation.x);
5243 }
5244 break;
5245
5246 case PROP_TRANSLATION_Y:
5247 {
5248 const ClutterTransformInfo *info;
5249
5250 info = _clutter_actor_get_transform_info_or_defaults (actor);
5251 g_value_set_float (value, info->translation.y);
5252 }
5253 break;
5254
5255 case PROP_TRANSLATION_Z:
5256 {
5257 const ClutterTransformInfo *info;
5258
5259 info = _clutter_actor_get_transform_info_or_defaults (actor);
5260 g_value_set_float (value, info->translation.z);
5261 }
5262 break;
5263
5264 case PROP_SCALE_X:
5265 {
5266 const ClutterTransformInfo *info;
5267
5268 info = _clutter_actor_get_transform_info_or_defaults (actor);
5269 g_value_set_double (value, info->scale_x);
5270 }
5271 break;
5272
5273 case PROP_SCALE_Y:
5274 {
5275 const ClutterTransformInfo *info;
5276
5277 info = _clutter_actor_get_transform_info_or_defaults (actor);
5278 g_value_set_double (value, info->scale_y);
5279 }
5280 break;
5281
5282 case PROP_SCALE_Z:
5283 {
5284 const ClutterTransformInfo *info;
5285
5286 info = _clutter_actor_get_transform_info_or_defaults (actor);
5287 g_value_set_double (value, info->scale_z);
5288 }
5289 break;
5290
5291 case PROP_REACTIVE:
5292 g_value_set_boolean (value, clutter_actor_get_reactive (actor));
5293 break;
5294
5295 case PROP_ROTATION_ANGLE_X:
5296 {
5297 const ClutterTransformInfo *info;
5298
5299 info = _clutter_actor_get_transform_info_or_defaults (actor);
5300 g_value_set_double (value, info->rx_angle);
5301 }
5302 break;
5303
5304 case PROP_ROTATION_ANGLE_Y:
5305 {
5306 const ClutterTransformInfo *info;
5307
5308 info = _clutter_actor_get_transform_info_or_defaults (actor);
5309 g_value_set_double (value, info->ry_angle);
5310 }
5311 break;
5312
5313 case PROP_ROTATION_ANGLE_Z:
5314 {
5315 const ClutterTransformInfo *info;
5316
5317 info = _clutter_actor_get_transform_info_or_defaults (actor);
5318 g_value_set_double (value, info->rz_angle);
5319 }
5320 break;
5321
5322 case PROP_TRANSFORM:
5323 {
5324 graphene_matrix_t m;
5325
5326 clutter_actor_get_transform (actor, &m);
5327 g_value_set_boxed (value, &m);
5328 }
5329 break;
5330
5331 case PROP_TRANSFORM_SET:
5332 {
5333 const ClutterTransformInfo *info;
5334
5335 info = _clutter_actor_get_transform_info_or_defaults (actor);
5336 g_value_set_boolean (value, info->transform_set);
5337 }
5338 break;
5339
5340 case PROP_CHILD_TRANSFORM:
5341 {
5342 graphene_matrix_t m;
5343
5344 clutter_actor_get_child_transform (actor, &m);
5345 g_value_set_boxed (value, &m);
5346 }
5347 break;
5348
5349 case PROP_CHILD_TRANSFORM_SET:
5350 {
5351 const ClutterTransformInfo *info;
5352
5353 info = _clutter_actor_get_transform_info_or_defaults (actor);
5354 g_value_set_boolean (value, info->child_transform_set);
5355 }
5356 break;
5357
5358 case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
5359 g_value_set_boolean (value, priv->show_on_set_parent);
5360 break;
5361
5362 case PROP_TEXT_DIRECTION:
5363 g_value_set_enum (value, priv->text_direction);
5364 break;
5365
5366 case PROP_HAS_POINTER:
5367 g_value_set_boolean (value, priv->has_pointer);
5368 break;
5369
5370 case PROP_LAYOUT_MANAGER:
5371 g_value_set_object (value, priv->layout_manager);
5372 break;
5373
5374 case PROP_X_EXPAND:
5375 {
5376 const ClutterLayoutInfo *info;
5377
5378 info = _clutter_actor_get_layout_info_or_defaults (actor);
5379 g_value_set_boolean (value, info->x_expand);
5380 }
5381 break;
5382
5383 case PROP_Y_EXPAND:
5384 {
5385 const ClutterLayoutInfo *info;
5386
5387 info = _clutter_actor_get_layout_info_or_defaults (actor);
5388 g_value_set_boolean (value, info->y_expand);
5389 }
5390 break;
5391
5392 case PROP_X_ALIGN:
5393 {
5394 const ClutterLayoutInfo *info;
5395
5396 info = _clutter_actor_get_layout_info_or_defaults (actor);
5397 g_value_set_enum (value, info->x_align);
5398 }
5399 break;
5400
5401 case PROP_Y_ALIGN:
5402 {
5403 const ClutterLayoutInfo *info;
5404
5405 info = _clutter_actor_get_layout_info_or_defaults (actor);
5406 g_value_set_enum (value, info->y_align);
5407 }
5408 break;
5409
5410 case PROP_MARGIN_TOP:
5411 {
5412 const ClutterLayoutInfo *info;
5413
5414 info = _clutter_actor_get_layout_info_or_defaults (actor);
5415 g_value_set_float (value, info->margin.top);
5416 }
5417 break;
5418
5419 case PROP_MARGIN_BOTTOM:
5420 {
5421 const ClutterLayoutInfo *info;
5422
5423 info = _clutter_actor_get_layout_info_or_defaults (actor);
5424 g_value_set_float (value, info->margin.bottom);
5425 }
5426 break;
5427
5428 case PROP_MARGIN_LEFT:
5429 {
5430 const ClutterLayoutInfo *info;
5431
5432 info = _clutter_actor_get_layout_info_or_defaults (actor);
5433 g_value_set_float (value, info->margin.left);
5434 }
5435 break;
5436
5437 case PROP_MARGIN_RIGHT:
5438 {
5439 const ClutterLayoutInfo *info;
5440
5441 info = _clutter_actor_get_layout_info_or_defaults (actor);
5442 g_value_set_float (value, info->margin.right);
5443 }
5444 break;
5445
5446 case PROP_BACKGROUND_COLOR_SET:
5447 g_value_set_boolean (value, priv->bg_color_set);
5448 break;
5449
5450 case PROP_BACKGROUND_COLOR:
5451 g_value_set_boxed (value, &priv->bg_color);
5452 break;
5453
5454 case PROP_FIRST_CHILD:
5455 g_value_set_object (value, priv->first_child);
5456 break;
5457
5458 case PROP_LAST_CHILD:
5459 g_value_set_object (value, priv->last_child);
5460 break;
5461
5462 case PROP_CONTENT:
5463 g_value_set_object (value, priv->content);
5464 break;
5465
5466 case PROP_CONTENT_GRAVITY:
5467 g_value_set_enum (value, priv->content_gravity);
5468 break;
5469
5470 case PROP_CONTENT_BOX:
5471 {
5472 ClutterActorBox box = { 0, };
5473
5474 clutter_actor_get_content_box (actor, &box);
5475 g_value_set_boxed (value, &box);
5476 }
5477 break;
5478
5479 case PROP_MINIFICATION_FILTER:
5480 g_value_set_enum (value, priv->min_filter);
5481 break;
5482
5483 case PROP_MAGNIFICATION_FILTER:
5484 g_value_set_enum (value, priv->mag_filter);
5485 break;
5486
5487 case PROP_CONTENT_REPEAT:
5488 g_value_set_flags (value, priv->content_repeat);
5489 break;
5490
5491 default:
5492 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5493 break;
5494 }
5495 }
5496
5497 static void
clutter_actor_dispose(GObject * object)5498 clutter_actor_dispose (GObject *object)
5499 {
5500 ClutterActor *self = CLUTTER_ACTOR (object);
5501 ClutterActorPrivate *priv = self->priv;
5502 ClutterBackend *backend = clutter_get_default_backend ();
5503
5504 CLUTTER_NOTE (MISC, "Dispose actor (name='%s', ref_count:%d) of type '%s'",
5505 _clutter_actor_get_debug_name (self),
5506 object->ref_count,
5507 g_type_name (G_OBJECT_TYPE (self)));
5508
5509 maybe_unset_key_focus (self);
5510
5511 /* Stop the emission of any property change */
5512 g_object_freeze_notify (object);
5513
5514 g_signal_emit (self, actor_signals[DESTROY], 0);
5515
5516 /* avoid recursing when called from clutter_actor_destroy() */
5517 if (priv->parent != NULL)
5518 {
5519 ClutterActor *parent = priv->parent;
5520 clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self);
5521 }
5522
5523 /* parent must be gone at this point */
5524 g_assert (priv->parent == NULL);
5525
5526 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
5527 {
5528 /* can't be mapped or realized with no parent */
5529 g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
5530 g_assert (!CLUTTER_ACTOR_IS_REALIZED (self));
5531 }
5532
5533 g_clear_signal_handler (&priv->resolution_changed_id, backend);
5534 g_clear_signal_handler (&priv->font_changed_id, backend);
5535
5536 g_clear_object (&priv->pango_context);
5537 g_clear_object (&priv->actions);
5538 g_clear_object (&priv->constraints);
5539 g_clear_object (&priv->effects);
5540 g_clear_object (&priv->flatten_effect);
5541
5542 if (priv->child_model != NULL)
5543 {
5544 if (priv->create_child_notify != NULL)
5545 priv->create_child_notify (priv->create_child_data);
5546
5547 priv->create_child_func = NULL;
5548 priv->create_child_data = NULL;
5549 priv->create_child_notify = NULL;
5550
5551 g_clear_object (&priv->child_model);
5552 }
5553
5554 if (priv->layout_manager != NULL)
5555 {
5556 g_clear_signal_handler (&priv->layout_changed_id, priv->layout_manager);
5557 clutter_layout_manager_set_container (priv->layout_manager, NULL);
5558 g_clear_object (&priv->layout_manager);
5559 }
5560
5561 if (priv->content != NULL)
5562 {
5563 _clutter_content_detached (priv->content, self);
5564 g_clear_object (&priv->content);
5565 }
5566
5567 if (priv->clones != NULL)
5568 {
5569 g_hash_table_unref (priv->clones);
5570 priv->clones = NULL;
5571 }
5572
5573 g_clear_pointer (&priv->stage_views, g_list_free);
5574
5575 G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
5576 }
5577
5578 static void
clutter_actor_finalize(GObject * object)5579 clutter_actor_finalize (GObject *object)
5580 {
5581 ClutterActorPrivate *priv = CLUTTER_ACTOR (object)->priv;
5582
5583 CLUTTER_NOTE (MISC, "Finalize actor (name='%s') of type '%s'",
5584 _clutter_actor_get_debug_name ((ClutterActor *) object),
5585 g_type_name (G_OBJECT_TYPE (object)));
5586
5587 g_free (priv->name);
5588
5589 g_free (priv->debug_name);
5590
5591 G_OBJECT_CLASS (clutter_actor_parent_class)->finalize (object);
5592 }
5593
5594
5595 /**
5596 * clutter_actor_get_accessible:
5597 * @self: a #ClutterActor
5598 *
5599 * Returns the accessible object that describes the actor to an
5600 * assistive technology.
5601 *
5602 * If no class-specific #AtkObject implementation is available for the
5603 * actor instance in question, it will inherit an #AtkObject
5604 * implementation from the first ancestor class for which such an
5605 * implementation is defined.
5606 *
5607 * The documentation of the <ulink
5608 * url="http://developer.gnome.org/doc/API/2.0/atk/index.html">ATK</ulink>
5609 * library contains more information about accessible objects and
5610 * their uses.
5611 *
5612 * Returns: (transfer none): the #AtkObject associated with @actor
5613 */
5614 AtkObject *
clutter_actor_get_accessible(ClutterActor * self)5615 clutter_actor_get_accessible (ClutterActor *self)
5616 {
5617 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
5618
5619 return CLUTTER_ACTOR_GET_CLASS (self)->get_accessible (self);
5620 }
5621
5622 static AtkObject *
clutter_actor_real_get_accessible(ClutterActor * actor)5623 clutter_actor_real_get_accessible (ClutterActor *actor)
5624 {
5625 return atk_gobject_accessible_for_object (G_OBJECT (actor));
5626 }
5627
5628 static AtkObject *
_clutter_actor_ref_accessible(AtkImplementor * implementor)5629 _clutter_actor_ref_accessible (AtkImplementor *implementor)
5630 {
5631 AtkObject *accessible;
5632
5633 accessible = clutter_actor_get_accessible (CLUTTER_ACTOR (implementor));
5634 if (accessible != NULL)
5635 g_object_ref (accessible);
5636
5637 return accessible;
5638 }
5639
5640 static void
atk_implementor_iface_init(AtkImplementorIface * iface)5641 atk_implementor_iface_init (AtkImplementorIface *iface)
5642 {
5643 iface->ref_accessible = _clutter_actor_ref_accessible;
5644 }
5645
5646 static gboolean
clutter_actor_update_default_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)5647 clutter_actor_update_default_paint_volume (ClutterActor *self,
5648 ClutterPaintVolume *volume)
5649 {
5650 ClutterActorPrivate *priv = self->priv;
5651 gboolean res = TRUE;
5652
5653 /* this should be checked before we call this function, but it's a
5654 * good idea to be explicit when it costs us nothing
5655 */
5656 if (priv->needs_allocation)
5657 return FALSE;
5658
5659 if (priv->has_clip)
5660 {
5661 graphene_point3d_t origin;
5662
5663 origin.x = priv->clip.origin.x;
5664 origin.y = priv->clip.origin.y;
5665 origin.z = 0;
5666
5667 clutter_paint_volume_set_origin (volume, &origin);
5668 clutter_paint_volume_set_width (volume, priv->clip.size.width);
5669 clutter_paint_volume_set_height (volume, priv->clip.size.height);
5670
5671 return TRUE;
5672 }
5673
5674 /* we start from the allocation */
5675 clutter_paint_volume_set_width (volume,
5676 priv->allocation.x2 - priv->allocation.x1);
5677 clutter_paint_volume_set_height (volume,
5678 priv->allocation.y2 - priv->allocation.y1);
5679
5680 /* if the actor has a clip set then we have a pretty definite
5681 * size for the paint volume: the actor cannot possibly paint
5682 * outside the clip region.
5683 */
5684 if (priv->clip_to_allocation)
5685 {
5686 /* the allocation has already been set, so we just flip the
5687 * return value
5688 */
5689 res = TRUE;
5690 }
5691 else
5692 {
5693 ClutterActor *child;
5694
5695 /* if we don't have children we just bail out here... */
5696 if (priv->n_children == 0)
5697 return res;
5698
5699 /* ...but if we have children then we ask for their paint volume in
5700 * our coordinates. if any of our children replies that it doesn't
5701 * have a paint volume, we bail out
5702 */
5703 for (child = priv->first_child;
5704 child != NULL;
5705 child = child->priv->next_sibling)
5706 {
5707 const ClutterPaintVolume *child_volume;
5708
5709 /* we ignore unmapped children, since they won't be painted.
5710 *
5711 * XXX: we also have to ignore mapped children without a valid
5712 * allocation, because apparently some code above Clutter allows
5713 * them.
5714 */
5715 if (!CLUTTER_ACTOR_IS_MAPPED (child) || !clutter_actor_has_allocation (child))
5716 continue;
5717
5718 child_volume = clutter_actor_get_transformed_paint_volume (child, self);
5719 if (child_volume == NULL)
5720 {
5721 res = FALSE;
5722 break;
5723 }
5724
5725 clutter_paint_volume_union (volume, child_volume);
5726 res = TRUE;
5727 }
5728 }
5729
5730 return res;
5731
5732 }
5733
5734 static gboolean
clutter_actor_real_get_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)5735 clutter_actor_real_get_paint_volume (ClutterActor *self,
5736 ClutterPaintVolume *volume)
5737 {
5738 ClutterActorClass *klass;
5739 gboolean res;
5740
5741 klass = CLUTTER_ACTOR_GET_CLASS (self);
5742
5743 /* XXX - this thoroughly sucks, but we don't want to penalize users
5744 * who use ClutterActor as a "new ClutterGroup" by forcing a full-stage
5745 * redraw. This should go away in 2.0.
5746 */
5747 if (klass->paint == clutter_actor_real_paint &&
5748 klass->get_paint_volume == clutter_actor_real_get_paint_volume)
5749 {
5750 res = TRUE;
5751 }
5752 else
5753 {
5754 /* this is the default return value: we cannot know if a class
5755 * is going to paint outside its allocation, so we take the
5756 * conservative approach.
5757 */
5758 res = FALSE;
5759 }
5760
5761 /* update_default_paint_volume() should only fail if one of the children
5762 * reported an invalid, or no, paint volume
5763 */
5764 if (!clutter_actor_update_default_paint_volume (self, volume))
5765 return FALSE;
5766
5767 return res;
5768 }
5769
5770 /**
5771 * clutter_actor_get_default_paint_volume:
5772 * @self: a #ClutterActor
5773 *
5774 * Retrieves the default paint volume for @self.
5775 *
5776 * This function provides the same #ClutterPaintVolume that would be
5777 * computed by the default implementation inside #ClutterActor of the
5778 * #ClutterActorClass.get_paint_volume() virtual function.
5779 *
5780 * This function should only be used by #ClutterActor subclasses that
5781 * cannot chain up to the parent implementation when computing their
5782 * paint volume.
5783 *
5784 * Return value: (transfer none): a pointer to the default
5785 * #ClutterPaintVolume, relative to the #ClutterActor, or %NULL if
5786 * the actor could not compute a valid paint volume. The returned value
5787 * is not guaranteed to be stable across multiple frames, so if you
5788 * want to retain it, you will need to copy it using
5789 * clutter_paint_volume_copy().
5790 *
5791 * Since: 1.10
5792 */
5793 const ClutterPaintVolume *
clutter_actor_get_default_paint_volume(ClutterActor * self)5794 clutter_actor_get_default_paint_volume (ClutterActor *self)
5795 {
5796 ClutterPaintVolume volume;
5797 ClutterPaintVolume *res;
5798
5799 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
5800
5801 res = NULL;
5802 _clutter_paint_volume_init_static (&volume, self);
5803 if (clutter_actor_update_default_paint_volume (self, &volume))
5804 {
5805 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
5806
5807 if (stage != NULL)
5808 {
5809 res = _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
5810 _clutter_paint_volume_copy_static (&volume, res);
5811 }
5812 }
5813
5814 clutter_paint_volume_free (&volume);
5815
5816 return res;
5817 }
5818
5819 static gboolean
clutter_actor_real_has_overlaps(ClutterActor * self)5820 clutter_actor_real_has_overlaps (ClutterActor *self)
5821 {
5822 /* By default we'll assume that all actors need an offscreen redirect to get
5823 * the correct opacity. Actors such as ClutterTexture that would never need
5824 * an offscreen redirect can override this to return FALSE. */
5825 return TRUE;
5826 }
5827
5828 static float
clutter_actor_real_calculate_resource_scale(ClutterActor * self,int phase)5829 clutter_actor_real_calculate_resource_scale (ClutterActor *self,
5830 int phase)
5831 {
5832 ClutterActorPrivate *priv = self->priv;
5833 GList *l;
5834 float new_resource_scale = -1.f;
5835
5836 for (l = priv->stage_views; l; l = l->next)
5837 {
5838 ClutterStageView *view = l->data;
5839
5840 new_resource_scale = MAX (clutter_stage_view_get_scale (view),
5841 new_resource_scale);
5842 }
5843
5844 return new_resource_scale;
5845 }
5846
5847 static void
clutter_actor_real_destroy(ClutterActor * actor)5848 clutter_actor_real_destroy (ClutterActor *actor)
5849 {
5850 ClutterActorIter iter;
5851
5852 g_object_freeze_notify (G_OBJECT (actor));
5853
5854 clutter_actor_iter_init (&iter, actor);
5855 while (clutter_actor_iter_next (&iter, NULL))
5856 clutter_actor_iter_destroy (&iter);
5857
5858 g_object_thaw_notify (G_OBJECT (actor));
5859 }
5860
5861 static GObject *
clutter_actor_constructor(GType gtype,guint n_props,GObjectConstructParam * props)5862 clutter_actor_constructor (GType gtype,
5863 guint n_props,
5864 GObjectConstructParam *props)
5865 {
5866 GObjectClass *gobject_class;
5867 ClutterActor *self;
5868 GObject *retval;
5869
5870 gobject_class = G_OBJECT_CLASS (clutter_actor_parent_class);
5871 retval = gobject_class->constructor (gtype, n_props, props);
5872 self = CLUTTER_ACTOR (retval);
5873
5874 if (self->priv->layout_manager == NULL)
5875 {
5876 ClutterLayoutManager *default_layout;
5877
5878 CLUTTER_NOTE (LAYOUT, "Creating default layout manager");
5879
5880 default_layout = clutter_fixed_layout_new ();
5881 clutter_actor_set_layout_manager (self, default_layout);
5882 }
5883
5884 return retval;
5885 }
5886
5887 static void
clutter_actor_class_init(ClutterActorClass * klass)5888 clutter_actor_class_init (ClutterActorClass *klass)
5889 {
5890 GObjectClass *object_class = G_OBJECT_CLASS (klass);
5891
5892 quark_actor_layout_info = g_quark_from_static_string ("-clutter-actor-layout-info");
5893 quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
5894 quark_actor_animation_info = g_quark_from_static_string ("-clutter-actor-animation-info");
5895
5896 quark_key = g_quark_from_static_string ("key");
5897 quark_motion = g_quark_from_static_string ("motion");
5898 quark_pointer_focus = g_quark_from_static_string ("pointer-focus");
5899 quark_button = g_quark_from_static_string ("button");
5900 quark_scroll = g_quark_from_static_string ("scroll");
5901 quark_stage = g_quark_from_static_string ("stage");
5902 quark_touch = g_quark_from_static_string ("touch");
5903 quark_touchpad = g_quark_from_static_string ("touchpad");
5904 quark_proximity = g_quark_from_static_string ("proximity");
5905 quark_pad = g_quark_from_static_string ("pad");
5906 quark_im = g_quark_from_static_string ("im");
5907
5908 object_class->constructor = clutter_actor_constructor;
5909 object_class->set_property = clutter_actor_set_property;
5910 object_class->get_property = clutter_actor_get_property;
5911 object_class->dispose = clutter_actor_dispose;
5912 object_class->finalize = clutter_actor_finalize;
5913
5914 klass->show = clutter_actor_real_show;
5915 klass->hide = clutter_actor_real_hide;
5916 klass->hide_all = clutter_actor_hide;
5917 klass->map = clutter_actor_real_map;
5918 klass->unmap = clutter_actor_real_unmap;
5919 klass->unrealize = clutter_actor_real_unrealize;
5920 klass->pick = clutter_actor_real_pick;
5921 klass->get_preferred_width = clutter_actor_real_get_preferred_width;
5922 klass->get_preferred_height = clutter_actor_real_get_preferred_height;
5923 klass->allocate = clutter_actor_real_allocate;
5924 klass->queue_relayout = clutter_actor_real_queue_relayout;
5925 klass->apply_transform = clutter_actor_real_apply_transform;
5926 klass->get_accessible = clutter_actor_real_get_accessible;
5927 klass->get_paint_volume = clutter_actor_real_get_paint_volume;
5928 klass->has_overlaps = clutter_actor_real_has_overlaps;
5929 klass->calculate_resource_scale = clutter_actor_real_calculate_resource_scale;
5930 klass->paint = clutter_actor_real_paint;
5931 klass->destroy = clutter_actor_real_destroy;
5932
5933 /**
5934 * ClutterActor:x:
5935 *
5936 * X coordinate of the actor in pixels. If written, forces a fixed
5937 * position for the actor. If read, returns the fixed position if any,
5938 * otherwise the allocation if available, otherwise 0.
5939 *
5940 * The #ClutterActor:x property is animatable.
5941 */
5942 obj_props[PROP_X] =
5943 g_param_spec_float ("x",
5944 P_("X coordinate"),
5945 P_("X coordinate of the actor"),
5946 -G_MAXFLOAT, G_MAXFLOAT,
5947 0.0,
5948 G_PARAM_READWRITE |
5949 G_PARAM_STATIC_STRINGS |
5950 G_PARAM_EXPLICIT_NOTIFY |
5951 CLUTTER_PARAM_ANIMATABLE);
5952
5953 /**
5954 * ClutterActor:y:
5955 *
5956 * Y coordinate of the actor in pixels. If written, forces a fixed
5957 * position for the actor. If read, returns the fixed position if
5958 * any, otherwise the allocation if available, otherwise 0.
5959 *
5960 * The #ClutterActor:y property is animatable.
5961 */
5962 obj_props[PROP_Y] =
5963 g_param_spec_float ("y",
5964 P_("Y coordinate"),
5965 P_("Y coordinate of the actor"),
5966 -G_MAXFLOAT, G_MAXFLOAT,
5967 0.0,
5968 G_PARAM_READWRITE |
5969 G_PARAM_STATIC_STRINGS |
5970 G_PARAM_EXPLICIT_NOTIFY |
5971 CLUTTER_PARAM_ANIMATABLE);
5972
5973 /**
5974 * ClutterActor:position:
5975 *
5976 * The position of the origin of the actor.
5977 *
5978 * This property is a shorthand for setting and getting the
5979 * #ClutterActor:x and #ClutterActor:y properties at the same
5980 * time.
5981 *
5982 * The #ClutterActor:position property is animatable.
5983 *
5984 * Since: 1.12
5985 */
5986 obj_props[PROP_POSITION] =
5987 g_param_spec_boxed ("position",
5988 P_("Position"),
5989 P_("The position of the origin of the actor"),
5990 GRAPHENE_TYPE_POINT,
5991 G_PARAM_READWRITE |
5992 G_PARAM_STATIC_STRINGS |
5993 G_PARAM_EXPLICIT_NOTIFY |
5994 CLUTTER_PARAM_ANIMATABLE);
5995
5996 /**
5997 * ClutterActor:width:
5998 *
5999 * Width of the actor (in pixels). If written, forces the minimum and
6000 * natural size request of the actor to the given width. If read, returns
6001 * the allocated width if available, otherwise the width request.
6002 *
6003 * The #ClutterActor:width property is animatable.
6004 */
6005 obj_props[PROP_WIDTH] =
6006 g_param_spec_float ("width",
6007 P_("Width"),
6008 P_("Width of the actor"),
6009 -1.0f, G_MAXFLOAT,
6010 0.0,
6011 G_PARAM_READWRITE |
6012 G_PARAM_STATIC_STRINGS |
6013 G_PARAM_EXPLICIT_NOTIFY |
6014 CLUTTER_PARAM_ANIMATABLE);
6015
6016 /**
6017 * ClutterActor:height:
6018 *
6019 * Height of the actor (in pixels). If written, forces the minimum and
6020 * natural size request of the actor to the given height. If read, returns
6021 * the allocated height if available, otherwise the height request.
6022 *
6023 * The #ClutterActor:height property is animatable.
6024 */
6025 obj_props[PROP_HEIGHT] =
6026 g_param_spec_float ("height",
6027 P_("Height"),
6028 P_("Height of the actor"),
6029 -1.0f, G_MAXFLOAT,
6030 0.0,
6031 G_PARAM_READWRITE |
6032 G_PARAM_STATIC_STRINGS |
6033 G_PARAM_EXPLICIT_NOTIFY |
6034 CLUTTER_PARAM_ANIMATABLE);
6035
6036 /**
6037 * ClutterActor:size:
6038 *
6039 * The size of the actor.
6040 *
6041 * This property is a shorthand for setting and getting the
6042 * #ClutterActor:width and #ClutterActor:height at the same time.
6043 *
6044 * The #ClutterActor:size property is animatable.
6045 *
6046 * Since: 1.12
6047 */
6048 obj_props[PROP_SIZE] =
6049 g_param_spec_boxed ("size",
6050 P_("Size"),
6051 P_("The size of the actor"),
6052 GRAPHENE_TYPE_SIZE,
6053 G_PARAM_READWRITE |
6054 G_PARAM_STATIC_STRINGS |
6055 G_PARAM_EXPLICIT_NOTIFY |
6056 CLUTTER_PARAM_ANIMATABLE);
6057
6058 /**
6059 * ClutterActor:fixed-x:
6060 *
6061 * The fixed X position of the actor in pixels.
6062 *
6063 * Writing this property sets #ClutterActor:fixed-position-set
6064 * property as well, as a side effect
6065 *
6066 * Since: 0.8
6067 */
6068 obj_props[PROP_FIXED_X] =
6069 g_param_spec_float ("fixed-x",
6070 P_("Fixed X"),
6071 P_("Forced X position of the actor"),
6072 -G_MAXFLOAT, G_MAXFLOAT,
6073 0.0,
6074 CLUTTER_PARAM_READWRITE |
6075 G_PARAM_EXPLICIT_NOTIFY);
6076
6077 /**
6078 * ClutterActor:fixed-y:
6079 *
6080 * The fixed Y position of the actor in pixels.
6081 *
6082 * Writing this property sets the #ClutterActor:fixed-position-set
6083 * property as well, as a side effect
6084 *
6085 * Since: 0.8
6086 */
6087 obj_props[PROP_FIXED_Y] =
6088 g_param_spec_float ("fixed-y",
6089 P_("Fixed Y"),
6090 P_("Forced Y position of the actor"),
6091 -G_MAXFLOAT, G_MAXFLOAT,
6092 0,
6093 CLUTTER_PARAM_READWRITE |
6094 G_PARAM_EXPLICIT_NOTIFY);
6095
6096 /**
6097 * ClutterActor:fixed-position-set:
6098 *
6099 * This flag controls whether the #ClutterActor:fixed-x and
6100 * #ClutterActor:fixed-y properties are used
6101 *
6102 * Since: 0.8
6103 */
6104 obj_props[PROP_FIXED_POSITION_SET] =
6105 g_param_spec_boolean ("fixed-position-set",
6106 P_("Fixed position set"),
6107 P_("Whether to use fixed positioning for the actor"),
6108 FALSE,
6109 CLUTTER_PARAM_READWRITE |
6110 G_PARAM_EXPLICIT_NOTIFY);
6111
6112 /**
6113 * ClutterActor:min-width:
6114 *
6115 * A forced minimum width request for the actor, in pixels
6116 *
6117 * Writing this property sets the #ClutterActor:min-width-set property
6118 * as well, as a side effect.
6119 *
6120 *This property overrides the usual width request of the actor.
6121 *
6122 * Since: 0.8
6123 */
6124 obj_props[PROP_MIN_WIDTH] =
6125 g_param_spec_float ("min-width",
6126 P_("Min Width"),
6127 P_("Forced minimum width request for the actor"),
6128 0.0, G_MAXFLOAT,
6129 0.0,
6130 CLUTTER_PARAM_READWRITE |
6131 G_PARAM_EXPLICIT_NOTIFY);
6132
6133 /**
6134 * ClutterActor:min-height:
6135 *
6136 * A forced minimum height request for the actor, in pixels
6137 *
6138 * Writing this property sets the #ClutterActor:min-height-set property
6139 * as well, as a side effect. This property overrides the usual height
6140 * request of the actor.
6141 *
6142 * Since: 0.8
6143 */
6144 obj_props[PROP_MIN_HEIGHT] =
6145 g_param_spec_float ("min-height",
6146 P_("Min Height"),
6147 P_("Forced minimum height request for the actor"),
6148 0.0, G_MAXFLOAT,
6149 0.0,
6150 CLUTTER_PARAM_READWRITE |
6151 G_PARAM_EXPLICIT_NOTIFY);
6152
6153 /**
6154 * ClutterActor:natural-width:
6155 *
6156 * A forced natural width request for the actor, in pixels
6157 *
6158 * Writing this property sets the #ClutterActor:natural-width-set
6159 * property as well, as a side effect. This property overrides the
6160 * usual width request of the actor
6161 *
6162 * Since: 0.8
6163 */
6164 obj_props[PROP_NATURAL_WIDTH] =
6165 g_param_spec_float ("natural-width",
6166 P_("Natural Width"),
6167 P_("Forced natural width request for the actor"),
6168 0.0, G_MAXFLOAT,
6169 0.0,
6170 CLUTTER_PARAM_READWRITE |
6171 G_PARAM_EXPLICIT_NOTIFY);
6172
6173 /**
6174 * ClutterActor:natural-height:
6175 *
6176 * A forced natural height request for the actor, in pixels
6177 *
6178 * Writing this property sets the #ClutterActor:natural-height-set
6179 * property as well, as a side effect. This property overrides the
6180 * usual height request of the actor
6181 *
6182 * Since: 0.8
6183 */
6184 obj_props[PROP_NATURAL_HEIGHT] =
6185 g_param_spec_float ("natural-height",
6186 P_("Natural Height"),
6187 P_("Forced natural height request for the actor"),
6188 0.0, G_MAXFLOAT,
6189 0.0,
6190 CLUTTER_PARAM_READWRITE |
6191 G_PARAM_EXPLICIT_NOTIFY);
6192
6193 /**
6194 * ClutterActor:min-width-set:
6195 *
6196 * This flag controls whether the #ClutterActor:min-width property
6197 * is used
6198 *
6199 * Since: 0.8
6200 */
6201 obj_props[PROP_MIN_WIDTH_SET] =
6202 g_param_spec_boolean ("min-width-set",
6203 P_("Minimum width set"),
6204 P_("Whether to use the min-width property"),
6205 FALSE,
6206 CLUTTER_PARAM_READWRITE |
6207 G_PARAM_EXPLICIT_NOTIFY);
6208
6209 /**
6210 * ClutterActor:min-height-set:
6211 *
6212 * This flag controls whether the #ClutterActor:min-height property
6213 * is used
6214 *
6215 * Since: 0.8
6216 */
6217 obj_props[PROP_MIN_HEIGHT_SET] =
6218 g_param_spec_boolean ("min-height-set",
6219 P_("Minimum height set"),
6220 P_("Whether to use the min-height property"),
6221 FALSE,
6222 CLUTTER_PARAM_READWRITE |
6223 G_PARAM_EXPLICIT_NOTIFY);
6224
6225 /**
6226 * ClutterActor:natural-width-set:
6227 *
6228 * This flag controls whether the #ClutterActor:natural-width property
6229 * is used
6230 *
6231 * Since: 0.8
6232 */
6233 obj_props[PROP_NATURAL_WIDTH_SET] =
6234 g_param_spec_boolean ("natural-width-set",
6235 P_("Natural width set"),
6236 P_("Whether to use the natural-width property"),
6237 FALSE,
6238 CLUTTER_PARAM_READWRITE |
6239 G_PARAM_EXPLICIT_NOTIFY);
6240
6241 /**
6242 * ClutterActor:natural-height-set:
6243 *
6244 * This flag controls whether the #ClutterActor:natural-height property
6245 * is used
6246 *
6247 * Since: 0.8
6248 */
6249 obj_props[PROP_NATURAL_HEIGHT_SET] =
6250 g_param_spec_boolean ("natural-height-set",
6251 P_("Natural height set"),
6252 P_("Whether to use the natural-height property"),
6253 FALSE,
6254 CLUTTER_PARAM_READWRITE |
6255 G_PARAM_EXPLICIT_NOTIFY);
6256
6257 /**
6258 * ClutterActor:allocation:
6259 *
6260 * The allocation for the actor, in pixels
6261 *
6262 * This is property is read-only, but you might monitor it to know when an
6263 * actor moves or resizes
6264 *
6265 * Since: 0.8
6266 */
6267 obj_props[PROP_ALLOCATION] =
6268 g_param_spec_boxed ("allocation",
6269 P_("Allocation"),
6270 P_("The actor's allocation"),
6271 CLUTTER_TYPE_ACTOR_BOX,
6272 G_PARAM_READABLE |
6273 G_PARAM_STATIC_STRINGS |
6274 G_PARAM_EXPLICIT_NOTIFY |
6275 CLUTTER_PARAM_ANIMATABLE);
6276
6277 /**
6278 * ClutterActor:request-mode:
6279 *
6280 * Request mode for the #ClutterActor. The request mode determines the
6281 * type of geometry management used by the actor, either height for width
6282 * (the default) or width for height.
6283 *
6284 * For actors implementing height for width, the parent container should get
6285 * the preferred width first, and then the preferred height for that width.
6286 *
6287 * For actors implementing width for height, the parent container should get
6288 * the preferred height first, and then the preferred width for that height.
6289 *
6290 * For instance:
6291 *
6292 * |[<!-- language="C" -->
6293 * ClutterRequestMode mode;
6294 * gfloat natural_width, min_width;
6295 * gfloat natural_height, min_height;
6296 *
6297 * mode = clutter_actor_get_request_mode (child);
6298 * if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
6299 * {
6300 * clutter_actor_get_preferred_width (child, -1,
6301 * &min_width,
6302 * &natural_width);
6303 * clutter_actor_get_preferred_height (child, natural_width,
6304 * &min_height,
6305 * &natural_height);
6306 * }
6307 * else if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
6308 * {
6309 * clutter_actor_get_preferred_height (child, -1,
6310 * &min_height,
6311 * &natural_height);
6312 * clutter_actor_get_preferred_width (child, natural_height,
6313 * &min_width,
6314 * &natural_width);
6315 * }
6316 * else if (mode == CLUTTER_REQUEST_CONTENT_SIZE)
6317 * {
6318 * ClutterContent *content = clutter_actor_get_content (child);
6319 *
6320 * min_width, min_height = 0;
6321 * natural_width = natural_height = 0;
6322 *
6323 * if (content != NULL)
6324 * clutter_content_get_preferred_size (content, &natural_width, &natural_height);
6325 * }
6326 * ]|
6327 *
6328 * will retrieve the minimum and natural width and height depending on the
6329 * preferred request mode of the #ClutterActor "child".
6330 *
6331 * The clutter_actor_get_preferred_size() function will implement this
6332 * check for you.
6333 *
6334 * Since: 0.8
6335 */
6336 obj_props[PROP_REQUEST_MODE] =
6337 g_param_spec_enum ("request-mode",
6338 P_("Request Mode"),
6339 P_("The actor's request mode"),
6340 CLUTTER_TYPE_REQUEST_MODE,
6341 CLUTTER_REQUEST_HEIGHT_FOR_WIDTH,
6342 CLUTTER_PARAM_READWRITE |
6343 G_PARAM_EXPLICIT_NOTIFY);
6344
6345 /**
6346 * ClutterActor:z-position:
6347 *
6348 * The actor's position on the Z axis, relative to the parent's
6349 * transformations.
6350 *
6351 * Positive values will bring the actor's position nearer to the user,
6352 * whereas negative values will bring the actor's position farther from
6353 * the user.
6354 *
6355 * The #ClutterActor:z-position does not affect the paint or allocation
6356 * order.
6357 *
6358 * The #ClutterActor:z-position property is animatable.
6359 *
6360 * Since: 1.12
6361 */
6362 obj_props[PROP_Z_POSITION] =
6363 g_param_spec_float ("z-position",
6364 P_("Z Position"),
6365 P_("The actor's position on the Z axis"),
6366 -G_MAXFLOAT, G_MAXFLOAT,
6367 0.0f,
6368 G_PARAM_READWRITE |
6369 G_PARAM_STATIC_STRINGS |
6370 G_PARAM_EXPLICIT_NOTIFY |
6371 CLUTTER_PARAM_ANIMATABLE);
6372
6373 /**
6374 * ClutterActor:opacity:
6375 *
6376 * Opacity of an actor, between 0 (fully transparent) and
6377 * 255 (fully opaque)
6378 *
6379 * The #ClutterActor:opacity property is animatable.
6380 */
6381 obj_props[PROP_OPACITY] =
6382 g_param_spec_uint ("opacity",
6383 P_("Opacity"),
6384 P_("Opacity of an actor"),
6385 0, 255,
6386 255,
6387 G_PARAM_READWRITE |
6388 G_PARAM_STATIC_STRINGS |
6389 G_PARAM_EXPLICIT_NOTIFY |
6390 CLUTTER_PARAM_ANIMATABLE);
6391
6392 /**
6393 * ClutterActor:offscreen-redirect:
6394 *
6395 * Determines the conditions in which the actor will be redirected
6396 * to an offscreen framebuffer while being painted. For example this
6397 * can be used to cache an actor in a framebuffer or for improved
6398 * handling of transparent actors. See
6399 * clutter_actor_set_offscreen_redirect() for details.
6400 *
6401 * Since: 1.8
6402 */
6403 obj_props[PROP_OFFSCREEN_REDIRECT] =
6404 g_param_spec_flags ("offscreen-redirect",
6405 P_("Offscreen redirect"),
6406 P_("Flags controlling when to flatten the actor into a single image"),
6407 CLUTTER_TYPE_OFFSCREEN_REDIRECT,
6408 0,
6409 CLUTTER_PARAM_READWRITE);
6410
6411 /**
6412 * ClutterActor:visible:
6413 *
6414 * Whether the actor is set to be visible or not
6415 *
6416 * See also #ClutterActor:mapped
6417 */
6418 obj_props[PROP_VISIBLE] =
6419 g_param_spec_boolean ("visible",
6420 P_("Visible"),
6421 P_("Whether the actor is visible or not"),
6422 FALSE,
6423 CLUTTER_PARAM_READWRITE |
6424 G_PARAM_EXPLICIT_NOTIFY);
6425
6426 /**
6427 * ClutterActor:mapped:
6428 *
6429 * Whether the actor is mapped (will be painted when the stage
6430 * to which it belongs is mapped)
6431 *
6432 * Since: 1.0
6433 */
6434 obj_props[PROP_MAPPED] =
6435 g_param_spec_boolean ("mapped",
6436 P_("Mapped"),
6437 P_("Whether the actor will be painted"),
6438 FALSE,
6439 CLUTTER_PARAM_READABLE |
6440 G_PARAM_EXPLICIT_NOTIFY);
6441
6442 /**
6443 * ClutterActor:realized:
6444 *
6445 * Whether the actor has been realized
6446 *
6447 * Since: 1.0
6448 */
6449 obj_props[PROP_REALIZED] =
6450 g_param_spec_boolean ("realized",
6451 P_("Realized"),
6452 P_("Whether the actor has been realized"),
6453 FALSE,
6454 CLUTTER_PARAM_READABLE |
6455 G_PARAM_EXPLICIT_NOTIFY);
6456
6457 /**
6458 * ClutterActor:reactive:
6459 *
6460 * Whether the actor is reactive to events or not
6461 *
6462 * Only reactive actors will emit event-related signals
6463 *
6464 * Since: 0.6
6465 */
6466 obj_props[PROP_REACTIVE] =
6467 g_param_spec_boolean ("reactive",
6468 P_("Reactive"),
6469 P_("Whether the actor is reactive to events"),
6470 FALSE,
6471 CLUTTER_PARAM_READWRITE |
6472 G_PARAM_EXPLICIT_NOTIFY);
6473
6474 /**
6475 * ClutterActor:has-clip:
6476 *
6477 * Whether the actor has the #ClutterActor:clip property set or not
6478 */
6479 obj_props[PROP_HAS_CLIP] =
6480 g_param_spec_boolean ("has-clip",
6481 P_("Has Clip"),
6482 P_("Whether the actor has a clip set"),
6483 FALSE,
6484 CLUTTER_PARAM_READABLE |
6485 G_PARAM_EXPLICIT_NOTIFY);
6486
6487 /**
6488 * ClutterActor:clip-rect:
6489 *
6490 * The visible region of the actor, in actor-relative coordinates,
6491 * expressed as a #graphene_rect_t.
6492 *
6493 * Setting this property to %NULL will unset the existing clip.
6494 *
6495 * Setting this property will change the #ClutterActor:has-clip
6496 * property as a side effect.
6497 *
6498 * Since: 1.12
6499 */
6500 obj_props[PROP_CLIP_RECT] =
6501 g_param_spec_boxed ("clip-rect",
6502 P_("Clip Rectangle"),
6503 P_("The visible region of the actor"),
6504 GRAPHENE_TYPE_RECT,
6505 G_PARAM_READWRITE |
6506 G_PARAM_STATIC_STRINGS |
6507 G_PARAM_EXPLICIT_NOTIFY);
6508
6509 /**
6510 * ClutterActor:name:
6511 *
6512 * The name of the actor
6513 *
6514 * Since: 0.2
6515 */
6516 obj_props[PROP_NAME] =
6517 g_param_spec_string ("name",
6518 P_("Name"),
6519 P_("Name of the actor"),
6520 NULL,
6521 CLUTTER_PARAM_READWRITE |
6522 G_PARAM_EXPLICIT_NOTIFY);
6523
6524 /**
6525 * ClutterActor:pivot-point:
6526 *
6527 * The point around which the scaling and rotation transformations occur.
6528 *
6529 * The pivot point is expressed in normalized coordinates space, with (0, 0)
6530 * being the top left corner of the actor and (1, 1) the bottom right corner
6531 * of the actor.
6532 *
6533 * The default pivot point is located at (0, 0).
6534 *
6535 * The #ClutterActor:pivot-point property is animatable.
6536 *
6537 * Since: 1.12
6538 */
6539 obj_props[PROP_PIVOT_POINT] =
6540 g_param_spec_boxed ("pivot-point",
6541 P_("Pivot Point"),
6542 P_("The point around which the scaling and rotation occur"),
6543 GRAPHENE_TYPE_POINT,
6544 G_PARAM_READWRITE |
6545 G_PARAM_STATIC_STRINGS |
6546 G_PARAM_EXPLICIT_NOTIFY |
6547 CLUTTER_PARAM_ANIMATABLE);
6548
6549 /**
6550 * ClutterActor:pivot-point-z:
6551 *
6552 * The Z component of the #ClutterActor:pivot-point, expressed as a value
6553 * along the Z axis.
6554 *
6555 * The #ClutterActor:pivot-point-z property is animatable.
6556 *
6557 * Since: 1.12
6558 */
6559 obj_props[PROP_PIVOT_POINT_Z] =
6560 g_param_spec_float ("pivot-point-z",
6561 P_("Pivot Point Z"),
6562 P_("Z component of the pivot point"),
6563 -G_MAXFLOAT, G_MAXFLOAT,
6564 0.f,
6565 G_PARAM_READWRITE |
6566 G_PARAM_STATIC_STRINGS |
6567 G_PARAM_EXPLICIT_NOTIFY |
6568 CLUTTER_PARAM_ANIMATABLE);
6569
6570 /**
6571 * ClutterActor:scale-x:
6572 *
6573 * The horizontal scale of the actor.
6574 *
6575 * The #ClutterActor:scale-x property is animatable.
6576 *
6577 * Since: 0.6
6578 */
6579 obj_props[PROP_SCALE_X] =
6580 g_param_spec_double ("scale-x",
6581 P_("Scale X"),
6582 P_("Scale factor on the X axis"),
6583 -G_MAXDOUBLE, G_MAXDOUBLE,
6584 1.0,
6585 G_PARAM_READWRITE |
6586 G_PARAM_STATIC_STRINGS |
6587 G_PARAM_EXPLICIT_NOTIFY |
6588 CLUTTER_PARAM_ANIMATABLE);
6589
6590 /**
6591 * ClutterActor:scale-y:
6592 *
6593 * The vertical scale of the actor.
6594 *
6595 * The #ClutterActor:scale-y property is animatable.
6596 *
6597 * Since: 0.6
6598 */
6599 obj_props[PROP_SCALE_Y] =
6600 g_param_spec_double ("scale-y",
6601 P_("Scale Y"),
6602 P_("Scale factor on the Y axis"),
6603 -G_MAXDOUBLE, G_MAXDOUBLE,
6604 1.0,
6605 G_PARAM_READWRITE |
6606 G_PARAM_STATIC_STRINGS |
6607 G_PARAM_EXPLICIT_NOTIFY |
6608 CLUTTER_PARAM_ANIMATABLE);
6609
6610 /**
6611 * ClutterActor:scale-z:
6612 *
6613 * The scale factor of the actor along the Z axis.
6614 *
6615 * The #ClutterActor:scale-y property is animatable.
6616 *
6617 * Since: 1.12
6618 */
6619 obj_props[PROP_SCALE_Z] =
6620 g_param_spec_double ("scale-z",
6621 P_("Scale Z"),
6622 P_("Scale factor on the Z axis"),
6623 -G_MAXDOUBLE, G_MAXDOUBLE,
6624 1.0,
6625 G_PARAM_READWRITE |
6626 G_PARAM_STATIC_STRINGS |
6627 G_PARAM_EXPLICIT_NOTIFY |
6628 CLUTTER_PARAM_ANIMATABLE);
6629
6630 /**
6631 * ClutterActor:rotation-angle-x:
6632 *
6633 * The rotation angle on the X axis.
6634 *
6635 * The #ClutterActor:rotation-angle-x property is animatable.
6636 *
6637 * Since: 0.6
6638 */
6639 obj_props[PROP_ROTATION_ANGLE_X] =
6640 g_param_spec_double ("rotation-angle-x",
6641 P_("Rotation Angle X"),
6642 P_("The rotation angle on the X axis"),
6643 -G_MAXDOUBLE, G_MAXDOUBLE,
6644 0.0,
6645 G_PARAM_READWRITE |
6646 G_PARAM_STATIC_STRINGS |
6647 G_PARAM_EXPLICIT_NOTIFY |
6648 CLUTTER_PARAM_ANIMATABLE);
6649
6650 /**
6651 * ClutterActor:rotation-angle-y:
6652 *
6653 * The rotation angle on the Y axis
6654 *
6655 * The #ClutterActor:rotation-angle-y property is animatable.
6656 *
6657 * Since: 0.6
6658 */
6659 obj_props[PROP_ROTATION_ANGLE_Y] =
6660 g_param_spec_double ("rotation-angle-y",
6661 P_("Rotation Angle Y"),
6662 P_("The rotation angle on the Y axis"),
6663 -G_MAXDOUBLE, G_MAXDOUBLE,
6664 0.0,
6665 G_PARAM_READWRITE |
6666 G_PARAM_STATIC_STRINGS |
6667 G_PARAM_EXPLICIT_NOTIFY |
6668 CLUTTER_PARAM_ANIMATABLE);
6669
6670 /**
6671 * ClutterActor:rotation-angle-z:
6672 *
6673 * The rotation angle on the Z axis
6674 *
6675 * The #ClutterActor:rotation-angle-z property is animatable.
6676 *
6677 * Since: 0.6
6678 */
6679 obj_props[PROP_ROTATION_ANGLE_Z] =
6680 g_param_spec_double ("rotation-angle-z",
6681 P_("Rotation Angle Z"),
6682 P_("The rotation angle on the Z axis"),
6683 -G_MAXDOUBLE, G_MAXDOUBLE,
6684 0.0,
6685 G_PARAM_READWRITE |
6686 G_PARAM_STATIC_STRINGS |
6687 CLUTTER_PARAM_ANIMATABLE);
6688
6689 /**
6690 * ClutterActor:translation-x:
6691 *
6692 * An additional translation applied along the X axis, relative
6693 * to the actor's #ClutterActor:pivot-point.
6694 *
6695 * The #ClutterActor:translation-x property is animatable.
6696 *
6697 * Since: 1.12
6698 */
6699 obj_props[PROP_TRANSLATION_X] =
6700 g_param_spec_float ("translation-x",
6701 P_("Translation X"),
6702 P_("Translation along the X axis"),
6703 -G_MAXFLOAT, G_MAXFLOAT,
6704 0.f,
6705 G_PARAM_READWRITE |
6706 G_PARAM_STATIC_STRINGS |
6707 G_PARAM_EXPLICIT_NOTIFY |
6708 CLUTTER_PARAM_ANIMATABLE);
6709
6710 /**
6711 * ClutterActor:translation-y:
6712 *
6713 * An additional translation applied along the Y axis, relative
6714 * to the actor's #ClutterActor:pivot-point.
6715 *
6716 * The #ClutterActor:translation-y property is animatable.
6717 *
6718 * Since: 1.12
6719 */
6720 obj_props[PROP_TRANSLATION_Y] =
6721 g_param_spec_float ("translation-y",
6722 P_("Translation Y"),
6723 P_("Translation along the Y axis"),
6724 -G_MAXFLOAT, G_MAXFLOAT,
6725 0.f,
6726 G_PARAM_READWRITE |
6727 G_PARAM_STATIC_STRINGS |
6728 G_PARAM_EXPLICIT_NOTIFY |
6729 CLUTTER_PARAM_ANIMATABLE);
6730
6731 /**
6732 * ClutterActor:translation-z:
6733 *
6734 * An additional translation applied along the Z axis, relative
6735 * to the actor's #ClutterActor:pivot-point.
6736 *
6737 * The #ClutterActor:translation-z property is animatable.
6738 *
6739 * Since: 1.12
6740 */
6741 obj_props[PROP_TRANSLATION_Z] =
6742 g_param_spec_float ("translation-z",
6743 P_("Translation Z"),
6744 P_("Translation along the Z axis"),
6745 -G_MAXFLOAT, G_MAXFLOAT,
6746 0.f,
6747 G_PARAM_READWRITE |
6748 G_PARAM_STATIC_STRINGS |
6749 G_PARAM_EXPLICIT_NOTIFY |
6750 CLUTTER_PARAM_ANIMATABLE);
6751
6752 /**
6753 * ClutterActor:transform:
6754 *
6755 * Overrides the transformations of a #ClutterActor with a custom
6756 * matrix.
6757 *
6758 * The matrix specified by the #ClutterActor:transform property is
6759 * applied to the actor and its children relative to the actor's
6760 * #ClutterActor:allocation and #ClutterActor:pivot-point.
6761 *
6762 * Application code should rarely need to use this function directly.
6763 *
6764 * Setting this property with a #graphene_matrix_t will set the
6765 * #ClutterActor:transform-set property to %TRUE as a side effect;
6766 * setting this property with %NULL will set the
6767 * #ClutterActor:transform-set property to %FALSE.
6768 *
6769 * The #ClutterActor:transform property is animatable.
6770 *
6771 * Since: 1.12
6772 */
6773 obj_props[PROP_TRANSFORM] =
6774 g_param_spec_boxed ("transform",
6775 P_("Transform"),
6776 P_("Transformation matrix"),
6777 GRAPHENE_TYPE_MATRIX,
6778 G_PARAM_READWRITE |
6779 G_PARAM_STATIC_STRINGS |
6780 G_PARAM_EXPLICIT_NOTIFY |
6781 CLUTTER_PARAM_ANIMATABLE);
6782
6783 /**
6784 * ClutterActor:transform-set:
6785 *
6786 * Whether the #ClutterActor:transform property is set.
6787 *
6788 * Since: 1.12
6789 */
6790 obj_props[PROP_TRANSFORM_SET] =
6791 g_param_spec_boolean ("transform-set",
6792 P_("Transform Set"),
6793 P_("Whether the transform property is set"),
6794 FALSE,
6795 G_PARAM_READABLE |
6796 G_PARAM_STATIC_STRINGS |
6797 G_PARAM_EXPLICIT_NOTIFY);
6798
6799 /**
6800 * ClutterActor:child-transform:
6801 *
6802 * Applies a transformation matrix on each child of an actor.
6803 *
6804 * Setting this property with a #graphene_matrix_t will set the
6805 * #ClutterActor:child-transform-set property to %TRUE as a side effect;
6806 * setting this property with %NULL will set the
6807 * #ClutterActor:child-transform-set property to %FALSE.
6808 *
6809 * The #ClutterActor:child-transform property is animatable.
6810 *
6811 * Since: 1.12
6812 */
6813 obj_props[PROP_CHILD_TRANSFORM] =
6814 g_param_spec_boxed ("child-transform",
6815 P_("Child Transform"),
6816 P_("Children transformation matrix"),
6817 GRAPHENE_TYPE_MATRIX,
6818 G_PARAM_READWRITE |
6819 G_PARAM_STATIC_STRINGS |
6820 G_PARAM_EXPLICIT_NOTIFY |
6821 CLUTTER_PARAM_ANIMATABLE);
6822
6823 /**
6824 * ClutterActor:child-transform-set:
6825 *
6826 * Whether the #ClutterActor:child-transform property is set.
6827 *
6828 * Since: 1.12
6829 */
6830 obj_props[PROP_CHILD_TRANSFORM_SET] =
6831 g_param_spec_boolean ("child-transform-set",
6832 P_("Child Transform Set"),
6833 P_("Whether the child-transform property is set"),
6834 FALSE,
6835 G_PARAM_READABLE |
6836 G_PARAM_STATIC_STRINGS |
6837 G_PARAM_EXPLICIT_NOTIFY);
6838
6839 /**
6840 * ClutterActor:show-on-set-parent:
6841 *
6842 * If %TRUE, the actor is automatically shown when parented.
6843 *
6844 * Calling clutter_actor_hide() on an actor which has not been
6845 * parented will set this property to %FALSE as a side effect.
6846 *
6847 * Since: 0.8
6848 */
6849 obj_props[PROP_SHOW_ON_SET_PARENT] = /* XXX:2.0 - remove */
6850 g_param_spec_boolean ("show-on-set-parent",
6851 P_("Show on set parent"),
6852 P_("Whether the actor is shown when parented"),
6853 TRUE,
6854 CLUTTER_PARAM_READWRITE);
6855
6856 /**
6857 * ClutterActor:clip-to-allocation:
6858 *
6859 * Whether the clip region should track the allocated area
6860 * of the actor.
6861 *
6862 * This property is ignored if a clip area has been explicitly
6863 * set using clutter_actor_set_clip().
6864 *
6865 * Since: 1.0
6866 */
6867 obj_props[PROP_CLIP_TO_ALLOCATION] =
6868 g_param_spec_boolean ("clip-to-allocation",
6869 P_("Clip to Allocation"),
6870 P_("Sets the clip region to track the actor's allocation"),
6871 FALSE,
6872 CLUTTER_PARAM_READWRITE |
6873 G_PARAM_EXPLICIT_NOTIFY);
6874
6875 /**
6876 * ClutterActor:text-direction:
6877 *
6878 * The direction of the text inside a #ClutterActor.
6879 *
6880 * Since: 1.0
6881 */
6882 obj_props[PROP_TEXT_DIRECTION] =
6883 g_param_spec_enum ("text-direction",
6884 P_("Text Direction"),
6885 P_("Direction of the text"),
6886 CLUTTER_TYPE_TEXT_DIRECTION,
6887 CLUTTER_TEXT_DIRECTION_LTR,
6888 CLUTTER_PARAM_READWRITE |
6889 G_PARAM_EXPLICIT_NOTIFY);
6890
6891 /**
6892 * ClutterActor:has-pointer:
6893 *
6894 * Whether the actor contains the pointer of a #ClutterInputDevice
6895 * or not.
6896 *
6897 * Since: 1.2
6898 */
6899 obj_props[PROP_HAS_POINTER] =
6900 g_param_spec_boolean ("has-pointer",
6901 P_("Has Pointer"),
6902 P_("Whether the actor contains the pointer of an input device"),
6903 FALSE,
6904 CLUTTER_PARAM_READABLE |
6905 G_PARAM_EXPLICIT_NOTIFY);
6906
6907 /**
6908 * ClutterActor:actions:
6909 *
6910 * Adds a #ClutterAction to the actor
6911 *
6912 * Since: 1.4
6913 */
6914 obj_props[PROP_ACTIONS] =
6915 g_param_spec_object ("actions",
6916 P_("Actions"),
6917 P_("Adds an action to the actor"),
6918 CLUTTER_TYPE_ACTION,
6919 CLUTTER_PARAM_WRITABLE |
6920 G_PARAM_EXPLICIT_NOTIFY);
6921
6922 /**
6923 * ClutterActor:constraints:
6924 *
6925 * Adds a #ClutterConstraint to the actor
6926 *
6927 * Since: 1.4
6928 */
6929 obj_props[PROP_CONSTRAINTS] =
6930 g_param_spec_object ("constraints",
6931 P_("Constraints"),
6932 P_("Adds a constraint to the actor"),
6933 CLUTTER_TYPE_CONSTRAINT,
6934 CLUTTER_PARAM_WRITABLE |
6935 G_PARAM_EXPLICIT_NOTIFY);
6936
6937 /**
6938 * ClutterActor:effect:
6939 *
6940 * Adds #ClutterEffect to the list of effects be applied on a #ClutterActor
6941 *
6942 * Since: 1.4
6943 */
6944 obj_props[PROP_EFFECT] =
6945 g_param_spec_object ("effect",
6946 P_("Effect"),
6947 P_("Add an effect to be applied on the actor"),
6948 CLUTTER_TYPE_EFFECT,
6949 CLUTTER_PARAM_WRITABLE |
6950 G_PARAM_EXPLICIT_NOTIFY);
6951
6952 /**
6953 * ClutterActor:layout-manager:
6954 *
6955 * A delegate object for controlling the layout of the children of
6956 * an actor.
6957 *
6958 * Since: 1.10
6959 */
6960 obj_props[PROP_LAYOUT_MANAGER] =
6961 g_param_spec_object ("layout-manager",
6962 P_("Layout Manager"),
6963 P_("The object controlling the layout of an actor's children"),
6964 CLUTTER_TYPE_LAYOUT_MANAGER,
6965 CLUTTER_PARAM_READWRITE |
6966 G_PARAM_EXPLICIT_NOTIFY);
6967
6968 /**
6969 * ClutterActor:x-expand:
6970 *
6971 * Whether a layout manager should assign more space to the actor on
6972 * the X axis.
6973 *
6974 * Since: 1.12
6975 */
6976 obj_props[PROP_X_EXPAND] =
6977 g_param_spec_boolean ("x-expand",
6978 P_("X Expand"),
6979 P_("Whether extra horizontal space should be assigned to the actor"),
6980 FALSE,
6981 G_PARAM_READWRITE |
6982 G_PARAM_STATIC_STRINGS |
6983 G_PARAM_EXPLICIT_NOTIFY);
6984
6985 /**
6986 * ClutterActor:y-expand:
6987 *
6988 * Whether a layout manager should assign more space to the actor on
6989 * the Y axis.
6990 *
6991 * Since: 1.12
6992 */
6993 obj_props[PROP_Y_EXPAND] =
6994 g_param_spec_boolean ("y-expand",
6995 P_("Y Expand"),
6996 P_("Whether extra vertical space should be assigned to the actor"),
6997 FALSE,
6998 G_PARAM_READWRITE |
6999 G_PARAM_STATIC_STRINGS |
7000 G_PARAM_EXPLICIT_NOTIFY);
7001
7002 /**
7003 * ClutterActor:x-align:
7004 *
7005 * The alignment of an actor on the X axis, if the actor has been given
7006 * extra space for its allocation. See also the #ClutterActor:x-expand
7007 * property.
7008 *
7009 * Since: 1.10
7010 */
7011 obj_props[PROP_X_ALIGN] =
7012 g_param_spec_enum ("x-align",
7013 P_("X Alignment"),
7014 P_("The alignment of the actor on the X axis within its allocation"),
7015 CLUTTER_TYPE_ACTOR_ALIGN,
7016 CLUTTER_ACTOR_ALIGN_FILL,
7017 CLUTTER_PARAM_READWRITE |
7018 G_PARAM_EXPLICIT_NOTIFY);
7019
7020 /**
7021 * ClutterActor:y-align:
7022 *
7023 * The alignment of an actor on the Y axis, if the actor has been given
7024 * extra space for its allocation.
7025 *
7026 * Since: 1.10
7027 */
7028 obj_props[PROP_Y_ALIGN] =
7029 g_param_spec_enum ("y-align",
7030 P_("Y Alignment"),
7031 P_("The alignment of the actor on the Y axis within its allocation"),
7032 CLUTTER_TYPE_ACTOR_ALIGN,
7033 CLUTTER_ACTOR_ALIGN_FILL,
7034 CLUTTER_PARAM_READWRITE |
7035 G_PARAM_EXPLICIT_NOTIFY);
7036
7037 /**
7038 * ClutterActor:margin-top:
7039 *
7040 * The margin (in pixels) from the top of the actor.
7041 *
7042 * This property adds a margin to the actor's preferred size; the margin
7043 * will be automatically taken into account when allocating the actor.
7044 *
7045 * The #ClutterActor:margin-top property is animatable.
7046 *
7047 * Since: 1.10
7048 */
7049 obj_props[PROP_MARGIN_TOP] =
7050 g_param_spec_float ("margin-top",
7051 P_("Margin Top"),
7052 P_("Extra space at the top"),
7053 0.0, G_MAXFLOAT,
7054 0.0,
7055 G_PARAM_READWRITE |
7056 G_PARAM_STATIC_STRINGS |
7057 G_PARAM_EXPLICIT_NOTIFY |
7058 CLUTTER_PARAM_ANIMATABLE);
7059
7060 /**
7061 * ClutterActor:margin-bottom:
7062 *
7063 * The margin (in pixels) from the bottom of the actor.
7064 *
7065 * This property adds a margin to the actor's preferred size; the margin
7066 * will be automatically taken into account when allocating the actor.
7067 *
7068 * The #ClutterActor:margin-bottom property is animatable.
7069 *
7070 * Since: 1.10
7071 */
7072 obj_props[PROP_MARGIN_BOTTOM] =
7073 g_param_spec_float ("margin-bottom",
7074 P_("Margin Bottom"),
7075 P_("Extra space at the bottom"),
7076 0.0, G_MAXFLOAT,
7077 0.0,
7078 G_PARAM_READWRITE |
7079 G_PARAM_STATIC_STRINGS |
7080 G_PARAM_EXPLICIT_NOTIFY |
7081 CLUTTER_PARAM_ANIMATABLE);
7082
7083 /**
7084 * ClutterActor:margin-left:
7085 *
7086 * The margin (in pixels) from the left of the actor.
7087 *
7088 * This property adds a margin to the actor's preferred size; the margin
7089 * will be automatically taken into account when allocating the actor.
7090 *
7091 * The #ClutterActor:margin-left property is animatable.
7092 *
7093 * Since: 1.10
7094 */
7095 obj_props[PROP_MARGIN_LEFT] =
7096 g_param_spec_float ("margin-left",
7097 P_("Margin Left"),
7098 P_("Extra space at the left"),
7099 0.0, G_MAXFLOAT,
7100 0.0,
7101 G_PARAM_READWRITE |
7102 G_PARAM_STATIC_STRINGS |
7103 G_PARAM_EXPLICIT_NOTIFY |
7104 CLUTTER_PARAM_ANIMATABLE);
7105
7106 /**
7107 * ClutterActor:margin-right:
7108 *
7109 * The margin (in pixels) from the right of the actor.
7110 *
7111 * This property adds a margin to the actor's preferred size; the margin
7112 * will be automatically taken into account when allocating the actor.
7113 *
7114 * The #ClutterActor:margin-right property is animatable.
7115 *
7116 * Since: 1.10
7117 */
7118 obj_props[PROP_MARGIN_RIGHT] =
7119 g_param_spec_float ("margin-right",
7120 P_("Margin Right"),
7121 P_("Extra space at the right"),
7122 0.0, G_MAXFLOAT,
7123 0.0,
7124 G_PARAM_READWRITE |
7125 G_PARAM_STATIC_STRINGS |
7126 G_PARAM_EXPLICIT_NOTIFY |
7127 CLUTTER_PARAM_ANIMATABLE);
7128
7129 /**
7130 * ClutterActor:background-color-set:
7131 *
7132 * Whether the #ClutterActor:background-color property has been set.
7133 *
7134 * Since: 1.10
7135 */
7136 obj_props[PROP_BACKGROUND_COLOR_SET] =
7137 g_param_spec_boolean ("background-color-set",
7138 P_("Background Color Set"),
7139 P_("Whether the background color is set"),
7140 FALSE,
7141 CLUTTER_PARAM_READABLE |
7142 G_PARAM_EXPLICIT_NOTIFY);
7143
7144 /**
7145 * ClutterActor:background-color:
7146 *
7147 * Paints a solid fill of the actor's allocation using the specified
7148 * color.
7149 *
7150 * The #ClutterActor:background-color property is animatable.
7151 *
7152 * Since: 1.10
7153 */
7154 obj_props[PROP_BACKGROUND_COLOR] =
7155 clutter_param_spec_color ("background-color",
7156 P_("Background color"),
7157 P_("The actor's background color"),
7158 CLUTTER_COLOR_Transparent,
7159 G_PARAM_READWRITE |
7160 G_PARAM_STATIC_STRINGS |
7161 G_PARAM_EXPLICIT_NOTIFY |
7162 CLUTTER_PARAM_ANIMATABLE);
7163
7164 /**
7165 * ClutterActor:first-child:
7166 *
7167 * The actor's first child.
7168 *
7169 * Since: 1.10
7170 */
7171 obj_props[PROP_FIRST_CHILD] =
7172 g_param_spec_object ("first-child",
7173 P_("First Child"),
7174 P_("The actor's first child"),
7175 CLUTTER_TYPE_ACTOR,
7176 CLUTTER_PARAM_READABLE |
7177 G_PARAM_EXPLICIT_NOTIFY);
7178
7179 /**
7180 * ClutterActor:last-child:
7181 *
7182 * The actor's last child.
7183 *
7184 * Since: 1.10
7185 */
7186 obj_props[PROP_LAST_CHILD] =
7187 g_param_spec_object ("last-child",
7188 P_("Last Child"),
7189 P_("The actor's last child"),
7190 CLUTTER_TYPE_ACTOR,
7191 CLUTTER_PARAM_READABLE |
7192 G_PARAM_EXPLICIT_NOTIFY);
7193
7194 /**
7195 * ClutterActor:content:
7196 *
7197 * The #ClutterContent implementation that controls the content
7198 * of the actor.
7199 *
7200 * Since: 1.10
7201 */
7202 obj_props[PROP_CONTENT] =
7203 g_param_spec_object ("content",
7204 P_("Content"),
7205 P_("Delegate object for painting the actor's content"),
7206 CLUTTER_TYPE_CONTENT,
7207 CLUTTER_PARAM_READWRITE |
7208 G_PARAM_EXPLICIT_NOTIFY);
7209
7210 /**
7211 * ClutterActor:content-gravity:
7212 *
7213 * The alignment that should be honoured by the #ClutterContent
7214 * set with the #ClutterActor:content property.
7215 *
7216 * Changing the value of this property will change the bounding box of
7217 * the content; you can use the #ClutterActor:content-box property to
7218 * get the position and size of the content within the actor's
7219 * allocation.
7220 *
7221 * This property is meaningful only for #ClutterContent implementations
7222 * that have a preferred size, and if the preferred size is smaller than
7223 * the actor's allocation.
7224 *
7225 * The #ClutterActor:content-gravity property is animatable.
7226 *
7227 * Since: 1.10
7228 */
7229 obj_props[PROP_CONTENT_GRAVITY] =
7230 g_param_spec_enum ("content-gravity",
7231 P_("Content Gravity"),
7232 P_("Alignment of the actor's content"),
7233 CLUTTER_TYPE_CONTENT_GRAVITY,
7234 CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
7235 CLUTTER_PARAM_READWRITE |
7236 G_PARAM_EXPLICIT_NOTIFY);
7237
7238 /**
7239 * ClutterActor:content-box:
7240 *
7241 * The bounding box for the #ClutterContent used by the actor.
7242 *
7243 * The value of this property is controlled by the #ClutterActor:allocation
7244 * and #ClutterActor:content-gravity properties of #ClutterActor.
7245 *
7246 * The bounding box for the content is guaranteed to never exceed the
7247 * allocation's of the actor.
7248 *
7249 * Since: 1.10
7250 */
7251 obj_props[PROP_CONTENT_BOX] =
7252 g_param_spec_boxed ("content-box",
7253 P_("Content Box"),
7254 P_("The bounding box of the actor's content"),
7255 CLUTTER_TYPE_ACTOR_BOX,
7256 G_PARAM_READABLE |
7257 G_PARAM_STATIC_STRINGS |
7258 G_PARAM_EXPLICIT_NOTIFY |
7259 CLUTTER_PARAM_ANIMATABLE);
7260
7261 obj_props[PROP_MINIFICATION_FILTER] =
7262 g_param_spec_enum ("minification-filter",
7263 P_("Minification Filter"),
7264 P_("The filter used when reducing the size of the content"),
7265 CLUTTER_TYPE_SCALING_FILTER,
7266 CLUTTER_SCALING_FILTER_LINEAR,
7267 CLUTTER_PARAM_READWRITE |
7268 G_PARAM_EXPLICIT_NOTIFY);
7269
7270 obj_props[PROP_MAGNIFICATION_FILTER] =
7271 g_param_spec_enum ("magnification-filter",
7272 P_("Magnification Filter"),
7273 P_("The filter used when increasing the size of the content"),
7274 CLUTTER_TYPE_SCALING_FILTER,
7275 CLUTTER_SCALING_FILTER_LINEAR,
7276 CLUTTER_PARAM_READWRITE |
7277 G_PARAM_EXPLICIT_NOTIFY);
7278
7279 /**
7280 * ClutterActor:content-repeat:
7281 *
7282 * The repeat policy for the actor's #ClutterActor:content.
7283 *
7284 * Since: 1.12
7285 */
7286 obj_props[PROP_CONTENT_REPEAT] =
7287 g_param_spec_flags ("content-repeat",
7288 P_("Content Repeat"),
7289 P_("The repeat policy for the actor's content"),
7290 CLUTTER_TYPE_CONTENT_REPEAT,
7291 CLUTTER_REPEAT_NONE,
7292 G_PARAM_READWRITE |
7293 G_PARAM_STATIC_STRINGS |
7294 G_PARAM_EXPLICIT_NOTIFY);
7295
7296 g_object_class_install_properties (object_class, PROP_LAST, obj_props);
7297
7298 /**
7299 * ClutterActor::destroy:
7300 * @actor: the #ClutterActor which emitted the signal
7301 *
7302 * The ::destroy signal notifies that all references held on the
7303 * actor which emitted it should be released.
7304 *
7305 * The ::destroy signal should be used by all holders of a reference
7306 * on @actor.
7307 *
7308 * This signal might result in the finalization of the #ClutterActor
7309 * if all references are released.
7310 *
7311 * Composite actors and actors implementing the #ClutterContainer
7312 * interface should override the default implementation of the
7313 * class handler of this signal and call clutter_actor_destroy() on
7314 * their children. When overriding the default class handler, it is
7315 * required to chain up to the parent's implementation.
7316 *
7317 * Since: 0.2
7318 */
7319 actor_signals[DESTROY] =
7320 g_signal_new (I_("destroy"),
7321 G_TYPE_FROM_CLASS (object_class),
7322 G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
7323 G_STRUCT_OFFSET (ClutterActorClass, destroy),
7324 NULL, NULL, NULL,
7325 G_TYPE_NONE, 0);
7326 /**
7327 * ClutterActor::show:
7328 * @actor: the object which received the signal
7329 *
7330 * The ::show signal is emitted when an actor is visible and
7331 * rendered on the stage.
7332 *
7333 * Since: 0.2
7334 */
7335 actor_signals[SHOW] =
7336 g_signal_new (I_("show"),
7337 G_TYPE_FROM_CLASS (object_class),
7338 G_SIGNAL_RUN_FIRST,
7339 G_STRUCT_OFFSET (ClutterActorClass, show),
7340 NULL, NULL, NULL,
7341 G_TYPE_NONE, 0);
7342 /**
7343 * ClutterActor::hide:
7344 * @actor: the object which received the signal
7345 *
7346 * The ::hide signal is emitted when an actor is no longer rendered
7347 * on the stage.
7348 *
7349 * Since: 0.2
7350 */
7351 actor_signals[HIDE] =
7352 g_signal_new (I_("hide"),
7353 G_TYPE_FROM_CLASS (object_class),
7354 G_SIGNAL_RUN_FIRST,
7355 G_STRUCT_OFFSET (ClutterActorClass, hide),
7356 NULL, NULL, NULL,
7357 G_TYPE_NONE, 0);
7358 /**
7359 * ClutterActor::parent-set:
7360 * @actor: the object which received the signal
7361 * @old_parent: (allow-none): the previous parent of the actor, or %NULL
7362 *
7363 * This signal is emitted when the parent of the actor changes.
7364 *
7365 * Since: 0.2
7366 */
7367 actor_signals[PARENT_SET] =
7368 g_signal_new (I_("parent-set"),
7369 G_TYPE_FROM_CLASS (object_class),
7370 G_SIGNAL_RUN_LAST,
7371 G_STRUCT_OFFSET (ClutterActorClass, parent_set),
7372 NULL, NULL, NULL,
7373 G_TYPE_NONE, 1,
7374 CLUTTER_TYPE_ACTOR);
7375
7376 /**
7377 * ClutterActor::queue-relayout:
7378 * @actor: the actor being queued for relayout
7379 *
7380 * The ::queue_layout signal is emitted when clutter_actor_queue_relayout()
7381 * is called on an actor.
7382 *
7383 * The default implementation for #ClutterActor chains up to the
7384 * parent actor and queues a relayout on the parent, thus "bubbling"
7385 * the relayout queue up through the actor graph.
7386 *
7387 * The main purpose of this signal is to allow relayout to be propagated
7388 * properly in the procense of #ClutterClone actors. Applications will
7389 * not normally need to connect to this signal.
7390 *
7391 * Since: 1.2
7392 */
7393 actor_signals[QUEUE_RELAYOUT] =
7394 g_signal_new (I_("queue-relayout"),
7395 G_TYPE_FROM_CLASS (object_class),
7396 G_SIGNAL_RUN_LAST |
7397 G_SIGNAL_NO_HOOKS,
7398 G_STRUCT_OFFSET (ClutterActorClass, queue_relayout),
7399 NULL, NULL, NULL,
7400 G_TYPE_NONE, 0);
7401
7402 /**
7403 * ClutterActor::event:
7404 * @actor: the actor which received the event
7405 * @event: a #ClutterEvent
7406 *
7407 * The ::event signal is emitted each time an event is received
7408 * by the @actor. This signal will be emitted on every actor,
7409 * following the hierarchy chain, until it reaches the top-level
7410 * container (the #ClutterStage).
7411 *
7412 * Return value: %TRUE if the event has been handled by the actor,
7413 * or %FALSE to continue the emission.
7414 *
7415 * Since: 0.6
7416 */
7417 actor_signals[EVENT] =
7418 g_signal_new (I_("event"),
7419 G_TYPE_FROM_CLASS (object_class),
7420 G_SIGNAL_RUN_LAST,
7421 G_STRUCT_OFFSET (ClutterActorClass, event),
7422 _clutter_boolean_handled_accumulator, NULL,
7423 _clutter_marshal_BOOLEAN__BOXED,
7424 G_TYPE_BOOLEAN, 1,
7425 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7426 g_signal_set_va_marshaller (actor_signals[EVENT],
7427 G_TYPE_FROM_CLASS (object_class),
7428 _clutter_marshal_BOOLEAN__BOXEDv);
7429 /**
7430 * ClutterActor::button-press-event:
7431 * @actor: the actor which received the event
7432 * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
7433 *
7434 * The ::button-press-event signal is emitted each time a mouse button
7435 * is pressed on @actor.
7436 *
7437 * Return value: %TRUE if the event has been handled by the actor,
7438 * or %FALSE to continue the emission.
7439 *
7440 * Since: 0.6
7441 */
7442 actor_signals[BUTTON_PRESS_EVENT] =
7443 g_signal_new (I_("button-press-event"),
7444 G_TYPE_FROM_CLASS (object_class),
7445 G_SIGNAL_RUN_LAST,
7446 G_STRUCT_OFFSET (ClutterActorClass, button_press_event),
7447 _clutter_boolean_handled_accumulator, NULL,
7448 _clutter_marshal_BOOLEAN__BOXED,
7449 G_TYPE_BOOLEAN, 1,
7450 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7451 g_signal_set_va_marshaller (actor_signals[BUTTON_PRESS_EVENT],
7452 G_TYPE_FROM_CLASS (object_class),
7453 _clutter_marshal_BOOLEAN__BOXEDv);
7454 /**
7455 * ClutterActor::button-release-event:
7456 * @actor: the actor which received the event
7457 * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
7458 *
7459 * The ::button-release-event signal is emitted each time a mouse button
7460 * is released on @actor.
7461 *
7462 * Return value: %TRUE if the event has been handled by the actor,
7463 * or %FALSE to continue the emission.
7464 *
7465 * Since: 0.6
7466 */
7467 actor_signals[BUTTON_RELEASE_EVENT] =
7468 g_signal_new (I_("button-release-event"),
7469 G_TYPE_FROM_CLASS (object_class),
7470 G_SIGNAL_RUN_LAST,
7471 G_STRUCT_OFFSET (ClutterActorClass, button_release_event),
7472 _clutter_boolean_handled_accumulator, NULL,
7473 _clutter_marshal_BOOLEAN__BOXED,
7474 G_TYPE_BOOLEAN, 1,
7475 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7476 g_signal_set_va_marshaller (actor_signals[BUTTON_RELEASE_EVENT],
7477 G_TYPE_FROM_CLASS (object_class),
7478 _clutter_marshal_BOOLEAN__BOXEDv);
7479 /**
7480 * ClutterActor::scroll-event:
7481 * @actor: the actor which received the event
7482 * @event: (type ClutterScrollEvent): a #ClutterScrollEvent
7483 *
7484 * The ::scroll-event signal is emitted each time the mouse is
7485 * scrolled on @actor
7486 *
7487 * Return value: %TRUE if the event has been handled by the actor,
7488 * or %FALSE to continue the emission.
7489 *
7490 * Since: 0.6
7491 */
7492 actor_signals[SCROLL_EVENT] =
7493 g_signal_new (I_("scroll-event"),
7494 G_TYPE_FROM_CLASS (object_class),
7495 G_SIGNAL_RUN_LAST,
7496 G_STRUCT_OFFSET (ClutterActorClass, scroll_event),
7497 _clutter_boolean_handled_accumulator, NULL,
7498 _clutter_marshal_BOOLEAN__BOXED,
7499 G_TYPE_BOOLEAN, 1,
7500 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7501 g_signal_set_va_marshaller (actor_signals[SCROLL_EVENT],
7502 G_TYPE_FROM_CLASS (object_class),
7503 _clutter_marshal_BOOLEAN__BOXEDv);
7504 /**
7505 * ClutterActor::key-press-event:
7506 * @actor: the actor which received the event
7507 * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
7508 *
7509 * The ::key-press-event signal is emitted each time a keyboard button
7510 * is pressed while @actor has key focus (see clutter_stage_set_key_focus()).
7511 *
7512 * Return value: %TRUE if the event has been handled by the actor,
7513 * or %FALSE to continue the emission.
7514 *
7515 * Since: 0.6
7516 */
7517 actor_signals[KEY_PRESS_EVENT] =
7518 g_signal_new (I_("key-press-event"),
7519 G_TYPE_FROM_CLASS (object_class),
7520 G_SIGNAL_RUN_LAST,
7521 G_STRUCT_OFFSET (ClutterActorClass, key_press_event),
7522 _clutter_boolean_handled_accumulator, NULL,
7523 _clutter_marshal_BOOLEAN__BOXED,
7524 G_TYPE_BOOLEAN, 1,
7525 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7526 g_signal_set_va_marshaller (actor_signals[KEY_PRESS_EVENT],
7527 G_TYPE_FROM_CLASS (object_class),
7528 _clutter_marshal_BOOLEAN__BOXEDv);
7529 /**
7530 * ClutterActor::key-release-event:
7531 * @actor: the actor which received the event
7532 * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
7533 *
7534 * The ::key-release-event signal is emitted each time a keyboard button
7535 * is released while @actor has key focus (see
7536 * clutter_stage_set_key_focus()).
7537 *
7538 * Return value: %TRUE if the event has been handled by the actor,
7539 * or %FALSE to continue the emission.
7540 *
7541 * Since: 0.6
7542 */
7543 actor_signals[KEY_RELEASE_EVENT] =
7544 g_signal_new (I_("key-release-event"),
7545 G_TYPE_FROM_CLASS (object_class),
7546 G_SIGNAL_RUN_LAST,
7547 G_STRUCT_OFFSET (ClutterActorClass, key_release_event),
7548 _clutter_boolean_handled_accumulator, NULL,
7549 _clutter_marshal_BOOLEAN__BOXED,
7550 G_TYPE_BOOLEAN, 1,
7551 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7552 g_signal_set_va_marshaller (actor_signals[KEY_RELEASE_EVENT],
7553 G_TYPE_FROM_CLASS (object_class),
7554 _clutter_marshal_BOOLEAN__BOXEDv);
7555 /**
7556 * ClutterActor::motion-event:
7557 * @actor: the actor which received the event
7558 * @event: (type ClutterMotionEvent): a #ClutterMotionEvent
7559 *
7560 * The ::motion-event signal is emitted each time the mouse pointer is
7561 * moved over @actor.
7562 *
7563 * Return value: %TRUE if the event has been handled by the actor,
7564 * or %FALSE to continue the emission.
7565 *
7566 * Since: 0.6
7567 */
7568 actor_signals[MOTION_EVENT] =
7569 g_signal_new (I_("motion-event"),
7570 G_TYPE_FROM_CLASS (object_class),
7571 G_SIGNAL_RUN_LAST,
7572 G_STRUCT_OFFSET (ClutterActorClass, motion_event),
7573 _clutter_boolean_handled_accumulator, NULL,
7574 _clutter_marshal_BOOLEAN__BOXED,
7575 G_TYPE_BOOLEAN, 1,
7576 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7577 g_signal_set_va_marshaller (actor_signals[MOTION_EVENT],
7578 G_TYPE_FROM_CLASS (object_class),
7579 _clutter_marshal_BOOLEAN__BOXEDv);
7580
7581 /**
7582 * ClutterActor::key-focus-in:
7583 * @actor: the actor which now has key focus
7584 *
7585 * The ::key-focus-in signal is emitted when @actor receives key focus.
7586 *
7587 * Since: 0.6
7588 */
7589 actor_signals[KEY_FOCUS_IN] =
7590 g_signal_new (I_("key-focus-in"),
7591 G_TYPE_FROM_CLASS (object_class),
7592 G_SIGNAL_RUN_LAST,
7593 G_STRUCT_OFFSET (ClutterActorClass, key_focus_in),
7594 NULL, NULL, NULL,
7595 G_TYPE_NONE, 0);
7596
7597 /**
7598 * ClutterActor::key-focus-out:
7599 * @actor: the actor which now has key focus
7600 *
7601 * The ::key-focus-out signal is emitted when @actor loses key focus.
7602 *
7603 * Since: 0.6
7604 */
7605 actor_signals[KEY_FOCUS_OUT] =
7606 g_signal_new (I_("key-focus-out"),
7607 G_TYPE_FROM_CLASS (object_class),
7608 G_SIGNAL_RUN_LAST,
7609 G_STRUCT_OFFSET (ClutterActorClass, key_focus_out),
7610 NULL, NULL, NULL,
7611 G_TYPE_NONE, 0);
7612
7613 /**
7614 * ClutterActor::enter-event:
7615 * @actor: the actor which the pointer has entered.
7616 * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
7617 *
7618 * The ::enter-event signal is emitted when the pointer enters the @actor
7619 *
7620 * Return value: %TRUE if the event has been handled by the actor,
7621 * or %FALSE to continue the emission.
7622 *
7623 * Since: 0.6
7624 */
7625 actor_signals[ENTER_EVENT] =
7626 g_signal_new (I_("enter-event"),
7627 G_TYPE_FROM_CLASS (object_class),
7628 G_SIGNAL_RUN_LAST,
7629 G_STRUCT_OFFSET (ClutterActorClass, enter_event),
7630 _clutter_boolean_handled_accumulator, NULL,
7631 _clutter_marshal_BOOLEAN__BOXED,
7632 G_TYPE_BOOLEAN, 1,
7633 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7634 g_signal_set_va_marshaller (actor_signals[ENTER_EVENT],
7635 G_TYPE_FROM_CLASS (object_class),
7636 _clutter_marshal_BOOLEAN__BOXEDv);
7637
7638 /**
7639 * ClutterActor::leave-event:
7640 * @actor: the actor which the pointer has left
7641 * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
7642 *
7643 * The ::leave-event signal is emitted when the pointer leaves the @actor.
7644 *
7645 * Return value: %TRUE if the event has been handled by the actor,
7646 * or %FALSE to continue the emission.
7647 *
7648 * Since: 0.6
7649 */
7650 actor_signals[LEAVE_EVENT] =
7651 g_signal_new (I_("leave-event"),
7652 G_TYPE_FROM_CLASS (object_class),
7653 G_SIGNAL_RUN_LAST,
7654 G_STRUCT_OFFSET (ClutterActorClass, leave_event),
7655 _clutter_boolean_handled_accumulator, NULL,
7656 _clutter_marshal_BOOLEAN__BOXED,
7657 G_TYPE_BOOLEAN, 1,
7658 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7659 g_signal_set_va_marshaller (actor_signals[LEAVE_EVENT],
7660 G_TYPE_FROM_CLASS (object_class),
7661 _clutter_marshal_BOOLEAN__BOXEDv);
7662
7663 /**
7664 * ClutterActor::captured-event:
7665 * @actor: the actor which received the signal
7666 * @event: a #ClutterEvent
7667 *
7668 * The ::captured-event signal is emitted when an event is captured
7669 * by Clutter. This signal will be emitted starting from the top-level
7670 * container (the #ClutterStage) to the actor which received the event
7671 * going down the hierarchy. This signal can be used to intercept every
7672 * event before the specialized events (like
7673 * ClutterActor::button-press-event or ::key-released-event) are
7674 * emitted.
7675 *
7676 * Return value: %TRUE if the event has been handled by the actor,
7677 * or %FALSE to continue the emission.
7678 *
7679 * Since: 0.6
7680 */
7681 actor_signals[CAPTURED_EVENT] =
7682 g_signal_new (I_("captured-event"),
7683 G_TYPE_FROM_CLASS (object_class),
7684 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
7685 G_STRUCT_OFFSET (ClutterActorClass, captured_event),
7686 _clutter_boolean_handled_accumulator, NULL,
7687 _clutter_marshal_BOOLEAN__BOXED,
7688 G_TYPE_BOOLEAN, 1,
7689 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7690 g_signal_set_va_marshaller (actor_signals[CAPTURED_EVENT],
7691 G_TYPE_FROM_CLASS (object_class),
7692 _clutter_marshal_BOOLEAN__BOXEDv);
7693
7694 /**
7695 * ClutterActor::realize:
7696 * @actor: the #ClutterActor that received the signal
7697 *
7698 * The ::realize signal is emitted each time an actor is being
7699 * realized.
7700 *
7701 * Since: 0.8
7702 *
7703 * Deprecated: 1.16: The signal should not be used in newly
7704 * written code
7705 */
7706 actor_signals[REALIZE] =
7707 g_signal_new (I_("realize"),
7708 G_TYPE_FROM_CLASS (object_class),
7709 G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
7710 G_STRUCT_OFFSET (ClutterActorClass, realize),
7711 NULL, NULL, NULL,
7712 G_TYPE_NONE, 0);
7713 /**
7714 * ClutterActor::unrealize:
7715 * @actor: the #ClutterActor that received the signal
7716 *
7717 * The ::unrealize signal is emitted each time an actor is being
7718 * unrealized.
7719 *
7720 * Since: 0.8
7721 *
7722 * Deprecated: 1.16: The signal should not be used in newly
7723 * written code
7724 */
7725 actor_signals[UNREALIZE] =
7726 g_signal_new (I_("unrealize"),
7727 G_TYPE_FROM_CLASS (object_class),
7728 G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
7729 G_STRUCT_OFFSET (ClutterActorClass, unrealize),
7730 NULL, NULL, NULL,
7731 G_TYPE_NONE, 0);
7732
7733 /**
7734 * ClutterActor::pick:
7735 * @actor: the #ClutterActor that received the signal
7736 * @pick_context: a #ClutterPickContext
7737 *
7738 * The ::pick signal is emitted each time an actor is being painted
7739 * in "pick mode". The pick mode is used to identify the actor during
7740 * the event handling phase, or by clutter_stage_get_actor_at_pos().
7741 *
7742 * Subclasses of #ClutterActor should override the class signal handler
7743 * and paint themselves in that function.
7744 *
7745 * It is possible to connect a handler to the ::pick signal in order
7746 * to set up some custom aspect of a paint in pick mode.
7747 *
7748 * Since: 1.0
7749 * Deprecated: 1.12: Override the #ClutterActorClass.pick virtual function
7750 * instead.
7751 */
7752 actor_signals[PICK] =
7753 g_signal_new (I_("pick"),
7754 G_TYPE_FROM_CLASS (object_class),
7755 G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
7756 G_STRUCT_OFFSET (ClutterActorClass, pick),
7757 NULL, NULL, NULL,
7758 G_TYPE_NONE, 1,
7759 CLUTTER_TYPE_PICK_CONTEXT);
7760
7761 /**
7762 * ClutterActor::transitions-completed:
7763 * @actor: a #ClutterActor
7764 *
7765 * The ::transitions-completed signal is emitted once all transitions
7766 * involving @actor are complete.
7767 *
7768 * Since: 1.10
7769 */
7770 actor_signals[TRANSITIONS_COMPLETED] =
7771 g_signal_new (I_("transitions-completed"),
7772 G_TYPE_FROM_CLASS (object_class),
7773 G_SIGNAL_RUN_LAST,
7774 0,
7775 NULL, NULL, NULL,
7776 G_TYPE_NONE, 0);
7777
7778 /**
7779 * ClutterActor::transition-stopped:
7780 * @actor: a #ClutterActor
7781 * @name: the name of the transition
7782 * @is_finished: whether the transition was finished, or stopped
7783 *
7784 * The ::transition-stopped signal is emitted once a transition
7785 * is stopped; a transition is stopped once it reached its total
7786 * duration (including eventual repeats), it has been stopped
7787 * using clutter_timeline_stop(), or it has been removed from the
7788 * transitions applied on @actor, using clutter_actor_remove_transition().
7789 *
7790 * Since: 1.12
7791 */
7792 actor_signals[TRANSITION_STOPPED] =
7793 g_signal_new (I_("transition-stopped"),
7794 G_TYPE_FROM_CLASS (object_class),
7795 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
7796 G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED,
7797 0,
7798 NULL, NULL,
7799 _clutter_marshal_VOID__STRING_BOOLEAN,
7800 G_TYPE_NONE, 2,
7801 G_TYPE_STRING,
7802 G_TYPE_BOOLEAN);
7803 g_signal_set_va_marshaller (actor_signals[TRANSITION_STOPPED],
7804 G_TYPE_FROM_CLASS (object_class),
7805 _clutter_marshal_VOID__STRING_BOOLEANv);
7806
7807 /**
7808 * ClutterActor::touch-event:
7809 * @actor: a #ClutterActor
7810 * @event: a #ClutterEvent
7811 *
7812 * The ::touch-event signal is emitted each time a touch
7813 * begin/end/update/cancel event.
7814 *
7815 * Return value: %CLUTTER_EVENT_STOP if the event has been handled by
7816 * the actor, or %CLUTTER_EVENT_PROPAGATE to continue the emission.
7817 *
7818 * Since: 1.12
7819 */
7820 actor_signals[TOUCH_EVENT] =
7821 g_signal_new (I_("touch-event"),
7822 G_TYPE_FROM_CLASS (object_class),
7823 G_SIGNAL_RUN_LAST,
7824 G_STRUCT_OFFSET (ClutterActorClass, touch_event),
7825 _clutter_boolean_handled_accumulator, NULL,
7826 _clutter_marshal_BOOLEAN__BOXED,
7827 G_TYPE_BOOLEAN, 1,
7828 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7829 g_signal_set_va_marshaller (actor_signals[TOUCH_EVENT],
7830 G_TYPE_FROM_CLASS (object_class),
7831 _clutter_marshal_BOOLEAN__BOXEDv);
7832
7833 /**
7834 * ClutterActor::stage-views-changed:
7835 * @actor: a #ClutterActor
7836 *
7837 * The ::stage-views-changed signal is emitted when the position or
7838 * size an actor is being painted at have changed so that it's visible
7839 * on different stage views.
7840 *
7841 * This signal is also emitted when the actor gets detached from the stage
7842 * or when the views of the stage have been invalidated and will be
7843 * replaced; it's not emitted when the actor gets hidden.
7844 */
7845 actor_signals[STAGE_VIEWS_CHANGED] =
7846 g_signal_new (I_("stage-views-changed"),
7847 G_TYPE_FROM_CLASS (object_class),
7848 G_SIGNAL_RUN_LAST,
7849 0,
7850 NULL, NULL, NULL,
7851 G_TYPE_NONE, 0);
7852
7853 /**
7854 * ClutterActor::resource-scale-changed:
7855 * @actor: a #ClutterActor
7856 *
7857 * The ::resource-scale-changed signal is emitted when the resource scale
7858 * value returned by clutter_actor_get_resource_scale() changes.
7859 *
7860 * This signal can be used to get notified about the correct resource scale
7861 * when the scale had to be queried outside of the paint cycle.
7862 */
7863 actor_signals[RESOURCE_SCALE_CHANGED] =
7864 g_signal_new (I_("resource-scale-changed"),
7865 G_TYPE_FROM_CLASS (object_class),
7866 G_SIGNAL_RUN_LAST,
7867 G_STRUCT_OFFSET (ClutterActorClass, resource_scale_changed),
7868 NULL, NULL, NULL,
7869 G_TYPE_NONE, 0);
7870 }
7871
7872 static void
clutter_actor_init(ClutterActor * self)7873 clutter_actor_init (ClutterActor *self)
7874 {
7875 ClutterActorPrivate *priv;
7876
7877 self->priv = priv = clutter_actor_get_instance_private (self);
7878
7879 priv->allocation = (ClutterActorBox) CLUTTER_ACTOR_BOX_UNINITIALIZED;
7880
7881 priv->opacity = 0xff;
7882 priv->show_on_set_parent = TRUE;
7883 priv->resource_scale = -1.0f;
7884
7885 priv->needs_width_request = TRUE;
7886 priv->needs_height_request = TRUE;
7887 priv->needs_allocation = TRUE;
7888 priv->needs_paint_volume_update = TRUE;
7889 priv->needs_update_stage_views = TRUE;
7890
7891 priv->cached_width_age = 1;
7892 priv->cached_height_age = 1;
7893
7894 priv->opacity_override = -1;
7895 priv->enable_model_view_transform = TRUE;
7896
7897 /* Initialize an empty paint volume to start with */
7898 _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
7899 priv->last_paint_volume_valid = TRUE;
7900
7901 priv->transform_valid = FALSE;
7902
7903 /* the default is to stretch the content, to match the
7904 * current behaviour of basically all actors. also, it's
7905 * the easiest thing to compute.
7906 */
7907 priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL;
7908 priv->min_filter = CLUTTER_SCALING_FILTER_LINEAR;
7909 priv->mag_filter = CLUTTER_SCALING_FILTER_LINEAR;
7910
7911 /* this flag will be set to TRUE if the actor gets a child
7912 * or if the [xy]-expand flags are explicitly set; until
7913 * then, the actor does not need to expand.
7914 *
7915 * this also allows us to avoid computing the expand flag
7916 * when building up a scene.
7917 */
7918 priv->needs_compute_expand = FALSE;
7919
7920 /* we start with an easing state with duration forcibly set
7921 * to 0, for backward compatibility.
7922 */
7923 clutter_actor_save_easing_state (self);
7924 clutter_actor_set_easing_duration (self, 0);
7925 }
7926
7927 /**
7928 * clutter_actor_new:
7929 *
7930 * Creates a new #ClutterActor.
7931 *
7932 * A newly created actor has a floating reference, which will be sunk
7933 * when it is added to another actor.
7934 *
7935 * Return value: the newly created #ClutterActor
7936 *
7937 * Since: 1.10
7938 */
7939 ClutterActor *
clutter_actor_new(void)7940 clutter_actor_new (void)
7941 {
7942 return g_object_new (CLUTTER_TYPE_ACTOR, NULL);
7943 }
7944
7945 /**
7946 * clutter_actor_destroy:
7947 * @self: a #ClutterActor
7948 *
7949 * Destroys an actor. When an actor is destroyed, it will break any
7950 * references it holds to other objects. If the actor is inside a
7951 * container, the actor will be removed.
7952 *
7953 * When you destroy a container, its children will be destroyed as well.
7954 */
7955 void
clutter_actor_destroy(ClutterActor * self)7956 clutter_actor_destroy (ClutterActor *self)
7957 {
7958 g_return_if_fail (CLUTTER_IS_ACTOR (self));
7959
7960 g_object_ref (self);
7961
7962 /* avoid recursion while destroying */
7963 if (!CLUTTER_ACTOR_IN_DESTRUCTION (self))
7964 {
7965 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
7966
7967 g_object_run_dispose (G_OBJECT (self));
7968
7969 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
7970 }
7971
7972 g_object_unref (self);
7973 }
7974
7975 void
_clutter_actor_queue_redraw_full(ClutterActor * self,const ClutterPaintVolume * volume,ClutterEffect * effect)7976 _clutter_actor_queue_redraw_full (ClutterActor *self,
7977 const ClutterPaintVolume *volume,
7978 ClutterEffect *effect)
7979 {
7980 ClutterActorPrivate *priv = self->priv;
7981 ClutterActor *stage;
7982
7983 /* Here's an outline of the actor queue redraw mechanism:
7984 *
7985 * The process starts in clutter_actor_queue_redraw() which is a
7986 * wrapper for this function. Additionally, an effect can queue a
7987 * redraw by wrapping this function in clutter_effect_queue_repaint().
7988 *
7989 * This functions queues an entry in a list associated with the
7990 * stage which is a list of actors that queued a redraw while
7991 * updating the timelines, performing layouting and processing other
7992 * mainloop sources before the next paint starts.
7993 *
7994 * When all updates are complete and we come to paint the stage then
7995 * we iterate this list and build the redraw clip of the stage by
7996 * either using the clip that was supplied to
7997 * _clutter_actor_queue_redraw_full() or by asking the actor for its
7998 * redraw clip using clutter_actor_get_redraw_clip().
7999 *
8000 * Doing this later during the stage update instead of now is an
8001 * important optimization, because later it's more likely we will be
8002 * able to determine the paint volume of an actor (its allocation
8003 * should be up to date).
8004 */
8005
8006 /* ignore queueing a redraw for actors being destroyed */
8007 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8008 return;
8009
8010 /* we can ignore unmapped actors, unless they are inside a cloned branch
8011 * of the scene graph, as unmapped actors will simply be left unpainted.
8012 *
8013 * this allows us to ignore redraws queued on leaf nodes when one
8014 * of their parents has been hidden
8015 */
8016 if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
8017 !clutter_actor_has_mapped_clones (self))
8018 {
8019 CLUTTER_NOTE (PAINT,
8020 "Skipping queue_redraw('%s'): mapped=%s, "
8021 "has_mapped_clones=%s",
8022 _clutter_actor_get_debug_name (self),
8023 CLUTTER_ACTOR_IS_MAPPED (self) ? "yes" : "no",
8024 clutter_actor_has_mapped_clones (self) ? "yes" : "no");
8025 return;
8026 }
8027
8028 /* given the check above we could end up queueing a redraw on an
8029 * unmapped actor with mapped clones, so we cannot assume that
8030 * get_stage() will return a Stage
8031 */
8032 stage = _clutter_actor_get_stage_internal (self);
8033 if (stage == NULL)
8034 return;
8035
8036 /* ignore queueing a redraw on stages that are being destroyed */
8037 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
8038 return;
8039
8040 clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
8041 self,
8042 volume);
8043
8044 /* If this is the first redraw queued then we can directly use the
8045 effect parameter */
8046 if (!priv->is_dirty)
8047 priv->effect_to_redraw = effect;
8048 /* Otherwise we need to merge it with the existing effect parameter */
8049 else if (effect != NULL)
8050 {
8051 /* If there's already an effect then we need to use whichever is
8052 later in the chain of actors. Otherwise a full redraw has
8053 already been queued on the actor so we need to ignore the
8054 effect parameter */
8055 if (priv->effect_to_redraw != NULL)
8056 {
8057 if (priv->effects == NULL)
8058 g_warning ("Redraw queued with an effect that is "
8059 "not applied to the actor");
8060 else
8061 {
8062 const GList *l;
8063
8064 for (l = _clutter_meta_group_peek_metas (priv->effects);
8065 l != NULL;
8066 l = l->next)
8067 {
8068 if (l->data == priv->effect_to_redraw ||
8069 l->data == effect)
8070 priv->effect_to_redraw = l->data;
8071 }
8072 }
8073 }
8074 }
8075 else
8076 {
8077 /* If no effect is specified then we need to redraw the whole
8078 actor */
8079 priv->effect_to_redraw = NULL;
8080 }
8081
8082 priv->is_dirty = TRUE;
8083
8084 if (!priv->propagated_one_redraw)
8085 _clutter_actor_propagate_queue_redraw (self, self);
8086 }
8087
8088 /**
8089 * clutter_actor_queue_redraw:
8090 * @self: A #ClutterActor
8091 *
8092 * Queues up a redraw of an actor and any children. The redraw occurs
8093 * once the main loop becomes idle (after the current batch of events
8094 * has been processed, roughly).
8095 *
8096 * Applications rarely need to call this, as redraws are handled
8097 * automatically by modification functions.
8098 *
8099 * This function will not do anything if @self is not visible, or
8100 * if the actor is inside an invisible part of the scenegraph.
8101 *
8102 * Also be aware that painting is a NOP for actors with an opacity of
8103 * 0
8104 *
8105 * When you are implementing a custom actor you must queue a redraw
8106 * whenever some private state changes that will affect painting or
8107 * picking of your actor.
8108 */
8109 void
clutter_actor_queue_redraw(ClutterActor * self)8110 clutter_actor_queue_redraw (ClutterActor *self)
8111 {
8112 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8113
8114 _clutter_actor_queue_redraw_full (self,
8115 NULL, /* clip volume */
8116 NULL /* effect */);
8117 }
8118
8119 static void
_clutter_actor_queue_relayout_on_clones(ClutterActor * self)8120 _clutter_actor_queue_relayout_on_clones (ClutterActor *self)
8121 {
8122 ClutterActorPrivate *priv = self->priv;
8123 GHashTableIter iter;
8124 gpointer key;
8125
8126 if (priv->clones == NULL)
8127 return;
8128
8129 g_hash_table_iter_init (&iter, priv->clones);
8130 while (g_hash_table_iter_next (&iter, &key, NULL))
8131 clutter_actor_queue_relayout (key);
8132 }
8133
8134 void
_clutter_actor_queue_only_relayout(ClutterActor * self)8135 _clutter_actor_queue_only_relayout (ClutterActor *self)
8136 {
8137 ClutterActorPrivate *priv = self->priv;
8138
8139 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8140 return;
8141
8142 if (priv->needs_width_request &&
8143 priv->needs_height_request &&
8144 priv->needs_allocation)
8145 return; /* save some cpu cycles */
8146
8147 #ifdef CLUTTER_ENABLE_DEBUG
8148 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) && CLUTTER_ACTOR_IN_RELAYOUT (self))
8149 {
8150 g_warning ("The actor '%s' is currently inside an allocation "
8151 "cycle; calling clutter_actor_queue_relayout() is "
8152 "not recommended",
8153 _clutter_actor_get_debug_name (self));
8154 }
8155 #endif /* CLUTTER_ENABLE_DEBUG */
8156
8157 _clutter_actor_queue_relayout_on_clones (self);
8158
8159 g_signal_emit (self, actor_signals[QUEUE_RELAYOUT], 0);
8160 }
8161
8162 /**
8163 * clutter_actor_queue_redraw_with_clip:
8164 * @self: a #ClutterActor
8165 * @clip: (allow-none): a rectangular clip region, or %NULL
8166 *
8167 * Queues a redraw on @self limited to a specific, actor-relative
8168 * rectangular area.
8169 *
8170 * If @clip is %NULL this function is equivalent to
8171 * clutter_actor_queue_redraw().
8172 *
8173 * Since: 1.10
8174 */
8175 void
clutter_actor_queue_redraw_with_clip(ClutterActor * self,const cairo_rectangle_int_t * clip)8176 clutter_actor_queue_redraw_with_clip (ClutterActor *self,
8177 const cairo_rectangle_int_t *clip)
8178 {
8179 ClutterPaintVolume volume;
8180 graphene_point3d_t origin;
8181
8182 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8183
8184 if (clip == NULL)
8185 {
8186 clutter_actor_queue_redraw (self);
8187 return;
8188 }
8189
8190 _clutter_paint_volume_init_static (&volume, self);
8191
8192 origin.x = clip->x;
8193 origin.y = clip->y;
8194 origin.z = 0.0f;
8195
8196 clutter_paint_volume_set_origin (&volume, &origin);
8197 clutter_paint_volume_set_width (&volume, clip->width);
8198 clutter_paint_volume_set_height (&volume, clip->height);
8199
8200 _clutter_actor_queue_redraw_full (self, &volume, NULL);
8201
8202 clutter_paint_volume_free (&volume);
8203 }
8204
8205 /**
8206 * clutter_actor_queue_relayout:
8207 * @self: A #ClutterActor
8208 *
8209 * Indicates that the actor's size request or other layout-affecting
8210 * properties may have changed. This function is used inside #ClutterActor
8211 * subclass implementations, not by applications directly.
8212 *
8213 * Queueing a new layout automatically queues a redraw as well.
8214 *
8215 * Since: 0.8
8216 */
8217 void
clutter_actor_queue_relayout(ClutterActor * self)8218 clutter_actor_queue_relayout (ClutterActor *self)
8219 {
8220 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8221
8222 _clutter_actor_queue_only_relayout (self);
8223 clutter_actor_queue_redraw (self);
8224 }
8225
8226 /**
8227 * clutter_actor_get_preferred_size:
8228 * @self: a #ClutterActor
8229 * @min_width_p: (out) (allow-none): return location for the minimum
8230 * width, or %NULL
8231 * @min_height_p: (out) (allow-none): return location for the minimum
8232 * height, or %NULL
8233 * @natural_width_p: (out) (allow-none): return location for the natural
8234 * width, or %NULL
8235 * @natural_height_p: (out) (allow-none): return location for the natural
8236 * height, or %NULL
8237 *
8238 * Computes the preferred minimum and natural size of an actor, taking into
8239 * account the actor's geometry management (either height-for-width
8240 * or width-for-height).
8241 *
8242 * The width and height used to compute the preferred height and preferred
8243 * width are the actor's natural ones.
8244 *
8245 * If you need to control the height for the preferred width, or the width for
8246 * the preferred height, you should use clutter_actor_get_preferred_width()
8247 * and clutter_actor_get_preferred_height(), and check the actor's preferred
8248 * geometry management using the #ClutterActor:request-mode property.
8249 *
8250 * Since: 0.8
8251 */
8252 void
clutter_actor_get_preferred_size(ClutterActor * self,gfloat * min_width_p,gfloat * min_height_p,gfloat * natural_width_p,gfloat * natural_height_p)8253 clutter_actor_get_preferred_size (ClutterActor *self,
8254 gfloat *min_width_p,
8255 gfloat *min_height_p,
8256 gfloat *natural_width_p,
8257 gfloat *natural_height_p)
8258 {
8259 ClutterActorPrivate *priv;
8260 gfloat min_width, min_height;
8261 gfloat natural_width, natural_height;
8262
8263 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8264
8265 priv = self->priv;
8266
8267 min_width = min_height = 0;
8268 natural_width = natural_height = 0;
8269
8270 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
8271 {
8272 CLUTTER_NOTE (LAYOUT, "Preferred size (height-for-width)");
8273 clutter_actor_get_preferred_width (self, -1,
8274 &min_width,
8275 &natural_width);
8276 clutter_actor_get_preferred_height (self, natural_width,
8277 &min_height,
8278 &natural_height);
8279 }
8280 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
8281 {
8282 CLUTTER_NOTE (LAYOUT, "Preferred size (width-for-height)");
8283 clutter_actor_get_preferred_height (self, -1,
8284 &min_height,
8285 &natural_height);
8286 clutter_actor_get_preferred_width (self, natural_height,
8287 &min_width,
8288 &natural_width);
8289 }
8290 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
8291 {
8292 CLUTTER_NOTE (LAYOUT, "Preferred size (content-size)");
8293
8294 if (priv->content != NULL)
8295 clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
8296 }
8297 else
8298 {
8299 CLUTTER_NOTE (LAYOUT, "Unknown request mode");
8300 }
8301
8302 if (min_width_p)
8303 *min_width_p = min_width;
8304
8305 if (min_height_p)
8306 *min_height_p = min_height;
8307
8308 if (natural_width_p)
8309 *natural_width_p = natural_width;
8310
8311 if (natural_height_p)
8312 *natural_height_p = natural_height;
8313 }
8314
8315 /*< private >
8316 * effective_align:
8317 * @align: a #ClutterActorAlign
8318 * @direction: a #ClutterTextDirection
8319 *
8320 * Retrieves the correct alignment depending on the text direction
8321 *
8322 * Return value: the effective alignment
8323 */
8324 static ClutterActorAlign
effective_align(ClutterActorAlign align,ClutterTextDirection direction)8325 effective_align (ClutterActorAlign align,
8326 ClutterTextDirection direction)
8327 {
8328 ClutterActorAlign res;
8329
8330 switch (align)
8331 {
8332 case CLUTTER_ACTOR_ALIGN_START:
8333 res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
8334 ? CLUTTER_ACTOR_ALIGN_END
8335 : CLUTTER_ACTOR_ALIGN_START;
8336 break;
8337
8338 case CLUTTER_ACTOR_ALIGN_END:
8339 res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
8340 ? CLUTTER_ACTOR_ALIGN_START
8341 : CLUTTER_ACTOR_ALIGN_END;
8342 break;
8343
8344 default:
8345 res = align;
8346 break;
8347 }
8348
8349 return res;
8350 }
8351
8352 /*< private >
8353 * _clutter_actor_get_effective_x_align:
8354 * @self: a #ClutterActor
8355 *
8356 * Retrieves the effective horizontal alignment, taking into
8357 * consideration the text direction of @self.
8358 *
8359 * Return value: the effective horizontal alignment
8360 */
8361 ClutterActorAlign
_clutter_actor_get_effective_x_align(ClutterActor * self)8362 _clutter_actor_get_effective_x_align (ClutterActor *self)
8363 {
8364 return effective_align (clutter_actor_get_x_align (self),
8365 clutter_actor_get_text_direction (self));
8366 }
8367
8368 static inline void
adjust_for_margin(float margin_start,float margin_end,float * minimum_size,float * natural_size,float * allocated_start,float * allocated_end)8369 adjust_for_margin (float margin_start,
8370 float margin_end,
8371 float *minimum_size,
8372 float *natural_size,
8373 float *allocated_start,
8374 float *allocated_end)
8375 {
8376 float min_size = *minimum_size;
8377 float nat_size = *natural_size;
8378 float start = *allocated_start;
8379 float end = *allocated_end;
8380
8381 min_size = MAX (min_size - (margin_start + margin_end), 0);
8382 nat_size = MAX (nat_size - (margin_start + margin_end), 0);
8383
8384 *minimum_size = min_size;
8385 *natural_size = nat_size;
8386
8387 start += margin_start;
8388 end -= margin_end;
8389
8390 if (end - start >= 0)
8391 {
8392 *allocated_start = start;
8393 *allocated_end = end;
8394 }
8395 }
8396
8397 static inline void
adjust_for_alignment(ClutterActorAlign alignment,float natural_size,float * allocated_start,float * allocated_end)8398 adjust_for_alignment (ClutterActorAlign alignment,
8399 float natural_size,
8400 float *allocated_start,
8401 float *allocated_end)
8402 {
8403 float allocated_size = *allocated_end - *allocated_start;
8404
8405 if (allocated_size <= 0.f)
8406 return;
8407
8408 switch (alignment)
8409 {
8410 case CLUTTER_ACTOR_ALIGN_FILL:
8411 /* do nothing */
8412 break;
8413
8414 case CLUTTER_ACTOR_ALIGN_START:
8415 /* keep start */
8416 *allocated_end = *allocated_start + MIN (natural_size, allocated_size);
8417 break;
8418
8419 case CLUTTER_ACTOR_ALIGN_END:
8420 if (allocated_size > natural_size)
8421 {
8422 *allocated_start += (allocated_size - natural_size);
8423 *allocated_end = *allocated_start + natural_size;
8424 }
8425 break;
8426
8427 case CLUTTER_ACTOR_ALIGN_CENTER:
8428 if (allocated_size > natural_size)
8429 {
8430 *allocated_start += floorf ((allocated_size - natural_size) / 2);
8431 *allocated_end = *allocated_start + MIN (allocated_size, natural_size);
8432 }
8433 break;
8434 }
8435 }
8436
8437 /*< private >
8438 * clutter_actor_adjust_width:
8439 * @self: a #ClutterActor
8440 * @minimum_width: (inout): the actor's preferred minimum width, which
8441 * will be adjusted depending on the margin
8442 * @natural_width: (inout): the actor's preferred natural width, which
8443 * will be adjusted depending on the margin
8444 * @adjusted_x1: (out): the adjusted x1 for the actor's bounding box
8445 * @adjusted_x2: (out): the adjusted x2 for the actor's bounding box
8446 *
8447 * Adjusts the preferred and allocated position and size of an actor,
8448 * depending on the margin and alignment properties.
8449 */
8450 static void
clutter_actor_adjust_width(ClutterActor * self,gfloat * minimum_width,gfloat * natural_width,gfloat * adjusted_x1,gfloat * adjusted_x2)8451 clutter_actor_adjust_width (ClutterActor *self,
8452 gfloat *minimum_width,
8453 gfloat *natural_width,
8454 gfloat *adjusted_x1,
8455 gfloat *adjusted_x2)
8456 {
8457 ClutterTextDirection text_dir;
8458 const ClutterLayoutInfo *info;
8459
8460 info = _clutter_actor_get_layout_info_or_defaults (self);
8461 text_dir = clutter_actor_get_text_direction (self);
8462
8463 CLUTTER_NOTE (LAYOUT, "Adjusting allocated X and width");
8464
8465 /* this will tweak natural_width to remove the margin, so that
8466 * adjust_for_alignment() will use the correct size
8467 */
8468 adjust_for_margin (info->margin.left, info->margin.right,
8469 minimum_width, natural_width,
8470 adjusted_x1, adjusted_x2);
8471
8472 adjust_for_alignment (effective_align (info->x_align, text_dir),
8473 *natural_width,
8474 adjusted_x1, adjusted_x2);
8475 }
8476
8477 /*< private >
8478 * clutter_actor_adjust_height:
8479 * @self: a #ClutterActor
8480 * @minimum_height: (inout): the actor's preferred minimum height, which
8481 * will be adjusted depending on the margin
8482 * @natural_height: (inout): the actor's preferred natural height, which
8483 * will be adjusted depending on the margin
8484 * @adjusted_y1: (out): the adjusted y1 for the actor's bounding box
8485 * @adjusted_y2: (out): the adjusted y2 for the actor's bounding box
8486 *
8487 * Adjusts the preferred and allocated position and size of an actor,
8488 * depending on the margin and alignment properties.
8489 */
8490 static void
clutter_actor_adjust_height(ClutterActor * self,gfloat * minimum_height,gfloat * natural_height,gfloat * adjusted_y1,gfloat * adjusted_y2)8491 clutter_actor_adjust_height (ClutterActor *self,
8492 gfloat *minimum_height,
8493 gfloat *natural_height,
8494 gfloat *adjusted_y1,
8495 gfloat *adjusted_y2)
8496 {
8497 const ClutterLayoutInfo *info;
8498
8499 info = _clutter_actor_get_layout_info_or_defaults (self);
8500
8501 CLUTTER_NOTE (LAYOUT, "Adjusting allocated Y and height");
8502
8503 /* this will tweak natural_height to remove the margin, so that
8504 * adjust_for_alignment() will use the correct size
8505 */
8506 adjust_for_margin (info->margin.top, info->margin.bottom,
8507 minimum_height, natural_height,
8508 adjusted_y1,
8509 adjusted_y2);
8510
8511 /* we don't use effective_align() here, because text direction
8512 * only affects the horizontal axis
8513 */
8514 adjust_for_alignment (info->y_align,
8515 *natural_height,
8516 adjusted_y1,
8517 adjusted_y2);
8518
8519 }
8520
8521 /* looks for a cached size request for this for_size. If not
8522 * found, returns the oldest entry so it can be overwritten */
8523 static gboolean
_clutter_actor_get_cached_size_request(gfloat for_size,SizeRequest * cached_size_requests,SizeRequest ** result)8524 _clutter_actor_get_cached_size_request (gfloat for_size,
8525 SizeRequest *cached_size_requests,
8526 SizeRequest **result)
8527 {
8528 guint i;
8529
8530 *result = &cached_size_requests[0];
8531
8532 for (i = 0; i < N_CACHED_SIZE_REQUESTS; i++)
8533 {
8534 SizeRequest *sr;
8535
8536 sr = &cached_size_requests[i];
8537
8538 if (sr->age > 0 &&
8539 sr->for_size == for_size)
8540 {
8541 CLUTTER_NOTE (LAYOUT, "Size cache hit for size: %.2f", for_size);
8542 *result = sr;
8543 return TRUE;
8544 }
8545 else if (sr->age < (*result)->age)
8546 {
8547 *result = sr;
8548 }
8549 }
8550
8551 CLUTTER_NOTE (LAYOUT, "Size cache miss for size: %.2f", for_size);
8552
8553 return FALSE;
8554 }
8555
8556 static void
clutter_actor_update_preferred_size_for_constraints(ClutterActor * self,ClutterOrientation direction,float for_size,float * minimum_size,float * natural_size)8557 clutter_actor_update_preferred_size_for_constraints (ClutterActor *self,
8558 ClutterOrientation direction,
8559 float for_size,
8560 float *minimum_size,
8561 float *natural_size)
8562 {
8563 ClutterActorPrivate *priv = self->priv;
8564 const GList *constraints, *l;
8565
8566 if (priv->constraints == NULL)
8567 return;
8568
8569 constraints = _clutter_meta_group_peek_metas (priv->constraints);
8570 for (l = constraints; l != NULL; l = l->next)
8571 {
8572 ClutterConstraint *constraint = l->data;
8573 ClutterActorMeta *meta = l->data;
8574
8575 if (!clutter_actor_meta_get_enabled (meta))
8576 continue;
8577
8578 clutter_constraint_update_preferred_size (constraint, self,
8579 direction,
8580 for_size,
8581 minimum_size,
8582 natural_size);
8583
8584 CLUTTER_NOTE (LAYOUT,
8585 "Preferred %s of '%s' after constraint '%s': "
8586 "{ min:%.2f, nat:%.2f }",
8587 direction == CLUTTER_ORIENTATION_HORIZONTAL
8588 ? "width"
8589 : "height",
8590 _clutter_actor_get_debug_name (self),
8591 _clutter_actor_meta_get_debug_name (meta),
8592 *minimum_size, *natural_size);
8593 }
8594 }
8595
8596 /**
8597 * clutter_actor_get_preferred_width:
8598 * @self: A #ClutterActor
8599 * @for_height: available height when computing the preferred width,
8600 * or a negative value to indicate that no height is defined
8601 * @min_width_p: (out) (allow-none): return location for minimum width,
8602 * or %NULL
8603 * @natural_width_p: (out) (allow-none): return location for the natural
8604 * width, or %NULL
8605 *
8606 * Computes the requested minimum and natural widths for an actor,
8607 * optionally depending on the specified height, or if they are
8608 * already computed, returns the cached values.
8609 *
8610 * An actor may not get its request - depending on the layout
8611 * manager that's in effect.
8612 *
8613 * A request should not incorporate the actor's scaleor translation;
8614 * those transformations do not affect layout, only rendering.
8615 *
8616 * Since: 0.8
8617 */
8618 void
clutter_actor_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)8619 clutter_actor_get_preferred_width (ClutterActor *self,
8620 gfloat for_height,
8621 gfloat *min_width_p,
8622 gfloat *natural_width_p)
8623 {
8624 float request_min_width, request_natural_width;
8625 SizeRequest *cached_size_request;
8626 const ClutterLayoutInfo *info;
8627 ClutterActorPrivate *priv;
8628 gboolean found_in_cache;
8629
8630 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8631
8632 priv = self->priv;
8633
8634 info = _clutter_actor_get_layout_info_or_defaults (self);
8635
8636 /* we shortcircuit the case of a fixed size set using set_width() */
8637 if (priv->min_width_set && priv->natural_width_set)
8638 {
8639 if (min_width_p != NULL)
8640 *min_width_p = info->minimum.width + (info->margin.left + info->margin.right);
8641
8642 if (natural_width_p != NULL)
8643 *natural_width_p = info->natural.width + (info->margin.left + info->margin.right);
8644
8645 return;
8646 }
8647
8648 /* if the request mode is CONTENT_SIZE we simply return the content width */
8649 if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
8650 {
8651 float content_width = 0.f;
8652
8653 if (priv->content != NULL)
8654 clutter_content_get_preferred_size (priv->content, &content_width, NULL);
8655
8656 if (min_width_p != NULL)
8657 *min_width_p = content_width;
8658
8659 if (natural_width_p != NULL)
8660 *natural_width_p = content_width;
8661
8662 return;
8663 }
8664
8665 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH);
8666
8667 /* the remaining cases are:
8668 *
8669 * - either min_width or natural_width have been set
8670 * - neither min_width or natural_width have been set
8671 *
8672 * in both cases, we go through the cache (and through the actor in case
8673 * of cache misses) and determine the authoritative value depending on
8674 * the *_set flags.
8675 */
8676
8677 if (!priv->needs_width_request)
8678 {
8679 found_in_cache =
8680 _clutter_actor_get_cached_size_request (for_height,
8681 priv->width_requests,
8682 &cached_size_request);
8683 }
8684 else
8685 {
8686 /* if the actor needs a width request we use the first slot */
8687 found_in_cache = FALSE;
8688 cached_size_request = &priv->width_requests[0];
8689 }
8690
8691 if (!found_in_cache)
8692 {
8693 gfloat minimum_width, natural_width;
8694 ClutterActorClass *klass;
8695
8696 minimum_width = natural_width = 0;
8697
8698 /* adjust for the margin */
8699 if (for_height >= 0)
8700 {
8701 for_height -= (info->margin.top + info->margin.bottom);
8702 if (for_height < 0)
8703 for_height = 0;
8704 }
8705
8706 CLUTTER_NOTE (LAYOUT, "Width request for %.2f px", for_height);
8707
8708 klass = CLUTTER_ACTOR_GET_CLASS (self);
8709 klass->get_preferred_width (self, for_height,
8710 &minimum_width,
8711 &natural_width);
8712
8713 /* adjust for constraints */
8714 clutter_actor_update_preferred_size_for_constraints (self,
8715 CLUTTER_ORIENTATION_HORIZONTAL,
8716 for_height,
8717 &minimum_width,
8718 &natural_width);
8719
8720 /* adjust for the margin */
8721 minimum_width += (info->margin.left + info->margin.right);
8722 natural_width += (info->margin.left + info->margin.right);
8723
8724 /* Due to accumulated float errors, it's better not to warn
8725 * on this, but just fix it.
8726 */
8727 if (natural_width < minimum_width)
8728 natural_width = minimum_width;
8729
8730 cached_size_request->min_size = minimum_width;
8731 cached_size_request->natural_size = natural_width;
8732 cached_size_request->for_size = for_height;
8733 cached_size_request->age = priv->cached_width_age;
8734
8735 priv->cached_width_age += 1;
8736 priv->needs_width_request = FALSE;
8737 }
8738
8739 if (!priv->min_width_set)
8740 request_min_width = cached_size_request->min_size;
8741 else
8742 request_min_width = info->margin.left
8743 + info->minimum.width
8744 + info->margin.right;
8745
8746 if (!priv->natural_width_set)
8747 request_natural_width = cached_size_request->natural_size;
8748 else
8749 request_natural_width = info->margin.left
8750 + info->natural.width
8751 + info->margin.right;
8752
8753 if (min_width_p)
8754 *min_width_p = request_min_width;
8755
8756 if (natural_width_p)
8757 *natural_width_p = request_natural_width;
8758
8759 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH);
8760 }
8761
8762 /**
8763 * clutter_actor_get_preferred_height:
8764 * @self: A #ClutterActor
8765 * @for_width: available width to assume in computing desired height,
8766 * or a negative value to indicate that no width is defined
8767 * @min_height_p: (out) (allow-none): return location for minimum height,
8768 * or %NULL
8769 * @natural_height_p: (out) (allow-none): return location for natural
8770 * height, or %NULL
8771 *
8772 * Computes the requested minimum and natural heights for an actor,
8773 * or if they are already computed, returns the cached values.
8774 *
8775 * An actor may not get its request - depending on the layout
8776 * manager that's in effect.
8777 *
8778 * A request should not incorporate the actor's scale or translation;
8779 * those transformations do not affect layout, only rendering.
8780 *
8781 * Since: 0.8
8782 */
8783 void
clutter_actor_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)8784 clutter_actor_get_preferred_height (ClutterActor *self,
8785 gfloat for_width,
8786 gfloat *min_height_p,
8787 gfloat *natural_height_p)
8788 {
8789 float request_min_height, request_natural_height;
8790 SizeRequest *cached_size_request;
8791 const ClutterLayoutInfo *info;
8792 ClutterActorPrivate *priv;
8793 gboolean found_in_cache;
8794
8795 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8796
8797 priv = self->priv;
8798
8799 info = _clutter_actor_get_layout_info_or_defaults (self);
8800
8801 /* we shortcircuit the case of a fixed size set using set_height() */
8802 if (priv->min_height_set && priv->natural_height_set)
8803 {
8804 if (min_height_p != NULL)
8805 *min_height_p = info->minimum.height + (info->margin.top + info->margin.bottom);
8806
8807 if (natural_height_p != NULL)
8808 *natural_height_p = info->natural.height + (info->margin.top + info->margin.bottom);
8809
8810 return;
8811 }
8812
8813 /* if the request mode is CONTENT_SIZE we simply return the content height */
8814 if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
8815 {
8816 float content_height = 0.f;
8817
8818 if (priv->content != NULL)
8819 clutter_content_get_preferred_size (priv->content, NULL, &content_height);
8820
8821 if (min_height_p != NULL)
8822 *min_height_p = content_height;
8823
8824 if (natural_height_p != NULL)
8825 *natural_height_p = content_height;
8826
8827 return;
8828 }
8829
8830 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT);
8831
8832 /* the remaining cases are:
8833 *
8834 * - either min_height or natural_height have been set
8835 * - neither min_height or natural_height have been set
8836 *
8837 * in both cases, we go through the cache (and through the actor in case
8838 * of cache misses) and determine the authoritative value depending on
8839 * the *_set flags.
8840 */
8841
8842 if (!priv->needs_height_request)
8843 {
8844 found_in_cache =
8845 _clutter_actor_get_cached_size_request (for_width,
8846 priv->height_requests,
8847 &cached_size_request);
8848 }
8849 else
8850 {
8851 found_in_cache = FALSE;
8852 cached_size_request = &priv->height_requests[0];
8853 }
8854
8855 if (!found_in_cache)
8856 {
8857 gfloat minimum_height, natural_height;
8858 ClutterActorClass *klass;
8859
8860 minimum_height = natural_height = 0;
8861
8862 CLUTTER_NOTE (LAYOUT, "Height request for %.2f px", for_width);
8863
8864 /* adjust for margin */
8865 if (for_width >= 0)
8866 {
8867 for_width -= (info->margin.left + info->margin.right);
8868 if (for_width < 0)
8869 for_width = 0;
8870 }
8871
8872 klass = CLUTTER_ACTOR_GET_CLASS (self);
8873 klass->get_preferred_height (self, for_width,
8874 &minimum_height,
8875 &natural_height);
8876
8877 /* adjust for constraints */
8878 clutter_actor_update_preferred_size_for_constraints (self,
8879 CLUTTER_ORIENTATION_VERTICAL,
8880 for_width,
8881 &minimum_height,
8882 &natural_height);
8883
8884 /* adjust for margin */
8885 minimum_height += (info->margin.top + info->margin.bottom);
8886 natural_height += (info->margin.top + info->margin.bottom);
8887
8888 /* Due to accumulated float errors, it's better not to warn
8889 * on this, but just fix it.
8890 */
8891 if (natural_height < minimum_height)
8892 natural_height = minimum_height;
8893
8894 cached_size_request->min_size = minimum_height;
8895 cached_size_request->natural_size = natural_height;
8896 cached_size_request->for_size = for_width;
8897 cached_size_request->age = priv->cached_height_age;
8898
8899 priv->cached_height_age += 1;
8900 priv->needs_height_request = FALSE;
8901 }
8902
8903 if (!priv->min_height_set)
8904 request_min_height = cached_size_request->min_size;
8905 else
8906 request_min_height = info->margin.top
8907 + info->minimum.height
8908 + info->margin.bottom;
8909
8910 if (!priv->natural_height_set)
8911 request_natural_height = cached_size_request->natural_size;
8912 else
8913 request_natural_height = info->margin.top
8914 + info->natural.height
8915 + info->margin.bottom;
8916
8917 if (min_height_p)
8918 *min_height_p = request_min_height;
8919
8920 if (natural_height_p)
8921 *natural_height_p = request_natural_height;
8922
8923 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT);
8924 }
8925
8926 /**
8927 * clutter_actor_get_allocation_box:
8928 * @self: A #ClutterActor
8929 * @box: (out): the function fills this in with the actor's allocation
8930 *
8931 * Gets the layout box an actor has been assigned. The allocation can
8932 * only be assumed valid inside a paint() method; anywhere else, it
8933 * may be out-of-date.
8934 *
8935 * An allocation does not incorporate the actor's scale or translation;
8936 * those transformations do not affect layout, only rendering.
8937 *
8938 * Do not call any of the clutter_actor_get_allocation_*() family
8939 * of functions inside the implementation of the get_preferred_width()
8940 * or get_preferred_height() virtual functions.
8941 *
8942 * Since: 0.8
8943 */
8944 void
clutter_actor_get_allocation_box(ClutterActor * self,ClutterActorBox * box)8945 clutter_actor_get_allocation_box (ClutterActor *self,
8946 ClutterActorBox *box)
8947 {
8948 g_return_if_fail (CLUTTER_IS_ACTOR (self));
8949
8950 /* XXX - if needs_allocation=TRUE, we can either 1) g_return_if_fail,
8951 * which limits calling get_allocation to inside paint() basically; or
8952 * we can 2) force a layout, which could be expensive if someone calls
8953 * get_allocation somewhere silly; or we can 3) just return the latest
8954 * value, allowing it to be out-of-date, and assume people know what
8955 * they are doing.
8956 *
8957 * The least-surprises approach that keeps existing code working is
8958 * likely to be 2). People can end up doing some inefficient things,
8959 * though, and in general code that requires 2) is probably broken.
8960 */
8961
8962 /* this implements 2) */
8963 if (G_UNLIKELY (self->priv->needs_allocation))
8964 {
8965 ClutterActor *stage = _clutter_actor_get_stage_internal (self);
8966
8967 /* do not queue a relayout on an unparented actor */
8968 if (stage)
8969 clutter_stage_maybe_relayout (stage);
8970 }
8971
8972 /* commenting out the code above and just keeping this assignment
8973 * implements 3)
8974 */
8975 *box = self->priv->allocation;
8976 }
8977
8978 static void
clutter_actor_update_constraints(ClutterActor * self,ClutterActorBox * allocation)8979 clutter_actor_update_constraints (ClutterActor *self,
8980 ClutterActorBox *allocation)
8981 {
8982 ClutterActorPrivate *priv = self->priv;
8983 const GList *constraints, *l;
8984
8985 if (priv->constraints == NULL)
8986 return;
8987
8988 constraints = _clutter_meta_group_peek_metas (priv->constraints);
8989 for (l = constraints; l != NULL; l = l->next)
8990 {
8991 ClutterConstraint *constraint = l->data;
8992 ClutterActorMeta *meta = l->data;
8993 gboolean changed = FALSE;
8994
8995 if (clutter_actor_meta_get_enabled (meta))
8996 {
8997 changed |=
8998 clutter_constraint_update_allocation (constraint,
8999 self,
9000 allocation);
9001
9002 CLUTTER_NOTE (LAYOUT,
9003 "Allocation of '%s' after constraint '%s': "
9004 "{ %.2f, %.2f, %.2f, %.2f } (changed:%s)",
9005 _clutter_actor_get_debug_name (self),
9006 _clutter_actor_meta_get_debug_name (meta),
9007 allocation->x1,
9008 allocation->y1,
9009 allocation->x2,
9010 allocation->y2,
9011 changed ? "yes" : "no");
9012 }
9013 }
9014 }
9015
9016 /*< private >
9017 * clutter_actor_adjust_allocation:
9018 * @self: a #ClutterActor
9019 * @allocation: (inout): the allocation to adjust
9020 *
9021 * Adjusts the passed allocation box taking into account the actor's
9022 * layout information, like alignment, expansion, and margin.
9023 */
9024 static void
clutter_actor_adjust_allocation(ClutterActor * self,ClutterActorBox * allocation)9025 clutter_actor_adjust_allocation (ClutterActor *self,
9026 ClutterActorBox *allocation)
9027 {
9028 ClutterActorBox adj_allocation;
9029 float alloc_width, alloc_height;
9030 float min_width, min_height;
9031 float nat_width, nat_height;
9032 ClutterRequestMode req_mode;
9033
9034 adj_allocation = *allocation;
9035
9036 clutter_actor_box_get_size (allocation, &alloc_width, &alloc_height);
9037
9038 /* There's no point in trying to adjust a zero-sized actor */
9039 if (alloc_width == 0.f && alloc_height == 0.f)
9040 return;
9041
9042 /* we want to hit the cache, so we use the public API */
9043 req_mode = clutter_actor_get_request_mode (self);
9044
9045 if (req_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
9046 {
9047 clutter_actor_get_preferred_width (self, -1,
9048 &min_width,
9049 &nat_width);
9050 clutter_actor_get_preferred_height (self, alloc_width,
9051 &min_height,
9052 &nat_height);
9053 }
9054 else if (req_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
9055 {
9056 clutter_actor_get_preferred_height (self, -1,
9057 &min_height,
9058 &nat_height);
9059 clutter_actor_get_preferred_width (self, alloc_height,
9060 &min_width,
9061 &nat_width);
9062 }
9063 else if (req_mode == CLUTTER_REQUEST_CONTENT_SIZE)
9064 {
9065 min_width = min_height = 0;
9066 nat_width = nat_height = 0;
9067
9068 if (self->priv->content != NULL)
9069 clutter_content_get_preferred_size (self->priv->content, &nat_width, &nat_height);
9070 }
9071
9072 #ifdef CLUTTER_ENABLE_DEBUG
9073 /* warn about underallocations */
9074 if (_clutter_diagnostic_enabled () &&
9075 (floorf (min_width - alloc_width) > 0 ||
9076 floorf (min_height - alloc_height) > 0))
9077 {
9078 ClutterActor *parent = clutter_actor_get_parent (self);
9079
9080 /* the only actors that are allowed to be underallocated are the Stage,
9081 * as it doesn't have an implicit size, and Actors that specifically
9082 * told us that they want to opt-out from layout control mechanisms
9083 * through the NO_LAYOUT escape hatch.
9084 */
9085 if (parent != NULL &&
9086 !(self->flags & CLUTTER_ACTOR_NO_LAYOUT) != 0)
9087 {
9088 g_warning (G_STRLOC ": The actor '%s' is getting an allocation "
9089 "of %.2f x %.2f from its parent actor '%s', but its "
9090 "requested minimum size is of %.2f x %.2f",
9091 _clutter_actor_get_debug_name (self),
9092 alloc_width, alloc_height,
9093 _clutter_actor_get_debug_name (parent),
9094 min_width, min_height);
9095 }
9096 }
9097 #endif
9098
9099 clutter_actor_adjust_width (self,
9100 &min_width,
9101 &nat_width,
9102 &adj_allocation.x1,
9103 &adj_allocation.x2);
9104
9105 clutter_actor_adjust_height (self,
9106 &min_height,
9107 &nat_height,
9108 &adj_allocation.y1,
9109 &adj_allocation.y2);
9110
9111 /* we maintain the invariant that an allocation cannot be adjusted
9112 * to be outside the parent-given box
9113 */
9114 if (adj_allocation.x1 < allocation->x1 ||
9115 adj_allocation.y1 < allocation->y1 ||
9116 adj_allocation.x2 > allocation->x2 ||
9117 adj_allocation.y2 > allocation->y2)
9118 {
9119 g_warning (G_STRLOC ": The actor '%s' tried to adjust its allocation "
9120 "to { %.2f, %.2f, %.2f, %.2f }, which is outside of its "
9121 "original allocation of { %.2f, %.2f, %.2f, %.2f }",
9122 _clutter_actor_get_debug_name (self),
9123 adj_allocation.x1, adj_allocation.y1,
9124 adj_allocation.x2 - adj_allocation.x1,
9125 adj_allocation.y2 - adj_allocation.y1,
9126 allocation->x1, allocation->y1,
9127 allocation->x2 - allocation->x1,
9128 allocation->y2 - allocation->y1);
9129 return;
9130 }
9131
9132 *allocation = adj_allocation;
9133 }
9134
9135 static void
clutter_actor_allocate_internal(ClutterActor * self,const ClutterActorBox * allocation)9136 clutter_actor_allocate_internal (ClutterActor *self,
9137 const ClutterActorBox *allocation)
9138 {
9139 ClutterActorClass *klass;
9140
9141 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9142
9143 CLUTTER_NOTE (LAYOUT, "Calling %s::allocate()",
9144 _clutter_actor_get_debug_name (self));
9145
9146 klass = CLUTTER_ACTOR_GET_CLASS (self);
9147 klass->allocate (self, allocation);
9148
9149 CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9150
9151 /* Caller should call clutter_actor_queue_redraw() if needed
9152 * for that particular case.
9153 */
9154 }
9155
9156 /**
9157 * clutter_actor_allocate:
9158 * @self: A #ClutterActor
9159 * @box: new allocation of the actor, in parent-relative coordinates
9160 *
9161 * Assigns the size of a #ClutterActor from the given @box.
9162 *
9163 * This function should only be called on the children of an actor when
9164 * overriding the #ClutterActorClass.allocate() virtual function.
9165 *
9166 * This function will adjust the stored allocation to take into account
9167 * the alignment flags set in the #ClutterActor:x-align and
9168 * #ClutterActor:y-align properties, as well as the margin values set in
9169 * the #ClutterActor:margin-top, #ClutterActor:margin-right,
9170 * #ClutterActor:margin-bottom, and #ClutterActor:margin-left properties.
9171 *
9172 * This function will respect the easing state of the #ClutterActor and
9173 * interpolate between the current allocation and the new one if the
9174 * easing state duration is a positive value.
9175 *
9176 * Actors can know from their allocation box whether they have moved
9177 * with respect to their parent actor. The @flags parameter describes
9178 * additional information about the allocation, for instance whether
9179 * the parent has moved with respect to the stage, for example because
9180 * a grandparent's origin has moved.
9181 *
9182 * Since: 0.8
9183 */
9184 void
clutter_actor_allocate(ClutterActor * self,const ClutterActorBox * box)9185 clutter_actor_allocate (ClutterActor *self,
9186 const ClutterActorBox *box)
9187 {
9188 ClutterActorBox old_allocation, real_allocation;
9189 gboolean origin_changed, size_changed;
9190 ClutterActorPrivate *priv;
9191
9192 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9193 if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL))
9194 {
9195 g_warning ("Spurious clutter_actor_allocate called for actor %p/%s "
9196 "which isn't a descendent of the stage!\n",
9197 self, _clutter_actor_get_debug_name (self));
9198 return;
9199 }
9200
9201 priv = self->priv;
9202
9203 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9204 !CLUTTER_ACTOR_IS_MAPPED (self) &&
9205 !clutter_actor_has_mapped_clones (self))
9206 return;
9207
9208 #ifdef COGL_HAS_TRACING
9209 COGL_TRACE_SCOPED_ANCHOR (ClutterActorAllocate);
9210
9211 if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DETAILED_TRACE))
9212 {
9213 COGL_TRACE_BEGIN_ANCHORED (ClutterActorAllocate,
9214 "ClutterActor (allocate)");
9215 COGL_TRACE_DESCRIBE (ClutterActorAllocate,
9216 _clutter_actor_get_debug_name (self));
9217 }
9218 #endif
9219
9220 old_allocation = priv->allocation;
9221 real_allocation = *box;
9222
9223 g_return_if_fail (!isnan (real_allocation.x1) &&
9224 !isnan (real_allocation.x2) &&
9225 !isnan (real_allocation.y1) &&
9226 !isnan (real_allocation.y2));
9227
9228 /* constraints are allowed to modify the allocation only here; we do
9229 * this prior to all the other checks so that we can bail out if the
9230 * allocation did not change
9231 */
9232 clutter_actor_update_constraints (self, &real_allocation);
9233
9234 /* adjust the allocation depending on the align/margin properties */
9235 clutter_actor_adjust_allocation (self, &real_allocation);
9236
9237 if (real_allocation.x2 < real_allocation.x1 ||
9238 real_allocation.y2 < real_allocation.y1)
9239 {
9240 g_warning (G_STRLOC ": Actor '%s' tried to allocate a size of %.2f x %.2f",
9241 _clutter_actor_get_debug_name (self),
9242 real_allocation.x2 - real_allocation.x1,
9243 real_allocation.y2 - real_allocation.y1);
9244 }
9245
9246 /* we allow 0-sized actors, but not negative-sized ones */
9247 real_allocation.x2 = MAX (real_allocation.x2, real_allocation.x1);
9248 real_allocation.y2 = MAX (real_allocation.y2, real_allocation.y1);
9249
9250 origin_changed = (real_allocation.x1 != old_allocation.x1 ||
9251 real_allocation.y1 != old_allocation.y1);
9252
9253 size_changed = (real_allocation.x2 != old_allocation.x2 ||
9254 real_allocation.y2 != old_allocation.y2);
9255
9256 /* When needs_allocation is set but we didn't move nor resize, we still
9257 * want to call the allocate() vfunc because a child probably called
9258 * queue_relayout() and needs a new allocation.
9259 *
9260 * In case needs_allocation isn't set and we didn't move nor resize, we
9261 * can safely stop allocating.
9262 */
9263 if (!priv->needs_allocation && !origin_changed && !size_changed)
9264 {
9265 CLUTTER_NOTE (LAYOUT, "No allocation needed");
9266 return;
9267 }
9268
9269 if (!origin_changed && !size_changed)
9270 {
9271 /* If the actor didn't move but needs_allocation is set, we just
9272 * need to allocate the children (see comment above) */
9273 clutter_actor_allocate_internal (self, &real_allocation);
9274 return;
9275 }
9276
9277 if (_clutter_actor_create_transition (self, obj_props[PROP_ALLOCATION],
9278 &priv->allocation,
9279 &real_allocation))
9280 clutter_actor_allocate_internal (self, &priv->allocation);
9281 }
9282
9283 /**
9284 * clutter_actor_set_allocation:
9285 * @self: a #ClutterActor
9286 * @box: a #ClutterActorBox
9287 *
9288 * Stores the allocation of @self as defined by @box.
9289 *
9290 * This function can only be called from within the implementation of
9291 * the #ClutterActorClass.allocate() virtual function.
9292 *
9293 * The allocation @box should have been adjusted to take into account
9294 * constraints, alignment, and margin properties.
9295 *
9296 * This function should only be used by subclasses of #ClutterActor
9297 * that wish to store their allocation but cannot chain up to the
9298 * parent's implementation; the default implementation of the
9299 * #ClutterActorClass.allocate() virtual function will call this
9300 * function.
9301 *
9302 * Since: 1.10
9303 */
9304 void
clutter_actor_set_allocation(ClutterActor * self,const ClutterActorBox * box)9305 clutter_actor_set_allocation (ClutterActor *self,
9306 const ClutterActorBox *box)
9307 {
9308 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9309 g_return_if_fail (box != NULL);
9310
9311 if (G_UNLIKELY (!CLUTTER_ACTOR_IN_RELAYOUT (self)))
9312 {
9313 g_critical (G_STRLOC ": The clutter_actor_set_allocation() function "
9314 "can only be called from within the implementation of "
9315 "the ClutterActor::allocate() virtual function.");
9316 return;
9317 }
9318
9319 g_object_freeze_notify (G_OBJECT (self));
9320
9321 clutter_actor_set_allocation_internal (self, box);
9322
9323 g_object_thaw_notify (G_OBJECT (self));
9324 }
9325
9326 /**
9327 * clutter_actor_set_position:
9328 * @self: A #ClutterActor
9329 * @x: New left position of actor in pixels.
9330 * @y: New top position of actor in pixels.
9331 *
9332 * Sets the actor's fixed position in pixels relative to any parent
9333 * actor.
9334 *
9335 * If a layout manager is in use, this position will override the
9336 * layout manager and force a fixed position.
9337 */
9338 void
clutter_actor_set_position(ClutterActor * self,gfloat x,gfloat y)9339 clutter_actor_set_position (ClutterActor *self,
9340 gfloat x,
9341 gfloat y)
9342 {
9343 graphene_point_t new_position;
9344 graphene_point_t cur_position;
9345
9346 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9347
9348 graphene_point_init (&new_position, x, y);
9349
9350 cur_position.x = clutter_actor_get_x (self);
9351 cur_position.y = clutter_actor_get_y (self);
9352
9353 if (!graphene_point_equal (&cur_position, &new_position))
9354 _clutter_actor_create_transition (self, obj_props[PROP_POSITION],
9355 &cur_position,
9356 &new_position);
9357 }
9358
9359 /**
9360 * clutter_actor_get_fixed_position_set:
9361 * @self: A #ClutterActor
9362 *
9363 * Checks whether an actor has a fixed position set (and will thus be
9364 * unaffected by any layout manager).
9365 *
9366 * Return value: %TRUE if the fixed position is set on the actor
9367 *
9368 * Since: 0.8
9369 */
9370 gboolean
clutter_actor_get_fixed_position_set(ClutterActor * self)9371 clutter_actor_get_fixed_position_set (ClutterActor *self)
9372 {
9373 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
9374
9375 return self->priv->position_set;
9376 }
9377
9378 /**
9379 * clutter_actor_set_fixed_position_set:
9380 * @self: A #ClutterActor
9381 * @is_set: whether to use fixed position
9382 *
9383 * Sets whether an actor has a fixed position set (and will thus be
9384 * unaffected by any layout manager).
9385 *
9386 * Since: 0.8
9387 */
9388 void
clutter_actor_set_fixed_position_set(ClutterActor * self,gboolean is_set)9389 clutter_actor_set_fixed_position_set (ClutterActor *self,
9390 gboolean is_set)
9391 {
9392 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9393
9394 if (self->priv->position_set == (is_set != FALSE))
9395 return;
9396
9397 if (!is_set)
9398 {
9399 ClutterLayoutInfo *info;
9400
9401 /* Ensure we set back the default fixed position of 0,0 so that setting
9402 just one of x/y always atomically gets 0 for the other */
9403 info = _clutter_actor_peek_layout_info (self);
9404 if (info != NULL)
9405 {
9406 info->fixed_pos.x = 0;
9407 info->fixed_pos.y = 0;
9408 }
9409 }
9410
9411 self->priv->position_set = is_set != FALSE;
9412 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIXED_POSITION_SET]);
9413
9414 clutter_actor_queue_relayout (self);
9415 }
9416
9417 /**
9418 * clutter_actor_move_by:
9419 * @self: A #ClutterActor
9420 * @dx: Distance to move Actor on X axis.
9421 * @dy: Distance to move Actor on Y axis.
9422 *
9423 * Moves an actor by the specified distance relative to its current
9424 * position in pixels.
9425 *
9426 * This function modifies the fixed position of an actor and thus removes
9427 * it from any layout management. Another way to move an actor is with an
9428 * additional translation, using clutter_actor_set_translation().
9429 *
9430 * Since: 0.2
9431 */
9432 void
clutter_actor_move_by(ClutterActor * self,gfloat dx,gfloat dy)9433 clutter_actor_move_by (ClutterActor *self,
9434 gfloat dx,
9435 gfloat dy)
9436 {
9437 const ClutterLayoutInfo *info;
9438 gfloat x, y;
9439
9440 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9441
9442 info = _clutter_actor_get_layout_info_or_defaults (self);
9443 x = info->fixed_pos.x;
9444 y = info->fixed_pos.y;
9445
9446 clutter_actor_set_position (self, x + dx, y + dy);
9447 }
9448
9449 static void
clutter_actor_set_min_width(ClutterActor * self,gfloat min_width)9450 clutter_actor_set_min_width (ClutterActor *self,
9451 gfloat min_width)
9452 {
9453 ClutterActorPrivate *priv = self->priv;
9454 ClutterActorBox old = { 0, };
9455 ClutterLayoutInfo *info;
9456
9457 /* if we are setting the size on a top-level actor and the
9458 * backend only supports static top-levels (e.g. framebuffers)
9459 * then we ignore the passed value and we override it with
9460 * the stage implementation's preferred size.
9461 */
9462 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9463 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9464 return;
9465
9466 info = _clutter_actor_get_layout_info (self);
9467
9468 if (priv->min_width_set && min_width == info->minimum.width)
9469 return;
9470
9471 g_object_freeze_notify (G_OBJECT (self));
9472
9473 clutter_actor_store_old_geometry (self, &old);
9474
9475 info->minimum.width = min_width;
9476 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH]);
9477 clutter_actor_set_min_width_set (self, TRUE);
9478
9479 clutter_actor_notify_if_geometry_changed (self, &old);
9480
9481 g_object_thaw_notify (G_OBJECT (self));
9482
9483 clutter_actor_queue_relayout (self);
9484 }
9485
9486 static void
clutter_actor_set_min_height(ClutterActor * self,gfloat min_height)9487 clutter_actor_set_min_height (ClutterActor *self,
9488 gfloat min_height)
9489
9490 {
9491 ClutterActorPrivate *priv = self->priv;
9492 ClutterActorBox old = { 0, };
9493 ClutterLayoutInfo *info;
9494
9495 /* if we are setting the size on a top-level actor and the
9496 * backend only supports static top-levels (e.g. framebuffers)
9497 * then we ignore the passed value and we override it with
9498 * the stage implementation's preferred size.
9499 */
9500 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9501 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9502 return;
9503
9504 info = _clutter_actor_get_layout_info (self);
9505
9506 if (priv->min_height_set && min_height == info->minimum.height)
9507 return;
9508
9509 g_object_freeze_notify (G_OBJECT (self));
9510
9511 clutter_actor_store_old_geometry (self, &old);
9512
9513 info->minimum.height = min_height;
9514 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT]);
9515 clutter_actor_set_min_height_set (self, TRUE);
9516
9517 clutter_actor_notify_if_geometry_changed (self, &old);
9518
9519 g_object_thaw_notify (G_OBJECT (self));
9520
9521 clutter_actor_queue_relayout (self);
9522 }
9523
9524 static void
clutter_actor_set_natural_width(ClutterActor * self,gfloat natural_width)9525 clutter_actor_set_natural_width (ClutterActor *self,
9526 gfloat natural_width)
9527 {
9528 ClutterActorPrivate *priv = self->priv;
9529 ClutterActorBox old = { 0, };
9530 ClutterLayoutInfo *info;
9531
9532 /* if we are setting the size on a top-level actor and the
9533 * backend only supports static top-levels (e.g. framebuffers)
9534 * then we ignore the passed value and we override it with
9535 * the stage implementation's preferred size.
9536 */
9537 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9538 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9539 return;
9540
9541 info = _clutter_actor_get_layout_info (self);
9542
9543 if (priv->natural_width_set && natural_width == info->natural.width)
9544 return;
9545
9546 g_object_freeze_notify (G_OBJECT (self));
9547
9548 clutter_actor_store_old_geometry (self, &old);
9549
9550 info->natural.width = natural_width;
9551 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH]);
9552 clutter_actor_set_natural_width_set (self, TRUE);
9553
9554 clutter_actor_notify_if_geometry_changed (self, &old);
9555
9556 g_object_thaw_notify (G_OBJECT (self));
9557
9558 clutter_actor_queue_relayout (self);
9559 }
9560
9561 static void
clutter_actor_set_natural_height(ClutterActor * self,gfloat natural_height)9562 clutter_actor_set_natural_height (ClutterActor *self,
9563 gfloat natural_height)
9564 {
9565 ClutterActorPrivate *priv = self->priv;
9566 ClutterActorBox old = { 0, };
9567 ClutterLayoutInfo *info;
9568
9569 /* if we are setting the size on a top-level actor and the
9570 * backend only supports static top-levels (e.g. framebuffers)
9571 * then we ignore the passed value and we override it with
9572 * the stage implementation's preferred size.
9573 */
9574 if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9575 clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9576 return;
9577
9578 info = _clutter_actor_get_layout_info (self);
9579
9580 if (priv->natural_height_set && natural_height == info->natural.height)
9581 return;
9582
9583 g_object_freeze_notify (G_OBJECT (self));
9584
9585 clutter_actor_store_old_geometry (self, &old);
9586
9587 info->natural.height = natural_height;
9588 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT]);
9589 clutter_actor_set_natural_height_set (self, TRUE);
9590
9591 clutter_actor_notify_if_geometry_changed (self, &old);
9592
9593 g_object_thaw_notify (G_OBJECT (self));
9594
9595 clutter_actor_queue_relayout (self);
9596 }
9597
9598 static void
clutter_actor_set_min_width_set(ClutterActor * self,gboolean use_min_width)9599 clutter_actor_set_min_width_set (ClutterActor *self,
9600 gboolean use_min_width)
9601 {
9602 ClutterActorPrivate *priv = self->priv;
9603 ClutterActorBox old = { 0, };
9604
9605 if (priv->min_width_set == (use_min_width != FALSE))
9606 return;
9607
9608 clutter_actor_store_old_geometry (self, &old);
9609
9610 priv->min_width_set = use_min_width != FALSE;
9611 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH_SET]);
9612
9613 clutter_actor_notify_if_geometry_changed (self, &old);
9614
9615 clutter_actor_queue_relayout (self);
9616 }
9617
9618 static void
clutter_actor_set_min_height_set(ClutterActor * self,gboolean use_min_height)9619 clutter_actor_set_min_height_set (ClutterActor *self,
9620 gboolean use_min_height)
9621 {
9622 ClutterActorPrivate *priv = self->priv;
9623 ClutterActorBox old = { 0, };
9624
9625 if (priv->min_height_set == (use_min_height != FALSE))
9626 return;
9627
9628 clutter_actor_store_old_geometry (self, &old);
9629
9630 priv->min_height_set = use_min_height != FALSE;
9631 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT_SET]);
9632
9633 clutter_actor_notify_if_geometry_changed (self, &old);
9634
9635 clutter_actor_queue_relayout (self);
9636 }
9637
9638 static void
clutter_actor_set_natural_width_set(ClutterActor * self,gboolean use_natural_width)9639 clutter_actor_set_natural_width_set (ClutterActor *self,
9640 gboolean use_natural_width)
9641 {
9642 ClutterActorPrivate *priv = self->priv;
9643 ClutterActorBox old = { 0, };
9644
9645 if (priv->natural_width_set == (use_natural_width != FALSE))
9646 return;
9647
9648 clutter_actor_store_old_geometry (self, &old);
9649
9650 priv->natural_width_set = use_natural_width != FALSE;
9651 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH_SET]);
9652
9653 clutter_actor_notify_if_geometry_changed (self, &old);
9654
9655 clutter_actor_queue_relayout (self);
9656 }
9657
9658 static void
clutter_actor_set_natural_height_set(ClutterActor * self,gboolean use_natural_height)9659 clutter_actor_set_natural_height_set (ClutterActor *self,
9660 gboolean use_natural_height)
9661 {
9662 ClutterActorPrivate *priv = self->priv;
9663 ClutterActorBox old = { 0, };
9664
9665 if (priv->natural_height_set == (use_natural_height != FALSE))
9666 return;
9667
9668 clutter_actor_store_old_geometry (self, &old);
9669
9670 priv->natural_height_set = use_natural_height != FALSE;
9671 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT_SET]);
9672
9673 clutter_actor_notify_if_geometry_changed (self, &old);
9674
9675 clutter_actor_queue_relayout (self);
9676 }
9677
9678 /**
9679 * clutter_actor_set_request_mode:
9680 * @self: a #ClutterActor
9681 * @mode: the request mode
9682 *
9683 * Sets the geometry request mode of @self.
9684 *
9685 * The @mode determines the order for invoking
9686 * clutter_actor_get_preferred_width() and
9687 * clutter_actor_get_preferred_height()
9688 *
9689 * Since: 1.2
9690 */
9691 void
clutter_actor_set_request_mode(ClutterActor * self,ClutterRequestMode mode)9692 clutter_actor_set_request_mode (ClutterActor *self,
9693 ClutterRequestMode mode)
9694 {
9695 ClutterActorPrivate *priv;
9696
9697 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9698
9699 priv = self->priv;
9700
9701 if (priv->request_mode == mode)
9702 return;
9703
9704 priv->request_mode = mode;
9705
9706 priv->needs_width_request = TRUE;
9707 priv->needs_height_request = TRUE;
9708
9709 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REQUEST_MODE]);
9710
9711 clutter_actor_queue_relayout (self);
9712 }
9713
9714 /**
9715 * clutter_actor_get_request_mode:
9716 * @self: a #ClutterActor
9717 *
9718 * Retrieves the geometry request mode of @self
9719 *
9720 * Return value: the request mode for the actor
9721 *
9722 * Since: 1.2
9723 */
9724 ClutterRequestMode
clutter_actor_get_request_mode(ClutterActor * self)9725 clutter_actor_get_request_mode (ClutterActor *self)
9726 {
9727 g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
9728 CLUTTER_REQUEST_HEIGHT_FOR_WIDTH);
9729
9730 return self->priv->request_mode;
9731 }
9732
9733 /* variant of set_width() without checks and without notification
9734 * freeze+thaw, for internal usage only
9735 */
9736 static inline void
clutter_actor_set_width_internal(ClutterActor * self,gfloat width)9737 clutter_actor_set_width_internal (ClutterActor *self,
9738 gfloat width)
9739 {
9740 if (width >= 0)
9741 {
9742 /* the Stage will use the :min-width to control the minimum
9743 * width to be resized to, so we should not be setting it
9744 * along with the :natural-width
9745 */
9746 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9747 clutter_actor_set_min_width (self, width);
9748
9749 clutter_actor_set_natural_width (self, width);
9750 }
9751 else
9752 {
9753 /* we only unset the :natural-width for the Stage */
9754 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9755 clutter_actor_set_min_width_set (self, FALSE);
9756
9757 clutter_actor_set_natural_width_set (self, FALSE);
9758 }
9759 }
9760
9761 /* variant of set_height() without checks and without notification
9762 * freeze+thaw, for internal usage only
9763 */
9764 static inline void
clutter_actor_set_height_internal(ClutterActor * self,gfloat height)9765 clutter_actor_set_height_internal (ClutterActor *self,
9766 gfloat height)
9767 {
9768 if (height >= 0)
9769 {
9770 /* see the comment above in set_width_internal() */
9771 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9772 clutter_actor_set_min_height (self, height);
9773
9774 clutter_actor_set_natural_height (self, height);
9775 }
9776 else
9777 {
9778 /* see the comment above in set_width_internal() */
9779 if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9780 clutter_actor_set_min_height_set (self, FALSE);
9781
9782 clutter_actor_set_natural_height_set (self, FALSE);
9783 }
9784 }
9785
9786 static void
clutter_actor_set_size_internal(ClutterActor * self,const graphene_size_t * size)9787 clutter_actor_set_size_internal (ClutterActor *self,
9788 const graphene_size_t *size)
9789 {
9790 if (size != NULL)
9791 {
9792 clutter_actor_set_width_internal (self, size->width);
9793 clutter_actor_set_height_internal (self, size->height);
9794 }
9795 else
9796 {
9797 clutter_actor_set_width_internal (self, -1);
9798 clutter_actor_set_height_internal (self, -1);
9799 }
9800 }
9801
9802 /**
9803 * clutter_actor_set_size:
9804 * @self: A #ClutterActor
9805 * @width: New width of actor in pixels, or -1
9806 * @height: New height of actor in pixels, or -1
9807 *
9808 * Sets the actor's size request in pixels. This overrides any
9809 * "normal" size request the actor would have. For example
9810 * a text actor might normally request the size of the text;
9811 * this function would force a specific size instead.
9812 *
9813 * If @width and/or @height are -1 the actor will use its
9814 * "normal" size request instead of overriding it, i.e.
9815 * you can "unset" the size with -1.
9816 *
9817 * This function sets or unsets both the minimum and natural size.
9818 */
9819 void
clutter_actor_set_size(ClutterActor * self,gfloat width,gfloat height)9820 clutter_actor_set_size (ClutterActor *self,
9821 gfloat width,
9822 gfloat height)
9823 {
9824 graphene_size_t new_size;
9825
9826 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9827
9828 graphene_size_init (&new_size, width, height);
9829
9830 /* minor optimization: if we don't have a duration then we can
9831 * skip the get_size() below, to avoid the chance of going through
9832 * get_preferred_width() and get_preferred_height() just to jump to
9833 * a new desired size
9834 */
9835 if (clutter_actor_get_easing_duration (self) == 0)
9836 {
9837 g_object_freeze_notify (G_OBJECT (self));
9838
9839 clutter_actor_set_size_internal (self, &new_size);
9840
9841 g_object_thaw_notify (G_OBJECT (self));
9842
9843 return;
9844 }
9845 else
9846 {
9847 graphene_size_t cur_size;
9848
9849 graphene_size_init (&cur_size,
9850 clutter_actor_get_width (self),
9851 clutter_actor_get_height (self));
9852
9853 _clutter_actor_create_transition (self,
9854 obj_props[PROP_SIZE],
9855 &cur_size,
9856 &new_size);
9857 }
9858 }
9859
9860 /**
9861 * clutter_actor_get_size:
9862 * @self: A #ClutterActor
9863 * @width: (out) (allow-none): return location for the width, or %NULL.
9864 * @height: (out) (allow-none): return location for the height, or %NULL.
9865 *
9866 * This function tries to "do what you mean" and return
9867 * the size an actor will have. If the actor has a valid
9868 * allocation, the allocation will be returned; otherwise,
9869 * the actors natural size request will be returned.
9870 *
9871 * If you care whether you get the request vs. the allocation, you
9872 * should probably call a different function like
9873 * clutter_actor_get_allocation_box() or
9874 * clutter_actor_get_preferred_width().
9875 *
9876 * Since: 0.2
9877 */
9878 void
clutter_actor_get_size(ClutterActor * self,gfloat * width,gfloat * height)9879 clutter_actor_get_size (ClutterActor *self,
9880 gfloat *width,
9881 gfloat *height)
9882 {
9883 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9884
9885 if (width)
9886 *width = clutter_actor_get_width (self);
9887
9888 if (height)
9889 *height = clutter_actor_get_height (self);
9890 }
9891
9892 /**
9893 * clutter_actor_get_position:
9894 * @self: a #ClutterActor
9895 * @x: (out) (allow-none): return location for the X coordinate, or %NULL
9896 * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
9897 *
9898 * This function tries to "do what you mean" and tell you where the
9899 * actor is, prior to any transformations. Retrieves the fixed
9900 * position of an actor in pixels, if one has been set; otherwise, if
9901 * the allocation is valid, returns the actor's allocated position;
9902 * otherwise, returns 0,0.
9903 *
9904 * The returned position is in pixels.
9905 *
9906 * Since: 0.6
9907 */
9908 void
clutter_actor_get_position(ClutterActor * self,gfloat * x,gfloat * y)9909 clutter_actor_get_position (ClutterActor *self,
9910 gfloat *x,
9911 gfloat *y)
9912 {
9913 g_return_if_fail (CLUTTER_IS_ACTOR (self));
9914
9915 if (x)
9916 *x = clutter_actor_get_x (self);
9917
9918 if (y)
9919 *y = clutter_actor_get_y (self);
9920 }
9921
9922 /**
9923 * clutter_actor_get_fixed_position:
9924 * @self: a #ClutterActor
9925 * @x: (out) (allow-none): return location for the X coordinate, or %NULL
9926 * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
9927 *
9928 * This function gets the fixed position of the actor, if set. If there
9929 * is no fixed position set, this function returns %FALSE and doesn't set
9930 * the x and y coordinates.
9931 *
9932 * Returns: %TRUE if the fixed position is set, %FALSE if it isn't
9933 */
9934 gboolean
clutter_actor_get_fixed_position(ClutterActor * self,float * x,float * y)9935 clutter_actor_get_fixed_position (ClutterActor *self,
9936 float *x,
9937 float *y)
9938 {
9939 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
9940
9941 if (self->priv->position_set)
9942 {
9943 const ClutterLayoutInfo *info;
9944
9945 info = _clutter_actor_get_layout_info_or_defaults (self);
9946
9947 if (x)
9948 *x = info->fixed_pos.x;
9949
9950 if (y)
9951 *y = info->fixed_pos.y;
9952
9953 return TRUE;
9954 }
9955
9956 return FALSE;
9957 }
9958
9959 /**
9960 * clutter_actor_get_transformed_extents:
9961 * @self: A #ClutterActor
9962 * @rect: (out): return location for the transformed bounding rect
9963 *
9964 * Gets the transformed bounding rect of an actor, in pixels relative to the stage.
9965 */
9966 void
clutter_actor_get_transformed_extents(ClutterActor * self,graphene_rect_t * rect)9967 clutter_actor_get_transformed_extents (ClutterActor *self,
9968 graphene_rect_t *rect)
9969 {
9970 graphene_quad_t quad;
9971 graphene_point3d_t v[4];
9972 ClutterActorBox box;
9973
9974 box.x1 = 0;
9975 box.y1 = 0;
9976 box.x2 = clutter_actor_box_get_width (&self->priv->allocation);
9977 box.y2 = clutter_actor_box_get_height (&self->priv->allocation);
9978 if (_clutter_actor_transform_and_project_box (self, &box, v))
9979 {
9980 graphene_quad_init (&quad,
9981 (graphene_point_t *) &v[0],
9982 (graphene_point_t *) &v[1],
9983 (graphene_point_t *) &v[2],
9984 (graphene_point_t *) &v[3]);
9985
9986 if (rect)
9987 graphene_quad_bounds (&quad, rect);
9988 }
9989 }
9990
9991 /**
9992 * clutter_actor_get_transformed_position:
9993 * @self: A #ClutterActor
9994 * @x: (out) (allow-none): return location for the X coordinate, or %NULL
9995 * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
9996 *
9997 * Gets the absolute position of an actor, in pixels relative to the stage.
9998 *
9999 * Since: 0.8
10000 */
10001 void
clutter_actor_get_transformed_position(ClutterActor * self,gfloat * x,gfloat * y)10002 clutter_actor_get_transformed_position (ClutterActor *self,
10003 gfloat *x,
10004 gfloat *y)
10005 {
10006 graphene_point3d_t v1;
10007 graphene_point3d_t v2;
10008
10009 v1.x = v1.y = v1.z = 0;
10010 clutter_actor_apply_transform_to_point (self, &v1, &v2);
10011
10012 if (x)
10013 *x = v2.x;
10014
10015 if (y)
10016 *y = v2.y;
10017 }
10018
10019 /**
10020 * clutter_actor_get_transformed_size:
10021 * @self: A #ClutterActor
10022 * @width: (out) (allow-none): return location for the width, or %NULL
10023 * @height: (out) (allow-none): return location for the height, or %NULL
10024 *
10025 * Gets the absolute size of an actor in pixels, taking into account the
10026 * scaling factors.
10027 *
10028 * If the actor has a valid allocation, the allocated size will be used.
10029 * If the actor has not a valid allocation then the preferred size will
10030 * be transformed and returned.
10031 *
10032 * If you want the transformed allocation, see
10033 * clutter_actor_get_abs_allocation_vertices() instead.
10034 *
10035 * When the actor (or one of its ancestors) is rotated around the
10036 * X or Y axis, it no longer appears as on the stage as a rectangle, but
10037 * as a generic quadrangle; in that case this function returns the size
10038 * of the smallest rectangle that encapsulates the entire quad. Please
10039 * note that in this case no assumptions can be made about the relative
10040 * position of this envelope to the absolute position of the actor, as
10041 * returned by clutter_actor_get_transformed_position(); if you need this
10042 * information, you need to use clutter_actor_get_abs_allocation_vertices()
10043 * to get the coords of the actual quadrangle.
10044 *
10045 * Since: 0.8
10046 */
10047 void
clutter_actor_get_transformed_size(ClutterActor * self,gfloat * width,gfloat * height)10048 clutter_actor_get_transformed_size (ClutterActor *self,
10049 gfloat *width,
10050 gfloat *height)
10051 {
10052 ClutterActorPrivate *priv;
10053 graphene_point3d_t v[4];
10054 gfloat x_min, x_max, y_min, y_max;
10055 gint i;
10056
10057 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10058
10059 priv = self->priv;
10060
10061 /* if the actor hasn't been allocated yet, get the preferred
10062 * size and transform that
10063 */
10064 if (priv->needs_allocation)
10065 {
10066 gfloat natural_width, natural_height;
10067 ClutterActorBox box;
10068
10069 /* Make a fake allocation to transform.
10070 *
10071 * NB: _clutter_actor_transform_and_project_box expects a box in
10072 * the actor's coordinate space... */
10073
10074 box.x1 = 0;
10075 box.y1 = 0;
10076
10077 natural_width = natural_height = 0;
10078 clutter_actor_get_preferred_size (self, NULL, NULL,
10079 &natural_width,
10080 &natural_height);
10081
10082 box.x2 = natural_width;
10083 box.y2 = natural_height;
10084
10085 _clutter_actor_transform_and_project_box (self, &box, v);
10086 }
10087 else
10088 clutter_actor_get_abs_allocation_vertices (self, v);
10089
10090 x_min = x_max = v[0].x;
10091 y_min = y_max = v[0].y;
10092
10093 for (i = 1; i < G_N_ELEMENTS (v); ++i)
10094 {
10095 if (v[i].x < x_min)
10096 x_min = v[i].x;
10097
10098 if (v[i].x > x_max)
10099 x_max = v[i].x;
10100
10101 if (v[i].y < y_min)
10102 y_min = v[i].y;
10103
10104 if (v[i].y > y_max)
10105 y_max = v[i].y;
10106 }
10107
10108 if (width)
10109 *width = x_max - x_min;
10110
10111 if (height)
10112 *height = y_max - y_min;
10113 }
10114
10115 /**
10116 * clutter_actor_get_width:
10117 * @self: A #ClutterActor
10118 *
10119 * Retrieves the width of a #ClutterActor.
10120 *
10121 * If the actor has a valid allocation, this function will return the
10122 * width of the allocated area given to the actor.
10123 *
10124 * If the actor does not have a valid allocation, this function will
10125 * return the actor's natural width, that is the preferred width of
10126 * the actor.
10127 *
10128 * If you care whether you get the preferred width or the width that
10129 * has been assigned to the actor, you should probably call a different
10130 * function like clutter_actor_get_allocation_box() to retrieve the
10131 * allocated size or clutter_actor_get_preferred_width() to retrieve the
10132 * preferred width.
10133 *
10134 * If an actor has a fixed width, for instance a width that has been
10135 * assigned using clutter_actor_set_width(), the width returned will
10136 * be the same value.
10137 *
10138 * Return value: the width of the actor, in pixels
10139 */
10140 gfloat
clutter_actor_get_width(ClutterActor * self)10141 clutter_actor_get_width (ClutterActor *self)
10142 {
10143 ClutterActorPrivate *priv;
10144
10145 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10146
10147 priv = self->priv;
10148
10149 if (priv->needs_allocation)
10150 {
10151 gfloat natural_width = 0;
10152
10153 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
10154 {
10155 clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
10156 }
10157 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
10158 {
10159 gfloat natural_height = 0;
10160
10161 clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
10162 clutter_actor_get_preferred_width (self, natural_height,
10163 NULL,
10164 &natural_width);
10165 }
10166 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
10167 {
10168 clutter_content_get_preferred_size (priv->content, &natural_width, NULL);
10169 }
10170
10171 return natural_width;
10172 }
10173 else
10174 return priv->allocation.x2 - priv->allocation.x1;
10175 }
10176
10177 /**
10178 * clutter_actor_get_height:
10179 * @self: A #ClutterActor
10180 *
10181 * Retrieves the height of a #ClutterActor.
10182 *
10183 * If the actor has a valid allocation, this function will return the
10184 * height of the allocated area given to the actor.
10185 *
10186 * If the actor does not have a valid allocation, this function will
10187 * return the actor's natural height, that is the preferred height of
10188 * the actor.
10189 *
10190 * If you care whether you get the preferred height or the height that
10191 * has been assigned to the actor, you should probably call a different
10192 * function like clutter_actor_get_allocation_box() to retrieve the
10193 * allocated size or clutter_actor_get_preferred_height() to retrieve the
10194 * preferred height.
10195 *
10196 * If an actor has a fixed height, for instance a height that has been
10197 * assigned using clutter_actor_set_height(), the height returned will
10198 * be the same value.
10199 *
10200 * Return value: the height of the actor, in pixels
10201 */
10202 gfloat
clutter_actor_get_height(ClutterActor * self)10203 clutter_actor_get_height (ClutterActor *self)
10204 {
10205 ClutterActorPrivate *priv;
10206
10207 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10208
10209 priv = self->priv;
10210
10211 if (priv->needs_allocation)
10212 {
10213 gfloat natural_height = 0;
10214
10215 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
10216 {
10217 gfloat natural_width = 0;
10218
10219 clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
10220 clutter_actor_get_preferred_height (self, natural_width,
10221 NULL, &natural_height);
10222 }
10223 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
10224 {
10225 clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
10226 }
10227 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
10228 {
10229 clutter_content_get_preferred_size (priv->content, NULL, &natural_height);
10230 }
10231
10232 return natural_height;
10233 }
10234 else
10235 return priv->allocation.y2 - priv->allocation.y1;
10236 }
10237
10238 /**
10239 * clutter_actor_set_width:
10240 * @self: A #ClutterActor
10241 * @width: Requested new width for the actor, in pixels, or -1
10242 *
10243 * Forces a width on an actor, causing the actor's preferred width
10244 * and height (if any) to be ignored.
10245 *
10246 * If @width is -1 the actor will use its preferred width request
10247 * instead of overriding it, i.e. you can "unset" the width with -1.
10248 *
10249 * This function sets both the minimum and natural size of the actor.
10250 *
10251 * since: 0.2
10252 */
10253 void
clutter_actor_set_width(ClutterActor * self,gfloat width)10254 clutter_actor_set_width (ClutterActor *self,
10255 gfloat width)
10256 {
10257 float cur_size;
10258
10259 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10260
10261 /* minor optimization: if we don't have a duration
10262 * then we can skip the get_width() below, to avoid
10263 * the chance of going through get_preferred_width()
10264 * just to jump to a new desired width.
10265 */
10266 if (clutter_actor_get_easing_duration (self) == 0)
10267 {
10268 g_object_freeze_notify (G_OBJECT (self));
10269
10270 clutter_actor_set_width_internal (self, width);
10271
10272 g_object_thaw_notify (G_OBJECT (self));
10273
10274 return;
10275 }
10276 else
10277 cur_size = clutter_actor_get_width (self);
10278
10279 _clutter_actor_create_transition (self,
10280 obj_props[PROP_WIDTH],
10281 cur_size,
10282 width);
10283 }
10284
10285 /**
10286 * clutter_actor_set_height:
10287 * @self: A #ClutterActor
10288 * @height: Requested new height for the actor, in pixels, or -1
10289 *
10290 * Forces a height on an actor, causing the actor's preferred width
10291 * and height (if any) to be ignored.
10292 *
10293 * If @height is -1 the actor will use its preferred height instead of
10294 * overriding it, i.e. you can "unset" the height with -1.
10295 *
10296 * This function sets both the minimum and natural size of the actor.
10297 *
10298 * since: 0.2
10299 */
10300 void
clutter_actor_set_height(ClutterActor * self,gfloat height)10301 clutter_actor_set_height (ClutterActor *self,
10302 gfloat height)
10303 {
10304 float cur_size;
10305
10306 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10307
10308 /* see the comment in clutter_actor_set_width() above */
10309 if (clutter_actor_get_easing_duration (self) == 0)
10310 {
10311 g_object_freeze_notify (G_OBJECT (self));
10312
10313 clutter_actor_set_height_internal (self, height);
10314
10315 g_object_thaw_notify (G_OBJECT (self));
10316
10317 return;
10318 }
10319 else
10320 cur_size = clutter_actor_get_height (self);
10321
10322 _clutter_actor_create_transition (self,
10323 obj_props[PROP_HEIGHT],
10324 cur_size,
10325 height);
10326 }
10327
10328 static inline void
clutter_actor_set_x_internal(ClutterActor * self,float x)10329 clutter_actor_set_x_internal (ClutterActor *self,
10330 float x)
10331 {
10332 ClutterActorPrivate *priv = self->priv;
10333 ClutterLayoutInfo *linfo;
10334 ClutterActorBox old = { 0, };
10335
10336 linfo = _clutter_actor_get_layout_info (self);
10337
10338 if (priv->position_set && linfo->fixed_pos.x == x)
10339 return;
10340
10341 clutter_actor_store_old_geometry (self, &old);
10342
10343 linfo->fixed_pos.x = x;
10344 clutter_actor_set_fixed_position_set (self, TRUE);
10345
10346 clutter_actor_notify_if_geometry_changed (self, &old);
10347
10348 clutter_actor_queue_relayout (self);
10349 }
10350
10351 static inline void
clutter_actor_set_y_internal(ClutterActor * self,float y)10352 clutter_actor_set_y_internal (ClutterActor *self,
10353 float y)
10354 {
10355 ClutterActorPrivate *priv = self->priv;
10356 ClutterLayoutInfo *linfo;
10357 ClutterActorBox old = { 0, };
10358
10359 linfo = _clutter_actor_get_layout_info (self);
10360
10361 if (priv->position_set && linfo->fixed_pos.y == y)
10362 return;
10363
10364 clutter_actor_store_old_geometry (self, &old);
10365
10366 linfo->fixed_pos.y = y;
10367 clutter_actor_set_fixed_position_set (self, TRUE);
10368
10369 clutter_actor_notify_if_geometry_changed (self, &old);
10370
10371 clutter_actor_queue_relayout (self);
10372 }
10373
10374 static void
clutter_actor_set_position_internal(ClutterActor * self,const graphene_point_t * position)10375 clutter_actor_set_position_internal (ClutterActor *self,
10376 const graphene_point_t *position)
10377 {
10378 ClutterActorPrivate *priv = self->priv;
10379 ClutterLayoutInfo *linfo;
10380 ClutterActorBox old = { 0, };
10381
10382 linfo = _clutter_actor_get_layout_info (self);
10383
10384 if (priv->position_set &&
10385 graphene_point_equal (position, &linfo->fixed_pos))
10386 return;
10387
10388 clutter_actor_store_old_geometry (self, &old);
10389
10390 if (position != NULL)
10391 {
10392 linfo->fixed_pos = *position;
10393 clutter_actor_set_fixed_position_set (self, TRUE);
10394 }
10395 else
10396 clutter_actor_set_fixed_position_set (self, FALSE);
10397
10398 clutter_actor_notify_if_geometry_changed (self, &old);
10399
10400 clutter_actor_queue_relayout (self);
10401 }
10402
10403 /**
10404 * clutter_actor_set_x:
10405 * @self: a #ClutterActor
10406 * @x: the actor's position on the X axis
10407 *
10408 * Sets the actor's X coordinate, relative to its parent, in pixels.
10409 *
10410 * Overrides any layout manager and forces a fixed position for
10411 * the actor.
10412 *
10413 * The #ClutterActor:x property is animatable.
10414 *
10415 * Since: 0.6
10416 */
10417 void
clutter_actor_set_x(ClutterActor * self,gfloat x)10418 clutter_actor_set_x (ClutterActor *self,
10419 gfloat x)
10420 {
10421 float cur_position = clutter_actor_get_x (self);
10422
10423 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10424
10425 _clutter_actor_create_transition (self, obj_props[PROP_X],
10426 cur_position,
10427 x);
10428 }
10429
10430 /**
10431 * clutter_actor_set_y:
10432 * @self: a #ClutterActor
10433 * @y: the actor's position on the Y axis
10434 *
10435 * Sets the actor's Y coordinate, relative to its parent, in pixels.#
10436 *
10437 * Overrides any layout manager and forces a fixed position for
10438 * the actor.
10439 *
10440 * The #ClutterActor:y property is animatable.
10441 *
10442 * Since: 0.6
10443 */
10444 void
clutter_actor_set_y(ClutterActor * self,gfloat y)10445 clutter_actor_set_y (ClutterActor *self,
10446 gfloat y)
10447 {
10448 float cur_position = clutter_actor_get_y (self);
10449
10450 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10451
10452 _clutter_actor_create_transition (self, obj_props[PROP_Y],
10453 cur_position,
10454 y);
10455 }
10456
10457 /**
10458 * clutter_actor_get_x:
10459 * @self: A #ClutterActor
10460 *
10461 * Retrieves the X coordinate of a #ClutterActor.
10462 *
10463 * This function tries to "do what you mean", by returning the
10464 * correct value depending on the actor's state.
10465 *
10466 * If the actor has a valid allocation, this function will return
10467 * the X coordinate of the origin of the allocation box.
10468 *
10469 * If the actor has any fixed coordinate set using clutter_actor_set_x(),
10470 * clutter_actor_set_position(), this function will return that coordinate.
10471 *
10472 * If both the allocation and a fixed position are missing, this function
10473 * will return 0.
10474 *
10475 * Return value: the X coordinate, in pixels, ignoring any
10476 * transformation (i.e. scaling, rotation)
10477 */
10478 gfloat
clutter_actor_get_x(ClutterActor * self)10479 clutter_actor_get_x (ClutterActor *self)
10480 {
10481 ClutterActorPrivate *priv;
10482
10483 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10484
10485 priv = self->priv;
10486
10487 if (priv->needs_allocation)
10488 {
10489 if (priv->position_set)
10490 {
10491 const ClutterLayoutInfo *info;
10492
10493 info = _clutter_actor_get_layout_info_or_defaults (self);
10494
10495 return info->fixed_pos.x;
10496 }
10497 else
10498 return 0;
10499 }
10500 else
10501 return priv->allocation.x1;
10502 }
10503
10504 /**
10505 * clutter_actor_get_y:
10506 * @self: A #ClutterActor
10507 *
10508 * Retrieves the Y coordinate of a #ClutterActor.
10509 *
10510 * This function tries to "do what you mean", by returning the
10511 * correct value depending on the actor's state.
10512 *
10513 * If the actor has a valid allocation, this function will return
10514 * the Y coordinate of the origin of the allocation box.
10515 *
10516 * If the actor has any fixed coordinate set using clutter_actor_set_y(),
10517 * clutter_actor_set_position(), this function will return that coordinate.
10518 *
10519 * If both the allocation and a fixed position are missing, this function
10520 * will return 0.
10521 *
10522 * Return value: the Y coordinate, in pixels, ignoring any
10523 * transformation (i.e. scaling, rotation)
10524 */
10525 gfloat
clutter_actor_get_y(ClutterActor * self)10526 clutter_actor_get_y (ClutterActor *self)
10527 {
10528 ClutterActorPrivate *priv;
10529
10530 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10531
10532 priv = self->priv;
10533
10534 if (priv->needs_allocation)
10535 {
10536 if (priv->position_set)
10537 {
10538 const ClutterLayoutInfo *info;
10539
10540 info = _clutter_actor_get_layout_info_or_defaults (self);
10541
10542 return info->fixed_pos.y;
10543 }
10544 else
10545 return 0;
10546 }
10547 else
10548 return priv->allocation.y1;
10549 }
10550
10551 /**
10552 * clutter_actor_set_scale:
10553 * @self: A #ClutterActor
10554 * @scale_x: double factor to scale actor by horizontally.
10555 * @scale_y: double factor to scale actor by vertically.
10556 *
10557 * Scales an actor with the given factors.
10558 *
10559 * The scale transformation is relative the the #ClutterActor:pivot-point.
10560 *
10561 * The #ClutterActor:scale-x and #ClutterActor:scale-y properties are
10562 * animatable.
10563 *
10564 * Since: 0.2
10565 */
10566 void
clutter_actor_set_scale(ClutterActor * self,gdouble scale_x,gdouble scale_y)10567 clutter_actor_set_scale (ClutterActor *self,
10568 gdouble scale_x,
10569 gdouble scale_y)
10570 {
10571 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10572
10573 g_object_freeze_notify (G_OBJECT (self));
10574
10575 clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
10576 clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
10577
10578 g_object_thaw_notify (G_OBJECT (self));
10579 }
10580
10581 /**
10582 * clutter_actor_set_scale_z:
10583 * @self: a #ClutterActor
10584 * @scale_z: the scaling factor along the Z axis
10585 *
10586 * Scales an actor on the Z axis by the given @scale_z factor.
10587 *
10588 * The scale transformation is relative the the #ClutterActor:pivot-point.
10589 *
10590 * The #ClutterActor:scale-z property is animatable.
10591 *
10592 * Since: 1.12
10593 */
10594 void
clutter_actor_set_scale_z(ClutterActor * self,gdouble scale_z)10595 clutter_actor_set_scale_z (ClutterActor *self,
10596 gdouble scale_z)
10597 {
10598 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10599
10600 clutter_actor_set_scale_factor (self, CLUTTER_Z_AXIS, scale_z);
10601 }
10602
10603 /**
10604 * clutter_actor_get_scale:
10605 * @self: A #ClutterActor
10606 * @scale_x: (out) (allow-none): Location to store horizontal
10607 * scale factor, or %NULL.
10608 * @scale_y: (out) (allow-none): Location to store vertical
10609 * scale factor, or %NULL.
10610 *
10611 * Retrieves an actors scale factors.
10612 *
10613 * Since: 0.2
10614 */
10615 void
clutter_actor_get_scale(ClutterActor * self,gdouble * scale_x,gdouble * scale_y)10616 clutter_actor_get_scale (ClutterActor *self,
10617 gdouble *scale_x,
10618 gdouble *scale_y)
10619 {
10620 const ClutterTransformInfo *info;
10621
10622 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10623
10624 info = _clutter_actor_get_transform_info_or_defaults (self);
10625
10626 if (scale_x)
10627 *scale_x = info->scale_x;
10628
10629 if (scale_y)
10630 *scale_y = info->scale_y;
10631 }
10632
10633 /**
10634 * clutter_actor_get_scale_z:
10635 * @self: A #ClutterActor
10636 *
10637 * Retrieves the scaling factor along the Z axis, as set using
10638 * clutter_actor_set_scale_z().
10639 *
10640 * Return value: the scaling factor along the Z axis
10641 *
10642 * Since: 1.12
10643 */
10644 gdouble
clutter_actor_get_scale_z(ClutterActor * self)10645 clutter_actor_get_scale_z (ClutterActor *self)
10646 {
10647 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.0);
10648
10649 return _clutter_actor_get_transform_info_or_defaults (self)->scale_z;
10650 }
10651
10652 static inline void
clutter_actor_set_opacity_internal(ClutterActor * self,guint8 opacity)10653 clutter_actor_set_opacity_internal (ClutterActor *self,
10654 guint8 opacity)
10655 {
10656 ClutterActorPrivate *priv = self->priv;
10657
10658 if (priv->opacity != opacity)
10659 {
10660 priv->opacity = opacity;
10661
10662 /* Queue a redraw from the flatten effect so that it can use
10663 its cached image if available instead of having to redraw the
10664 actual actor. If it doesn't end up using the FBO then the
10665 effect is still able to continue the paint anyway. If there
10666 is no flatten effect yet then this is equivalent to queueing
10667 a full redraw */
10668 _clutter_actor_queue_redraw_full (self,
10669 NULL, /* clip */
10670 priv->flatten_effect);
10671
10672 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OPACITY]);
10673 }
10674 }
10675
10676 /**
10677 * clutter_actor_set_opacity:
10678 * @self: A #ClutterActor
10679 * @opacity: New opacity value for the actor.
10680 *
10681 * Sets the actor's opacity, with zero being completely transparent and
10682 * 255 (0xff) being fully opaque.
10683 *
10684 * The #ClutterActor:opacity property is animatable.
10685 */
10686 void
clutter_actor_set_opacity(ClutterActor * self,guint8 opacity)10687 clutter_actor_set_opacity (ClutterActor *self,
10688 guint8 opacity)
10689 {
10690 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10691
10692 _clutter_actor_create_transition (self, obj_props[PROP_OPACITY],
10693 self->priv->opacity,
10694 opacity);
10695 }
10696
10697 /*
10698 * clutter_actor_get_paint_opacity_internal:
10699 * @self: a #ClutterActor
10700 *
10701 * Retrieves the absolute opacity of the actor, as it appears on the stage
10702 *
10703 * This function does not do type checks
10704 *
10705 * Return value: the absolute opacity of the actor
10706 */
10707 static guint8
clutter_actor_get_paint_opacity_internal(ClutterActor * self)10708 clutter_actor_get_paint_opacity_internal (ClutterActor *self)
10709 {
10710 ClutterActorPrivate *priv = self->priv;
10711 ClutterActor *parent;
10712
10713 /* override the top-level opacity to always be 255; even in
10714 * case of ClutterStage:use-alpha being TRUE we want the rest
10715 * of the scene to be painted
10716 */
10717 if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
10718 return 255;
10719
10720 if (priv->opacity_override >= 0)
10721 return priv->opacity_override;
10722
10723 parent = priv->parent;
10724
10725 /* Factor in the actual actors opacity with parents */
10726 if (parent != NULL)
10727 {
10728 guint8 opacity = clutter_actor_get_paint_opacity_internal (parent);
10729
10730 if (opacity != 0xff)
10731 return (opacity * priv->opacity) / 0xff;
10732 }
10733
10734 return priv->opacity;
10735
10736 }
10737
10738 /**
10739 * clutter_actor_get_paint_opacity:
10740 * @self: A #ClutterActor
10741 *
10742 * Retrieves the absolute opacity of the actor, as it appears on the stage.
10743 *
10744 * This function traverses the hierarchy chain and composites the opacity of
10745 * the actor with that of its parents.
10746 *
10747 * This function is intended for subclasses to use in the paint virtual
10748 * function, to paint themselves with the correct opacity.
10749 *
10750 * Return value: The actor opacity value.
10751 *
10752 * Since: 0.8
10753 */
10754 guint8
clutter_actor_get_paint_opacity(ClutterActor * self)10755 clutter_actor_get_paint_opacity (ClutterActor *self)
10756 {
10757 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10758
10759 return clutter_actor_get_paint_opacity_internal (self);
10760 }
10761
10762 /**
10763 * clutter_actor_get_opacity:
10764 * @self: a #ClutterActor
10765 *
10766 * Retrieves the opacity value of an actor, as set by
10767 * clutter_actor_set_opacity().
10768 *
10769 * For retrieving the absolute opacity of the actor inside a paint
10770 * virtual function, see clutter_actor_get_paint_opacity().
10771 *
10772 * Return value: the opacity of the actor
10773 */
10774 guint8
clutter_actor_get_opacity(ClutterActor * self)10775 clutter_actor_get_opacity (ClutterActor *self)
10776 {
10777 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10778
10779 return self->priv->opacity;
10780 }
10781
10782 /**
10783 * clutter_actor_set_offscreen_redirect:
10784 * @self: A #ClutterActor
10785 * @redirect: New offscreen redirect flags for the actor.
10786 *
10787 * Defines the circumstances where the actor should be redirected into
10788 * an offscreen image. The offscreen image is used to flatten the
10789 * actor into a single image while painting for two main reasons.
10790 * Firstly, when the actor is painted a second time without any of its
10791 * contents changing it can simply repaint the cached image without
10792 * descending further down the actor hierarchy. Secondly, it will make
10793 * the opacity look correct even if there are overlapping primitives
10794 * in the actor.
10795 *
10796 * Caching the actor could in some cases be a performance win and in
10797 * some cases be a performance lose so it is important to determine
10798 * which value is right for an actor before modifying this value. For
10799 * example, there is never any reason to flatten an actor that is just
10800 * a single texture (such as a #ClutterTexture) because it is
10801 * effectively already cached in an image so the offscreen would be
10802 * redundant. Also if the actor contains primitives that are far apart
10803 * with a large transparent area in the middle (such as a large
10804 * CluterGroup with a small actor in the top left and a small actor in
10805 * the bottom right) then the cached image will contain the entire
10806 * image of the large area and the paint will waste time blending all
10807 * of the transparent pixels in the middle.
10808 *
10809 * The default method of implementing opacity on a container simply
10810 * forwards on the opacity to all of the children. If the children are
10811 * overlapping then it will appear as if they are two separate glassy
10812 * objects and there will be a break in the color where they
10813 * overlap. By redirecting to an offscreen buffer it will be as if the
10814 * two opaque objects are combined into one and then made transparent
10815 * which is usually what is expected.
10816 *
10817 * The image below demonstrates the difference between redirecting and
10818 * not. The image shows two Clutter groups, each containing a red and
10819 * a green rectangle which overlap. The opacity on the group is set to
10820 * 128 (which is 50%). When the offscreen redirect is not used, the
10821 * red rectangle can be seen through the blue rectangle as if the two
10822 * rectangles were separately transparent. When the redirect is used
10823 * the group as a whole is transparent instead so the red rectangle is
10824 * not visible where they overlap.
10825 *
10826 * <figure id="offscreen-redirect">
10827 * <title>Sample of using an offscreen redirect for transparency</title>
10828 * <graphic fileref="offscreen-redirect.png" format="PNG"/>
10829 * </figure>
10830 *
10831 * The default value for this property is 0, so we effectively will
10832 * never redirect an actor offscreen by default. This means that there
10833 * are times that transparent actors may look glassy as described
10834 * above. The reason this is the default is because there is a
10835 * performance trade off between quality and performance here. In many
10836 * cases the default form of glassy opacity looks good enough, but if
10837 * it's not you will need to set the
10838 * %CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY flag to enable
10839 * redirection for opacity.
10840 *
10841 * Custom actors that don't contain any overlapping primitives are
10842 * recommended to override the has_overlaps() virtual to return %FALSE
10843 * for maximum efficiency.
10844 *
10845 * Since: 1.8
10846 */
10847 void
clutter_actor_set_offscreen_redirect(ClutterActor * self,ClutterOffscreenRedirect redirect)10848 clutter_actor_set_offscreen_redirect (ClutterActor *self,
10849 ClutterOffscreenRedirect redirect)
10850 {
10851 ClutterActorPrivate *priv;
10852
10853 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10854
10855 priv = self->priv;
10856
10857 if (priv->offscreen_redirect != redirect)
10858 {
10859 priv->offscreen_redirect = redirect;
10860
10861 /* Queue a redraw from the effect so that it can use its cached
10862 image if available instead of having to redraw the actual
10863 actor. If it doesn't end up using the FBO then the effect is
10864 still able to continue the paint anyway. If there is no
10865 effect then this is equivalent to queuing a full redraw */
10866 _clutter_actor_queue_redraw_full (self,
10867 NULL, /* clip */
10868 priv->flatten_effect);
10869
10870 g_object_notify_by_pspec (G_OBJECT (self),
10871 obj_props[PROP_OFFSCREEN_REDIRECT]);
10872 }
10873 }
10874
10875 /**
10876 * clutter_actor_get_offscreen_redirect:
10877 * @self: a #ClutterActor
10878 *
10879 * Retrieves whether to redirect the actor to an offscreen buffer, as
10880 * set by clutter_actor_set_offscreen_redirect().
10881 *
10882 * Return value: the value of the offscreen-redirect property of the actor
10883 *
10884 * Since: 1.8
10885 */
10886 ClutterOffscreenRedirect
clutter_actor_get_offscreen_redirect(ClutterActor * self)10887 clutter_actor_get_offscreen_redirect (ClutterActor *self)
10888 {
10889 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10890
10891 return self->priv->offscreen_redirect;
10892 }
10893
10894 /**
10895 * clutter_actor_set_name:
10896 * @self: A #ClutterActor
10897 * @name: Textual tag to apply to actor
10898 *
10899 * Sets the given name to @self. The name can be used to identify
10900 * a #ClutterActor.
10901 */
10902 void
clutter_actor_set_name(ClutterActor * self,const gchar * name)10903 clutter_actor_set_name (ClutterActor *self,
10904 const gchar *name)
10905 {
10906 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10907
10908 g_free (self->priv->name);
10909 self->priv->name = g_strdup (name);
10910
10911 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
10912 }
10913
10914 /**
10915 * clutter_actor_get_name:
10916 * @self: A #ClutterActor
10917 *
10918 * Retrieves the name of @self.
10919 *
10920 * Return value: the name of the actor, or %NULL. The returned string is
10921 * owned by the actor and should not be modified or freed.
10922 */
10923 const gchar *
clutter_actor_get_name(ClutterActor * self)10924 clutter_actor_get_name (ClutterActor *self)
10925 {
10926 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
10927
10928 return self->priv->name;
10929 }
10930
10931 static inline void
clutter_actor_set_z_position_internal(ClutterActor * self,float z_position)10932 clutter_actor_set_z_position_internal (ClutterActor *self,
10933 float z_position)
10934 {
10935 ClutterTransformInfo *info;
10936
10937 info = _clutter_actor_get_transform_info (self);
10938
10939 if (memcmp (&info->z_position, &z_position, sizeof (float)) != 0)
10940 {
10941 info->z_position = z_position;
10942
10943 transform_changed (self);
10944
10945 clutter_actor_queue_redraw (self);
10946
10947 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Z_POSITION]);
10948 }
10949 }
10950
10951 /**
10952 * clutter_actor_set_z_position:
10953 * @self: a #ClutterActor
10954 * @z_position: the position on the Z axis
10955 *
10956 * Sets the actor's position on the Z axis.
10957 *
10958 * See #ClutterActor:z-position.
10959 *
10960 * Since: 1.12
10961 */
10962 void
clutter_actor_set_z_position(ClutterActor * self,gfloat z_position)10963 clutter_actor_set_z_position (ClutterActor *self,
10964 gfloat z_position)
10965 {
10966 const ClutterTransformInfo *info;
10967
10968 g_return_if_fail (CLUTTER_IS_ACTOR (self));
10969
10970 info = _clutter_actor_get_transform_info_or_defaults (self);
10971
10972 _clutter_actor_create_transition (self, obj_props[PROP_Z_POSITION],
10973 info->z_position,
10974 z_position);
10975 }
10976
10977 /**
10978 * clutter_actor_get_z_position:
10979 * @self: a #ClutterActor
10980 *
10981 * Retrieves the actor's position on the Z axis.
10982 *
10983 * Return value: the position on the Z axis.
10984 *
10985 * Since: 1.12
10986 */
10987 gfloat
clutter_actor_get_z_position(ClutterActor * self)10988 clutter_actor_get_z_position (ClutterActor *self)
10989 {
10990 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
10991
10992 return _clutter_actor_get_transform_info_or_defaults (self)->z_position;
10993 }
10994
10995 /**
10996 * clutter_actor_set_pivot_point:
10997 * @self: a #ClutterActor
10998 * @pivot_x: the normalized X coordinate of the pivot point
10999 * @pivot_y: the normalized Y coordinate of the pivot point
11000 *
11001 * Sets the position of the #ClutterActor:pivot-point around which the
11002 * scaling and rotation transformations occur.
11003 *
11004 * The pivot point's coordinates are in normalized space, with the (0, 0)
11005 * point being the top left corner of the actor, and the (1, 1) point being
11006 * the bottom right corner.
11007 *
11008 * Since: 1.12
11009 */
11010 void
clutter_actor_set_pivot_point(ClutterActor * self,gfloat pivot_x,gfloat pivot_y)11011 clutter_actor_set_pivot_point (ClutterActor *self,
11012 gfloat pivot_x,
11013 gfloat pivot_y)
11014 {
11015 graphene_point_t pivot = GRAPHENE_POINT_INIT (pivot_x, pivot_y);
11016 const ClutterTransformInfo *info;
11017
11018 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11019
11020 info = _clutter_actor_get_transform_info_or_defaults (self);
11021 _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT],
11022 &info->pivot,
11023 &pivot);
11024 }
11025
11026 /**
11027 * clutter_actor_get_pivot_point:
11028 * @self: a #ClutterActor
11029 * @pivot_x: (out) (allow-none): return location for the normalized X
11030 * coordinate of the pivot point, or %NULL
11031 * @pivot_y: (out) (allow-none): return location for the normalized Y
11032 * coordinate of the pivot point, or %NULL
11033 *
11034 * Retrieves the coordinates of the #ClutterActor:pivot-point.
11035 *
11036 * Since: 1.12
11037 */
11038 void
clutter_actor_get_pivot_point(ClutterActor * self,gfloat * pivot_x,gfloat * pivot_y)11039 clutter_actor_get_pivot_point (ClutterActor *self,
11040 gfloat *pivot_x,
11041 gfloat *pivot_y)
11042 {
11043 const ClutterTransformInfo *info;
11044
11045 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11046
11047 info = _clutter_actor_get_transform_info_or_defaults (self);
11048
11049 if (pivot_x != NULL)
11050 *pivot_x = info->pivot.x;
11051
11052 if (pivot_y != NULL)
11053 *pivot_y = info->pivot.y;
11054 }
11055
11056 /**
11057 * clutter_actor_set_pivot_point_z:
11058 * @self: a #ClutterActor
11059 * @pivot_z: the Z coordinate of the actor's pivot point
11060 *
11061 * Sets the component on the Z axis of the #ClutterActor:pivot-point around
11062 * which the scaling and rotation transformations occur.
11063 *
11064 * The @pivot_z value is expressed as a distance along the Z axis.
11065 *
11066 * Since: 1.12
11067 */
11068 void
clutter_actor_set_pivot_point_z(ClutterActor * self,gfloat pivot_z)11069 clutter_actor_set_pivot_point_z (ClutterActor *self,
11070 gfloat pivot_z)
11071 {
11072 const ClutterTransformInfo *info;
11073
11074 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11075
11076 info = _clutter_actor_get_transform_info_or_defaults (self);
11077 _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT_Z],
11078 info->pivot_z,
11079 pivot_z);
11080 }
11081
11082 /**
11083 * clutter_actor_get_pivot_point_z:
11084 * @self: a #ClutterActor
11085 *
11086 * Retrieves the Z component of the #ClutterActor:pivot-point.
11087 *
11088 * Since: 1.12
11089 */
11090 gfloat
clutter_actor_get_pivot_point_z(ClutterActor * self)11091 clutter_actor_get_pivot_point_z (ClutterActor *self)
11092 {
11093 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
11094
11095 return _clutter_actor_get_transform_info_or_defaults (self)->pivot_z;
11096 }
11097
11098 /**
11099 * clutter_actor_set_clip:
11100 * @self: A #ClutterActor
11101 * @xoff: X offset of the clip rectangle
11102 * @yoff: Y offset of the clip rectangle
11103 * @width: Width of the clip rectangle
11104 * @height: Height of the clip rectangle
11105 *
11106 * Sets clip area for @self. The clip area is always computed from the
11107 * upper left corner of the actor.
11108 *
11109 * Since: 0.6
11110 */
11111 void
clutter_actor_set_clip(ClutterActor * self,gfloat xoff,gfloat yoff,gfloat width,gfloat height)11112 clutter_actor_set_clip (ClutterActor *self,
11113 gfloat xoff,
11114 gfloat yoff,
11115 gfloat width,
11116 gfloat height)
11117 {
11118 ClutterActorPrivate *priv;
11119 GObject *obj;
11120
11121 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11122
11123 priv = self->priv;
11124
11125 if (priv->has_clip &&
11126 priv->clip.origin.x == xoff &&
11127 priv->clip.origin.y == yoff &&
11128 priv->clip.size.width == width &&
11129 priv->clip.size.height == height)
11130 return;
11131
11132 obj = G_OBJECT (self);
11133
11134 priv->clip.origin.x = xoff;
11135 priv->clip.origin.y = yoff;
11136 priv->clip.size.width = width;
11137 priv->clip.size.height = height;
11138
11139 priv->has_clip = TRUE;
11140
11141 queue_update_paint_volume (self);
11142 clutter_actor_queue_redraw (self);
11143
11144 g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
11145 g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
11146 }
11147
11148 /**
11149 * clutter_actor_remove_clip:
11150 * @self: A #ClutterActor
11151 *
11152 * Removes clip area from @self.
11153 */
11154 void
clutter_actor_remove_clip(ClutterActor * self)11155 clutter_actor_remove_clip (ClutterActor *self)
11156 {
11157 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11158
11159 if (!self->priv->has_clip)
11160 return;
11161
11162 self->priv->has_clip = FALSE;
11163
11164 queue_update_paint_volume (self);
11165 clutter_actor_queue_redraw (self);
11166
11167 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
11168 }
11169
11170 /**
11171 * clutter_actor_has_clip:
11172 * @self: a #ClutterActor
11173 *
11174 * Determines whether the actor has a clip area set or not.
11175 *
11176 * Return value: %TRUE if the actor has a clip area set.
11177 *
11178 * Since: 0.2
11179 */
11180 gboolean
clutter_actor_has_clip(ClutterActor * self)11181 clutter_actor_has_clip (ClutterActor *self)
11182 {
11183 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
11184
11185 return self->priv->has_clip;
11186 }
11187
11188 /**
11189 * clutter_actor_get_clip:
11190 * @self: a #ClutterActor
11191 * @xoff: (out) (allow-none): return location for the X offset of
11192 * the clip rectangle, or %NULL
11193 * @yoff: (out) (allow-none): return location for the Y offset of
11194 * the clip rectangle, or %NULL
11195 * @width: (out) (allow-none): return location for the width of
11196 * the clip rectangle, or %NULL
11197 * @height: (out) (allow-none): return location for the height of
11198 * the clip rectangle, or %NULL
11199 *
11200 * Gets the clip area for @self, if any is set.
11201 *
11202 * Since: 0.6
11203 */
11204 void
clutter_actor_get_clip(ClutterActor * self,gfloat * xoff,gfloat * yoff,gfloat * width,gfloat * height)11205 clutter_actor_get_clip (ClutterActor *self,
11206 gfloat *xoff,
11207 gfloat *yoff,
11208 gfloat *width,
11209 gfloat *height)
11210 {
11211 ClutterActorPrivate *priv;
11212
11213 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11214
11215 priv = self->priv;
11216
11217 if (!priv->has_clip)
11218 return;
11219
11220 if (xoff != NULL)
11221 *xoff = priv->clip.origin.x;
11222
11223 if (yoff != NULL)
11224 *yoff = priv->clip.origin.y;
11225
11226 if (width != NULL)
11227 *width = priv->clip.size.width;
11228
11229 if (height != NULL)
11230 *height = priv->clip.size.height;
11231 }
11232
11233 /**
11234 * clutter_actor_get_children:
11235 * @self: a #ClutterActor
11236 *
11237 * Retrieves the list of children of @self.
11238 *
11239 * Return value: (transfer container) (element-type ClutterActor): A newly
11240 * allocated #GList of #ClutterActor<!-- -->s. Use g_list_free() when
11241 * done.
11242 *
11243 * Since: 1.10
11244 */
11245 GList *
clutter_actor_get_children(ClutterActor * self)11246 clutter_actor_get_children (ClutterActor *self)
11247 {
11248 ClutterActor *iter;
11249 GList *res;
11250
11251 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
11252
11253 /* we walk the list backward so that we can use prepend(),
11254 * which is O(1)
11255 */
11256 for (iter = self->priv->last_child, res = NULL;
11257 iter != NULL;
11258 iter = iter->priv->prev_sibling)
11259 {
11260 res = g_list_prepend (res, iter);
11261 }
11262
11263 return res;
11264 }
11265
11266 /*< private >
11267 * insert_child_at_depth:
11268 * @self: a #ClutterActor
11269 * @child: a #ClutterActor
11270 *
11271 * Inserts @child inside the list of children held by @self, using
11272 * the depth as the insertion criteria.
11273 *
11274 * This sadly makes the insertion not O(1), but we can keep the
11275 * list sorted so that the painters algorithm we use for painting
11276 * the children will work correctly.
11277 */
11278 static void
insert_child_at_depth(ClutterActor * self,ClutterActor * child,gpointer dummy G_GNUC_UNUSED)11279 insert_child_at_depth (ClutterActor *self,
11280 ClutterActor *child,
11281 gpointer dummy G_GNUC_UNUSED)
11282 {
11283 ClutterActor *iter;
11284 float child_depth;
11285
11286 child->priv->parent = self;
11287
11288 child_depth =
11289 _clutter_actor_get_transform_info_or_defaults (child)->z_position;
11290
11291 /* special-case the first child */
11292 if (self->priv->n_children == 0)
11293 {
11294 self->priv->first_child = child;
11295 self->priv->last_child = child;
11296
11297 child->priv->next_sibling = NULL;
11298 child->priv->prev_sibling = NULL;
11299
11300 return;
11301 }
11302
11303 /* Find the right place to insert the child so that it will still be
11304 sorted and the child will be after all of the actors at the same
11305 dept */
11306 for (iter = self->priv->first_child;
11307 iter != NULL;
11308 iter = iter->priv->next_sibling)
11309 {
11310 float iter_depth;
11311
11312 iter_depth =
11313 _clutter_actor_get_transform_info_or_defaults (iter)->z_position;
11314
11315 if (iter_depth > child_depth)
11316 break;
11317 }
11318
11319 if (iter != NULL)
11320 {
11321 ClutterActor *tmp = iter->priv->prev_sibling;
11322
11323 if (tmp != NULL)
11324 tmp->priv->next_sibling = child;
11325
11326 /* Insert the node before the found one */
11327 child->priv->prev_sibling = iter->priv->prev_sibling;
11328 child->priv->next_sibling = iter;
11329 iter->priv->prev_sibling = child;
11330 }
11331 else
11332 {
11333 ClutterActor *tmp = self->priv->last_child;
11334
11335 if (tmp != NULL)
11336 tmp->priv->next_sibling = child;
11337
11338 /* insert the node at the end of the list */
11339 child->priv->prev_sibling = self->priv->last_child;
11340 child->priv->next_sibling = NULL;
11341 }
11342
11343 if (child->priv->prev_sibling == NULL)
11344 self->priv->first_child = child;
11345
11346 if (child->priv->next_sibling == NULL)
11347 self->priv->last_child = child;
11348 }
11349
11350 static void
insert_child_at_index(ClutterActor * self,ClutterActor * child,gpointer data_)11351 insert_child_at_index (ClutterActor *self,
11352 ClutterActor *child,
11353 gpointer data_)
11354 {
11355 gint index_ = GPOINTER_TO_INT (data_);
11356
11357 child->priv->parent = self;
11358
11359 if (index_ == 0)
11360 {
11361 ClutterActor *tmp = self->priv->first_child;
11362
11363 if (tmp != NULL)
11364 tmp->priv->prev_sibling = child;
11365
11366 child->priv->prev_sibling = NULL;
11367 child->priv->next_sibling = tmp;
11368 }
11369 else if (index_ < 0 || index_ >= self->priv->n_children)
11370 {
11371 ClutterActor *tmp = self->priv->last_child;
11372
11373 if (tmp != NULL)
11374 tmp->priv->next_sibling = child;
11375
11376 child->priv->prev_sibling = tmp;
11377 child->priv->next_sibling = NULL;
11378 }
11379 else
11380 {
11381 ClutterActor *iter;
11382 int i;
11383
11384 for (iter = self->priv->first_child, i = 0;
11385 iter != NULL;
11386 iter = iter->priv->next_sibling, i += 1)
11387 {
11388 if (index_ == i)
11389 {
11390 ClutterActor *tmp = iter->priv->prev_sibling;
11391
11392 child->priv->prev_sibling = tmp;
11393 child->priv->next_sibling = iter;
11394
11395 iter->priv->prev_sibling = child;
11396
11397 if (tmp != NULL)
11398 tmp->priv->next_sibling = child;
11399
11400 break;
11401 }
11402 }
11403 }
11404
11405 if (child->priv->prev_sibling == NULL)
11406 self->priv->first_child = child;
11407
11408 if (child->priv->next_sibling == NULL)
11409 self->priv->last_child = child;
11410 }
11411
11412 static void
insert_child_above(ClutterActor * self,ClutterActor * child,gpointer data)11413 insert_child_above (ClutterActor *self,
11414 ClutterActor *child,
11415 gpointer data)
11416 {
11417 ClutterActor *sibling = data;
11418
11419 child->priv->parent = self;
11420
11421 if (sibling == NULL)
11422 sibling = self->priv->last_child;
11423
11424 child->priv->prev_sibling = sibling;
11425
11426 if (sibling != NULL)
11427 {
11428 ClutterActor *tmp = sibling->priv->next_sibling;
11429
11430 child->priv->next_sibling = tmp;
11431
11432 if (tmp != NULL)
11433 tmp->priv->prev_sibling = child;
11434
11435 sibling->priv->next_sibling = child;
11436 }
11437 else
11438 child->priv->next_sibling = NULL;
11439
11440 if (child->priv->prev_sibling == NULL)
11441 self->priv->first_child = child;
11442
11443 if (child->priv->next_sibling == NULL)
11444 self->priv->last_child = child;
11445 }
11446
11447 static void
insert_child_below(ClutterActor * self,ClutterActor * child,gpointer data)11448 insert_child_below (ClutterActor *self,
11449 ClutterActor *child,
11450 gpointer data)
11451 {
11452 ClutterActor *sibling = data;
11453
11454 child->priv->parent = self;
11455
11456 if (sibling == NULL)
11457 sibling = self->priv->first_child;
11458
11459 child->priv->next_sibling = sibling;
11460
11461 if (sibling != NULL)
11462 {
11463 ClutterActor *tmp = sibling->priv->prev_sibling;
11464
11465 child->priv->prev_sibling = tmp;
11466
11467 if (tmp != NULL)
11468 tmp->priv->next_sibling = child;
11469
11470 sibling->priv->prev_sibling = child;
11471 }
11472 else
11473 child->priv->prev_sibling = NULL;
11474
11475 if (child->priv->prev_sibling == NULL)
11476 self->priv->first_child = child;
11477
11478 if (child->priv->next_sibling == NULL)
11479 self->priv->last_child = child;
11480 }
11481
11482 typedef void (* ClutterActorAddChildFunc) (ClutterActor *parent,
11483 ClutterActor *child,
11484 gpointer data);
11485
11486 typedef enum
11487 {
11488 ADD_CHILD_CREATE_META = 1 << 0,
11489 ADD_CHILD_EMIT_PARENT_SET = 1 << 1,
11490 ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2,
11491 ADD_CHILD_CHECK_STATE = 1 << 3,
11492 ADD_CHILD_NOTIFY_FIRST_LAST = 1 << 4,
11493 ADD_CHILD_SHOW_ON_SET_PARENT = 1 << 5,
11494
11495 /* default flags for public API */
11496 ADD_CHILD_DEFAULT_FLAGS = ADD_CHILD_CREATE_META |
11497 ADD_CHILD_EMIT_PARENT_SET |
11498 ADD_CHILD_EMIT_ACTOR_ADDED |
11499 ADD_CHILD_CHECK_STATE |
11500 ADD_CHILD_NOTIFY_FIRST_LAST |
11501 ADD_CHILD_SHOW_ON_SET_PARENT,
11502 } ClutterActorAddChildFlags;
11503
11504 /*< private >
11505 * clutter_actor_add_child_internal:
11506 * @self: a #ClutterActor
11507 * @child: a #ClutterActor
11508 * @flags: control flags for actions
11509 * @add_func: delegate function
11510 * @data: (closure): data to pass to @add_func
11511 *
11512 * Adds @child to the list of children of @self.
11513 *
11514 * The actual insertion inside the list is delegated to @add_func: this
11515 * function will just set up the state, perform basic checks, and emit
11516 * signals.
11517 *
11518 * The @flags argument is used to perform additional operations.
11519 */
11520 static inline void
clutter_actor_add_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorAddChildFlags flags,ClutterActorAddChildFunc add_func,gpointer data)11521 clutter_actor_add_child_internal (ClutterActor *self,
11522 ClutterActor *child,
11523 ClutterActorAddChildFlags flags,
11524 ClutterActorAddChildFunc add_func,
11525 gpointer data)
11526 {
11527 ClutterTextDirection text_dir;
11528 gboolean create_meta;
11529 gboolean emit_parent_set, emit_actor_added;
11530 gboolean check_state;
11531 gboolean notify_first_last;
11532 gboolean show_on_set_parent;
11533 ClutterActor *old_first_child, *old_last_child;
11534 GObject *obj;
11535
11536 if (self == child)
11537 {
11538 g_warning ("Cannot add the actor '%s' to itself.",
11539 _clutter_actor_get_debug_name (self));
11540 return;
11541 }
11542
11543 if (child->priv->parent != NULL)
11544 {
11545 g_warning ("The actor '%s' already has a parent, '%s'. You must "
11546 "use clutter_actor_remove_child() first.",
11547 _clutter_actor_get_debug_name (child),
11548 _clutter_actor_get_debug_name (child->priv->parent));
11549 return;
11550 }
11551
11552 if (CLUTTER_ACTOR_IS_TOPLEVEL (child))
11553 {
11554 g_warning ("The actor '%s' is a top-level actor, and cannot be "
11555 "a child of another actor.",
11556 _clutter_actor_get_debug_name (child));
11557 return;
11558 }
11559
11560 /* the following check disallows calling methods that change the stacking
11561 * order within the destruction sequence, by triggering a critical
11562 * warning first, and leaving the actor in an undefined state, which
11563 * then ends up being caught by an assertion.
11564 *
11565 * the reproducible sequence is:
11566 *
11567 * - actor gets destroyed;
11568 * - another actor, linked to the first, will try to change the
11569 * stacking order of the first actor;
11570 * - changing the stacking order is a composite operation composed
11571 * by the following steps:
11572 * 1. ref() the child;
11573 * 2. remove_child_internal(), which removes the reference;
11574 * 3. add_child_internal(), which adds a reference;
11575 * - the state of the actor is not changed between (2) and (3), as
11576 * it could be an expensive recomputation;
11577 * - if (3) bails out, then the actor is in an undefined state, but
11578 * still alive;
11579 * - the destruction sequence terminates, but the actor is unparented
11580 * while its state indicates being parented instead.
11581 * - assertion failure.
11582 *
11583 * the obvious fix would be to decompose each set_child_*_sibling()
11584 * method into proper remove_child()/add_child(), with state validation;
11585 * this may cause excessive work, though, and trigger a cascade of other
11586 * bugs in code that assumes that a change in the stacking order is an
11587 * atomic operation.
11588 *
11589 * another potential fix is to just remove this check here, and let
11590 * code doing stacking order changes inside the destruction sequence
11591 * of an actor continue doing the stacking changes as before; this
11592 * option still performs more work than necessary.
11593 *
11594 * the third fix is to silently bail out early from every
11595 * set_child_*_sibling() and set_child_at_index() method, and avoid
11596 * doing stack changes altogether; Clutter implements this last option.
11597 *
11598 * see bug: https://bugzilla.gnome.org/show_bug.cgi?id=670647
11599 */
11600 if (CLUTTER_ACTOR_IN_DESTRUCTION (child))
11601 {
11602 g_warning ("The actor '%s' is currently being destroyed, and "
11603 "cannot be added as a child of another actor.",
11604 _clutter_actor_get_debug_name (child));
11605 return;
11606 }
11607
11608 create_meta = (flags & ADD_CHILD_CREATE_META) != 0;
11609 emit_parent_set = (flags & ADD_CHILD_EMIT_PARENT_SET) != 0;
11610 emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0;
11611 check_state = (flags & ADD_CHILD_CHECK_STATE) != 0;
11612 notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0;
11613 show_on_set_parent = (flags & ADD_CHILD_SHOW_ON_SET_PARENT) != 0;
11614
11615 old_first_child = self->priv->first_child;
11616 old_last_child = self->priv->last_child;
11617
11618 obj = G_OBJECT (self);
11619 g_object_freeze_notify (obj);
11620
11621 if (create_meta)
11622 clutter_container_create_child_meta (CLUTTER_CONTAINER (self), child);
11623
11624 g_object_ref_sink (child);
11625 child->priv->parent = NULL;
11626 child->priv->next_sibling = NULL;
11627 child->priv->prev_sibling = NULL;
11628
11629 /* delegate the actual insertion */
11630 add_func (self, child, data);
11631
11632 g_assert (child->priv->parent == self);
11633
11634 self->priv->n_children += 1;
11635
11636 self->priv->age += 1;
11637
11638 if (self->priv->in_cloned_branch)
11639 clutter_actor_push_in_cloned_branch (child, self->priv->in_cloned_branch);
11640
11641 if (self->priv->unmapped_paint_branch_counter)
11642 push_in_paint_unmapped_branch (child, self->priv->unmapped_paint_branch_counter);
11643
11644 /* children may cause their parent to expand, if they are set
11645 * to expand; if a child is not expanded then it cannot change
11646 * its parent's state. any further change later on will queue
11647 * an expand state check.
11648 *
11649 * this check, with the initial state of the needs_compute_expand
11650 * flag set to FALSE, should avoid recomputing the expand flags
11651 * state while building the actor tree.
11652 */
11653 if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
11654 (child->priv->needs_compute_expand ||
11655 child->priv->needs_x_expand ||
11656 child->priv->needs_y_expand))
11657 {
11658 clutter_actor_queue_compute_expand (self);
11659 }
11660
11661 if (emit_parent_set)
11662 g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
11663
11664 if (check_state)
11665 {
11666 /* If parent is mapped or realized, we need to also be mapped or
11667 * realized once we're inside the parent.
11668 */
11669 clutter_actor_update_map_state (child, MAP_STATE_CHECK);
11670
11671 /* propagate the parent's text direction to the child */
11672 text_dir = clutter_actor_get_text_direction (self);
11673 clutter_actor_set_text_direction (child, text_dir);
11674 }
11675
11676 /* this may end up queueing a redraw, in case the actor is
11677 * not visible but the show-on-set-parent property is still
11678 * set.
11679 *
11680 * XXX:2.0 - remove this check and unconditionally show() the
11681 * actor once we remove the show-on-set-parent property
11682 */
11683 if (show_on_set_parent && child->priv->show_on_set_parent)
11684 clutter_actor_show (child);
11685
11686 /* on the other hand, this will catch any other case where
11687 * the actor is supposed to be visible when it's added
11688 */
11689 if (CLUTTER_ACTOR_IS_MAPPED (child))
11690 clutter_actor_queue_redraw (child);
11691
11692 if (emit_actor_added)
11693 _clutter_container_emit_actor_added (CLUTTER_CONTAINER (self), child);
11694
11695 if (notify_first_last)
11696 {
11697 if (old_first_child != self->priv->first_child)
11698 g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
11699
11700 if (old_last_child != self->priv->last_child)
11701 g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
11702 }
11703
11704 g_object_thaw_notify (obj);
11705 }
11706
11707 /**
11708 * clutter_actor_add_child:
11709 * @self: a #ClutterActor
11710 * @child: a #ClutterActor
11711 *
11712 * Adds @child to the children of @self.
11713 *
11714 * This function will acquire a reference on @child that will only
11715 * be released when calling clutter_actor_remove_child().
11716 *
11717 * This function will take into consideration the #ClutterActor:depth
11718 * of @child, and will keep the list of children sorted.
11719 *
11720 * This function will emit the #ClutterContainer::actor-added signal
11721 * on @self.
11722 *
11723 * Since: 1.10
11724 */
11725 void
clutter_actor_add_child(ClutterActor * self,ClutterActor * child)11726 clutter_actor_add_child (ClutterActor *self,
11727 ClutterActor *child)
11728 {
11729 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11730 g_return_if_fail (CLUTTER_IS_ACTOR (child));
11731 g_return_if_fail (self != child);
11732 g_return_if_fail (child->priv->parent == NULL);
11733
11734 clutter_actor_add_child_internal (self, child,
11735 ADD_CHILD_DEFAULT_FLAGS,
11736 insert_child_at_depth,
11737 NULL);
11738 }
11739
11740 /**
11741 * clutter_actor_insert_child_at_index:
11742 * @self: a #ClutterActor
11743 * @child: a #ClutterActor
11744 * @index_: the index
11745 *
11746 * Inserts @child into the list of children of @self, using the
11747 * given @index_. If @index_ is greater than the number of children
11748 * in @self, or is less than 0, then the new child is added at the end.
11749 *
11750 * This function will acquire a reference on @child that will only
11751 * be released when calling clutter_actor_remove_child().
11752 *
11753 * This function will not take into consideration the #ClutterActor:depth
11754 * of @child.
11755 *
11756 * This function will emit the #ClutterContainer::actor-added signal
11757 * on @self.
11758 *
11759 * Since: 1.10
11760 */
11761 void
clutter_actor_insert_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)11762 clutter_actor_insert_child_at_index (ClutterActor *self,
11763 ClutterActor *child,
11764 gint index_)
11765 {
11766 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11767 g_return_if_fail (CLUTTER_IS_ACTOR (child));
11768 g_return_if_fail (self != child);
11769 g_return_if_fail (child->priv->parent == NULL);
11770
11771 clutter_actor_add_child_internal (self, child,
11772 ADD_CHILD_DEFAULT_FLAGS,
11773 insert_child_at_index,
11774 GINT_TO_POINTER (index_));
11775 }
11776
11777 /**
11778 * clutter_actor_insert_child_above:
11779 * @self: a #ClutterActor
11780 * @child: a #ClutterActor
11781 * @sibling: (allow-none): a child of @self, or %NULL
11782 *
11783 * Inserts @child into the list of children of @self, above another
11784 * child of @self or, if @sibling is %NULL, above all the children
11785 * of @self.
11786 *
11787 * This function will acquire a reference on @child that will only
11788 * be released when calling clutter_actor_remove_child().
11789 *
11790 * This function will not take into consideration the #ClutterActor:depth
11791 * of @child.
11792 *
11793 * This function will emit the #ClutterContainer::actor-added signal
11794 * on @self.
11795 *
11796 * Since: 1.10
11797 */
11798 void
clutter_actor_insert_child_above(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)11799 clutter_actor_insert_child_above (ClutterActor *self,
11800 ClutterActor *child,
11801 ClutterActor *sibling)
11802 {
11803 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11804 g_return_if_fail (CLUTTER_IS_ACTOR (child));
11805 g_return_if_fail (self != child);
11806 g_return_if_fail (child != sibling);
11807 g_return_if_fail (child->priv->parent == NULL);
11808 g_return_if_fail (sibling == NULL ||
11809 (CLUTTER_IS_ACTOR (sibling) &&
11810 sibling->priv->parent == self));
11811
11812 clutter_actor_add_child_internal (self, child,
11813 ADD_CHILD_DEFAULT_FLAGS,
11814 insert_child_above,
11815 sibling);
11816 }
11817
11818 /**
11819 * clutter_actor_insert_child_below:
11820 * @self: a #ClutterActor
11821 * @child: a #ClutterActor
11822 * @sibling: (allow-none): a child of @self, or %NULL
11823 *
11824 * Inserts @child into the list of children of @self, below another
11825 * child of @self or, if @sibling is %NULL, below all the children
11826 * of @self.
11827 *
11828 * This function will acquire a reference on @child that will only
11829 * be released when calling clutter_actor_remove_child().
11830 *
11831 * This function will not take into consideration the #ClutterActor:depth
11832 * of @child.
11833 *
11834 * This function will emit the #ClutterContainer::actor-added signal
11835 * on @self.
11836 *
11837 * Since: 1.10
11838 */
11839 void
clutter_actor_insert_child_below(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)11840 clutter_actor_insert_child_below (ClutterActor *self,
11841 ClutterActor *child,
11842 ClutterActor *sibling)
11843 {
11844 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11845 g_return_if_fail (CLUTTER_IS_ACTOR (child));
11846 g_return_if_fail (self != child);
11847 g_return_if_fail (child != sibling);
11848 g_return_if_fail (child->priv->parent == NULL);
11849 g_return_if_fail (sibling == NULL ||
11850 (CLUTTER_IS_ACTOR (sibling) &&
11851 sibling->priv->parent == self));
11852
11853 clutter_actor_add_child_internal (self, child,
11854 ADD_CHILD_DEFAULT_FLAGS,
11855 insert_child_below,
11856 sibling);
11857 }
11858
11859 /**
11860 * clutter_actor_get_parent:
11861 * @self: A #ClutterActor
11862 *
11863 * Retrieves the parent of @self.
11864 *
11865 * Return Value: (transfer none): The #ClutterActor parent, or %NULL
11866 * if no parent is set
11867 */
11868 ClutterActor *
clutter_actor_get_parent(ClutterActor * self)11869 clutter_actor_get_parent (ClutterActor *self)
11870 {
11871 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
11872
11873 return self->priv->parent;
11874 }
11875
11876 /**
11877 * clutter_actor_get_paint_visibility:
11878 * @self: A #ClutterActor
11879 *
11880 * Retrieves the 'paint' visibility of an actor recursively checking for non
11881 * visible parents.
11882 *
11883 * This is by definition the same as %CLUTTER_ACTOR_IS_MAPPED.
11884 *
11885 * Return Value: %TRUE if the actor is visible and will be painted.
11886 *
11887 * Since: 0.8
11888 */
11889 gboolean
clutter_actor_get_paint_visibility(ClutterActor * actor)11890 clutter_actor_get_paint_visibility (ClutterActor *actor)
11891 {
11892 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
11893
11894 return CLUTTER_ACTOR_IS_MAPPED (actor);
11895 }
11896
11897 /**
11898 * clutter_actor_remove_child:
11899 * @self: a #ClutterActor
11900 * @child: a #ClutterActor
11901 *
11902 * Removes @child from the children of @self.
11903 *
11904 * This function will release the reference added by
11905 * clutter_actor_add_child(), so if you want to keep using @child
11906 * you will have to acquire a referenced on it before calling this
11907 * function.
11908 *
11909 * This function will emit the #ClutterContainer::actor-removed
11910 * signal on @self.
11911 *
11912 * Since: 1.10
11913 */
11914 void
clutter_actor_remove_child(ClutterActor * self,ClutterActor * child)11915 clutter_actor_remove_child (ClutterActor *self,
11916 ClutterActor *child)
11917 {
11918 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11919 g_return_if_fail (CLUTTER_IS_ACTOR (child));
11920 g_return_if_fail (self != child);
11921 g_return_if_fail (child->priv->parent != NULL);
11922 g_return_if_fail (child->priv->parent == self);
11923
11924 clutter_actor_remove_child_internal (self, child,
11925 REMOVE_CHILD_DEFAULT_FLAGS);
11926 }
11927
11928 /**
11929 * clutter_actor_remove_all_children:
11930 * @self: a #ClutterActor
11931 *
11932 * Removes all children of @self.
11933 *
11934 * This function releases the reference added by inserting a child actor
11935 * in the list of children of @self.
11936 *
11937 * If the reference count of a child drops to zero, the child will be
11938 * destroyed. If you want to ensure the destruction of all the children
11939 * of @self, use clutter_actor_destroy_all_children().
11940 *
11941 * Since: 1.10
11942 */
11943 void
clutter_actor_remove_all_children(ClutterActor * self)11944 clutter_actor_remove_all_children (ClutterActor *self)
11945 {
11946 ClutterActorIter iter;
11947
11948 g_return_if_fail (CLUTTER_IS_ACTOR (self));
11949
11950 if (self->priv->n_children == 0)
11951 return;
11952
11953 g_object_freeze_notify (G_OBJECT (self));
11954
11955 clutter_actor_iter_init (&iter, self);
11956 while (clutter_actor_iter_next (&iter, NULL))
11957 clutter_actor_iter_remove (&iter);
11958
11959 g_object_thaw_notify (G_OBJECT (self));
11960
11961 /* sanity check */
11962 g_assert (self->priv->first_child == NULL);
11963 g_assert (self->priv->last_child == NULL);
11964 g_assert (self->priv->n_children == 0);
11965 }
11966
11967 /**
11968 * clutter_actor_destroy_all_children:
11969 * @self: a #ClutterActor
11970 *
11971 * Destroys all children of @self.
11972 *
11973 * This function releases the reference added by inserting a child
11974 * actor in the list of children of @self, and ensures that the
11975 * #ClutterActor::destroy signal is emitted on each child of the
11976 * actor.
11977 *
11978 * By default, #ClutterActor will emit the #ClutterActor::destroy signal
11979 * when its reference count drops to 0; the default handler of the
11980 * #ClutterActor::destroy signal will destroy all the children of an
11981 * actor. This function ensures that all children are destroyed, instead
11982 * of just removed from @self, unlike clutter_actor_remove_all_children()
11983 * which will merely release the reference and remove each child.
11984 *
11985 * Unless you acquired an additional reference on each child of @self
11986 * prior to calling clutter_actor_remove_all_children() and want to reuse
11987 * the actors, you should use clutter_actor_destroy_all_children() in
11988 * order to make sure that children are destroyed and signal handlers
11989 * are disconnected even in cases where circular references prevent this
11990 * from automatically happening through reference counting alone.
11991 *
11992 * Since: 1.10
11993 */
11994 void
clutter_actor_destroy_all_children(ClutterActor * self)11995 clutter_actor_destroy_all_children (ClutterActor *self)
11996 {
11997 ClutterActorIter iter;
11998
11999 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12000
12001 if (self->priv->n_children == 0)
12002 return;
12003
12004 g_object_freeze_notify (G_OBJECT (self));
12005
12006 clutter_actor_iter_init (&iter, self);
12007 while (clutter_actor_iter_next (&iter, NULL))
12008 clutter_actor_iter_destroy (&iter);
12009
12010 g_object_thaw_notify (G_OBJECT (self));
12011
12012 /* sanity check */
12013 g_assert (self->priv->first_child == NULL);
12014 g_assert (self->priv->last_child == NULL);
12015 g_assert (self->priv->n_children == 0);
12016 }
12017
12018 typedef struct _InsertBetweenData {
12019 ClutterActor *prev_sibling;
12020 ClutterActor *next_sibling;
12021 } InsertBetweenData;
12022
12023 static void
insert_child_between(ClutterActor * self,ClutterActor * child,gpointer data_)12024 insert_child_between (ClutterActor *self,
12025 ClutterActor *child,
12026 gpointer data_)
12027 {
12028 InsertBetweenData *data = data_;
12029 ClutterActor *prev_sibling = data->prev_sibling;
12030 ClutterActor *next_sibling = data->next_sibling;
12031
12032 child->priv->parent = self;
12033 child->priv->prev_sibling = prev_sibling;
12034 child->priv->next_sibling = next_sibling;
12035
12036 if (prev_sibling != NULL)
12037 prev_sibling->priv->next_sibling = child;
12038
12039 if (next_sibling != NULL)
12040 next_sibling->priv->prev_sibling = child;
12041
12042 if (child->priv->prev_sibling == NULL)
12043 self->priv->first_child = child;
12044
12045 if (child->priv->next_sibling == NULL)
12046 self->priv->last_child = child;
12047 }
12048
12049 /**
12050 * clutter_actor_replace_child:
12051 * @self: a #ClutterActor
12052 * @old_child: the child of @self to replace
12053 * @new_child: the #ClutterActor to replace @old_child
12054 *
12055 * Replaces @old_child with @new_child in the list of children of @self.
12056 *
12057 * Since: 1.10
12058 */
12059 void
clutter_actor_replace_child(ClutterActor * self,ClutterActor * old_child,ClutterActor * new_child)12060 clutter_actor_replace_child (ClutterActor *self,
12061 ClutterActor *old_child,
12062 ClutterActor *new_child)
12063 {
12064 ClutterActor *prev_sibling, *next_sibling;
12065 InsertBetweenData clos;
12066
12067 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12068 g_return_if_fail (CLUTTER_IS_ACTOR (old_child));
12069 g_return_if_fail (old_child->priv->parent == self);
12070 g_return_if_fail (CLUTTER_IS_ACTOR (new_child));
12071 g_return_if_fail (old_child != new_child);
12072 g_return_if_fail (new_child != self);
12073 g_return_if_fail (new_child->priv->parent == NULL);
12074
12075 prev_sibling = old_child->priv->prev_sibling;
12076 next_sibling = old_child->priv->next_sibling;
12077 clutter_actor_remove_child_internal (self, old_child,
12078 REMOVE_CHILD_DEFAULT_FLAGS);
12079
12080 clos.prev_sibling = prev_sibling;
12081 clos.next_sibling = next_sibling;
12082 clutter_actor_add_child_internal (self, new_child,
12083 ADD_CHILD_DEFAULT_FLAGS,
12084 insert_child_between,
12085 &clos);
12086 }
12087
12088 /**
12089 * clutter_actor_contains:
12090 * @self: A #ClutterActor
12091 * @descendant: A #ClutterActor, possibly contained in @self
12092 *
12093 * Determines if @descendant is contained inside @self (either as an
12094 * immediate child, or as a deeper descendant). If @self and
12095 * @descendant point to the same actor then it will also return %TRUE.
12096 *
12097 * Return value: whether @descendent is contained within @self
12098 *
12099 * Since: 1.4
12100 */
12101 gboolean
clutter_actor_contains(ClutterActor * self,ClutterActor * descendant)12102 clutter_actor_contains (ClutterActor *self,
12103 ClutterActor *descendant)
12104 {
12105 ClutterActor *actor;
12106
12107 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
12108 g_return_val_if_fail (CLUTTER_IS_ACTOR (descendant), FALSE);
12109
12110 for (actor = descendant; actor; actor = actor->priv->parent)
12111 if (actor == self)
12112 return TRUE;
12113
12114 return FALSE;
12115 }
12116
12117 /**
12118 * clutter_actor_set_child_above_sibling:
12119 * @self: a #ClutterActor
12120 * @child: a #ClutterActor child of @self
12121 * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
12122 *
12123 * Sets @child to be above @sibling in the list of children of @self.
12124 *
12125 * If @sibling is %NULL, @child will be the new last child of @self.
12126 *
12127 * This function is logically equivalent to removing @child and using
12128 * clutter_actor_insert_child_above(), but it will not emit signals
12129 * or change state on @child.
12130 *
12131 * Since: 1.10
12132 */
12133 void
clutter_actor_set_child_above_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)12134 clutter_actor_set_child_above_sibling (ClutterActor *self,
12135 ClutterActor *child,
12136 ClutterActor *sibling)
12137 {
12138 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12139 g_return_if_fail (CLUTTER_IS_ACTOR (child));
12140 g_return_if_fail (child->priv->parent == self);
12141 g_return_if_fail (child != sibling);
12142 g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
12143
12144 if (sibling != NULL)
12145 g_return_if_fail (sibling->priv->parent == self);
12146
12147 if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
12148 CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
12149 (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
12150 return;
12151
12152 /* we don't want to change the state of child, or emit signals, or
12153 * regenerate ChildMeta instances here, but we still want to follow
12154 * the correct sequence of steps encoded in remove_child() and
12155 * add_child(), so that correctness is ensured, and we only go
12156 * through one known code path.
12157 */
12158 g_object_ref (child);
12159 clutter_actor_remove_child_internal (self, child, 0);
12160 clutter_actor_add_child_internal (self, child,
12161 ADD_CHILD_NOTIFY_FIRST_LAST,
12162 insert_child_above,
12163 sibling);
12164 g_object_unref(child);
12165
12166 clutter_actor_queue_relayout (self);
12167 }
12168
12169 /**
12170 * clutter_actor_set_child_below_sibling:
12171 * @self: a #ClutterActor
12172 * @child: a #ClutterActor child of @self
12173 * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
12174 *
12175 * Sets @child to be below @sibling in the list of children of @self.
12176 *
12177 * If @sibling is %NULL, @child will be the new first child of @self.
12178 *
12179 * This function is logically equivalent to removing @self and using
12180 * clutter_actor_insert_child_below(), but it will not emit signals
12181 * or change state on @child.
12182 *
12183 * Since: 1.10
12184 */
12185 void
clutter_actor_set_child_below_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)12186 clutter_actor_set_child_below_sibling (ClutterActor *self,
12187 ClutterActor *child,
12188 ClutterActor *sibling)
12189 {
12190 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12191 g_return_if_fail (CLUTTER_IS_ACTOR (child));
12192 g_return_if_fail (child->priv->parent == self);
12193 g_return_if_fail (child != sibling);
12194 g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
12195
12196 if (sibling != NULL)
12197 g_return_if_fail (sibling->priv->parent == self);
12198
12199 if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
12200 CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
12201 (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
12202 return;
12203
12204 /* see the comment in set_child_above_sibling() */
12205 g_object_ref (child);
12206 clutter_actor_remove_child_internal (self, child, 0);
12207 clutter_actor_add_child_internal (self, child,
12208 ADD_CHILD_NOTIFY_FIRST_LAST,
12209 insert_child_below,
12210 sibling);
12211 g_object_unref(child);
12212
12213 clutter_actor_queue_relayout (self);
12214 }
12215
12216 /**
12217 * clutter_actor_set_child_at_index:
12218 * @self: a #ClutterActor
12219 * @child: a #ClutterActor child of @self
12220 * @index_: the new index for @child
12221 *
12222 * Changes the index of @child in the list of children of @self.
12223 *
12224 * This function is logically equivalent to removing @child and
12225 * calling clutter_actor_insert_child_at_index(), but it will not
12226 * emit signals or change state on @child.
12227 *
12228 * Since: 1.10
12229 */
12230 void
clutter_actor_set_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)12231 clutter_actor_set_child_at_index (ClutterActor *self,
12232 ClutterActor *child,
12233 gint index_)
12234 {
12235 g_return_if_fail (CLUTTER_IS_ACTOR (self));
12236 g_return_if_fail (CLUTTER_IS_ACTOR (child));
12237 g_return_if_fail (child->priv->parent == self);
12238 g_return_if_fail (index_ <= self->priv->n_children);
12239
12240 if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
12241 CLUTTER_ACTOR_IN_DESTRUCTION (child))
12242 return;
12243
12244 g_object_ref (child);
12245 clutter_actor_remove_child_internal (self, child, 0);
12246 clutter_actor_add_child_internal (self, child,
12247 ADD_CHILD_NOTIFY_FIRST_LAST,
12248 insert_child_at_index,
12249 GINT_TO_POINTER (index_));
12250 g_object_unref (child);
12251
12252 clutter_actor_queue_relayout (self);
12253 }
12254
12255 /*
12256 * Event handling
12257 */
12258
12259 /**
12260 * clutter_actor_event:
12261 * @actor: a #ClutterActor
12262 * @event: a #ClutterEvent
12263 * @capture: %TRUE if event in in capture phase, %FALSE otherwise.
12264 *
12265 * This function is used to emit an event on the main stage.
12266 * You should rarely need to use this function, except for
12267 * synthetising events.
12268 *
12269 * Return value: the return value from the signal emission: %TRUE
12270 * if the actor handled the event, or %FALSE if the event was
12271 * not handled
12272 *
12273 * Since: 0.6
12274 */
12275 gboolean
clutter_actor_event(ClutterActor * actor,const ClutterEvent * event,gboolean capture)12276 clutter_actor_event (ClutterActor *actor,
12277 const ClutterEvent *event,
12278 gboolean capture)
12279 {
12280 gboolean retval = FALSE;
12281 gint signal_num = -1;
12282 GQuark detail = 0;
12283
12284 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
12285 g_return_val_if_fail (event != NULL, FALSE);
12286
12287 g_object_ref (actor);
12288
12289 switch (event->type)
12290 {
12291 case CLUTTER_NOTHING:
12292 break;
12293 case CLUTTER_BUTTON_PRESS:
12294 signal_num = BUTTON_PRESS_EVENT;
12295 detail = quark_button;
12296 break;
12297 case CLUTTER_BUTTON_RELEASE:
12298 signal_num = BUTTON_RELEASE_EVENT;
12299 detail = quark_button;
12300 break;
12301 case CLUTTER_SCROLL:
12302 signal_num = SCROLL_EVENT;
12303 detail = quark_scroll;
12304 break;
12305 case CLUTTER_KEY_PRESS:
12306 signal_num = KEY_PRESS_EVENT;
12307 detail = quark_key;
12308 break;
12309 case CLUTTER_KEY_RELEASE:
12310 signal_num = KEY_RELEASE_EVENT;
12311 detail = quark_key;
12312 break;
12313 case CLUTTER_MOTION:
12314 signal_num = MOTION_EVENT;
12315 detail = quark_motion;
12316 break;
12317 case CLUTTER_ENTER:
12318 signal_num = ENTER_EVENT;
12319 detail = quark_pointer_focus;
12320 break;
12321 case CLUTTER_LEAVE:
12322 signal_num = LEAVE_EVENT;
12323 detail = quark_pointer_focus;
12324 break;
12325 case CLUTTER_TOUCH_BEGIN:
12326 case CLUTTER_TOUCH_END:
12327 case CLUTTER_TOUCH_UPDATE:
12328 case CLUTTER_TOUCH_CANCEL:
12329 signal_num = TOUCH_EVENT;
12330 detail = quark_touch;
12331 break;
12332 case CLUTTER_TOUCHPAD_PINCH:
12333 case CLUTTER_TOUCHPAD_SWIPE:
12334 signal_num = -1;
12335 detail = quark_touchpad;
12336 break;
12337 case CLUTTER_PROXIMITY_IN:
12338 case CLUTTER_PROXIMITY_OUT:
12339 signal_num = -1;
12340 detail = quark_proximity;
12341 break;
12342 case CLUTTER_PAD_BUTTON_PRESS:
12343 case CLUTTER_PAD_BUTTON_RELEASE:
12344 case CLUTTER_PAD_STRIP:
12345 case CLUTTER_PAD_RING:
12346 signal_num = -1;
12347 detail = quark_pad;
12348 break;
12349 case CLUTTER_IM_COMMIT:
12350 case CLUTTER_IM_DELETE:
12351 case CLUTTER_IM_PREEDIT:
12352 signal_num = -1;
12353 detail = quark_im;
12354 case CLUTTER_DEVICE_ADDED:
12355 case CLUTTER_DEVICE_REMOVED:
12356 break;
12357 case CLUTTER_EVENT_LAST: /* Just keep compiler warnings quiet */
12358 break;
12359 }
12360
12361 if (capture)
12362 g_signal_emit (actor, actor_signals[CAPTURED_EVENT], detail, event, &retval);
12363 else
12364 {
12365 g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
12366
12367 if (!retval && signal_num != -1)
12368 g_signal_emit (actor, actor_signals[signal_num], 0, event, &retval);
12369 }
12370
12371 g_object_unref (actor);
12372
12373 return retval;
12374 }
12375
12376 /**
12377 * clutter_actor_set_reactive:
12378 * @actor: a #ClutterActor
12379 * @reactive: whether the actor should be reactive to events
12380 *
12381 * Sets @actor as reactive. Reactive actors will receive events.
12382 *
12383 * Since: 0.6
12384 */
12385 void
clutter_actor_set_reactive(ClutterActor * actor,gboolean reactive)12386 clutter_actor_set_reactive (ClutterActor *actor,
12387 gboolean reactive)
12388 {
12389 g_return_if_fail (CLUTTER_IS_ACTOR (actor));
12390
12391 if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor))
12392 return;
12393
12394 if (reactive)
12395 CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
12396 else
12397 CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
12398
12399 g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]);
12400 }
12401
12402 /**
12403 * clutter_actor_get_reactive:
12404 * @actor: a #ClutterActor
12405 *
12406 * Checks whether @actor is marked as reactive.
12407 *
12408 * Return value: %TRUE if the actor is reactive
12409 *
12410 * Since: 0.6
12411 */
12412 gboolean
clutter_actor_get_reactive(ClutterActor * actor)12413 clutter_actor_get_reactive (ClutterActor *actor)
12414 {
12415 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
12416
12417 return CLUTTER_ACTOR_IS_REACTIVE (actor) ? TRUE : FALSE;
12418 }
12419
12420 static void
clutter_actor_store_content_box(ClutterActor * self,const ClutterActorBox * box)12421 clutter_actor_store_content_box (ClutterActor *self,
12422 const ClutterActorBox *box)
12423 {
12424 if (box != NULL)
12425 {
12426 self->priv->content_box = *box;
12427 self->priv->content_box_valid = TRUE;
12428 }
12429 else
12430 self->priv->content_box_valid = FALSE;
12431
12432 clutter_actor_queue_redraw (self);
12433
12434 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
12435 }
12436
12437 static void
clutter_container_iface_init(ClutterContainerIface * iface)12438 clutter_container_iface_init (ClutterContainerIface *iface)
12439 {
12440 /* we don't override anything, as ClutterContainer already has a default
12441 * implementation that we can use, and which calls into our own API.
12442 */
12443 }
12444
12445 typedef enum
12446 {
12447 PARSE_X,
12448 PARSE_Y,
12449 PARSE_WIDTH,
12450 PARSE_HEIGHT,
12451 } ParseDimension;
12452
12453 static gfloat
parse_units(ClutterActor * self,ParseDimension dimension,JsonNode * node)12454 parse_units (ClutterActor *self,
12455 ParseDimension dimension,
12456 JsonNode *node)
12457 {
12458 GValue value = G_VALUE_INIT;
12459 gfloat retval = 0;
12460
12461 if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
12462 return 0;
12463
12464 json_node_get_value (node, &value);
12465
12466 if (G_VALUE_HOLDS (&value, G_TYPE_INT64))
12467 {
12468 retval = (gfloat) g_value_get_int64 (&value);
12469 }
12470 else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE))
12471 {
12472 retval = g_value_get_double (&value);
12473 }
12474 else if (G_VALUE_HOLDS (&value, G_TYPE_STRING))
12475 {
12476 ClutterUnits units;
12477 gboolean res;
12478
12479 res = clutter_units_from_string (&units, g_value_get_string (&value));
12480 if (res)
12481 retval = clutter_units_to_pixels (&units);
12482 else
12483 {
12484 g_warning ("Invalid value '%s': integers, strings or floating point "
12485 "values can be used for the x, y, width and height "
12486 "properties. Valid modifiers for strings are 'px', 'mm', "
12487 "'pt' and 'em'.",
12488 g_value_get_string (&value));
12489 retval = 0;
12490 }
12491 }
12492 else
12493 {
12494 g_warning ("Invalid value of type '%s': integers, strings of floating "
12495 "point values can be used for the x, y, width, and height "
12496 "properties.",
12497 g_type_name (G_VALUE_TYPE (&value)));
12498 }
12499
12500 g_value_unset (&value);
12501
12502 return retval;
12503 }
12504
12505 typedef struct {
12506 ClutterRotateAxis axis;
12507
12508 gdouble angle;
12509
12510 gfloat center_x;
12511 gfloat center_y;
12512 gfloat center_z;
12513 } RotationInfo;
12514
12515 static inline gboolean
parse_rotation_array(ClutterActor * actor,JsonArray * array,RotationInfo * info)12516 parse_rotation_array (ClutterActor *actor,
12517 JsonArray *array,
12518 RotationInfo *info)
12519 {
12520 JsonNode *element;
12521
12522 if (json_array_get_length (array) != 2)
12523 return FALSE;
12524
12525 /* angle */
12526 element = json_array_get_element (array, 0);
12527 if (JSON_NODE_TYPE (element) == JSON_NODE_VALUE)
12528 info->angle = json_node_get_double (element);
12529 else
12530 return FALSE;
12531
12532 /* center */
12533 element = json_array_get_element (array, 1);
12534 if (JSON_NODE_TYPE (element) == JSON_NODE_ARRAY)
12535 {
12536 JsonArray *center = json_node_get_array (element);
12537
12538 if (json_array_get_length (center) != 2)
12539 return FALSE;
12540
12541 switch (info->axis)
12542 {
12543 case CLUTTER_X_AXIS:
12544 info->center_y = parse_units (actor, PARSE_Y,
12545 json_array_get_element (center, 0));
12546 info->center_z = parse_units (actor, PARSE_Y,
12547 json_array_get_element (center, 1));
12548 return TRUE;
12549
12550 case CLUTTER_Y_AXIS:
12551 info->center_x = parse_units (actor, PARSE_X,
12552 json_array_get_element (center, 0));
12553 info->center_z = parse_units (actor, PARSE_X,
12554 json_array_get_element (center, 1));
12555 return TRUE;
12556
12557 case CLUTTER_Z_AXIS:
12558 info->center_x = parse_units (actor, PARSE_X,
12559 json_array_get_element (center, 0));
12560 info->center_y = parse_units (actor, PARSE_Y,
12561 json_array_get_element (center, 1));
12562 return TRUE;
12563 }
12564 }
12565
12566 return FALSE;
12567 }
12568
12569 static gboolean
parse_rotation(ClutterActor * actor,JsonNode * node,RotationInfo * info)12570 parse_rotation (ClutterActor *actor,
12571 JsonNode *node,
12572 RotationInfo *info)
12573 {
12574 JsonArray *array;
12575 guint len, i;
12576 gboolean retval = FALSE;
12577
12578 if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
12579 {
12580 g_warning ("Invalid node of type '%s' found, expecting an array",
12581 json_node_type_name (node));
12582 return FALSE;
12583 }
12584
12585 array = json_node_get_array (node);
12586 len = json_array_get_length (array);
12587
12588 for (i = 0; i < len; i++)
12589 {
12590 JsonNode *element = json_array_get_element (array, i);
12591 JsonObject *object;
12592 JsonNode *member;
12593
12594 if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
12595 {
12596 g_warning ("Invalid node of type '%s' found, expecting an object",
12597 json_node_type_name (element));
12598 return FALSE;
12599 }
12600
12601 object = json_node_get_object (element);
12602
12603 if (json_object_has_member (object, "x-axis"))
12604 {
12605 member = json_object_get_member (object, "x-axis");
12606
12607 info->axis = CLUTTER_X_AXIS;
12608
12609 if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
12610 {
12611 info->angle = json_node_get_double (member);
12612 retval = TRUE;
12613 }
12614 else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
12615 retval = parse_rotation_array (actor,
12616 json_node_get_array (member),
12617 info);
12618 else
12619 retval = FALSE;
12620 }
12621 else if (json_object_has_member (object, "y-axis"))
12622 {
12623 member = json_object_get_member (object, "y-axis");
12624
12625 info->axis = CLUTTER_Y_AXIS;
12626
12627 if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
12628 {
12629 info->angle = json_node_get_double (member);
12630 retval = TRUE;
12631 }
12632 else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
12633 retval = parse_rotation_array (actor,
12634 json_node_get_array (member),
12635 info);
12636 else
12637 retval = FALSE;
12638 }
12639 else if (json_object_has_member (object, "z-axis"))
12640 {
12641 member = json_object_get_member (object, "z-axis");
12642
12643 info->axis = CLUTTER_Z_AXIS;
12644
12645 if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
12646 {
12647 info->angle = json_node_get_double (member);
12648 retval = TRUE;
12649 }
12650 else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
12651 retval = parse_rotation_array (actor,
12652 json_node_get_array (member),
12653 info);
12654 else
12655 retval = FALSE;
12656 }
12657 }
12658
12659 return retval;
12660 }
12661
12662 static GSList *
parse_actor_metas(ClutterScript * script,ClutterActor * actor,JsonNode * node)12663 parse_actor_metas (ClutterScript *script,
12664 ClutterActor *actor,
12665 JsonNode *node)
12666 {
12667 GList *elements, *l;
12668 GSList *retval = NULL;
12669
12670 if (!JSON_NODE_HOLDS_ARRAY (node))
12671 return NULL;
12672
12673 elements = json_array_get_elements (json_node_get_array (node));
12674
12675 for (l = elements; l != NULL; l = l->next)
12676 {
12677 JsonNode *element = l->data;
12678 const gchar *id_ = _clutter_script_get_id_from_node (element);
12679 GObject *meta;
12680
12681 if (id_ == NULL || *id_ == '\0')
12682 continue;
12683
12684 meta = clutter_script_get_object (script, id_);
12685 if (meta == NULL)
12686 continue;
12687
12688 retval = g_slist_prepend (retval, meta);
12689 }
12690
12691 g_list_free (elements);
12692
12693 return g_slist_reverse (retval);
12694 }
12695
12696 static ClutterMargin *
parse_margin(ClutterActor * self,JsonNode * node)12697 parse_margin (ClutterActor *self,
12698 JsonNode *node)
12699 {
12700 ClutterMargin *margin;
12701 JsonArray *array;
12702
12703 if (!JSON_NODE_HOLDS_ARRAY (node))
12704 {
12705 g_warning ("The margin property must be an array of 1 to 4 elements");
12706 return NULL;
12707 }
12708
12709 margin = clutter_margin_new ();
12710 array = json_node_get_array (node);
12711 switch (json_array_get_length (array))
12712 {
12713 case 1:
12714 margin->top = margin->right = margin->bottom = margin->left =
12715 parse_units (self, 0, json_array_get_element (array, 0));
12716 break;
12717
12718 case 2:
12719 margin->top = margin->bottom =
12720 parse_units (self, 0, json_array_get_element (array, 0));
12721 margin->right = margin->left =
12722 parse_units (self, 0, json_array_get_element (array, 1));
12723 break;
12724
12725 case 3:
12726 margin->top =
12727 parse_units (self, 0, json_array_get_element (array, 0));
12728 margin->right = margin->left =
12729 parse_units (self, 0, json_array_get_element (array, 1));
12730 margin->bottom =
12731 parse_units (self, 0, json_array_get_element (array, 2));
12732 break;
12733
12734 case 4:
12735 margin->top =
12736 parse_units (self, 0, json_array_get_element (array, 0));
12737 margin->right =
12738 parse_units (self, 0, json_array_get_element (array, 1));
12739 margin->bottom =
12740 parse_units (self, 0, json_array_get_element (array, 2));
12741 margin->left =
12742 parse_units (self, 0, json_array_get_element (array, 3));
12743 break;
12744
12745 default:
12746 g_warning ("The margin property must be an array of 1 to 4 elements");
12747 clutter_margin_free (margin);
12748 return NULL;
12749 }
12750 return margin;
12751 }
12752
12753 static gboolean
clutter_actor_parse_custom_node(ClutterScriptable * scriptable,ClutterScript * script,GValue * value,const gchar * name,JsonNode * node)12754 clutter_actor_parse_custom_node (ClutterScriptable *scriptable,
12755 ClutterScript *script,
12756 GValue *value,
12757 const gchar *name,
12758 JsonNode *node)
12759 {
12760 ClutterActor *actor = CLUTTER_ACTOR (scriptable);
12761 gboolean retval = FALSE;
12762
12763 if ((name[0] == 'x' && name[1] == '\0') ||
12764 (name[0] == 'y' && name[1] == '\0') ||
12765 (strcmp (name, "width") == 0) ||
12766 (strcmp (name, "height") == 0))
12767 {
12768 ParseDimension dimension;
12769 gfloat units;
12770
12771 if (name[0] == 'x')
12772 dimension = PARSE_X;
12773 else if (name[0] == 'y')
12774 dimension = PARSE_Y;
12775 else if (name[0] == 'w')
12776 dimension = PARSE_WIDTH;
12777 else if (name[0] == 'h')
12778 dimension = PARSE_HEIGHT;
12779 else
12780 return FALSE;
12781
12782 units = parse_units (actor, dimension, node);
12783
12784 /* convert back to pixels: all properties are pixel-based */
12785 g_value_init (value, G_TYPE_FLOAT);
12786 g_value_set_float (value, units);
12787
12788 retval = TRUE;
12789 }
12790 else if (strcmp (name, "rotation") == 0)
12791 {
12792 RotationInfo *info;
12793
12794 info = g_new0 (RotationInfo, 1);
12795 retval = parse_rotation (actor, node, info);
12796
12797 if (retval)
12798 {
12799 g_value_init (value, G_TYPE_POINTER);
12800 g_value_set_pointer (value, info);
12801 }
12802 else
12803 g_free (info);
12804 }
12805 else if (strcmp (name, "actions") == 0 ||
12806 strcmp (name, "constraints") == 0 ||
12807 strcmp (name, "effects") == 0)
12808 {
12809 GSList *l;
12810
12811 l = parse_actor_metas (script, actor, node);
12812
12813 g_value_init (value, G_TYPE_POINTER);
12814 g_value_set_pointer (value, l);
12815
12816 retval = TRUE;
12817 }
12818 else if (strcmp (name, "margin") == 0)
12819 {
12820 ClutterMargin *margin = parse_margin (actor, node);
12821
12822 if (margin)
12823 {
12824 g_value_init (value, CLUTTER_TYPE_MARGIN);
12825 g_value_set_boxed (value, margin);
12826 retval = TRUE;
12827 }
12828 }
12829
12830 return retval;
12831 }
12832
12833 static void
clutter_actor_set_custom_property(ClutterScriptable * scriptable,ClutterScript * script,const gchar * name,const GValue * value)12834 clutter_actor_set_custom_property (ClutterScriptable *scriptable,
12835 ClutterScript *script,
12836 const gchar *name,
12837 const GValue *value)
12838 {
12839 ClutterActor *actor = CLUTTER_ACTOR (scriptable);
12840
12841 #ifdef CLUTTER_ENABLE_DEBUG
12842 if (G_UNLIKELY (CLUTTER_HAS_DEBUG (SCRIPT)))
12843 {
12844 gchar *tmp = g_strdup_value_contents (value);
12845
12846 CLUTTER_NOTE (SCRIPT,
12847 "in ClutterActor::set_custom_property('%s') = %s",
12848 name,
12849 tmp);
12850
12851 g_free (tmp);
12852 }
12853 #endif /* CLUTTER_ENABLE_DEBUG */
12854
12855 if (strcmp (name, "rotation") == 0)
12856 {
12857 RotationInfo *info;
12858
12859 if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
12860 return;
12861
12862 info = g_value_get_pointer (value);
12863
12864 clutter_actor_set_rotation_angle (actor, info->axis, info->angle);
12865
12866 g_free (info);
12867
12868 return;
12869 }
12870
12871 if (strcmp (name, "actions") == 0 ||
12872 strcmp (name, "constraints") == 0 ||
12873 strcmp (name, "effects") == 0)
12874 {
12875 GSList *metas, *l;
12876
12877 if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
12878 return;
12879
12880 metas = g_value_get_pointer (value);
12881 for (l = metas; l != NULL; l = l->next)
12882 {
12883 if (name[0] == 'a')
12884 clutter_actor_add_action (actor, l->data);
12885
12886 if (name[0] == 'c')
12887 clutter_actor_add_constraint (actor, l->data);
12888
12889 if (name[0] == 'e')
12890 clutter_actor_add_effect (actor, l->data);
12891 }
12892
12893 g_slist_free (metas);
12894
12895 return;
12896 }
12897 if (strcmp (name, "margin") == 0)
12898 {
12899 clutter_actor_set_margin (actor, g_value_get_boxed (value));
12900 return;
12901 }
12902
12903 g_object_set_property (G_OBJECT (scriptable), name, value);
12904 }
12905
12906 static void
clutter_scriptable_iface_init(ClutterScriptableIface * iface)12907 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
12908 {
12909 iface->parse_custom_node = clutter_actor_parse_custom_node;
12910 iface->set_custom_property = clutter_actor_set_custom_property;
12911 }
12912
12913 static gboolean
get_layout_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)12914 get_layout_from_animation_property (ClutterActor *actor,
12915 const gchar *name,
12916 gchar **name_p)
12917 {
12918 g_auto (GStrv) tokens = NULL;
12919
12920 if (!g_str_has_prefix (name, "@layout"))
12921 return FALSE;
12922
12923 tokens = g_strsplit (name, ".", -1);
12924 if (tokens == NULL || g_strv_length (tokens) != 2)
12925 {
12926 CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
12927 name + 1);
12928 return FALSE;
12929 }
12930
12931 if (name_p != NULL)
12932 *name_p = g_strdup (tokens[1]);
12933
12934 return TRUE;
12935 }
12936
12937 static gboolean
get_content_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)12938 get_content_from_animation_property (ClutterActor *actor,
12939 const gchar *name,
12940 gchar **name_p)
12941 {
12942 g_auto (GStrv) tokens = NULL;
12943
12944 if (!g_str_has_prefix (name, "@content"))
12945 return FALSE;
12946
12947 if (!actor->priv->content)
12948 {
12949 CLUTTER_NOTE (ANIMATION, "No ClutterContent available for '%s'",
12950 name + 1);
12951 return FALSE;
12952 }
12953
12954 tokens = g_strsplit (name, ".", -1);
12955 if (tokens == NULL || g_strv_length (tokens) != 2)
12956 {
12957 CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
12958 name + 1);
12959 return FALSE;
12960 }
12961
12962 if (name_p != NULL)
12963 *name_p = g_strdup (tokens[1]);
12964
12965 return TRUE;
12966 }
12967
12968 static ClutterActorMeta *
get_meta_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)12969 get_meta_from_animation_property (ClutterActor *actor,
12970 const gchar *name,
12971 gchar **name_p)
12972 {
12973 ClutterActorPrivate *priv = actor->priv;
12974 ClutterActorMeta *meta = NULL;
12975 gchar **tokens;
12976
12977 /* if this is not a special property, fall through */
12978 if (name[0] != '@')
12979 return NULL;
12980
12981 /* detect the properties named using the following spec:
12982 *
12983 * @<section>.<meta-name>.<property-name>
12984 *
12985 * where <section> can be one of the following:
12986 *
12987 * - actions
12988 * - constraints
12989 * - effects
12990 *
12991 * and <meta-name> is the name set on a specific ActorMeta
12992 */
12993
12994 tokens = g_strsplit (name + 1, ".", -1);
12995 if (tokens == NULL || g_strv_length (tokens) != 3)
12996 {
12997 CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
12998 name + 1);
12999 g_strfreev (tokens);
13000 return NULL;
13001 }
13002
13003 if (strcmp (tokens[0], "actions") == 0)
13004 meta = _clutter_meta_group_get_meta (priv->actions, tokens[1]);
13005
13006 if (strcmp (tokens[0], "constraints") == 0)
13007 meta = _clutter_meta_group_get_meta (priv->constraints, tokens[1]);
13008
13009 if (strcmp (tokens[0], "effects") == 0)
13010 meta = _clutter_meta_group_get_meta (priv->effects, tokens[1]);
13011
13012 if (name_p != NULL)
13013 *name_p = g_strdup (tokens[2]);
13014
13015 CLUTTER_NOTE (ANIMATION,
13016 "Looking for property '%s' of object '%s' in section '%s'",
13017 tokens[2],
13018 tokens[1],
13019 tokens[0]);
13020
13021 g_strfreev (tokens);
13022
13023 return meta;
13024 }
13025
13026 static GParamSpec *
clutter_actor_find_property(ClutterAnimatable * animatable,const gchar * property_name)13027 clutter_actor_find_property (ClutterAnimatable *animatable,
13028 const gchar *property_name)
13029 {
13030 ClutterActor *actor = CLUTTER_ACTOR (animatable);
13031 ClutterActorMeta *meta = NULL;
13032 GObjectClass *klass = NULL;
13033 GParamSpec *pspec = NULL;
13034 gchar *p_name = NULL;
13035 gboolean use_content = FALSE;
13036 gboolean use_layout;
13037
13038 use_layout = get_layout_from_animation_property (actor,
13039 property_name,
13040 &p_name);
13041
13042 if (!use_layout)
13043 use_content = get_content_from_animation_property (actor,
13044 property_name,
13045 &p_name);
13046
13047 if (!use_layout && !use_content)
13048 meta = get_meta_from_animation_property (actor,
13049 property_name,
13050 &p_name);
13051
13052 if (meta != NULL)
13053 {
13054 klass = G_OBJECT_GET_CLASS (meta);
13055
13056 pspec = g_object_class_find_property (klass, p_name);
13057 }
13058 else if (use_layout)
13059 {
13060 klass = G_OBJECT_GET_CLASS (actor->priv->layout_manager);
13061
13062 pspec = g_object_class_find_property (klass, p_name);
13063 }
13064 else if (use_content)
13065 {
13066 klass = G_OBJECT_GET_CLASS (actor->priv->content);
13067
13068 pspec = g_object_class_find_property (klass, p_name);
13069 }
13070 else
13071 {
13072 klass = G_OBJECT_GET_CLASS (animatable);
13073
13074 pspec = g_object_class_find_property (klass, property_name);
13075 }
13076
13077 g_free (p_name);
13078
13079 return pspec;
13080 }
13081
13082 static void
clutter_actor_get_initial_state(ClutterAnimatable * animatable,const gchar * property_name,GValue * initial)13083 clutter_actor_get_initial_state (ClutterAnimatable *animatable,
13084 const gchar *property_name,
13085 GValue *initial)
13086 {
13087 ClutterActor *actor = CLUTTER_ACTOR (animatable);
13088 ClutterActorMeta *meta = NULL;
13089 gchar *p_name = NULL;
13090 gboolean use_content = FALSE;
13091 gboolean use_layout;
13092
13093 use_layout = get_layout_from_animation_property (actor,
13094 property_name,
13095 &p_name);
13096
13097 if (!use_layout)
13098 use_content = get_content_from_animation_property (actor,
13099 property_name,
13100 &p_name);
13101
13102 if (!use_layout && !use_content)
13103 meta = get_meta_from_animation_property (actor,
13104 property_name,
13105 &p_name);
13106
13107 if (meta != NULL)
13108 g_object_get_property (G_OBJECT (meta), p_name, initial);
13109 else if (use_layout)
13110 g_object_get_property (G_OBJECT (actor->priv->layout_manager), p_name, initial);
13111 else if (use_content)
13112 g_object_get_property (G_OBJECT (actor->priv->content), p_name, initial);
13113 else
13114 g_object_get_property (G_OBJECT (animatable), property_name, initial);
13115
13116 g_free (p_name);
13117 }
13118
13119 /*
13120 * clutter_actor_set_animatable_property:
13121 * @actor: a #ClutterActor
13122 * @prop_id: the paramspec id
13123 * @value: the value to set
13124 * @pspec: the paramspec
13125 *
13126 * Sets values of animatable properties.
13127 *
13128 * This is a variant of clutter_actor_set_property() that gets called
13129 * by the #ClutterAnimatable implementation of #ClutterActor for the
13130 * properties with the %CLUTTER_PARAM_ANIMATABLE flag set on their
13131 * #GParamSpec.
13132 *
13133 * Unlike the implementation of #GObjectClass.set_property(), this
13134 * function will not update the interval if a transition involving an
13135 * animatable property is in progress - this avoids cycles with the
13136 * transition API calling the public API.
13137 */
13138 static void
clutter_actor_set_animatable_property(ClutterActor * actor,guint prop_id,const GValue * value,GParamSpec * pspec)13139 clutter_actor_set_animatable_property (ClutterActor *actor,
13140 guint prop_id,
13141 const GValue *value,
13142 GParamSpec *pspec)
13143 {
13144 GObject *obj = G_OBJECT (actor);
13145
13146 g_object_freeze_notify (obj);
13147
13148 switch (prop_id)
13149 {
13150 case PROP_X:
13151 clutter_actor_set_x_internal (actor, g_value_get_float (value));
13152 break;
13153
13154 case PROP_Y:
13155 clutter_actor_set_y_internal (actor, g_value_get_float (value));
13156 break;
13157
13158 case PROP_POSITION:
13159 clutter_actor_set_position_internal (actor, g_value_get_boxed (value));
13160 break;
13161
13162 case PROP_WIDTH:
13163 clutter_actor_set_width_internal (actor, g_value_get_float (value));
13164 break;
13165
13166 case PROP_HEIGHT:
13167 clutter_actor_set_height_internal (actor, g_value_get_float (value));
13168 break;
13169
13170 case PROP_SIZE:
13171 clutter_actor_set_size_internal (actor, g_value_get_boxed (value));
13172 break;
13173
13174 case PROP_ALLOCATION:
13175 clutter_actor_allocate_internal (actor, g_value_get_boxed (value));
13176 clutter_actor_queue_redraw (actor);
13177 break;
13178
13179 case PROP_Z_POSITION:
13180 clutter_actor_set_z_position_internal (actor, g_value_get_float (value));
13181 break;
13182
13183 case PROP_OPACITY:
13184 clutter_actor_set_opacity_internal (actor, g_value_get_uint (value));
13185 break;
13186
13187 case PROP_BACKGROUND_COLOR:
13188 clutter_actor_set_background_color_internal (actor, clutter_value_get_color (value));
13189 break;
13190
13191 case PROP_PIVOT_POINT:
13192 clutter_actor_set_pivot_point_internal (actor, g_value_get_boxed (value));
13193 break;
13194
13195 case PROP_PIVOT_POINT_Z:
13196 clutter_actor_set_pivot_point_z_internal (actor, g_value_get_float (value));
13197 break;
13198
13199 case PROP_TRANSLATION_X:
13200 case PROP_TRANSLATION_Y:
13201 case PROP_TRANSLATION_Z:
13202 clutter_actor_set_translation_internal (actor,
13203 g_value_get_float (value),
13204 pspec);
13205 break;
13206
13207 case PROP_SCALE_X:
13208 case PROP_SCALE_Y:
13209 case PROP_SCALE_Z:
13210 clutter_actor_set_scale_factor_internal (actor,
13211 g_value_get_double (value),
13212 pspec);
13213 break;
13214
13215 case PROP_ROTATION_ANGLE_X:
13216 case PROP_ROTATION_ANGLE_Y:
13217 case PROP_ROTATION_ANGLE_Z:
13218 clutter_actor_set_rotation_angle_internal (actor,
13219 g_value_get_double (value),
13220 pspec);
13221 break;
13222
13223 case PROP_CONTENT_BOX:
13224 clutter_actor_store_content_box (actor, g_value_get_boxed (value));
13225 break;
13226
13227 case PROP_MARGIN_TOP:
13228 case PROP_MARGIN_BOTTOM:
13229 case PROP_MARGIN_LEFT:
13230 case PROP_MARGIN_RIGHT:
13231 clutter_actor_set_margin_internal (actor, g_value_get_float (value),
13232 pspec);
13233 break;
13234
13235 case PROP_TRANSFORM:
13236 clutter_actor_set_transform_internal (actor, g_value_get_boxed (value));
13237 break;
13238
13239 case PROP_CHILD_TRANSFORM:
13240 clutter_actor_set_child_transform_internal (actor, g_value_get_boxed (value));
13241 break;
13242
13243 default:
13244 g_object_set_property (obj, pspec->name, value);
13245 break;
13246 }
13247
13248 g_object_thaw_notify (obj);
13249 }
13250
13251 static void
clutter_actor_set_final_state(ClutterAnimatable * animatable,const gchar * property_name,const GValue * final)13252 clutter_actor_set_final_state (ClutterAnimatable *animatable,
13253 const gchar *property_name,
13254 const GValue *final)
13255 {
13256 ClutterActor *actor = CLUTTER_ACTOR (animatable);
13257 ClutterActorMeta *meta = NULL;
13258 gchar *p_name = NULL;
13259 gboolean use_content = FALSE;
13260 gboolean use_layout;
13261
13262 use_layout = get_layout_from_animation_property (actor,
13263 property_name,
13264 &p_name);
13265
13266 if (!use_layout)
13267 use_content = get_content_from_animation_property (actor,
13268 property_name,
13269 &p_name);
13270
13271 if (!use_layout && !use_content)
13272 meta = get_meta_from_animation_property (actor,
13273 property_name,
13274 &p_name);
13275
13276 if (meta != NULL)
13277 g_object_set_property (G_OBJECT (meta), p_name, final);
13278 else if (use_layout)
13279 g_object_set_property (G_OBJECT (actor->priv->layout_manager), p_name, final);
13280 else if (use_content)
13281 g_object_set_property (G_OBJECT (actor->priv->content), p_name, final);
13282 else
13283 {
13284 GObjectClass *obj_class = G_OBJECT_GET_CLASS (animatable);
13285 GParamSpec *pspec;
13286
13287 pspec = g_object_class_find_property (obj_class, property_name);
13288
13289 if (pspec != NULL)
13290 {
13291 if ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0)
13292 {
13293 /* XXX - I'm going to the special hell for this */
13294 clutter_actor_set_animatable_property (actor, pspec->param_id, final, pspec);
13295 }
13296 else
13297 g_object_set_property (G_OBJECT (animatable), pspec->name, final);
13298 }
13299 }
13300
13301 g_free (p_name);
13302 }
13303
13304 static ClutterActor *
clutter_actor_get_actor(ClutterAnimatable * animatable)13305 clutter_actor_get_actor (ClutterAnimatable *animatable)
13306 {
13307 return CLUTTER_ACTOR (animatable);
13308 }
13309
13310 static void
clutter_animatable_iface_init(ClutterAnimatableInterface * iface)13311 clutter_animatable_iface_init (ClutterAnimatableInterface *iface)
13312 {
13313 iface->find_property = clutter_actor_find_property;
13314 iface->get_initial_state = clutter_actor_get_initial_state;
13315 iface->set_final_state = clutter_actor_set_final_state;
13316 iface->get_actor = clutter_actor_get_actor;
13317 }
13318
13319 /**
13320 * clutter_actor_transform_stage_point:
13321 * @self: A #ClutterActor
13322 * @x: (in): x screen coordinate of the point to unproject
13323 * @y: (in): y screen coordinate of the point to unproject
13324 * @x_out: (out): return location for the unprojected x coordinance
13325 * @y_out: (out): return location for the unprojected y coordinance
13326 *
13327 * This function translates screen coordinates (@x, @y) to
13328 * coordinates relative to the actor. For example, it can be used to translate
13329 * screen events from global screen coordinates into actor-local coordinates.
13330 *
13331 * The conversion can fail, notably if the transform stack results in the
13332 * actor being projected on the screen as a mere line.
13333 *
13334 * The conversion should not be expected to be pixel-perfect due to the
13335 * nature of the operation. In general the error grows when the skewing
13336 * of the actor rectangle on screen increases.
13337 *
13338 * This function can be computationally intensive.
13339 *
13340 * This function only works when the allocation is up-to-date, i.e. inside of
13341 * the #ClutterActorClass.paint() implementation
13342 *
13343 * Return value: %TRUE if conversion was successful.
13344 *
13345 * Since: 0.6
13346 */
13347 gboolean
clutter_actor_transform_stage_point(ClutterActor * self,gfloat x,gfloat y,gfloat * x_out,gfloat * y_out)13348 clutter_actor_transform_stage_point (ClutterActor *self,
13349 gfloat x,
13350 gfloat y,
13351 gfloat *x_out,
13352 gfloat *y_out)
13353 {
13354 graphene_point3d_t v[4];
13355 double ST[3][3];
13356 double RQ[3][3];
13357 int du, dv;
13358 double px, py;
13359 double det;
13360 float xf, yf, wf;
13361 ClutterActorPrivate *priv;
13362
13363 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13364
13365 priv = self->priv;
13366
13367 /* This implementation is based on the quad -> quad projection algorithm
13368 * described by Paul Heckbert in:
13369 *
13370 * http://www.cs.cmu.edu/~ph/texfund/texfund.pdf
13371 *
13372 * and the sample implementation at:
13373 *
13374 * http://www.cs.cmu.edu/~ph/src/texfund/
13375 *
13376 * Our texture is a rectangle with origin [0, 0], so we are mapping from
13377 * quad to rectangle only, which significantly simplifies things.
13378 * Function calls have been unrolled.
13379 */
13380 clutter_actor_get_abs_allocation_vertices (self, v);
13381
13382 /* Keeping these as ints simplifies the multiplication (no significant
13383 * loss of precision here).
13384 */
13385 du = ceilf (priv->allocation.x2 - priv->allocation.x1);
13386 dv = ceilf (priv->allocation.y2 - priv->allocation.y1);
13387
13388 if (du == 0 || dv == 0)
13389 return FALSE;
13390
13391 #define DET(a,b,c,d) (((a) * (d)) - ((b) * (c)))
13392
13393 /* First, find mapping from unit uv square to xy quadrilateral; this
13394 * equivalent to the pmap_square_quad() functions in the sample
13395 * implementation, which we can simplify, since our target is always
13396 * a rectangle.
13397 */
13398 px = v[0].x - v[1].x + v[3].x - v[2].x;
13399 py = v[0].y - v[1].y + v[3].y - v[2].y;
13400
13401 if ((int) px == 0 && (int) py == 0)
13402 {
13403 /* affine transform */
13404 RQ[0][0] = v[1].x - v[0].x;
13405 RQ[1][0] = v[3].x - v[1].x;
13406 RQ[2][0] = v[0].x;
13407 RQ[0][1] = v[1].y - v[0].y;
13408 RQ[1][1] = v[3].y - v[1].y;
13409 RQ[2][1] = v[0].y;
13410 RQ[0][2] = 0.0;
13411 RQ[1][2] = 0.0;
13412 RQ[2][2] = 1.0;
13413 }
13414 else
13415 {
13416 /* projective transform */
13417 double dx1, dx2, dy1, dy2;
13418
13419 dx1 = v[1].x - v[3].x;
13420 dx2 = v[2].x - v[3].x;
13421 dy1 = v[1].y - v[3].y;
13422 dy2 = v[2].y - v[3].y;
13423
13424 det = DET (dx1, dx2, dy1, dy2);
13425 if (fabs (det) <= DBL_EPSILON)
13426 return FALSE;
13427
13428 RQ[0][2] = DET (px, dx2, py, dy2) / det;
13429 RQ[1][2] = DET (dx1, px, dy1, py) / det;
13430 RQ[2][2] = 1.0;
13431 RQ[0][0] = v[1].x - v[0].x + (RQ[0][2] * v[1].x);
13432 RQ[1][0] = v[2].x - v[0].x + (RQ[1][2] * v[2].x);
13433 RQ[2][0] = v[0].x;
13434 RQ[0][1] = v[1].y - v[0].y + (RQ[0][2] * v[1].y);
13435 RQ[1][1] = v[2].y - v[0].y + (RQ[1][2] * v[2].y);
13436 RQ[2][1] = v[0].y;
13437 }
13438
13439 /*
13440 * Now combine with transform from our rectangle (u0,v0,u1,v1) to unit
13441 * square. Since our rectangle is based at 0,0 we only need to scale.
13442 */
13443 RQ[0][0] /= du;
13444 RQ[1][0] /= dv;
13445 RQ[0][1] /= du;
13446 RQ[1][1] /= dv;
13447 RQ[0][2] /= du;
13448 RQ[1][2] /= dv;
13449
13450 /*
13451 * Now RQ is transform from uv rectangle to xy quadrilateral; we need an
13452 * inverse of that.
13453 */
13454 ST[0][0] = DET (RQ[1][1], RQ[1][2], RQ[2][1], RQ[2][2]);
13455 ST[1][0] = DET (RQ[1][2], RQ[1][0], RQ[2][2], RQ[2][0]);
13456 ST[2][0] = DET (RQ[1][0], RQ[1][1], RQ[2][0], RQ[2][1]);
13457 ST[0][1] = DET (RQ[2][1], RQ[2][2], RQ[0][1], RQ[0][2]);
13458 ST[1][1] = DET (RQ[2][2], RQ[2][0], RQ[0][2], RQ[0][0]);
13459 ST[2][1] = DET (RQ[2][0], RQ[2][1], RQ[0][0], RQ[0][1]);
13460 ST[0][2] = DET (RQ[0][1], RQ[0][2], RQ[1][1], RQ[1][2]);
13461 ST[1][2] = DET (RQ[0][2], RQ[0][0], RQ[1][2], RQ[1][0]);
13462 ST[2][2] = DET (RQ[0][0], RQ[0][1], RQ[1][0], RQ[1][1]);
13463
13464 /*
13465 * Check the resulting matrix is OK.
13466 */
13467 det = (RQ[0][0] * ST[0][0])
13468 + (RQ[0][1] * ST[0][1])
13469 + (RQ[0][2] * ST[0][2]);
13470 if (fabs (det) <= DBL_EPSILON)
13471 return FALSE;
13472
13473 /*
13474 * Now transform our point with the ST matrix; the notional w
13475 * coordinate is 1, hence the last part is simply added.
13476 */
13477 xf = x * ST[0][0] + y * ST[1][0] + ST[2][0];
13478 yf = x * ST[0][1] + y * ST[1][1] + ST[2][1];
13479 wf = x * ST[0][2] + y * ST[1][2] + ST[2][2];
13480
13481 if (x_out)
13482 *x_out = xf / wf;
13483
13484 if (y_out)
13485 *y_out = yf / wf;
13486
13487 #undef DET
13488
13489 return TRUE;
13490 }
13491
13492 /**
13493 * clutter_actor_is_rotated:
13494 * @self: a #ClutterActor
13495 *
13496 * Checks whether any rotation is applied to the actor.
13497 *
13498 * Return value: %TRUE if the actor is rotated.
13499 *
13500 * Since: 0.6
13501 */
13502 gboolean
clutter_actor_is_rotated(ClutterActor * self)13503 clutter_actor_is_rotated (ClutterActor *self)
13504 {
13505 const ClutterTransformInfo *info;
13506
13507 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13508
13509 info = _clutter_actor_get_transform_info_or_defaults (self);
13510
13511 if (info->rx_angle || info->ry_angle || info->rz_angle)
13512 return TRUE;
13513
13514 return FALSE;
13515 }
13516
13517 /**
13518 * clutter_actor_is_scaled:
13519 * @self: a #ClutterActor
13520 *
13521 * Checks whether the actor is scaled in either dimension.
13522 *
13523 * Return value: %TRUE if the actor is scaled.
13524 *
13525 * Since: 0.6
13526 */
13527 gboolean
clutter_actor_is_scaled(ClutterActor * self)13528 clutter_actor_is_scaled (ClutterActor *self)
13529 {
13530 const ClutterTransformInfo *info;
13531
13532 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13533
13534 info = _clutter_actor_get_transform_info_or_defaults (self);
13535
13536 if (info->scale_x != 1.0 || info->scale_y != 1.0)
13537 return TRUE;
13538
13539 return FALSE;
13540 }
13541
13542 ClutterActor *
_clutter_actor_get_stage_internal(ClutterActor * actor)13543 _clutter_actor_get_stage_internal (ClutterActor *actor)
13544 {
13545 while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor))
13546 actor = actor->priv->parent;
13547
13548 return actor;
13549 }
13550
13551 /**
13552 * clutter_actor_get_stage:
13553 * @actor: a #ClutterActor
13554 *
13555 * Retrieves the #ClutterStage where @actor is contained.
13556 *
13557 * Return value: (transfer none) (type Clutter.Stage): the stage
13558 * containing the actor, or %NULL
13559 *
13560 * Since: 0.8
13561 */
13562 ClutterActor *
clutter_actor_get_stage(ClutterActor * actor)13563 clutter_actor_get_stage (ClutterActor *actor)
13564 {
13565 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
13566
13567 return _clutter_actor_get_stage_internal (actor);
13568 }
13569
13570 /**
13571 * clutter_actor_allocate_available_size:
13572 * @self: a #ClutterActor
13573 * @x: the actor's X coordinate
13574 * @y: the actor's Y coordinate
13575 * @available_width: the maximum available width, or -1 to use the
13576 * actor's natural width
13577 * @available_height: the maximum available height, or -1 to use the
13578 * actor's natural height
13579 *
13580 * Allocates @self taking into account the #ClutterActor's
13581 * preferred size, but limiting it to the maximum available width
13582 * and height provided.
13583 *
13584 * This function will do the right thing when dealing with the
13585 * actor's request mode.
13586 *
13587 * The implementation of this function is equivalent to:
13588 *
13589 * |[<!-- language="C" -->
13590 * if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
13591 * {
13592 * clutter_actor_get_preferred_width (self, available_height,
13593 * &min_width,
13594 * &natural_width);
13595 * width = CLAMP (natural_width, min_width, available_width);
13596 *
13597 * clutter_actor_get_preferred_height (self, width,
13598 * &min_height,
13599 * &natural_height);
13600 * height = CLAMP (natural_height, min_height, available_height);
13601 * }
13602 * else if (request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
13603 * {
13604 * clutter_actor_get_preferred_height (self, available_width,
13605 * &min_height,
13606 * &natural_height);
13607 * height = CLAMP (natural_height, min_height, available_height);
13608 *
13609 * clutter_actor_get_preferred_width (self, height,
13610 * &min_width,
13611 * &natural_width);
13612 * width = CLAMP (natural_width, min_width, available_width);
13613 * }
13614 * else if (request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
13615 * {
13616 * clutter_content_get_preferred_size (content, &natural_width, &natural_height);
13617 *
13618 * width = CLAMP (natural_width, 0, available_width);
13619 * height = CLAMP (natural_height, 0, available_height);
13620 * }
13621 *
13622 * box.x1 = x; box.y1 = y;
13623 * box.x2 = box.x1 + available_width;
13624 * box.y2 = box.y1 + available_height;
13625 * clutter_actor_allocate (self, &box);
13626 * ]|
13627 *
13628 * This function can be used by fluid layout managers to allocate
13629 * an actor's preferred size without making it bigger than the area
13630 * available for the container.
13631 *
13632 * Since: 1.0
13633 */
13634 void
clutter_actor_allocate_available_size(ClutterActor * self,gfloat x,gfloat y,gfloat available_width,gfloat available_height)13635 clutter_actor_allocate_available_size (ClutterActor *self,
13636 gfloat x,
13637 gfloat y,
13638 gfloat available_width,
13639 gfloat available_height)
13640 {
13641 ClutterActorPrivate *priv;
13642 gfloat width, height;
13643 gfloat min_width, min_height;
13644 gfloat natural_width, natural_height;
13645 ClutterActorBox box;
13646
13647 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13648
13649 priv = self->priv;
13650
13651 width = height = 0.0;
13652
13653 switch (priv->request_mode)
13654 {
13655 case CLUTTER_REQUEST_HEIGHT_FOR_WIDTH:
13656 clutter_actor_get_preferred_width (self, available_height,
13657 &min_width,
13658 &natural_width);
13659 width = CLAMP (natural_width, min_width, available_width);
13660
13661 clutter_actor_get_preferred_height (self, width,
13662 &min_height,
13663 &natural_height);
13664 height = CLAMP (natural_height, min_height, available_height);
13665 break;
13666
13667 case CLUTTER_REQUEST_WIDTH_FOR_HEIGHT:
13668 clutter_actor_get_preferred_height (self, available_width,
13669 &min_height,
13670 &natural_height);
13671 height = CLAMP (natural_height, min_height, available_height);
13672
13673 clutter_actor_get_preferred_width (self, height,
13674 &min_width,
13675 &natural_width);
13676 width = CLAMP (natural_width, min_width, available_width);
13677 break;
13678
13679 case CLUTTER_REQUEST_CONTENT_SIZE:
13680 if (priv->content != NULL)
13681 {
13682 clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
13683
13684 width = CLAMP (natural_width, 0, available_width);
13685 height = CLAMP (natural_height, 0, available_height);
13686 }
13687 break;
13688 }
13689
13690
13691 box.x1 = x;
13692 box.y1 = y;
13693 box.x2 = box.x1 + width;
13694 box.y2 = box.y1 + height;
13695 clutter_actor_allocate (self, &box);
13696 }
13697
13698 /**
13699 * clutter_actor_allocate_preferred_size:
13700 * @self: a #ClutterActor
13701 * @x: the actor's X coordinate
13702 * @y: the actor's Y coordinate
13703 *
13704 * Allocates the natural size of @self.
13705 *
13706 * This function is a utility call for #ClutterActor implementations
13707 * that allocates the actor's preferred natural size. It can be used
13708 * by fixed layout managers (like #ClutterGroup or so called
13709 * 'composite actors') inside the ClutterActor::allocate
13710 * implementation to give each child exactly how much space it
13711 * requires, regardless of the size of the parent.
13712 *
13713 * This function is not meant to be used by applications. It is also
13714 * not meant to be used outside the implementation of the
13715 * #ClutterActorClass.allocate virtual function.
13716 *
13717 * Since: 0.8
13718 */
13719 void
clutter_actor_allocate_preferred_size(ClutterActor * self,float x,float y)13720 clutter_actor_allocate_preferred_size (ClutterActor *self,
13721 float x,
13722 float y)
13723 {
13724 gfloat natural_width, natural_height;
13725 ClutterActorBox actor_box;
13726
13727 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13728
13729 clutter_actor_get_preferred_size (self,
13730 NULL, NULL,
13731 &natural_width,
13732 &natural_height);
13733
13734 actor_box.x1 = x;
13735 actor_box.y1 = y;
13736 actor_box.x2 = actor_box.x1 + natural_width;
13737 actor_box.y2 = actor_box.y1 + natural_height;
13738
13739 clutter_actor_allocate (self, &actor_box);
13740 }
13741
13742 /**
13743 * clutter_actor_allocate_align_fill:
13744 * @self: a #ClutterActor
13745 * @box: a #ClutterActorBox, containing the available width and height
13746 * @x_align: the horizontal alignment, between 0 and 1
13747 * @y_align: the vertical alignment, between 0 and 1
13748 * @x_fill: whether the actor should fill horizontally
13749 * @y_fill: whether the actor should fill vertically
13750 *
13751 * Allocates @self by taking into consideration the available allocation
13752 * area; an alignment factor on either axis; and whether the actor should
13753 * fill the allocation on either axis.
13754 *
13755 * The @box should contain the available allocation width and height;
13756 * if the x1 and y1 members of #ClutterActorBox are not set to 0, the
13757 * allocation will be offset by their value.
13758 *
13759 * This function takes into consideration the geometry request specified by
13760 * the #ClutterActor:request-mode property, and the text direction.
13761 *
13762 * This function is useful for fluid layout managers using legacy alignment
13763 * flags. Newly written layout managers should use the #ClutterActor:x-align
13764 * and #ClutterActor:y-align properties, instead, and just call
13765 * clutter_actor_allocate() inside their #ClutterActorClass.allocate()
13766 * implementation.
13767 *
13768 * Since: 1.4
13769 */
13770 void
clutter_actor_allocate_align_fill(ClutterActor * self,const ClutterActorBox * box,gdouble x_align,gdouble y_align,gboolean x_fill,gboolean y_fill)13771 clutter_actor_allocate_align_fill (ClutterActor *self,
13772 const ClutterActorBox *box,
13773 gdouble x_align,
13774 gdouble y_align,
13775 gboolean x_fill,
13776 gboolean y_fill)
13777 {
13778 ClutterActorPrivate *priv;
13779 ClutterActorBox allocation = CLUTTER_ACTOR_BOX_INIT_ZERO;
13780 gfloat x_offset, y_offset;
13781 gfloat available_width, available_height;
13782 gfloat child_width = 0.f, child_height = 0.f;
13783
13784 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13785 g_return_if_fail (box != NULL);
13786 g_return_if_fail (x_align >= 0.0 && x_align <= 1.0);
13787 g_return_if_fail (y_align >= 0.0 && y_align <= 1.0);
13788
13789 priv = self->priv;
13790
13791 clutter_actor_box_get_origin (box, &x_offset, &y_offset);
13792 clutter_actor_box_get_size (box, &available_width, &available_height);
13793
13794 if (available_width <= 0)
13795 available_width = 0.f;
13796
13797 if (available_height <= 0)
13798 available_height = 0.f;
13799
13800 allocation.x1 = x_offset;
13801 allocation.y1 = y_offset;
13802
13803 if (available_width == 0.f && available_height == 0.f)
13804 goto out;
13805
13806 if (x_fill)
13807 child_width = available_width;
13808
13809 if (y_fill)
13810 child_height = available_height;
13811
13812 /* if we are filling horizontally and vertically then we're done */
13813 if (x_fill && y_fill)
13814 goto out;
13815
13816 if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
13817 {
13818 gfloat min_width, natural_width;
13819 gfloat min_height, natural_height;
13820
13821 if (!x_fill)
13822 {
13823 clutter_actor_get_preferred_width (self, available_height,
13824 &min_width,
13825 &natural_width);
13826
13827 child_width = CLAMP (natural_width, min_width, available_width);
13828 }
13829
13830 if (!y_fill)
13831 {
13832 clutter_actor_get_preferred_height (self, child_width,
13833 &min_height,
13834 &natural_height);
13835
13836 child_height = CLAMP (natural_height, min_height, available_height);
13837 }
13838 }
13839 else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
13840 {
13841 gfloat min_width, natural_width;
13842 gfloat min_height, natural_height;
13843
13844 if (!y_fill)
13845 {
13846 clutter_actor_get_preferred_height (self, available_width,
13847 &min_height,
13848 &natural_height);
13849
13850 child_height = CLAMP (natural_height, min_height, available_height);
13851 }
13852
13853 if (!x_fill)
13854 {
13855 clutter_actor_get_preferred_width (self, child_height,
13856 &min_width,
13857 &natural_width);
13858
13859 child_width = CLAMP (natural_width, min_width, available_width);
13860 }
13861 }
13862 else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
13863 {
13864 gfloat natural_width, natural_height;
13865
13866 clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
13867
13868 if (!x_fill)
13869 child_width = CLAMP (natural_width, 0, available_width);
13870
13871 if (!y_fill)
13872 child_height = CLAMP (natural_height, 0, available_height);
13873 }
13874
13875 /* invert the horizontal alignment for RTL languages */
13876 if (priv->text_direction == CLUTTER_TEXT_DIRECTION_RTL)
13877 x_align = 1.0 - x_align;
13878
13879 if (!x_fill)
13880 allocation.x1 += ((available_width - child_width) * x_align);
13881
13882 if (!y_fill)
13883 allocation.y1 += ((available_height - child_height) * y_align);
13884
13885 out:
13886
13887 allocation.x1 = floorf (allocation.x1);
13888 allocation.y1 = floorf (allocation.y1);
13889 allocation.x2 = ceilf (allocation.x1 + MAX (child_width, 0));
13890 allocation.y2 = ceilf (allocation.y1 + MAX (child_height, 0));
13891
13892 clutter_actor_allocate (self, &allocation);
13893 }
13894
13895 /**
13896 * clutter_actor_grab_key_focus:
13897 * @self: a #ClutterActor
13898 *
13899 * Sets the key focus of the #ClutterStage including @self
13900 * to this #ClutterActor.
13901 *
13902 * Since: 1.0
13903 */
13904 void
clutter_actor_grab_key_focus(ClutterActor * self)13905 clutter_actor_grab_key_focus (ClutterActor *self)
13906 {
13907 ClutterActor *stage;
13908
13909 g_return_if_fail (CLUTTER_IS_ACTOR (self));
13910
13911 if (self->priv->has_key_focus)
13912 return;
13913
13914 stage = _clutter_actor_get_stage_internal (self);
13915 if (stage != NULL)
13916 clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self);
13917 }
13918
13919 static void
update_pango_context(ClutterBackend * backend,PangoContext * context)13920 update_pango_context (ClutterBackend *backend,
13921 PangoContext *context)
13922 {
13923 ClutterSettings *settings;
13924 PangoFontDescription *font_desc;
13925 const cairo_font_options_t *font_options;
13926 gchar *font_name;
13927 PangoDirection pango_dir;
13928 gdouble resolution;
13929
13930 settings = clutter_settings_get_default ();
13931
13932 /* update the text direction */
13933 if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
13934 pango_dir = PANGO_DIRECTION_RTL;
13935 else
13936 pango_dir = PANGO_DIRECTION_LTR;
13937
13938 pango_context_set_base_dir (context, pango_dir);
13939
13940 g_object_get (settings, "font-name", &font_name, NULL);
13941
13942 /* get the configuration for the PangoContext from the backend */
13943 font_options = clutter_backend_get_font_options (backend);
13944 resolution = clutter_backend_get_resolution (backend);
13945
13946 font_desc = pango_font_description_from_string (font_name);
13947
13948 if (resolution < 0)
13949 resolution = 96.0; /* fall back */
13950
13951 pango_context_set_font_description (context, font_desc);
13952 pango_cairo_context_set_font_options (context, font_options);
13953 pango_cairo_context_set_resolution (context, resolution);
13954
13955 pango_font_description_free (font_desc);
13956 g_free (font_name);
13957 }
13958
13959 /**
13960 * clutter_actor_get_pango_context:
13961 * @self: a #ClutterActor
13962 *
13963 * Retrieves the #PangoContext for @self. The actor's #PangoContext
13964 * is already configured using the appropriate font map, resolution
13965 * and font options.
13966 *
13967 * Unlike clutter_actor_create_pango_context(), this context is owend
13968 * by the #ClutterActor and it will be updated each time the options
13969 * stored by the #ClutterBackend change.
13970 *
13971 * You can use the returned #PangoContext to create a #PangoLayout
13972 * and render text using cogl_pango_show_layout() to reuse the
13973 * glyphs cache also used by Clutter.
13974 *
13975 * Return value: (transfer none): the #PangoContext for a #ClutterActor.
13976 * The returned #PangoContext is owned by the actor and should not be
13977 * unreferenced by the application code
13978 *
13979 * Since: 1.0
13980 */
13981 PangoContext *
clutter_actor_get_pango_context(ClutterActor * self)13982 clutter_actor_get_pango_context (ClutterActor *self)
13983 {
13984 ClutterActorPrivate *priv;
13985 ClutterBackend *backend = clutter_get_default_backend ();
13986
13987 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
13988
13989 priv = self->priv;
13990
13991 if (G_UNLIKELY (priv->pango_context == NULL))
13992 {
13993 priv->pango_context = clutter_actor_create_pango_context (self);
13994
13995 priv->resolution_changed_id =
13996 g_signal_connect_object (backend, "resolution-changed",
13997 G_CALLBACK (update_pango_context), priv->pango_context, 0);
13998 priv->font_changed_id =
13999 g_signal_connect_object (backend, "font-changed",
14000 G_CALLBACK (update_pango_context), priv->pango_context, 0);
14001 }
14002 else
14003 update_pango_context (backend, priv->pango_context);
14004
14005 return priv->pango_context;
14006 }
14007
14008 /**
14009 * clutter_actor_create_pango_context:
14010 * @self: a #ClutterActor
14011 *
14012 * Creates a #PangoContext for the given actor. The #PangoContext
14013 * is already configured using the appropriate font map, resolution
14014 * and font options.
14015 *
14016 * See also clutter_actor_get_pango_context().
14017 *
14018 * Return value: (transfer full): the newly created #PangoContext.
14019 * Use g_object_unref() on the returned value to deallocate its
14020 * resources
14021 *
14022 * Since: 1.0
14023 */
14024 PangoContext *
clutter_actor_create_pango_context(ClutterActor * self)14025 clutter_actor_create_pango_context (ClutterActor *self)
14026 {
14027 CoglPangoFontMap *font_map;
14028 PangoContext *context;
14029
14030 font_map = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
14031
14032 context = cogl_pango_font_map_create_context (font_map);
14033 update_pango_context (clutter_get_default_backend (), context);
14034 pango_context_set_language (context, pango_language_get_default ());
14035
14036 return context;
14037 }
14038
14039 /**
14040 * clutter_actor_create_pango_layout:
14041 * @self: a #ClutterActor
14042 * @text: (allow-none): the text to set on the #PangoLayout, or %NULL
14043 *
14044 * Creates a new #PangoLayout from the same #PangoContext used
14045 * by the #ClutterActor. The #PangoLayout is already configured
14046 * with the font map, resolution and font options, and the
14047 * given @text.
14048 *
14049 * If you want to keep around a #PangoLayout created by this
14050 * function you will have to connect to the #ClutterBackend::font-changed
14051 * and #ClutterBackend::resolution-changed signals, and call
14052 * pango_layout_context_changed() in response to them.
14053 *
14054 * Return value: (transfer full): the newly created #PangoLayout.
14055 * Use g_object_unref() when done
14056 *
14057 * Since: 1.0
14058 */
14059 PangoLayout *
clutter_actor_create_pango_layout(ClutterActor * self,const gchar * text)14060 clutter_actor_create_pango_layout (ClutterActor *self,
14061 const gchar *text)
14062 {
14063 PangoContext *context;
14064 PangoLayout *layout;
14065
14066 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
14067
14068 context = clutter_actor_get_pango_context (self);
14069 layout = pango_layout_new (context);
14070
14071 if (text)
14072 pango_layout_set_text (layout, text, -1);
14073
14074 return layout;
14075 }
14076
14077 /**
14078 * clutter_actor_set_opacity_override:
14079 * @self: a #ClutterActor
14080 * @opacity: the override opacity value, or -1 to reset
14081 *
14082 * Allows overriding the calculated paint opacity (as returned by
14083 * clutter_actor_get_paint_opacity()). This is used internally by
14084 * ClutterClone and ClutterOffscreenEffect, and should be used by
14085 * actors that need to mimic those.
14086 *
14087 * In almost all cases this should not used by applications.
14088 *
14089 * Stability: unstable
14090 */
14091 void
clutter_actor_set_opacity_override(ClutterActor * self,gint opacity)14092 clutter_actor_set_opacity_override (ClutterActor *self,
14093 gint opacity)
14094 {
14095 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14096
14097 /* ensure bounds */
14098 if (opacity >= 0)
14099 opacity = CLAMP (opacity, 0, 255);
14100 else
14101 opacity = -1;
14102
14103 self->priv->opacity_override = opacity;
14104 }
14105
14106 /**
14107 * clutter_actor_get_opacity_override:
14108 * @self: a #ClutterActor
14109 *
14110 * See clutter_actor_set_opacity_override()
14111 *
14112 * Returns: the override value for the actor's opacity, or -1 if no override
14113 * is set.
14114 *
14115 * Since: 1.22
14116 *
14117 * Stability: unstable
14118 */
14119 gint
clutter_actor_get_opacity_override(ClutterActor * self)14120 clutter_actor_get_opacity_override (ClutterActor *self)
14121 {
14122 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), -1);
14123
14124 return self->priv->opacity_override;
14125 }
14126
14127 /**
14128 * clutter_actor_inhibit_culling:
14129 * @actor: a #ClutterActor
14130 *
14131 * Increases the culling inhibitor counter. Inhibiting culling
14132 * forces the actor to be painted even when outside the visible
14133 * bounds of the stage view.
14134 *
14135 * This is usually necessary when an actor is being painted on
14136 * another paint context.
14137 *
14138 * Pair with clutter_actor_uninhibit_culling() when the actor doesn't
14139 * need to be painted anymore.
14140 */
14141 void
clutter_actor_inhibit_culling(ClutterActor * actor)14142 clutter_actor_inhibit_culling (ClutterActor *actor)
14143 {
14144 ClutterActorPrivate *priv;
14145
14146 g_return_if_fail (CLUTTER_IS_ACTOR (actor));
14147
14148 priv = actor->priv;
14149
14150 priv->inhibit_culling_counter++;
14151 _clutter_actor_set_enable_paint_unmapped (actor, TRUE);
14152 }
14153
14154 /**
14155 * clutter_actor_uninhibit_culling:
14156 * @actor: a #ClutterActor
14157 *
14158 * Decreases the culling inhibitor counter. See clutter_actor_inhibit_culling()
14159 * for when inhibit culling is necessary.
14160 *
14161 * Calling this function without a matching call to
14162 * clutter_actor_inhibit_culling() is a programming error.
14163 */
14164 void
clutter_actor_uninhibit_culling(ClutterActor * actor)14165 clutter_actor_uninhibit_culling (ClutterActor *actor)
14166 {
14167 ClutterActorPrivate *priv;
14168
14169 g_return_if_fail (CLUTTER_IS_ACTOR (actor));
14170
14171 priv = actor->priv;
14172
14173 if (priv->inhibit_culling_counter == 0)
14174 {
14175 g_critical ("Unpaired call to clutter_actor_uninhibit_culling");
14176 return;
14177 }
14178
14179 priv->inhibit_culling_counter--;
14180 if (priv->inhibit_culling_counter == 0)
14181 _clutter_actor_set_enable_paint_unmapped (actor, FALSE);
14182 }
14183
14184 /* Allows you to disable applying the actors model view transform during
14185 * a paint. Used by ClutterClone. */
14186 void
_clutter_actor_set_enable_model_view_transform(ClutterActor * self,gboolean enable)14187 _clutter_actor_set_enable_model_view_transform (ClutterActor *self,
14188 gboolean enable)
14189 {
14190 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14191
14192 self->priv->enable_model_view_transform = enable;
14193 }
14194
14195 void
_clutter_actor_set_enable_paint_unmapped(ClutterActor * self,gboolean enable)14196 _clutter_actor_set_enable_paint_unmapped (ClutterActor *self,
14197 gboolean enable)
14198 {
14199 ClutterActorPrivate *priv;
14200
14201 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14202
14203 priv = self->priv;
14204
14205 if (priv->enable_paint_unmapped == enable)
14206 return;
14207
14208 priv->enable_paint_unmapped = enable;
14209
14210 if (enable)
14211 {
14212 push_in_paint_unmapped_branch (self, 1);
14213
14214 /* Make sure that the parents of the widget are realized first;
14215 * otherwise checks in clutter_actor_update_map_state() will
14216 * fail.
14217 */
14218 clutter_actor_realize (self);
14219
14220 /* If the actor isn't ultimately connected to a toplevel, it can't be
14221 * realized or painted.
14222 */
14223 if (CLUTTER_ACTOR_IS_REALIZED (self))
14224 clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
14225 }
14226 else
14227 {
14228 clutter_actor_update_map_state (self, MAP_STATE_CHECK);
14229 pop_in_paint_unmapped_branch (self, 1);
14230 }
14231 }
14232
14233 /**
14234 * clutter_actor_get_flags:
14235 * @self: a #ClutterActor
14236 *
14237 * Retrieves the flags set on @self
14238 *
14239 * Return value: a bitwise or of #ClutterActorFlags or 0
14240 *
14241 * Since: 1.0
14242 */
14243 ClutterActorFlags
clutter_actor_get_flags(ClutterActor * self)14244 clutter_actor_get_flags (ClutterActor *self)
14245 {
14246 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
14247
14248 return self->flags;
14249 }
14250
14251 /**
14252 * clutter_actor_set_flags:
14253 * @self: a #ClutterActor
14254 * @flags: the flags to set
14255 *
14256 * Sets @flags on @self
14257 *
14258 * This function will emit notifications for the changed properties
14259 *
14260 * Since: 1.0
14261 */
14262 void
clutter_actor_set_flags(ClutterActor * self,ClutterActorFlags flags)14263 clutter_actor_set_flags (ClutterActor *self,
14264 ClutterActorFlags flags)
14265 {
14266 ClutterActorFlags old_flags;
14267 GObject *obj;
14268 gboolean was_reactive_set, reactive_set;
14269 gboolean was_realized_set, realized_set;
14270 gboolean was_mapped_set, mapped_set;
14271 gboolean was_visible_set, visible_set;
14272
14273 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14274
14275 if (self->flags == flags)
14276 return;
14277
14278 obj = G_OBJECT (self);
14279 g_object_ref (obj);
14280 g_object_freeze_notify (obj);
14281
14282 old_flags = self->flags;
14283
14284 was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
14285 was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
14286 was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0);
14287 was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0);
14288
14289 self->flags |= flags;
14290
14291 reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
14292 realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
14293 mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0);
14294 visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0);
14295
14296 if (reactive_set != was_reactive_set)
14297 g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
14298
14299 if (realized_set != was_realized_set)
14300 g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
14301
14302 if (mapped_set != was_mapped_set)
14303 g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
14304
14305 if (visible_set != was_visible_set)
14306 g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
14307
14308 g_object_thaw_notify (obj);
14309 g_object_unref (obj);
14310 }
14311
14312 /**
14313 * clutter_actor_unset_flags:
14314 * @self: a #ClutterActor
14315 * @flags: the flags to unset
14316 *
14317 * Unsets @flags on @self
14318 *
14319 * This function will emit notifications for the changed properties
14320 *
14321 * Since: 1.0
14322 */
14323 void
clutter_actor_unset_flags(ClutterActor * self,ClutterActorFlags flags)14324 clutter_actor_unset_flags (ClutterActor *self,
14325 ClutterActorFlags flags)
14326 {
14327 ClutterActorFlags old_flags;
14328 GObject *obj;
14329 gboolean was_reactive_set, reactive_set;
14330 gboolean was_realized_set, realized_set;
14331 gboolean was_mapped_set, mapped_set;
14332 gboolean was_visible_set, visible_set;
14333
14334 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14335
14336 obj = G_OBJECT (self);
14337 g_object_freeze_notify (obj);
14338
14339 old_flags = self->flags;
14340
14341 was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
14342 was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
14343 was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0);
14344 was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0);
14345
14346 self->flags &= ~flags;
14347
14348 if (self->flags == old_flags)
14349 return;
14350
14351 reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
14352 realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
14353 mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0);
14354 visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0);
14355
14356 if (reactive_set != was_reactive_set)
14357 g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
14358
14359 if (realized_set != was_realized_set)
14360 g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
14361
14362 if (mapped_set != was_mapped_set)
14363 g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
14364
14365 if (visible_set != was_visible_set)
14366 g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
14367
14368 g_object_thaw_notify (obj);
14369 }
14370
14371 static void
clutter_actor_set_transform_internal(ClutterActor * self,const graphene_matrix_t * transform)14372 clutter_actor_set_transform_internal (ClutterActor *self,
14373 const graphene_matrix_t *transform)
14374 {
14375 ClutterTransformInfo *info;
14376 gboolean was_set;
14377 GObject *obj;
14378
14379 obj = G_OBJECT (self);
14380
14381 info = _clutter_actor_get_transform_info (self);
14382
14383 was_set = info->transform_set;
14384
14385 info->transform = *transform;
14386 info->transform_set = !graphene_matrix_is_identity (&info->transform);
14387
14388 transform_changed (self);
14389
14390 clutter_actor_queue_redraw (self);
14391
14392 g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM]);
14393
14394 if (was_set != info->transform_set)
14395 g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM_SET]);
14396 }
14397
14398 /**
14399 * clutter_actor_set_transform:
14400 * @self: a #ClutterActor
14401 * @transform: (allow-none): a #graphene_matrix_t, or %NULL to
14402 * unset a custom transformation
14403 *
14404 * Overrides the transformations of a #ClutterActor with a custom
14405 * matrix, which will be applied relative to the origin of the
14406 * actor's allocation and to the actor's pivot point.
14407 *
14408 * The #ClutterActor:transform property is animatable.
14409 *
14410 * Since: 1.12
14411 */
14412 void
clutter_actor_set_transform(ClutterActor * self,const graphene_matrix_t * transform)14413 clutter_actor_set_transform (ClutterActor *self,
14414 const graphene_matrix_t *transform)
14415 {
14416 const ClutterTransformInfo *info;
14417 graphene_matrix_t new_transform;
14418
14419 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14420
14421 info = _clutter_actor_get_transform_info_or_defaults (self);
14422
14423 if (transform != NULL)
14424 graphene_matrix_init_from_matrix (&new_transform, transform);
14425 else
14426 graphene_matrix_init_identity (&new_transform);
14427
14428 _clutter_actor_create_transition (self, obj_props[PROP_TRANSFORM],
14429 &info->transform,
14430 &new_transform);
14431 }
14432
14433 /**
14434 * clutter_actor_get_transform:
14435 * @self: a #ClutterActor
14436 * @transform: (out caller-allocates): a #graphene_matrix_t
14437 *
14438 * Retrieves the current transformation matrix of a #ClutterActor.
14439 *
14440 * Since: 1.12
14441 */
14442 void
clutter_actor_get_transform(ClutterActor * self,graphene_matrix_t * transform)14443 clutter_actor_get_transform (ClutterActor *self,
14444 graphene_matrix_t *transform)
14445 {
14446 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14447 g_return_if_fail (transform != NULL);
14448
14449 graphene_matrix_init_identity (transform);
14450 _clutter_actor_apply_modelview_transform (self, transform);
14451 }
14452
14453 void
_clutter_actor_set_in_clone_paint(ClutterActor * self,gboolean is_in_clone_paint)14454 _clutter_actor_set_in_clone_paint (ClutterActor *self,
14455 gboolean is_in_clone_paint)
14456 {
14457 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14458 self->priv->in_clone_paint = is_in_clone_paint;
14459 }
14460
14461 /**
14462 * clutter_actor_is_in_clone_paint:
14463 * @self: a #ClutterActor
14464 *
14465 * Checks whether @self is being currently painted by a #ClutterClone
14466 *
14467 * This function is useful only inside implementations of the
14468 * #ClutterActorClass.paint() virtual function.
14469 *
14470 * This function should not be used by applications
14471 *
14472 * Return value: %TRUE if the #ClutterActor is currently being painted
14473 * by a #ClutterClone, and %FALSE otherwise
14474 *
14475 * Since: 1.0
14476 */
14477 gboolean
clutter_actor_is_in_clone_paint(ClutterActor * self)14478 clutter_actor_is_in_clone_paint (ClutterActor *self)
14479 {
14480 ClutterActor *parent;
14481
14482 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14483
14484 if (self->priv->in_clone_paint)
14485 return TRUE;
14486
14487 if (self->priv->in_cloned_branch == 0)
14488 return FALSE;
14489
14490 parent = self->priv->parent;
14491 while (parent != NULL)
14492 {
14493 if (parent->priv->in_cloned_branch == 0)
14494 break;
14495
14496 if (parent->priv->in_clone_paint)
14497 return TRUE;
14498
14499 parent = parent->priv->parent;
14500 }
14501
14502 return FALSE;
14503 }
14504
14505 gboolean
clutter_actor_is_painting_unmapped(ClutterActor * self)14506 clutter_actor_is_painting_unmapped (ClutterActor *self)
14507 {
14508 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14509
14510 return self->priv->unmapped_paint_branch_counter > 0;
14511 }
14512
14513 gboolean
clutter_actor_has_damage(ClutterActor * actor)14514 clutter_actor_has_damage (ClutterActor *actor)
14515 {
14516 return actor->priv->is_dirty;
14517 }
14518
14519 static gboolean
set_direction_recursive(ClutterActor * actor,gpointer user_data)14520 set_direction_recursive (ClutterActor *actor,
14521 gpointer user_data)
14522 {
14523 ClutterTextDirection text_dir = GPOINTER_TO_INT (user_data);
14524
14525 clutter_actor_set_text_direction (actor, text_dir);
14526
14527 return TRUE;
14528 }
14529
14530 /**
14531 * clutter_actor_set_text_direction:
14532 * @self: a #ClutterActor
14533 * @text_dir: the text direction for @self
14534 *
14535 * Sets the #ClutterTextDirection for an actor
14536 *
14537 * The passed text direction must not be %CLUTTER_TEXT_DIRECTION_DEFAULT
14538 *
14539 * If @self implements #ClutterContainer then this function will recurse
14540 * inside all the children of @self (including the internal ones).
14541 *
14542 * Composite actors not implementing #ClutterContainer, or actors requiring
14543 * special handling when the text direction changes, should connect to
14544 * the #GObject::notify signal for the #ClutterActor:text-direction property
14545 *
14546 * Since: 1.2
14547 */
14548 void
clutter_actor_set_text_direction(ClutterActor * self,ClutterTextDirection text_dir)14549 clutter_actor_set_text_direction (ClutterActor *self,
14550 ClutterTextDirection text_dir)
14551 {
14552 ClutterActorPrivate *priv;
14553
14554 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14555 g_return_if_fail (text_dir != CLUTTER_TEXT_DIRECTION_DEFAULT);
14556
14557 priv = self->priv;
14558
14559 if (priv->text_direction != text_dir)
14560 {
14561 priv->text_direction = text_dir;
14562
14563 /* we need to emit the notify::text-direction first, so that
14564 * the sub-classes can catch that and do specific handling of
14565 * the text direction; see clutter_text_direction_changed_cb()
14566 * inside clutter-text.c
14567 */
14568 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT_DIRECTION]);
14569
14570 _clutter_actor_foreach_child (self, set_direction_recursive,
14571 GINT_TO_POINTER (text_dir));
14572
14573 clutter_actor_queue_relayout (self);
14574 }
14575 }
14576
14577 void
_clutter_actor_set_has_pointer(ClutterActor * self,gboolean has_pointer)14578 _clutter_actor_set_has_pointer (ClutterActor *self,
14579 gboolean has_pointer)
14580 {
14581 ClutterActorPrivate *priv = self->priv;
14582
14583 if (priv->has_pointer != has_pointer)
14584 {
14585 priv->has_pointer = has_pointer;
14586
14587 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_POINTER]);
14588 }
14589 }
14590
14591 void
_clutter_actor_set_has_key_focus(ClutterActor * self,gboolean has_key_focus)14592 _clutter_actor_set_has_key_focus (ClutterActor *self,
14593 gboolean has_key_focus)
14594 {
14595 ClutterActorPrivate *priv = self->priv;
14596
14597 if (priv->has_key_focus != has_key_focus)
14598 {
14599 priv->has_key_focus = has_key_focus;
14600
14601 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
14602 return;
14603
14604 if (has_key_focus)
14605 g_signal_emit (self, actor_signals[KEY_FOCUS_IN], 0);
14606 else
14607 g_signal_emit (self, actor_signals[KEY_FOCUS_OUT], 0);
14608 }
14609 }
14610
14611 /**
14612 * clutter_actor_get_text_direction:
14613 * @self: a #ClutterActor
14614 *
14615 * Retrieves the value set using clutter_actor_set_text_direction()
14616 *
14617 * If no text direction has been previously set, the default text
14618 * direction, as returned by clutter_get_default_text_direction(), will
14619 * be returned instead
14620 *
14621 * Return value: the #ClutterTextDirection for the actor
14622 *
14623 * Since: 1.2
14624 */
14625 ClutterTextDirection
clutter_actor_get_text_direction(ClutterActor * self)14626 clutter_actor_get_text_direction (ClutterActor *self)
14627 {
14628 ClutterActorPrivate *priv;
14629
14630 g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
14631 CLUTTER_TEXT_DIRECTION_LTR);
14632
14633 priv = self->priv;
14634
14635 /* if no direction has been set yet use the default */
14636 if (priv->text_direction == CLUTTER_TEXT_DIRECTION_DEFAULT)
14637 priv->text_direction = clutter_get_default_text_direction ();
14638
14639 return priv->text_direction;
14640 }
14641
14642 /**
14643 * clutter_actor_has_pointer:
14644 * @self: a #ClutterActor
14645 *
14646 * Checks whether an actor contains the pointer of a
14647 * #ClutterInputDevice
14648 *
14649 * Return value: %TRUE if the actor contains the pointer, and
14650 * %FALSE otherwise
14651 *
14652 * Since: 1.2
14653 */
14654 gboolean
clutter_actor_has_pointer(ClutterActor * self)14655 clutter_actor_has_pointer (ClutterActor *self)
14656 {
14657 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14658
14659 return self->priv->has_pointer;
14660 }
14661
14662 /**
14663 * clutter_actor_has_allocation:
14664 * @self: a #ClutterActor
14665 *
14666 * Checks if the actor has an up-to-date allocation assigned to
14667 * it. This means that the actor should have an allocation: it's
14668 * visible and has a parent. It also means that there is no
14669 * outstanding relayout request in progress for the actor or its
14670 * children (There might be other outstanding layout requests in
14671 * progress that will cause the actor to get a new allocation
14672 * when the stage is laid out, however).
14673 *
14674 * If this function returns %FALSE, then the actor will normally
14675 * be allocated before it is next drawn on the screen.
14676 *
14677 * Return value: %TRUE if the actor has an up-to-date allocation
14678 *
14679 * Since: 1.4
14680 */
14681 gboolean
clutter_actor_has_allocation(ClutterActor * self)14682 clutter_actor_has_allocation (ClutterActor *self)
14683 {
14684 ClutterActorPrivate *priv;
14685
14686 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14687
14688 priv = self->priv;
14689
14690 return priv->parent != NULL &&
14691 CLUTTER_ACTOR_IS_VISIBLE (self) &&
14692 !priv->needs_allocation;
14693 }
14694
14695 /**
14696 * clutter_actor_add_action:
14697 * @self: a #ClutterActor
14698 * @action: a #ClutterAction
14699 *
14700 * Adds @action to the list of actions applied to @self
14701 *
14702 * A #ClutterAction can only belong to one actor at a time
14703 *
14704 * The #ClutterActor will hold a reference on @action until either
14705 * clutter_actor_remove_action() or clutter_actor_clear_actions()
14706 * is called
14707 *
14708 * Since: 1.4
14709 */
14710 void
clutter_actor_add_action(ClutterActor * self,ClutterAction * action)14711 clutter_actor_add_action (ClutterActor *self,
14712 ClutterAction *action)
14713 {
14714 ClutterActorPrivate *priv;
14715
14716 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14717 g_return_if_fail (CLUTTER_IS_ACTION (action));
14718
14719 priv = self->priv;
14720
14721 if (priv->actions == NULL)
14722 {
14723 priv->actions = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
14724 priv->actions->actor = self;
14725 }
14726
14727 _clutter_meta_group_add_meta (priv->actions, CLUTTER_ACTOR_META (action));
14728
14729 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
14730 }
14731
14732 /**
14733 * clutter_actor_add_action_with_name:
14734 * @self: a #ClutterActor
14735 * @name: the name to set on the action
14736 * @action: a #ClutterAction
14737 *
14738 * A convenience function for setting the name of a #ClutterAction
14739 * while adding it to the list of actions applied to @self
14740 *
14741 * This function is the logical equivalent of:
14742 *
14743 * |[<!-- language="C" -->
14744 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
14745 * clutter_actor_add_action (self, action);
14746 * ]|
14747 *
14748 * Since: 1.4
14749 */
14750 void
clutter_actor_add_action_with_name(ClutterActor * self,const gchar * name,ClutterAction * action)14751 clutter_actor_add_action_with_name (ClutterActor *self,
14752 const gchar *name,
14753 ClutterAction *action)
14754 {
14755 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14756 g_return_if_fail (name != NULL);
14757 g_return_if_fail (CLUTTER_IS_ACTION (action));
14758
14759 clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
14760 clutter_actor_add_action (self, action);
14761 }
14762
14763 /**
14764 * clutter_actor_remove_action:
14765 * @self: a #ClutterActor
14766 * @action: a #ClutterAction
14767 *
14768 * Removes @action from the list of actions applied to @self
14769 *
14770 * The reference held by @self on the #ClutterAction will be released
14771 *
14772 * Since: 1.4
14773 */
14774 void
clutter_actor_remove_action(ClutterActor * self,ClutterAction * action)14775 clutter_actor_remove_action (ClutterActor *self,
14776 ClutterAction *action)
14777 {
14778 ClutterActorPrivate *priv;
14779
14780 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14781 g_return_if_fail (CLUTTER_IS_ACTION (action));
14782
14783 priv = self->priv;
14784
14785 if (priv->actions == NULL)
14786 return;
14787
14788 _clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action));
14789
14790 if (_clutter_meta_group_peek_metas (priv->actions) == NULL)
14791 g_clear_object (&priv->actions);
14792
14793 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
14794 }
14795
14796 /**
14797 * clutter_actor_remove_action_by_name:
14798 * @self: a #ClutterActor
14799 * @name: the name of the action to remove
14800 *
14801 * Removes the #ClutterAction with the given name from the list
14802 * of actions applied to @self
14803 *
14804 * Since: 1.4
14805 */
14806 void
clutter_actor_remove_action_by_name(ClutterActor * self,const gchar * name)14807 clutter_actor_remove_action_by_name (ClutterActor *self,
14808 const gchar *name)
14809 {
14810 ClutterActorPrivate *priv;
14811 ClutterActorMeta *meta;
14812
14813 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14814 g_return_if_fail (name != NULL);
14815
14816 priv = self->priv;
14817
14818 if (priv->actions == NULL)
14819 return;
14820
14821 meta = _clutter_meta_group_get_meta (priv->actions, name);
14822 if (meta == NULL)
14823 return;
14824
14825 _clutter_meta_group_remove_meta (priv->actions, meta);
14826
14827 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
14828 }
14829
14830 /**
14831 * clutter_actor_get_actions:
14832 * @self: a #ClutterActor
14833 *
14834 * Retrieves the list of actions applied to @self
14835 *
14836 * Return value: (transfer container) (element-type Clutter.Action): a copy
14837 * of the list of #ClutterAction<!-- -->s. The contents of the list are
14838 * owned by the #ClutterActor. Use g_list_free() to free the resources
14839 * allocated by the returned #GList
14840 *
14841 * Since: 1.4
14842 */
14843 GList *
clutter_actor_get_actions(ClutterActor * self)14844 clutter_actor_get_actions (ClutterActor *self)
14845 {
14846 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
14847
14848 if (self->priv->actions == NULL)
14849 return NULL;
14850
14851 return _clutter_meta_group_get_metas_no_internal (self->priv->actions);
14852 }
14853
14854 /**
14855 * clutter_actor_get_action:
14856 * @self: a #ClutterActor
14857 * @name: the name of the action to retrieve
14858 *
14859 * Retrieves the #ClutterAction with the given name in the list
14860 * of actions applied to @self
14861 *
14862 * Return value: (transfer none): a #ClutterAction for the given
14863 * name, or %NULL. The returned #ClutterAction is owned by the
14864 * actor and it should not be unreferenced directly
14865 *
14866 * Since: 1.4
14867 */
14868 ClutterAction *
clutter_actor_get_action(ClutterActor * self,const gchar * name)14869 clutter_actor_get_action (ClutterActor *self,
14870 const gchar *name)
14871 {
14872 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
14873 g_return_val_if_fail (name != NULL, NULL);
14874
14875 if (self->priv->actions == NULL)
14876 return NULL;
14877
14878 return CLUTTER_ACTION (_clutter_meta_group_get_meta (self->priv->actions, name));
14879 }
14880
14881 /**
14882 * clutter_actor_clear_actions:
14883 * @self: a #ClutterActor
14884 *
14885 * Clears the list of actions applied to @self
14886 *
14887 * Since: 1.4
14888 */
14889 void
clutter_actor_clear_actions(ClutterActor * self)14890 clutter_actor_clear_actions (ClutterActor *self)
14891 {
14892 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14893
14894 if (self->priv->actions == NULL)
14895 return;
14896
14897 _clutter_meta_group_clear_metas_no_internal (self->priv->actions);
14898 }
14899
14900 /**
14901 * clutter_actor_add_constraint:
14902 * @self: a #ClutterActor
14903 * @constraint: a #ClutterConstraint
14904 *
14905 * Adds @constraint to the list of #ClutterConstraint<!-- -->s applied
14906 * to @self
14907 *
14908 * The #ClutterActor will hold a reference on the @constraint until
14909 * either clutter_actor_remove_constraint() or
14910 * clutter_actor_clear_constraints() is called.
14911 *
14912 * Since: 1.4
14913 */
14914 void
clutter_actor_add_constraint(ClutterActor * self,ClutterConstraint * constraint)14915 clutter_actor_add_constraint (ClutterActor *self,
14916 ClutterConstraint *constraint)
14917 {
14918 ClutterActorPrivate *priv;
14919
14920 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14921 g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
14922
14923 priv = self->priv;
14924
14925 if (priv->constraints == NULL)
14926 {
14927 priv->constraints = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
14928 priv->constraints->actor = self;
14929 }
14930
14931 _clutter_meta_group_add_meta (priv->constraints,
14932 CLUTTER_ACTOR_META (constraint));
14933 clutter_actor_queue_relayout (self);
14934
14935 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
14936 }
14937
14938 /**
14939 * clutter_actor_add_constraint_with_name:
14940 * @self: a #ClutterActor
14941 * @name: the name to set on the constraint
14942 * @constraint: a #ClutterConstraint
14943 *
14944 * A convenience function for setting the name of a #ClutterConstraint
14945 * while adding it to the list of constraints applied to @self
14946 *
14947 * This function is the logical equivalent of:
14948 *
14949 * |[<!-- language="C" -->
14950 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
14951 * clutter_actor_add_constraint (self, constraint);
14952 * ]|
14953 *
14954 * Since: 1.4
14955 */
14956 void
clutter_actor_add_constraint_with_name(ClutterActor * self,const gchar * name,ClutterConstraint * constraint)14957 clutter_actor_add_constraint_with_name (ClutterActor *self,
14958 const gchar *name,
14959 ClutterConstraint *constraint)
14960 {
14961 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14962 g_return_if_fail (name != NULL);
14963 g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
14964
14965 clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
14966 clutter_actor_add_constraint (self, constraint);
14967 }
14968
14969 /**
14970 * clutter_actor_remove_constraint:
14971 * @self: a #ClutterActor
14972 * @constraint: a #ClutterConstraint
14973 *
14974 * Removes @constraint from the list of constraints applied to @self
14975 *
14976 * The reference held by @self on the #ClutterConstraint will be released
14977 *
14978 * Since: 1.4
14979 */
14980 void
clutter_actor_remove_constraint(ClutterActor * self,ClutterConstraint * constraint)14981 clutter_actor_remove_constraint (ClutterActor *self,
14982 ClutterConstraint *constraint)
14983 {
14984 ClutterActorPrivate *priv;
14985
14986 g_return_if_fail (CLUTTER_IS_ACTOR (self));
14987 g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
14988
14989 priv = self->priv;
14990
14991 if (priv->constraints == NULL)
14992 return;
14993
14994 _clutter_meta_group_remove_meta (priv->constraints,
14995 CLUTTER_ACTOR_META (constraint));
14996
14997 if (_clutter_meta_group_peek_metas (priv->constraints) == NULL)
14998 g_clear_object (&priv->constraints);
14999
15000 clutter_actor_queue_relayout (self);
15001
15002 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
15003 }
15004
15005 /**
15006 * clutter_actor_remove_constraint_by_name:
15007 * @self: a #ClutterActor
15008 * @name: the name of the constraint to remove
15009 *
15010 * Removes the #ClutterConstraint with the given name from the list
15011 * of constraints applied to @self
15012 *
15013 * Since: 1.4
15014 */
15015 void
clutter_actor_remove_constraint_by_name(ClutterActor * self,const gchar * name)15016 clutter_actor_remove_constraint_by_name (ClutterActor *self,
15017 const gchar *name)
15018 {
15019 ClutterActorPrivate *priv;
15020 ClutterActorMeta *meta;
15021
15022 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15023 g_return_if_fail (name != NULL);
15024
15025 priv = self->priv;
15026
15027 if (priv->constraints == NULL)
15028 return;
15029
15030 meta = _clutter_meta_group_get_meta (priv->constraints, name);
15031 if (meta == NULL)
15032 return;
15033
15034 _clutter_meta_group_remove_meta (priv->constraints, meta);
15035 clutter_actor_queue_relayout (self);
15036 }
15037
15038 /**
15039 * clutter_actor_get_constraints:
15040 * @self: a #ClutterActor
15041 *
15042 * Retrieves the list of constraints applied to @self
15043 *
15044 * Return value: (transfer container) (element-type Clutter.Constraint): a copy
15045 * of the list of #ClutterConstraint<!-- -->s. The contents of the list are
15046 * owned by the #ClutterActor. Use g_list_free() to free the resources
15047 * allocated by the returned #GList
15048 *
15049 * Since: 1.4
15050 */
15051 GList *
clutter_actor_get_constraints(ClutterActor * self)15052 clutter_actor_get_constraints (ClutterActor *self)
15053 {
15054 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15055
15056 if (self->priv->constraints == NULL)
15057 return NULL;
15058
15059 return _clutter_meta_group_get_metas_no_internal (self->priv->constraints);
15060 }
15061
15062 /**
15063 * clutter_actor_get_constraint:
15064 * @self: a #ClutterActor
15065 * @name: the name of the constraint to retrieve
15066 *
15067 * Retrieves the #ClutterConstraint with the given name in the list
15068 * of constraints applied to @self
15069 *
15070 * Return value: (transfer none): a #ClutterConstraint for the given
15071 * name, or %NULL. The returned #ClutterConstraint is owned by the
15072 * actor and it should not be unreferenced directly
15073 *
15074 * Since: 1.4
15075 */
15076 ClutterConstraint *
clutter_actor_get_constraint(ClutterActor * self,const gchar * name)15077 clutter_actor_get_constraint (ClutterActor *self,
15078 const gchar *name)
15079 {
15080 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15081 g_return_val_if_fail (name != NULL, NULL);
15082
15083 if (self->priv->constraints == NULL)
15084 return NULL;
15085
15086 return CLUTTER_CONSTRAINT (_clutter_meta_group_get_meta (self->priv->constraints, name));
15087 }
15088
15089 /**
15090 * clutter_actor_clear_constraints:
15091 * @self: a #ClutterActor
15092 *
15093 * Clears the list of constraints applied to @self
15094 *
15095 * Since: 1.4
15096 */
15097 void
clutter_actor_clear_constraints(ClutterActor * self)15098 clutter_actor_clear_constraints (ClutterActor *self)
15099 {
15100 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15101
15102 if (self->priv->constraints == NULL)
15103 return;
15104
15105 _clutter_meta_group_clear_metas_no_internal (self->priv->constraints);
15106
15107 clutter_actor_queue_relayout (self);
15108 }
15109
15110 /**
15111 * clutter_actor_set_clip_to_allocation:
15112 * @self: a #ClutterActor
15113 * @clip_set: %TRUE to apply a clip tracking the allocation
15114 *
15115 * Sets whether @self should be clipped to the same size as its
15116 * allocation
15117 *
15118 * Since: 1.4
15119 */
15120 void
clutter_actor_set_clip_to_allocation(ClutterActor * self,gboolean clip_set)15121 clutter_actor_set_clip_to_allocation (ClutterActor *self,
15122 gboolean clip_set)
15123 {
15124 ClutterActorPrivate *priv;
15125
15126 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15127
15128 clip_set = !!clip_set;
15129
15130 priv = self->priv;
15131
15132 if (priv->clip_to_allocation != clip_set)
15133 {
15134 priv->clip_to_allocation = clip_set;
15135
15136 queue_update_paint_volume (self);
15137 clutter_actor_queue_redraw (self);
15138
15139 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]);
15140 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
15141 }
15142 }
15143
15144 /**
15145 * clutter_actor_get_clip_to_allocation:
15146 * @self: a #ClutterActor
15147 *
15148 * Retrieves the value set using clutter_actor_set_clip_to_allocation()
15149 *
15150 * Return value: %TRUE if the #ClutterActor is clipped to its allocation
15151 *
15152 * Since: 1.4
15153 */
15154 gboolean
clutter_actor_get_clip_to_allocation(ClutterActor * self)15155 clutter_actor_get_clip_to_allocation (ClutterActor *self)
15156 {
15157 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15158
15159 return self->priv->clip_to_allocation;
15160 }
15161
15162 /**
15163 * clutter_actor_add_effect:
15164 * @self: a #ClutterActor
15165 * @effect: a #ClutterEffect
15166 *
15167 * Adds @effect to the list of #ClutterEffect<!-- -->s applied to @self
15168 *
15169 * The #ClutterActor will hold a reference on the @effect until either
15170 * clutter_actor_remove_effect() or clutter_actor_clear_effects() is
15171 * called.
15172 *
15173 * Since: 1.4
15174 */
15175 void
clutter_actor_add_effect(ClutterActor * self,ClutterEffect * effect)15176 clutter_actor_add_effect (ClutterActor *self,
15177 ClutterEffect *effect)
15178 {
15179 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15180 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
15181
15182 _clutter_actor_add_effect_internal (self, effect);
15183
15184 clutter_actor_queue_redraw (self);
15185
15186 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
15187 }
15188
15189 /**
15190 * clutter_actor_add_effect_with_name:
15191 * @self: a #ClutterActor
15192 * @name: the name to set on the effect
15193 * @effect: a #ClutterEffect
15194 *
15195 * A convenience function for setting the name of a #ClutterEffect
15196 * while adding it to the list of effectss applied to @self
15197 *
15198 * This function is the logical equivalent of:
15199 *
15200 * |[<!-- language="C" -->
15201 * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
15202 * clutter_actor_add_effect (self, effect);
15203 * ]|
15204 *
15205 * Since: 1.4
15206 */
15207 void
clutter_actor_add_effect_with_name(ClutterActor * self,const gchar * name,ClutterEffect * effect)15208 clutter_actor_add_effect_with_name (ClutterActor *self,
15209 const gchar *name,
15210 ClutterEffect *effect)
15211 {
15212 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15213 g_return_if_fail (name != NULL);
15214 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
15215
15216 clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
15217 clutter_actor_add_effect (self, effect);
15218 }
15219
15220 /**
15221 * clutter_actor_remove_effect:
15222 * @self: a #ClutterActor
15223 * @effect: a #ClutterEffect
15224 *
15225 * Removes @effect from the list of effects applied to @self
15226 *
15227 * The reference held by @self on the #ClutterEffect will be released
15228 *
15229 * Since: 1.4
15230 */
15231 void
clutter_actor_remove_effect(ClutterActor * self,ClutterEffect * effect)15232 clutter_actor_remove_effect (ClutterActor *self,
15233 ClutterEffect *effect)
15234 {
15235 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15236 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
15237
15238 _clutter_actor_remove_effect_internal (self, effect);
15239
15240 clutter_actor_queue_redraw (self);
15241
15242 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
15243 }
15244
15245 /**
15246 * clutter_actor_remove_effect_by_name:
15247 * @self: a #ClutterActor
15248 * @name: the name of the effect to remove
15249 *
15250 * Removes the #ClutterEffect with the given name from the list
15251 * of effects applied to @self
15252 *
15253 * Since: 1.4
15254 */
15255 void
clutter_actor_remove_effect_by_name(ClutterActor * self,const gchar * name)15256 clutter_actor_remove_effect_by_name (ClutterActor *self,
15257 const gchar *name)
15258 {
15259 ClutterActorPrivate *priv;
15260 ClutterActorMeta *meta;
15261
15262 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15263 g_return_if_fail (name != NULL);
15264
15265 priv = self->priv;
15266
15267 if (priv->effects == NULL)
15268 return;
15269
15270 meta = _clutter_meta_group_get_meta (priv->effects, name);
15271 if (meta == NULL)
15272 return;
15273
15274 clutter_actor_remove_effect (self, CLUTTER_EFFECT (meta));
15275 }
15276
15277 /**
15278 * clutter_actor_get_effects:
15279 * @self: a #ClutterActor
15280 *
15281 * Retrieves the #ClutterEffect<!-- -->s applied on @self, if any
15282 *
15283 * Return value: (transfer container) (element-type Clutter.Effect): a list
15284 * of #ClutterEffect<!-- -->s, or %NULL. The elements of the returned
15285 * list are owned by Clutter and they should not be freed. You should
15286 * free the returned list using g_list_free() when done
15287 *
15288 * Since: 1.4
15289 */
15290 GList *
clutter_actor_get_effects(ClutterActor * self)15291 clutter_actor_get_effects (ClutterActor *self)
15292 {
15293 ClutterActorPrivate *priv;
15294
15295 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15296
15297 priv = self->priv;
15298
15299 if (priv->effects == NULL)
15300 return NULL;
15301
15302 return _clutter_meta_group_get_metas_no_internal (priv->effects);
15303 }
15304
15305 /**
15306 * clutter_actor_get_effect:
15307 * @self: a #ClutterActor
15308 * @name: the name of the effect to retrieve
15309 *
15310 * Retrieves the #ClutterEffect with the given name in the list
15311 * of effects applied to @self
15312 *
15313 * Return value: (transfer none): a #ClutterEffect for the given
15314 * name, or %NULL. The returned #ClutterEffect is owned by the
15315 * actor and it should not be unreferenced directly
15316 *
15317 * Since: 1.4
15318 */
15319 ClutterEffect *
clutter_actor_get_effect(ClutterActor * self,const gchar * name)15320 clutter_actor_get_effect (ClutterActor *self,
15321 const gchar *name)
15322 {
15323 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15324 g_return_val_if_fail (name != NULL, NULL);
15325
15326 if (self->priv->effects == NULL)
15327 return NULL;
15328
15329 return CLUTTER_EFFECT (_clutter_meta_group_get_meta (self->priv->effects, name));
15330 }
15331
15332 /**
15333 * clutter_actor_clear_effects:
15334 * @self: a #ClutterActor
15335 *
15336 * Clears the list of effects applied to @self
15337 *
15338 * Since: 1.4
15339 */
15340 void
clutter_actor_clear_effects(ClutterActor * self)15341 clutter_actor_clear_effects (ClutterActor *self)
15342 {
15343 g_return_if_fail (CLUTTER_IS_ACTOR (self));
15344
15345 if (self->priv->effects == NULL)
15346 return;
15347
15348 _clutter_meta_group_clear_metas_no_internal (self->priv->effects);
15349
15350 clutter_actor_queue_redraw (self);
15351 }
15352
15353 /**
15354 * clutter_actor_has_key_focus:
15355 * @self: a #ClutterActor
15356 *
15357 * Checks whether @self is the #ClutterActor that has key focus
15358 *
15359 * Return value: %TRUE if the actor has key focus, and %FALSE otherwise
15360 *
15361 * Since: 1.4
15362 */
15363 gboolean
clutter_actor_has_key_focus(ClutterActor * self)15364 clutter_actor_has_key_focus (ClutterActor *self)
15365 {
15366 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15367
15368 return self->priv->has_key_focus;
15369 }
15370
15371 static gboolean
_clutter_actor_get_paint_volume_real(ClutterActor * self,ClutterPaintVolume * pv)15372 _clutter_actor_get_paint_volume_real (ClutterActor *self,
15373 ClutterPaintVolume *pv)
15374 {
15375 ClutterActorPrivate *priv = self->priv;
15376
15377 /* Actors are only expected to report a valid paint volume
15378 * while they have a valid allocation. */
15379 if (G_UNLIKELY (priv->needs_allocation))
15380 {
15381 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15382 "Actor needs allocation",
15383 _clutter_actor_get_debug_name (self));
15384 return FALSE;
15385 }
15386
15387 _clutter_paint_volume_init_static (pv, self);
15388
15389 if (!CLUTTER_ACTOR_GET_CLASS (self)->get_paint_volume (self, pv))
15390 {
15391 clutter_paint_volume_free (pv);
15392 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15393 "Actor failed to report a volume",
15394 _clutter_actor_get_debug_name (self));
15395 return FALSE;
15396 }
15397
15398 /* since effects can modify the paint volume, we allow them to actually
15399 * do this by making get_paint_volume() "context sensitive"
15400 */
15401 if (priv->effects != NULL)
15402 {
15403 if (priv->current_effect != NULL)
15404 {
15405 const GList *effects, *l;
15406
15407 /* if we are being called from within the paint sequence of
15408 * an actor, get the paint volume up to the current effect
15409 */
15410 effects = _clutter_meta_group_peek_metas (priv->effects);
15411 for (l = effects;
15412 l != NULL && l->data != priv->current_effect;
15413 l = l->next)
15414 {
15415 if (!_clutter_effect_modify_paint_volume (l->data, pv))
15416 {
15417 clutter_paint_volume_free (pv);
15418 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15419 "Effect (%s) failed to report a volume",
15420 _clutter_actor_get_debug_name (self),
15421 _clutter_actor_meta_get_debug_name (l->data));
15422 return FALSE;
15423 }
15424 }
15425 }
15426 else
15427 {
15428 const GList *effects, *l;
15429
15430 /* otherwise, get the cumulative volume */
15431 effects = _clutter_meta_group_peek_metas (priv->effects);
15432 for (l = effects; l != NULL; l = l->next)
15433 if (!_clutter_effect_modify_paint_volume (l->data, pv))
15434 {
15435 clutter_paint_volume_free (pv);
15436 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15437 "Effect (%s) failed to report a volume",
15438 _clutter_actor_get_debug_name (self),
15439 _clutter_actor_meta_get_debug_name (l->data));
15440 return FALSE;
15441 }
15442 }
15443 }
15444
15445 return TRUE;
15446 }
15447
15448 static gboolean
_clutter_actor_has_active_paint_volume_override_effects(ClutterActor * self)15449 _clutter_actor_has_active_paint_volume_override_effects (ClutterActor *self)
15450 {
15451 const GList *l;
15452
15453 if (self->priv->effects == NULL)
15454 return FALSE;
15455
15456 /* We just need to all effects current effect to see
15457 * if anyone wants to override the paint volume. If so, then
15458 * we need to recompute, since the paint volume returned can
15459 * change from call to call. */
15460 for (l = _clutter_meta_group_peek_metas (self->priv->effects);
15461 l != NULL;
15462 l = l->next)
15463 {
15464 ClutterEffect *effect = l->data;
15465
15466 if (clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)) &&
15467 _clutter_effect_has_custom_paint_volume (effect))
15468 return TRUE;
15469 }
15470
15471 return FALSE;
15472 }
15473
15474 /* The public clutter_actor_get_paint_volume API returns a const
15475 * pointer since we return a pointer directly to the cached
15476 * PaintVolume associated with the actor and don't want the user to
15477 * inadvertently modify it, but for internal uses we sometimes need
15478 * access to the same PaintVolume but need to apply some book-keeping
15479 * modifications to it so we don't want a const pointer.
15480 */
15481 static ClutterPaintVolume *
_clutter_actor_get_paint_volume_mutable(ClutterActor * self)15482 _clutter_actor_get_paint_volume_mutable (ClutterActor *self)
15483 {
15484 gboolean has_paint_volume_override_effects;
15485 ClutterActorPrivate *priv;
15486
15487 priv = self->priv;
15488
15489 has_paint_volume_override_effects = _clutter_actor_has_active_paint_volume_override_effects (self);
15490
15491 if (priv->paint_volume_valid)
15492 {
15493 /* If effects are applied, the actor paint volume
15494 * needs to be recomputed on each paint, since those
15495 * paint volumes could change over the duration of the
15496 * effect.
15497 *
15498 * We also need to update the paint volume if we went
15499 * from having effects to not having effects on the last
15500 * paint volume update. */
15501 if (!priv->needs_paint_volume_update &&
15502 priv->current_effect == NULL &&
15503 !has_paint_volume_override_effects &&
15504 !priv->had_effects_on_last_paint_volume_update)
15505 return &priv->paint_volume;
15506 clutter_paint_volume_free (&priv->paint_volume);
15507 }
15508
15509 priv->had_effects_on_last_paint_volume_update = has_paint_volume_override_effects;
15510
15511 if (_clutter_actor_get_paint_volume_real (self, &priv->paint_volume))
15512 {
15513 priv->paint_volume_valid = TRUE;
15514 priv->needs_paint_volume_update = FALSE;
15515 return &priv->paint_volume;
15516 }
15517 else
15518 {
15519 priv->paint_volume_valid = FALSE;
15520 return NULL;
15521 }
15522 }
15523
15524 /**
15525 * clutter_actor_get_paint_volume:
15526 * @self: a #ClutterActor
15527 *
15528 * Retrieves the paint volume of the passed #ClutterActor, or %NULL
15529 * when a paint volume can't be determined.
15530 *
15531 * The paint volume is defined as the 3D space occupied by an actor
15532 * when being painted.
15533 *
15534 * This function will call the #ClutterActorClass.get_paint_volume()
15535 * virtual function of the #ClutterActor class. Sub-classes of #ClutterActor
15536 * should not usually care about overriding the default implementation,
15537 * unless they are, for instance: painting outside their allocation, or
15538 * actors with a depth factor (not in terms of #ClutterActor:depth but real
15539 * 3D depth).
15540 *
15541 * Note: 2D actors overriding #ClutterActorClass.get_paint_volume()
15542 * should ensure that their volume has a depth of 0. (This will be true
15543 * as long as you don't call clutter_paint_volume_set_depth().)
15544 *
15545 * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
15546 * or %NULL if no volume could be determined. The returned pointer
15547 * is not guaranteed to be valid across multiple frames; if you want
15548 * to keep it, you will need to copy it using clutter_paint_volume_copy().
15549 *
15550 * Since: 1.6
15551 */
15552 const ClutterPaintVolume *
clutter_actor_get_paint_volume(ClutterActor * self)15553 clutter_actor_get_paint_volume (ClutterActor *self)
15554 {
15555 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15556
15557 return _clutter_actor_get_paint_volume_mutable (self);
15558 }
15559
15560 /**
15561 * clutter_actor_get_transformed_paint_volume:
15562 * @self: a #ClutterActor
15563 * @relative_to_ancestor: A #ClutterActor that is an ancestor of @self
15564 * (or %NULL for the stage)
15565 *
15566 * Retrieves the 3D paint volume of an actor like
15567 * clutter_actor_get_paint_volume() does (Please refer to the
15568 * documentation of clutter_actor_get_paint_volume() for more
15569 * details.) and it additionally transforms the paint volume into the
15570 * coordinate space of @relative_to_ancestor. (Or the stage if %NULL
15571 * is passed for @relative_to_ancestor)
15572 *
15573 * This can be used by containers that base their paint volume on
15574 * the volume of their children. Such containers can query the
15575 * transformed paint volume of all of its children and union them
15576 * together using clutter_paint_volume_union().
15577 *
15578 * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
15579 * or %NULL if no volume could be determined. The returned pointer is
15580 * not guaranteed to be valid across multiple frames; if you wish to
15581 * keep it, you will have to copy it using clutter_paint_volume_copy().
15582 *
15583 * Since: 1.6
15584 */
15585 const ClutterPaintVolume *
clutter_actor_get_transformed_paint_volume(ClutterActor * self,ClutterActor * relative_to_ancestor)15586 clutter_actor_get_transformed_paint_volume (ClutterActor *self,
15587 ClutterActor *relative_to_ancestor)
15588 {
15589 const ClutterPaintVolume *volume;
15590 ClutterActor *stage;
15591 ClutterPaintVolume *transformed_volume;
15592
15593 stage = _clutter_actor_get_stage_internal (self);
15594 if (G_UNLIKELY (stage == NULL))
15595 return NULL;
15596
15597 if (relative_to_ancestor == NULL)
15598 relative_to_ancestor = stage;
15599
15600 volume = clutter_actor_get_paint_volume (self);
15601 if (volume == NULL)
15602 return NULL;
15603
15604 transformed_volume =
15605 _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
15606
15607 _clutter_paint_volume_copy_static (volume, transformed_volume);
15608
15609 _clutter_paint_volume_transform_relative (transformed_volume,
15610 relative_to_ancestor);
15611
15612 return transformed_volume;
15613 }
15614
15615 /**
15616 * clutter_actor_get_paint_box:
15617 * @self: a #ClutterActor
15618 * @box: (out): return location for a #ClutterActorBox
15619 *
15620 * Retrieves the paint volume of the passed #ClutterActor, and
15621 * transforms it into a 2D bounding box in stage coordinates.
15622 *
15623 * This function is useful to determine the on screen area occupied by
15624 * the actor. The box is only an approximation and may often be
15625 * considerably larger due to the optimizations used to calculate the
15626 * box. The box is never smaller though, so it can reliably be used
15627 * for culling.
15628 *
15629 * There are times when a 2D paint box can't be determined, e.g.
15630 * because the actor isn't yet parented under a stage or because
15631 * the actor is unable to determine a paint volume.
15632 *
15633 * Return value: %TRUE if a 2D paint box could be determined, else
15634 * %FALSE.
15635 *
15636 * Since: 1.6
15637 */
15638 gboolean
clutter_actor_get_paint_box(ClutterActor * self,ClutterActorBox * box)15639 clutter_actor_get_paint_box (ClutterActor *self,
15640 ClutterActorBox *box)
15641 {
15642 ClutterActor *stage;
15643 ClutterPaintVolume *pv;
15644
15645 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15646 g_return_val_if_fail (box != NULL, FALSE);
15647
15648 stage = _clutter_actor_get_stage_internal (self);
15649 if (G_UNLIKELY (!stage))
15650 return FALSE;
15651
15652 pv = _clutter_actor_get_paint_volume_mutable (self);
15653 if (G_UNLIKELY (!pv))
15654 return FALSE;
15655
15656 _clutter_paint_volume_get_stage_paint_box (pv, CLUTTER_STAGE (stage), box);
15657
15658 return TRUE;
15659 }
15660
15661 static ClutterActorTraverseVisitFlags
clear_stage_views_cb(ClutterActor * actor,int depth,gpointer user_data)15662 clear_stage_views_cb (ClutterActor *actor,
15663 int depth,
15664 gpointer user_data)
15665 {
15666 g_autoptr (GList) old_stage_views = NULL;
15667
15668 actor->priv->needs_update_stage_views = TRUE;
15669
15670 old_stage_views = g_steal_pointer (&actor->priv->stage_views);
15671
15672 if (old_stage_views)
15673 g_signal_emit (actor, actor_signals[STAGE_VIEWS_CHANGED], 0);
15674
15675 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
15676 }
15677
15678 void
clutter_actor_clear_stage_views_recursive(ClutterActor * self)15679 clutter_actor_clear_stage_views_recursive (ClutterActor *self)
15680 {
15681 _clutter_actor_traverse (self,
15682 CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
15683 clear_stage_views_cb,
15684 NULL,
15685 NULL);
15686 }
15687
15688 float
clutter_actor_get_real_resource_scale(ClutterActor * self)15689 clutter_actor_get_real_resource_scale (ClutterActor *self)
15690 {
15691 ClutterActorPrivate *priv = self->priv;
15692 float guessed_scale;
15693
15694 if (priv->resource_scale != -1.f)
15695 return priv->resource_scale;
15696
15697 /* If the scale hasn't been computed yet, we return a best guess */
15698
15699 if (priv->parent != NULL)
15700 {
15701 /* If the scale hasn't been calculated yet, assume this actor is located
15702 * inside its parents box and go up the hierarchy.
15703 */
15704 guessed_scale = clutter_actor_get_real_resource_scale (priv->parent);
15705 }
15706 else if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
15707 {
15708 /* This must be the first allocation cycle and the resource scale of
15709 * the stage has not been updated yet, so return it manually.
15710 */
15711 GList *l;
15712 ClutterStage *stage = CLUTTER_STAGE (self);
15713 float max_scale = -1.f;
15714
15715 for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
15716 {
15717 ClutterStageView *view = l->data;
15718
15719 max_scale = MAX (clutter_stage_view_get_scale (view), max_scale);
15720 }
15721
15722 guessed_scale = max_scale;
15723 }
15724 else
15725 {
15726 ClutterBackend *backend = clutter_get_default_backend ();
15727
15728 guessed_scale = clutter_backend_get_fallback_resource_scale (backend);
15729 }
15730
15731 g_assert (guessed_scale >= 1.f);
15732
15733 /* Always return this value until we compute the correct one later.
15734 * If our guess turns out to be wrong, we'll emit "resource-scale-changed"
15735 * and correct it before painting.
15736 */
15737 priv->resource_scale = guessed_scale;
15738
15739 return priv->resource_scale;
15740 }
15741
15742 /**
15743 * clutter_actor_get_resource_scale:
15744 * @self: A #ClutterActor
15745 *
15746 * Retrieves the resource scale for this actor.
15747 *
15748 * The resource scale refers to the scale the actor should use for its resources.
15749 * For example if an actor draws a a picture of size 100 x 100 in the stage
15750 * coordinate space, it should use a texture of twice the size (i.e. 200 x 200)
15751 * if the resource scale is 2.
15752 *
15753 * The resource scale is determined by calculating the highest #ClutterStageView
15754 * scale the actor will get painted on.
15755 *
15756 * Note that the scale returned by this function is only guaranteed to be
15757 * correct when queried during the paint cycle, in all other cases this
15758 * function will only return a best guess. If your implementation really
15759 * needs to get a resource scale outside of the paint cycle, make sure to
15760 * subscribe to the "resource-scale-changed" signal to get notified about
15761 * the new, correct resource scale before painting.
15762 *
15763 * Also avoid getting the resource scale for actors that are not attached
15764 * to a stage. There's no sane way for Clutter to guess which #ClutterStageView
15765 * the actor is going to be painted on, so you'll probably end up receiving
15766 * the "resource-scale-changed" signal and having to rebuild your resources.
15767 *
15768 * The best guess this function may return is usually just the last resource
15769 * scale the actor got painted with. If this resource scale couldn't be found
15770 * because the actor was never painted so far or Clutter was unable to
15771 * determine its position and size, this function will return the resource
15772 * scale of a parent.
15773 *
15774 * Returns: The resource scale the actor should use for its textures
15775 */
15776 float
clutter_actor_get_resource_scale(ClutterActor * self)15777 clutter_actor_get_resource_scale (ClutterActor *self)
15778 {
15779 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.f);
15780
15781 return ceilf (clutter_actor_get_real_resource_scale (self));
15782 }
15783
15784 static gboolean
sorted_lists_equal(GList * list_a,GList * list_b)15785 sorted_lists_equal (GList *list_a,
15786 GList *list_b)
15787 {
15788 GList *a, *b;
15789
15790 if (!list_a && !list_b)
15791 return TRUE;
15792
15793 for (a = list_a, b = list_b;
15794 a && b;
15795 a = a->next, b = b->next)
15796 {
15797 if (a->data != b->data)
15798 break;
15799
15800 if (!a->next && !b->next)
15801 return TRUE;
15802 }
15803
15804 return FALSE;
15805 }
15806
15807 static void
update_stage_views(ClutterActor * self)15808 update_stage_views (ClutterActor *self)
15809 {
15810 ClutterActorPrivate *priv = self->priv;
15811 g_autoptr (GList) old_stage_views = NULL;
15812 ClutterStage *stage;
15813 graphene_rect_t bounding_rect;
15814
15815 old_stage_views = g_steal_pointer (&priv->stage_views);
15816
15817 if (priv->needs_allocation)
15818 {
15819 g_warning ("Can't update stage views actor %s is on because it needs an "
15820 "allocation.", _clutter_actor_get_debug_name (self));
15821 goto out;
15822 }
15823
15824 stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
15825 g_return_if_fail (stage);
15826
15827 clutter_actor_get_transformed_extents (self, &bounding_rect);
15828
15829 if (bounding_rect.size.width == 0.0 ||
15830 bounding_rect.size.height == 0.0)
15831 goto out;
15832
15833 priv->stage_views = clutter_stage_get_views_for_rect (stage,
15834 &bounding_rect);
15835
15836 out:
15837 if (!sorted_lists_equal (old_stage_views, priv->stage_views))
15838 g_signal_emit (self, actor_signals[STAGE_VIEWS_CHANGED], 0);
15839 }
15840
15841 static void
update_resource_scale(ClutterActor * self,int phase)15842 update_resource_scale (ClutterActor *self,
15843 int phase)
15844 {
15845 ClutterActorPrivate *priv = self->priv;
15846 float new_resource_scale, old_resource_scale;
15847
15848 new_resource_scale =
15849 CLUTTER_ACTOR_GET_CLASS (self)->calculate_resource_scale (self, phase);
15850
15851 if (priv->resource_scale == new_resource_scale)
15852 return;
15853
15854 /* If the actor moved out of the stage, simply keep the last scale */
15855 if (new_resource_scale == -1.f)
15856 return;
15857
15858 old_resource_scale = priv->resource_scale;
15859 priv->resource_scale = new_resource_scale;
15860
15861 /* Never notify the initial change, otherwise, to be consistent,
15862 * we'd also have to notify if we guessed correctly in
15863 * clutter_actor_get_real_resource_scale().
15864 */
15865 if (old_resource_scale == -1.f)
15866 return;
15867
15868 if (ceilf (old_resource_scale) != ceilf (priv->resource_scale))
15869 g_signal_emit (self, actor_signals[RESOURCE_SCALE_CHANGED], 0);
15870 }
15871
15872 void
clutter_actor_finish_layout(ClutterActor * self,gboolean use_max_scale)15873 clutter_actor_finish_layout (ClutterActor *self,
15874 gboolean use_max_scale)
15875 {
15876 ClutterActorPrivate *priv = self->priv;
15877 ClutterActor *child;
15878
15879 if (!CLUTTER_ACTOR_IS_MAPPED (self) ||
15880 CLUTTER_ACTOR_IN_DESTRUCTION (self))
15881 return;
15882
15883 _clutter_actor_update_last_paint_volume (self);
15884
15885 if (priv->needs_update_stage_views)
15886 {
15887 update_stage_views (self);
15888 update_resource_scale (self, use_max_scale);
15889
15890 priv->needs_update_stage_views = FALSE;
15891 }
15892
15893 for (child = priv->first_child; child; child = child->priv->next_sibling)
15894 clutter_actor_finish_layout (child, use_max_scale);
15895 }
15896
15897 /**
15898 * clutter_actor_peek_stage_views:
15899 * @self: A #ClutterActor
15900 *
15901 * Retrieves the list of #ClutterStageView<!-- -->s the actor is being
15902 * painted on.
15903 *
15904 * If this function is called during the paint cycle, the list is guaranteed
15905 * to be up-to-date, if called outside the paint cycle, the list will
15906 * contain the views the actor was painted on last.
15907 *
15908 * The list returned by this function is not updated when the actors
15909 * visibility changes: If an actor gets hidden and is not being painted
15910 * anymore, this function will return the list of views the actor was
15911 * painted on last.
15912 *
15913 * If an actor is not attached to a stage (realized), this function will
15914 * always return an empty list.
15915 *
15916 * Returns: (transfer none) (element-type Clutter.StageView): The list of
15917 * #ClutterStageView<!-- -->s the actor is being painted on. The list and
15918 * its contents are owned by the #ClutterActor and the list may not be
15919 * freed or modified.
15920 */
15921 GList *
clutter_actor_peek_stage_views(ClutterActor * self)15922 clutter_actor_peek_stage_views (ClutterActor *self)
15923 {
15924 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15925
15926 return self->priv->stage_views;
15927 }
15928
15929 gboolean
clutter_actor_is_effectively_on_stage_view(ClutterActor * self,ClutterStageView * view)15930 clutter_actor_is_effectively_on_stage_view (ClutterActor *self,
15931 ClutterStageView *view)
15932 {
15933 ClutterActor *actor;
15934
15935 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15936
15937 if (g_list_find (self->priv->stage_views, view))
15938 return TRUE;
15939
15940 for (actor = self; actor; actor = actor->priv->parent)
15941 {
15942 if (actor->priv->clones)
15943 {
15944 GHashTableIter iter;
15945 gpointer key;
15946
15947 g_hash_table_iter_init (&iter, actor->priv->clones);
15948 while (g_hash_table_iter_next (&iter, &key, NULL))
15949 {
15950 ClutterActor *clone = key;
15951 GList *clone_views;
15952
15953 clone_views = clutter_actor_peek_stage_views (clone);
15954 if (g_list_find (clone_views, view))
15955 return TRUE;
15956 }
15957 }
15958 }
15959
15960 return FALSE;
15961 }
15962
15963 /**
15964 * clutter_actor_pick_frame_clock: (skip)
15965 * @self: a #ClutterActor
15966 * @out_actor: (nullable): a pointer to an #ClutterActor
15967 *
15968 * Pick the most suitable frame clock for driving animations for this actor.
15969 *
15970 * The #ClutterActor used for picking the frame clock is written @out_actor.
15971 *
15972 * Returns: (transfer none): a #ClutterFrameClock
15973 */
15974 ClutterFrameClock *
clutter_actor_pick_frame_clock(ClutterActor * self,ClutterActor ** out_actor)15975 clutter_actor_pick_frame_clock (ClutterActor *self,
15976 ClutterActor **out_actor)
15977 {
15978 ClutterActorPrivate *priv = self->priv;
15979 GList *stage_views_list;
15980 float max_refresh_rate = 0.0;
15981 ClutterStageView *best_view = NULL;
15982 GList *l;
15983
15984 stage_views_list = CLUTTER_IS_STAGE (self)
15985 ? clutter_stage_peek_stage_views (CLUTTER_STAGE (self))
15986 : priv->stage_views;
15987
15988 if (!stage_views_list)
15989 {
15990 if (priv->parent)
15991 return clutter_actor_pick_frame_clock (priv->parent, out_actor);
15992 else
15993 return NULL;
15994 }
15995
15996 for (l = stage_views_list; l; l = l->next)
15997 {
15998 ClutterStageView *view = l->data;
15999 float refresh_rate;
16000
16001 refresh_rate = clutter_stage_view_get_refresh_rate (view);
16002 if (refresh_rate > max_refresh_rate)
16003 {
16004 best_view = view;
16005 max_refresh_rate = refresh_rate;
16006 }
16007 }
16008
16009 if (best_view)
16010 {
16011 if (out_actor)
16012 *out_actor = self;
16013 return clutter_stage_view_get_frame_clock (best_view);
16014 }
16015 else
16016 {
16017 return NULL;
16018 }
16019 }
16020
16021 /**
16022 * clutter_actor_has_overlaps:
16023 * @self: A #ClutterActor
16024 *
16025 * Asks the actor's implementation whether it may contain overlapping
16026 * primitives.
16027 *
16028 * For example; Clutter may use this to determine whether the painting
16029 * should be redirected to an offscreen buffer to correctly implement
16030 * the opacity property.
16031 *
16032 * Custom actors can override the default response by implementing the
16033 * #ClutterActorClass.has_overlaps() virtual function. See
16034 * clutter_actor_set_offscreen_redirect() for more information.
16035 *
16036 * Return value: %TRUE if the actor may have overlapping primitives, and
16037 * %FALSE otherwise
16038 *
16039 * Since: 1.8
16040 */
16041 gboolean
clutter_actor_has_overlaps(ClutterActor * self)16042 clutter_actor_has_overlaps (ClutterActor *self)
16043 {
16044 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
16045
16046 return CLUTTER_ACTOR_GET_CLASS (self)->has_overlaps (self);
16047 }
16048
16049 /**
16050 * clutter_actor_has_effects:
16051 * @self: A #ClutterActor
16052 *
16053 * Returns whether the actor has any effects applied.
16054 *
16055 * Return value: %TRUE if the actor has any effects,
16056 * %FALSE otherwise
16057 *
16058 * Since: 1.10
16059 */
16060 gboolean
clutter_actor_has_effects(ClutterActor * self)16061 clutter_actor_has_effects (ClutterActor *self)
16062 {
16063 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16064
16065 if (self->priv->effects == NULL)
16066 return FALSE;
16067
16068 return _clutter_meta_group_has_metas_no_internal (self->priv->effects);
16069 }
16070
16071 /**
16072 * clutter_actor_has_constraints:
16073 * @self: A #ClutterActor
16074 *
16075 * Returns whether the actor has any constraints applied.
16076 *
16077 * Return value: %TRUE if the actor has any constraints,
16078 * %FALSE otherwise
16079 *
16080 * Since: 1.10
16081 */
16082 gboolean
clutter_actor_has_constraints(ClutterActor * self)16083 clutter_actor_has_constraints (ClutterActor *self)
16084 {
16085 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16086
16087 if (self->priv->constraints == NULL)
16088 return FALSE;
16089
16090 return _clutter_meta_group_has_metas_no_internal (self->priv->constraints);
16091 }
16092
16093 /**
16094 * clutter_actor_has_actions:
16095 * @self: A #ClutterActor
16096 *
16097 * Returns whether the actor has any actions applied.
16098 *
16099 * Return value: %TRUE if the actor has any actions,
16100 * %FALSE otherwise
16101 *
16102 * Since: 1.10
16103 */
16104 gboolean
clutter_actor_has_actions(ClutterActor * self)16105 clutter_actor_has_actions (ClutterActor *self)
16106 {
16107 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16108
16109 if (self->priv->actions == NULL)
16110 return FALSE;
16111
16112 return _clutter_meta_group_has_metas_no_internal (self->priv->actions);
16113 }
16114
16115 /**
16116 * clutter_actor_get_n_children:
16117 * @self: a #ClutterActor
16118 *
16119 * Retrieves the number of children of @self.
16120 *
16121 * Return value: the number of children of an actor
16122 *
16123 * Since: 1.10
16124 */
16125 gint
clutter_actor_get_n_children(ClutterActor * self)16126 clutter_actor_get_n_children (ClutterActor *self)
16127 {
16128 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
16129
16130 return self->priv->n_children;
16131 }
16132
16133 /**
16134 * clutter_actor_get_child_at_index:
16135 * @self: a #ClutterActor
16136 * @index_: the position in the list of children
16137 *
16138 * Retrieves the actor at the given @index_ inside the list of
16139 * children of @self.
16140 *
16141 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
16142 *
16143 * Since: 1.10
16144 */
16145 ClutterActor *
clutter_actor_get_child_at_index(ClutterActor * self,gint index_)16146 clutter_actor_get_child_at_index (ClutterActor *self,
16147 gint index_)
16148 {
16149 ClutterActor *iter;
16150 int i;
16151
16152 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16153 g_return_val_if_fail (index_ <= self->priv->n_children, NULL);
16154
16155 for (iter = self->priv->first_child, i = 0;
16156 iter != NULL && i < index_;
16157 iter = iter->priv->next_sibling, i += 1)
16158 ;
16159
16160 return iter;
16161 }
16162
16163 /*< private >
16164 * _clutter_actor_foreach_child:
16165 * @actor: The actor whose children you want to iterate
16166 * @callback: The function to call for each child
16167 * @user_data: Private data to pass to @callback
16168 *
16169 * Calls a given @callback once for each child of the specified @actor and
16170 * passing the @user_data pointer each time.
16171 *
16172 * Return value: returns %TRUE if all children were iterated, else
16173 * %FALSE if a callback broke out of iteration early.
16174 */
16175 gboolean
_clutter_actor_foreach_child(ClutterActor * self,ClutterForeachCallback callback,gpointer user_data)16176 _clutter_actor_foreach_child (ClutterActor *self,
16177 ClutterForeachCallback callback,
16178 gpointer user_data)
16179 {
16180 ClutterActor *iter;
16181 gboolean cont;
16182
16183 if (self->priv->first_child == NULL)
16184 return TRUE;
16185
16186 cont = TRUE;
16187 iter = self->priv->first_child;
16188
16189 /* we use this form so that it's safe to change the children
16190 * list while iterating it
16191 */
16192 while (cont && iter != NULL)
16193 {
16194 ClutterActor *next = iter->priv->next_sibling;
16195
16196 cont = callback (iter, user_data);
16197
16198 iter = next;
16199 }
16200
16201 return cont;
16202 }
16203
16204 #if 0
16205 /* For debugging purposes this gives us a simple way to print out
16206 * the scenegraph e.g in gdb using:
16207 * [|
16208 * _clutter_actor_traverse (stage,
16209 * 0,
16210 * clutter_debug_print_actor_cb,
16211 * NULL,
16212 * NULL);
16213 * |]
16214 */
16215 static ClutterActorTraverseVisitFlags
16216 clutter_debug_print_actor_cb (ClutterActor *actor,
16217 int depth,
16218 void *user_data)
16219 {
16220 g_print ("%*s%s:%p\n",
16221 depth * 2, "",
16222 _clutter_actor_get_debug_name (actor),
16223 actor);
16224
16225 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
16226 }
16227 #endif
16228
16229 static void
_clutter_actor_traverse_breadth(ClutterActor * actor,ClutterTraverseCallback callback,gpointer user_data)16230 _clutter_actor_traverse_breadth (ClutterActor *actor,
16231 ClutterTraverseCallback callback,
16232 gpointer user_data)
16233 {
16234 GQueue *queue = g_queue_new ();
16235 ClutterActor dummy;
16236 int current_depth = 0;
16237
16238 g_queue_push_tail (queue, actor);
16239 g_queue_push_tail (queue, &dummy); /* use to delimit depth changes */
16240
16241 while ((actor = g_queue_pop_head (queue)))
16242 {
16243 ClutterActorTraverseVisitFlags flags;
16244
16245 if (actor == &dummy)
16246 {
16247 current_depth++;
16248 g_queue_push_tail (queue, &dummy);
16249 continue;
16250 }
16251
16252 flags = callback (actor, current_depth, user_data);
16253 if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
16254 break;
16255 else if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
16256 {
16257 ClutterActor *iter;
16258
16259 for (iter = actor->priv->first_child;
16260 iter != NULL;
16261 iter = iter->priv->next_sibling)
16262 {
16263 g_queue_push_tail (queue, iter);
16264 }
16265 }
16266 }
16267
16268 g_queue_free (queue);
16269 }
16270
16271 static ClutterActorTraverseVisitFlags
_clutter_actor_traverse_depth(ClutterActor * actor,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,int current_depth,gpointer user_data)16272 _clutter_actor_traverse_depth (ClutterActor *actor,
16273 ClutterTraverseCallback before_children_callback,
16274 ClutterTraverseCallback after_children_callback,
16275 int current_depth,
16276 gpointer user_data)
16277 {
16278 ClutterActorTraverseVisitFlags flags;
16279
16280 flags = before_children_callback (actor, current_depth, user_data);
16281 if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
16282 return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
16283
16284 if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
16285 {
16286 ClutterActor *iter;
16287
16288 for (iter = actor->priv->first_child;
16289 iter != NULL;
16290 iter = iter->priv->next_sibling)
16291 {
16292 flags = _clutter_actor_traverse_depth (iter,
16293 before_children_callback,
16294 after_children_callback,
16295 current_depth + 1,
16296 user_data);
16297
16298 if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
16299 return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
16300 }
16301 }
16302
16303 if (after_children_callback)
16304 return after_children_callback (actor, current_depth, user_data);
16305 else
16306 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
16307 }
16308
16309 /* _clutter_actor_traverse:
16310 * @actor: The actor to start traversing the graph from
16311 * @flags: These flags may affect how the traversal is done
16312 * @before_children_callback: A function to call before visiting the
16313 * children of the current actor.
16314 * @after_children_callback: A function to call after visiting the
16315 * children of the current actor. (Ignored if
16316 * %CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST is passed to @flags.)
16317 * @user_data: The private data to pass to the callbacks
16318 *
16319 * Traverses the scenegraph starting at the specified @actor and
16320 * descending through all its children and its children's children.
16321 * For each actor traversed @before_children_callback and
16322 * @after_children_callback are called with the specified
16323 * @user_data, before and after visiting that actor's children.
16324 *
16325 * The callbacks can return flags that affect the ongoing traversal
16326 * such as by skipping over an actors children or bailing out of
16327 * any further traversing.
16328 */
16329 void
_clutter_actor_traverse(ClutterActor * actor,ClutterActorTraverseFlags flags,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,gpointer user_data)16330 _clutter_actor_traverse (ClutterActor *actor,
16331 ClutterActorTraverseFlags flags,
16332 ClutterTraverseCallback before_children_callback,
16333 ClutterTraverseCallback after_children_callback,
16334 gpointer user_data)
16335 {
16336 if (flags & CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST)
16337 _clutter_actor_traverse_breadth (actor,
16338 before_children_callback,
16339 user_data);
16340 else /* DEPTH_FIRST */
16341 _clutter_actor_traverse_depth (actor,
16342 before_children_callback,
16343 after_children_callback,
16344 0, /* start depth */
16345 user_data);
16346 }
16347
16348 static void
on_layout_manager_changed(ClutterLayoutManager * manager,ClutterActor * self)16349 on_layout_manager_changed (ClutterLayoutManager *manager,
16350 ClutterActor *self)
16351 {
16352 clutter_actor_queue_relayout (self);
16353 }
16354
16355 /**
16356 * clutter_actor_set_layout_manager:
16357 * @self: a #ClutterActor
16358 * @manager: (allow-none): a #ClutterLayoutManager, or %NULL to unset it
16359 *
16360 * Sets the #ClutterLayoutManager delegate object that will be used to
16361 * lay out the children of @self.
16362 *
16363 * The #ClutterActor will take a reference on the passed @manager which
16364 * will be released either when the layout manager is removed, or when
16365 * the actor is destroyed.
16366 *
16367 * Since: 1.10
16368 */
16369 void
clutter_actor_set_layout_manager(ClutterActor * self,ClutterLayoutManager * manager)16370 clutter_actor_set_layout_manager (ClutterActor *self,
16371 ClutterLayoutManager *manager)
16372 {
16373 ClutterActorPrivate *priv;
16374
16375 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16376 g_return_if_fail (manager == NULL || CLUTTER_IS_LAYOUT_MANAGER (manager));
16377
16378 priv = self->priv;
16379
16380 if (priv->layout_manager != NULL)
16381 {
16382 g_clear_signal_handler (&priv->layout_changed_id, priv->layout_manager);
16383 clutter_layout_manager_set_container (priv->layout_manager, NULL);
16384 g_clear_object (&priv->layout_manager);
16385 }
16386
16387 priv->layout_manager = manager;
16388
16389 if (priv->layout_manager != NULL)
16390 {
16391 g_object_ref_sink (priv->layout_manager);
16392 clutter_layout_manager_set_container (priv->layout_manager,
16393 CLUTTER_CONTAINER (self));
16394 priv->layout_changed_id =
16395 g_signal_connect (priv->layout_manager, "layout-changed",
16396 G_CALLBACK (on_layout_manager_changed),
16397 self);
16398 }
16399
16400 clutter_actor_queue_relayout (self);
16401
16402 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAYOUT_MANAGER]);
16403 }
16404
16405 /**
16406 * clutter_actor_get_layout_manager:
16407 * @self: a #ClutterActor
16408 *
16409 * Retrieves the #ClutterLayoutManager used by @self.
16410 *
16411 * Return value: (transfer none): a pointer to the #ClutterLayoutManager,
16412 * or %NULL
16413 *
16414 * Since: 1.10
16415 */
16416 ClutterLayoutManager *
clutter_actor_get_layout_manager(ClutterActor * self)16417 clutter_actor_get_layout_manager (ClutterActor *self)
16418 {
16419 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16420
16421 return self->priv->layout_manager;
16422 }
16423
16424 static const ClutterLayoutInfo default_layout_info = {
16425 GRAPHENE_POINT_INIT_ZERO, /* fixed-pos */
16426 { 0, 0, 0, 0 }, /* margin */
16427 CLUTTER_ACTOR_ALIGN_FILL, /* x-align */
16428 CLUTTER_ACTOR_ALIGN_FILL, /* y-align */
16429 FALSE, FALSE, /* expand */
16430 GRAPHENE_SIZE_INIT_ZERO, /* minimum */
16431 GRAPHENE_SIZE_INIT_ZERO, /* natural */
16432 };
16433
16434 static void
layout_info_free(gpointer data)16435 layout_info_free (gpointer data)
16436 {
16437 if (G_LIKELY (data != NULL))
16438 g_free (data);
16439 }
16440
16441 /*< private >
16442 * _clutter_actor_peek_layout_info:
16443 * @self: a #ClutterActor
16444 *
16445 * Retrieves a pointer to the ClutterLayoutInfo structure.
16446 *
16447 * If the actor does not have a ClutterLayoutInfo associated to it, %NULL is returned.
16448 *
16449 * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
16450 */
16451 ClutterLayoutInfo *
_clutter_actor_peek_layout_info(ClutterActor * self)16452 _clutter_actor_peek_layout_info (ClutterActor *self)
16453 {
16454 return g_object_get_qdata (G_OBJECT (self), quark_actor_layout_info);
16455 }
16456
16457 /*< private >
16458 * _clutter_actor_get_layout_info:
16459 * @self: a #ClutterActor
16460 *
16461 * Retrieves a pointer to the ClutterLayoutInfo structure.
16462 *
16463 * If the actor does not have a ClutterLayoutInfo associated to it, one
16464 * will be created and initialized to the default values.
16465 *
16466 * This function should be used for setters.
16467 *
16468 * For getters, you should use _clutter_actor_get_layout_info_or_defaults()
16469 * instead.
16470 *
16471 * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
16472 */
16473 ClutterLayoutInfo *
_clutter_actor_get_layout_info(ClutterActor * self)16474 _clutter_actor_get_layout_info (ClutterActor *self)
16475 {
16476 ClutterLayoutInfo *retval;
16477
16478 retval = _clutter_actor_peek_layout_info (self);
16479 if (retval == NULL)
16480 {
16481 retval = g_new0 (ClutterLayoutInfo, 1);
16482
16483 *retval = default_layout_info;
16484
16485 g_object_set_qdata_full (G_OBJECT (self), quark_actor_layout_info,
16486 retval,
16487 layout_info_free);
16488 }
16489
16490 return retval;
16491 }
16492
16493 /*< private >
16494 * _clutter_actor_get_layout_info_or_defaults:
16495 * @self: a #ClutterActor
16496 *
16497 * Retrieves the ClutterLayoutInfo structure associated to an actor.
16498 *
16499 * If the actor does not have a ClutterLayoutInfo structure associated to it,
16500 * then the default structure will be returned.
16501 *
16502 * This function should only be used for getters.
16503 *
16504 * Return value: a const pointer to the ClutterLayoutInfo structure
16505 */
16506 const ClutterLayoutInfo *
_clutter_actor_get_layout_info_or_defaults(ClutterActor * self)16507 _clutter_actor_get_layout_info_or_defaults (ClutterActor *self)
16508 {
16509 const ClutterLayoutInfo *info;
16510
16511 info = _clutter_actor_peek_layout_info (self);
16512 if (info == NULL)
16513 return &default_layout_info;
16514
16515 return info;
16516 }
16517
16518 /**
16519 * clutter_actor_set_x_align:
16520 * @self: a #ClutterActor
16521 * @x_align: the horizontal alignment policy
16522 *
16523 * Sets the horizontal alignment policy of a #ClutterActor, in case the
16524 * actor received extra horizontal space.
16525 *
16526 * See also the #ClutterActor:x-align property.
16527 *
16528 * Since: 1.10
16529 */
16530 void
clutter_actor_set_x_align(ClutterActor * self,ClutterActorAlign x_align)16531 clutter_actor_set_x_align (ClutterActor *self,
16532 ClutterActorAlign x_align)
16533 {
16534 ClutterLayoutInfo *info;
16535
16536 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16537
16538 info = _clutter_actor_get_layout_info (self);
16539
16540 if (info->x_align != x_align)
16541 {
16542 info->x_align = x_align;
16543
16544 clutter_actor_queue_relayout (self);
16545
16546 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_X_ALIGN]);
16547 }
16548 }
16549
16550 /**
16551 * clutter_actor_get_x_align:
16552 * @self: a #ClutterActor
16553 *
16554 * Retrieves the horizontal alignment policy set using
16555 * clutter_actor_set_x_align().
16556 *
16557 * Return value: the horizontal alignment policy.
16558 *
16559 * Since: 1.10
16560 */
16561 ClutterActorAlign
clutter_actor_get_x_align(ClutterActor * self)16562 clutter_actor_get_x_align (ClutterActor *self)
16563 {
16564 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
16565
16566 return _clutter_actor_get_layout_info_or_defaults (self)->x_align;
16567 }
16568
16569 /**
16570 * clutter_actor_set_y_align:
16571 * @self: a #ClutterActor
16572 * @y_align: the vertical alignment policy
16573 *
16574 * Sets the vertical alignment policy of a #ClutterActor, in case the
16575 * actor received extra vertical space.
16576 *
16577 * See also the #ClutterActor:y-align property.
16578 *
16579 * Since: 1.10
16580 */
16581 void
clutter_actor_set_y_align(ClutterActor * self,ClutterActorAlign y_align)16582 clutter_actor_set_y_align (ClutterActor *self,
16583 ClutterActorAlign y_align)
16584 {
16585 ClutterLayoutInfo *info;
16586
16587 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16588
16589 info = _clutter_actor_get_layout_info (self);
16590
16591 if (info->y_align != y_align)
16592 {
16593 info->y_align = y_align;
16594
16595 clutter_actor_queue_relayout (self);
16596
16597 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Y_ALIGN]);
16598 }
16599 }
16600
16601 /**
16602 * clutter_actor_get_y_align:
16603 * @self: a #ClutterActor
16604 *
16605 * Retrieves the vertical alignment policy set using
16606 * clutter_actor_set_y_align().
16607 *
16608 * Return value: the vertical alignment policy.
16609 *
16610 * Since: 1.10
16611 */
16612 ClutterActorAlign
clutter_actor_get_y_align(ClutterActor * self)16613 clutter_actor_get_y_align (ClutterActor *self)
16614 {
16615 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
16616
16617 return _clutter_actor_get_layout_info_or_defaults (self)->y_align;
16618 }
16619
16620 static inline void
clutter_actor_set_margin_internal(ClutterActor * self,gfloat margin,GParamSpec * pspec)16621 clutter_actor_set_margin_internal (ClutterActor *self,
16622 gfloat margin,
16623 GParamSpec *pspec)
16624 {
16625 ClutterLayoutInfo *info;
16626
16627 info = _clutter_actor_get_layout_info (self);
16628
16629 if (pspec == obj_props[PROP_MARGIN_TOP])
16630 info->margin.top = margin;
16631 else if (pspec == obj_props[PROP_MARGIN_RIGHT])
16632 info->margin.right = margin;
16633 else if (pspec == obj_props[PROP_MARGIN_BOTTOM])
16634 info->margin.bottom = margin;
16635 else
16636 info->margin.left = margin;
16637
16638 clutter_actor_queue_relayout (self);
16639 g_object_notify_by_pspec (G_OBJECT (self), pspec);
16640 }
16641
16642 /**
16643 * clutter_actor_set_margin:
16644 * @self: a #ClutterActor
16645 * @margin: a #ClutterMargin
16646 *
16647 * Sets all the components of the margin of a #ClutterActor.
16648 *
16649 * Since: 1.10
16650 */
16651 void
clutter_actor_set_margin(ClutterActor * self,const ClutterMargin * margin)16652 clutter_actor_set_margin (ClutterActor *self,
16653 const ClutterMargin *margin)
16654 {
16655 ClutterLayoutInfo *info;
16656
16657 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16658 g_return_if_fail (margin != NULL);
16659
16660 info = _clutter_actor_get_layout_info (self);
16661
16662 if (info->margin.top != margin->top)
16663 clutter_actor_set_margin_top (self, margin->top);
16664
16665 if (info->margin.right != margin->right)
16666 clutter_actor_set_margin_right (self, margin->right);
16667
16668 if (info->margin.bottom != margin->bottom)
16669 clutter_actor_set_margin_bottom (self, margin->bottom);
16670
16671 if (info->margin.left != margin->left)
16672 clutter_actor_set_margin_left (self, margin->left);
16673 }
16674
16675 /**
16676 * clutter_actor_get_margin:
16677 * @self: a #ClutterActor
16678 * @margin: (out caller-allocates): return location for a #ClutterMargin
16679 *
16680 * Retrieves all the components of the margin of a #ClutterActor.
16681 *
16682 * Since: 1.10
16683 */
16684 void
clutter_actor_get_margin(ClutterActor * self,ClutterMargin * margin)16685 clutter_actor_get_margin (ClutterActor *self,
16686 ClutterMargin *margin)
16687 {
16688 const ClutterLayoutInfo *info;
16689
16690 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16691 g_return_if_fail (margin != NULL);
16692
16693 info = _clutter_actor_get_layout_info_or_defaults (self);
16694
16695 *margin = info->margin;
16696 }
16697
16698 /**
16699 * clutter_actor_set_margin_top:
16700 * @self: a #ClutterActor
16701 * @margin: the top margin
16702 *
16703 * Sets the margin from the top of a #ClutterActor.
16704 *
16705 * The #ClutterActor:margin-top property is animatable.
16706 *
16707 * Since: 1.10
16708 */
16709 void
clutter_actor_set_margin_top(ClutterActor * self,gfloat margin)16710 clutter_actor_set_margin_top (ClutterActor *self,
16711 gfloat margin)
16712 {
16713 const ClutterLayoutInfo *info;
16714
16715 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16716 g_return_if_fail (margin >= 0.f);
16717
16718 info = _clutter_actor_get_layout_info_or_defaults (self);
16719
16720 if (info->margin.top == margin)
16721 return;
16722
16723 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_TOP],
16724 info->margin.top,
16725 margin);
16726 }
16727
16728 /**
16729 * clutter_actor_get_margin_top:
16730 * @self: a #ClutterActor
16731 *
16732 * Retrieves the top margin of a #ClutterActor.
16733 *
16734 * Return value: the top margin
16735 *
16736 * Since: 1.10
16737 */
16738 gfloat
clutter_actor_get_margin_top(ClutterActor * self)16739 clutter_actor_get_margin_top (ClutterActor *self)
16740 {
16741 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16742
16743 return _clutter_actor_get_layout_info_or_defaults (self)->margin.top;
16744 }
16745
16746 /**
16747 * clutter_actor_set_margin_bottom:
16748 * @self: a #ClutterActor
16749 * @margin: the bottom margin
16750 *
16751 * Sets the margin from the bottom of a #ClutterActor.
16752 *
16753 * The #ClutterActor:margin-bottom property is animatable.
16754 *
16755 * Since: 1.10
16756 */
16757 void
clutter_actor_set_margin_bottom(ClutterActor * self,gfloat margin)16758 clutter_actor_set_margin_bottom (ClutterActor *self,
16759 gfloat margin)
16760 {
16761 const ClutterLayoutInfo *info;
16762
16763 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16764 g_return_if_fail (margin >= 0.f);
16765
16766 info = _clutter_actor_get_layout_info_or_defaults (self);
16767
16768 if (info->margin.bottom == margin)
16769 return;
16770
16771 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_BOTTOM],
16772 info->margin.bottom,
16773 margin);
16774 }
16775
16776 /**
16777 * clutter_actor_get_margin_bottom:
16778 * @self: a #ClutterActor
16779 *
16780 * Retrieves the bottom margin of a #ClutterActor.
16781 *
16782 * Return value: the bottom margin
16783 *
16784 * Since: 1.10
16785 */
16786 gfloat
clutter_actor_get_margin_bottom(ClutterActor * self)16787 clutter_actor_get_margin_bottom (ClutterActor *self)
16788 {
16789 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16790
16791 return _clutter_actor_get_layout_info_or_defaults (self)->margin.bottom;
16792 }
16793
16794 /**
16795 * clutter_actor_set_margin_left:
16796 * @self: a #ClutterActor
16797 * @margin: the left margin
16798 *
16799 * Sets the margin from the left of a #ClutterActor.
16800 *
16801 * The #ClutterActor:margin-left property is animatable.
16802 *
16803 * Since: 1.10
16804 */
16805 void
clutter_actor_set_margin_left(ClutterActor * self,gfloat margin)16806 clutter_actor_set_margin_left (ClutterActor *self,
16807 gfloat margin)
16808 {
16809 const ClutterLayoutInfo *info;
16810
16811 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16812 g_return_if_fail (margin >= 0.f);
16813
16814 info = _clutter_actor_get_layout_info_or_defaults (self);
16815
16816 if (info->margin.left == margin)
16817 return;
16818
16819 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_LEFT],
16820 info->margin.left,
16821 margin);
16822 }
16823
16824 /**
16825 * clutter_actor_get_margin_left:
16826 * @self: a #ClutterActor
16827 *
16828 * Retrieves the left margin of a #ClutterActor.
16829 *
16830 * Return value: the left margin
16831 *
16832 * Since: 1.10
16833 */
16834 gfloat
clutter_actor_get_margin_left(ClutterActor * self)16835 clutter_actor_get_margin_left (ClutterActor *self)
16836 {
16837 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16838
16839 return _clutter_actor_get_layout_info_or_defaults (self)->margin.left;
16840 }
16841
16842 /**
16843 * clutter_actor_set_margin_right:
16844 * @self: a #ClutterActor
16845 * @margin: the right margin
16846 *
16847 * Sets the margin from the right of a #ClutterActor.
16848 *
16849 * The #ClutterActor:margin-right property is animatable.
16850 *
16851 * Since: 1.10
16852 */
16853 void
clutter_actor_set_margin_right(ClutterActor * self,gfloat margin)16854 clutter_actor_set_margin_right (ClutterActor *self,
16855 gfloat margin)
16856 {
16857 const ClutterLayoutInfo *info;
16858
16859 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16860 g_return_if_fail (margin >= 0.f);
16861
16862 info = _clutter_actor_get_layout_info_or_defaults (self);
16863
16864 if (info->margin.right == margin)
16865 return;
16866
16867 _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_RIGHT],
16868 info->margin.right,
16869 margin);
16870 }
16871
16872 /**
16873 * clutter_actor_get_margin_right:
16874 * @self: a #ClutterActor
16875 *
16876 * Retrieves the right margin of a #ClutterActor.
16877 *
16878 * Return value: the right margin
16879 *
16880 * Since: 1.10
16881 */
16882 gfloat
clutter_actor_get_margin_right(ClutterActor * self)16883 clutter_actor_get_margin_right (ClutterActor *self)
16884 {
16885 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16886
16887 return _clutter_actor_get_layout_info_or_defaults (self)->margin.right;
16888 }
16889
16890 static inline void
clutter_actor_set_background_color_internal(ClutterActor * self,const ClutterColor * color)16891 clutter_actor_set_background_color_internal (ClutterActor *self,
16892 const ClutterColor *color)
16893 {
16894 ClutterActorPrivate *priv = self->priv;
16895 GObject *obj;
16896
16897 if (priv->bg_color_set && clutter_color_equal (color, &priv->bg_color))
16898 return;
16899
16900 obj = G_OBJECT (self);
16901
16902 priv->bg_color = *color;
16903 priv->bg_color_set = TRUE;
16904
16905 clutter_actor_queue_redraw (self);
16906
16907 g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
16908 g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR]);
16909 }
16910
16911 /**
16912 * clutter_actor_set_background_color:
16913 * @self: a #ClutterActor
16914 * @color: (allow-none): a #ClutterColor, or %NULL to unset a previously
16915 * set color
16916 *
16917 * Sets the background color of a #ClutterActor.
16918 *
16919 * The background color will be used to cover the whole allocation of the
16920 * actor. The default background color of an actor is transparent.
16921 *
16922 * To check whether an actor has a background color, you can use the
16923 * #ClutterActor:background-color-set actor property.
16924 *
16925 * The #ClutterActor:background-color property is animatable.
16926 *
16927 * Since: 1.10
16928 */
16929 void
clutter_actor_set_background_color(ClutterActor * self,const ClutterColor * color)16930 clutter_actor_set_background_color (ClutterActor *self,
16931 const ClutterColor *color)
16932 {
16933 ClutterActorPrivate *priv;
16934
16935 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16936
16937 priv = self->priv;
16938
16939 if (color == NULL)
16940 {
16941 GObject *obj = G_OBJECT (self);
16942
16943 priv->bg_color_set = FALSE;
16944
16945 clutter_actor_queue_redraw (self);
16946
16947 g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
16948 }
16949 else
16950 _clutter_actor_create_transition (self,
16951 obj_props[PROP_BACKGROUND_COLOR],
16952 &priv->bg_color,
16953 color);
16954 }
16955
16956 /**
16957 * clutter_actor_get_background_color:
16958 * @self: a #ClutterActor
16959 * @color: (out caller-allocates): return location for a #ClutterColor
16960 *
16961 * Retrieves the color set using clutter_actor_set_background_color().
16962 *
16963 * Since: 1.10
16964 */
16965 void
clutter_actor_get_background_color(ClutterActor * self,ClutterColor * color)16966 clutter_actor_get_background_color (ClutterActor *self,
16967 ClutterColor *color)
16968 {
16969 g_return_if_fail (CLUTTER_IS_ACTOR (self));
16970 g_return_if_fail (color != NULL);
16971
16972 *color = self->priv->bg_color;
16973 }
16974
16975 /**
16976 * clutter_actor_get_previous_sibling:
16977 * @self: a #ClutterActor
16978 *
16979 * Retrieves the sibling of @self that comes before it in the list
16980 * of children of @self's parent.
16981 *
16982 * The returned pointer is only valid until the scene graph changes; it
16983 * is not safe to modify the list of children of @self while iterating
16984 * it.
16985 *
16986 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
16987 *
16988 * Since: 1.10
16989 */
16990 ClutterActor *
clutter_actor_get_previous_sibling(ClutterActor * self)16991 clutter_actor_get_previous_sibling (ClutterActor *self)
16992 {
16993 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16994
16995 return self->priv->prev_sibling;
16996 }
16997
16998 /**
16999 * clutter_actor_get_next_sibling:
17000 * @self: a #ClutterActor
17001 *
17002 * Retrieves the sibling of @self that comes after it in the list
17003 * of children of @self's parent.
17004 *
17005 * The returned pointer is only valid until the scene graph changes; it
17006 * is not safe to modify the list of children of @self while iterating
17007 * it.
17008 *
17009 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17010 *
17011 * Since: 1.10
17012 */
17013 ClutterActor *
clutter_actor_get_next_sibling(ClutterActor * self)17014 clutter_actor_get_next_sibling (ClutterActor *self)
17015 {
17016 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17017
17018 return self->priv->next_sibling;
17019 }
17020
17021 /**
17022 * clutter_actor_get_first_child:
17023 * @self: a #ClutterActor
17024 *
17025 * Retrieves the first child of @self.
17026 *
17027 * The returned pointer is only valid until the scene graph changes; it
17028 * is not safe to modify the list of children of @self while iterating
17029 * it.
17030 *
17031 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17032 *
17033 * Since: 1.10
17034 */
17035 ClutterActor *
clutter_actor_get_first_child(ClutterActor * self)17036 clutter_actor_get_first_child (ClutterActor *self)
17037 {
17038 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17039
17040 return self->priv->first_child;
17041 }
17042
17043 /**
17044 * clutter_actor_get_last_child:
17045 * @self: a #ClutterActor
17046 *
17047 * Retrieves the last child of @self.
17048 *
17049 * The returned pointer is only valid until the scene graph changes; it
17050 * is not safe to modify the list of children of @self while iterating
17051 * it.
17052 *
17053 * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17054 *
17055 * Since: 1.10
17056 */
17057 ClutterActor *
clutter_actor_get_last_child(ClutterActor * self)17058 clutter_actor_get_last_child (ClutterActor *self)
17059 {
17060 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17061
17062 return self->priv->last_child;
17063 }
17064
17065 /* easy way to have properly named fields instead of the dummy ones
17066 * we use in the public structure
17067 */
17068 typedef struct _RealActorIter
17069 {
17070 ClutterActor *root; /* dummy1 */
17071 ClutterActor *current; /* dummy2 */
17072 gpointer padding_1; /* dummy3 */
17073 gint age; /* dummy4 */
17074 gpointer padding_2; /* dummy5 */
17075 } RealActorIter;
17076
17077 /**
17078 * clutter_actor_iter_init:
17079 * @iter: a #ClutterActorIter
17080 * @root: a #ClutterActor
17081 *
17082 * Initializes a #ClutterActorIter, which can then be used to iterate
17083 * efficiently over a section of the scene graph, and associates it
17084 * with @root.
17085 *
17086 * Modifying the scene graph section that contains @root will invalidate
17087 * the iterator.
17088 *
17089 * |[<!-- language="C" -->
17090 * ClutterActorIter iter;
17091 * ClutterActor *child;
17092 *
17093 * clutter_actor_iter_init (&iter, container);
17094 * while (clutter_actor_iter_next (&iter, &child))
17095 * {
17096 * // do something with child
17097 * }
17098 * ]|
17099 *
17100 * Since: 1.10
17101 */
17102 void
clutter_actor_iter_init(ClutterActorIter * iter,ClutterActor * root)17103 clutter_actor_iter_init (ClutterActorIter *iter,
17104 ClutterActor *root)
17105 {
17106 RealActorIter *ri = (RealActorIter *) iter;
17107
17108 g_return_if_fail (iter != NULL);
17109 g_return_if_fail (CLUTTER_IS_ACTOR (root));
17110
17111 ri->root = root;
17112 ri->current = NULL;
17113 ri->age = root->priv->age;
17114 }
17115
17116 /**
17117 * clutter_actor_iter_is_valid:
17118 * @iter: a #ClutterActorIter
17119 *
17120 * Checks whether a #ClutterActorIter is still valid.
17121 *
17122 * An iterator is considered valid if it has been initialized, and
17123 * if the #ClutterActor that it refers to hasn't been modified after
17124 * the initialization.
17125 *
17126 * Return value: %TRUE if the iterator is valid, and %FALSE otherwise
17127 *
17128 * Since: 1.12
17129 */
17130 gboolean
clutter_actor_iter_is_valid(const ClutterActorIter * iter)17131 clutter_actor_iter_is_valid (const ClutterActorIter *iter)
17132 {
17133 RealActorIter *ri = (RealActorIter *) iter;
17134
17135 g_return_val_if_fail (iter != NULL, FALSE);
17136
17137 if (ri->root == NULL)
17138 return FALSE;
17139
17140 return ri->root->priv->age == ri->age;
17141 }
17142
17143 /**
17144 * clutter_actor_iter_next:
17145 * @iter: a #ClutterActorIter
17146 * @child: (out) (transfer none): return location for a #ClutterActor
17147 *
17148 * Advances the @iter and retrieves the next child of the root #ClutterActor
17149 * that was used to initialize the #ClutterActorIterator.
17150 *
17151 * If the iterator can advance, this function returns %TRUE and sets the
17152 * @child argument.
17153 *
17154 * If the iterator cannot advance, this function returns %FALSE, and
17155 * the contents of @child are undefined.
17156 *
17157 * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
17158 *
17159 * Since: 1.10
17160 */
17161 gboolean
clutter_actor_iter_next(ClutterActorIter * iter,ClutterActor ** child)17162 clutter_actor_iter_next (ClutterActorIter *iter,
17163 ClutterActor **child)
17164 {
17165 RealActorIter *ri = (RealActorIter *) iter;
17166
17167 g_return_val_if_fail (iter != NULL, FALSE);
17168 g_return_val_if_fail (ri->root != NULL, FALSE);
17169 #ifndef G_DISABLE_ASSERT
17170 g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
17171 #endif
17172
17173 if (ri->current == NULL)
17174 ri->current = ri->root->priv->first_child;
17175 else
17176 ri->current = ri->current->priv->next_sibling;
17177
17178 if (child != NULL)
17179 *child = ri->current;
17180
17181 return ri->current != NULL;
17182 }
17183
17184 /**
17185 * clutter_actor_iter_prev:
17186 * @iter: a #ClutterActorIter
17187 * @child: (out) (transfer none): return location for a #ClutterActor
17188 *
17189 * Advances the @iter and retrieves the previous child of the root
17190 * #ClutterActor that was used to initialize the #ClutterActorIterator.
17191 *
17192 * If the iterator can advance, this function returns %TRUE and sets the
17193 * @child argument.
17194 *
17195 * If the iterator cannot advance, this function returns %FALSE, and
17196 * the contents of @child are undefined.
17197 *
17198 * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
17199 *
17200 * Since: 1.10
17201 */
17202 gboolean
clutter_actor_iter_prev(ClutterActorIter * iter,ClutterActor ** child)17203 clutter_actor_iter_prev (ClutterActorIter *iter,
17204 ClutterActor **child)
17205 {
17206 RealActorIter *ri = (RealActorIter *) iter;
17207
17208 g_return_val_if_fail (iter != NULL, FALSE);
17209 g_return_val_if_fail (ri->root != NULL, FALSE);
17210 #ifndef G_DISABLE_ASSERT
17211 g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
17212 #endif
17213
17214 if (ri->current == NULL)
17215 ri->current = ri->root->priv->last_child;
17216 else
17217 ri->current = ri->current->priv->prev_sibling;
17218
17219 if (child != NULL)
17220 *child = ri->current;
17221
17222 return ri->current != NULL;
17223 }
17224
17225 /**
17226 * clutter_actor_iter_remove:
17227 * @iter: a #ClutterActorIter
17228 *
17229 * Safely removes the #ClutterActor currently pointer to by the iterator
17230 * from its parent.
17231 *
17232 * This function can only be called after clutter_actor_iter_next() or
17233 * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
17234 * than once for the same actor.
17235 *
17236 * This function will call clutter_actor_remove_child() internally.
17237 *
17238 * Since: 1.10
17239 */
17240 void
clutter_actor_iter_remove(ClutterActorIter * iter)17241 clutter_actor_iter_remove (ClutterActorIter *iter)
17242 {
17243 RealActorIter *ri = (RealActorIter *) iter;
17244 ClutterActor *cur;
17245
17246 g_return_if_fail (iter != NULL);
17247 g_return_if_fail (ri->root != NULL);
17248 #ifndef G_DISABLE_ASSERT
17249 g_return_if_fail (ri->age == ri->root->priv->age);
17250 #endif
17251 g_return_if_fail (ri->current != NULL);
17252
17253 cur = ri->current;
17254
17255 if (cur != NULL)
17256 {
17257 ri->current = cur->priv->prev_sibling;
17258
17259 clutter_actor_remove_child_internal (ri->root, cur,
17260 REMOVE_CHILD_DEFAULT_FLAGS);
17261
17262 ri->age += 1;
17263 }
17264 }
17265
17266 /**
17267 * clutter_actor_iter_destroy:
17268 * @iter: a #ClutterActorIter
17269 *
17270 * Safely destroys the #ClutterActor currently pointer to by the iterator
17271 * from its parent.
17272 *
17273 * This function can only be called after clutter_actor_iter_next() or
17274 * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
17275 * than once for the same actor.
17276 *
17277 * This function will call clutter_actor_destroy() internally.
17278 *
17279 * Since: 1.10
17280 */
17281 void
clutter_actor_iter_destroy(ClutterActorIter * iter)17282 clutter_actor_iter_destroy (ClutterActorIter *iter)
17283 {
17284 RealActorIter *ri = (RealActorIter *) iter;
17285 ClutterActor *cur;
17286
17287 g_return_if_fail (iter != NULL);
17288 g_return_if_fail (ri->root != NULL);
17289 #ifndef G_DISABLE_ASSERT
17290 g_return_if_fail (ri->age == ri->root->priv->age);
17291 #endif
17292 g_return_if_fail (ri->current != NULL);
17293
17294 cur = ri->current;
17295
17296 if (cur != NULL)
17297 {
17298 ri->current = cur->priv->prev_sibling;
17299
17300 clutter_actor_destroy (cur);
17301
17302 ri->age += 1;
17303 }
17304 }
17305
17306 static const ClutterAnimationInfo default_animation_info = {
17307 NULL, /* states */
17308 NULL, /* cur_state */
17309 NULL, /* transitions */
17310 };
17311
17312 static void
clutter_animation_info_free(gpointer data)17313 clutter_animation_info_free (gpointer data)
17314 {
17315 if (data != NULL)
17316 {
17317 ClutterAnimationInfo *info = data;
17318
17319 if (info->transitions != NULL)
17320 g_hash_table_unref (info->transitions);
17321
17322 if (info->states != NULL)
17323 g_array_unref (info->states);
17324
17325 g_free (info);
17326 }
17327 }
17328
17329 const ClutterAnimationInfo *
_clutter_actor_get_animation_info_or_defaults(ClutterActor * self)17330 _clutter_actor_get_animation_info_or_defaults (ClutterActor *self)
17331 {
17332 const ClutterAnimationInfo *res;
17333 GObject *obj = G_OBJECT (self);
17334
17335 res = g_object_get_qdata (obj, quark_actor_animation_info);
17336 if (res != NULL)
17337 return res;
17338
17339 return &default_animation_info;
17340 }
17341
17342 ClutterAnimationInfo *
_clutter_actor_get_animation_info(ClutterActor * self)17343 _clutter_actor_get_animation_info (ClutterActor *self)
17344 {
17345 GObject *obj = G_OBJECT (self);
17346 ClutterAnimationInfo *res;
17347
17348 res = g_object_get_qdata (obj, quark_actor_animation_info);
17349 if (res == NULL)
17350 {
17351 res = g_new0 (ClutterAnimationInfo, 1);
17352
17353 *res = default_animation_info;
17354
17355 g_object_set_qdata_full (obj, quark_actor_animation_info,
17356 res,
17357 clutter_animation_info_free);
17358 }
17359
17360 return res;
17361 }
17362
17363 static void
transition_closure_free(gpointer data)17364 transition_closure_free (gpointer data)
17365 {
17366 if (G_LIKELY (data != NULL))
17367 {
17368 TransitionClosure *clos = data;
17369 ClutterTimeline *timeline;
17370
17371 timeline = CLUTTER_TIMELINE (clos->transition);
17372
17373 /* we disconnect the signal handler before stopping the timeline,
17374 * so that we don't end up inside on_transition_stopped() from
17375 * a call to g_hash_table_remove().
17376 */
17377 g_clear_signal_handler (&clos->completed_id, clos->transition);
17378
17379 if (clutter_timeline_is_playing (timeline))
17380 clutter_timeline_stop (timeline);
17381 else if (clutter_timeline_get_delay (timeline) > 0)
17382 clutter_timeline_cancel_delay (timeline);
17383
17384 /* remove the reference added in add_transition_internal() */
17385 g_object_unref (clos->transition);
17386
17387 g_free (clos->name);
17388
17389 g_free (clos);
17390 }
17391 }
17392
17393 static void
on_transition_stopped(ClutterTransition * transition,gboolean is_finished,TransitionClosure * clos)17394 on_transition_stopped (ClutterTransition *transition,
17395 gboolean is_finished,
17396 TransitionClosure *clos)
17397 {
17398 ClutterActor *actor = clos->actor;
17399 ClutterAnimationInfo *info;
17400 GQuark t_quark;
17401 gchar *t_name;
17402
17403 if (clos->name == NULL)
17404 return;
17405
17406 /* reset the caches used by animations */
17407 clutter_actor_store_content_box (actor, NULL);
17408
17409 info = _clutter_actor_get_animation_info (actor);
17410
17411 /* we need copies because we emit the signal after the
17412 * TransitionClosure data structure has been freed
17413 */
17414 t_quark = g_quark_from_string (clos->name);
17415 t_name = g_strdup (clos->name);
17416
17417 if (clutter_transition_get_remove_on_complete (transition))
17418 {
17419 /* this is safe, because the timeline has now stopped,
17420 * so we won't recurse; the reference on the Animatable
17421 * will be dropped by the ::stopped signal closure in
17422 * ClutterTransition, which is RUN_LAST, and thus will
17423 * be called after this handler
17424 */
17425 g_hash_table_remove (info->transitions, clos->name);
17426 }
17427
17428 /* we emit the ::transition-stopped after removing the
17429 * transition, so that we can chain up new transitions
17430 * without interfering with the one that just finished
17431 */
17432 g_signal_emit (actor, actor_signals[TRANSITION_STOPPED], t_quark,
17433 t_name,
17434 is_finished);
17435
17436 g_free (t_name);
17437
17438 /* if it's the last transition then we clean up */
17439 if (g_hash_table_size (info->transitions) == 0)
17440 {
17441 g_hash_table_unref (info->transitions);
17442 info->transitions = NULL;
17443
17444 CLUTTER_NOTE (ANIMATION, "Transitions for '%s' completed",
17445 _clutter_actor_get_debug_name (actor));
17446
17447 g_signal_emit (actor, actor_signals[TRANSITIONS_COMPLETED], 0);
17448 }
17449 }
17450
17451 static void
clutter_actor_add_transition_internal(ClutterActor * self,const gchar * name,ClutterTransition * transition)17452 clutter_actor_add_transition_internal (ClutterActor *self,
17453 const gchar *name,
17454 ClutterTransition *transition)
17455 {
17456 ClutterTimeline *timeline;
17457 TransitionClosure *clos;
17458 ClutterAnimationInfo *info;
17459
17460 info = _clutter_actor_get_animation_info (self);
17461
17462 if (info->transitions == NULL)
17463 info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
17464 NULL,
17465 transition_closure_free);
17466
17467 if (g_hash_table_lookup (info->transitions, name) != NULL)
17468 {
17469 g_warning ("A transition with name '%s' already exists for "
17470 "the actor '%s'",
17471 name,
17472 _clutter_actor_get_debug_name (self));
17473 return;
17474 }
17475
17476 clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (self));
17477
17478 timeline = CLUTTER_TIMELINE (transition);
17479
17480 clos = g_new0 (TransitionClosure, 1);
17481 clos->actor = self;
17482 clos->transition = g_object_ref (transition);
17483 clos->name = g_strdup (name);
17484 clos->completed_id = g_signal_connect (timeline, "stopped",
17485 G_CALLBACK (on_transition_stopped),
17486 clos);
17487
17488 CLUTTER_NOTE (ANIMATION,
17489 "Adding transition '%s' [%p] to actor '%s'",
17490 clos->name,
17491 clos->transition,
17492 _clutter_actor_get_debug_name (self));
17493
17494 g_hash_table_insert (info->transitions, clos->name, clos);
17495 clutter_timeline_start (timeline);
17496 }
17497
17498 static gboolean
should_skip_implicit_transition(ClutterActor * self,GParamSpec * pspec)17499 should_skip_implicit_transition (ClutterActor *self,
17500 GParamSpec *pspec)
17501 {
17502 ClutterActorPrivate *priv = self->priv;
17503 const ClutterAnimationInfo *info;
17504
17505 /* this function is called from _clutter_actor_create_transition() which
17506 * calls _clutter_actor_get_animation_info() first, so we're guaranteed
17507 * to have the correct ClutterAnimationInfo pointer
17508 */
17509 info = _clutter_actor_get_animation_info_or_defaults (self);
17510
17511 /* if the easing state has a non-zero duration we always want an
17512 * implicit transition to occur
17513 */
17514 if (info->cur_state->easing_duration == 0)
17515 return TRUE;
17516
17517 /* on the other hand, if the actor hasn't been allocated yet, we want to
17518 * skip all transitions on the :allocation, to avoid actors "flying in"
17519 * into their new position and size
17520 */
17521 if (pspec == obj_props[PROP_ALLOCATION] &&
17522 !clutter_actor_box_is_initialized (&priv->allocation))
17523 return TRUE;
17524
17525 /* if the actor is not mapped and is not part of a branch of the scene
17526 * graph that is being cloned, then we always skip implicit transitions
17527 * on the account of the fact that the actor is not going to be visible
17528 * when those transitions happen
17529 */
17530 if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
17531 !clutter_actor_has_mapped_clones (self))
17532 return TRUE;
17533
17534 return FALSE;
17535 }
17536
17537 /*< private >*
17538 * _clutter_actor_create_transition:
17539 * @actor: a #ClutterActor
17540 * @pspec: the property used for the transition
17541 * @...: initial and final state
17542 *
17543 * Creates a #ClutterTransition for the property represented by @pspec.
17544 *
17545 * Return value: a #ClutterTransition
17546 */
17547 ClutterTransition *
_clutter_actor_create_transition(ClutterActor * actor,GParamSpec * pspec,...)17548 _clutter_actor_create_transition (ClutterActor *actor,
17549 GParamSpec *pspec,
17550 ...)
17551 {
17552 ClutterTimeline *timeline;
17553 ClutterInterval *interval;
17554 ClutterAnimationInfo *info;
17555 ClutterTransition *res = NULL;
17556 gboolean call_restore = FALSE;
17557 TransitionClosure *clos;
17558 va_list var_args;
17559 g_auto (GValue) initial = G_VALUE_INIT;
17560 g_auto (GValue) final = G_VALUE_INIT;
17561 GType ptype;
17562 char *error;
17563
17564 g_assert (pspec != NULL);
17565 g_assert ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0);
17566
17567 info = _clutter_actor_get_animation_info (actor);
17568
17569 /* XXX - this will go away in 2.0
17570 *
17571 * if no state has been pushed, we assume that the easing state is
17572 * in "compatibility mode": all transitions have a duration of 0
17573 * msecs, which means that they happen immediately. in Clutter 2.0
17574 * this will turn into a g_assert(info->states != NULL), as every
17575 * actor will start with a predefined easing state
17576 */
17577 if (info->states == NULL)
17578 {
17579 clutter_actor_save_easing_state (actor);
17580 clutter_actor_set_easing_duration (actor, 0);
17581 call_restore = TRUE;
17582 }
17583
17584 if (info->transitions == NULL)
17585 info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
17586 NULL,
17587 transition_closure_free);
17588
17589 va_start (var_args, pspec);
17590
17591 ptype = G_PARAM_SPEC_VALUE_TYPE (pspec);
17592
17593 G_VALUE_COLLECT_INIT (&initial, ptype,
17594 var_args, 0,
17595 &error);
17596 if (error != NULL)
17597 {
17598 g_critical ("%s: %s", G_STRLOC, error);
17599 g_free (error);
17600 goto out;
17601 }
17602
17603 G_VALUE_COLLECT_INIT (&final, ptype,
17604 var_args, 0,
17605 &error);
17606 if (error != NULL)
17607 {
17608 g_critical ("%s: %s", G_STRLOC, error);
17609 g_free (error);
17610 goto out;
17611 }
17612
17613 if (should_skip_implicit_transition (actor, pspec))
17614 {
17615 CLUTTER_NOTE (ANIMATION, "Skipping implicit transition for '%s::%s'",
17616 _clutter_actor_get_debug_name (actor),
17617 pspec->name);
17618
17619 /* remove a transition, if one exists */
17620 clutter_actor_remove_transition (actor, pspec->name);
17621
17622 /* we don't go through the Animatable interface because we
17623 * already know we got here through an animatable property.
17624 */
17625 clutter_actor_set_animatable_property (actor,
17626 pspec->param_id,
17627 &final,
17628 pspec);
17629
17630 goto out;
17631 }
17632
17633 clos = g_hash_table_lookup (info->transitions, pspec->name);
17634 if (clos == NULL)
17635 {
17636 res = clutter_property_transition_new (pspec->name);
17637
17638 clutter_transition_set_remove_on_complete (res, TRUE);
17639
17640 interval = clutter_interval_new_with_values (ptype, &initial, &final);
17641 clutter_transition_set_interval (res, interval);
17642
17643 timeline = CLUTTER_TIMELINE (res);
17644 clutter_timeline_set_delay (timeline, info->cur_state->easing_delay);
17645 clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
17646 clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
17647
17648 #ifdef CLUTTER_ENABLE_DEBUG
17649 if (CLUTTER_HAS_DEBUG (ANIMATION))
17650 {
17651 gchar *initial_v, *final_v;
17652
17653 initial_v = g_strdup_value_contents (&initial);
17654 final_v = g_strdup_value_contents (&final);
17655
17656 CLUTTER_NOTE (ANIMATION,
17657 "Created transition for %s:%s "
17658 "(len:%u, mode:%s, delay:%u) "
17659 "initial:%s, final:%s",
17660 _clutter_actor_get_debug_name (actor),
17661 pspec->name,
17662 info->cur_state->easing_duration,
17663 clutter_get_easing_name_for_mode (info->cur_state->easing_mode),
17664 info->cur_state->easing_delay,
17665 initial_v, final_v);
17666
17667 g_free (initial_v);
17668 g_free (final_v);
17669 }
17670 #endif /* CLUTTER_ENABLE_DEBUG */
17671
17672 /* this will start the transition as well */
17673 clutter_actor_add_transition_internal (actor, pspec->name, res);
17674
17675 /* the actor now owns the transition */
17676 g_object_unref (res);
17677 }
17678 else
17679 {
17680 ClutterAnimationMode cur_mode;
17681 guint cur_duration;
17682
17683 CLUTTER_NOTE (ANIMATION, "Existing transition for %s:%s",
17684 _clutter_actor_get_debug_name (actor),
17685 pspec->name);
17686
17687 timeline = CLUTTER_TIMELINE (clos->transition);
17688
17689 cur_duration = clutter_timeline_get_duration (timeline);
17690 if (cur_duration != info->cur_state->easing_duration)
17691 clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
17692
17693 cur_mode = clutter_timeline_get_progress_mode (timeline);
17694 if (cur_mode != info->cur_state->easing_mode)
17695 clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
17696
17697 clutter_timeline_rewind (timeline);
17698
17699 interval = clutter_transition_get_interval (clos->transition);
17700 clutter_interval_set_initial_value (interval, &initial);
17701 clutter_interval_set_final_value (interval, &final);
17702
17703 res = clos->transition;
17704 }
17705
17706 out:
17707 if (call_restore)
17708 clutter_actor_restore_easing_state (actor);
17709
17710 va_end (var_args);
17711
17712 return res;
17713 }
17714
17715 /**
17716 * clutter_actor_add_transition:
17717 * @self: a #ClutterActor
17718 * @name: the name of the transition to add
17719 * @transition: the #ClutterTransition to add
17720 *
17721 * Adds a @transition to the #ClutterActor's list of animations.
17722 *
17723 * The @name string is a per-actor unique identifier of the @transition: only
17724 * one #ClutterTransition can be associated to the specified @name.
17725 *
17726 * The @transition will be started once added.
17727 *
17728 * This function will take a reference on the @transition.
17729 *
17730 * This function is usually called implicitly when modifying an animatable
17731 * property.
17732 *
17733 * Since: 1.10
17734 */
17735 void
clutter_actor_add_transition(ClutterActor * self,const char * name,ClutterTransition * transition)17736 clutter_actor_add_transition (ClutterActor *self,
17737 const char *name,
17738 ClutterTransition *transition)
17739 {
17740 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17741 g_return_if_fail (name != NULL);
17742 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
17743
17744 clutter_actor_add_transition_internal (self, name, transition);
17745 }
17746
17747 /**
17748 * clutter_actor_remove_transition:
17749 * @self: a #ClutterActor
17750 * @name: the name of the transition to remove
17751 *
17752 * Removes the transition stored inside a #ClutterActor using @name
17753 * identifier.
17754 *
17755 * If the transition is currently in progress, it will be stopped.
17756 *
17757 * This function releases the reference acquired when the transition
17758 * was added to the #ClutterActor.
17759 *
17760 * Since: 1.10
17761 */
17762 void
clutter_actor_remove_transition(ClutterActor * self,const char * name)17763 clutter_actor_remove_transition (ClutterActor *self,
17764 const char *name)
17765 {
17766 const ClutterAnimationInfo *info;
17767 TransitionClosure *clos;
17768 gboolean was_playing;
17769 GQuark t_quark;
17770 gchar *t_name;
17771
17772 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17773 g_return_if_fail (name != NULL);
17774
17775 info = _clutter_actor_get_animation_info_or_defaults (self);
17776
17777 if (info->transitions == NULL)
17778 return;
17779
17780 clos = g_hash_table_lookup (info->transitions, name);
17781 if (clos == NULL)
17782 return;
17783
17784 was_playing =
17785 clutter_timeline_is_playing (CLUTTER_TIMELINE (clos->transition));
17786 t_quark = g_quark_from_string (clos->name);
17787 t_name = g_strdup (clos->name);
17788
17789 g_hash_table_remove (info->transitions, name);
17790
17791 /* we want to maintain the invariant that ::transition-stopped is
17792 * emitted after the transition has been removed, to allow replacing
17793 * or chaining; removing the transition from the hash table will
17794 * stop it, but transition_closure_free() will disconnect the signal
17795 * handler we install in add_transition_internal(), to avoid loops
17796 * or segfaults.
17797 *
17798 * since we know already that a transition will stop once it's removed
17799 * from an actor, we can simply emit the ::transition-stopped here
17800 * ourselves, if the timeline was playing (if it wasn't, then the
17801 * signal was already emitted at least once).
17802 */
17803 if (was_playing)
17804 {
17805 g_signal_emit (self, actor_signals[TRANSITION_STOPPED],
17806 t_quark,
17807 t_name,
17808 FALSE);
17809 }
17810
17811 g_free (t_name);
17812 }
17813
17814 /**
17815 * clutter_actor_remove_all_transitions:
17816 * @self: a #ClutterActor
17817 *
17818 * Removes all transitions associated to @self.
17819 *
17820 * Since: 1.10
17821 */
17822 void
clutter_actor_remove_all_transitions(ClutterActor * self)17823 clutter_actor_remove_all_transitions (ClutterActor *self)
17824 {
17825 const ClutterAnimationInfo *info;
17826
17827 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17828
17829 info = _clutter_actor_get_animation_info_or_defaults (self);
17830 if (info->transitions == NULL)
17831 return;
17832
17833 g_hash_table_remove_all (info->transitions);
17834 }
17835
17836 /**
17837 * clutter_actor_set_easing_duration:
17838 * @self: a #ClutterActor
17839 * @msecs: the duration of the easing, or %NULL
17840 *
17841 * Sets the duration of the tweening for animatable properties
17842 * of @self for the current easing state.
17843 *
17844 * Since: 1.10
17845 */
17846 void
clutter_actor_set_easing_duration(ClutterActor * self,guint msecs)17847 clutter_actor_set_easing_duration (ClutterActor *self,
17848 guint msecs)
17849 {
17850 ClutterAnimationInfo *info;
17851
17852 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17853
17854 info = _clutter_actor_get_animation_info (self);
17855
17856 if (info->cur_state == NULL)
17857 {
17858 g_warning ("You must call clutter_actor_save_easing_state() prior "
17859 "to calling clutter_actor_set_easing_duration().");
17860 return;
17861 }
17862
17863 if (info->cur_state->easing_duration != msecs)
17864 info->cur_state->easing_duration = msecs;
17865 }
17866
17867 /**
17868 * clutter_actor_get_easing_duration:
17869 * @self: a #ClutterActor
17870 *
17871 * Retrieves the duration of the tweening for animatable
17872 * properties of @self for the current easing state.
17873 *
17874 * Return value: the duration of the tweening, in milliseconds
17875 *
17876 * Since: 1.10
17877 */
17878 guint
clutter_actor_get_easing_duration(ClutterActor * self)17879 clutter_actor_get_easing_duration (ClutterActor *self)
17880 {
17881 const ClutterAnimationInfo *info;
17882
17883 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
17884
17885 info = _clutter_actor_get_animation_info_or_defaults (self);
17886
17887 if (info->cur_state != NULL)
17888 return info->cur_state->easing_duration;
17889
17890 return 0;
17891 }
17892
17893 /**
17894 * clutter_actor_set_easing_mode:
17895 * @self: a #ClutterActor
17896 * @mode: an easing mode, excluding %CLUTTER_CUSTOM_MODE
17897 *
17898 * Sets the easing mode for the tweening of animatable properties
17899 * of @self.
17900 *
17901 * Since: 1.10
17902 */
17903 void
clutter_actor_set_easing_mode(ClutterActor * self,ClutterAnimationMode mode)17904 clutter_actor_set_easing_mode (ClutterActor *self,
17905 ClutterAnimationMode mode)
17906 {
17907 ClutterAnimationInfo *info;
17908
17909 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17910 g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
17911 g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
17912
17913 info = _clutter_actor_get_animation_info (self);
17914
17915 if (info->cur_state == NULL)
17916 {
17917 g_warning ("You must call clutter_actor_save_easing_state() prior "
17918 "to calling clutter_actor_set_easing_mode().");
17919 return;
17920 }
17921
17922 if (info->cur_state->easing_mode != mode)
17923 info->cur_state->easing_mode = mode;
17924 }
17925
17926 /**
17927 * clutter_actor_get_easing_mode:
17928 * @self: a #ClutterActor
17929 *
17930 * Retrieves the easing mode for the tweening of animatable properties
17931 * of @self for the current easing state.
17932 *
17933 * Return value: an easing mode
17934 *
17935 * Since: 1.10
17936 */
17937 ClutterAnimationMode
clutter_actor_get_easing_mode(ClutterActor * self)17938 clutter_actor_get_easing_mode (ClutterActor *self)
17939 {
17940 const ClutterAnimationInfo *info;
17941
17942 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_EASE_OUT_CUBIC);
17943
17944 info = _clutter_actor_get_animation_info_or_defaults (self);
17945
17946 if (info->cur_state != NULL)
17947 return info->cur_state->easing_mode;
17948
17949 return CLUTTER_EASE_OUT_CUBIC;
17950 }
17951
17952 /**
17953 * clutter_actor_set_easing_delay:
17954 * @self: a #ClutterActor
17955 * @msecs: the delay before the start of the tweening, in milliseconds
17956 *
17957 * Sets the delay that should be applied before tweening animatable
17958 * properties.
17959 *
17960 * Since: 1.10
17961 */
17962 void
clutter_actor_set_easing_delay(ClutterActor * self,guint msecs)17963 clutter_actor_set_easing_delay (ClutterActor *self,
17964 guint msecs)
17965 {
17966 ClutterAnimationInfo *info;
17967
17968 g_return_if_fail (CLUTTER_IS_ACTOR (self));
17969
17970 info = _clutter_actor_get_animation_info (self);
17971
17972 if (info->cur_state == NULL)
17973 {
17974 g_warning ("You must call clutter_actor_save_easing_state() prior "
17975 "to calling clutter_actor_set_easing_delay().");
17976 return;
17977 }
17978
17979 if (info->cur_state->easing_delay != msecs)
17980 info->cur_state->easing_delay = msecs;
17981 }
17982
17983 /**
17984 * clutter_actor_get_easing_delay:
17985 * @self: a #ClutterActor
17986 *
17987 * Retrieves the delay that should be applied when tweening animatable
17988 * properties.
17989 *
17990 * Return value: a delay, in milliseconds
17991 *
17992 * Since: 1.10
17993 */
17994 guint
clutter_actor_get_easing_delay(ClutterActor * self)17995 clutter_actor_get_easing_delay (ClutterActor *self)
17996 {
17997 const ClutterAnimationInfo *info;
17998
17999 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
18000
18001 info = _clutter_actor_get_animation_info_or_defaults (self);
18002
18003 if (info->cur_state != NULL)
18004 return info->cur_state->easing_delay;
18005
18006 return 0;
18007 }
18008
18009 /**
18010 * clutter_actor_get_transition:
18011 * @self: a #ClutterActor
18012 * @name: the name of the transition
18013 *
18014 * Retrieves the #ClutterTransition of a #ClutterActor by using the
18015 * transition @name.
18016 *
18017 * Transitions created for animatable properties use the name of the
18018 * property itself, for instance the code below:
18019 *
18020 * |[<!-- language="C" -->
18021 * clutter_actor_set_easing_duration (actor, 1000);
18022 * clutter_actor_set_rotation_angle (actor, CLUTTER_Y_AXIS, 360.0);
18023 *
18024 * transition = clutter_actor_get_transition (actor, "rotation-angle-y");
18025 * g_signal_connect (transition, "stopped",
18026 * G_CALLBACK (on_transition_stopped),
18027 * actor);
18028 * ]|
18029 *
18030 * will call the `on_transition_stopped` callback when the transition
18031 * is finished.
18032 *
18033 * If you just want to get notifications of the completion of a transition,
18034 * you should use the #ClutterActor::transition-stopped signal, using the
18035 * transition name as the signal detail.
18036 *
18037 * Return value: (transfer none): a #ClutterTransition, or %NULL is none
18038 * was found to match the passed name; the returned instance is owned
18039 * by Clutter and it should not be freed
18040 *
18041 * Since: 1.10
18042 */
18043 ClutterTransition *
clutter_actor_get_transition(ClutterActor * self,const char * name)18044 clutter_actor_get_transition (ClutterActor *self,
18045 const char *name)
18046 {
18047 TransitionClosure *clos;
18048 const ClutterAnimationInfo *info;
18049
18050 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18051 g_return_val_if_fail (name != NULL, NULL);
18052
18053 info = _clutter_actor_get_animation_info_or_defaults (self);
18054 if (info->transitions == NULL)
18055 return NULL;
18056
18057 clos = g_hash_table_lookup (info->transitions, name);
18058 if (clos == NULL)
18059 return NULL;
18060
18061 return clos->transition;
18062 }
18063
18064 /**
18065 * clutter_actor_has_transitions: (skip)
18066 */
18067 gboolean
clutter_actor_has_transitions(ClutterActor * self)18068 clutter_actor_has_transitions (ClutterActor *self)
18069 {
18070 const ClutterAnimationInfo *info;
18071
18072 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18073
18074 info = _clutter_actor_get_animation_info_or_defaults (self);
18075 if (info->transitions == NULL)
18076 return FALSE;
18077
18078 return g_hash_table_size (info->transitions) > 0;
18079 }
18080
18081 /**
18082 * clutter_actor_save_easing_state:
18083 * @self: a #ClutterActor
18084 *
18085 * Saves the current easing state for animatable properties, and creates
18086 * a new state with the default values for easing mode and duration.
18087 *
18088 * New transitions created after calling this function will inherit the
18089 * duration, easing mode, and delay of the new easing state; this also
18090 * applies to transitions modified in flight.
18091 *
18092 * Since: 1.10
18093 */
18094 void
clutter_actor_save_easing_state(ClutterActor * self)18095 clutter_actor_save_easing_state (ClutterActor *self)
18096 {
18097 ClutterAnimationInfo *info;
18098 AState new_state;
18099
18100 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18101
18102 info = _clutter_actor_get_animation_info (self);
18103
18104 if (info->states == NULL)
18105 info->states = g_array_new (FALSE, FALSE, sizeof (AState));
18106
18107 new_state.easing_mode = CLUTTER_EASE_OUT_CUBIC;
18108 new_state.easing_duration = 250;
18109 new_state.easing_delay = 0;
18110
18111 g_array_append_val (info->states, new_state);
18112
18113 info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
18114 }
18115
18116 /**
18117 * clutter_actor_restore_easing_state:
18118 * @self: a #ClutterActor
18119 *
18120 * Restores the easing state as it was prior to a call to
18121 * clutter_actor_save_easing_state().
18122 *
18123 * Since: 1.10
18124 */
18125 void
clutter_actor_restore_easing_state(ClutterActor * self)18126 clutter_actor_restore_easing_state (ClutterActor *self)
18127 {
18128 ClutterAnimationInfo *info;
18129
18130 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18131
18132 info = _clutter_actor_get_animation_info (self);
18133
18134 if (info->states == NULL)
18135 {
18136 g_critical ("The function clutter_actor_restore_easing_state() has "
18137 "been called without a previous call to "
18138 "clutter_actor_save_easing_state().");
18139 return;
18140 }
18141
18142 g_array_remove_index (info->states, info->states->len - 1);
18143
18144 if (info->states->len > 0)
18145 info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
18146 else
18147 {
18148 g_array_unref (info->states);
18149 info->states = NULL;
18150 info->cur_state = NULL;
18151 }
18152 }
18153
18154 /**
18155 * clutter_actor_set_content:
18156 * @self: a #ClutterActor
18157 * @content: (allow-none): a #ClutterContent, or %NULL
18158 *
18159 * Sets the contents of a #ClutterActor.
18160 *
18161 * Since: 1.10
18162 */
18163 void
clutter_actor_set_content(ClutterActor * self,ClutterContent * content)18164 clutter_actor_set_content (ClutterActor *self,
18165 ClutterContent *content)
18166 {
18167 ClutterActorPrivate *priv;
18168
18169 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18170 g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content));
18171
18172 priv = self->priv;
18173
18174 if (priv->content == content)
18175 return;
18176
18177 if (priv->content != NULL)
18178 {
18179 _clutter_content_detached (priv->content, self);
18180 g_clear_object (&priv->content);
18181 }
18182
18183 priv->content = content;
18184
18185 if (priv->content != NULL)
18186 {
18187 g_object_ref (priv->content);
18188 _clutter_content_attached (priv->content, self);
18189 }
18190
18191 /* if the actor's preferred size is the content's preferred size,
18192 * then we need to conditionally queue a relayout here...
18193 */
18194 if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
18195 _clutter_actor_queue_only_relayout (self);
18196
18197 clutter_actor_queue_redraw (self);
18198
18199 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]);
18200
18201 /* if the content gravity is not resize-fill, and the new content has a
18202 * different preferred size than the previous one, then the content box
18203 * may have been changed. since we compute that lazily, we just notify
18204 * here, and let whomever watches :content-box do whatever they need to
18205 * do.
18206 */
18207 if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
18208 {
18209 if (priv->content_box_valid)
18210 {
18211 ClutterActorBox from_box, to_box;
18212
18213 clutter_actor_get_content_box (self, &from_box);
18214
18215 /* invalidate the cached content box */
18216 priv->content_box_valid = FALSE;
18217 clutter_actor_get_content_box (self, &to_box);
18218
18219 if (!clutter_actor_box_equal (&from_box, &to_box))
18220 _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
18221 &from_box,
18222 &to_box);
18223 }
18224
18225 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
18226 }
18227 }
18228
18229 /**
18230 * clutter_actor_get_content:
18231 * @self: a #ClutterActor
18232 *
18233 * Retrieves the contents of @self.
18234 *
18235 * Return value: (transfer none): a pointer to the #ClutterContent instance,
18236 * or %NULL if none was set
18237 *
18238 * Since: 1.10
18239 */
18240 ClutterContent *
clutter_actor_get_content(ClutterActor * self)18241 clutter_actor_get_content (ClutterActor *self)
18242 {
18243 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18244
18245 return self->priv->content;
18246 }
18247
18248 /**
18249 * clutter_actor_set_content_gravity:
18250 * @self: a #ClutterActor
18251 * @gravity: the #ClutterContentGravity
18252 *
18253 * Sets the gravity of the #ClutterContent used by @self.
18254 *
18255 * See the description of the #ClutterActor:content-gravity property for
18256 * more information.
18257 *
18258 * The #ClutterActor:content-gravity property is animatable.
18259 *
18260 * Since: 1.10
18261 */
18262 void
clutter_actor_set_content_gravity(ClutterActor * self,ClutterContentGravity gravity)18263 clutter_actor_set_content_gravity (ClutterActor *self,
18264 ClutterContentGravity gravity)
18265 {
18266 ClutterActorPrivate *priv;
18267 ClutterActorBox from_box, to_box;
18268
18269 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18270
18271 priv = self->priv;
18272
18273 if (priv->content_gravity == gravity)
18274 return;
18275
18276 priv->content_box_valid = FALSE;
18277
18278 clutter_actor_get_content_box (self, &from_box);
18279
18280 priv->content_gravity = gravity;
18281
18282 clutter_actor_get_content_box (self, &to_box);
18283
18284 _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
18285 &from_box,
18286 &to_box);
18287
18288 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]);
18289 }
18290
18291 /**
18292 * clutter_actor_get_content_gravity:
18293 * @self: a #ClutterActor
18294 *
18295 * Retrieves the content gravity as set using
18296 * clutter_actor_set_content_gravity().
18297 *
18298 * Return value: the content gravity
18299 *
18300 * Since: 1.10
18301 */
18302 ClutterContentGravity
clutter_actor_get_content_gravity(ClutterActor * self)18303 clutter_actor_get_content_gravity (ClutterActor *self)
18304 {
18305 g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
18306 CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
18307
18308 return self->priv->content_gravity;
18309 }
18310
18311 /**
18312 * clutter_actor_get_content_box:
18313 * @self: a #ClutterActor
18314 * @box: (out caller-allocates): the return location for the bounding
18315 * box for the #ClutterContent
18316 *
18317 * Retrieves the bounding box for the #ClutterContent of @self.
18318 *
18319 * The bounding box is relative to the actor's allocation.
18320 *
18321 * If no #ClutterContent is set for @self, or if @self has not been
18322 * allocated yet, then the result is undefined.
18323 *
18324 * The content box is guaranteed to be, at most, as big as the allocation
18325 * of the #ClutterActor.
18326 *
18327 * If the #ClutterContent used by the actor has a preferred size, then
18328 * it is possible to modify the content box by using the
18329 * #ClutterActor:content-gravity property.
18330 *
18331 * Since: 1.10
18332 */
18333 void
clutter_actor_get_content_box(ClutterActor * self,ClutterActorBox * box)18334 clutter_actor_get_content_box (ClutterActor *self,
18335 ClutterActorBox *box)
18336 {
18337 ClutterActorPrivate *priv;
18338 gfloat content_w, content_h;
18339 gfloat alloc_w, alloc_h;
18340
18341 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18342 g_return_if_fail (box != NULL);
18343
18344 priv = self->priv;
18345
18346 box->x1 = 0.f;
18347 box->y1 = 0.f;
18348 box->x2 = priv->allocation.x2 - priv->allocation.x1;
18349 box->y2 = priv->allocation.y2 - priv->allocation.y1;
18350
18351 if (priv->content_box_valid)
18352 {
18353 *box = priv->content_box;
18354 return;
18355 }
18356
18357 /* no need to do any more work */
18358 if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
18359 return;
18360
18361 if (priv->content == NULL)
18362 return;
18363
18364 /* if the content does not have a preferred size then there is
18365 * no point in computing the content box
18366 */
18367 if (!clutter_content_get_preferred_size (priv->content,
18368 &content_w,
18369 &content_h))
18370 return;
18371
18372 alloc_w = box->x2;
18373 alloc_h = box->y2;
18374
18375 switch (priv->content_gravity)
18376 {
18377 case CLUTTER_CONTENT_GRAVITY_TOP_LEFT:
18378 box->x2 = box->x1 + MIN (content_w, alloc_w);
18379 box->y2 = box->y1 + MIN (content_h, alloc_h);
18380 break;
18381
18382 case CLUTTER_CONTENT_GRAVITY_TOP:
18383 if (alloc_w > content_w)
18384 {
18385 box->x1 += ceilf ((alloc_w - content_w) / 2.0);
18386 box->x2 = box->x1 + content_w;
18387 }
18388 box->y2 = box->y1 + MIN (content_h, alloc_h);
18389 break;
18390
18391 case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT:
18392 if (alloc_w > content_w)
18393 {
18394 box->x1 += (alloc_w - content_w);
18395 box->x2 = box->x1 + content_w;
18396 }
18397 box->y2 = box->y1 + MIN (content_h, alloc_h);
18398 break;
18399
18400 case CLUTTER_CONTENT_GRAVITY_LEFT:
18401 box->x2 = box->x1 + MIN (content_w, alloc_w);
18402 if (alloc_h > content_h)
18403 {
18404 box->y1 += ceilf ((alloc_h - content_h) / 2.0);
18405 box->y2 = box->y1 + content_h;
18406 }
18407 break;
18408
18409 case CLUTTER_CONTENT_GRAVITY_CENTER:
18410 if (alloc_w > content_w)
18411 {
18412 box->x1 += ceilf ((alloc_w - content_w) / 2.0);
18413 box->x2 = box->x1 + content_w;
18414 }
18415 if (alloc_h > content_h)
18416 {
18417 box->y1 += ceilf ((alloc_h - content_h) / 2.0);
18418 box->y2 = box->y1 + content_h;
18419 }
18420 break;
18421
18422 case CLUTTER_CONTENT_GRAVITY_RIGHT:
18423 if (alloc_w > content_w)
18424 {
18425 box->x1 += (alloc_w - content_w);
18426 box->x2 = box->x1 + content_w;
18427 }
18428 if (alloc_h > content_h)
18429 {
18430 box->y1 += ceilf ((alloc_h - content_h) / 2.0);
18431 box->y2 = box->y1 + content_h;
18432 }
18433 break;
18434
18435 case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT:
18436 box->x2 = box->x1 + MIN (content_w, alloc_w);
18437 if (alloc_h > content_h)
18438 {
18439 box->y1 += (alloc_h - content_h);
18440 box->y2 = box->y1 + content_h;
18441 }
18442 break;
18443
18444 case CLUTTER_CONTENT_GRAVITY_BOTTOM:
18445 if (alloc_w > content_w)
18446 {
18447 box->x1 += ceilf ((alloc_w - content_w) / 2.0);
18448 box->x2 = box->x1 + content_w;
18449 }
18450 if (alloc_h > content_h)
18451 {
18452 box->y1 += (alloc_h - content_h);
18453 box->y2 = box->y1 + content_h;
18454 }
18455 break;
18456
18457 case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT:
18458 if (alloc_w > content_w)
18459 {
18460 box->x1 += (alloc_w - content_w);
18461 box->x2 = box->x1 + content_w;
18462 }
18463 if (alloc_h > content_h)
18464 {
18465 box->y1 += (alloc_h - content_h);
18466 box->y2 = box->y1 + content_h;
18467 }
18468 break;
18469
18470 case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL:
18471 g_assert_not_reached ();
18472 break;
18473
18474 case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT:
18475 {
18476 double r_c = content_w / content_h;
18477
18478 if ((alloc_w / r_c) > alloc_h)
18479 {
18480 box->y1 = 0.f;
18481 box->y2 = alloc_h;
18482
18483 box->x1 = (alloc_w - (alloc_h * r_c)) / 2.0f;
18484 box->x2 = box->x1 + (alloc_h * r_c);
18485 }
18486 else
18487 {
18488 box->x1 = 0.f;
18489 box->x2 = alloc_w;
18490
18491 box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f;
18492 box->y2 = box->y1 + (alloc_w / r_c);
18493 }
18494
18495 CLUTTER_NOTE (LAYOUT,
18496 "r_c: %.3f, r_a: %.3f\t"
18497 "a: [%.2fx%.2f], c: [%.2fx%.2f]\t"
18498 "b: [%.2f, %.2f, %.2f, %.2f]",
18499 r_c, alloc_w / alloc_h,
18500 alloc_w, alloc_h,
18501 content_w, content_h,
18502 box->x1, box->y1, box->x2, box->y2);
18503 }
18504 break;
18505 }
18506 }
18507
18508 /**
18509 * clutter_actor_set_content_scaling_filters:
18510 * @self: a #ClutterActor
18511 * @min_filter: the minification filter for the content
18512 * @mag_filter: the magnification filter for the content
18513 *
18514 * Sets the minification and magnification filter to be applied when
18515 * scaling the #ClutterActor:content of a #ClutterActor.
18516 *
18517 * The #ClutterActor:minification-filter will be used when reducing
18518 * the size of the content; the #ClutterActor:magnification-filter
18519 * will be used when increasing the size of the content.
18520 *
18521 * Since: 1.10
18522 */
18523 void
clutter_actor_set_content_scaling_filters(ClutterActor * self,ClutterScalingFilter min_filter,ClutterScalingFilter mag_filter)18524 clutter_actor_set_content_scaling_filters (ClutterActor *self,
18525 ClutterScalingFilter min_filter,
18526 ClutterScalingFilter mag_filter)
18527 {
18528 ClutterActorPrivate *priv;
18529 gboolean changed;
18530 GObject *obj;
18531
18532 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18533
18534 priv = self->priv;
18535 obj = G_OBJECT (self);
18536
18537 g_object_freeze_notify (obj);
18538
18539 changed = FALSE;
18540
18541 if (priv->min_filter != min_filter)
18542 {
18543 priv->min_filter = min_filter;
18544 changed = TRUE;
18545
18546 g_object_notify_by_pspec (obj, obj_props[PROP_MINIFICATION_FILTER]);
18547 }
18548
18549 if (priv->mag_filter != mag_filter)
18550 {
18551 priv->mag_filter = mag_filter;
18552 changed = TRUE;
18553
18554 g_object_notify_by_pspec (obj, obj_props[PROP_MAGNIFICATION_FILTER]);
18555 }
18556
18557 if (changed)
18558 clutter_actor_queue_redraw (self);
18559
18560 g_object_thaw_notify (obj);
18561 }
18562
18563 /**
18564 * clutter_actor_get_content_scaling_filters:
18565 * @self: a #ClutterActor
18566 * @min_filter: (out) (allow-none): return location for the minification
18567 * filter, or %NULL
18568 * @mag_filter: (out) (allow-none): return location for the magnification
18569 * filter, or %NULL
18570 *
18571 * Retrieves the values set using clutter_actor_set_content_scaling_filters().
18572 *
18573 * Since: 1.10
18574 */
18575 void
clutter_actor_get_content_scaling_filters(ClutterActor * self,ClutterScalingFilter * min_filter,ClutterScalingFilter * mag_filter)18576 clutter_actor_get_content_scaling_filters (ClutterActor *self,
18577 ClutterScalingFilter *min_filter,
18578 ClutterScalingFilter *mag_filter)
18579 {
18580 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18581
18582 if (min_filter != NULL)
18583 *min_filter = self->priv->min_filter;
18584
18585 if (mag_filter != NULL)
18586 *mag_filter = self->priv->mag_filter;
18587 }
18588
18589 /*
18590 * clutter_actor_queue_compute_expand:
18591 * @self: a #ClutterActor
18592 *
18593 * Invalidates the needs_x_expand and needs_y_expand flags on @self
18594 * and its parents up to the top-level actor.
18595 *
18596 * This function also queues a relayout if anything changed.
18597 */
18598 static inline void
clutter_actor_queue_compute_expand(ClutterActor * self)18599 clutter_actor_queue_compute_expand (ClutterActor *self)
18600 {
18601 ClutterActor *parent;
18602 gboolean changed;
18603
18604 if (self->priv->needs_compute_expand)
18605 return;
18606
18607 changed = FALSE;
18608 parent = self;
18609 while (parent != NULL)
18610 {
18611 if (!parent->priv->needs_compute_expand)
18612 {
18613 parent->priv->needs_compute_expand = TRUE;
18614 changed = TRUE;
18615 }
18616
18617 parent = parent->priv->parent;
18618 }
18619
18620 if (changed)
18621 clutter_actor_queue_relayout (self);
18622 }
18623
18624 /**
18625 * clutter_actor_set_x_expand:
18626 * @self: a #ClutterActor
18627 * @expand: whether the actor should expand horizontally
18628 *
18629 * Sets whether a #ClutterActor should expand horizontally; this means
18630 * that layout manager should allocate extra space for the actor, if
18631 * possible.
18632 *
18633 * Setting an actor to expand will also make all its parent expand, so
18634 * that it's possible to build an actor tree and only set this flag on
18635 * its leaves and not on every single actor.
18636 *
18637 * Since: 1.12
18638 */
18639 void
clutter_actor_set_x_expand(ClutterActor * self,gboolean expand)18640 clutter_actor_set_x_expand (ClutterActor *self,
18641 gboolean expand)
18642 {
18643 ClutterLayoutInfo *info;
18644
18645 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18646
18647 expand = !!expand;
18648
18649 info = _clutter_actor_get_layout_info (self);
18650 if (info->x_expand != expand)
18651 {
18652 info->x_expand = expand;
18653
18654 self->priv->x_expand_set = TRUE;
18655
18656 clutter_actor_queue_compute_expand (self);
18657
18658 g_object_notify_by_pspec (G_OBJECT (self),
18659 obj_props[PROP_X_EXPAND]);
18660 }
18661 }
18662
18663 /**
18664 * clutter_actor_get_x_expand:
18665 * @self: a #ClutterActor
18666 *
18667 * Retrieves the value set with clutter_actor_set_x_expand().
18668 *
18669 * See also: clutter_actor_needs_expand()
18670 *
18671 * Return value: %TRUE if the actor has been set to expand
18672 *
18673 * Since: 1.12
18674 */
18675 gboolean
clutter_actor_get_x_expand(ClutterActor * self)18676 clutter_actor_get_x_expand (ClutterActor *self)
18677 {
18678 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18679
18680 return _clutter_actor_get_layout_info_or_defaults (self)->x_expand;
18681 }
18682
18683 /**
18684 * clutter_actor_set_y_expand:
18685 * @self: a #ClutterActor
18686 * @expand: whether the actor should expand vertically
18687 *
18688 * Sets whether a #ClutterActor should expand horizontally; this means
18689 * that layout manager should allocate extra space for the actor, if
18690 * possible.
18691 *
18692 * Setting an actor to expand will also make all its parent expand, so
18693 * that it's possible to build an actor tree and only set this flag on
18694 * its leaves and not on every single actor.
18695 *
18696 * Since: 1.12
18697 */
18698 void
clutter_actor_set_y_expand(ClutterActor * self,gboolean expand)18699 clutter_actor_set_y_expand (ClutterActor *self,
18700 gboolean expand)
18701 {
18702 ClutterLayoutInfo *info;
18703
18704 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18705
18706 expand = !!expand;
18707
18708 info = _clutter_actor_get_layout_info (self);
18709 if (info->y_expand != expand)
18710 {
18711 info->y_expand = expand;
18712
18713 self->priv->y_expand_set = TRUE;
18714
18715 clutter_actor_queue_compute_expand (self);
18716
18717 g_object_notify_by_pspec (G_OBJECT (self),
18718 obj_props[PROP_Y_EXPAND]);
18719 }
18720 }
18721
18722 /**
18723 * clutter_actor_get_y_expand:
18724 * @self: a #ClutterActor
18725 *
18726 * Retrieves the value set with clutter_actor_set_y_expand().
18727 *
18728 * See also: clutter_actor_needs_expand()
18729 *
18730 * Return value: %TRUE if the actor has been set to expand
18731 *
18732 * Since: 1.12
18733 */
18734 gboolean
clutter_actor_get_y_expand(ClutterActor * self)18735 clutter_actor_get_y_expand (ClutterActor *self)
18736 {
18737 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18738
18739 return _clutter_actor_get_layout_info_or_defaults (self)->y_expand;
18740 }
18741
18742 static void
clutter_actor_compute_expand_recursive(ClutterActor * self,gboolean * x_expand_p,gboolean * y_expand_p)18743 clutter_actor_compute_expand_recursive (ClutterActor *self,
18744 gboolean *x_expand_p,
18745 gboolean *y_expand_p)
18746 {
18747 ClutterActorIter iter;
18748 ClutterActor *child;
18749 gboolean x_expand, y_expand;
18750
18751 x_expand = y_expand = FALSE;
18752
18753 /* note that we don't recurse into children if we're already set to expand;
18754 * this avoids traversing the whole actor tree, even if it may lead to some
18755 * child left with the needs_compute_expand flag set.
18756 */
18757 clutter_actor_iter_init (&iter, self);
18758 while (clutter_actor_iter_next (&iter, &child))
18759 {
18760 x_expand = x_expand ||
18761 clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL);
18762
18763 y_expand = y_expand ||
18764 clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL);
18765 }
18766
18767 *x_expand_p = x_expand;
18768 *y_expand_p = y_expand;
18769 }
18770
18771 static void
clutter_actor_compute_expand(ClutterActor * self)18772 clutter_actor_compute_expand (ClutterActor *self)
18773 {
18774 if (self->priv->needs_compute_expand)
18775 {
18776 const ClutterLayoutInfo *info;
18777 gboolean x_expand, y_expand;
18778
18779 info = _clutter_actor_get_layout_info_or_defaults (self);
18780
18781 if (self->priv->x_expand_set)
18782 x_expand = info->x_expand;
18783 else
18784 x_expand = FALSE;
18785
18786 if (self->priv->y_expand_set)
18787 y_expand = info->y_expand;
18788 else
18789 y_expand = FALSE;
18790
18791 /* we don't need to recurse down to the children if the
18792 * actor has been forcibly set to expand
18793 */
18794 if (!(self->priv->x_expand_set && self->priv->y_expand_set))
18795 {
18796 if (self->priv->n_children != 0)
18797 {
18798 gboolean *x_expand_p, *y_expand_p;
18799 gboolean ignored = FALSE;
18800
18801 x_expand_p = self->priv->x_expand_set ? &ignored : &x_expand;
18802 y_expand_p = self->priv->y_expand_set ? &ignored : &y_expand;
18803
18804 clutter_actor_compute_expand_recursive (self,
18805 x_expand_p,
18806 y_expand_p);
18807 }
18808 }
18809
18810 self->priv->needs_compute_expand = FALSE;
18811 self->priv->needs_x_expand = (x_expand != FALSE);
18812 self->priv->needs_y_expand = (y_expand != FALSE);
18813 }
18814 }
18815
18816 /**
18817 * clutter_actor_needs_expand:
18818 * @self: a #ClutterActor
18819 * @orientation: the direction of expansion
18820 *
18821 * Checks whether an actor, or any of its children, is set to expand
18822 * horizontally or vertically.
18823 *
18824 * This function should only be called by layout managers that can
18825 * assign extra space to their children.
18826 *
18827 * If you want to know whether the actor was explicitly set to expand,
18828 * use clutter_actor_get_x_expand() or clutter_actor_get_y_expand().
18829 *
18830 * Return value: %TRUE if the actor should expand
18831 *
18832 * Since: 1.12
18833 */
18834 gboolean
clutter_actor_needs_expand(ClutterActor * self,ClutterOrientation orientation)18835 clutter_actor_needs_expand (ClutterActor *self,
18836 ClutterOrientation orientation)
18837 {
18838 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18839
18840 if (!CLUTTER_ACTOR_IS_VISIBLE (self))
18841 return FALSE;
18842
18843 if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
18844 return FALSE;
18845
18846 clutter_actor_compute_expand (self);
18847
18848 switch (orientation)
18849 {
18850 case CLUTTER_ORIENTATION_HORIZONTAL:
18851 return self->priv->needs_x_expand;
18852
18853 case CLUTTER_ORIENTATION_VERTICAL:
18854 return self->priv->needs_y_expand;
18855 }
18856
18857 return FALSE;
18858 }
18859
18860 /**
18861 * clutter_actor_set_content_repeat:
18862 * @self: a #ClutterActor
18863 * @repeat: the repeat policy
18864 *
18865 * Sets the policy for repeating the #ClutterActor:content of a
18866 * #ClutterActor. The behaviour is deferred to the #ClutterContent
18867 * implementation.
18868 *
18869 * Since: 1.12
18870 */
18871 void
clutter_actor_set_content_repeat(ClutterActor * self,ClutterContentRepeat repeat)18872 clutter_actor_set_content_repeat (ClutterActor *self,
18873 ClutterContentRepeat repeat)
18874 {
18875 g_return_if_fail (CLUTTER_IS_ACTOR (self));
18876
18877 if (self->priv->content_repeat == repeat)
18878 return;
18879
18880 self->priv->content_repeat = repeat;
18881
18882 clutter_actor_queue_redraw (self);
18883 }
18884
18885 /**
18886 * clutter_actor_get_content_repeat:
18887 * @self: a #ClutterActor
18888 *
18889 * Retrieves the repeat policy for a #ClutterActor set by
18890 * clutter_actor_set_content_repeat().
18891 *
18892 * Return value: the content repeat policy
18893 *
18894 * Since: 1.12
18895 */
18896 ClutterContentRepeat
clutter_actor_get_content_repeat(ClutterActor * self)18897 clutter_actor_get_content_repeat (ClutterActor *self)
18898 {
18899 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_REPEAT_NONE);
18900
18901 return self->priv->content_repeat;
18902 }
18903
18904 void
_clutter_actor_handle_event(ClutterActor * self,const ClutterEvent * event)18905 _clutter_actor_handle_event (ClutterActor *self,
18906 const ClutterEvent *event)
18907 {
18908 GPtrArray *event_tree;
18909 ClutterActor *iter;
18910 gboolean is_key_event;
18911 gint i = 0;
18912
18913 /* XXX - for historical reasons that are now lost in the mists of time,
18914 * key events are delivered regardless of whether an actor is set as
18915 * reactive; this should be changed for 2.0.
18916 */
18917 is_key_event = event->type == CLUTTER_KEY_PRESS ||
18918 event->type == CLUTTER_KEY_RELEASE;
18919
18920 event_tree = g_ptr_array_sized_new (64);
18921 g_ptr_array_set_free_func (event_tree, (GDestroyNotify) g_object_unref);
18922
18923 /* build the list of of emitters for the event */
18924 iter = self;
18925 while (iter != NULL)
18926 {
18927 ClutterActor *parent = iter->priv->parent;
18928
18929 if (CLUTTER_ACTOR_IS_REACTIVE (iter) || /* an actor must be reactive */
18930 parent == NULL || /* unless it's the stage */
18931 is_key_event) /* or this is a key event */
18932 {
18933 /* keep a reference on the actor, so that it remains valid
18934 * for the duration of the signal emission
18935 */
18936 g_ptr_array_add (event_tree, g_object_ref (iter));
18937 }
18938
18939 iter = parent;
18940 }
18941
18942 /* Capture: from top-level downwards */
18943 for (i = event_tree->len - 1; i >= 0; i--)
18944 if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE))
18945 goto done;
18946
18947 /* Bubble: from source upwards */
18948 for (i = 0; i < event_tree->len; i++)
18949 if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE))
18950 goto done;
18951
18952 done:
18953 g_ptr_array_free (event_tree, TRUE);
18954 }
18955
18956 static void
clutter_actor_set_child_transform_internal(ClutterActor * self,const graphene_matrix_t * transform)18957 clutter_actor_set_child_transform_internal (ClutterActor *self,
18958 const graphene_matrix_t *transform)
18959 {
18960 ClutterTransformInfo *info = _clutter_actor_get_transform_info (self);
18961 ClutterActorIter iter;
18962 ClutterActor *child;
18963 GObject *obj;
18964 gboolean was_set = info->child_transform_set;
18965
18966 graphene_matrix_init_from_matrix (&info->child_transform, transform);
18967
18968 /* if it's the identity matrix, we need to toggle the boolean flag */
18969 info->child_transform_set = !graphene_matrix_is_identity (transform);
18970
18971 /* we need to reset the transform_valid flag on each child */
18972 clutter_actor_iter_init (&iter, self);
18973 while (clutter_actor_iter_next (&iter, &child))
18974 transform_changed (child);
18975
18976 clutter_actor_queue_redraw (self);
18977
18978 obj = G_OBJECT (self);
18979 g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM]);
18980
18981 if (was_set != info->child_transform_set)
18982 g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM_SET]);
18983 }
18984
18985 /**
18986 * clutter_actor_set_child_transform:
18987 * @self: a #ClutterActor
18988 * @transform: (allow-none): a #graphene_matrix_t, or %NULL
18989 *
18990 * Sets the transformation matrix to be applied to all the children
18991 * of @self prior to their own transformations. The default child
18992 * transformation is the identity matrix.
18993 *
18994 * If @transform is %NULL, the child transform will be unset.
18995 *
18996 * The #ClutterActor:child-transform property is animatable.
18997 *
18998 * Since: 1.12
18999 */
19000 void
clutter_actor_set_child_transform(ClutterActor * self,const graphene_matrix_t * transform)19001 clutter_actor_set_child_transform (ClutterActor *self,
19002 const graphene_matrix_t *transform)
19003 {
19004 const ClutterTransformInfo *info;
19005 graphene_matrix_t new_transform;
19006
19007 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19008
19009 info = _clutter_actor_get_transform_info_or_defaults (self);
19010
19011 if (transform != NULL)
19012 graphene_matrix_init_from_matrix (&new_transform, transform);
19013 else
19014 graphene_matrix_init_identity (&new_transform);
19015
19016 _clutter_actor_create_transition (self, obj_props[PROP_CHILD_TRANSFORM],
19017 &info->child_transform,
19018 &new_transform);
19019 }
19020
19021 /**
19022 * clutter_actor_get_child_transform:
19023 * @self: a #ClutterActor
19024 * @transform: (out caller-allocates): a #graphene_matrix_t
19025 *
19026 * Retrieves the child transformation matrix set using
19027 * clutter_actor_set_child_transform(); if none is currently set,
19028 * the @transform matrix will be initialized to the identity matrix.
19029 *
19030 * Since: 1.12
19031 */
19032 void
clutter_actor_get_child_transform(ClutterActor * self,graphene_matrix_t * transform)19033 clutter_actor_get_child_transform (ClutterActor *self,
19034 graphene_matrix_t *transform)
19035 {
19036 const ClutterTransformInfo *info;
19037
19038 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19039 g_return_if_fail (transform != NULL);
19040
19041 info = _clutter_actor_get_transform_info_or_defaults (self);
19042
19043 if (info->child_transform_set)
19044 graphene_matrix_init_from_matrix (transform, &info->child_transform);
19045 else
19046 graphene_matrix_init_identity (transform);
19047 }
19048
19049 static void
clutter_actor_push_in_cloned_branch(ClutterActor * self,gulong count)19050 clutter_actor_push_in_cloned_branch (ClutterActor *self,
19051 gulong count)
19052 {
19053 ClutterActor *iter;
19054
19055 for (iter = self->priv->first_child;
19056 iter != NULL;
19057 iter = iter->priv->next_sibling)
19058 clutter_actor_push_in_cloned_branch (iter, count);
19059
19060 self->priv->in_cloned_branch += count;
19061 }
19062
19063 static void
clutter_actor_pop_in_cloned_branch(ClutterActor * self,gulong count)19064 clutter_actor_pop_in_cloned_branch (ClutterActor *self,
19065 gulong count)
19066 {
19067 ClutterActor *iter;
19068
19069 self->priv->in_cloned_branch -= count;
19070
19071 for (iter = self->priv->first_child;
19072 iter != NULL;
19073 iter = iter->priv->next_sibling)
19074 clutter_actor_pop_in_cloned_branch (iter, count);
19075 }
19076
19077 void
_clutter_actor_attach_clone(ClutterActor * actor,ClutterActor * clone)19078 _clutter_actor_attach_clone (ClutterActor *actor,
19079 ClutterActor *clone)
19080 {
19081 ClutterActorPrivate *priv = actor->priv;
19082
19083 g_assert (clone != NULL);
19084
19085 if (priv->clones == NULL)
19086 priv->clones = g_hash_table_new (NULL, NULL);
19087
19088 g_hash_table_add (priv->clones, clone);
19089
19090 clutter_actor_push_in_cloned_branch (actor, 1);
19091 }
19092
19093 void
_clutter_actor_detach_clone(ClutterActor * actor,ClutterActor * clone)19094 _clutter_actor_detach_clone (ClutterActor *actor,
19095 ClutterActor *clone)
19096 {
19097 ClutterActorPrivate *priv = actor->priv;
19098
19099 g_assert (clone != NULL);
19100
19101 if (priv->clones == NULL ||
19102 g_hash_table_lookup (priv->clones, clone) == NULL)
19103 return;
19104
19105 clutter_actor_pop_in_cloned_branch (actor, 1);
19106
19107 g_hash_table_remove (priv->clones, clone);
19108
19109 if (g_hash_table_size (priv->clones) == 0)
19110 {
19111 g_hash_table_unref (priv->clones);
19112 priv->clones = NULL;
19113 }
19114 }
19115
19116 /**
19117 * clutter_actor_has_mapped_clones:
19118 * @self: a #ClutterActor
19119 *
19120 * Returns whether a #ClutterActor or any parent actors have mapped clones
19121 * that are clone-painting @self.
19122 *
19123 * Returns: %TRUE if the actor has mapped clones, %FALSE otherwise
19124 */
19125 gboolean
clutter_actor_has_mapped_clones(ClutterActor * self)19126 clutter_actor_has_mapped_clones (ClutterActor *self)
19127 {
19128 ClutterActor *actor;
19129 GHashTableIter iter;
19130 gpointer key;
19131
19132 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
19133
19134 if (self->priv->in_cloned_branch == 0)
19135 return FALSE;
19136
19137 for (actor = self; actor; actor = actor->priv->parent)
19138 {
19139 if (actor->priv->clones)
19140 {
19141 g_hash_table_iter_init (&iter, actor->priv->clones);
19142 while (g_hash_table_iter_next (&iter, &key, NULL))
19143 {
19144 if (CLUTTER_ACTOR_IS_MAPPED (key))
19145 return TRUE;
19146 }
19147 }
19148
19149 /* Clones will force-show their own source actor but not children of
19150 * it, so if we're hidden and an actor up the hierarchy has a clone,
19151 * we won't be visible.
19152 */
19153 if (!CLUTTER_ACTOR_IS_VISIBLE (actor))
19154 return FALSE;
19155 }
19156
19157 return FALSE;
19158 }
19159
19160 static void
push_in_paint_unmapped_branch(ClutterActor * self,guint count)19161 push_in_paint_unmapped_branch (ClutterActor *self,
19162 guint count)
19163 {
19164 ClutterActor *iter;
19165
19166 for (iter = self->priv->first_child;
19167 iter != NULL;
19168 iter = iter->priv->next_sibling)
19169 push_in_paint_unmapped_branch (iter, count);
19170
19171 self->priv->unmapped_paint_branch_counter += count;
19172 }
19173
19174 static void
pop_in_paint_unmapped_branch(ClutterActor * self,guint count)19175 pop_in_paint_unmapped_branch (ClutterActor *self,
19176 guint count)
19177 {
19178 ClutterActor *iter;
19179
19180 self->priv->unmapped_paint_branch_counter -= count;
19181
19182 for (iter = self->priv->first_child;
19183 iter != NULL;
19184 iter = iter->priv->next_sibling)
19185 pop_in_paint_unmapped_branch (iter, count);
19186 }
19187
19188 static void
clutter_actor_child_model__items_changed(GListModel * model,guint position,guint removed,guint added,gpointer user_data)19189 clutter_actor_child_model__items_changed (GListModel *model,
19190 guint position,
19191 guint removed,
19192 guint added,
19193 gpointer user_data)
19194 {
19195 ClutterActor *parent = user_data;
19196 ClutterActorPrivate *priv = parent->priv;
19197 guint i;
19198
19199 while (removed--)
19200 {
19201 ClutterActor *child = clutter_actor_get_child_at_index (parent, position);
19202 clutter_actor_destroy (child);
19203 }
19204
19205 for (i = 0; i < added; i++)
19206 {
19207 GObject *item = g_list_model_get_item (model, position + i);
19208 ClutterActor *child = priv->create_child_func (item, priv->create_child_data);
19209
19210 /* The actor returned by the function can have a floating reference,
19211 * if the implementation is in pure C, or have a full reference, usually
19212 * the case for language bindings. To avoid leaking references, we
19213 * try to assume ownership of the instance, and release the reference
19214 * at the end unconditionally, leaving the only reference to the actor
19215 * itself.
19216 */
19217 if (g_object_is_floating (child))
19218 g_object_ref_sink (child);
19219
19220 clutter_actor_insert_child_at_index (parent, child, position + i);
19221
19222 g_object_unref (child);
19223 g_object_unref (item);
19224 }
19225 }
19226
19227 /**
19228 * clutter_actor_bind_model:
19229 * @self: a #ClutterActor
19230 * @model: (nullable): a #GListModel
19231 * @create_child_func: a function that creates #ClutterActor instances
19232 * from the contents of the @model
19233 * @user_data: user data passed to @create_child_func
19234 * @notify: function called when unsetting the @model
19235 *
19236 * Binds a #GListModel to a #ClutterActor.
19237 *
19238 * If the #ClutterActor was already bound to a #GListModel, the previous
19239 * binding is destroyed.
19240 *
19241 * The existing children of #ClutterActor are destroyed when setting a
19242 * model, and new children are created and added, representing the contents
19243 * of the @model. The #ClutterActor is updated whenever the @model changes.
19244 * If @model is %NULL, the #ClutterActor is left empty.
19245 *
19246 * When a #ClutterActor is bound to a model, adding and removing children
19247 * directly is undefined behaviour.
19248 *
19249 * Since: 1.24
19250 */
19251 void
clutter_actor_bind_model(ClutterActor * self,GListModel * model,ClutterActorCreateChildFunc create_child_func,gpointer user_data,GDestroyNotify notify)19252 clutter_actor_bind_model (ClutterActor *self,
19253 GListModel *model,
19254 ClutterActorCreateChildFunc create_child_func,
19255 gpointer user_data,
19256 GDestroyNotify notify)
19257 {
19258 ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
19259
19260 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19261 g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
19262 g_return_if_fail (model == NULL || create_child_func != NULL);
19263
19264 if (priv->child_model != NULL)
19265 {
19266 if (priv->create_child_notify != NULL)
19267 priv->create_child_notify (priv->create_child_data);
19268
19269 g_signal_handlers_disconnect_by_func (priv->child_model,
19270 clutter_actor_child_model__items_changed,
19271 self);
19272 g_clear_object (&priv->child_model);
19273 priv->create_child_func = NULL;
19274 priv->create_child_data = NULL;
19275 priv->create_child_notify = NULL;
19276 }
19277
19278 clutter_actor_destroy_all_children (self);
19279
19280 if (model == NULL)
19281 return;
19282
19283 priv->child_model = g_object_ref (model);
19284 priv->create_child_func = create_child_func;
19285 priv->create_child_data = user_data;
19286 priv->create_child_notify = notify;
19287
19288 g_signal_connect (priv->child_model, "items-changed",
19289 G_CALLBACK (clutter_actor_child_model__items_changed),
19290 self);
19291
19292 clutter_actor_child_model__items_changed (priv->child_model,
19293 0,
19294 0,
19295 g_list_model_get_n_items (priv->child_model),
19296 self);
19297 }
19298
19299 typedef struct {
19300 GType child_type;
19301 GArray *props;
19302 } BindClosure;
19303
19304 typedef struct {
19305 const char *model_property;
19306 const char *child_property;
19307 GBindingFlags flags;
19308 } BindProperty;
19309
19310 static void
bind_closure_free(gpointer data_)19311 bind_closure_free (gpointer data_)
19312 {
19313 BindClosure *data = data_;
19314
19315 if (data == NULL)
19316 return;
19317
19318 g_array_unref (data->props);
19319 g_free (data);
19320 }
19321
19322 static ClutterActor *
bind_child_with_properties(gpointer item,gpointer data_)19323 bind_child_with_properties (gpointer item,
19324 gpointer data_)
19325 {
19326 BindClosure *data = data_;
19327 ClutterActor *res;
19328 guint i;
19329
19330 res = g_object_new (data->child_type, NULL);
19331
19332 for (i = 0; i < data->props->len; i++)
19333 {
19334 const BindProperty *prop = &g_array_index (data->props, BindProperty, i);
19335
19336 g_object_bind_property (item, prop->model_property,
19337 res, prop->child_property,
19338 prop->flags);
19339 }
19340
19341 return res;
19342 }
19343
19344 /**
19345 * clutter_actor_bind_model_with_properties:
19346 * @self: a #ClutterActor
19347 * @model: a #GListModel
19348 * @child_type: the type of #ClutterActor to use when creating
19349 * children mapping to items inside the @model
19350 * @first_model_property: the first property of @model to bind
19351 * @...: tuples of property names on the @model, on the child, and the
19352 * #GBindingFlags used to bind them, terminated by %NULL
19353 *
19354 * Binds a #GListModel to a #ClutterActor.
19355 *
19356 * Unlike clutter_actor_bind_model(), this function automatically creates
19357 * a child #ClutterActor of type @child_type, and binds properties on the
19358 * items inside the @model to the corresponding properties on the child,
19359 * for instance:
19360 *
19361 * |[<!-- language="C" -->
19362 * clutter_actor_bind_model_with_properties (actor, model,
19363 * MY_TYPE_CHILD_VIEW,
19364 * "label", "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
19365 * "icon", "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
19366 * "selected", "selected", G_BINDING_BIDIRECTIONAL,
19367 * "active", "active", G_BINDING_BIDIRECTIONAL,
19368 * NULL);
19369 * ]|
19370 *
19371 * is the equivalent of calling clutter_actor_bind_model() with a
19372 * #ClutterActorCreateChildFunc of:
19373 *
19374 * |[<!-- language="C" -->
19375 * ClutterActor *res = g_object_new (MY_TYPE_CHILD_VIEW, NULL);
19376 *
19377 * g_object_bind_property (item, "label", res, "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
19378 * g_object_bind_property (item, "icon", res, "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
19379 * g_object_bind_property (item, "selected", res, "selected", G_BINDING_BIDIRECTIONAL);
19380 * g_object_bind_property (item, "active", res, "active", G_BINDING_BIDIRECTIONAL);
19381 *
19382 * return res;
19383 * ]|
19384 *
19385 * If the #ClutterActor was already bound to a #GListModel, the previous
19386 * binding is destroyed.
19387 *
19388 * When a #ClutterActor is bound to a model, adding and removing children
19389 * directly is undefined behaviour.
19390 *
19391 * See also: clutter_actor_bind_model()
19392 *
19393 * Since: 1.24
19394 */
19395 void
clutter_actor_bind_model_with_properties(ClutterActor * self,GListModel * model,GType child_type,const char * first_model_property,...)19396 clutter_actor_bind_model_with_properties (ClutterActor *self,
19397 GListModel *model,
19398 GType child_type,
19399 const char *first_model_property,
19400 ...)
19401 {
19402 va_list args;
19403 BindClosure *clos;
19404 const char *model_property;
19405
19406 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19407 g_return_if_fail (G_IS_LIST_MODEL (model));
19408 g_return_if_fail (g_type_is_a (child_type, CLUTTER_TYPE_ACTOR));
19409
19410 clos = g_new0 (BindClosure, 1);
19411 clos->child_type = child_type;
19412 clos->props = g_array_new (FALSE, FALSE, sizeof (BindProperty));
19413
19414 va_start (args, first_model_property);
19415 model_property = first_model_property;
19416 while (model_property != NULL)
19417 {
19418 const char *child_property = va_arg (args, char *);
19419 GBindingFlags binding_flags = va_arg (args, guint);
19420 BindProperty bind;
19421
19422 bind.model_property = g_intern_string (model_property);
19423 bind.child_property = g_intern_string (child_property);
19424 bind.flags = binding_flags;
19425
19426 g_array_append_val (clos->props, bind);
19427
19428 model_property = va_arg (args, char *);
19429 }
19430 va_end (args);
19431
19432 clutter_actor_bind_model (self, model, bind_child_with_properties, clos, bind_closure_free);
19433 }
19434
19435 /*< private >
19436 * clutter_actor_create_texture_paint_node:
19437 * @self: a #ClutterActor
19438 * @texture: a #CoglTexture
19439 *
19440 * Creates a #ClutterPaintNode initialized using the state of the
19441 * given #ClutterActor, ready to be used inside the implementation
19442 * of the #ClutterActorClass.paint_node virtual function.
19443 *
19444 * The returned paint node has the geometry set to the size of the
19445 * #ClutterActor:content-box property; it uses the filters specified
19446 * in the #ClutterActor:minification-filter and #ClutterActor:magnification-filter
19447 * properties; and respects the #ClutterActor:content-repeat property.
19448 *
19449 * Returns: (transfer full): The newly created #ClutterPaintNode
19450 *
19451 * Since: 1.24
19452 */
19453 ClutterPaintNode *
clutter_actor_create_texture_paint_node(ClutterActor * self,CoglTexture * texture)19454 clutter_actor_create_texture_paint_node (ClutterActor *self,
19455 CoglTexture *texture)
19456 {
19457 ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
19458 ClutterPaintNode *node;
19459 ClutterActorBox box;
19460 ClutterColor color;
19461
19462 g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
19463 g_return_val_if_fail (texture != NULL, NULL);
19464
19465 clutter_actor_get_content_box (self, &box);
19466
19467 /* ClutterTextureNode will premultiply the blend color, so we
19468 * want it to be white with the paint opacity
19469 */
19470 color.red = 255;
19471 color.green = 255;
19472 color.blue = 255;
19473 color.alpha = clutter_actor_get_paint_opacity_internal (self);
19474
19475 node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter);
19476 clutter_paint_node_set_static_name (node, "Texture");
19477
19478 if (priv->content_repeat == CLUTTER_REPEAT_NONE)
19479 clutter_paint_node_add_rectangle (node, &box);
19480 else
19481 {
19482 float t_w = 1.f, t_h = 1.f;
19483
19484 if ((priv->content_repeat & CLUTTER_REPEAT_X_AXIS) != FALSE)
19485 t_w = (box.x2 - box.x1) / cogl_texture_get_width (texture);
19486
19487 if ((priv->content_repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE)
19488 t_h = (box.y2 - box.y1) / cogl_texture_get_height (texture);
19489
19490 clutter_paint_node_add_texture_rectangle (node, &box,
19491 0.f, 0.f,
19492 t_w, t_h);
19493 }
19494
19495 return node;
19496 }
19497
19498 gboolean
clutter_actor_has_accessible(ClutterActor * actor)19499 clutter_actor_has_accessible (ClutterActor *actor)
19500 {
19501 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
19502
19503 if (CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible)
19504 return CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible (actor);
19505
19506 return TRUE;
19507 }
19508
19509 void
clutter_actor_queue_immediate_relayout(ClutterActor * self)19510 clutter_actor_queue_immediate_relayout (ClutterActor *self)
19511 {
19512 ClutterStage *stage;
19513
19514 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19515
19516 clutter_actor_queue_relayout (self);
19517
19518 stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
19519 if (stage)
19520 clutter_stage_set_actor_needs_immediate_relayout (stage);
19521 }
19522
19523 /**
19524 * clutter_actor_invalidate_transform:
19525 * @self: A #ClutterActor
19526 *
19527 * Invalidate the cached transformation matrix of @self.
19528 * This is needed for implementations overriding the apply_transform()
19529 * vfunc and has to be called if the matrix returned by apply_transform()
19530 * would change.
19531 */
19532 void
clutter_actor_invalidate_transform(ClutterActor * self)19533 clutter_actor_invalidate_transform (ClutterActor *self)
19534 {
19535 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19536
19537 transform_changed (self);
19538 }
19539
19540 /**
19541 * clutter_actor_invalidate_paint_volume:
19542 * @self: A #ClutterActor
19543 *
19544 * Invalidates the cached paint volume of @self. This is needed for
19545 * implementations overriding the #ClutterActorClass.get_paint_volume()
19546 * virtual function and has to be called every time the paint volume
19547 * returned by that function would change.
19548 */
19549 void
clutter_actor_invalidate_paint_volume(ClutterActor * self)19550 clutter_actor_invalidate_paint_volume (ClutterActor *self)
19551 {
19552 g_return_if_fail (CLUTTER_IS_ACTOR (self));
19553
19554 queue_update_paint_volume (self);
19555 }
19556
19557 gboolean
clutter_actor_get_redraw_clip(ClutterActor * self,ClutterPaintVolume * dst_old_pv,ClutterPaintVolume * dst_new_pv)19558 clutter_actor_get_redraw_clip (ClutterActor *self,
19559 ClutterPaintVolume *dst_old_pv,
19560 ClutterPaintVolume *dst_new_pv)
19561 {
19562 ClutterActorPrivate *priv = self->priv;
19563 ClutterPaintVolume *paint_volume;
19564
19565 paint_volume = _clutter_actor_get_paint_volume_mutable (self);
19566 if (!paint_volume || !priv->last_paint_volume_valid)
19567 return FALSE;
19568
19569 _clutter_paint_volume_set_from_volume (dst_old_pv, &priv->last_paint_volume);
19570 _clutter_paint_volume_set_from_volume (dst_new_pv, paint_volume);
19571
19572 return TRUE;
19573 }
19574