Transfer the Mac SDK bockbuild profiles & resources inside the Mono repository.
[mono.git] / bockbuild / mac-sdk / patches / gtk / 0023-Improve-overshooting-behavior.patch
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
5
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).
11
12 Also rename the timeout functions from deceleration to snap back, since
13 these are not handling kinetic deceleration in this code.
14 ---
15  gtk/gtkscrolledwindow.c |  179 ++++++++++++++++++++++++++---------------------
16  1 file changed, 99 insertions(+), 80 deletions(-)
17
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
22 @@ -84,6 +84,10 @@
23  #define OVERSHOOT_INVERSE_ACCELERATION 0.003
24  #define RELEASE_EVENT_TIMEOUT 1000
25
26 +#define BAND_STIFFNESS 20.0f
27 +#define BAND_AMPLITUDE 0.31f
28 +#define BAND_PERIOD 1.6f
29 +
30  /* Overlay scrollbars */
31  #define SCROLL_INTERVAL_INITIAL 300
32  #define SCROLL_INTERVAL_REPEAT 100
33 @@ -107,6 +111,9 @@ typedef struct {
34    gdouble                x_velocity;
35    gdouble                y_velocity;
36
37 +  gdouble                x_force;
38 +  gdouble                y_force;
39 +
40    gdouble                unclamped_hadj_value;
41    gdouble                unclamped_vadj_value;
42
43 @@ -143,12 +150,13 @@ typedef struct {
44  typedef struct
45  {
46    GtkScrolledWindow     *scrolled_window;
47 -  gint64                 last_deceleration_time;
48 +  gint64                 start_snap_back_time;
49
50    gdouble                x_velocity;
51    gdouble                y_velocity;
52 -  gdouble                vel_cosine;
53 -  gdouble                vel_sine;
54 +
55 +  gint                   x_overshoot;
56 +  gint                   y_overshoot;
57  } KineticScrollData;
58
59  enum {
60 @@ -260,7 +268,7 @@ static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *setting
61                                                              gpointer     user_data);
62
63
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,
67                                                           GdkEvent          *event);
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;
72
73 +      if (is_momentum_event && !is_overshot)
74 +        gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
75 +
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)))
82              {
83 +              gboolean may_overshoot = FALSE;
84                GtkAdjustment *adj;
85
86 +              /* Overshooting is allowed once the adjustment has reached
87 +               * the left/right.
88 +               */
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;
94
95 -              if (scrolled_window->hscrollbar_visible)
96 +              if (scrolled_window->hscrollbar_visible && (is_overshot || may_overshoot))
97                  {
98 +                  gdouble damped_delta;
99 +
100 +                  priv->x_force += delta_x;
101 +                  damped_delta = ceil(priv->x_force / BAND_STIFFNESS);
102 +                  damped_delta = damped_delta - old_overshoot_x;
103 +
104                    _gtk_scrolled_window_set_adjustment_value (scrolled_window,
105                                                               adj,
106 -                                                             priv->unclamped_hadj_value + delta_x,
107 +                                                             priv->unclamped_hadj_value + damped_delta,
108                                                               TRUE,
109                                                               FALSE);
110                  }
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)))
114              {
115 +              gboolean may_overshoot = FALSE;
116                GtkAdjustment *adj;
117
118 +              /* Overshooting is allowed once the adjustment has reached
119 +               * the top/bottom.
120 +               */
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;
126
127 -              if (scrolled_window->vscrollbar_visible)
128 +              if (scrolled_window->vscrollbar_visible && (is_overshot || may_overshoot))
129                  {
130 +                  gdouble damped_delta;
131 +
132 +                  priv->y_force += delta_y;
133 +                  damped_delta = ceil(priv->y_force / BAND_STIFFNESS);
134 +                  damped_delta = damped_delta - old_overshoot_y;
135 +
136                    _gtk_scrolled_window_set_adjustment_value (scrolled_window,
137                                                               adj,
138 -                                                             priv->unclamped_vadj_value + delta_y,
139 +                                                             priv->unclamped_vadj_value + damped_delta,
140                                                               TRUE,
141                                                               FALSE);
142                  }
143 @@ -2093,10 +2132,6 @@ gtk_scrolled_window_scroll_event (GtkWidget      *widget,
144
145                handled = TRUE;
146              }
147 -
148 -
149 -          priv->last_scroll_event_time = gdk_event_get_time ((GdkEvent *)event);
150 -          gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
151          }
152
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.
156         */
157        if (is_overshot &&
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;
162
163 +       /* Reset force if gesture has ended. */
164 +       if (event->phase == GDK_EVENT_SCROLL_PHASE_END)
165 +         {
166 +           priv->x_force = 0.0;
167 +           priv->y_force = 0.0;
168 +         }
169 +
170        /* If we should start a snap back and no current deceleration
171         * is active, start the snap back.
172         */
173 @@ -2130,9 +2173,10 @@ gtk_scrolled_window_scroll_event (GtkWidget      *widget,
174
175            if (new_overshoot_x != 0 || new_overshoot_y != 0)
176              {
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;
182              }
183          }
184      }
185 @@ -2207,16 +2251,16 @@ _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
186  }
187
188  static gboolean
189 -scrolled_window_deceleration_cb (gpointer user_data)
190 +scrolled_window_snap_back_cb (gpointer user_data)
191  {
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;
197 -  gdouble value;
198    gint64 current_time;
199 -  guint elapsed;
200 +  gdouble elapsed;
201 +  gdouble damp_factor;
202
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);
207
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);
213
214    if (hadjustment && scrolled_window->hscrollbar_visible)
215      {
216 -      value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
217 +      gdouble delta_x, value;
218 +
219 +      delta_x = (data->x_overshoot + (data->x_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
220 +
221 +      if (fabs (delta_x) >= 1.0)
222 +        value = priv->unclamped_hadj_value + (delta_x - old_overshoot_x);
223 +      else
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));
228
229        if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
230                                                       hadjustment,
231 @@ -2243,7 +2297,17 @@ scrolled_window_deceleration_cb (gpointer user_data)
232
233    if (vadjustment && scrolled_window->vscrollbar_visible)
234      {
235 -      value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
236 +      gdouble delta_y, value;
237 +
238 +      delta_y = (data->y_overshoot + (data->y_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
239 +
240 +      if (fabs (delta_y) >= 1.0)
241 +        value = priv->unclamped_vadj_value + (delta_y - old_overshoot_y);
242 +      else
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));
247
248        if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
249                                                       vadjustment,
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);
253
254 -  if (overshoot_x == 0)
255 -    {
256 -      if (old_overshoot_x != 0)
257 -        {
258 -          /* Overshooting finished snapping back */
259 -          data->x_velocity = 0;
260 -        }
261 -      else if (data->x_velocity > 0)
262 -        {
263 -          data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
264 -          data->x_velocity = MAX (0, data->x_velocity);
265 -        }
266 -      else if (data->x_velocity < 0)
267 -        {
268 -          data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
269 -          data->x_velocity = MIN (0, data->x_velocity);
270 -        }
271 -    }
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;
278
279 -  if (overshoot_y == 0)
280 -    {
281 -      if (old_overshoot_y != 0)
282 -        {
283 -          /* Overshooting finished snapping back */
284 -          data->y_velocity = 0;
285 -        }
286 -      else if (data->y_velocity > 0)
287 -        {
288 -          data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
289 -          data->y_velocity = MAX (0, data->y_velocity);
290 -        }
291 -      else if (data->y_velocity < 0)
292 -        {
293 -          data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
294 -          data->y_velocity = MIN (0, data->y_velocity);
295 -        }
296 -    }
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;
303
304    if (old_overshoot_x != overshoot_x ||
305        old_overshoot_y != overshoot_y)
306      _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
307
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)
311      return TRUE;
312    else
313      {
314 @@ -2317,29 +2340,24 @@ scrolled_window_deceleration_cb (gpointer user_data)
315  }
316
317  static void
318 -gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
319 +gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window)
320  {
321    GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
322    KineticScrollData *data;
323 -  gdouble angle;
324
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;
331 -
332 -  /* We use sine/cosine as a factor to deceleration x/y components
333 -   * of the vector, so we care about the sign later.
334 -   */
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);
341
342    priv->deceleration_id =
343      gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
344                                    FRAME_INTERVAL,
345 -                                  scrolled_window_deceleration_cb,
346 +                                  scrolled_window_snap_back_cb,
347                                    data, (GDestroyNotify) g_free);
348  }
349
350 @@ -2360,13 +2378,14 @@ gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
351        gdouble delta_x, delta_y;
352
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)
356          {
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);
359 -
360 -          priv->last_scroll_event_time = _time;
361          }
362 +
363 +      priv->last_scroll_event_time = _time;
364      }
365
366  #undef STILL_THRESHOLD
367 --
368 1.7.10.2 (Apple Git-33)