summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2015-06-06 20:20:03 +0200
committerJiří Techet <techet@gmail.com>2015-08-31 00:19:37 +0200
commitddc5f2087b0a02c2e33c1939e9d310e37eb46f33 (patch)
tree84ebe695f68f910411a2bcc9f57b5ef64ce8a699
parent30c52851a8e73baa81a4439492c8022d3a5a90b5 (diff)
downloadlibchamplain-ddc5f2087b0a02c2e33c1939e9d310e37eb46f33.tar.gz
view: Implement pinch/zoom gesture
Set up a ClutterZoomAction listening for 2-finger touch gestures, and make it take control over over the view position/zoom level. Zooming in/out isn't too smooth yet, because we're constrained to discrete zoom levels (it does animate between these though), this is enough at least for the expected interaction. https://bugzilla.gnome.org/show_bug.cgi?id=709606
-rw-r--r--champlain/champlain-view.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/champlain/champlain-view.c b/champlain/champlain-view.c
index 6ad4b4e..7b96b18 100644
--- a/champlain/champlain-view.c
+++ b/champlain/champlain-view.c
@@ -206,6 +206,13 @@ struct _ChamplainViewPrivate
gint tile_y_first;
gint tile_x_last;
gint tile_y_last;
+
+ /* Zoom gesture */
+ ClutterGestureAction *zoom_gesture;
+ guint initial_gesture_zoom;
+ gdouble focus_lat;
+ gdouble focus_lon;
+ gboolean zoom_started;
};
G_DEFINE_TYPE (ChamplainView, champlain_view, CLUTTER_TYPE_ACTOR);
@@ -658,6 +665,14 @@ champlain_view_dispose (GObject *object)
priv->tile_map = NULL;
}
+ if (priv->zoom_gesture)
+ {
+ clutter_actor_remove_action (CLUTTER_ACTOR (view),
+ CLUTTER_ACTION (priv->zoom_gesture));
+ priv->zoom_gesture = NULL;
+ }
+
+
priv->map_layer = NULL;
priv->license_actor = NULL;
priv->user_layers = NULL;
@@ -1101,6 +1116,93 @@ slice_free_gint64 (gpointer data)
}
+static guint
+view_find_suitable_zoom (ChamplainView *view,
+ gdouble factor)
+{
+ ChamplainViewPrivate *priv = GET_PRIVATE (view);
+ guint zoom_level = priv->initial_gesture_zoom;
+
+ while (factor > 2)
+ {
+ factor /= 2;
+ zoom_level++;
+ }
+
+ while (factor < 0.5)
+ {
+ factor *= 2;
+ zoom_level--;
+ }
+
+ return zoom_level;
+}
+
+
+static gboolean
+zoom_gesture_zoom_cb (ClutterZoomAction *gesture,
+ G_GNUC_UNUSED ClutterActor *actor,
+ ClutterPoint *focal_point,
+ gdouble factor,
+ gpointer user_data)
+{
+ ChamplainView *view = user_data;
+ ChamplainViewPrivate *priv = GET_PRIVATE (view);
+ gdouble dx, dy, lat, lon;
+ ClutterPoint focus;
+
+ if (!priv->zoom_started)
+ {
+ priv->zoom_started = TRUE;
+ priv->focus_lat = champlain_view_y_to_latitude (user_data, focal_point->y);
+ priv->focus_lon = champlain_view_x_to_longitude (user_data, focal_point->x);
+ priv->initial_gesture_zoom = priv->zoom_level;
+ }
+ else
+ {
+ guint zoom_level;
+
+ zoom_level = view_find_suitable_zoom (view, factor);
+
+ focus.x = champlain_map_source_get_x (priv->map_source,
+ zoom_level, priv->focus_lon);
+ focus.y = champlain_map_source_get_y (priv->map_source,
+ zoom_level, priv->focus_lat);
+
+ dx = (priv->viewport_width / 2.0) - focal_point->x;
+ dy = (priv->viewport_height / 2.0) - focal_point->y;
+
+ lon = champlain_map_source_get_longitude (priv->map_source, zoom_level, focus.x + dx);
+ lat = champlain_map_source_get_latitude (priv->map_source, zoom_level, focus.y + dy);
+
+ champlain_view_center_on (view, lat, lon);
+ champlain_view_set_zoom_level (view, zoom_level);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+zoom_gesture_begin_cb (ClutterGestureAction *gesture,
+ G_GNUC_UNUSED ClutterActor *actor,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ /* Give up on >2 finger input */
+ return clutter_gesture_action_get_n_current_points (gesture) == 2;
+}
+
+
+static void
+zoom_gesture_finish_cb (ClutterGestureAction *gesture,
+ G_GNUC_UNUSED ClutterActor *actor,
+ gpointer user_data)
+{
+ ChamplainViewPrivate *priv = GET_PRIVATE (user_data);
+
+ priv->zoom_started = FALSE;
+}
+
+
static void
champlain_view_init (ChamplainView *view)
{
@@ -1194,6 +1296,19 @@ champlain_view_init (ChamplainView *view)
g_signal_connect (priv->kinetic_scroll, "button-press-event",
G_CALLBACK (kinetic_scroll_button_press_cb), view);
+ /* Setup zoom gesture */
+ priv->zoom_gesture = CLUTTER_GESTURE_ACTION (clutter_zoom_action_new ());
+ g_signal_connect (priv->zoom_gesture, "zoom",
+ G_CALLBACK (zoom_gesture_zoom_cb), view);
+ g_signal_connect (priv->zoom_gesture, "gesture-begin",
+ G_CALLBACK (zoom_gesture_begin_cb), view);
+ g_signal_connect (priv->zoom_gesture, "gesture-end",
+ G_CALLBACK (zoom_gesture_finish_cb), view);
+ g_signal_connect (priv->zoom_gesture, "gesture-cancel",
+ G_CALLBACK (zoom_gesture_finish_cb), view);
+ clutter_actor_add_action (CLUTTER_ACTOR (view),
+ CLUTTER_ACTION (priv->zoom_gesture));
+
/* Setup stage */
clutter_actor_add_child (CLUTTER_ACTOR (view), priv->kinetic_scroll);
priv->zoom_overlay_actor = clutter_actor_new ();