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
Showing
3 changed files
with
108 additions
and
30 deletions
... | @@ -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 | ||
182 | + function filterMaskedKeys(map, caller, remove) { | ||
183 | + var masked = [], | ||
184 | + msgs = []; | ||
185 | + | ||
186 | + d3.map(map).keys().forEach(function (key) { | ||
187 | + if (keyHandler.maskedKeys[key]) { | ||
188 | + masked.push(key); | ||
189 | + msgs.push(caller, ': Key "' + key + '" is reserved'); | ||
190 | + } | ||
191 | + }); | ||
192 | + | ||
193 | + if (msgs.length) { | ||
194 | + $log.warn(msgs.join('\n')); | ||
195 | + } | ||
196 | + | ||
197 | + if (remove) { | ||
198 | + masked.forEach(function (k) { | ||
199 | + delete map[k]; | ||
200 | + }); | ||
201 | + } | ||
202 | + return masked; | ||
203 | + } | ||
204 | + | ||
205 | + function unexParam(fname, x) { | ||
206 | + $log.warn(fname, ": unexpected parameter-- ", x); | ||
207 | + } | ||
208 | + | ||
173 | function setKeyBindings(keyArg) { | 209 | function setKeyBindings(keyArg) { |
174 | - var viewKeys, | 210 | + var fname = 'setKeyBindings()', |
175 | - masked = []; | 211 | + kFunc = fs.isF(keyArg), |
212 | + kMap = fs.isO(keyArg); | ||
176 | 213 | ||
177 | - if (fs.isF(keyArg)) { | 214 | + if (kFunc) { |
178 | // set general key handler callback | 215 | // set general key handler callback |
179 | - keyHandler.viewFn = keyArg; | 216 | + keyHandler.viewFn = kFunc; |
217 | + } else if (kMap) { | ||
218 | + filterMaskedKeys(kMap, fname, true); | ||
219 | + keyHandler.viewKeys = kMap; | ||
180 | } else { | 220 | } else { |
181 | - // set specific key filter map | 221 | + unexParam(fname, keyArg); |
182 | - viewKeys = d3.map(keyArg).keys(); | ||
183 | - viewKeys.forEach(function (key) { | ||
184 | - if (keyHandler.maskedKeys[key]) { | ||
185 | - masked.push('setKeyBindings(): Key "' + key + '" is reserved'); | ||
186 | - } | ||
187 | - }); | ||
188 | - | ||
189 | - if (masked.length) { | ||
190 | - $log.warn(masked.join('\n')); | ||
191 | - } | ||
192 | - keyHandler.viewKeys = 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) { | ... | ... |
-
Please register or login to post a comment