Merge branch 'wip/exalm/accumulate-scrolling-backport' into 'gtk-3-24'

scrolledwindow: Accumulate velocity with kinetic scrolling

See merge request GNOME/gtk!2818
This commit is contained in:
Carlos Garnacho
2020-12-01 13:14:40 +00:00
3 changed files with 65 additions and 38 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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