[gtk/wip/exalm/accumulate-scrolling-backport] scrolledwindow: Accumulate velocity with kinetic scrolling




commit 1d62a010eff89055635faf2656088a28663faa2e
Author: Chris Lord <chrislord net gmail com>
Date:   Fri Oct 30 18:36:53 2020 +0000

    scrolledwindow: Accumulate velocity with kinetic scrolling
    
    Accumulate existing velocity when decelerating from a swipe if the swipe
    velocity is above a certain fraction of that existing velocity.

 gtk/gtkkineticscrolling.c |  5 ++-
 gtk/gtkkineticscrolling.h |  3 +-
 gtk/gtkscrolledwindow.c   | 95 +++++++++++++++++++++++++++++------------------
 3 files changed, 65 insertions(+), 38 deletions(-)
---
diff --git a/gtk/gtkkineticscrolling.c b/gtk/gtkkineticscrolling.c
index 29bf085674..9036e158dc 100644
--- a/gtk/gtkkineticscrolling.c
+++ b/gtk/gtkkineticscrolling.c
@@ -146,7 +146,8 @@ gtk_kinetic_scrolling_init_overshoot (GtkKineticScrolling *data,
 gboolean
 gtk_kinetic_scrolling_tick (GtkKineticScrolling *data,
                             gdouble              time_delta,
-                            gdouble             *position)
+                            gdouble             *position,
+                            gdouble             *velocity)
 {
   switch(data->phase)
     {
@@ -213,6 +214,8 @@ gtk_kinetic_scrolling_tick (GtkKineticScrolling *data,
 
   if (position)
     *position = data->position;
+  if (velocity)
+    *velocity = data->velocity;
 
   return data->phase != GTK_KINETIC_SCROLLING_PHASE_FINISHED;
 }
diff --git a/gtk/gtkkineticscrolling.h b/gtk/gtkkineticscrolling.h
index 92883b8c1c..d00f1d05f6 100644
--- a/gtk/gtkkineticscrolling.h
+++ b/gtk/gtkkineticscrolling.h
@@ -36,7 +36,8 @@ void                     gtk_kinetic_scrolling_free (GtkKineticScrolling  *kinet
 
 gboolean                 gtk_kinetic_scrolling_tick (GtkKineticScrolling  *data,
                                                      gdouble               time_delta,
-                                                     gdouble              *position);
+                                                     gdouble              *position,
+                                                     gdouble              *velocity);
 
 G_END_DECLS
 
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index f17863d805..0f4193b14b 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -179,6 +179,9 @@
 #define DECELERATION_FRICTION 4
 #define OVERSHOOT_FRICTION 20
 #define SCROLL_CAPTURE_THRESHOLD_MS 150
+#define VELOCITY_ACCUMULATION_FLOOR 0.33
+#define VELOCITY_ACCUMULATION_CEIL 1.0
+#define VELOCITY_ACCUMULATION_MAX 6.0
 
 /* Animated scrolling */
 #define ANIMATION_DURATION 200
@@ -252,6 +255,9 @@ struct _GtkScrolledWindowPrivate
   /* Kinetic scrolling */
   GtkGesture *long_press_gesture;
   GtkGesture *swipe_gesture;
+  GtkKineticScrolling *hscrolling;
+  GtkKineticScrolling *vscrolling;
+  gint64 last_deceleration_time;
 
   GArray *scroll_history;
   GdkDevice *scroll_device;
@@ -279,15 +285,6 @@ struct _GtkScrolledWindowPrivate
   gdouble                unclamped_vadj_value;
 };
 
-typedef struct
-{
-  GtkScrolledWindow     *scrolled_window;
-  gint64                 last_deceleration_time;
-
-  GtkKineticScrolling   *hscrolling;
-  GtkKineticScrolling   *vscrolling;
-} KineticScrollData;
-
 enum {
   PROP_0,
   PROP_HADJUSTMENT,
@@ -2837,6 +2834,9 @@ gtk_scrolled_window_destroy (GtkWidget *widget)
       priv->deceleration_id = 0;
     }
 
+  g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
+  g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
+
   if (priv->scroll_events_overshoot_id)
     {
       g_source_remove (priv->scroll_events_overshoot_id);
@@ -3687,41 +3687,40 @@ scrolled_window_deceleration_cb (GtkWidget         *widget,
                                  GdkFrameClock     *frame_clock,
                                  gpointer           user_data)
 {
-  KineticScrollData *data = user_data;
-  GtkScrolledWindow *scrolled_window = data->scrolled_window;
+  GtkScrolledWindow *scrolled_window = user_data;
   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
   GtkAdjustment *hadjustment, *vadjustment;
   gint64 current_time;
   gdouble position, elapsed;
 
   current_time = gdk_frame_clock_get_frame_time (frame_clock);
-  elapsed = (current_time - data->last_deceleration_time) / 1000000.0;
-  data->last_deceleration_time = current_time;
+  elapsed = (current_time - priv->last_deceleration_time) / (double)G_TIME_SPAN_SECOND;
+  priv->last_deceleration_time = current_time;
 
   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
 
   gtk_scrolled_window_invalidate_overshoot (scrolled_window);
 
-  if (data->hscrolling &&
-      gtk_kinetic_scrolling_tick (data->hscrolling, elapsed, &position))
+  if (priv->hscrolling &&
+      gtk_kinetic_scrolling_tick (priv->hscrolling, elapsed, &position, NULL))
     {
       priv->unclamped_hadj_value = position;
       gtk_adjustment_set_value (hadjustment, position);
     }
-  else if (data->hscrolling)
-    g_clear_pointer (&data->hscrolling, gtk_kinetic_scrolling_free);
+  else if (priv->hscrolling)
+    g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
 
-  if (data->vscrolling &&
-      gtk_kinetic_scrolling_tick (data->vscrolling, elapsed, &position))
+  if (priv->vscrolling &&
+      gtk_kinetic_scrolling_tick (priv->vscrolling, elapsed, &position, NULL))
     {
       priv->unclamped_vadj_value = position;
       gtk_adjustment_set_value (vadjustment, position);
     }
-  else if (data->vscrolling)
-    g_clear_pointer (&data->vscrolling, gtk_kinetic_scrolling_free);
+  else if (priv->vscrolling)
+    g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
 
-  if (!data->hscrolling && !data->vscrolling)
+  if (!priv->hscrolling && !priv->vscrolling)
     {
       gtk_scrolled_window_cancel_deceleration (scrolled_window);
       return G_SOURCE_REMOVE;
@@ -3746,14 +3745,29 @@ gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
 }
 
 static void
-kinetic_scroll_data_free (KineticScrollData *data)
+kinetic_scroll_stop_notify (GtkScrolledWindow *scrolled_window)
 {
-  if (data->hscrolling)
-    gtk_kinetic_scrolling_free (data->hscrolling);
-  if (data->vscrolling)
-    gtk_kinetic_scrolling_free (data->vscrolling);
+  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
+  priv->deceleration_id = 0;
+}
 
-  g_free (data);
+static void
+gtk_scrolled_window_accumulate_velocity (GtkKineticScrolling **scrolling, double elapsed, double *velocity)
+{
+    if (!*scrolling)
+      return;
+
+    double last_velocity;
+    gtk_kinetic_scrolling_tick (*scrolling, elapsed, NULL, &last_velocity);
+    if (((*velocity >= 0) == (last_velocity >= 0)) &&
+        (fabs (*velocity) >= fabs (last_velocity) * VELOCITY_ACCUMULATION_FLOOR))
+      {
+        double min_velocity = last_velocity * VELOCITY_ACCUMULATION_FLOOR;
+        double max_velocity = last_velocity * VELOCITY_ACCUMULATION_CEIL;
+        double accumulation_multiplier = (*velocity - min_velocity) / (max_velocity - min_velocity);
+        *velocity += last_velocity * fmin (accumulation_multiplier, VELOCITY_ACCUMULATION_MAX);
+      }
+    g_clear_pointer (scrolling, gtk_kinetic_scrolling_free);
 }
 
 static void
@@ -3761,26 +3775,29 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
 {
   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
   GdkFrameClock *frame_clock;
-  KineticScrollData *data;
+  gint64 current_time;
+  double elapsed;
 
   g_return_if_fail (priv->deceleration_id == 0);
 
   frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window));
 
-  data = g_new0 (KineticScrollData, 1);
-  data->scrolled_window = scrolled_window;
-  data->last_deceleration_time = gdk_frame_clock_get_frame_time (frame_clock);
+  current_time = gdk_frame_clock_get_frame_time (frame_clock);
+  elapsed = (current_time - priv->last_deceleration_time) / (double)G_TIME_SPAN_SECOND;
+  priv->last_deceleration_time = current_time;
 
   if (may_hscroll (scrolled_window))
     {
       gdouble lower,upper;
       GtkAdjustment *hadjustment;
 
+      gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, elapsed, &priv->x_velocity);
+
       hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
       lower = gtk_adjustment_get_lower (hadjustment);
       upper = gtk_adjustment_get_upper (hadjustment);
       upper -= gtk_adjustment_get_page_size (hadjustment);
-      data->hscrolling =
+      priv->hscrolling =
         gtk_kinetic_scrolling_new (lower,
                                    upper,
                                    MAX_OVERSHOOT_DISTANCE,
@@ -3789,17 +3806,21 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
                                    priv->unclamped_hadj_value,
                                    priv->x_velocity);
     }
+  else
+    g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
 
   if (may_vscroll (scrolled_window))
     {
       gdouble lower,upper;
       GtkAdjustment *vadjustment;
 
+      gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, elapsed, &priv->y_velocity);
+
       vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
       lower = gtk_adjustment_get_lower(vadjustment);
       upper = gtk_adjustment_get_upper(vadjustment);
       upper -= gtk_adjustment_get_page_size(vadjustment);
-      data->vscrolling =
+      priv->vscrolling =
         gtk_kinetic_scrolling_new (lower,
                                    upper,
                                    MAX_OVERSHOOT_DISTANCE,
@@ -3808,11 +3829,13 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
                                    priv->unclamped_vadj_value,
                                    priv->y_velocity);
     }
+  else
+    g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
 
   scrolled_window->priv->deceleration_id =
     gtk_widget_add_tick_callback (GTK_WIDGET (scrolled_window),
-                                  scrolled_window_deceleration_cb, data,
-                                  (GDestroyNotify) kinetic_scroll_data_free);
+                                  scrolled_window_deceleration_cb, scrolled_window,
+                                  (GDestroyNotify) kinetic_scroll_stop_notify);
 }
 
 static gboolean


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