Unified go to actions (#201166)

* Unified go to actions

* Update for labeless and dynamic
This commit is contained in:
Peng Lyu
2024-01-29 11:10:06 -08:00
committed by GitHub
parent fc9dcad098
commit c9555f641b
4 changed files with 154 additions and 21 deletions
@@ -171,6 +171,7 @@ export class MenuId {
static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');
static readonly NotebookCellListTop = new MenuId('NotebookCellTop');
static readonly NotebookCellExecute = new MenuId('NotebookCellExecute');
static readonly NotebookCellExecuteGoTo = new MenuId('NotebookCellExecuteGoTo');
static readonly NotebookCellExecutePrimary = new MenuId('NotebookCellExecutePrimary');
static readonly NotebookDiffCellInputTitle = new MenuId('NotebookDiffCellInputTitle');
static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle');
@@ -11,7 +11,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { localize } from 'vs/nls';
import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -622,13 +622,21 @@ registerAction2(class InterruptNotebook extends CancelNotebook {
});
MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
title: localize('revealRunningCellShort', "Go To"),
submenu: MenuId.NotebookCellExecuteGoTo,
group: 'navigation/execute',
order: 20,
icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')
});
registerAction2(class RevealRunningCellAction extends NotebookAction {
constructor() {
super({
id: REVEAL_RUNNING_CELL,
title: localize('revealRunningCell', "Go to Running Cell"),
tooltip: localize('revealRunningCell', "Go to Running Cell"),
shortTitle: localize('revealRunningCellShort', "Go To"),
shortTitle: localize('revealRunningCell', "Go to Running Cell"),
precondition: NOTEBOOK_HAS_RUNNING_CELL,
menu: [
{
@@ -642,7 +650,7 @@ registerAction2(class RevealRunningCellAction extends NotebookAction {
order: 0
},
{
id: MenuId.NotebookToolbar,
id: MenuId.NotebookCellExecuteGoTo,
when: ContextKeyExpr.and(
NOTEBOOK_IS_ACTIVE_EDITOR,
NOTEBOOK_HAS_RUNNING_CELL,
@@ -703,7 +711,7 @@ registerAction2(class RevealLastFailedCellAction extends NotebookAction {
id: REVEAL_LAST_FAILED_CELL,
title: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),
tooltip: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),
shortTitle: localize('revealLastFailedCellShort', "Go To"),
shortTitle: localize('revealLastFailedCellShort', "Go to Most Recently Failed Cell"),
precondition: NOTEBOOK_LAST_CELL_FAILED,
menu: [
{
@@ -718,7 +726,7 @@ registerAction2(class RevealLastFailedCellAction extends NotebookAction {
order: 0
},
{
id: MenuId.NotebookToolbar,
id: MenuId.NotebookCellExecuteGoTo,
when: ContextKeyExpr.and(
NOTEBOOK_IS_ACTIVE_EDITOR,
NOTEBOOK_LAST_CELL_FAILED,
@@ -5,7 +5,14 @@
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import * as DOM from 'vs/base/browser/dom';
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenuEntryActionViewItemOptions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IActionProvider } from 'vs/base/browser/ui/dropdown/dropdown';
import { MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ThemeIcon } from 'vs/base/common/themables';
export class CodiconActionViewItem extends MenuEntryActionViewItem {
@@ -35,3 +42,61 @@ export class ActionViewWithLabel extends MenuEntryActionViewItem {
}
}
}
export class UnifiedSubmenuActionView extends SubmenuEntryActionViewItem {
private _actionLabel?: HTMLAnchorElement;
constructor(
action: SubmenuItemAction,
options: IMenuEntryActionViewItemOptions | undefined,
readonly renderLabel: boolean,
readonly subActionProvider: IActionProvider,
readonly subActionViewItemProvider: IActionViewItemProvider | undefined,
@IKeybindingService _keybindingService: IKeybindingService,
@IContextMenuService _contextMenuService: IContextMenuService,
@IThemeService _themeService: IThemeService
) {
super(action, options, _keybindingService, _contextMenuService, _themeService);
}
override render(container: HTMLElement): void {
super.render(container);
container.classList.add('notebook-action-view-item');
this._actionLabel = document.createElement('a');
container.appendChild(this._actionLabel);
this.updateLabel();
}
protected override updateLabel() {
const actions = this.subActionProvider.getActions();
if (this._actionLabel) {
const primaryAction = actions[0];
if (primaryAction && primaryAction instanceof MenuItemAction) {
const element = this.element;
if (element && primaryAction.item.icon && ThemeIcon.isThemeIcon(primaryAction.item.icon)) {
const iconClasses = ThemeIcon.asClassNameArray(primaryAction.item.icon);
// remove all classes started with 'codicon-'
element.classList.forEach((cl) => {
if (cl.startsWith('codicon-')) {
element.classList.remove(cl);
}
});
element.classList.add(...iconClasses);
}
if (this.renderLabel) {
this._actionLabel.classList.add('notebook-label');
this._actionLabel.innerText = this._action.label;
this._actionLabel.title = primaryAction.tooltip.length ? primaryAction.tooltip : primaryAction.label;
}
} else {
if (this.renderLabel) {
this._actionLabel.classList.add('notebook-label');
this._actionLabel.innerText = this._action.label;
this._actionLabel.title = this._action.tooltip.length ? this._action.tooltip : this._action.label;
}
}
}
}
}
@@ -10,7 +10,7 @@ import { IAction, Separator } from 'vs/base/common/actions';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenu, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -21,13 +21,12 @@ import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controll
import { NOTEBOOK_EDITOR_ID, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView';
import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView';
import { ActionViewWithLabel, UnifiedSubmenuActionView } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions';
import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IActionViewItem, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { disposableTimeout } from 'vs/base/common/async';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { HiddenItemStrategy, IWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
interface IActionModel {
@@ -73,15 +72,28 @@ class WorkbenchAlwaysLabelStrategy implements IActionLayoutStrategy {
constructor(
readonly notebookEditor: INotebookEditorDelegate,
readonly editorToolbar: NotebookEditorWorkbenchToolbar,
readonly goToMenu: IMenu,
readonly instantiationService: IInstantiationService) { }
actionProvider(action: IAction): ActionViewItem | undefined {
actionProvider(action: IAction): IActionViewItem | undefined {
if (action.id === SELECT_KERNEL_ID) {
// this is being disposed by the consumer
return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor);
}
return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action, undefined) : undefined;
if (action instanceof MenuItemAction) {
return this.instantiationService.createInstance(ActionViewWithLabel, action, undefined);
}
if (action instanceof SubmenuItemAction && action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, true, {
getActions: () => {
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
}
}, this.actionProvider.bind(this));
}
return undefined;
}
calculateActions(leftToolbarContainerMaxWidth: number): { primaryActions: IAction[]; secondaryActions: IAction[] } {
@@ -100,15 +112,32 @@ class WorkbenchNeverLabelStrategy implements IActionLayoutStrategy {
constructor(
readonly notebookEditor: INotebookEditorDelegate,
readonly editorToolbar: NotebookEditorWorkbenchToolbar,
readonly goToMenu: IMenu,
readonly instantiationService: IInstantiationService) { }
actionProvider(action: IAction): ActionViewItem | undefined {
actionProvider(action: IAction): IActionViewItem | undefined {
if (action.id === SELECT_KERNEL_ID) {
// this is being disposed by the consumer
return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor);
}
return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined;
if (action instanceof MenuItemAction) {
return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined);
}
if (action instanceof SubmenuItemAction) {
if (action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, false, {
getActions: () => {
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
}
}, this.actionProvider.bind(this));
} else {
return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined);
}
}
return undefined;
}
calculateActions(leftToolbarContainerMaxWidth: number): { primaryActions: IAction[]; secondaryActions: IAction[] } {
@@ -127,9 +156,10 @@ class WorkbenchDynamicLabelStrategy implements IActionLayoutStrategy {
constructor(
readonly notebookEditor: INotebookEditorDelegate,
readonly editorToolbar: NotebookEditorWorkbenchToolbar,
readonly goToMenu: IMenu,
readonly instantiationService: IInstantiationService) { }
actionProvider(action: IAction): ActionViewItem | undefined {
actionProvider(action: IAction): IActionViewItem | undefined {
if (action.id === SELECT_KERNEL_ID) {
// this is being disposed by the consumer
return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor);
@@ -137,9 +167,37 @@ class WorkbenchDynamicLabelStrategy implements IActionLayoutStrategy {
const a = this.editorToolbar.primaryActions.find(a => a.action.id === action.id);
if (!a || a.renderLabel) {
return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action, undefined) : undefined;
if (action instanceof MenuItemAction) {
return this.instantiationService.createInstance(ActionViewWithLabel, action, undefined);
}
if (action instanceof SubmenuItemAction && action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, true, {
getActions: () => {
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
}
}, this.actionProvider.bind(this));
}
return undefined;
} else {
return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined;
if (action instanceof MenuItemAction) {
this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined);
}
if (action instanceof SubmenuItemAction) {
if (action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) {
return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, false, {
getActions: () => {
return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? [];
}
}, this.actionProvider.bind(this));
} else {
return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined);
}
}
return undefined;
}
}
@@ -160,6 +218,7 @@ export class NotebookEditorWorkbenchToolbar extends Disposable {
private _notebookTopLeftToolbarContainer!: HTMLElement;
private _notebookTopRightToolbarContainer!: HTMLElement;
private _notebookGlobalActionsMenu!: IMenu;
private _executeGoToActionsMenu!: IMenu;
private _notebookLeftToolbar!: WorkbenchToolBar;
private _primaryActions: IActionModel[];
get primaryActions(): IActionModel[] {
@@ -251,7 +310,7 @@ export class NotebookEditorWorkbenchToolbar extends Disposable {
private _registerNotebookActionsToolbar() {
this._notebookGlobalActionsMenu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.notebookToolbar, this.contextKeyService));
this._register(this._notebookGlobalActionsMenu);
this._executeGoToActionsMenu = this._register(this.menuService.createMenu(MenuId.NotebookCellExecuteGoTo, this.contextKeyService));
this._useGlobalToolbar = this.notebookOptions.getDisplayOptions().globalToolbar;
this._renderLabel = this._convertConfiguration(this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel));
@@ -379,13 +438,13 @@ export class NotebookEditorWorkbenchToolbar extends Disposable {
private _updateStrategy() {
switch (this._renderLabel) {
case RenderLabel.Always:
this._strategy = new WorkbenchAlwaysLabelStrategy(this.notebookEditor, this, this.instantiationService);
this._strategy = new WorkbenchAlwaysLabelStrategy(this.notebookEditor, this, this._executeGoToActionsMenu, this.instantiationService);
break;
case RenderLabel.Never:
this._strategy = new WorkbenchNeverLabelStrategy(this.notebookEditor, this, this.instantiationService);
this._strategy = new WorkbenchNeverLabelStrategy(this.notebookEditor, this, this._executeGoToActionsMenu, this.instantiationService);
break;
case RenderLabel.Dynamic:
this._strategy = new WorkbenchDynamicLabelStrategy(this.notebookEditor, this, this.instantiationService);
this._strategy = new WorkbenchDynamicLabelStrategy(this.notebookEditor, this, this._executeGoToActionsMenu, this.instantiationService);
break;
}
}