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 @@ ...@@ -23,7 +23,7 @@
23 'use strict'; 23 'use strict';
24 24
25 // injected refs 25 // injected refs
26 - var $log, $window, fs, ps, bns; 26 + var $log, $window, fs, ps, bns, ks;
27 27
28 // configuration 28 // configuration
29 var defaultSettings = { 29 var defaultSettings = {
...@@ -32,11 +32,8 @@ ...@@ -32,11 +32,8 @@
32 }; 32 };
33 33
34 // internal state 34 // internal state
35 - var pApi, panel, dApi; 35 + var pApi, panel, dApi,
36 - 36 + keyBindings = {};
37 - // TODO: ONOS-3741
38 - // TODO: ESC key invokes Cancel callback
39 - // TODO: Enter invokes OK callback
40 37
41 // create the dialog; return its API 38 // create the dialog; return its API
42 function createDialog(id, opts) { 39 function createDialog(id, opts) {
...@@ -99,13 +96,20 @@ ...@@ -99,13 +96,20 @@
99 }; 96 };
100 } 97 }
101 98
102 - function makeButton(text, callback) { 99 + function makeButton(text, callback, keyName) {
103 - var cb = fs.isF(callback); 100 + var cb = fs.isF(callback),
101 + key = fs.isS(keyName);
104 102
105 function invoke() { 103 function invoke() {
106 cb && cb(); 104 cb && cb();
105 + clearBindings();
107 panel.hide(); 106 panel.hide();
108 } 107 }
108 +
109 + if (key) {
110 + keyBindings[key] = invoke;
111 + }
112 +
109 return createDiv('dialog-button') 113 return createDiv('dialog-button')
110 .text(text) 114 .text(text)
111 .on('click', invoke); 115 .on('click', invoke);
...@@ -125,13 +129,26 @@ ...@@ -125,13 +129,26 @@
125 return dApi; 129 return dApi;
126 } 130 }
127 131
128 - function addButton(text, cb) { 132 + function addButton(text, cb, key) {
129 if (pApi) { 133 if (pApi) {
130 - pApi.appendFooter(makeButton(text, cb)); 134 + pApi.appendFooter(makeButton(text, cb, key));
131 } 135 }
132 return dApi; 136 return dApi;
133 } 137 }
134 138
139 + function addOk(cb) {
140 + return addButton('OK', cb, 'enter');
141 + }
142 +
143 + function addCancel(cb) {
144 + return addButton('Cancel', cb, 'esc');
145 + }
146 +
147 + function clearBindings() {
148 + keyBindings = {};
149 + ks.dialogKeys();
150 + }
151 +
135 // opens the dialog (creates if necessary) 152 // opens the dialog (creates if necessary)
136 function openDialog(id, opts) { 153 function openDialog(id, opts) {
137 $log.debug('Open DIALOG', id, opts); 154 $log.debug('Open DIALOG', id, opts);
...@@ -145,7 +162,12 @@ ...@@ -145,7 +162,12 @@
145 dApi = { 162 dApi = {
146 setTitle: setTitle, 163 setTitle: setTitle,
147 addContent: addContent, 164 addContent: addContent,
148 - addButton: addButton 165 + addButton: addButton,
166 + addOk: addOk,
167 + addCancel: addCancel,
168 + bindKeys: function () {
169 + ks.dialogKeys(keyBindings);
170 + }
149 }; 171 };
150 return dApi; 172 return dApi;
151 } 173 }
...@@ -154,6 +176,7 @@ ...@@ -154,6 +176,7 @@
154 function closeDialog() { 176 function closeDialog() {
155 $log.debug('Close DIALOG'); 177 $log.debug('Close DIALOG');
156 if (pApi) { 178 if (pApi) {
179 + clearBindings();
157 panel.hide(); 180 panel.hide();
158 pApi.destroy(); 181 pApi.destroy();
159 pApi = null; 182 pApi = null;
...@@ -174,16 +197,18 @@ ...@@ -174,16 +197,18 @@
174 angular.module('onosLayer') 197 angular.module('onosLayer')
175 .factory('DialogService', 198 .factory('DialogService',
176 ['$log', '$window', 'FnService', 'PanelService', 'ButtonService', 199 ['$log', '$window', 'FnService', 'PanelService', 'ButtonService',
200 + 'KeyService',
177 201
178 // TODO: for now, $window is not used, but we should provide an option 202 // TODO: for now, $window is not used, but we should provide an option
179 // to center the dialog on the window. 203 // to center the dialog on the window.
180 204
181 - function (_$log_, _$window_, _fs_, _ps_, _bns_) { 205 + function (_$log_, _$window_, _fs_, _ps_, _bns_, _ks_) {
182 $log = _$log_; 206 $log = _$log_;
183 $window = _$window_; 207 $window = _$window_;
184 fs = _fs_; 208 fs = _fs_;
185 ps = _ps_; 209 ps = _ps_;
186 bns = _bns_; 210 bns = _bns_;
211 + ks = _ks_;
187 212
188 return { 213 return {
189 openDialog: openDialog, 214 openDialog: openDialog,
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
29 keyHandler = { 29 keyHandler = {
30 globalKeys: {}, 30 globalKeys: {},
31 maskedKeys: {}, 31 maskedKeys: {},
32 + dialogKeys: {},
32 viewKeys: {}, 33 viewKeys: {},
33 viewFn: null, 34 viewFn: null,
34 viewGestures: [] 35 viewGestures: []
...@@ -104,6 +105,8 @@ ...@@ -104,6 +105,8 @@
104 kh = keyHandler, 105 kh = keyHandler,
105 gk = kh.globalKeys[key], 106 gk = kh.globalKeys[key],
106 gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])), 107 gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])),
108 + dk = kh.dialogKeys[key],
109 + dcb = fs.isF(dk),
107 vk = kh.viewKeys[key], 110 vk = kh.viewKeys[key],
108 kl = fs.isF(kh.viewKeys._keyListener), 111 kl = fs.isF(kh.viewKeys._keyListener),
109 vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn), 112 vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn),
...@@ -119,6 +122,12 @@ ...@@ -119,6 +122,12 @@
119 // if the event was 'handled', we are done 122 // if the event was 'handled', we are done
120 return; 123 return;
121 } 124 }
125 + // dialog callback?
126 + if (dcb) {
127 + dcb(token, key, keyCode, event);
128 + // assume dialog handled the event
129 + return;
130 + }
122 // otherwise, let the view callback have a shot 131 // otherwise, let the view callback have a shot
123 if (vcb) { 132 if (vcb) {
124 vcb(token, key, keyCode, event); 133 vcb(token, key, keyCode, event);
...@@ -170,26 +179,46 @@ ...@@ -170,26 +179,46 @@
170 return true; 179 return true;
171 } 180 }
172 181
173 - function setKeyBindings(keyArg) { 182 + function filterMaskedKeys(map, caller, remove) {
174 - var viewKeys, 183 + var masked = [],
175 - masked = []; 184 + msgs = [];
176 185
177 - if (fs.isF(keyArg)) { 186 + d3.map(map).keys().forEach(function (key) {
178 - // set general key handler callback
179 - keyHandler.viewFn = keyArg;
180 - } else {
181 - // set specific key filter map
182 - viewKeys = d3.map(keyArg).keys();
183 - viewKeys.forEach(function (key) {
184 if (keyHandler.maskedKeys[key]) { 187 if (keyHandler.maskedKeys[key]) {
185 - masked.push('setKeyBindings(): Key "' + key + '" is reserved'); 188 + masked.push(key);
189 + msgs.push(caller, ': Key "' + key + '" is reserved');
186 } 190 }
187 }); 191 });
188 192
189 - if (masked.length) { 193 + if (msgs.length) {
190 - $log.warn(masked.join('\n')); 194 + $log.warn(msgs.join('\n'));
195 + }
196 +
197 + if (remove) {
198 + masked.forEach(function (k) {
199 + delete map[k];
200 + });
191 } 201 }
192 - keyHandler.viewKeys = keyArg; 202 + return masked;
203 + }
204 +
205 + function unexParam(fname, x) {
206 + $log.warn(fname, ": unexpected parameter-- ", x);
207 + }
208 +
209 + function setKeyBindings(keyArg) {
210 + var fname = 'setKeyBindings()',
211 + kFunc = fs.isF(keyArg),
212 + kMap = fs.isO(keyArg);
213 +
214 + if (kFunc) {
215 + // set general key handler callback
216 + keyHandler.viewFn = kFunc;
217 + } else if (kMap) {
218 + filterMaskedKeys(kMap, fname, true);
219 + keyHandler.viewKeys = kMap;
220 + } else {
221 + unexParam(fname, keyArg);
193 } 222 }
194 } 223 }
195 224
...@@ -213,6 +242,22 @@ ...@@ -213,6 +242,22 @@
213 keyHandler.viewGestures = []; 242 keyHandler.viewGestures = [];
214 } 243 }
215 244
245 + function bindDialogKeys(map) {
246 + var fname = 'bindDialogKeys()',
247 + kMap = fs.isO(map);
248 +
249 + if (kMap) {
250 + filterMaskedKeys(map, fname, true);
251 + keyHandler.dialogKeys = kMap;
252 + } else {
253 + unexParam(fname, map);
254 + }
255 + }
256 +
257 + function unbindDialogKeys() {
258 + keyHandler.dialogKeys = {};
259 + }
260 +
216 function checkNotGlobal(o) { 261 function checkNotGlobal(o) {
217 var oops = []; 262 var oops = [];
218 if (fs.isO(o)) { 263 if (fs.isO(o)) {
...@@ -259,6 +304,13 @@ ...@@ -259,6 +304,13 @@
259 } 304 }
260 }, 305 },
261 unbindKeys: unbindKeys, 306 unbindKeys: unbindKeys,
307 + dialogKeys: function (x) {
308 + if (x === undefined) {
309 + unbindDialogKeys();
310 + } else {
311 + bindDialogKeys(x);
312 + }
313 + },
262 addSeq: function (word, data) { 314 addSeq: function (word, data) {
263 fs.addToTrie(seq, word, data); 315 fs.addToTrie(seq, word, data);
264 }, 316 },
......
...@@ -308,8 +308,9 @@ ...@@ -308,8 +308,9 @@
308 ds.openDialog(dialogId, dialogOpts) 308 ds.openDialog(dialogId, dialogOpts)
309 .setTitle('Confirm Action') 309 .setTitle('Confirm Action')
310 .addContent(createConfirmationText(action, itemId)) 310 .addContent(createConfirmationText(action, itemId))
311 - .addButton('OK', dOk) 311 + .addOk(dOk)
312 - .addButton('Cancel', dCancel); 312 + .addCancel(dCancel)
313 + .bindKeys();
313 } 314 }
314 315
315 $scope.appAction = function (action) { 316 $scope.appAction = function (action) {
......