Skip to content

Commit 27ef610

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
RPP: update UI for keyboard navigation shortcut dialog
Classic mode: screen/628PBCtSSGDNjCM Modern mode: screen/ALKdxWdbJ9p9aVA Bug: 406008928 Change-Id: Iecbc14632c2c867ef484ba05fbf7c655ecea8055 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6415910 Reviewed-by: Paul Irish <[email protected]> Feels: Paul Irish <[email protected]> Commit-Queue: Jack Franklin <[email protected]>
1 parent f87193b commit 27ef610

File tree

7 files changed

+146
-62
lines changed

7 files changed

+146
-62
lines changed

front_end/panels/recorder/RecorderController.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,26 +1117,30 @@ export class RecorderController extends LitElement {
11171117
}
11181118

11191119
#getShortcutsInfo(): Dialogs.ShortcutDialog.Shortcut[] {
1120-
const getBindingForAction = (action: Actions.RecorderActions): string[][] => {
1120+
const getBindingForAction = (action: Actions.RecorderActions): Dialogs.ShortcutDialog.ShortcutPart[][] => {
11211121
const shortcuts = UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutsForAction(action);
1122-
const shortcutsWithSplitBindings =
1123-
shortcuts.map(shortcut => shortcut.title().split(/[\s+]+/).map(word => word.trim()));
1122+
const shortcutsWithSplitBindings = shortcuts.map(shortcut => shortcut.title().split(/[\s+]+/).map(word => {
1123+
return {key: word.trim()};
1124+
}));
11241125
return shortcutsWithSplitBindings;
11251126
};
11261127

11271128
return [
11281129
{
11291130
title: i18nString(UIStrings.startStopRecording),
1130-
bindings: getBindingForAction(Actions.RecorderActions.START_RECORDING),
1131+
rows: getBindingForAction(Actions.RecorderActions.START_RECORDING),
11311132
},
11321133
{
11331134
title: i18nString(UIStrings.replayRecording),
1134-
bindings: getBindingForAction(Actions.RecorderActions.REPLAY_RECORDING),
1135+
rows: getBindingForAction(Actions.RecorderActions.REPLAY_RECORDING),
1136+
},
1137+
{
1138+
title: i18nString(UIStrings.copyShortcut),
1139+
rows: Host.Platform.isMac() ? [[{key: '⌘'}, {key: 'C'}]] : [[{key: 'Ctrl'}, {key: 'C'}]]
11351140
},
1136-
{title: i18nString(UIStrings.copyShortcut), bindings: Host.Platform.isMac() ? [['⌘', 'C']] : [['Ctrl', 'C']]},
11371141
{
11381142
title: i18nString(UIStrings.toggleCode),
1139-
bindings: getBindingForAction(Actions.RecorderActions.TOGGLE_CODE_VIEW),
1143+
rows: getBindingForAction(Actions.RecorderActions.TOGGLE_CODE_VIEW),
11401144
},
11411145
];
11421146
}

front_end/panels/timeline/TimelinePanel.ts

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -317,21 +317,13 @@ const UIStrings = {
317317
*/
318318
backToLiveMetrics: 'Go back to the live metrics page',
319319
/**
320-
* @description Description of the Timeline up/down scroll action that appears in the Performance panel shortcuts dialog.
320+
* @description Description of the Timeline zoom keyboard instructions that appear in the shortcuts dialog
321321
*/
322-
timelineScrollUpDown: 'Move up/down',
322+
timelineZoom: 'Zoom',
323323
/**
324-
* @description Description of the Timeline left/right panning action that appears in the Performance panel shortcuts dialog.
324+
* @description Description of the Timeline scrolling & panning instructions that appear in the shortcuts dialog.
325325
*/
326-
timelinePanLeftRight: 'Move left/right',
327-
/**
328-
* @description Description of the Timeline in/out zoom action that appears in the Performance panel shortcuts dialog.
329-
*/
330-
timelineZoomInOut: 'Zoom in/out',
331-
/**
332-
* @description Description of the Timeline fast in/out zoom action that appears in the Performance panel shortcuts dialog.
333-
*/
334-
timelineFastZoomInOut: 'Fast zoom in/out',
326+
timelineScrollPan: 'Scroll & Pan',
335327
/**
336328
* @description Title for the Dim 3rd Parties checkbox.
337329
*/
@@ -340,6 +332,10 @@ const UIStrings = {
340332
* @description Description for the Dim 3rd Parties checkbox tooltip describing how 3rd parties are classified.
341333
*/
342334
thirdPartiesByThirdPartyWeb: '3rd parties classified by third-party-web',
335+
/**
336+
* @description Title of the shortcuts dialog shown to the user that lists keyboard shortcuts.
337+
*/
338+
shortcutsDialogTitle: 'Keyboard shortcuts for flamechart'
343339
} as const;
344340
const str_ = i18n.i18n.registerUIStrings('panels/timeline/TimelinePanel.ts', UIStrings);
345341
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -481,10 +477,10 @@ export class TimelinePanel extends UI.Panel.Panel implements Client, TimelineMod
481477
* Navigation radio buttons located in the shortcuts dialog.
482478
*/
483479
#navigationRadioButtons = document.createElement('form');
484-
#modernNavRadioButton =
485-
UI.UIUtils.createRadioButton('flamechart-selected-navigation', 'Modern', 'timeline.select-modern-navigation');
486-
#classicNavRadioButton =
487-
UI.UIUtils.createRadioButton('flamechart-selected-navigation', 'Classic', 'timeline.select-classic-navigation');
480+
#modernNavRadioButton = UI.UIUtils.createRadioButton(
481+
'flamechart-selected-navigation', 'Modern - normal scrolling', 'timeline.select-modern-navigation');
482+
#classicNavRadioButton = UI.UIUtils.createRadioButton(
483+
'flamechart-selected-navigation', 'Classic - scroll to zoom', 'timeline.select-classic-navigation');
488484

489485
#onMainEntryHovered: (event: Common.EventTarget.EventTargetEvent<number>) => void;
490486

@@ -1180,6 +1176,7 @@ export class TimelinePanel extends UI.Panel.Panel implements Client, TimelineMod
11801176
// need to update the radio buttons selection when the dialog is open.
11811177
this.#shortcutsDialog.addEventListener('click', this.#updateNavigationSettingSelection.bind(this));
11821178
this.#shortcutsDialog.data = {
1179+
customTitle: i18nString(UIStrings.shortcutsDialogTitle),
11831180
shortcuts: this.#getShortcutsInfo(currentNavSetting === 'classic'),
11841181
open: !userHadShortcutsDialogOpenedOnce && hideTheDialogForTests !== 'true' &&
11851182
!Host.InspectorFrontendHost.isUnderTest(),
@@ -1218,29 +1215,54 @@ export class TimelinePanel extends UI.Panel.Panel implements Client, TimelineMod
12181215
}
12191216

12201217
#getShortcutsInfo(isNavClassic: boolean): Dialogs.ShortcutDialog.Shortcut[] {
1218+
const metaKey = Host.Platform.isMac() ? '⌘' : 'Ctrl';
12211219
if (isNavClassic) {
1220+
// Classic navigation = scroll to zoom.
12221221
return [
1223-
{title: i18nString(UIStrings.timelineScrollUpDown), bindings: [['Shift', 'Scroll up/down'], ['Shift', '↑/↓']]},
12241222
{
1225-
title: i18nString(UIStrings.timelinePanLeftRight),
1226-
bindings: [['Shift', '←/→'], ['Scroll left/right'], ['A/D']]
1223+
title: i18nString(UIStrings.timelineZoom),
1224+
rows: [
1225+
[{key: 'Scroll ↕'}], [{key: 'W'}, {key: 'S'}, {joinText: 'or'}, {key: '+'}, {key: '-'}],
1226+
{footnote: 'hold shift for fast zoom'}
1227+
]
12271228
},
1228-
{title: i18nString(UIStrings.timelineZoomInOut), bindings: [['Scroll up/down'], ['W/S'], ['+/-']]},
1229-
{title: i18nString(UIStrings.timelineFastZoomInOut), bindings: [['Shift', 'W/S'], ['Shift', '+/-']]},
1229+
{
1230+
title: i18nString(UIStrings.timelineScrollPan),
1231+
rows: [
1232+
[{key: 'Shift'}, {joinText: '+'}, {key: 'Scroll ↕'}],
1233+
[{key: 'Scroll ↔'}, {joinText: 'or'}, {key: 'A'}, {key: 'D'}],
1234+
[
1235+
{key: 'Drag'}, {joinText: 'or'}, {key: 'Shift'}, {joinText: '+'}, {key: '↑'}, {key: '↓'}, {key: '←'},
1236+
{key: '→'}
1237+
],
1238+
]
1239+
}
12301240
];
12311241
}
12321242

1243+
// New navigation where scroll = scroll.
12331244
return [
1234-
{title: i18nString(UIStrings.timelineScrollUpDown), bindings: [['Scroll up/down'], ['Shift', '↑/↓']]},
12351245
{
1236-
title: i18nString(UIStrings.timelinePanLeftRight),
1237-
bindings: [['Shift', 'Scroll up/down'], ['Scroll left/right'], ['Shift', '←/→'], ['A/D']],
1246+
title: i18nString(UIStrings.timelineZoom),
1247+
rows: [
1248+
[{key: metaKey}, {joinText: '+'}, {key: 'Scroll ↕'}],
1249+
[{key: 'W'}, {key: 'S'}, {joinText: 'or'}, {key: '+'}, {key: '-'}], {footnote: ''}
1250+
]
12381251
},
12391252
{
1240-
title: i18nString(UIStrings.timelineZoomInOut),
1241-
bindings: [[Host.Platform.isMac() ? '⌘' : 'Ctrl', 'Scroll up/down'], ['W/S'], ['+/-']],
1242-
},
1243-
{title: i18nString(UIStrings.timelineFastZoomInOut), bindings: [['Shift', 'W/S'], ['Shift', '+/-']]},
1253+
title: i18nString(UIStrings.timelineScrollPan),
1254+
rows: [
1255+
[{key: 'Scroll ↕'}],
1256+
[
1257+
{key: 'Shift'}, {joinText: '+'}, {key: 'Scroll ↕'}, {joinText: 'or'}, {key: 'Scroll ↔'}, {joinText: 'or'},
1258+
{key: 'A'}, {key: 'D'}
1259+
],
1260+
[
1261+
{key: 'Drag'}, {joinText: 'or'}, {key: 'Shift'}, {joinText: '+'}, {key: '↑'}, {key: '↓'}, {key: '←'},
1262+
{key: '→'}
1263+
],
1264+
]
1265+
}
12441266
];
12451267
}
12461268

front_end/ui/components/dialogs/ShortcutDialog.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,16 @@ describeWithLocale('ShortcutDialog', () => {
1616
if (prependedElement) {
1717
shortcutDialog.prependElement(prependedElement);
1818
}
19-
shortcutDialog.data = {shortcuts: [{title: 'Shortcut Title', bindings: [['Ctrl+E']]}], open};
19+
shortcutDialog.data = {
20+
shortcuts: [{
21+
title: 'Shortcut Title',
22+
rows: [
23+
[{key: 'Cmd'}, {joinText: '+'}, {key: 'W'}],
24+
{footnote: 'close the window'},
25+
]
26+
}],
27+
open
28+
};
2029
Helpers.renderElementIntoDOM(shortcutDialog);
2130
await RenderCoordinator.done();
2231

front_end/ui/components/dialogs/ShortcutDialog.ts

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as i18n from '../../../core/i18n/i18n.js';
77
import type * as Platform from '../../../core/platform/platform.js';
88
import * as Buttons from '../../../ui/components/buttons/buttons.js';
99
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
10-
import {html, nothing, render} from '../../../ui/lit/lit.js';
10+
import {html, nothing, render, type TemplateResult} from '../../../ui/lit/lit.js';
1111

1212
import type {ButtonDialogData} from './ButtonDialog.js';
1313
import shortcutDialogStylesRaw from './shortcutDialog.css.js';
@@ -37,13 +37,20 @@ declare global {
3737
}
3838
}
3939

40+
export type ShortcutPart = {
41+
key: string,
42+
}|{joinText: string};
43+
44+
export type ShortcutRow = ShortcutPart[]|{footnote: string};
45+
4046
export interface Shortcut {
4147
title: string|Platform.UIString.LocalizedString;
42-
bindings: string[][];
48+
rows: readonly ShortcutRow[];
4349
}
4450
export interface ShortcutDialogData {
4551
shortcuts: Shortcut[];
4652
open?: boolean;
53+
customTitle?: Platform.UIString.LocalizedString;
4754
}
4855

4956
export class ShortcutDialog extends HTMLElement {
@@ -52,17 +59,29 @@ export class ShortcutDialog extends HTMLElement {
5259

5360
#shortcuts: Shortcut[] = [];
5461
#openOnRender = false;
62+
#customTitle?: Platform.UIString.LocalizedString;
5563
#prependedElement: HTMLElement|null = null;
5664

5765
connectedCallback(): void {
5866
this.#shadow.adoptedStyleSheets = [shortcutDialogStyles];
5967
}
6068

69+
get data(): ShortcutDialogData {
70+
return {
71+
shortcuts: this.#shortcuts,
72+
open: this.#openOnRender,
73+
customTitle: this.#customTitle,
74+
};
75+
}
76+
6177
set data(data: ShortcutDialogData) {
6278
this.#shortcuts = data.shortcuts;
6379
if (data.open) {
6480
this.#openOnRender = data.open;
6581
}
82+
if (data.customTitle) {
83+
this.#customTitle = data.customTitle;
84+
}
6685

6786
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#renderBound);
6887
}
@@ -71,6 +90,22 @@ export class ShortcutDialog extends HTMLElement {
7190
this.#prependedElement = element;
7291
}
7392

93+
#renderRow(row: ShortcutRow): TemplateResult {
94+
if (!Array.isArray(row)) {
95+
// If it's not an array it's a footnote, which is the easier case, so
96+
// render that and return.
97+
return html`<span class="footnote">${row.footnote}</span>`;
98+
}
99+
100+
return html`${row.map(part => {
101+
if ('key' in part) {
102+
return html`<span class="keybinds-key">${part.key}</span>`;
103+
}
104+
return html`<span class="keybinds-join-text">${part.joinText}</span>`;
105+
})}
106+
`;
107+
}
108+
74109
#render(): void {
75110
if (!ComponentHelpers.ScheduledRender.isScheduledRender(this)) {
76111
throw new Error('Shortcut dialog render was not scheduled');
@@ -82,7 +117,7 @@ export class ShortcutDialog extends HTMLElement {
82117
<devtools-button-dialog .data=${{
83118
openOnRender: this.#openOnRender,
84119
closeButton: true,
85-
dialogTitle: i18nString(UIStrings.dialogTitle),
120+
dialogTitle: this.#customTitle ?? i18nString(UIStrings.dialogTitle),
86121
variant: Buttons.Button.Variant.TOOLBAR,
87122
iconName: 'help',
88123
iconTitle: i18nString(UIStrings.showShortcutTitle),
@@ -94,16 +129,11 @@ export class ShortcutDialog extends HTMLElement {
94129
<li class="keybinds-list-item">
95130
<div class="keybinds-list-title">${shortcut.title}</div>
96131
<div class="shortcuts-for-actions">
97-
${shortcut.bindings.map(binding => {
98-
return html`
99-
<div class="keys-container">
100-
${binding.map(key => html`
101-
<span class="keybinds-key">${key}</span>
102-
`)}
103-
</div>
132+
${shortcut.rows.map(row => {
133+
return html`<div class="row-container">${this.#renderRow(row)}</div>
104134
`;
105-
})}
106-
</div>
135+
})}
136+
</div>
107137
</li>`,
108138
)}
109139
</ul>

front_end/ui/components/dialogs/shortcutDialog.css

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,45 @@
1313
}
1414

1515
.keybinds-list-item {
16-
display: grid;
16+
display: block;
17+
min-width: 315px;
1718
align-items: baseline;
18-
justify-content: space-between;
19-
grid-template-rows: 1fr;
20-
grid-template-columns: 1fr var(--sys-size-11) 1fr;
2119
border-bottom: var(--sys-size-1) solid var(--sys-color-divider);
2220
padding: var(--sys-size-4) 0;
21+
position: relative;
22+
2323

2424
&:last-of-type {
2525
border-bottom: unset;
2626
}
2727

2828
.keybinds-list-title {
29-
grid-row: 1/1;
30-
}
31-
32-
.shortcuts-for-actions {
33-
grid-area: auto / 3 / auto / span 1;
29+
/* Put the list title absolute so it is out the flow, this enables the
30+
* shortcuts to sit on the same row as the title */
31+
position: absolute;
32+
top: var(--sys-size-4);
33+
left: 0;
34+
/* Put the text vertically central so it aligns with the shortcuts shown */
35+
display: flex;
36+
align-items: center;
37+
height: var(--sys-size-11); /* IMPORTANT: this has to be the same height as .keybinds-key to maintain text alignment */
3438
}
3539
}
3640

37-
.keys-container {
41+
.row-container {
3842
display: flex;
39-
gap: var(--sys-size-3);
43+
gap: var(--sys-size-5);
44+
align-items: center;
45+
46+
.keybinds-join-text, .footnote {
47+
color: var(--color-text-primary);
48+
opacity: 50%;
49+
}
50+
51+
.footnote {
52+
display: block;
53+
height: 15px;
54+
}
4055
}
4156

4257
.shortcuts-for-actions {
@@ -57,6 +72,10 @@
5772
font: var(--sys-typescale-body3-regular);
5873
gap: var(--sys-size-2);
5974
}
75+
76+
input[type="radio"] {
77+
margin-left: 0;
78+
}
6079
}
6180

6281
.keybinds-key {
@@ -70,5 +89,5 @@
7089
font: var(--sys-typescale-body5-medium);
7190
white-space: nowrap;
7291
border-radius: var(--sys-shape-corner-small);
73-
background: var(--sys-color-tonal-container);
92+
background: var(--sys-color-base-container);
7493
}

front_end/ui/components/docs/dialog/shortcut_dialog.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ shortcutDialog.data = {
1414
shortcuts: [
1515
{
1616
title: 'First Shortcut Title',
17-
bindings: [['Ctrl+E']],
17+
rows: [[{key: 'Ctrl'}, {key: 'E'}]],
1818
},
1919
{
2020
title: 'Second Shortcut Title',
21-
bindings: [['Ctrl', 'Enter'], ['F8']],
21+
rows: [[{key: 'F8'}]],
2222
},
2323
],
2424
};
Loading

0 commit comments

Comments
 (0)