activitybarPart.ts 9.3 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';
E
Erich Gamma 已提交
27 28

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

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

P
Pine Wu 已提交
35 36
	private enabledExternalViewlets: string[];
	private registeredViewlets: { [viewletId: string]: ViewletDescriptor; };
37

P
Pine Wu 已提交
38 39
	private externalViewletIdToOpen: string;

P
Pine Wu 已提交
40
	private static ENABLED_EXTERNAL_VIEWLETS = 'workbench.activityBar.enabledExternalViewlets';
41

E
Erich Gamma 已提交
42
	constructor(
43 44
		id: string,
		@IViewletService private viewletService: IViewletService,
45
		@IKeybindingService private keybindingService: IKeybindingService,
I
isidor 已提交
46
		@IInstantiationService private instantiationService: IInstantiationService,
P
Pine Wu 已提交
47 48
		@IPartService private partService: IPartService,
		@IStorageService private storageService: IStorageService
E
Erich Gamma 已提交
49 50 51 52
	) {
		super(id);

		this.activityActionItems = {};
53
		this.compositeIdToActions = {};
E
Erich Gamma 已提交
54

P
Pine Wu 已提交
55 56 57
		const enabledExternalViewletsJson = this.storageService.get(ActivitybarPart.ENABLED_EXTERNAL_VIEWLETS);
		this.enabledExternalViewlets = enabledExternalViewletsJson ? JSON.parse(enabledExternalViewletsJson) : [];
		this.registeredViewlets = {};
58

E
Erich Gamma 已提交
59 60 61 62 63 64
		this.registerListeners();
	}

	private registerListeners(): void {

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

		// Deactivate viewlet action on close
68
		this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.onCompositeClosed(viewlet)));
69 70 71

		// Update activity bar on registering an external viewlet
		this.toUnbind.push(
72 73
			(<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets))
				.onDidRegisterExternalViewlets(descriptors => this.onDidRegisterExternalViewlets(descriptors))
74 75 76
		);
	}

P
Pine Wu 已提交
77 78 79 80 81 82 83 84
	private onDidRegisterExternalViewlets(descriptors: ViewletDescriptor[]): void {
		descriptors.forEach(d => {
			this.registeredViewlets[d.id] = d;
		});

		this.viewletSwitcherBar.push(this.getAllEnabledExternalViewlets().map(d => this.toAction(d)), { label: true, icon: true });
	}

85 86 87
	private onActiveCompositeChanged(composite: IComposite): void {
		if (this.compositeIdToActions[composite.getId()]) {
			this.compositeIdToActions[composite.getId()].activate();
E
Erich Gamma 已提交
88 89 90
		}
	}

91 92 93
	private onCompositeClosed(composite: IComposite): void {
		if (this.compositeIdToActions[composite.getId()]) {
			this.compositeIdToActions[composite.getId()].deactivate();
E
Erich Gamma 已提交
94 95 96
		}
	}

P
Pine Wu 已提交
97
	getRegisteredViewletsIsEnabled(): { [viewletId: string]: boolean } {
98
		const result = {};
P
Pine Wu 已提交
99 100 101
		for (let viewletId in this.registeredViewlets) {
			result[viewletId] = (this.enabledExternalViewlets.indexOf(viewletId) !== -1);
		}
102 103 104
		return result;
	}

P
Pine Wu 已提交
105
	toggleViewlet(viewletId: string): void {
P
Pine Wu 已提交
106 107 108 109 110 111 112 113
		const index = this.enabledExternalViewlets.indexOf(viewletId);
		if (index === -1) {
			this.enabledExternalViewlets.push(viewletId);
		} else {
			this.enabledExternalViewlets.splice(index, 1);
		}

		this.setEnabledExternalViewlets();
P
Pine Wu 已提交
114
		this.refreshViewletSwitcher();
115 116
	}

P
Pine Wu 已提交
117 118
	private setEnabledExternalViewlets(): void {
		this.storageService.store(ActivitybarPart.ENABLED_EXTERNAL_VIEWLETS, JSON.stringify(this.enabledExternalViewlets));
119 120
	}

121 122
	public showActivity(compositeId: string, badge: IBadge, clazz?: string): void {
		const action = this.compositeIdToActions[compositeId];
E
Erich Gamma 已提交
123 124 125 126 127 128 129 130
		if (action) {
			action.setBadge(badge);
			if (clazz) {
				action.class = clazz;
			}
		}
	}

131 132
	public clearActivity(compositeId: string): void {
		this.showActivity(compositeId, null);
E
Erich Gamma 已提交
133 134 135
	}

	public createContentArea(parent: Builder): Builder {
136 137
		const $el = $(parent);
		const $result = $('.content').appendTo($el);
E
Erich Gamma 已提交
138 139

		// Top Actionbar with action items for each viewlet action
P
Pine Wu 已提交
140
		this.createViewletSwitcher($result.clone().addClass('position-top'));
E
Erich Gamma 已提交
141 142 143 144

		return $result;
	}

I
isidor 已提交
145 146
	private createViewletSwitcher(div: Builder): void {
		this.viewletSwitcherBar = new ActionBar(div, {
147
			actionItemProvider: (action: Action) => this.activityActionItems[action.id],
148 149
			orientation: ActionsOrientation.VERTICAL,
			ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher")
E
Erich Gamma 已提交
150 151
		});

P
Pine Wu 已提交
152 153
		const allStockViewlets = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets)).getViewlets().filter(descriptor => !descriptor.isExternal);
		this.fillViewletSwitcher(allStockViewlets);
P
Pine Wu 已提交
154
	}
J
Joao Moreno 已提交
155

P
Pine Wu 已提交
156 157 158
	private refreshViewletSwitcher(): void {
		this.viewletSwitcherBar.clear();

P
Pine Wu 已提交
159
		const allStockViewlets = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets)).getViewlets().filter(descriptor => !descriptor.isExternal);
P
Pine Wu 已提交
160 161
		const allEnabledExternalViewlets = this.getAllEnabledExternalViewlets();
		this.fillViewletSwitcher(allStockViewlets.concat(allEnabledExternalViewlets));
P
Pine Wu 已提交
162 163 164
	}

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

P
Pine Wu 已提交
169 170 171 172 173 174 175 176 177 178 179
	// Get an ordered list of all enabled external viewlets
	private getAllEnabledExternalViewlets(): ViewletDescriptor[] {
		const externalViewletsToShow: ViewletDescriptor[] = [];
		this.enabledExternalViewlets.forEach(viewletId => {
			if (this.registeredViewlets[viewletId]) {
				externalViewletsToShow.push(this.registeredViewlets[viewletId]);
			}
		});
		return externalViewletsToShow;
	}

I
isidor 已提交
180
	private toAction(composite: ViewletDescriptor): ActivityAction {
181
		const action = this.instantiationService.createInstance(ViewletActivityAction, composite.id + '.activity-bar-action', composite);
P
Pine Wu 已提交
182 183 184
		// Store the viewletId of the external viewlet that is about to open.
		// Later retrieved by TreeExplorerViewlet, which wouldn't know its id until
		// its construction at runtime.
P
Pine Wu 已提交
185
		action.onOpenExternalViewlet((viewletId) => {
P
Pine Wu 已提交
186 187
			this.externalViewletIdToOpen = viewletId;
		});
I
isidor 已提交
188

189
		this.activityActionItems[action.id] = new ActivityActionItem(action, composite.name, this.getKeybindingLabel(composite.id));
I
isidor 已提交
190 191 192 193 194
		this.compositeIdToActions[composite.id] = action;

		return action;
	};

P
Pine Wu 已提交
195 196 197 198
	getExternalViewletIdToOpen(): string {
		return this.externalViewletIdToOpen;
	}

199 200 201 202 203 204 205 206 207
	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 已提交
208
	public dispose(): void {
I
isidor 已提交
209 210 211 212 213
		if (this.viewletSwitcherBar) {
			this.viewletSwitcherBar.dispose();
			this.viewletSwitcherBar = null;
		}

E
Erich Gamma 已提交
214 215 216 217
		super.dispose();
	}
}

218
class ViewletActivityAction extends ActivityAction {
E
Erich Gamma 已提交
219
	private static preventDoubleClickDelay = 300;
220
	private lastRun: number = 0;
E
Erich Gamma 已提交
221

P
Pine Wu 已提交
222 223
	private _onOpenExternalViewlet = new Emitter<string>();
	get onOpenExternalViewlet(): Event<string> { return this._onOpenExternalViewlet.event; };
P
Pine Wu 已提交
224

E
Erich Gamma 已提交
225
	constructor(
226 227
		id: string,
		private viewlet: ViewletDescriptor,
228 229
		@IViewletService private viewletService: IViewletService,
		@IPartService private partService: IPartService
E
Erich Gamma 已提交
230
	) {
231
		super(id, viewlet.name, viewlet.cssClass);
E
Erich Gamma 已提交
232 233
	}

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

236
		// prevent accident trigger on a doubleclick (to help nervous people)
237
		const now = Date.now();
238
		if (now - this.lastRun < ViewletActivityAction.preventDoubleClickDelay) {
A
Alex Dima 已提交
239
			return TPromise.as(true);
E
Erich Gamma 已提交
240
		}
241
		this.lastRun = now;
E
Erich Gamma 已提交
242

I
isidor 已提交
243 244
		const sideBarHidden = this.partService.isSideBarHidden();
		const activeViewlet = this.viewletService.getActiveViewlet();
E
Erich Gamma 已提交
245 246

		// Hide sidebar if selected viewlet already visible
247
		if (!sideBarHidden && activeViewlet && activeViewlet.getId() === this.viewlet.id) {
E
Erich Gamma 已提交
248
			this.partService.setSideBarHidden(true);
I
isidor 已提交
249
		} else {
P
Pine Wu 已提交
250 251 252
			if (this.viewlet.isExternal) {
				this._onOpenExternalViewlet.fire(this.viewlet.id);
			}
253
			this.viewletService.openViewlet(this.viewlet.id, true).done(null, errors.onUnexpectedError);
I
isidor 已提交
254
			this.activate();
E
Erich Gamma 已提交
255
		}
256 257

		return TPromise.as(true);
I
isidor 已提交
258 259
	}
}