mirror of
https://github.com/brl/mutter.git
synced 2024-12-26 21:02:14 +00:00
a73ae93d59
Since they are all refer to the days clutter used to be a separate library Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2441>
659 lines
20 KiB
C
659 lines
20 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Copyright (C) 2012 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
/**
|
|
* ClutterTransition:
|
|
*
|
|
* Transition between two values
|
|
*
|
|
* #ClutterTransition is an abstract subclass of [class@Timeline] that
|
|
* computes the interpolation between two values, stored by a [class@Interval].
|
|
*/
|
|
|
|
#include "clutter-build-config.h"
|
|
|
|
#include "clutter-transition.h"
|
|
|
|
#include "clutter-animatable.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-interval.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-timeline.h"
|
|
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
struct _ClutterTransitionPrivate
|
|
{
|
|
ClutterInterval *interval;
|
|
ClutterAnimatable *animatable;
|
|
|
|
guint remove_on_complete : 1;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_INTERVAL,
|
|
PROP_ANIMATABLE,
|
|
PROP_REMOVE_ON_COMPLETE,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
|
|
|
static GQuark quark_animatable_set = 0;
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterTransition, clutter_transition, CLUTTER_TYPE_TIMELINE)
|
|
|
|
static void
|
|
clutter_transition_attach (ClutterTransition *transition,
|
|
ClutterAnimatable *animatable)
|
|
{
|
|
CLUTTER_TRANSITION_GET_CLASS (transition)->attached (transition, animatable);
|
|
}
|
|
|
|
static void
|
|
clutter_transition_detach (ClutterTransition *transition,
|
|
ClutterAnimatable *animatable)
|
|
{
|
|
CLUTTER_TRANSITION_GET_CLASS (transition)->detached (transition, animatable);
|
|
}
|
|
|
|
static void
|
|
clutter_transition_real_compute_value (ClutterTransition *transition,
|
|
ClutterAnimatable *animatable,
|
|
ClutterInterval *interval,
|
|
gdouble progress)
|
|
{
|
|
}
|
|
|
|
static void
|
|
clutter_transition_real_attached (ClutterTransition *transition,
|
|
ClutterAnimatable *animatable)
|
|
{
|
|
}
|
|
|
|
static void
|
|
clutter_transition_real_detached (ClutterTransition *transition,
|
|
ClutterAnimatable *animatable)
|
|
{
|
|
}
|
|
|
|
static void
|
|
clutter_transition_new_frame (ClutterTimeline *timeline,
|
|
gint elapsed G_GNUC_UNUSED)
|
|
{
|
|
ClutterTransition *transition = CLUTTER_TRANSITION (timeline);
|
|
ClutterTransitionPrivate *priv = transition->priv;
|
|
gdouble progress;
|
|
|
|
if (priv->interval == NULL ||
|
|
priv->animatable == NULL)
|
|
return;
|
|
|
|
progress = clutter_timeline_get_progress (timeline);
|
|
|
|
CLUTTER_TRANSITION_GET_CLASS (timeline)->compute_value (transition,
|
|
priv->animatable,
|
|
priv->interval,
|
|
progress);
|
|
}
|
|
|
|
static void
|
|
clutter_transition_stopped (ClutterTimeline *timeline,
|
|
gboolean is_finished)
|
|
{
|
|
ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (timeline)->priv;
|
|
|
|
if (is_finished &&
|
|
priv->animatable != NULL &&
|
|
priv->remove_on_complete)
|
|
{
|
|
clutter_transition_detach (CLUTTER_TRANSITION (timeline),
|
|
priv->animatable);
|
|
g_clear_object (&priv->animatable);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_transition_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterTransition *transition = CLUTTER_TRANSITION (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_INTERVAL:
|
|
clutter_transition_set_interval (transition, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_ANIMATABLE:
|
|
clutter_transition_set_animatable (transition, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_REMOVE_ON_COMPLETE:
|
|
clutter_transition_set_remove_on_complete (transition, g_value_get_boolean (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_transition_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_INTERVAL:
|
|
g_value_set_object (value, priv->interval);
|
|
break;
|
|
|
|
case PROP_ANIMATABLE:
|
|
g_value_set_object (value, priv->animatable);
|
|
break;
|
|
|
|
case PROP_REMOVE_ON_COMPLETE:
|
|
g_value_set_boolean (value, priv->remove_on_complete);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_transition_dispose (GObject *gobject)
|
|
{
|
|
ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv;
|
|
|
|
if (priv->animatable != NULL)
|
|
clutter_transition_detach (CLUTTER_TRANSITION (gobject),
|
|
priv->animatable);
|
|
|
|
g_clear_object (&priv->interval);
|
|
g_clear_object (&priv->animatable);
|
|
|
|
G_OBJECT_CLASS (clutter_transition_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_transition_class_init (ClutterTransitionClass *klass)
|
|
{
|
|
ClutterTimelineClass *timeline_class = CLUTTER_TIMELINE_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
quark_animatable_set =
|
|
g_quark_from_static_string ("-clutter-transition-animatable-set");
|
|
|
|
klass->compute_value = clutter_transition_real_compute_value;
|
|
klass->attached = clutter_transition_real_attached;
|
|
klass->detached = clutter_transition_real_detached;
|
|
|
|
timeline_class->new_frame = clutter_transition_new_frame;
|
|
timeline_class->stopped = clutter_transition_stopped;
|
|
|
|
gobject_class->set_property = clutter_transition_set_property;
|
|
gobject_class->get_property = clutter_transition_get_property;
|
|
gobject_class->dispose = clutter_transition_dispose;
|
|
|
|
/**
|
|
* ClutterTransition:interval:
|
|
*
|
|
* The [class@Interval] used to describe the initial and final states
|
|
* of the transition.
|
|
*/
|
|
obj_props[PROP_INTERVAL] =
|
|
g_param_spec_object ("interval",
|
|
P_("Interval"),
|
|
P_("The interval of values to transition"),
|
|
CLUTTER_TYPE_INTERVAL,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* ClutterTransition:animatable:
|
|
*
|
|
* The [iface@Animatable] instance currently being animated.
|
|
*/
|
|
obj_props[PROP_ANIMATABLE] =
|
|
g_param_spec_object ("animatable",
|
|
P_("Animatable"),
|
|
P_("The animatable object"),
|
|
CLUTTER_TYPE_ANIMATABLE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* ClutterTransition:remove-on-complete:
|
|
*
|
|
* Whether the #ClutterTransition should be automatically detached
|
|
* from the [property@Transition:animatable] instance whenever the
|
|
* [signal@Timeline::stopped] signal is emitted.
|
|
*
|
|
* The [property@Transition:remove-on-complete] property takes into
|
|
* account the value of the [property@Timeline:repeat-count] property,
|
|
* and it only detaches the transition if the transition is not
|
|
* repeating.
|
|
*/
|
|
obj_props[PROP_REMOVE_ON_COMPLETE] =
|
|
g_param_spec_boolean ("remove-on-complete",
|
|
P_("Remove on Complete"),
|
|
P_("Detach the transition when completed"),
|
|
FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
|
|
}
|
|
|
|
static void
|
|
clutter_transition_init (ClutterTransition *self)
|
|
{
|
|
self->priv = clutter_transition_get_instance_private (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_set_interval:
|
|
* @transition: a #ClutterTransition
|
|
* @interval: (allow-none): a #ClutterInterval, or %NULL
|
|
*
|
|
* Sets the [property@Transition:interval] property using @interval.
|
|
*
|
|
* The @transition will acquire a reference on the @interval, sinking
|
|
* the floating flag on it if necessary.
|
|
*/
|
|
void
|
|
clutter_transition_set_interval (ClutterTransition *transition,
|
|
ClutterInterval *interval)
|
|
{
|
|
ClutterTransitionPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
|
|
g_return_if_fail (interval == NULL || CLUTTER_IS_INTERVAL (interval));
|
|
|
|
priv = transition->priv;
|
|
|
|
if (priv->interval == interval)
|
|
return;
|
|
|
|
g_clear_object (&priv->interval);
|
|
|
|
if (interval != NULL)
|
|
priv->interval = g_object_ref_sink (interval);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (transition), obj_props[PROP_INTERVAL]);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_get_interval:
|
|
* @transition: a #ClutterTransition
|
|
*
|
|
* Retrieves the interval set using [method@Transition.set_interval]
|
|
*
|
|
* Return value: (transfer none): a [class@Interval], or %NULL; the returned
|
|
* interval is owned by the #ClutterTransition and it should not be freed
|
|
* directly
|
|
*/
|
|
ClutterInterval *
|
|
clutter_transition_get_interval (ClutterTransition *transition)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL);
|
|
|
|
return transition->priv->interval;
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_set_animatable:
|
|
* @transition: a #ClutterTransition
|
|
* @animatable: (allow-none): a #ClutterAnimatable, or %NULL
|
|
*
|
|
* Sets the [property@Transition:animatable] property.
|
|
*
|
|
* The @transition will acquire a reference to the @animatable instance,
|
|
* and will call the [vfunc@Transition.attached] virtual function.
|
|
*
|
|
* If an existing [iface@Animatable] is attached to @transition, the
|
|
* reference will be released, and the [vfunc@Transition.detached]
|
|
* virtual function will be called.
|
|
*/
|
|
void
|
|
clutter_transition_set_animatable (ClutterTransition *transition,
|
|
ClutterAnimatable *animatable)
|
|
{
|
|
ClutterTransitionPrivate *priv;
|
|
ClutterActor *actor;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
|
|
g_return_if_fail (animatable == NULL || CLUTTER_IS_ANIMATABLE (animatable));
|
|
|
|
priv = transition->priv;
|
|
|
|
if (priv->animatable == animatable)
|
|
return;
|
|
|
|
if (priv->animatable != NULL)
|
|
clutter_transition_detach (transition, priv->animatable);
|
|
|
|
g_clear_object (&priv->animatable);
|
|
|
|
if (animatable != NULL)
|
|
{
|
|
priv->animatable = g_object_ref (animatable);
|
|
clutter_transition_attach (transition, priv->animatable);
|
|
}
|
|
|
|
actor = clutter_animatable_get_actor (animatable);
|
|
clutter_timeline_set_actor (CLUTTER_TIMELINE (transition), actor);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_get_animatable:
|
|
* @transition: a #ClutterTransition
|
|
*
|
|
* Retrieves the [iface@Animatable] set using [method@Transition.set_animatable].
|
|
*
|
|
* Return value: (transfer none): a [iface@Animatable], or %NULL; the returned
|
|
* animatable is owned by the #ClutterTransition, and it should not be freed
|
|
* directly.
|
|
*/
|
|
ClutterAnimatable *
|
|
clutter_transition_get_animatable (ClutterTransition *transition)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL);
|
|
|
|
return transition->priv->animatable;
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_set_remove_on_complete:
|
|
* @transition: a #ClutterTransition
|
|
* @remove_complete: whether to detach @transition when complete
|
|
*
|
|
* Sets whether @transition should be detached from the [iface@Animatable]
|
|
* set using [method@Transition.set_animatable] when the
|
|
* [signal@Timeline::completed] signal is emitted.
|
|
*/
|
|
void
|
|
clutter_transition_set_remove_on_complete (ClutterTransition *transition,
|
|
gboolean remove_complete)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
|
|
|
|
remove_complete = !!remove_complete;
|
|
|
|
if (transition->priv->remove_on_complete == remove_complete)
|
|
return;
|
|
|
|
transition->priv->remove_on_complete = remove_complete;
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (transition),
|
|
obj_props[PROP_REMOVE_ON_COMPLETE]);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_get_remove_on_complete:
|
|
* @transition: a #ClutterTransition
|
|
*
|
|
* Retrieves the value of the [property@Transition:remove-on-complete] property.
|
|
*
|
|
* Return value: %TRUE if the @transition should be detached when complete,
|
|
* and %FALSE otherwise
|
|
*/
|
|
gboolean
|
|
clutter_transition_get_remove_on_complete (ClutterTransition *transition)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), FALSE);
|
|
|
|
return transition->priv->remove_on_complete;
|
|
}
|
|
|
|
typedef void (* IntervalSetFunc) (ClutterInterval *interval,
|
|
const GValue *value);
|
|
|
|
static inline void
|
|
clutter_transition_set_value (ClutterTransition *transition,
|
|
IntervalSetFunc interval_set_func,
|
|
const GValue *value)
|
|
{
|
|
ClutterTransitionPrivate *priv = transition->priv;
|
|
GType interval_type;
|
|
|
|
if (priv->interval == NULL)
|
|
{
|
|
priv->interval = clutter_interval_new_with_values (G_VALUE_TYPE (value),
|
|
NULL,
|
|
NULL);
|
|
g_object_ref_sink (priv->interval);
|
|
}
|
|
|
|
interval_type = clutter_interval_get_value_type (priv->interval);
|
|
|
|
if (!g_type_is_a (G_VALUE_TYPE (value), interval_type))
|
|
{
|
|
if (g_value_type_compatible (G_VALUE_TYPE (value), interval_type))
|
|
{
|
|
interval_set_func (priv->interval, value);
|
|
return;
|
|
}
|
|
|
|
if (g_value_type_transformable (G_VALUE_TYPE (value), interval_type))
|
|
{
|
|
GValue transform = G_VALUE_INIT;
|
|
|
|
g_value_init (&transform, interval_type);
|
|
if (g_value_transform (value, &transform))
|
|
interval_set_func (priv->interval, &transform);
|
|
else
|
|
{
|
|
g_warning ("%s: Unable to convert a value of type '%s' into "
|
|
"the value type '%s' of the interval used by the "
|
|
"transition.",
|
|
G_STRLOC,
|
|
g_type_name (G_VALUE_TYPE (value)),
|
|
g_type_name (interval_type));
|
|
}
|
|
|
|
g_value_unset (&transform);
|
|
}
|
|
}
|
|
else
|
|
interval_set_func (priv->interval, value);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_set_from_value: (rename-to clutter_transition_set_from)
|
|
* @transition: a #ClutterTransition
|
|
* @value: a #GValue with the initial value of the transition
|
|
*
|
|
* Sets the initial value of the transition.
|
|
*
|
|
* This is a convenience function that will either create the
|
|
* [class@Interval] used by @transition, or will update it if
|
|
* the [property@Transition:interval] is already set.
|
|
*
|
|
* This function will copy the contents of @value, so it is
|
|
* safe to call [method@GObject.Value.unset] after it returns.
|
|
*
|
|
* If @transition already has a [property@Transition:interval] set,
|
|
* then @value must hold the same type, or a transformable type,
|
|
* as the interval's [property@Interval:value-type] property.
|
|
*
|
|
* This function is meant to be used by language bindings.
|
|
*/
|
|
void
|
|
clutter_transition_set_from_value (ClutterTransition *transition,
|
|
const GValue *value)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
|
|
clutter_transition_set_value (transition,
|
|
clutter_interval_set_initial_value,
|
|
value);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_set_to_value: (rename-to clutter_transition_set_to)
|
|
* @transition: a #ClutterTransition
|
|
* @value: a #GValue with the final value of the transition
|
|
*
|
|
* Sets the final value of the transition.
|
|
*
|
|
* This is a convenience function that will either create the
|
|
* #ClutterInterval used by @transition, or will update it if
|
|
* the [property@Transition:interval] is already set.
|
|
*
|
|
* This function will copy the contents of @value, so it is
|
|
* safe to call [method@GObject.Value.unset] after it returns.
|
|
*
|
|
* If @transition already has a [property@Transition:interval] set,
|
|
* then @value must hold the same type, or a transformable type,
|
|
* as the interval's [property@Interval:value-type] property.
|
|
*
|
|
* This function is meant to be used by language bindings.
|
|
*/
|
|
void
|
|
clutter_transition_set_to_value (ClutterTransition *transition,
|
|
const GValue *value)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
|
|
clutter_transition_set_value (transition,
|
|
clutter_interval_set_final_value,
|
|
value);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_set_from: (skip)
|
|
* @transition: a #ClutterTransition
|
|
* @value_type: the type of the value to set
|
|
* @...: the initial value
|
|
*
|
|
* Sets the initial value of the transition.
|
|
*
|
|
* This is a convenience function that will either create the
|
|
* #ClutterInterval used by @transition, or will update it if
|
|
* the [property@Transition:interval] is already set.
|
|
*
|
|
* If @transition already has a [property@Transition:interval] set,
|
|
* then @value must hold the same type, or a transformable type,
|
|
* as the interval's [property@Interval:value-type] property.
|
|
*
|
|
* This is a convenience function for the C API; language bindings
|
|
* should use [method@Transition.set_from_value] instead.
|
|
*/
|
|
void
|
|
clutter_transition_set_from (ClutterTransition *transition,
|
|
GType value_type,
|
|
...)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
gchar *error = NULL;
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
|
|
g_return_if_fail (value_type != G_TYPE_INVALID);
|
|
|
|
va_start (args, value_type);
|
|
|
|
G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error);
|
|
|
|
va_end (args);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
return;
|
|
}
|
|
|
|
clutter_transition_set_value (transition,
|
|
clutter_interval_set_initial_value,
|
|
&value);
|
|
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
/**
|
|
* clutter_transition_set_to: (skip)
|
|
* @transition: a #ClutterTransition
|
|
* @value_type: the type of the value to set
|
|
* @...: the final value
|
|
*
|
|
* Sets the final value of the transition.
|
|
*
|
|
* This is a convenience function that will either create the
|
|
* #ClutterInterval used by @transition, or will update it if
|
|
* the [property@Transition:interval] is already set.
|
|
*
|
|
* If @transition already has a [property@Transition:interval] set,
|
|
* then @value must hold the same type, or a transformable type,
|
|
* as the interval's [property@Interval:value-type] property.
|
|
*
|
|
* This is a convenience function for the C API; language bindings
|
|
* should use [method@Transition.set_to_value] instead.
|
|
*/
|
|
void
|
|
clutter_transition_set_to (ClutterTransition *transition,
|
|
GType value_type,
|
|
...)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
gchar *error = NULL;
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
|
|
g_return_if_fail (value_type != G_TYPE_INVALID);
|
|
|
|
va_start (args, value_type);
|
|
|
|
G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error);
|
|
|
|
va_end (args);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
return;
|
|
}
|
|
|
|
clutter_transition_set_value (transition,
|
|
clutter_interval_set_final_value,
|
|
&value);
|
|
|
|
g_value_unset (&value);
|
|
}
|