[gnome-shell/wip/gdm-shell] wip: StScrollView: add swipe support
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/gdm-shell] wip: StScrollView: add swipe support
- Date: Thu, 7 Jul 2011 04:00:24 +0000 (UTC)
commit 8013b96e5141666a7b2697988168bf1d4c832d36
Author: Ray Strode <rstrode redhat com>
Date: Wed Jul 6 23:58:04 2011 -0400
wip: StScrollView: add swipe support
This adds the ability to drag around the scroll view without
using the scrollbars.
TODO: need to propagate button press events to client if we
decide the user isn't swiping.
src/st/st-scroll-view.c | 340 +++++++++++++++++++++++++++++++++++++++++++++++
src/st/st-scroll-view.h | 4 +
2 files changed, 344 insertions(+), 0 deletions(-)
---
diff --git a/src/st/st-scroll-view.c b/src/st/st-scroll-view.c
index a628690..f472440 100644
--- a/src/st/st-scroll-view.c
+++ b/src/st/st-scroll-view.c
@@ -64,6 +64,8 @@
#include "st-scrollable.h"
#include "st-scroll-view-fade.h"
#include <clutter/clutter.h>
+#include <clutter/x11/clutter-x11.h>
+#include <gdk/gdkx.h>
#include <math.h>
static void clutter_container_iface_init (ClutterContainerIface *iface);
@@ -78,6 +80,8 @@ G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN,
ST_TYPE_SCROLL_VIEW, \
StScrollViewPrivate))
+#define ANIMATION_TIME 2500
+
struct _StScrollViewPrivate
{
/* a pointer to the child; this is actually stored
@@ -91,6 +95,14 @@ struct _StScrollViewPrivate
StAdjustment *vadjustment;
ClutterActor *vscroll;
+ int last_motion_time;
+ double drag_start_horizontal_value;
+ double drag_start_vertical_value;
+ float drag_start_x;
+ float drag_start_y;
+ float drag_x;
+ float drag_y;
+
GtkPolicyType hscrollbar_policy;
GtkPolicyType vscrollbar_policy;
@@ -99,9 +111,13 @@ struct _StScrollViewPrivate
StScrollViewFade *vfade_effect;
+ guint captured_event_id;
+
gboolean row_size_set : 1;
gboolean column_size_set : 1;
guint mouse_scroll : 1;
+ guint swipe_scroll : 1;
+ guint in_swipe : 1;
guint hscrollbar_visible : 1;
guint vscrollbar_visible : 1;
};
@@ -114,6 +130,7 @@ enum {
PROP_HSCROLLBAR_POLICY,
PROP_VSCROLLBAR_POLICY,
PROP_MOUSE_SCROLL,
+ PROP_SWIPE_SCROLL,
};
static void
@@ -141,6 +158,9 @@ st_scroll_view_get_property (GObject *object,
case PROP_MOUSE_SCROLL:
g_value_set_boolean (value, priv->mouse_scroll);
break;
+ case PROP_SWIPE_SCROLL:
+ g_value_set_boolean (value, priv->swipe_scroll);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -200,6 +220,10 @@ st_scroll_view_set_property (GObject *object,
st_scroll_view_set_mouse_scrolling (self,
g_value_get_boolean (value));
break;
+ case PROP_SWIPE_SCROLL:
+ st_scroll_view_set_swipe_scrolling (self,
+ g_value_get_boolean (value));
+ break;
case PROP_HSCROLLBAR_POLICY:
st_scroll_view_set_policy (self,
g_value_get_enum (value),
@@ -251,6 +275,13 @@ st_scroll_view_dispose (GObject *object)
priv->vadjustment = NULL;
}
+ if (priv->captured_event_id > 0)
+ {
+ g_signal_handler_disconnect (clutter_actor_get_stage (CLUTTER_ACTOR (object)),
+ priv->captured_event_id);
+ priv->captured_event_id = 0;
+ }
+
G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
}
@@ -731,6 +762,242 @@ st_scroll_view_scroll_event (ClutterActor *self,
}
static void
+get_area_of_monitor_surrounding_actor (ClutterActor *actor,
+ GdkRectangle *area)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ GdkScreen *screen;
+ ClutterActor *stage;
+ Window xwindow;
+ GdkWindow *window;
+ float x, y;
+ int monitor;
+
+ stage = clutter_actor_get_stage (actor);
+
+ xwindow = clutter_x11_get_stage_window (CLUTTER_STAGE (stage));
+
+ window = gdk_x11_window_lookup_for_display (display, xwindow);
+ if (window)
+ g_object_ref (window);
+ else
+ window = gdk_x11_window_foreign_new_for_display (display, xwindow);
+
+ screen = gdk_window_get_screen (window);
+
+ g_object_unref (window);
+
+ clutter_actor_get_transformed_position (actor, &x, &y);
+
+ /* assumes stage covers screen
+ */
+ monitor = gdk_screen_get_monitor_at_point (screen, (int) x + .5, (int) y + .5);
+
+ gdk_screen_get_monitor_geometry (screen, monitor, area);
+}
+
+static gboolean
+handle_captured_button_press_event (StScrollView *self,
+ ClutterEvent *event)
+{
+ StScrollViewPrivate *priv = self->priv;
+ float x, y;
+ ClutterActorBox box;
+
+ if (!priv->swipe_scroll || clutter_event_get_button (event) != 1 ||
+ !(priv->hscrollbar_visible || priv->vscrollbar_visible))
+ return FALSE;
+
+ clutter_event_get_coords (event, &x, &y);
+ clutter_actor_get_paint_box (CLUTTER_ACTOR (self), &box);
+
+ if (!clutter_actor_box_contains (&box, x, y))
+ return FALSE;
+
+ clutter_actor_get_paint_box (CLUTTER_ACTOR (priv->hscroll), &box);
+
+ if (clutter_actor_box_contains (&box, x, y))
+ return FALSE;
+
+ clutter_actor_get_paint_box (CLUTTER_ACTOR (priv->vscroll), &box);
+
+ if (clutter_actor_box_contains (&box, x, y))
+ return FALSE;
+
+ priv->drag_start_x = priv->drag_x = x;
+ priv->drag_start_y = priv->drag_y = y;
+ g_object_get (priv->hadjustment, "value", &priv->drag_start_horizontal_value, NULL);
+ g_object_get (priv->vadjustment, "value", &priv->drag_start_vertical_value, NULL);
+ priv->last_motion_time = -1;
+ priv->in_swipe = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+handle_captured_button_release_event (StScrollView *self,
+ ClutterEvent *event)
+{
+ StScrollViewPrivate *priv = self->priv;
+ GtkSettings *settings;
+ int threshold;
+ gfloat x, y;
+ gdouble new_horizontal_value, horizontal_value, min_horizontal_value, max_horizontal_value;
+ gdouble new_vertical_value, vertical_value, min_vertical_value, max_vertical_value;
+ gboolean was_thrown;
+
+ if (!priv->in_swipe)
+ return FALSE;
+
+ priv->in_swipe = FALSE;
+
+ settings = gtk_settings_get_default();
+ g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &threshold, NULL);
+
+ clutter_event_get_coords (event, &x, &y);
+
+ /* See if the user has moved the mouse enough to trigger
+ * a drag
+ */
+ if (ABS (x - priv->drag_start_x) < threshold &&
+ ABS (y - priv->drag_start_y) < threshold)
+ {
+ /* No motion? It's a click! */
+ return FALSE;
+ }
+
+ g_object_get (priv->hadjustment,
+ "value", &horizontal_value,
+ "lower", &min_horizontal_value,
+ "upper", &max_horizontal_value,
+ NULL);
+
+ g_object_get (priv->vadjustment,
+ "value", &vertical_value,
+ "lower", &min_vertical_value,
+ "upper", &max_vertical_value,
+ NULL);
+
+ /* We detect if the user threw what they were dragging by
+ comparing the timestamp of the button release with the
+ timestamp of the last motion. Experimentally, a difference
+ of 0 or 1 millisecond indicates that the mouse is in motion,
+ a larger difference indicates the user dropped what they
+ were dragging. */
+ was_thrown = priv->last_motion_time > 0 && priv->last_motion_time > clutter_event_get_time (event) - 2;
+
+ if (!was_thrown)
+ {
+ new_horizontal_value = horizontal_value;
+ new_vertical_value = vertical_value;
+ }
+ else
+ {
+ GdkRectangle area;
+ gfloat dx, dy;
+
+ dx = priv->drag_start_x - x;
+ dy = priv->drag_start_y - y;
+ get_area_of_monitor_surrounding_actor (CLUTTER_ACTOR (self), &area);
+
+ if (st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_RTL)
+ dx *= -1;
+
+ new_horizontal_value = horizontal_value + (dx / area.width) * (max_horizontal_value - min_horizontal_value);
+ new_vertical_value = vertical_value + (dy / area.height) * (max_vertical_value - min_vertical_value);
+ }
+
+ st_adjustment_interpolate (priv->hadjustment, new_horizontal_value, ANIMATION_TIME);
+ st_adjustment_interpolate (priv->vadjustment, new_vertical_value, ANIMATION_TIME);
+
+ return TRUE;
+}
+
+static gboolean
+handle_captured_motion_event (StScrollView *self,
+ ClutterEvent *event)
+{
+ StScrollViewPrivate *priv = self->priv;
+ GtkSettings *settings;
+ int threshold;
+ gfloat x, y;
+ gfloat dx, dy;
+ GdkRectangle area;
+ gdouble horizontal_value, horizontal_page_size;
+ gdouble vertical_value, vertical_page_size;
+
+ if (!priv->in_swipe)
+ return FALSE;
+
+ settings = gtk_settings_get_default();
+ g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &threshold, NULL);
+
+ clutter_event_get_coords (event, &x, &y);
+
+ g_object_get (priv->hadjustment,
+ "value", &horizontal_value,
+ "page-size", &horizontal_page_size,
+ NULL);
+
+ g_object_get (priv->vadjustment,
+ "value", &vertical_value,
+ "page-size", &vertical_page_size,
+ NULL);
+
+ dx = priv->drag_x - x;
+ dy = priv->drag_y - y;
+ get_area_of_monitor_surrounding_actor (CLUTTER_ACTOR (self), &area);
+
+ priv->drag_x = x;
+ priv->drag_y = y;
+ priv->last_motion_time = clutter_event_get_time (event);
+
+ /* See if the user has moved the mouse enough to trigger
+ a drag */
+ if ((ABS (x - priv->drag_start_x) < threshold) &&
+ (ABS (y - priv->drag_start_y) < threshold))
+ return TRUE;
+
+ if (st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_RTL)
+ st_adjustment_set_value (priv->hadjustment, horizontal_value - (dx / area.width) * horizontal_page_size);
+ else
+ st_adjustment_set_value (priv->hadjustment, horizontal_value + (dx / area.width) * horizontal_page_size);
+
+ st_adjustment_set_value (priv->vadjustment, vertical_value + (dy / area.height) * vertical_page_size);
+ return TRUE;
+}
+
+static gboolean
+on_captured_event (ClutterActor *stage,
+ ClutterEvent *event,
+ gpointer data)
+{
+ StScrollView *self = ST_SCROLL_VIEW (data);
+
+ switch (event->type)
+ {
+ case CLUTTER_BUTTON_PRESS:
+ return handle_captured_button_press_event (self, event);
+
+ case CLUTTER_BUTTON_RELEASE:
+ return handle_captured_button_release_event (self, event);
+
+ case CLUTTER_MOTION:
+ return handle_captured_motion_event (self, event);
+
+ /* Block enter/leave events to avoid prelights
+ during swipe-scroll */
+ case CLUTTER_ENTER:
+ case CLUTTER_LEAVE:
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
st_scroll_view_class_init (StScrollViewClass *klass)
{
GParamSpec *pspec;
@@ -795,6 +1062,43 @@ st_scroll_view_class_init (StScrollViewClass *klass)
PROP_MOUSE_SCROLL,
pspec);
+ pspec = g_param_spec_boolean ("enable-swipe-scrolling",
+ "Enable Swipe Scrolling",
+ "Enable scrolling when dragging the mouse",
+ TRUE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class,
+ PROP_SWIPE_SCROLL,
+ pspec);
+
+}
+
+static void
+on_notify_visible (GObject *object,
+ GParamSpec param_id,
+ gpointer data)
+{
+ StScrollView *self = ST_SCROLL_VIEW (object);
+ ClutterActor *actor = CLUTTER_ACTOR (self);
+ StScrollViewPrivate *priv = self->priv;
+
+ if (CLUTTER_ACTOR_IS_VISIBLE (actor))
+ {
+ if (priv->captured_event_id <= 0)
+ priv->captured_event_id = g_signal_connect (clutter_actor_get_stage (actor),
+ "captured-event",
+ G_CALLBACK (on_captured_event),
+ self);
+ }
+ else
+ {
+ if (priv->captured_event_id > 0)
+ {
+ g_signal_handler_disconnect (clutter_actor_get_stage (actor),
+ priv->captured_event_id);
+ priv->captured_event_id = 0;
+ }
+ }
}
static void
@@ -823,6 +1127,10 @@ st_scroll_view_init (StScrollView *self)
/* mouse scroll is enabled by default, so we also need to be reactive */
priv->mouse_scroll = TRUE;
g_object_set (G_OBJECT (self), "reactive", TRUE, NULL);
+
+ g_signal_connect (G_OBJECT (self), "notify::visible",
+ G_CALLBACK (on_notify_visible),
+ NULL);
}
static void
@@ -1057,6 +1365,38 @@ st_scroll_view_get_mouse_scrolling (StScrollView *scroll)
return priv->mouse_scroll;
}
+void
+st_scroll_view_set_swipe_scrolling (StScrollView *scroll,
+ gboolean enabled)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ if (priv->swipe_scroll != enabled)
+ {
+ priv->swipe_scroll = enabled;
+
+ /* make sure we can receive swipe events */
+ if (enabled)
+ clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE);
+ }
+}
+
+gboolean
+st_scroll_view_get_swipe_scrolling (StScrollView *scroll)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ return priv->swipe_scroll;
+}
+
/**
* st_scroll_view_set_policy:
* @scroll: A #StScrollView
diff --git a/src/st/st-scroll-view.h b/src/st/st-scroll-view.h
index c00d16e..ad2d203 100644
--- a/src/st/st-scroll-view.h
+++ b/src/st/st-scroll-view.h
@@ -80,6 +80,10 @@ void st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
gboolean enabled);
gboolean st_scroll_view_get_mouse_scrolling (StScrollView *scroll);
+void st_scroll_view_set_swipe_scrolling (StScrollView *scroll,
+ gboolean enabled);
+gboolean st_scroll_view_get_swipe_scrolling (StScrollView *scroll);
+
void st_scroll_view_set_policy (StScrollView *scroll,
GtkPolicyType hscroll,
GtkPolicyType vscroll);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]