diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2015-06-06 20:20:03 +0200 |
---|---|---|
committer | Jiří Techet <techet@gmail.com> | 2015-08-31 00:19:37 +0200 |
commit | ddc5f2087b0a02c2e33c1939e9d310e37eb46f33 (patch) | |
tree | 84ebe695f68f910411a2bcc9f57b5ef64ce8a699 | |
parent | 30c52851a8e73baa81a4439492c8022d3a5a90b5 (diff) | |
download | libchamplain-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.c | 115 |
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 (); |