kineticscrolling: use computed frame times

Instead of accumulating a series of doubles, use the actual computed frame
time for duration tracking. While there is extremely low chance of
aliasing with animations under a few seconds, this just ensures we're
using the same clocking inside and outside of GtkKineticScrolling.
This commit is contained in:
Christian Hergert
2024-10-21 09:52:17 -07:00
parent 4eb6d6bc7d
commit c9facb5554
3 changed files with 40 additions and 34 deletions

View File

@@ -70,18 +70,20 @@ struct _GtkKineticScrolling
double c2;
double equilibrium_position;
double t;
gint64 t;
double position;
double velocity;
};
static void gtk_kinetic_scrolling_init_overshoot (GtkKineticScrolling *data,
gint64 frame_time,
double equilibrium_position,
double initial_position,
double initial_velocity);
GtkKineticScrolling *
gtk_kinetic_scrolling_new (double lower,
gtk_kinetic_scrolling_new (gint64 frame_time,
double lower,
double upper,
double overshoot_width,
double decel_friction,
@@ -96,16 +98,18 @@ gtk_kinetic_scrolling_new (double lower,
data->upper = upper;
data->decel_friction = decel_friction;
data->overshoot_friction = overshoot_friction;
if(initial_position < lower)
if (initial_position < lower)
{
gtk_kinetic_scrolling_init_overshoot (data,
frame_time,
lower,
initial_position,
initial_velocity);
}
else if(initial_position > upper)
else if (initial_position > upper)
{
gtk_kinetic_scrolling_init_overshoot (data,
frame_time,
upper,
initial_position,
initial_velocity);
@@ -115,9 +119,9 @@ gtk_kinetic_scrolling_new (double lower,
data->phase = GTK_KINETIC_SCROLLING_PHASE_DECELERATING;
data->c1 = initial_velocity / decel_friction + initial_position;
data->c2 = -initial_velocity / decel_friction;
data->t = 0;
data->position = initial_position;
data->velocity = initial_velocity;
data->t = frame_time;
}
return data;
@@ -160,6 +164,7 @@ gtk_kinetic_scrolling_free (GtkKineticScrolling *kinetic)
static void
gtk_kinetic_scrolling_init_overshoot (GtkKineticScrolling *data,
gint64 frame_time,
double equilibrium_position,
double initial_position,
double initial_velocity)
@@ -168,36 +173,36 @@ gtk_kinetic_scrolling_init_overshoot (GtkKineticScrolling *data,
data->equilibrium_position = equilibrium_position;
data->c1 = initial_position - equilibrium_position;
data->c2 = initial_velocity + data->overshoot_friction / 2 * data->c1;
data->t = 0;
data->t = frame_time;
}
gboolean
gtk_kinetic_scrolling_tick (GtkKineticScrolling *data,
double time_delta,
gint64 frame_time,
double *position,
double *velocity)
{
switch(data->phase)
double t = (frame_time - data->t) / (double)G_USEC_PER_SEC;
switch (data->phase)
{
case GTK_KINETIC_SCROLLING_PHASE_DECELERATING:
{
double exp_part;
data->t += time_delta;
exp_part = exp (-data->decel_friction * data->t);
exp_part = exp (-data->decel_friction * t);
data->position = data->c1 + data->c2 * exp_part;
data->velocity = -data->decel_friction * data->c2 * exp_part;
if(data->position < data->lower)
if (data->position < data->lower)
{
gtk_kinetic_scrolling_init_overshoot(data,data->lower,data->position,data->velocity);
gtk_kinetic_scrolling_init_overshoot (data, frame_time, data->lower, data->position, data->velocity);
}
else if (data->position > data->upper)
{
gtk_kinetic_scrolling_init_overshoot(data, data->upper, data->position, data->velocity);
gtk_kinetic_scrolling_init_overshoot (data, frame_time, data->upper, data->position, data->velocity);
}
else if (fabs(data->velocity) < 0.1)
else if (fabs (data->velocity) < 0.1)
{
gtk_kinetic_scrolling_stop (data);
}
@@ -208,21 +213,20 @@ gtk_kinetic_scrolling_tick (GtkKineticScrolling *data,
{
double exp_part, pos;
data->t += time_delta;
exp_part = exp(-data->overshoot_friction / 2 * data->t);
pos = exp_part * (data->c1 + data->c2 * data->t);
exp_part = exp (-data->overshoot_friction / 2 * t);
pos = exp_part * (data->c1 + data->c2 * t);
if (pos < data->lower - 50 || pos > data->upper + 50)
{
pos = CLAMP (pos, data->lower - 50, data->upper + 50);
gtk_kinetic_scrolling_init_overshoot (data, data->equilibrium_position, pos, 0);
gtk_kinetic_scrolling_init_overshoot (data, frame_time, data->equilibrium_position, pos, 0);
}
else
data->velocity = data->c2 * exp_part - data->overshoot_friction / 2 * pos;
data->position = pos + data->equilibrium_position;
if(fabs (pos) < 0.1)
if (fabs (pos) < 0.1)
{
data->phase = GTK_KINETIC_SCROLLING_PHASE_FINISHED;
data->position = data->equilibrium_position;

View File

@@ -31,7 +31,8 @@ typedef enum {
typedef struct _GtkKineticScrolling GtkKineticScrolling;
GtkKineticScrolling * gtk_kinetic_scrolling_new (double lower,
GtkKineticScrolling * gtk_kinetic_scrolling_new (gint64 frame_time,
double lower,
double upper,
double overshoot_width,
double decel_friction,
@@ -45,7 +46,7 @@ GtkKineticScrollingChange gtk_kinetic_scrolling_update_size (GtkKineticScrolling
double upper);
gboolean gtk_kinetic_scrolling_tick (GtkKineticScrolling *data,
double time_delta,
gint64 frame_time,
double *position,
double *velocity);

View File

@@ -3263,11 +3263,10 @@ scrolled_window_deceleration_cb (GtkWidget *widget,
GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
GtkAdjustment *hadjustment, *vadjustment;
gint64 current_time;
double position, elapsed;
double position;
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;
priv->last_deceleration_time = current_time;
hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
@@ -3276,7 +3275,7 @@ scrolled_window_deceleration_cb (GtkWidget *widget,
gtk_scrolled_window_invalidate_overshoot (scrolled_window);
if (priv->hscrolling &&
gtk_kinetic_scrolling_tick (priv->hscrolling, elapsed, &position, NULL))
gtk_kinetic_scrolling_tick (priv->hscrolling, current_time, &position, NULL))
{
priv->unclamped_hadj_value = position;
gtk_adjustment_set_value (hadjustment, position);
@@ -3284,7 +3283,7 @@ scrolled_window_deceleration_cb (GtkWidget *widget,
}
if (priv->vscrolling &&
gtk_kinetic_scrolling_tick (priv->vscrolling, elapsed, &position, NULL))
gtk_kinetic_scrolling_tick (priv->vscrolling, current_time, &position, NULL))
{
priv->unclamped_vadj_value = position;
gtk_adjustment_set_value (vadjustment, position);
@@ -3320,13 +3319,15 @@ kinetic_scroll_stop_notify (GtkScrolledWindow *scrolled_window)
}
static void
gtk_scrolled_window_accumulate_velocity (GtkKineticScrolling **scrolling, double elapsed, double *velocity)
gtk_scrolled_window_accumulate_velocity (GtkKineticScrolling **scrolling,
gint64 current_time,
double *velocity)
{
if (!*scrolling)
return;
double last_velocity;
gtk_kinetic_scrolling_tick (*scrolling, elapsed, NULL, &last_velocity);
gtk_kinetic_scrolling_tick (*scrolling, current_time, NULL, &last_velocity);
if (((*velocity >= 0) == (last_velocity >= 0)) &&
(fabs (*velocity) >= fabs (last_velocity) * VELOCITY_ACCUMULATION_FLOOR))
{
@@ -3344,7 +3345,6 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
GdkFrameClock *frame_clock;
gint64 current_time;
double elapsed;
int overshoot_x, overshoot_y;
g_return_if_fail (priv->deceleration_id == 0);
@@ -3352,7 +3352,6 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window));
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;
_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y);
@@ -3362,7 +3361,7 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
double lower,upper;
GtkAdjustment *hadjustment;
gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, elapsed, &priv->x_velocity);
gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, current_time, &priv->x_velocity);
g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
if (priv->x_velocity != 0 || overshoot_x != 0)
@@ -3372,7 +3371,8 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
upper = gtk_adjustment_get_upper (hadjustment);
upper -= gtk_adjustment_get_page_size (hadjustment);
priv->hscrolling =
gtk_kinetic_scrolling_new (lower,
gtk_kinetic_scrolling_new (current_time,
lower,
upper,
MAX_OVERSHOOT_DISTANCE,
DECELERATION_FRICTION,
@@ -3389,7 +3389,7 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
double lower,upper;
GtkAdjustment *vadjustment;
gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, elapsed, &priv->y_velocity);
gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, current_time, &priv->y_velocity);
g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
if (priv->y_velocity != 0 || overshoot_y != 0)
@@ -3399,7 +3399,8 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
upper = gtk_adjustment_get_upper(vadjustment);
upper -= gtk_adjustment_get_page_size(vadjustment);
priv->vscrolling =
gtk_kinetic_scrolling_new (lower,
gtk_kinetic_scrolling_new (current_time,
lower,
upper,
MAX_OVERSHOOT_DISTANCE,
DECELERATION_FRICTION,