mirror of
https://github.com/brl/mutter.git
synced 2024-12-26 04:42:14 +00:00
d39277d0cf
This allows hiding + moving + showing an actor without repainting the wrong area. http://bugzilla.clutter-project.org/show_bug.cgi?id=2395
11861 lines
349 KiB
C
11861 lines
349 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
*
|
|
* Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
|
|
* Copyright (C) 2009, 2010 Intel Corp
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-actor
|
|
* @short_description: Base abstract class for all visual stage actors.
|
|
*
|
|
* #ClutterActor is a base abstract class for all visual elements on the
|
|
* stage. Every object that must appear on the main #ClutterStage must also
|
|
* be a #ClutterActor, either by using one of the classes provided by
|
|
* Clutter, or by implementing a new #ClutterActor subclass.
|
|
*
|
|
* Every actor is a 2D surface positioned and optionally transformed
|
|
* in 3D space. The actor is positioned relative to top left corner of
|
|
* it parent with the childs origin being its anchor point (also top
|
|
* left by default).
|
|
*
|
|
* <refsect2 id="ClutterActor-box">
|
|
* <title>Actor bounding box and transformations</title>
|
|
* <para>Any actor's 2D surface is contained inside its bounding box,
|
|
* as described by the #ClutterActorBox structure:</para>
|
|
* <figure id="actor-box">
|
|
* <title>Bounding box of an Actor</title>
|
|
* <graphic fileref="actor-box.png" format="PNG"/>
|
|
* </figure>
|
|
* <para>The actor box represents the untransformed area occupied by an
|
|
* actor. Each visible actor that has been put on a #ClutterStage also
|
|
* has a transformed area, depending on the actual transformations
|
|
* applied to it by the developer (scale, rotation). Tranforms will
|
|
* also be applied to any child actors. Also applied to all actors by
|
|
* the #ClutterStage is a perspective transformation. API is provided
|
|
* for both tranformed and untransformed actor geometry information.</para>
|
|
* <para>The GL 'modelview' transform matrix for the actor is constructed
|
|
* from the actor settings by the following order of operations:</para>
|
|
* <orderedlist>
|
|
* <listitem><para>Translation by actor x, y coords,</para></listitem>
|
|
* <listitem><para>Translation by actor depth (z),</para></listitem>
|
|
* <listitem><para>Scaling by scale_x, scale_y,</para></listitem>
|
|
* <listitem><para>Rotation around z axis,</para></listitem>
|
|
* <listitem><para>Rotation around y axis,</para></listitem>
|
|
* <listitem><para>Rotation around x axis,</para></listitem>
|
|
* <listitem><para>Negative translation by anchor point x,
|
|
* y,</para></listitem>
|
|
* <listitem><para>Rectangular Clip is applied (this is not an operation
|
|
* on the matrix as such, but it is done as part of the transform set
|
|
* up).</para></listitem>
|
|
* </orderedlist>
|
|
* <para>An actor can either be explicitly sized and positioned, using the
|
|
* various size and position accessors, like clutter_actor_set_x() or
|
|
* clutter_actor_set_width(); or it can have a preferred width and
|
|
* height, which then allows a layout manager to implicitly size and
|
|
* position it by "allocating" an area for an actor. This allows for
|
|
* actors to be manipulated in both a fixed (or static) parent container
|
|
* (i.e. children of #ClutterGroup) and a more automatic (or dynamic)
|
|
* layout based parent container.</para>
|
|
* <para>When accessing the position and size of an actor, the simple
|
|
* accessors like clutter_actor_get_width() and clutter_actor_get_x()
|
|
* will return a value depending on whether the actor has been explicitly
|
|
* sized and positioned by the developer or implicitly by the layout
|
|
* manager.</para>
|
|
* <para>Depending on whether you are querying an actor or implementing a
|
|
* layout manager, you should either use the simple accessors or use the
|
|
* size negotiation API.</para>
|
|
* </refsect2>
|
|
*
|
|
* <refsect2 id="ClutterActor-event-handling">
|
|
* <title>Event Handling</title>
|
|
* <para>Clutter actors are also able to receive input events and react to
|
|
* them. Events are handled in the following ways:</para>
|
|
* <orderedlist>
|
|
* <listitem><para>Actors emit pointer events if set reactive, see
|
|
* clutter_actor_set_reactive()</para></listitem>
|
|
* <listitem><para>The stage is always reactive</para></listitem>
|
|
* <listitem><para>Events are handled by connecting signal handlers to
|
|
* the numerous event signal types.</para></listitem>
|
|
* <listitem><para>Event handlers must return %TRUE if they handled
|
|
* the event and wish to block the event emission chain, or %FALSE
|
|
* if the emission chain must continue</para></listitem>
|
|
* <listitem><para>Keyboard events are emitted if actor has focus, see
|
|
* clutter_stage_set_key_focus()</para></listitem>
|
|
* <listitem><para>Motion events (motion, enter, leave) are not emitted
|
|
* if clutter_set_motion_events_enabled() is called with %FALSE.
|
|
* See clutter_set_motion_events_enabled() documentation for more
|
|
* information.</para></listitem>
|
|
* <listitem><para>Once emitted, an event emission chain has two
|
|
* phases: capture and bubble. An emitted event starts in the capture
|
|
* phase (see ClutterActor::captured-event) beginning at the stage and
|
|
* traversing every child actor until the event source actor is reached.
|
|
* The emission then enters the bubble phase, traversing back up the
|
|
* chain via parents until it reaches the stage. Any event handler can
|
|
* abort this chain by returning %TRUE (meaning "event handled").
|
|
* </para></listitem>
|
|
* <listitem><para>Pointer events will 'pass through' non reactive
|
|
* overlapping actors.</para></listitem>
|
|
* </orderedlist>
|
|
* <figure id="event-flow">
|
|
* <title>Event flow in Clutter</title>
|
|
* <graphic fileref="event-flow.png" format="PNG"/>
|
|
* </figure>
|
|
* <para>Every '?' box in the diagram above is an entry point for
|
|
* application code.</para>
|
|
* </refsect2>
|
|
*
|
|
* <refsect2 id="ClutterActor-subclassing">
|
|
* <title>Implementing a ClutterActor</title>
|
|
* <para>For implementing a new custom actor class, please read <link
|
|
* linkend="clutter-subclassing-ClutterActor">the corresponding
|
|
* section</link> of the API reference.</para>
|
|
* </refsect2>
|
|
*
|
|
* <refsect2 id="ClutterActor-script">
|
|
* <title>ClutterActor custom properties for #ClutterScript</title>
|
|
* <para>#ClutterActor defines a custom "rotation" property which
|
|
* allows a short-hand description of the rotations to be applied
|
|
* to an actor.</para>
|
|
* <para>The syntax of the "rotation" property is the following:</para>
|
|
* <informalexample>
|
|
* <programlisting>
|
|
* "rotation" : [
|
|
* { "<axis>" : [ <angle>, [ <center> ] ] }
|
|
* ]
|
|
* </programlisting>
|
|
* </informalexample>
|
|
* <para>where the <emphasis>axis</emphasis> is the name of an enumeration
|
|
* value of type #ClutterRotateAxis and <emphasis>angle</emphasis> is a
|
|
* floating point value representing the rotation angle on the given axis,
|
|
* in degrees.</para>
|
|
* <para>The <emphasis>center</emphasis> array is optional, and if present
|
|
* it must contain the center of rotation as described by two coordinates:
|
|
* Y and Z for "x-axis"; X and Z for "y-axis"; and X and Y for
|
|
* "z-axis".</para>
|
|
* <para>#ClutterActor will also parse every positional and dimensional
|
|
* property defined as a string through clutter_units_from_string(); you
|
|
* should read the documentation for the #ClutterUnits parser format for
|
|
* the valid units and syntax.</para>
|
|
* </refsect2>
|
|
*
|
|
* <refsect2 id="ClutterActor-animating">
|
|
* <title>Custom animatable properties</title>
|
|
* <para>#ClutterActor allows accessing properties of #ClutterAction
|
|
* and #ClutterConstraint instances associated to an actor instance
|
|
* for animation purposes.</para>
|
|
* <para>In order to access a specific #ClutterAction or a #ClutterConstraint
|
|
* property it is necessary to set the #ClutterActorMeta:name property on the
|
|
* given action or constraint.</para>
|
|
* <para>The property can be accessed using the the following syntax:</para>
|
|
* <informalexample>
|
|
* <programlisting>
|
|
* @<section>.<meta-name>.<property-name>
|
|
* </programlisting>
|
|
* </informalexample>
|
|
* <para>The initial <emphasis>@</emphasis> is mandatory.</para>
|
|
* <para>The <emphasis>section</emphasis> fragment can be one between
|
|
* "actions", "constraints" and "effects".</para>
|
|
* <para>The <emphasis>meta-name</emphasis> fragment is the name of the
|
|
* action or constraint, as specified by the #ClutterActorMeta:name
|
|
* property.</para>
|
|
* <para>The <emphasis>property-name</emphasis> fragment is the name of the
|
|
* action or constraint property to be animated.</para>
|
|
* <example id="example-ClutterActor-animating-meta">
|
|
* <title>Animating a constraint property</title>
|
|
* <para>The example below animates a #ClutterBindConstraint applied to an
|
|
* actor using clutter_actor_animate(). The <emphasis>rect</emphasis> has
|
|
* a binding constraint for the <emphasis>origin</emphasis> actor, and in
|
|
* its initial state is fully transparent and overlapping the actor to
|
|
* which is bound to. </para>
|
|
* <programlisting>
|
|
* constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_X, 0.0);
|
|
* clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-x");
|
|
* clutter_actor_add_constraint (rect, constraint);
|
|
*
|
|
* constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_Y, 0.0);
|
|
* clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-y");
|
|
* clutter_actor_add_constraint (rect, constraint);
|
|
*
|
|
* clutter_actor_set_reactive (rect, TRUE);
|
|
* clutter_actor_set_opacity (rect, 0);
|
|
*
|
|
* g_signal_connect (rect, "button-press-event",
|
|
* G_CALLBACK (on_button_press),
|
|
* NULL);
|
|
* </programlisting>
|
|
* <para>On button press, the rectangle "slides" from behind the actor to
|
|
* which is bound to, using the #ClutterBindConstraint:offset property and
|
|
* the #ClutterActor:opacity property.</para>
|
|
* <programlisting>
|
|
* float new_offset = clutter_actor_get_width (origin) + h_padding;
|
|
*
|
|
* clutter_actor_animate (rect, CLUTTER_EASE_OUT_CUBIC, 500,
|
|
* "opacity", 255,
|
|
* "@constraints.bind-x.offset", new_offset,
|
|
* NULL);
|
|
* </programlisting>
|
|
* </example>
|
|
* </refsect2>
|
|
*/
|
|
|
|
/**
|
|
* CLUTTER_ACTOR_IS_MAPPED:
|
|
* @a: a #ClutterActor
|
|
*
|
|
* Evaluates to %TRUE if the %CLUTTER_ACTOR_MAPPED flag is set.
|
|
*
|
|
* Means "the actor will be painted if the stage is mapped."
|
|
*
|
|
* %TRUE if the actor is visible; and all parents with possible exception
|
|
* of the stage are visible; and an ancestor of the actor is a toplevel.
|
|
*
|
|
* Clutter auto-maintains the mapped flag whenever actors are
|
|
* reparented or shown/hidden.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
|
|
/**
|
|
* CLUTTER_ACTOR_IS_REALIZED:
|
|
* @a: a #ClutterActor
|
|
*
|
|
* Evaluates to %TRUE if the %CLUTTER_ACTOR_REALIZED flag is set.
|
|
*
|
|
* The realized state has an actor-dependant interpretation. If an
|
|
* actor wants to delay allocating resources until it is attached to a
|
|
* stage, it may use the realize state to do so. However it is
|
|
* perfectly acceptable for an actor to allocate Cogl resources before
|
|
* being realized because there is only one GL context used by Clutter
|
|
* so any resources will work on any stage. If an actor is mapped it
|
|
* must also be realized, but an actor can be realized and unmapped
|
|
* (this is so hiding an actor temporarily doesn't do an expensive
|
|
* unrealize/realize).
|
|
*
|
|
* To be realized an actor must be inside a stage, and all its parents
|
|
* must be realized.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
|
|
/**
|
|
* CLUTTER_ACTOR_IS_VISIBLE:
|
|
* @a: a #ClutterActor
|
|
*
|
|
* Evaluates to %TRUE if the actor has been shown, %FALSE if it's hidden.
|
|
* Equivalent to the ClutterActor::visible object property.
|
|
*
|
|
* Note that an actor is only painted onscreen if it's mapped, which
|
|
* means it's visible, and all its parents are visible, and one of the
|
|
* parents is a toplevel stage.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
|
|
/**
|
|
* CLUTTER_ACTOR_IS_REACTIVE:
|
|
* @a: a #ClutterActor
|
|
*
|
|
* Evaluates to %TRUE if the %CLUTTER_ACTOR_REACTIVE flag is set.
|
|
*
|
|
* Only reactive actors will receive event-related signals.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "cogl/cogl.h"
|
|
|
|
#include "clutter-actor-private.h"
|
|
|
|
#include "clutter-action.h"
|
|
#include "clutter-actor-meta-private.h"
|
|
#include "clutter-animatable.h"
|
|
#include "clutter-constraint.h"
|
|
#include "clutter-container.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-effect.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-main.h"
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-paint-volume-private.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-profile.h"
|
|
#include "clutter-scriptable.h"
|
|
#include "clutter-script-private.h"
|
|
#include "clutter-shader.h"
|
|
#include "clutter-stage-private.h"
|
|
#include "clutter-units.h"
|
|
|
|
#undef CLUTTER_DISABLE_DEPRECATED
|
|
#include "clutter-behaviour.h"
|
|
|
|
typedef struct _ShaderData ShaderData;
|
|
typedef struct _AnchorCoord AnchorCoord;
|
|
|
|
#define CLUTTER_ACTOR_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ACTOR, ClutterActorPrivate))
|
|
|
|
/* Internal helper struct to represent a point that can be stored in
|
|
either direct pixel coordinates or as a fraction of the actor's
|
|
size. It is used for the anchor point, scale center and rotation
|
|
centers. */
|
|
struct _AnchorCoord
|
|
{
|
|
gboolean is_fractional;
|
|
|
|
union
|
|
{
|
|
/* Used when is_fractional == TRUE */
|
|
struct
|
|
{
|
|
gdouble x;
|
|
gdouble y;
|
|
} fraction;
|
|
|
|
/* Use when is_fractional == FALSE */
|
|
ClutterVertex units;
|
|
} v;
|
|
};
|
|
|
|
/* 3 entries should be a good compromise, few layout managers
|
|
* will ask for 3 different preferred size in each allocation cycle */
|
|
#define N_CACHED_SIZE_REQUESTS 3
|
|
typedef struct _SizeRequest SizeRequest;
|
|
struct _SizeRequest
|
|
{
|
|
guint age;
|
|
gfloat for_size;
|
|
gfloat min_size;
|
|
gfloat natural_size;
|
|
};
|
|
|
|
/* Internal enum used to control mapped state update. This is a hint
|
|
* which indicates when to do something other than just enforce
|
|
* invariants.
|
|
*/
|
|
typedef enum {
|
|
MAP_STATE_CHECK, /* just enforce invariants. */
|
|
MAP_STATE_MAKE_UNREALIZED, /* force unrealize, ignoring invariants,
|
|
* used when about to unparent.
|
|
*/
|
|
MAP_STATE_MAKE_MAPPED, /* set mapped, error if invariants not met;
|
|
* used to set mapped on toplevels.
|
|
*/
|
|
MAP_STATE_MAKE_UNMAPPED /* set unmapped, even if parent is mapped,
|
|
* used just before unmapping parent.
|
|
*/
|
|
} MapStateChange;
|
|
|
|
struct _ClutterActorPrivate
|
|
{
|
|
/* fixed_x, fixed_y, and the allocation box are all in parent
|
|
* coordinates.
|
|
*/
|
|
gfloat fixed_x;
|
|
gfloat fixed_y;
|
|
|
|
/* request mode */
|
|
ClutterRequestMode request_mode;
|
|
|
|
/* our cached size requests for different width / height */
|
|
SizeRequest width_requests[N_CACHED_SIZE_REQUESTS];
|
|
SizeRequest height_requests[N_CACHED_SIZE_REQUESTS];
|
|
|
|
/* An age of 0 means the entry is not set */
|
|
guint cached_height_age;
|
|
guint cached_width_age;
|
|
|
|
gfloat request_min_width;
|
|
gfloat request_min_height;
|
|
gfloat request_natural_width;
|
|
gfloat request_natural_height;
|
|
|
|
ClutterActorBox allocation;
|
|
ClutterAllocationFlags allocation_flags;
|
|
|
|
guint position_set : 1;
|
|
guint min_width_set : 1;
|
|
guint min_height_set : 1;
|
|
guint natural_width_set : 1;
|
|
guint natural_height_set : 1;
|
|
/* cached request is invalid (implies allocation is too) */
|
|
guint needs_width_request : 1;
|
|
/* cached request is invalid (implies allocation is too) */
|
|
guint needs_height_request : 1;
|
|
/* cached allocation is invalid (request has changed, probably) */
|
|
guint needs_allocation : 1;
|
|
guint show_on_set_parent : 1;
|
|
guint has_clip : 1;
|
|
guint clip_to_allocation : 1;
|
|
guint enable_model_view_transform : 1;
|
|
guint enable_paint_unmapped : 1;
|
|
guint has_pointer : 1;
|
|
guint propagated_one_redraw : 1;
|
|
guint paint_volume_valid : 1;
|
|
guint last_paint_box_valid : 1;
|
|
|
|
gfloat clip[4];
|
|
|
|
/* Rotation angles */
|
|
gdouble rxang;
|
|
gdouble ryang;
|
|
gdouble rzang;
|
|
|
|
/* Rotation center: X axis */
|
|
AnchorCoord rx_center;
|
|
|
|
/* Rotation center: Y axis */
|
|
AnchorCoord ry_center;
|
|
|
|
/* Rotation center: Z axis */
|
|
AnchorCoord rz_center;
|
|
|
|
/* Anchor point coordinates */
|
|
AnchorCoord anchor;
|
|
|
|
/* depth */
|
|
gfloat z;
|
|
|
|
guint8 opacity;
|
|
|
|
ClutterActor *parent_actor;
|
|
GList *children;
|
|
gint n_children;
|
|
|
|
gchar *name;
|
|
guint32 id; /* Unique ID */
|
|
|
|
gdouble scale_x;
|
|
gdouble scale_y;
|
|
|
|
AnchorCoord scale_center;
|
|
|
|
ShaderData *shader_data;
|
|
|
|
PangoContext *pango_context;
|
|
|
|
ClutterActor *opacity_parent;
|
|
|
|
ClutterTextDirection text_direction;
|
|
|
|
gint internal_child;
|
|
|
|
/* XXX: This is a workaround for not being able to break the ABI
|
|
* of the QUEUE_REDRAW signal. It's an out-of-band argument.
|
|
* See clutter_actor_queue_clipped_redraw() for details.
|
|
*/
|
|
const ClutterPaintVolume *oob_queue_redraw_clip;
|
|
|
|
ClutterMetaGroup *actions;
|
|
ClutterMetaGroup *constraints;
|
|
ClutterMetaGroup *effects;
|
|
|
|
/* used when painting, to update the paint volume */
|
|
ClutterActorMeta *current_effect;
|
|
|
|
ClutterPaintVolume paint_volume;
|
|
|
|
ClutterActorBox last_paint_box;
|
|
|
|
ClutterStageQueueRedrawEntry *queue_redraw_entry;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_NAME,
|
|
|
|
/* X, Y, WIDTH, HEIGHT are "do what I mean" properties;
|
|
* when set they force a size request, when gotten they
|
|
* get the allocation if the allocation is valid, and the
|
|
* request otherwise
|
|
*/
|
|
PROP_X,
|
|
PROP_Y,
|
|
PROP_WIDTH,
|
|
PROP_HEIGHT,
|
|
|
|
/* Then the rest of these size-related properties are the "actual"
|
|
* underlying properties set or gotten by X, Y, WIDTH, HEIGHT
|
|
*/
|
|
PROP_FIXED_X,
|
|
PROP_FIXED_Y,
|
|
|
|
PROP_FIXED_POSITION_SET,
|
|
|
|
PROP_MIN_WIDTH,
|
|
PROP_MIN_WIDTH_SET,
|
|
|
|
PROP_MIN_HEIGHT,
|
|
PROP_MIN_HEIGHT_SET,
|
|
|
|
PROP_NATURAL_WIDTH,
|
|
PROP_NATURAL_WIDTH_SET,
|
|
|
|
PROP_NATURAL_HEIGHT,
|
|
PROP_NATURAL_HEIGHT_SET,
|
|
|
|
PROP_REQUEST_MODE,
|
|
|
|
/* Allocation properties are read-only */
|
|
PROP_ALLOCATION,
|
|
|
|
PROP_DEPTH,
|
|
|
|
PROP_CLIP,
|
|
PROP_HAS_CLIP,
|
|
PROP_CLIP_TO_ALLOCATION,
|
|
|
|
PROP_OPACITY,
|
|
|
|
PROP_VISIBLE,
|
|
PROP_MAPPED,
|
|
PROP_REALIZED,
|
|
PROP_REACTIVE,
|
|
|
|
PROP_SCALE_X,
|
|
PROP_SCALE_Y,
|
|
PROP_SCALE_CENTER_X,
|
|
PROP_SCALE_CENTER_Y,
|
|
PROP_SCALE_GRAVITY,
|
|
|
|
PROP_ROTATION_ANGLE_X,
|
|
PROP_ROTATION_ANGLE_Y,
|
|
PROP_ROTATION_ANGLE_Z,
|
|
PROP_ROTATION_CENTER_X,
|
|
PROP_ROTATION_CENTER_Y,
|
|
PROP_ROTATION_CENTER_Z,
|
|
/* This property only makes sense for the z rotation because the
|
|
others would depend on the actor having a size along the
|
|
z-axis */
|
|
PROP_ROTATION_CENTER_Z_GRAVITY,
|
|
|
|
PROP_ANCHOR_X,
|
|
PROP_ANCHOR_Y,
|
|
PROP_ANCHOR_GRAVITY,
|
|
|
|
PROP_SHOW_ON_SET_PARENT,
|
|
|
|
PROP_TEXT_DIRECTION,
|
|
PROP_HAS_POINTER,
|
|
|
|
PROP_ACTIONS,
|
|
PROP_CONSTRAINTS,
|
|
PROP_EFFECT,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *obj_props[PROP_LAST];
|
|
|
|
enum
|
|
{
|
|
SHOW,
|
|
HIDE,
|
|
DESTROY,
|
|
PARENT_SET,
|
|
KEY_FOCUS_IN,
|
|
KEY_FOCUS_OUT,
|
|
PAINT,
|
|
PICK,
|
|
REALIZE,
|
|
UNREALIZE,
|
|
QUEUE_REDRAW,
|
|
QUEUE_RELAYOUT,
|
|
EVENT,
|
|
CAPTURED_EVENT,
|
|
BUTTON_PRESS_EVENT,
|
|
BUTTON_RELEASE_EVENT,
|
|
SCROLL_EVENT,
|
|
KEY_PRESS_EVENT,
|
|
KEY_RELEASE_EVENT,
|
|
MOTION_EVENT,
|
|
ENTER_EVENT,
|
|
LEAVE_EVENT,
|
|
ALLOCATION_CHANGED,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint actor_signals[LAST_SIGNAL] = { 0, };
|
|
|
|
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
|
|
static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
|
|
static void atk_implementor_iface_init (AtkImplementorIface *iface);
|
|
|
|
static void clutter_actor_shader_pre_paint (ClutterActor *actor,
|
|
gboolean repeat);
|
|
static void clutter_actor_shader_post_paint (ClutterActor *actor);
|
|
|
|
static void destroy_shader_data (ClutterActor *self);
|
|
|
|
/* These setters are all static for now, maybe they should be in the
|
|
* public API, but they are perhaps obscure enough to leave only as
|
|
* properties
|
|
*/
|
|
static void clutter_actor_set_min_width (ClutterActor *self,
|
|
gfloat min_width);
|
|
static void clutter_actor_set_min_height (ClutterActor *self,
|
|
gfloat min_height);
|
|
static void clutter_actor_set_natural_width (ClutterActor *self,
|
|
gfloat natural_width);
|
|
static void clutter_actor_set_natural_height (ClutterActor *self,
|
|
gfloat natural_height);
|
|
static void clutter_actor_set_min_width_set (ClutterActor *self,
|
|
gboolean use_min_width);
|
|
static void clutter_actor_set_min_height_set (ClutterActor *self,
|
|
gboolean use_min_height);
|
|
static void clutter_actor_set_natural_width_set (ClutterActor *self,
|
|
gboolean use_natural_width);
|
|
static void clutter_actor_set_natural_height_set (ClutterActor *self,
|
|
gboolean use_natural_height);
|
|
static void clutter_actor_update_map_state (ClutterActor *self,
|
|
MapStateChange change);
|
|
static void clutter_actor_unrealize_not_hiding (ClutterActor *self);
|
|
|
|
/* Helper routines for managing anchor coords */
|
|
static void clutter_anchor_coord_get_units (ClutterActor *self,
|
|
const AnchorCoord *coord,
|
|
gfloat *x,
|
|
gfloat *y,
|
|
gfloat *z);
|
|
static void clutter_anchor_coord_set_units (AnchorCoord *coord,
|
|
gfloat x,
|
|
gfloat y,
|
|
gfloat z);
|
|
|
|
static ClutterGravity clutter_anchor_coord_get_gravity (AnchorCoord *coord);
|
|
static void clutter_anchor_coord_set_gravity (AnchorCoord *coord,
|
|
ClutterGravity gravity);
|
|
|
|
static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord);
|
|
|
|
static void _clutter_actor_queue_only_relayout (ClutterActor *self);
|
|
|
|
static void _clutter_actor_get_relative_modelview (ClutterActor *self,
|
|
ClutterActor *ancestor,
|
|
CoglMatrix *matrix);
|
|
|
|
static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self);
|
|
|
|
/* Helper macro which translates by the anchor coord, applies the
|
|
given transformation and then translates back */
|
|
#define TRANSFORM_ABOUT_ANCHOR_COORD(a,m,c,_transform) G_STMT_START { \
|
|
gfloat _tx, _ty, _tz; \
|
|
clutter_anchor_coord_get_units ((a), (c), &_tx, &_ty, &_tz); \
|
|
cogl_matrix_translate ((m), _tx, _ty, _tz); \
|
|
{ _transform; } \
|
|
cogl_matrix_translate ((m), -_tx, -_ty, -_tz); } G_STMT_END
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterActor,
|
|
clutter_actor,
|
|
G_TYPE_INITIALLY_UNOWNED,
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
|
|
clutter_scriptable_iface_init)
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
|
|
clutter_animatable_iface_init)
|
|
G_IMPLEMENT_INTERFACE (ATK_TYPE_IMPLEMENTOR,
|
|
atk_implementor_iface_init));
|
|
|
|
G_CONST_RETURN gchar *
|
|
_clutter_actor_get_debug_name (ClutterActor *actor)
|
|
{
|
|
return actor->priv->name != NULL ? actor->priv->name
|
|
: G_OBJECT_TYPE_NAME (actor);
|
|
}
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
/* XXX - this is for debugging only, remove once working (or leave
|
|
* in only in some debug mode). Should leave it for a little while
|
|
* until we're confident in the new map/realize/visible handling.
|
|
*/
|
|
static inline void
|
|
clutter_actor_verify_map_state (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
if (CLUTTER_ACTOR_IS_REALIZED (self))
|
|
{
|
|
/* all bets are off during reparent when we're potentially realized,
|
|
* but should not be according to invariants
|
|
*/
|
|
if (!CLUTTER_ACTOR_IN_REPARENT (self))
|
|
{
|
|
if (priv->parent_actor == NULL)
|
|
{
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
}
|
|
else
|
|
g_warning ("Realized non-toplevel actor '%s' should "
|
|
"have a parent",
|
|
_clutter_actor_get_debug_name (self));
|
|
}
|
|
else if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_actor))
|
|
{
|
|
g_warning ("Realized actor %s has an unrealized parent %s",
|
|
_clutter_actor_get_debug_name (self),
|
|
_clutter_actor_get_debug_name (priv->parent_actor));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (self))
|
|
{
|
|
if (!CLUTTER_ACTOR_IS_REALIZED (self))
|
|
g_warning ("Actor '%s' is mapped but not realized",
|
|
_clutter_actor_get_debug_name (self));
|
|
|
|
/* remaining bets are off during reparent when we're potentially
|
|
* mapped, but should not be according to invariants
|
|
*/
|
|
if (!CLUTTER_ACTOR_IN_REPARENT (self))
|
|
{
|
|
if (priv->parent_actor == NULL)
|
|
{
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (self) &&
|
|
!CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
{
|
|
g_warning ("Toplevel actor '%s' is mapped "
|
|
"but not visible",
|
|
_clutter_actor_get_debug_name (self));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Mapped actor '%s' should have a parent",
|
|
_clutter_actor_get_debug_name (self));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ClutterActor *iter = self;
|
|
|
|
/* check for the enable_paint_unmapped flag on the actor
|
|
* and parents; if the flag is enabled at any point of this
|
|
* branch of the scene graph then all the later checks
|
|
* become pointless
|
|
*/
|
|
while (iter != NULL)
|
|
{
|
|
if (iter->priv->enable_paint_unmapped)
|
|
return;
|
|
|
|
iter = iter->priv->parent_actor;
|
|
}
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (priv->parent_actor))
|
|
{
|
|
g_warning ("Actor '%s' should not be mapped if parent '%s'"
|
|
"is not visible",
|
|
_clutter_actor_get_debug_name (self),
|
|
_clutter_actor_get_debug_name (priv->parent_actor));
|
|
}
|
|
|
|
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_actor))
|
|
{
|
|
g_warning ("Actor '%s' should not be mapped if parent '%s'"
|
|
"is not realized",
|
|
_clutter_actor_get_debug_name (self),
|
|
_clutter_actor_get_debug_name (priv->parent_actor));
|
|
}
|
|
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (priv->parent_actor))
|
|
{
|
|
if (!CLUTTER_ACTOR_IS_MAPPED (priv->parent_actor))
|
|
g_warning ("Actor '%s' is mapped but its non-toplevel "
|
|
"parent '%s' is not mapped",
|
|
_clutter_actor_get_debug_name (self),
|
|
_clutter_actor_get_debug_name (priv->parent_actor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* CLUTTER_ENABLE_DEBUG */
|
|
|
|
static void
|
|
clutter_actor_set_mapped (ClutterActor *self,
|
|
gboolean mapped)
|
|
{
|
|
if (CLUTTER_ACTOR_IS_MAPPED (self) == mapped)
|
|
return;
|
|
|
|
if (mapped)
|
|
{
|
|
CLUTTER_ACTOR_GET_CLASS (self)->map (self);
|
|
g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
|
|
}
|
|
else
|
|
{
|
|
CLUTTER_ACTOR_GET_CLASS (self)->unmap (self);
|
|
g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
|
|
}
|
|
}
|
|
|
|
/* this function updates the mapped and realized states according to
|
|
* invariants, in the appropriate order.
|
|
*/
|
|
static void
|
|
clutter_actor_update_map_state (ClutterActor *self,
|
|
MapStateChange change)
|
|
{
|
|
gboolean was_mapped;
|
|
|
|
was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
|
|
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
/* the mapped flag on top-level actors must be set by the
|
|
* per-backend implementation because it might be asynchronous.
|
|
*
|
|
* That is, the MAPPED flag on toplevels currently tracks the X
|
|
* server mapped-ness of the window, while the expected behavior
|
|
* (if used to GTK) may be to track WM_STATE!=WithdrawnState.
|
|
* This creates some weird complexity by breaking the invariant
|
|
* that if we're visible and all ancestors shown then we are
|
|
* also mapped - instead, we are mapped if all ancestors
|
|
* _possibly excepting_ the stage are mapped. The stage
|
|
* will map/unmap for example when it is minimized or
|
|
* moved to another workspace.
|
|
*
|
|
* So, the only invariant on the stage is that if visible it
|
|
* should be realized, and that it has to be visible to be
|
|
* mapped.
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (self))
|
|
clutter_actor_realize (self);
|
|
|
|
switch (change)
|
|
{
|
|
case MAP_STATE_CHECK:
|
|
break;
|
|
|
|
case MAP_STATE_MAKE_MAPPED:
|
|
g_assert (!was_mapped);
|
|
clutter_actor_set_mapped (self, TRUE);
|
|
break;
|
|
|
|
case MAP_STATE_MAKE_UNMAPPED:
|
|
g_assert (was_mapped);
|
|
clutter_actor_set_mapped (self, FALSE);
|
|
break;
|
|
|
|
case MAP_STATE_MAKE_UNREALIZED:
|
|
/* we only use MAKE_UNREALIZED in unparent,
|
|
* and unparenting a stage isn't possible.
|
|
* If someone wants to just unrealize a stage
|
|
* then clutter_actor_unrealize() doesn't
|
|
* go through this codepath.
|
|
*/
|
|
g_warning ("Trying to force unrealize stage is not allowed");
|
|
break;
|
|
}
|
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (self) &&
|
|
!CLUTTER_ACTOR_IS_VISIBLE (self) &&
|
|
!CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
{
|
|
g_warning ("Clutter toplevel of type '%s' is not visible, but "
|
|
"it is somehow still mapped",
|
|
G_OBJECT_TYPE_NAME (self));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActor *parent = priv->parent_actor;
|
|
gboolean should_be_mapped;
|
|
gboolean may_be_realized;
|
|
gboolean must_be_realized;
|
|
|
|
should_be_mapped = FALSE;
|
|
may_be_realized = TRUE;
|
|
must_be_realized = FALSE;
|
|
|
|
if (parent == NULL || change == MAP_STATE_MAKE_UNREALIZED)
|
|
{
|
|
may_be_realized = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* Maintain invariant that if parent is mapped, and we are
|
|
* visible, then we are mapped ... unless parent is a
|
|
* stage, in which case we map regardless of parent's map
|
|
* state but do require stage to be visible and realized.
|
|
*
|
|
* If parent is realized, that does not force us to be
|
|
* realized; but if parent is unrealized, that does force
|
|
* us to be unrealized.
|
|
*
|
|
* The reason we don't force children to realize with
|
|
* parents is _clutter_actor_rerealize(); if we require that
|
|
* a realized parent means children are realized, then to
|
|
* unrealize an actor we would have to unrealize its
|
|
* parents, which would end up meaning unrealizing and
|
|
* hiding the entire stage. So we allow unrealizing a
|
|
* child (as long as that child is not mapped) while that
|
|
* child still has a realized parent.
|
|
*
|
|
* Also, if we unrealize from leaf nodes to root, and
|
|
* realize from root to leaf, the invariants are never
|
|
* violated if we allow children to be unrealized
|
|
* while parents are realized.
|
|
*
|
|
* When unmapping, MAP_STATE_MAKE_UNMAPPED is specified
|
|
* to force us to unmap, even though parent is still
|
|
* mapped. This is because we're unmapping from leaf nodes
|
|
* up to root nodes.
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (self) &&
|
|
change != MAP_STATE_MAKE_UNMAPPED)
|
|
{
|
|
gboolean parent_is_visible_realized_toplevel;
|
|
|
|
parent_is_visible_realized_toplevel =
|
|
(CLUTTER_ACTOR_IS_TOPLEVEL (parent) &&
|
|
CLUTTER_ACTOR_IS_VISIBLE (parent) &&
|
|
CLUTTER_ACTOR_IS_REALIZED (parent));
|
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (parent) ||
|
|
parent_is_visible_realized_toplevel)
|
|
{
|
|
must_be_realized = TRUE;
|
|
should_be_mapped = TRUE;
|
|
}
|
|
}
|
|
|
|
/* if the actor has been set to be painted even if unmapped
|
|
* then we should map it and check for realization as well;
|
|
* this is an override for the branch of the scene graph
|
|
* which begins with this node
|
|
*/
|
|
if (priv->enable_paint_unmapped)
|
|
{
|
|
if (priv->parent_actor == NULL)
|
|
g_warning ("Attempting to map an unparented actor '%s'",
|
|
_clutter_actor_get_debug_name (self));
|
|
|
|
should_be_mapped = TRUE;
|
|
must_be_realized = TRUE;
|
|
}
|
|
|
|
if (!CLUTTER_ACTOR_IS_REALIZED (parent))
|
|
may_be_realized = FALSE;
|
|
}
|
|
|
|
if (change == MAP_STATE_MAKE_MAPPED && !should_be_mapped)
|
|
{
|
|
if (parent == NULL)
|
|
g_warning ("Attempting to map a child that does not "
|
|
"meet the necessary invariants: the actor '%s' "
|
|
"has no parent",
|
|
_clutter_actor_get_debug_name (self));
|
|
else
|
|
g_warning ("Attempting to map a child that does not "
|
|
"meet the necessary invariants: the actor '%s' "
|
|
"is parented to an unmapped actor '%s'",
|
|
_clutter_actor_get_debug_name (self),
|
|
_clutter_actor_get_debug_name (priv->parent_actor));
|
|
}
|
|
|
|
/* If in reparent, we temporarily suspend unmap and unrealize.
|
|
*
|
|
* We want to go in the order "realize, map" and "unmap, unrealize"
|
|
*/
|
|
|
|
/* Unmap */
|
|
if (!should_be_mapped && !CLUTTER_ACTOR_IN_REPARENT (self))
|
|
clutter_actor_set_mapped (self, FALSE);
|
|
|
|
/* Realize */
|
|
if (must_be_realized)
|
|
clutter_actor_realize (self);
|
|
|
|
/* if we must be realized then we may be, presumably */
|
|
g_assert (!(must_be_realized && !may_be_realized));
|
|
|
|
/* Unrealize */
|
|
if (!may_be_realized && !CLUTTER_ACTOR_IN_REPARENT (self))
|
|
clutter_actor_unrealize_not_hiding (self);
|
|
|
|
/* Map */
|
|
if (should_be_mapped)
|
|
{
|
|
if (!must_be_realized)
|
|
g_warning ("Somehow we think actor '%s' should be mapped but "
|
|
"not realized, which isn't allowed",
|
|
_clutter_actor_get_debug_name (self));
|
|
|
|
/* realization is allowed to fail (though I don't know what
|
|
* an app is supposed to do about that - shouldn't it just
|
|
* be a g_error? anyway, we have to avoid mapping if this
|
|
* happens)
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_REALIZED (self))
|
|
clutter_actor_set_mapped (self, TRUE);
|
|
}
|
|
}
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
/* check all invariants were kept */
|
|
clutter_actor_verify_map_state (self);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_map (ClutterActor *self)
|
|
{
|
|
g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
|
|
|
|
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
|
|
|
|
/* notify on parent mapped before potentially mapping
|
|
* children, so apps see a top-down notification.
|
|
*/
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
|
|
|
|
if (CLUTTER_IS_CONTAINER (self))
|
|
clutter_container_foreach_with_internals (CLUTTER_CONTAINER (self),
|
|
CLUTTER_CALLBACK (clutter_actor_map),
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_map:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Sets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly maps
|
|
* and realizes its children if they are visible. Does nothing if the
|
|
* actor is not visible.
|
|
*
|
|
* Calling this is allowed in only one case: you are implementing the
|
|
* #ClutterActor <function>map()</function> virtual function in an actor
|
|
* and you need to map the children of that actor. It is not necessary
|
|
* to call this if you implement #ClutterContainer because the default
|
|
* implementation will automatically map children of containers.
|
|
*
|
|
* When overriding map, it is mandatory to chain up to the parent
|
|
* implementation.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_map (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (self))
|
|
return;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (self))
|
|
return;
|
|
|
|
clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_unmap (ClutterActor *self)
|
|
{
|
|
g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
|
|
|
|
if (CLUTTER_IS_CONTAINER (self))
|
|
clutter_container_foreach_with_internals (CLUTTER_CONTAINER (self),
|
|
CLUTTER_CALLBACK (clutter_actor_unmap),
|
|
NULL);
|
|
|
|
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
|
|
|
|
/* unset the contents of the last paint box, so that hiding + moving +
|
|
* showing will not result in the wrong area being repainted
|
|
*/
|
|
memset (&self->priv->last_paint_box, 0, sizeof (ClutterActorBox));
|
|
self->priv->last_paint_box_valid = TRUE;
|
|
|
|
/* notify on parent mapped after potentially unmapping
|
|
* children, so apps see a bottom-up notification.
|
|
*/
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
|
|
|
|
/* relinquish keyboard focus if we were unmapped while owning it */
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
ClutterActor *stage;
|
|
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
|
|
if (stage != NULL &&
|
|
clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == self)
|
|
{
|
|
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_unmap:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Unsets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly
|
|
* unmaps its children if they were mapped.
|
|
*
|
|
* Calling this is allowed in only one case: you are implementing the
|
|
* #ClutterActor <function>unmap()</function> virtual function in an actor
|
|
* and you need to unmap the children of that actor. It is not necessary
|
|
* to call this if you implement #ClutterContainer because the default
|
|
* implementation will automatically unmap children of containers.
|
|
*
|
|
* When overriding unmap, it is mandatory to chain up to the parent
|
|
* implementation.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_unmap (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (!CLUTTER_ACTOR_IS_MAPPED (self))
|
|
return;
|
|
|
|
clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNMAPPED);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_show (ClutterActor *self)
|
|
{
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (self))
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
|
|
|
|
/* we notify on the "visible" flag in the clutter_actor_show()
|
|
* wrapper so the entire show signal emission completes first
|
|
* (?)
|
|
*/
|
|
clutter_actor_update_map_state (self, MAP_STATE_CHECK);
|
|
|
|
/* we queue a relayout unless the actor is inside a
|
|
* container that explicitly told us not to
|
|
*/
|
|
if (priv->parent_actor &&
|
|
(!(priv->parent_actor->flags & CLUTTER_ACTOR_NO_LAYOUT)))
|
|
{
|
|
/* While an actor is hidden the parent may not have
|
|
* allocated/requested so we need to start from scratch
|
|
* and avoid the short-circuiting in
|
|
* clutter_actor_queue_relayout().
|
|
*/
|
|
priv->needs_width_request = FALSE;
|
|
priv->needs_height_request = FALSE;
|
|
priv->needs_allocation = FALSE;
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
set_show_on_set_parent (ClutterActor *self,
|
|
gboolean set_show)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
set_show = !!set_show;
|
|
|
|
if (priv->show_on_set_parent == set_show)
|
|
return;
|
|
|
|
if (priv->parent_actor == NULL)
|
|
{
|
|
priv->show_on_set_parent = set_show;
|
|
_clutter_notify_by_pspec (G_OBJECT (self),
|
|
obj_props[PROP_SHOW_ON_SET_PARENT]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_show:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Flags an actor to be displayed. An actor that isn't shown will not
|
|
* be rendered on the stage.
|
|
*
|
|
* Actors are visible by default.
|
|
*
|
|
* If this function is called on an actor without a parent, the
|
|
* #ClutterActor:show-on-set-parent will be set to %TRUE as a side
|
|
* effect.
|
|
*/
|
|
void
|
|
clutter_actor_show (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
/* simple optimization */
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (self))
|
|
{
|
|
/* we still need to set the :show-on-set-parent property, in
|
|
* case show() is called on an unparented actor
|
|
*/
|
|
set_show_on_set_parent (self, TRUE);
|
|
return;
|
|
}
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
clutter_actor_verify_map_state (self);
|
|
#endif
|
|
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
set_show_on_set_parent (self, TRUE);
|
|
|
|
g_signal_emit (self, actor_signals[SHOW], 0);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
|
|
|
|
if (priv->parent_actor)
|
|
clutter_actor_queue_redraw (priv->parent_actor);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_show_all:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Calls clutter_actor_show() on all children of an actor (if any).
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_show_all (ClutterActor *self)
|
|
{
|
|
ClutterActorClass *klass;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
klass = CLUTTER_ACTOR_GET_CLASS (self);
|
|
if (klass->show_all)
|
|
klass->show_all (self);
|
|
}
|
|
|
|
void
|
|
clutter_actor_real_hide (ClutterActor *self)
|
|
{
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (self))
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
|
|
|
|
/* we notify on the "visible" flag in the clutter_actor_hide()
|
|
* wrapper so the entire hide signal emission completes first
|
|
* (?)
|
|
*/
|
|
clutter_actor_update_map_state (self, MAP_STATE_CHECK);
|
|
|
|
/* we queue a relayout unless the actor is inside a
|
|
* container that explicitly told us not to
|
|
*/
|
|
if (priv->parent_actor &&
|
|
(!(priv->parent_actor->flags & CLUTTER_ACTOR_NO_LAYOUT)))
|
|
clutter_actor_queue_relayout (priv->parent_actor);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_hide:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Flags an actor to be hidden. A hidden actor will not be
|
|
* rendered on the stage.
|
|
*
|
|
* Actors are visible by default.
|
|
*
|
|
* If this function is called on an actor without a parent, the
|
|
* #ClutterActor:show-on-set-parent property will be set to %FALSE
|
|
* as a side-effect.
|
|
*/
|
|
void
|
|
clutter_actor_hide (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
/* simple optimization */
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (self))
|
|
{
|
|
/* we still need to set the :show-on-set-parent property, in
|
|
* case hide() is called on an unparented actor
|
|
*/
|
|
set_show_on_set_parent (self, FALSE);
|
|
return;
|
|
}
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
clutter_actor_verify_map_state (self);
|
|
#endif
|
|
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
set_show_on_set_parent (self, FALSE);
|
|
|
|
g_signal_emit (self, actor_signals[HIDE], 0);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
|
|
|
|
if (priv->parent_actor)
|
|
clutter_actor_queue_redraw (priv->parent_actor);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_hide_all:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Calls clutter_actor_hide() on all child actors (if any).
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_hide_all (ClutterActor *self)
|
|
{
|
|
ClutterActorClass *klass;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
klass = CLUTTER_ACTOR_GET_CLASS (self);
|
|
if (klass->hide_all)
|
|
klass->hide_all (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_realize:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Realization informs the actor that it is attached to a stage. It
|
|
* can use this to allocate resources if it wanted to delay allocation
|
|
* until it would be rendered. However it is perfectly acceptable for
|
|
* an actor to create resources before being realized because Clutter
|
|
* only ever has a single rendering context so that actor is free to
|
|
* be moved from one stage to another.
|
|
*
|
|
* This function does nothing if the actor is already realized.
|
|
*
|
|
* Because a realized actor must have realized parent actors, calling
|
|
* clutter_actor_realize() will also realize all parents of the actor.
|
|
*
|
|
* This function does not realize child actors, except in the special
|
|
* case that realizing the stage, when the stage is visible, will
|
|
* suddenly map (and thus realize) the children of the stage.
|
|
**/
|
|
void
|
|
clutter_actor_realize (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
clutter_actor_verify_map_state (self);
|
|
#endif
|
|
|
|
if (CLUTTER_ACTOR_IS_REALIZED (self))
|
|
return;
|
|
|
|
/* To be realized, our parent actors must be realized first.
|
|
* This will only succeed if we're inside a toplevel.
|
|
*/
|
|
if (priv->parent_actor != NULL)
|
|
clutter_actor_realize (priv->parent_actor);
|
|
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
/* toplevels can be realized at any time */
|
|
}
|
|
else
|
|
{
|
|
/* "Fail" the realization if parent is missing or unrealized;
|
|
* this should really be a g_warning() not some kind of runtime
|
|
* failure; how can an app possibly recover? Instead it's a bug
|
|
* in the app and the app should get an explanatory warning so
|
|
* someone can fix it. But for now it's too hard to fix this
|
|
* because e.g. ClutterTexture needs reworking.
|
|
*/
|
|
if (priv->parent_actor == NULL ||
|
|
!CLUTTER_ACTOR_IS_REALIZED (priv->parent_actor))
|
|
return;
|
|
}
|
|
|
|
CLUTTER_NOTE (ACTOR, "Realizing actor '%s'", _clutter_actor_get_debug_name (self));
|
|
|
|
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
|
|
|
|
g_signal_emit (self, actor_signals[REALIZE], 0);
|
|
|
|
/* Stage actor is allowed to unset the realized flag again in its
|
|
* default signal handler, though that is a pathological situation.
|
|
*/
|
|
|
|
/* If realization "failed" we'll have to update child state. */
|
|
clutter_actor_update_map_state (self, MAP_STATE_CHECK);
|
|
}
|
|
|
|
void
|
|
clutter_actor_real_unrealize (ClutterActor *self)
|
|
{
|
|
/* we must be unmapped (implying our children are also unmapped) */
|
|
g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
|
|
|
|
if (CLUTTER_IS_CONTAINER (self))
|
|
clutter_container_foreach_with_internals (CLUTTER_CONTAINER (self),
|
|
CLUTTER_CALLBACK (clutter_actor_unrealize_not_hiding),
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_unrealize:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Unrealization informs the actor that it may be being destroyed or
|
|
* moved to another stage. The actor may want to destroy any
|
|
* underlying graphics resources at this point. However it is
|
|
* perfectly acceptable for it to retain the resources until the actor
|
|
* is destroyed because Clutter only ever uses a single rendering
|
|
* context and all of the graphics resources are valid on any stage.
|
|
*
|
|
* Because mapped actors must be realized, actors may not be
|
|
* unrealized if they are mapped. This function hides the actor to be
|
|
* sure it isn't mapped, an application-visible side effect that you
|
|
* may not be expecting.
|
|
*
|
|
* This function should not be called by application code.
|
|
*/
|
|
void
|
|
clutter_actor_unrealize (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (!CLUTTER_ACTOR_IS_MAPPED (self));
|
|
|
|
/* This function should not really be in the public API, because
|
|
* there isn't a good reason to call it. ClutterActor will already
|
|
* unrealize things for you when it's important to do so.
|
|
*
|
|
* If you were using clutter_actor_unrealize() in a dispose
|
|
* implementation, then don't, just chain up to ClutterActor's
|
|
* dispose.
|
|
*
|
|
* If you were using clutter_actor_unrealize() to implement
|
|
* unrealizing children of your container, then don't, ClutterActor
|
|
* will already take care of that.
|
|
*
|
|
* If you were using clutter_actor_unrealize() to re-realize to
|
|
* create your resources in a different way, then use
|
|
* _clutter_actor_rerealize() (inside Clutter) or just call your
|
|
* code that recreates your resources directly (outside Clutter).
|
|
*/
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
clutter_actor_verify_map_state (self);
|
|
#endif
|
|
|
|
clutter_actor_hide (self);
|
|
|
|
clutter_actor_unrealize_not_hiding (self);
|
|
}
|
|
|
|
/*
|
|
* clutter_actor_unrealize_not_hiding:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Unrealization informs the actor that it may be being destroyed or
|
|
* moved to another stage. The actor may want to destroy any
|
|
* underlying graphics resources at this point. However it is
|
|
* perfectly acceptable for it to retain the resources until the actor
|
|
* is destroyed because Clutter only ever uses a single rendering
|
|
* context and all of the graphics resources are valid on any stage.
|
|
*
|
|
* Because mapped actors must be realized, actors may not be
|
|
* unrealized if they are mapped. You must hide the actor or one of
|
|
* its parents before attempting to unrealize.
|
|
*
|
|
* This function is separate from clutter_actor_unrealize() because it
|
|
* does not automatically hide the actor.
|
|
* Actors need not be hidden to be unrealized, they just need to
|
|
* be unmapped. In fact we don't want to mess up the application's
|
|
* setting of the "visible" flag, so hiding is very undesirable.
|
|
*
|
|
* clutter_actor_unrealize() does a clutter_actor_hide() just for
|
|
* backward compatibility.
|
|
*/
|
|
static void
|
|
clutter_actor_unrealize_not_hiding (ClutterActor *self)
|
|
{
|
|
/* All callers of clutter_actor_unrealize_not_hiding() should have
|
|
* taken care of unmapping the actor first. This means
|
|
* all our children should also be unmapped.
|
|
*/
|
|
g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
|
|
|
|
if (!CLUTTER_ACTOR_IS_REALIZED (self))
|
|
return;
|
|
|
|
/* The default handler for the signal should recursively unrealize
|
|
* child actors. We want to unset the realized flag only _after_
|
|
* child actors are unrealized, to maintain invariants.
|
|
*/
|
|
|
|
g_signal_emit (self, actor_signals[UNREALIZE], 0);
|
|
|
|
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
|
|
}
|
|
|
|
/*
|
|
* _clutter_actor_rerealize:
|
|
* @self: A #ClutterActor
|
|
* @callback: Function to call while unrealized
|
|
* @data: data for callback
|
|
*
|
|
* If an actor is already unrealized, this just calls the callback.
|
|
*
|
|
* If it is realized, it unrealizes temporarily, calls the callback,
|
|
* and then re-realizes the actor.
|
|
*
|
|
* As a side effect, leaves all children of the actor unrealized if
|
|
* the actor was realized but not showing. This is because when we
|
|
* unrealize the actor temporarily we must unrealize its children
|
|
* (e.g. children of a stage can't be realized if stage window is
|
|
* gone). And we aren't clever enough to save the realization state of
|
|
* all children. In most cases this should not matter, because
|
|
* the children will automatically realize when they next become mapped.
|
|
*/
|
|
void
|
|
_clutter_actor_rerealize (ClutterActor *self,
|
|
ClutterCallback callback,
|
|
void *data)
|
|
{
|
|
gboolean was_mapped;
|
|
gboolean was_showing;
|
|
gboolean was_realized;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
clutter_actor_verify_map_state (self);
|
|
#endif
|
|
|
|
was_realized = CLUTTER_ACTOR_IS_REALIZED (self);
|
|
was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
|
|
was_showing = CLUTTER_ACTOR_IS_VISIBLE (self);
|
|
|
|
/* Must be unmapped to unrealize. Note we only have to hide this
|
|
* actor if it was mapped (if all parents were showing). If actor
|
|
* is merely visible (but not mapped), then that's fine, we can
|
|
* leave it visible.
|
|
*/
|
|
if (was_mapped)
|
|
clutter_actor_hide (self);
|
|
|
|
g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
|
|
|
|
/* unrealize self and all children */
|
|
clutter_actor_unrealize_not_hiding (self);
|
|
|
|
if (callback != NULL)
|
|
{
|
|
(* callback) (self, data);
|
|
}
|
|
|
|
if (was_showing)
|
|
clutter_actor_show (self); /* will realize only if mapping implies it */
|
|
else if (was_realized)
|
|
clutter_actor_realize (self); /* realize self and all parents */
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_pick (ClutterActor *self,
|
|
const ClutterColor *color)
|
|
{
|
|
/* the default implementation is just to paint a rectangle
|
|
* with the same size of the actor using the passed color
|
|
*/
|
|
if (clutter_actor_should_pick_paint (self))
|
|
{
|
|
ClutterActorBox box = { 0, };
|
|
float width, height;
|
|
|
|
clutter_actor_get_allocation_box (self, &box);
|
|
|
|
width = box.x2 - box.x1;
|
|
height = box.y2 - box.y1;
|
|
|
|
cogl_set_source_color4ub (color->red,
|
|
color->green,
|
|
color->blue,
|
|
color->alpha);
|
|
|
|
cogl_rectangle (0, 0, width, height);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_should_pick_paint:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Should be called inside the implementation of the
|
|
* #ClutterActor::pick virtual function in order to check whether
|
|
* the actor should paint itself in pick mode or not.
|
|
*
|
|
* This function should never be called directly by applications.
|
|
*
|
|
* Return value: %TRUE if the actor should paint its silhouette,
|
|
* %FALSE otherwise
|
|
*/
|
|
gboolean
|
|
clutter_actor_should_pick_paint (ClutterActor *self)
|
|
{
|
|
ClutterMainContext *context;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
context = _clutter_context_get_default ();
|
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (self) &&
|
|
(G_UNLIKELY (context->pick_mode == CLUTTER_PICK_ALL) ||
|
|
CLUTTER_ACTOR_IS_REACTIVE (self)))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_get_preferred_width (ClutterActor *self,
|
|
gfloat for_height,
|
|
gfloat *min_width_p,
|
|
gfloat *natural_width_p)
|
|
{
|
|
/* Default implementation is always 0x0, usually an actor
|
|
* using this default is relying on someone to set the
|
|
* request manually
|
|
*/
|
|
CLUTTER_NOTE (LAYOUT, "Default preferred width: 0, 0");
|
|
|
|
if (min_width_p)
|
|
*min_width_p = 0;
|
|
|
|
if (natural_width_p)
|
|
*natural_width_p = 0;
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_get_preferred_height (ClutterActor *self,
|
|
gfloat for_width,
|
|
gfloat *min_height_p,
|
|
gfloat *natural_height_p)
|
|
{
|
|
/* Default implementation is always 0x0, usually an actor
|
|
* using this default is relying on someone to set the
|
|
* request manually
|
|
*/
|
|
CLUTTER_NOTE (LAYOUT, "Default preferred height: 0, 0");
|
|
|
|
if (min_height_p)
|
|
*min_height_p = 0;
|
|
|
|
if (natural_height_p)
|
|
*natural_height_p = 0;
|
|
}
|
|
|
|
static void
|
|
clutter_actor_store_old_geometry (ClutterActor *self,
|
|
ClutterActorBox *box)
|
|
{
|
|
*box = self->priv->allocation;
|
|
}
|
|
|
|
static inline void
|
|
clutter_actor_notify_if_geometry_changed (ClutterActor *self,
|
|
const ClutterActorBox *old)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
GObject *obj = G_OBJECT (self);
|
|
|
|
g_object_freeze_notify (obj);
|
|
|
|
/* to avoid excessive requisition or allocation cycles we
|
|
* use the cached values.
|
|
*
|
|
* - if we don't have an allocation we assume that we need
|
|
* to notify anyway
|
|
* - if we don't have a width or a height request we notify
|
|
* width and height
|
|
* - if we have a valid allocation then we check the old
|
|
* bounding box with the current allocation and we notify
|
|
* the changes
|
|
*/
|
|
if (priv->needs_allocation)
|
|
{
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_X]);
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_Y]);
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
|
|
}
|
|
else if (priv->needs_width_request || priv->needs_height_request)
|
|
{
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
|
|
}
|
|
else
|
|
{
|
|
gfloat xu, yu;
|
|
gfloat widthu, heightu;
|
|
|
|
xu = priv->allocation.x1;
|
|
yu = priv->allocation.y1;
|
|
widthu = priv->allocation.x2 - priv->allocation.x1;
|
|
heightu = priv->allocation.y2 - priv->allocation.y1;
|
|
|
|
if (xu != old->x1)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_X]);
|
|
|
|
if (yu != old->y1)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_Y]);
|
|
|
|
if (widthu != (old->x2 - old->x1))
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
|
|
|
|
if (heightu != (old->y2 - old->y1))
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
|
|
}
|
|
|
|
g_object_thaw_notify (obj);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_allocate (ClutterActor *self,
|
|
const ClutterActorBox *box,
|
|
ClutterAllocationFlags flags)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
gboolean x1_changed, y1_changed, x2_changed, y2_changed;
|
|
gboolean flags_changed;
|
|
ClutterActorBox old_alloc = { 0, };
|
|
|
|
clutter_actor_store_old_geometry (self, &old_alloc);
|
|
|
|
x1_changed = priv->allocation.x1 != box->x1;
|
|
y1_changed = priv->allocation.y1 != box->y1;
|
|
x2_changed = priv->allocation.x2 != box->x2;
|
|
y2_changed = priv->allocation.y2 != box->y2;
|
|
|
|
flags_changed = priv->allocation_flags != flags;
|
|
|
|
priv->allocation = *box;
|
|
priv->allocation_flags = flags;
|
|
priv->needs_allocation = FALSE;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
if (x1_changed || y1_changed || x2_changed || y2_changed || flags_changed)
|
|
{
|
|
CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed",
|
|
_clutter_actor_get_debug_name (self));
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ALLOCATION]);
|
|
|
|
/* we also emit the ::allocation-changed signal for people
|
|
* that wish to track the allocation flags
|
|
*/
|
|
g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
|
|
&priv->allocation,
|
|
flags);
|
|
}
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old_alloc);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
static void
|
|
_clutter_actor_signal_queue_redraw (ClutterActor *self,
|
|
ClutterActor *origin)
|
|
{
|
|
/* no point in queuing a redraw on a destroyed actor */
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
return;
|
|
|
|
/* NB: We can't bail out early here if the actor is hidden in case
|
|
* the actor bas been cloned. In this case the clone will need to
|
|
* receive the signal so it can queue its own redraw.
|
|
*/
|
|
|
|
/* calls klass->queue_redraw in default handler */
|
|
g_signal_emit (self, actor_signals[QUEUE_REDRAW], 0, origin);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_queue_redraw (ClutterActor *self,
|
|
ClutterActor *origin)
|
|
{
|
|
ClutterActor *parent;
|
|
|
|
CLUTTER_NOTE (PAINT, "Redraw queued on '%s' (from: '%s')",
|
|
_clutter_actor_get_debug_name (self),
|
|
origin != NULL ? _clutter_actor_get_debug_name (origin)
|
|
: "same actor");
|
|
|
|
/* no point in queuing a redraw on a destroyed actor */
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
return;
|
|
|
|
/* If the actor isn't visible, we still had to emit the signal
|
|
* to allow for a ClutterClone, but the appearance of the parent
|
|
* won't change so we don't have to propagate up the hierarchy.
|
|
*/
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (self))
|
|
return;
|
|
|
|
/* Although we could determine here that a full stage redraw
|
|
* has already been queued and immediately bail out, we actually
|
|
* guarantee that we will propagate a queue-redraw signal to our
|
|
* parent at least once so that it's possible to implement a
|
|
* container that tracks which of its children have queued a
|
|
* redraw.
|
|
*/
|
|
if (self->priv->propagated_one_redraw)
|
|
{
|
|
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
|
|
if (stage != NULL &&
|
|
_clutter_stage_has_full_redraw_queued (CLUTTER_STAGE (stage)))
|
|
return;
|
|
}
|
|
|
|
self->priv->propagated_one_redraw = TRUE;
|
|
|
|
/* notify parents, if they are all visible eventually we'll
|
|
* queue redraw on the stage, which queues the redraw idle.
|
|
*/
|
|
parent = clutter_actor_get_parent (self);
|
|
if (parent != NULL)
|
|
{
|
|
/* this will go up recursively */
|
|
_clutter_actor_signal_queue_redraw (parent, origin);
|
|
}
|
|
}
|
|
|
|
void
|
|
clutter_actor_real_queue_relayout (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
/* no point in queueing a redraw on a destroyed actor */
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
return;
|
|
|
|
priv->needs_width_request = TRUE;
|
|
priv->needs_height_request = TRUE;
|
|
priv->needs_allocation = TRUE;
|
|
|
|
/* reset the cached size requests */
|
|
memset (priv->width_requests, 0,
|
|
N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
|
|
memset (priv->height_requests, 0,
|
|
N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
|
|
|
|
/* We need to go all the way up the hierarchy */
|
|
if (priv->parent_actor != NULL)
|
|
_clutter_actor_queue_only_relayout (priv->parent_actor);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_apply_relative_transform_to_point:
|
|
* @self: A #ClutterActor
|
|
* @ancestor: (allow-none): A #ClutterActor ancestor, or %NULL to use the
|
|
* default #ClutterStage
|
|
* @point: A point as #ClutterVertex
|
|
* @vertex: The translated #ClutterVertex
|
|
*
|
|
* Transforms @point in coordinates relative to the actor into
|
|
* ancestor-relative coordinates using the relevant transform
|
|
* stack (i.e. scale, rotation, etc).
|
|
*
|
|
* If @ancestor is %NULL the ancestor will be the #ClutterStage. In
|
|
* this case, the coordinates returned will be the coordinates on
|
|
* the stage before the projection is applied. This is different from
|
|
* the behaviour of clutter_actor_apply_transform_to_point().
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_apply_relative_transform_to_point (ClutterActor *self,
|
|
ClutterActor *ancestor,
|
|
const ClutterVertex *point,
|
|
ClutterVertex *vertex)
|
|
{
|
|
gfloat w;
|
|
CoglMatrix matrix;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
|
|
g_return_if_fail (point != NULL);
|
|
g_return_if_fail (vertex != NULL);
|
|
|
|
*vertex = *point;
|
|
w = 1.0;
|
|
|
|
if (ancestor == NULL)
|
|
ancestor = _clutter_actor_get_stage_internal (self);
|
|
|
|
if (ancestor == NULL)
|
|
{
|
|
*vertex = *point;
|
|
return;
|
|
}
|
|
|
|
_clutter_actor_get_relative_modelview (self, ancestor, &matrix);
|
|
cogl_matrix_transform_point (&matrix, &vertex->x, &vertex->y, &vertex->z, &w);
|
|
}
|
|
|
|
static gboolean
|
|
_clutter_actor_fully_transform_vertices (ClutterActor *self,
|
|
const ClutterVertex *vertices_in,
|
|
ClutterVertex *vertices_out,
|
|
int n_vertices)
|
|
{
|
|
ClutterActor *stage;
|
|
CoglMatrix modelview;
|
|
CoglMatrix projection;
|
|
int viewport[4];
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
/* NB: _clutter_actor_apply_modelview_transform_recursive will never
|
|
* include the transformation between stage coordinates and OpenGL
|
|
* window coordinates, we have to explicitly use the
|
|
* stage->apply_transform to get that... */
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
|
|
/* We really can't do anything meaningful in this case so don't try
|
|
* to do any transform */
|
|
if (stage == NULL)
|
|
return FALSE;
|
|
|
|
/* Setup the modelview */
|
|
cogl_matrix_init_identity (&modelview);
|
|
_clutter_actor_apply_modelview_transform (stage, &modelview);
|
|
_clutter_actor_apply_modelview_transform_recursive (self, stage, &modelview);
|
|
|
|
/* Fetch the projection and viewport */
|
|
_clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
|
|
_clutter_stage_get_viewport (CLUTTER_STAGE (stage),
|
|
&viewport[0],
|
|
&viewport[1],
|
|
&viewport[2],
|
|
&viewport[3]);
|
|
|
|
_clutter_util_fully_transform_vertices (&modelview,
|
|
&projection,
|
|
viewport,
|
|
vertices_in,
|
|
vertices_out,
|
|
n_vertices);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_apply_transform_to_point:
|
|
* @self: A #ClutterActor
|
|
* @point: A point as #ClutterVertex
|
|
* @vertex: The translated #ClutterVertex
|
|
*
|
|
* Transforms @point in coordinates relative to the actor
|
|
* into screen-relative coordinates with the current actor
|
|
* transformation (i.e. scale, rotation, etc)
|
|
*
|
|
* Since: 0.4
|
|
**/
|
|
void
|
|
clutter_actor_apply_transform_to_point (ClutterActor *self,
|
|
const ClutterVertex *point,
|
|
ClutterVertex *vertex)
|
|
{
|
|
g_return_if_fail (point != NULL);
|
|
g_return_if_fail (vertex != NULL);
|
|
_clutter_actor_fully_transform_vertices (self, point, vertex, 1);
|
|
}
|
|
|
|
/* _clutter_actor_get_relative_modelview:
|
|
*
|
|
* Retrieves the modelview transformation relative to some ancestor
|
|
* actor, or the stage if NULL is given for the ancestor.
|
|
*
|
|
* Note: This will never include the transformations from
|
|
* stage::apply_transform since that would give you a modelview
|
|
* transform relative to the OpenGL window coordinate space that the
|
|
* stage lies within.
|
|
*
|
|
* If you need to do a full modelview + projective transform and get
|
|
* to window coordinates then you should explicitly apply the stage
|
|
* transform to an identity matrix and use
|
|
* _clutter_actor_apply_modelview_transform like:
|
|
*
|
|
* cogl_matrix_init_identity (&mtx);
|
|
* stage = _clutter_actor_get_stage_internal (self);
|
|
* _clutter_actor_apply_modelview_transform (stage, &mtx);
|
|
*/
|
|
/* FIXME: We should be caching the stage relative modelview along with the
|
|
* actor itself */
|
|
static void
|
|
_clutter_actor_get_relative_modelview (ClutterActor *self,
|
|
ClutterActor *ancestor,
|
|
CoglMatrix *matrix)
|
|
{
|
|
g_return_if_fail (ancestor != NULL);
|
|
|
|
cogl_matrix_init_identity (matrix);
|
|
|
|
_clutter_actor_apply_modelview_transform_recursive (self, ancestor, matrix);
|
|
}
|
|
|
|
/* Project the given @box into stage window coordinates, writing the
|
|
* transformed vertices to @verts[]. */
|
|
static gboolean
|
|
_clutter_actor_transform_and_project_box (ClutterActor *self,
|
|
const ClutterActorBox *box,
|
|
ClutterVertex verts[])
|
|
{
|
|
ClutterVertex box_vertices[4];
|
|
|
|
box_vertices[0].x = box->x1;
|
|
box_vertices[0].y = box->y1;
|
|
box_vertices[0].z = 0;
|
|
box_vertices[1].x = box->x2;
|
|
box_vertices[1].y = box->y1;
|
|
box_vertices[1].z = 0;
|
|
box_vertices[2].x = box->x1;
|
|
box_vertices[2].y = box->y2;
|
|
box_vertices[2].z = 0;
|
|
box_vertices[3].x = box->x2;
|
|
box_vertices[3].y = box->y2;
|
|
box_vertices[3].z = 0;
|
|
|
|
return
|
|
_clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_allocation_vertices:
|
|
* @self: A #ClutterActor
|
|
* @ancestor: (allow-none): A #ClutterActor to calculate the vertices
|
|
* against, or %NULL to use the #ClutterStage
|
|
* @verts: (out) (array fixed-size=4) (element-type Clutter.Vertex): return
|
|
* location for an array of 4 #ClutterVertex in which to store the result
|
|
*
|
|
* Calculates the transformed coordinates of the four corners of the
|
|
* actor in the plane of @ancestor. The returned vertices relate to
|
|
* the #ClutterActorBox coordinates as follows:
|
|
* <itemizedlist>
|
|
* <listitem><para>@verts[0] contains (x1, y1)</para></listitem>
|
|
* <listitem><para>@verts[1] contains (x2, y1)</para></listitem>
|
|
* <listitem><para>@verts[2] contains (x1, y2)</para></listitem>
|
|
* <listitem><para>@verts[3] contains (x2, y2)</para></listitem>
|
|
* </itemizedlist>
|
|
*
|
|
* If @ancestor is %NULL the ancestor will be the #ClutterStage. In
|
|
* this case, the coordinates returned will be the coordinates on
|
|
* the stage before the projection is applied. This is different from
|
|
* the behaviour of clutter_actor_get_abs_allocation_vertices().
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_get_allocation_vertices (ClutterActor *self,
|
|
ClutterActor *ancestor,
|
|
ClutterVertex verts[])
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorBox box;
|
|
ClutterVertex vertices[4];
|
|
CoglMatrix modelview;
|
|
float w;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
|
|
|
|
if (ancestor == NULL)
|
|
ancestor = _clutter_actor_get_stage_internal (self);
|
|
|
|
/* Fallback to a NOP transform if the actor isn't parented under a
|
|
* stage. */
|
|
if (ancestor == NULL)
|
|
ancestor = self;
|
|
|
|
priv = self->priv;
|
|
|
|
/* if the actor needs to be allocated we force a relayout, so that
|
|
* we will have valid values to use in the transformations */
|
|
if (priv->needs_allocation)
|
|
{
|
|
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
|
|
if (stage)
|
|
_clutter_stage_maybe_relayout (stage);
|
|
else
|
|
{
|
|
box.x1 = box.y1 = 0;
|
|
/* The result isn't really meaningful in this case but at
|
|
* least try to do something *vaguely* reasonable... */
|
|
clutter_actor_get_size (self, &box.x2, &box.y2);
|
|
}
|
|
}
|
|
|
|
clutter_actor_get_allocation_box (self, &box);
|
|
|
|
vertices[0].x = box.x1;
|
|
vertices[0].y = box.y1;
|
|
vertices[0].z = 0;
|
|
vertices[1].x = box.x2;
|
|
vertices[1].y = box.y1;
|
|
vertices[1].z = 0;
|
|
vertices[2].x = box.x1;
|
|
vertices[2].y = box.y2;
|
|
vertices[2].z = 0;
|
|
vertices[3].x = box.x2;
|
|
vertices[3].y = box.y2;
|
|
vertices[3].z = 0;
|
|
|
|
_clutter_actor_get_relative_modelview (self, ancestor, &modelview);
|
|
|
|
w = 1;
|
|
cogl_matrix_transform_point (&modelview,
|
|
&vertices[0].x, &vertices[0].y, &vertices[0].z,
|
|
&w);
|
|
cogl_matrix_transform_point (&modelview,
|
|
&vertices[1].x, &vertices[1].y, &vertices[1].z,
|
|
&w);
|
|
cogl_matrix_transform_point (&modelview,
|
|
&vertices[2].x, &vertices[2].y, &vertices[2].z,
|
|
&w);
|
|
cogl_matrix_transform_point (&modelview,
|
|
&vertices[3].x, &vertices[3].y, &vertices[3].z,
|
|
&w);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_abs_allocation_vertices:
|
|
* @self: A #ClutterActor
|
|
* @verts: (out) (array fixed-size=4): Pointer to a location of an array
|
|
* of 4 #ClutterVertex where to store the result.
|
|
*
|
|
* Calculates the transformed screen coordinates of the four corners of
|
|
* the actor; the returned vertices relate to the #ClutterActorBox
|
|
* coordinates as follows:
|
|
* <itemizedlist>
|
|
* <listitem><para>v[0] contains (x1, y1)</para></listitem>
|
|
* <listitem><para>v[1] contains (x2, y1)</para></listitem>
|
|
* <listitem><para>v[2] contains (x1, y2)</para></listitem>
|
|
* <listitem><para>v[3] contains (x2, y2)</para></listitem>
|
|
* </itemizedlist>
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_actor_get_abs_allocation_vertices (ClutterActor *self,
|
|
ClutterVertex verts[])
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorBox actor_space_allocation;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
/* if the actor needs to be allocated we force a relayout, so that
|
|
* the actor allocation box will be valid for
|
|
* _clutter_actor_transform_and_project_box()
|
|
*/
|
|
if (priv->needs_allocation)
|
|
{
|
|
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
|
|
/* There's nothing meaningful we can do now */
|
|
if (!stage)
|
|
return;
|
|
|
|
_clutter_stage_maybe_relayout (stage);
|
|
}
|
|
|
|
/* NB: _clutter_actor_transform_and_project_box expects a box in the actor's
|
|
* own coordinate space... */
|
|
actor_space_allocation.x1 = 0;
|
|
actor_space_allocation.y1 = 0;
|
|
actor_space_allocation.x2 = priv->allocation.x2 - priv->allocation.x1;
|
|
actor_space_allocation.y2 = priv->allocation.y2 - priv->allocation.y1;
|
|
_clutter_actor_transform_and_project_box (self,
|
|
&actor_space_allocation,
|
|
verts);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_real_apply_transform (ClutterActor *self,
|
|
CoglMatrix *matrix)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
cogl_matrix_translate (matrix,
|
|
priv->allocation.x1,
|
|
priv->allocation.y1,
|
|
0.0);
|
|
|
|
if (priv->z)
|
|
cogl_matrix_translate (matrix, 0, 0, priv->z);
|
|
|
|
/*
|
|
* because the rotation involves translations, we must scale before
|
|
* applying the rotations (if we apply the scale after the rotations,
|
|
* the translations included in the rotation are not scaled and so the
|
|
* entire object will move on the screen as a result of rotating it).
|
|
*/
|
|
if (priv->scale_x != 1.0 || priv->scale_y != 1.0)
|
|
{
|
|
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
|
|
&priv->scale_center,
|
|
cogl_matrix_scale (matrix,
|
|
priv->scale_x,
|
|
priv->scale_y,
|
|
1.0));
|
|
}
|
|
|
|
if (priv->rzang)
|
|
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
|
|
&priv->rz_center,
|
|
cogl_matrix_rotate (matrix,
|
|
priv->rzang,
|
|
0, 0, 1.0));
|
|
|
|
if (priv->ryang)
|
|
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
|
|
&priv->ry_center,
|
|
cogl_matrix_rotate (matrix,
|
|
priv->ryang,
|
|
0, 1.0, 0));
|
|
|
|
if (priv->rxang)
|
|
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
|
|
&priv->rx_center,
|
|
cogl_matrix_rotate (matrix,
|
|
priv->rxang,
|
|
1.0, 0, 0));
|
|
|
|
if (!clutter_anchor_coord_is_zero (&priv->anchor))
|
|
{
|
|
gfloat x, y, z;
|
|
|
|
clutter_anchor_coord_get_units (self, &priv->anchor, &x, &y, &z);
|
|
cogl_matrix_translate (matrix, -x, -y, -z);
|
|
}
|
|
}
|
|
|
|
/* Applies the transforms associated with this actor to the given
|
|
* matrix. */
|
|
void
|
|
_clutter_actor_apply_modelview_transform (ClutterActor *self,
|
|
CoglMatrix *matrix)
|
|
{
|
|
CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix);
|
|
}
|
|
|
|
static gboolean
|
|
_clutter_actor_effects_pre_paint (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
const GList *effects, *l;
|
|
gboolean was_pre_painted = FALSE;
|
|
|
|
priv->current_effect = NULL;
|
|
|
|
effects = _clutter_meta_group_peek_metas (priv->effects);
|
|
for (l = effects; l != NULL; l = l->next)
|
|
{
|
|
ClutterEffect *effect = l->data;
|
|
ClutterActorMeta *meta = l->data;
|
|
|
|
if (!clutter_actor_meta_get_enabled (meta))
|
|
continue;
|
|
|
|
priv->current_effect = l->data;
|
|
|
|
was_pre_painted |= _clutter_effect_pre_paint (effect);
|
|
}
|
|
|
|
priv->current_effect = NULL;
|
|
|
|
return was_pre_painted;
|
|
}
|
|
|
|
static void
|
|
_clutter_actor_effects_post_paint (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
const GList *effects, *l;
|
|
|
|
priv->current_effect = NULL;
|
|
|
|
/* we walk the list backwards, to unwind the post-paint order */
|
|
effects = _clutter_meta_group_peek_metas (priv->effects);
|
|
for (l = g_list_last ((GList *) effects); l != NULL; l = l->prev)
|
|
{
|
|
ClutterEffect *effect = l->data;
|
|
ClutterActorMeta *meta = l->data;
|
|
|
|
if (!clutter_actor_meta_get_enabled (meta))
|
|
continue;
|
|
|
|
priv->current_effect = l->data;
|
|
|
|
_clutter_effect_post_paint (effect);
|
|
}
|
|
|
|
priv->current_effect = NULL;
|
|
}
|
|
|
|
/* Recursively applies the transforms associated with this actor and
|
|
* its ancestors to the given matrix. Use NULL if you want this
|
|
* to go all the way down to the stage.
|
|
*/
|
|
void
|
|
_clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
|
|
ClutterActor *ancestor,
|
|
CoglMatrix *matrix)
|
|
{
|
|
ClutterActor *parent;
|
|
|
|
/* Note we terminate before ever calling stage->apply_transform()
|
|
* since that would conceptually be relative to the underlying
|
|
* window OpenGL coordinates so we'd need a special @ancestor
|
|
* value to represent the fake parent of the stage. */
|
|
if (self == ancestor)
|
|
return;
|
|
|
|
parent = clutter_actor_get_parent (self);
|
|
|
|
if (parent != NULL)
|
|
_clutter_actor_apply_modelview_transform_recursive (parent, ancestor,
|
|
matrix);
|
|
|
|
_clutter_actor_apply_modelview_transform (self, matrix);
|
|
}
|
|
|
|
static void
|
|
_clutter_actor_draw_paint_volume (ClutterActor *self)
|
|
{
|
|
static CoglMaterial *outline = NULL;
|
|
CoglHandle vbo;
|
|
ClutterPaintVolume *pv;
|
|
gboolean free_fake_pv;
|
|
ClutterPaintVolume fake_pv;
|
|
ClutterVertex line_ends[12 * 2];
|
|
int n_vertices;
|
|
PangoLayout *layout;
|
|
CoglColor color;
|
|
|
|
if (outline == NULL)
|
|
outline = cogl_material_new ();
|
|
|
|
pv = _clutter_actor_get_paint_volume_mutable (self);
|
|
if (!pv)
|
|
{
|
|
gfloat width, height;
|
|
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
|
|
_clutter_paint_volume_init_static (stage, &fake_pv);
|
|
free_fake_pv = TRUE;
|
|
|
|
clutter_actor_get_size (self, &width, &height);
|
|
clutter_paint_volume_set_width (&fake_pv, width);
|
|
clutter_paint_volume_set_height (&fake_pv, height);
|
|
|
|
pv = &fake_pv;
|
|
cogl_color_init_from_4f (&color, 0, 0, 1, 1);
|
|
}
|
|
else
|
|
{
|
|
cogl_color_init_from_4f (&color, 0, 1, 0, 1);
|
|
free_fake_pv = FALSE;
|
|
}
|
|
|
|
_clutter_paint_volume_complete (pv);
|
|
|
|
n_vertices = pv->is_2d ? 4 * 2 : 12 * 2;
|
|
|
|
/* Front face */
|
|
line_ends[0] = pv->vertices[0]; line_ends[1] = pv->vertices[1];
|
|
line_ends[2] = pv->vertices[1]; line_ends[3] = pv->vertices[2];
|
|
line_ends[4] = pv->vertices[2]; line_ends[5] = pv->vertices[3];
|
|
line_ends[6] = pv->vertices[3]; line_ends[7] = pv->vertices[0];
|
|
|
|
if (!pv->is_2d)
|
|
{
|
|
/* Back face */
|
|
line_ends[8] = pv->vertices[4]; line_ends[9] = pv->vertices[5];
|
|
line_ends[10] = pv->vertices[5]; line_ends[11] = pv->vertices[6];
|
|
line_ends[12] = pv->vertices[6]; line_ends[13] = pv->vertices[7];
|
|
line_ends[14] = pv->vertices[7]; line_ends[15] = pv->vertices[4];
|
|
|
|
/* Lines connecting front face to back face */
|
|
line_ends[16] = pv->vertices[0]; line_ends[17] = pv->vertices[4];
|
|
line_ends[18] = pv->vertices[1]; line_ends[19] = pv->vertices[5];
|
|
line_ends[20] = pv->vertices[2]; line_ends[21] = pv->vertices[6];
|
|
line_ends[22] = pv->vertices[3]; line_ends[23] = pv->vertices[7];
|
|
}
|
|
|
|
vbo = cogl_vertex_buffer_new (n_vertices);
|
|
cogl_vertex_buffer_add (vbo,
|
|
"gl_Vertex",
|
|
3, /* n_components */
|
|
COGL_ATTRIBUTE_TYPE_FLOAT,
|
|
FALSE, /* normalized */
|
|
0, /* stride */
|
|
line_ends);
|
|
|
|
cogl_material_set_color (outline, &color);
|
|
cogl_set_source (outline);
|
|
cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_LINES,
|
|
0 , n_vertices);
|
|
cogl_object_unref (vbo);
|
|
|
|
layout = pango_layout_new (clutter_actor_get_pango_context (self));
|
|
pango_layout_set_text (layout, G_OBJECT_TYPE_NAME (self), -1);
|
|
cogl_pango_render_layout (layout,
|
|
pv->vertices[0].x,
|
|
pv->vertices[0].y,
|
|
&color,
|
|
0);
|
|
g_object_unref (layout);
|
|
|
|
if (free_fake_pv)
|
|
clutter_paint_volume_free (&fake_pv);
|
|
}
|
|
|
|
/* Returns TRUE if the actor can be ignored */
|
|
static gboolean
|
|
cull_actor (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActor *stage;
|
|
const ClutterGeometry *stage_clip;
|
|
ClutterActorBox *box;
|
|
ClutterGeometry paint_geom;
|
|
|
|
if (G_UNLIKELY (priv->last_paint_box_valid == FALSE))
|
|
return FALSE;
|
|
|
|
if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING))
|
|
return FALSE;
|
|
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
stage_clip = _clutter_stage_get_clip (CLUTTER_STAGE (stage));
|
|
if (G_UNLIKELY (!stage_clip))
|
|
return FALSE;
|
|
|
|
/* XXX: It might be better if _get_paint_box returned a
|
|
* ClutterGeometry instead. */
|
|
box = &priv->last_paint_box;
|
|
paint_geom.x = box->x1;
|
|
paint_geom.y = box->y1;
|
|
paint_geom.width = box->x2 - box->x1;
|
|
paint_geom.height = box->y2 - box->y1;
|
|
|
|
if (!clutter_geometry_intersects (stage_clip, &paint_geom))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_paint:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Renders the actor to display.
|
|
*
|
|
* This function should not be called directly by applications.
|
|
* Call clutter_actor_queue_redraw() to queue paints, instead.
|
|
*
|
|
* This function is context-aware, and will either cause a
|
|
* regular paint or a pick paint.
|
|
*
|
|
* This function will emit the #ClutterActor::paint signal or
|
|
* the #ClutterActor::pick signal, depending on the context.
|
|
*
|
|
* This function does not paint the actor if the actor is set to 0,
|
|
* unless it is performing a pick paint.
|
|
*/
|
|
void
|
|
clutter_actor_paint (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterMainContext *context;
|
|
gboolean clip_set = FALSE;
|
|
CLUTTER_STATIC_COUNTER (actor_paint_counter,
|
|
"Actor real-paint counter",
|
|
"Increments each time any actor is painted",
|
|
0 /* no application private data */);
|
|
CLUTTER_STATIC_COUNTER (actor_pick_counter,
|
|
"Actor pick-paint counter",
|
|
"Increments each time any actor is painted "
|
|
"for picking",
|
|
0 /* no application private data */);
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
return;
|
|
|
|
priv = self->priv;
|
|
|
|
context = _clutter_context_get_default ();
|
|
|
|
/* It's an important optimization that we consider painting of
|
|
* actors with 0 opacity to be a NOP... */
|
|
if (context->pick_mode == CLUTTER_PICK_NONE &&
|
|
/* ignore top-levels, since they might be transparent */
|
|
!CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
|
|
/* If the actor is being painted from a clone then check the
|
|
clone's opacity instead */
|
|
(priv->opacity_parent ? priv->opacity_parent->priv : priv)->opacity == 0)
|
|
{
|
|
priv->propagated_one_redraw = FALSE;
|
|
return;
|
|
}
|
|
|
|
/* if we aren't paintable (not in a toplevel with all
|
|
* parents paintable) then do nothing.
|
|
*/
|
|
if (!CLUTTER_ACTOR_IS_MAPPED (self))
|
|
return;
|
|
|
|
/* mark that we are in the paint process */
|
|
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT);
|
|
|
|
cogl_push_matrix();
|
|
|
|
if (priv->enable_model_view_transform)
|
|
{
|
|
CoglMatrix matrix;
|
|
/* XXX: It could be better to cache the modelview with the actor
|
|
* instead of progressively building up the transformations on
|
|
* the matrix stack every time we paint. */
|
|
cogl_get_modelview_matrix (&matrix);
|
|
_clutter_actor_apply_modelview_transform (self, &matrix);
|
|
cogl_set_modelview_matrix (&matrix);
|
|
}
|
|
|
|
if (priv->has_clip)
|
|
{
|
|
cogl_clip_push_rectangle (priv->clip[0],
|
|
priv->clip[1],
|
|
priv->clip[0] + priv->clip[2],
|
|
priv->clip[1] + priv->clip[3]);
|
|
clip_set = TRUE;
|
|
}
|
|
else if (priv->clip_to_allocation)
|
|
{
|
|
gfloat width, height;
|
|
|
|
width = priv->allocation.x2 - priv->allocation.x1;
|
|
height = priv->allocation.y2 - priv->allocation.y1;
|
|
|
|
cogl_clip_push_rectangle (0, 0, width, height);
|
|
clip_set = TRUE;
|
|
}
|
|
|
|
if (context->pick_mode == CLUTTER_PICK_NONE)
|
|
{
|
|
gboolean effect_painted = FALSE;
|
|
gboolean need_paint_box;
|
|
|
|
CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter);
|
|
|
|
if (G_UNLIKELY (clutter_paint_debug_flags &
|
|
CLUTTER_DEBUG_DISABLE_CULLING &&
|
|
clutter_paint_debug_flags &
|
|
CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS))
|
|
need_paint_box = FALSE;
|
|
else
|
|
need_paint_box = TRUE;
|
|
|
|
/* We save the current paint box so that the next time the
|
|
* actor queues a redraw we can constrain the redraw to just
|
|
* cover the union of the new bounding box and the old.
|
|
*
|
|
* We also fetch the current paint box to perform culling so we
|
|
* can avoid painting actors outside the current clip region.
|
|
*
|
|
* XXX: We are starting to do a lot of vertex transforms on
|
|
* the CPU in a typical paint, so at some point we should
|
|
* audit these and consider caching some things.
|
|
*
|
|
* XXX: We should consider doing all our culling in the
|
|
* stage's model space using PaintVolumes so we don't have
|
|
* to project actor paint volumes all the way into window
|
|
* coordinates!
|
|
* XXX: To do this we also need a way to store an
|
|
* "absolute paint volume" in some way. Currently the
|
|
* paint volumes are defined relative to a referenced
|
|
* actor's coordinates, but we'd need to be able to cache
|
|
* the last paint volume used with the actor's *current*
|
|
* modelview and either with a specific projection matrix
|
|
* or we'd need to be able to invalidate paint-volumes on
|
|
* projection changes.
|
|
*/
|
|
if (G_LIKELY (need_paint_box) &&
|
|
clutter_actor_get_paint_box (self, &priv->last_paint_box))
|
|
priv->last_paint_box_valid = TRUE;
|
|
else
|
|
priv->last_paint_box_valid = FALSE;
|
|
|
|
if (cull_actor (self))
|
|
goto done;
|
|
|
|
if (priv->effects != NULL)
|
|
effect_painted = _clutter_actor_effects_pre_paint (self);
|
|
else if (priv->shader_data != NULL)
|
|
clutter_actor_shader_pre_paint (self, FALSE);
|
|
|
|
priv->propagated_one_redraw = FALSE;
|
|
g_signal_emit (self, actor_signals[PAINT], 0);
|
|
|
|
if (effect_painted)
|
|
_clutter_actor_effects_post_paint (self);
|
|
else if (priv->shader_data != NULL)
|
|
clutter_actor_shader_post_paint (self);
|
|
|
|
if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES))
|
|
_clutter_actor_draw_paint_volume (self);
|
|
}
|
|
else
|
|
{
|
|
ClutterColor col = { 0, };
|
|
|
|
CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_pick_counter);
|
|
|
|
_clutter_id_to_color (clutter_actor_get_gid (self), &col);
|
|
|
|
/* Actor will then paint silhouette of itself in supplied
|
|
* color. See clutter_stage_get_actor_at_pos() for where
|
|
* picking is enabled.
|
|
*/
|
|
g_signal_emit (self, actor_signals[PICK], 0, &col);
|
|
}
|
|
|
|
done:
|
|
if (clip_set)
|
|
cogl_clip_pop();
|
|
|
|
cogl_pop_matrix();
|
|
|
|
/* paint sequence complete */
|
|
CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT);
|
|
}
|
|
|
|
/* internal helper function set the rotation angle without affecting
|
|
the center point
|
|
*/
|
|
static void
|
|
clutter_actor_set_rotation_internal (ClutterActor *self,
|
|
ClutterRotateAxis axis,
|
|
gdouble angle)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
switch (axis)
|
|
{
|
|
case CLUTTER_X_AXIS:
|
|
priv->rxang = angle;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_ANGLE_X]);
|
|
break;
|
|
|
|
case CLUTTER_Y_AXIS:
|
|
priv->ryang = angle;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_ANGLE_Y]);
|
|
break;
|
|
|
|
case CLUTTER_Z_AXIS:
|
|
priv->rzang = angle;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_ANGLE_Z]);
|
|
break;
|
|
}
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterActor *actor = CLUTTER_ACTOR (object);
|
|
ClutterActorPrivate *priv = actor->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_X:
|
|
clutter_actor_set_x (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_Y:
|
|
clutter_actor_set_y (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_WIDTH:
|
|
clutter_actor_set_width (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_HEIGHT:
|
|
clutter_actor_set_height (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_FIXED_X:
|
|
clutter_actor_set_x (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_FIXED_Y:
|
|
clutter_actor_set_y (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_FIXED_POSITION_SET:
|
|
clutter_actor_set_fixed_position_set (actor, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_MIN_WIDTH:
|
|
clutter_actor_set_min_width (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_MIN_HEIGHT:
|
|
clutter_actor_set_min_height (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_NATURAL_WIDTH:
|
|
clutter_actor_set_natural_width (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_NATURAL_HEIGHT:
|
|
clutter_actor_set_natural_height (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_MIN_WIDTH_SET:
|
|
clutter_actor_set_min_width_set (actor, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_MIN_HEIGHT_SET:
|
|
clutter_actor_set_min_height_set (actor, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_NATURAL_WIDTH_SET:
|
|
clutter_actor_set_natural_width_set (actor, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_NATURAL_HEIGHT_SET:
|
|
clutter_actor_set_natural_height_set (actor, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_REQUEST_MODE:
|
|
clutter_actor_set_request_mode (actor, g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_DEPTH:
|
|
clutter_actor_set_depth (actor, g_value_get_float (value));
|
|
break;
|
|
|
|
case PROP_OPACITY:
|
|
clutter_actor_set_opacity (actor, g_value_get_uint (value));
|
|
break;
|
|
|
|
case PROP_NAME:
|
|
clutter_actor_set_name (actor, g_value_get_string (value));
|
|
break;
|
|
|
|
case PROP_VISIBLE:
|
|
if (g_value_get_boolean (value) == TRUE)
|
|
clutter_actor_show (actor);
|
|
else
|
|
clutter_actor_hide (actor);
|
|
break;
|
|
|
|
case PROP_SCALE_X:
|
|
clutter_actor_set_scale (actor,
|
|
g_value_get_double (value),
|
|
priv->scale_y);
|
|
break;
|
|
|
|
case PROP_SCALE_Y:
|
|
clutter_actor_set_scale (actor,
|
|
priv->scale_x,
|
|
g_value_get_double (value));
|
|
break;
|
|
|
|
case PROP_SCALE_CENTER_X:
|
|
{
|
|
gfloat center_x = g_value_get_float (value);
|
|
gfloat center_y;
|
|
|
|
clutter_anchor_coord_get_units (actor, &priv->scale_center,
|
|
NULL,
|
|
¢er_y,
|
|
NULL);
|
|
clutter_actor_set_scale_full (actor,
|
|
priv->scale_x,
|
|
priv->scale_y,
|
|
center_x,
|
|
center_y);
|
|
}
|
|
break;
|
|
|
|
case PROP_SCALE_CENTER_Y:
|
|
{
|
|
gfloat center_y = g_value_get_float (value);
|
|
gfloat center_x;
|
|
|
|
clutter_anchor_coord_get_units (actor, &priv->scale_center,
|
|
¢er_x,
|
|
NULL,
|
|
NULL);
|
|
clutter_actor_set_scale_full (actor,
|
|
priv->scale_x,
|
|
priv->scale_y,
|
|
center_x,
|
|
center_y);
|
|
}
|
|
break;
|
|
|
|
case PROP_SCALE_GRAVITY:
|
|
clutter_actor_set_scale_with_gravity (actor,
|
|
priv->scale_x,
|
|
priv->scale_y,
|
|
g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_CLIP:
|
|
{
|
|
const ClutterGeometry *geom = g_value_get_boxed (value);
|
|
|
|
clutter_actor_set_clip (actor,
|
|
geom->x, geom->y,
|
|
geom->width, geom->height);
|
|
}
|
|
break;
|
|
|
|
case PROP_CLIP_TO_ALLOCATION:
|
|
clutter_actor_set_clip_to_allocation (actor, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_REACTIVE:
|
|
clutter_actor_set_reactive (actor, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_ROTATION_ANGLE_X:
|
|
clutter_actor_set_rotation_internal (actor,
|
|
CLUTTER_X_AXIS,
|
|
g_value_get_double (value));
|
|
break;
|
|
|
|
case PROP_ROTATION_ANGLE_Y:
|
|
clutter_actor_set_rotation_internal (actor,
|
|
CLUTTER_Y_AXIS,
|
|
g_value_get_double (value));
|
|
break;
|
|
|
|
case PROP_ROTATION_ANGLE_Z:
|
|
clutter_actor_set_rotation_internal (actor,
|
|
CLUTTER_Z_AXIS,
|
|
g_value_get_double (value));
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_X:
|
|
{
|
|
const ClutterVertex *center;
|
|
|
|
if ((center = g_value_get_boxed (value)))
|
|
clutter_actor_set_rotation (actor,
|
|
CLUTTER_X_AXIS,
|
|
priv->rxang,
|
|
center->x,
|
|
center->y,
|
|
center->z);
|
|
}
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_Y:
|
|
{
|
|
const ClutterVertex *center;
|
|
|
|
if ((center = g_value_get_boxed (value)))
|
|
clutter_actor_set_rotation (actor,
|
|
CLUTTER_Y_AXIS,
|
|
priv->ryang,
|
|
center->x,
|
|
center->y,
|
|
center->z);
|
|
}
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_Z:
|
|
{
|
|
const ClutterVertex *center;
|
|
|
|
if ((center = g_value_get_boxed (value)))
|
|
clutter_actor_set_rotation (actor,
|
|
CLUTTER_Z_AXIS,
|
|
priv->rzang,
|
|
center->x,
|
|
center->y,
|
|
center->z);
|
|
}
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_Z_GRAVITY:
|
|
clutter_actor_set_z_rotation_from_gravity (actor, priv->rzang,
|
|
g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_ANCHOR_X:
|
|
{
|
|
gfloat anchor_x = g_value_get_float (value);
|
|
gfloat anchor_y;
|
|
|
|
clutter_anchor_coord_get_units (actor, &priv->anchor,
|
|
NULL,
|
|
&anchor_y,
|
|
NULL);
|
|
clutter_actor_set_anchor_point (actor, anchor_x, anchor_y);
|
|
}
|
|
break;
|
|
|
|
case PROP_ANCHOR_Y:
|
|
{
|
|
gfloat anchor_y = g_value_get_float (value);
|
|
gfloat anchor_x;
|
|
|
|
clutter_anchor_coord_get_units (actor, &priv->anchor,
|
|
&anchor_x,
|
|
NULL,
|
|
NULL);
|
|
clutter_actor_set_anchor_point (actor, anchor_x, anchor_y);
|
|
}
|
|
break;
|
|
|
|
case PROP_ANCHOR_GRAVITY:
|
|
clutter_actor_set_anchor_point_from_gravity (actor,
|
|
g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_SHOW_ON_SET_PARENT:
|
|
priv->show_on_set_parent = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_TEXT_DIRECTION:
|
|
clutter_actor_set_text_direction (actor, g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_ACTIONS:
|
|
clutter_actor_add_action (actor, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_CONSTRAINTS:
|
|
clutter_actor_add_constraint (actor, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_EFFECT:
|
|
clutter_actor_add_effect (actor, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_actor_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterActor *actor = CLUTTER_ACTOR (object);
|
|
ClutterActorPrivate *priv = actor->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_X:
|
|
g_value_set_float (value, clutter_actor_get_x (actor));
|
|
break;
|
|
|
|
case PROP_Y:
|
|
g_value_set_float (value, clutter_actor_get_y (actor));
|
|
break;
|
|
|
|
case PROP_WIDTH:
|
|
g_value_set_float (value, clutter_actor_get_width (actor));
|
|
break;
|
|
|
|
case PROP_HEIGHT:
|
|
g_value_set_float (value, clutter_actor_get_height (actor));
|
|
break;
|
|
|
|
case PROP_FIXED_X:
|
|
g_value_set_float (value, priv->fixed_x);
|
|
break;
|
|
|
|
case PROP_FIXED_Y:
|
|
g_value_set_float (value, priv->fixed_y);
|
|
break;
|
|
|
|
case PROP_FIXED_POSITION_SET:
|
|
g_value_set_boolean (value, priv->position_set);
|
|
break;
|
|
|
|
case PROP_MIN_WIDTH:
|
|
g_value_set_float (value, priv->request_min_width);
|
|
break;
|
|
|
|
case PROP_MIN_HEIGHT:
|
|
g_value_set_float (value, priv->request_min_height);
|
|
break;
|
|
|
|
case PROP_NATURAL_WIDTH:
|
|
g_value_set_float (value, priv->request_natural_width);
|
|
break;
|
|
|
|
case PROP_NATURAL_HEIGHT:
|
|
g_value_set_float (value, priv->request_natural_height);
|
|
break;
|
|
|
|
case PROP_MIN_WIDTH_SET:
|
|
g_value_set_boolean (value, priv->min_width_set);
|
|
break;
|
|
|
|
case PROP_MIN_HEIGHT_SET:
|
|
g_value_set_boolean (value, priv->min_height_set);
|
|
break;
|
|
|
|
case PROP_NATURAL_WIDTH_SET:
|
|
g_value_set_boolean (value, priv->natural_width_set);
|
|
break;
|
|
|
|
case PROP_NATURAL_HEIGHT_SET:
|
|
g_value_set_boolean (value, priv->natural_height_set);
|
|
break;
|
|
|
|
case PROP_REQUEST_MODE:
|
|
g_value_set_enum (value, priv->request_mode);
|
|
break;
|
|
|
|
case PROP_ALLOCATION:
|
|
g_value_set_boxed (value, &priv->allocation);
|
|
break;
|
|
|
|
case PROP_DEPTH:
|
|
g_value_set_float (value, clutter_actor_get_depth (actor));
|
|
break;
|
|
|
|
case PROP_OPACITY:
|
|
g_value_set_uint (value, priv->opacity);
|
|
break;
|
|
|
|
case PROP_NAME:
|
|
g_value_set_string (value, priv->name);
|
|
break;
|
|
|
|
case PROP_VISIBLE:
|
|
g_value_set_boolean (value, CLUTTER_ACTOR_IS_VISIBLE (actor));
|
|
break;
|
|
|
|
case PROP_MAPPED:
|
|
g_value_set_boolean (value, CLUTTER_ACTOR_IS_MAPPED (actor));
|
|
break;
|
|
|
|
case PROP_REALIZED:
|
|
g_value_set_boolean (value, CLUTTER_ACTOR_IS_REALIZED (actor));
|
|
break;
|
|
|
|
case PROP_HAS_CLIP:
|
|
g_value_set_boolean (value, priv->has_clip);
|
|
break;
|
|
|
|
case PROP_CLIP:
|
|
{
|
|
ClutterGeometry clip = { 0, };
|
|
|
|
clip.x = priv->clip[0];
|
|
clip.y = priv->clip[1];
|
|
clip.width = priv->clip[2];
|
|
clip.height = priv->clip[3];
|
|
|
|
g_value_set_boxed (value, &clip);
|
|
}
|
|
break;
|
|
|
|
case PROP_CLIP_TO_ALLOCATION:
|
|
g_value_set_boolean (value, priv->clip_to_allocation);
|
|
break;
|
|
|
|
case PROP_SCALE_X:
|
|
g_value_set_double (value, priv->scale_x);
|
|
break;
|
|
|
|
case PROP_SCALE_Y:
|
|
g_value_set_double (value, priv->scale_y);
|
|
break;
|
|
|
|
case PROP_SCALE_CENTER_X:
|
|
{
|
|
gfloat center;
|
|
|
|
clutter_actor_get_scale_center (actor, ¢er, NULL);
|
|
|
|
g_value_set_float (value, center);
|
|
}
|
|
break;
|
|
|
|
case PROP_SCALE_CENTER_Y:
|
|
{
|
|
gfloat center;
|
|
|
|
clutter_actor_get_scale_center (actor, NULL, ¢er);
|
|
|
|
g_value_set_float (value, center);
|
|
}
|
|
break;
|
|
|
|
case PROP_SCALE_GRAVITY:
|
|
g_value_set_enum (value, clutter_actor_get_scale_gravity (actor));
|
|
break;
|
|
|
|
case PROP_REACTIVE:
|
|
g_value_set_boolean (value, clutter_actor_get_reactive (actor));
|
|
break;
|
|
|
|
case PROP_ROTATION_ANGLE_X:
|
|
g_value_set_double (value, priv->rxang);
|
|
break;
|
|
|
|
case PROP_ROTATION_ANGLE_Y:
|
|
g_value_set_double (value, priv->ryang);
|
|
break;
|
|
|
|
case PROP_ROTATION_ANGLE_Z:
|
|
g_value_set_double (value, priv->rzang);
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_X:
|
|
{
|
|
ClutterVertex center;
|
|
|
|
clutter_actor_get_rotation (actor, CLUTTER_X_AXIS,
|
|
¢er.x,
|
|
¢er.y,
|
|
¢er.z);
|
|
|
|
g_value_set_boxed (value, ¢er);
|
|
}
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_Y:
|
|
{
|
|
ClutterVertex center;
|
|
|
|
clutter_actor_get_rotation (actor, CLUTTER_Y_AXIS,
|
|
¢er.x,
|
|
¢er.y,
|
|
¢er.z);
|
|
|
|
g_value_set_boxed (value, ¢er);
|
|
}
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_Z:
|
|
{
|
|
ClutterVertex center;
|
|
|
|
clutter_actor_get_rotation (actor, CLUTTER_Z_AXIS,
|
|
¢er.x,
|
|
¢er.y,
|
|
¢er.z);
|
|
|
|
g_value_set_boxed (value, ¢er);
|
|
}
|
|
break;
|
|
|
|
case PROP_ROTATION_CENTER_Z_GRAVITY:
|
|
g_value_set_enum (value, clutter_actor_get_z_rotation_gravity (actor));
|
|
break;
|
|
|
|
case PROP_ANCHOR_X:
|
|
{
|
|
gfloat anchor_x;
|
|
|
|
clutter_anchor_coord_get_units (actor, &priv->anchor,
|
|
&anchor_x,
|
|
NULL,
|
|
NULL);
|
|
g_value_set_float (value, anchor_x);
|
|
}
|
|
break;
|
|
|
|
case PROP_ANCHOR_Y:
|
|
{
|
|
gfloat anchor_y;
|
|
|
|
clutter_anchor_coord_get_units (actor, &priv->anchor,
|
|
NULL,
|
|
&anchor_y,
|
|
NULL);
|
|
g_value_set_float (value, anchor_y);
|
|
}
|
|
break;
|
|
|
|
case PROP_ANCHOR_GRAVITY:
|
|
g_value_set_enum (value, clutter_actor_get_anchor_point_gravity (actor));
|
|
break;
|
|
|
|
case PROP_SHOW_ON_SET_PARENT:
|
|
g_value_set_boolean (value, priv->show_on_set_parent);
|
|
break;
|
|
|
|
case PROP_TEXT_DIRECTION:
|
|
g_value_set_enum (value, priv->text_direction);
|
|
break;
|
|
|
|
case PROP_HAS_POINTER:
|
|
g_value_set_boolean (value, priv->has_pointer);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_actor_dispose (GObject *object)
|
|
{
|
|
ClutterActor *self = CLUTTER_ACTOR (object);
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
CLUTTER_NOTE (MISC, "Disposing of object (id=%d) of type '%s' (ref_count:%d)",
|
|
self->priv->id,
|
|
g_type_name (G_OBJECT_TYPE (self)),
|
|
object->ref_count);
|
|
|
|
/* avoid recursing when called from clutter_actor_destroy() */
|
|
if (priv->parent_actor != NULL)
|
|
{
|
|
ClutterActor *parent = priv->parent_actor;
|
|
|
|
/* go through the Container implementation unless this
|
|
* is an internal child and has been marked as such
|
|
*/
|
|
if (CLUTTER_IS_CONTAINER (parent) &&
|
|
!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
|
|
{
|
|
clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self);
|
|
}
|
|
else
|
|
clutter_actor_unparent (self);
|
|
}
|
|
|
|
/* parent should be gone */
|
|
g_assert (priv->parent_actor == NULL);
|
|
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
/* can't be mapped or realized with no parent */
|
|
g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
|
|
g_assert (!CLUTTER_ACTOR_IS_REALIZED (self));
|
|
}
|
|
|
|
destroy_shader_data (self);
|
|
|
|
if (priv->pango_context)
|
|
{
|
|
g_object_unref (priv->pango_context);
|
|
priv->pango_context = NULL;
|
|
}
|
|
|
|
if (priv->actions != NULL)
|
|
{
|
|
g_object_unref (priv->actions);
|
|
priv->actions = NULL;
|
|
}
|
|
|
|
if (priv->constraints != NULL)
|
|
{
|
|
g_object_unref (priv->constraints);
|
|
priv->constraints = NULL;
|
|
}
|
|
|
|
if (priv->effects != NULL)
|
|
{
|
|
g_object_unref (priv->effects);
|
|
priv->effects = NULL;
|
|
}
|
|
|
|
g_signal_emit (self, actor_signals[DESTROY], 0);
|
|
|
|
G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_finalize (GObject *object)
|
|
{
|
|
ClutterActorPrivate *priv = CLUTTER_ACTOR (object)->priv;
|
|
|
|
CLUTTER_NOTE (MISC, "Finalize actor (name='%s', id=%d) of type '%s'",
|
|
priv->name != NULL ? priv->name : "<none>",
|
|
priv->id,
|
|
g_type_name (G_OBJECT_TYPE (object)));
|
|
|
|
g_free (priv->name);
|
|
clutter_id_pool_remove (CLUTTER_CONTEXT()->id_pool, priv->id);
|
|
|
|
G_OBJECT_CLASS (clutter_actor_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
/**
|
|
* clutter_actor_get_accessible:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Returns the accessible object that describes the actor to an
|
|
* assistive technology.
|
|
*
|
|
* If no class-specific #AtkObject implementation is available for the
|
|
* actor instance in question, it will inherit an #AtkObject
|
|
* implementation from the first ancestor class for which such an
|
|
* implementation is defined.
|
|
*
|
|
* The documentation of the <ulink
|
|
* url="http://developer.gnome.org/doc/API/2.0/atk/index.html">ATK</ulink>
|
|
* library contains more information about accessible objects and
|
|
* their uses.
|
|
*
|
|
* Returns: (transfer none): the #AtkObject associated with @actor
|
|
*/
|
|
AtkObject *
|
|
clutter_actor_get_accessible (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
return CLUTTER_ACTOR_GET_CLASS (self)->get_accessible (self);
|
|
}
|
|
|
|
static AtkObject *
|
|
clutter_actor_real_get_accessible (ClutterActor *actor)
|
|
{
|
|
return atk_gobject_accessible_for_object (G_OBJECT (actor));
|
|
}
|
|
|
|
static AtkObject *
|
|
_clutter_actor_ref_accessible (AtkImplementor *implementor)
|
|
{
|
|
AtkObject *accessible;
|
|
|
|
accessible = clutter_actor_get_accessible (CLUTTER_ACTOR (implementor));
|
|
if (accessible != NULL)
|
|
g_object_ref (accessible);
|
|
|
|
return accessible;
|
|
}
|
|
|
|
static void
|
|
atk_implementor_iface_init (AtkImplementorIface *iface)
|
|
{
|
|
iface->ref_accessible = _clutter_actor_ref_accessible;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_actor_real_get_paint_volume (ClutterActor *self,
|
|
ClutterPaintVolume *volume)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
clutter_actor_class_init (ClutterActorClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
object_class->set_property = clutter_actor_set_property;
|
|
object_class->get_property = clutter_actor_get_property;
|
|
object_class->dispose = clutter_actor_dispose;
|
|
object_class->finalize = clutter_actor_finalize;
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterActorPrivate));
|
|
|
|
/**
|
|
* ClutterActor:x:
|
|
*
|
|
* X coordinate of the actor in pixels. If written, forces a fixed
|
|
* position for the actor. If read, returns the fixed position if any,
|
|
* otherwise the allocation if available, otherwise 0.
|
|
*/
|
|
pspec = g_param_spec_float ("x",
|
|
P_("X coordinate"),
|
|
P_("X coordinate of the actor"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_X] = pspec;
|
|
g_object_class_install_property (object_class, PROP_X, pspec);
|
|
|
|
/**
|
|
* ClutterActor:y:
|
|
*
|
|
* Y coordinate of the actor in pixels. If written, forces a fixed
|
|
* position for the actor. If read, returns the fixed position if
|
|
* any, otherwise the allocation if available, otherwise 0.
|
|
*/
|
|
pspec = g_param_spec_float ("y",
|
|
P_("Y coordinate"),
|
|
P_("Y coordinate of the actor"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_Y] = pspec;
|
|
g_object_class_install_property (object_class, PROP_Y, pspec);
|
|
|
|
/**
|
|
* ClutterActor:width:
|
|
*
|
|
* Width of the actor (in pixels). If written, forces the minimum and
|
|
* natural size request of the actor to the given width. If read, returns
|
|
* the allocated width if available, otherwise the width request.
|
|
*/
|
|
pspec = g_param_spec_float ("width",
|
|
P_("Width"),
|
|
P_("Width of the actor"),
|
|
0.0, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_WIDTH] = pspec;
|
|
g_object_class_install_property (object_class, PROP_WIDTH, pspec);
|
|
/**
|
|
* ClutterActor:height:
|
|
*
|
|
* Height of the actor (in pixels). If written, forces the minimum and
|
|
* natural size request of the actor to the given height. If read, returns
|
|
* the allocated height if available, otherwise the height request.
|
|
*/
|
|
pspec = g_param_spec_float ("height",
|
|
P_("Height"),
|
|
P_("Height of the actor"),
|
|
0.0, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_HEIGHT] = pspec;
|
|
g_object_class_install_property (object_class, PROP_HEIGHT, pspec);
|
|
|
|
/**
|
|
* ClutterActor:fixed-x:
|
|
*
|
|
* The fixed X position of the actor in pixels.
|
|
*
|
|
* Writing this property sets #ClutterActor:fixed-position-set
|
|
* property as well, as a side effect
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("fixed-x",
|
|
P_("Fixed X"),
|
|
P_("Forced X position of the actor"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_FIXED_X] = pspec;
|
|
g_object_class_install_property (object_class, PROP_FIXED_X, pspec);
|
|
|
|
/**
|
|
* ClutterActor:fixed-y:
|
|
*
|
|
* The fixed Y position of the actor in pixels.
|
|
*
|
|
* Writing this property sets the #ClutterActor:fixed-position-set
|
|
* property as well, as a side effect
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("fixed-y",
|
|
P_("Fixed Y"),
|
|
P_("Forced Y position of the actor"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_FIXED_Y] = pspec;
|
|
g_object_class_install_property (object_class, PROP_FIXED_Y, pspec);
|
|
|
|
/**
|
|
* ClutterActor:fixed-position-set:
|
|
*
|
|
* This flag controls whether the #ClutterActor:fixed-x and
|
|
* #ClutterActor:fixed-y properties are used
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_boolean ("fixed-position-set",
|
|
P_("Fixed position set"),
|
|
P_("Whether to use fixed positioning "
|
|
"for the actor"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_FIXED_POSITION_SET] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_FIXED_POSITION_SET,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:min-width:
|
|
*
|
|
* A forced minimum width request for the actor, in pixels
|
|
*
|
|
* Writing this property sets the #ClutterActor:min-width-set property
|
|
* as well, as a side effect.
|
|
*
|
|
*This property overrides the usual width request of the actor.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("min-width",
|
|
P_("Min Width"),
|
|
P_("Forced minimum width request for the actor"),
|
|
0.0, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_MIN_WIDTH] = pspec;
|
|
g_object_class_install_property (object_class, PROP_MIN_WIDTH, pspec);
|
|
|
|
/**
|
|
* ClutterActor:min-height:
|
|
*
|
|
* A forced minimum height request for the actor, in pixels
|
|
*
|
|
* Writing this property sets the #ClutterActor:min-height-set property
|
|
* as well, as a side effect. This property overrides the usual height
|
|
* request of the actor.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("min-height",
|
|
P_("Min Height"),
|
|
P_("Forced minimum height request for the actor"),
|
|
0.0, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_MIN_HEIGHT] = pspec;
|
|
g_object_class_install_property (object_class, PROP_MIN_HEIGHT, pspec);
|
|
|
|
/**
|
|
* ClutterActor:natural-width:
|
|
*
|
|
* A forced natural width request for the actor, in pixels
|
|
*
|
|
* Writing this property sets the #ClutterActor:natural-width-set
|
|
* property as well, as a side effect. This property overrides the
|
|
* usual width request of the actor
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("natural-width",
|
|
P_("Natural Width"),
|
|
P_("Forced natural width request for the actor"),
|
|
0.0, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_NATURAL_WIDTH] = pspec;
|
|
g_object_class_install_property (object_class, PROP_NATURAL_WIDTH, pspec);
|
|
|
|
/**
|
|
* ClutterActor:natural-height:
|
|
*
|
|
* A forced natural height request for the actor, in pixels
|
|
*
|
|
* Writing this property sets the #ClutterActor:natural-height-set
|
|
* property as well, as a side effect. This property overrides the
|
|
* usual height request of the actor
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("natural-height",
|
|
P_("Natural Height"),
|
|
P_("Forced natural height request for the actor"),
|
|
0.0, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_NATURAL_HEIGHT] = pspec;
|
|
g_object_class_install_property (object_class, PROP_NATURAL_HEIGHT, pspec);
|
|
|
|
/**
|
|
* ClutterActor:min-width-set:
|
|
*
|
|
* This flag controls whether the #ClutterActor:min-width property
|
|
* is used
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_boolean ("min-width-set",
|
|
P_("Minimum width set"),
|
|
P_("Whether to use the min-width property"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_MIN_WIDTH_SET] = pspec;
|
|
g_object_class_install_property (object_class, PROP_MIN_WIDTH_SET, pspec);
|
|
|
|
/**
|
|
* ClutterActor:min-height-set:
|
|
*
|
|
* This flag controls whether the #ClutterActor:min-height property
|
|
* is used
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_boolean ("min-height-set",
|
|
P_("Minimum height set"),
|
|
P_("Whether to use the min-height property"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_MIN_HEIGHT_SET] = pspec;
|
|
g_object_class_install_property (object_class, PROP_MIN_HEIGHT_SET, pspec);
|
|
|
|
/**
|
|
* ClutterActor:natural-width-set:
|
|
*
|
|
* This flag controls whether the #ClutterActor:natural-width property
|
|
* is used
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_boolean ("natural-width-set",
|
|
P_("Natural width set"),
|
|
P_("Whether to use the natural-width property"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_NATURAL_WIDTH_SET] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_NATURAL_WIDTH_SET,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:natural-height-set:
|
|
*
|
|
* This flag controls whether the #ClutterActor:natural-height property
|
|
* is used
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_boolean ("natural-height-set",
|
|
P_("Natural height set"),
|
|
P_("Whether to use the natural-height property"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_NATURAL_HEIGHT_SET] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_NATURAL_HEIGHT_SET,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:allocation:
|
|
*
|
|
* The allocation for the actor, in pixels
|
|
*
|
|
* This is property is read-only, but you might monitor it to know when an
|
|
* actor moves or resizes
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_boxed ("allocation",
|
|
P_("Allocation"),
|
|
P_("The actor's allocation"),
|
|
CLUTTER_TYPE_ACTOR_BOX,
|
|
CLUTTER_PARAM_READABLE);
|
|
obj_props[PROP_ALLOCATION] = pspec;
|
|
g_object_class_install_property (object_class, PROP_ALLOCATION, pspec);
|
|
|
|
/**
|
|
* ClutterActor:request-mode:
|
|
*
|
|
* Request mode for the #ClutterActor. The request mode determines the
|
|
* type of geometry management used by the actor, either height for width
|
|
* (the default) or width for height.
|
|
*
|
|
* For actors implementing height for width, the parent container should get
|
|
* the preferred width first, and then the preferred height for that width.
|
|
*
|
|
* For actors implementing width for height, the parent container should get
|
|
* the preferred height first, and then the preferred width for that height.
|
|
*
|
|
* For instance:
|
|
*
|
|
* |[
|
|
* ClutterRequestMode mode;
|
|
* gfloat natural_width, min_width;
|
|
* gfloat natural_height, min_height;
|
|
*
|
|
* mode = clutter_actor_get_request_mode (child);
|
|
* if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
|
* {
|
|
* clutter_actor_get_preferred_width (child, -1,
|
|
* &min_width,
|
|
* &natural_width);
|
|
* clutter_actor_get_preferred_height (child, natural_width,
|
|
* &min_height,
|
|
* &natural_height);
|
|
* }
|
|
* else
|
|
* {
|
|
* clutter_actor_get_preferred_height (child, -1,
|
|
* &min_height,
|
|
* &natural_height);
|
|
* clutter_actor_get_preferred_width (child, natural_height,
|
|
* &min_width,
|
|
* &natural_width);
|
|
* }
|
|
* ]|
|
|
*
|
|
* will retrieve the minimum and natural width and height depending on the
|
|
* preferred request mode of the #ClutterActor "child".
|
|
*
|
|
* The clutter_actor_get_preferred_size() function will implement this
|
|
* check for you.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_enum ("request-mode",
|
|
P_("Request Mode"),
|
|
P_("The actor's request mode"),
|
|
CLUTTER_TYPE_REQUEST_MODE,
|
|
CLUTTER_REQUEST_HEIGHT_FOR_WIDTH,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_REQUEST_MODE] = pspec;
|
|
g_object_class_install_property (object_class, PROP_REQUEST_MODE, pspec);
|
|
|
|
/**
|
|
* ClutterActor:depth:
|
|
*
|
|
* The position of the actor on the Z axis
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_float ("depth",
|
|
P_("Depth"),
|
|
P_("Position on the Z axis"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_DEPTH] = pspec;
|
|
g_object_class_install_property (object_class, PROP_DEPTH, pspec);
|
|
|
|
/**
|
|
* ClutterActor:opacity:
|
|
*
|
|
* Opacity of an actor, between 0 (fully transparent) and
|
|
* 255 (fully opaque)
|
|
*/
|
|
pspec = g_param_spec_uint ("opacity",
|
|
P_("Opacity"),
|
|
P_("Opacity of an actor"),
|
|
0, 255,
|
|
255,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_OPACITY] = pspec;
|
|
g_object_class_install_property (object_class, PROP_OPACITY, pspec);
|
|
|
|
/**
|
|
* ClutterActor:visible:
|
|
*
|
|
* Whether the actor is set to be visible or not
|
|
*
|
|
* See also #ClutterActor:mapped
|
|
*/
|
|
pspec = g_param_spec_boolean ("visible",
|
|
P_("Visible"),
|
|
P_("Whether the actor is visible or not"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_VISIBLE] = pspec;
|
|
g_object_class_install_property (object_class, PROP_VISIBLE, pspec);
|
|
|
|
/**
|
|
* ClutterActor:mapped:
|
|
*
|
|
* Whether the actor is mapped (will be painted when the stage
|
|
* to which it belongs is mapped)
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_boolean ("mapped",
|
|
P_("Mapped"),
|
|
P_("Whether the actor will be painted"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READABLE);
|
|
obj_props[PROP_MAPPED] = pspec;
|
|
g_object_class_install_property (object_class, PROP_MAPPED, pspec);
|
|
|
|
/**
|
|
* ClutterActor:realized:
|
|
*
|
|
* Whether the actor has been realized
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_boolean ("realized",
|
|
P_("Realized"),
|
|
P_("Whether the actor has been realized"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READABLE);
|
|
obj_props[PROP_REALIZED] = pspec;
|
|
g_object_class_install_property (object_class, PROP_REALIZED, pspec);
|
|
|
|
/**
|
|
* ClutterActor:reactive:
|
|
*
|
|
* Whether the actor is reactive to events or not
|
|
*
|
|
* Only reactive actors will emit event-related signals
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_boolean ("reactive",
|
|
P_("Reactive"),
|
|
P_("Whether the actor is reactive to events"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_REACTIVE] = pspec;
|
|
g_object_class_install_property (object_class, PROP_REACTIVE, pspec);
|
|
|
|
/**
|
|
* ClutterActor:has-clip:
|
|
*
|
|
* Whether the actor has the #ClutterActor:clip property set or not
|
|
*/
|
|
pspec = g_param_spec_boolean ("has-clip",
|
|
P_("Has Clip"),
|
|
P_("Whether the actor has a clip set"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READABLE);
|
|
obj_props[PROP_HAS_CLIP] = pspec;
|
|
g_object_class_install_property (object_class, PROP_HAS_CLIP, pspec);
|
|
|
|
/**
|
|
* ClutterActor:clip:
|
|
*
|
|
* The clip region for the actor, in actor-relative coordinates
|
|
*
|
|
* Every part of the actor outside the clip region will not be
|
|
* painted
|
|
*/
|
|
pspec = g_param_spec_boxed ("clip",
|
|
P_("Clip"),
|
|
P_("The clip region for the actor"),
|
|
CLUTTER_TYPE_GEOMETRY,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_CLIP] = pspec;
|
|
g_object_class_install_property (object_class, PROP_CLIP, pspec);
|
|
|
|
/**
|
|
* ClutterActor:name:
|
|
*
|
|
* The name of the actor
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
pspec = g_param_spec_string ("name",
|
|
P_("Name"),
|
|
P_("Name of the actor"),
|
|
NULL,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_NAME] = pspec;
|
|
g_object_class_install_property (object_class, PROP_NAME, pspec);
|
|
|
|
/**
|
|
* ClutterActor:scale-x:
|
|
*
|
|
* The horizontal scale of the actor
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_double ("scale-x",
|
|
P_("Scale X"),
|
|
P_("Scale factor on the X axis"),
|
|
0.0, G_MAXDOUBLE,
|
|
1.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SCALE_X] = pspec;
|
|
g_object_class_install_property (object_class, PROP_SCALE_X, pspec);
|
|
|
|
/**
|
|
* ClutterActor:scale-y:
|
|
*
|
|
* The vertical scale of the actor
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_double ("scale-y",
|
|
P_("Scale Y"),
|
|
P_("Scale factor on the Y axis"),
|
|
0.0, G_MAXDOUBLE,
|
|
1.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SCALE_Y] = pspec;
|
|
g_object_class_install_property (object_class, PROP_SCALE_Y, pspec);
|
|
|
|
/**
|
|
* ClutterActor:scale-center-x:
|
|
*
|
|
* The horizontal center point for scaling
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_float ("scale-center-x",
|
|
P_("Scale Center X"),
|
|
P_("Horizontal scale center"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SCALE_CENTER_X] = pspec;
|
|
g_object_class_install_property (object_class, PROP_SCALE_CENTER_X, pspec);
|
|
|
|
/**
|
|
* ClutterActor:scale-center-y:
|
|
*
|
|
* The vertical center point for scaling
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_float ("scale-center-y",
|
|
P_("Scale Center Y"),
|
|
P_("Vertical scale center"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SCALE_CENTER_Y] = pspec;
|
|
g_object_class_install_property (object_class, PROP_SCALE_CENTER_Y, pspec);
|
|
|
|
/**
|
|
* ClutterActor:scale-gravity:
|
|
*
|
|
* The center point for scaling expressed as a #ClutterGravity
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_enum ("scale-gravity",
|
|
P_("Scale Gravity"),
|
|
P_("The center of scaling"),
|
|
CLUTTER_TYPE_GRAVITY,
|
|
CLUTTER_GRAVITY_NONE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SCALE_GRAVITY] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_SCALE_GRAVITY,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:rotation-angle-x:
|
|
*
|
|
* The rotation angle on the X axis
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_double ("rotation-angle-x",
|
|
P_("Rotation Angle X"),
|
|
P_("The rotation angle on the X axis"),
|
|
-G_MAXDOUBLE, G_MAXDOUBLE,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ROTATION_ANGLE_X] = pspec;
|
|
g_object_class_install_property (object_class, PROP_ROTATION_ANGLE_X, pspec);
|
|
|
|
/**
|
|
* ClutterActor:rotation-angle-y:
|
|
*
|
|
* The rotation angle on the Y axis
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_double ("rotation-angle-y",
|
|
P_("Rotation Angle Y"),
|
|
P_("The rotation angle on the Y axis"),
|
|
-G_MAXDOUBLE, G_MAXDOUBLE,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ROTATION_ANGLE_Y] = pspec;
|
|
g_object_class_install_property (object_class, PROP_ROTATION_ANGLE_Y, pspec);
|
|
|
|
/**
|
|
* ClutterActor:rotation-angle-z:
|
|
*
|
|
* The rotation angle on the Z axis
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_double ("rotation-angle-z",
|
|
P_("Rotation Angle Z"),
|
|
P_("The rotation angle on the Z axis"),
|
|
-G_MAXDOUBLE, G_MAXDOUBLE,
|
|
0.0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ROTATION_ANGLE_Z] = pspec;
|
|
g_object_class_install_property (object_class, PROP_ROTATION_ANGLE_Z, pspec);
|
|
|
|
/**
|
|
* ClutterActor:rotation-center-x:
|
|
*
|
|
* The rotation center on the X axis.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_boxed ("rotation-center-x",
|
|
P_("Rotation Center X"),
|
|
P_("The rotation center on the X axis"),
|
|
CLUTTER_TYPE_VERTEX,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ROTATION_CENTER_X] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_ROTATION_CENTER_X,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:rotation-center-y:
|
|
*
|
|
* The rotation center on the Y axis.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_boxed ("rotation-center-y",
|
|
P_("Rotation Center Y"),
|
|
P_("The rotation center on the Y axis"),
|
|
CLUTTER_TYPE_VERTEX,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ROTATION_CENTER_Y] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_ROTATION_CENTER_Y,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:rotation-center-z:
|
|
*
|
|
* The rotation center on the Z axis.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_boxed ("rotation-center-z",
|
|
P_("Rotation Center Z"),
|
|
P_("The rotation center on the Z axis"),
|
|
CLUTTER_TYPE_VERTEX,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ROTATION_CENTER_Z] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_ROTATION_CENTER_Z,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:rotation-center-z-gravity:
|
|
*
|
|
* The rotation center on the Z axis expressed as a #ClutterGravity.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_enum ("rotation-center-z-gravity",
|
|
P_("Rotation Center Z Gravity"),
|
|
P_("Center point for rotation around the Z axis"),
|
|
CLUTTER_TYPE_GRAVITY,
|
|
CLUTTER_GRAVITY_NONE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ROTATION_CENTER_Z_GRAVITY] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_ROTATION_CENTER_Z_GRAVITY,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:anchor-x:
|
|
*
|
|
* The X coordinate of an actor's anchor point, relative to
|
|
* the actor coordinate space, in pixels
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("anchor-x",
|
|
P_("Anchor X"),
|
|
P_("X coordinate of the anchor point"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ANCHOR_X] = pspec;
|
|
g_object_class_install_property (object_class, PROP_ANCHOR_X, pspec);
|
|
|
|
/**
|
|
* ClutterActor:anchor-y:
|
|
*
|
|
* The Y coordinate of an actor's anchor point, relative to
|
|
* the actor coordinate space, in pixels
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_float ("anchor-y",
|
|
P_("Anchor Y"),
|
|
P_("Y coordinate of the anchor point"),
|
|
-G_MAXFLOAT, G_MAXFLOAT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ANCHOR_Y] = pspec;
|
|
g_object_class_install_property (object_class, PROP_ANCHOR_Y, pspec);
|
|
|
|
/**
|
|
* ClutterActor:anchor-gravity:
|
|
*
|
|
* The anchor point expressed as a #ClutterGravity
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_enum ("anchor-gravity",
|
|
P_("Anchor Gravity"),
|
|
P_("The anchor point as a ClutterGravity"),
|
|
CLUTTER_TYPE_GRAVITY,
|
|
CLUTTER_GRAVITY_NONE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_ANCHOR_GRAVITY] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_ANCHOR_GRAVITY, pspec);
|
|
|
|
/**
|
|
* ClutterActor:show-on-set-parent:
|
|
*
|
|
* If %TRUE, the actor is automatically shown when parented.
|
|
*
|
|
* Calling clutter_actor_hide() on an actor which has not been
|
|
* parented will set this property to %FALSE as a side effect.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
pspec = g_param_spec_boolean ("show-on-set-parent",
|
|
P_("Show on set parent"),
|
|
P_("Whether the actor is shown when parented"),
|
|
TRUE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SHOW_ON_SET_PARENT] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_SHOW_ON_SET_PARENT,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:clip-to-allocation:
|
|
*
|
|
* Whether the clip region should track the allocated area
|
|
* of the actor.
|
|
*
|
|
* This property is ignored if a clip area has been explicitly
|
|
* set using clutter_actor_set_clip().
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_boolean ("clip-to-allocation",
|
|
P_("Clip to Allocation"),
|
|
P_("Sets the clip region to track the "
|
|
"actor's allocation"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_CLIP_TO_ALLOCATION] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_CLIP_TO_ALLOCATION,
|
|
pspec);
|
|
|
|
pspec = g_param_spec_enum ("text-direction",
|
|
P_("Text Direction"),
|
|
P_("Direction of the text"),
|
|
CLUTTER_TYPE_TEXT_DIRECTION,
|
|
CLUTTER_TEXT_DIRECTION_LTR,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_TEXT_DIRECTION] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_TEXT_DIRECTION,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:has-pointer:
|
|
*
|
|
* Whether the actor contains the pointer of a #ClutterInputDevice
|
|
* or not.
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
pspec = g_param_spec_boolean ("has-pointer",
|
|
P_("Has Pointer"),
|
|
P_("Whether the actor contains the pointer "
|
|
"of an input device"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READABLE);
|
|
obj_props[PROP_HAS_POINTER] = pspec;
|
|
g_object_class_install_property (object_class,
|
|
PROP_HAS_POINTER,
|
|
pspec);
|
|
|
|
/**
|
|
* ClutterActor:actions:
|
|
*
|
|
* Adds a #ClutterAction to the actor
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_object ("actions",
|
|
P_("Actions"),
|
|
P_("Adds an action to the actor"),
|
|
CLUTTER_TYPE_ACTION,
|
|
CLUTTER_PARAM_WRITABLE);
|
|
obj_props[PROP_ACTIONS] = pspec;
|
|
g_object_class_install_property (object_class, PROP_ACTIONS, pspec);
|
|
|
|
/**
|
|
* ClutterActor:constraints:
|
|
*
|
|
* Adds a #ClutterConstraint to the actor
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_object ("constraints",
|
|
P_("Constraints"),
|
|
P_("Adds a constraint to the actor"),
|
|
CLUTTER_TYPE_CONSTRAINT,
|
|
CLUTTER_PARAM_WRITABLE);
|
|
obj_props[PROP_CONSTRAINTS] = pspec;
|
|
g_object_class_install_property (object_class, PROP_CONSTRAINTS, pspec);
|
|
|
|
/**
|
|
* ClutterActor:effect:
|
|
*
|
|
* Adds #ClutterEffect to the list of effects be applied on a #ClutterActor
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_object ("effect",
|
|
"Effect",
|
|
"Add an effect to be applied on the actor",
|
|
CLUTTER_TYPE_EFFECT,
|
|
CLUTTER_PARAM_WRITABLE);
|
|
obj_props[PROP_EFFECT] = pspec;
|
|
g_object_class_install_property (object_class, PROP_EFFECT, pspec);
|
|
|
|
/**
|
|
* ClutterActor::destroy:
|
|
* @actor: the object which received the signal
|
|
*
|
|
* The ::destroy signal is emitted when an actor is destroyed,
|
|
* either by direct invocation of clutter_actor_destroy() or
|
|
* when the #ClutterGroup that contains the actor is destroyed.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
actor_signals[DESTROY] =
|
|
g_signal_new (I_("destroy"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
G_STRUCT_OFFSET (ClutterActorClass, destroy),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
/**
|
|
* ClutterActor::show:
|
|
* @actor: the object which received the signal
|
|
*
|
|
* The ::show signal is emitted when an actor is visible and
|
|
* rendered on the stage.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
actor_signals[SHOW] =
|
|
g_signal_new (I_("show"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, show),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
/**
|
|
* ClutterActor::hide:
|
|
* @actor: the object which received the signal
|
|
*
|
|
* The ::hide signal is emitted when an actor is no longer rendered
|
|
* on the stage.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
actor_signals[HIDE] =
|
|
g_signal_new (I_("hide"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, hide),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
/**
|
|
* ClutterActor::parent-set:
|
|
* @actor: the object which received the signal
|
|
* @old_parent: the previous parent of the actor, or %NULL
|
|
*
|
|
* This signal is emitted when the parent of the actor changes.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
actor_signals[PARENT_SET] =
|
|
g_signal_new (I_("parent-set"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, parent_set),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_ACTOR);
|
|
|
|
/**
|
|
* ClutterActor::queue-redraw:
|
|
* @actor: the actor we're bubbling the redraw request through
|
|
* @origin: the actor which initiated the redraw request
|
|
*
|
|
* The ::queue_redraw signal is emitted when clutter_actor_queue_redraw()
|
|
* is called on @origin.
|
|
*
|
|
* The default implementation for #ClutterActor chains up to the
|
|
* parent actor and queues a redraw on the parent, thus "bubbling"
|
|
* the redraw queue up through the actor graph. The default
|
|
* implementation for #ClutterStage queues a clutter_redraw() in a
|
|
* main loop idle handler.
|
|
*
|
|
* Note that the @origin actor may be the stage, or a container; it
|
|
* does not have to be a leaf node in the actor graph.
|
|
*
|
|
* Toolkits embedding a #ClutterStage which require a redraw and
|
|
* relayout cycle can stop the emission of this signal using the
|
|
* GSignal API, redraw the UI and then call clutter_redraw()
|
|
* themselves, like:
|
|
*
|
|
* |[
|
|
* static void
|
|
* on_redraw_complete (void)
|
|
* {
|
|
* /* execute the Clutter drawing pipeline */
|
|
* clutter_redraw ();
|
|
* }
|
|
*
|
|
* static void
|
|
* on_stage_queue_redraw (ClutterStage *stage)
|
|
* {
|
|
* /* this prevents the default handler to run */
|
|
* g_signal_stop_emission_by_name (stage, "queue-redraw");
|
|
*
|
|
* /* queue a redraw with the host toolkit and call
|
|
* * a function when the redraw has been completed
|
|
* */
|
|
* queue_a_redraw (G_CALLBACK (on_redraw_complete));
|
|
* }
|
|
* ]|
|
|
*
|
|
* <note><para>This signal is emitted before the Clutter paint
|
|
* pipeline is executed. If you want to know when the pipeline has
|
|
* been completed you should connect to the ::paint signal on the
|
|
* Stage with g_signal_connect_after().</para></note>
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
actor_signals[QUEUE_REDRAW] =
|
|
g_signal_new (I_("queue-redraw"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, queue_redraw),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_ACTOR);
|
|
|
|
/**
|
|
* ClutterActor::queue-relayout
|
|
* @actor: the actor being queued for relayout
|
|
*
|
|
* The ::queue_layout signal is emitted when clutter_actor_queue_relayout()
|
|
* is called on an actor.
|
|
*
|
|
* The default implementation for #ClutterActor chains up to the
|
|
* parent actor and queues a relayout on the parent, thus "bubbling"
|
|
* the relayout queue up through the actor graph.
|
|
*
|
|
* The main purpose of this signal is to allow relayout to be propagated
|
|
* properly in the procense of #ClutterClone actors. Applications will
|
|
* not normally need to connect to this signal.
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
actor_signals[QUEUE_RELAYOUT] =
|
|
g_signal_new (I_("queue-relayout"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, queue_relayout),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/**
|
|
* ClutterActor::event:
|
|
* @actor: the actor which received the event
|
|
* @event: a #ClutterEvent
|
|
*
|
|
* The ::event signal is emitted each time an event is received
|
|
* by the @actor. This signal will be emitted on every actor,
|
|
* following the hierarchy chain, until it reaches the top-level
|
|
* container (the #ClutterStage).
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[EVENT] =
|
|
g_signal_new (I_("event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
/**
|
|
* ClutterActor::button-press-event:
|
|
* @actor: the actor which received the event
|
|
* @event: a #ClutterButtonEvent
|
|
*
|
|
* The ::button-press-event signal is emitted each time a mouse button
|
|
* is pressed on @actor.
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[BUTTON_PRESS_EVENT] =
|
|
g_signal_new (I_("button-press-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, button_press_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
/**
|
|
* ClutterActor::button-release-event:
|
|
* @actor: the actor which received the event
|
|
* @event: a #ClutterButtonEvent
|
|
*
|
|
* The ::button-release-event signal is emitted each time a mouse button
|
|
* is released on @actor.
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[BUTTON_RELEASE_EVENT] =
|
|
g_signal_new (I_("button-release-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, button_release_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
/**
|
|
* ClutterActor::scroll-event:
|
|
* @actor: the actor which received the event
|
|
* @event: a #ClutterScrollEvent
|
|
*
|
|
* The ::scroll-event signal is emitted each time the mouse is
|
|
* scrolled on @actor
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[SCROLL_EVENT] =
|
|
g_signal_new (I_("scroll-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, scroll_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
/**
|
|
* ClutterActor::key-press-event:
|
|
* @actor: the actor which received the event
|
|
* @event: a #ClutterKeyEvent
|
|
*
|
|
* The ::key-press-event signal is emitted each time a keyboard button
|
|
* is pressed while @actor has key focus (see clutter_stage_set_key_focus()).
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[KEY_PRESS_EVENT] =
|
|
g_signal_new (I_("key-press-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, key_press_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
/**
|
|
* ClutterActor::key-release-event:
|
|
* @actor: the actor which received the event
|
|
* @event: a #ClutterKeyEvent
|
|
*
|
|
* The ::key-release-event signal is emitted each time a keyboard button
|
|
* is released while @actor has key focus (see
|
|
* clutter_stage_set_key_focus()).
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[KEY_RELEASE_EVENT] =
|
|
g_signal_new (I_("key-release-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, key_release_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
/**
|
|
* ClutterActor::motion-event:
|
|
* @actor: the actor which received the event
|
|
* @event: a #ClutterMotionEvent
|
|
*
|
|
* The ::motion-event signal is emitted each time the mouse pointer is
|
|
* moved over @actor.
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[MOTION_EVENT] =
|
|
g_signal_new (I_("motion-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, motion_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
/**
|
|
* ClutterActor::key-focus-in:
|
|
* @actor: the actor which now has key focus
|
|
*
|
|
* The ::key-focus-in signal is emitted when @actor receives key focus.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[KEY_FOCUS_IN] =
|
|
g_signal_new (I_("key-focus-in"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, key_focus_in),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/**
|
|
* ClutterActor::key-focus-out:
|
|
* @actor: the actor which now has key focus
|
|
*
|
|
* The ::key-focus-out signal is emitted when @actor loses key focus.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[KEY_FOCUS_OUT] =
|
|
g_signal_new (I_("key-focus-out"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, key_focus_out),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/**
|
|
* ClutterActor::enter-event:
|
|
* @actor: the actor which the pointer has entered.
|
|
* @event: a #ClutterCrossingEvent
|
|
*
|
|
* The ::enter-event signal is emitted when the pointer enters the @actor
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[ENTER_EVENT] =
|
|
g_signal_new (I_("enter-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, enter_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
/**
|
|
* ClutterActor::leave-event:
|
|
* @actor: the actor which the pointer has left
|
|
* @event: a #ClutterCrossingEvent
|
|
*
|
|
* The ::leave-event signal is emitted when the pointer leaves the @actor.
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[LEAVE_EVENT] =
|
|
g_signal_new (I_("leave-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, leave_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
/**
|
|
* ClutterActor::captured-event:
|
|
* @actor: the actor which received the signal
|
|
* @event: a #ClutterEvent
|
|
*
|
|
* The ::captured-event signal is emitted when an event is captured
|
|
* by Clutter. This signal will be emitted starting from the top-level
|
|
* container (the #ClutterStage) to the actor which received the event
|
|
* going down the hierarchy. This signal can be used to intercept every
|
|
* event before the specialized events (like
|
|
* ClutterActor::button-press-event or ::key-released-event) are
|
|
* emitted.
|
|
*
|
|
* Return value: %TRUE if the event has been handled by the actor,
|
|
* or %FALSE to continue the emission.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
actor_signals[CAPTURED_EVENT] =
|
|
g_signal_new (I_("captured-event"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, captured_event),
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__BOXED,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
/**
|
|
* ClutterActor::paint:
|
|
* @actor: the #ClutterActor that received the signal
|
|
*
|
|
* The ::paint signal is emitted each time an actor is being painted.
|
|
*
|
|
* Subclasses of #ClutterActor should override the class signal handler
|
|
* and paint themselves in that function.
|
|
*
|
|
* It is possible to connect a handler to the ::paint signal in order
|
|
* to set up some custom aspect of a paint.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
actor_signals[PAINT] =
|
|
g_signal_new (I_("paint"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, paint),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
/**
|
|
* ClutterActor::realize:
|
|
* @actor: the #ClutterActor that received the signal
|
|
*
|
|
* The ::realize signal is emitted each time an actor is being
|
|
* realized.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
actor_signals[REALIZE] =
|
|
g_signal_new (I_("realize"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, realize),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
/**
|
|
* ClutterActor::unrealize:
|
|
* @actor: the #ClutterActor that received the signal
|
|
*
|
|
* The ::unrealize signal is emitted each time an actor is being
|
|
* unrealized.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
actor_signals[UNREALIZE] =
|
|
g_signal_new (I_("unrealize"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, unrealize),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/**
|
|
* ClutterActor::pick:
|
|
* @actor: the #ClutterActor that received the signal
|
|
* @color: the #ClutterColor to be used when picking
|
|
*
|
|
* The ::pick signal is emitted each time an actor is being painted
|
|
* in "pick mode". The pick mode is used to identify the actor during
|
|
* the event handling phase, or by clutter_stage_get_actor_at_pos().
|
|
* The actor should paint its shape using the passed @pick_color.
|
|
*
|
|
* Subclasses of #ClutterActor should override the class signal handler
|
|
* and paint themselves in that function.
|
|
*
|
|
* It is possible to connect a handler to the ::pick signal in order
|
|
* to set up some custom aspect of a paint in pick mode.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
actor_signals[PICK] =
|
|
g_signal_new (I_("pick"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterActorClass, pick),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__BOXED,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_COLOR | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
/**
|
|
* ClutterActor::allocation-changed:
|
|
* @actor: the #ClutterActor that emitted the signal
|
|
* @box: a #ClutterActorBox with the new allocation
|
|
* @flags: #ClutterAllocationFlags for the allocation
|
|
*
|
|
* The ::allocation-changed signal is emitted when the
|
|
* #ClutterActor:allocation property changes. Usually, application
|
|
* code should just use the notifications for the :allocation property
|
|
* but if you want to track the allocation flags as well, for instance
|
|
* to know whether the absolute origin of @actor changed, then you might
|
|
* want use this signal instead.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
actor_signals[ALLOCATION_CHANGED] =
|
|
g_signal_new (I_("allocation-changed"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__BOXED_FLAGS,
|
|
G_TYPE_NONE, 2,
|
|
CLUTTER_TYPE_ACTOR_BOX,
|
|
CLUTTER_TYPE_ALLOCATION_FLAGS);
|
|
|
|
klass->show = clutter_actor_real_show;
|
|
klass->show_all = clutter_actor_show;
|
|
klass->hide = clutter_actor_real_hide;
|
|
klass->hide_all = clutter_actor_hide;
|
|
klass->map = clutter_actor_real_map;
|
|
klass->unmap = clutter_actor_real_unmap;
|
|
klass->unrealize = clutter_actor_real_unrealize;
|
|
klass->pick = clutter_actor_real_pick;
|
|
klass->get_preferred_width = clutter_actor_real_get_preferred_width;
|
|
klass->get_preferred_height = clutter_actor_real_get_preferred_height;
|
|
klass->allocate = clutter_actor_real_allocate;
|
|
klass->queue_redraw = clutter_actor_real_queue_redraw;
|
|
klass->queue_relayout = clutter_actor_real_queue_relayout;
|
|
klass->apply_transform = clutter_actor_real_apply_transform;
|
|
klass->get_accessible = clutter_actor_real_get_accessible;
|
|
klass->get_paint_volume = clutter_actor_real_get_paint_volume;
|
|
}
|
|
|
|
static void
|
|
clutter_actor_init (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
self->priv = priv = CLUTTER_ACTOR_GET_PRIVATE (self);
|
|
|
|
priv->parent_actor = NULL;
|
|
priv->has_clip = FALSE;
|
|
priv->opacity = 0xff;
|
|
priv->id = clutter_id_pool_add (CLUTTER_CONTEXT()->id_pool, self);
|
|
priv->scale_x = 1.0;
|
|
priv->scale_y = 1.0;
|
|
priv->shader_data = NULL;
|
|
priv->show_on_set_parent = TRUE;
|
|
|
|
priv->needs_width_request = TRUE;
|
|
priv->needs_height_request = TRUE;
|
|
priv->needs_allocation = TRUE;
|
|
|
|
priv->cached_width_age = 1;
|
|
priv->cached_height_age = 1;
|
|
|
|
priv->opacity_parent = NULL;
|
|
priv->enable_model_view_transform = TRUE;
|
|
|
|
/* Initialize an empty paint box to start with */
|
|
memset (&priv->last_paint_box, 0, sizeof (ClutterActorBox));
|
|
priv->last_paint_box_valid = TRUE;
|
|
|
|
memset (priv->clip, 0, sizeof (gfloat) * 4);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_destroy:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Destroys an actor. When an actor is destroyed, it will break any
|
|
* references it holds to other objects. If the actor is inside a
|
|
* container, the actor will be removed.
|
|
*
|
|
* When you destroy a container, its children will be destroyed as well.
|
|
*
|
|
* Note: you cannot destroy the #ClutterStage returned by
|
|
* clutter_stage_get_default().
|
|
*/
|
|
void
|
|
clutter_actor_destroy (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
g_object_ref (self);
|
|
|
|
/* avoid recursion while destroying */
|
|
if (!CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
{
|
|
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
|
|
|
|
/* if we are destroying we want to unrealize ourselves
|
|
* first before the dispose run removes the parent
|
|
*/
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNREALIZED);
|
|
|
|
g_object_run_dispose (G_OBJECT (self));
|
|
|
|
CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
|
|
}
|
|
|
|
g_object_unref (self);
|
|
}
|
|
|
|
void
|
|
_clutter_actor_finish_queue_redraw (ClutterActor *self,
|
|
ClutterPaintVolume *clip)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
const ClutterPaintVolume *pv;
|
|
gboolean clipped;
|
|
|
|
/* If we've been explicitly passed a clip volume then there's
|
|
* nothing more to calculate, but otherwhise the only thing we know
|
|
* is that the change is constrained to the given actor.
|
|
*
|
|
* The idea is that if we know the paint box for where the actor was
|
|
* last drawn and we also have the paint volume for where it will be
|
|
* drawn next then if we queue a redraw for both these regions that
|
|
* will cover everything that needs to be redrawn to clear the old
|
|
* view and show the latest view of the actor.
|
|
*
|
|
* Don't clip this redraw if we don't know what position we had for
|
|
* the previous redraw since we don't know where to set the clip so
|
|
* it will clear the actor as it is currently.
|
|
*/
|
|
if (clip)
|
|
{
|
|
_clutter_actor_set_queue_redraw_clip (self, clip);
|
|
clipped = TRUE;
|
|
}
|
|
else if (G_LIKELY (priv->last_paint_box_valid))
|
|
{
|
|
pv = clutter_actor_get_paint_volume (self);
|
|
if (pv)
|
|
{
|
|
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
|
|
ClutterPaintVolume stage_pv;
|
|
ClutterActorBox *box = &priv->last_paint_box;
|
|
ClutterVertex origin;
|
|
|
|
_clutter_paint_volume_init_static (stage, &stage_pv);
|
|
|
|
origin.x = box->x1;
|
|
origin.y = box->y1;
|
|
origin.z = 0;
|
|
clutter_paint_volume_set_origin (&stage_pv, &origin);
|
|
clutter_paint_volume_set_width (&stage_pv, box->x2 - box->x1);
|
|
clutter_paint_volume_set_height (&stage_pv, box->y2 - box->y1);
|
|
|
|
/* make sure we redraw the actors old position... */
|
|
_clutter_actor_set_queue_redraw_clip (stage, &stage_pv);
|
|
_clutter_actor_signal_queue_redraw (stage, stage);
|
|
_clutter_actor_set_queue_redraw_clip (stage, NULL);
|
|
|
|
clutter_paint_volume_free (&stage_pv);
|
|
|
|
/* XXX: Ideally the redraw signal would take a clip volume
|
|
* argument, but that would be an ABI break. Until we can
|
|
* break the ABI we pass the argument out-of-band via an
|
|
* actor->priv member...
|
|
*/
|
|
|
|
/* setup the clip for the actors new position... */
|
|
_clutter_actor_set_queue_redraw_clip (self, pv);
|
|
clipped = TRUE;
|
|
}
|
|
else
|
|
clipped = FALSE;
|
|
}
|
|
else
|
|
clipped = FALSE;
|
|
|
|
_clutter_actor_signal_queue_redraw (self, self);
|
|
|
|
/* Just in case anyone is manually firing redraw signals without
|
|
* using the public queue_redraw() API we are careful to ensure that
|
|
* our out-of-band clip member is cleared before returning...
|
|
*
|
|
* Note: A NULL clip denotes a full-stage, un-clipped redraw
|
|
*/
|
|
if (G_LIKELY (clipped))
|
|
_clutter_actor_set_queue_redraw_clip (self, NULL);
|
|
|
|
priv->queue_redraw_entry = NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_queue_redraw:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Queues up a redraw of an actor and any children. The redraw occurs
|
|
* once the main loop becomes idle (after the current batch of events
|
|
* has been processed, roughly).
|
|
*
|
|
* Applications rarely need to call this, as redraws are handled
|
|
* automatically by modification functions.
|
|
*
|
|
* This function will not do anything if @self is not visible, or
|
|
* if the actor is inside an invisible part of the scenegraph.
|
|
*
|
|
* Also be aware that painting is a NOP for actors with an opacity of
|
|
* 0
|
|
*
|
|
* When you are implementing a custom actor you must queue a redraw
|
|
* whenever some private state changes that will affect painting or
|
|
* picking of your actor.
|
|
*/
|
|
void
|
|
clutter_actor_queue_redraw (ClutterActor *self)
|
|
{
|
|
ClutterActor *stage;
|
|
|
|
/* Here's an outline of the actor queue redraw mechanism:
|
|
*
|
|
* The process starts either here or in
|
|
* _clutter_actor_queue_redraw_with_clip.
|
|
*
|
|
* These functions queue an entry in a list associated with the
|
|
* stage which is a list of actors that queued a redraw while
|
|
* updating the timelines, performing layouting and processing other
|
|
* mainloop sources before the next paint starts.
|
|
*
|
|
* We aim to minimize the processing done at this point because
|
|
* there is a good chance other events will happen while updating
|
|
* the scenegraph that would invalidate any expensive work we might
|
|
* otherwise try to do here. For example we don't try and resolve
|
|
* the screen space bounding box of an actor at this stage so as to
|
|
* minimize how much of the screen redraw because it's possible
|
|
* something else will happen which will force a full redraw anyway.
|
|
*
|
|
* When all updates are complete and we come to paint the stage then
|
|
* we iterate this list and actually emit the "queue-redraw" signals
|
|
* for each of the listed actors which will bubble up to the stage
|
|
* for each actor and at that point we will transform the actors
|
|
* paint volume into screen coordinates to determine the clip region
|
|
* for what needs to be redrawn in the next paint.
|
|
*
|
|
* Besides minimizing redundant work another reason for this
|
|
* deferred design is that it's more likely we will be able to
|
|
* determine the paint volume of an actor once we've finished
|
|
* updating the scenegraph because its allocation should be up to
|
|
* date. NB: If we can't determine an actors paint volume then we
|
|
* can't automatically queue a clipped redraw which can make a big
|
|
* difference to performance.
|
|
*
|
|
* So the control flow goes like this:
|
|
* clutter_actor_queue_redraw and
|
|
* _clutter_actor_queue_redraw_with_clip
|
|
*
|
|
* then control moves to:
|
|
* _clutter_stage_queue_actor_redraw
|
|
*
|
|
* later during _clutter_stage_do_update, once relayouting is done
|
|
* and the scenegraph has been updated we will call:
|
|
* _clutter_stage_finish_queue_redraws
|
|
*
|
|
* _clutter_stage_finish_queue_redraws will call
|
|
* _clutter_actor_finish_queue_redraw for each listed actor.
|
|
* Note: actors *are* allowed to queue further redraws during this
|
|
* process (considering clone actors or texture_new_from_actor which
|
|
* respond to their source queueing a redraw by queuing a redraw
|
|
* themselves). We repeat the process until the list is empty.
|
|
*
|
|
* This will result in the "queue-redraw" signal being fired for
|
|
* each actor which will pass control to the default signal handler:
|
|
* clutter_actor_real_queue_redraw
|
|
*
|
|
* This will bubble up to the stages handler:
|
|
* clutter_stage_real_queue_redraw
|
|
*
|
|
* clutter_stage_real_queue_redraw will transform the actors paint
|
|
* volume into screen space and add it as a clip region for the next
|
|
* paint.
|
|
*/
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
/* Ignore queuing a redraw for actors not descended from a stage */
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
if (stage == NULL)
|
|
return;
|
|
|
|
self->priv->queue_redraw_entry =
|
|
_clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
|
|
self->priv->queue_redraw_entry,
|
|
self,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
_clutter_actor_get_allocation_clip (ClutterActor *self,
|
|
ClutterActorBox *clip)
|
|
{
|
|
ClutterActorBox allocation;
|
|
|
|
/* XXX: we don't care if we get an out of date allocation here
|
|
* because clutter_actor_queue_redraw_with_clip knows to ignore
|
|
* the clip if the actor's allocation is invalid.
|
|
*
|
|
* This is noted because clutter_actor_get_allocation_box does some
|
|
* unnecessary work to support buggy code with a comment suggesting
|
|
* that it could be changed later which would be good for this use
|
|
* case!
|
|
*/
|
|
clutter_actor_get_allocation_box (self, &allocation);
|
|
|
|
/* NB: clutter_actor_queue_redraw_with_clip expects a box in the
|
|
* actor's own coordinate space but the allocation is in parent
|
|
* coordinates */
|
|
clip->x1 = 0;
|
|
clip->y1 = 0;
|
|
clip->x2 = allocation.x2 - allocation.x1;
|
|
clip->y2 = allocation.y2 - allocation.y1;
|
|
}
|
|
|
|
/*
|
|
* clutter_actor_queue_redraw_with_clip:
|
|
* @self: A #ClutterActor
|
|
* @flags: A mask of #ClutterRedrawFlags controlling the behaviour of
|
|
* this queue redraw.
|
|
* @volume: A #ClutterPaintVolume describing the bounds of what needs to be
|
|
* redrawn or %NULL if you are just using a @flag to state your
|
|
* desired clipping.
|
|
*
|
|
* Queues up a clipped redraw of an actor and any children. The redraw
|
|
* occurs once the main loop becomes idle (after the current batch of
|
|
* events has been processed, roughly).
|
|
*
|
|
* If no flags are given the clip volume is defined by @volume
|
|
* specified in actor coordinates and tells Clutter that only content
|
|
* within this volume has been changed so Clutter can optionally
|
|
* optimize the redraw.
|
|
*
|
|
* If the %CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION @flag is used, @volume
|
|
* should be %NULL and this tells Clutter to use the actor's current
|
|
* allocation as a clip box. This flag can only be used for 2D actors,
|
|
* because any actor with depth may be projected outside its
|
|
* allocation.
|
|
*
|
|
* Applications rarely need to call this, as redraws are handled
|
|
* automatically by modification functions.
|
|
*
|
|
* This function will not do anything if @self is not visible, or if
|
|
* the actor is inside an invisible part of the scenegraph.
|
|
*
|
|
* Also be aware that painting is a NOP for actors with an opacity of
|
|
* 0
|
|
*
|
|
* When you are implementing a custom actor you must queue a redraw
|
|
* whenever some private state changes that will affect painting or
|
|
* picking of your actor.
|
|
*/
|
|
void
|
|
_clutter_actor_queue_redraw_with_clip (ClutterActor *self,
|
|
ClutterRedrawFlags flags,
|
|
ClutterPaintVolume *volume)
|
|
{
|
|
ClutterPaintVolume allocation_pv;
|
|
ClutterPaintVolume *pv;
|
|
gboolean should_free_pv;
|
|
ClutterActor *stage;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION)
|
|
{
|
|
ClutterActorBox allocation_clip;
|
|
ClutterVertex origin;
|
|
|
|
/* If the actor doesn't have a valid allocation then we will
|
|
* queue a full stage redraw. */
|
|
if (self->priv->needs_allocation)
|
|
{
|
|
/* NB: NULL denotes an undefined clip which will result in a
|
|
* full redraw... */
|
|
_clutter_actor_set_queue_redraw_clip (self, NULL);
|
|
_clutter_actor_signal_queue_redraw (self, self);
|
|
return;
|
|
}
|
|
|
|
_clutter_paint_volume_init_static (self, &allocation_pv);
|
|
pv = &allocation_pv;
|
|
|
|
_clutter_actor_get_allocation_clip (self, &allocation_clip);
|
|
|
|
origin.x = allocation_clip.x1;
|
|
origin.y = allocation_clip.y1;
|
|
origin.z = 0;
|
|
clutter_paint_volume_set_origin (pv, &origin);
|
|
clutter_paint_volume_set_width (pv,
|
|
allocation_clip.x2 - allocation_clip.x1);
|
|
clutter_paint_volume_set_height (pv,
|
|
allocation_clip.y2 -
|
|
allocation_clip.y1);
|
|
should_free_pv = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pv = volume;
|
|
should_free_pv = FALSE;
|
|
}
|
|
|
|
/* Ignore queuing a redraw for actors not descended from a stage */
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
if (stage == NULL)
|
|
return;
|
|
|
|
_clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
|
|
self->priv->queue_redraw_entry,
|
|
self,
|
|
pv);
|
|
}
|
|
|
|
static void
|
|
_clutter_actor_queue_only_relayout (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
return;
|
|
|
|
if (priv->needs_width_request &&
|
|
priv->needs_height_request &&
|
|
priv->needs_allocation)
|
|
return; /* save some cpu cycles */
|
|
|
|
#if CLUTTER_ENABLE_DEBUG
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) && CLUTTER_ACTOR_IN_RELAYOUT (self))
|
|
{
|
|
g_warning ("The actor '%s' is currently inside an allocation "
|
|
"cycle; calling clutter_actor_queue_relayout() is "
|
|
"not recommended",
|
|
_clutter_actor_get_debug_name (self));
|
|
}
|
|
#endif /* CLUTTER_ENABLE_DEBUG */
|
|
|
|
g_signal_emit (self, actor_signals[QUEUE_RELAYOUT], 0);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_queue_relayout:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Indicates that the actor's size request or other layout-affecting
|
|
* properties may have changed. This function is used inside #ClutterActor
|
|
* subclass implementations, not by applications directly.
|
|
*
|
|
* Queueing a new layout automatically queues a redraw as well.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_queue_relayout (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
_clutter_actor_queue_only_relayout (self);
|
|
clutter_actor_queue_redraw (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_preferred_size:
|
|
* @self: a #ClutterActor
|
|
* @min_width_p: (out) (allow-none): return location for the minimum
|
|
* width, or %NULL
|
|
* @min_height_p: (out) (allow-none): return location for the minimum
|
|
* height, or %NULL
|
|
* @natural_width_p: (out) (allow-none): return location for the natural
|
|
* width, or %NULL
|
|
* @natural_height_p: (out) (allow-none): return location for the natural
|
|
* height, or %NULL
|
|
*
|
|
* Computes the preferred minimum and natural size of an actor, taking into
|
|
* account the actor's geometry management (either height-for-width
|
|
* or width-for-height).
|
|
*
|
|
* The width and height used to compute the preferred height and preferred
|
|
* width are the actor's natural ones.
|
|
*
|
|
* If you need to control the height for the preferred width, or the width for
|
|
* the preferred height, you should use clutter_actor_get_preferred_width()
|
|
* and clutter_actor_get_preferred_height(), and check the actor's preferred
|
|
* geometry management using the #ClutterActor:request-mode property.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_get_preferred_size (ClutterActor *self,
|
|
gfloat *min_width_p,
|
|
gfloat *min_height_p,
|
|
gfloat *natural_width_p,
|
|
gfloat *natural_height_p)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
gfloat min_width, min_height;
|
|
gfloat natural_width, natural_height;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
min_width = min_height = 0;
|
|
natural_width = natural_height = 0;
|
|
|
|
if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
|
{
|
|
CLUTTER_NOTE (LAYOUT, "Preferred size (height-for-width)");
|
|
clutter_actor_get_preferred_width (self, -1,
|
|
&min_width,
|
|
&natural_width);
|
|
clutter_actor_get_preferred_height (self, natural_width,
|
|
&min_height,
|
|
&natural_height);
|
|
}
|
|
else
|
|
{
|
|
CLUTTER_NOTE (LAYOUT, "Preferred size (width-for-height)");
|
|
clutter_actor_get_preferred_height (self, -1,
|
|
&min_height,
|
|
&natural_height);
|
|
clutter_actor_get_preferred_width (self, natural_height,
|
|
&min_width,
|
|
&natural_width);
|
|
}
|
|
|
|
if (min_width_p)
|
|
*min_width_p = min_width;
|
|
|
|
if (min_height_p)
|
|
*min_height_p = min_height;
|
|
|
|
if (natural_width_p)
|
|
*natural_width_p = natural_width;
|
|
|
|
if (natural_height_p)
|
|
*natural_height_p = natural_height;
|
|
}
|
|
|
|
/* looks for a cached size request for this for_size. If not
|
|
* found, returns the oldest entry so it can be overwritten */
|
|
static gboolean
|
|
_clutter_actor_get_cached_size_request (gfloat for_size,
|
|
SizeRequest *cached_size_requests,
|
|
SizeRequest **result)
|
|
{
|
|
guint i;
|
|
|
|
*result = &cached_size_requests[0];
|
|
|
|
for (i = 0; i < N_CACHED_SIZE_REQUESTS; i++)
|
|
{
|
|
SizeRequest *sr;
|
|
|
|
sr = &cached_size_requests[i];
|
|
|
|
if (sr->age > 0 &&
|
|
sr->for_size == for_size)
|
|
{
|
|
CLUTTER_NOTE (LAYOUT, "Size cache hit for size: %.2f", for_size);
|
|
*result = sr;
|
|
return TRUE;
|
|
}
|
|
else if (sr->age < (*result)->age)
|
|
{
|
|
*result = sr;
|
|
}
|
|
}
|
|
|
|
CLUTTER_NOTE (LAYOUT, "Size cache miss for size: %.2f", for_size);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_preferred_width:
|
|
* @self: A #ClutterActor
|
|
* @for_height: available height when computing the preferred width,
|
|
* or a negative value to indicate that no height is defined
|
|
* @min_width_p: (out) (allow-none): return location for minimum width,
|
|
* or %NULL
|
|
* @natural_width_p: (out) (allow-none): return location for the natural
|
|
* width, or %NULL
|
|
*
|
|
* Computes the requested minimum and natural widths for an actor,
|
|
* optionally depending on the specified height, or if they are
|
|
* already computed, returns the cached values.
|
|
*
|
|
* An actor may not get its request - depending on the layout
|
|
* manager that's in effect.
|
|
*
|
|
* A request should not incorporate the actor's scale or anchor point;
|
|
* those transformations do not affect layout, only rendering.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_get_preferred_width (ClutterActor *self,
|
|
gfloat for_height,
|
|
gfloat *min_width_p,
|
|
gfloat *natural_width_p)
|
|
{
|
|
ClutterActorClass *klass;
|
|
ClutterActorPrivate *priv;
|
|
gboolean found_in_cache;
|
|
SizeRequest *cached_size_request;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
klass = CLUTTER_ACTOR_GET_CLASS (self);
|
|
priv = self->priv;
|
|
|
|
found_in_cache = FALSE;
|
|
cached_size_request = &priv->width_requests[0];
|
|
|
|
if (!priv->needs_width_request)
|
|
found_in_cache = _clutter_actor_get_cached_size_request (for_height,
|
|
priv->width_requests,
|
|
&cached_size_request);
|
|
|
|
if (!found_in_cache)
|
|
{
|
|
gfloat min_width, natural_width;
|
|
|
|
min_width = natural_width = 0;
|
|
|
|
CLUTTER_NOTE (LAYOUT, "Width request for %.2f px", for_height);
|
|
|
|
klass->get_preferred_width (self, for_height,
|
|
&min_width,
|
|
&natural_width);
|
|
|
|
/* Due to accumulated float errors, it's better not to warn
|
|
* on this, but just fix it.
|
|
*/
|
|
if (natural_width < min_width)
|
|
natural_width = min_width;
|
|
|
|
cached_size_request->min_size = min_width;
|
|
cached_size_request->natural_size = natural_width;
|
|
cached_size_request->for_size = for_height;
|
|
cached_size_request->age = priv->cached_width_age;
|
|
|
|
priv->cached_width_age ++;
|
|
priv->needs_width_request = FALSE;
|
|
}
|
|
|
|
if (!priv->min_width_set)
|
|
priv->request_min_width = cached_size_request->min_size;
|
|
|
|
if (!priv->natural_width_set)
|
|
priv->request_natural_width = cached_size_request->natural_size;
|
|
|
|
if (min_width_p)
|
|
*min_width_p = priv->request_min_width;
|
|
|
|
if (natural_width_p)
|
|
*natural_width_p = priv->request_natural_width;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_preferred_height:
|
|
* @self: A #ClutterActor
|
|
* @for_width: available width to assume in computing desired height,
|
|
* or a negative value to indicate that no width is defined
|
|
* @min_height_p: (out) (allow-none): return location for minimum height,
|
|
* or %NULL
|
|
* @natural_height_p: (out) (allow-none): return location for natural
|
|
* height, or %NULL
|
|
*
|
|
* Computes the requested minimum and natural heights for an actor,
|
|
* or if they are already computed, returns the cached values.
|
|
*
|
|
* An actor may not get its request - depending on the layout
|
|
* manager that's in effect.
|
|
*
|
|
* A request should not incorporate the actor's scale or anchor point;
|
|
* those transformations do not affect layout, only rendering.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_get_preferred_height (ClutterActor *self,
|
|
gfloat for_width,
|
|
gfloat *min_height_p,
|
|
gfloat *natural_height_p)
|
|
{
|
|
ClutterActorClass *klass;
|
|
ClutterActorPrivate *priv;
|
|
gboolean found_in_cache;
|
|
SizeRequest *cached_size_request;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
klass = CLUTTER_ACTOR_GET_CLASS (self);
|
|
priv = self->priv;
|
|
|
|
found_in_cache = FALSE;
|
|
cached_size_request = &priv->height_requests[0];
|
|
|
|
if (!priv->needs_height_request)
|
|
found_in_cache = _clutter_actor_get_cached_size_request (for_width,
|
|
priv->height_requests,
|
|
&cached_size_request);
|
|
|
|
if (!found_in_cache)
|
|
{
|
|
gfloat min_height, natural_height;
|
|
|
|
min_height = natural_height = 0;
|
|
|
|
CLUTTER_NOTE (LAYOUT, "Height request for %.2f px", for_width);
|
|
|
|
klass->get_preferred_height (self, for_width,
|
|
&min_height,
|
|
&natural_height);
|
|
|
|
/* Due to accumulated float errors, it's better not to warn
|
|
* on this, but just fix it.
|
|
*/
|
|
if (natural_height < min_height)
|
|
natural_height = min_height;
|
|
|
|
if (!priv->min_height_set)
|
|
{
|
|
priv->request_min_height = min_height;
|
|
}
|
|
|
|
if (!priv->natural_height_set)
|
|
{
|
|
priv->request_natural_height = natural_height;
|
|
}
|
|
|
|
cached_size_request->min_size = min_height;
|
|
cached_size_request->natural_size = natural_height;
|
|
cached_size_request->for_size = for_width;
|
|
cached_size_request->age = priv->cached_height_age;
|
|
|
|
priv->cached_height_age ++;
|
|
|
|
priv->needs_height_request = FALSE;
|
|
}
|
|
|
|
if (!priv->min_height_set)
|
|
priv->request_min_height = cached_size_request->min_size;
|
|
|
|
if (!priv->natural_height_set)
|
|
priv->request_natural_height = cached_size_request->natural_size;
|
|
|
|
if (min_height_p)
|
|
*min_height_p = priv->request_min_height;
|
|
|
|
if (natural_height_p)
|
|
*natural_height_p = priv->request_natural_height;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_allocation_box:
|
|
* @self: A #ClutterActor
|
|
* @box: (out): the function fills this in with the actor's allocation
|
|
*
|
|
* Gets the layout box an actor has been assigned. The allocation can
|
|
* only be assumed valid inside a paint() method; anywhere else, it
|
|
* may be out-of-date.
|
|
*
|
|
* An allocation does not incorporate the actor's scale or anchor point;
|
|
* those transformations do not affect layout, only rendering.
|
|
*
|
|
* <note>Do not call any of the clutter_actor_get_allocation_*() family
|
|
* of functions inside the implementation of the get_preferred_width()
|
|
* or get_preferred_height() virtual functions.</note>
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_get_allocation_box (ClutterActor *self,
|
|
ClutterActorBox *box)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
/* XXX - if needs_allocation=TRUE, we can either 1) g_return_if_fail,
|
|
* which limits calling get_allocation to inside paint() basically; or
|
|
* we can 2) force a layout, which could be expensive if someone calls
|
|
* get_allocation somewhere silly; or we can 3) just return the latest
|
|
* value, allowing it to be out-of-date, and assume people know what
|
|
* they are doing.
|
|
*
|
|
* The least-surprises approach that keeps existing code working is
|
|
* likely to be 2). People can end up doing some inefficient things,
|
|
* though, and in general code that requires 2) is probably broken.
|
|
*/
|
|
|
|
/* this implements 2) */
|
|
if (G_UNLIKELY (self->priv->needs_allocation))
|
|
{
|
|
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
|
|
|
|
/* do not queue a relayout on an unparented actor */
|
|
if (stage)
|
|
_clutter_stage_maybe_relayout (stage);
|
|
}
|
|
|
|
/* commenting out the code above and just keeping this assigment
|
|
* implements 3)
|
|
*/
|
|
*box = self->priv->allocation;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_allocation_geometry:
|
|
* @self: A #ClutterActor
|
|
* @geom: (out): allocation geometry in pixels
|
|
*
|
|
* Gets the layout box an actor has been assigned. The allocation can
|
|
* only be assumed valid inside a paint() method; anywhere else, it
|
|
* may be out-of-date.
|
|
*
|
|
* An allocation does not incorporate the actor's scale or anchor point;
|
|
* those transformations do not affect layout, only rendering.
|
|
*
|
|
* The returned rectangle is in pixels.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_get_allocation_geometry (ClutterActor *self,
|
|
ClutterGeometry *geom)
|
|
{
|
|
ClutterActorBox box;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (geom != NULL);
|
|
|
|
clutter_actor_get_allocation_box (self, &box);
|
|
|
|
geom->x = clutter_actor_box_get_x (&box);
|
|
geom->y = clutter_actor_box_get_y (&box);
|
|
geom->width = clutter_actor_box_get_width (&box);
|
|
geom->height = clutter_actor_box_get_height (&box);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_allocate:
|
|
* @self: A #ClutterActor
|
|
* @box: new allocation of the actor, in parent-relative coordinates
|
|
* @flags: flags that control the allocation
|
|
*
|
|
* Called by the parent of an actor to assign the actor its size.
|
|
* Should never be called by applications (except when implementing
|
|
* a container or layout manager).
|
|
*
|
|
* Actors can know from their allocation box whether they have moved
|
|
* with respect to their parent actor. The @flags parameter describes
|
|
* additional information about the allocation, for instance whether
|
|
* the parent has moved with respect to the stage, for example because
|
|
* a grandparent's origin has moved.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_allocate (ClutterActor *self,
|
|
const ClutterActorBox *box,
|
|
ClutterAllocationFlags flags)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorClass *klass;
|
|
ClutterActorBox alloc;
|
|
gboolean child_moved;
|
|
gboolean stage_allocation_changed;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL))
|
|
{
|
|
g_warning ("Spurious clutter_actor_allocate called for actor %p/%s "
|
|
"which isn't a descendent of the stage!\n",
|
|
self, G_OBJECT_TYPE_NAME (self));
|
|
return;
|
|
}
|
|
|
|
priv = self->priv;
|
|
|
|
alloc = *box;
|
|
|
|
if (priv->constraints != NULL)
|
|
{
|
|
const GList *constraints, *l;
|
|
|
|
constraints = _clutter_meta_group_peek_metas (priv->constraints);
|
|
for (l = constraints; l != NULL; l = l->next)
|
|
{
|
|
ClutterConstraint *constraint = l->data;
|
|
ClutterActorMeta *meta = l->data;
|
|
|
|
if (clutter_actor_meta_get_enabled (meta))
|
|
_clutter_constraint_update_allocation (constraint, self, &alloc);
|
|
}
|
|
}
|
|
|
|
child_moved = (alloc.x1 != priv->allocation.x1 ||
|
|
alloc.y1 != priv->allocation.y1);
|
|
|
|
if (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED ||
|
|
child_moved ||
|
|
alloc.x2 != priv->allocation.x2 ||
|
|
alloc.y2 != priv->allocation.y2)
|
|
stage_allocation_changed = TRUE;
|
|
else
|
|
stage_allocation_changed = FALSE;
|
|
|
|
/* If we get an allocation "out of the blue"
|
|
* (we did not queue relayout), then we want to
|
|
* ignore it. But if we have needs_allocation set,
|
|
* we want to guarantee that allocate() virtual
|
|
* method is always called, i.e. that queue_relayout()
|
|
* always results in an allocate() invocation on
|
|
* an actor.
|
|
*
|
|
* The optimization here is to avoid re-allocating
|
|
* actors that did not queue relayout and were
|
|
* not moved.
|
|
*/
|
|
|
|
if (!priv->needs_allocation && !stage_allocation_changed)
|
|
{
|
|
CLUTTER_NOTE (LAYOUT, "No allocation needed");
|
|
return;
|
|
}
|
|
|
|
/* When ABSOLUTE_ORIGIN_CHANGED is passed in to
|
|
* clutter_actor_allocate(), it indicates whether the parent has its
|
|
* absolute origin moved; when passed in to ClutterActor::allocate()
|
|
* virtual method though, it indicates whether the child has its
|
|
* absolute origin moved. So we set it when child_moved is TRUE
|
|
*/
|
|
if (child_moved)
|
|
flags |= CLUTTER_ABSOLUTE_ORIGIN_CHANGED;
|
|
|
|
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
|
|
|
|
klass = CLUTTER_ACTOR_GET_CLASS (self);
|
|
klass->allocate (self, &alloc, flags);
|
|
|
|
CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
|
|
|
|
if (stage_allocation_changed)
|
|
clutter_actor_queue_redraw (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_geometry:
|
|
* @self: A #ClutterActor
|
|
* @geometry: A #ClutterGeometry
|
|
*
|
|
* Sets the actor's fixed position and forces its minimum and natural
|
|
* size, in pixels. This means the untransformed actor will have the
|
|
* given geometry. This is the same as calling clutter_actor_set_position()
|
|
* and clutter_actor_set_size().
|
|
*/
|
|
void
|
|
clutter_actor_set_geometry (ClutterActor *self,
|
|
const ClutterGeometry *geometry)
|
|
{
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_position (self, geometry->x, geometry->y);
|
|
clutter_actor_set_size (self, geometry->width, geometry->height);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_geometry:
|
|
* @self: A #ClutterActor
|
|
* @geometry: (out): A location to store actors #ClutterGeometry
|
|
*
|
|
* Gets the size and position of an actor relative to its parent
|
|
* actor. This is the same as calling clutter_actor_get_position() and
|
|
* clutter_actor_get_size(). It tries to "do what you mean" and get the
|
|
* requested size and position if the actor's allocation is invalid.
|
|
*/
|
|
void
|
|
clutter_actor_get_geometry (ClutterActor *self,
|
|
ClutterGeometry *geometry)
|
|
{
|
|
gfloat x, y, width, height;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (geometry != NULL);
|
|
|
|
clutter_actor_get_position (self, &x, &y);
|
|
clutter_actor_get_size (self, &width, &height);
|
|
|
|
geometry->x = (int) x;
|
|
geometry->y = (int) y;
|
|
geometry->width = (int) width;
|
|
geometry->height = (int) height;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_position
|
|
* @self: A #ClutterActor
|
|
* @x: New left position of actor in pixels.
|
|
* @y: New top position of actor in pixels.
|
|
*
|
|
* Sets the actor's fixed position in pixels relative to any parent
|
|
* actor.
|
|
*
|
|
* If a layout manager is in use, this position will override the
|
|
* layout manager and force a fixed position.
|
|
*/
|
|
void
|
|
clutter_actor_set_position (ClutterActor *self,
|
|
gfloat x,
|
|
gfloat y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_x (self, x);
|
|
clutter_actor_set_y (self, y);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_fixed_position_set:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Checks whether an actor has a fixed position set (and will thus be
|
|
* unaffected by any layout manager).
|
|
*
|
|
* Return value: %TRUE if the fixed position is set on the actor
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
gboolean
|
|
clutter_actor_get_fixed_position_set (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
return self->priv->position_set;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_fixed_position_set:
|
|
* @self: A #ClutterActor
|
|
* @is_set: whether to use fixed position
|
|
*
|
|
* Sets whether an actor has a fixed position set (and will thus be
|
|
* unaffected by any layout manager).
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_set_fixed_position_set (ClutterActor *self,
|
|
gboolean is_set)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (self->priv->position_set == (is_set != FALSE))
|
|
return;
|
|
|
|
self->priv->position_set = is_set != FALSE;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIXED_POSITION_SET]);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_move_by:
|
|
* @self: A #ClutterActor
|
|
* @dx: Distance to move Actor on X axis.
|
|
* @dy: Distance to move Actor on Y axis.
|
|
*
|
|
* Moves an actor by the specified distance relative to its current
|
|
* position in pixels.
|
|
*
|
|
* This function modifies the fixed position of an actor and thus removes
|
|
* it from any layout management. Another way to move an actor is with an
|
|
* anchor point, see clutter_actor_set_anchor_point().
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_move_by (ClutterActor *self,
|
|
gfloat dx,
|
|
gfloat dy)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
gfloat x, y;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
x = priv->fixed_x;
|
|
y = priv->fixed_y;
|
|
|
|
clutter_actor_set_position (self, x + dx, y + dy);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_min_width (ClutterActor *self,
|
|
gfloat min_width)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
/* if we are setting the size on a top-level actor and the
|
|
* backend only supports static top-levels (e.g. framebuffers)
|
|
* then we ignore the passed value and we override it with
|
|
* the stage implementation's preferred size.
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
|
|
clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
|
|
return;
|
|
|
|
if (priv->min_width_set && min_width == priv->request_min_width)
|
|
return;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->request_min_width = min_width;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH]);
|
|
clutter_actor_set_min_width_set (self, TRUE);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_min_height (ClutterActor *self,
|
|
gfloat min_height)
|
|
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
/* if we are setting the size on a top-level actor and the
|
|
* backend only supports static top-levels (e.g. framebuffers)
|
|
* then we ignore the passed value and we override it with
|
|
* the stage implementation's preferred size.
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
|
|
clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
|
|
return;
|
|
|
|
if (priv->min_height_set && min_height == priv->request_min_height)
|
|
return;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->request_min_height = min_height;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT]);
|
|
clutter_actor_set_min_height_set (self, TRUE);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_natural_width (ClutterActor *self,
|
|
gfloat natural_width)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
/* if we are setting the size on a top-level actor and the
|
|
* backend only supports static top-levels (e.g. framebuffers)
|
|
* then we ignore the passed value and we override it with
|
|
* the stage implementation's preferred size.
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
|
|
clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
|
|
return;
|
|
|
|
if (priv->natural_width_set &&
|
|
natural_width == priv->request_natural_width)
|
|
return;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->request_natural_width = natural_width;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH]);
|
|
clutter_actor_set_natural_width_set (self, TRUE);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_natural_height (ClutterActor *self,
|
|
gfloat natural_height)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
/* if we are setting the size on a top-level actor and the
|
|
* backend only supports static top-levels (e.g. framebuffers)
|
|
* then we ignore the passed value and we override it with
|
|
* the stage implementation's preferred size.
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
|
|
clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
|
|
return;
|
|
|
|
if (priv->natural_height_set &&
|
|
natural_height == priv->request_natural_height)
|
|
return;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->request_natural_height = natural_height;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT]);
|
|
clutter_actor_set_natural_height_set (self, TRUE);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_min_width_set (ClutterActor *self,
|
|
gboolean use_min_width)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
if (priv->min_width_set == (use_min_width != FALSE))
|
|
return;
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->min_width_set = use_min_width != FALSE;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH_SET]);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_min_height_set (ClutterActor *self,
|
|
gboolean use_min_height)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
if (priv->min_height_set == (use_min_height != FALSE))
|
|
return;
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->min_height_set = use_min_height != FALSE;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT_SET]);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_natural_width_set (ClutterActor *self,
|
|
gboolean use_natural_width)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
if (priv->natural_width_set == (use_natural_width != FALSE))
|
|
return;
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->natural_width_set = use_natural_width != FALSE;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH_SET]);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_natural_height_set (ClutterActor *self,
|
|
gboolean use_natural_height)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActorBox old = { 0, };
|
|
|
|
if (priv->natural_height_set == (use_natural_height != FALSE))
|
|
return;
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->natural_height_set = use_natural_height != FALSE;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT_SET]);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_request_mode:
|
|
* @self: a #ClutterActor
|
|
* @mode: the request mode
|
|
*
|
|
* Sets the geometry request mode of @self.
|
|
*
|
|
* The @mode determines the order for invoking
|
|
* clutter_actor_get_preferred_width() and
|
|
* clutter_actor_get_preferred_height()
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
void
|
|
clutter_actor_set_request_mode (ClutterActor *self,
|
|
ClutterRequestMode mode)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->request_mode == mode)
|
|
return;
|
|
|
|
priv->request_mode = mode;
|
|
|
|
priv->needs_width_request = TRUE;
|
|
priv->needs_height_request = TRUE;
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REQUEST_MODE]);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_request_mode:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the geometry request mode of @self
|
|
*
|
|
* Return value: the request mode for the actor
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
ClutterRequestMode
|
|
clutter_actor_get_request_mode (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
|
|
CLUTTER_REQUEST_HEIGHT_FOR_WIDTH);
|
|
|
|
return self->priv->request_mode;
|
|
}
|
|
|
|
/* variant of set_width() without checks and without notification
|
|
* freeze+thaw, for internal usage only
|
|
*/
|
|
static inline void
|
|
clutter_actor_set_width_internal (ClutterActor *self,
|
|
gfloat width)
|
|
{
|
|
if (width >= 0)
|
|
{
|
|
/* the Stage will use the :min-width to control the minimum
|
|
* width to be resized to, so we should not be setting it
|
|
* along with the :natural-width
|
|
*/
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
clutter_actor_set_min_width (self, width);
|
|
|
|
clutter_actor_set_natural_width (self, width);
|
|
}
|
|
else
|
|
{
|
|
/* we only unset the :natural-width for the Stage */
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
clutter_actor_set_min_width_set (self, FALSE);
|
|
|
|
clutter_actor_set_natural_width_set (self, FALSE);
|
|
}
|
|
}
|
|
|
|
/* variant of set_height() without checks and without notification
|
|
* freeze+thaw, for internal usage only
|
|
*/
|
|
static inline void
|
|
clutter_actor_set_height_internal (ClutterActor *self,
|
|
gfloat height)
|
|
{
|
|
if (height >= 0)
|
|
{
|
|
/* see the comment above in set_width_internal() */
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
clutter_actor_set_min_height (self, height);
|
|
|
|
clutter_actor_set_natural_height (self, height);
|
|
}
|
|
else
|
|
{
|
|
/* see the comment above in set_width_internal() */
|
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
clutter_actor_set_min_height_set (self, FALSE);
|
|
|
|
clutter_actor_set_natural_height_set (self, FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_size
|
|
* @self: A #ClutterActor
|
|
* @width: New width of actor in pixels, or -1
|
|
* @height: New height of actor in pixels, or -1
|
|
*
|
|
* Sets the actor's size request in pixels. This overrides any
|
|
* "normal" size request the actor would have. For example
|
|
* a text actor might normally request the size of the text;
|
|
* this function would force a specific size instead.
|
|
*
|
|
* If @width and/or @height are -1 the actor will use its
|
|
* "normal" size request instead of overriding it, i.e.
|
|
* you can "unset" the size with -1.
|
|
*
|
|
* This function sets or unsets both the minimum and natural size.
|
|
*/
|
|
void
|
|
clutter_actor_set_size (ClutterActor *self,
|
|
gfloat width,
|
|
gfloat height)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_width_internal (self, width);
|
|
clutter_actor_set_height_internal (self, height);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_size:
|
|
* @self: A #ClutterActor
|
|
* @width: (out) (allow-none): return location for the width, or %NULL.
|
|
* @height: (out) (allow-none): return location for the height, or %NULL.
|
|
*
|
|
* This function tries to "do what you mean" and return
|
|
* the size an actor will have. If the actor has a valid
|
|
* allocation, the allocation will be returned; otherwise,
|
|
* the actors natural size request will be returned.
|
|
*
|
|
* If you care whether you get the request vs. the allocation, you
|
|
* should probably call a different function like
|
|
* clutter_actor_get_allocation_box() or
|
|
* clutter_actor_get_preferred_width().
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_get_size (ClutterActor *self,
|
|
gfloat *width,
|
|
gfloat *height)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (width)
|
|
*width = clutter_actor_get_width (self);
|
|
|
|
if (height)
|
|
*height = clutter_actor_get_height (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_position:
|
|
* @self: a #ClutterActor
|
|
* @x: (out) (allow-none): return location for the X coordinate, or %NULL
|
|
* @y: (out) (allow-none): return location for the Y coordinate, or %NULL
|
|
*
|
|
* This function tries to "do what you mean" and tell you where the
|
|
* actor is, prior to any transformations. Retrieves the fixed
|
|
* position of an actor in pixels, if one has been set; otherwise, if
|
|
* the allocation is valid, returns the actor's allocated position;
|
|
* otherwise, returns 0,0.
|
|
*
|
|
* The returned position is in pixels.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_get_position (ClutterActor *self,
|
|
gfloat *x,
|
|
gfloat *y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (x)
|
|
*x = clutter_actor_get_x (self);
|
|
|
|
if (y)
|
|
*y = clutter_actor_get_y (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_transformed_position:
|
|
* @self: A #ClutterActor
|
|
* @x: (out) (allow-none): return location for the X coordinate, or %NULL
|
|
* @y: (out) (allow-none): return location for the Y coordinate, or %NULL
|
|
*
|
|
* Gets the absolute position of an actor, in pixels relative to the stage.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_get_transformed_position (ClutterActor *self,
|
|
gfloat *x,
|
|
gfloat *y)
|
|
{
|
|
ClutterVertex v1;
|
|
ClutterVertex v2;
|
|
|
|
v1.x = v1.y = v1.z = 0;
|
|
clutter_actor_apply_transform_to_point (self, &v1, &v2);
|
|
|
|
if (x)
|
|
*x = v2.x;
|
|
|
|
if (y)
|
|
*y = v2.y;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_transformed_size:
|
|
* @self: A #ClutterActor
|
|
* @width: (out) (allow-none): return location for the width, or %NULL
|
|
* @height: (out) (allow-none): return location for the height, or %NULL
|
|
*
|
|
* Gets the absolute size of an actor in pixels, taking into account the
|
|
* scaling factors.
|
|
*
|
|
* If the actor has a valid allocation, the allocated size will be used.
|
|
* If the actor has not a valid allocation then the preferred size will
|
|
* be transformed and returned.
|
|
*
|
|
* If you want the transformed allocation, see
|
|
* clutter_actor_get_abs_allocation_vertices() instead.
|
|
*
|
|
* <note>When the actor (or one of its ancestors) is rotated around the
|
|
* X or Y axis, it no longer appears as on the stage as a rectangle, but
|
|
* as a generic quadrangle; in that case this function returns the size
|
|
* of the smallest rectangle that encapsulates the entire quad. Please
|
|
* note that in this case no assumptions can be made about the relative
|
|
* position of this envelope to the absolute position of the actor, as
|
|
* returned by clutter_actor_get_transformed_position(); if you need this
|
|
* information, you need to use clutter_actor_get_abs_allocation_vertices()
|
|
* to get the coords of the actual quadrangle.</note>
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_get_transformed_size (ClutterActor *self,
|
|
gfloat *width,
|
|
gfloat *height)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterVertex v[4];
|
|
gfloat x_min, x_max, y_min, y_max;
|
|
gint i;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
/* if the actor hasn't been allocated yet, get the preferred
|
|
* size and transform that
|
|
*/
|
|
if (priv->needs_allocation)
|
|
{
|
|
gfloat natural_width, natural_height;
|
|
ClutterActorBox box;
|
|
|
|
/* Make a fake allocation to transform.
|
|
*
|
|
* NB: _clutter_actor_transform_and_project_box expects a box in
|
|
* the actor's coordinate space... */
|
|
|
|
box.x1 = 0;
|
|
box.y1 = 0;
|
|
|
|
natural_width = natural_height = 0;
|
|
clutter_actor_get_preferred_size (self, NULL, NULL,
|
|
&natural_width,
|
|
&natural_height);
|
|
|
|
box.x2 = natural_width;
|
|
box.y2 = natural_height;
|
|
|
|
_clutter_actor_transform_and_project_box (self, &box, v);
|
|
}
|
|
else
|
|
clutter_actor_get_abs_allocation_vertices (self, v);
|
|
|
|
x_min = x_max = v[0].x;
|
|
y_min = y_max = v[0].y;
|
|
|
|
for (i = 1; i < G_N_ELEMENTS (v); ++i)
|
|
{
|
|
if (v[i].x < x_min)
|
|
x_min = v[i].x;
|
|
|
|
if (v[i].x > x_max)
|
|
x_max = v[i].x;
|
|
|
|
if (v[i].y < y_min)
|
|
y_min = v[i].y;
|
|
|
|
if (v[i].y > y_max)
|
|
y_max = v[i].y;
|
|
}
|
|
|
|
if (width)
|
|
*width = x_max - x_min;
|
|
|
|
if (height)
|
|
*height = y_max - y_min;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_width:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the width of a #ClutterActor.
|
|
*
|
|
* If the actor has a valid allocation, this function will return the
|
|
* width of the allocated area given to the actor.
|
|
*
|
|
* If the actor does not have a valid allocation, this function will
|
|
* return the actor's natural width, that is the preferred width of
|
|
* the actor.
|
|
*
|
|
* If you care whether you get the preferred width or the width that
|
|
* has been assigned to the actor, you should probably call a different
|
|
* function like clutter_actor_get_allocation_box() to retrieve the
|
|
* allocated size or clutter_actor_get_preferred_width() to retrieve the
|
|
* preferred width.
|
|
*
|
|
* If an actor has a fixed width, for instance a width that has been
|
|
* assigned using clutter_actor_set_width(), the width returned will
|
|
* be the same value.
|
|
*
|
|
* Return value: the width of the actor, in pixels
|
|
*/
|
|
gfloat
|
|
clutter_actor_get_width (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->needs_allocation)
|
|
{
|
|
gfloat natural_width = 0;
|
|
|
|
if (self->priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
|
clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
|
|
else
|
|
{
|
|
gfloat natural_height = 0;
|
|
|
|
clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
|
|
clutter_actor_get_preferred_width (self, natural_height,
|
|
NULL,
|
|
&natural_width);
|
|
}
|
|
|
|
return natural_width;
|
|
}
|
|
else
|
|
return priv->allocation.x2 - priv->allocation.x1;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_height:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the height of a #ClutterActor.
|
|
*
|
|
* If the actor has a valid allocation, this function will return the
|
|
* height of the allocated area given to the actor.
|
|
*
|
|
* If the actor does not have a valid allocation, this function will
|
|
* return the actor's natural height, that is the preferred height of
|
|
* the actor.
|
|
*
|
|
* If you care whether you get the preferred height or the height that
|
|
* has been assigned to the actor, you should probably call a different
|
|
* function like clutter_actor_get_allocation_box() to retrieve the
|
|
* allocated size or clutter_actor_get_preferred_height() to retrieve the
|
|
* preferred height.
|
|
*
|
|
* If an actor has a fixed height, for instance a height that has been
|
|
* assigned using clutter_actor_set_height(), the height returned will
|
|
* be the same value.
|
|
*
|
|
* Return value: the height of the actor, in pixels
|
|
*/
|
|
gfloat
|
|
clutter_actor_get_height (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->needs_allocation)
|
|
{
|
|
gfloat natural_height = 0;
|
|
|
|
if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
|
{
|
|
gfloat natural_width = 0;
|
|
|
|
clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
|
|
clutter_actor_get_preferred_height (self, natural_width,
|
|
NULL, &natural_height);
|
|
}
|
|
else
|
|
clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
|
|
|
|
return natural_height;
|
|
}
|
|
else
|
|
return priv->allocation.y2 - priv->allocation.y1;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_width
|
|
* @self: A #ClutterActor
|
|
* @width: Requested new width for the actor, in pixels, or -1
|
|
*
|
|
* Forces a width on an actor, causing the actor's preferred width
|
|
* and height (if any) to be ignored.
|
|
*
|
|
* If @width is -1 the actor will use its preferred width request
|
|
* instead of overriding it, i.e. you can "unset" the width with -1.
|
|
*
|
|
* This function sets both the minimum and natural size of the actor.
|
|
*
|
|
* since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_set_width (ClutterActor *self,
|
|
gfloat width)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_width_internal (self, width);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_height
|
|
* @self: A #ClutterActor
|
|
* @height: Requested new height for the actor, in pixels, or -1
|
|
*
|
|
* Forces a height on an actor, causing the actor's preferred width
|
|
* and height (if any) to be ignored.
|
|
*
|
|
* If @height is -1 the actor will use its preferred height instead of
|
|
* overriding it, i.e. you can "unset" the height with -1.
|
|
*
|
|
* This function sets both the minimum and natural size of the actor.
|
|
*
|
|
* since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_set_height (ClutterActor *self,
|
|
gfloat height)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_height_internal (self, height);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_x:
|
|
* @self: a #ClutterActor
|
|
* @x: the actor's position on the X axis
|
|
*
|
|
* Sets the actor's X coordinate, relative to its parent, in pixels.
|
|
*
|
|
* Overrides any layout manager and forces a fixed position for
|
|
* the actor.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_set_x (ClutterActor *self,
|
|
gfloat x)
|
|
{
|
|
ClutterActorBox old = { 0, };
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->position_set && priv->fixed_x == x)
|
|
return;
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->fixed_x = x;
|
|
clutter_actor_set_fixed_position_set (self, TRUE);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_y:
|
|
* @self: a #ClutterActor
|
|
* @y: the actor's position on the Y axis
|
|
*
|
|
* Sets the actor's Y coordinate, relative to its parent, in pixels.#
|
|
*
|
|
* Overrides any layout manager and forces a fixed position for
|
|
* the actor.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_set_y (ClutterActor *self,
|
|
gfloat y)
|
|
{
|
|
ClutterActorBox old = { 0, };
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->position_set && priv->fixed_y == y)
|
|
return;
|
|
|
|
clutter_actor_store_old_geometry (self, &old);
|
|
|
|
priv->fixed_y = y;
|
|
clutter_actor_set_fixed_position_set (self, TRUE);
|
|
|
|
clutter_actor_notify_if_geometry_changed (self, &old);
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_x
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the X coordinate of a #ClutterActor.
|
|
*
|
|
* This function tries to "do what you mean", by returning the
|
|
* correct value depending on the actor's state.
|
|
*
|
|
* If the actor has a valid allocation, this function will return
|
|
* the X coordinate of the origin of the allocation box.
|
|
*
|
|
* If the actor has any fixed coordinate set using clutter_actor_set_x(),
|
|
* clutter_actor_set_position() or clutter_actor_set_geometry(), this
|
|
* function will return that coordinate.
|
|
*
|
|
* If both the allocation and a fixed position are missing, this function
|
|
* will return 0.
|
|
*
|
|
* Return value: the X coordinate, in pixels, ignoring any
|
|
* transformation (i.e. scaling, rotation)
|
|
*/
|
|
gfloat
|
|
clutter_actor_get_x (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->needs_allocation)
|
|
{
|
|
if (priv->position_set)
|
|
return priv->fixed_x;
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return priv->allocation.x1;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_y
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the Y coordinate of a #ClutterActor.
|
|
*
|
|
* This function tries to "do what you mean", by returning the
|
|
* correct value depending on the actor's state.
|
|
*
|
|
* If the actor has a valid allocation, this function will return
|
|
* the Y coordinate of the origin of the allocation box.
|
|
*
|
|
* If the actor has any fixed coordinate set using clutter_actor_set_y(),
|
|
* clutter_actor_set_position() or clutter_actor_set_geometry(), this
|
|
* function will return that coordinate.
|
|
*
|
|
* If both the allocation and a fixed position are missing, this function
|
|
* will return 0.
|
|
*
|
|
* Return value: the Y coordinate, in pixels, ignoring any
|
|
* transformation (i.e. scaling, rotation)
|
|
*/
|
|
gfloat
|
|
clutter_actor_get_y (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->needs_allocation)
|
|
{
|
|
if (priv->position_set)
|
|
return priv->fixed_y;
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return priv->allocation.y1;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_scale:
|
|
* @self: A #ClutterActor
|
|
* @scale_x: double factor to scale actor by horizontally.
|
|
* @scale_y: double factor to scale actor by vertically.
|
|
*
|
|
* Scales an actor with the given factors. The scaling is relative to
|
|
* the scale center and the anchor point. The scale center is
|
|
* unchanged by this function and defaults to 0,0.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_set_scale (ClutterActor *self,
|
|
gdouble scale_x,
|
|
gdouble scale_y)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
priv->scale_x = scale_x;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_X]);
|
|
|
|
priv->scale_y = scale_y;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_Y]);
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_scale_full:
|
|
* @self: A #ClutterActor
|
|
* @scale_x: double factor to scale actor by horizontally.
|
|
* @scale_y: double factor to scale actor by vertically.
|
|
* @center_x: X coordinate of the center of the scale.
|
|
* @center_y: Y coordinate of the center of the scale
|
|
*
|
|
* Scales an actor with the given factors around the given center
|
|
* point. The center point is specified in pixels relative to the
|
|
* anchor point (usually the top left corner of the actor).
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_set_scale_full (ClutterActor *self,
|
|
gdouble scale_x,
|
|
gdouble scale_y,
|
|
gfloat center_x,
|
|
gfloat center_y)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_scale (self, scale_x, scale_y);
|
|
|
|
if (priv->scale_center.is_fractional)
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_GRAVITY]);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_CENTER_X]);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_CENTER_Y]);
|
|
|
|
clutter_anchor_coord_set_units (&priv->scale_center, center_x, center_y, 0);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_scale_with_gravity:
|
|
* @self: A #ClutterActor
|
|
* @scale_x: double factor to scale actor by horizontally.
|
|
* @scale_y: double factor to scale actor by vertically.
|
|
* @gravity: the location of the scale center expressed as a compass
|
|
* direction.
|
|
*
|
|
* Scales an actor with the given factors around the given
|
|
* center point. The center point is specified as one of the compass
|
|
* directions in #ClutterGravity. For example, setting it to north
|
|
* will cause the top of the actor to remain unchanged and the rest of
|
|
* the actor to expand left, right and downwards.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_set_scale_with_gravity (ClutterActor *self,
|
|
gdouble scale_x,
|
|
gdouble scale_y,
|
|
ClutterGravity gravity)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (gravity == CLUTTER_GRAVITY_NONE)
|
|
clutter_actor_set_scale_full (self, scale_x, scale_y, 0, 0);
|
|
else
|
|
{
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_scale (self, scale_x, scale_y);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_GRAVITY]);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_CENTER_X]);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SCALE_CENTER_Y]);
|
|
|
|
clutter_anchor_coord_set_gravity (&priv->scale_center, gravity);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_scale:
|
|
* @self: A #ClutterActor
|
|
* @scale_x: (out) (allow-none): Location to store horizonal
|
|
* scale factor, or %NULL.
|
|
* @scale_y: (out) (allow-none): Location to store vertical
|
|
* scale factor, or %NULL.
|
|
*
|
|
* Retrieves an actors scale factors.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_get_scale (ClutterActor *self,
|
|
gdouble *scale_x,
|
|
gdouble *scale_y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (scale_x)
|
|
*scale_x = self->priv->scale_x;
|
|
|
|
if (scale_y)
|
|
*scale_y = self->priv->scale_y;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_scale_center:
|
|
* @self: A #ClutterActor
|
|
* @center_x: (out) (allow-none): Location to store the X position
|
|
* of the scale center, or %NULL.
|
|
* @center_y: (out) (allow-none): Location to store the Y position
|
|
* of the scale center, or %NULL.
|
|
*
|
|
* Retrieves the scale center coordinate in pixels relative to the top
|
|
* left corner of the actor. If the scale center was specified using a
|
|
* #ClutterGravity this will calculate the pixel offset using the
|
|
* current size of the actor.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_get_scale_center (ClutterActor *self,
|
|
gfloat *center_x,
|
|
gfloat *center_y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
clutter_anchor_coord_get_units (self, &self->priv->scale_center,
|
|
center_x,
|
|
center_y,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_scale_gravity:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the scale center as a compass direction. If the scale
|
|
* center was specified in pixels or units this will return
|
|
* %CLUTTER_GRAVITY_NONE.
|
|
*
|
|
* Return value: the scale gravity
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterGravity
|
|
clutter_actor_get_scale_gravity (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
|
|
|
|
return clutter_anchor_coord_get_gravity (&self->priv->scale_center);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_opacity:
|
|
* @self: A #ClutterActor
|
|
* @opacity: New opacity value for the actor.
|
|
*
|
|
* Sets the actor's opacity, with zero being completely transparent and
|
|
* 255 (0xff) being fully opaque.
|
|
*/
|
|
void
|
|
clutter_actor_set_opacity (ClutterActor *self,
|
|
guint8 opacity)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->opacity != opacity)
|
|
{
|
|
priv->opacity = opacity;
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OPACITY]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* clutter_actor_get_paint_opacity_internal:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the absolute opacity of the actor, as it appears on the stage
|
|
*
|
|
* This function does not do type checks
|
|
*
|
|
* Return value: the absolute opacity of the actor
|
|
*/
|
|
static guint8
|
|
clutter_actor_get_paint_opacity_internal (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
ClutterActor *parent;
|
|
|
|
/* override the top-level opacity to always be 255; even in
|
|
* case of ClutterStage:use-alpha being TRUE we want the rest
|
|
* of the scene to be painted
|
|
*/
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
return 255;
|
|
|
|
if (priv->opacity_parent != NULL)
|
|
return clutter_actor_get_paint_opacity_internal (priv->opacity_parent);
|
|
|
|
parent = priv->parent_actor;
|
|
|
|
/* Factor in the actual actors opacity with parents */
|
|
if (parent != NULL)
|
|
{
|
|
guint8 opacity = clutter_actor_get_paint_opacity_internal (parent);
|
|
|
|
if (opacity != 0xff)
|
|
return (opacity * priv->opacity) / 0xff;
|
|
}
|
|
|
|
return priv->opacity;
|
|
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_paint_opacity:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the absolute opacity of the actor, as it appears on the stage.
|
|
*
|
|
* This function traverses the hierarchy chain and composites the opacity of
|
|
* the actor with that of its parents.
|
|
*
|
|
* This function is intended for subclasses to use in the paint virtual
|
|
* function, to paint themselves with the correct opacity.
|
|
*
|
|
* Return value: The actor opacity value.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
guint8
|
|
clutter_actor_get_paint_opacity (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
return clutter_actor_get_paint_opacity_internal (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_opacity:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the opacity value of an actor, as set by
|
|
* clutter_actor_set_opacity().
|
|
*
|
|
* For retrieving the absolute opacity of the actor inside a paint
|
|
* virtual function, see clutter_actor_get_paint_opacity().
|
|
*
|
|
* Return value: the opacity of the actor
|
|
*/
|
|
guint8
|
|
clutter_actor_get_opacity (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
return self->priv->opacity;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_name:
|
|
* @self: A #ClutterActor
|
|
* @name: Textual tag to apply to actor
|
|
*
|
|
* Sets the given name to @self. The name can be used to identify
|
|
* a #ClutterActor.
|
|
*/
|
|
void
|
|
clutter_actor_set_name (ClutterActor *self,
|
|
const gchar *name)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
g_free (self->priv->name);
|
|
self->priv->name = g_strdup (name);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_name:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the name of @self.
|
|
*
|
|
* Return value: the name of the actor, or %NULL. The returned string is
|
|
* owned by the actor and should not be modified or freed.
|
|
*/
|
|
G_CONST_RETURN gchar *
|
|
clutter_actor_get_name (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
return self->priv->name;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_gid:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the unique id for @self.
|
|
*
|
|
* Return value: Globally unique value for this object instance.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
guint32
|
|
clutter_actor_get_gid (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
return self->priv->id;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_depth:
|
|
* @self: a #ClutterActor
|
|
* @depth: Z co-ord
|
|
*
|
|
* Sets the Z coordinate of @self to @depth.
|
|
*
|
|
* The unit used by @depth is dependant on the perspective setup. See
|
|
* also clutter_stage_set_perspective().
|
|
*/
|
|
void
|
|
clutter_actor_set_depth (ClutterActor *self,
|
|
gfloat depth)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->z != depth)
|
|
{
|
|
/* Sets Z value - XXX 2.0: should we invert? */
|
|
priv->z = depth;
|
|
|
|
if (priv->parent_actor && CLUTTER_IS_CONTAINER (priv->parent_actor))
|
|
{
|
|
ClutterContainer *parent;
|
|
|
|
/* We need to resort the container stacking order as to
|
|
* correctly render alpha values.
|
|
*
|
|
* FIXME: This is sub-optimal. maybe queue the the sort
|
|
* before stacking
|
|
*/
|
|
parent = CLUTTER_CONTAINER (priv->parent_actor);
|
|
clutter_container_sort_depth_order (parent);
|
|
}
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DEPTH]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_depth:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the depth of @self.
|
|
*
|
|
* Return value: the depth of the actor
|
|
*/
|
|
gfloat
|
|
clutter_actor_get_depth (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), -1);
|
|
|
|
return self->priv->z;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_rotation:
|
|
* @self: a #ClutterActor
|
|
* @axis: the axis of rotation
|
|
* @angle: the angle of rotation
|
|
* @x: X coordinate of the rotation center
|
|
* @y: Y coordinate of the rotation center
|
|
* @z: Z coordinate of the rotation center
|
|
*
|
|
* Sets the rotation angle of @self around the given axis.
|
|
*
|
|
* The rotation center coordinates used depend on the value of @axis:
|
|
* <itemizedlist>
|
|
* <listitem><para>%CLUTTER_X_AXIS requires @y and @z</para></listitem>
|
|
* <listitem><para>%CLUTTER_Y_AXIS requires @x and @z</para></listitem>
|
|
* <listitem><para>%CLUTTER_Z_AXIS requires @x and @y</para></listitem>
|
|
* </itemizedlist>
|
|
*
|
|
* The rotation coordinates are relative to the anchor point of the
|
|
* actor, set using clutter_actor_set_anchor_point(). If no anchor
|
|
* point is set, the upper left corner is assumed as the origin.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_set_rotation (ClutterActor *self,
|
|
ClutterRotateAxis axis,
|
|
gdouble angle,
|
|
gfloat x,
|
|
gfloat y,
|
|
gfloat z)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_rotation_internal (self, axis, angle);
|
|
|
|
switch (axis)
|
|
{
|
|
case CLUTTER_X_AXIS:
|
|
clutter_anchor_coord_set_units (&priv->rx_center, x, y, z);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_CENTER_X]);
|
|
break;
|
|
|
|
case CLUTTER_Y_AXIS:
|
|
clutter_anchor_coord_set_units (&priv->ry_center, x, y, z);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_CENTER_Y]);
|
|
break;
|
|
|
|
case CLUTTER_Z_AXIS:
|
|
if (priv->rz_center.is_fractional)
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]);
|
|
clutter_anchor_coord_set_units (&priv->rz_center, x, y, z);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_CENTER_Z]);
|
|
break;
|
|
}
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_z_rotation_from_gravity:
|
|
* @self: a #ClutterActor
|
|
* @angle: the angle of rotation
|
|
* @gravity: the center point of the rotation
|
|
*
|
|
* Sets the rotation angle of @self around the Z axis using the center
|
|
* point specified as a compass point. For example to rotate such that
|
|
* the center of the actor remains static you can use
|
|
* %CLUTTER_GRAVITY_CENTER. If the actor changes size the center point
|
|
* will move accordingly.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_set_z_rotation_from_gravity (ClutterActor *self,
|
|
gdouble angle,
|
|
ClutterGravity gravity)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (gravity == CLUTTER_GRAVITY_NONE)
|
|
clutter_actor_set_rotation (self, CLUTTER_Z_AXIS, angle, 0, 0, 0);
|
|
else
|
|
{
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_rotation_internal (self, CLUTTER_Z_AXIS, angle);
|
|
|
|
clutter_anchor_coord_set_gravity (&priv->rz_center, gravity);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ROTATION_CENTER_Z]);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_rotation:
|
|
* @self: a #ClutterActor
|
|
* @axis: the axis of rotation
|
|
* @x: (out): return value for the X coordinate of the center of rotation
|
|
* @y: (out): return value for the Y coordinate of the center of rotation
|
|
* @z: (out): return value for the Z coordinate of the center of rotation
|
|
*
|
|
* Retrieves the angle and center of rotation on the given axis,
|
|
* set using clutter_actor_set_rotation().
|
|
*
|
|
* Return value: the angle of rotation
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
gdouble
|
|
clutter_actor_get_rotation (ClutterActor *self,
|
|
ClutterRotateAxis axis,
|
|
gfloat *x,
|
|
gfloat *y,
|
|
gfloat *z)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
gdouble retval = 0;
|
|
AnchorCoord *anchor_coord = NULL;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
priv = self->priv;
|
|
|
|
switch (axis)
|
|
{
|
|
case CLUTTER_X_AXIS:
|
|
anchor_coord = &priv->rx_center;
|
|
retval = priv->rxang;
|
|
break;
|
|
|
|
case CLUTTER_Y_AXIS:
|
|
anchor_coord = &priv->ry_center;
|
|
retval = priv->ryang;
|
|
break;
|
|
|
|
case CLUTTER_Z_AXIS:
|
|
anchor_coord = &priv->rz_center;
|
|
retval = priv->rzang;
|
|
break;
|
|
}
|
|
|
|
clutter_anchor_coord_get_units (self, anchor_coord, x, y, z);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_z_rotation_gravity:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the center for the rotation around the Z axis as a
|
|
* compass direction. If the center was specified in pixels or units
|
|
* this will return %CLUTTER_GRAVITY_NONE.
|
|
*
|
|
* Return value: the Z rotation center
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterGravity
|
|
clutter_actor_get_z_rotation_gravity (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0);
|
|
|
|
return clutter_anchor_coord_get_gravity (&self->priv->rz_center);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_clip:
|
|
* @self: A #ClutterActor
|
|
* @xoff: X offset of the clip rectangle
|
|
* @yoff: Y offset of the clip rectangle
|
|
* @width: Width of the clip rectangle
|
|
* @height: Height of the clip rectangle
|
|
*
|
|
* Sets clip area for @self. The clip area is always computed from the
|
|
* upper left corner of the actor, even if the anchor point is set
|
|
* otherwise.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_set_clip (ClutterActor *self,
|
|
gfloat xoff,
|
|
gfloat yoff,
|
|
gfloat width,
|
|
gfloat height)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->has_clip &&
|
|
priv->clip[0] == xoff &&
|
|
priv->clip[1] == yoff &&
|
|
priv->clip[2] == width &&
|
|
priv->clip[3] == height)
|
|
return;
|
|
|
|
priv->clip[0] = xoff;
|
|
priv->clip[1] = yoff;
|
|
priv->clip[2] = width;
|
|
priv->clip[3] = height;
|
|
|
|
priv->has_clip = TRUE;
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_remove_clip
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Removes clip area from @self.
|
|
*/
|
|
void
|
|
clutter_actor_remove_clip (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (!self->priv->has_clip)
|
|
return;
|
|
|
|
self->priv->has_clip = FALSE;
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_has_clip:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Determines whether the actor has a clip area set or not.
|
|
*
|
|
* Return value: %TRUE if the actor has a clip area set.
|
|
*
|
|
* Since: 0.1.1
|
|
*/
|
|
gboolean
|
|
clutter_actor_has_clip (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
return self->priv->has_clip;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_clip:
|
|
* @self: a #ClutterActor
|
|
* @xoff: (out) (allow-none): return location for the X offset of
|
|
* the clip rectangle, or %NULL
|
|
* @yoff: (out) (allow-none): return location for the Y offset of
|
|
* the clip rectangle, or %NULL
|
|
* @width: (out) (allow-none): return location for the width of
|
|
* the clip rectangle, or %NULL
|
|
* @height: (out) (allow-none): return location for the height of
|
|
* the clip rectangle, or %NULL
|
|
*
|
|
* Gets the clip area for @self, if any is set
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_get_clip (ClutterActor *self,
|
|
gfloat *xoff,
|
|
gfloat *yoff,
|
|
gfloat *width,
|
|
gfloat *height)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (!priv->has_clip)
|
|
return;
|
|
|
|
if (xoff)
|
|
*xoff = priv->clip[0];
|
|
|
|
if (yoff)
|
|
*yoff = priv->clip[1];
|
|
|
|
if (width)
|
|
*width = priv->clip[2];
|
|
|
|
if (height)
|
|
*height = priv->clip[3];
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_parent:
|
|
* @self: A #ClutterActor
|
|
* @parent: A new #ClutterActor parent
|
|
*
|
|
* Sets the parent of @self to @parent. The opposite function is
|
|
* clutter_actor_unparent().
|
|
*
|
|
* This function should not be used by applications, but by custom
|
|
* container actor subclasses.
|
|
*/
|
|
void
|
|
clutter_actor_set_parent (ClutterActor *self,
|
|
ClutterActor *parent)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorPrivate *parent_priv;
|
|
ClutterTextDirection text_dir;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (parent));
|
|
g_return_if_fail (self != parent);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->parent_actor != NULL)
|
|
{
|
|
g_warning ("Cannot set a parent on an actor which has a parent.\n"
|
|
"You must use clutter_actor_unparent() first.\n");
|
|
return;
|
|
}
|
|
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
g_warning ("Cannot set a parent on a toplevel actor\n");
|
|
return;
|
|
}
|
|
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
{
|
|
g_warning ("Cannot set a parent currently being destroyed");
|
|
return;
|
|
}
|
|
|
|
g_object_ref_sink (self);
|
|
priv->parent_actor = parent;
|
|
|
|
/* Maintain an explicit list of children for every actor... */
|
|
parent_priv = parent->priv;
|
|
parent_priv->children =
|
|
g_list_prepend (parent_priv->children, self);
|
|
parent_priv->n_children++;
|
|
|
|
/* if push_internal() has been called then we automatically set
|
|
* the flag on the actor
|
|
*/
|
|
if (parent->priv->internal_child)
|
|
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_INTERNAL_CHILD);
|
|
|
|
/* clutter_actor_reparent() will emit ::parent-set for us */
|
|
if (!CLUTTER_ACTOR_IN_REPARENT (self))
|
|
g_signal_emit (self, actor_signals[PARENT_SET], 0, NULL);
|
|
|
|
/* If parent is mapped or realized, we need to also be mapped or
|
|
* realized once we're inside the parent.
|
|
*/
|
|
clutter_actor_update_map_state (self, MAP_STATE_CHECK);
|
|
|
|
/* propagate the parent's text direction to the child */
|
|
text_dir = clutter_actor_get_text_direction (parent);
|
|
clutter_actor_set_text_direction (self, text_dir);
|
|
|
|
if (priv->show_on_set_parent)
|
|
clutter_actor_show (self);
|
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (self))
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
/* maintain the invariant that if an actor needs layout,
|
|
* its parents do as well
|
|
*/
|
|
if (priv->needs_width_request ||
|
|
priv->needs_height_request ||
|
|
priv->needs_allocation)
|
|
{
|
|
/* we work around the short-circuiting we do
|
|
* in clutter_actor_queue_relayout() since we
|
|
* want to force a relayout
|
|
*/
|
|
priv->needs_width_request = TRUE;
|
|
priv->needs_height_request = TRUE;
|
|
priv->needs_allocation = TRUE;
|
|
|
|
clutter_actor_queue_relayout (priv->parent_actor);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_parent:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the parent of @self.
|
|
*
|
|
* Return Value: (transfer none): The #ClutterActor parent, or %NULL
|
|
* if no parent is set
|
|
*/
|
|
ClutterActor *
|
|
clutter_actor_get_parent (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
return self->priv->parent_actor;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_paint_visibility:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Retrieves the 'paint' visibility of an actor recursively checking for non
|
|
* visible parents.
|
|
*
|
|
* This is by definition the same as CLUTTER_ACTOR_IS_MAPPED().
|
|
*
|
|
* Return Value: TRUE if the actor is visibile and will be painted.
|
|
*
|
|
* Since: 0.8.4
|
|
*/
|
|
gboolean
|
|
clutter_actor_get_paint_visibility (ClutterActor *actor)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
|
|
|
|
return CLUTTER_ACTOR_IS_MAPPED (actor);
|
|
}
|
|
|
|
static gboolean
|
|
invalidate_queue_redraw_entry (ClutterActor *self,
|
|
gpointer user_data)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
if (priv->queue_redraw_entry != NULL)
|
|
{
|
|
_clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
|
|
priv->queue_redraw_entry = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_unparent:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Removes the parent of @self.
|
|
*
|
|
* This function should not be used in applications. It should be called by
|
|
* implementations of container actors, to dissociate a child from the
|
|
* container.
|
|
*
|
|
* Since: 0.1.1
|
|
*/
|
|
void
|
|
clutter_actor_unparent (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActor *old_parent;
|
|
ClutterActorPrivate *old_parent_priv;
|
|
gboolean was_mapped;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->parent_actor == NULL)
|
|
return;
|
|
|
|
/* We take this opportunity to invalidate any queue redraw entry
|
|
* associated with the actor and descendants since we won't be able to
|
|
* determine the appropriate stage after this. */
|
|
_clutter_actor_traverse (self,
|
|
0,
|
|
invalidate_queue_redraw_entry,
|
|
NULL);
|
|
|
|
was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
|
|
|
|
/* we need to unrealize *before* we set parent_actor to NULL,
|
|
* because in an unrealize method actors are dissociating from the
|
|
* stage, which means they need to be able to
|
|
* clutter_actor_get_stage(). This should unmap and unrealize,
|
|
* unless we're reparenting.
|
|
*/
|
|
clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNREALIZED);
|
|
|
|
old_parent = priv->parent_actor;
|
|
priv->parent_actor = NULL;
|
|
|
|
/* clutter_actor_reparent() will emit ::parent-set for us */
|
|
if (!CLUTTER_ACTOR_IN_REPARENT (self))
|
|
g_signal_emit (self, actor_signals[PARENT_SET], 0, old_parent);
|
|
|
|
old_parent_priv = old_parent->priv;
|
|
old_parent_priv->children = g_list_remove (old_parent_priv->children, self);
|
|
old_parent_priv->n_children--;
|
|
|
|
/* Queue a redraw on old_parent only if we were painted in the first
|
|
* place. Will be no-op if old parent is not shown.
|
|
*/
|
|
if (was_mapped && !CLUTTER_ACTOR_IS_MAPPED (self))
|
|
clutter_actor_queue_redraw (old_parent);
|
|
|
|
/* remove the reference we acquired in clutter_actor_set_parent() */
|
|
g_object_unref (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_reparent:
|
|
* @self: a #ClutterActor
|
|
* @new_parent: the new #ClutterActor parent
|
|
*
|
|
* This function resets the parent actor of @self. It is
|
|
* logically equivalent to calling clutter_actor_unparent()
|
|
* and clutter_actor_set_parent(), but more efficiently
|
|
* implemented, ensures the child is not finalized
|
|
* when unparented, and emits the parent-set signal only
|
|
* one time.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_actor_reparent (ClutterActor *self,
|
|
ClutterActor *new_parent)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (new_parent));
|
|
g_return_if_fail (self != new_parent);
|
|
|
|
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
|
{
|
|
g_warning ("Cannot set a parent on a toplevel actor");
|
|
return;
|
|
}
|
|
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
|
{
|
|
g_warning ("Cannot set a parent currently being destroyed");
|
|
return;
|
|
}
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->parent_actor != new_parent)
|
|
{
|
|
ClutterActor *old_parent;
|
|
|
|
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_REPARENT);
|
|
|
|
old_parent = priv->parent_actor;
|
|
|
|
g_object_ref (self);
|
|
|
|
/* go through the Container implementation if this is a regular
|
|
* child and not an internal one
|
|
*/
|
|
if (CLUTTER_IS_CONTAINER (priv->parent_actor) &&
|
|
!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
|
|
{
|
|
ClutterContainer *parent = CLUTTER_CONTAINER (priv->parent_actor);
|
|
|
|
/* this will have to call unparent() */
|
|
clutter_container_remove_actor (parent, self);
|
|
}
|
|
else
|
|
clutter_actor_unparent (self);
|
|
|
|
/* Note, will call parent() */
|
|
if (CLUTTER_IS_CONTAINER (new_parent))
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (new_parent), self);
|
|
else
|
|
clutter_actor_set_parent (self, new_parent);
|
|
|
|
/* we emit the ::parent-set signal once */
|
|
g_signal_emit (self, actor_signals[PARENT_SET], 0, old_parent);
|
|
|
|
g_object_unref (self);
|
|
|
|
CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_REPARENT);
|
|
|
|
/* the IN_REPARENT flag suspends state updates */
|
|
clutter_actor_update_map_state (self, MAP_STATE_CHECK);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_contains:
|
|
* @self: A #ClutterActor
|
|
* @descendant: A #ClutterActor, possibly contained in @self
|
|
*
|
|
* Determines if @descendant is contained inside @self (either as an
|
|
* immediate child, or as a deeper descendant). If @self and
|
|
* @descendant point to the same actor then it will also return %TRUE.
|
|
*
|
|
* Return value: whether @descendent is contained within @self
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
clutter_actor_contains (ClutterActor *self,
|
|
ClutterActor *descendant)
|
|
{
|
|
ClutterActor *actor;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (descendant), FALSE);
|
|
|
|
for (actor = descendant; actor; actor = actor->priv->parent_actor)
|
|
if (actor == self)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_raise:
|
|
* @self: A #ClutterActor
|
|
* @below: (allow-none): A #ClutterActor to raise above.
|
|
*
|
|
* Puts @self above @below.
|
|
*
|
|
* Both actors must have the same parent, and the parent must implement
|
|
* the #ClutterContainer interface
|
|
*
|
|
* This function is the equivalent of clutter_container_raise_child().
|
|
*/
|
|
void
|
|
clutter_actor_raise (ClutterActor *self,
|
|
ClutterActor *below)
|
|
{
|
|
ClutterActor *parent;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
parent = clutter_actor_get_parent (self);
|
|
if (parent == NULL || !CLUTTER_IS_CONTAINER (parent))
|
|
{
|
|
g_warning ("%s: Actor '%s' is not inside a container",
|
|
G_STRFUNC,
|
|
_clutter_actor_get_debug_name (self));
|
|
return;
|
|
}
|
|
|
|
if (below != NULL)
|
|
{
|
|
if (parent != clutter_actor_get_parent (below))
|
|
{
|
|
g_warning ("%s Actor '%s' is not in the same container as "
|
|
"actor '%s'",
|
|
G_STRFUNC,
|
|
_clutter_actor_get_debug_name (self),
|
|
_clutter_actor_get_debug_name (below));
|
|
return;
|
|
}
|
|
}
|
|
|
|
clutter_container_raise_child (CLUTTER_CONTAINER (parent), self, below);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_lower:
|
|
* @self: A #ClutterActor
|
|
* @above: (allow-none): A #ClutterActor to lower below
|
|
*
|
|
* Puts @self below @above.
|
|
*
|
|
* Both actors must have the same parent, and the parent must implement
|
|
* the #ClutterContainer interface.
|
|
*
|
|
* This function is the equivalent of clutter_container_lower_child().
|
|
*/
|
|
void
|
|
clutter_actor_lower (ClutterActor *self,
|
|
ClutterActor *above)
|
|
{
|
|
ClutterActor *parent;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR(self));
|
|
|
|
parent = clutter_actor_get_parent (self);
|
|
if (parent == NULL || !CLUTTER_IS_CONTAINER (parent))
|
|
{
|
|
g_warning ("%s: Actor of type %s is not inside a container",
|
|
G_STRFUNC,
|
|
_clutter_actor_get_debug_name (self));
|
|
return;
|
|
}
|
|
|
|
if (above)
|
|
{
|
|
if (parent != clutter_actor_get_parent (above))
|
|
{
|
|
g_warning ("%s: Actor '%s' is not in the same container as "
|
|
"actor '%s'",
|
|
G_STRFUNC,
|
|
_clutter_actor_get_debug_name (self),
|
|
_clutter_actor_get_debug_name (above));
|
|
return;
|
|
}
|
|
}
|
|
|
|
clutter_container_lower_child (CLUTTER_CONTAINER (parent), self, above);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_raise_top:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Raises @self to the top.
|
|
*
|
|
* This function calls clutter_actor_raise() internally.
|
|
*/
|
|
void
|
|
clutter_actor_raise_top (ClutterActor *self)
|
|
{
|
|
clutter_actor_raise (self, NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_lower_bottom:
|
|
* @self: A #ClutterActor
|
|
*
|
|
* Lowers @self to the bottom.
|
|
*
|
|
* This function calls clutter_actor_lower() internally.
|
|
*/
|
|
void
|
|
clutter_actor_lower_bottom (ClutterActor *self)
|
|
{
|
|
clutter_actor_lower (self, NULL);
|
|
}
|
|
|
|
/*
|
|
* Event handling
|
|
*/
|
|
|
|
/**
|
|
* clutter_actor_event:
|
|
* @actor: a #ClutterActor
|
|
* @event: a #ClutterEvent
|
|
* @capture: TRUE if event in in capture phase, FALSE otherwise.
|
|
*
|
|
* This function is used to emit an event on the main stage.
|
|
* You should rarely need to use this function, except for
|
|
* synthetising events.
|
|
*
|
|
* Return value: the return value from the signal emission: %TRUE
|
|
* if the actor handled the event, or %FALSE if the event was
|
|
* not handled
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_actor_event (ClutterActor *actor,
|
|
ClutterEvent *event,
|
|
gboolean capture)
|
|
{
|
|
gboolean retval = FALSE;
|
|
gint signal_num = -1;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
g_object_ref (actor);
|
|
|
|
if (capture)
|
|
{
|
|
g_signal_emit (actor, actor_signals[CAPTURED_EVENT], 0,
|
|
event,
|
|
&retval);
|
|
goto out;
|
|
}
|
|
|
|
g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
|
|
|
|
if (!retval)
|
|
{
|
|
switch (event->type)
|
|
{
|
|
case CLUTTER_NOTHING:
|
|
break;
|
|
case CLUTTER_BUTTON_PRESS:
|
|
signal_num = BUTTON_PRESS_EVENT;
|
|
break;
|
|
case CLUTTER_BUTTON_RELEASE:
|
|
signal_num = BUTTON_RELEASE_EVENT;
|
|
break;
|
|
case CLUTTER_SCROLL:
|
|
signal_num = SCROLL_EVENT;
|
|
break;
|
|
case CLUTTER_KEY_PRESS:
|
|
signal_num = KEY_PRESS_EVENT;
|
|
break;
|
|
case CLUTTER_KEY_RELEASE:
|
|
signal_num = KEY_RELEASE_EVENT;
|
|
break;
|
|
case CLUTTER_MOTION:
|
|
signal_num = MOTION_EVENT;
|
|
break;
|
|
case CLUTTER_ENTER:
|
|
signal_num = ENTER_EVENT;
|
|
break;
|
|
case CLUTTER_LEAVE:
|
|
signal_num = LEAVE_EVENT;
|
|
break;
|
|
case CLUTTER_DELETE:
|
|
case CLUTTER_DESTROY_NOTIFY:
|
|
case CLUTTER_CLIENT_MESSAGE:
|
|
default:
|
|
signal_num = -1;
|
|
break;
|
|
}
|
|
|
|
if (signal_num != -1)
|
|
g_signal_emit (actor, actor_signals[signal_num], 0,
|
|
event, &retval);
|
|
}
|
|
|
|
out:
|
|
g_object_unref (actor);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_reactive:
|
|
* @actor: a #ClutterActor
|
|
* @reactive: whether the actor should be reactive to events
|
|
*
|
|
* Sets @actor as reactive. Reactive actors will receive events.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_set_reactive (ClutterActor *actor,
|
|
gboolean reactive)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor))
|
|
return;
|
|
|
|
if (reactive)
|
|
CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
|
|
else
|
|
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_reactive:
|
|
* @actor: a #ClutterActor
|
|
*
|
|
* Checks whether @actor is marked as reactive.
|
|
*
|
|
* Return value: %TRUE if the actor is reactive
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_actor_get_reactive (ClutterActor *actor)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
|
|
|
|
return CLUTTER_ACTOR_IS_REACTIVE (actor) ? TRUE : FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_anchor_point:
|
|
* @self: a #ClutterActor
|
|
* @anchor_x: (out): return location for the X coordinate of the anchor point
|
|
* @anchor_y: (out): return location for the Y coordinate of the anchor point
|
|
*
|
|
* Gets the current anchor point of the @actor in pixels.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_get_anchor_point (ClutterActor *self,
|
|
gfloat *anchor_x,
|
|
gfloat *anchor_y)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
clutter_anchor_coord_get_units (self, &priv->anchor,
|
|
anchor_x,
|
|
anchor_y,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_anchor_point:
|
|
* @self: a #ClutterActor
|
|
* @anchor_x: X coordinate of the anchor point
|
|
* @anchor_y: Y coordinate of the anchor point
|
|
*
|
|
* Sets an anchor point for @self. The anchor point is a point in the
|
|
* coordinate space of an actor to which the actor position within its
|
|
* parent is relative; the default is (0, 0), i.e. the top-left corner
|
|
* of the actor.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_set_anchor_point (ClutterActor *self,
|
|
gfloat anchor_x,
|
|
gfloat anchor_y)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
gboolean changed = FALSE;
|
|
gfloat old_anchor_x, old_anchor_y;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_anchor_coord_get_units (self, &priv->anchor,
|
|
&old_anchor_x,
|
|
&old_anchor_y,
|
|
NULL);
|
|
|
|
if (priv->anchor.is_fractional)
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ANCHOR_GRAVITY]);
|
|
|
|
if (old_anchor_x != anchor_x)
|
|
{
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ANCHOR_X]);
|
|
changed = TRUE;
|
|
}
|
|
|
|
if (old_anchor_y != anchor_y)
|
|
{
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ANCHOR_Y]);
|
|
changed = TRUE;
|
|
}
|
|
|
|
clutter_anchor_coord_set_units (&priv->anchor, anchor_x, anchor_y, 0);
|
|
|
|
if (changed)
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_anchor_point_gravity:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the anchor position expressed as a #ClutterGravity. If
|
|
* the anchor point was specified using pixels or units this will
|
|
* return %CLUTTER_GRAVITY_NONE.
|
|
*
|
|
* Return value: the #ClutterGravity used by the anchor point
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterGravity
|
|
clutter_actor_get_anchor_point_gravity (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
|
|
|
|
priv = self->priv;
|
|
|
|
return clutter_anchor_coord_get_gravity (&priv->anchor);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_move_anchor_point:
|
|
* @self: a #ClutterActor
|
|
* @anchor_x: X coordinate of the anchor point
|
|
* @anchor_y: Y coordinate of the anchor point
|
|
*
|
|
* Sets an anchor point for the actor, and adjusts the actor postion so that
|
|
* the relative position of the actor toward its parent remains the same.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_move_anchor_point (ClutterActor *self,
|
|
gfloat anchor_x,
|
|
gfloat anchor_y)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
gfloat old_anchor_x, old_anchor_y;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
clutter_anchor_coord_get_units (self, &priv->anchor,
|
|
&old_anchor_x,
|
|
&old_anchor_y,
|
|
NULL);
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_actor_set_anchor_point (self, anchor_x, anchor_y);
|
|
|
|
if (priv->position_set)
|
|
clutter_actor_move_by (self,
|
|
anchor_x - old_anchor_x,
|
|
anchor_y - old_anchor_y);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_move_anchor_point_from_gravity:
|
|
* @self: a #ClutterActor
|
|
* @gravity: #ClutterGravity.
|
|
*
|
|
* Sets an anchor point on the actor based on the given gravity, adjusting the
|
|
* actor postion so that its relative position within its parent remains
|
|
* unchanged.
|
|
*
|
|
* Since version 1.0 the anchor point will be stored as a gravity so
|
|
* that if the actor changes size then the anchor point will move. For
|
|
* example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST
|
|
* and later double the size of the actor, the anchor point will move
|
|
* to the bottom right.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_move_anchor_point_from_gravity (ClutterActor *self,
|
|
ClutterGravity gravity)
|
|
{
|
|
gfloat old_anchor_x, old_anchor_y, new_anchor_x, new_anchor_y;
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
clutter_anchor_coord_get_units (self, &priv->anchor,
|
|
&old_anchor_x,
|
|
&old_anchor_y,
|
|
NULL);
|
|
clutter_actor_set_anchor_point_from_gravity (self, gravity);
|
|
clutter_anchor_coord_get_units (self, &priv->anchor,
|
|
&new_anchor_x,
|
|
&new_anchor_y,
|
|
NULL);
|
|
|
|
if (priv->position_set)
|
|
clutter_actor_move_by (self,
|
|
new_anchor_x - old_anchor_x,
|
|
new_anchor_y - old_anchor_y);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_anchor_point_from_gravity:
|
|
* @self: a #ClutterActor
|
|
* @gravity: #ClutterGravity.
|
|
*
|
|
* Sets an anchor point on the actor, based on the given gravity (this is a
|
|
* convenience function wrapping clutter_actor_set_anchor_point()).
|
|
*
|
|
* Since version 1.0 the anchor point will be stored as a gravity so
|
|
* that if the actor changes size then the anchor point will move. For
|
|
* example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST
|
|
* and later double the size of the actor, the anchor point will move
|
|
* to the bottom right.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_actor_set_anchor_point_from_gravity (ClutterActor *self,
|
|
ClutterGravity gravity)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (gravity == CLUTTER_GRAVITY_NONE)
|
|
clutter_actor_set_anchor_point (self, 0, 0);
|
|
else
|
|
{
|
|
clutter_anchor_coord_set_gravity (&self->priv->anchor, gravity);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ANCHOR_GRAVITY]);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ANCHOR_X]);
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ANCHOR_Y]);
|
|
}
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
PARSE_X,
|
|
PARSE_Y,
|
|
PARSE_WIDTH,
|
|
PARSE_HEIGHT,
|
|
PARSE_ANCHOR_X,
|
|
PARSE_ANCHOR_Y
|
|
} ParseDimension;
|
|
|
|
static gfloat
|
|
parse_units (ClutterActor *self,
|
|
ParseDimension dimension,
|
|
JsonNode *node)
|
|
{
|
|
GValue value = { 0, };
|
|
gfloat retval = 0;
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
|
|
return 0;
|
|
|
|
json_node_get_value (node, &value);
|
|
|
|
if (G_VALUE_HOLDS (&value, G_TYPE_INT64))
|
|
{
|
|
retval = (gfloat) g_value_get_int64 (&value);
|
|
}
|
|
else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE))
|
|
{
|
|
retval = g_value_get_double (&value);
|
|
}
|
|
else if (G_VALUE_HOLDS (&value, G_TYPE_STRING))
|
|
{
|
|
ClutterUnits units;
|
|
gboolean res;
|
|
|
|
res = clutter_units_from_string (&units, g_value_get_string (&value));
|
|
if (res)
|
|
retval = clutter_units_to_pixels (&units);
|
|
else
|
|
{
|
|
g_warning ("Invalid value '%s': integers, strings or floating point "
|
|
"values can be used for the x, y, width and height "
|
|
"properties. Valid modifiers for strings are 'px', 'mm', "
|
|
"'pt' and 'em'.",
|
|
g_value_get_string (&value));
|
|
retval = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Invalid value of type '%s': integers, strings of floating "
|
|
"point values can be used for the x, y, width, height "
|
|
"anchor-x and anchor-y properties.",
|
|
g_type_name (G_VALUE_TYPE (&value)));
|
|
}
|
|
|
|
g_value_unset (&value);
|
|
|
|
return retval;
|
|
}
|
|
|
|
typedef struct {
|
|
ClutterRotateAxis axis;
|
|
|
|
gdouble angle;
|
|
|
|
gfloat center_x;
|
|
gfloat center_y;
|
|
gfloat center_z;
|
|
} RotationInfo;
|
|
|
|
static inline gboolean
|
|
parse_rotation_array (ClutterActor *actor,
|
|
JsonArray *array,
|
|
RotationInfo *info)
|
|
{
|
|
JsonNode *element;
|
|
|
|
if (json_array_get_length (array) != 2)
|
|
return FALSE;
|
|
|
|
/* angle */
|
|
element = json_array_get_element (array, 0);
|
|
if (JSON_NODE_TYPE (element) == JSON_NODE_VALUE)
|
|
info->angle = json_node_get_double (element);
|
|
else
|
|
return FALSE;
|
|
|
|
/* center */
|
|
element = json_array_get_element (array, 1);
|
|
if (JSON_NODE_TYPE (element) == JSON_NODE_ARRAY)
|
|
{
|
|
JsonArray *center = json_node_get_array (element);
|
|
|
|
if (json_array_get_length (center) != 2)
|
|
return FALSE;
|
|
|
|
switch (info->axis)
|
|
{
|
|
case CLUTTER_X_AXIS:
|
|
info->center_y = parse_units (actor, PARSE_Y,
|
|
json_array_get_element (center, 0));
|
|
info->center_z = parse_units (actor, PARSE_Y,
|
|
json_array_get_element (center, 1));
|
|
return TRUE;
|
|
|
|
case CLUTTER_Y_AXIS:
|
|
info->center_x = parse_units (actor, PARSE_X,
|
|
json_array_get_element (center, 0));
|
|
info->center_z = parse_units (actor, PARSE_X,
|
|
json_array_get_element (center, 1));
|
|
return TRUE;
|
|
|
|
case CLUTTER_Z_AXIS:
|
|
info->center_x = parse_units (actor, PARSE_X,
|
|
json_array_get_element (center, 0));
|
|
info->center_y = parse_units (actor, PARSE_Y,
|
|
json_array_get_element (center, 1));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_rotation (ClutterActor *actor,
|
|
JsonNode *node,
|
|
RotationInfo *info)
|
|
{
|
|
JsonArray *array;
|
|
guint len, i;
|
|
gboolean retval = FALSE;
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
|
|
{
|
|
g_warning ("Invalid node of type '%s' found, expecting an array",
|
|
json_node_type_name (node));
|
|
return FALSE;
|
|
}
|
|
|
|
array = json_node_get_array (node);
|
|
len = json_array_get_length (array);
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
JsonNode *element = json_array_get_element (array, i);
|
|
JsonObject *object;
|
|
JsonNode *member;
|
|
|
|
if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
|
|
{
|
|
g_warning ("Invalid node of type '%s' found, expecting an object",
|
|
json_node_type_name (element));
|
|
return FALSE;
|
|
}
|
|
|
|
object = json_node_get_object (element);
|
|
|
|
if (json_object_has_member (object, "x-axis"))
|
|
{
|
|
member = json_object_get_member (object, "x-axis");
|
|
|
|
info->axis = CLUTTER_X_AXIS;
|
|
|
|
if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
|
|
{
|
|
info->angle = json_node_get_double (member);
|
|
retval = TRUE;
|
|
}
|
|
else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
|
|
retval = parse_rotation_array (actor,
|
|
json_node_get_array (member),
|
|
info);
|
|
else
|
|
retval = FALSE;
|
|
}
|
|
else if (json_object_has_member (object, "y-axis"))
|
|
{
|
|
member = json_object_get_member (object, "y-axis");
|
|
|
|
info->axis = CLUTTER_Y_AXIS;
|
|
|
|
if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
|
|
{
|
|
info->angle = json_node_get_double (member);
|
|
retval = TRUE;
|
|
}
|
|
else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
|
|
retval = parse_rotation_array (actor,
|
|
json_node_get_array (member),
|
|
info);
|
|
else
|
|
retval = FALSE;
|
|
}
|
|
else if (json_object_has_member (object, "z-axis"))
|
|
{
|
|
member = json_object_get_member (object, "z-axis");
|
|
|
|
info->axis = CLUTTER_Z_AXIS;
|
|
|
|
if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
|
|
{
|
|
info->angle = json_node_get_double (member);
|
|
retval = TRUE;
|
|
}
|
|
else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
|
|
retval = parse_rotation_array (actor,
|
|
json_node_get_array (member),
|
|
info);
|
|
else
|
|
retval = FALSE;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static GSList *
|
|
parse_actor_metas (ClutterScript *script,
|
|
ClutterActor *actor,
|
|
JsonNode *node)
|
|
{
|
|
GList *elements, *l;
|
|
GSList *retval = NULL;
|
|
|
|
if (!JSON_NODE_HOLDS_ARRAY (node))
|
|
return NULL;
|
|
|
|
elements = json_array_get_elements (json_node_get_array (node));
|
|
|
|
for (l = elements; l != NULL; l = l->next)
|
|
{
|
|
JsonNode *element = l->data;
|
|
const gchar *id = _clutter_script_get_id_from_node (element);
|
|
GObject *meta;
|
|
|
|
if (id == NULL || *id == '\0')
|
|
continue;
|
|
|
|
meta = clutter_script_get_object (script, id);
|
|
if (meta == NULL)
|
|
continue;
|
|
|
|
retval = g_slist_prepend (retval, meta);
|
|
}
|
|
|
|
g_list_free (elements);
|
|
|
|
return g_slist_reverse (retval);
|
|
}
|
|
|
|
static GSList *
|
|
parse_behaviours (ClutterScript *script,
|
|
ClutterActor *actor,
|
|
JsonNode *node)
|
|
{
|
|
GList *elements, *l;
|
|
GSList *retval = NULL;
|
|
|
|
if (!JSON_NODE_HOLDS_ARRAY (node))
|
|
return NULL;
|
|
|
|
elements = json_array_get_elements (json_node_get_array (node));
|
|
|
|
for (l = elements; l != NULL; l = l->next)
|
|
{
|
|
JsonNode *element = l->data;
|
|
const gchar *id = _clutter_script_get_id_from_node (element);
|
|
GObject *behaviour;
|
|
|
|
if (id == NULL || *id == '\0')
|
|
continue;
|
|
|
|
behaviour = clutter_script_get_object (script, id);
|
|
if (behaviour == NULL)
|
|
continue;
|
|
|
|
retval = g_slist_prepend (retval, behaviour);
|
|
}
|
|
|
|
g_list_free (elements);
|
|
|
|
return g_slist_reverse (retval);
|
|
}
|
|
|
|
static gboolean
|
|
clutter_actor_parse_custom_node (ClutterScriptable *scriptable,
|
|
ClutterScript *script,
|
|
GValue *value,
|
|
const gchar *name,
|
|
JsonNode *node)
|
|
{
|
|
ClutterActor *actor = CLUTTER_ACTOR (scriptable);
|
|
gboolean retval = FALSE;
|
|
|
|
if ((name[0] == 'x' && name[1] == '\0') ||
|
|
(name[0] == 'y' && name[1] == '\0') ||
|
|
(strcmp (name, "width") == 0) ||
|
|
(strcmp (name, "height") == 0) ||
|
|
(strcmp (name, "anchor_x") == 0) ||
|
|
(strcmp (name, "anchor_y") == 0))
|
|
{
|
|
ParseDimension dimension;
|
|
gfloat units;
|
|
|
|
if (name[0] == 'x')
|
|
dimension = PARSE_X;
|
|
else if (name[0] == 'y')
|
|
dimension = PARSE_Y;
|
|
else if (name[0] == 'w')
|
|
dimension = PARSE_WIDTH;
|
|
else if (name[0] == 'h')
|
|
dimension = PARSE_HEIGHT;
|
|
else if (name[0] == 'a' && name[7] == 'x')
|
|
dimension = PARSE_ANCHOR_X;
|
|
else if (name[0] == 'a' && name[7] == 'y')
|
|
dimension = PARSE_ANCHOR_Y;
|
|
else
|
|
return FALSE;
|
|
|
|
units = parse_units (actor, dimension, node);
|
|
|
|
/* convert back to pixels: all properties are pixel-based */
|
|
g_value_init (value, G_TYPE_FLOAT);
|
|
g_value_set_float (value, units);
|
|
|
|
retval = TRUE;
|
|
}
|
|
else if (strcmp (name, "rotation") == 0)
|
|
{
|
|
RotationInfo *info;
|
|
|
|
info = g_slice_new0 (RotationInfo);
|
|
retval = parse_rotation (actor, node, info);
|
|
|
|
if (retval)
|
|
{
|
|
g_value_init (value, G_TYPE_POINTER);
|
|
g_value_set_pointer (value, info);
|
|
}
|
|
else
|
|
g_slice_free (RotationInfo, info);
|
|
}
|
|
else if (strcmp (name, "behaviours") == 0)
|
|
{
|
|
GSList *l;
|
|
|
|
l = parse_behaviours (script, actor, node);
|
|
|
|
g_value_init (value, G_TYPE_POINTER);
|
|
g_value_set_pointer (value, l);
|
|
|
|
retval = TRUE;
|
|
}
|
|
else if (strcmp (name, "actions") == 0 ||
|
|
strcmp (name, "constraints") == 0 ||
|
|
strcmp (name, "effects") == 0)
|
|
{
|
|
GSList *l;
|
|
|
|
l = parse_actor_metas (script, actor, node);
|
|
|
|
g_value_init (value, G_TYPE_POINTER);
|
|
g_value_set_pointer (value, l);
|
|
|
|
retval = TRUE;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_custom_property (ClutterScriptable *scriptable,
|
|
ClutterScript *script,
|
|
const gchar *name,
|
|
const GValue *value)
|
|
{
|
|
ClutterActor *actor = CLUTTER_ACTOR (scriptable);
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
if (G_UNLIKELY (CLUTTER_HAS_DEBUG (SCRIPT)))
|
|
{
|
|
gchar *tmp = g_strdup_value_contents (value);
|
|
|
|
CLUTTER_NOTE (SCRIPT,
|
|
"in ClutterActor::set_custom_property('%s') = %s",
|
|
name,
|
|
tmp);
|
|
|
|
g_free (tmp);
|
|
}
|
|
#endif /* CLUTTER_ENABLE_DEBUG */
|
|
|
|
if (strcmp (name, "rotation") == 0)
|
|
{
|
|
RotationInfo *info;
|
|
|
|
if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
|
|
return;
|
|
|
|
info = g_value_get_pointer (value);
|
|
|
|
clutter_actor_set_rotation (actor,
|
|
info->axis, info->angle,
|
|
info->center_x,
|
|
info->center_y,
|
|
info->center_z);
|
|
|
|
g_slice_free (RotationInfo, info);
|
|
|
|
return;
|
|
}
|
|
|
|
if (strcmp (name, "behaviours") == 0)
|
|
{
|
|
GSList *behaviours, *l;
|
|
|
|
if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
|
|
return;
|
|
|
|
behaviours = g_value_get_pointer (value);
|
|
for (l = behaviours; l != NULL; l = l->next)
|
|
{
|
|
ClutterBehaviour *behaviour = l->data;
|
|
|
|
clutter_behaviour_apply (behaviour, actor);
|
|
}
|
|
|
|
g_slist_free (behaviours);
|
|
|
|
return;
|
|
}
|
|
|
|
if (strcmp (name, "actions") == 0 ||
|
|
strcmp (name, "constraints") == 0 ||
|
|
strcmp (name, "effects") == 0)
|
|
{
|
|
GSList *metas, *l;
|
|
|
|
if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
|
|
return;
|
|
|
|
metas = g_value_get_pointer (value);
|
|
for (l = metas; l != NULL; l = l->next)
|
|
{
|
|
if (name[0] == 'a')
|
|
clutter_actor_add_action (actor, l->data);
|
|
|
|
if (name[0] == 'c')
|
|
clutter_actor_add_constraint (actor, l->data);
|
|
|
|
if (name[0] == 'e')
|
|
clutter_actor_add_effect (actor, l->data);
|
|
}
|
|
|
|
g_slist_free (metas);
|
|
|
|
return;
|
|
}
|
|
|
|
g_object_set_property (G_OBJECT (scriptable), name, value);
|
|
}
|
|
|
|
static void
|
|
clutter_scriptable_iface_init (ClutterScriptableIface *iface)
|
|
{
|
|
iface->parse_custom_node = clutter_actor_parse_custom_node;
|
|
iface->set_custom_property = clutter_actor_set_custom_property;
|
|
}
|
|
|
|
static ClutterActorMeta *
|
|
get_meta_from_animation_property (ClutterActor *actor,
|
|
const gchar *name,
|
|
gchar **name_p)
|
|
{
|
|
ClutterActorPrivate *priv = actor->priv;
|
|
ClutterActorMeta *meta = NULL;
|
|
gchar **tokens;
|
|
|
|
/* if this is not a special property, fall through */
|
|
if (name[0] != '@')
|
|
return NULL;
|
|
|
|
/* detect the properties named using the following spec:
|
|
*
|
|
* @<section>.<meta-name>.<property-name>
|
|
*
|
|
* where <section> can be one of the following:
|
|
*
|
|
* - actions
|
|
* - constraints
|
|
* - effects
|
|
*
|
|
* and <meta-name> is the name set on a specific ActorMeta
|
|
*/
|
|
|
|
tokens = g_strsplit (name + 1, ".", -1);
|
|
if (tokens == NULL || g_strv_length (tokens) != 3)
|
|
{
|
|
CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
|
|
name + 1);
|
|
g_strfreev (tokens);
|
|
return NULL;
|
|
}
|
|
|
|
if (strcmp (tokens[0], "actions") == 0)
|
|
meta = _clutter_meta_group_get_meta (priv->actions, tokens[1]);
|
|
|
|
if (strcmp (tokens[0], "constraints") == 0)
|
|
meta = _clutter_meta_group_get_meta (priv->constraints, tokens[1]);
|
|
|
|
if (strcmp (tokens[0], "effects") == 0)
|
|
meta = _clutter_meta_group_get_meta (priv->effects, tokens[1]);
|
|
|
|
if (name_p != NULL)
|
|
*name_p = g_strdup (tokens[2]);
|
|
|
|
CLUTTER_NOTE (ANIMATION,
|
|
"Looking for property '%s' of object '%s' in section '%s'",
|
|
tokens[2],
|
|
tokens[1],
|
|
tokens[0]);
|
|
|
|
g_strfreev (tokens);
|
|
|
|
return meta;
|
|
}
|
|
|
|
static GParamSpec *
|
|
clutter_actor_find_property (ClutterAnimatable *animatable,
|
|
const gchar *property_name)
|
|
{
|
|
ClutterActorMeta *meta = NULL;
|
|
GObjectClass *klass = NULL;
|
|
GParamSpec *pspec = NULL;
|
|
gchar *p_name = NULL;
|
|
|
|
meta = get_meta_from_animation_property (CLUTTER_ACTOR (animatable),
|
|
property_name,
|
|
&p_name);
|
|
|
|
if (meta != NULL)
|
|
{
|
|
klass = G_OBJECT_GET_CLASS (meta);
|
|
|
|
pspec = g_object_class_find_property (klass, p_name);
|
|
|
|
g_free (p_name);
|
|
}
|
|
else
|
|
{
|
|
klass = G_OBJECT_GET_CLASS (animatable);
|
|
|
|
pspec = g_object_class_find_property (klass, property_name);
|
|
}
|
|
|
|
return pspec;
|
|
}
|
|
|
|
static void
|
|
clutter_actor_get_initial_state (ClutterAnimatable *animatable,
|
|
const gchar *property_name,
|
|
GValue *initial)
|
|
{
|
|
ClutterActorMeta *meta = NULL;
|
|
gchar *p_name = NULL;
|
|
|
|
meta = get_meta_from_animation_property (CLUTTER_ACTOR (animatable),
|
|
property_name,
|
|
&p_name);
|
|
|
|
if (meta != NULL)
|
|
g_object_get_property (G_OBJECT (meta), p_name, initial);
|
|
else
|
|
g_object_get_property (G_OBJECT (animatable), property_name, initial);
|
|
|
|
g_free (p_name);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_set_final_state (ClutterAnimatable *animatable,
|
|
const gchar *property_name,
|
|
const GValue *final)
|
|
{
|
|
ClutterActorMeta *meta = NULL;
|
|
gchar *p_name = NULL;
|
|
|
|
meta = get_meta_from_animation_property (CLUTTER_ACTOR (animatable),
|
|
property_name,
|
|
&p_name);
|
|
if (meta != NULL)
|
|
g_object_set_property (G_OBJECT (meta), p_name, final);
|
|
else
|
|
g_object_set_property (G_OBJECT (animatable), property_name, final);
|
|
|
|
g_free (p_name);
|
|
}
|
|
|
|
static gboolean
|
|
clutter_actor_animate_property (ClutterAnimatable *animatable,
|
|
ClutterAnimation *animation,
|
|
const gchar *property_name,
|
|
const GValue *initial,
|
|
const GValue *final,
|
|
gdouble progress,
|
|
GValue *new_value)
|
|
{
|
|
ClutterInterval *interval;
|
|
|
|
interval = clutter_animation_get_interval (animation, property_name);
|
|
|
|
return clutter_interval_compute_value (interval, progress, new_value);
|
|
}
|
|
|
|
static void
|
|
clutter_animatable_iface_init (ClutterAnimatableIface *iface)
|
|
{
|
|
iface->animate_property = clutter_actor_animate_property;
|
|
iface->find_property = clutter_actor_find_property;
|
|
iface->get_initial_state = clutter_actor_get_initial_state;
|
|
iface->set_final_state = clutter_actor_set_final_state;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_transform_stage_point:
|
|
* @self: A #ClutterActor
|
|
* @x: (in): x screen coordinate of the point to unproject
|
|
* @y: (in): y screen coordinate of the point to unproject
|
|
* @x_out: (out): return location for the unprojected x coordinance
|
|
* @y_out: (out): return location for the unprojected y coordinance
|
|
*
|
|
* This function translates screen coordinates (@x, @y) to
|
|
* coordinates relative to the actor. For example, it can be used to translate
|
|
* screen events from global screen coordinates into actor-local coordinates.
|
|
*
|
|
* The conversion can fail, notably if the transform stack results in the
|
|
* actor being projected on the screen as a mere line.
|
|
*
|
|
* The conversion should not be expected to be pixel-perfect due to the
|
|
* nature of the operation. In general the error grows when the skewing
|
|
* of the actor rectangle on screen increases.
|
|
*
|
|
* <note><para>This function can be computationally intensive.</para></note>
|
|
*
|
|
* <note><para>This function only works when the allocation is up-to-date,
|
|
* i.e. inside of paint().</para></note>
|
|
*
|
|
* Return value: %TRUE if conversion was successful.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_actor_transform_stage_point (ClutterActor *self,
|
|
gfloat x,
|
|
gfloat y,
|
|
gfloat *x_out,
|
|
gfloat *y_out)
|
|
{
|
|
ClutterVertex v[4];
|
|
float ST[3][3];
|
|
float RQ[3][3];
|
|
int du, dv, xi, yi;
|
|
float px, py;
|
|
float xf, yf, wf, det;
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
priv = self->priv;
|
|
|
|
/* This implementation is based on the quad -> quad projection algorithm
|
|
* described by Paul Heckbert in:
|
|
*
|
|
* http://www.cs.cmu.edu/~ph/texfund/texfund.pdf
|
|
*
|
|
* and the sample implementation at:
|
|
*
|
|
* http://www.cs.cmu.edu/~ph/src/texfund/
|
|
*
|
|
* Our texture is a rectangle with origin [0, 0], so we are mapping from
|
|
* quad to rectangle only, which significantly simplifies things; the
|
|
* function calls have been unrolled, and most of the math is done in fixed
|
|
* point.
|
|
*/
|
|
|
|
clutter_actor_get_abs_allocation_vertices (self, v);
|
|
|
|
/* Keeping these as ints simplifies the multiplication (no significant
|
|
* loss of precision here).
|
|
*/
|
|
du = (int) (priv->allocation.x2 - priv->allocation.x1);
|
|
dv = (int) (priv->allocation.y2 - priv->allocation.y1);
|
|
|
|
if (!du || !dv)
|
|
return FALSE;
|
|
|
|
#define UX2FP(x) (x)
|
|
#define DET2FP(a,b,c,d) (((a) * (d)) - ((b) * (c)))
|
|
|
|
/* First, find mapping from unit uv square to xy quadrilateral; this
|
|
* equivalent to the pmap_square_quad() functions in the sample
|
|
* implementation, which we can simplify, since our target is always
|
|
* a rectangle.
|
|
*/
|
|
px = v[0].x - v[1].x + v[3].x - v[2].x;
|
|
py = v[0].y - v[1].y + v[3].y - v[2].y;
|
|
|
|
if (!px && !py)
|
|
{
|
|
/* affine transform */
|
|
RQ[0][0] = UX2FP (v[1].x - v[0].x);
|
|
RQ[1][0] = UX2FP (v[3].x - v[1].x);
|
|
RQ[2][0] = UX2FP (v[0].x);
|
|
RQ[0][1] = UX2FP (v[1].y - v[0].y);
|
|
RQ[1][1] = UX2FP (v[3].y - v[1].y);
|
|
RQ[2][1] = UX2FP (v[0].y);
|
|
RQ[0][2] = 0;
|
|
RQ[1][2] = 0;
|
|
RQ[2][2] = 1.0;
|
|
}
|
|
else
|
|
{
|
|
/* projective transform */
|
|
double dx1, dx2, dy1, dy2, del;
|
|
|
|
dx1 = UX2FP (v[1].x - v[3].x);
|
|
dx2 = UX2FP (v[2].x - v[3].x);
|
|
dy1 = UX2FP (v[1].y - v[3].y);
|
|
dy2 = UX2FP (v[2].y - v[3].y);
|
|
|
|
del = DET2FP (dx1, dx2, dy1, dy2);
|
|
if (!del)
|
|
return FALSE;
|
|
|
|
/*
|
|
* The division here needs to be done in floating point for
|
|
* precisions reasons.
|
|
*/
|
|
RQ[0][2] = (DET2FP (UX2FP (px), dx2, UX2FP (py), dy2) / del);
|
|
RQ[1][2] = (DET2FP (dx1, UX2FP (px), dy1, UX2FP (py)) / del);
|
|
RQ[1][2] = (DET2FP (dx1, UX2FP (px), dy1, UX2FP (py)) / del);
|
|
RQ[2][2] = 1.0;
|
|
RQ[0][0] = UX2FP (v[1].x - v[0].x) + (RQ[0][2] * UX2FP (v[1].x));
|
|
RQ[1][0] = UX2FP (v[2].x - v[0].x) + (RQ[1][2] * UX2FP (v[2].x));
|
|
RQ[2][0] = UX2FP (v[0].x);
|
|
RQ[0][1] = UX2FP (v[1].y - v[0].y) + (RQ[0][2] * UX2FP (v[1].y));
|
|
RQ[1][1] = UX2FP (v[2].y - v[0].y) + (RQ[1][2] * UX2FP (v[2].y));
|
|
RQ[2][1] = UX2FP (v[0].y);
|
|
}
|
|
|
|
/*
|
|
* Now combine with transform from our rectangle (u0,v0,u1,v1) to unit
|
|
* square. Since our rectangle is based at 0,0 we only need to scale.
|
|
*/
|
|
RQ[0][0] /= du;
|
|
RQ[1][0] /= dv;
|
|
RQ[0][1] /= du;
|
|
RQ[1][1] /= dv;
|
|
RQ[0][2] /= du;
|
|
RQ[1][2] /= dv;
|
|
|
|
/*
|
|
* Now RQ is transform from uv rectangle to xy quadrilateral; we need an
|
|
* inverse of that.
|
|
*/
|
|
ST[0][0] = DET2FP (RQ[1][1], RQ[1][2], RQ[2][1], RQ[2][2]);
|
|
ST[1][0] = DET2FP (RQ[1][2], RQ[1][0], RQ[2][2], RQ[2][0]);
|
|
ST[2][0] = DET2FP (RQ[1][0], RQ[1][1], RQ[2][0], RQ[2][1]);
|
|
ST[0][1] = DET2FP (RQ[2][1], RQ[2][2], RQ[0][1], RQ[0][2]);
|
|
ST[1][1] = DET2FP (RQ[2][2], RQ[2][0], RQ[0][2], RQ[0][0]);
|
|
ST[2][1] = DET2FP (RQ[2][0], RQ[2][1], RQ[0][0], RQ[0][1]);
|
|
ST[0][2] = DET2FP (RQ[0][1], RQ[0][2], RQ[1][1], RQ[1][2]);
|
|
ST[1][2] = DET2FP (RQ[0][2], RQ[0][0], RQ[1][2], RQ[1][0]);
|
|
ST[2][2] = DET2FP (RQ[0][0], RQ[0][1], RQ[1][0], RQ[1][1]);
|
|
|
|
/*
|
|
* Check the resulting matrix is OK.
|
|
*/
|
|
det = (RQ[0][0] * ST[0][0])
|
|
+ (RQ[0][1] * ST[0][1])
|
|
+ (RQ[0][2] * ST[0][2]);
|
|
if (!det)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Now transform our point with the ST matrix; the notional w
|
|
* coordinate is 1, hence the last part is simply added.
|
|
*/
|
|
xi = (int) x;
|
|
yi = (int) y;
|
|
|
|
xf = xi * ST[0][0] + yi * ST[1][0] + ST[2][0];
|
|
yf = xi * ST[0][1] + yi * ST[1][1] + ST[2][1];
|
|
wf = xi * ST[0][2] + yi * ST[1][2] + ST[2][2];
|
|
|
|
if (x_out)
|
|
*x_out = xf / wf;
|
|
|
|
if (y_out)
|
|
*y_out = yf / wf;
|
|
|
|
#undef UX2FP
|
|
#undef DET2FP
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ClutterGeometry
|
|
*/
|
|
|
|
static ClutterGeometry*
|
|
clutter_geometry_copy (const ClutterGeometry *geometry)
|
|
{
|
|
return g_slice_dup (ClutterGeometry, geometry);
|
|
}
|
|
|
|
static void
|
|
clutter_geometry_free (ClutterGeometry *geometry)
|
|
{
|
|
if (G_LIKELY (geometry != NULL))
|
|
g_slice_free (ClutterGeometry, geometry);
|
|
}
|
|
|
|
/**
|
|
* clutter_geometry_union:
|
|
* @geometry_a: a #ClutterGeometry
|
|
* @geometry_b: another #ClutterGeometry
|
|
* @result: (out): location to store the result
|
|
*
|
|
* Find the union of two rectangles represented as #ClutterGeometry.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_geometry_union (const ClutterGeometry *geometry_a,
|
|
const ClutterGeometry *geometry_b,
|
|
ClutterGeometry *result)
|
|
{
|
|
/* We don't try to handle rectangles that can't be represented
|
|
* as a signed integer box */
|
|
gint x_1 = MIN (geometry_a->x, geometry_b->x);
|
|
gint y_1 = MIN (geometry_a->y, geometry_b->y);
|
|
gint x_2 = MAX (geometry_a->x + (gint)geometry_a->width,
|
|
geometry_b->x + (gint)geometry_b->width);
|
|
gint y_2 = MAX (geometry_a->y + (gint)geometry_a->height,
|
|
geometry_b->y + (gint)geometry_b->height);
|
|
result->x = x_1;
|
|
result->y = y_1;
|
|
result->width = x_2 - x_1;
|
|
result->height = y_2 - y_1;
|
|
}
|
|
|
|
/**
|
|
* clutter_geometry_intersects:
|
|
* @geometry0: The first geometry to test
|
|
* @geometry1: The second geometry to test
|
|
*
|
|
* Determines if @geometry0 and geometry1 intersect returning %TRUE if
|
|
* they do else %FALSE.
|
|
*
|
|
* Return value: %TRUE of @geometry0 and geometry1 intersect else
|
|
* %FALSE.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
clutter_geometry_intersects (const ClutterGeometry *geometry0,
|
|
const ClutterGeometry *geometry1)
|
|
{
|
|
if (geometry1->x >= (geometry0->x + (gint)geometry0->width) ||
|
|
geometry1->y >= (geometry0->y + (gint)geometry0->height) ||
|
|
(geometry1->x + (gint)geometry1->width) <= geometry0->x ||
|
|
(geometry1->y + (gint)geometry1->height) <= geometry0->y)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_geometry_progress (const GValue *a,
|
|
const GValue *b,
|
|
gdouble progress,
|
|
GValue *retval)
|
|
{
|
|
const ClutterGeometry *a_geom = g_value_get_boxed (a);
|
|
const ClutterGeometry *b_geom = g_value_get_boxed (b);
|
|
ClutterGeometry res = { 0, };
|
|
|
|
res.x = a_geom->x + (b_geom->x - a_geom->x) * progress;
|
|
res.y = a_geom->y + (b_geom->y - a_geom->y) * progress;
|
|
|
|
res.width = a_geom->width + (b_geom->width - a_geom->width) * progress;
|
|
res.height = a_geom->height + (b_geom->height - a_geom->height) * progress;
|
|
|
|
g_value_set_boxed (retval, &res);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterGeometry, clutter_geometry,
|
|
clutter_geometry_copy,
|
|
clutter_geometry_free,
|
|
CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_geometry_progress));
|
|
|
|
/*
|
|
* ClutterVertices
|
|
*/
|
|
|
|
/**
|
|
* clutter_vertex_new:
|
|
* @x: X coordinate
|
|
* @y: Y coordinate
|
|
* @z: Z coordinate
|
|
*
|
|
* Creates a new #ClutterVertex for the point in 3D space
|
|
* identified by the 3 coordinates @x, @y, @z
|
|
*
|
|
* Return value: the newly allocate #ClutterVertex. Use
|
|
* clutter_vertex_free() to free the resources
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterVertex *
|
|
clutter_vertex_new (gfloat x,
|
|
gfloat y,
|
|
gfloat z)
|
|
{
|
|
ClutterVertex *vertex;
|
|
|
|
vertex = g_slice_new (ClutterVertex);
|
|
vertex->x = x;
|
|
vertex->y = y;
|
|
vertex->z = z;
|
|
|
|
return vertex;
|
|
}
|
|
|
|
/**
|
|
* clutter_vertex_copy:
|
|
* @vertex: a #ClutterVertex
|
|
*
|
|
* Copies @vertex
|
|
*
|
|
* Return value: a newly allocated copy of #ClutterVertex. Use
|
|
* clutter_vertex_free() to free the allocated resources
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterVertex *
|
|
clutter_vertex_copy (const ClutterVertex *vertex)
|
|
{
|
|
if (G_LIKELY (vertex != NULL))
|
|
return g_slice_dup (ClutterVertex, vertex);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_vertex_free:
|
|
* @vertex: a #ClutterVertex
|
|
*
|
|
* Frees a #ClutterVertex allocated using clutter_vertex_copy()
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_vertex_free (ClutterVertex *vertex)
|
|
{
|
|
if (G_UNLIKELY (vertex != NULL))
|
|
g_slice_free (ClutterVertex, vertex);
|
|
}
|
|
|
|
/**
|
|
* clutter_vertex_equal:
|
|
* @vertex_a: a #ClutterVertex
|
|
* @vertex_b: a #ClutterVertex
|
|
*
|
|
* Compares @vertex_a and @vertex_b for equality
|
|
*
|
|
* Return value: %TRUE if the passed #ClutterVertex are equal
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gboolean
|
|
clutter_vertex_equal (const ClutterVertex *vertex_a,
|
|
const ClutterVertex *vertex_b)
|
|
{
|
|
g_return_val_if_fail (vertex_a != NULL && vertex_b != NULL, FALSE);
|
|
|
|
if (vertex_a == vertex_b)
|
|
return TRUE;
|
|
|
|
return vertex_a->x == vertex_b->x &&
|
|
vertex_a->y == vertex_b->y &&
|
|
vertex_a->z == vertex_b->z;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_vertex_progress (const GValue *a,
|
|
const GValue *b,
|
|
gdouble progress,
|
|
GValue *retval)
|
|
{
|
|
const ClutterVertex *av = g_value_get_boxed (a);
|
|
const ClutterVertex *bv = g_value_get_boxed (b);
|
|
ClutterVertex res = { 0, };
|
|
|
|
res.x = av->x + (bv->x - av->x) * progress;
|
|
res.y = av->y + (bv->y - av->y) * progress;
|
|
res.z = av->z + (bv->z - av->z) * progress;
|
|
|
|
g_value_set_boxed (retval, &res);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterVertex, clutter_vertex,
|
|
clutter_vertex_copy,
|
|
clutter_vertex_free,
|
|
CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_vertex_progress));
|
|
|
|
/******************************************************************************/
|
|
|
|
struct _ShaderData
|
|
{
|
|
ClutterShader *shader;
|
|
|
|
/* list of values that should be set on the shader
|
|
* before each paint cycle
|
|
*/
|
|
GHashTable *value_hash;
|
|
};
|
|
|
|
static void
|
|
shader_value_free (gpointer data)
|
|
{
|
|
GValue *var = data;
|
|
g_value_unset (var);
|
|
g_slice_free (GValue, var);
|
|
}
|
|
|
|
static void
|
|
destroy_shader_data (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *actor_priv = self->priv;
|
|
ShaderData *shader_data = actor_priv->shader_data;
|
|
|
|
if (shader_data == NULL)
|
|
return;
|
|
|
|
if (shader_data->shader)
|
|
{
|
|
g_object_unref (shader_data->shader);
|
|
shader_data->shader = NULL;
|
|
}
|
|
|
|
if (shader_data->value_hash)
|
|
{
|
|
g_hash_table_destroy (shader_data->value_hash);
|
|
shader_data->value_hash = NULL;
|
|
}
|
|
|
|
g_slice_free (ShaderData, shader_data);
|
|
actor_priv->shader_data = NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* clutter_actor_get_shader:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Queries the currently set #ClutterShader on @self.
|
|
*
|
|
* Return value: (transfer none): The currently set #ClutterShader
|
|
* or %NULL if no shader is set.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterShader *
|
|
clutter_actor_get_shader (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *actor_priv;
|
|
ShaderData *shader_data;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
actor_priv = self->priv;
|
|
shader_data = actor_priv->shader_data;
|
|
|
|
if (shader_data == NULL)
|
|
return NULL;
|
|
|
|
return shader_data->shader;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_shader:
|
|
* @self: a #ClutterActor
|
|
* @shader: (allow-none): a #ClutterShader or %NULL to unset the shader.
|
|
*
|
|
* Sets the #ClutterShader to be used when rendering @self.
|
|
*
|
|
* If @shader is %NULL it will unset any currently set shader
|
|
* for the actor.
|
|
*
|
|
* Return value: %TRUE if the shader was successfully applied
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_actor_set_shader (ClutterActor *self,
|
|
ClutterShader *shader)
|
|
{
|
|
ClutterActorPrivate *actor_priv;
|
|
ShaderData *shader_data;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
g_return_val_if_fail (shader == NULL || CLUTTER_IS_SHADER (shader), FALSE);
|
|
|
|
if (shader != NULL)
|
|
g_object_ref (shader);
|
|
else
|
|
{
|
|
/* if shader passed in is NULL we destroy the shader */
|
|
destroy_shader_data (self);
|
|
return TRUE;
|
|
}
|
|
|
|
actor_priv = self->priv;
|
|
shader_data = actor_priv->shader_data;
|
|
|
|
if (shader_data == NULL)
|
|
{
|
|
actor_priv->shader_data = shader_data = g_slice_new (ShaderData);
|
|
shader_data->shader = NULL;
|
|
shader_data->value_hash =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free,
|
|
shader_value_free);
|
|
}
|
|
if (shader_data->shader != NULL)
|
|
g_object_unref (shader_data->shader);
|
|
|
|
shader_data->shader = shader;
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
set_each_param (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
ClutterShader *shader = user_data;
|
|
GValue *var = value;
|
|
|
|
clutter_shader_set_uniform (shader, (const gchar *)key, var);
|
|
}
|
|
|
|
static void
|
|
clutter_actor_shader_pre_paint (ClutterActor *actor,
|
|
gboolean repeat)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ShaderData *shader_data;
|
|
ClutterShader *shader;
|
|
ClutterMainContext *context;
|
|
|
|
priv = actor->priv;
|
|
shader_data = priv->shader_data;
|
|
|
|
if (!shader_data)
|
|
return;
|
|
|
|
context = _clutter_context_get_default ();
|
|
shader = shader_data->shader;
|
|
|
|
if (shader)
|
|
{
|
|
clutter_shader_set_is_enabled (shader, TRUE);
|
|
|
|
g_hash_table_foreach (shader_data->value_hash, set_each_param, shader);
|
|
|
|
if (!repeat)
|
|
context->shaders = g_slist_prepend (context->shaders, actor);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_actor_shader_post_paint (ClutterActor *actor)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ShaderData *shader_data;
|
|
ClutterShader *shader;
|
|
ClutterMainContext *context;
|
|
|
|
priv = actor->priv;
|
|
shader_data = priv->shader_data;
|
|
|
|
if (!shader_data)
|
|
return;
|
|
|
|
context = _clutter_context_get_default ();
|
|
shader = shader_data->shader;
|
|
|
|
if (shader)
|
|
{
|
|
clutter_shader_set_is_enabled (shader, FALSE);
|
|
|
|
context->shaders = g_slist_remove (context->shaders, actor);
|
|
if (context->shaders)
|
|
{
|
|
/* call pre-paint again, this time with the second argument being
|
|
* TRUE, indicating that we are reapplying the shader and thus
|
|
* should not be prepended to the stack
|
|
*/
|
|
clutter_actor_shader_pre_paint (context->shaders->data, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_shader_param:
|
|
* @self: a #ClutterActor
|
|
* @param: the name of the parameter
|
|
* @value: the value of the parameter
|
|
*
|
|
* Sets the value for a named parameter of the shader applied
|
|
* to @actor.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_set_shader_param (ClutterActor *self,
|
|
const gchar *param,
|
|
const GValue *value)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ShaderData *shader_data;
|
|
GValue *var;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (param != NULL);
|
|
g_return_if_fail (CLUTTER_VALUE_HOLDS_SHADER_FLOAT (value) ||
|
|
CLUTTER_VALUE_HOLDS_SHADER_INT (value) ||
|
|
CLUTTER_VALUE_HOLDS_SHADER_MATRIX (value) ||
|
|
G_VALUE_HOLDS_FLOAT (value) ||
|
|
G_VALUE_HOLDS_INT (value));
|
|
|
|
priv = self->priv;
|
|
shader_data = priv->shader_data;
|
|
|
|
if (!shader_data)
|
|
return;
|
|
|
|
var = g_slice_new0 (GValue);
|
|
g_value_init (var, G_VALUE_TYPE (value));
|
|
g_value_copy (value, var);
|
|
g_hash_table_insert (shader_data->value_hash, g_strdup (param), var);
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_shader_param_float:
|
|
* @self: a #ClutterActor
|
|
* @param: the name of the parameter
|
|
* @value: the value of the parameter
|
|
*
|
|
* Sets the value for a named float parameter of the shader applied
|
|
* to @actor.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_set_shader_param_float (ClutterActor *self,
|
|
const gchar *param,
|
|
gfloat value)
|
|
{
|
|
GValue var = { 0, };
|
|
|
|
g_value_init (&var, G_TYPE_FLOAT);
|
|
g_value_set_float (&var, value);
|
|
|
|
clutter_actor_set_shader_param (self, param, &var);
|
|
|
|
g_value_unset (&var);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_shader_param_int:
|
|
* @self: a #ClutterActor
|
|
* @param: the name of the parameter
|
|
* @value: the value of the parameter
|
|
*
|
|
* Sets the value for a named int parameter of the shader applied to
|
|
* @actor.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_set_shader_param_int (ClutterActor *self,
|
|
const gchar *param,
|
|
gint value)
|
|
{
|
|
GValue var = { 0, };
|
|
|
|
g_value_init (&var, G_TYPE_INT);
|
|
g_value_set_int (&var, value);
|
|
|
|
clutter_actor_set_shader_param (self, param, &var);
|
|
|
|
g_value_unset (&var);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_is_rotated:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Checks whether any rotation is applied to the actor.
|
|
*
|
|
* Return value: %TRUE if the actor is rotated.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_actor_is_rotated (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->rxang || priv->ryang || priv->rzang)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_is_scaled:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Checks whether the actor is scaled in either dimension.
|
|
*
|
|
* Return value: %TRUE if the actor is scaled.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_actor_is_scaled (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->scale_x != 1.0 || priv->scale_y != 1.0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ClutterActor *
|
|
_clutter_actor_get_stage_internal (ClutterActor *actor)
|
|
{
|
|
while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor))
|
|
actor = actor->priv->parent_actor;
|
|
|
|
return actor;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_stage:
|
|
* @actor: a #ClutterActor
|
|
*
|
|
* Retrieves the #ClutterStage where @actor is contained.
|
|
*
|
|
* Return value: (transfer none): the stage containing the actor, or %NULL
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
ClutterActor *
|
|
clutter_actor_get_stage (ClutterActor *actor)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
|
|
|
|
return _clutter_actor_get_stage_internal (actor);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_allocate_available_size:
|
|
* @self: a #ClutterActor
|
|
* @x: the actor's X coordinate
|
|
* @y: the actor's Y coordinate
|
|
* @available_width: the maximum available width, or -1 to use the
|
|
* actor's natural width
|
|
* @available_height: the maximum available height, or -1 to use the
|
|
* actor's natural height
|
|
* @flags: flags controlling the allocation
|
|
*
|
|
* Allocates @self taking into account the #ClutterActor<!-- -->'s
|
|
* preferred size, but limiting it to the maximum available width
|
|
* and height provided.
|
|
*
|
|
* This function will do the right thing when dealing with the
|
|
* actor's request mode.
|
|
*
|
|
* The implementation of this function is equivalent to:
|
|
*
|
|
* |[
|
|
* if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
|
* {
|
|
* clutter_actor_get_preferred_width (self, available_height,
|
|
* &min_width,
|
|
* &natural_width);
|
|
* width = CLAMP (natural_width, min_width, available_width);
|
|
*
|
|
* clutter_actor_get_preferred_height (self, width,
|
|
* &min_height,
|
|
* &natural_height);
|
|
* height = CLAMP (natural_height, min_height, available_height);
|
|
* }
|
|
* else
|
|
* {
|
|
* clutter_actor_get_preferred_height (self, available_width,
|
|
* &min_height,
|
|
* &natural_height);
|
|
* height = CLAMP (natural_height, min_height, available_height);
|
|
*
|
|
* clutter_actor_get_preferred_width (self, height,
|
|
* &min_width,
|
|
* &natural_width);
|
|
* width = CLAMP (natural_width, min_width, available_width);
|
|
* }
|
|
*
|
|
* box.x1 = x; box.y1 = y;
|
|
* box.x2 = box.x1 + available_width;
|
|
* box.y2 = box.y1 + available_height;
|
|
* clutter_actor_allocate (self, &box, flags);
|
|
* ]|
|
|
*
|
|
* This function can be used by fluid layout managers to allocate
|
|
* an actor's preferred size without making it bigger than the area
|
|
* available for the container.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_allocate_available_size (ClutterActor *self,
|
|
gfloat x,
|
|
gfloat y,
|
|
gfloat available_width,
|
|
gfloat available_height,
|
|
ClutterAllocationFlags flags)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
gfloat width, height;
|
|
gfloat min_width, min_height;
|
|
gfloat natural_width, natural_height;
|
|
ClutterActorBox box;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
width = height = 0.0;
|
|
|
|
switch (priv->request_mode)
|
|
{
|
|
case CLUTTER_REQUEST_HEIGHT_FOR_WIDTH:
|
|
clutter_actor_get_preferred_width (self, available_height,
|
|
&min_width,
|
|
&natural_width);
|
|
width = CLAMP (natural_width, min_width, available_width);
|
|
|
|
clutter_actor_get_preferred_height (self, width,
|
|
&min_height,
|
|
&natural_height);
|
|
height = CLAMP (natural_height, min_height, available_height);
|
|
break;
|
|
|
|
case CLUTTER_REQUEST_WIDTH_FOR_HEIGHT:
|
|
clutter_actor_get_preferred_height (self, available_width,
|
|
&min_height,
|
|
&natural_height);
|
|
height = CLAMP (natural_height, min_height, available_height);
|
|
|
|
clutter_actor_get_preferred_width (self, height,
|
|
&min_width,
|
|
&natural_width);
|
|
width = CLAMP (natural_width, min_width, available_width);
|
|
break;
|
|
}
|
|
|
|
|
|
box.x1 = x;
|
|
box.y1 = y;
|
|
box.x2 = box.x1 + width;
|
|
box.y2 = box.y1 + height;
|
|
clutter_actor_allocate (self, &box, flags);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_allocate_preferred_size:
|
|
* @self: a #ClutterActor
|
|
* @flags: flags controlling the allocation
|
|
*
|
|
* Allocates the natural size of @self.
|
|
*
|
|
* This function is a utility call for #ClutterActor implementations
|
|
* that allocates the actor's preferred natural size. It can be used
|
|
* by fixed layout managers (like #ClutterGroup or so called
|
|
* 'composite actors') inside the ClutterActor::allocate
|
|
* implementation to give each child exactly how much space it
|
|
* requires.
|
|
*
|
|
* This function is not meant to be used by applications. It is also
|
|
* not meant to be used outside the implementation of the
|
|
* ClutterActor::allocate virtual function.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_actor_allocate_preferred_size (ClutterActor *self,
|
|
ClutterAllocationFlags flags)
|
|
{
|
|
gfloat actor_x, actor_y;
|
|
gfloat natural_width, natural_height;
|
|
ClutterActorBox actor_box;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
actor_x = clutter_actor_get_x (self);
|
|
actor_y = clutter_actor_get_y (self);
|
|
|
|
clutter_actor_get_preferred_size (self,
|
|
NULL, NULL,
|
|
&natural_width,
|
|
&natural_height);
|
|
|
|
actor_box.x1 = actor_x;
|
|
actor_box.y1 = actor_y;
|
|
actor_box.x2 = actor_box.x1 + natural_width;
|
|
actor_box.y2 = actor_box.y1 + natural_height;
|
|
|
|
clutter_actor_allocate (self, &actor_box, flags);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_allocate_align_fill:
|
|
* @self: a #ClutterActor
|
|
* @box: a #ClutterActorBox, containing the available width and height
|
|
* @x_align: the horizontal alignment, between 0 and 1
|
|
* @y_align: the vertical alignment, between 0 and 1
|
|
* @x_fill: whether the actor should fill horizontally
|
|
* @y_fill: whether the actor should fill vertically
|
|
* @flags: allocation flags to be passed to clutter_actor_allocate()
|
|
*
|
|
* Allocates @self by taking into consideration the available allocation
|
|
* area; an alignment factor on either axis; and whether the actor should
|
|
* fill the allocation on either axis.
|
|
*
|
|
* The @box should contain the available allocation width and height;
|
|
* if the x1 and y1 members of #ClutterActorBox are not set to 0, the
|
|
* allocation will be offset by their value.
|
|
*
|
|
* This function takes into consideration the geometry request specified by
|
|
* the #ClutterActor:request-mode property, and the text direction.
|
|
*
|
|
* This function is useful for fluid layout managers, like #ClutterBinLayout
|
|
* or #ClutterTableLayout
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_allocate_align_fill (ClutterActor *self,
|
|
const ClutterActorBox *box,
|
|
gdouble x_align,
|
|
gdouble y_align,
|
|
gboolean x_fill,
|
|
gboolean y_fill,
|
|
ClutterAllocationFlags flags)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorBox allocation = { 0, };
|
|
gfloat x_offset, y_offset;
|
|
gfloat available_width, available_height;
|
|
gfloat child_width, child_height;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (box != NULL);
|
|
g_return_if_fail (x_align >= 0.0 && x_align <= 1.0);
|
|
g_return_if_fail (y_align >= 0.0 && y_align <= 1.0);
|
|
|
|
priv = self->priv;
|
|
|
|
clutter_actor_box_get_origin (box, &x_offset, &y_offset);
|
|
clutter_actor_box_get_size (box, &available_width, &available_height);
|
|
|
|
if (available_width < 0)
|
|
available_width = 0;
|
|
|
|
if (available_height < 0)
|
|
available_height = 0;
|
|
|
|
if (x_fill)
|
|
{
|
|
allocation.x1 = x_offset;
|
|
allocation.x2 = allocation.x1 + available_width;
|
|
}
|
|
|
|
if (y_fill)
|
|
{
|
|
allocation.y1 = y_offset;
|
|
allocation.y2 = allocation.y1 + available_height;
|
|
}
|
|
|
|
/* if we are filling horizontally and vertically then we're done */
|
|
if (x_fill && y_fill)
|
|
goto out;
|
|
|
|
child_width = child_height = 0.0f;
|
|
|
|
if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
|
{
|
|
gfloat min_width, natural_width;
|
|
gfloat min_height, natural_height;
|
|
|
|
clutter_actor_get_preferred_width (self, available_height,
|
|
&min_width,
|
|
&natural_width);
|
|
|
|
child_width = CLAMP (natural_width, min_width, available_width);
|
|
|
|
if (!y_fill)
|
|
{
|
|
clutter_actor_get_preferred_height (self, child_width,
|
|
&min_height,
|
|
&natural_height);
|
|
|
|
child_height = CLAMP (natural_height, min_height, available_height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gfloat min_width, natural_width;
|
|
gfloat min_height, natural_height;
|
|
|
|
clutter_actor_get_preferred_height (self, available_width,
|
|
&min_height,
|
|
&natural_height);
|
|
|
|
child_height = CLAMP (natural_height, min_height, available_height);
|
|
|
|
if (!x_fill)
|
|
{
|
|
clutter_actor_get_preferred_width (self, child_height,
|
|
&min_width,
|
|
&natural_width);
|
|
|
|
child_width = CLAMP (natural_width, min_width, available_width);
|
|
}
|
|
}
|
|
|
|
/* invert the horizontal alignment for RTL languages */
|
|
if (priv->text_direction == CLUTTER_TEXT_DIRECTION_RTL)
|
|
x_align = 1.0 - x_align;
|
|
|
|
if (!x_fill)
|
|
{
|
|
allocation.x1 = x_offset
|
|
+ ((available_width - child_width) * x_align);
|
|
allocation.x2 = allocation.x1 + child_width;
|
|
}
|
|
|
|
if (!y_fill)
|
|
{
|
|
allocation.y1 = y_offset
|
|
+ ((available_height - child_height) * y_align);
|
|
allocation.y2 = allocation.y1 + child_height;
|
|
}
|
|
|
|
out:
|
|
clutter_actor_box_clamp_to_pixel (&allocation);
|
|
clutter_actor_allocate (self, &allocation, flags);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_grab_key_focus:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Sets the key focus of the #ClutterStage including @self
|
|
* to this #ClutterActor.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_grab_key_focus (ClutterActor *self)
|
|
{
|
|
ClutterActor *stage;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
if (stage != NULL)
|
|
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_pango_context:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the #PangoContext for @self. The actor's #PangoContext
|
|
* is already configured using the appropriate font map, resolution
|
|
* and font options.
|
|
*
|
|
* Unlike clutter_actor_create_pango_context(), this context is owend
|
|
* by the #ClutterActor and it will be updated each time the options
|
|
* stored by the #ClutterBackend change.
|
|
*
|
|
* You can use the returned #PangoContext to create a #PangoLayout
|
|
* and render text using cogl_pango_render_layout() to reuse the
|
|
* glyphs cache also used by Clutter.
|
|
*
|
|
* Return value: (transfer none): the #PangoContext for a #ClutterActor.
|
|
* The returned #PangoContext is owned by the actor and should not be
|
|
* unreferenced by the application code
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
PangoContext *
|
|
clutter_actor_get_pango_context (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterMainContext *ctx;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->pango_context)
|
|
return priv->pango_context;
|
|
|
|
ctx = CLUTTER_CONTEXT ();
|
|
priv->pango_context = _clutter_context_get_pango_context (ctx);
|
|
g_object_ref (priv->pango_context);
|
|
|
|
return priv->pango_context;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_create_pango_context:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Creates a #PangoContext for the given actor. The #PangoContext
|
|
* is already configured using the appropriate font map, resolution
|
|
* and font options.
|
|
*
|
|
* See also clutter_actor_get_pango_context().
|
|
*
|
|
* Return value: (transfer full): the newly created #PangoContext.
|
|
* Use g_object_unref() on the returned value to deallocate its
|
|
* resources
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
PangoContext *
|
|
clutter_actor_create_pango_context (ClutterActor *self)
|
|
{
|
|
ClutterMainContext *ctx;
|
|
PangoContext *retval;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
ctx = CLUTTER_CONTEXT ();
|
|
retval = _clutter_context_create_pango_context (ctx);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_create_pango_layout:
|
|
* @self: a #ClutterActor
|
|
* @text: (allow-none) the text to set on the #PangoLayout, or %NULL
|
|
*
|
|
* Creates a new #PangoLayout from the same #PangoContext used
|
|
* by the #ClutterActor. The #PangoLayout is already configured
|
|
* with the font map, resolution and font options, and the
|
|
* given @text.
|
|
*
|
|
* If you want to keep around a #PangoLayout created by this
|
|
* function you will have to connect to the #ClutterBackend::font-changed
|
|
* and #ClutterBackend::resolution-changed signals, and call
|
|
* pango_layout_context_changed() in response to them.
|
|
*
|
|
* Return value: (transfer full): the newly created #PangoLayout.
|
|
* Use g_object_unref() when done
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
PangoLayout *
|
|
clutter_actor_create_pango_layout (ClutterActor *self,
|
|
const gchar *text)
|
|
{
|
|
PangoContext *context;
|
|
PangoLayout *layout;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
context = clutter_actor_get_pango_context (self);
|
|
layout = pango_layout_new (context);
|
|
|
|
if (text)
|
|
pango_layout_set_text (layout, text, -1);
|
|
|
|
return layout;
|
|
}
|
|
|
|
/* Allows overriding the parent traversed when querying an actors paint
|
|
* opacity. Used by ClutterClone. */
|
|
void
|
|
_clutter_actor_set_opacity_parent (ClutterActor *self,
|
|
ClutterActor *parent)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
self->priv->opacity_parent = parent;
|
|
}
|
|
|
|
/* Allows you to disable applying the actors model view transform during
|
|
* a paint. Used by ClutterClone. */
|
|
void
|
|
_clutter_actor_set_enable_model_view_transform (ClutterActor *self,
|
|
gboolean enable)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
self->priv->enable_model_view_transform = enable;
|
|
}
|
|
|
|
void
|
|
_clutter_actor_set_enable_paint_unmapped (ClutterActor *self,
|
|
gboolean enable)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
priv->enable_paint_unmapped = enable;
|
|
|
|
if (priv->enable_paint_unmapped)
|
|
{
|
|
/* Make sure that the parents of the widget are realized first;
|
|
* otherwise checks in clutter_actor_update_map_state() will
|
|
* fail.
|
|
*/
|
|
clutter_actor_realize (self);
|
|
|
|
clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
|
|
}
|
|
else
|
|
{
|
|
clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNMAPPED);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_anchor_coord_get_units (ClutterActor *self,
|
|
const AnchorCoord *coord,
|
|
gfloat *x,
|
|
gfloat *y,
|
|
gfloat *z)
|
|
{
|
|
if (coord->is_fractional)
|
|
{
|
|
gfloat actor_width, actor_height;
|
|
|
|
clutter_actor_get_size (self, &actor_width, &actor_height);
|
|
|
|
if (x)
|
|
*x = actor_width * coord->v.fraction.x;
|
|
|
|
if (y)
|
|
*y = actor_height * coord->v.fraction.y;
|
|
|
|
if (z)
|
|
*z = 0;
|
|
}
|
|
else
|
|
{
|
|
if (x)
|
|
*x = coord->v.units.x;
|
|
|
|
if (y)
|
|
*y = coord->v.units.y;
|
|
|
|
if (z)
|
|
*z = coord->v.units.z;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_anchor_coord_set_units (AnchorCoord *coord,
|
|
gfloat x,
|
|
gfloat y,
|
|
gfloat z)
|
|
{
|
|
coord->is_fractional = FALSE;
|
|
coord->v.units.x = x;
|
|
coord->v.units.y = y;
|
|
coord->v.units.z = z;
|
|
}
|
|
|
|
static ClutterGravity
|
|
clutter_anchor_coord_get_gravity (AnchorCoord *coord)
|
|
{
|
|
if (coord->is_fractional)
|
|
{
|
|
if (coord->v.fraction.x == 0.0)
|
|
{
|
|
if (coord->v.fraction.y == 0.0)
|
|
return CLUTTER_GRAVITY_NORTH_WEST;
|
|
else if (coord->v.fraction.y == 0.5)
|
|
return CLUTTER_GRAVITY_WEST;
|
|
else if (coord->v.fraction.y == 1.0)
|
|
return CLUTTER_GRAVITY_SOUTH_WEST;
|
|
else
|
|
return CLUTTER_GRAVITY_NONE;
|
|
}
|
|
else if (coord->v.fraction.x == 0.5)
|
|
{
|
|
if (coord->v.fraction.y == 0.0)
|
|
return CLUTTER_GRAVITY_NORTH;
|
|
else if (coord->v.fraction.y == 0.5)
|
|
return CLUTTER_GRAVITY_CENTER;
|
|
else if (coord->v.fraction.y == 1.0)
|
|
return CLUTTER_GRAVITY_SOUTH;
|
|
else
|
|
return CLUTTER_GRAVITY_NONE;
|
|
}
|
|
else if (coord->v.fraction.x == 1.0)
|
|
{
|
|
if (coord->v.fraction.y == 0.0)
|
|
return CLUTTER_GRAVITY_NORTH_EAST;
|
|
else if (coord->v.fraction.y == 0.5)
|
|
return CLUTTER_GRAVITY_EAST;
|
|
else if (coord->v.fraction.y == 1.0)
|
|
return CLUTTER_GRAVITY_SOUTH_EAST;
|
|
else
|
|
return CLUTTER_GRAVITY_NONE;
|
|
}
|
|
else
|
|
return CLUTTER_GRAVITY_NONE;
|
|
}
|
|
else
|
|
return CLUTTER_GRAVITY_NONE;
|
|
}
|
|
|
|
static void
|
|
clutter_anchor_coord_set_gravity (AnchorCoord *coord,
|
|
ClutterGravity gravity)
|
|
{
|
|
switch (gravity)
|
|
{
|
|
case CLUTTER_GRAVITY_NORTH:
|
|
coord->v.fraction.x = 0.5;
|
|
coord->v.fraction.y = 0.0;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_NORTH_EAST:
|
|
coord->v.fraction.x = 1.0;
|
|
coord->v.fraction.y = 0.0;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_EAST:
|
|
coord->v.fraction.x = 1.0;
|
|
coord->v.fraction.y = 0.5;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_SOUTH_EAST:
|
|
coord->v.fraction.x = 1.0;
|
|
coord->v.fraction.y = 1.0;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_SOUTH:
|
|
coord->v.fraction.x = 0.5;
|
|
coord->v.fraction.y = 1.0;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_SOUTH_WEST:
|
|
coord->v.fraction.x = 0.0;
|
|
coord->v.fraction.y = 1.0;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_WEST:
|
|
coord->v.fraction.x = 0.0;
|
|
coord->v.fraction.y = 0.5;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_NORTH_WEST:
|
|
coord->v.fraction.x = 0.0;
|
|
coord->v.fraction.y = 0.0;
|
|
break;
|
|
|
|
case CLUTTER_GRAVITY_CENTER:
|
|
coord->v.fraction.x = 0.5;
|
|
coord->v.fraction.y = 0.5;
|
|
break;
|
|
|
|
default:
|
|
coord->v.fraction.x = 0.0;
|
|
coord->v.fraction.y = 0.0;
|
|
break;
|
|
}
|
|
|
|
coord->is_fractional = TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_anchor_coord_is_zero (const AnchorCoord *coord)
|
|
{
|
|
if (coord->is_fractional)
|
|
return coord->v.fraction.x == 0.0 && coord->v.fraction.y == 0.0;
|
|
else
|
|
return (coord->v.units.x == 0.0
|
|
&& coord->v.units.y == 0.0
|
|
&& coord->v.units.z == 0.0);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_flags:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the flags set on @self
|
|
*
|
|
* Return value: a bitwise or of #ClutterActorFlags or 0
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterActorFlags
|
|
clutter_actor_get_flags (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
|
|
|
|
return self->flags;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_flags:
|
|
* @self: a #ClutterActor
|
|
* @flags: the flags to set
|
|
*
|
|
* Sets @flags on @self
|
|
*
|
|
* This function will emit notifications for the changed properties
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_set_flags (ClutterActor *self,
|
|
ClutterActorFlags flags)
|
|
{
|
|
ClutterActorFlags old_flags;
|
|
GObject *obj;
|
|
gboolean was_reactive_set, reactive_set;
|
|
gboolean was_realized_set, realized_set;
|
|
gboolean was_mapped_set, mapped_set;
|
|
gboolean was_visible_set, visible_set;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (self->flags == flags)
|
|
return;
|
|
|
|
obj = G_OBJECT (self);
|
|
g_object_ref (obj);
|
|
g_object_freeze_notify (obj);
|
|
|
|
old_flags = self->flags;
|
|
|
|
was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
|
|
was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
|
|
was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0);
|
|
was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0);
|
|
|
|
self->flags |= flags;
|
|
|
|
reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
|
|
realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
|
|
mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0);
|
|
visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0);
|
|
|
|
if (reactive_set != was_reactive_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
|
|
|
|
if (realized_set != was_realized_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
|
|
|
|
if (mapped_set != was_mapped_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
|
|
|
|
if (visible_set != was_visible_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
|
|
|
|
g_object_thaw_notify (obj);
|
|
g_object_unref (obj);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_unset_flags:
|
|
* @self: a #ClutterActor
|
|
* @flags: the flags to unset
|
|
*
|
|
* Unsets @flags on @self
|
|
*
|
|
* This function will emit notifications for the changed properties
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_unset_flags (ClutterActor *self,
|
|
ClutterActorFlags flags)
|
|
{
|
|
ClutterActorFlags old_flags;
|
|
GObject *obj;
|
|
gboolean was_reactive_set, reactive_set;
|
|
gboolean was_realized_set, realized_set;
|
|
gboolean was_mapped_set, mapped_set;
|
|
gboolean was_visible_set, visible_set;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
obj = G_OBJECT (self);
|
|
g_object_freeze_notify (obj);
|
|
|
|
old_flags = self->flags;
|
|
|
|
was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
|
|
was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
|
|
was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0);
|
|
was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0);
|
|
|
|
self->flags &= ~flags;
|
|
|
|
if (self->flags == old_flags)
|
|
return;
|
|
|
|
reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
|
|
realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
|
|
mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0);
|
|
visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0);
|
|
|
|
if (reactive_set != was_reactive_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
|
|
|
|
if (realized_set != was_realized_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
|
|
|
|
if (mapped_set != was_mapped_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
|
|
|
|
if (visible_set != was_visible_set)
|
|
_clutter_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
|
|
|
|
g_object_thaw_notify (obj);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_transformation_matrix:
|
|
* @self: a #ClutterActor
|
|
* @matrix: (out): the return location for a #CoglMatrix
|
|
*
|
|
* Retrieves the transformations applied to @self relative to its
|
|
* parent.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_actor_get_transformation_matrix (ClutterActor *self,
|
|
CoglMatrix *matrix)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
cogl_matrix_init_identity (matrix);
|
|
|
|
_clutter_actor_apply_modelview_transform (self, matrix);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_is_in_clone_paint:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Checks whether @self is being currently painted by a #ClutterClone
|
|
*
|
|
* This function is useful only inside the ::paint virtual function
|
|
* implementations or within handlers for the #ClutterActor::paint
|
|
* signal
|
|
*
|
|
* This function should not be used by applications
|
|
*
|
|
* Return value: %TRUE if the #ClutterActor is currently being painted
|
|
* by a #ClutterClone, and %FALSE otherwise
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gboolean
|
|
clutter_actor_is_in_clone_paint (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
/* XXX - keep in sync with the overrides set by ClutterClone:
|
|
*
|
|
* - opacity_parent != NULL
|
|
* - enable_model_view_transform == FALSE
|
|
*/
|
|
|
|
priv = self->priv;
|
|
|
|
return priv->opacity_parent != NULL &&
|
|
!priv->enable_model_view_transform;
|
|
}
|
|
|
|
static void
|
|
set_direction_recursive (ClutterActor *actor,
|
|
gpointer user_data)
|
|
{
|
|
ClutterTextDirection text_dir = GPOINTER_TO_INT (user_data);
|
|
|
|
clutter_actor_set_text_direction (actor, text_dir);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_text_direction:
|
|
* @self: a #ClutterActor
|
|
* @text_dir: the text direction for @self
|
|
*
|
|
* Sets the #ClutterTextDirection for an actor
|
|
*
|
|
* The passed text direction must not be %CLUTTER_TEXT_DIRECTION_DEFAULT
|
|
*
|
|
* If @self implements #ClutterContainer then this function will recurse
|
|
* inside all the children of @self (including the internal ones).
|
|
*
|
|
* Composite actors not implementing #ClutterContainer, or actors requiring
|
|
* special handling when the text direction changes, should connect to
|
|
* the #GObject::notify signal for the #ClutterActor:text-direction property
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
void
|
|
clutter_actor_set_text_direction (ClutterActor *self,
|
|
ClutterTextDirection text_dir)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (text_dir != CLUTTER_TEXT_DIRECTION_DEFAULT);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->text_direction != text_dir)
|
|
{
|
|
priv->text_direction = text_dir;
|
|
|
|
/* we need to emit the notify::text-direction first, so that
|
|
* the sub-classes can catch that and do specific handling of
|
|
* the text direction; see clutter_text_direction_changed_cb()
|
|
* inside clutter-text.c
|
|
*/
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT_DIRECTION]);
|
|
|
|
/* if this is a container we need to recurse */
|
|
if (CLUTTER_IS_CONTAINER (self))
|
|
{
|
|
ClutterContainer *container = CLUTTER_CONTAINER (self);
|
|
|
|
clutter_container_foreach_with_internals (container,
|
|
set_direction_recursive,
|
|
GINT_TO_POINTER (text_dir));
|
|
}
|
|
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
}
|
|
|
|
void
|
|
_clutter_actor_set_has_pointer (ClutterActor *self,
|
|
gboolean has_pointer)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
|
|
if (priv->has_pointer != has_pointer)
|
|
{
|
|
priv->has_pointer = has_pointer;
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_POINTER]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_text_direction:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the value set using clutter_actor_set_text_direction()
|
|
*
|
|
* If no text direction has been previously set, the default text
|
|
* direction, as returned by clutter_get_default_text_direction(), will
|
|
* be returned instead
|
|
*
|
|
* Return value: the #ClutterTextDirection for the actor
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
ClutterTextDirection
|
|
clutter_actor_get_text_direction (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
|
|
CLUTTER_TEXT_DIRECTION_LTR);
|
|
|
|
priv = self->priv;
|
|
|
|
/* if no direction has been set yet use the default */
|
|
if (priv->text_direction == CLUTTER_TEXT_DIRECTION_DEFAULT)
|
|
priv->text_direction = clutter_get_default_text_direction ();
|
|
|
|
return priv->text_direction;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_push_internal:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Should be used by actors implementing the #ClutterContainer and with
|
|
* internal children added through clutter_actor_set_parent(), for instance:
|
|
*
|
|
* |[
|
|
* static void
|
|
* my_actor_init (MyActor *self)
|
|
* {
|
|
* self->priv = SELF_ACTOR_GET_PRIVATE (self);
|
|
*
|
|
* clutter_actor_push_internal (CLUTTER_ACTOR (self));
|
|
*
|
|
* /* calling clutter_actor_set_parent() now will result in
|
|
* * the internal flag being set on a child of MyActor
|
|
* */
|
|
*
|
|
* /* internal child - a background texture */
|
|
* self->priv->background_tex = clutter_texture_new ();
|
|
* clutter_actor_set_parent (self->priv->background_tex,
|
|
* CLUTTER_ACTOR (self));
|
|
*
|
|
* /* internal child - a label */
|
|
* self->priv->label = clutter_text_new ();
|
|
* clutter_actor_set_parent (self->priv->label,
|
|
* CLUTTER_ACTOR (self));
|
|
*
|
|
* clutter_actor_pop_internal (CLUTTER_ACTOR (self));
|
|
*
|
|
* /* calling clutter_actor_set_parent() now will not result in
|
|
* * the internal flag being set on a child of MyActor
|
|
* */
|
|
* }
|
|
* ]|
|
|
*
|
|
* This function will be used by Clutter to toggle an "internal child"
|
|
* flag whenever clutter_actor_set_parent() is called; internal children
|
|
* are handled differently by Clutter, specifically when destroying their
|
|
* parent.
|
|
*
|
|
* Call clutter_actor_pop_internal() when you finished adding internal
|
|
* children.
|
|
*
|
|
* Nested calls to clutter_actor_push_internal() are allowed, but each
|
|
* one must by followed by a clutter_actor_pop_internal() call.
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
void
|
|
clutter_actor_push_internal (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
self->priv->internal_child += 1;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_pop_internal:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Disables the effects of clutter_actor_pop_internal()
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
void
|
|
clutter_actor_pop_internal (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->internal_child == 0)
|
|
{
|
|
g_warning ("Mismatched %s: you need to call "
|
|
"clutter_actor_push_composite() at least once before "
|
|
"calling this function", G_STRFUNC);
|
|
return;
|
|
}
|
|
|
|
priv->internal_child -= 1;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_has_pointer:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Checks whether an actor contains the the pointer of a
|
|
* #ClutterInputDevice
|
|
*
|
|
* Return value: %TRUE if the actor contains the pointer, and
|
|
* %FALSE otherwise
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
gboolean
|
|
clutter_actor_has_pointer (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
return self->priv->has_pointer;
|
|
}
|
|
|
|
/* XXX: This is a workaround for not being able to break the ABI of
|
|
* the QUEUE_REDRAW signal. It is an out-of-band argument. See
|
|
* clutter_actor_queue_clipped_redraw() for details.
|
|
*/
|
|
const ClutterPaintVolume *
|
|
_clutter_actor_get_queue_redraw_clip (ClutterActor *self)
|
|
{
|
|
return self->priv->oob_queue_redraw_clip;
|
|
}
|
|
|
|
void
|
|
_clutter_actor_set_queue_redraw_clip (ClutterActor *self,
|
|
const ClutterPaintVolume *clip)
|
|
{
|
|
self->priv->oob_queue_redraw_clip = clip;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_has_allocation:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Checks if the actor has an up-to-date allocation assigned to
|
|
* it. This means that the actor should have an allocation: it's
|
|
* visible and has a parent. It also means that there is no
|
|
* outstanding relayout request in progress for the actor or its
|
|
* children (There might be other outstanding layout requests in
|
|
* progress that will cause the actor to get a new allocation
|
|
* when the stage is laid out, however).
|
|
*
|
|
* If this function returns %FALSE, then the actor will normally
|
|
* be allocated before it is next drawn on the screen.
|
|
*
|
|
* Return value: %TRUE if the actor has an up-to-date allocation
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
clutter_actor_has_allocation (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
priv = self->priv;
|
|
|
|
return priv->parent_actor != NULL &&
|
|
CLUTTER_ACTOR_IS_VISIBLE (self) &&
|
|
!priv->needs_allocation;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_add_action:
|
|
* @self: a #ClutterActor
|
|
* @action: a #ClutterAction
|
|
*
|
|
* Adds @action to the list of actions applied to @self
|
|
*
|
|
* A #ClutterAction can only belong to one actor at a time
|
|
*
|
|
* The #ClutterActor will hold a reference on @action until either
|
|
* clutter_actor_remove_action() or clutter_actor_clear_actions()
|
|
* is called
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_add_action (ClutterActor *self,
|
|
ClutterAction *action)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_ACTION (action));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->actions == NULL)
|
|
{
|
|
priv->actions = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
|
|
priv->actions->actor = self;
|
|
}
|
|
|
|
_clutter_meta_group_add_meta (priv->actions, CLUTTER_ACTOR_META (action));
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_add_action_with_name:
|
|
* @self: a #ClutterActor
|
|
* @name: the name to set on the action
|
|
* @action: a #ClutterAction
|
|
*
|
|
* A convenience function for setting the name of a #ClutterAction
|
|
* while adding it to the list of actions applied to @self
|
|
*
|
|
* This function is the logical equivalent of:
|
|
*
|
|
* |[
|
|
* clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
|
|
* clutter_actor_add_action (self, action);
|
|
* ]|
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_add_action_with_name (ClutterActor *self,
|
|
const gchar *name,
|
|
ClutterAction *action)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (name != NULL);
|
|
g_return_if_fail (CLUTTER_IS_ACTION (action));
|
|
|
|
clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
|
|
clutter_actor_add_action (self, action);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_remove_action:
|
|
* @self: a #ClutterActor
|
|
* @action: a #ClutterAction
|
|
*
|
|
* Removes @action from the list of actions applied to @self
|
|
*
|
|
* The reference held by @self on the #ClutterAction will be released
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_remove_action (ClutterActor *self,
|
|
ClutterAction *action)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_ACTION (action));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->actions == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action));
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_remove_action_by_name:
|
|
* @self: a #ClutterActor
|
|
* @name: the name of the action to remove
|
|
*
|
|
* Removes the #ClutterAction with the given name from the list
|
|
* of actions applied to @self
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_remove_action_by_name (ClutterActor *self,
|
|
const gchar *name)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorMeta *meta;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (name != NULL);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->actions == NULL)
|
|
return;
|
|
|
|
meta = _clutter_meta_group_get_meta (priv->actions, name);
|
|
if (meta == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_remove_meta (priv->actions, meta);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_actions:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the list of actions applied to @self
|
|
*
|
|
* Return value: (transfer container) (element-type Clutter.Action): a copy
|
|
* of the list of #ClutterAction<!-- -->s. The contents of the list are
|
|
* owned by the #ClutterActor. Use g_list_free() to free the resources
|
|
* allocated by the returned #GList
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
GList *
|
|
clutter_actor_get_actions (ClutterActor *self)
|
|
{
|
|
const GList *actions;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
if (self->priv->actions == NULL)
|
|
return NULL;
|
|
|
|
actions = _clutter_meta_group_peek_metas (self->priv->actions);
|
|
|
|
return g_list_copy ((GList *) actions);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_action:
|
|
* @self: a #ClutterActor
|
|
* @name: the name of the action to retrieve
|
|
*
|
|
* Retrieves the #ClutterAction with the given name in the list
|
|
* of actions applied to @self
|
|
*
|
|
* Return value: (transfer none): a #ClutterAction for the given
|
|
* name, or %NULL. The returned #ClutterAction is owned by the
|
|
* actor and it should not be unreferenced directly
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
ClutterAction *
|
|
clutter_actor_get_action (ClutterActor *self,
|
|
const gchar *name)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
if (self->priv->actions == NULL)
|
|
return NULL;
|
|
|
|
return CLUTTER_ACTION (_clutter_meta_group_get_meta (self->priv->actions, name));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_clear_actions:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Clears the list of actions applied to @self
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_clear_actions (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (self->priv->actions == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_clear_metas (self->priv->actions);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_add_constraint:
|
|
* @self: a #ClutterActor
|
|
* @constraint: a #ClutterConstraint
|
|
*
|
|
* Adds @constraint to the list of #ClutterConstraint<!-- -->s applied
|
|
* to @self
|
|
*
|
|
* The #ClutterActor will hold a reference on the @constraint until
|
|
* either clutter_actor_remove_constraint() or
|
|
* clutter_actor_clear_constraints() is called.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_add_constraint (ClutterActor *self,
|
|
ClutterConstraint *constraint)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->constraints == NULL)
|
|
{
|
|
priv->constraints = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
|
|
priv->constraints->actor = self;
|
|
}
|
|
|
|
_clutter_meta_group_add_meta (priv->constraints,
|
|
CLUTTER_ACTOR_META (constraint));
|
|
clutter_actor_queue_relayout (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_add_constraint_with_name:
|
|
* @self: a #ClutterActor
|
|
* @name: the name to set on the constraint
|
|
* @constraint: a #ClutterConstraint
|
|
*
|
|
* A convenience function for setting the name of a #ClutterConstraint
|
|
* while adding it to the list of constraints applied to @self
|
|
*
|
|
* This function is the logical equivalent of:
|
|
*
|
|
* |[
|
|
* clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
|
|
* clutter_actor_add_constraint (self, constraint);
|
|
* ]|
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_add_constraint_with_name (ClutterActor *self,
|
|
const gchar *name,
|
|
ClutterConstraint *constraint)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (name != NULL);
|
|
g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
|
|
|
|
clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
|
|
clutter_actor_add_constraint (self, constraint);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_remove_constraint:
|
|
* @self: a #ClutterActor
|
|
* @constraint: a #ClutterConstraint
|
|
*
|
|
* Removes @constraint from the list of constraints applied to @self
|
|
*
|
|
* The reference held by @self on the #ClutterConstraint will be released
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_remove_constraint (ClutterActor *self,
|
|
ClutterConstraint *constraint)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->constraints == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_remove_meta (priv->constraints,
|
|
CLUTTER_ACTOR_META (constraint));
|
|
clutter_actor_queue_relayout (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_remove_constraint_by_name:
|
|
* @self: a #ClutterActor
|
|
* @name: the name of the constraint to remove
|
|
*
|
|
* Removes the #ClutterConstraint with the given name from the list
|
|
* of constraints applied to @self
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_remove_constraint_by_name (ClutterActor *self,
|
|
const gchar *name)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorMeta *meta;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (name != NULL);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->constraints == NULL)
|
|
return;
|
|
|
|
meta = _clutter_meta_group_get_meta (priv->constraints, name);
|
|
if (meta == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_remove_meta (priv->constraints, meta);
|
|
clutter_actor_queue_relayout (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_constraints:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the list of constraints applied to @self
|
|
*
|
|
* Return value: (transfer container) (element-type Clutter.Constraint): a copy
|
|
* of the list of #ClutterConstraint<!-- -->s. The contents of the list are
|
|
* owned by the #ClutterActor. Use g_list_free() to free the resources
|
|
* allocated by the returned #GList
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
GList *
|
|
clutter_actor_get_constraints (ClutterActor *self)
|
|
{
|
|
const GList *constraints;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
if (self->priv->constraints == NULL)
|
|
return NULL;
|
|
|
|
constraints = _clutter_meta_group_peek_metas (self->priv->constraints);
|
|
|
|
return g_list_copy ((GList *) constraints);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_constraint:
|
|
* @self: a #ClutterActor
|
|
* @name: the name of the constraint to retrieve
|
|
*
|
|
* Retrieves the #ClutterConstraint with the given name in the list
|
|
* of constraints applied to @self
|
|
*
|
|
* Return value: (transfer none): a #ClutterConstraint for the given
|
|
* name, or %NULL. The returned #ClutterConstraint is owned by the
|
|
* actor and it should not be unreferenced directly
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
ClutterConstraint *
|
|
clutter_actor_get_constraint (ClutterActor *self,
|
|
const gchar *name)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
if (self->priv->constraints == NULL)
|
|
return NULL;
|
|
|
|
return CLUTTER_CONSTRAINT (_clutter_meta_group_get_meta (self->priv->constraints, name));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_clear_constraints:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Clears the list of constraints applied to @self
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_clear_constraints (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (self->priv->constraints == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_clear_metas (self->priv->constraints);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_set_clip_to_allocation:
|
|
* @self: a #ClutterActor
|
|
* @clip_set: %TRUE to apply a clip tracking the allocation
|
|
*
|
|
* Sets whether @self should be clipped to the same size as its
|
|
* allocation
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_set_clip_to_allocation (ClutterActor *self,
|
|
gboolean clip_set)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
clip_set = !!clip_set;
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->clip_to_allocation != clip_set)
|
|
{
|
|
priv->clip_to_allocation = clip_set;
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_clip_to_allocation:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the value set using clutter_actor_set_clip_to_allocation()
|
|
*
|
|
* Return value: %TRUE if the #ClutterActor is clipped to its allocation
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
clutter_actor_get_clip_to_allocation (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
return self->priv->clip_to_allocation;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_add_effect:
|
|
* @self: a #ClutterActor
|
|
* @effect: a #ClutterEffect
|
|
*
|
|
* Adds @effect to the list of #ClutterEffect<!-- -->s applied to @self
|
|
*
|
|
* The #ClutterActor will hold a reference on the @effect until either
|
|
* clutter_actor_remove_effect() or clutter_actor_clear_effects() is
|
|
* called.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_add_effect (ClutterActor *self,
|
|
ClutterEffect *effect)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_EFFECT (effect));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->effects == NULL)
|
|
{
|
|
priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
|
|
priv->effects->actor = self;
|
|
}
|
|
|
|
_clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect));
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_add_effect_with_name:
|
|
* @self: a #ClutterActor
|
|
* @name: the name to set on the effect
|
|
* @effect: a #ClutterEffect
|
|
*
|
|
* A convenience function for setting the name of a #ClutterEffect
|
|
* while adding it to the list of effectss applied to @self
|
|
*
|
|
* This function is the logical equivalent of:
|
|
*
|
|
* |[
|
|
* clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
|
|
* clutter_actor_add_effect (self, effect);
|
|
* ]|
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_add_effect_with_name (ClutterActor *self,
|
|
const gchar *name,
|
|
ClutterEffect *effect)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (name != NULL);
|
|
g_return_if_fail (CLUTTER_IS_EFFECT (effect));
|
|
|
|
clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
|
|
clutter_actor_add_effect (self, effect);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_remove_effect:
|
|
* @self: a #ClutterActor
|
|
* @effect: a #ClutterEffect
|
|
*
|
|
* Removes @effect from the list of effects applied to @self
|
|
*
|
|
* The reference held by @self on the #ClutterEffect will be released
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_remove_effect (ClutterActor *self,
|
|
ClutterEffect *effect)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (CLUTTER_IS_EFFECT (effect));
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->effects == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
|
|
|
|
clutter_actor_queue_redraw (self);
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_remove_effect_by_name:
|
|
* @self: a #ClutterActor
|
|
* @name: the name of the effect to remove
|
|
*
|
|
* Removes the #ClutterEffect with the given name from the list
|
|
* of effects applied to @self
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_remove_effect_by_name (ClutterActor *self,
|
|
const gchar *name)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterActorMeta *meta;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
g_return_if_fail (name != NULL);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->effects == NULL)
|
|
return;
|
|
|
|
meta = _clutter_meta_group_get_meta (priv->effects, name);
|
|
if (meta == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_remove_meta (priv->effects, meta);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_effects:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the #ClutterEffect<!-- -->s applied on @self, if any
|
|
*
|
|
* Return value: (transfer container) (element-type Clutter.Effect): a list
|
|
* of #ClutterEffect<!-- -->s, or %NULL. The elements of the returned
|
|
* list are owned by Clutter and they should not be freed. You should
|
|
* free the returned list using g_list_free() when done
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
GList *
|
|
clutter_actor_get_effects (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
const GList *effects;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->effects == NULL)
|
|
return NULL;
|
|
|
|
effects = _clutter_meta_group_peek_metas (priv->effects);
|
|
|
|
return g_list_copy ((GList *) effects);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_effect:
|
|
* @self: a #ClutterActor
|
|
* @name: the name of the effect to retrieve
|
|
*
|
|
* Retrieves the #ClutterEffect with the given name in the list
|
|
* of effects applied to @self
|
|
*
|
|
* Return value: (transfer none): a #ClutterEffect for the given
|
|
* name, or %NULL. The returned #ClutterEffect is owned by the
|
|
* actor and it should not be unreferenced directly
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
ClutterEffect *
|
|
clutter_actor_get_effect (ClutterActor *self,
|
|
const gchar *name)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
if (self->priv->effects == NULL)
|
|
return NULL;
|
|
|
|
return CLUTTER_EFFECT (_clutter_meta_group_get_meta (self->priv->effects, name));
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_clear_effects:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Clears the list of effects applied to @self
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_actor_clear_effects (ClutterActor *self)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (self));
|
|
|
|
if (self->priv->effects == NULL)
|
|
return;
|
|
|
|
_clutter_meta_group_clear_metas (self->priv->effects);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_has_key_focus:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Checks whether @self is the #ClutterActor that has key focus
|
|
*
|
|
* Return value: %TRUE if the actor has key focus, and %FALSE otherwise
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
clutter_actor_has_key_focus (ClutterActor *self)
|
|
{
|
|
ClutterActor *stage;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
if (stage == NULL)
|
|
return FALSE;
|
|
|
|
return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == self;
|
|
}
|
|
|
|
/* The public clutter_actor_get_paint_volume API returns a const
|
|
* pointer since we return a pointer directly to the cached
|
|
* PaintVolume associated with the actor and don't want the user to
|
|
* inadvertently modify it, but for internal uses we sometimes need
|
|
* access to the same PaintVolume but need to apply some book-keeping
|
|
* modifications to it so we don't want a const pointer.
|
|
*/
|
|
static ClutterPaintVolume *
|
|
_clutter_actor_get_paint_volume_mutable (ClutterActor *self)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
ClutterPaintVolume *pv;
|
|
|
|
priv = self->priv;
|
|
|
|
if (priv->paint_volume_valid)
|
|
{
|
|
clutter_paint_volume_free (&priv->paint_volume);
|
|
priv->paint_volume_valid = FALSE;
|
|
}
|
|
|
|
/* Actors are only expected to report a valid paint volume
|
|
* while they have a valid allocation. */
|
|
if (G_UNLIKELY (priv->needs_allocation))
|
|
return NULL;
|
|
|
|
/* Check if there are any handlers connected to the paint
|
|
* signal. If there are then all bets are off for what the paint
|
|
* volume for this actor might possibly be!
|
|
*
|
|
* XXX: It's expected that this is going to end up being quite a
|
|
* costly check to have to do here, but we haven't come up with
|
|
* another solution that can reliably catch paint signal handlers at
|
|
* the right time to either avoid artefacts due to invalid stage
|
|
* clipping or due to incorrect culling.
|
|
*
|
|
* Previously we checked in clutter_actor_paint(), but at that time
|
|
* we may already be using a stage clip that could be derived from
|
|
* an invalid paint-volume. We used to try and handle that by
|
|
* queuing a follow up, unclipped, redraw but still the previous
|
|
* checking wasn't enough to catch invalid volumes involved in
|
|
* culling (considering that containers may derive their volume from
|
|
* children that haven't yet been painted)
|
|
*
|
|
* Longer term, improved solutions could be:
|
|
* - Disallow painting in the paint signal, only allow using it
|
|
* for tracking when paints happen. We can add another API that
|
|
* allows monkey patching the paint of arbitrary actors but in a
|
|
* more controlled way and that also supports modifying the
|
|
* paint-volume.
|
|
* - If we could be notified somehow when signal handlers are
|
|
* connected we wouldn't have to poll for handlers like this.
|
|
*/
|
|
if (g_signal_has_handler_pending (self,
|
|
actor_signals[PAINT],
|
|
0,
|
|
TRUE))
|
|
return NULL;
|
|
|
|
pv = &priv->paint_volume;
|
|
_clutter_paint_volume_init_static (self, pv);
|
|
|
|
if (!CLUTTER_ACTOR_GET_CLASS (self)->get_paint_volume (self, pv))
|
|
{
|
|
clutter_paint_volume_free (pv);
|
|
return NULL;
|
|
}
|
|
|
|
/* since effects can modify the paint volume, we allow them to actually
|
|
* do this by making get_paint_volume() "context sensitive"
|
|
*/
|
|
if (priv->effects != NULL)
|
|
{
|
|
if (priv->current_effect != NULL)
|
|
{
|
|
const GList *effects, *l;
|
|
|
|
/* if we are being called from within the paint sequence of
|
|
* an actor, get the paint volume up to the current effect
|
|
*/
|
|
effects = _clutter_meta_group_peek_metas (priv->effects);
|
|
for (l = effects;
|
|
l != NULL || (l != NULL && l->data != priv->current_effect);
|
|
l = l->next)
|
|
{
|
|
if (!_clutter_effect_get_paint_volume (l->data, pv))
|
|
{
|
|
clutter_paint_volume_free (pv);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const GList *effects, *l;
|
|
|
|
/* otherwise, get the cumulative volume */
|
|
effects = _clutter_meta_group_peek_metas (priv->effects);
|
|
for (l = effects; l != NULL; l = l->next)
|
|
if (!_clutter_effect_get_paint_volume (l->data, pv))
|
|
{
|
|
clutter_paint_volume_free (pv);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
priv->paint_volume_valid = TRUE;
|
|
return pv;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_paint_volume:
|
|
* @self: a #ClutterActor
|
|
*
|
|
* Retrieves the paint volume of the passed #ClutterActor, or %NULL
|
|
* when a paint volume can't be determined.
|
|
*
|
|
* The paint volume is defined as the 3D space occupied by an actor
|
|
* when being painted.
|
|
*
|
|
* This function will call the <function>get_paint_volume()</function>
|
|
* virtual function of the #ClutterActor class. Sub-classes of #ClutterActor
|
|
* should not usually care about overriding the default implementation,
|
|
* unless they are, for instance: painting outside their allocation, or
|
|
* actors with a depth factor (not in terms of #ClutterActor:depth but real
|
|
* 3D depth).
|
|
*
|
|
* <note>2D actors overriding <function>get_paint_volume()</function>
|
|
* ensure their volume has a depth of 0. (This will be true so long as
|
|
* you don't call clutter_paint_volume_set_depth().)</note>
|
|
*
|
|
* Return value: (transfer none): a pointer to a #ClutterPaintVolume
|
|
* or %NULL if no volume could be determined.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
const ClutterPaintVolume *
|
|
clutter_actor_get_paint_volume (ClutterActor *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
|
|
|
|
return _clutter_actor_get_paint_volume_mutable (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_transformed_paint_volume:
|
|
* @self: a #ClutterActor
|
|
* @relative_to_ancestor: A #ClutterActor that is an ancestor of @self
|
|
* (or %NULL for the stage)
|
|
*
|
|
* Retrieves the 3D paint volume of an actor like
|
|
* clutter_actor_get_paint_volume() does (Please refer to the
|
|
* documentation of clutter_actor_get_paint_volume() for more
|
|
* details.) and it additionally transforms the paint volume into the
|
|
* coordinate space of @relative_to_ancestor. (Or the stage if %NULL
|
|
* is passed for @relative_to_ancestor)
|
|
*
|
|
* This can be used by containers that base their paint volume on
|
|
* the volume of their children. Such containers can query the
|
|
* transformed paint volume of all of its children and union them
|
|
* together using clutter_paint_volume_union().
|
|
*
|
|
* Return value: (transfer none): a pointer to a #ClutterPaintVolume
|
|
* or %NULL if no volume could be determined.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
const ClutterPaintVolume *
|
|
clutter_actor_get_transformed_paint_volume (ClutterActor *self,
|
|
ClutterActor *relative_to_ancestor)
|
|
{
|
|
CoglMatrix matrix;
|
|
const ClutterPaintVolume *volume;
|
|
ClutterStage *stage;
|
|
ClutterPaintVolume *transformed_volume;
|
|
|
|
if (relative_to_ancestor == NULL)
|
|
relative_to_ancestor = _clutter_actor_get_stage_internal (self);
|
|
|
|
if (relative_to_ancestor == NULL)
|
|
return NULL;
|
|
|
|
volume = clutter_actor_get_paint_volume (self);
|
|
if (volume == NULL)
|
|
return NULL;
|
|
|
|
_clutter_actor_get_relative_modelview (self, relative_to_ancestor, &matrix);
|
|
|
|
stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
|
|
transformed_volume = _clutter_stage_paint_volume_stack_allocate (stage);
|
|
_clutter_paint_volume_copy_static (volume, transformed_volume);
|
|
_clutter_paint_volume_transform (transformed_volume, &matrix);
|
|
_clutter_paint_volume_axis_align (transformed_volume);
|
|
_clutter_paint_volume_set_reference_actor (transformed_volume,
|
|
relative_to_ancestor);
|
|
|
|
return transformed_volume;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_get_paint_box:
|
|
* @self: a #ClutterActor
|
|
* @box: (out): return location for a #ClutterActorBox
|
|
*
|
|
* Retrieves the paint volume of the passed #ClutterActor, and
|
|
* transforms it into a 2D bounding box in stage coordinates.
|
|
*
|
|
* This function is useful to determine the on screen area occupied by
|
|
* the actor. The box is only an approximation and may often be
|
|
* considerably larger due to the optimizations used to calculate the
|
|
* box. The box is never smaller though, so it can reliably be used
|
|
* for culling.
|
|
*
|
|
* There are times when a 2D paint box can't be determined, e.g.
|
|
* because the actor isn't yet parented under a stage or because
|
|
* the actor is unable to determine a paint volume.
|
|
*
|
|
* Return value: %TRUE if a 2D paint box could be determined, else
|
|
* %FALSE.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
clutter_actor_get_paint_box (ClutterActor *self,
|
|
ClutterActorBox *box)
|
|
{
|
|
ClutterActor *stage;
|
|
const ClutterPaintVolume *pv;
|
|
CoglMatrix modelview;
|
|
CoglMatrix projection;
|
|
int viewport[4];
|
|
ClutterPaintVolume projected_pv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
|
|
g_return_val_if_fail (box != NULL, FALSE);
|
|
|
|
stage = _clutter_actor_get_stage_internal (self);
|
|
if (G_UNLIKELY (!stage))
|
|
return FALSE;
|
|
|
|
pv = clutter_actor_get_paint_volume (self);
|
|
if (G_UNLIKELY (!pv))
|
|
return FALSE;
|
|
|
|
/* NB: _clutter_actor_apply_modelview_transform_recursive will never
|
|
* include the transformation between stage coordinates and OpenGL
|
|
* window coordinates, we have to explicitly use the
|
|
* stage->apply_transform to get that... */
|
|
cogl_matrix_init_identity (&modelview);
|
|
_clutter_actor_apply_modelview_transform (stage, &modelview);
|
|
_clutter_actor_apply_modelview_transform_recursive (pv->actor,
|
|
stage, &modelview);
|
|
|
|
_clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
|
|
_clutter_stage_get_viewport (CLUTTER_STAGE (stage),
|
|
&viewport[0],
|
|
&viewport[1],
|
|
&viewport[2],
|
|
&viewport[3]);
|
|
|
|
_clutter_paint_volume_copy_static (pv, &projected_pv);
|
|
_clutter_paint_volume_project (&projected_pv,
|
|
&modelview,
|
|
&projection,
|
|
viewport);
|
|
|
|
_clutter_paint_volume_get_bounding_box (&projected_pv, box);
|
|
clutter_actor_box_clamp_to_pixel (box);
|
|
|
|
clutter_paint_volume_free (&projected_pv);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gint
|
|
_clutter_actor_get_n_children (ClutterActor *self)
|
|
{
|
|
return self->priv->n_children;
|
|
}
|
|
|
|
/* _clutter_actor_foreach_child:
|
|
* @actor: The actor whos children you want to iterate
|
|
* @callback: The function to call for each child
|
|
* @user_data: Private data to pass to @callback
|
|
*
|
|
* Calls a given @callback once for each child of the specified @actor and
|
|
* passing the @user_data pointer each time.
|
|
*
|
|
* Return value: returns %TRUE if all children were iterated, else
|
|
* %FALSE if a callback broke out of iteration early.
|
|
*/
|
|
gboolean
|
|
_clutter_actor_foreach_child (ClutterActor *self,
|
|
ClutterForeachCallback callback,
|
|
void *user_data)
|
|
{
|
|
ClutterActorPrivate *priv = self->priv;
|
|
gboolean cont;
|
|
GList *l;
|
|
|
|
for (cont = TRUE, l = priv->children; cont && l; l = l->next)
|
|
cont = callback (l->data, user_data);
|
|
|
|
return cont;
|
|
}
|
|
|
|
/* _clutter_actor_traverse:
|
|
* @actor: The actor to start traversing the graph from
|
|
* @flags: These flags may affect how the traversal is done
|
|
* @callback: The function to call for each actor traversed
|
|
* @user_data: The private data to pass to the @callback
|
|
*
|
|
* Traverses the scenegraph starting at the specified @actor and
|
|
* descending through all its children and its children's children.
|
|
* For each actor traversed @callback is called with the specified
|
|
* @user_data.
|
|
*
|
|
* If @callback ever returns %FALSE then no more actors will be
|
|
* traversed.
|
|
*
|
|
* Return value: %TRUE if @actor and all its descendants were
|
|
* traversed or %FALSE if the @callback returned %FALSE to stop
|
|
* traversal early.
|
|
*/
|
|
gboolean
|
|
_clutter_actor_traverse (ClutterActor *actor,
|
|
ClutterActorTraverseFlags flags,
|
|
ClutterForeachCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
ClutterActorPrivate *priv;
|
|
GList *l;
|
|
gboolean cont;
|
|
|
|
if (!callback (actor, user_data))
|
|
return FALSE;
|
|
|
|
priv = actor->priv;
|
|
|
|
for (cont = TRUE, l = priv->children; cont && l; l = l->next)
|
|
cont = _clutter_actor_traverse (l->data, flags, callback, user_data);
|
|
|
|
return cont;
|
|
}
|
|
|