6bdd276d05
Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
871 lines
31 KiB
Diff
871 lines
31 KiB
Diff
From 79c1e789097b4e46b9f3fdcbc96a2aae69e24144 Mon Sep 17 00:00:00 2001
|
|
From: Kristian Rietveld <kris@lanedo.com>
|
|
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)
|