mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-724: Implement base support for displaying multiple connections in a tiled grid.
This commit is contained in:
@@ -279,20 +279,37 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
/**
|
||||
* The client which should be attached to the client UI.
|
||||
*
|
||||
* @type ManagedClient
|
||||
* @type ManagedClient[]
|
||||
*/
|
||||
$scope.client = guacClientManager.getManagedClient($routeParams.id, $routeParams.params);
|
||||
$scope.clients = (function getClients() {
|
||||
|
||||
var clients = [];
|
||||
|
||||
var ids = $routeParams.id.split(/[ +]/);
|
||||
ids.forEach(function addClient(id) {
|
||||
clients.push(guacClientManager.getManagedClient(id, $routeParams.params));
|
||||
});
|
||||
|
||||
return clients;
|
||||
|
||||
})();
|
||||
|
||||
/**
|
||||
* All active clients which are not the current client ($scope.client).
|
||||
* All active clients which are not any current client ($scope.clients).
|
||||
* Each key is the ID of the connection used by that client.
|
||||
*
|
||||
* @type Object.<String, ManagedClient>
|
||||
*/
|
||||
$scope.otherClients = (function getOtherClients(clients) {
|
||||
|
||||
var otherClients = angular.extend({}, clients);
|
||||
delete otherClients[$scope.client.id];
|
||||
|
||||
$scope.clients.forEach(function removeActiveCLient(client) {
|
||||
delete otherClients[client.id];
|
||||
});
|
||||
|
||||
return otherClients;
|
||||
|
||||
})(guacClientManager.getManagedClients());
|
||||
|
||||
/**
|
||||
@@ -526,7 +543,9 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
$scope.menu.connectionParameters = ManagedClient.getArgumentModel($scope.client);
|
||||
|
||||
// Disable client keyboard if the menu is shown
|
||||
$scope.client.clientProperties.keyboardEnabled = !menuShown;
|
||||
angular.forEach($scope.clients, function updateKeyboardEnabled(client) {
|
||||
client.clientProperties.keyboardEnabled = !menuShown;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -731,7 +750,15 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
* otherwise.
|
||||
*/
|
||||
$scope.isConnectionUnstable = function isConnectionUnstable() {
|
||||
return $scope.client && $scope.client.clientState.tunnelUnstable;
|
||||
|
||||
var unstable = false;
|
||||
|
||||
angular.forEach($scope.clients, function checkStability(client) {
|
||||
unstable |= client.clientState.tunnelUnstable;
|
||||
});
|
||||
|
||||
return unstable;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -421,7 +421,7 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
||||
|
||||
// Translate local keydown events to remote keydown events if keyboard is enabled
|
||||
$scope.$on('guacKeydown', function keydownListener(event, keysym, keyboard) {
|
||||
if ($scope.client.clientProperties.keyboardEnabled && !event.defaultPrevented) {
|
||||
if ($scope.client.clientProperties.keyboardEnabled && $scope.client.clientProperties.focused) {
|
||||
client.sendKeyEvent(1, keysym);
|
||||
event.preventDefault();
|
||||
}
|
||||
@@ -429,7 +429,7 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
||||
|
||||
// Translate local keyup events to remote keyup events if keyboard is enabled
|
||||
$scope.$on('guacKeyup', function keyupListener(event, keysym, keyboard) {
|
||||
if ($scope.client.clientProperties.keyboardEnabled && !event.defaultPrevented) {
|
||||
if ($scope.client.clientProperties.keyboardEnabled && $scope.client.clientProperties.focused) {
|
||||
client.sendKeyEvent(0, keysym);
|
||||
event.preventDefault();
|
||||
}
|
||||
@@ -437,12 +437,14 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
||||
|
||||
// Universally handle all synthetic keydown events
|
||||
$scope.$on('guacSyntheticKeydown', function syntheticKeydownListener(event, keysym) {
|
||||
client.sendKeyEvent(1, keysym);
|
||||
if ($scope.client.clientProperties.focused)
|
||||
client.sendKeyEvent(1, keysym);
|
||||
});
|
||||
|
||||
// Universally handle all synthetic keyup events
|
||||
$scope.$on('guacSyntheticKeyup', function syntheticKeyupListener(event, keysym) {
|
||||
client.sendKeyEvent(0, keysym);
|
||||
if ($scope.client.clientProperties.focused)
|
||||
client.sendKeyEvent(0, keysym);
|
||||
});
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A directive which displays one or more Guacamole clients in an evenly-tiled
|
||||
* view. The number of rows and columns used for the arrangement of tiles is
|
||||
* automatically determined by the number of clients present.
|
||||
*/
|
||||
angular.module('client').directive('guacTiledClients', [function guacTiledClients() {
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: 'app/client/templates/guacTiledClients.html',
|
||||
};
|
||||
|
||||
directive.scope = {
|
||||
|
||||
/**
|
||||
* The Guacamole clients that should be displayed in an evenly-tiled
|
||||
* grid arrangement.
|
||||
*
|
||||
* @type ManagedClient[]
|
||||
*/
|
||||
clients : '='
|
||||
|
||||
};
|
||||
|
||||
directive.controller = ['$scope', '$injector', '$element',
|
||||
function guacTiledListController($scope, $injector, $element) {
|
||||
|
||||
/**
|
||||
* Returns the number of columns that should be used to evenly arrange
|
||||
* all provided clients in a tiled grid.
|
||||
*
|
||||
* @returns {Number}
|
||||
* The number of columns that should be used for the grid of
|
||||
* clients.
|
||||
*/
|
||||
var getColumns = function getColumns() {
|
||||
|
||||
if (!$scope.clients || !$scope.clients.length)
|
||||
return 0;
|
||||
|
||||
return Math.ceil(Math.sqrt($scope.clients.length));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of rows that should be used to evenly arrange all
|
||||
* provided clients in a tiled grid.
|
||||
*
|
||||
* @returns {Number}
|
||||
* The number of rows that should be used for the grid of clients.
|
||||
*/
|
||||
var getRows = function getRows() {
|
||||
|
||||
if (!$scope.clients || !$scope.clients.length)
|
||||
return 0;
|
||||
|
||||
return Math.ceil($scope.clients.length / getColumns());
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Assigns keyboard focus to the given client, allowing that client to
|
||||
* receive and handle keyboard events. Multiple clients may have
|
||||
* keyboard focus simultaneously.
|
||||
*
|
||||
* @param {ManagedClient} client
|
||||
* The client that should receive keyboard focus.
|
||||
*/
|
||||
$scope.assignFocus = function assignFocus(client) {
|
||||
client.clientProperties.focused = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether multiple clients are currently shown within the
|
||||
* tiled grid.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if two or more clients are currently present, false
|
||||
* otherwise.
|
||||
*/
|
||||
$scope.hasMultipleClients = function hasMultipleClients() {
|
||||
return $scope.clients && $scope.clients.length > 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the CSS width that should be applied to each tile to
|
||||
* achieve an even arrangement.
|
||||
*
|
||||
* @returns {String}
|
||||
* The CSS width that should be applied to each tile.
|
||||
*/
|
||||
$scope.getTileWidth = function getTileWidth() {
|
||||
return Math.floor(100 / getColumns()) + '%';
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the CSS height that should be applied to each tile to
|
||||
* achieve an even arrangement.
|
||||
*
|
||||
* @returns {String}
|
||||
* The CSS height that should be applied to each tile.
|
||||
*/
|
||||
$scope.getTileHeight = function getTileHeight() {
|
||||
return Math.floor(100 / getRows()) + '%';
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the display title of the given Guacamole client. If the
|
||||
* title is not yet known, a placeholder title will be returned.
|
||||
*
|
||||
* @param {ManagedClient} client
|
||||
* The client whose title should be retrieved.
|
||||
*
|
||||
* @returns {String}
|
||||
* The title of the given client, or a placeholder title if the
|
||||
* client's title is not yet known.
|
||||
*/
|
||||
$scope.getClientTitle = function getClientTitle(client) {
|
||||
return client.title || '...';
|
||||
};
|
||||
|
||||
}];
|
||||
|
||||
return directive;
|
||||
|
||||
}]);
|
@@ -103,7 +103,7 @@ body.client {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.client-view .client-body .main {
|
||||
.client-view .client-body .tiled-client-list {
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.tiled-client-list {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.tiled-client-list li.client-tile {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tiled-client-list li.client-tile h3 {
|
||||
margin: 0;
|
||||
background: #444;
|
||||
padding: 0 0.25em;
|
||||
font-size: 0.8em;
|
||||
color: white;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tiled-client-list.multiple-clients li.client-tile h3 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tiled-client-list.multiple-clients li.client-tile {
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
.tiled-client-list li.client-tile.focused {
|
||||
border-color: #3161a9;
|
||||
}
|
||||
|
||||
.tiled-client-list li.client-tile.focused h3 {
|
||||
background-color: #3161a9;
|
||||
}
|
||||
|
||||
.tiled-client-list li.client-tile .main {
|
||||
flex: 1;
|
||||
}
|
@@ -8,8 +8,8 @@
|
||||
<!-- Central portion of view -->
|
||||
<div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch">
|
||||
|
||||
<!-- Client for current connection -->
|
||||
<guac-client client="client"></guac-client>
|
||||
<!-- All connections in current display -->
|
||||
<guac-tiled-clients clients="clients"></guac-tiled-clients>
|
||||
|
||||
<!-- All other active connections -->
|
||||
<div id="other-connections">
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<a class="connection" ng-href="{{ item.getClientURL() }}">
|
||||
<div class="icon type" ng-class="item.protocol"></div>
|
||||
<input type="checkbox">
|
||||
<span class="name">{{item.name}}</span>
|
||||
</a>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<a class="connection-group" ng-href="{{ item.getClientURL() }}">
|
||||
<div ng-show="item.balancing" class="icon type balancer"></div>
|
||||
<input type="checkbox">
|
||||
<span class="name">{{item.name}}</span>
|
||||
</a>
|
||||
|
@@ -0,0 +1,13 @@
|
||||
<ul class="tiled-client-list" ng-class="{ 'multiple-clients' : hasMultipleClients() }">
|
||||
|
||||
<li class="client-tile"
|
||||
ng-repeat="client in clients"
|
||||
ng-style="{ 'width' : getTileWidth(), 'height' : getTileHeight() }"
|
||||
ng-class="{ 'focused' : client.clientProperties.focused }"
|
||||
ng-click="assignFocus(client)">
|
||||
|
||||
<h3>{{ getClientTitle(client) }}</h3>
|
||||
<guac-client client="client"></guac-client>
|
||||
</li>
|
||||
|
||||
</ul>
|
@@ -69,12 +69,20 @@ angular.module('client').factory('ClientProperties', ['$injector', function defi
|
||||
this.maxScale = template.maxScale || 3;
|
||||
|
||||
/**
|
||||
* Whether or not the client should listen to keyboard events.
|
||||
* Whether this client should listen to keyboard events that it
|
||||
* receives.
|
||||
*
|
||||
* @type Boolean
|
||||
*/
|
||||
this.keyboardEnabled = template.keyboardEnabled || true;
|
||||
|
||||
/**
|
||||
* Whether this client should receive keyboard events.
|
||||
*
|
||||
* @type Boolean
|
||||
*/
|
||||
this.focused = template.focused || false;
|
||||
|
||||
/**
|
||||
* Whether translation of touch to mouse events should emulate an
|
||||
* absolute pointer device, or a relative pointer device.
|
||||
|
Reference in New Issue
Block a user