Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 4 | |
Tim van der Lippe | ee97fa3 | 2020-04-23 15:20:56 | [diff] [blame] | 5 | // @ts-nocheck |
| 6 | // TODO(crbug.com/1011811): Enable TypeScript compiler checks |
| 7 | |
Paul Lewis | 0fd4371 | 2020-01-08 17:07:36 | [diff] [blame] | 8 | import * as Host from '../host/host.js'; |
Tim van der Lippe | ee97fa3 | 2020-04-23 15:20:56 | [diff] [blame] | 9 | import * as Platform from '../platform/platform.js'; |
Tim van der Lippe | aa76aa2 | 2020-02-14 14:38:24 | [diff] [blame] | 10 | |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 11 | import {Action} from './Action.js'; // eslint-disable-line no-unused-vars |
| 12 | import {ActionRegistry} from './ActionRegistry.js'; // eslint-disable-line no-unused-vars |
| 13 | import {Context} from './Context.js'; |
| 14 | import {Dialog} from './Dialog.js'; |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 15 | import {Descriptor, KeyboardShortcut, Modifiers, Type} from './KeyboardShortcut.js'; // eslint-disable-line no-unused-vars |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 16 | import {isEditing} from './UIUtils.js'; |
| 17 | |
Sigurd Schneider | 46da7db | 2020-05-20 13:45:11 | [diff] [blame^] | 18 | |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 19 | export class ShortcutRegistry { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 20 | /** |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 21 | * @param {!ActionRegistry} actionRegistry |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 22 | */ |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 23 | constructor(actionRegistry) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 24 | this._actionRegistry = actionRegistry; |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 25 | /** @type {!Platform.Multimap.<number, !KeyboardShortcut>} */ |
| 26 | this._keyToShortcut = new Platform.Multimap(); |
| 27 | /** @type {!Platform.Multimap.<string, !KeyboardShortcut>} */ |
| 28 | this._actionToShortcut = new Platform.Multimap(); |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 29 | this._keyMap = new ShortcutTreeNode(0, 0); |
| 30 | /** @type {?ShortcutTreeNode} */ |
| 31 | this._activePrefixKey = null; |
| 32 | /** @type {?number} */ |
| 33 | this._activePrefixTimeout = null; |
| 34 | /** @type {?function():Promise<void>} */ |
| 35 | this._consumePrefix = null; |
Jack Lynch | f1f00fa | 2020-05-01 22:16:12 | [diff] [blame] | 36 | const keybindSetSetting = self.Common.settings.moduleSetting('activeKeybindSet'); |
| 37 | if (!Root.Runtime.experiments.isEnabled('customKeyboardShortcuts') && |
| 38 | keybindSetSetting.get() !== DefaultShortcutSetting) { |
| 39 | keybindSetSetting.set(DefaultShortcutSetting); |
| 40 | } |
| 41 | keybindSetSetting.addChangeListener(this._registerBindings, this); |
| 42 | |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 43 | this._registerBindings(); |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 44 | } |
| 45 | |
| 46 | /** |
| 47 | * @param {number} key |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 48 | * @param {!Object.<string, function():Promise.<boolean>>=} handlers |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 49 | * @return {!Array.<!Action>} |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 50 | */ |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 51 | _applicableActions(key, handlers = {}) { |
| 52 | let actions = []; |
| 53 | const keyMap = this._activePrefixKey || this._keyMap; |
| 54 | const keyNode = keyMap.getNode(key); |
| 55 | if (keyNode) { |
| 56 | actions = keyNode.actions(); |
| 57 | } |
| 58 | const applicableActions = this._actionRegistry.applicableActions(actions, self.UI.context); |
| 59 | if (keyNode) { |
| 60 | for (const actionId of Object.keys(handlers)) { |
| 61 | if (keyNode.actions().indexOf(actionId) >= 0) { |
| 62 | const action = this._actionRegistry.action(actionId); |
| 63 | if (action) { |
| 64 | applicableActions.push(action); |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | return applicableActions; |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | /** |
Jack Lynch | 575e9fb | 2020-03-26 22:20:51 | [diff] [blame] | 73 | * @param {string} action |
| 74 | * @return {!Array.<!KeyboardShortcut>} |
| 75 | */ |
| 76 | shortcutsForAction(action) { |
| 77 | return [...this._actionToShortcut.get(action)]; |
| 78 | } |
| 79 | |
| 80 | /** |
Joel Einbinder | 67f28fb | 2018-08-02 00:33:47 | [diff] [blame] | 81 | * @return {!Array<number>} |
| 82 | */ |
| 83 | globalShortcutKeys() { |
| 84 | const keys = []; |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 85 | for (const node of this._keyMap.chords().values()) { |
| 86 | const actions = node.actions(); |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 87 | const applicableActions = this._actionRegistry.applicableActions(actions, new Context()); |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 88 | if (applicableActions.length || node.hasChords()) { |
| 89 | keys.push(node.key()); |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 90 | } |
Joel Einbinder | 67f28fb | 2018-08-02 00:33:47 | [diff] [blame] | 91 | } |
| 92 | return keys; |
| 93 | } |
| 94 | |
| 95 | /** |
Jack Lynch | b8fb3c7 | 2020-04-21 05:36:16 | [diff] [blame] | 96 | * @deprecated this function is obsolete and will be removed in the |
| 97 | * future along with the legacy shortcuts settings tab |
| 98 | * crbug.com/174309 |
| 99 | * |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 100 | * @param {string} actionId |
Tim van der Lippe | aa76aa2 | 2020-02-14 14:38:24 | [diff] [blame] | 101 | * @return {!Array.<!Descriptor>} |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 102 | */ |
| 103 | shortcutDescriptorsForAction(actionId) { |
Jack Lynch | b8fb3c7 | 2020-04-21 05:36:16 | [diff] [blame] | 104 | return [...this._actionToShortcut.get(actionId)].map(shortcut => shortcut.descriptors[0]); |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | /** |
| 108 | * @param {!Array.<string>} actionIds |
| 109 | * @return {!Array.<number>} |
| 110 | */ |
| 111 | keysForActions(actionIds) { |
Jack Lynch | b8fb3c7 | 2020-04-21 05:36:16 | [diff] [blame] | 112 | const keys = actionIds.flatMap( |
| 113 | action => [...this._actionToShortcut.get(action)].flatMap( |
| 114 | shortcut => shortcut.descriptors.map(descriptor => descriptor.key))); |
| 115 | return [...(new Set(keys))]; |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | /** |
| 119 | * @param {string} actionId |
| 120 | * @return {string|undefined} |
| 121 | */ |
| 122 | shortcutTitleForAction(actionId) { |
Jack Lynch | b8fb3c7 | 2020-04-21 05:36:16 | [diff] [blame] | 123 | const shortcuts = this._actionToShortcut.get(actionId); |
| 124 | if (shortcuts.size) { |
| 125 | return shortcuts.firstValue().title(); |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 126 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | /** |
| 130 | * @param {!KeyboardEvent} event |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 131 | * @param {!Object.<string, function():Promise.<boolean>>=} handlers |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 132 | */ |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 133 | handleShortcut(event, handlers) { |
| 134 | this.handleKey(KeyboardShortcut.makeKeyFromEvent(event), event.key, event, handlers); |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | /** |
| 138 | * @param {!Element} element |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 139 | * @param {!Object.<string, function():Promise.<boolean>>} handlers |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 140 | */ |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 141 | addShortcutListener(element, handlers) { |
| 142 | // We only want keys for these specific actions to get handled this |
| 143 | // way; all others should be allowed to bubble up |
| 144 | const whitelistKeyMap = new ShortcutTreeNode(0, 0); |
| 145 | const shortcuts = Object.keys(handlers).flatMap(action => [...this._actionToShortcut.get(action)]); |
| 146 | shortcuts.forEach(shortcut => { |
| 147 | whitelistKeyMap.addKeyMapping(shortcut.descriptors.map(descriptor => descriptor.key), shortcut.action); |
| 148 | }); |
| 149 | |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 150 | element.addEventListener('keydown', event => { |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 151 | const key = KeyboardShortcut.makeKeyFromEvent(/** @type {!KeyboardEvent} */ (event)); |
| 152 | let keyMap = whitelistKeyMap; |
| 153 | if (this._activePrefixKey) { |
| 154 | keyMap = keyMap.getNode(this._activePrefixKey.key()); |
| 155 | if (!keyMap) { |
| 156 | return; |
| 157 | } |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 158 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 159 | if (keyMap.getNode(key)) { |
| 160 | this.handleShortcut(/** @type {!KeyboardEvent} */ (event), handlers); |
| 161 | } |
| 162 | }); |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | /** |
| 166 | * @param {number} key |
| 167 | * @param {string} domKey |
| 168 | * @param {!KeyboardEvent=} event |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 169 | * @param {!Object.<string, function():Promise.<boolean>>=} handlers |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 170 | */ |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 171 | async handleKey(key, domKey, event, handlers) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 172 | const keyModifiers = key >> 8; |
Jack Lynch | a0dd1f0 | 2020-05-04 23:33:35 | [diff] [blame] | 173 | const hasHandlersOrPrefixKey = !!handlers || !!this._activePrefixKey; |
Jack Lynch | 4a6f753 | 2020-05-07 00:48:09 | [diff] [blame] | 174 | const keyMapNode = this._keyMap.getNode(key); |
| 175 | const maybeHasActions = this._applicableActions(key, handlers).length > 0 || (keyMapNode && keyMapNode.hasChords()); |
Jack Lynch | a0dd1f0 | 2020-05-04 23:33:35 | [diff] [blame] | 176 | if ((!hasHandlersOrPrefixKey && isPossiblyInputKey()) || !maybeHasActions || |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 177 | KeyboardShortcut.isModifier(KeyboardShortcut.keyCodeAndModifiersFromKey(key).keyCode)) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 178 | return; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 179 | } |
Jack Lynch | a0dd1f0 | 2020-05-04 23:33:35 | [diff] [blame] | 180 | if (event) { |
| 181 | event.consume(true); |
| 182 | } |
| 183 | if (!hasHandlersOrPrefixKey && Dialog.hasInstance()) { |
| 184 | return; |
| 185 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 186 | |
| 187 | if (this._activePrefixTimeout) { |
| 188 | clearTimeout(this._activePrefixTimeout); |
| 189 | const handled = await maybeExecuteActionForKey.call(this); |
| 190 | this._activePrefixKey = null; |
| 191 | this._activePrefixTimeout = null; |
| 192 | if (handled) { |
| 193 | return; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 194 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 195 | if (this._consumePrefix) { |
| 196 | await this._consumePrefix(); |
| 197 | } |
| 198 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 199 | if (keyMapNode && keyMapNode.hasChords()) { |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 200 | this._activePrefixKey = keyMapNode; |
| 201 | this._consumePrefix = async () => { |
| 202 | this._activePrefixKey = null; |
| 203 | this._activePrefixTimeout = null; |
| 204 | await maybeExecuteActionForKey.call(this); |
| 205 | }; |
| 206 | this._activePrefixTimeout = setTimeout(this._consumePrefix, KeyTimeout); |
| 207 | } else { |
| 208 | await maybeExecuteActionForKey.call(this); |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 209 | } |
| 210 | |
| 211 | /** |
| 212 | * @return {boolean} |
| 213 | */ |
| 214 | function isPossiblyInputKey() { |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 215 | if (!event || !isEditing() || /^F\d+|Control|Shift|Alt|Meta|Escape|Win|U\+001B$/.test(domKey)) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 216 | return false; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 217 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 218 | |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 219 | if (!keyModifiers) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 220 | return true; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 221 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 222 | |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 223 | const modifiers = Modifiers; |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 224 | // Undo/Redo will also cause input, so textual undo should take precedence over DevTools undo when editing. |
Paul Lewis | 0fd4371 | 2020-01-08 17:07:36 | [diff] [blame] | 225 | if (Host.Platform.isMac()) { |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 226 | if (KeyboardShortcut.makeKey('z', modifiers.Meta) === key) { |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 227 | return true; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 228 | } |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 229 | if (KeyboardShortcut.makeKey('z', modifiers.Meta | modifiers.Shift) === key) { |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 230 | return true; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 231 | } |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 232 | } else { |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 233 | if (KeyboardShortcut.makeKey('z', modifiers.Ctrl) === key) { |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 234 | return true; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 235 | } |
Paul Lewis | 9950e18 | 2019-12-16 16:06:07 | [diff] [blame] | 236 | if (KeyboardShortcut.makeKey('y', modifiers.Ctrl) === key) { |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 237 | return true; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 238 | } |
Paul Lewis | 0fd4371 | 2020-01-08 17:07:36 | [diff] [blame] | 239 | if (!Host.Platform.isWin() && KeyboardShortcut.makeKey('z', modifiers.Ctrl | modifiers.Shift) === key) { |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 240 | return true; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 241 | } |
Joel Einbinder | a66e5bf | 2018-05-31 01:26:37 | [diff] [blame] | 242 | } |
| 243 | |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 244 | if ((keyModifiers & (modifiers.Ctrl | modifiers.Alt)) === (modifiers.Ctrl | modifiers.Alt)) { |
Paul Lewis | 0fd4371 | 2020-01-08 17:07:36 | [diff] [blame] | 245 | return Host.Platform.isWin(); |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 246 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 247 | |
| 248 | return !hasModifier(modifiers.Ctrl) && !hasModifier(modifiers.Alt) && !hasModifier(modifiers.Meta); |
| 249 | } |
| 250 | |
| 251 | /** |
| 252 | * @param {number} mod |
| 253 | * @return {boolean} |
| 254 | */ |
| 255 | function hasModifier(mod) { |
| 256 | return !!(keyModifiers & mod); |
| 257 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 258 | |
| 259 | /** |
| 260 | * @return {!Promise.<boolean>}; |
| 261 | * @this {!ShortcutRegistry} |
| 262 | */ |
| 263 | async function maybeExecuteActionForKey() { |
| 264 | const actions = this._applicableActions(key, handlers); |
| 265 | if (!actions.length) { |
| 266 | return false; |
| 267 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 268 | for (const action of actions) { |
| 269 | let handled; |
| 270 | if (handlers && handlers[action.id()]) { |
| 271 | handled = await handlers[action.id()](); |
| 272 | } |
| 273 | if (!handlers) { |
| 274 | handled = await action.execute(); |
| 275 | } |
| 276 | if (handled) { |
| 277 | Host.userMetrics.keyboardShortcutFired(action.id()); |
| 278 | return true; |
| 279 | } |
| 280 | } |
| 281 | return false; |
| 282 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | /** |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 286 | * @param {!KeyboardShortcut} shortcut |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 287 | */ |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 288 | _registerShortcut(shortcut) { |
| 289 | this._actionToShortcut.set(shortcut.action, shortcut); |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 290 | this._keyMap.addKeyMapping(shortcut.descriptors.map(descriptor => descriptor.key), shortcut.action); |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 291 | } |
| 292 | |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 293 | _registerBindings() { |
Jack Lynch | f1f00fa | 2020-05-01 22:16:12 | [diff] [blame] | 294 | this._keyToShortcut.clear(); |
| 295 | this._actionToShortcut.clear(); |
| 296 | this._keyMap.clear(); |
| 297 | const keybindSet = self.Common.settings.moduleSetting('activeKeybindSet').get(); |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 298 | const extensions = self.runtime.extensions('action'); |
| 299 | extensions.forEach(registerExtension, this); |
| 300 | |
| 301 | /** |
Tim van der Lippe | 99e59b8 | 2019-09-30 20:00:59 | [diff] [blame] | 302 | * @param {!Root.Runtime.Extension} extension |
Tim van der Lippe | 0830b3d | 2019-10-03 13:20:07 | [diff] [blame] | 303 | * @this {ShortcutRegistry} |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 304 | */ |
| 305 | function registerExtension(extension) { |
| 306 | const descriptor = extension.descriptor(); |
Jack Lynch | 94a9b0c | 2020-03-11 21:45:14 | [diff] [blame] | 307 | const bindings = descriptor.bindings; |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 308 | for (let i = 0; bindings && i < bindings.length; ++i) { |
Jack Lynch | f1f00fa | 2020-05-01 22:16:12 | [diff] [blame] | 309 | const keybindSets = bindings[i].keybindSets; |
| 310 | if (!platformMatches(bindings[i].platform) || !keybindSetsMatch(keybindSets)) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 311 | continue; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 312 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 313 | const keys = bindings[i].shortcut.split(/\s+/); |
| 314 | const shortcutDescriptors = keys.map(KeyboardShortcut.makeDescriptorFromBindingShortcut); |
| 315 | if (shortcutDescriptors.length > 0) { |
Jack Lynch | f1f00fa | 2020-05-01 22:16:12 | [diff] [blame] | 316 | const actionId = /** @type {string} */ (descriptor.actionId); |
| 317 | if (!keybindSets) { |
| 318 | this._registerShortcut(new KeyboardShortcut(shortcutDescriptors, actionId, Type.DefaultShortcut)); |
| 319 | } else { |
| 320 | this._registerShortcut( |
| 321 | new KeyboardShortcut(shortcutDescriptors, actionId, Type.KeybindSetShortcut, keybindSet)); |
| 322 | } |
Jack Lynch | f0d1424 | 2020-04-07 22:42:04 | [diff] [blame] | 323 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 324 | } |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * @param {string=} platformsString |
| 329 | * @return {boolean} |
| 330 | */ |
| 331 | function platformMatches(platformsString) { |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 332 | if (!platformsString) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 333 | return true; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 334 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 335 | const platforms = platformsString.split(','); |
| 336 | let isMatch = false; |
Paul Lewis | 0fd4371 | 2020-01-08 17:07:36 | [diff] [blame] | 337 | const currentPlatform = Host.Platform.platform(); |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 338 | for (let i = 0; !isMatch && i < platforms.length; ++i) { |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 339 | isMatch = platforms[i] === currentPlatform; |
Tim van der Lippe | 1d6e57a | 2019-09-30 11:55:34 | [diff] [blame] | 340 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 341 | return isMatch; |
| 342 | } |
Jack Lynch | f1f00fa | 2020-05-01 22:16:12 | [diff] [blame] | 343 | |
| 344 | /** |
| 345 | * @param {!Array<string>=} keybindSets |
| 346 | */ |
| 347 | function keybindSetsMatch(keybindSets) { |
| 348 | if (!keybindSets) { |
| 349 | return true; |
| 350 | } |
| 351 | return keybindSets.includes(keybindSet); |
| 352 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 353 | } |
Tim van der Lippe | 0830b3d | 2019-10-03 13:20:07 | [diff] [blame] | 354 | } |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 355 | |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 356 | export class ShortcutTreeNode { |
| 357 | /** |
| 358 | * @param {number} key |
| 359 | * @param {number=} depth |
| 360 | */ |
| 361 | constructor(key, depth = 0) { |
| 362 | this._key = key; |
| 363 | /** @type {!Array.<string>} */ |
| 364 | this._actions = []; |
| 365 | this._chords = new Map(); |
| 366 | this._depth = depth; |
| 367 | } |
| 368 | |
| 369 | /** |
| 370 | * @param {string} action |
| 371 | */ |
| 372 | addAction(action) { |
| 373 | this._actions.push(action); |
| 374 | } |
| 375 | |
| 376 | /** |
| 377 | * @return {number} |
| 378 | */ |
| 379 | key() { |
| 380 | return this._key; |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * @return {!Map.<number, !ShortcutTreeNode>} |
| 385 | */ |
| 386 | chords() { |
| 387 | return this._chords; |
| 388 | } |
| 389 | |
| 390 | /** |
| 391 | * @return {boolean} |
| 392 | */ |
| 393 | hasChords() { |
| 394 | return this._chords.size > 0; |
| 395 | } |
| 396 | |
| 397 | /** |
| 398 | * @param {!Array.<number>} keys |
| 399 | * @param {string} action |
| 400 | */ |
| 401 | addKeyMapping(keys, action) { |
| 402 | if (keys.length < this._depth) { |
| 403 | return; |
| 404 | } |
| 405 | |
| 406 | if (keys.length === this._depth) { |
| 407 | this.addAction(action); |
| 408 | } else { |
| 409 | const key = keys[this._depth]; |
| 410 | if (!this._chords.has(key)) { |
| 411 | this._chords.set(key, new ShortcutTreeNode(key, this._depth + 1)); |
| 412 | } |
| 413 | this._chords.get(key).addKeyMapping(keys, action); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | /** |
| 418 | * @param {number} key |
| 419 | * @return {?ShortcutTreeNode} |
| 420 | */ |
| 421 | getNode(key) { |
| 422 | return this._chords.get(key) || null; |
| 423 | } |
| 424 | |
| 425 | /** |
| 426 | * @return {!Array.<string>} |
| 427 | */ |
| 428 | actions() { |
| 429 | return this._actions; |
| 430 | } |
Jack Lynch | f1f00fa | 2020-05-01 22:16:12 | [diff] [blame] | 431 | |
| 432 | clear() { |
| 433 | this._actions = []; |
| 434 | this._chords = new Map(); |
| 435 | } |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 436 | } |
| 437 | |
Sigurd Schneider | 46da7db | 2020-05-20 13:45:11 | [diff] [blame^] | 438 | |
Tim van der Lippe | 0830b3d | 2019-10-03 13:20:07 | [diff] [blame] | 439 | export class ForwardedShortcut {} |
Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame] | 440 | |
Tim van der Lippe | 0830b3d | 2019-10-03 13:20:07 | [diff] [blame] | 441 | ForwardedShortcut.instance = new ForwardedShortcut(); |
Jack Lynch | 966040b | 2020-04-23 20:11:44 | [diff] [blame] | 442 | |
| 443 | export const KeyTimeout = 1000; |
Jack Lynch | f1f00fa | 2020-05-01 22:16:12 | [diff] [blame] | 444 | export const DefaultShortcutSetting = 'devToolsDefault'; |