Commit a4ca2dbb authored by Manuel Genovés's avatar Manuel Genovés
Browse files

Add timed-animation subclass

parent aadcce54
......@@ -15,6 +15,8 @@
#include "adw-version.h"
#include "adw-enums-private.h"
#include "adw-animation.h"
G_BEGIN_DECLS
typedef void (*AdwAnimationTargetFunc) (double value,
gpointer user_data);
......@@ -29,10 +31,6 @@ AdwAnimationTarget *adw_animation_target_new (AdwAnimationTargetFunc callback,
void adw_animation_target_set_value (AdwAnimationTarget *target,
double value);
#define ADW_TYPE_ANIMATION (adw_animation_get_type())
G_DECLARE_DERIVABLE_TYPE (AdwAnimation, adw_animation, ADW, ANIMATION, GObject)
typedef void (*AdwAnimationDoneCallback) (gpointer user_data);
typedef double (*AdwAnimationEasingFunc) (double t);
......@@ -58,6 +56,9 @@ struct _AdwAnimationClass
{
GObjectClass parent_class;
gint64 (*estimate_duration) (AdwAnimation *self);
double (*calculate_value) (AdwAnimation *self, gint64 t);
/*< private >*/
gpointer padding[4];
};
......@@ -69,9 +70,6 @@ AdwAnimation *adw_animation_new (GtkWidget *widget,
AdwAnimationTargetFunc value_cb,
gpointer user_data) G_GNUC_WARN_UNUSED_RESULT;
void adw_animation_start (AdwAnimation *self);
void adw_animation_stop (AdwAnimation *self);
GtkWidget *adw_animation_get_widget (AdwAnimation *self);
double adw_animation_get_value (AdwAnimation *self);
......
......@@ -146,6 +146,56 @@ adw_animation_set_property (GObject *object,
}
}
static gint64
adw_animation_estimate_duration (AdwAnimation *self)
{
AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
return priv->duration;
}
static double
adw_animation_calculate_value (AdwAnimation *self,
gint64 t)
{
AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
double value;
gint64 duration = adw_animation_estimate_duration (self);
if (duration == 0)
{
if (t == 0)
return priv->value_from;
else
return priv->value_to;
}
if (t == duration)
return priv->value_to;
double iptr;
double mapped_duration = modf(((double) t / duration), &iptr);
g_print("%li\t%li\t%f\n", t, duration, mapped_duration);
switch (priv->interpolator) {
case ADW_ANIMATION_INTERPOLATOR_EASE_IN:
value = adw_ease_in_cubic (mapped_duration);
break;
case ADW_ANIMATION_INTERPOLATOR_EASE_OUT:
value = adw_ease_out_cubic (mapped_duration);
break;
case ADW_ANIMATION_INTERPOLATOR_EASE_IN_OUT:
value = adw_ease_in_out_cubic (mapped_duration);
break;
default:
g_assert_not_reached ();
}
return adw_lerp (priv->value_from, priv->value_to, value);
}
static void
set_value (AdwAnimation *self,
double value)
......@@ -163,10 +213,10 @@ adw_animation_constructed (GObject *object)
AdwAnimation *self = ADW_ANIMATION (object);
AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
priv->value = priv->value_from;
G_OBJECT_CLASS (adw_animation_parent_class)->constructed (object);
priv->value = adw_animation_calculate_value (self, 0);
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_VALUE]);
G_OBJECT_CLASS (adw_animation_parent_class)->constructed (object);
}
static void
......@@ -178,6 +228,9 @@ adw_animation_class_init (AdwAnimationClass *klass)
object_class->set_property = adw_animation_set_property;
object_class->get_property = adw_animation_get_property;
klass->estimate_duration = adw_animation_estimate_duration;
klass->calculate_value = adw_animation_calculate_value;
props[PROP_VALUE] =
g_param_spec_double ("value",
"Value",
......@@ -283,13 +336,13 @@ tick_cb (GtkWidget *widget,
AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
gint64 frame_time = gdk_frame_clock_get_frame_time (frame_clock) / 1000; /* ms */
double t = (double) (frame_time - priv->start_time) / priv->duration;
double value;
gint64 duration = adw_animation_estimate_duration(self);
gint64 t = (double) (frame_time - priv->start_time);
if (t >= 1) {
if (t >= duration && duration > 0) {
priv->tick_cb_id = 0;
set_value (self, priv->value_to);
set_value (self, adw_animation_calculate_value (self, duration));
if (priv->unmap_cb_id) {
g_signal_handler_disconnect (priv->widget, priv->unmap_cb_id);
......@@ -301,21 +354,7 @@ tick_cb (GtkWidget *widget,
return G_SOURCE_REMOVE;
}
switch (priv->interpolator) {
case ADW_ANIMATION_INTERPOLATOR_EASE_IN:
value = adw_ease_in_cubic (t);
break;
case ADW_ANIMATION_INTERPOLATOR_EASE_OUT:
value = adw_ease_out_cubic (t);
break;
case ADW_ANIMATION_INTERPOLATOR_EASE_IN_OUT:
value = adw_ease_in_out_cubic (t);
break;
default:
g_assert_not_reached ();
}
set_value (self, adw_lerp (priv->value_from, priv->value_to, value));
set_value (self, adw_animation_calculate_value (self, t));
return G_SOURCE_CONTINUE;
}
......
/*
* Copyright (C) 2019 Purism SPC
* Copyright (C) 2021 Manuel Genovés <manuel.genoves@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
#error "Only <adwaita.h> can be included directly."
#endif
#include <gtk/gtk.h>
#include "adw-version.h"
G_BEGIN_DECLS
#define ADW_TYPE_ANIMATION (adw_animation_get_type())
G_DECLARE_DERIVABLE_TYPE (AdwAnimation, adw_animation, ADW, ANIMATION, GObject)
ADW_AVAILABLE_IN_ALL
void adw_animation_start (AdwAnimation *self);
ADW_AVAILABLE_IN_ALL
void adw_animation_stop (AdwAnimation *self);
G_END_DECLS
......@@ -8,6 +8,7 @@
#include "adw-leaflet.h"
#include "adw-navigation-direction.h"
#include "adw-squeezer.h"
#include "adw-timed-animation.h"
#include "adw-view-switcher.h"
/*** END file-header ***/
......
/*
* Copyright (C) 2019-2020 Purism SPC
* Copyright (C) 2021 Manuel Genovés <manuel.genoves@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "adw-animation-util-private.h"
#include "adw-animation-private.h"
#include "adw-timed-animation.h"
struct _AdwTimedAnimation
{
AdwAnimation parent_instance;
int repetitions;
gboolean reverse;
gboolean alternate;
double value_from;
double value_to;
gint64 duration; /* ms */
gint64 start_time; /* ms */
AdwTimedAnimationInterpolator interpolator;
};
G_DEFINE_TYPE (AdwTimedAnimation, adw_timed_animation, ADW_TYPE_ANIMATION)
enum {
PROP_0,
PROP_REPETITIONS,
PROP_REVERSE,
PROP_ALTERNATE,
PROP_VALUE_FROM,
PROP_VALUE_TO,
PROP_DURATION,
PROP_INTERPOLATOR,
LAST_PROP,
};
static GParamSpec *props[LAST_PROP];
static gint64
adw_timed_animation_estimate_duration (AdwAnimation *animation)
{
AdwTimedAnimation *self = ADW_TIMED_ANIMATION (animation);
gint64 period_duration = adw_timed_animation_get_duration (self);
if (period_duration == -1)
return period_duration;
return period_duration * adw_timed_animation_get_repetitions (self);
}
static double
adw_timed_animation_calculate_value (AdwAnimation *animation,
gint64 t)
{
AdwTimedAnimation *self = ADW_TIMED_ANIMATION (animation);
double value;
gint64 duration = adw_timed_animation_get_duration (self);
double mapped_duration;
double value_from = adw_timed_animation_get_value_from (self);
double value_to = adw_timed_animation_get_value_to (self);
if (duration == 0)
{
if (t == 0)
return value_from;
else
return value_to;
}
if (t == duration)
return value_to;
double repetition;
double float_time;
float_time = modf(((double) t / duration), &repetition);
int direction;
if (adw_timed_animation_get_alternate (self))
direction = ((int) repetition % 2) ? -1 : 1;
else
direction = 1;
if (adw_timed_animation_get_reverse (self))
direction *= -1;
if (direction == 1)
mapped_duration = float_time;
else if (direction == -1)
mapped_duration = 1 - float_time;
switch (self->interpolator) {
case ADW_TIMED_ANIMATION_INTERPOLATOR_EASE_IN:
value = adw_ease_in_cubic (mapped_duration);
break;
case ADW_TIMED_ANIMATION_INTERPOLATOR_EASE_OUT:
value = adw_ease_out_cubic (mapped_duration);
break;
case ADW_TIMED_ANIMATION_INTERPOLATOR_EASE_IN_OUT:
value = adw_ease_in_out_cubic (mapped_duration);
break;
default:
g_assert_not_reached ();
}
return adw_lerp (value_from, value_to, value);
}
static void
adw_timed_animation_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
AdwTimedAnimation *self = ADW_TIMED_ANIMATION (object);
switch (prop_id) {
case PROP_REPETITIONS:
g_value_set_int (value, adw_timed_animation_get_repetitions (self));
break;
case PROP_REVERSE:
g_value_set_boolean (value, adw_timed_animation_get_reverse (self));
break;
case PROP_ALTERNATE:
g_value_set_boolean (value, adw_timed_animation_get_alternate (self));
break;
case PROP_VALUE_FROM:
g_value_set_double (value, adw_timed_animation_get_value_from (self));
break;
case PROP_VALUE_TO:
g_value_set_double (value, adw_timed_animation_get_value_to (self));
break;
case PROP_DURATION:
g_value_set_int64 (value, adw_timed_animation_get_duration (self));
break;
case PROP_INTERPOLATOR:
g_value_set_enum (value, adw_timed_animation_get_interpolator (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
adw_timed_animation_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
AdwTimedAnimation *self = ADW_TIMED_ANIMATION (object);
switch (prop_id) {
case PROP_REPETITIONS:
adw_timed_animation_set_repetitions (self, g_value_get_int (value));
break;
case PROP_REVERSE:
adw_timed_animation_set_reverse (self, g_value_get_boolean (value));
break;
case PROP_ALTERNATE:
adw_timed_animation_set_alternate (self, g_value_get_boolean (value));
break;
case PROP_VALUE_FROM:
adw_timed_animation_set_value_from (self, g_value_get_double (value));
break;
case PROP_VALUE_TO:
adw_timed_animation_set_value_to (self, g_value_get_double (value));
break;
case PROP_DURATION:
adw_timed_animation_set_duration (self, g_value_get_int64 (value));
break;
case PROP_INTERPOLATOR:
adw_timed_animation_set_interpolator (self, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
adw_timed_animation_class_init (AdwTimedAnimationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
AdwAnimationClass *animation_class = ADW_ANIMATION_CLASS (klass);
object_class->set_property = adw_timed_animation_set_property;
object_class->get_property = adw_timed_animation_get_property;
animation_class->estimate_duration = adw_timed_animation_estimate_duration;
animation_class->calculate_value = adw_timed_animation_calculate_value;
props[PROP_REPETITIONS] =
g_param_spec_int ("repetitions",
"Repetitions",
"Number of times the animation should play. Set -1 for a looped animation",
-1, G_MAXINT,
1,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
props[PROP_REVERSE] =
g_param_spec_boolean ("reverse",
"Reverse",
"Wheter the animation should play backwards",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
props[PROP_ALTERNATE] =
g_param_spec_boolean ("alternate",
"Alternate",
"Wheter the animation should alternate playing forwards/backwards each time it's played",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
props[PROP_VALUE_FROM] =
g_param_spec_double ("value-from",
"Initial value",
"Initial value of the animation",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
props[PROP_VALUE_TO] =
g_param_spec_double ("value-to",
"Final value",
"Final value of the animation",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
props[PROP_DURATION] =
g_param_spec_int64 ("duration",
"Duration",
"Duration of the animation",
0,
G_MAXINT64,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
props[PROP_INTERPOLATOR] =
g_param_spec_enum ("interpolator",
"Interpolator",
"Easing function used in the animation",
ADW_TYPE_TIMED_ANIMATION_INTERPOLATOR,
ADW_TIMED_ANIMATION_INTERPOLATOR_EASE_OUT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
g_object_class_install_properties (object_class, LAST_PROP, props);
}
static void
adw_timed_animation_init (AdwTimedAnimation *self)
{
}
AdwAnimation *
adw_timed_animation_new (GtkWidget *widget,
double from,
double to,
gint64 duration,
AdwAnimationTargetFunc target_func,
gpointer user_data)
{
AdwAnimationTarget *target;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
g_return_val_if_fail (target_func != NULL, NULL);
target = adw_animation_target_new(target_func, user_data);
return g_object_new (ADW_TYPE_ANIMATION,
"widget", widget,
"value-from", from,
"value-to", to,
"duration", duration,
"target", target,
NULL);
}
int
adw_timed_animation_get_repetitions (AdwTimedAnimation *self)
{
g_return_val_if_fail (ADW_IS_TIMED_ANIMATION (self), 0);
return self->repetitions;
}
void
adw_timed_animation_set_repetitions (AdwTimedAnimation *self,
int value)
{
g_return_if_fail (ADW_IS_TIMED_ANIMATION (self));
if (self->repetitions == value)
return;
self->repetitions = value;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REPETITIONS]);
}
gboolean
adw_timed_animation_get_reverse (AdwTimedAnimation *self)
{
g_return_val_if_fail (ADW_IS_TIMED_ANIMATION (self), FALSE);
return self->reverse;
}
void
adw_timed_animation_set_reverse (AdwTimedAnimation *self,
gboolean value)
{
g_return_if_fail (ADW_IS_TIMED_ANIMATION (self));
if (self->reverse == value)
return;
self->reverse = value;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REVERSE]);
}
gboolean
adw_timed_animation_get_alternate (AdwTimedAnimation *self)
{
g_return_val_if_fail (ADW_IS_TIMED_ANIMATION (self), FALSE);
return self->alternate;
}
void
adw_timed_animation_set_alternate (AdwTimedAnimation *self,
gboolean value)
{
g_return_if_fail (ADW_IS_TIMED_ANIMATION (self));
if (self->alternate == value)
return;
self->alternate = value;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ALTERNATE]);
}
double
adw_timed_animation_get_value_from (AdwTimedAnimation *self)
{
g_return_val_if_fail (ADW_IS_TIMED_ANIMATION (self), 0.0);
return self->value_from;
}
void
adw_timed_animation_set_value_from (AdwTimedAnimation *self,
double value)
{
g_return_if_fail (ADW_IS_TIMED_ANIMATION (self));
if (self->value_from == value)
return;
self->value_from = value;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_VALUE_FROM]);
}
double
adw_timed_animation_get_value_to (AdwTimedAnimation *self)
{
g_return_val_if_fail (ADW_IS_TIMED_ANIMATION (self), 0.0);
return self->value_to;
}
void
adw_timed_animation_set_value_to (AdwTimedAnimation *self,
double value)
{
g_return_if_fail (ADW_IS_TIMED_ANIMATION (self));
if (self->value_to == value)
return;
self->value_to = value;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_VALUE_TO]);
}
gint64
adw_timed_animation_get_duration (AdwTimedAnimation *self)
{
g_return_val_if_fail (ADW_IS_TIMED_ANIMATION (self), 0);
return self->duration;
}
void
adw_timed_animation_set_duration (AdwTimedAnimation *self,
gint64 duration)
{