Skip to content

Commit c36c037

Browse files
Kateryna ProkopenkoDevtools-frontend LUCI CQ
authored andcommitted
Add option to add accelerators to context menus
Bug: 368240754 Change-Id: Iae0c701e42a17350744e036c66ade1c9fcf43b8f Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5934529 Reviewed-by: Kim-Anh Tran <[email protected]> Commit-Queue: Kateryna Prokopenko <[email protected]>
1 parent 2c03ae3 commit c36c037

File tree

10 files changed

+77
-44
lines changed

10 files changed

+77
-44
lines changed

front_end/core/host/InspectorFrontendHostAPI.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,16 @@ export interface InspectorFrontendHostAPI {
379379
recordKeyDown(event: KeyDownEvent): void;
380380
}
381381

382+
export interface AcceleratorDescriptor {
383+
keyCode: number;
384+
modifiers: number;
385+
}
386+
382387
export interface ContextMenuDescriptor {
383388
type: 'checkbox'|'item'|'separator'|'subMenu';
384389
id?: number;
385390
label?: string;
391+
accelerator?: AcceleratorDescriptor;
386392
isExperimentalFeature?: boolean;
387393
enabled?: boolean;
388394
checked?: boolean;

front_end/panels/console/ConsoleView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1363,7 +1363,7 @@ export class ConsoleView extends UI.Widget.VBox implements
13631363

13641364
private registerShortcuts(): void {
13651365
this.shortcuts.set(
1366-
UI.KeyboardShortcut.KeyboardShortcut.makeKey('u', UI.KeyboardShortcut.Modifiers.Ctrl),
1366+
UI.KeyboardShortcut.KeyboardShortcut.makeKey('u', UI.KeyboardShortcut.Modifiers.Ctrl.value),
13671367
this.clearPromptBackwards.bind(this));
13681368
}
13691369

front_end/panels/elements/ElementsTreeElement.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
797797
const isShadowRoot = this.nodeInternal.isShadowRoot();
798798

799799
const createShortcut = UI.KeyboardShortcut.KeyboardShortcut.shortcutToString.bind(null);
800-
const modifier = UI.KeyboardShortcut.Modifiers.CtrlOrMeta;
800+
const modifier = UI.KeyboardShortcut.Modifiers.CtrlOrMeta.value;
801801
const treeOutline = this.treeOutline;
802802
if (!treeOutline) {
803803
return;

front_end/panels/recorder/models/RecordingSession.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ const createShortcuts = (descriptors: number[][]): Shortcut[] => {
9292
shortcutBase.keyCode = keyCode;
9393
const modifiersMap = UI.KeyboardShortcut.Modifiers;
9494

95-
shortcutBase.ctrl = Boolean(modifiers & modifiersMap.Ctrl);
96-
shortcutBase.meta = Boolean(modifiers & modifiersMap.Meta);
97-
shortcutBase.shift = Boolean(modifiers & modifiersMap.Shift);
98-
shortcutBase.shift = Boolean(modifiers & modifiersMap.Alt);
95+
shortcutBase.ctrl = Boolean(modifiers & modifiersMap.Ctrl.value);
96+
shortcutBase.meta = Boolean(modifiers & modifiersMap.Meta.value);
97+
shortcutBase.shift = Boolean(modifiers & modifiersMap.Shift.value);
98+
shortcutBase.shift = Boolean(modifiers & modifiersMap.Alt.value);
9999

100100
if (shortcutBase.keyCode !== -1) {
101101
shortcuts.push(shortcutBase);

front_end/panels/screencast/ScreencastView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
193193
this.context = this.canvasElement.getContext('2d') as CanvasRenderingContext2D;
194194
this.checkerboardPattern = this.createCheckerboardPattern(this.context);
195195

196-
this.shortcuts[UI.KeyboardShortcut.KeyboardShortcut.makeKey('l', UI.KeyboardShortcut.Modifiers.Ctrl)] =
196+
this.shortcuts[UI.KeyboardShortcut.KeyboardShortcut.makeKey('l', UI.KeyboardShortcut.Modifiers.Ctrl.value)] =
197197
this.focusNavigationBar.bind(this);
198198

199199
SDK.TargetManager.TargetManager.instance().addEventListener(

front_end/ui/legacy/ContextMenu.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ import * as Root from '../../core/root/root.js';
3535
import * as VisualLogging from '../visual_logging/visual_logging.js';
3636

3737
import {ActionRegistry} from './ActionRegistry.js';
38+
import type {Key, Modifier} from './KeyboardShortcut.js';
3839
import {ShortcutRegistry} from './ShortcutRegistry.js';
3940
import {SoftContextMenu, type SoftContextMenuDescriptor} from './SoftContextMenu.js';
4041
import {deepElementFromEvent} from './UIUtils.js';
4142

4243
export class Item {
4344
private readonly typeInternal: string;
4445
protected readonly label: string|undefined;
46+
protected accelerator?: Host.InspectorFrontendHostAPI.AcceleratorDescriptor;
4547
protected readonly previewFeature: boolean;
4648
protected disabled: boolean|undefined;
4749
private readonly checked: boolean|undefined;
@@ -54,11 +56,13 @@ export class Item {
5456

5557
constructor(
5658
contextMenu: ContextMenu|null, type: 'checkbox'|'item'|'separator'|'subMenu', label?: string,
57-
isPreviewFeature?: boolean, disabled?: boolean, checked?: boolean, tooltip?: Platform.UIString.LocalizedString,
59+
isPreviewFeature?: boolean, disabled?: boolean, checked?: boolean,
60+
accelerator?: Host.InspectorFrontendHostAPI.AcceleratorDescriptor, tooltip?: Platform.UIString.LocalizedString,
5861
jslogContext?: string) {
5962
this.typeInternal = type;
6063
this.label = label;
6164
this.previewFeature = Boolean(isPreviewFeature);
65+
this.accelerator = accelerator;
6266
this.disabled = disabled;
6367
this.checked = checked;
6468
this.contextMenu = contextMenu;
@@ -113,6 +117,9 @@ export class Item {
113117
if (this.shortcut) {
114118
result.shortcut = this.shortcut;
115119
}
120+
if (this.accelerator) {
121+
result.accelerator = this.accelerator;
122+
}
116123
return result;
117124
}
118125
case 'separator': {
@@ -145,6 +152,11 @@ export class Item {
145152
throw new Error('Invalid item type:' + this.typeInternal);
146153
}
147154

155+
setAccelerator(key: Key, modifiers: Modifier[]): void {
156+
const modifierSum = modifiers.reduce((result, modifier) => result + modifier.value, 0);
157+
this.accelerator = {keyCode: key.code, modifiers: modifierSum};
158+
}
159+
148160
setShortcut(shortcut: string): void {
149161
this.shortcut = shortcut;
150162
}
@@ -159,15 +171,16 @@ export class Section {
159171
}
160172

161173
appendItem(label: string, handler: () => void, options?: {
174+
accelerator?: Host.InspectorFrontendHostAPI.AcceleratorDescriptor,
162175
isPreviewFeature?: boolean,
163176
disabled?: boolean,
164177
additionalElement?: Element,
165178
tooltip?: Platform.UIString.LocalizedString,
166179
jslogContext?: string,
167180
}): Item {
168181
const item = new Item(
169-
this.contextMenu, 'item', label, options?.isPreviewFeature, options?.disabled, undefined, options?.tooltip,
170-
options?.jslogContext);
182+
this.contextMenu, 'item', label, options?.isPreviewFeature, options?.disabled, undefined, options?.accelerator,
183+
options?.tooltip, options?.jslogContext);
171184
if (options?.additionalElement) {
172185
item.customElement = options?.additionalElement;
173186
}
@@ -179,8 +192,8 @@ export class Section {
179192
}
180193

181194
appendCustomItem(element: Element, jslogContext?: string): Item {
182-
const item =
183-
new Item(this.contextMenu, 'item', undefined, undefined, undefined, undefined, undefined, jslogContext);
195+
const item = new Item(
196+
this.contextMenu, 'item', undefined, undefined, undefined, undefined, undefined, undefined, jslogContext);
184197
item.customElement = element;
185198
this.items.push(item);
186199
return item;
@@ -225,8 +238,8 @@ export class Section {
225238
jslogContext?: string,
226239
}): Item {
227240
const item = new Item(
228-
this.contextMenu, 'checkbox', label, undefined, options?.disabled, options?.checked, options?.tooltip,
229-
options?.jslogContext);
241+
this.contextMenu, 'checkbox', label, undefined, options?.disabled, options?.checked, undefined,
242+
options?.tooltip, options?.jslogContext);
230243
this.items.push(item);
231244
if (this.contextMenu) {
232245
this.contextMenu.setHandler(item.id(), handler);
@@ -243,7 +256,7 @@ export class SubMenu extends Item {
243256
private readonly sectionList: Section[];
244257

245258
constructor(contextMenu: ContextMenu|null, label?: string, disabled?: boolean, jslogContext?: string) {
246-
super(contextMenu, 'subMenu', label, undefined, disabled, undefined, undefined, jslogContext);
259+
super(contextMenu, 'subMenu', label, undefined, disabled, undefined, undefined, undefined, jslogContext);
247260
this.sections = new Map();
248261
this.sectionList = [];
249262
}
@@ -317,6 +330,7 @@ export class SubMenu extends Item {
317330
const result: Host.InspectorFrontendHostAPI.ContextMenuDescriptor|SoftContextMenuDescriptor = {
318331
type: 'subMenu',
319332
label: this.label,
333+
accelerator: this.accelerator,
320334
isExperimentalFeature: this.previewFeature,
321335
enabled: !this.disabled,
322336
subItems: [],

front_end/ui/legacy/FilterBar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export class NamedBitSetFilterUI extends Common.ObjectWrapper.ObjectWrapper<Filt
282282
ARIAUtils.markAsListBox(this.filtersElement);
283283
ARIAUtils.markAsMultiSelectable(this.filtersElement);
284284
Tooltip.install(this.filtersElement, i18nString(UIStrings.sclickToSelectMultipleTypes, {
285-
PH1: KeyboardShortcut.shortcutToString('', Modifiers.CtrlOrMeta),
285+
PH1: KeyboardShortcut.shortcutToString('', Modifiers.CtrlOrMeta.value),
286286
}));
287287

288288
this.typeFilterElementTypeNames = new WeakMap();

front_end/ui/legacy/KeyboardShortcut.ts

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -98,23 +98,23 @@ export class KeyboardShortcut {
9898
if (typeof keyCode === 'string') {
9999
keyCode = keyCode.charCodeAt(0) - (/^[a-z]/.test(keyCode) ? 32 : 0);
100100
}
101-
modifiers = modifiers || Modifiers.None;
101+
modifiers = modifiers || Modifiers.None.value;
102102
return KeyboardShortcut.makeKeyFromCodeAndModifiers(keyCode, modifiers);
103103
}
104104

105105
static makeKeyFromEvent(keyboardEvent: KeyboardEvent): number {
106-
let modifiers = Modifiers.None;
106+
let modifiers = Modifiers.None.value;
107107
if (keyboardEvent.shiftKey) {
108-
modifiers |= Modifiers.Shift;
108+
modifiers |= Modifiers.Shift.value;
109109
}
110110
if (keyboardEvent.ctrlKey) {
111-
modifiers |= Modifiers.Ctrl;
111+
modifiers |= Modifiers.Ctrl.value;
112112
}
113113
if (keyboardEvent.altKey) {
114-
modifiers |= Modifiers.Alt;
114+
modifiers |= Modifiers.Alt.value;
115115
}
116116
if (keyboardEvent.metaKey) {
117-
modifiers |= Modifiers.Meta;
117+
modifiers |= Modifiers.Meta.value;
118118
}
119119

120120
// Use either a real or a synthetic keyCode (for events originating from extensions).
@@ -126,7 +126,7 @@ export class KeyboardShortcut {
126126
static makeKeyFromEventIgnoringModifiers(keyboardEvent: KeyboardEvent): number {
127127
// @ts-ignore ExtensionServer.js installs '__keyCode' on some events.
128128
const keyCode = keyboardEvent.keyCode || keyboardEvent['__keyCode'];
129-
return KeyboardShortcut.makeKeyFromCodeAndModifiers(keyCode, Modifiers.None);
129+
return KeyboardShortcut.makeKeyFromCodeAndModifiers(keyCode, Modifiers.None.value);
130130
}
131131

132132
// This checks if a "control equivalent" key is pressed. For non-mac platforms this means checking
@@ -159,7 +159,7 @@ export class KeyboardShortcut {
159159
const [keyString, ...modifierStrings] = shortcut.split(/\+(?!$)/).reverse();
160160
let modifiers = 0;
161161
for (const modifierString of modifierStrings) {
162-
const modifier = Modifiers[modifierString];
162+
const modifier = Modifiers[modifierString].value;
163163
console.assert(
164164
typeof modifier !== 'undefined', `Only one key other than modifier is allowed in shortcut <${shortcut}>`);
165165
modifiers |= modifier;
@@ -168,7 +168,7 @@ export class KeyboardShortcut {
168168

169169
const key = Keys[keyString] || KeyBindings[keyString];
170170
if (key && 'shiftKey' in key && key.shiftKey) {
171-
modifiers |= Modifiers.Shift;
171+
modifiers |= Modifiers.Shift.value;
172172
}
173173
return KeyboardShortcut.makeDescriptor(key ? key : keyString, modifiers);
174174
}
@@ -218,28 +218,37 @@ export class KeyboardShortcut {
218218
]);
219219
return [m.Meta, m.Ctrl, m.Alt, m.Shift].map(mapModifiers).join('');
220220

221-
function mapModifiers(m: number): string {
222-
return (modifiers || 0) & m ? /** @type {string} */ modifierNames.get(m) as string : '';
221+
function mapModifiers(m: Modifier): string {
222+
return (modifiers || 0) & m.value ? /** @type {string} */ modifierNames.get(m) as string : '';
223223
}
224224
}
225225
}
226226

227+
export interface Modifier {
228+
value: number;
229+
name: string;
230+
}
231+
227232
/**
228233
* Constants for encoding modifier key set as a bit mask.
229234
* see #makeKeyFromCodeAndModifiers
230235
*/
231236
export const Modifiers: {
232-
[x: string]: number,
237+
[x: string]: Modifier,
233238
} = {
234-
None: 0,
235-
Shift: 1,
236-
Ctrl: 2,
237-
Alt: 4,
238-
Meta: 8,
239-
// "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
240-
CtrlOrMeta: Host.Platform.isMac() ? 8 /* Meta */ : 2 /* Ctrl */,
241-
// Option on Mac, Shift on other platforms
242-
ShiftOrOption: Host.Platform.isMac() ? 4 /* Alt */ : 1 /* Shift */,
239+
None: {value: 0, name: 'None'},
240+
Shift: {value: 1, name: 'Shift'},
241+
Ctrl: {value: 2, name: 'Ctrl'},
242+
Alt: {value: 4, name: 'Alt'},
243+
Meta: {value: 8, name: 'Meta'},
244+
CtrlOrMeta: {
245+
value: Host.Platform.isMac() ? 8 /* Meta */ : 2 /* Ctrl */,
246+
name: Host.Platform.isMac() ? 'Meta' : 'Ctrl',
247+
},
248+
ShiftOrOption: {
249+
value: Host.Platform.isMac() ? 4 /* Alt */ : 1 /* Shift */,
250+
name: Host.Platform.isMac() ? 'Alt' : 'Shift',
251+
},
243252
};
244253

245254
const leftKey = {

front_end/ui/legacy/ShortcutRegistry.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,29 +227,32 @@ export class ShortcutRegistry {
227227
const modifiers = Modifiers;
228228
// Undo/Redo will also cause input, so textual undo should take precedence over DevTools undo when editing.
229229
if (Host.Platform.isMac()) {
230-
if (KeyboardShortcut.makeKey('z', modifiers.Meta) === key) {
230+
if (KeyboardShortcut.makeKey('z', modifiers.Meta.value) === key) {
231231
return true;
232232
}
233-
if (KeyboardShortcut.makeKey('z', modifiers.Meta | modifiers.Shift) === key) {
233+
if (KeyboardShortcut.makeKey('z', modifiers.Meta.value | modifiers.Shift.value) === key) {
234234
return true;
235235
}
236236
} else {
237-
if (KeyboardShortcut.makeKey('z', modifiers.Ctrl) === key) {
237+
if (KeyboardShortcut.makeKey('z', modifiers.Ctrl.value) === key) {
238238
return true;
239239
}
240-
if (KeyboardShortcut.makeKey('y', modifiers.Ctrl) === key) {
240+
if (KeyboardShortcut.makeKey('y', modifiers.Ctrl.value) === key) {
241241
return true;
242242
}
243-
if (!Host.Platform.isWin() && KeyboardShortcut.makeKey('z', modifiers.Ctrl | modifiers.Shift) === key) {
243+
if (!Host.Platform.isWin() &&
244+
KeyboardShortcut.makeKey('z', modifiers.Ctrl.value | modifiers.Shift.value) === key) {
244245
return true;
245246
}
246247
}
247248

248-
if ((keyModifiers & (modifiers.Ctrl | modifiers.Alt)) === (modifiers.Ctrl | modifiers.Alt)) {
249+
if ((keyModifiers & (modifiers.Ctrl.value | modifiers.Alt.value)) ===
250+
(modifiers.Ctrl.value | modifiers.Alt.value)) {
249251
return Host.Platform.isWin();
250252
}
251253

252-
return !hasModifier(modifiers.Ctrl) && !hasModifier(modifiers.Alt) && !hasModifier(modifiers.Meta);
254+
return !hasModifier(modifiers.Ctrl.value) && !hasModifier(modifiers.Alt.value) &&
255+
!hasModifier(modifiers.Meta.value);
253256
}
254257

255258
function hasModifier(mod: number): boolean {

front_end/ui/legacy/SoftContextMenu.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ export interface SoftContextMenuDescriptor {
640640
type: 'checkbox'|'item'|'separator'|'subMenu';
641641
id?: number;
642642
label?: string;
643+
accelerator?: {keyCode: number, modifiers: number};
643644
isExperimentalFeature?: boolean;
644645
enabled?: boolean;
645646
checked?: boolean;

0 commit comments

Comments
 (0)