From 8695fecbb1a43842d9565a28ca2955cd7f6e59e9 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Wed, 19 Feb 2025 14:13:47 +0000 Subject: [PATCH 1/5] First attempt at implementing Show/Hide for 'More Actions...' button --- src/vs/base/browser/ui/toolbar/toolbar.ts | 3 +- src/vs/platform/actions/browser/toolbar.ts | 35 +++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 75ab73a535419..2c14347938bbd 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -60,6 +60,7 @@ export class ToolBar extends Disposable { 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..0089763e6a507 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -88,6 +88,10 @@ 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; + constructor( container: HTMLElement, private _options: IWorkbenchToolBarOptions | undefined, @@ -119,7 +123,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(); @@ -255,14 +261,41 @@ export class WorkbenchToolBar extends ToolBar { if (this._options?.resetMenu && !menuIds) { menuIds = [this._options.resetMenu]; } + let separatorAdded = false; if (someAreHidden && menuIds) { actions.push(new Separator()); + separatorAdded = true; actions.push(toAction({ id: 'resetThisMenu', label: localize('resetThisMenu', "Reset Menu"), run: () => this._menuService.resetHiddenStates(menuIds) })); } + if (secondary.length > 0 || extraSecondary.length > 0) { + if (!separatorAdded) { + actions.push(new Separator()); + separatorAdded = true; + } + if (this.isToggleMenuHidden) { + actions.push(toAction({ + id: 'showToggleMenu', + label: localize('showToggleMenu', "Show 'More Actions...'"), + run: () => { + this.isToggleMenuHidden = false; + this.setActions(this._primaryActions, this._secondaryActions, this._menuIds); + } + })); + } else { + actions.push(toAction({ + id: 'hideToggleMenu', + label: localize('hideToggleMenu', "Hide 'More Actions...'"), + run: () => { + this.isToggleMenuHidden = true; + this.setActions(this._primaryActions, this._secondaryActions, this._menuIds); + } + })); + } + } if (actions.length === 0) { return; From 94bceb58540973806fb2694fb7762af1919fd5ab Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Wed, 19 Feb 2025 23:29:57 +0000 Subject: [PATCH 2/5] Improve presentation of Show/Hide More Actions option and persist its state --- src/vs/base/browser/ui/toolbar/toolbar.ts | 2 +- src/vs/platform/actions/browser/toolbar.ts | 30 +++++++++++++------ src/vs/platform/actions/common/actions.ts | 10 +++++++ src/vs/platform/actions/common/menuService.ts | 8 +++++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 2c14347938bbd..0b2fd0d68fdc2 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -56,7 +56,7 @@ 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; diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index 0089763e6a507..1a55f087ea9f7 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -91,6 +91,7 @@ export class WorkbenchToolBar extends ToolBar { private _primaryActions: ReadonlyArray = []; private _secondaryActions: ReadonlyArray | undefined; private _menuIds: readonly MenuId[] | undefined; + private readonly _resetMenu: MenuId | undefined; constructor( container: HTMLElement, @@ -112,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) { @@ -245,6 +251,18 @@ export class WorkbenchToolBar extends ToolBar { } primaryActions.push(action.hideActions.hide); + } else if (action instanceof ToggleMenuAction) { + primaryActions.push(toAction({ + id: 'label', + label: localize('hide.moreActions', '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', @@ -282,15 +300,9 @@ export class WorkbenchToolBar extends ToolBar { label: localize('showToggleMenu', "Show 'More Actions...'"), run: () => { this.isToggleMenuHidden = false; - this.setActions(this._primaryActions, this._secondaryActions, this._menuIds); - } - })); - } else { - actions.push(toAction({ - id: 'hideToggleMenu', - label: localize('hideToggleMenu', "Hide 'More Actions...'"), - run: () => { - this.isToggleMenuHidden = true; + if (this._resetMenu) { + this._menuService.setHiddenState(this._resetMenu, this.toggleMenuAction.id, false); + } this.setActions(this._primaryActions, this._secondaryActions, this._menuIds); } })); diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index ca9d4bebeb743..805776b10b021 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -335,6 +335,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 { From 9a292cbc9e0d9870b3a7a3d8f636e2a196878692 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Thu, 20 Feb 2025 00:20:17 +0000 Subject: [PATCH 3/5] Improvements --- src/vs/platform/actions/browser/toolbar.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index 1a55f087ea9f7..ba19288c95166 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -251,10 +251,11 @@ export class WorkbenchToolBar extends ToolBar { } primaryActions.push(action.hideActions.hide); - } else if (action instanceof ToggleMenuAction) { + } 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('hide.moreActions', 'Hide \'{0}\'', action.label), + label: localize('hideToggleMenu', 'Hide \'{0}\'', action.label), run: () => { this.isToggleMenuHidden = true; if (this._resetMenu) { @@ -297,7 +298,7 @@ export class WorkbenchToolBar extends ToolBar { if (this.isToggleMenuHidden) { actions.push(toAction({ id: 'showToggleMenu', - label: localize('showToggleMenu', "Show 'More Actions...'"), + label: localize('showToggleMenu', 'Show \'{0}\'', this.toggleMenuAction.label), run: () => { this.isToggleMenuHidden = false; if (this._resetMenu) { From 4dd30b1d115e0e45dfe6641b15005553f8039bbf Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Thu, 20 Feb 2025 10:33:15 +0000 Subject: [PATCH 4/5] Fix CI --- src/vs/workbench/test/browser/workbenchTestServices.ts | 7 +++++++ 1 file changed, 7 insertions(+) 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 { From bdd91ca0e89d040623188b861c3eaf128dbdbafb Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Thu, 20 Feb 2025 12:04:02 +0000 Subject: [PATCH 5/5] Add `More Actions...` submenu on context menu of all buttons if '...' button hidden --- src/vs/platform/actions/browser/toolbar.ts | 42 +++++++++++++--------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index ba19288c95166..68f21a4eff2e9 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -212,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); @@ -276,26 +282,12 @@ export class WorkbenchToolBar extends ToolBar { const actions = Separator.join(primaryActions, toggleActions); - // add "Reset Menu" action - if (this._options?.resetMenu && !menuIds) { - menuIds = [this._options.resetMenu]; - } + // Add option to show the More Actions button if it is hidden let separatorAdded = false; - if (someAreHidden && menuIds) { - actions.push(new Separator()); - separatorAdded = true; - actions.push(toAction({ - id: 'resetThisMenu', - label: localize('resetThisMenu', "Reset Menu"), - run: () => this._menuService.resetHiddenStates(menuIds) - })); - } - if (secondary.length > 0 || extraSecondary.length > 0) { - if (!separatorAdded) { + if (secondary.length + extraSecondary.length > 0) { + if (this.isToggleMenuHidden) { actions.push(new Separator()); separatorAdded = true; - } - if (this.isToggleMenuHidden) { actions.push(toAction({ id: 'showToggleMenu', label: localize('showToggleMenu', 'Show \'{0}\'', this.toggleMenuAction.label), @@ -310,6 +302,22 @@ export class WorkbenchToolBar extends ToolBar { } } + // add "Reset Menu" action + if (this._options?.resetMenu && !menuIds) { + menuIds = [this._options.resetMenu]; + } + if (someAreHidden && menuIds) { + if (!separatorAdded) { + actions.push(new Separator()); + separatorAdded = true; + } + actions.push(toAction({ + id: 'resetThisMenu', + label: localize('resetThisMenu', "Reset Menu"), + run: () => this._menuService.resetHiddenStates(menuIds) + })); + } + if (actions.length === 0) { return; }