activitybarPart.ts 10.2 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');
A
Alex Dima 已提交
10
import {TPromise} from 'vs/base/common/winjs.base';
E
Erich Gamma 已提交
11
import {Builder, $} from 'vs/base/browser/builder';
A
tslint  
Alex Dima 已提交
12
import {Action} from 'vs/base/common/actions';
E
Erich Gamma 已提交
13 14 15
import errors = require('vs/base/common/errors');
import {ActionsOrientation, ActionBar, IActionItem} from 'vs/base/browser/ui/actionbar/actionbar';
import {Registry} from 'vs/platform/platform';
16 17
import {IComposite} from 'vs/workbench/common/composite';
import {IPanel} from 'vs/workbench/common/panel';
I
isidor 已提交
18 19
import {ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions, Viewlet} from 'vs/workbench/browser/viewlet';
import {CompositeDescriptor, Composite} from 'vs/workbench/browser/composite';
I
isidor 已提交
20
import {Panel, PanelRegistry, Extensions as PanelExtensions} from 'vs/workbench/browser/panel';
E
Erich Gamma 已提交
21 22
import {Part} from 'vs/workbench/browser/part';
import {ActivityAction, ActivityActionItem} from 'vs/workbench/browser/parts/activitybar/activityAction';
23
import {TogglePanelAction} from 'vs/workbench/browser/parts/panel/panelPart';
E
Erich Gamma 已提交
24
import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService';
I
isidor 已提交
25
import {IPanelService} from 'vs/workbench/services/panel/common/panelService';
E
Erich Gamma 已提交
26 27 28 29
import {IActivityService, IBadge} from 'vs/workbench/services/activity/common/activityService';
import {IPartService} from 'vs/workbench/services/part/common/partService';
import {IContextMenuService} from 'vs/platform/contextview/browser/contextView';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
A
tslint  
Alex Dima 已提交
30
import {IMessageService} from 'vs/platform/message/common/message';
E
Erich Gamma 已提交
31
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
32
import {IKeybindingService} from 'vs/platform/keybinding/common/keybinding';
E
Erich Gamma 已提交
33 34

export class ActivitybarPart extends Part implements IActivityService {
35
	public _serviceBrand: any;
I
isidor 已提交
36 37
	private viewletSwitcherBar: ActionBar;
	private panelSwitcherBar: ActionBar;
E
Erich Gamma 已提交
38
	private activityActionItems: { [actionId: string]: IActionItem; };
39
	private compositeIdToActions: { [compositeId: string]: ActivityAction; };
40
	private panelActions: ActivityAction[];
41
	private showPanelAction: TogglePanelAction;
E
Erich Gamma 已提交
42 43

	constructor(
44 45
		id: string,
		@IViewletService private viewletService: IViewletService,
46
		@IPanelService private panelService: IPanelService,
47 48 49
		@IMessageService private messageService: IMessageService,
		@ITelemetryService private telemetryService: ITelemetryService,
		@IContextMenuService private contextMenuService: IContextMenuService,
50
		@IKeybindingService private keybindingService: IKeybindingService,
I
isidor 已提交
51 52
		@IInstantiationService private instantiationService: IInstantiationService,
		@IPartService private partService: IPartService
E
Erich Gamma 已提交
53 54 55 56
	) {
		super(id);

		this.activityActionItems = {};
57
		this.compositeIdToActions = {};
E
Erich Gamma 已提交
58 59 60 61 62 63 64

		this.registerListeners();
	}

	private registerListeners(): void {

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

		// Deactivate viewlet action on close
69 70
		this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.onCompositeClosed(viewlet)));
		this.toUnbind.push(this.panelService.onDidPanelClose(panel => this.onPanelClosed(panel)));
E
Erich Gamma 已提交
71 72
	}

73 74 75
	private onActiveCompositeChanged(composite: IComposite): void {
		if (this.compositeIdToActions[composite.getId()]) {
			this.compositeIdToActions[composite.getId()].activate();
E
Erich Gamma 已提交
76 77 78
		}
	}

79
	private onActivePanelChanged(panel: IPanel): void {
I
isidor 已提交
80
		this.updatePanelSwitcher();
81 82 83 84 85 86
		this.onActiveCompositeChanged(panel);
	}

	private onCompositeClosed(composite: IComposite): void {
		if (this.compositeIdToActions[composite.getId()]) {
			this.compositeIdToActions[composite.getId()].deactivate();
E
Erich Gamma 已提交
87 88 89
		}
	}

90
	private onPanelClosed(panel: IPanel): void {
I
isidor 已提交
91
		this.updatePanelSwitcher();
92 93 94
		this.onCompositeClosed(panel);
	}

95 96
	public showActivity(compositeId: string, badge: IBadge, clazz?: string): void {
		const action = this.compositeIdToActions[compositeId];
E
Erich Gamma 已提交
97 98 99 100 101 102 103 104
		if (action) {
			action.setBadge(badge);
			if (clazz) {
				action.class = clazz;
			}
		}
	}

105 106
	public clearActivity(compositeId: string): void {
		this.showActivity(compositeId, null);
E
Erich Gamma 已提交
107 108 109
	}

	public createContentArea(parent: Builder): Builder {
110 111
		const $el = $(parent);
		const $result = $('.content').appendTo($el);
E
Erich Gamma 已提交
112 113

		// Top Actionbar with action items for each viewlet action
I
isidor 已提交
114 115
		this.createViewletSwitcher($result.clone());
		this.createPanelSwitcher($result.clone());
E
Erich Gamma 已提交
116 117 118 119

		return $result;
	}

I
isidor 已提交
120
	private createViewletSwitcher(div: Builder): void {
E
Erich Gamma 已提交
121

122
		// Composite switcher is on top
I
isidor 已提交
123
		this.viewletSwitcherBar = new ActionBar(div, {
124
			actionItemProvider: (action: Action) => this.activityActionItems[action.id],
125 126
			orientation: ActionsOrientation.VERTICAL,
			ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher")
E
Erich Gamma 已提交
127
		});
I
isidor 已提交
128
		this.viewletSwitcherBar.getContainer().addClass('position-top');
E
Erich Gamma 已提交
129

J
Joao Moreno 已提交
130
		// Build Viewlet Actions in correct order
I
isidor 已提交
131
		const allViewlets = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets)).getViewlets();
I
isidor 已提交
132
		const viewletActions = allViewlets.sort((v1, v2) => v1.order - v2.order).map(viewlet => this.toAction(viewlet));
J
Joao Moreno 已提交
133

I
isidor 已提交
134 135
		this.viewletSwitcherBar.push(viewletActions, { label: true, icon: true });
	}
J
Joao Moreno 已提交
136

I
isidor 已提交
137
	private createPanelSwitcher(div: Builder): void {
E
Erich Gamma 已提交
138

I
isidor 已提交
139 140 141 142 143 144 145
		// Composite switcher is on top
		this.panelSwitcherBar = new ActionBar(div, {
			actionItemProvider: (action: Action) => this.activityActionItems[action.id],
			orientation: ActionsOrientation.VERTICAL,
			ariaLabel: nls.localize('activityBarPanelAriaLabel', "Active Panel Switcher")
		});
		this.panelSwitcherBar.getContainer().addClass('position-bottom');
E
Erich Gamma 已提交
146

I
isidor 已提交
147 148 149
		// Build Viewlet Actions in correct order

		const allPanels = (<PanelRegistry>Registry.as(PanelExtensions.Panels)).getPanels();
J
Joao Moreno 已提交
150

151
		this.showPanelAction = this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL);
152
		this.activityActionItems[this.showPanelAction.id] = new ActivityActionItem(this.showPanelAction);
I
isidor 已提交
153
		this.panelActions = allPanels.sort((p1, p2) => p1.order - p2.order).map(panel => this.toAction(panel));
J
Joao Moreno 已提交
154

I
isidor 已提交
155
		// Add both viewlet and panel actions to the switcher
I
isidor 已提交
156
		this.updatePanelSwitcher();
I
isidor 已提交
157 158
	}

I
isidor 已提交
159 160
	private updatePanelSwitcher(): void {
		this.panelSwitcherBar.clear();
161 162 163 164
		const actions:ActivityAction[] = [this.showPanelAction];
		if (!this.partService.isPanelHidden()) {
			actions.push(...this.panelActions);
		}
I
isidor 已提交
165

I
isidor 已提交
166
		this.panelSwitcherBar.push(actions, { label: true, icon: true });
J
Joao Moreno 已提交
167 168
	}

I
isidor 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	private toAction(composite: CompositeDescriptor<Viewlet | Panel>): ActivityAction {
		const activeViewlet = this.viewletService.getActiveViewlet();
		const activePanel = this.panelService.getActivePanel();
		const action = composite instanceof ViewletDescriptor ? this.instantiationService.createInstance(ViewletActivityAction, composite.id + '.activity-bar-action', composite)
			: this.instantiationService.createInstance(PanelActivityAction, composite.id + '.activity-bar-action', composite);

		let keybinding: string = null;
		const keys = this.keybindingService.lookupKeybindings(composite.id).map(k => this.keybindingService.getLabelFor(k));
		if (keys && keys.length) {
			keybinding = keys[0];
		}

		this.activityActionItems[action.id] = new ActivityActionItem(action, composite.name, keybinding);
		this.compositeIdToActions[composite.id] = action;

		// Mark active viewlet and panel action as active
		if (activeViewlet && activeViewlet.getId() === composite.id || activePanel && activePanel.getId() === composite.id) {
			action.activate();
		}

		return action;
	};

E
Erich Gamma 已提交
192
	public dispose(): void {
I
isidor 已提交
193 194 195 196 197 198 199 200
		if (this.viewletSwitcherBar) {
			this.viewletSwitcherBar.dispose();
			this.viewletSwitcherBar = null;
		}

		if (this.panelSwitcherBar) {
			this.panelSwitcherBar.dispose();
			this.panelSwitcherBar = null;
E
Erich Gamma 已提交
201 202
		}

203 204
		if (this.showPanelAction) {
			this.showPanelAction.dispose();
E
Erich Gamma 已提交
205 206 207 208 209 210
		}

		super.dispose();
	}
}

I
isidor 已提交
211
abstract class CompositeActivityAction<T extends Composite> extends ActivityAction {
E
Erich Gamma 已提交
212
	private static preventDoubleClickDelay = 300;
213
	private lastRun: number = 0;
E
Erich Gamma 已提交
214

I
isidor 已提交
215
	protected composite: CompositeDescriptor<T>;
E
Erich Gamma 已提交
216 217

	constructor(
I
isidor 已提交
218 219 220 221
		id: string, composite: CompositeDescriptor<T>,
		@IViewletService protected viewletService: IViewletService,
		@IPanelService protected panelService: IPanelService,
		@IPartService protected partService: IPartService
E
Erich Gamma 已提交
222
	) {
I
isidor 已提交
223
		super(id, composite.name, composite.cssClass);
E
Erich Gamma 已提交
224

I
isidor 已提交
225
		this.composite = composite;
E
Erich Gamma 已提交
226 227
	}

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

230
		// prevent accident trigger on a doubleclick (to help nervous people)
231
		const now = Date.now();
I
isidor 已提交
232
		if (now - this.lastRun < CompositeActivityAction.preventDoubleClickDelay) {
A
Alex Dima 已提交
233
			return TPromise.as(true);
E
Erich Gamma 已提交
234
		}
235
		this.lastRun = now;
E
Erich Gamma 已提交
236

I
isidor 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249
		this.toggleComposite();

		return TPromise.as(true);
	}

	protected abstract toggleComposite(): void;
}

class ViewletActivityAction extends CompositeActivityAction<Viewlet> {

	protected toggleComposite(): void {
		const sideBarHidden = this.partService.isSideBarHidden();
		const activeViewlet = this.viewletService.getActiveViewlet();
E
Erich Gamma 已提交
250 251

		// Hide sidebar if selected viewlet already visible
I
isidor 已提交
252
		if (!sideBarHidden && activeViewlet && activeViewlet.getId() === this.composite.id) {
E
Erich Gamma 已提交
253
			this.partService.setSideBarHidden(true);
I
isidor 已提交
254 255 256
		} else {
			this.viewletService.openViewlet(this.composite.id, true).done(null, errors.onUnexpectedError);
			this.activate();
E
Erich Gamma 已提交
257
		}
I
isidor 已提交
258 259 260 261 262 263
	}
}

class PanelActivityAction extends CompositeActivityAction<Panel> {

	protected toggleComposite(): void {
E
Erich Gamma 已提交
264

I
isidor 已提交
265 266 267 268 269 270 271 272
		const panelHidden = this.partService.isPanelHidden();
		const activePanel = this.panelService.getActivePanel();

		// Hide panel if selected panel already visible
		if (!panelHidden && activePanel && activePanel.getId() === this.composite.id) {
			this.partService.setPanelHidden(true);
		} else {
			this.panelService.openPanel(this.composite.id, true).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
273 274 275
			this.activate();
		}
	}
I
isidor 已提交
276
}