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

'use strict';

B
Benjamin Pasero 已提交
8
import 'vs/css!./media/activitybarpart';
9
import nls = require('vs/nls');
J
Johannes Rieken 已提交
10
import { TPromise } from 'vs/base/common/winjs.base';
P
Pine Wu 已提交
11
import Event, { Emitter } from 'vs/base/common/event';
J
Johannes Rieken 已提交
12 13
import { Builder, $ } from 'vs/base/browser/builder';
import { Action } from 'vs/base/common/actions';
E
Erich Gamma 已提交
14
import errors = require('vs/base/common/errors');
J
Johannes Rieken 已提交
15 16 17 18 19 20 21 22 23 24 25
import { ActionsOrientation, ActionBar, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { Registry } from 'vs/platform/platform';
import { IComposite } from 'vs/workbench/common/composite';
import { ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
import { Part } from 'vs/workbench/browser/part';
import { ActivityAction, ActivityActionItem } from 'vs/workbench/browser/parts/activitybar/activityAction';
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
import { IActivityService, IBadge } from 'vs/workbench/services/activity/common/activityService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
P
Pine Wu 已提交
26
import { IStorageService } from 'vs/platform/storage/common/storage';
27
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
E
Erich Gamma 已提交
28 29

export class ActivitybarPart extends Part implements IActivityService {
30
	public _serviceBrand: any;
P
Pine Wu 已提交
31

I
isidor 已提交
32
	private viewletSwitcherBar: ActionBar;
E
Erich Gamma 已提交
33
	private activityActionItems: { [actionId: string]: IActionItem; };
34
	private compositeIdToActions: { [compositeId: string]: ActivityAction; };
E
Erich Gamma 已提交
35

P
Pine Wu 已提交
36 37
	private enabledExtViewlets: string[];
	private extViewlets: { [viewletId: string]: ViewletDescriptor; };
38

P
Pine Wu 已提交
39
	// Serves two purposes:
P
Pine Wu 已提交
40
	// 1. Expose the viewletId that will be assigned to an extension viewlet,
P
Pine Wu 已提交
41
	//    which wouldn't know its viewletId until construction time.
P
Pine Wu 已提交
42
	// 2. When workbench restores sidebar, if the last-opened viewlet is an extension viewlet,
P
Pine Wu 已提交
43
	//    it'll set this value and defer restoration until all extensions are loaded.
P
Pine Wu 已提交
44 45 46
	private _extViewletIdToOpen: string;
	public get extViewletIdToOpen() { return this._extViewletIdToOpen; };
	public set extViewletIdToOpen(viewletId: string) { this._extViewletIdToOpen = viewletId; };
P
Pine Wu 已提交
47

P
Pine Wu 已提交
48
	private static ENABLED_EXT_VIEWLETS = 'workbench.activityBar.enabledExtViewlets';
49

E
Erich Gamma 已提交
50
	constructor(
51 52
		id: string,
		@IViewletService private viewletService: IViewletService,
53
		@IKeybindingService private keybindingService: IKeybindingService,
I
isidor 已提交
54
		@IInstantiationService private instantiationService: IInstantiationService,
P
Pine Wu 已提交
55
		@IPartService private partService: IPartService,
56 57
		@IStorageService private storageService: IStorageService,
		@IExtensionService private extensionService: IExtensionService
E
Erich Gamma 已提交
58 59 60 61
	) {
		super(id);

		this.activityActionItems = {};
62
		this.compositeIdToActions = {};
E
Erich Gamma 已提交
63

P
Pine Wu 已提交
64 65 66
		const enabledExtViewletsJson = this.storageService.get(ActivitybarPart.ENABLED_EXT_VIEWLETS);
		this.enabledExtViewlets = enabledExtViewletsJson ? JSON.parse(enabledExtViewletsJson) : [];
		this.extViewlets = {};
67

E
Erich Gamma 已提交
68 69 70 71 72 73
		this.registerListeners();
	}

	private registerListeners(): void {

		// Activate viewlet action on opening of a viewlet
74
		this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.onActiveCompositeChanged(viewlet)));
E
Erich Gamma 已提交
75 76

		// Deactivate viewlet action on close
77
		this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.onCompositeClosed(viewlet)));
78

P
Pine Wu 已提交
79
		// Update activity bar on registering extension viewlets
80 81
		this.extensionService.onReady().then(() => {
			const viewlets = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets)).getViewlets();
P
Pine Wu 已提交
82
			this.onExtensionServiceReady(viewlets);
83
		});
84 85
	}

P
Pine Wu 已提交
86
	private onExtensionServiceReady(viewlets: ViewletDescriptor[]): void {
P
Pine Wu 已提交
87
		viewlets.forEach(v => {
P
Pine Wu 已提交
88
			if (v.isExtension) {
P
Pine Wu 已提交
89
				this.extViewlets[v.id] = v;
P
Pine Wu 已提交
90
			}
P
Pine Wu 已提交
91 92
		});

P
Pine Wu 已提交
93 94 95
		this.viewletSwitcherBar.push(this.getAllEnabledExtViewlets().map(d => this.toAction(d)), { label: true, icon: true });
		if (this._extViewletIdToOpen) {
			this.compositeIdToActions[this._extViewletIdToOpen].run().done();
P
Pine Wu 已提交
96
		}
P
Pine Wu 已提交
97 98
	}

99 100 101
	private onActiveCompositeChanged(composite: IComposite): void {
		if (this.compositeIdToActions[composite.getId()]) {
			this.compositeIdToActions[composite.getId()].activate();
E
Erich Gamma 已提交
102 103 104
		}
	}

105 106 107
	private onCompositeClosed(composite: IComposite): void {
		if (this.compositeIdToActions[composite.getId()]) {
			this.compositeIdToActions[composite.getId()].deactivate();
E
Erich Gamma 已提交
108 109 110
		}
	}

P
Pine Wu 已提交
111
	public getInfoForExtViewlets(): {
P
Pine Wu 已提交
112 113 114 115 116
		[viewletId: string]: {
			isEnabled: boolean;
			treeLabel: string;
		}
	} {
117
		const result = {};
P
Pine Wu 已提交
118
		for (let viewletId in this.extViewlets) {
P
Pine Wu 已提交
119
			result[viewletId] = {
P
Pine Wu 已提交
120 121
				isEnabled: (this.enabledExtViewlets.indexOf(viewletId) !== -1),
				treeLabel: this.extViewlets[viewletId].name
P
Pine Wu 已提交
122
			};
P
Pine Wu 已提交
123
		}
124 125 126
		return result;
	}

P
Pine Wu 已提交
127 128
	public toggleExtViewlet(viewletId: string): void {
		const index = this.enabledExtViewlets.indexOf(viewletId);
P
Pine Wu 已提交
129
		if (index === -1) {
P
Pine Wu 已提交
130
			this.enabledExtViewlets.push(viewletId);
P
Pine Wu 已提交
131
		} else {
P
Pine Wu 已提交
132
			this.enabledExtViewlets.splice(index, 1);
P
Pine Wu 已提交
133 134
		}

P
Pine Wu 已提交
135
		this.setEnabledExtViewlets();
P
Pine Wu 已提交
136
		this.refreshViewletSwitcher();
137 138
	}

P
Pine Wu 已提交
139 140
	private setEnabledExtViewlets(): void {
		this.storageService.store(ActivitybarPart.ENABLED_EXT_VIEWLETS, JSON.stringify(this.enabledExtViewlets));
141 142
	}

143 144
	public showActivity(compositeId: string, badge: IBadge, clazz?: string): void {
		const action = this.compositeIdToActions[compositeId];
E
Erich Gamma 已提交
145 146 147 148 149 150 151 152
		if (action) {
			action.setBadge(badge);
			if (clazz) {
				action.class = clazz;
			}
		}
	}

153 154
	public clearActivity(compositeId: string): void {
		this.showActivity(compositeId, null);
E
Erich Gamma 已提交
155 156 157
	}

	public createContentArea(parent: Builder): Builder {
158 159
		const $el = $(parent);
		const $result = $('.content').appendTo($el);
E
Erich Gamma 已提交
160 161

		// Top Actionbar with action items for each viewlet action
P
Pine Wu 已提交
162
		this.createViewletSwitcher($result.clone());
E
Erich Gamma 已提交
163 164 165 166

		return $result;
	}

I
isidor 已提交
167 168
	private createViewletSwitcher(div: Builder): void {
		this.viewletSwitcherBar = new ActionBar(div, {
169
			actionItemProvider: (action: Action) => this.activityActionItems[action.id],
170 171
			orientation: ActionsOrientation.VERTICAL,
			ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher")
E
Erich Gamma 已提交
172 173
		});

P
Pine Wu 已提交
174
		const allStockViewlets = this.getAllStockViewlets();
P
Pine Wu 已提交
175
		this.fillViewletSwitcher(allStockViewlets);
P
Pine Wu 已提交
176
	}
J
Joao Moreno 已提交
177

P
Pine Wu 已提交
178 179 180
	private refreshViewletSwitcher(): void {
		this.viewletSwitcherBar.clear();

P
Pine Wu 已提交
181
		const allStockViewlets = this.getAllStockViewlets();
P
Pine Wu 已提交
182 183
		const allEnabledExtViewlets = this.getAllEnabledExtViewlets();
		this.fillViewletSwitcher(allStockViewlets.concat(allEnabledExtViewlets));
P
Pine Wu 已提交
184 185 186
	}

	private fillViewletSwitcher(viewlets: ViewletDescriptor[]) {
P
Pine Wu 已提交
187
		const viewletActions = viewlets.map(v => this.toAction(v));
I
isidor 已提交
188 189
		this.viewletSwitcherBar.push(viewletActions, { label: true, icon: true });
	}
J
Joao Moreno 已提交
190

P
Pine Wu 已提交
191 192 193 194
	// Get an ordered list of all stock viewlets
	private getAllStockViewlets(): ViewletDescriptor[] {
		return (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets))
			.getViewlets()
P
Pine Wu 已提交
195
			.filter(viewlet => !viewlet.isExtension)
P
Pine Wu 已提交
196 197 198
			.sort((v1, v2) => v1.order - v2.order);
	}

P
Pine Wu 已提交
199 200 201 202 203
	// Get a list of all enabled extension viewlets, ordered by the enabling sequence
	private getAllEnabledExtViewlets(): ViewletDescriptor[] {
		return this.enabledExtViewlets
			.filter(viewletId => this.extViewlets[viewletId])
			.map(viewletId => this.extViewlets[viewletId]);
P
Pine Wu 已提交
204 205
	}

I
isidor 已提交
206
	private toAction(composite: ViewletDescriptor): ActivityAction {
207
		const action = this.instantiationService.createInstance(ViewletActivityAction, composite.id + '.activity-bar-action', composite);
P
Pine Wu 已提交
208
		// Store the viewletId of the extension viewlet that is about to open.
P
Pine Wu 已提交
209 210
		// Later retrieved by TreeExplorerViewlet, which wouldn't know its id until
		// its construction at runtime.
P
Pine Wu 已提交
211 212
		action.onOpenExtViewlet((viewletId) => {
			this._extViewletIdToOpen = viewletId;
P
Pine Wu 已提交
213
		});
I
isidor 已提交
214

215
		this.activityActionItems[action.id] = new ActivityActionItem(action, composite.name, this.getKeybindingLabel(composite.id));
I
isidor 已提交
216 217 218 219 220
		this.compositeIdToActions[composite.id] = action;

		return action;
	};

221 222 223 224 225 226 227 228 229
	private getKeybindingLabel(id: string): string {
		const keys = this.keybindingService.lookupKeybindings(id).map(k => this.keybindingService.getLabelFor(k));
		if (keys && keys.length) {
			return keys[0];
		}

		return null;
	}

E
Erich Gamma 已提交
230
	public dispose(): void {
I
isidor 已提交
231 232 233 234 235
		if (this.viewletSwitcherBar) {
			this.viewletSwitcherBar.dispose();
			this.viewletSwitcherBar = null;
		}

E
Erich Gamma 已提交
236 237 238 239
		super.dispose();
	}
}

240
class ViewletActivityAction extends ActivityAction {
E
Erich Gamma 已提交
241
	private static preventDoubleClickDelay = 300;
242
	private lastRun: number = 0;
E
Erich Gamma 已提交
243

P
Pine Wu 已提交
244 245
	private _onOpenExtViewlet = new Emitter<string>();
	public get onOpenExtViewlet(): Event<string> { return this._onOpenExtViewlet.event; };
P
Pine Wu 已提交
246

E
Erich Gamma 已提交
247
	constructor(
248 249
		id: string,
		private viewlet: ViewletDescriptor,
250 251
		@IViewletService private viewletService: IViewletService,
		@IPartService private partService: IPartService
E
Erich Gamma 已提交
252
	) {
253
		super(id, viewlet.name, viewlet.cssClass);
E
Erich Gamma 已提交
254 255
	}

A
Alex Dima 已提交
256
	public run(): TPromise<any> {
E
Erich Gamma 已提交
257

258
		// prevent accident trigger on a doubleclick (to help nervous people)
259
		const now = Date.now();
260
		if (now - this.lastRun < ViewletActivityAction.preventDoubleClickDelay) {
A
Alex Dima 已提交
261
			return TPromise.as(true);
E
Erich Gamma 已提交
262
		}
263
		this.lastRun = now;
E
Erich Gamma 已提交
264

I
isidor 已提交
265 266
		const sideBarHidden = this.partService.isSideBarHidden();
		const activeViewlet = this.viewletService.getActiveViewlet();
E
Erich Gamma 已提交
267 268

		// Hide sidebar if selected viewlet already visible
269
		if (!sideBarHidden && activeViewlet && activeViewlet.getId() === this.viewlet.id) {
E
Erich Gamma 已提交
270
			this.partService.setSideBarHidden(true);
I
isidor 已提交
271
		} else {
P
Pine Wu 已提交
272
			if (this.viewlet.isExtension) {
P
Pine Wu 已提交
273
				this._onOpenExtViewlet.fire(this.viewlet.id);
P
Pine Wu 已提交
274
			}
275
			this.viewletService.openViewlet(this.viewlet.id, true).done(null, errors.onUnexpectedError);
I
isidor 已提交
276
			this.activate();
E
Erich Gamma 已提交
277
		}
278 279

		return TPromise.as(true);
I
isidor 已提交
280 281
	}
}