Skip to content

Commit c9555f6

Browse files
authored
Unified go to actions (microsoft#201166)
* Unified go to actions * Update for labeless and dynamic
1 parent fc9dcad commit c9555f6

File tree

4 files changed

+154
-21
lines changed

4 files changed

+154
-21
lines changed

src/vs/platform/actions/common/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ export class MenuId {
171171
static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');
172172
static readonly NotebookCellListTop = new MenuId('NotebookCellTop');
173173
static readonly NotebookCellExecute = new MenuId('NotebookCellExecute');
174+
static readonly NotebookCellExecuteGoTo = new MenuId('NotebookCellExecuteGoTo');
174175
static readonly NotebookCellExecutePrimary = new MenuId('NotebookCellExecutePrimary');
175176
static readonly NotebookDiffCellInputTitle = new MenuId('NotebookDiffCellInputTitle');
176177
static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle');

src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
1111
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
1212
import { ILanguageService } from 'vs/editor/common/languages/language';
1313
import { localize } from 'vs/nls';
14-
import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
14+
import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
1515
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1616
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
1717
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -622,13 +622,21 @@ registerAction2(class InterruptNotebook extends CancelNotebook {
622622
});
623623

624624

625+
MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
626+
title: localize('revealRunningCellShort', "Go To"),
627+
submenu: MenuId.NotebookCellExecuteGoTo,
628+
group: 'navigation/execute',
629+
order: 20,
630+
icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')
631+
});
632+
625633
registerAction2(class RevealRunningCellAction extends NotebookAction {
626634
constructor() {
627635
super({
628636
id: REVEAL_RUNNING_CELL,
629637
title: localize('revealRunningCell', "Go to Running Cell"),
630638
tooltip: localize('revealRunningCell', "Go to Running Cell"),
631-
shortTitle: localize('revealRunningCellShort', "Go To"),
639+
shortTitle: localize('revealRunningCell', "Go to Running Cell"),
632640
precondition: NOTEBOOK_HAS_RUNNING_CELL,
633641
menu: [
634642
{
@@ -642,7 +650,7 @@ registerAction2(class RevealRunningCellAction extends NotebookAction {
642650
order: 0
643651
},
644652
{
645-
id: MenuId.NotebookToolbar,
653+
id: MenuId.NotebookCellExecuteGoTo,
646654
when: ContextKeyExpr.and(
647655
NOTEBOOK_IS_ACTIVE_EDITOR,
648656
NOTEBOOK_HAS_RUNNING_CELL,
@@ -703,7 +711,7 @@ registerAction2(class RevealLastFailedCellAction extends NotebookAction {
703711
id: REVEAL_LAST_FAILED_CELL,
704712
title: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),
705713
tooltip: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),
706-
shortTitle: localize('revealLastFailedCellShort', "Go To"),
714+
shortTitle: localize('revealLastFailedCellShort', "Go to Most Recently Failed Cell"),
707715
precondition: NOTEBOOK_LAST_CELL_FAILED,
708716
menu: [
709717
{
@@ -718,7 +726,7 @@ registerAction2(class RevealLastFailedCellAction extends NotebookAction {
718726
order: 0
719727
},
720728
{
721-
id: MenuId.NotebookToolbar,
729+
id: MenuId.NotebookCellExecuteGoTo,
722730
when: ContextKeyExpr.and(
723731
NOTEBOOK_IS_ACTIVE_EDITOR,
724732
NOTEBOOK_LAST_CELL_FAILED,

src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55

66
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
77
import * as DOM from 'vs/base/browser/dom';
8-
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
8+
import { IMenuEntryActionViewItemOptions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
9+
import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
10+
import { IActionProvider } from 'vs/base/browser/ui/dropdown/dropdown';
11+
import { MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
12+
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
13+
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
14+
import { IThemeService } from 'vs/platform/theme/common/themeService';
15+
import { ThemeIcon } from 'vs/base/common/themables';
916

1017
export class CodiconActionViewItem extends MenuEntryActionViewItem {
1118

@@ -35,3 +42,61 @@ export class ActionViewWithLabel extends MenuEntryActionViewItem {
3542
}
3643
}
3744
}
45+
export class UnifiedSubmenuActionView extends SubmenuEntryActionViewItem {
46+
private _actionLabel?: HTMLAnchorElement;
47+
48+
constructor(
49+
action: SubmenuItemAction,
50+
options: IMenuEntryActionViewItemOptions | undefined,
51+
readonly renderLabel: boolean,
52+
readonly subActionProvider: IActionProvider,
53+
readonly subActionViewItemProvider: IActionViewItemProvider | undefined,
54+
@IKeybindingService _keybindingService: IKeybindingService,
55+
@IContextMenuService _contextMenuService: IContextMenuService,
56+
@IThemeService _themeService: IThemeService
57+
) {
58+
super(action, options, _keybindingService, _contextMenuService, _themeService);
59+
}
60+
61+
override render(container: HTMLElement): void {
62+
super.render(container);
63+
container.classList.add('notebook-action-view-item');
64+
this._actionLabel = document.createElement('a');
65+
container.appendChild(this._actionLabel);
66+
this.updateLabel();
67+
}
68+
69+
protected override updateLabel() {
70+
const actions = this.subActionProvider.getActions();
71+
if (this._actionLabel) {
72+
const primaryAction = actions[0];
73+
74+
if (primaryAction && primaryAction instanceof MenuItemAction) {
75+
const element = this.element;
76+
77+
if (element && primaryAction.item.icon && ThemeIcon.isThemeIcon(primaryAction.item.icon)) {
78+
const iconClasses = ThemeIcon.asClassNameArray(primaryAction.item.icon);
79+
// remove all classes started with 'codicon-'
80+
element.classList.forEach((cl) => {
81+
if (cl.startsWith('codicon-')) {
82+
element.classList.remove(cl);
83+
}
84+
});
85+
element.classList.add(...iconClasses);
86+
}
87+
88+
if (this.renderLabel) {
89+
this._actionLabel.classList.add('notebook-label');
90+
this._actionLabel.innerText = this._action.label;
91+
this._actionLabel.title = primaryAction.tooltip.length ? primaryAction.tooltip : primaryAction.label;
92+
}
93+
} else {
94+
if (this.renderLabel) {
95+
this._actionLabel.classList.add('notebook-label');
96+
this._actionLabel.innerText = this._action.label;
97+
this._actionLabel.title = this._action.tooltip.length ? this._action.tooltip : this._action.label;
98+
}
99+
}
100+
}
101+
}
102+
}

src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { IAction, Separator } from 'vs/base/common/actions';
1010
import { Emitter, Event } from 'vs/base/common/event';
1111
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
1212
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
13-
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
13+
import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
1414
import { IMenu, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
1515
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1616
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -21,13 +21,12 @@ import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controll
2121
import { NOTEBOOK_EDITOR_ID, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon';
2222
import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
2323
import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView';
24-
import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView';
24+
import { ActionViewWithLabel, UnifiedSubmenuActionView } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView';
2525
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
2626
import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService';
2727
import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions';
28-
import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
28+
import { IActionViewItem, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
2929
import { disposableTimeout } from 'vs/base/common/async';
30-
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
3130
import { HiddenItemStrategy, IWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
3231

3332
interface IActionModel {
@@ -73,15 +72,28 @@ class WorkbenchAlwaysLabelStrategy implements IActionLayoutStrategy {
7372
constructor(
7473
readonly notebookEditor: INotebookEditorDelegate,
7574
readonly editorToolbar: NotebookEditorWorkbenchToolbar,
75+
readonly goToMenu: IMenu,
7676
readonly instantiationService: IInstantiationService) { }
7777

78-
actionProvider(action: IAction): ActionViewItem | undefined {
78+
actionProvider(action: IAction): IActionViewItem | undefined {
7979
if (action.id === SELECT_KERNEL_ID) {
8080
// this is being disposed by the consumer
8181
return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor);
8282
}
8383

84-
return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action, undefined) : undefined;
84+
if (action instanceof MenuItemAction) {
85+
return this.instantiationService.createInstance(ActionViewWithLabel, action, undefined);
86+
}
87+
88+
if (action instanceof SubmenuItemAction && action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
89+
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, true, {
90+
getActions: () => {
91+
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
92+
}
93+
}, this.actionProvider.bind(this));
94+
}
95+
96+
return undefined;
8597
}
8698

8799
calculateActions(leftToolbarContainerMaxWidth: number): { primaryActions: IAction[]; secondaryActions: IAction[] } {
@@ -100,15 +112,32 @@ class WorkbenchNeverLabelStrategy implements IActionLayoutStrategy {
100112
constructor(
101113
readonly notebookEditor: INotebookEditorDelegate,
102114
readonly editorToolbar: NotebookEditorWorkbenchToolbar,
115+
readonly goToMenu: IMenu,
103116
readonly instantiationService: IInstantiationService) { }
104117

105-
actionProvider(action: IAction): ActionViewItem | undefined {
118+
actionProvider(action: IAction): IActionViewItem | undefined {
106119
if (action.id === SELECT_KERNEL_ID) {
107120
// this is being disposed by the consumer
108121
return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor);
109122
}
110123

111-
return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined;
124+
if (action instanceof MenuItemAction) {
125+
return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined);
126+
}
127+
128+
if (action instanceof SubmenuItemAction) {
129+
if (action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
130+
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, false, {
131+
getActions: () => {
132+
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
133+
}
134+
}, this.actionProvider.bind(this));
135+
} else {
136+
return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined);
137+
}
138+
}
139+
140+
return undefined;
112141
}
113142

114143
calculateActions(leftToolbarContainerMaxWidth: number): { primaryActions: IAction[]; secondaryActions: IAction[] } {
@@ -127,19 +156,48 @@ class WorkbenchDynamicLabelStrategy implements IActionLayoutStrategy {
127156
constructor(
128157
readonly notebookEditor: INotebookEditorDelegate,
129158
readonly editorToolbar: NotebookEditorWorkbenchToolbar,
159+
readonly goToMenu: IMenu,
130160
readonly instantiationService: IInstantiationService) { }
131161

132-
actionProvider(action: IAction): ActionViewItem | undefined {
162+
actionProvider(action: IAction): IActionViewItem | undefined {
133163
if (action.id === SELECT_KERNEL_ID) {
134164
// this is being disposed by the consumer
135165
return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor);
136166
}
137167

138168
const a = this.editorToolbar.primaryActions.find(a => a.action.id === action.id);
139169
if (!a || a.renderLabel) {
140-
return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action, undefined) : undefined;
170+
if (action instanceof MenuItemAction) {
171+
return this.instantiationService.createInstance(ActionViewWithLabel, action, undefined);
172+
}
173+
174+
if (action instanceof SubmenuItemAction && action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
175+
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, true, {
176+
getActions: () => {
177+
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
178+
}
179+
}, this.actionProvider.bind(this));
180+
}
181+
182+
return undefined;
141183
} else {
142-
return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined;
184+
if (action instanceof MenuItemAction) {
185+
this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined);
186+
}
187+
188+
if (action instanceof SubmenuItemAction) {
189+
if (action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
190+
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, false, {
191+
getActions: () => {
192+
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
193+
}
194+
}, this.actionProvider.bind(this));
195+
} else {
196+
return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined);
197+
}
198+
}
199+
200+
return undefined;
143201
}
144202
}
145203

@@ -160,6 +218,7 @@ export class NotebookEditorWorkbenchToolbar extends Disposable {
160218
private _notebookTopLeftToolbarContainer!: HTMLElement;
161219
private _notebookTopRightToolbarContainer!: HTMLElement;
162220
private _notebookGlobalActionsMenu!: IMenu;
221+
private _executeGoToActionsMenu!: IMenu;
163222
private _notebookLeftToolbar!: WorkbenchToolBar;
164223
private _primaryActions: IActionModel[];
165224
get primaryActions(): IActionModel[] {
@@ -251,7 +310,7 @@ export class NotebookEditorWorkbenchToolbar extends Disposable {
251310

252311
private _registerNotebookActionsToolbar() {
253312
this._notebookGlobalActionsMenu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.notebookToolbar, this.contextKeyService));
254-
this._register(this._notebookGlobalActionsMenu);
313+
this._executeGoToActionsMenu = this._register(this.menuService.createMenu(MenuId.NotebookCellExecuteGoTo, this.contextKeyService));
255314

256315
this._useGlobalToolbar = this.notebookOptions.getDisplayOptions().globalToolbar;
257316
this._renderLabel = this._convertConfiguration(this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel));
@@ -379,13 +438,13 @@ export class NotebookEditorWorkbenchToolbar extends Disposable {
379438
private _updateStrategy() {
380439
switch (this._renderLabel) {
381440
case RenderLabel.Always:
382-
this._strategy = new WorkbenchAlwaysLabelStrategy(this.notebookEditor, this, this.instantiationService);
441+
this._strategy = new WorkbenchAlwaysLabelStrategy(this.notebookEditor, this, this._executeGoToActionsMenu, this.instantiationService);
383442
break;
384443
case RenderLabel.Never:
385-
this._strategy = new WorkbenchNeverLabelStrategy(this.notebookEditor, this, this.instantiationService);
444+
this._strategy = new WorkbenchNeverLabelStrategy(this.notebookEditor, this, this._executeGoToActionsMenu, this.instantiationService);
386445
break;
387446
case RenderLabel.Dynamic:
388-
this._strategy = new WorkbenchDynamicLabelStrategy(this.notebookEditor, this, this.instantiationService);
447+
this._strategy = new WorkbenchDynamicLabelStrategy(this.notebookEditor, this, this._executeGoToActionsMenu, this.instantiationService);
389448
break;
390449
}
391450
}

0 commit comments

Comments
 (0)