Skip to content

Commit 2175d4c

Browse files
committed
- Introduce menu id to contribute view title context menu actions
- Use this menu id to get context menu action for a view
1 parent 1cc556a commit 2175d4c

10 files changed

Lines changed: 206 additions & 149 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export const enum MenuId {
104104
TunnelTitle,
105105
ViewItemContext,
106106
ViewTitle,
107+
ViewTitleContext,
107108
CommentThreadTitle,
108109
CommentThreadActions,
109110
CommentTitle,

src/vs/workbench/browser/parts/activitybar/activitybarPart.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
125125
actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar")));
126126
return actions;
127127
},
128+
getContextMenuActionsForComposite: () => [],
128129
getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(),
129130
hidePart: () => this.layoutService.setSideBarHidden(true),
130131
compositeSize: 50,

src/vs/workbench/browser/parts/compositeBar.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface ICompositeBarOptions {
4040
getCompositePinnedAction: (compositeId: string) => Action;
4141
getOnCompositeClickAction: (compositeId: string) => Action;
4242
getContextMenuActions: () => Action[];
43-
getContextMenuActionsForCompositeId?: (compositeId: string) => Action[];
43+
getContextMenuActionsForComposite: (compositeId: string) => Action[];
4444
openComposite: (compositeId: string) => Promise<any>;
4545
getDefaultCompositeId: () => string;
4646
hidePart: () => void;
@@ -103,7 +103,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
103103
const item = this.model.findItem(action.id);
104104
return item && this.instantiationService.createInstance(
105105
CompositeActionViewItem, action as ActivityAction, item.pinnedAction,
106-
(compositeId: string) => { return this.options.getContextMenuActionsForCompositeId === undefined ? [] : this.options.getContextMenuActionsForCompositeId(compositeId); },
106+
(compositeId: string) => this.options.getContextMenuActionsForComposite(compositeId),
107107
() => this.getContextMenuActions() as Action[],
108108
this.options.colors,
109109
this.options.icon,

src/vs/workbench/browser/parts/compositeBarActions.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
463463
constructor(
464464
private compositeActivityAction: ActivityAction,
465465
private toggleCompositePinnedAction: Action,
466-
private compositeMenuActionsProvider: (compositeId: string) => ReadonlyArray<Action>,
466+
private compositeContextMenuActionsProvider: (compositeId: string) => ReadonlyArray<Action>,
467467
private contextMenuActionsProvider: () => ReadonlyArray<Action>,
468468
colors: (theme: ITheme) => ICompositeBarColors,
469469
icon: boolean,
@@ -598,9 +598,9 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
598598
private showContextMenu(container: HTMLElement): void {
599599
const actions: Action[] = [this.toggleCompositePinnedAction];
600600

601-
const compositeSpecificActions = this.compositeMenuActionsProvider(this.activity.id);
602-
if (compositeSpecificActions.length) {
603-
actions.push(...compositeSpecificActions);
601+
const compositeContextMenuActions = this.compositeContextMenuActionsProvider(this.activity.id);
602+
if (compositeContextMenuActions.length) {
603+
actions.push(...compositeContextMenuActions);
604604
}
605605

606606
if ((<any>this.compositeActivityAction.activity).extensionId) {

src/vs/workbench/browser/parts/panel/panelPart.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
3434
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
3535
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
3636
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection } from 'vs/workbench/common/views';
37+
import { MenuId } from 'vs/platform/actions/common/actions';
38+
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
3739

3840
interface ICachedPanel {
3941
id: string;
@@ -137,7 +139,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
137139
.map(({ id, label }) => this.instantiationService.createInstance(SetPanelPositionAction, id, label)),
138140
this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel"))
139141
] as Action[],
140-
getContextMenuActionsForCompositeId: (compositeId: string) => this.getContextMenuActionsForCompositeId(compositeId) as Action[],
142+
getContextMenuActionsForComposite: (compositeId: string) => this.getContextMenuActionsForComposite(compositeId) as Action[],
141143
getDefaultCompositeId: () => this.panelRegistry.getDefaultPanelId(),
142144
hidePart: () => this.layoutService.setPanelHidden(true),
143145
compositeSize: 0,
@@ -161,14 +163,18 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
161163
this.onDidRegisterPanels([...this.getPanels()]);
162164
}
163165

164-
private getContextMenuActionsForCompositeId(compositeId: string): readonly IAction[] {
165-
const panel = this.getActivePanel();
166-
167-
if (!panel || panel.getId() !== compositeId) {
168-
return [];
166+
private getContextMenuActionsForComposite(compositeId: string): readonly IAction[] {
167+
const result: IAction[] = [];
168+
const container = this.getViewContainer(compositeId);
169+
if (container) {
170+
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(container);
171+
if (viewDescriptors.allViewDescriptors.length === 1) {
172+
const viewMenuActions = this.instantiationService.createInstance(ViewMenuActions, viewDescriptors.allViewDescriptors[0].id, MenuId.ViewTitle, MenuId.ViewTitleContext);
173+
result.push(...viewMenuActions.getContextMenuActions());
174+
viewMenuActions.dispose();
175+
}
169176
}
170-
171-
return panel.getContextMenuActions();
177+
return result;
172178
}
173179

174180
private onDidRegisterPanels(panels: PanelDescriptor[]): void {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { IAction } from 'vs/base/common/actions';
7+
import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
8+
import { Emitter, Event } from 'vs/base/common/event';
9+
import { MenuId, IMenuService } from 'vs/platform/actions/common/actions';
10+
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
11+
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
12+
13+
export class ViewMenuActions extends Disposable {
14+
15+
private primaryActions: IAction[] = [];
16+
private readonly titleActionsDisposable = this._register(new MutableDisposable());
17+
private secondaryActions: IAction[] = [];
18+
private contextMenuActions: IAction[] = [];
19+
20+
private _onDidChangeTitle = this._register(new Emitter<void>());
21+
readonly onDidChangeTitle: Event<void> = this._onDidChangeTitle.event;
22+
23+
constructor(
24+
viewId: string,
25+
menuId: MenuId,
26+
contextMenuId: MenuId,
27+
@IContextKeyService private readonly contextKeyService: IContextKeyService,
28+
@IMenuService private readonly menuService: IMenuService,
29+
) {
30+
super();
31+
32+
const scopedContextKeyService = this._register(this.contextKeyService.createScoped());
33+
scopedContextKeyService.createKey('view', viewId);
34+
35+
const menu = this._register(this.menuService.createMenu(menuId, scopedContextKeyService));
36+
const updateActions = () => {
37+
this.primaryActions = [];
38+
this.secondaryActions = [];
39+
this.titleActionsDisposable.value = createAndFillInActionBarActions(menu, undefined, { primary: this.primaryActions, secondary: this.secondaryActions });
40+
this._onDidChangeTitle.fire();
41+
};
42+
this._register(menu.onDidChange(updateActions));
43+
updateActions();
44+
45+
const contextMenu = this._register(this.menuService.createMenu(contextMenuId, scopedContextKeyService));
46+
const updateContextMenuActions = () => {
47+
this.contextMenuActions = [];
48+
this.titleActionsDisposable.value = createAndFillInActionBarActions(contextMenu, undefined, { primary: [], secondary: this.contextMenuActions });
49+
};
50+
this._register(contextMenu.onDidChange(updateContextMenuActions));
51+
updateContextMenuActions();
52+
53+
this._register(toDisposable(() => {
54+
this.primaryActions = [];
55+
this.secondaryActions = [];
56+
this.contextMenuActions = [];
57+
}));
58+
}
59+
60+
getPrimaryActions(): IAction[] {
61+
return this.primaryActions;
62+
}
63+
64+
getSecondaryActions(): IAction[] {
65+
return this.secondaryActions;
66+
}
67+
68+
getContextMenuActions(): IAction[] {
69+
return this.contextMenuActions;
70+
}
71+
}

src/vs/workbench/browser/parts/views/viewPaneContainer.ts

Lines changed: 35 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
1010
import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler';
1111
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER } from 'vs/workbench/common/theme';
1212
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom';
13-
import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
13+
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
1414
import { firstIndex } from 'vs/base/common/arrays';
1515
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
1616
import { IActionViewItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -25,7 +25,7 @@ import { PaneView, IPaneViewOptions, IPaneOptions, Pane, DefaultPaneDndControlle
2525
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2626
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
2727
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
28-
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, Extensions as ViewsExtensions, ViewContainerLocation, IViewsService, IViewsRegistry } from 'vs/workbench/common/views';
28+
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
2929
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
3030
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
3131
import { assertIsDefined } from 'vs/base/common/types';
@@ -36,8 +36,9 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
3636
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
3737
import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer';
3838
import { Component } from 'vs/workbench/common/component';
39-
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
40-
import { createAndFillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
39+
import { MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
40+
import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
41+
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
4142

4243
export interface IPaneColors extends IColorMapping {
4344
dropBackground?: ColorIdentifier;
@@ -76,7 +77,7 @@ export abstract class ViewPane extends Pane implements IView {
7677
readonly id: string;
7778
title: string;
7879

79-
private readonly menuActions?: ViewMenuActions;
80+
private readonly menuActions: ViewMenuActions;
8081

8182
protected actionRunner?: IActionRunner;
8283
protected toolbar?: ToolBar;
@@ -101,10 +102,8 @@ export abstract class ViewPane extends Pane implements IView {
101102
this.showActionsAlways = !!options.showActionsAlways;
102103
this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService);
103104

104-
if (options.titleMenuId !== undefined) {
105-
this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId));
106-
this._register(this.menuActions.onDidChangeTitle(() => this.updateActions()));
107-
}
105+
this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId || MenuId.ViewTitle, MenuId.ViewTitleContext));
106+
this._register(this.menuActions.onDidChangeTitle(() => this.updateActions()));
108107
}
109108

110109
setVisible(visible: boolean): void {
@@ -225,6 +224,10 @@ export abstract class ViewPane extends Pane implements IView {
225224
return this.menuActions ? this.menuActions.getSecondaryActions() : [];
226225
}
227226

227+
getContextMenuActions(): IAction[] {
228+
return this.menuActions ? this.menuActions.getContextMenuActions() : [];
229+
}
230+
228231
getActionViewItem(action: IAction): IActionViewItem | undefined {
229232
if (action instanceof MenuItemAction) {
230233
return this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action);
@@ -391,55 +394,39 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
391394
}
392395

393396
getContextMenuActions(viewDescriptor?: IViewDescriptor): IAction[] {
397+
const result: IAction[] = [];
394398

395-
if (this.isViewMergedWithContainer()) {
396-
const viewId = this.panes[0].id;
397-
viewDescriptor = Registry.as<IViewsRegistry>(ViewsExtensions.ViewsRegistry).getView(viewId)!;
399+
if (!viewDescriptor && this.isViewMergedWithContainer()) {
400+
viewDescriptor = this.viewDescriptorService.getViewDescriptor(this.panes[0].id) || undefined;
398401
}
399402

400-
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewsExtensions.ViewContainersRegistry);
401-
const currentLocation = viewContainerRegistry.getViewContainerLocation(this.viewContainer);
402-
403-
const result: IAction[] = [];
404403
if (viewDescriptor) {
405-
// For now, restrict any additional actions to the sidebar only
406-
if (!this.isViewMergedWithContainer() && currentLocation === ViewContainerLocation.Sidebar) {
407-
result.push(<IAction>{
408-
id: `${viewDescriptor.id}.removeView`,
409-
label: nls.localize('hideView', "Hide"),
410-
enabled: viewDescriptor.canToggleVisibility,
411-
run: () => this.toggleViewVisibility(viewDescriptor!.id)
412-
});
413-
}
414-
415-
if (viewDescriptor.canMoveView) {
416-
const newLocation = currentLocation === ViewContainerLocation.Panel ? ViewContainerLocation.Sidebar : ViewContainerLocation.Panel;
417-
result.push(<IAction>{
418-
id: `${viewDescriptor.id}.moveView`,
419-
label: newLocation === ViewContainerLocation.Sidebar ? nls.localize('moveViewToSidebar', "Move to Sidebar") : nls.localize('moveViewToPanel', "Move to Panel"),
420-
enabled: true,
421-
run: () => this.moveView(viewDescriptor!, newLocation)
422-
});
404+
result.push(<IAction>{
405+
id: `${viewDescriptor.id}.removeView`,
406+
label: nls.localize('hideView', "Hide"),
407+
enabled: viewDescriptor.canToggleVisibility,
408+
run: () => this.toggleViewVisibility(viewDescriptor!.id)
409+
});
410+
const view = this.getView(viewDescriptor.id);
411+
if (view) {
412+
result.push(...view.getContextMenuActions());
423413
}
424414
}
425415

426-
// For now, restrict any additional actions to the sidebar only
427-
if (currentLocation === ViewContainerLocation.Sidebar) {
428-
const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => (<IAction>{
429-
id: `${viewDescriptor.id}.toggleVisibility`,
430-
label: viewDescriptor.name,
431-
checked: this.viewsModel.isVisible(viewDescriptor.id),
432-
enabled: viewDescriptor.canToggleVisibility,
433-
run: () => this.toggleViewVisibility(viewDescriptor.id)
434-
}));
435-
436-
if (result.length && viewToggleActions.length) {
437-
result.push(new Separator());
438-
}
416+
const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => (<IAction>{
417+
id: `${viewDescriptor.id}.toggleVisibility`,
418+
label: viewDescriptor.name,
419+
checked: this.viewsModel.isVisible(viewDescriptor.id),
420+
enabled: viewDescriptor.canToggleVisibility,
421+
run: () => this.toggleViewVisibility(viewDescriptor.id)
422+
}));
439423

440-
result.push(...viewToggleActions);
424+
if (result.length && viewToggleActions.length) {
425+
result.push(new Separator());
441426
}
442427

428+
result.push(...viewToggleActions);
429+
443430
return result;
444431
}
445432

@@ -803,50 +790,3 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
803790
}
804791
}
805792
}
806-
807-
808-
class ViewMenuActions extends Disposable {
809-
810-
private primaryActions: IAction[] = [];
811-
private readonly titleActionsDisposable = this._register(new MutableDisposable());
812-
private secondaryActions: IAction[] = [];
813-
814-
private _onDidChangeTitle = this._register(new Emitter<void>());
815-
readonly onDidChangeTitle: Event<void> = this._onDidChangeTitle.event;
816-
817-
constructor(
818-
viewId: string,
819-
menuId: MenuId,
820-
@IContextKeyService private readonly contextKeyService: IContextKeyService,
821-
@IMenuService private readonly menuService: IMenuService,
822-
) {
823-
super();
824-
825-
const scopedContextKeyService = this._register(this.contextKeyService.createScoped());
826-
scopedContextKeyService.createKey('view', viewId);
827-
828-
const menu = this._register(this.menuService.createMenu(menuId, scopedContextKeyService));
829-
const updateActions = () => {
830-
this.primaryActions = [];
831-
this.secondaryActions = [];
832-
this.titleActionsDisposable.value = createAndFillInActionBarActions(menu, undefined, { primary: this.primaryActions, secondary: this.secondaryActions });
833-
this._onDidChangeTitle.fire();
834-
};
835-
836-
this._register(menu.onDidChange(updateActions));
837-
updateActions();
838-
839-
this._register(toDisposable(() => {
840-
this.primaryActions = [];
841-
this.secondaryActions = [];
842-
}));
843-
}
844-
845-
getPrimaryActions(): IAction[] {
846-
return this.primaryActions;
847-
}
848-
849-
getSecondaryActions(): IAction[] {
850-
return this.secondaryActions;
851-
}
852-
}

0 commit comments

Comments
 (0)