[gtk+/wip/animated-scrolling] Proof-of-concept: Animated scrolling



commit b457c1d7cd88accb22c690b69baefe8a48d805ff
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Jun 28 02:28:29 2014 -0400

    Proof-of-concept: Animated scrolling
    
    This is a proof-of-concept patch to make scrolled windows animate
    motion. The implementation of the tick callback is in GtkAdjustment,
    so we can animate both scrollbar clicks and key bindings.
    
    Still to do: figure out what to do about duration - does it need to
    depend on the distance of the jump ? Is the easing function the right
    choice ? Do we want to animate all range jumps, or just scrollbars ?
    
    https://bugzilla.gnome.org/show_bug.cgi?id=732376

 gtk/Makefile.am            |    1 +
 gtk/gtkadjustment.c        |  137 ++++++++++++++++++++++++++++++++++++++++++--
 gtk/gtkadjustmentprivate.h |   34 +++++++++++
 gtk/gtkscrolledwindow.c    |   31 ++++++++++
 4 files changed, 197 insertions(+), 6 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 3928411..cdb4a20 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -498,6 +498,7 @@ gtk_private_h_sources =             \
        gtkactionmuxer.h        \
        gtkactionobserver.h     \
        gtkactionobservable.h   \
+       gtkadjustmentprivate.h  \
        gtkapplicationprivate.h \
        gtkaccelgroupprivate.h  \
        gtkaccelmapprivate.h    \
diff --git a/gtk/gtkadjustment.c b/gtk/gtkadjustment.c
index 9a5087d..5171417 100644
--- a/gtk/gtkadjustment.c
+++ b/gtk/gtkadjustment.c
@@ -57,6 +57,15 @@ struct _GtkAdjustmentPrivate {
   gdouble step_increment;
   gdouble page_increment;
   gdouble page_size;
+
+  gdouble source;
+  gdouble target;
+
+  guint duration;
+  gint64 start_time;
+  gint64 end_time;
+  guint tick_id;
+  GdkFrameClock *clock;
 };
 
 enum
@@ -97,10 +106,25 @@ static guint64 adjustment_changed_stamp = 0; /* protected by global gdk lock */
 G_DEFINE_TYPE_WITH_PRIVATE (GtkAdjustment, gtk_adjustment, G_TYPE_INITIALLY_UNOWNED)
 
 static void
+gtk_adjustment_finalize (GObject *object)
+{
+  GtkAdjustment *adjustment = GTK_ADJUSTMENT (object);
+  GtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->tick_id)
+    g_signal_handler_disconnect (priv->clock, priv->tick_id);
+  if (priv->clock)
+    g_object_unref (priv->clock);
+
+  G_OBJECT_CLASS (gtk_adjustment_parent_class)->finalize (object);
+}
+
+static void
 gtk_adjustment_class_init (GtkAdjustmentClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
 
+  gobject_class->finalize                    = gtk_adjustment_finalize;
   gobject_class->set_property                = gtk_adjustment_set_property;
   gobject_class->get_property                = gtk_adjustment_get_property;
   gobject_class->dispatch_properties_changed = gtk_adjustment_dispatch_properties_changed;
@@ -404,6 +428,95 @@ gtk_adjustment_get_value (GtkAdjustment *adjustment)
   return adjustment->priv->value;
 }
 
+static void
+adjustment_set_value (GtkAdjustment *adjustment,
+                      gdouble        value)
+{
+  if (value != adjustment->priv->value)
+    {
+      adjustment->priv->value = value;
+      gtk_adjustment_value_changed (adjustment);
+    }
+}
+
+/* From clutter-easing.c, based on Robert Penner's
+ * infamous easing equations, MIT license.
+ */
+static gdouble
+ease_out_cubic (gdouble t)
+{
+  gdouble p = t - 1;
+
+  return p * p * p + 1;
+}
+
+static gboolean
+gtk_adjustment_animate_step (GtkAdjustment *adjustment,
+                             gint64         now)
+{
+  GtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (now < priv->end_time)
+    {
+      gdouble t;
+
+      t = (now - priv->start_time) / (gdouble) (priv->end_time - priv->start_time);
+      t = ease_out_cubic (t);
+      adjustment_set_value (adjustment, priv->source + t * (priv->target - priv->source));
+
+      return TRUE;
+    }
+  else
+    {
+      adjustment_set_value (adjustment, priv->target);
+
+      return FALSE;
+    }
+}
+
+static void
+gtk_adjustment_on_frame_clock_update (GdkFrameClock *clock, 
+                                      GtkAdjustment *adjustment)
+{
+  GtkAdjustmentPrivate *priv = adjustment->priv;
+  gint64 now;
+
+  now = gdk_frame_clock_get_frame_time (clock);
+  if (!gtk_adjustment_animate_step (adjustment, now))
+    {
+      g_signal_handler_disconnect (priv->clock, priv->tick_id);
+      priv->tick_id = 0;
+      gdk_frame_clock_end_updating (priv->clock);
+    }
+}
+
+static void
+gtk_adjustment_maybe_animate (GtkAdjustment *adjustment,
+                              gdouble        target)
+{
+  GtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->target == target)
+    return;
+
+  priv->target = target;
+  
+  if (priv->duration != 0 && priv->clock != NULL)
+    {
+      priv->source = priv->value;
+      priv->start_time = gdk_frame_clock_get_frame_time (priv->clock);
+      priv->end_time = priv->start_time + 1000 * priv->duration;
+      if (priv->tick_id == 0)
+        {
+          priv->tick_id = g_signal_connect (priv->clock, "update",
+                                            G_CALLBACK (gtk_adjustment_on_frame_clock_update), adjustment);
+          gdk_frame_clock_begin_updating (priv->clock);
+        }
+    }
+  else
+    adjustment_set_value (adjustment, target);
+}
+
 /**
  * gtk_adjustment_set_value:
  * @adjustment: a #GtkAdjustment.
@@ -432,12 +545,7 @@ gtk_adjustment_set_value (GtkAdjustment *adjustment,
   value = MIN (value, priv->upper - priv->page_size);
   value = MAX (value, priv->lower);
 
-  if (value != priv->value)
-    {
-      priv->value = value;
-
-      gtk_adjustment_value_changed (adjustment);
-    }
+  gtk_adjustment_maybe_animate (adjustment, value);
 }
 
 /**
@@ -847,3 +955,20 @@ gtk_adjustment_get_minimum_increment (GtkAdjustment *adjustment)
   return minimum_increment;
 }
 
+void
+gtk_adjustment_enable_animation (GtkAdjustment *adjustment,
+                                 GdkFrameClock *clock,
+                                 guint          duration)
+{
+  GtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->clock)
+    g_object_unref (priv->clock);
+
+  priv->clock = clock;
+
+  if (priv->clock)
+    g_object_ref (priv->clock);
+
+  priv->duration = duration; 
+}
diff --git a/gtk/gtkadjustmentprivate.h b/gtk/gtkadjustmentprivate.h
new file mode 100644
index 0000000..bc9c810
--- /dev/null
+++ b/gtk/gtkadjustmentprivate.h
@@ -0,0 +1,34 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2014 Red Hat, Inc
+ *
+ * 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/>.
+ */
+
+#ifndef __GTK_ADJUSTMENT_PRIVATE_H__
+#define __GTK_ADJUSTMENT_PRIVATE_H__
+
+
+#include <gtk/gtkadjustment.h>
+
+
+G_BEGIN_DECLS
+
+void gtk_adjustment_enable_animation (GtkAdjustment *adjustment,
+                                      GdkFrameClock *clock,
+                                      guint          duration);
+
+G_END_DECLS
+
+
+#endif /* __GTK_ADJUSTMENT_PRIVATE_H__ */
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index f9d9e29..db23f2d 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -27,6 +27,7 @@
 #include "gtkscrolledwindow.h"
 
 #include "gtkadjustment.h"
+#include "gtkadjustmentprivate.h"
 #include "gtkbindings.h"
 #include "gtkdnd.h"
 #include "gtkintl.h"
@@ -3123,12 +3124,40 @@ gtk_scrolled_window_unrealize (GtkWidget *widget)
 }
 
 static void
+gtk_scrolled_window_enable_animation (GtkScrolledWindow *sw)
+{
+  GtkAdjustment *adjustment;
+  GdkFrameClock *clock;
+
+  clock = gtk_widget_get_frame_clock (GTK_WIDGET (sw)),
+  adjustment = gtk_range_get_adjustment (GTK_RANGE (sw->priv->hscrollbar));
+  gtk_adjustment_enable_animation (adjustment, clock, 200);
+
+  adjustment = gtk_range_get_adjustment (GTK_RANGE (sw->priv->vscrollbar));
+  gtk_adjustment_enable_animation (adjustment, clock, 200);
+}
+
+static void
+gtk_scrolled_window_disable_animation (GtkScrolledWindow *sw)
+{
+  GtkAdjustment *adjustment;
+
+  adjustment = gtk_range_get_adjustment (GTK_RANGE (sw->priv->hscrollbar));
+  gtk_adjustment_enable_animation (adjustment, NULL, 0);
+
+  adjustment = gtk_range_get_adjustment (GTK_RANGE (sw->priv->vscrollbar));
+  gtk_adjustment_enable_animation (adjustment, NULL, 0);
+}
+
+static void
 gtk_scrolled_window_map (GtkWidget *widget)
 {
   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
 
   gdk_window_show (scrolled_window->priv->overshoot_window);
 
+  gtk_scrolled_window_enable_animation (scrolled_window);
+
   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
 }
 
@@ -3137,6 +3166,8 @@ gtk_scrolled_window_unmap (GtkWidget *widget)
 {
   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
 
+  gtk_scrolled_window_disable_animation (scrolled_window);
+
   gdk_window_hide (scrolled_window->priv->overshoot_window);
 
   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);


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