activitybarPart.ts 9.7 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 23
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';
I
isidor 已提交
24
import {IPanelService} from 'vs/workbench/services/panel/common/panelService';
E
Erich Gamma 已提交
25 26 27 28
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 已提交
29
import {IMessageService} from 'vs/platform/message/common/message';
E
Erich Gamma 已提交
30
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
31
import {IKeybindingService} from 'vs/platform/keybinding/common/keybinding';
E
Erich Gamma 已提交
32 33

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

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

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

		this.registerListeners();
	}

	private registerListeners(): void {

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

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

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

78 79 80 81 82 83 84 85
	private onActivePanelChanged(panel: IPanel): void {
		this.updateActionBar();
		this.onActiveCompositeChanged(panel);
	}

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

89 90 91 92 93
	private onPanelClosed(panel: IPanel): void {
		this.updateActionBar();
		this.onCompositeClosed(panel);
	}

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

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

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

		// Top Actionbar with action items for each viewlet action
113
		this.createCompositeSwitcher($result.clone());
E
Erich Gamma 已提交
114 115 116 117

		return $result;
	}

118
	private createCompositeSwitcher(div: Builder): void {
E
Erich Gamma 已提交
119

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

J
Joao Moreno 已提交
128 129
		// Build Viewlet Actions in correct order
		const activeViewlet = this.viewletService.getActiveViewlet();
130
		const activePanel = this.panelService.getActivePanel();
I
isidor 已提交
131 132
		const allViewlets = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets)).getViewlets();
		const allPanels = (<PanelRegistry>Registry.as(PanelExtensions.Panels)).getPanels();
J
Joao Moreno 已提交
133

134 135 136
		const toAction = (composite: CompositeDescriptor<Viewlet | Panel>) => {
			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);
J
Joao Moreno 已提交
137 138

			let keybinding: string = null;
139
			const keys = this.keybindingService.lookupKeybindings(composite.id).map(k => this.keybindingService.getLabelFor(k));
J
Joao Moreno 已提交
140 141 142
			if (keys && keys.length) {
				keybinding = keys[0];
			}
E
Erich Gamma 已提交
143

144 145
			this.activityActionItems[action.id] = new ActivityActionItem(action, composite.name, keybinding);
			this.compositeIdToActions[composite.id] = action;
E
Erich Gamma 已提交
146

147 148
			// Mark active viewlet and panel action as active
			if (activeViewlet && activeViewlet.getId() === composite.id || activePanel && activePanel.getId() === composite.id) {
J
Joao Moreno 已提交
149
				action.activate();
E
Erich Gamma 已提交
150
			}
J
Joao Moreno 已提交
151 152 153

			return action;
		};
I
isidor 已提交
154 155
		this.viewletActions = allViewlets.sort((v1, v2) => v1.order - v2.order).map(toAction);
		this.showPanelAction = this.instantiationService.createInstance(ShowPanelAction);
156
		this.activityActionItems[this.showPanelAction.id] = new ActivityActionItem(this.showPanelAction);
I
isidor 已提交
157 158
		this.panelActions = allPanels.sort((p1, p2) => p1.order - p2.order).map(toAction);

J
Joao Moreno 已提交
159

I
isidor 已提交
160
		// Add both viewlet and panel actions to the switcher
I
isidor 已提交
161 162 163 164 165 166 167 168
		this.updateActionBar();
	}

	private updateActionBar(): void {
		this.compositeSwitcherBar.clear();
		const actions = this.partService.isPanelHidden() ? this.viewletActions.concat(this.showPanelAction) : this.viewletActions.concat(this.panelActions);

		this.compositeSwitcherBar.push(actions, { label: true, icon: true });
J
Joao Moreno 已提交
169 170
	}

E
Erich Gamma 已提交
171
	public dispose(): void {
172 173 174
		if (this.compositeSwitcherBar) {
			this.compositeSwitcherBar.dispose();
			this.compositeSwitcherBar = null;
E
Erich Gamma 已提交
175 176
		}

177 178
		if (this.showPanelAction) {
			this.showPanelAction.dispose();
E
Erich Gamma 已提交
179 180 181 182 183 184
		}

		super.dispose();
	}
}

I
isidor 已提交
185
abstract class CompositeActivityAction<T extends Composite> extends ActivityAction {
E
Erich Gamma 已提交
186
	private static preventDoubleClickDelay = 300;
187
	private lastRun: number = 0;
E
Erich Gamma 已提交
188

I
isidor 已提交
189
	protected composite: CompositeDescriptor<T>;
E
Erich Gamma 已提交
190 191

	constructor(
I
isidor 已提交
192 193 194 195
		id: string, composite: CompositeDescriptor<T>,
		@IViewletService protected viewletService: IViewletService,
		@IPanelService protected panelService: IPanelService,
		@IPartService protected partService: IPartService
E
Erich Gamma 已提交
196
	) {
I
isidor 已提交
197
		super(id, composite.name, composite.cssClass);
E
Erich Gamma 已提交
198

I
isidor 已提交
199
		this.composite = composite;
E
Erich Gamma 已提交
200 201
	}

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

204
		// prevent accident trigger on a doubleclick (to help nervous people)
205
		const now = Date.now();
I
isidor 已提交
206
		if (now - this.lastRun < CompositeActivityAction.preventDoubleClickDelay) {
A
Alex Dima 已提交
207
			return TPromise.as(true);
E
Erich Gamma 已提交
208
		}
209
		this.lastRun = now;
E
Erich Gamma 已提交
210

I
isidor 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223
		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 已提交
224 225

		// Hide sidebar if selected viewlet already visible
I
isidor 已提交
226
		if (!sideBarHidden && activeViewlet && activeViewlet.getId() === this.composite.id) {
E
Erich Gamma 已提交
227
			this.partService.setSideBarHidden(true);
I
isidor 已提交
228 229 230
		} else {
			this.viewletService.openViewlet(this.composite.id, true).done(null, errors.onUnexpectedError);
			this.activate();
E
Erich Gamma 已提交
231
		}
I
isidor 已提交
232 233 234 235 236 237
	}
}

class PanelActivityAction extends CompositeActivityAction<Panel> {

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

I
isidor 已提交
239 240 241 242 243 244 245 246
		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 已提交
247 248 249
			this.activate();
		}
	}
I
isidor 已提交
250
}
I
isidor 已提交
251 252 253 254 255 256 257 258 259 260 261 262 263

class ShowPanelAction extends ActivityAction {
	private static ID = 'workbench.action.panel.show';

	constructor(@IPartService private partService: IPartService) {
		super(ShowPanelAction.ID, nls.localize('showPanel', "Show Panel"), 'panel');
		}

	public run(): TPromise<any> {
		this.partService.setPanelHidden(false);
		return TPromise.as(null);
	}
}