1 From 5001b3d9a9b6445ac199156c0f91f28c2112e262 Mon Sep 17 00:00:00 2001
2 From: Carlos Garcia Campos <cgarcia@igalia.com>
3 Date: Fri, 11 Feb 2011 13:43:56 +0100
4 Subject: [PATCH 11/68] scrolledwindow: Kinetic scrolling support
6 If the scrolling doesn't start after a long press, the scrolling is
7 cancelled and events are handled by child widgets normally.
9 When clicked again close to the previous button press location
10 (assuming it had ~0 movement), the scrolled window will allow
11 the child to handle the events immediately.
13 This is so the user doesn't have to wait to the press-and-hold
14 timeout in order to operate on the scrolledwindow child.
16 The innermost scrolled window always gets to capture the events, all
17 scrolled windows above it just let the event go through. Ideally
18 reaching a limit on the innermost scrolled window would propagate
19 the dragging up the hierarchy in order to keep following the touch
20 coords, although that'd involve rather evil hacks just to cater
25 docs/reference/gtk/gtk3-sections.txt
27 gtk/gtkscrolledwindow.c
28 gtk/gtkscrolledwindow.h
31 gtk/gtkscrolledwindow.c | 1061 ++++++++++++++++++++++++++++++++++++++++++++++-
32 gtk/gtkscrolledwindow.h | 8 +
33 3 files changed, 1058 insertions(+), 15 deletions(-)
35 diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
36 index 6d5d5b6..d13ca41 100644
39 @@ -3730,6 +3730,8 @@ gtk_scrollbar_get_type G_GNUC_CONST
40 gtk_scrolled_window_add_with_viewport
41 gtk_scrolled_window_get_hadjustment
42 gtk_scrolled_window_get_hscrollbar
43 +gtk_scrolled_window_get_kinetic_scrolling
44 +gtk_scrolled_window_get_capture_button_press
45 gtk_scrolled_window_get_placement
46 gtk_scrolled_window_get_policy
47 gtk_scrolled_window_get_shadow_type
48 @@ -3738,6 +3740,8 @@ gtk_scrolled_window_get_vadjustment
49 gtk_scrolled_window_get_vscrollbar
50 gtk_scrolled_window_new
51 gtk_scrolled_window_set_hadjustment
52 +gtk_scrolled_window_set_kinetic_scrolling
53 +gtk_scrolled_window_set_capture_button_press
54 gtk_scrolled_window_set_placement
55 gtk_scrolled_window_set_policy
56 gtk_scrolled_window_set_shadow_type
57 diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
58 index 2288b2f..694d20a 100644
59 --- a/gtk/gtkscrolledwindow.c
60 +++ b/gtk/gtkscrolledwindow.c
62 #include "gtkwindow.h"
63 #include "gtkprivate.h"
73 #define DEFAULT_SCROLLBAR_SPACING 3
74 +#define TOUCH_BYPASS_CAPTURED_THRESHOLD 30
76 +/* Kinetic scrolling */
77 +#define FRAME_INTERVAL (1000 / 60)
78 +#define MAX_OVERSHOOT_DISTANCE 50
79 +#define FRICTION_DECELERATION 0.003
80 +#define OVERSHOOT_INVERSE_ACCELERATION 0.003
81 +#define RELEASE_EVENT_TIMEOUT 1000
84 - gboolean window_placement_set;
85 - GtkCornerType real_window_placement;
86 + gboolean window_placement_set;
87 + GtkCornerType real_window_placement;
89 + /* Kinetic scrolling */
90 + GdkEvent *button_press_event;
91 + GdkWindow *overshoot_window;
92 + guint pointer_grabbed : 1;
93 + guint kinetic_scrolling : 1;
94 + guint capture_button_press : 1;
96 + guint last_button_event_valid : 1;
98 + guint release_timeout_id;
99 + guint deceleration_id;
101 + gdouble last_button_event_x_root;
102 + gdouble last_button_event_y_root;
104 + gdouble last_motion_event_x_root;
105 + gdouble last_motion_event_y_root;
106 + guint32 last_motion_event_time;
108 + gdouble x_velocity;
109 + gdouble y_velocity;
111 + gdouble unclamped_hadj_value;
112 + gdouble unclamped_vadj_value;
113 } GtkScrolledWindowPrivate;
115 #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
119 + GtkScrolledWindow *scrolled_window;
120 + gint64 last_deceleration_time;
122 + gdouble x_velocity;
123 + gdouble y_velocity;
124 + gdouble vel_cosine;
126 +} KineticScrollData;
131 @@ -88,7 +134,8 @@ enum {
132 PROP_VSCROLLBAR_POLICY,
133 PROP_WINDOW_PLACEMENT,
134 PROP_WINDOW_PLACEMENT_SET,
137 + PROP_KINETIC_SCROLLING
141 @@ -119,6 +166,8 @@ static void gtk_scrolled_window_size_allocate (GtkWidget *widge
142 GtkAllocation *allocation);
143 static gboolean gtk_scrolled_window_scroll_event (GtkWidget *widget,
144 GdkEventScroll *event);
145 +static gboolean gtk_scrolled_window_captured_event (GtkWidget *widget,
147 static gboolean gtk_scrolled_window_focus (GtkWidget *widget,
148 GtkDirectionType direction);
149 static void gtk_scrolled_window_add (GtkContainer *container,
150 @@ -139,9 +188,24 @@ static void gtk_scrolled_window_relative_allocation(GtkWidget *widge
151 GtkAllocation *allocation);
152 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
154 +static void gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
157 static void gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window);
159 +static void gtk_scrolled_window_realize (GtkWidget *widget);
160 +static void gtk_scrolled_window_unrealize (GtkWidget *widget);
161 +static void gtk_scrolled_window_map (GtkWidget *widget);
162 +static void gtk_scrolled_window_unmap (GtkWidget *widget);
163 +static void gtk_scrolled_window_grab_notify (GtkWidget *widget,
164 + gboolean was_grabbed);
166 +static gboolean _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
167 + GtkAdjustment *adjustment,
169 + gboolean allow_overshooting,
170 + gboolean snap_to_border);
172 static guint signals[LAST_SIGNAL] = {0};
174 G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
175 @@ -202,6 +266,11 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
176 widget_class->size_allocate = gtk_scrolled_window_size_allocate;
177 widget_class->scroll_event = gtk_scrolled_window_scroll_event;
178 widget_class->focus = gtk_scrolled_window_focus;
179 + widget_class->realize = gtk_scrolled_window_realize;
180 + widget_class->unrealize = gtk_scrolled_window_unrealize;
181 + widget_class->map = gtk_scrolled_window_map;
182 + widget_class->unmap = gtk_scrolled_window_unmap;
183 + widget_class->grab_notify = gtk_scrolled_window_grab_notify;
185 container_class->add = gtk_scrolled_window_add;
186 container_class->remove = gtk_scrolled_window_remove;
187 @@ -301,6 +370,22 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
188 GTK_PARAM_READABLE));
191 + * GtkScrolledWindow:kinetic-scrolling:
193 + * The kinetic scrolling behavior flags.
197 + g_object_class_install_property (gobject_class,
198 + PROP_KINETIC_SCROLLING,
199 + g_param_spec_boolean ("kinetic-scrolling",
200 + P_("Kinetic Scrolling"),
201 + P_("Kinetic scrolling mode."),
203 + GTK_PARAM_READABLE |
204 + GTK_PARAM_WRITABLE));
207 * GtkScrolledWindow::scroll-child:
208 * @scrolled_window: a #GtkScrolledWindow
209 * @scroll: a #GtkScrollType describing how much to scroll
210 @@ -371,6 +456,12 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
211 scrolled_window->focus_out = FALSE;
212 scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
213 gtk_scrolled_window_update_real_placement (scrolled_window);
215 + if (g_getenv ("GTK2_KINETIC_SCROLLING"))
217 + gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
218 + gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
223 @@ -458,8 +549,13 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
225 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
227 + g_signal_connect (hadjustment,
229 + G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
231 gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
233 + gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
236 gtk_widget_set_scroll_adjustments (bin->child,
237 gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
238 @@ -519,7 +615,12 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
240 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
242 + g_signal_connect (vadjustment,
244 + G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
246 gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
247 + gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
250 gtk_widget_set_scroll_adjustments (bin->child,
251 @@ -842,10 +943,135 @@ gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
252 return scrolled_window->shadow_type;
256 + * gtk_scrolled_window_set_kinetic_scrolling:
257 + * @scrolled_window: a #GtkScrolledWindow
258 + * @kinetic_scrolling: %TRUE to enable kinetic scrolling
260 + * Turns kinetic scrolling on or off.
261 + * Kinetic scrolling only applies to devices with source
262 + * %GDK_SOURCE_TOUCHSCREEN.
267 +gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
268 + gboolean kinetic_scrolling)
270 + GtkScrolledWindowPrivate *priv;
272 + g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
274 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
276 + if (priv->kinetic_scrolling == kinetic_scrolling)
279 + priv->kinetic_scrolling = kinetic_scrolling;
280 + if (priv->kinetic_scrolling)
282 + _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window),
283 + gtk_scrolled_window_captured_event);
287 + _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), NULL);
288 + if (priv->release_timeout_id)
290 + g_source_remove (priv->release_timeout_id);
291 + priv->release_timeout_id = 0;
293 + if (priv->deceleration_id)
295 + g_source_remove (priv->deceleration_id);
296 + priv->deceleration_id = 0;
299 + g_object_notify (G_OBJECT (scrolled_window), "kinetic-scrolling");
303 + * gtk_scrolled_window_get_kinetic_scrolling:
304 + * @scrolled_window: a #GtkScrolledWindow
306 + * Returns the specified kinetic scrolling behavior.
308 + * Return value: the scrolling behavior flags.
313 +gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
315 + GtkScrolledWindowPrivate *priv;
317 + g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
319 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
321 + return priv->kinetic_scrolling;
325 + * gtk_scrolled_window_set_capture_button_press:
326 + * @scrolled_window: a #GtkScrolledWindow
327 + * @capture_button_press: %TRUE to capture button presses
329 + * Changes the behaviour of @scrolled_window wrt. to the initial
330 + * event that possibly starts kinetic scrolling. When @capture_button_press
331 + * is set to %TRUE, the event is captured by the scrolled window, and
332 + * then later replayed if it is meant to go to the child widget.
334 + * This should be enabled if any child widgets perform non-reversible
335 + * actions on #GtkWidget::button-press-event. If they don't, and handle
336 + * additionally handle #GtkWidget::grab-broken-event, it might be better
337 + * to set @capture_button_press to %FALSE.
339 + * This setting only has an effect if kinetic scrolling is enabled.
344 +gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
345 + gboolean capture_button_press)
347 + GtkScrolledWindowPrivate *priv;
349 + g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
351 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
352 + priv->capture_button_press = capture_button_press;
356 + * gtk_scrolled_window_get_capture_button_press:
357 + * @scrolled_window: a #GtkScrolledWindow
359 + * Return whether button presses are captured during kinetic
360 + * scrolling. See gtk_scrolled_window_set_capture_button_press().
362 + * Returns: %TRUE if button presses are captured during kinetic scrolling
367 +gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window)
369 + GtkScrolledWindowPrivate *priv;
371 + g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
373 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
375 + return priv->capture_button_press;
380 gtk_scrolled_window_destroy (GtkObject *object)
382 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
383 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
385 if (scrolled_window->hscrollbar)
387 @@ -868,6 +1094,23 @@ gtk_scrolled_window_destroy (GtkObject *object)
388 scrolled_window->vscrollbar = NULL;
391 + if (priv->release_timeout_id)
393 + g_source_remove (priv->release_timeout_id);
394 + priv->release_timeout_id = 0;
396 + if (priv->deceleration_id)
398 + g_source_remove (priv->deceleration_id);
399 + priv->deceleration_id = 0;
402 + if (priv->button_press_event)
404 + gdk_event_free (priv->button_press_event);
405 + priv->button_press_event = NULL;
408 GTK_OBJECT_CLASS (gtk_scrolled_window_parent_class)->destroy (object);
411 @@ -912,6 +1155,10 @@ gtk_scrolled_window_set_property (GObject *object,
412 gtk_scrolled_window_set_shadow_type (scrolled_window,
413 g_value_get_enum (value));
415 + case PROP_KINETIC_SCROLLING:
416 + gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
417 + g_value_get_boolean (value));
420 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
422 @@ -952,6 +1199,9 @@ gtk_scrolled_window_get_property (GObject *object,
423 case PROP_SHADOW_TYPE:
424 g_value_set_enum (value, scrolled_window->shadow_type);
426 + case PROP_KINETIC_SCROLLING:
427 + g_value_set_boolean (value, priv->kinetic_scrolling);
430 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
432 @@ -1019,10 +1269,10 @@ gtk_scrolled_window_paint (GtkWidget *widget,
435 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
436 + GtkAllocation relative_allocation;
438 if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
440 - GtkAllocation relative_allocation;
441 gboolean scrollbars_within_bevel;
443 gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
444 @@ -1378,6 +1628,111 @@ gtk_scrolled_window_relative_allocation (GtkWidget *widget,
449 +_gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
453 + GtkScrolledWindowPrivate *priv;
454 + GtkAdjustment *vadjustment, *hadjustment;
455 + gdouble lower, upper, x, y;
457 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
459 + /* Vertical overshoot */
460 + vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
461 + lower = gtk_adjustment_get_lower (vadjustment);
462 + upper = gtk_adjustment_get_upper (vadjustment) -
463 + gtk_adjustment_get_page_size (vadjustment);
465 + if (priv->unclamped_vadj_value < lower)
466 + y = priv->unclamped_vadj_value - lower;
467 + else if (priv->unclamped_vadj_value > upper)
468 + y = priv->unclamped_vadj_value - upper;
472 + /* Horizontal overshoot */
473 + hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
474 + lower = gtk_adjustment_get_lower (hadjustment);
475 + upper = gtk_adjustment_get_upper (hadjustment) -
476 + gtk_adjustment_get_page_size (hadjustment);
478 + if (priv->unclamped_hadj_value < lower)
479 + x = priv->unclamped_hadj_value - lower;
480 + else if (priv->unclamped_hadj_value > upper)
481 + x = priv->unclamped_hadj_value - upper;
491 + return (x != 0 || y != 0);
495 +_gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_window)
497 + GtkAllocation window_allocation, relative_allocation, allocation;
498 + GtkScrolledWindowPrivate *priv;
499 + GtkWidget *widget = GTK_WIDGET (scrolled_window);
500 + gint overshoot_x, overshoot_y;
502 + if (!gtk_widget_get_realized (widget))
505 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
507 + gtk_widget_get_allocation (widget, &allocation);
508 + gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
509 + _gtk_scrolled_window_get_overshoot (scrolled_window,
510 + &overshoot_x, &overshoot_y);
512 + window_allocation = relative_allocation;
513 + window_allocation.x += allocation.x;
514 + window_allocation.y += allocation.y;
516 + if (overshoot_x < 0)
517 + window_allocation.x += -overshoot_x;
519 + if (overshoot_y < 0)
520 + window_allocation.y += -overshoot_y;
522 + window_allocation.width -= ABS (overshoot_x);
523 + window_allocation.height -= ABS (overshoot_y);
525 + gdk_window_move_resize (priv->overshoot_window,
526 + window_allocation.x, window_allocation.y,
527 + window_allocation.width, window_allocation.height);
531 +gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
532 + GtkAllocation *relative_allocation)
534 + GtkWidget *widget = GTK_WIDGET (swindow), *child;
535 + GtkAllocation allocation;
536 + GtkAllocation child_allocation;
537 + gint overshoot_x, overshoot_y;
539 + child = gtk_bin_get_child (GTK_BIN (widget));
541 + gtk_widget_get_allocation (widget, &allocation);
543 + gtk_scrolled_window_relative_allocation (widget, relative_allocation);
544 + _gtk_scrolled_window_get_overshoot (swindow, &overshoot_x, &overshoot_y);
546 + child_allocation.x = child_allocation.y = 0;
547 + child_allocation.width = relative_allocation->width;
548 + child_allocation.height = relative_allocation->height;
550 + gtk_widget_size_allocate (child, &child_allocation);
554 gtk_scrolled_window_size_allocate (GtkWidget *widget,
555 GtkAllocation *allocation)
556 @@ -1389,7 +1744,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
557 GtkAllocation child_allocation;
558 gboolean scrollbars_within_bevel;
559 gint scrollbar_spacing;
562 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
563 g_return_if_fail (allocation != NULL);
565 @@ -1420,17 +1775,9 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
569 - gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
571 - child_allocation.x = relative_allocation.x + allocation->x;
572 - child_allocation.y = relative_allocation.y + allocation->y;
573 - child_allocation.width = relative_allocation.width;
574 - child_allocation.height = relative_allocation.height;
576 previous_hvis = scrolled_window->hscrollbar_visible;
577 previous_vvis = scrolled_window->vscrollbar_visible;
579 - gtk_widget_size_allocate (bin->child, &child_allocation);
580 + gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
582 /* If, after the first iteration, the hscrollbar and the
583 * vscrollbar flip visiblity, then we need both.
584 @@ -1442,6 +1789,8 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
585 scrolled_window->hscrollbar_visible = TRUE;
586 scrolled_window->vscrollbar_visible = TRUE;
588 + gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
590 /* a new resize is already queued at this point,
591 * so we will immediatedly get reinvoked
593 @@ -1559,6 +1908,8 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
595 else if (gtk_widget_get_visible (scrolled_window->vscrollbar))
596 gtk_widget_hide (scrolled_window->vscrollbar);
598 + _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
602 @@ -1639,6 +1990,555 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
606 +_gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
607 + GtkAdjustment *adjustment,
609 + gboolean allow_overshooting,
610 + gboolean snap_to_border)
612 + GtkScrolledWindowPrivate *priv;
613 + gdouble lower, upper, *prev_value;
615 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
617 + lower = gtk_adjustment_get_lower (adjustment);
618 + upper = gtk_adjustment_get_upper (adjustment) -
619 + gtk_adjustment_get_page_size (adjustment);
621 + if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)))
622 + prev_value = &priv->unclamped_hadj_value;
623 + else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)))
624 + prev_value = &priv->unclamped_vadj_value;
628 + if (snap_to_border)
630 + if (*prev_value < 0 && value > 0)
632 + else if (*prev_value > upper && value < upper)
636 + if (allow_overshooting)
638 + lower -= MAX_OVERSHOOT_DISTANCE;
639 + upper += MAX_OVERSHOOT_DISTANCE;
642 + *prev_value = CLAMP (value, lower, upper);
643 + gtk_adjustment_set_value (adjustment, *prev_value);
645 + return (*prev_value != value);
649 +scrolled_window_deceleration_cb (gpointer user_data)
651 + KineticScrollData *data = user_data;
652 + GtkScrolledWindow *scrolled_window = data->scrolled_window;
653 + GtkScrolledWindowPrivate *priv;
654 + GtkAdjustment *hadjustment, *vadjustment;
655 + gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
657 + gint64 current_time;
660 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
661 + hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
662 + vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
664 + _gtk_scrolled_window_get_overshoot (scrolled_window,
665 + &old_overshoot_x, &old_overshoot_y);
667 + current_time = g_get_monotonic_time ();
668 + elapsed = (current_time - data->last_deceleration_time) / 1000;
669 + data->last_deceleration_time = current_time;
671 + if (hadjustment && scrolled_window->hscrollbar_visible)
673 + value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
675 + if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
677 + value, TRUE, TRUE))
678 + data->x_velocity = 0;
681 + data->x_velocity = 0;
683 + if (vadjustment && scrolled_window->vscrollbar_visible)
685 + value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
687 + if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
689 + value, TRUE, TRUE))
690 + data->y_velocity = 0;
693 + data->y_velocity = 0;
695 + _gtk_scrolled_window_get_overshoot (scrolled_window,
696 + &overshoot_x, &overshoot_y);
698 + if (overshoot_x == 0)
700 + if (old_overshoot_x != 0)
702 + /* Overshooting finished snapping back */
703 + data->x_velocity = 0;
705 + else if (data->x_velocity > 0)
707 + data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
708 + data->x_velocity = MAX (0, data->x_velocity);
710 + else if (data->x_velocity < 0)
712 + data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
713 + data->x_velocity = MIN (0, data->x_velocity);
716 + else if (overshoot_x < 0)
717 + data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
718 + else if (overshoot_x > 0)
719 + data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
721 + if (overshoot_y == 0)
723 + if (old_overshoot_y != 0)
725 + /* Overshooting finished snapping back */
726 + data->y_velocity = 0;
728 + else if (data->y_velocity > 0)
730 + data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
731 + data->y_velocity = MAX (0, data->y_velocity);
733 + else if (data->y_velocity < 0)
735 + data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
736 + data->y_velocity = MIN (0, data->y_velocity);
739 + else if (overshoot_y < 0)
740 + data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
741 + else if (overshoot_y > 0)
742 + data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
744 + if (old_overshoot_x != overshoot_x ||
745 + old_overshoot_y != overshoot_y)
746 + _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
748 + if (overshoot_x != 0 || overshoot_y != 0 ||
749 + data->x_velocity != 0 || data->y_velocity != 0)
753 + priv->deceleration_id = 0;
759 +gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
761 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
763 + if (priv->deceleration_id)
765 + g_source_remove (priv->deceleration_id);
766 + priv->deceleration_id = 0;
771 +gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
773 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
774 + KineticScrollData *data;
777 + data = g_new0 (KineticScrollData, 1);
778 + data->scrolled_window = scrolled_window;
779 + data->last_deceleration_time = g_get_monotonic_time ();
780 + data->x_velocity = priv->x_velocity;
781 + data->y_velocity = priv->y_velocity;
783 + /* We use sine/cosine as a factor to deceleration x/y components
784 + * of the vector, so we care about the sign later.
786 + angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
787 + data->vel_cosine = cos (angle);
788 + data->vel_sine = sin (angle);
790 + priv->deceleration_id =
791 + gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
793 + scrolled_window_deceleration_cb,
794 + data, (GDestroyNotify) g_free);
798 +gtk_scrolled_window_release_captured_event (GtkScrolledWindow *scrolled_window)
800 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
802 + /* Cancel the scrolling and send the button press
803 + * event to the child widget
805 + if (!priv->button_press_event)
808 + if (priv->pointer_grabbed)
810 + gtk_grab_remove (GTK_WIDGET (scrolled_window));
811 + priv->pointer_grabbed = FALSE;
814 + if (priv->capture_button_press)
816 + GtkWidget *event_widget;
818 + event_widget = gtk_get_event_widget (priv->button_press_event);
820 + if (!_gtk_propagate_captured_event (event_widget,
821 + priv->button_press_event,
822 + gtk_bin_get_child (GTK_BIN (scrolled_window))))
823 + gtk_propagate_event (event_widget, priv->button_press_event);
825 + gdk_event_free (priv->button_press_event);
826 + priv->button_press_event = NULL;
829 + if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
830 + gtk_scrolled_window_start_deceleration (scrolled_window);
836 +gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
839 + GtkScrolledWindowPrivate *priv;
840 + gdouble x_root, y_root;
843 +#define STILL_THRESHOLD 40
845 + if (!gdk_event_get_root_coords (event, &x_root, &y_root))
848 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
849 + _time = gdk_event_get_time (event);
851 + if (priv->last_motion_event_x_root != x_root ||
852 + priv->last_motion_event_y_root != y_root ||
853 + ABS (_time - priv->last_motion_event_time) > STILL_THRESHOLD)
855 + priv->x_velocity = (priv->last_motion_event_x_root - x_root) /
856 + (gdouble) (_time - priv->last_motion_event_time);
857 + priv->y_velocity = (priv->last_motion_event_y_root - y_root) /
858 + (gdouble) (_time - priv->last_motion_event_time);
861 + priv->last_motion_event_x_root = x_root;
862 + priv->last_motion_event_y_root = y_root;
863 + priv->last_motion_event_time = _time;
865 +#undef STILL_THRESHOLD
871 +gtk_scrolled_window_captured_button_release (GtkWidget *widget,
874 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
875 + GtkScrolledWindowPrivate *priv;
877 + gboolean overshoot;
878 + gdouble x_root, y_root;
880 + if (event->button.button != 1)
883 + child = gtk_bin_get_child (GTK_BIN (widget));
887 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
888 + gtk_grab_remove (widget);
889 + priv->pointer_grabbed = FALSE;
891 + if (priv->release_timeout_id)
893 + g_source_remove (priv->release_timeout_id);
894 + priv->release_timeout_id = 0;
897 + overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
900 + gdk_pointer_ungrab (gdk_event_get_time (event));
903 + /* There hasn't been scrolling at all, so just let the
904 + * child widget handle the button press normally
906 + gtk_scrolled_window_release_captured_event (scrolled_window);
911 + priv->in_drag = FALSE;
913 + if (priv->button_press_event)
915 + gdk_event_free (priv->button_press_event);
916 + priv->button_press_event = NULL;
919 + gtk_scrolled_window_calculate_velocity (scrolled_window, event);
921 + /* Zero out vector components without a visible scrollbar */
922 + if (!scrolled_window->hscrollbar_visible)
923 + priv->x_velocity = 0;
924 + if (!scrolled_window->vscrollbar_visible)
925 + priv->y_velocity = 0;
927 + if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
929 + gtk_scrolled_window_start_deceleration (scrolled_window);
930 + priv->x_velocity = priv->y_velocity = 0;
931 + priv->last_button_event_valid = FALSE;
935 + gdk_event_get_root_coords (event, &x_root, &y_root);
936 + priv->last_button_event_x_root = x_root;
937 + priv->last_button_event_y_root = y_root;
938 + priv->last_button_event_valid = TRUE;
941 + if (priv->capture_button_press)
948 +gtk_scrolled_window_captured_motion_notify (GtkWidget *widget,
951 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
952 + GtkScrolledWindowPrivate *priv;
953 + gint old_overshoot_x, old_overshoot_y;
954 + gint new_overshoot_x, new_overshoot_y;
956 + GtkAdjustment *hadjustment;
957 + GtkAdjustment *vadjustment;
959 + GdkModifierType state;
960 + gdouble x_root, y_root;
962 + gdk_event_get_state (event, &state);
963 + if (!(state & GDK_BUTTON1_MASK))
966 + child = gtk_bin_get_child (GTK_BIN (widget));
970 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
972 + /* Check if we've passed the drag threshold */
973 + gdk_event_get_root_coords (event, &x_root, &y_root);
974 + if (!priv->in_drag)
976 + if (gtk_drag_check_threshold (widget,
977 + priv->last_button_event_x_root,
978 + priv->last_button_event_y_root,
981 + if (priv->release_timeout_id)
983 + g_source_remove (priv->release_timeout_id);
984 + priv->release_timeout_id = 0;
987 + priv->last_button_event_valid = FALSE;
988 + priv->in_drag = TRUE;
994 + gdk_pointer_grab (gtk_widget_get_window (widget),
996 + GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK,
998 + gdk_event_get_time (event));
1000 + priv->last_button_event_valid = FALSE;
1002 + if (priv->button_press_event)
1004 + gdk_event_free (priv->button_press_event);
1005 + priv->button_press_event = NULL;
1008 + _gtk_scrolled_window_get_overshoot (scrolled_window,
1009 + &old_overshoot_x, &old_overshoot_y);
1011 + hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
1012 + if (hadjustment && scrolled_window->hscrollbar_visible)
1014 + dx = (priv->last_motion_event_x_root - x_root) + priv->unclamped_hadj_value;
1015 + _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment,
1019 + vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
1020 + if (vadjustment && scrolled_window->vscrollbar_visible)
1022 + dy = (priv->last_motion_event_y_root - y_root) + priv->unclamped_vadj_value;
1023 + _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment,
1027 + _gtk_scrolled_window_get_overshoot (scrolled_window,
1028 + &new_overshoot_x, &new_overshoot_y);
1030 + if (old_overshoot_x != new_overshoot_x ||
1031 + old_overshoot_y != new_overshoot_y)
1032 + _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
1034 + gtk_scrolled_window_calculate_velocity (scrolled_window, event);
1040 +gtk_scrolled_window_captured_button_press (GtkWidget *widget,
1043 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1044 + GtkScrolledWindowPrivate *priv;
1046 + GtkWidget *event_widget;
1047 + gdouble x_root, y_root;
1049 + /* If scrollbars are not visible, we don't do kinetic scrolling */
1050 + if (!scrolled_window->vscrollbar_visible &&
1051 + !scrolled_window->hscrollbar_visible)
1054 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
1056 + event_widget = gtk_get_event_widget (event);
1058 + /* If there's another scrolled window between the widget
1059 + * receiving the event and this capturing scrolled window,
1060 + * let it handle the events.
1062 + if (widget != gtk_widget_get_ancestor (event_widget, GTK_TYPE_SCROLLED_WINDOW))
1065 + /* Check whether the button press is close to the previous one,
1066 + * take that as a shortcut to get the child widget handle events
1068 + gdk_event_get_root_coords (event, &x_root, &y_root);
1069 + if (priv->last_button_event_valid &&
1070 + ABS (x_root - priv->last_button_event_x_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD &&
1071 + ABS (y_root - priv->last_button_event_y_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD)
1073 + priv->last_button_event_valid = FALSE;
1077 + priv->last_button_event_x_root = priv->last_motion_event_x_root = x_root;
1078 + priv->last_button_event_y_root = priv->last_motion_event_y_root = y_root;
1079 + priv->last_motion_event_time = gdk_event_get_time (event);
1080 + priv->last_button_event_valid = TRUE;
1082 + if (event->button.button != 1)
1085 + child = gtk_bin_get_child (GTK_BIN (widget));
1089 + if (scrolled_window->hscrollbar == event_widget ||
1090 + scrolled_window->vscrollbar == event_widget)
1093 + priv->pointer_grabbed = TRUE;
1094 + gtk_grab_add (widget);
1096 + gtk_scrolled_window_cancel_deceleration (scrolled_window);
1098 + /* Only set the timeout if we're going to store an event */
1099 + if (priv->capture_button_press)
1100 + priv->release_timeout_id =
1101 + gdk_threads_add_timeout (RELEASE_EVENT_TIMEOUT,
1102 + (GSourceFunc) gtk_scrolled_window_release_captured_event,
1105 + priv->in_drag = FALSE;
1107 + if (priv->capture_button_press)
1109 + /* Store the button press event in
1110 + * case we need to propagate it later
1112 + priv->button_press_event = gdk_event_copy (event);
1120 +gtk_scrolled_window_captured_event (GtkWidget *widget,
1123 + gboolean retval = FALSE;
1124 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (widget);
1126 + switch (event->type)
1128 + case GDK_BUTTON_PRESS:
1129 + retval = gtk_scrolled_window_captured_button_press (widget, event);
1131 + case GDK_BUTTON_RELEASE:
1132 + if (priv->pointer_grabbed)
1133 + retval = gtk_scrolled_window_captured_button_release (widget, event);
1135 + priv->last_button_event_valid = FALSE;
1137 + case GDK_MOTION_NOTIFY:
1138 + if (priv->pointer_grabbed)
1139 + retval = gtk_scrolled_window_captured_motion_notify (widget, event);
1141 + case GDK_LEAVE_NOTIFY:
1142 + case GDK_ENTER_NOTIFY:
1143 + if (priv->in_drag &&
1144 + event->crossing.mode != GDK_CROSSING_GRAB)
1155 gtk_scrolled_window_focus (GtkWidget *widget,
1156 GtkDirectionType direction)
1158 @@ -1714,17 +2614,42 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
1162 +gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
1163 + gpointer user_data)
1165 + GtkScrolledWindow *scrolled_window = user_data;
1166 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
1168 + /* Allow overshooting for kinetic scrolling operations */
1169 + if (priv->pointer_grabbed || priv->deceleration_id)
1172 + /* Ensure GtkAdjustment and unclamped values are in sync */
1173 + if (scrolled_window->vscrollbar &&
1174 + adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)))
1175 + priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
1176 + else if (scrolled_window->hscrollbar &&
1177 + adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)))
1178 + priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
1182 gtk_scrolled_window_add (GtkContainer *container,
1185 GtkScrolledWindow *scrolled_window;
1186 + GtkScrolledWindowPrivate *priv;
1189 bin = GTK_BIN (container);
1190 + priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (container);
1191 g_return_if_fail (bin->child == NULL);
1193 scrolled_window = GTK_SCROLLED_WINDOW (container);
1195 + if (gtk_widget_get_realized (GTK_WIDGET (bin)))
1196 + gtk_widget_set_parent_window (child, priv->overshoot_window);
1199 gtk_widget_set_parent (child, GTK_WIDGET (bin));
1201 @@ -1837,5 +2762,111 @@ _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
1206 +gtk_scrolled_window_realize (GtkWidget *widget)
1208 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1209 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
1210 + GtkAllocation allocation, relative_allocation;
1211 + GdkWindowAttr attributes;
1212 + GtkWidget *child_widget;
1213 + gint attributes_mask;
1215 + gtk_widget_set_realized (widget, TRUE);
1216 + gtk_widget_get_allocation (widget, &allocation);
1217 + gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1219 + attributes.window_type = GDK_WINDOW_CHILD;
1220 + attributes.x = allocation.x + relative_allocation.x;
1221 + attributes.y = allocation.y + relative_allocation.y;
1222 + attributes.width = relative_allocation.width;
1223 + attributes.height = relative_allocation.height;
1224 + attributes.wclass = GDK_INPUT_OUTPUT;
1225 + attributes.visual = gtk_widget_get_visual (widget);
1226 + attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
1227 + GDK_BUTTON_MOTION_MASK;
1229 + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1231 + priv->overshoot_window =
1232 + gdk_window_new (gtk_widget_get_parent_window (widget),
1233 + &attributes, attributes_mask);
1235 + gdk_window_set_user_data (priv->overshoot_window, widget);
1237 + child_widget = gtk_bin_get_child (GTK_BIN (widget));
1240 + gtk_widget_set_parent_window (child_widget,
1241 + priv->overshoot_window);
1243 + GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
1247 +gtk_scrolled_window_unrealize (GtkWidget *widget)
1249 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1250 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
1252 + gdk_window_set_user_data (priv->overshoot_window, NULL);
1253 + gdk_window_destroy (priv->overshoot_window);
1254 + priv->overshoot_window = NULL;
1256 + gtk_widget_set_realized (widget, FALSE);
1258 + GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
1262 +gtk_scrolled_window_map (GtkWidget *widget)
1264 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1265 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
1267 + gdk_window_show (priv->overshoot_window);
1269 + GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
1273 +gtk_scrolled_window_unmap (GtkWidget *widget)
1275 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1276 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
1278 + gdk_window_hide (priv->overshoot_window);
1280 + GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
1284 +gtk_scrolled_window_grab_notify (GtkWidget *widget,
1285 + gboolean was_grabbed)
1287 + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1288 + GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
1290 + if (priv->pointer_grabbed && !was_grabbed)
1292 + gdk_pointer_ungrab (gtk_get_current_event_time ());
1293 + priv->pointer_grabbed = FALSE;
1294 + priv->in_drag = FALSE;
1296 + if (priv->release_timeout_id)
1298 + g_source_remove (priv->release_timeout_id);
1299 + priv->release_timeout_id = 0;
1302 + if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
1303 + gtk_scrolled_window_start_deceleration (scrolled_window);
1305 + gtk_scrolled_window_cancel_deceleration (scrolled_window);
1307 + priv->last_button_event_valid = FALSE;
1311 #define __GTK_SCROLLED_WINDOW_C__
1312 #include "gtkaliasdef.c"
1313 diff --git a/gtk/gtkscrolledwindow.h b/gtk/gtkscrolledwindow.h
1314 index 5407547..1f555e0 100644
1315 --- a/gtk/gtkscrolledwindow.h
1316 +++ b/gtk/gtkscrolledwindow.h
1317 @@ -127,6 +127,14 @@ GtkShadowType gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolle
1318 void gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
1321 +void gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
1322 + gboolean kinetic_scrolling);
1323 +gboolean gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window);
1325 +void gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
1326 + gboolean capture_button_press);
1327 +gboolean gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window);
1329 gint _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window);
1333 1.7.10.2 (Apple Git-33)