369 lines
15 KiB
Diff
369 lines
15 KiB
Diff
|
From ced3c2c63872021885cf91f134dde818096bae42 Mon Sep 17 00:00:00 2001
|
||
|
From: Kristian Rietveld <kris@lanedo.com>
|
||
|
Date: Wed, 19 Sep 2012 07:22:39 +0200
|
||
|
Subject: [PATCH 23/68] Improve overshooting behavior
|
||
|
|
||
|
This is done by taking the stiffness calculations as found in the WebKit
|
||
|
source code. These calculations are now used to compute the overshooting
|
||
|
distance as well as handling the snap back animation. Also, we only
|
||
|
start overshooting after we have hit a border in the container (i.e. an
|
||
|
adjustment has reached its upper/lower bound).
|
||
|
|
||
|
Also rename the timeout functions from deceleration to snap back, since
|
||
|
these are not handling kinetic deceleration in this code.
|
||
|
---
|
||
|
gtk/gtkscrolledwindow.c | 179 ++++++++++++++++++++++++++---------------------
|
||
|
1 file changed, 99 insertions(+), 80 deletions(-)
|
||
|
|
||
|
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
|
||
|
index 92e75a0..f3be303 100644
|
||
|
--- a/gtk/gtkscrolledwindow.c
|
||
|
+++ b/gtk/gtkscrolledwindow.c
|
||
|
@@ -84,6 +84,10 @@
|
||
|
#define OVERSHOOT_INVERSE_ACCELERATION 0.003
|
||
|
#define RELEASE_EVENT_TIMEOUT 1000
|
||
|
|
||
|
+#define BAND_STIFFNESS 20.0f
|
||
|
+#define BAND_AMPLITUDE 0.31f
|
||
|
+#define BAND_PERIOD 1.6f
|
||
|
+
|
||
|
/* Overlay scrollbars */
|
||
|
#define SCROLL_INTERVAL_INITIAL 300
|
||
|
#define SCROLL_INTERVAL_REPEAT 100
|
||
|
@@ -107,6 +111,9 @@ typedef struct {
|
||
|
gdouble x_velocity;
|
||
|
gdouble y_velocity;
|
||
|
|
||
|
+ gdouble x_force;
|
||
|
+ gdouble y_force;
|
||
|
+
|
||
|
gdouble unclamped_hadj_value;
|
||
|
gdouble unclamped_vadj_value;
|
||
|
|
||
|
@@ -143,12 +150,13 @@ typedef struct {
|
||
|
typedef struct
|
||
|
{
|
||
|
GtkScrolledWindow *scrolled_window;
|
||
|
- gint64 last_deceleration_time;
|
||
|
+ gint64 start_snap_back_time;
|
||
|
|
||
|
gdouble x_velocity;
|
||
|
gdouble y_velocity;
|
||
|
- gdouble vel_cosine;
|
||
|
- gdouble vel_sine;
|
||
|
+
|
||
|
+ gint x_overshoot;
|
||
|
+ gint y_overshoot;
|
||
|
} KineticScrollData;
|
||
|
|
||
|
enum {
|
||
|
@@ -260,7 +268,7 @@ static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *setting
|
||
|
gpointer user_data);
|
||
|
|
||
|
|
||
|
-static void gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window);
|
||
|
+static void gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window);
|
||
|
static gboolean gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
|
||
|
GdkEvent *event);
|
||
|
static void gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *window);
|
||
|
@@ -2016,6 +2024,9 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
|
||
|
if (event->phase == GDK_EVENT_SCROLL_PHASE_START)
|
||
|
priv->is_snapping_back = FALSE;
|
||
|
|
||
|
+ if (is_momentum_event && !is_overshot)
|
||
|
+ gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
|
||
|
+
|
||
|
/* Scroll events are handled in two cases:
|
||
|
* 1) We are not overshot and not snapping back, so scroll as
|
||
|
* usual and also handle momentum events.
|
||
|
@@ -2031,15 +2042,29 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
|
||
|
if (delta_x != 0.0 && scrolled_window->hscrollbar &&
|
||
|
(priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
|
||
|
{
|
||
|
+ gboolean may_overshoot = FALSE;
|
||
|
GtkAdjustment *adj;
|
||
|
|
||
|
+ /* Overshooting is allowed once the adjustment has reached
|
||
|
+ * the left/right.
|
||
|
+ */
|
||
|
adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
|
||
|
+ gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
|
||
|
+ if (gtk_adjustment_get_value (adj) < 1.0 ||
|
||
|
+ gtk_adjustment_get_value (adj) > max_adj - 1.0)
|
||
|
+ may_overshoot = TRUE;
|
||
|
|
||
|
- if (scrolled_window->hscrollbar_visible)
|
||
|
+ if (scrolled_window->hscrollbar_visible && (is_overshot || may_overshoot))
|
||
|
{
|
||
|
+ gdouble damped_delta;
|
||
|
+
|
||
|
+ priv->x_force += delta_x;
|
||
|
+ damped_delta = ceil(priv->x_force / BAND_STIFFNESS);
|
||
|
+ damped_delta = damped_delta - old_overshoot_x;
|
||
|
+
|
||
|
_gtk_scrolled_window_set_adjustment_value (scrolled_window,
|
||
|
adj,
|
||
|
- priv->unclamped_hadj_value + delta_x,
|
||
|
+ priv->unclamped_hadj_value + damped_delta,
|
||
|
TRUE,
|
||
|
FALSE);
|
||
|
}
|
||
|
@@ -2064,15 +2089,29 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
|
||
|
if (delta_y != 0.0 && scrolled_window->vscrollbar &&
|
||
|
(priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
|
||
|
{
|
||
|
+ gboolean may_overshoot = FALSE;
|
||
|
GtkAdjustment *adj;
|
||
|
|
||
|
+ /* Overshooting is allowed once the adjustment has reached
|
||
|
+ * the top/bottom.
|
||
|
+ */
|
||
|
adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
|
||
|
+ gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
|
||
|
+ if (gtk_adjustment_get_value (adj) < 1.0 ||
|
||
|
+ gtk_adjustment_get_value (adj) > max_adj - 1.0)
|
||
|
+ may_overshoot = TRUE;
|
||
|
|
||
|
- if (scrolled_window->vscrollbar_visible)
|
||
|
+ if (scrolled_window->vscrollbar_visible && (is_overshot || may_overshoot))
|
||
|
{
|
||
|
+ gdouble damped_delta;
|
||
|
+
|
||
|
+ priv->y_force += delta_y;
|
||
|
+ damped_delta = ceil(priv->y_force / BAND_STIFFNESS);
|
||
|
+ damped_delta = damped_delta - old_overshoot_y;
|
||
|
+
|
||
|
_gtk_scrolled_window_set_adjustment_value (scrolled_window,
|
||
|
adj,
|
||
|
- priv->unclamped_vadj_value + delta_y,
|
||
|
+ priv->unclamped_vadj_value + damped_delta,
|
||
|
TRUE,
|
||
|
FALSE);
|
||
|
}
|
||
|
@@ -2093,10 +2132,6 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
|
||
|
|
||
|
handled = TRUE;
|
||
|
}
|
||
|
-
|
||
|
-
|
||
|
- priv->last_scroll_event_time = gdk_event_get_time ((GdkEvent *)event);
|
||
|
- gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
|
||
|
}
|
||
|
|
||
|
_gtk_scrolled_window_get_overshoot (scrolled_window,
|
||
|
@@ -2112,9 +2147,17 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
|
||
|
* also signals that the user's gesture has ended.
|
||
|
*/
|
||
|
if (is_overshot &&
|
||
|
- (event->phase == GDK_EVENT_SCROLL_PHASE_END || is_momentum_event))
|
||
|
+ ((priv->last_scroll_event_time > 0 && is_momentum_event) ||
|
||
|
+ event->phase == GDK_EVENT_SCROLL_PHASE_END))
|
||
|
start_snap_back = TRUE;
|
||
|
|
||
|
+ /* Reset force if gesture has ended. */
|
||
|
+ if (event->phase == GDK_EVENT_SCROLL_PHASE_END)
|
||
|
+ {
|
||
|
+ priv->x_force = 0.0;
|
||
|
+ priv->y_force = 0.0;
|
||
|
+ }
|
||
|
+
|
||
|
/* If we should start a snap back and no current deceleration
|
||
|
* is active, start the snap back.
|
||
|
*/
|
||
|
@@ -2130,9 +2173,10 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
|
||
|
|
||
|
if (new_overshoot_x != 0 || new_overshoot_y != 0)
|
||
|
{
|
||
|
- gtk_scrolled_window_start_deceleration (scrolled_window);
|
||
|
+ gtk_scrolled_window_start_snap_back (scrolled_window);
|
||
|
priv->x_velocity = 0.0;
|
||
|
priv->y_velocity = 0.0;
|
||
|
+ priv->last_scroll_event_time = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
@@ -2207,16 +2251,16 @@ _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
-scrolled_window_deceleration_cb (gpointer user_data)
|
||
|
+scrolled_window_snap_back_cb (gpointer user_data)
|
||
|
{
|
||
|
KineticScrollData *data = user_data;
|
||
|
GtkScrolledWindow *scrolled_window = data->scrolled_window;
|
||
|
GtkScrolledWindowPrivate *priv;
|
||
|
GtkAdjustment *hadjustment, *vadjustment;
|
||
|
gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
|
||
|
- gdouble value;
|
||
|
gint64 current_time;
|
||
|
- guint elapsed;
|
||
|
+ gdouble elapsed;
|
||
|
+ gdouble damp_factor;
|
||
|
|
||
|
priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
|
||
|
hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
|
||
|
@@ -2226,12 +2270,22 @@ scrolled_window_deceleration_cb (gpointer user_data)
|
||
|
&old_overshoot_x, &old_overshoot_y);
|
||
|
|
||
|
current_time = g_get_monotonic_time ();
|
||
|
- elapsed = (current_time - data->last_deceleration_time) / 1000;
|
||
|
- data->last_deceleration_time = current_time;
|
||
|
+ elapsed = (current_time - data->start_snap_back_time) / 1000000.0;
|
||
|
+ damp_factor = exp((((double)-elapsed) * BAND_STIFFNESS) / BAND_PERIOD);
|
||
|
|
||
|
if (hadjustment && scrolled_window->hscrollbar_visible)
|
||
|
{
|
||
|
- value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
|
||
|
+ gdouble delta_x, value;
|
||
|
+
|
||
|
+ delta_x = (data->x_overshoot + (data->x_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
|
||
|
+
|
||
|
+ if (fabs (delta_x) >= 1.0)
|
||
|
+ value = priv->unclamped_hadj_value + (delta_x - old_overshoot_x);
|
||
|
+ else
|
||
|
+ value = CLAMP (priv->unclamped_hadj_value,
|
||
|
+ gtk_adjustment_get_lower (hadjustment),
|
||
|
+ gtk_adjustment_get_upper (hadjustment) -
|
||
|
+ gtk_adjustment_get_page_size (hadjustment));
|
||
|
|
||
|
if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
|
||
|
hadjustment,
|
||
|
@@ -2243,7 +2297,17 @@ scrolled_window_deceleration_cb (gpointer user_data)
|
||
|
|
||
|
if (vadjustment && scrolled_window->vscrollbar_visible)
|
||
|
{
|
||
|
- value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
|
||
|
+ gdouble delta_y, value;
|
||
|
+
|
||
|
+ delta_y = (data->y_overshoot + (data->y_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
|
||
|
+
|
||
|
+ if (fabs (delta_y) >= 1.0)
|
||
|
+ value = priv->unclamped_vadj_value + (delta_y - old_overshoot_y);
|
||
|
+ else
|
||
|
+ value = CLAMP (priv->unclamped_vadj_value,
|
||
|
+ gtk_adjustment_get_lower (vadjustment),
|
||
|
+ gtk_adjustment_get_upper (vadjustment) -
|
||
|
+ gtk_adjustment_get_page_size (vadjustment));
|
||
|
|
||
|
if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
|
||
|
vadjustment,
|
||
|
@@ -2256,58 +2320,17 @@ scrolled_window_deceleration_cb (gpointer user_data)
|
||
|
_gtk_scrolled_window_get_overshoot (scrolled_window,
|
||
|
&overshoot_x, &overshoot_y);
|
||
|
|
||
|
- if (overshoot_x == 0)
|
||
|
- {
|
||
|
- if (old_overshoot_x != 0)
|
||
|
- {
|
||
|
- /* Overshooting finished snapping back */
|
||
|
- data->x_velocity = 0;
|
||
|
- }
|
||
|
- else if (data->x_velocity > 0)
|
||
|
- {
|
||
|
- data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
|
||
|
- data->x_velocity = MAX (0, data->x_velocity);
|
||
|
- }
|
||
|
- else if (data->x_velocity < 0)
|
||
|
- {
|
||
|
- data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
|
||
|
- data->x_velocity = MIN (0, data->x_velocity);
|
||
|
- }
|
||
|
- }
|
||
|
- else if (overshoot_x < 0)
|
||
|
- data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
|
||
|
- else if (overshoot_x > 0)
|
||
|
- data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
|
||
|
+ if (overshoot_x != 0)
|
||
|
+ priv->x_force = overshoot_x * BAND_STIFFNESS;
|
||
|
|
||
|
- if (overshoot_y == 0)
|
||
|
- {
|
||
|
- if (old_overshoot_y != 0)
|
||
|
- {
|
||
|
- /* Overshooting finished snapping back */
|
||
|
- data->y_velocity = 0;
|
||
|
- }
|
||
|
- else if (data->y_velocity > 0)
|
||
|
- {
|
||
|
- data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
|
||
|
- data->y_velocity = MAX (0, data->y_velocity);
|
||
|
- }
|
||
|
- else if (data->y_velocity < 0)
|
||
|
- {
|
||
|
- data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
|
||
|
- data->y_velocity = MIN (0, data->y_velocity);
|
||
|
- }
|
||
|
- }
|
||
|
- else if (overshoot_y < 0)
|
||
|
- data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
|
||
|
- else if (overshoot_y > 0)
|
||
|
- data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
|
||
|
+ if (overshoot_y != 0)
|
||
|
+ priv->y_force = overshoot_y * BAND_STIFFNESS;
|
||
|
|
||
|
if (old_overshoot_x != overshoot_x ||
|
||
|
old_overshoot_y != overshoot_y)
|
||
|
_gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
|
||
|
|
||
|
- if (overshoot_x != 0 || overshoot_y != 0 ||
|
||
|
- data->x_velocity != 0 || data->y_velocity != 0)
|
||
|
+ if (overshoot_x != 0 || overshoot_y != 0)
|
||
|
return TRUE;
|
||
|
else
|
||
|
{
|
||
|
@@ -2317,29 +2340,24 @@ scrolled_window_deceleration_cb (gpointer user_data)
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
-gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
|
||
|
+gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window)
|
||
|
{
|
||
|
GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
|
||
|
KineticScrollData *data;
|
||
|
- gdouble angle;
|
||
|
|
||
|
data = g_new0 (KineticScrollData, 1);
|
||
|
data->scrolled_window = scrolled_window;
|
||
|
- data->last_deceleration_time = g_get_monotonic_time ();
|
||
|
+ data->start_snap_back_time = g_get_monotonic_time ();
|
||
|
data->x_velocity = priv->x_velocity;
|
||
|
data->y_velocity = priv->y_velocity;
|
||
|
-
|
||
|
- /* We use sine/cosine as a factor to deceleration x/y components
|
||
|
- * of the vector, so we care about the sign later.
|
||
|
- */
|
||
|
- angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
|
||
|
- data->vel_cosine = cos (angle);
|
||
|
- data->vel_sine = sin (angle);
|
||
|
+ _gtk_scrolled_window_get_overshoot (scrolled_window,
|
||
|
+ &data->x_overshoot,
|
||
|
+ &data->y_overshoot);
|
||
|
|
||
|
priv->deceleration_id =
|
||
|
gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
|
||
|
FRAME_INTERVAL,
|
||
|
- scrolled_window_deceleration_cb,
|
||
|
+ scrolled_window_snap_back_cb,
|
||
|
data, (GDestroyNotify) g_free);
|
||
|
}
|
||
|
|
||
|
@@ -2360,13 +2378,14 @@ gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
|
||
|
gdouble delta_x, delta_y;
|
||
|
|
||
|
if (gdk_event_get_scroll_deltas (event, &delta_x, &delta_y) &&
|
||
|
+ priv->last_scroll_event_time > 0 &&
|
||
|
ABS (_time - priv->last_scroll_event_time) > STILL_THRESHOLD)
|
||
|
{
|
||
|
priv->x_velocity = delta_x / (gdouble) (_time - priv->last_scroll_event_time);
|
||
|
priv->y_velocity = delta_y / (gdouble) (_time - priv->last_scroll_event_time);
|
||
|
-
|
||
|
- priv->last_scroll_event_time = _time;
|
||
|
}
|
||
|
+
|
||
|
+ priv->last_scroll_event_time = _time;
|
||
|
}
|
||
|
|
||
|
#undef STILL_THRESHOLD
|
||
|
--
|
||
|
1.7.10.2 (Apple Git-33)
|