From 5bb629a709fcf0f2ba3755f6786b0bdeb5ce452e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 18 Apr 2014 16:56:32 -0700 Subject: [PATCH] GUAC-324: Fix panning and pinch. Add placeholder menu. --- guacamole/src/main/webapp/client.xhtml | 19 +- .../src/main/webapp/scripts/client-ui.js | 170 +++++++++++++----- guacamole/src/main/webapp/styles/client.css | 25 ++- 3 files changed, 163 insertions(+), 51 deletions(-) diff --git a/guacamole/src/main/webapp/client.xhtml b/guacamole/src/main/webapp/client.xhtml index 3c36f86d3..4b4d64203 100644 --- a/guacamole/src/main/webapp/client.xhtml +++ b/guacamole/src/main/webapp/client.xhtml @@ -30,7 +30,7 @@ - + Guacamole ${project.version} @@ -38,9 +38,11 @@ -
-
-
+
+
+
+
+
@@ -51,6 +53,15 @@
+ + +
diff --git a/guacamole/src/main/webapp/scripts/client-ui.js b/guacamole/src/main/webapp/scripts/client-ui.js index 97f95015a..76c194b44 100644 --- a/guacamole/src/main/webapp/scripts/client-ui.js +++ b/guacamole/src/main/webapp/scripts/client-ui.js @@ -219,6 +219,7 @@ GuacUI.Client = { /* UI Components */ "viewport" : document.getElementById("viewportClone"), + "main" : document.getElementById("main"), "display" : document.getElementById("display"), "notification_area" : document.getElementById("notificationArea"), @@ -430,7 +431,7 @@ GuacUI.Client.Pan = function(element) { * @private */ var guac_pan = this; - + /** * The starting X location of the pan gesture. * @private @@ -566,12 +567,26 @@ GuacUI.Client.Pinch = function(element) { */ this.ratio = 1; + /** + * The X-coordinate of the current center of the pinch gesture. + * @type Number + */ + this.centerX = 0; + + /** + * The Y-coordinate of the current center of the pinch gesture. + * @type Number + */ + this.centerY = 0; + /** * Called when a zoom gesture begins. * * @event * @param {Number} ratio The relative value of the starting zoom. This will * ALWAYS be 1. + * @param {Number} x The X-coordinate of the center of the pinch gesture. + * @param {Number} y The Y-coordinate of the center of the pinch gesture. */ this.onzoomstart = null; @@ -581,6 +596,8 @@ GuacUI.Client.Pinch = function(element) { * @event * @param {Number} ratio The relative value of the changed zoom, with 1 * being no change. + * @param {Number} x The X-coordinate of the center of the pinch gesture. + * @param {Number} y The Y-coordinate of the center of the pinch gesture. */ this.onzoomchange = null; @@ -590,6 +607,8 @@ GuacUI.Client.Pinch = function(element) { * @event * @param {Number} ratio The relative value of the final zoom, with 1 * being no change. + * @param {Number} x The X-coordinate of the center of the pinch gesture. + * @param {Number} y The Y-coordinate of the center of the pinch gesture. */ this.onzoomend = null; @@ -613,6 +632,40 @@ GuacUI.Client.Pinch = function(element) { } + /** + * Given a touch event, calculates the center between the first two + * touches in pixels, returning the X coordinate of this center. + * + * @param {TouchEvent} e The touch event to use when performing center + * calculation. + * @return {Number} The X-coordinate of the center of the first two touches. + */ + function pinch_center_x(e) { + + var touch_a = e.touches[0]; + var touch_b = e.touches[1]; + + return (touch_a.clientX + touch_b.clientX) / 2; + + } + + /** + * Given a touch event, calculates the center between the first two + * touches in pixels, returning the Y coordinate of this center. + * + * @param {TouchEvent} e The touch event to use when performing center + * calculation. + * @return {Number} The Y-coordinate of the center of the first two touches. + */ + function pinch_center_y(e) { + + var touch_a = e.touches[0]; + var touch_b = e.touches[1]; + + return (touch_a.clientY + touch_b.clientY) / 2; + + } + // When there are exactly two touches, monitor the distance between // them, firing zoom events as appropriate element.addEventListener("touchmove", function(e) { @@ -624,19 +677,23 @@ GuacUI.Client.Pinch = function(element) { // Calculate current zoom level var current = pinch_distance(e); + // Calculate center + guac_zoom.centerX = pinch_center_x(e); + guac_zoom.centerY = pinch_center_y(e); + // If gesture just starting, fire zoom start if (!start_length) { start_length = current; guac_zoom.ratio = 1; if (guac_zoom.onzoomstart) - guac_zoom.onzoomstart(guac_zoom.ratio); + guac_zoom.onzoomstart(guac_zoom.ratio, guac_zoom.centerX, guac_zoom.centerY); } // Otherwise, notify of zoom change else { guac_zoom.ratio = current / start_length; if (guac_zoom.onzoomchange) - guac_zoom.onzoomchange(guac_zoom.ratio); + guac_zoom.onzoomchange(guac_zoom.ratio, guac_zoom.centerX, guac_zoom.centerY); } } @@ -652,7 +709,7 @@ GuacUI.Client.Pinch = function(element) { start_length = null; if (guac_zoom.onzoomend) - guac_zoom.onzoomend(guac_zoom.ratio); + guac_zoom.onzoomend(guac_zoom.ratio, guac_zoom.centerX, guac_zoom.centerY); guac_zoom.ratio = 1; } @@ -1065,6 +1122,64 @@ GuacUI.Client.attach = function(guac) { }; + /* + * Pinch-to-zoom + */ + + var guac_pinch = new GuacUI.Client.Pinch(document.body); + var initial_scale = null; + var initial_center_x = null; + var initial_center_y = null; + + guac_pinch.onzoomstart = function(ratio, x, y) { + initial_scale = guac.getScale(); + initial_center_x = (x + GuacUI.Client.main.scrollLeft) / initial_scale; + initial_center_y = (y + GuacUI.Client.main.scrollTop) / initial_scale; + }; + + guac_pinch.onzoomchange = function(ratio, x, y) { + + // Rescale based on new ratio + var new_scale = initial_scale * ratio; + new_scale = Math.max(new_scale, GuacUI.Client.min_zoom); + new_scale = Math.min(new_scale, GuacUI.Client.max_zoom); + guac.scale(new_scale); + + // Calculate point at currently at center of touch + var point_at_center_x = (x + GuacUI.Client.main.scrollLeft) / new_scale; + var point_at_center_y = (y + GuacUI.Client.main.scrollTop) / new_scale; + + // Correct position to keep point-of-interest within center of pinch + GuacUI.Client.main.scrollLeft += (initial_center_x - point_at_center_x) * new_scale; + GuacUI.Client.main.scrollTop += (initial_center_y - point_at_center_y) * new_scale; + + }; + + /* + * Touch panning + */ + + var guac_pan = new GuacUI.Client.Pan(document.body); + + var last_pan_dx = 0; + var last_pan_dy = 0; + + guac_pan.onpanstart = function(dx, dy) { + last_pan_dx = dx; + last_pan_dy = dy; + }; + + guac_pan.onpanchange = function(dx, dy) { + if (!touch.currentState.left) { + var change_pan_dx = dx - last_pan_dx; + var change_pan_dy = dy - last_pan_dy; + GuacUI.Client.main.scrollLeft -= change_pan_dx; + GuacUI.Client.main.scrollTop -= change_pan_dy; + last_pan_dx = dx; + last_pan_dy = dy; + } + }; + // Hide any existing status notifications GuacUI.Client.hideStatus(); @@ -1215,48 +1330,6 @@ GuacUI.Client.attach = function(guac) { }, false); - /* - * Pinch-to-zoom - */ - - var guac_pinch = new GuacUI.Client.Pinch(document.body); - var initial_scale = null; - - guac_pinch.onzoomstart = function() { - initial_scale = guac.getScale(); - }; - - guac_pinch.onzoomchange = function(ratio) { - var new_scale = initial_scale * ratio; - new_scale = Math.max(new_scale, GuacUI.Client.min_zoom); - new_scale = Math.min(new_scale, GuacUI.Client.max_zoom); - guac.scale(new_scale); - }; - - /* - * Touch panning - */ - - var guac_pan = new GuacUI.Client.Pan(document.body); - - var last_pan_dx = 0; - var last_pan_dy = 0; - - guac_pan.onpanstart = function(dx, dy) { - last_pan_dx = dx; - last_pan_dy = dy; - }; - - guac_pan.onpanchange = function(dx, dy) { - if (!touch.currentState.left) { - var change_pan_dx = dx - last_pan_dx; - var change_pan_dy = dy - last_pan_dy; - window.scrollBy(-change_pan_dx, -change_pan_dy); - last_pan_dx = dx; - last_pan_dy = dy; - } - }; - /* * Disconnect and update thumbnail on close */ @@ -1417,4 +1490,9 @@ GuacUI.Client.attach = function(guac) { }, false); + // Prevent default on all touch events + document.addEventListener("touchstart", function(e) { + e.preventDefault(); + }, false); + })(); diff --git a/guacamole/src/main/webapp/styles/client.css b/guacamole/src/main/webapp/styles/client.css index 71d99b2a3..713981790 100644 --- a/guacamole/src/main/webapp/styles/client.css +++ b/guacamole/src/main/webapp/styles/client.css @@ -113,6 +113,15 @@ div.dialog p { margin: 0; } +div#main { + overflow: auto; + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + div.displayOuter { height: 100%; width: 100%; @@ -489,4 +498,18 @@ p.hint { width: 0; height: 0; overflow: hidden; -} \ No newline at end of file +} + +/* Menu */ + +#menu { + overflow: auto; + position: fixed; + top: 0; + left: 0; + bottom: 0; + max-width: 100%; + width: 320px; + padding: 1em; + background: red; +}