mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-08 14:11:21 +00:00
Merge pull request #26 from glyptodon/guac-input
GUAC-810: Complete implementation of text input
This commit is contained in:
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* The module for code used to connect to a connection or balancing group.
|
* The module for code used to connect to a connection or balancing group.
|
||||||
*/
|
*/
|
||||||
angular.module('client', ['auth', 'history', 'osk', 'rest']);
|
angular.module('client', ['auth', 'history', 'osk', 'rest', 'textInput']);
|
||||||
|
@@ -204,8 +204,9 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
|
|||||||
// Show/hide UI elements depending on input method
|
// Show/hide UI elements depending on input method
|
||||||
$scope.$watch('inputMethod', function setInputMethod(inputMethod) {
|
$scope.$watch('inputMethod', function setInputMethod(inputMethod) {
|
||||||
|
|
||||||
// Show on-screen keyboard only if selected
|
// Show input methods only if selected
|
||||||
$scope.showOSK = (inputMethod === 'osk');
|
$scope.showOSK = (inputMethod === 'osk');
|
||||||
|
$scope.showTextInput = (inputMethod === 'text');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -115,6 +115,13 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
*/
|
*/
|
||||||
var main = $element[0];
|
var main = $element[0];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element which functions as a detector for size changes.
|
||||||
|
*
|
||||||
|
* @type Element
|
||||||
|
*/
|
||||||
|
var resizeSensor = $element.find('.resize-sensor')[0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guacamole mouse event object, wrapped around the main client
|
* Guacamole mouse event object, wrapped around the main client
|
||||||
* display.
|
* display.
|
||||||
@@ -491,8 +498,8 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
$scope.clientProperties.scale = $scope.clientProperties.minScale;
|
$scope.clientProperties.scale = $scope.clientProperties.minScale;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If the window is resized, attempt to resize client
|
// If the element is resized, attempt to resize client
|
||||||
$window.addEventListener('resize', function onResizeWindow() {
|
resizeSensor.contentWindow.addEventListener('resize', function mainElementResized() {
|
||||||
|
|
||||||
// Send new display size, if changed
|
// Send new display size, if changed
|
||||||
if (client && display) {
|
if (client && display) {
|
||||||
@@ -530,6 +537,16 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Universally handle all synthetic keydown events
|
||||||
|
$scope.$on('guacSyntheticKeydown', function syntheticKeydownListener(event, keysym) {
|
||||||
|
client.sendKeyEvent(1, keysym);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Universally handle all synthetic keyup events
|
||||||
|
$scope.$on('guacSyntheticKeyup', function syntheticKeyupListener(event, keysym) {
|
||||||
|
client.sendKeyEvent(0, keysym);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given bytes to a base64-encoded string.
|
* Converts the given bytes to a base64-encoded string.
|
||||||
*
|
*
|
||||||
|
@@ -27,18 +27,6 @@ body.client {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Viewport Clone */
|
|
||||||
|
|
||||||
#viewportClone {
|
|
||||||
display: table;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#preload {
|
#preload {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -48,3 +36,21 @@ body.client {
|
|||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.client-view {
|
||||||
|
display: table;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-view .client-body {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-view .client-bottom {
|
||||||
|
display: table-row;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
@@ -32,11 +32,21 @@
|
|||||||
|
|
||||||
div.main {
|
div.main {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: fixed;
|
width: 100%;
|
||||||
top: 0;
|
height: 100%;
|
||||||
bottom: 0;
|
position: relative;
|
||||||
right: 0;
|
}
|
||||||
|
|
||||||
|
.resize-sensor {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border: none;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.displayOuter {
|
div.displayOuter {
|
||||||
|
@@ -19,17 +19,4 @@
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
<html><body></body></html>
|
||||||
<div ng-show="errorPresent" class="dialogOuter guac-error">
|
|
||||||
<div class="dialogMiddle">
|
|
||||||
<div class="dialog">
|
|
||||||
<p class="title">{{'client.error.connectionErrorTitle' | translate}}</p>
|
|
||||||
|
|
||||||
<p class="status">{{errorStatus}}</p>
|
|
||||||
|
|
||||||
<div class="reconnect">
|
|
||||||
<button ng-click="reconnect()">{{'client.error.reconnect' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@@ -20,99 +20,107 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<div id="clientContainer">
|
<!-- Client view -->
|
||||||
|
<div class="client-view">
|
||||||
|
|
||||||
<!-- Client -->
|
<!-- Central portion of view -->
|
||||||
<guac-client
|
<div class="client-body">
|
||||||
client-properties="clientProperties"
|
|
||||||
id="id"
|
|
||||||
connection-parameters="connectionParameters"
|
|
||||||
></guac-client>
|
|
||||||
|
|
||||||
<!-- Text input target -->
|
<!-- Client -->
|
||||||
<div id="text-input"><div id="text-input-field"><div id="sent-history"></div><textarea rows="1" id="target"></textarea></div><div id="text-input-buttons"><button class="key" data-keysym="0xFFE3" data-sticky="true">{{'client.ctrl' | translate}}</button><button class="key" data-keysym="0xFFE9" data-sticky="true">{{'client.alt' | translate}}</button><button class="key" data-keysym="0xFF1B">{{'client.esc' | translate}}</button><button class="key" data-keysym="0xFF09">{{'client.tab' | translate}}</button></div></div>
|
<guac-client
|
||||||
|
client-properties="clientProperties"
|
||||||
|
id="id"
|
||||||
|
connection-parameters="connectionParameters"/></guac-client>
|
||||||
|
|
||||||
<!-- Dimensional clone of viewport -->
|
</div>
|
||||||
<div id="viewportClone"></div>
|
|
||||||
|
|
||||||
<!-- Menu -->
|
<!-- Bottom portion of view -->
|
||||||
<div ng-class="{open: menuShown}" id="menu">
|
<div class="client-bottom">
|
||||||
<h2>{{'client.clipboard' | translate}}</h2>
|
|
||||||
<div class="content" id="clipboard-settings">
|
|
||||||
<p class="description">{{'client.copiedText' | translate}}</p>
|
|
||||||
<textarea ng-model="clipboardData" rows="10" cols="40" id="clipboard"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>{{'client.inputMethod' | translate}}</h2>
|
<!-- Text input -->
|
||||||
<div class="content" id="keyboard-settings">
|
<div class="text-input-container" ng-show="showTextInput">
|
||||||
|
<guac-text-input needs-focus="showTextInput"></guac-text-input>
|
||||||
<!-- No IME -->
|
|
||||||
<div class="choice">
|
|
||||||
<label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="none"/> {{'client.none' | translate}}</label>
|
|
||||||
<p class="caption"><label for="ime-none">{{'client.noneDesc' | translate}}</label></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Text input -->
|
|
||||||
<div class="choice">
|
|
||||||
<div class="figure"><label for="ime-text"><img src="images/settings/tablet-keys.png" alt=""/></label></div>
|
|
||||||
<label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="text"/> {{'client.textInput' | translate}}</label>
|
|
||||||
<p class="caption"><label for="ime-text">{{'client.textInputDesc' | translate}} </label></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Guac OSK -->
|
|
||||||
<div class="choice">
|
|
||||||
<label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="osk"/> {{'client.osk' | translate}}</label>
|
|
||||||
<p class="caption"><label for="ime-osk">{{'client.oskDesc' | translate}}</label></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>{{'client.mouseMode' | translate}}</h2>
|
|
||||||
<div class="content" id="mouse-settings">
|
|
||||||
<p class="description">{{'client.mouseModeDesc' | translate}}</p>
|
|
||||||
|
|
||||||
<!-- Touchscreen -->
|
|
||||||
<div class="choice">
|
|
||||||
<input name="mouse-mode" ng-change="closeMenu()" ng-model="clientProperties.emulateAbsoluteMouse" type="radio" ng-value="true" checked="checked" id="absolute"/>
|
|
||||||
<div class="figure">
|
|
||||||
<label for="absolute"><img src="images/settings/touchscreen.png" alt="{{'client.touchscreen' | translate}}"/></label>
|
|
||||||
<p class="caption"><label for="absolute">{{'client.touchscreenDesc' | translate}}</label></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Touchpad -->
|
|
||||||
<div class="choice">
|
|
||||||
<input name="mouse-mode" ng-change="closeMenu()" ng-model="clientProperties.emulateAbsoluteMouse" type="radio" ng-value="false" id="relative"/>
|
|
||||||
<div class="figure">
|
|
||||||
<label for="relative"><img src="images/settings/touchpad.png" alt="{{'client.touchpad' | translate}}"/></label>
|
|
||||||
<p class="caption"><label for="relative">{{'client.touchpadDesc' | translate}}</label></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>{{'client.display' | translate}}</h2>
|
|
||||||
<div class="content">
|
|
||||||
<div id="zoom-settings">
|
|
||||||
<div ng-click="zoomOut()" id="zoom-out"><img src="images/settings/zoom-out.png" alt="-"/></div>
|
|
||||||
<div id="zoom-state">{{formattedScale()}}%</div>
|
|
||||||
<div ng-click="zoomIn()" id="zoom-in"><img src="images/settings/zoom-in.png" alt="+"/></div>
|
|
||||||
</div>
|
|
||||||
<div><label><input ng-model="autoFit" ng-change="changeAutoFit()" ng-disabled="autoFitDisabled()" type="checkbox" id="auto-fit"/> {{'client.autoFit' | translate}}</label></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- On-screen keyboard -->
|
</div>
|
||||||
<div class="keyboard-container" ng-class="{shown: showOSK}">
|
|
||||||
<guac-osk layout="'client.oskLayout' | translate"/>
|
<!-- On-screen keyboard -->
|
||||||
</div>
|
<div class="keyboard-container" ng-class="{shown: showOSK}">
|
||||||
|
<guac-osk layout="'client.oskLayout' | translate"/>
|
||||||
<!-- Images which should be preloaded -->
|
</div>
|
||||||
<div id="preload">
|
|
||||||
<img src="images/action-icons/guac-close.png" alt=""/>
|
<!-- Menu -->
|
||||||
<img src="images/progress.png" alt=""/>
|
<div ng-class="{open: menuShown}" id="menu">
|
||||||
</div>
|
<h2>{{'client.clipboard' | translate}}</h2>
|
||||||
|
<div class="content" id="clipboard-settings">
|
||||||
<ng-include src="app/client/template/clientError.html"/>
|
<p class="description">{{'client.copiedText' | translate}}</p>
|
||||||
|
<textarea ng-model="clipboardData" rows="10" cols="40" id="clipboard"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>{{'client.inputMethod' | translate}}</h2>
|
||||||
|
<div class="content" id="keyboard-settings">
|
||||||
|
|
||||||
|
<!-- No IME -->
|
||||||
|
<div class="choice">
|
||||||
|
<label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="none"/> {{'client.none' | translate}}</label>
|
||||||
|
<p class="caption"><label for="ime-none">{{'client.noneDesc' | translate}}</label></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Text input -->
|
||||||
|
<div class="choice">
|
||||||
|
<div class="figure"><label for="ime-text"><img src="images/settings/tablet-keys.png" alt=""/></label></div>
|
||||||
|
<label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="text"/> {{'client.textInput' | translate}}</label>
|
||||||
|
<p class="caption"><label for="ime-text">{{'client.textInputDesc' | translate}} </label></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Guac OSK -->
|
||||||
|
<div class="choice">
|
||||||
|
<label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="osk"/> {{'client.osk' | translate}}</label>
|
||||||
|
<p class="caption"><label for="ime-osk">{{'client.oskDesc' | translate}}</label></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>{{'client.mouseMode' | translate}}</h2>
|
||||||
|
<div class="content" id="mouse-settings">
|
||||||
|
<p class="description">{{'client.mouseModeDesc' | translate}}</p>
|
||||||
|
|
||||||
|
<!-- Touchscreen -->
|
||||||
|
<div class="choice">
|
||||||
|
<input name="mouse-mode" ng-change="closeMenu()" ng-model="clientProperties.emulateAbsoluteMouse" type="radio" ng-value="true" checked="checked" id="absolute"/>
|
||||||
|
<div class="figure">
|
||||||
|
<label for="absolute"><img src="images/settings/touchscreen.png" alt="{{'client.touchscreen' | translate}}"/></label>
|
||||||
|
<p class="caption"><label for="absolute">{{'client.touchscreenDesc' | translate}}</label></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Touchpad -->
|
||||||
|
<div class="choice">
|
||||||
|
<input name="mouse-mode" ng-change="closeMenu()" ng-model="clientProperties.emulateAbsoluteMouse" type="radio" ng-value="false" id="relative"/>
|
||||||
|
<div class="figure">
|
||||||
|
<label for="relative"><img src="images/settings/touchpad.png" alt="{{'client.touchpad' | translate}}"/></label>
|
||||||
|
<p class="caption"><label for="relative">{{'client.touchpadDesc' | translate}}</label></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>{{'client.display' | translate}}</h2>
|
||||||
|
<div class="content">
|
||||||
|
<div id="zoom-settings">
|
||||||
|
<div ng-click="zoomOut()" id="zoom-out"><img src="images/settings/zoom-out.png" alt="-"/></div>
|
||||||
|
<div id="zoom-state">{{formattedScale()}}%</div>
|
||||||
|
<div ng-click="zoomIn()" id="zoom-in"><img src="images/settings/zoom-in.png" alt="+"/></div>
|
||||||
|
</div>
|
||||||
|
<div><label><input ng-model="autoFit" ng-change="changeAutoFit()" ng-disabled="autoFitDisabled()" type="checkbox" id="auto-fit"/> {{'client.autoFit' | translate}}</label></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Images which should be preloaded -->
|
||||||
|
<div id="preload">
|
||||||
|
<img src="images/action-icons/guac-close.png" alt=""/>
|
||||||
|
<img src="images/progress.png" alt=""/>
|
||||||
</div>
|
</div>
|
@@ -21,6 +21,9 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- Resize sensor -->
|
||||||
|
<iframe class="resize-sensor" src="app/client/templates/blank.html"></iframe>
|
||||||
|
|
||||||
<!-- Display -->
|
<!-- Display -->
|
||||||
<div class="displayOuter">
|
<div class="displayOuter">
|
||||||
<div class="displayMiddle">
|
<div class="displayMiddle">
|
||||||
|
@@ -197,15 +197,31 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
// Create event listeners at the global level
|
// Create event listeners at the global level
|
||||||
var keyboard = new Guacamole.Keyboard($document[0]);
|
var keyboard = new Guacamole.Keyboard($document[0]);
|
||||||
|
|
||||||
// Broadcast keydown events down the scope heirarchy
|
// Broadcast keydown events
|
||||||
keyboard.onkeydown = function onkeydown(keysym) {
|
keyboard.onkeydown = function onkeydown(keysym) {
|
||||||
|
|
||||||
|
// Warn of pending keydown
|
||||||
|
var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeydown', keysym, keyboard);
|
||||||
|
if (guacBeforeKeydownEvent.defaultPrevented)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// If not prevented via guacBeforeKeydown, fire corresponding keydown event
|
||||||
var guacKeydownEvent = $scope.$broadcast('guacKeydown', keysym, keyboard);
|
var guacKeydownEvent = $scope.$broadcast('guacKeydown', keysym, keyboard);
|
||||||
return !guacKeydownEvent.defaultPrevented;
|
return !guacKeydownEvent.defaultPrevented;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Broadcast keyup events down the scope heirarchy
|
// Broadcast keyup events
|
||||||
keyboard.onkeyup = function onkeyup(keysym) {
|
keyboard.onkeyup = function onkeyup(keysym) {
|
||||||
|
|
||||||
|
// Warn of pending keyup
|
||||||
|
var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeyup', keysym, keyboard);
|
||||||
|
if (guacBeforeKeydownEvent.defaultPrevented)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If not prevented via guacBeforeKeyup, fire corresponding keydown event
|
||||||
$scope.$broadcast('guacKeyup', keysym, keyboard);
|
$scope.$broadcast('guacKeyup', keysym, keyboard);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Release all keys when window loses focus
|
// Release all keys when window loses focus
|
||||||
|
@@ -88,12 +88,12 @@ angular.module('osk').directive('guacOsk', [function guacOsk() {
|
|||||||
|
|
||||||
// Broadcast keydown for each key pressed
|
// Broadcast keydown for each key pressed
|
||||||
keyboard.onkeydown = function(keysym) {
|
keyboard.onkeydown = function(keysym) {
|
||||||
$rootScope.$broadcast('guacKeydown', keysym);
|
$rootScope.$broadcast('guacSyntheticKeydown', keysym);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Broadcast keydown for each key released
|
// Broadcast keydown for each key released
|
||||||
keyboard.onkeyup = function(keysym) {
|
keyboard.onkeyup = function(keysym) {
|
||||||
$rootScope.$broadcast('guacKeyup', keysym);
|
$rootScope.$broadcast('guacSyntheticKeyup', keysym);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Resize keyboard whenever window changes size
|
// Resize keyboard whenever window changes size
|
||||||
|
@@ -90,8 +90,8 @@ angular.module('textInput').directive('guacKey', [function guacKey() {
|
|||||||
|
|
||||||
// For all non-sticky keys, press and release key immediately
|
// For all non-sticky keys, press and release key immediately
|
||||||
else {
|
else {
|
||||||
$rootScope.$broadcast('guacKeydown', $scope.keysym);
|
$rootScope.$broadcast('guacSyntheticKeydown', $scope.keysym);
|
||||||
$rootScope.$broadcast('guacKeyup', $scope.keysym);
|
$rootScope.$broadcast('guacSyntheticKeyup', $scope.keysym);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -101,11 +101,11 @@ angular.module('textInput').directive('guacKey', [function guacKey() {
|
|||||||
|
|
||||||
// If the key is pressed now, send keydown
|
// If the key is pressed now, send keydown
|
||||||
if (isPressed)
|
if (isPressed)
|
||||||
$rootScope.$broadcast('guacKeydown', $scope.keysym);
|
$rootScope.$broadcast('guacSyntheticKeydown', $scope.keysym);
|
||||||
|
|
||||||
// If the key was pressed, but is not pressed any longer, send keyup
|
// If the key was pressed, but is not pressed any longer, send keyup
|
||||||
else if (wasPressed)
|
else if (wasPressed)
|
||||||
$rootScope.$broadcast('guacKeyup', $scope.keysym);
|
$rootScope.$broadcast('guacSyntheticKeyup', $scope.keysym);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -28,7 +28,17 @@ angular.module('textInput').directive('guacTextInput', [function guacTextInput()
|
|||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
replace: true,
|
replace: true,
|
||||||
scope: {},
|
scope: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the text input UI should have focus. Setting this value
|
||||||
|
* is not guaranteed to work, due to browser limitations.
|
||||||
|
*
|
||||||
|
* @type Boolean
|
||||||
|
*/
|
||||||
|
needsFocus : '=?'
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
templateUrl: 'app/textInput/templates/guacTextInput.html',
|
templateUrl: 'app/textInput/templates/guacTextInput.html',
|
||||||
controller: ['$scope', '$rootScope', '$element', '$timeout',
|
controller: ['$scope', '$rootScope', '$element', '$timeout',
|
||||||
@@ -50,6 +60,47 @@ angular.module('textInput').directive('guacTextInput', [function guacTextInput()
|
|||||||
*/
|
*/
|
||||||
var TEXT_INPUT_PADDING_CODEPOINT = 0x200B;
|
var TEXT_INPUT_PADDING_CODEPOINT = 0x200B;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys which should be allowed through to the client when in text
|
||||||
|
* input mode, providing corresponding key events are received.
|
||||||
|
* Keys in this set will be allowed through to the server.
|
||||||
|
*
|
||||||
|
* @type Object.<Number, Boolean>
|
||||||
|
*/
|
||||||
|
var ALLOWED_KEYS = {
|
||||||
|
0xFE03: true, /* AltGr */
|
||||||
|
0xFF08: true, /* Backspace */
|
||||||
|
0xFF09: true, /* Tab */
|
||||||
|
0xFF0D: true, /* Enter */
|
||||||
|
0xFF1B: true, /* Escape */
|
||||||
|
0xFF50: true, /* Home */
|
||||||
|
0xFF51: true, /* Left */
|
||||||
|
0xFF52: true, /* Up */
|
||||||
|
0xFF53: true, /* Right */
|
||||||
|
0xFF54: true, /* Down */
|
||||||
|
0xFF57: true, /* End */
|
||||||
|
0xFF64: true, /* Insert */
|
||||||
|
0xFFBE: true, /* F1 */
|
||||||
|
0xFFBF: true, /* F2 */
|
||||||
|
0xFFC0: true, /* F3 */
|
||||||
|
0xFFC1: true, /* F4 */
|
||||||
|
0xFFC2: true, /* F5 */
|
||||||
|
0xFFC3: true, /* F6 */
|
||||||
|
0xFFC4: true, /* F7 */
|
||||||
|
0xFFC5: true, /* F8 */
|
||||||
|
0xFFC6: true, /* F9 */
|
||||||
|
0xFFC7: true, /* F10 */
|
||||||
|
0xFFC8: true, /* F11 */
|
||||||
|
0xFFC9: true, /* F12 */
|
||||||
|
0xFFE1: true, /* Left shift */
|
||||||
|
0xFFE2: true, /* Right shift */
|
||||||
|
0xFFE3: true, /* Left ctrl */
|
||||||
|
0xFFE4: true, /* Right ctrl */
|
||||||
|
0xFFE9: true, /* Left alt */
|
||||||
|
0xFFEA: true, /* Right alt */
|
||||||
|
0xFFFF: true /* Delete */
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recently-sent text, ordered from oldest to most recent.
|
* Recently-sent text, ordered from oldest to most recent.
|
||||||
*
|
*
|
||||||
@@ -151,8 +202,8 @@ angular.module('textInput').directive('guacTextInput', [function guacTextInput()
|
|||||||
* @param {Number} keysym The keysym of the key to send.
|
* @param {Number} keysym The keysym of the key to send.
|
||||||
*/
|
*/
|
||||||
var sendKeysym = function sendKeysym(keysym) {
|
var sendKeysym = function sendKeysym(keysym) {
|
||||||
$rootScope.$broadcast('guacKeydown', keysym);
|
$rootScope.$broadcast('guacSyntheticKeydown', keysym);
|
||||||
$rootScope.$broadcast('guacKeyup', keysym);
|
$rootScope.$broadcast('guacSyntheticKeyup', keysym);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -283,6 +334,28 @@ angular.module('textInput').directive('guacTextInput', [function guacTextInput()
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
|
// Attempt to change focus depending on need
|
||||||
|
$scope.$watch('needsFocus', function focusDesireChanged(focusNeeded) {
|
||||||
|
|
||||||
|
if (focusNeeded)
|
||||||
|
target.focus();
|
||||||
|
else
|
||||||
|
target.blur();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the text input UI has focus, prevent keydown events
|
||||||
|
$scope.$on('guacBeforeKeydown', function filterKeydown(event, keysym) {
|
||||||
|
if (hasFocus && !ALLOWED_KEYS[keysym])
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the text input UI has focus, prevent keyup events
|
||||||
|
$scope.$on('guacBeforeKeyup', function filterKeyup(event, keysym) {
|
||||||
|
if (hasFocus && !ALLOWED_KEYS[keysym])
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user