Simon Hunt

ONOS-3741: Bind Escape to Cancel and Enter to OK in dialog service.

 - also allow arbitrary keybindings to arbitrary text buttons in dialogs.

Change-Id: I5a01abb13fce41f81e8686866d82d2d08c34a71b
......@@ -23,7 +23,7 @@
'use strict';
// injected refs
var $log, $window, fs, ps, bns;
var $log, $window, fs, ps, bns, ks;
// configuration
var defaultSettings = {
......@@ -32,11 +32,8 @@
};
// internal state
var pApi, panel, dApi;
// TODO: ONOS-3741
// TODO: ESC key invokes Cancel callback
// TODO: Enter invokes OK callback
var pApi, panel, dApi,
keyBindings = {};
// create the dialog; return its API
function createDialog(id, opts) {
......@@ -99,13 +96,20 @@
};
}
function makeButton(text, callback) {
var cb = fs.isF(callback);
function makeButton(text, callback, keyName) {
var cb = fs.isF(callback),
key = fs.isS(keyName);
function invoke() {
cb && cb();
clearBindings();
panel.hide();
}
if (key) {
keyBindings[key] = invoke;
}
return createDiv('dialog-button')
.text(text)
.on('click', invoke);
......@@ -125,13 +129,26 @@
return dApi;
}
function addButton(text, cb) {
function addButton(text, cb, key) {
if (pApi) {
pApi.appendFooter(makeButton(text, cb));
pApi.appendFooter(makeButton(text, cb, key));
}
return dApi;
}
function addOk(cb) {
return addButton('OK', cb, 'enter');
}
function addCancel(cb) {
return addButton('Cancel', cb, 'esc');
}
function clearBindings() {
keyBindings = {};
ks.dialogKeys();
}
// opens the dialog (creates if necessary)
function openDialog(id, opts) {
$log.debug('Open DIALOG', id, opts);
......@@ -145,7 +162,12 @@
dApi = {
setTitle: setTitle,
addContent: addContent,
addButton: addButton
addButton: addButton,
addOk: addOk,
addCancel: addCancel,
bindKeys: function () {
ks.dialogKeys(keyBindings);
}
};
return dApi;
}
......@@ -154,6 +176,7 @@
function closeDialog() {
$log.debug('Close DIALOG');
if (pApi) {
clearBindings();
panel.hide();
pApi.destroy();
pApi = null;
......@@ -174,16 +197,18 @@
angular.module('onosLayer')
.factory('DialogService',
['$log', '$window', 'FnService', 'PanelService', 'ButtonService',
'KeyService',
// TODO: for now, $window is not used, but we should provide an option
// to center the dialog on the window.
function (_$log_, _$window_, _fs_, _ps_, _bns_) {
function (_$log_, _$window_, _fs_, _ps_, _bns_, _ks_) {
$log = _$log_;
$window = _$window_;
fs = _fs_;
ps = _ps_;
bns = _bns_;
ks = _ks_;
return {
openDialog: openDialog,
......
......@@ -29,6 +29,7 @@
keyHandler = {
globalKeys: {},
maskedKeys: {},
dialogKeys: {},
viewKeys: {},
viewFn: null,
viewGestures: []
......@@ -104,6 +105,8 @@
kh = keyHandler,
gk = kh.globalKeys[key],
gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])),
dk = kh.dialogKeys[key],
dcb = fs.isF(dk),
vk = kh.viewKeys[key],
kl = fs.isF(kh.viewKeys._keyListener),
vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn),
......@@ -119,6 +122,12 @@
// if the event was 'handled', we are done
return;
}
// dialog callback?
if (dcb) {
dcb(token, key, keyCode, event);
// assume dialog handled the event
return;
}
// otherwise, let the view callback have a shot
if (vcb) {
vcb(token, key, keyCode, event);
......@@ -170,26 +179,46 @@
return true;
}
function setKeyBindings(keyArg) {
var viewKeys,
masked = [];
function filterMaskedKeys(map, caller, remove) {
var masked = [],
msgs = [];
if (fs.isF(keyArg)) {
// set general key handler callback
keyHandler.viewFn = keyArg;
} else {
// set specific key filter map
viewKeys = d3.map(keyArg).keys();
viewKeys.forEach(function (key) {
d3.map(map).keys().forEach(function (key) {
if (keyHandler.maskedKeys[key]) {
masked.push('setKeyBindings(): Key "' + key + '" is reserved');
masked.push(key);
msgs.push(caller, ': Key "' + key + '" is reserved');
}
});
if (masked.length) {
$log.warn(masked.join('\n'));
if (msgs.length) {
$log.warn(msgs.join('\n'));
}
if (remove) {
masked.forEach(function (k) {
delete map[k];
});
}
keyHandler.viewKeys = keyArg;
return masked;
}
function unexParam(fname, x) {
$log.warn(fname, ": unexpected parameter-- ", x);
}
function setKeyBindings(keyArg) {
var fname = 'setKeyBindings()',
kFunc = fs.isF(keyArg),
kMap = fs.isO(keyArg);
if (kFunc) {
// set general key handler callback
keyHandler.viewFn = kFunc;
} else if (kMap) {
filterMaskedKeys(kMap, fname, true);
keyHandler.viewKeys = kMap;
} else {
unexParam(fname, keyArg);
}
}
......@@ -213,6 +242,22 @@
keyHandler.viewGestures = [];
}
function bindDialogKeys(map) {
var fname = 'bindDialogKeys()',
kMap = fs.isO(map);
if (kMap) {
filterMaskedKeys(map, fname, true);
keyHandler.dialogKeys = kMap;
} else {
unexParam(fname, map);
}
}
function unbindDialogKeys() {
keyHandler.dialogKeys = {};
}
function checkNotGlobal(o) {
var oops = [];
if (fs.isO(o)) {
......@@ -259,6 +304,13 @@
}
},
unbindKeys: unbindKeys,
dialogKeys: function (x) {
if (x === undefined) {
unbindDialogKeys();
} else {
bindDialogKeys(x);
}
},
addSeq: function (word, data) {
fs.addToTrie(seq, word, data);
},
......
......@@ -308,8 +308,9 @@
ds.openDialog(dialogId, dialogOpts)
.setTitle('Confirm Action')
.addContent(createConfirmationText(action, itemId))
.addButton('OK', dOk)
.addButton('Cancel', dCancel);
.addOk(dOk)
.addCancel(dCancel)
.bindKeys();
}
$scope.appAction = function (action) {
......