diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 1c8796a581b..d43218b0dea 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -74,6 +74,11 @@ export class MenuId { static readonly EditorContextCopy = new MenuId('EditorContextCopy'); static readonly EditorContextPeek = new MenuId('EditorContextPeek'); static readonly EditorContextShare = new MenuId('EditorContextShare'); + // --- Start Positron --- + static readonly EditorActionsLeft = new MenuId('EditorActionsLeft'); + static readonly EditorActionsCenter = new MenuId('EditorActionsCenter'); + static readonly EditorActionsRight = new MenuId('EditorActionsRight'); + // --- End Positron --- static readonly EditorTitle = new MenuId('EditorTitle'); static readonly EditorTitleRun = new MenuId('EditorTitleRun'); static readonly EditorTitleContext = new MenuId('EditorTitleContext'); diff --git a/src/vs/workbench/browser/parts/editor/editorActionBarFactory.tsx b/src/vs/workbench/browser/parts/editor/editorActionBarFactory.tsx index b6dee968b89..5da3fca01b5 100644 --- a/src/vs/workbench/browser/parts/editor/editorActionBarFactory.tsx +++ b/src/vs/workbench/browser/parts/editor/editorActionBarFactory.tsx @@ -68,14 +68,14 @@ export class EditorActionBarFactory extends Disposable { //#region Private Properties /** - * Gets the menu disposable store. + * Gets the menu disposable stores. */ - private readonly _menuDisposableStore = this._register(new DisposableStore()); + private readonly _menuDisposableStores = new Map(); /** - * Gets or sets the editor title menu. + * Gets the menus. */ - private _editorTitleMenu: IMenu; + private readonly _menus = new Map(); /** * Gets the onDidActionsChange event emitter. @@ -93,6 +93,20 @@ export class EditorActionBarFactory extends Disposable { //#endregion Public Events + //#region Private Properties + + /** + * Gets the context key service. + */ + private get contextKeyService() { + // If there is an active editor pane, use its scoped context key service, if possible. + // Otherwise, use the editor group's scoped context key service. + return this._editorGroup.activeEditorPane?.scopedContextKeyService ?? + this._editorGroup.scopedContextKeyService; + } + + //#endregion Private Properties + //#region Constructor /** @@ -112,48 +126,22 @@ export class EditorActionBarFactory extends Disposable { super(); /** - * Creates the editor title menu. - * @returns The editor title menu. + * Creates the menus. */ - const createEditorTitleMenu = () => { - // Clear the menu disposable store. - this._menuDisposableStore.clear(); - - // If there is an active editor pane, use its scoped context key service, if possible. - // Otherwise, use the editor group's scoped context key service. - const contextKeyService = this._editorGroup.activeEditorPane?.scopedContextKeyService ?? - this._editorGroup.scopedContextKeyService; - - // Create the menu. - const editorTitleMenu = this._menuDisposableStore.add(this._menuService.createMenu( - MenuId.EditorTitle, - contextKeyService, - { - emitEventsForSubmenuChanges: true, - eventDebounceDelay: 0 - } - )); - - // Add the onDidChange event handler. - this._menuDisposableStore.add(editorTitleMenu.onDidChange(() => { - // Create the menu. - this._editorTitleMenu = createEditorTitleMenu(); - - // Raise the onDidActionsChange event. - this._onDidActionsChangeEmitter.fire(); - })); - - // Return the menu. - return editorTitleMenu; + const createMenus = () => { + this.createMenu(MenuId.EditorActionsLeft); + this.createMenu(MenuId.EditorActionsCenter); + this.createMenu(MenuId.EditorActionsRight); + this.createMenu(MenuId.EditorTitle); }; - // Create the menu. - this._editorTitleMenu = createEditorTitleMenu(); + // Create the menus. + createMenus(); // Add the onDidActiveEditorChange event handler. - this._register(this._editorGroup.onDidActiveEditorChange(() => { - // Create the menu. - this._editorTitleMenu = createEditorTitleMenu(); + this._register(this._editorGroup.onDidActiveEditorChange(e => { + // Recreate the menus. + createMenus(); // Raise the onDidActionsChange event. this._onDidActionsChangeEmitter.fire(); @@ -162,26 +150,146 @@ export class EditorActionBarFactory extends Disposable { //#endregion Constructor - //#region Public Properties + //#region Public Methods /** - * Gets the menu. + * Creates the action bar. + * @param auxiliaryWindow A value which indicates whether the window is an auxiliary window. + * @returns The action bar. */ - get menu() { - return this._editorTitleMenu; + create(auxiliaryWindow?: boolean) { + // Create the set of processed actions. + const processedActions = new Set(); + + // Build the left action bar elements from the editor actions left menu. + const leftActionBarElements = this.buildActionBarElements( + processedActions, + MenuId.EditorActionsLeft, + false + ); + + // Build the center action bar elements from the editor actions center menu. + const centerActionBarElements = this.buildActionBarElements( + processedActions, + MenuId.EditorActionsCenter, + false + ); + + // Build the right action bar elements from the editor actions right menu and the editor + // title menu. + let rightActionBarElements = [ + // Build the right action bar elements from the editor actions right menu. + ...this.buildActionBarElements( + processedActions, + MenuId.EditorActionsRight, + false + ), + // Build the right action bar elements from the editor title menu. + ...this.buildActionBarElements( + processedActions, + MenuId.EditorTitle, + true + ) + ]; + + // Splice the move editor to new window command button into the right action bar elements. + if (auxiliaryWindow !== undefined) { + rightActionBarElements.splice( + rightActionBarElements.length - 1, + 0, + + ); + } + + // Return the action bar. + return ( + + {leftActionBarElements.length > 0 && + + {leftActionBarElements} + + } + {centerActionBarElements.length > 0 && + + {centerActionBarElements} + + } + {rightActionBarElements.length > 0 && + + {rightActionBarElements} + + } + + ); } - //#endregion Public Properties + //#endregion Public Methods - //#region Public Methods + //#region Private Methods /** - * Creates the action bar. - * @param auxiliaryWindow A value which indicates whether the window is an auxiliary window. - * @returns The action bar. + * Creates a menu. + * @param menuId The menu ID. */ - create(auxiliaryWindow?: boolean) { - // Break the actions into primary actions, secondary actions, and submenu descriptors. + private createMenu(menuId: MenuId) { + // Dispose the current menu disposable store. + this._menuDisposableStores.get(menuId)?.dispose(); + + // Add the menu disposable store. + const disposableStore = new DisposableStore(); + this._menuDisposableStores.set(menuId, disposableStore); + + // Create the menu. + const menu = disposableStore.add(this._menuService.createMenu( + menuId, + this.contextKeyService, + { + emitEventsForSubmenuChanges: true, + eventDebounceDelay: 0 + } + )); + this._menus.set(menuId, menu); + + // Add the onDidChange event handler to the menu. + disposableStore.add(menu.onDidChange(() => { + // Recreate the menu. + this.createMenu(menuId); + + // Raise the onDidActionsChange event. + this._onDidActionsChangeEmitter.fire(); + })); + } + + /** + * Builds action bar elements for a menu. + * @param processedActions The processed actions. + * @param menuId The menu ID. + * @param buildSecondaryActions A value which indicates whether to build secondary actions. + */ + private buildActionBarElements( + processedActions: Set, + menuId: MenuId, + buildSecondaryActions: boolean + ) { + // Get the menu. + const menu = this._menus.get(menuId); + if (!menu) { + return []; + } + + // Process the menu actions. const primaryActions: IAction[] = []; const secondaryActions: IAction[] = []; const submenuDescriptors = new Set(); @@ -189,9 +297,11 @@ export class EditorActionBarFactory extends Disposable { arg: this._editorGroup.activeEditor?.resource, shouldForwardArgs: true } satisfies IMenuActionOptions; - for (const [group, actions] of this._editorTitleMenu.getActions(options)) { + for (const [group, actions] of menu.getActions(options)) { // Determine the target actions. - const targetActions = this.isPrimaryGroup(group) ? primaryActions : secondaryActions; + const targetActions = !buildSecondaryActions || this.isPrimaryGroup(group) ? + primaryActions : + secondaryActions; // Push a separator between groups. if (targetActions.length > 0) { @@ -217,7 +327,9 @@ export class EditorActionBarFactory extends Disposable { // Inline submenus, where possible. for (const { group, action, index } of submenuDescriptors) { // Set the target. - const target = this.isPrimaryGroup(group) ? primaryActions : secondaryActions; + const target = !buildSecondaryActions || this.isPrimaryGroup(group) ? + primaryActions : + secondaryActions; // Inline the submenu, if possible. if (this.shouldInlineSubmenuAction(group, action)) { @@ -234,7 +346,10 @@ export class EditorActionBarFactory extends Disposable { elements.push(); } else if (action instanceof MenuItemAction) { // Menu item action. - elements.push(); + if (!processedActions.has(action.id)) { + processedActions.add(action.id); + elements.push(); + } } else if (action instanceof SubmenuAction) { // Submenu action. Get the first action. const firstAction = action.actions[0]; @@ -273,19 +388,6 @@ export class EditorActionBarFactory extends Disposable { } } - // If we know whether we're in an auxiliary window, add the move into new window button. - if (auxiliaryWindow !== undefined) { - elements.push( - - ); - } - // If there are secondary actions, add the more actions button. Note that the normal // dropdown arrow is hidden on this button because it uses the ยทยทยท icon. if (secondaryActions.length) { @@ -301,26 +403,10 @@ export class EditorActionBarFactory extends Disposable { ); } - // Return the elements. - return ( - - - {elements} - - - ); + // Return the action bar elements. + return elements; } - //#endregion Public Methods - - //#region Private Methods - /** * Determines whether a group is the primary group. * @param group The group. diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index de5267bf08c..ba96b46b209 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -63,6 +63,23 @@ const apiMenus: IAPIMenu[] = [ description: localize('menus.touchBar', "The touch bar (macOS only)"), supportsSubmenus: false }, + // --- Start Positron --- + { + key: 'editor/actions/left', + id: MenuId.EditorActionsLeft, + description: localize('menus.editorActionsLeft', "The editor actions left menu") + }, + { + key: 'editor/actions/center', + id: MenuId.EditorActionsCenter, + description: localize('menus.editorActionsCenter', "The editor actions center menu") + }, + { + key: 'editor/actions/right', + id: MenuId.EditorActionsRight, + description: localize('menus.editorActionsRight', "The editor actions right menu") + }, + // --- End Positron --- { key: 'editor/title', id: MenuId.EditorTitle,