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/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/keyboard.css b/guacamole/src/main/webapp/app/client/styles/keyboard.css index 270153a4d..665301f68 100644 --- a/guacamole/src/main/webapp/app/client/styles/keyboard.css +++ b/guacamole/src/main/webapp/app/client/styles/keyboard.css @@ -23,9 +23,6 @@ .keyboard-container { text-align: center; - position: fixed; - left: 0; - bottom: 0; width: 100%; margin: 0; padding: 0; @@ -34,10 +31,5 @@ background: #222; opacity: 0.85; - visibility: hidden; z-index: 1; } - -.keyboard-container.shown { - visibility: visible; -} 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..879a53cec 100644 --- a/guacamole/src/main/webapp/app/client/templates/client.html +++ b/guacamole/src/main/webapp/app/client/templates/client.html @@ -21,38 +21,42 @@ --> -
+ +
- -
+ +
- - + + -
+
- -
+ +
+ + +
+ +
+ + +
+ +
- -
-
-
- -
- -
+ -