mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 13:41:21 +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.
|
* 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.
|
* Each key is the ID of the connection used by that client.
|
||||||
*
|
*
|
||||||
* @type Object.<String, ManagedClient>
|
* @type Object.<String, ManagedClient>
|
||||||
*/
|
*/
|
||||||
$scope.otherClients = (function getOtherClients(clients) {
|
$scope.otherClients = (function getOtherClients(clients) {
|
||||||
|
|
||||||
var otherClients = angular.extend({}, clients);
|
var otherClients = angular.extend({}, clients);
|
||||||
delete otherClients[$scope.client.id];
|
|
||||||
|
$scope.clients.forEach(function removeActiveCLient(client) {
|
||||||
|
delete otherClients[client.id];
|
||||||
|
});
|
||||||
|
|
||||||
return otherClients;
|
return otherClients;
|
||||||
|
|
||||||
})(guacClientManager.getManagedClients());
|
})(guacClientManager.getManagedClients());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -526,7 +543,9 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
$scope.menu.connectionParameters = ManagedClient.getArgumentModel($scope.client);
|
$scope.menu.connectionParameters = ManagedClient.getArgumentModel($scope.client);
|
||||||
|
|
||||||
// Disable client keyboard if the menu is shown
|
// 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.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
$scope.isConnectionUnstable = function isConnectionUnstable() {
|
$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
|
// Translate local keydown events to remote keydown events if keyboard is enabled
|
||||||
$scope.$on('guacKeydown', function keydownListener(event, keysym, keyboard) {
|
$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);
|
client.sendKeyEvent(1, keysym);
|
||||||
event.preventDefault();
|
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
|
// Translate local keyup events to remote keyup events if keyboard is enabled
|
||||||
$scope.$on('guacKeyup', function keyupListener(event, keysym, keyboard) {
|
$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);
|
client.sendKeyEvent(0, keysym);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
@@ -437,11 +437,13 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
|
|
||||||
// Universally handle all synthetic keydown events
|
// Universally handle all synthetic keydown events
|
||||||
$scope.$on('guacSyntheticKeydown', function syntheticKeydownListener(event, keysym) {
|
$scope.$on('guacSyntheticKeydown', function syntheticKeydownListener(event, keysym) {
|
||||||
|
if ($scope.client.clientProperties.focused)
|
||||||
client.sendKeyEvent(1, keysym);
|
client.sendKeyEvent(1, keysym);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Universally handle all synthetic keyup events
|
// Universally handle all synthetic keyup events
|
||||||
$scope.$on('guacSyntheticKeyup', function syntheticKeyupListener(event, keysym) {
|
$scope.$on('guacSyntheticKeyup', function syntheticKeyupListener(event, keysym) {
|
||||||
|
if ($scope.client.clientProperties.focused)
|
||||||
client.sendKeyEvent(0, keysym);
|
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;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-view .client-body .main {
|
.client-view .client-body .tiled-client-list {
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
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 -->
|
<!-- Central portion of view -->
|
||||||
<div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch">
|
<div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch">
|
||||||
|
|
||||||
<!-- Client for current connection -->
|
<!-- All connections in current display -->
|
||||||
<guac-client client="client"></guac-client>
|
<guac-tiled-clients clients="clients"></guac-tiled-clients>
|
||||||
|
|
||||||
<!-- All other active connections -->
|
<!-- All other active connections -->
|
||||||
<div id="other-connections">
|
<div id="other-connections">
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<a class="connection" ng-href="{{ item.getClientURL() }}">
|
<a class="connection" ng-href="{{ item.getClientURL() }}">
|
||||||
<div class="icon type" ng-class="item.protocol"></div>
|
<div class="icon type" ng-class="item.protocol"></div>
|
||||||
|
<input type="checkbox">
|
||||||
<span class="name">{{item.name}}</span>
|
<span class="name">{{item.name}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<a class="connection-group" ng-href="{{ item.getClientURL() }}">
|
<a class="connection-group" ng-href="{{ item.getClientURL() }}">
|
||||||
<div ng-show="item.balancing" class="icon type balancer"></div>
|
<div ng-show="item.balancing" class="icon type balancer"></div>
|
||||||
|
<input type="checkbox">
|
||||||
<span class="name">{{item.name}}</span>
|
<span class="name">{{item.name}}</span>
|
||||||
</a>
|
</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;
|
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
|
* @type Boolean
|
||||||
*/
|
*/
|
||||||
this.keyboardEnabled = template.keyboardEnabled || true;
|
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
|
* Whether translation of touch to mouse events should emulate an
|
||||||
* absolute pointer device, or a relative pointer device.
|
* absolute pointer device, or a relative pointer device.
|
||||||
|
Reference in New Issue
Block a user