[libadwaita] animation: Refactor adw-animation into a GObject



commit 7b845842459e34521184fbb1e760c85b92505616
Author: Manuel Genovés <manuel genoves gmail com>
Date:   Wed Jun 23 22:12:23 2021 +0200

    animation: Refactor adw-animation into a GObject
    
    As grounding work for a proper public animation API
    this commit refactors the existing code from a boxed type
    to a GObject. It adds as well a "done" signal for when the
    animation ends and an enum-based state property for future
    state handling.

 src/adw-animation-private.h                   |  78 +++-
 src/adw-animation-util-private.h              |  24 ++
 src/adw-animation-util.c                      |  95 ++++
 src/{adw-animation.h => adw-animation-util.h} |   4 +-
 src/adw-animation.c                           | 596 +++++++++++++++++++-------
 src/adw-carousel-indicator-dots.c             |  15 +-
 src/adw-carousel-indicator-lines.c            |  14 +-
 src/adw-carousel.c                            |  26 +-
 src/adw-clamp-layout.c                        |   1 +
 src/adw-enums-private.c.in                    |   1 +
 src/adw-flap.c                                |  29 +-
 src/adw-leaflet.c                             |   1 +
 src/adw-preferences-window.c                  |   3 +-
 src/adw-squeezer.c                            |   1 +
 src/adw-tab-box.c                             | 102 +++--
 src/adw-tab.c                                 |  14 +-
 src/adwaita.h                                 |   2 +-
 src/gtkprogresstracker.c                      |   2 +-
 src/meson.build                               |   4 +-
 19 files changed, 780 insertions(+), 232 deletions(-)
---
diff --git a/src/adw-animation-private.h b/src/adw-animation-private.h
index a853bcde..51b66b2b 100644
--- a/src/adw-animation-private.h
+++ b/src/adw-animation-private.h
@@ -10,46 +10,86 @@
 #error "Only <adwaita.h> can be included directly."
 #endif
 
-#include "adw-animation.h"
+#include <gtk/gtk.h>
+#include "adw-version.h"
+#include "adw-enums-private.h"
 
 G_BEGIN_DECLS
+typedef void   (*AdwAnimationTargetFunc) (double   value,
+                                          gpointer user_data);
+
+#define ADW_TYPE_ANIMATION_TARGET (adw_animation_target_get_type())
+
+G_DECLARE_FINAL_TYPE (AdwAnimationTarget, adw_animation_target, ADW, ANIMATION_TARGET, GObject)
+
+
+AdwAnimationTarget *adw_animation_target_new (AdwAnimationTargetFunc callback,
+                                              gpointer               data);
 
 #define ADW_TYPE_ANIMATION (adw_animation_get_type())
 
-typedef struct _AdwAnimation AdwAnimation;
+G_DECLARE_DERIVABLE_TYPE (AdwAnimation, adw_animation, ADW, ANIMATION, GObject)
 
-typedef void   (*AdwAnimationValueCallback) (double   value,
-                                             gpointer user_data);
 typedef void   (*AdwAnimationDoneCallback)  (gpointer user_data);
 typedef double (*AdwAnimationEasingFunc)    (double   t);
 
-GType adw_animation_get_type  (void) G_GNUC_CONST;
+typedef enum {
+  ADW_ANIMATION_INTERPOLATOR_EASE_IN,
+  ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+  ADW_ANIMATION_INTERPOLATOR_EASE_IN_OUT,
+} AdwAnimationInterpolator;
+
+typedef enum {
+  ADW_ANIMATION_STATUS_NONE,
+  ADW_ANIMATION_STATUS_COMPLETED,
+  ADW_ANIMATION_STATUS_RUNNING,
+  ADW_ANIMATION_STATUS_PAUSED,
+  ADW_ANIMATION_STATUS_CANCELED,
+} AdwAnimationStatus;
+
+/**
+ * AdwAnimation
+ * @parent_class: The parent class
+ */
+struct _AdwAnimationClass
+{
+  GObjectClass parent_class;
+
+  /*< private >*/
+  gpointer padding[4];
+};
 
 AdwAnimation *adw_animation_new (GtkWidget                 *widget,
                                  double                     from,
                                  double                     to,
                                  gint64                     duration,
-                                 AdwAnimationEasingFunc     easing_func,
-                                 AdwAnimationValueCallback  value_cb,
-                                 AdwAnimationDoneCallback   done_cb,
+                                 AdwAnimationTargetFunc     value_cb,
                                  gpointer                   user_data) G_GNUC_WARN_UNUSED_RESULT;
 
-AdwAnimation *adw_animation_ref   (AdwAnimation *self);
-void          adw_animation_unref (AdwAnimation *self);
-
 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);
 
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (AdwAnimation, adw_animation_unref)
-
-double adw_lerp (double a,
-                 double b,
-                 double t);
-
-double adw_ease_in_cubic     (double t);
-double adw_ease_in_out_cubic (double t);
+double                    adw_animation_get_value_from   (AdwAnimation *self);
+double                    adw_animation_get_value_to     (AdwAnimation *self);
+gint64                    adw_animation_get_duration     (AdwAnimation *self);
+AdwAnimationInterpolator  adw_animation_get_interpolator (AdwAnimation *self);
+AdwAnimationTarget       *adw_animation_get_target       (AdwAnimation *self);
+AdwAnimationStatus        adw_animation_get_status       (AdwAnimation *self);
+
+void adw_animation_set_value_from   (AdwAnimation             *self,
+                                     double                    value);
+void adw_animation_set_value_to     (AdwAnimation             *self,
+                                     double                    value);
+void adw_animation_set_duration     (AdwAnimation             *self,
+                                     gint64                    duration);
+void adw_animation_set_interpolator (AdwAnimation             *self,
+                                     AdwAnimationInterpolator  interpolator);
+void adw_animation_target_set_value (AdwAnimationTarget       *target,
+                                     double                    value);
+void adw_animation_set_status       (AdwAnimation             *self,
+                                     AdwAnimationStatus        status);
 
 G_END_DECLS
diff --git a/src/adw-animation-util-private.h b/src/adw-animation-util-private.h
new file mode 100644
index 00000000..cd9a975e
--- /dev/null
+++ b/src/adw-animation-util-private.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-version.h"
+#include "adw-animation-util.h"
+
+G_BEGIN_DECLS
+
+double adw_ease_in_cubic     (double t);
+double adw_ease_in_out_cubic (double t);
+double adw_lerp (double a,
+                 double b,
+                 double t);
+
+G_END_DECLS
diff --git a/src/adw-animation-util.c b/src/adw-animation-util.c
new file mode 100644
index 00000000..48cd7abb
--- /dev/null
+++ b/src/adw-animation-util.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019-2020 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "config.h"
+
+#include "adw-animation-util-private.h"
+
+/**
+ * adw_lerp:
+ * @a: the start
+ * @b: the end
+ * @t: the interpolation rate
+ *
+ * Computes the linear interpolation between @a and @b for @t.
+ *
+ * Returns: the linear interpolation between @a and @b for @t
+ *
+ * Since: 1.0
+ */
+double
+adw_lerp (double a, double b, double t)
+{
+  return a * (1.0 - t) + b * t;
+}
+
+/* From clutter-easing.c, based on Robert Penner's
+ * infamous easing equations, MIT license.
+ */
+
+/**
+ * adw_ease_out_cubic:
+ * @t: the term
+ *
+ * Computes the ease out for @t.
+ *
+ * Returns: the ease out for @t
+ *
+ * Since: 1.0
+ */
+
+double
+adw_ease_out_cubic (double t)
+{
+  double p = t - 1;
+  return p * p * p + 1;
+}
+
+double
+adw_ease_in_cubic (gdouble t)
+{
+  return t * t * t;
+}
+
+double
+adw_ease_in_out_cubic (double t)
+{
+  double p = t * 2;
+
+  if (p < 1)
+    return 0.5 * p * p * p;
+
+  p -= 2;
+
+  return 0.5 * (p * p * p + 2);
+}
+
+/**
+ * adw_get_enable_animations:
+ * @widget: a `GtkWidget`
+ *
+ * Checks whether animations are enabled for @widget.
+ *
+ * This should be used when implementing an animated widget to know whether to
+ * animate it or not.
+ *
+ * Returns: whether animations are enabled for @widget
+ *
+ * Since: 1.0
+ */
+gboolean
+adw_get_enable_animations (GtkWidget *widget)
+{
+  gboolean enable_animations = TRUE;
+
+  g_assert (GTK_IS_WIDGET (widget));
+
+  g_object_get (gtk_widget_get_settings (widget),
+                "gtk-enable-animations", &enable_animations,
+                NULL);
+
+  return enable_animations;
+}
diff --git a/src/adw-animation.h b/src/adw-animation-util.h
similarity index 100%
rename from src/adw-animation.h
rename to src/adw-animation-util.h
index ad37e52d..742c40eb 100644
--- a/src/adw-animation.h
+++ b/src/adw-animation-util.h
@@ -17,9 +17,9 @@
 G_BEGIN_DECLS
 
 ADW_AVAILABLE_IN_ALL
-gboolean adw_get_enable_animations (GtkWidget *widget);
+double adw_ease_out_cubic (double t);
 
 ADW_AVAILABLE_IN_ALL
-double adw_ease_out_cubic (double t);
+gboolean adw_get_enable_animations (GtkWidget *widget);
 
 G_END_DECLS
diff --git a/src/adw-animation.c b/src/adw-animation.c
index 3608e918..d59eadc6 100644
--- a/src/adw-animation.c
+++ b/src/adw-animation.c
@@ -6,14 +6,12 @@
 
 #include "config.h"
 
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 
-G_DEFINE_BOXED_TYPE (AdwAnimation, adw_animation, adw_animation_ref, adw_animation_unref)
 
-struct _AdwAnimation
+typedef struct
 {
-  gatomicrefcount ref_count;
-
   GtkWidget *widget;
 
   double value;
@@ -26,30 +24,253 @@ struct _AdwAnimation
   guint tick_cb_id;
   gulong unmap_cb_id;
 
-  AdwAnimationEasingFunc easing_func;
-  AdwAnimationValueCallback value_cb;
-  AdwAnimationDoneCallback done_cb;
+  AdwAnimationInterpolator interpolator;
+  AdwAnimationTargetFunc value_cb;
+  AdwAnimationTarget *target;
   gpointer user_data;
 
-  gboolean is_done;
+  AdwAnimationStatus status;
+} AdwAnimationPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (AdwAnimation, adw_animation, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_VALUE,
+  PROP_WIDGET,
+  PROP_VALUE_FROM,
+  PROP_VALUE_TO,
+  PROP_DURATION,
+  PROP_INTERPOLATOR,
+  PROP_TARGET,
+  PROP_STATUS,
+  LAST_PROP,
+};
+
+static GParamSpec *props[LAST_PROP];
+
+enum {
+  SIGNAL_DONE,
+  SIGNAL_LAST_SIGNAL,
 };
 
+static guint signals[SIGNAL_LAST_SIGNAL];
+
+static void
+adw_animation_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  AdwAnimation *self = ADW_ANIMATION (object);
+
+  switch (prop_id) {
+  case PROP_VALUE:
+    g_value_set_double (value, adw_animation_get_value (self));
+    break;
+
+  case PROP_WIDGET:
+    g_value_set_object (value, adw_animation_get_widget (self));
+    break;
+
+  case PROP_VALUE_FROM:
+    g_value_set_double (value, adw_animation_get_value_from (self));
+    break;
+
+  case PROP_VALUE_TO:
+    g_value_set_double (value, adw_animation_get_value_to (self));
+    break;
+
+  case PROP_DURATION:
+    g_value_set_int64 (value, adw_animation_get_duration (self));
+    break;
+
+  case PROP_INTERPOLATOR:
+    g_value_set_enum (value, adw_animation_get_interpolator (self));
+    break;
+
+  case PROP_TARGET:
+    g_value_set_object (value, adw_animation_get_target (self));
+    break;
+
+  case PROP_STATUS:
+    g_value_set_enum (value, adw_animation_get_status (self));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+adw_animation_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  AdwAnimation *self = ADW_ANIMATION (object);
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  switch (prop_id) {
+  case PROP_WIDGET:
+    g_set_object (&priv->widget, g_value_get_object (value));
+    break;
+
+  case PROP_VALUE_FROM:
+    adw_animation_set_value_from (self, g_value_get_double (value));
+    break;
+
+  case PROP_VALUE_TO:
+    adw_animation_set_value_to (self, g_value_get_double (value));
+    break;
+
+  case PROP_DURATION:
+    adw_animation_set_duration (self, g_value_get_int64 (value));
+    break;
+
+  case PROP_INTERPOLATOR:
+    adw_animation_set_interpolator (self, g_value_get_enum (value));
+    break;
+
+  case PROP_TARGET:
+    g_set_object (&priv->target, g_value_get_object (value));
+    break;
+
+  case PROP_STATUS:
+    adw_animation_set_status (self, g_value_get_enum (value));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
 static void
 set_value (AdwAnimation *self,
            double        value)
 {
-  self->value = value;
-  self->value_cb (value, self->user_data);
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  priv->value = value;
+  adw_animation_target_set_value (priv->target, value);
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_VALUE]);
+}
+
+static void
+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_notify_by_pspec (G_OBJECT (self), props[PROP_VALUE]);
+
+  G_OBJECT_CLASS (adw_animation_parent_class)->constructed (object);
+}
+
+static void
+adw_animation_class_init (AdwAnimationClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = adw_animation_constructed;
+  object_class->set_property = adw_animation_set_property;
+  object_class->get_property = adw_animation_get_property;
+
+  props[PROP_VALUE] =
+    g_param_spec_double ("value",
+                         "Value",
+                         "The current value of the animation",
+                         -G_MAXDOUBLE,
+                         G_MAXDOUBLE,
+                         0,
+                         G_PARAM_READABLE);
+
+  props[PROP_WIDGET] =
+    g_param_spec_object ("widget",
+                         "Widget",
+                         "The target widget whose property will be animated",
+                         GTK_TYPE_WIDGET,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  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_ANIMATION_INTERPOLATOR,
+                       ADW_ANIMATION_INTERPOLATOR_EASE_IN,
+                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+  props[PROP_TARGET] =
+    g_param_spec_object ("target",
+                         "Target",
+                         "Target",
+                         ADW_TYPE_ANIMATION_TARGET,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+  props[PROP_STATUS] =
+    g_param_spec_enum ("status",
+                       "Status",
+                       "Status of the animation",
+                       ADW_TYPE_ANIMATION_STATUS,
+                       ADW_ANIMATION_STATUS_NONE,
+                       G_PARAM_READABLE);
+
+  g_object_class_install_properties (object_class, LAST_PROP, props);
+
+  signals[SIGNAL_DONE] =
+    g_signal_new ("done",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static void
+adw_animation_init (AdwAnimation *self)
+{
 }
 
+
 static void
 done (AdwAnimation *self)
 {
-  if (self->is_done)
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  if (priv->status == ADW_ANIMATION_STATUS_COMPLETED)
     return;
 
-  self->is_done = TRUE;
-  self->done_cb (self->user_data);
+  priv->status = ADW_ANIMATION_STATUS_COMPLETED;
+  g_signal_emit (self, signals[SIGNAL_DONE], 0);
 }
 
 static gboolean
@@ -57,17 +278,20 @@ tick_cb (GtkWidget     *widget,
          GdkFrameClock *frame_clock,
          AdwAnimation  *self)
 {
+  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 - self->start_time) / self->duration;
+  double t = (double) (frame_time - priv->start_time) / priv->duration;
+  double value;
 
   if (t >= 1) {
-    self->tick_cb_id = 0;
+    priv->tick_cb_id = 0;
 
-    set_value (self, self->value_to);
+    set_value (self, priv->value_to);
 
-    if (self->unmap_cb_id) {
-      g_signal_handler_disconnect (self->widget, self->unmap_cb_id);
-      self->unmap_cb_id = 0;
+    if (priv->unmap_cb_id) {
+      g_signal_handler_disconnect (priv->widget, priv->unmap_cb_id);
+      priv->unmap_cb_id = 0;
     }
 
     done (self);
@@ -75,17 +299,23 @@ tick_cb (GtkWidget     *widget,
     return G_SOURCE_REMOVE;
   }
 
-  set_value (self, adw_lerp (self->value_from, self->value_to, self->easing_func (t)));
-
-  return G_SOURCE_CONTINUE;
-}
+  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 ();
+  }
 
-static void
-adw_animation_free (AdwAnimation *self)
-{
-  adw_animation_stop (self);
+  set_value (self, adw_lerp (priv->value_from, priv->value_to, value));
 
-  g_slice_free (AdwAnimation, self);
+  return G_SOURCE_CONTINUE;
 }
 
 AdwAnimation *
@@ -93,95 +323,71 @@ adw_animation_new (GtkWidget                 *widget,
                    double                     from,
                    double                     to,
                    gint64                     duration,
-                   AdwAnimationEasingFunc     easing_func,
-                   AdwAnimationValueCallback  value_cb,
-                   AdwAnimationDoneCallback   done_cb,
+                   AdwAnimationTargetFunc     target_func,
                    gpointer                   user_data)
 {
   AdwAnimation *self;
+  AdwAnimationTarget *target;
 
   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
-  g_return_val_if_fail (easing_func != NULL, NULL);
-  g_return_val_if_fail (value_cb != NULL, NULL);
-  g_return_val_if_fail (done_cb != NULL, NULL);
-
-  self = g_slice_new0 (AdwAnimation);
-
-  g_atomic_ref_count_init (&self->ref_count);
-
-  self->widget = widget;
-  self->value_from = from;
-  self->value_to = to;
-  self->duration = duration;
-  self->easing_func = easing_func;
-  self->value_cb = value_cb;
-  self->done_cb = done_cb;
-  self->user_data = user_data;
-
-  self->value = from;
-  self->is_done = FALSE;
-
-  return self;
-}
+  g_return_val_if_fail (target_func != NULL, NULL);
 
-AdwAnimation *
-adw_animation_ref (AdwAnimation *self)
-{
-  g_return_val_if_fail (self != NULL, NULL);
-
-  g_atomic_ref_count_inc (&self->ref_count);
+  target = adw_animation_target_new(target_func, user_data);
+  self = g_object_new (ADW_TYPE_ANIMATION,
+                       "widget", widget,
+                       "value-from", from,
+                       "value-to", to,
+                       "duration", duration,
+                       "target", target,
+                       NULL);
 
   return self;
 }
 
-void
-adw_animation_unref (AdwAnimation *self)
-{
-  g_return_if_fail (self != NULL);
-
-  if (g_atomic_ref_count_dec (&self->ref_count))
-    adw_animation_free (self);
-}
 
 void
 adw_animation_start (AdwAnimation *self)
 {
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
   g_return_if_fail (self != NULL);
 
-  if (!adw_get_enable_animations (self->widget) ||
-      !gtk_widget_get_mapped (self->widget) ||
-      self->duration <= 0) {
-    set_value (self, self->value_to);
+  if (!adw_get_enable_animations (priv->widget) ||
+      !gtk_widget_get_mapped (priv->widget) ||
+      priv->duration <= 0) {
+    set_value (self, priv->value_to);
 
     done (self);
 
     return;
   }
 
-  self->start_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (self->widget)) / 1000;
+  priv->start_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (priv->widget)) / 1000;
 
-  if (self->tick_cb_id)
+  if (priv->tick_cb_id)
     return;
 
-  self->unmap_cb_id =
-    g_signal_connect_swapped (self->widget, "unmap",
+  priv->unmap_cb_id =
+    g_signal_connect_swapped (priv->widget, "unmap",
                               G_CALLBACK (adw_animation_stop), self);
-  self->tick_cb_id = gtk_widget_add_tick_callback (self->widget, (GtkTickCallback) tick_cb, self, NULL);
+  priv->tick_cb_id = gtk_widget_add_tick_callback (priv->widget, (GtkTickCallback) tick_cb, self, NULL);
 }
 
 void
 adw_animation_stop (AdwAnimation *self)
 {
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
   g_return_if_fail (self != NULL);
 
-  if (self->tick_cb_id) {
-    gtk_widget_remove_tick_callback (self->widget, self->tick_cb_id);
-    self->tick_cb_id = 0;
+  if (priv->tick_cb_id) {
+    gtk_widget_remove_tick_callback (priv->widget, priv->tick_cb_id);
+    priv->tick_cb_id = 0;
   }
 
-  if (self->unmap_cb_id) {
-    g_signal_handler_disconnect (self->widget, self->unmap_cb_id);
-    self->unmap_cb_id = 0;
+  if (priv->unmap_cb_id) {
+    g_signal_handler_disconnect (priv->widget, priv->unmap_cb_id);
+    priv->unmap_cb_id = 0;
   }
 
   done (self);
@@ -190,100 +396,196 @@ adw_animation_stop (AdwAnimation *self)
 double
 adw_animation_get_value (AdwAnimation *self)
 {
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
   g_return_val_if_fail (self != NULL, 0.0);
 
-  return self->value;
+  return priv->value;
 }
 
 GtkWidget *
 adw_animation_get_widget (AdwAnimation *self)
 {
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
   g_return_val_if_fail (self != NULL, NULL);
 
-  return self->widget;
+  return priv->widget;
 }
 
-/**
- * adw_get_enable_animations:
- * @widget: a `GtkWidget`
- *
- * Checks whether animations are enabled for @widget.
- *
- * This should be used when implementing an animated widget to know whether to
- * animate it or not.
- *
- * Returns: whether animations are enabled for @widget
- *
- * Since: 1.0
- */
-gboolean
-adw_get_enable_animations (GtkWidget *widget)
+double
+adw_animation_get_value_from (AdwAnimation *self)
 {
-  gboolean enable_animations = TRUE;
-
-  g_assert (GTK_IS_WIDGET (widget));
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
 
-  g_object_get (gtk_widget_get_settings (widget),
-                "gtk-enable-animations", &enable_animations,
-                NULL);
+  g_return_val_if_fail (ADW_IS_ANIMATION (self), 0.0);
 
-  return enable_animations;
+  return priv->value_from;
 }
 
-/**
- * adw_lerp:
- * @a: the start
- * @b: the end
- * @t: the interpolation rate
- *
- * Computes the linear interpolation between @a and @b for @t.
- *
- * Returns: the linear interpolation between @a and @b for @t
- *
- * Since: 1.0
- */
 double
-adw_lerp (double a, double b, double t)
+adw_animation_get_value_to (AdwAnimation *self)
 {
-  return a * (1.0 - t) + b * t;
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_val_if_fail (ADW_IS_ANIMATION (self), 0.0);
+
+  return priv->value_to;
 }
 
-/* From clutter-easing.c, based on Robert Penner's
- * infamous easing equations, MIT license.
- */
+gint64
+adw_animation_get_duration (AdwAnimation *self)
+{
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
 
-/**
- * adw_ease_out_cubic:
- * @t: the term
- *
- * Computes the ease out for @t.
- *
- * Returns: the ease out for @t
- *
- * Since: 1.0
- */
-double
-adw_ease_out_cubic (double t)
+  g_return_val_if_fail (ADW_IS_ANIMATION (self), 0);
+
+  return priv->duration;
+}
+
+AdwAnimationInterpolator
+adw_animation_get_interpolator (AdwAnimation *self)
 {
-  double p = t - 1;
-  return p * p * p + 1;
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_val_if_fail (ADW_IS_ANIMATION (self), 0);
+
+  return priv->interpolator;
 }
 
-double
-adw_ease_in_cubic (gdouble t)
+AdwAnimationTarget *
+adw_animation_get_target (AdwAnimation *self)
 {
-  return t * t * t;
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return priv->target;
 }
 
-double
-adw_ease_in_out_cubic (double t)
+AdwAnimationStatus
+adw_animation_get_status (AdwAnimation *self)
+{
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_val_if_fail (ADW_IS_ANIMATION (self), 0);
+
+  return priv->status;
+}
+
+void
+adw_animation_set_value_from (AdwAnimation *self,
+                              double        value)
+{
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_if_fail (ADW_IS_ANIMATION (self));
+
+  if (priv->value_from == value)
+    return;
+
+  priv->value_from = value;
+}
+
+void
+adw_animation_set_value_to (AdwAnimation *self,
+                            double        value)
+{
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_if_fail (ADW_IS_ANIMATION (self));
+
+  if (priv->value_to == value)
+    return;
+
+  priv->value_to = value;
+}
+
+void
+adw_animation_set_duration (AdwAnimation *self,
+                            gint64        duration)
+{
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_if_fail (ADW_IS_ANIMATION (self));
+
+  if (priv->duration == duration)
+    return;
+
+  priv->duration = duration;
+}
+
+void
+adw_animation_set_interpolator (AdwAnimation             *self,
+                                AdwAnimationInterpolator  interpolator)
+{
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_if_fail (ADW_IS_ANIMATION (self));
+  g_return_if_fail (interpolator <= ADW_ANIMATION_INTERPOLATOR_EASE_IN_OUT);
+
+  if (priv->interpolator == interpolator)
+    return;
+
+  priv->interpolator = interpolator;
+}
+
+void
+adw_animation_set_status (AdwAnimation       *self,
+                          AdwAnimationStatus  status)
+{
+  AdwAnimationPrivate *priv = adw_animation_get_instance_private (self);
+
+  g_return_if_fail (ADW_IS_ANIMATION (self));
+  g_return_if_fail (status <= ADW_ANIMATION_STATUS_CANCELED);
+
+  if (priv->status == status)
+    return;
+
+  priv->status = status;
+}
+
+struct _AdwAnimationTarget
 {
-  double p = t * 2;
+  GObject parent_instance;
 
-  if (p < 1)
-    return 0.5 * p * p * p;
+  AdwAnimationTargetFunc callback;
+  gpointer user_data;
+};
+
+G_DEFINE_TYPE (AdwAnimationTarget, adw_animation_target, G_TYPE_OBJECT);
+
+static void
+adw_animation_target_class_init (AdwAnimationTargetClass *klass)
+{
+}
+
+static void
+adw_animation_target_init (AdwAnimationTarget *self)
+{
+}
 
-  p -= 2;
+AdwAnimationTarget *
+adw_animation_target_new (AdwAnimationTargetFunc callback,
+                          gpointer               user_data)
+{
+  AdwAnimationTarget *self;
+
+  g_return_val_if_fail (callback != NULL, NULL);
+
+  self = g_object_new (ADW_TYPE_ANIMATION_TARGET, NULL);
+
+  self->callback = callback;
+  self->user_data = user_data;
+
+  return self;
+}
+
+void
+adw_animation_target_set_value (AdwAnimationTarget *self,
+                                double              value)
+{
+  g_return_if_fail (ADW_IS_ANIMATION_TARGET (self));
 
-  return 0.5 * (p * p * p + 2);
+  self->callback (value, self->user_data);
 }
diff --git a/src/adw-carousel-indicator-dots.c b/src/adw-carousel-indicator-dots.c
index d237493d..2c43500b 100644
--- a/src/adw-carousel-indicator-dots.c
+++ b/src/adw-carousel-indicator-dots.c
@@ -8,6 +8,8 @@
 
 #include "adw-carousel-indicator-dots.h"
 
+#include "adw-animation-util.h"
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 #include "adw-swipeable.h"
 
@@ -74,7 +76,7 @@ value_cb (double     value,
 static void
 done_cb (AdwCarouselIndicatorDots *self)
 {
-  g_clear_pointer (&self->animation, adw_animation_unref);
+  g_clear_object (&self->animation);
 }
 
 static void
@@ -85,11 +87,16 @@ animate (AdwCarouselIndicatorDots *self,
     adw_animation_stop (self->animation);
 
   self->animation =
-    adw_animation_new (GTK_WIDGET (self), 0, 1, duration, adw_ease_out_cubic,
-                       (AdwAnimationValueCallback) value_cb,
-                       (AdwAnimationDoneCallback) done_cb,
+    adw_animation_new (GTK_WIDGET (self), 0, 1, duration,
+                       (AdwAnimationTargetFunc) value_cb,
                        self);
 
+  g_object_set (self->animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+    
+  g_signal_connect_swapped(self->animation, "done", G_CALLBACK (done_cb), self);
+
   adw_animation_start (self->animation);
 }
 
diff --git a/src/adw-carousel-indicator-lines.c b/src/adw-carousel-indicator-lines.c
index bc229047..a4a9e9d3 100644
--- a/src/adw-carousel-indicator-lines.c
+++ b/src/adw-carousel-indicator-lines.c
@@ -8,6 +8,7 @@
 
 #include "adw-carousel-indicator-lines.h"
 
+#include "adw-animation-util.h"
 #include "adw-animation-private.h"
 #include "adw-swipeable.h"
 
@@ -73,7 +74,7 @@ value_cb (double     value,
 static void
 done_cb (AdwCarouselIndicatorLines *self)
 {
-  g_clear_pointer (&self->animation, adw_animation_unref);
+  g_clear_object (&self->animation);
 }
 
 static void
@@ -84,11 +85,16 @@ animate (AdwCarouselIndicatorLines *self,
     adw_animation_stop (self->animation);
 
   self->animation =
-    adw_animation_new (GTK_WIDGET (self), 0, 1, duration, adw_ease_out_cubic,
-                       (AdwAnimationValueCallback) value_cb,
-                       (AdwAnimationDoneCallback) done_cb,
+    adw_animation_new (GTK_WIDGET (self), 0, 1, duration,
+                       (AdwAnimationTargetFunc) value_cb,
                        self);
 
+  g_object_set (self->animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(self->animation, "done", G_CALLBACK (done_cb), self);
+
   adw_animation_start (self->animation);
 }
 
diff --git a/src/adw-carousel.c b/src/adw-carousel.c
index 936c05be..c12742d8 100644
--- a/src/adw-carousel.c
+++ b/src/adw-carousel.c
@@ -8,6 +8,8 @@
 
 #include "adw-carousel.h"
 
+#include "adw-animation-util.h"
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 #include "adw-navigation-direction.h"
 #include "adw-swipe-tracker.h"
@@ -295,7 +297,7 @@ resize_animation_done_cb (ChildInfo *child)
 {
   AdwCarousel *self = ADW_CAROUSEL (adw_animation_get_widget (child->resize_animation));
 
-  g_clear_pointer (&child->resize_animation, adw_animation_unref);
+  g_clear_object (&child->resize_animation);
 
   if (child->adding)
     child->adding = FALSE;
@@ -324,11 +326,15 @@ animate_child_resize (AdwCarousel *self,
 
   child->resize_animation =
     adw_animation_new (GTK_WIDGET (self), old_size, value, duration,
-                       adw_ease_out_cubic,
-                       (AdwAnimationValueCallback) resize_animation_value_cb,
-                       (AdwAnimationDoneCallback) resize_animation_done_cb,
+                       (AdwAnimationTargetFunc) resize_animation_value_cb,
                        child);
 
+  g_object_set (child->resize_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(child->resize_animation, "done", G_CALLBACK (resize_animation_done_cb), child);
+
   adw_animation_start (child->resize_animation);
 }
 
@@ -359,7 +365,7 @@ scroll_animation_done_cb (AdwCarousel *self)
   GtkWidget *child;
   int index;
 
-  g_clear_pointer (&self->animation, adw_animation_unref);
+  g_clear_object (&self->animation);
   self->animation_source_position = 0;
   self->animation_target_child = NULL;
 
@@ -382,11 +388,15 @@ scroll_to (AdwCarousel *self,
 
   self->animation =
     adw_animation_new (GTK_WIDGET (self), 0, 1, duration,
-                       adw_ease_out_cubic,
-                       (AdwAnimationValueCallback) scroll_animation_value_cb,
-                       (AdwAnimationDoneCallback) scroll_animation_done_cb,
+                       (AdwAnimationTargetFunc) scroll_animation_value_cb,
                        self);
 
+  g_object_set (self->animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(self->animation, "done", G_CALLBACK (scroll_animation_done_cb), self);
+
   adw_animation_start (self->animation);
 }
 
diff --git a/src/adw-clamp-layout.c b/src/adw-clamp-layout.c
index e4e83a0e..93ca317e 100644
--- a/src/adw-clamp-layout.c
+++ b/src/adw-clamp-layout.c
@@ -9,6 +9,7 @@
 
 #include <math.h>
 
+#include "adw-animation-util.h"
 #include "adw-animation-private.h"
 
 /**
diff --git a/src/adw-enums-private.c.in b/src/adw-enums-private.c.in
index 754b4715..097ec60e 100644
--- a/src/adw-enums-private.c.in
+++ b/src/adw-enums-private.c.in
@@ -2,6 +2,7 @@
 
 #include "config.h"
 #include "adw-enums-private.h"
+#include "adw-animation-private.h"
 
 /*** END file-header ***/
 
diff --git a/src/adw-flap.c b/src/adw-flap.c
index f3459d21..bbdb3261 100644
--- a/src/adw-flap.c
+++ b/src/adw-flap.c
@@ -10,6 +10,8 @@
 
 #include <math.h>
 
+#include "adw-animation-util.h"
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 #include "adw-gizmo-private.h"
 #include "adw-shadow-helper-private.h"
@@ -285,7 +287,7 @@ fold_animation_value_cb (double   value,
 static void
 fold_animation_done_cb (AdwFlap *self)
 {
-  g_clear_pointer (&self->fold_animation, adw_animation_unref);
+  g_clear_object (&self->fold_animation);
 }
 
 static void
@@ -300,11 +302,15 @@ animate_fold (AdwFlap *self)
                        self->folded ? 1 : 0,
                        /* When the flap is completely hidden, we can skip animation */
                        (self->reveal_progress > 0) ? self->fold_duration : 0,
-                       adw_ease_out_cubic,
-                       (AdwAnimationValueCallback) fold_animation_value_cb,
-                       (AdwAnimationDoneCallback) fold_animation_done_cb,
+                       (AdwAnimationTargetFunc) fold_animation_value_cb,
                        self);
 
+  g_object_set (self->fold_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(self->fold_animation, "done", G_CALLBACK (fold_animation_done_cb), self);
+
   adw_animation_start (self->fold_animation);
 }
 
@@ -318,7 +324,7 @@ reveal_animation_value_cb (double   value,
 static void
 reveal_animation_done_cb (AdwFlap *self)
 {
-  g_clear_pointer (&self->reveal_animation, adw_animation_unref);
+  g_clear_object (&self->reveal_animation);
 
   if (self->schedule_fold) {
     self->schedule_fold = FALSE;
@@ -340,13 +346,16 @@ animate_reveal (AdwFlap *self,
   self->reveal_animation =
     adw_animation_new (GTK_WIDGET (self),
                        self->reveal_progress,
-                       to,
-                       duration,
-                       adw_ease_out_cubic,
-                       (AdwAnimationValueCallback) reveal_animation_value_cb,
-                       (AdwAnimationDoneCallback) reveal_animation_done_cb,
+                       to, duration,
+                       (AdwAnimationTargetFunc) reveal_animation_value_cb,
                        self);
 
+  g_object_set (self->reveal_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(self->reveal_animation, "done", G_CALLBACK (reveal_animation_done_cb), self);
+
   adw_animation_start (self->reveal_animation);
 }
 
diff --git a/src/adw-leaflet.c b/src/adw-leaflet.c
index ad6db16d..a0f1482e 100644
--- a/src/adw-leaflet.c
+++ b/src/adw-leaflet.c
@@ -8,6 +8,7 @@
 #include "config.h"
 
 #include "gtkprogresstrackerprivate.h"
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 #include "adw-enums-private.h"
 #include "adw-fold-threshold-policy.h"
diff --git a/src/adw-preferences-window.c b/src/adw-preferences-window.c
index 8238ef49..4679b6a5 100644
--- a/src/adw-preferences-window.c
+++ b/src/adw-preferences-window.c
@@ -9,7 +9,8 @@
 
 #include "adw-preferences-window.h"
 
-#include "adw-animation.h"
+#include "adw-animation-private.h"
+#include "adw-animation-util-private.h"
 #include "adw-action-row.h"
 #include "adw-leaflet.h"
 #include "adw-macros-private.h"
diff --git a/src/adw-squeezer.c b/src/adw-squeezer.c
index 790447e5..c3029baf 100644
--- a/src/adw-squeezer.c
+++ b/src/adw-squeezer.c
@@ -19,6 +19,7 @@
 #include "adw-squeezer.h"
 
 #include "gtkprogresstrackerprivate.h"
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 
 /**
diff --git a/src/adw-tab-box.c b/src/adw-tab-box.c
index 3b3fc837..6d77a5ec 100644
--- a/src/adw-tab-box.c
+++ b/src/adw-tab-box.c
@@ -9,6 +9,8 @@
 #include "config.h"
 
 #include "adw-tab-box-private.h"
+#include "adw-animation-util.h"
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 #include "adw-tab-private.h"
 #include "adw-tab-bar-private.h"
@@ -430,7 +432,7 @@ resize_animation_done_cb (gpointer user_data)
   self->end_padding = 0;
   gtk_widget_queue_resize (GTK_WIDGET (self));
 
-  g_clear_pointer (&self->resize_animation, adw_animation_unref);
+  g_clear_object (&self->resize_animation);
 }
 
 static void
@@ -465,11 +467,15 @@ set_tab_resize_mode (AdwTabBox     *self,
     self->resize_animation =
       adw_animation_new (GTK_WIDGET (self), 0, 1,
                          RESIZE_ANIMATION_DURATION,
-                         adw_ease_out_cubic,
                          resize_animation_value_cb,
-                         resize_animation_done_cb,
                          self);
 
+    g_object_set (self->resize_animation,
+                  "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                  NULL);
+
+    g_signal_connect_swapped(self->resize_animation, "done", G_CALLBACK (resize_animation_done_cb), self);
+
     adw_animation_start (self->resize_animation);
   }
 
@@ -797,7 +803,7 @@ animate_scroll (AdwTabBox *self,
   if (self->scroll_animation)
     adw_animation_stop (self->scroll_animation);
 
-  g_clear_pointer (&self->scroll_animation, adw_animation_unref);
+  g_clear_object (&self->scroll_animation);
   self->scroll_animation_done = FALSE;
   self->scroll_animation_from = gtk_adjustment_get_value (self->adjustment);
   self->scroll_animation_tab = info;
@@ -810,11 +816,15 @@ animate_scroll (AdwTabBox *self,
 
   self->scroll_animation =
     adw_animation_new (GTK_WIDGET (self), 0, 1, duration,
-                       adw_ease_out_cubic,
                        scroll_animation_value_cb,
-                       scroll_animation_done_cb,
                        self);
 
+  g_object_set (self->scroll_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(self->scroll_animation, "done", G_CALLBACK (scroll_animation_done_cb), self);
+
   adw_animation_start (self->scroll_animation);
 }
 
@@ -1029,7 +1039,7 @@ reorder_animation_done_cb (gpointer user_data)
   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (dest_tab->tab));
   AdwTabBox *self = ADW_TAB_BOX (parent);
 
-  g_clear_pointer (&self->reorder_animation, adw_animation_unref);
+  g_clear_object (&self->reorder_animation);
   check_end_reordering (self);
 }
 
@@ -1043,11 +1053,15 @@ animate_reordering (AdwTabBox *self,
   self->reorder_animation =
     adw_animation_new (GTK_WIDGET (self), 0, 1,
                        REORDER_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        reorder_animation_value_cb,
-                       reorder_animation_done_cb,
                        dest_tab);
 
+  g_object_set (self->reorder_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(self->reorder_animation, "done", G_CALLBACK (reorder_animation_done_cb), 
dest_tab);
+
   adw_animation_start (self->reorder_animation);
 
   check_end_reordering (self);
@@ -1071,7 +1085,7 @@ reorder_offset_animation_done_cb (gpointer user_data)
   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (info->tab));
   AdwTabBox *self = ADW_TAB_BOX (parent);
 
-  g_clear_pointer (&info->reorder_animation, adw_animation_unref);
+  g_clear_object (&info->reorder_animation);
   check_end_reordering (self);
 }
 
@@ -1095,11 +1109,15 @@ animate_reorder_offset (AdwTabBox *self,
   info->reorder_animation =
     adw_animation_new (GTK_WIDGET (self), info->reorder_offset, offset,
                        REORDER_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        reorder_offset_animation_value_cb,
-                       reorder_offset_animation_done_cb,
                        info);
 
+  g_object_set (info->reorder_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(info->reorder_animation, "done", G_CALLBACK (reorder_offset_animation_done_cb), 
info);
+
   adw_animation_start (info->reorder_animation);
 }
 
@@ -1618,7 +1636,7 @@ open_animation_done_cb (gpointer user_data)
 {
   TabInfo *info = user_data;
 
-  g_clear_pointer (&info->appear_animation, adw_animation_unref);
+  g_clear_object (&info->appear_animation);
 }
 
 static TabInfo *
@@ -1676,11 +1694,15 @@ page_attached_cb (AdwTabBox  *self,
   info->appear_animation =
     adw_animation_new (GTK_WIDGET (self), 0, 1,
                        OPEN_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        appear_animation_value_cb,
-                       open_animation_done_cb,
                        info);
 
+  g_object_set (info->appear_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(info->appear_animation, "done", G_CALLBACK (open_animation_done_cb), info);
+
   l = find_nth_alive_tab (self, position);
   self->tabs = g_list_insert_before (self->tabs, l, info);
 
@@ -1703,7 +1725,7 @@ close_animation_done_cb (gpointer user_data)
   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (info->tab));
   AdwTabBox *self = ADW_TAB_BOX (parent);
 
-  g_clear_pointer (&info->appear_animation, adw_animation_unref);
+  g_clear_object (&info->appear_animation);
 
   self->tabs = g_list_remove (self->tabs, info);
 
@@ -1783,11 +1805,15 @@ page_detached_cb (AdwTabBox  *self,
   info->appear_animation =
     adw_animation_new (GTK_WIDGET (self), info->appear_progress, 0,
                        CLOSE_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        appear_animation_value_cb,
-                       close_animation_done_cb,
                        info);
 
+  g_object_set (info->appear_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(info->appear_animation, "done", G_CALLBACK (close_animation_done_cb), info);
+
   adw_animation_start (info->appear_animation);
 }
 
@@ -1976,11 +2002,15 @@ insert_placeholder (AdwTabBox  *self,
   info->appear_animation =
     adw_animation_new (GTK_WIDGET (self), initial_progress, 1,
                        OPEN_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        insert_animation_value_cb,
-                       open_animation_done_cb,
                        info);
 
+  g_object_set (info->appear_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(info->appear_animation, "done", G_CALLBACK (open_animation_done_cb), info);
+
   adw_animation_start (info->appear_animation);
 }
 
@@ -1991,7 +2021,7 @@ replace_animation_done_cb (gpointer user_data)
   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (info->tab));
   AdwTabBox *self = ADW_TAB_BOX (parent);
 
-  g_clear_pointer (&info->appear_animation, adw_animation_unref);
+  g_clear_object (&info->appear_animation);
   self->reorder_placeholder = NULL;
   self->can_remove_placeholder = TRUE;
 }
@@ -2025,11 +2055,15 @@ replace_placeholder (AdwTabBox  *self,
   info->appear_animation =
     adw_animation_new (GTK_WIDGET (self), initial_progress, 1,
                        OPEN_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        appear_animation_value_cb,
-                       replace_animation_done_cb,
                        info);
 
+  g_object_set (info->appear_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(info->appear_animation, "done", G_CALLBACK (replace_animation_done_cb), info);
+
   adw_animation_start (info->appear_animation);
 }
 
@@ -2040,7 +2074,7 @@ remove_animation_done_cb (gpointer user_data)
   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (info->tab));
   AdwTabBox *self = ADW_TAB_BOX (parent);
 
-  g_clear_pointer (&info->appear_animation, adw_animation_unref);
+  g_clear_object (&info->appear_animation);
 
   if (!self->can_remove_placeholder) {
     adw_tab_set_page (info->tab, self->placeholder_page);
@@ -2098,11 +2132,15 @@ remove_placeholder (AdwTabBox *self)
   info->appear_animation =
     adw_animation_new (GTK_WIDGET (self), info->appear_progress, 0,
                        CLOSE_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        appear_animation_value_cb,
-                       remove_animation_done_cb,
                        info);
 
+  g_object_set (info->appear_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(info->appear_animation, "done", G_CALLBACK (remove_animation_done_cb), info);
+
   adw_animation_start (info->appear_animation);
 }
 
@@ -2301,7 +2339,7 @@ icon_resize_animation_done_cb (gpointer user_data)
 {
   DragIcon *icon = user_data;
 
-  g_clear_pointer (&icon->resize_animation, adw_animation_unref);
+  g_clear_object (&icon->resize_animation);
 }
 
 static void
@@ -2321,11 +2359,15 @@ resize_drag_icon (AdwTabBox *self,
   icon->resize_animation =
     adw_animation_new (GTK_WIDGET (icon->tab), icon->width, width,
                        ICON_RESIZE_ANIMATION_DURATION,
-                       adw_ease_out_cubic,
                        icon_resize_animation_value_cb,
-                       icon_resize_animation_done_cb,
                        icon);
 
+  g_object_set (icon->resize_animation,
+                "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_OUT,
+                NULL);
+
+  g_signal_connect_swapped(icon->resize_animation, "done", G_CALLBACK (icon_resize_animation_done_cb), icon);
+
   adw_animation_start (icon->resize_animation);
 }
 
@@ -3006,7 +3048,7 @@ adw_tab_box_size_allocate (GtkWidget *widget,
     if (self->scroll_animation_done) {
         self->scroll_animation_done = FALSE;
         self->scroll_animation_tab = NULL;
-        g_clear_pointer (&self->scroll_animation, adw_animation_unref);
+        g_clear_object (&self->scroll_animation);
     }
   }
 
diff --git a/src/adw-tab.c b/src/adw-tab.c
index 80fbfae6..6ea82b32 100644
--- a/src/adw-tab.c
+++ b/src/adw-tab.c
@@ -9,6 +9,8 @@
 #include "config.h"
 #include "adw-tab-private.h"
 
+#include "adw-animation-util.h"
+#include "adw-animation-util-private.h"
 #include "adw-animation-private.h"
 #include "adw-bidi-private.h"
 #include "adw-fading-label-private.h"
@@ -99,7 +101,7 @@ close_btn_animation_done_cb (AdwTab *self)
 {
   gtk_widget_set_opacity (self->close_btn, self->show_close ? 1 : 0);
   gtk_widget_set_can_target (self->close_btn, self->show_close);
-  g_clear_pointer (&self->close_btn_animation, adw_animation_unref);
+  g_clear_object (&self->close_btn_animation);
 }
 
 static void
@@ -134,11 +136,15 @@ update_state (AdwTab *self)
                          opacity,
                          self->show_close ? 1 : 0,
                          CLOSE_BTN_ANIMATION_DURATION,
-                         adw_ease_in_out_cubic,
-                         (AdwAnimationValueCallback) close_btn_animation_value_cb,
-                         (AdwAnimationDoneCallback) close_btn_animation_done_cb,
+                         (AdwAnimationTargetFunc) close_btn_animation_value_cb,
                          self);
 
+    g_object_set (self->close_btn_animation,
+                  "interpolator", ADW_ANIMATION_INTERPOLATOR_EASE_IN_OUT,
+                  NULL);
+
+    g_signal_connect_swapped(self->close_btn_animation, "done", G_CALLBACK (close_btn_animation_done_cb), 
self);
+
     adw_animation_start (self->close_btn_animation);
   }
 }
diff --git a/src/adwaita.h b/src/adwaita.h
index 34b1cc0a..8cb47858 100644
--- a/src/adwaita.h
+++ b/src/adwaita.h
@@ -22,7 +22,7 @@ G_BEGIN_DECLS
 
 #include "adw-version.h"
 #include "adw-action-row.h"
-#include "adw-animation.h"
+#include "adw-animation-util.h"
 #include "adw-application-window.h"
 #include "adw-avatar.h"
 #include "adw-bin.h"
diff --git a/src/gtkprogresstracker.c b/src/gtkprogresstracker.c
index 85300116..1f32edfc 100644
--- a/src/gtkprogresstracker.c
+++ b/src/gtkprogresstracker.c
@@ -24,7 +24,7 @@
 #include <math.h>
 #include <string.h>
 
-#include "adw-animation-private.h"
+#include "adw-animation-util.h"
 
 /*
  * Progress tracker is small helper for tracking progress through gtk
diff --git a/src/meson.build b/src/meson.build
index 4fe2d2f2..96ecce41 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -21,6 +21,7 @@ adw_public_enum_headers = [
 ]
 
 adw_private_enum_headers = [
+  'adw-animation-private.h',
 ]
 
 version_data = configuration_data()
@@ -67,7 +68,7 @@ libadwaita_generated_headers += [adw_public_enums[1]]
 
 src_headers = [
   'adw-action-row.h',
-  'adw-animation.h',
+  'adw-animation-util.h',
   'adw-application-window.h',
   'adw-avatar.h',
   'adw-bin.h',
@@ -121,6 +122,7 @@ src_sources = [
   'gtkprogresstracker.c',
   'adw-action-row.c',
   'adw-animation.c',
+  'adw-animation-util.c',
   'adw-application-window.c',
   'adw-avatar.c',
   'adw-bidi.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]