sidebarPart.ts 8.0 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/sidebarpart';
J
Johannes Rieken 已提交
7
import { TPromise } from 'vs/base/common/winjs.base';
8
import * as nls from 'vs/nls';
9
import { Registry } from 'vs/platform/registry/common/platform';
S
Sandeep Somavarapu 已提交
10
import { Action } from 'vs/base/common/actions';
J
Johannes Rieken 已提交
11 12
import { CompositePart } from 'vs/workbench/browser/parts/compositePart';
import { Viewlet, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
B
Benjamin Pasero 已提交
13
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
J
Johannes Rieken 已提交
14
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
B
Benjamin Pasero 已提交
15
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
16
import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
J
Johannes Rieken 已提交
17 18 19 20 21 22 23
import { IViewlet } from 'vs/workbench/common/viewlet';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
24
import { Event, mapEvent } from 'vs/base/common/event';
25
import { IThemeService } from 'vs/platform/theme/common/themeService';
26
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
27
import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme';
28
import { INotificationService } from 'vs/platform/notification/common/notification';
29
import { Dimension, EventType, addDisposableListener, trackFocus } from 'vs/base/browser/dom';
30
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
31 32 33 34
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';

const SidebarFocusContextId = 'sidebarFocus';
export const SidebarFocusContext = new RawContextKey<boolean>(SidebarFocusContextId, false);
E
Erich Gamma 已提交
35

36
export class SidebarPart extends CompositePart<Viewlet> {
E
Erich Gamma 已提交
37

B
Benjamin Pasero 已提交
38
	static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid';
E
Erich Gamma 已提交
39

40
	private sidebarFocusContextKey: IContextKey<boolean>;
E
Erich Gamma 已提交
41 42 43
	private blockOpeningViewlet: boolean;

	constructor(
44
		id: string,
45
		@INotificationService notificationService: INotificationService,
46 47 48 49
		@IStorageService storageService: IStorageService,
		@ITelemetryService telemetryService: ITelemetryService,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IPartService partService: IPartService,
50
		@IKeybindingService keybindingService: IKeybindingService,
51
		@IInstantiationService instantiationService: IInstantiationService,
52 53
		@IThemeService themeService: IThemeService,
		@IContextKeyService contextKeyService: IContextKeyService,
E
Erich Gamma 已提交
54
	) {
55
		super(
56
			notificationService,
57 58 59 60 61
			storageService,
			telemetryService,
			contextMenuService,
			partService,
			keybindingService,
62
			instantiationService,
63
			themeService,
64
			Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets),
65
			SidebarPart.activeViewletSettingsKey,
66
			Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).getDefaultViewletId(),
67 68
			'sideBar',
			'viewlet',
69
			SIDE_BAR_TITLE_FOREGROUND,
70
			id,
71
			{ hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 }
72
		);
73 74

		this.sidebarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService);
E
Erich Gamma 已提交
75 76
	}

B
Benjamin Pasero 已提交
77
	get onDidViewletOpen(): Event<IViewlet> {
78
		return mapEvent(this._onDidCompositeOpen.event, compositeEvent => <IViewlet>compositeEvent.composite);
79 80
	}

B
Benjamin Pasero 已提交
81
	get onDidViewletClose(): Event<IViewlet> {
B
Benjamin Pasero 已提交
82
		return this._onDidCompositeClose.event as Event<IViewlet>;
83 84
	}

85 86 87 88 89 90 91 92 93 94 95 96 97
	create(parent: HTMLElement): void {
		super.create(parent);

		const focusTracker = trackFocus(parent);

		focusTracker.onDidFocus(() => {
			this.sidebarFocusContextKey.set(true);
		});
		focusTracker.onDidBlur(() => {
			this.sidebarFocusContextKey.set(false);
		});
	}

B
Benjamin Pasero 已提交
98
	createTitleArea(parent: HTMLElement): HTMLElement {
99
		const titleArea = super.createTitleArea(parent);
B
Benjamin Pasero 已提交
100 101 102 103

		this._register(addDisposableListener(titleArea, EventType.CONTEXT_MENU, e => {
			this.onTitleAreaContextMenu(new StandardMouseEvent(e));
		}));
104 105 106 107

		return titleArea;
	}

B
Benjamin Pasero 已提交
108
	updateStyles(): void {
109 110 111
		super.updateStyles();

		// Part container
B
Benjamin Pasero 已提交
112
		const container = this.getContainer();
113

B
Benjamin Pasero 已提交
114 115
		container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND);
		container.style.color = this.getColor(SIDE_BAR_FOREGROUND);
116

117
		const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);
118
		const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT;
B
Benjamin Pasero 已提交
119 120 121 122 123 124
		container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null;
		container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null;
		container.style.borderRightColor = isPositionLeft ? borderColor : null;
		container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : null;
		container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : null;
		container.style.borderLeftColor = !isPositionLeft ? borderColor : null;
125 126
	}

127
	openViewlet(id: string, focus?: boolean): Viewlet {
E
Erich Gamma 已提交
128
		if (this.blockOpeningViewlet) {
129
			return null; // Workaround against a potential race condition
E
Erich Gamma 已提交
130 131 132
		}

		// First check if sidebar is hidden and show if so
133
		if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
E
Erich Gamma 已提交
134 135
			try {
				this.blockOpeningViewlet = true;
136
				this.partService.setSideBarHidden(false);
E
Erich Gamma 已提交
137 138 139 140 141
			} finally {
				this.blockOpeningViewlet = false;
			}
		}

142
		return this.openComposite(id, focus) as Viewlet;
E
Erich Gamma 已提交
143 144
	}

B
Benjamin Pasero 已提交
145
	getActiveViewlet(): IViewlet {
M
Maxime Quandalle 已提交
146
		return <IViewlet>this.getActiveComposite();
E
Erich Gamma 已提交
147 148
	}

B
Benjamin Pasero 已提交
149
	getLastActiveViewletId(): string {
150
		return this.getLastActiveCompositetId();
E
Erich Gamma 已提交
151 152
	}

153 154
	hideActiveViewlet(): void {
		this.hideActiveComposite();
E
Erich Gamma 已提交
155
	}
S
Sandeep Somavarapu 已提交
156

B
Benjamin Pasero 已提交
157
	layout(dimension: Dimension): Dimension[] {
I
isidor 已提交
158 159 160 161 162 163
		if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
			return [dimension];
		}

		return super.layout(dimension);
	}
164 165 166 167 168 169 170 171 172

	private onTitleAreaContextMenu(event: StandardMouseEvent): void {
		const activeViewlet = this.getActiveViewlet() as Viewlet;
		if (activeViewlet) {
			const contextMenuActions = activeViewlet ? activeViewlet.getContextMenuActions() : [];
			if (contextMenuActions.length) {
				const anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
				this.contextMenuService.showContextMenu({
					getAnchor: () => anchor,
173
					getActions: () => Promise.resolve(contextMenuActions),
174
					getActionItem: action => this.actionItemProvider(action as Action),
B
Benjamin Pasero 已提交
175
					actionRunner: activeViewlet.getActionRunner()
176 177 178 179
				});
			}
		}
	}
E
Erich Gamma 已提交
180 181
}

I
isidor 已提交
182
class FocusSideBarAction extends Action {
E
Erich Gamma 已提交
183

B
Benjamin Pasero 已提交
184 185
	static readonly ID = 'workbench.action.focusSideBar';
	static readonly LABEL = nls.localize('focusSideBar', "Focus into Side Bar");
E
Erich Gamma 已提交
186 187 188 189 190 191 192 193 194 195

	constructor(
		id: string,
		label: string,
		@IViewletService private viewletService: IViewletService,
		@IPartService private partService: IPartService
	) {
		super(id, label);
	}

B
Benjamin Pasero 已提交
196
	run(): TPromise<any> {
E
Erich Gamma 已提交
197 198

		// Show side bar
199
		if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
200
			return Promise.resolve(this.partService.setSideBarHidden(false));
E
Erich Gamma 已提交
201 202 203
		}

		// Focus into active viewlet
204 205 206
		let viewlet = this.viewletService.getActiveViewlet();
		if (viewlet) {
			viewlet.focus();
E
Erich Gamma 已提交
207
		}
208
		return Promise.resolve(true);
E
Erich Gamma 已提交
209 210 211
	}
}

B
Benjamin Pasero 已提交
212
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
E
Erich Gamma 已提交
213 214
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, {
	primary: KeyMod.CtrlCmd | KeyCode.KEY_0
215
}), 'View: Focus into Side Bar', nls.localize('viewCategory', "View"));