diff --git a/src/vs/platform/actions/browser/menusExtensionPoint.ts b/src/vs/platform/actions/browser/menusExtensionPoint.ts index c25c4195a9d..72c6de07141 100644 --- a/src/vs/platform/actions/browser/menusExtensionPoint.ts +++ b/src/vs/platform/actions/browser/menusExtensionPoint.ts @@ -37,6 +37,7 @@ namespace schema { case 'scm/title': return MenuId.SCMTitle; case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext; case 'scm/resource/context': return MenuId.SCMResourceContext; + case 'viewlet/title': return MenuId.ViewletTitle; } return void 0; diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 7c78678d45c..266941d4d12 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -46,6 +46,7 @@ export class MenuId { static readonly SCMResourceGroupContext = new MenuId('12'); static readonly SCMResourceContext = new MenuId('13'); static readonly CommandPalette = new MenuId('14'); + static readonly ViewletTitle = new MenuId('15'); constructor(private _id: string) { diff --git a/src/vs/workbench/api/node/mainThreadTreeExplorers.ts b/src/vs/workbench/api/node/mainThreadTreeExplorers.ts index 178c1e4ac93..1b1e6121e7f 100644 --- a/src/vs/workbench/api/node/mainThreadTreeExplorers.ts +++ b/src/vs/workbench/api/node/mainThreadTreeExplorers.ts @@ -30,6 +30,7 @@ export class MainThreadTreeExplorers extends MainThreadTreeExplorersShape { const onError = err => { this.messageService.show(Severity.Error, err); }; this.treeExplorerService.registerTreeExplorerNodeProvider(providerId, { + id: providerId, provideRootNode: (): TPromise => { return this._proxy.$provideRootNode(providerId).then(rootNode => rootNode, onError); }, diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index 95fe8c06a4e..8c63d61bfd1 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -440,7 +440,7 @@ export abstract class CollapsibleViewletView extends CollapsibleView implements collapsed: boolean, private viewName: string, protected messageService: IMessageService, - private keybindingService: IKeybindingService, + protected keybindingService: IKeybindingService, protected contextMenuService: IContextMenuService, headerSize?: number ) { diff --git a/src/vs/workbench/parts/explorers/browser/treeExplorerMenus.ts b/src/vs/workbench/parts/explorers/browser/treeExplorerMenus.ts new file mode 100644 index 00000000000..22e73fbda29 --- /dev/null +++ b/src/vs/workbench/parts/explorers/browser/treeExplorerMenus.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IAction } from 'vs/base/common/actions'; +import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { ITreeExplorerService } from 'vs/workbench/parts/explorers/common/treeExplorerService'; + + +export class TreeExplorerMenus implements IDisposable { + + private disposables: IDisposable[] = []; + + private activeProviderId: string; + private titleDisposable: IDisposable = EmptyDisposable; + private titleActions: IAction[] = []; + private titleSecondaryActions: IAction[] = []; + + private _onDidChangeTitle = new Emitter(); + get onDidChangeTitle(): Event { return this._onDidChangeTitle.event; } + + constructor( + @IContextKeyService private contextKeyService: IContextKeyService, + @ITreeExplorerService private treeExplorerService: ITreeExplorerService, + @IMenuService private menuService: IMenuService + ) { + this.setActiveProvider(this.treeExplorerService.activeProvider); + this.treeExplorerService.onDidChangeProvider(this.setActiveProvider, this, this.disposables); + } + + private setActiveProvider(activeProvider: string | undefined): void { + if (this.titleDisposable) { + this.titleDisposable.dispose(); + this.titleDisposable = EmptyDisposable; + } + + if (!activeProvider) { + return; + } + + this.activeProviderId = activeProvider; + + const titleMenu = this.menuService.createMenu(MenuId.ViewletTitle, this.contextKeyService); + const updateActions = () => { + this.titleActions = []; + this.titleSecondaryActions = []; + fillInActions(titleMenu, null, { primary: this.titleActions, secondary: this.titleSecondaryActions }); + this._onDidChangeTitle.fire(); + }; + + const listener = titleMenu.onDidChange(updateActions); + updateActions(); + + this.titleDisposable = toDisposable(() => { + listener.dispose(); + titleMenu.dispose(); + this.titleActions = []; + this.titleSecondaryActions = []; + }); + } + + getTitleActions(): IAction[] { + return this.titleActions; + } + + getTitleSecondaryActions(): IAction[] { + return this.titleSecondaryActions; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/vs/workbench/parts/explorers/browser/treeExplorerService.ts b/src/vs/workbench/parts/explorers/browser/treeExplorerService.ts index 5046d42c534..e3034eb8c52 100644 --- a/src/vs/workbench/parts/explorers/browser/treeExplorerService.ts +++ b/src/vs/workbench/parts/explorers/browser/treeExplorerService.ts @@ -10,19 +10,47 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { InternalTreeExplorerNode, InternalTreeExplorerNodeProvider } from 'vs/workbench/parts/explorers/common/treeExplorerViewModel'; import { ITreeExplorerService } from 'vs/workbench/parts/explorers/common/treeExplorerService'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; export class TreeExplorerService implements ITreeExplorerService { public _serviceBrand: any; + private _activeProvider: string; + private activeProviderContextKey: IContextKey; + + private _onDidChangeProvider = new Emitter(); + get onDidChangeProvider(): Event { return this._onDidChangeProvider.event; } + private _onTreeExplorerNodeProviderRegistered = new Emitter(); public get onTreeExplorerNodeProviderRegistered(): Event { return this._onTreeExplorerNodeProviderRegistered.event; }; private _treeExplorerNodeProviders: { [providerId: string]: InternalTreeExplorerNodeProvider }; constructor( - @IMessageService private messageService: IMessageService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IMessageService private messageService: IMessageService ) { this._treeExplorerNodeProviders = Object.create(null); + this.activeProviderContextKey = this.contextKeyService.createKey('treeExplorerProvider', void 0); + } + + get activeProvider(): string { + return this._activeProvider; + } + + set activeProvider(provider: string) { + if (!provider) { + throw new Error('invalid provider'); + } + + if (provider && !!this._treeExplorerNodeProviders[provider]) { + throw new Error('Provider not registered'); + } + + this._activeProvider = provider; + this.activeProviderContextKey.set(provider ? provider : void 0); + + this._onDidChangeProvider.fire(provider); } public registerTreeExplorerNodeProvider(providerId: string, provider: InternalTreeExplorerNodeProvider): void { diff --git a/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts b/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts index 1fd14221bd2..15c66eb1e09 100644 --- a/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts +++ b/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts @@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Builder, $ } from 'vs/base/browser/builder'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { CollapsibleViewletView } from 'vs/workbench/browser/viewlet'; -import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { IAction, IActionRunner, IActionItem } from 'vs/base/common/actions'; import { IMessageService } from 'vs/platform/message/common/message'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -20,9 +20,15 @@ import { ITreeExplorerService } from 'vs/workbench/parts/explorers/common/treeEx import { ITree } from 'vs/base/parts/tree/browser/tree'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { TreeExplorerViewletState, TreeDataSource, TreeRenderer, TreeController } from 'vs/workbench/parts/explorers/browser/views/treeExplorerViewer'; +import { TreeExplorerMenus } from 'vs/workbench/parts/explorers/browser/treeExplorerMenus'; import { RefreshViewExplorerAction } from 'vs/workbench/parts/explorers/browser/treeExplorerActions'; +import { createActionItem } from 'vs/platform/actions/browser/menuItemActionItem'; + export class TreeExplorerView extends CollapsibleViewletView { + + private menus: TreeExplorerMenus; + constructor( private viewletState: TreeExplorerViewletState, private treeNodeProviderId: string, @@ -37,7 +43,8 @@ export class TreeExplorerView extends CollapsibleViewletView { @IListService private listService: IListService ) { super(actionRunner, false, nls.localize('treeExplorerViewlet.tree', "Tree Explorer Section"), messageService, keybindingService, contextMenuService, headerSize); - + this.treeExplorerService.activeProvider = treeNodeProviderId; + this.menus = this.instantiationService.createInstance(TreeExplorerMenus); this.create(); } @@ -66,10 +73,16 @@ export class TreeExplorerView extends CollapsibleViewletView { return tree; } - public getActions(): IAction[] { - const refresh = this.instantiationService.createInstance(RefreshViewExplorerAction, this); + getActions(): IAction[] { + return [...this.menus.getTitleActions(), new RefreshViewExplorerAction(this)]; + } - return [refresh]; + getSecondaryActions(): IAction[] { + return this.menus.getTitleSecondaryActions(); + } + + getActionItem(action: IAction): IActionItem { + return createActionItem(action, this.keybindingService, this.messageService); } public create(): TPromise { diff --git a/src/vs/workbench/parts/explorers/common/treeExplorerService.ts b/src/vs/workbench/parts/explorers/common/treeExplorerService.ts index 1ce4896a478..f28f59e281d 100644 --- a/src/vs/workbench/parts/explorers/common/treeExplorerService.ts +++ b/src/vs/workbench/parts/explorers/common/treeExplorerService.ts @@ -14,8 +14,10 @@ export const ITreeExplorerService = createDecorator('treeE export interface ITreeExplorerService { _serviceBrand: any; - onTreeExplorerNodeProviderRegistered: Event; + onDidChangeProvider: Event; + activeProvider: string; + onTreeExplorerNodeProviderRegistered: Event; registerTreeExplorerNodeProvider(providerId: string, provider: InternalTreeExplorerNodeProvider): void; hasProvider(providerId: string): boolean; diff --git a/src/vs/workbench/parts/explorers/common/treeExplorerViewModel.ts b/src/vs/workbench/parts/explorers/common/treeExplorerViewModel.ts index 7e6b265faf3..0a0c0bb9a52 100644 --- a/src/vs/workbench/parts/explorers/common/treeExplorerViewModel.ts +++ b/src/vs/workbench/parts/explorers/common/treeExplorerViewModel.ts @@ -17,6 +17,7 @@ export interface InternalTreeExplorerNode extends InternalTreeExplorerNodeConten } export interface InternalTreeExplorerNodeProvider { + id: string; provideRootNode(): Thenable; resolveChildren(node: InternalTreeExplorerNodeContent): Thenable; executeCommand(node: InternalTreeExplorerNodeContent): TPromise;