diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 75ab73a535419..0b2fd0d68fdc2 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -56,10 +56,11 @@ export interface IToolBarOptions { export class ToolBar extends Disposable { private options: IToolBarOptions; protected readonly actionBar: ActionBar; - private toggleMenuAction: ToggleMenuAction; + protected readonly toggleMenuAction: ToggleMenuAction; private toggleMenuActionViewItem: DropdownMenuActionViewItem | undefined; private submenuActionViewItems: DropdownMenuActionViewItem[] = []; private hasSecondaryActions: boolean = false; + protected isToggleMenuHidden: boolean = false; private readonly element: HTMLElement; private _onDidChangeDropdownVisibility = this._register(new EventMultiplexer()); @@ -200,7 +201,7 @@ export class ToolBar extends Disposable { // Inject additional action to open secondary actions if present this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0); - if (this.hasSecondaryActions && secondaryActions) { + if (this.hasSecondaryActions && secondaryActions && !this.isToggleMenuHidden) { this.toggleMenuAction.menuActions = secondaryActions.slice(0); primaryActionsToSet.push(this.toggleMenuAction); } diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index 5e46b390271de..68f21a4eff2e9 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -88,6 +88,11 @@ export class WorkbenchToolBar extends ToolBar { private readonly _sessionDisposables = this._store.add(new DisposableStore()); + private _primaryActions: ReadonlyArray = []; + private _secondaryActions: ReadonlyArray | undefined; + private _menuIds: readonly MenuId[] | undefined; + private readonly _resetMenu: MenuId | undefined; + constructor( container: HTMLElement, private _options: IWorkbenchToolBarOptions | undefined, @@ -108,6 +113,11 @@ export class WorkbenchToolBar extends ToolBar { skipTelemetry: typeof _options?.telemetrySource === 'string', }); + if (_options?.resetMenu) { + this._resetMenu = _options.resetMenu; + this.isToggleMenuHidden = this._menuService.getHiddenState(this._resetMenu, this.toggleMenuAction.id); + } + // telemetry logic const telemetrySource = _options?.telemetrySource; if (telemetrySource) { @@ -119,7 +129,9 @@ export class WorkbenchToolBar extends ToolBar { } override setActions(_primary: readonly IAction[], _secondary: readonly IAction[] = [], menuIds?: readonly MenuId[]): void { - + this._primaryActions = _primary; + this._secondaryActions = _secondary; + this._menuIds = menuIds; this._sessionDisposables.clear(); const primary: Array = _primary.slice(); // for hiding and overflow we set some items to undefined const secondary = _secondary.slice(); @@ -200,6 +212,12 @@ export class WorkbenchToolBar extends ToolBar { const primaryActions = []; + // More Actions... submenu if dedicated button has been hidden + if (this.isToggleMenuHidden && (secondary.length + extraSecondary.length) > 0) { + primaryActions.push(new SubmenuAction(ToggleMenuAction.ID, this.toggleMenuAction.label, Separator.join(extraSecondary, secondary))); + primaryActions.push(new Separator()); + } + // -- Configure Keybinding Action -- if (action instanceof MenuItemAction && action.menuKeybinding) { primaryActions.push(action.menuKeybinding); @@ -239,6 +257,19 @@ export class WorkbenchToolBar extends ToolBar { } primaryActions.push(action.hideActions.hide); + } else if (action instanceof ToggleMenuAction && primary.length > 0 && this._resetMenu) { + // Only offered if caller provided a reset menu (to persist state on) and at least one primary button remains (from which to access Show 'More Actions...') + primaryActions.push(toAction({ + id: 'label', + label: localize('hideToggleMenu', 'Hide \'{0}\'', action.label), + run: () => { + this.isToggleMenuHidden = true; + if (this._resetMenu) { + this._menuService.setHiddenState(this._resetMenu, this.toggleMenuAction.id, true); + } + this.setActions(this._primaryActions, this._secondaryActions, this._menuIds); + } + })); } else { primaryActions.push(toAction({ id: 'label', @@ -251,12 +282,35 @@ export class WorkbenchToolBar extends ToolBar { const actions = Separator.join(primaryActions, toggleActions); + // Add option to show the More Actions button if it is hidden + let separatorAdded = false; + if (secondary.length + extraSecondary.length > 0) { + if (this.isToggleMenuHidden) { + actions.push(new Separator()); + separatorAdded = true; + actions.push(toAction({ + id: 'showToggleMenu', + label: localize('showToggleMenu', 'Show \'{0}\'', this.toggleMenuAction.label), + run: () => { + this.isToggleMenuHidden = false; + if (this._resetMenu) { + this._menuService.setHiddenState(this._resetMenu, this.toggleMenuAction.id, false); + } + this.setActions(this._primaryActions, this._secondaryActions, this._menuIds); + } + })); + } + } + // add "Reset Menu" action if (this._options?.resetMenu && !menuIds) { menuIds = [this._options.resetMenu]; } if (someAreHidden && menuIds) { - actions.push(new Separator()); + if (!separatorAdded) { + actions.push(new Separator()); + separatorAdded = true; + } actions.push(toAction({ id: 'resetThisMenu', label: localize('resetThisMenu', "Reset Menu"), diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 676182697e79f..3d076bd6c5db9 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -334,6 +334,16 @@ export interface IMenuService { * Reset the menu's hidden states. */ resetHiddenStates(menuIds: readonly MenuId[] | undefined): void; + + /** + * Get the hidden state of a menu item. + */ + getHiddenState(id: MenuId, commandId: string): boolean; + + /** + * Set the hidden state of a menu item. + */ + setHiddenState(id: MenuId, commandId: string, value: boolean): void; } type ICommandsMap = Map; diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index f5b7046bbb967..db93b00021b14 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -49,6 +49,14 @@ export class MenuService implements IMenuService { resetHiddenStates(ids?: MenuId[]): void { this._hiddenStates.reset(ids); } + + getHiddenState(id: MenuId, commandId: string): boolean { + return this._hiddenStates.isHidden(id, commandId); + } + + setHiddenState(id: MenuId, commandId: string, value: boolean): void { + this._hiddenStates.updateHidden(id, commandId, value); + } } class PersistedMenuHideState { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 6161e10f542fc..303a43516e525 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -574,6 +574,13 @@ export class TestMenuService implements IMenuService { resetHiddenStates(): void { // nothing } + getHiddenState(id: MenuId, commandId: string): boolean { + return false; + } + + setHiddenState(id: MenuId, commandId: string, value: boolean): void { + // nothing + } } export class TestFileDialogService implements IFileDialogService {