From 274e2b221f3ef04788b86e8ef1c70f9109f9ceb9 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 15 Mar 2022 21:52:35 +0100 Subject: [PATCH 1/5] gtkkineticscroll: Do not reset velocity after stop() We may want to fetch the last velocity obtained, even though we preemptively called stop() on a kinetic scroll helper. Keep this velocity so it can be queried later on. --- gtk/gtkkineticscrolling.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gtk/gtkkineticscrolling.c b/gtk/gtkkineticscrolling.c index 4eab5dcc84..8c697d54cd 100644 --- a/gtk/gtkkineticscrolling.c +++ b/gtk/gtkkineticscrolling.c @@ -254,6 +254,5 @@ gtk_kinetic_scrolling_stop (GtkKineticScrolling *data) { data->phase = GTK_KINETIC_SCROLLING_PHASE_FINISHED; data->position = round (data->position); - data->velocity = 0; } } From 129bc27d5336a9789602ab26c42790fe71da76c3 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 15 Mar 2022 21:59:12 +0100 Subject: [PATCH 2/5] gtkscrolledwindow: Refactor kinetic scroll animation Do not depend on the kinetic scroll helpers existing or not before exiting the animation, as we may want to keep those a little bit longer after stopped. --- gtk/gtkscrolledwindow.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 416c8288eb..a5294d8003 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -3268,6 +3268,7 @@ scrolled_window_deceleration_cb (GtkWidget *widget, GtkAdjustment *hadjustment, *vadjustment; gint64 current_time; double position, elapsed; + gboolean retval = G_SOURCE_REMOVE; current_time = gdk_frame_clock_get_frame_time (frame_clock); elapsed = (current_time - priv->last_deceleration_time) / (double)G_TIME_SPAN_SECOND; @@ -3283,6 +3284,7 @@ scrolled_window_deceleration_cb (GtkWidget *widget, { priv->unclamped_hadj_value = position; gtk_adjustment_set_value (hadjustment, position); + retval = G_SOURCE_CONTINUE; } else if (priv->hscrolling) g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free); @@ -3292,19 +3294,17 @@ scrolled_window_deceleration_cb (GtkWidget *widget, { priv->unclamped_vadj_value = position; gtk_adjustment_set_value (vadjustment, position); + retval = G_SOURCE_CONTINUE; } else if (priv->vscrolling) g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free); - if (!priv->hscrolling && !priv->vscrolling) - { - gtk_scrolled_window_cancel_deceleration (scrolled_window); - return G_SOURCE_REMOVE; - } + if (retval == G_SOURCE_REMOVE) + gtk_scrolled_window_cancel_deceleration (scrolled_window); + else + gtk_scrolled_window_invalidate_overshoot (scrolled_window); - gtk_scrolled_window_invalidate_overshoot (scrolled_window); - - return G_SOURCE_CONTINUE; + return retval; } static void From df40db137b8d881605ef7a487ea6ff4fda7d1bde Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 15 Mar 2022 22:01:36 +0100 Subject: [PATCH 3/5] gtkscrolledwindow: Change lifetime of kinetic scroll helpers In order to properly accumulate scroll velocities, we need to keep the kinetic scroll helpers after we have possibly stopped them in the process of initiating a further scroll flick. So, instead of stopping (and destroying) those helpers on scroll-begin, keep them until the next scroll-end if a scroll was initiated before kinetic scroll finished. This way we can fetch the last velocity when calculating the extra kick. In order to ensure the helpers don't live beyond what it is expected, we now also remove them after a finished hold event. Fixes the accumulation of scrolling velocity on consecutive scroll sequences. --- gtk/gtkscrolledwindow.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index a5294d8003..227e3e2741 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -1056,6 +1056,11 @@ gtk_scrolled_window_decelerate (GtkScrolledWindow *scrolled_window, gtk_scrolled_window_start_deceleration (scrolled_window); priv->x_velocity = priv->y_velocity = 0; } + else + { + g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free); + g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free); + } } static void @@ -3286,8 +3291,6 @@ scrolled_window_deceleration_cb (GtkWidget *widget, gtk_adjustment_set_value (hadjustment, position); retval = G_SOURCE_CONTINUE; } - else if (priv->hscrolling) - g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free); if (priv->vscrolling && gtk_kinetic_scrolling_tick (priv->vscrolling, elapsed, &position, NULL)) @@ -3296,8 +3299,6 @@ scrolled_window_deceleration_cb (GtkWidget *widget, gtk_adjustment_set_value (vadjustment, position); retval = G_SOURCE_CONTINUE; } - else if (priv->vscrolling) - g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free); if (retval == G_SOURCE_REMOVE) gtk_scrolled_window_cancel_deceleration (scrolled_window); @@ -3368,6 +3369,7 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) GtkAdjustment *hadjustment; gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, elapsed, &priv->x_velocity); + g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free); hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)); lower = gtk_adjustment_get_lower (hadjustment); @@ -3391,6 +3393,7 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) GtkAdjustment *vadjustment; gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, elapsed, &priv->y_velocity); + g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free); vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)); lower = gtk_adjustment_get_lower(vadjustment); From 65839f67f8b69e6bffe3cfe9940041d3a1836cf1 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 15 Mar 2022 22:41:27 +0100 Subject: [PATCH 4/5] gtkscrolledwindow: Do not trigger kinetic helpers on 0 velocity Doing this is pointless, so it could be skipped. --- gtk/gtkscrolledwindow.c | 54 +++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 227e3e2741..db76dae551 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -3371,18 +3371,21 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, elapsed, &priv->x_velocity); g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free); - hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)); - lower = gtk_adjustment_get_lower (hadjustment); - upper = gtk_adjustment_get_upper (hadjustment); - upper -= gtk_adjustment_get_page_size (hadjustment); - priv->hscrolling = - gtk_kinetic_scrolling_new (lower, - upper, - MAX_OVERSHOOT_DISTANCE, - DECELERATION_FRICTION, - OVERSHOOT_FRICTION, - priv->unclamped_hadj_value, - priv->x_velocity); + if (priv->x_velocity != 0) + { + hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)); + lower = gtk_adjustment_get_lower (hadjustment); + upper = gtk_adjustment_get_upper (hadjustment); + upper -= gtk_adjustment_get_page_size (hadjustment); + priv->hscrolling = + gtk_kinetic_scrolling_new (lower, + upper, + MAX_OVERSHOOT_DISTANCE, + DECELERATION_FRICTION, + OVERSHOOT_FRICTION, + priv->unclamped_hadj_value, + priv->x_velocity); + } } else g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free); @@ -3395,18 +3398,21 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, elapsed, &priv->y_velocity); g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free); - vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)); - lower = gtk_adjustment_get_lower(vadjustment); - upper = gtk_adjustment_get_upper(vadjustment); - upper -= gtk_adjustment_get_page_size(vadjustment); - priv->vscrolling = - gtk_kinetic_scrolling_new (lower, - upper, - MAX_OVERSHOOT_DISTANCE, - DECELERATION_FRICTION, - OVERSHOOT_FRICTION, - priv->unclamped_vadj_value, - priv->y_velocity); + if (priv->y_velocity != 0) + { + vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)); + lower = gtk_adjustment_get_lower(vadjustment); + upper = gtk_adjustment_get_upper(vadjustment); + upper -= gtk_adjustment_get_page_size(vadjustment); + priv->vscrolling = + gtk_kinetic_scrolling_new (lower, + upper, + MAX_OVERSHOOT_DISTANCE, + DECELERATION_FRICTION, + OVERSHOOT_FRICTION, + priv->unclamped_vadj_value, + priv->y_velocity); + } } else g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free); From f7d9ede82dca41241902d86e5ee7d1997c02f4a2 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 15 Mar 2022 22:42:13 +0100 Subject: [PATCH 5/5] gtkkineticscrolling: Do not take distance based shortcuts The pixel distance could be small enough between tick() calls that this kind of checks might potentially become a problem. Rely only on the calculated velocity to trigger the STOPPED phase, and use a lower threshold to avoid cutting the animation too early. Related: https://gitlab.gnome.org/GNOME/gtk/-/issues/4725 --- gtk/gtkkineticscrolling.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/gtk/gtkkineticscrolling.c b/gtk/gtkkineticscrolling.c index 8c697d54cd..1f02fe7993 100644 --- a/gtk/gtkkineticscrolling.c +++ b/gtk/gtkkineticscrolling.c @@ -181,8 +181,6 @@ gtk_kinetic_scrolling_tick (GtkKineticScrolling *data, { case GTK_KINETIC_SCROLLING_PHASE_DECELERATING: { - double last_position = data->position; - double last_time = data->t; double exp_part; data->t += time_delta; @@ -199,8 +197,7 @@ gtk_kinetic_scrolling_tick (GtkKineticScrolling *data, { gtk_kinetic_scrolling_init_overshoot(data, data->upper, data->position, data->velocity); } - else if (fabs(data->velocity) < 1 || - (last_time != 0.0 && fabs(data->position - last_position) < 1)) + else if (fabs(data->velocity) < 0.1) { gtk_kinetic_scrolling_stop (data); }