From 79c1e789097b4e46b9f3fdcbc96a2aae69e24144 Mon Sep 17 00:00:00 2001 From: Kristian Rietveld Date: Mon, 3 Sep 2012 10:45:13 +0200 Subject: [PATCH 21/68] Make scrolled window work well with Mac touchpad Modify the scrolled window code to work with a Mac touchpad as you would expect. When precise deltas are received from the Mac touchpad, overshooting and snapping back will work. The existing kinetic scrolling code is only used for snapping back. The amount of pixels to overshoot is determined by the incoming scroll events from the touchpad. We use the deceleration mechanism that was already in place to snap back. Other changes made are: - Increased the kinetic scrolling distance, this makes things feel nicer IMHO. - Removed everything related to handling kinetic scrolling by making drag gestures. We don't need this code. --- gtk/gtkscrolledwindow.c | 680 +++++++++++------------------------------------ 1 file changed, 157 insertions(+), 523 deletions(-) diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 9d0d87a..5341b50 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -79,7 +79,7 @@ /* Kinetic scrolling */ #define FRAME_INTERVAL (1000 / 60) -#define MAX_OVERSHOOT_DISTANCE 50 +#define MAX_OVERSHOOT_DISTANCE 100 #define FRICTION_DECELERATION 0.003 #define OVERSHOOT_INVERSE_ACCELERATION 0.003 #define RELEASE_EVENT_TIMEOUT 1000 @@ -98,20 +98,11 @@ typedef struct { GdkWindow *vbackground_window; GdkWindow *hbackground_window; guint pointer_grabbed : 1; - guint kinetic_scrolling : 1; - guint capture_button_press : 1; guint in_drag : 1; - guint last_button_event_valid : 1; - guint release_timeout_id; guint deceleration_id; - gdouble last_button_event_x_root; - gdouble last_button_event_y_root; - - gdouble last_motion_event_x_root; - gdouble last_motion_event_y_root; - guint32 last_motion_event_time; + guint32 last_scroll_event_time; gdouble x_velocity; gdouble y_velocity; @@ -144,6 +135,7 @@ typedef struct { guint sb_scroll_timeout_id; gboolean overlay_scrollbars; + gboolean is_snapping_back; } GtkScrolledWindowPrivate; #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate)) @@ -167,8 +159,7 @@ enum { PROP_VSCROLLBAR_POLICY, PROP_WINDOW_PLACEMENT, PROP_WINDOW_PLACEMENT_SET, - PROP_SHADOW_TYPE, - PROP_KINETIC_SCROLLING + PROP_SHADOW_TYPE }; /* Signals */ @@ -268,6 +259,13 @@ static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *setting GParamSpec *arg, gpointer user_data); + +static void gtk_scrolled_window_start_deceleration (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); + + static guint signals[LAST_SIGNAL] = {0}; G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN) @@ -432,22 +430,6 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class) GTK_PARAM_READABLE)); /** - * GtkScrolledWindow:kinetic-scrolling: - * - * The kinetic scrolling behavior flags. - * - * Since: X.XX - */ - g_object_class_install_property (gobject_class, - PROP_KINETIC_SCROLLING, - g_param_spec_boolean ("kinetic-scrolling", - P_("Kinetic Scrolling"), - P_("Kinetic scrolling mode."), - TRUE, - GTK_PARAM_READABLE | - GTK_PARAM_WRITABLE)); - - /** * GtkScrolledWindow::scroll-child: * @scrolled_window: a #GtkScrolledWindow * @scroll: a #GtkScrollType describing how much to scroll @@ -531,8 +513,7 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window) G_CALLBACK (gtk_scrolled_window_overlay_scrollbars_changed), scrolled_window); - gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE); - gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE); + gtk_scrolled_window_init_overlay_scrollbars (scrolled_window); priv->opacity = g_object_new (GTK_TYPE_ADJUSTMENT, "lower", 0.0, @@ -1075,130 +1056,6 @@ gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window) return scrolled_window->shadow_type; } -/** - * gtk_scrolled_window_set_kinetic_scrolling: - * @scrolled_window: a #GtkScrolledWindow - * @kinetic_scrolling: %TRUE to enable kinetic scrolling - * - * Turns kinetic scrolling on or off. - * Kinetic scrolling only applies to devices with source - * %GDK_SOURCE_TOUCHSCREEN. - * - * Since: X.XX - **/ -void -gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window, - gboolean kinetic_scrolling) -{ - GtkScrolledWindowPrivate *priv; - - g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window)); - - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - - if (priv->kinetic_scrolling == kinetic_scrolling) - return; - - priv->kinetic_scrolling = kinetic_scrolling; - if (priv->kinetic_scrolling) - { - _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), - gtk_scrolled_window_captured_event); - } - else - { - _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), NULL); - if (priv->release_timeout_id) - { - g_source_remove (priv->release_timeout_id); - priv->release_timeout_id = 0; - } - if (priv->deceleration_id) - { - g_source_remove (priv->deceleration_id); - priv->deceleration_id = 0; - } - } - g_object_notify (G_OBJECT (scrolled_window), "kinetic-scrolling"); -} - -/** - * gtk_scrolled_window_get_kinetic_scrolling: - * @scrolled_window: a #GtkScrolledWindow - * - * Returns the specified kinetic scrolling behavior. - * - * Return value: the scrolling behavior flags. - * - * Since: X.XX - */ -gboolean -gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window) -{ - GtkScrolledWindowPrivate *priv; - - g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE); - - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - - return priv->kinetic_scrolling; -} - -/** - * gtk_scrolled_window_set_capture_button_press: - * @scrolled_window: a #GtkScrolledWindow - * @capture_button_press: %TRUE to capture button presses - * - * Changes the behaviour of @scrolled_window wrt. to the initial - * event that possibly starts kinetic scrolling. When @capture_button_press - * is set to %TRUE, the event is captured by the scrolled window, and - * then later replayed if it is meant to go to the child widget. - * - * This should be enabled if any child widgets perform non-reversible - * actions on #GtkWidget::button-press-event. If they don't, and handle - * additionally handle #GtkWidget::grab-broken-event, it might be better - * to set @capture_button_press to %FALSE. - * - * This setting only has an effect if kinetic scrolling is enabled. - * - * Since: X.XX - */ -void -gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window, - gboolean capture_button_press) -{ - GtkScrolledWindowPrivate *priv; - - g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window)); - - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - priv->capture_button_press = capture_button_press; -} - -/** - * gtk_scrolled_window_get_capture_button_press: - * @scrolled_window: a #GtkScrolledWindow - * - * Return whether button presses are captured during kinetic - * scrolling. See gtk_scrolled_window_set_capture_button_press(). - * - * Returns: %TRUE if button presses are captured during kinetic scrolling - * - * Since: X.XX - */ -gboolean -gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window) -{ - GtkScrolledWindowPrivate *priv; - - g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE); - - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - - return priv->capture_button_press; -} - - static void gtk_scrolled_window_destroy (GtkObject *object) { @@ -1246,11 +1103,6 @@ gtk_scrolled_window_destroy (GtkObject *object) G_CALLBACK (gtk_scrolled_window_overlay_scrollbars_changed), scrolled_window); - if (priv->release_timeout_id) - { - g_source_remove (priv->release_timeout_id); - priv->release_timeout_id = 0; - } if (priv->deceleration_id) { g_source_remove (priv->deceleration_id); @@ -1313,10 +1165,6 @@ gtk_scrolled_window_set_property (GObject *object, gtk_scrolled_window_set_shadow_type (scrolled_window, g_value_get_enum (value)); break; - case PROP_KINETIC_SCROLLING: - gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, - g_value_get_boolean (value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1357,9 +1205,6 @@ gtk_scrolled_window_get_property (GObject *object, case PROP_SHADOW_TYPE: g_value_set_enum (value, scrolled_window->shadow_type); break; - case PROP_KINETIC_SCROLLING: - g_value_set_boolean (value, priv->kinetic_scrolling); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2152,40 +1997,146 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget, if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y)) { - if (delta_x != 0.0 && scrolled_window->hscrollbar && - (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar))) + gint new_overshoot_x, new_overshoot_y; + gint old_overshoot_x, old_overshoot_y; + gboolean start_snap_back = FALSE; + gboolean is_overshot = FALSE; + gboolean is_momentum_event = event->phase == GDK_EVENT_SCROLL_PHASE_NONE; + + _gtk_scrolled_window_get_overshoot (scrolled_window, + &old_overshoot_x, &old_overshoot_y); + + /* Check if the view is currently overshot. */ + if (old_overshoot_x != 0 || old_overshoot_y != 0) + is_overshot = TRUE; + + /* In case the view is not overshot, no snap back is active + * and this event is not a momentum event, then a new scrolling + * gesture has started. In case we are still in snapping back + * state we can reset this (because the snapback has ended). + */ + if (!is_overshot && priv->deceleration_id == 0 && !is_momentum_event) + priv->is_snapping_back = FALSE; + + /* 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. + * 2) If the view is currently overshot, then do not handle + * momentum events but handle non-momentum events as usual. + * + * For both cases we only allow overshooting in a direction if + * that particular scrollbar is actually visible. + */ + if ((!is_overshot && !priv->is_snapping_back) || + (is_overshot && !is_momentum_event)) { - GtkAdjustment *adj; - gdouble new_value; + if (delta_x != 0.0 && scrolled_window->hscrollbar && + (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar))) + { + GtkAdjustment *adj; - adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)); + adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)); - new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x, - gtk_adjustment_get_lower (adj), - gtk_adjustment_get_upper (adj) - - gtk_adjustment_get_page_size (adj)); + if (scrolled_window->hscrollbar_visible) + { + _gtk_scrolled_window_set_adjustment_value (scrolled_window, + adj, + priv->unclamped_hadj_value + delta_x, + TRUE, + FALSE); + } + else + { + gdouble new_value; - gtk_adjustment_set_value (adj, new_value); + /* If the scrollbar is not visible, clamp the value and + * don't trigger overshooting. + */ + new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x, + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); - handled = TRUE; + gtk_adjustment_set_value (adj, new_value); + } + + handled = TRUE; + } + + if (delta_y != 0.0 && scrolled_window->vscrollbar && + (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar))) + { + GtkAdjustment *adj; + + adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)); + + if (scrolled_window->vscrollbar_visible) + { + _gtk_scrolled_window_set_adjustment_value (scrolled_window, + adj, + priv->unclamped_vadj_value + delta_y, + TRUE, + FALSE); + } + else + { + gdouble new_value; + + /* If the scrollbar is not visible, clamp the value and + * don't trigger overshooting. + */ + new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y, + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); + + gtk_adjustment_set_value (adj, new_value); + } + + handled = TRUE; + } + + + priv->last_scroll_event_time = gdk_event_get_time ((GdkEvent *)event); + gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event); } - if (delta_y != 0.0 && scrolled_window->vscrollbar && - (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar))) - { - GtkAdjustment *adj; - gdouble new_value; + _gtk_scrolled_window_get_overshoot (scrolled_window, + &new_overshoot_x, &new_overshoot_y); - adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)); + if (old_overshoot_x != new_overshoot_x || + old_overshoot_y != new_overshoot_y) + _gtk_scrolled_window_allocate_overshoot_window (scrolled_window); - new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y, - gtk_adjustment_get_lower (adj), - gtk_adjustment_get_upper (adj) - - gtk_adjustment_get_page_size (adj)); + /* In two cases we want to start snapping back: + * 1) The view is overshot and the gesture has ended (signalled + * by an event with both deltas set to zero. + * 2) The view is overshot and we receive a momentum event, which + * also signals that the user's gesture has ended. + */ + if (is_overshot && + ((delta_x == 0.0 && delta_y == 0.0) || is_momentum_event)) + start_snap_back = TRUE; - gtk_adjustment_set_value (adj, new_value); + /* If we should start a snap back and no current deceleration + * is active, start the snap back. + */ + if (start_snap_back && priv->deceleration_id == 0) + { + /* Zero out vector components without a visible scrollbar */ + if (!scrolled_window->hscrollbar_visible) + priv->x_velocity = 0; + if (!scrolled_window->vscrollbar_visible) + priv->y_velocity = 0; - handled = TRUE; + priv->is_snapping_back = TRUE; + + if (new_overshoot_x != 0 || new_overshoot_y != 0) + { + gtk_scrolled_window_start_deceleration (scrolled_window); + priv->x_velocity = 0.0; + priv->y_velocity = 0.0; + } } } else @@ -2369,18 +2320,6 @@ scrolled_window_deceleration_cb (gpointer user_data) } static void -gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window) -{ - GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - - if (priv->deceleration_id) - { - g_source_remove (priv->deceleration_id); - priv->deceleration_id = 0; - } -} - -static void gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) { GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); @@ -2408,327 +2347,36 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) } static gboolean -gtk_scrolled_window_release_captured_event (GtkScrolledWindow *scrolled_window) -{ - GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - - /* Cancel the scrolling and send the button press - * event to the child widget - */ - if (!priv->button_press_event) - return FALSE; - - if (priv->pointer_grabbed) - { - gtk_grab_remove (GTK_WIDGET (scrolled_window)); - priv->pointer_grabbed = FALSE; - } - - if (priv->capture_button_press) - { - GtkWidget *event_widget; - - event_widget = gtk_get_event_widget (priv->button_press_event); - - if (!_gtk_propagate_captured_event (event_widget, - priv->button_press_event, - gtk_bin_get_child (GTK_BIN (scrolled_window)))) - gtk_propagate_event (event_widget, priv->button_press_event); - - gdk_event_free (priv->button_press_event); - priv->button_press_event = NULL; - } - - if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL)) - gtk_scrolled_window_start_deceleration (scrolled_window); - - return FALSE; -} - -static gboolean gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window, GdkEvent *event) { GtkScrolledWindowPrivate *priv; - gdouble x_root, y_root; guint32 _time; -#define STILL_THRESHOLD 40 - - if (!gdk_event_get_root_coords (event, &x_root, &y_root)) - return FALSE; - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); _time = gdk_event_get_time (event); - if (priv->last_motion_event_x_root != x_root || - priv->last_motion_event_y_root != y_root || - ABS (_time - priv->last_motion_event_time) > STILL_THRESHOLD) - { - priv->x_velocity = (priv->last_motion_event_x_root - x_root) / - (gdouble) (_time - priv->last_motion_event_time); - priv->y_velocity = (priv->last_motion_event_y_root - y_root) / - (gdouble) (_time - priv->last_motion_event_time); - } - - priv->last_motion_event_x_root = x_root; - priv->last_motion_event_y_root = y_root; - priv->last_motion_event_time = _time; - -#undef STILL_THRESHOLD - - return TRUE; -} - -static gboolean -gtk_scrolled_window_captured_button_release_kinetic (GtkWidget *widget, - GdkEvent *event) -{ - GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); - GtkScrolledWindowPrivate *priv; - GtkWidget *child; - gboolean overshoot; - gdouble x_root, y_root; - - if (event->button.button != 1) - return FALSE; - - child = gtk_bin_get_child (GTK_BIN (widget)); - if (!child) - return FALSE; - - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - gtk_grab_remove (widget); - priv->pointer_grabbed = FALSE; - - if (priv->release_timeout_id) - { - g_source_remove (priv->release_timeout_id); - priv->release_timeout_id = 0; - } - - overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL); - - if (priv->in_drag) - gdk_pointer_ungrab (gdk_event_get_time (event)); - else - { - /* There hasn't been scrolling at all, so just let the - * child widget handle the button press normally - */ - gtk_scrolled_window_release_captured_event (scrolled_window); - - if (!overshoot) - return FALSE; - } - priv->in_drag = FALSE; - - if (priv->button_press_event) - { - gdk_event_free (priv->button_press_event); - priv->button_press_event = NULL; - } - - gtk_scrolled_window_calculate_velocity (scrolled_window, event); - - /* Zero out vector components without a visible scrollbar */ - if (!scrolled_window->hscrollbar_visible) - priv->x_velocity = 0; - if (!scrolled_window->vscrollbar_visible) - priv->y_velocity = 0; +#define STILL_THRESHOLD 40 - if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot) + if (event->type == GDK_SCROLL) { - gtk_scrolled_window_start_deceleration (scrolled_window); - priv->x_velocity = priv->y_velocity = 0; - priv->last_button_event_valid = FALSE; - } - else - { - gdk_event_get_root_coords (event, &x_root, &y_root); - priv->last_button_event_x_root = x_root; - priv->last_button_event_y_root = y_root; - priv->last_button_event_valid = TRUE; - } - - if (priv->capture_button_press) - return TRUE; - else - return FALSE; -} + gdouble delta_x, delta_y; -static gboolean -gtk_scrolled_window_captured_motion_notify_kinetic (GtkWidget *widget, - GdkEvent *event) -{ - GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); - GtkScrolledWindowPrivate *priv; - gint old_overshoot_x, old_overshoot_y; - gint new_overshoot_x, new_overshoot_y; - GtkWidget *child; - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - gdouble dx, dy; - GdkModifierType state; - gdouble x_root, y_root; - - gdk_event_get_state (event, &state); - if (!(state & GDK_BUTTON1_MASK)) - return FALSE; - - child = gtk_bin_get_child (GTK_BIN (widget)); - if (!child) - return FALSE; - - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - - /* Check if we've passed the drag threshold */ - gdk_event_get_root_coords (event, &x_root, &y_root); - if (!priv->in_drag) - { - if (gtk_drag_check_threshold (widget, - priv->last_button_event_x_root, - priv->last_button_event_y_root, - x_root, y_root)) + if (gdk_event_get_scroll_deltas (event, &delta_x, &delta_y) && + ABS (_time - priv->last_scroll_event_time) > STILL_THRESHOLD) { - if (priv->release_timeout_id) - { - g_source_remove (priv->release_timeout_id); - priv->release_timeout_id = 0; - } + 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_button_event_valid = FALSE; - priv->in_drag = TRUE; + priv->last_scroll_event_time = _time; } - else - return TRUE; - } - - gdk_pointer_grab (gtk_widget_get_window (widget), - TRUE, - GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK, - NULL, NULL, - gdk_event_get_time (event)); - - priv->last_button_event_valid = FALSE; - - if (priv->button_press_event) - { - gdk_event_free (priv->button_press_event); - priv->button_press_event = NULL; - } - - _gtk_scrolled_window_get_overshoot (scrolled_window, - &old_overshoot_x, &old_overshoot_y); - - hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)); - if (hadjustment && scrolled_window->hscrollbar_visible) - { - dx = (priv->last_motion_event_x_root - x_root) + priv->unclamped_hadj_value; - _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment, - dx, TRUE, FALSE); - } - - vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)); - if (vadjustment && scrolled_window->vscrollbar_visible) - { - dy = (priv->last_motion_event_y_root - y_root) + priv->unclamped_vadj_value; - _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment, - dy, TRUE, FALSE); } - _gtk_scrolled_window_get_overshoot (scrolled_window, - &new_overshoot_x, &new_overshoot_y); - - if (old_overshoot_x != new_overshoot_x || - old_overshoot_y != new_overshoot_y) - _gtk_scrolled_window_allocate_overshoot_window (scrolled_window); - - gtk_scrolled_window_calculate_velocity (scrolled_window, event); +#undef STILL_THRESHOLD return TRUE; } -static gboolean -gtk_scrolled_window_captured_button_press_kinetic (GtkWidget *widget, - GdkEvent *event) -{ - GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); - GtkScrolledWindowPrivate *priv; - GtkWidget *child; - GtkWidget *event_widget; - gdouble x_root, y_root; - - /* If scrollbars are not visible, we don't do kinetic scrolling */ - if (!scrolled_window->vscrollbar_visible && - !scrolled_window->hscrollbar_visible) - return FALSE; - - priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window); - - event_widget = gtk_get_event_widget (event); - - /* If there's another scrolled window between the widget - * receiving the event and this capturing scrolled window, - * let it handle the events. - */ - if (widget != gtk_widget_get_ancestor (event_widget, GTK_TYPE_SCROLLED_WINDOW)) - return FALSE; - - /* Check whether the button press is close to the previous one, - * take that as a shortcut to get the child widget handle events - */ - gdk_event_get_root_coords (event, &x_root, &y_root); - if (priv->last_button_event_valid && - ABS (x_root - priv->last_button_event_x_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD && - ABS (y_root - priv->last_button_event_y_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD) - { - priv->last_button_event_valid = FALSE; - return FALSE; - } - - priv->last_button_event_x_root = priv->last_motion_event_x_root = x_root; - priv->last_button_event_y_root = priv->last_motion_event_y_root = y_root; - priv->last_motion_event_time = gdk_event_get_time (event); - priv->last_button_event_valid = TRUE; - - if (event->button.button != 1) - return FALSE; - - child = gtk_bin_get_child (GTK_BIN (widget)); - if (!child) - return FALSE; - - if (scrolled_window->hscrollbar == event_widget || - scrolled_window->vscrollbar == event_widget) - return FALSE; - - priv->pointer_grabbed = TRUE; - gtk_grab_add (widget); - - gtk_scrolled_window_cancel_deceleration (scrolled_window); - - /* Only set the timeout if we're going to store an event */ - if (priv->capture_button_press) - priv->release_timeout_id = - gdk_threads_add_timeout (RELEASE_EVENT_TIMEOUT, - (GSourceFunc) gtk_scrolled_window_release_captured_event, - scrolled_window); - - priv->in_drag = FALSE; - - if (priv->capture_button_press) - { - /* Store the button press event in - * case we need to propagate it later - */ - priv->button_press_event = gdk_event_copy (event); - return TRUE; - } - else - return FALSE; -} - static void gtk_scrolled_window_scroll_step (GtkScrolledWindow *scrolled_window) { @@ -3011,22 +2659,14 @@ gtk_scrolled_window_captured_event (GtkWidget *widget, { case GDK_BUTTON_PRESS: retval = gtk_scrolled_window_captured_button_press_scrollbar (widget, event); - if (!retval) - retval = gtk_scrolled_window_captured_button_press_kinetic (widget, event); break; case GDK_BUTTON_RELEASE: if (priv->sb_pointer_grabbed) retval = gtk_scrolled_window_captured_button_release_scrollbar (widget, event); - else if (priv->pointer_grabbed) - retval = gtk_scrolled_window_captured_button_release_kinetic (widget, event); - else - priv->last_button_event_valid = FALSE; break; case GDK_MOTION_NOTIFY: if (priv->sb_pointer_grabbed || !priv->pointer_grabbed) retval = gtk_scrolled_window_captured_motion_notify_scrollbar (widget, event); - else if (priv->pointer_grabbed) - retval = gtk_scrolled_window_captured_motion_notify_kinetic (widget, event); break; case GDK_LEAVE_NOTIFY: if (!priv->in_drag && !priv->sb_pointer_grabbed) @@ -3439,19 +3079,6 @@ gtk_scrolled_window_grab_notify (GtkWidget *widget, gdk_pointer_ungrab (gtk_get_current_event_time ()); priv->pointer_grabbed = FALSE; priv->in_drag = FALSE; - - if (priv->release_timeout_id) - { - g_source_remove (priv->release_timeout_id); - priv->release_timeout_id = 0; - } - - if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL)) - gtk_scrolled_window_start_deceleration (scrolled_window); - else - gtk_scrolled_window_cancel_deceleration (scrolled_window); - - priv->last_button_event_valid = FALSE; } if (priv->sb_pointer_grabbed && !was_grabbed) @@ -3923,6 +3550,13 @@ gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj, } static void +gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *scrolled_window) +{ + _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), + gtk_scrolled_window_captured_event); +} + +static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *settings, GParamSpec *arg, gpointer user_data) -- 1.7.10.2 (Apple Git-33)