diff --git a/src/vs/workbench/parts/files/browser/explorerViewlet.ts b/src/vs/workbench/parts/files/browser/explorerViewlet.ts index a20a0f0d3a3489bf56115383d609b06ee7153ea2..f672e4fd133edaf71f07e4b04e5f06048e682adc 100644 --- a/src/vs/workbench/parts/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/browser/explorerViewlet.ts @@ -6,6 +6,7 @@ 'use strict'; import 'vs/css!./media/explorerviewlet'; +import { localize } from 'vs/nls'; import { IActionRunner } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; import * as DOM from 'vs/base/browser/dom'; @@ -93,7 +94,8 @@ export class ExplorerViewlet extends ComposedViewsViewlet { location: ViewLocation.Explorer, ctor: OpenEditorsView, order: 0, - when: OpenEditorsVisibleCondition + when: OpenEditorsVisibleCondition, + canToggleVisibility: true }; } @@ -103,17 +105,19 @@ export class ExplorerViewlet extends ComposedViewsViewlet { name: EmptyView.NAME, location: ViewLocation.Explorer, ctor: EmptyView, - order: 1 + order: 1, + canToggleVisibility: true }; } private createExplorerViewDescriptor(): IViewDescriptor { return { id: ExplorerView.ID, - name: this.contextService.getWorkspace().name, + name: localize('folders', "Folders"), location: ViewLocation.Explorer, ctor: ExplorerView, - order: 1 + order: 1, + canToggleVisibility: true }; } diff --git a/src/vs/workbench/parts/views/browser/treeView.ts b/src/vs/workbench/parts/views/browser/treeView.ts index 84c376d4bd23ff5917d3ebc236a153fb638ed212..f5ff3bfb62cdaf33ccf9bc5e04e642470391eb74 100644 --- a/src/vs/workbench/parts/views/browser/treeView.ts +++ b/src/vs/workbench/parts/views/browser/treeView.ts @@ -314,6 +314,9 @@ class TreeController extends DefaultController { } public onContextMenu(tree: ITree, node: ITreeItem, event: ContextMenuEvent): boolean { + event.preventDefault(); + event.stopPropagation(); + tree.setFocus(node); const actions = this.menus.getResourceContextActions(node); if (!actions.length) { diff --git a/src/vs/workbench/parts/views/browser/views.ts b/src/vs/workbench/parts/views/browser/views.ts index 7e7f9044a3eeef8d5e4df18e1bee45296bbd7eab..a85b245c96a30c876b21160cf558d30b8d7ae0d3 100644 --- a/src/vs/workbench/parts/views/browser/views.ts +++ b/src/vs/workbench/parts/views/browser/views.ts @@ -12,7 +12,7 @@ import { $, Dimension, Builder } from 'vs/base/browser/builder'; import { Scope } from 'vs/workbench/common/memento'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { Registry } from 'vs/platform/registry/common/platform'; import { prepareActions } from 'vs/workbench/browser/actions'; import { Viewlet, ViewletRegistry, Extensions } from 'vs/workbench/browser/viewlet'; @@ -58,6 +58,8 @@ export interface IView extends IBaseView, IThemable { name: string; + getHeaderElement(): HTMLElement; + create(): TPromise; setVisible(visible: boolean): TPromise; @@ -141,6 +143,10 @@ export abstract class CollapsibleView extends AbstractCollapsibleView implements return TPromise.as(null); } + getHeaderElement(): HTMLElement { + return this.header; + } + public renderHeader(container: HTMLElement): void { // Tool bar @@ -306,6 +312,7 @@ export class ComposedViewsViewlet extends Viewlet { private splitView: SplitView; protected views: IView[]; + private viewHeaderContextMenuListeners: IDisposable[] = []; private dimension: Dimension; private viewletSettings: object; @@ -348,7 +355,6 @@ export class ComposedViewsViewlet extends Viewlet { this.viewletContainer = DOM.append(parent.getHTMLElement(), DOM.$('')); this.splitView = this._register(new SplitView(this.viewletContainer)); this._register(this.splitView.onFocus((view: IView) => this.lastFocusedView = view)); - this._register(DOM.addDisposableListener(this.viewletContainer, 'contextmenu', e => this.onContextMenu(new StandardMouseEvent(e)))); return this.onViewDescriptorsChanged() .then(() => { @@ -373,18 +379,20 @@ export class ComposedViewsViewlet extends Viewlet { } public getSecondaryActions(): IAction[] { - let actions = []; if (this.hasSingleView() && this.views[0]) { - actions = this.views[0].getSecondaryActions(); - } - - if (actions.length) { - actions.push(new Separator()); + return this.views[0].getSecondaryActions(); } + return []; + } - actions.push(...this.getToggleVisibilityActions(this.getViewDescriptorsFromRegistry())); - - return actions; + public getContextMenuActions(): IAction[] { + return this.getVisibilityManageableViewDescriptors().map(viewDescriptor => ({ + id: `${viewDescriptor.id}.toggleVisibility`, + label: viewDescriptor.name, + checked: this.isCurrentlyVisible(viewDescriptor), + enabled: this.contextKeyService.contextMatchesRules(viewDescriptor.when), + run: () => this.toggleViewVisibility(viewDescriptor.id) + })); } public setVisible(visible: boolean): TPromise { @@ -428,28 +436,9 @@ export class ComposedViewsViewlet extends Viewlet { } } - private onContextMenu(event: StandardMouseEvent): void { - let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => TPromise.as(this.getSecondaryActions()), - }); - } - - private getToggleVisibilityActions(viewDescriptors: IViewDescriptor[]): IAction[] { - // return viewDescriptors.map(viewDescriptor => ({ - // id: `${viewDescriptor.id}.toggleVisibility`, - // label: viewDescriptor.name, - // checked: this.isCurrentlyVisible(viewDescriptor), - // enabled: this.contextKeyService.contextMatchesRules(viewDescriptor.when), - // run: () => this.toggleViewVisibility(viewDescriptor) - // })); - return []; - } - - protected toggleViewVisibility(viewDescriptor: IViewDescriptor): void { - const view = this.getView(viewDescriptor.id); - let viewState = this.viewsStates.get(viewDescriptor.id); + private toggleViewVisibility(id: string): void { + const view = this.getView(id); + let viewState = this.viewsStates.get(id); if (view) { viewState = viewState || this.createViewState(view); viewState.isHidden = true; @@ -457,7 +446,7 @@ export class ComposedViewsViewlet extends Viewlet { viewState = viewState || { collapsed: true, size: void 0, isHidden: false }; viewState.isHidden = false; } - this.viewsStates.set(viewDescriptor.id, viewState); + this.viewsStates.set(id, viewState); this.updateViews(); } @@ -602,6 +591,14 @@ export class ComposedViewsViewlet extends Viewlet { // Update title area since the title actions have changed. this.updateTitleArea(); + this.viewHeaderContextMenuListeners = dispose(this.viewHeaderContextMenuListeners); + for (const viewDescriptor of this.getVisibilityManageableViewDescriptors()) { + const view = this.getView(viewDescriptor.id); + if (view) { + this.viewHeaderContextMenuListeners.push(DOM.addDisposableListener(view.getHeaderElement(), DOM.EventType.CONTEXT_MENU, (e) => this.onContextMenu(new StandardMouseEvent(e), view))); + } + } + if (this.dimension) { this.layoutViews(); } @@ -609,6 +606,22 @@ export class ComposedViewsViewlet extends Viewlet { return this.setVisible(this.isVisible()); } + private onContextMenu(event: StandardMouseEvent, view: IView): void { + event.stopPropagation(); + event.preventDefault(); + + let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.as([{ + id: `${view.id}.removeView`, + label: nls.localize('removeView', "Remove from {0}", this.getTitle()), + enabled: true, + run: () => this.toggleViewVisibility(view.id) + }]), + }); + } + private hasSingleView(): boolean { if (this.views.length > 1) { return false; @@ -620,6 +633,10 @@ export class ComposedViewsViewlet extends Viewlet { return true; } + private getVisibilityManageableViewDescriptors(): IViewDescriptor[] { + return this.getViewDescriptorsFromRegistry().filter(viewDescriptor => viewDescriptor.canToggleVisibility); + } + private getViewDescriptorsFromRegistry(): IViewDescriptor[] { return ViewsRegistry.getViews(this.location) .sort((a, b) => { diff --git a/src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts b/src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts index ec0856e217bef14980376798112f6337cf62635e..c27775d646cdf00777aa6a5aae8038db5e020712 100644 --- a/src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; -import { ViewLocation, ViewsRegistry } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { ViewLocation, ViewsRegistry, IViewDescriptor } from 'vs/workbench/parts/views/browser/viewsRegistry'; import { TreeView } from 'vs/workbench/parts/views/browser/treeView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -92,12 +92,13 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyV return; } - const viewDescriptors = entry.value.map(item => ({ + const viewDescriptors = entry.value.map(item => ({ id: item.id, name: item.name, ctor: TreeView, location, - when: ContextKeyExpr.deserialize(item.when) + when: ContextKeyExpr.deserialize(item.when), + canToggleVisibility: true })); ViewsRegistry.registerViews(viewDescriptors); }); diff --git a/src/vs/workbench/parts/views/browser/viewsRegistry.ts b/src/vs/workbench/parts/views/browser/viewsRegistry.ts index 1e413e83990d05c824cf21d37e03af22fa684fb8..c10d1922f8256cda5b61f509d4083001f0d69088 100644 --- a/src/vs/workbench/parts/views/browser/viewsRegistry.ts +++ b/src/vs/workbench/parts/views/browser/viewsRegistry.ts @@ -44,6 +44,8 @@ export interface IViewDescriptor { readonly order?: number; readonly size?: number; + + readonly canToggleVisibility?: boolean; } export interface IViewsRegistry {