From c39ba2151b51b2d0d37b030655c4b8487aa4a50a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 28 Dec 2014 02:27:44 -0800 Subject: [PATCH 1/3] GUAC-955: Add guacViewport directive which maintains content within the visible portion of the browser window, even if the browser magically scrolls itself. --- .../app/client/directives/guacViewport.js | 87 +++++++++++++++++++ .../main/webapp/app/client/styles/client.css | 2 +- .../webapp/app/client/styles/viewport.css | 30 +++++++ .../webapp/app/client/templates/client.html | 41 +++++---- .../app/client/templates/guacViewport.html | 23 +++++ 5 files changed, 163 insertions(+), 20 deletions(-) create mode 100644 guacamole/src/main/webapp/app/client/directives/guacViewport.js create mode 100644 guacamole/src/main/webapp/app/client/styles/viewport.css create mode 100644 guacamole/src/main/webapp/app/client/templates/guacViewport.html diff --git a/guacamole/src/main/webapp/app/client/directives/guacViewport.js b/guacamole/src/main/webapp/app/client/directives/guacViewport.js new file mode 100644 index 000000000..6d6d213ac --- /dev/null +++ b/guacamole/src/main/webapp/app/client/directives/guacViewport.js @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A directive which provides a fullscreen environment for its content. + */ +angular.module('client').directive('guacViewport', [function guacViewport() { + + return { + // Element only + restrict: 'E', + scope: false, + transclude: true, + templateUrl: 'app/client/templates/guacViewport.html', + controller: ['$window', '$document', '$element', + function guacViewportController($window, $document, $element) { + + /** + * The fullscreen container element. + * + * @type Element + */ + var element = $element.find('.viewport')[0]; + + /** + * The main document object. + * + * @type Document + */ + var document = $document[0]; + + /** + * The current adjusted height of the viewport element, if any. + * + * @type Number + */ + var currentAdjustedHeight = null; + + // Fit container within visible region when window scrolls + $window.onscroll = function fitScrollArea() { + + // Pull scroll properties + var scrollLeft = document.body.scrollLeft; + var scrollTop = document.body.scrollTop; + var scrollWidth = document.body.scrollWidth; + var scrollHeight = document.body.scrollHeight; + + // Calculate new height + var adjustedHeight = $window.innerHeight - scrollTop; + + // Only update if not in response to our own call to scrollTo() + if (scrollLeft !== scrollWidth && scrollTop !== scrollHeight + && currentAdjustedHeight !== adjustedHeight) { + + // Adjust element to fit exactly within visible area + element.style.height = adjustedHeight + 'px'; + currentAdjustedHeight = adjustedHeight; + + // Scroll to bottom + $window.scrollTo(scrollWidth, scrollHeight); + + } + + }; + + }] + }; +}]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/client/styles/client.css b/guacamole/src/main/webapp/app/client/styles/client.css index 6f20b41b4..90f0ed3a7 100644 --- a/guacamole/src/main/webapp/app/client/styles/client.css +++ b/guacamole/src/main/webapp/app/client/styles/client.css @@ -39,7 +39,7 @@ body.client { .client-view { display: table; - position: fixed; + position: absolute; top: 0; left: 0; width: 100%; diff --git a/guacamole/src/main/webapp/app/client/styles/viewport.css b/guacamole/src/main/webapp/app/client/styles/viewport.css new file mode 100644 index 000000000..d22120c02 --- /dev/null +++ b/guacamole/src/main/webapp/app/client/styles/viewport.css @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +.viewport { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; +} \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/client/templates/client.html b/guacamole/src/main/webapp/app/client/templates/client.html index 02ba4dd26..88abf2dc7 100644 --- a/guacamole/src/main/webapp/app/client/templates/client.html +++ b/guacamole/src/main/webapp/app/client/templates/client.html @@ -21,35 +21,38 @@ --> -
+ +
- -
+ +
- - + + -
+
- -
+ +
+ + +
+ +
- -
-
-
+ +
+ +
- -
- -
+ + +
+ +
+
- -
- -
diff --git a/guacamole/src/main/webapp/app/osk/directives/guacOsk.js b/guacamole/src/main/webapp/app/osk/directives/guacOsk.js index f8891e437..b9ee43963 100644 --- a/guacamole/src/main/webapp/app/osk/directives/guacOsk.js +++ b/guacamole/src/main/webapp/app/osk/directives/guacOsk.js @@ -57,6 +57,13 @@ angular.module('osk').directive('guacOsk', [function guacOsk() { */ var main = $element[0]; + /** + * The element which functions as a detector for size changes. + * + * @type Element + */ + var resizeSensor = $element.find('.resize-sensor')[0]; + /** * Event listener which resizes the current keyboard, if any, such * that it fits within available space. @@ -73,8 +80,10 @@ angular.module('osk').directive('guacOsk', [function guacOsk() { $scope.$watch("layout", function setLayout(layout) { // Remove current keyboard - keyboard = null; - main.innerHTML = ""; + if (keyboard) { + main.removeChild(keyboard.getElement()); + keyboard = null; + } // Load new keyboard if (layout) { @@ -96,18 +105,13 @@ angular.module('osk').directive('guacOsk', [function guacOsk() { $rootScope.$broadcast('guacSyntheticKeyup', keysym); }; - // Resize keyboard whenever window changes size - $window.addEventListener('resize', resizeListener); + // Resize keyboard whenever element changes size + resizeSensor.contentWindow.addEventListener('resize', resizeListener); } }); // end layout scope watch - // Clean up event listeners upon destroy - $scope.$on('$destroy', function destroyKeyboard() { - $window.removeEventListener('resize', resizeListener); - }); - }] }; diff --git a/guacamole/src/main/webapp/app/osk/styles/osk.css b/guacamole/src/main/webapp/app/osk/styles/osk.css index ae5d26bf9..c36d40629 100644 --- a/guacamole/src/main/webapp/app/osk/styles/osk.css +++ b/guacamole/src/main/webapp/app/osk/styles/osk.css @@ -20,6 +20,18 @@ * THE SOFTWARE. */ +.osk .resize-sensor { + height: 100%; + width: 100%; + position: absolute; + left: 0; + top: 0; + overflow: hidden; + border: none; + opacity: 0; + z-index: -1; +} + .guac-keyboard { display: inline-block; width: 100%; diff --git a/guacamole/src/main/webapp/app/osk/templates/blank.html b/guacamole/src/main/webapp/app/osk/templates/blank.html new file mode 100644 index 000000000..62b9f6aa2 --- /dev/null +++ b/guacamole/src/main/webapp/app/osk/templates/blank.html @@ -0,0 +1,22 @@ + + diff --git a/guacamole/src/main/webapp/app/osk/templates/guacOsk.html b/guacamole/src/main/webapp/app/osk/templates/guacOsk.html index 42a73462c..e541dc64b 100644 --- a/guacamole/src/main/webapp/app/osk/templates/guacOsk.html +++ b/guacamole/src/main/webapp/app/osk/templates/guacOsk.html @@ -21,4 +21,7 @@ THE SOFTWARE. --> + + + From d3e4a5307c277fd8473e4eb39705fa73f07d46e2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 28 Dec 2014 14:47:04 -0800 Subject: [PATCH 3/3] GUAC-955: Add element module and guacScroll directive. Refactor guacFocus to element module. Use guacScroll to update menu scroll during drag. --- .../main/webapp/app/client/clientModule.js | 2 +- .../client/controllers/clientController.js | 29 +++++-- .../webapp/app/client/templates/client.html | 2 +- .../directives/guacFocus.js | 2 +- .../app/element/directives/guacScroll.js | 85 +++++++++++++++++++ .../main/webapp/app/element/elementModule.js | 27 ++++++ .../webapp/app/element/types/ScrollState.js | 63 ++++++++++++++ .../src/main/webapp/app/login/loginModule.js | 2 +- 8 files changed, 203 insertions(+), 9 deletions(-) rename guacamole/src/main/webapp/app/{login => element}/directives/guacFocus.js (96%) create mode 100644 guacamole/src/main/webapp/app/element/directives/guacScroll.js create mode 100644 guacamole/src/main/webapp/app/element/elementModule.js create mode 100644 guacamole/src/main/webapp/app/element/types/ScrollState.js diff --git a/guacamole/src/main/webapp/app/client/clientModule.js b/guacamole/src/main/webapp/app/client/clientModule.js index cb66904a6..ebdb45fe1 100644 --- a/guacamole/src/main/webapp/app/client/clientModule.js +++ b/guacamole/src/main/webapp/app/client/clientModule.js @@ -23,4 +23,4 @@ /** * The module for code used to connect to a connection or balancing group. */ -angular.module('client', ['auth', 'history', 'osk', 'rest', 'textInput', 'touch']); +angular.module('client', ['auth', 'element', 'history', 'osk', 'rest', 'textInput', 'touch']); diff --git a/guacamole/src/main/webapp/app/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js index 93c50cc63..fd03d5508 100644 --- a/guacamole/src/main/webapp/app/client/controllers/clientController.js +++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js @@ -26,6 +26,14 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams', '$injector', function clientController($scope, $routeParams, $injector) { + // Required types + var ClientProperties = $injector.get('ClientProperties'); + var ScrollState = $injector.get('ScrollState'); + + // Required services + var connectionGroupService = $injector.get('connectionGroupService'); + var connectionService = $injector.get('connectionService'); + /** * The minimum number of pixels a drag gesture must move to result in the * menu being shown or hidden. @@ -156,11 +164,6 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams', remaining: 15 }; - // Get services for reading connections and groups - var connectionGroupService = $injector.get('connectionGroupService'); - var connectionService = $injector.get('connectionService'); - var ClientProperties = $injector.get('ClientProperties'); - // Client settings and state $scope.clientProperties = new ClientProperties(); @@ -178,6 +181,13 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams', $scope.menuShown = false; }; + /** + * The current scroll state of the menu. + * + * @type ScrollState + */ + $scope.menuScrollState = new ScrollState(); + // Update the model when clipboard data received from client $scope.$on('guacClientClipboard', function clientClipboardListener(event, client, mimetype, clipboardData) { $scope.clipboardData = clipboardData; @@ -228,10 +238,19 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams', // Hide menu when the user swipes from the right $scope.menuDrag = function menuDrag(inProgress, startX, startY, currentX, currentY, deltaX, deltaY) { + // Hide menu if swipe gesture is detected if (Math.abs(currentY - startY) < MENU_DRAG_VERTICAL_TOLERANCE && startX - currentX >= MENU_DRAG_DELTA) $scope.menuShown = false; + // Scroll menu by default + else { + $scope.menuScrollState.left -= deltaX; + $scope.menuScrollState.top -= deltaY; + } + + return false; + }; // Update menu or client based on dragging gestures diff --git a/guacamole/src/main/webapp/app/client/templates/client.html b/guacamole/src/main/webapp/app/client/templates/client.html index 9bc5776db..879a53cec 100644 --- a/guacamole/src/main/webapp/app/client/templates/client.html +++ b/guacamole/src/main/webapp/app/client/templates/client.html @@ -56,7 +56,7 @@ -