1 From ced3c2c63872021885cf91f134dde818096bae42 Mon Sep 17 00:00:00 2001
2 From: Kristian Rietveld <kris@lanedo.com>
3 Date: Wed, 19 Sep 2012 07:22:39 +0200
4 Subject: [PATCH 23/68] Improve overshooting behavior
6 This is done by taking the stiffness calculations as found in the WebKit
7 source code. These calculations are now used to compute the overshooting
8 distance as well as handling the snap back animation. Also, we only
9 start overshooting after we have hit a border in the container (i.e. an
10 adjustment has reached its upper/lower bound).
12 Also rename the timeout functions from deceleration to snap back, since
13 these are not handling kinetic deceleration in this code.
15 gtk/gtkscrolledwindow.c | 179 ++++++++++++++++++++++++++---------------------
16 1 file changed, 99 insertions(+), 80 deletions(-)
18 diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
19 index 92e75a0..f3be303 100644
20 --- a/gtk/gtkscrolledwindow.c
21 +++ b/gtk/gtkscrolledwindow.c
23 #define OVERSHOOT_INVERSE_ACCELERATION 0.003
24 #define RELEASE_EVENT_TIMEOUT 1000
26 +#define BAND_STIFFNESS 20.0f
27 +#define BAND_AMPLITUDE 0.31f
28 +#define BAND_PERIOD 1.6f
30 /* Overlay scrollbars */
31 #define SCROLL_INTERVAL_INITIAL 300
32 #define SCROLL_INTERVAL_REPEAT 100
33 @@ -107,6 +111,9 @@ typedef struct {
40 gdouble unclamped_hadj_value;
41 gdouble unclamped_vadj_value;
43 @@ -143,12 +150,13 @@ typedef struct {
46 GtkScrolledWindow *scrolled_window;
47 - gint64 last_deceleration_time;
48 + gint64 start_snap_back_time;
60 @@ -260,7 +268,7 @@ static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *setting
64 -static void gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window);
65 +static void gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window);
66 static gboolean gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
68 static void gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *window);
69 @@ -2016,6 +2024,9 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
70 if (event->phase == GDK_EVENT_SCROLL_PHASE_START)
71 priv->is_snapping_back = FALSE;
73 + if (is_momentum_event && !is_overshot)
74 + gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
76 /* Scroll events are handled in two cases:
77 * 1) We are not overshot and not snapping back, so scroll as
78 * usual and also handle momentum events.
79 @@ -2031,15 +2042,29 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
80 if (delta_x != 0.0 && scrolled_window->hscrollbar &&
81 (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
83 + gboolean may_overshoot = FALSE;
86 + /* Overshooting is allowed once the adjustment has reached
89 adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
90 + gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
91 + if (gtk_adjustment_get_value (adj) < 1.0 ||
92 + gtk_adjustment_get_value (adj) > max_adj - 1.0)
93 + may_overshoot = TRUE;
95 - if (scrolled_window->hscrollbar_visible)
96 + if (scrolled_window->hscrollbar_visible && (is_overshot || may_overshoot))
98 + gdouble damped_delta;
100 + priv->x_force += delta_x;
101 + damped_delta = ceil(priv->x_force / BAND_STIFFNESS);
102 + damped_delta = damped_delta - old_overshoot_x;
104 _gtk_scrolled_window_set_adjustment_value (scrolled_window,
106 - priv->unclamped_hadj_value + delta_x,
107 + priv->unclamped_hadj_value + damped_delta,
111 @@ -2064,15 +2089,29 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
112 if (delta_y != 0.0 && scrolled_window->vscrollbar &&
113 (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
115 + gboolean may_overshoot = FALSE;
118 + /* Overshooting is allowed once the adjustment has reached
121 adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
122 + gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
123 + if (gtk_adjustment_get_value (adj) < 1.0 ||
124 + gtk_adjustment_get_value (adj) > max_adj - 1.0)
125 + may_overshoot = TRUE;
127 - if (scrolled_window->vscrollbar_visible)
128 + if (scrolled_window->vscrollbar_visible && (is_overshot || may_overshoot))
130 + gdouble damped_delta;
132 + priv->y_force += delta_y;
133 + damped_delta = ceil(priv->y_force / BAND_STIFFNESS);
134 + damped_delta = damped_delta - old_overshoot_y;
136 _gtk_scrolled_window_set_adjustment_value (scrolled_window,
138 - priv->unclamped_vadj_value + delta_y,
139 + priv->unclamped_vadj_value + damped_delta,
143 @@ -2093,10 +2132,6 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
149 - priv->last_scroll_event_time = gdk_event_get_time ((GdkEvent *)event);
150 - gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
153 _gtk_scrolled_window_get_overshoot (scrolled_window,
154 @@ -2112,9 +2147,17 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
155 * also signals that the user's gesture has ended.
158 - (event->phase == GDK_EVENT_SCROLL_PHASE_END || is_momentum_event))
159 + ((priv->last_scroll_event_time > 0 && is_momentum_event) ||
160 + event->phase == GDK_EVENT_SCROLL_PHASE_END))
161 start_snap_back = TRUE;
163 + /* Reset force if gesture has ended. */
164 + if (event->phase == GDK_EVENT_SCROLL_PHASE_END)
166 + priv->x_force = 0.0;
167 + priv->y_force = 0.0;
170 /* If we should start a snap back and no current deceleration
171 * is active, start the snap back.
173 @@ -2130,9 +2173,10 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
175 if (new_overshoot_x != 0 || new_overshoot_y != 0)
177 - gtk_scrolled_window_start_deceleration (scrolled_window);
178 + gtk_scrolled_window_start_snap_back (scrolled_window);
179 priv->x_velocity = 0.0;
180 priv->y_velocity = 0.0;
181 + priv->last_scroll_event_time = 0;
185 @@ -2207,16 +2251,16 @@ _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
189 -scrolled_window_deceleration_cb (gpointer user_data)
190 +scrolled_window_snap_back_cb (gpointer user_data)
192 KineticScrollData *data = user_data;
193 GtkScrolledWindow *scrolled_window = data->scrolled_window;
194 GtkScrolledWindowPrivate *priv;
195 GtkAdjustment *hadjustment, *vadjustment;
196 gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
201 + gdouble damp_factor;
203 priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
204 hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
205 @@ -2226,12 +2270,22 @@ scrolled_window_deceleration_cb (gpointer user_data)
206 &old_overshoot_x, &old_overshoot_y);
208 current_time = g_get_monotonic_time ();
209 - elapsed = (current_time - data->last_deceleration_time) / 1000;
210 - data->last_deceleration_time = current_time;
211 + elapsed = (current_time - data->start_snap_back_time) / 1000000.0;
212 + damp_factor = exp((((double)-elapsed) * BAND_STIFFNESS) / BAND_PERIOD);
214 if (hadjustment && scrolled_window->hscrollbar_visible)
216 - value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
217 + gdouble delta_x, value;
219 + delta_x = (data->x_overshoot + (data->x_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
221 + if (fabs (delta_x) >= 1.0)
222 + value = priv->unclamped_hadj_value + (delta_x - old_overshoot_x);
224 + value = CLAMP (priv->unclamped_hadj_value,
225 + gtk_adjustment_get_lower (hadjustment),
226 + gtk_adjustment_get_upper (hadjustment) -
227 + gtk_adjustment_get_page_size (hadjustment));
229 if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
231 @@ -2243,7 +2297,17 @@ scrolled_window_deceleration_cb (gpointer user_data)
233 if (vadjustment && scrolled_window->vscrollbar_visible)
235 - value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
236 + gdouble delta_y, value;
238 + delta_y = (data->y_overshoot + (data->y_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
240 + if (fabs (delta_y) >= 1.0)
241 + value = priv->unclamped_vadj_value + (delta_y - old_overshoot_y);
243 + value = CLAMP (priv->unclamped_vadj_value,
244 + gtk_adjustment_get_lower (vadjustment),
245 + gtk_adjustment_get_upper (vadjustment) -
246 + gtk_adjustment_get_page_size (vadjustment));
248 if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
250 @@ -2256,58 +2320,17 @@ scrolled_window_deceleration_cb (gpointer user_data)
251 _gtk_scrolled_window_get_overshoot (scrolled_window,
252 &overshoot_x, &overshoot_y);
254 - if (overshoot_x == 0)
256 - if (old_overshoot_x != 0)
258 - /* Overshooting finished snapping back */
259 - data->x_velocity = 0;
261 - else if (data->x_velocity > 0)
263 - data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
264 - data->x_velocity = MAX (0, data->x_velocity);
266 - else if (data->x_velocity < 0)
268 - data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
269 - data->x_velocity = MIN (0, data->x_velocity);
272 - else if (overshoot_x < 0)
273 - data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
274 - else if (overshoot_x > 0)
275 - data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
276 + if (overshoot_x != 0)
277 + priv->x_force = overshoot_x * BAND_STIFFNESS;
279 - if (overshoot_y == 0)
281 - if (old_overshoot_y != 0)
283 - /* Overshooting finished snapping back */
284 - data->y_velocity = 0;
286 - else if (data->y_velocity > 0)
288 - data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
289 - data->y_velocity = MAX (0, data->y_velocity);
291 - else if (data->y_velocity < 0)
293 - data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
294 - data->y_velocity = MIN (0, data->y_velocity);
297 - else if (overshoot_y < 0)
298 - data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
299 - else if (overshoot_y > 0)
300 - data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
301 + if (overshoot_y != 0)
302 + priv->y_force = overshoot_y * BAND_STIFFNESS;
304 if (old_overshoot_x != overshoot_x ||
305 old_overshoot_y != overshoot_y)
306 _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
308 - if (overshoot_x != 0 || overshoot_y != 0 ||
309 - data->x_velocity != 0 || data->y_velocity != 0)
310 + if (overshoot_x != 0 || overshoot_y != 0)
314 @@ -2317,29 +2340,24 @@ scrolled_window_deceleration_cb (gpointer user_data)
318 -gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
319 +gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window)
321 GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
322 KineticScrollData *data;
325 data = g_new0 (KineticScrollData, 1);
326 data->scrolled_window = scrolled_window;
327 - data->last_deceleration_time = g_get_monotonic_time ();
328 + data->start_snap_back_time = g_get_monotonic_time ();
329 data->x_velocity = priv->x_velocity;
330 data->y_velocity = priv->y_velocity;
332 - /* We use sine/cosine as a factor to deceleration x/y components
333 - * of the vector, so we care about the sign later.
335 - angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
336 - data->vel_cosine = cos (angle);
337 - data->vel_sine = sin (angle);
338 + _gtk_scrolled_window_get_overshoot (scrolled_window,
339 + &data->x_overshoot,
340 + &data->y_overshoot);
342 priv->deceleration_id =
343 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
345 - scrolled_window_deceleration_cb,
346 + scrolled_window_snap_back_cb,
347 data, (GDestroyNotify) g_free);
350 @@ -2360,13 +2378,14 @@ gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
351 gdouble delta_x, delta_y;
353 if (gdk_event_get_scroll_deltas (event, &delta_x, &delta_y) &&
354 + priv->last_scroll_event_time > 0 &&
355 ABS (_time - priv->last_scroll_event_time) > STILL_THRESHOLD)
357 priv->x_velocity = delta_x / (gdouble) (_time - priv->last_scroll_event_time);
358 priv->y_velocity = delta_y / (gdouble) (_time - priv->last_scroll_event_time);
360 - priv->last_scroll_event_time = _time;
363 + priv->last_scroll_event_time = _time;
366 #undef STILL_THRESHOLD
368 1.7.10.2 (Apple Git-33)