diff --git a/gtk/gtkkineticscrolling.c b/gtk/gtkkineticscrolling.c index a03f683876..eb9c13bdfd 100644 --- a/gtk/gtkkineticscrolling.c +++ b/gtk/gtkkineticscrolling.c @@ -145,7 +145,8 @@ gtk_kinetic_scrolling_init_overshoot (GtkKineticScrolling *data, gboolean gtk_kinetic_scrolling_tick (GtkKineticScrolling *data, double time_delta, - double *position) + double *position, + double *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/gtkkineticscrollingprivate.h b/gtk/gtkkineticscrollingprivate.h index 29f3eae228..b97fac360f 100644 --- a/gtk/gtkkineticscrollingprivate.h +++ b/gtk/gtkkineticscrollingprivate.h @@ -36,7 +36,8 @@ void gtk_kinetic_scrolling_free (GtkKineticScrolling *kinet gboolean gtk_kinetic_scrolling_tick (GtkKineticScrolling *data, double time_delta, - double *position); + double *position, + double *velocity); G_END_DECLS diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 5fcbec1338..85cdacacd5 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -184,6 +184,9 @@ #define MAX_OVERSHOOT_DISTANCE 100 #define DECELERATION_FRICTION 4 #define OVERSHOOT_FRICTION 20 +#define VELOCITY_ACCUMULATION_FLOOR 0.33 +#define VELOCITY_ACCUMULATION_CEIL 1.0 +#define VELOCITY_ACCUMULATION_MAX 6.0 /* Animated scrolling */ #define ANIMATION_DURATION 200 @@ -274,6 +277,9 @@ typedef struct /* Kinetic scrolling */ GtkGesture *long_press_gesture; GtkGesture *swipe_gesture; + GtkKineticScrolling *hscrolling; + GtkKineticScrolling *vscrolling; + gint64 last_deceleration_time; /* These two gestures are mutually exclusive */ GtkGesture *drag_gesture; @@ -294,15 +300,6 @@ typedef struct double unclamped_vadj_value; } GtkScrolledWindowPrivate; -typedef struct -{ - GtkScrolledWindow *scrolled_window; - gint64 last_deceleration_time; - - GtkKineticScrolling *hscrolling; - GtkKineticScrolling *vscrolling; -} KineticScrollData; - enum { PROP_0, PROP_HADJUSTMENT, @@ -2578,6 +2575,9 @@ gtk_scrolled_window_dispose (GObject *object) 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); @@ -3193,41 +3193,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 = gtk_scrolled_window_get_instance_private (scrolled_window); GtkAdjustment *hadjustment, *vadjustment; gint64 current_time; double 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_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)); vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (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; @@ -3252,14 +3251,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 @@ -3267,26 +3281,29 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) { GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window); 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)) { double lower,upper; GtkAdjustment *hadjustment; + gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, elapsed, &priv->x_velocity); + 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); - data->hscrolling = + priv->hscrolling = gtk_kinetic_scrolling_new (lower, upper, MAX_OVERSHOOT_DISTANCE, @@ -3295,17 +3312,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)) { double lower,upper; GtkAdjustment *vadjustment; + gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, elapsed, &priv->y_velocity); + 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); - data->vscrolling = + priv->vscrolling = gtk_kinetic_scrolling_new (lower, upper, MAX_OVERSHOOT_DISTANCE, @@ -3314,10 +3335,12 @@ 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); 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