mutter/clutter/clutter/clutter-transition.c
Bilal Elmoussaoui b852bbba47 cleanup: Stop translating nick/blurb for pspecs
As those strings are intended to be used by some UI but nothing uses
that in reality except GStreamer.
So drop them similar to what GTK did at
https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4717

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3101>
2023-07-19 11:33:59 +00:00

653 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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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);
}