viewsViewlet.ts 22.1 KB
Newer Older
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
9 10
import * as DOM from 'vs/base/browser/dom';
import { $, Dimension, Builder } from 'vs/base/browser/builder';
11
import { Scope } from 'vs/workbench/common/memento';
12 13
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IAction, IActionRunner } from 'vs/base/common/actions';
14
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
J
Joao Moreno 已提交
15
import { firstIndex } from 'vs/base/common/arrays';
16
import { DelayedDragHandler } from 'vs/base/browser/dnd';
S
Sandeep Somavarapu 已提交
17
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
18 19
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
S
Sandeep Somavarapu 已提交
20
import { ViewsRegistry, ViewLocation, IViewDescriptor, IViewsViewlet } from 'vs/workbench/common/views';
21 22 23 24
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
25
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
26
import { IContextKeyService, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey';
27
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
28 29
import { PanelViewlet, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IPanelOptions } from 'vs/base/browser/ui/splitview/panelview';
S
Sandeep Somavarapu 已提交
30 31 32
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
33

34
export interface IViewOptions extends IPanelOptions {
S
#27823  
Sandeep Somavarapu 已提交
35 36 37 38
	id: string;
	name: string;
	actionRunner: IActionRunner;
}
39

40
export abstract class ViewsViewletPanel extends ViewletPanel {
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	private _isVisible: boolean;

	readonly id: string;
	readonly name: string;

	constructor(
		options: IViewOptions,
		protected keybindingService: IKeybindingService,
		protected contextMenuService: IContextMenuService
	) {
		super(options.name, options, keybindingService, contextMenuService);

		this.id = options.id;
		this.name = options.name;
		this._expanded = options.expanded;
	}

	setVisible(visible: boolean): TPromise<void> {
		if (this._isVisible !== visible) {
			this._isVisible = visible;
		}

		return TPromise.wrap(null);
	}

	isVisible(): boolean {
		return this._isVisible;
	}

	getActions(): IAction[] {
		return [];
	}

	getSecondaryActions(): IAction[] {
		return [];
	}

	getActionItem(action: IAction): IActionItem {
		return null;
	}

	getActionsContext(): any {
		return undefined;
	}

	getOptimalWidth(): number {
		return 0;
	}

	create(): TPromise<void> {
		return TPromise.as(null);
	}

	shutdown(): void {
		// Subclass to implement
	}

}

export abstract class TreeViewsViewletPanel extends ViewsViewletPanel {

S
#27823  
Sandeep Somavarapu 已提交
103
	readonly id: string;
104
	readonly name: string;
105
	protected treeContainer: HTMLElement;
J
Joao Moreno 已提交
106

J
Joao Moreno 已提交
107
	protected tree: WorkbenchTree;
108 109 110 111
	protected isDisposed: boolean;
	private dragHandler: DelayedDragHandler;

	constructor(
112
		options: IViewOptions,
113
		protected keybindingService: IKeybindingService,
S
#27823  
Sandeep Somavarapu 已提交
114
		protected contextMenuService: IContextMenuService
115
	) {
116
		super(options, keybindingService, contextMenuService);
117

S
#27823  
Sandeep Somavarapu 已提交
118
		this.id = options.id;
119
		this.name = options.name;
J
Joao Moreno 已提交
120
		this._expanded = options.expanded;
121 122
	}

123 124 125
	setExpanded(expanded: boolean): void {
		this.updateTreeVisibility(this.tree, expanded);
		super.setExpanded(expanded);
126 127
	}

128 129
	protected renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
130 131

		// Expand on drag over
132
		this.dragHandler = new DelayedDragHandler(container, () => this.setExpanded(true));
133 134 135
	}

	protected renderViewTree(container: HTMLElement): HTMLElement {
S
#27823  
Sandeep Somavarapu 已提交
136 137 138
		const treeContainer = document.createElement('div');
		container.appendChild(treeContainer);
		return treeContainer;
139 140
	}

J
Joao Moreno 已提交
141
	getViewer(): WorkbenchTree {
142 143 144
		return this.tree;
	}

J
Joao 已提交
145
	setVisible(visible: boolean): TPromise<void> {
146 147 148
		if (this.isVisible() !== visible) {
			return super.setVisible(visible)
				.then(() => this.updateTreeVisibility(this.tree, visible && this.isExpanded()));
S
#27823  
Sandeep Somavarapu 已提交
149
		}
150

151
		return TPromise.wrap(null);
152 153
	}

154 155
	focus(): void {
		super.focus();
S
#27823  
Sandeep Somavarapu 已提交
156
		this.focusTree();
157 158 159
	}

	protected reveal(element: any, relativeTop?: number): TPromise<void> {
S
#27823  
Sandeep Somavarapu 已提交
160 161 162 163 164
		if (!this.tree) {
			return TPromise.as(null); // return early if viewlet has not yet been created
		}

		return this.tree.reveal(element, relativeTop);
165 166
	}

J
Joao 已提交
167
	layoutBody(size: number): void {
S
Sandeep Somavarapu 已提交
168 169 170 171
		if (this.tree) {
			this.treeContainer.style.height = size + 'px';
			this.tree.layout(size);
		}
172 173
	}

J
Joao 已提交
174
	getActions(): IAction[] {
175 176 177
		return [];
	}

J
Joao 已提交
178
	getSecondaryActions(): IAction[] {
179 180 181
		return [];
	}

J
Joao 已提交
182
	getActionItem(action: IAction): IActionItem {
183 184 185
		return null;
	}

J
Joao 已提交
186
	getActionsContext(): any {
J
Joao Moreno 已提交
187 188 189
		return undefined;
	}

J
Joao 已提交
190
	getOptimalWidth(): number {
191 192 193
		return 0;
	}

194 195 196 197 198 199 200 201
	create(): TPromise<void> {
		return TPromise.as(null);
	}

	shutdown(): void {
		// Subclass to implement
	}

J
Joao 已提交
202
	dispose(): void {
203 204
		this.isDisposed = true;
		this.treeContainer = null;
205 206 207 208

		if (this.tree) {
			this.tree.dispose();
		}
209 210 211 212 213 214 215 216

		if (this.dragHandler) {
			this.dragHandler.dispose();
		}

		super.dispose();
	}

S
Sandeep Somavarapu 已提交
217
	protected updateTreeVisibility(tree: WorkbenchTree, isVisible: boolean): void {
S
#27823  
Sandeep Somavarapu 已提交
218 219 220
		if (!tree) {
			return;
		}
221

S
#27823  
Sandeep Somavarapu 已提交
222 223 224 225 226
		if (isVisible) {
			$(tree.getHTMLElement()).show();
		} else {
			$(tree.getHTMLElement()).hide(); // make sure the tree goes out of the tabindex world by hiding it
		}
227

S
#27823  
Sandeep Somavarapu 已提交
228 229 230 231 232
		if (isVisible) {
			tree.onVisible();
		} else {
			tree.onHidden();
		}
233 234
	}

S
#27823  
Sandeep Somavarapu 已提交
235 236 237 238
	private focusTree(): void {
		if (!this.tree) {
			return; // return early if viewlet has not yet been created
		}
239

S
#27823  
Sandeep Somavarapu 已提交
240 241 242 243 244
		// Make sure the current selected element is revealed
		const selection = this.tree.getSelection();
		if (selection.length > 0) {
			this.reveal(selection[0], 0.5).done(null, errors.onUnexpectedError);
		}
245

S
#27823  
Sandeep Somavarapu 已提交
246 247 248
		// Pass Focus to Viewer
		this.tree.DOMFocus();
	}
249 250
}

S
#27823  
Sandeep Somavarapu 已提交
251
export interface IViewletViewOptions extends IViewOptions {
B
Benjamin Pasero 已提交
252
	viewletSettings: object;
253 254
}

J
Joao Moreno 已提交
255
export interface IViewState {
256
	collapsed: boolean;
J
Joao 已提交
257
	size: number | undefined;
258
	isHidden: boolean;
259
	order: number;
260 261
}

S
Sandeep Somavarapu 已提交
262
export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
263

S
Sandeep Somavarapu 已提交
264
	private viewHeaderContextMenuListeners: IDisposable[] = [];
B
Benjamin Pasero 已提交
265
	private viewletSettings: object;
266
	private readonly viewsContextKeys: Set<string> = new Set<string>();
267
	private viewsViewletPanels: ViewsViewletPanel[] = [];
J
Joao Moreno 已提交
268
	private didLayout = false;
S
Sandeep Somavarapu 已提交
269
	private dimension: Dimension;
J
Joao Moreno 已提交
270
	protected viewsStates: Map<string, IViewState> = new Map<string, IViewState>();
S
Sandeep Somavarapu 已提交
271
	private areExtensionsReady: boolean = false;
272 273 274 275

	constructor(
		id: string,
		private location: ViewLocation,
276
		private showHeaderInTitleWhenSingleView: boolean,
277 278 279 280
		@ITelemetryService telemetryService: ITelemetryService,
		@IStorageService protected storageService: IStorageService,
		@IInstantiationService protected instantiationService: IInstantiationService,
		@IThemeService themeService: IThemeService,
281
		@IContextKeyService protected contextKeyService: IContextKeyService,
J
Joao Moreno 已提交
282
		@IContextMenuService protected contextMenuService: IContextMenuService,
283
		@IExtensionService protected extensionService: IExtensionService
284
	) {
J
Joao Moreno 已提交
285
		super(id, { showHeaderInTitleWhenSingleView, dnd: true }, telemetryService, themeService);
286

287
		this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE);
288 289
	}

290
	async create(parent: Builder): TPromise<void> {
S
Sandeep Somavarapu 已提交
291
		await super.create(parent);
292

293
		this._register(this.onDidSashChange(() => this.snapshotViewsStates()));
294 295
		this._register(ViewsRegistry.onViewsRegistered(this.onViewsRegistered, this));
		this._register(ViewsRegistry.onViewsDeregistered(this.onViewsDeregistered, this));
J
Johannes Rieken 已提交
296
		this._register(this.contextKeyService.onDidChangeContext(this.onContextChanged, this));
297

298
		// Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609
299
		this.extensionService.whenInstalledExtensionsRegistered().then(() => {
300
			this.areExtensionsReady = true;
S
Sandeep Somavarapu 已提交
301
			this.updateHeaders();
302
		});
303

304
		this.onViewsRegistered(ViewsRegistry.getViews(this.location));
305
		this.focus();
S
Sandeep Somavarapu 已提交
306
	}
307

J
Joao 已提交
308
	getContextMenuActions(): IAction[] {
S
Sandeep Somavarapu 已提交
309
		return this.getViewDescriptorsFromRegistry(true)
S
Sandeep Somavarapu 已提交
310
			.filter(viewDescriptor => viewDescriptor.canToggleVisibility && this.contextKeyService.contextMatchesRules(viewDescriptor.when))
S
Sandeep Somavarapu 已提交
311 312 313 314
			.map(viewDescriptor => (<IAction>{
				id: `${viewDescriptor.id}.toggleVisibility`,
				label: viewDescriptor.name,
				checked: this.isCurrentlyVisible(viewDescriptor),
S
Sandeep Somavarapu 已提交
315
				enabled: true,
S
Sandeep Somavarapu 已提交
316 317
				run: () => this.toggleViewVisibility(viewDescriptor.id)
			}));
318 319
	}

J
Joao 已提交
320
	setVisible(visible: boolean): TPromise<void> {
321
		return super.setVisible(visible)
322
			.then(() => TPromise.join(this.viewsViewletPanels.filter(view => view.isVisible() !== visible)
323
				.map((view) => view.setVisible(visible))))
324 325 326
			.then(() => void 0);
	}

327
	openView(id: string): void {
S
Sandeep Somavarapu 已提交
328 329 330 331 332
		this.focus();
		const view = this.getView(id);
		if (view) {
			view.setExpanded(true);
			view.focus();
333 334
		} else {
			this.toggleViewVisibility(id);
S
Sandeep Somavarapu 已提交
335 336 337
		}
	}

338 339
	layout(dimension: Dimension): void {
		super.layout(dimension);
S
Sandeep Somavarapu 已提交
340
		this.dimension = dimension;
341 342 343
		if (this.didLayout) {
			this.snapshotViewsStates();
		} else {
J
Joao Moreno 已提交
344
			this.didLayout = true;
345
			this.resizePanels();
J
Joao Moreno 已提交
346 347
		}

348 349
	}

J
Joao 已提交
350
	getOptimalWidth(): number {
351
		const additionalMargin = 16;
352
		const optimalWidth = Math.max(...this.viewsViewletPanels.map(view => view.getOptimalWidth() || 0));
353 354 355
		return optimalWidth + additionalMargin;
	}

J
Joao 已提交
356
	shutdown(): void {
357
		this.viewsViewletPanels.forEach((view) => view.shutdown());
358 359 360
		super.shutdown();
	}

S
Sandeep Somavarapu 已提交
361
	toggleViewVisibility(id: string): void {
S
Sandeep Somavarapu 已提交
362
		let viewState = this.viewsStates.get(id);
S
Sandeep Somavarapu 已提交
363
		if (!viewState) {
J
Joao Moreno 已提交
364 365 366
			return;
		}

367
		viewState.isHidden = !!this.getView(id);
S
Sandeep Somavarapu 已提交
368 369 370
		this.updateViews()
			.then(() => {
				if (!viewState.isHidden) {
371
					this.openView(id);
S
Sandeep Somavarapu 已提交
372 373 374 375
				} else {
					this.focus();
				}
			});
376 377
	}

S
Sandeep Somavarapu 已提交
378
	private onViewsRegistered(views: IViewDescriptor[]): void {
379 380 381 382 383 384 385 386
		this.viewsContextKeys.clear();
		for (const viewDescriptor of this.getViewDescriptorsFromRegistry()) {
			if (viewDescriptor.when) {
				for (const key of viewDescriptor.when.keys()) {
					this.viewsContextKeys.add(key);
				}
			}
		}
387

S
Sandeep Somavarapu 已提交
388
		this.updateViews();
389 390
	}

S
Sandeep Somavarapu 已提交
391 392
	private onViewsDeregistered(views: IViewDescriptor[]): void {
		this.updateViews(views);
393 394
	}

J
Johannes Rieken 已提交
395 396
	private onContextChanged(event: IContextKeyChangeEvent): void {
		if (event.affectsSome(this.viewsContextKeys)) {
397 398
			this.updateViews();
		}
S
#27823  
Sandeep Somavarapu 已提交
399 400
	}

401 402 403 404 405
	protected updateViews(unregisteredViews: IViewDescriptor[] = []): TPromise<ViewsViewletPanel[]> {
		const registeredViews = this.getViewDescriptorsFromRegistry();
		const [visible, toAdd, toRemove] = registeredViews.reduce<[IViewDescriptor[], IViewDescriptor[], IViewDescriptor[]]>((result, viewDescriptor) => {
			const isCurrentlyVisible = this.isCurrentlyVisible(viewDescriptor);
			const canBeVisible = this.canBeVisible(viewDescriptor);
406

407 408 409
			if (canBeVisible) {
				result[0].push(viewDescriptor);
			}
410

411 412 413
			if (!isCurrentlyVisible && canBeVisible) {
				result[1].push(viewDescriptor);
			}
414

415 416 417
			if (isCurrentlyVisible && !canBeVisible) {
				result[2].push(viewDescriptor);
			}
418

419
			return result;
420

S
Sandeep Somavarapu 已提交
421 422 423
		}, [[], [], []]);

		toRemove.push(...unregisteredViews.filter(viewDescriptor => this.isCurrentlyVisible(viewDescriptor)));
424

425
		const toCreate: ViewsViewletPanel[] = [];
426

427
		if (toAdd.length || toRemove.length) {
428

429
			this.snapshotViewsStates();
S
#27823  
Sandeep Somavarapu 已提交
430

431 432 433 434
			if (toRemove.length) {
				for (const viewDescriptor of toRemove) {
					let view = this.getView(viewDescriptor.id);
					this.removePanel(view);
435
					this.viewsViewletPanels.splice(this.viewsViewletPanels.indexOf(view), 1);
S
Sandeep Somavarapu 已提交
436
				}
437
			}
S
Sandeep Somavarapu 已提交
438

439 440 441 442 443 444 445 446
			for (const viewDescriptor of toAdd) {
				let viewState = this.viewsStates.get(viewDescriptor.id);
				let index = visible.indexOf(viewDescriptor);
				const view = this.createView(viewDescriptor,
					{
						id: viewDescriptor.id,
						name: viewDescriptor.name,
						actionRunner: this.getActionRunner(),
S
Sandeep Somavarapu 已提交
447
						expanded: !(viewState ? viewState.collapsed : viewDescriptor.collapsed),
448 449 450 451
						viewletSettings: this.viewletSettings
					});
				toCreate.push(view);

S
Sandeep Somavarapu 已提交
452
				const size = (viewState && viewState.size) || 200;
453
				this.addPanel(view, size, index);
454
				this.viewsViewletPanels.splice(index, 0, view);
455
			}
456 457 458

			return TPromise.join(toCreate.map(view => view.create()))
				.then(() => this.onViewsUpdated())
459 460 461 462
				.then(() => {
					this.resizePanels(toCreate);
					return toCreate;
				});
463
		}
464

465
		return TPromise.as([]);
466 467
	}

468
	private resizePanels(panels: ViewsViewletPanel[] = this.viewsViewletPanels): void {
J
Joao Moreno 已提交
469
		if (!this.didLayout) {
470
			// Do not do anything if layout has not happened yet
J
Joao Moreno 已提交
471 472 473
			return;
		}

S
Sandeep Somavarapu 已提交
474
		let initialSizes;
475
		for (const panel of panels) {
J
Joao Moreno 已提交
476
			const viewState = this.viewsStates.get(panel.id);
S
Sandeep Somavarapu 已提交
477 478 479 480 481 482
			if (viewState && viewState.size) {
				this.resizePanel(panel, viewState.size);
			} else {
				initialSizes = initialSizes ? initialSizes : this.computeInitialSizes();
				this.resizePanel(panel, initialSizes[panel.id] || 200);
			}
J
Joao Moreno 已提交
483
		}
484 485

		this.snapshotViewsStates();
J
Joao Moreno 已提交
486 487
	}

S
Sandeep Somavarapu 已提交
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
	private computeInitialSizes(): { [id: string]: number } {
		let sizes = {};
		if (this.dimension) {
			let totalWeight = 0;
			const allViewDescriptors = this.getViewDescriptorsFromRegistry();
			const viewDescriptors: IViewDescriptor[] = [];
			for (const panel of this.viewsViewletPanels) {
				const viewDescriptor = allViewDescriptors.filter(viewDescriptor => viewDescriptor.id === panel.id)[0];
				totalWeight = totalWeight + (viewDescriptor.weight || 20);
				viewDescriptors.push(viewDescriptor);
			}
			for (const viewDescriptor of viewDescriptors) {
				sizes[viewDescriptor.id] = this.dimension.height * (viewDescriptor.weight || 20) / totalWeight;
			}
		}
		return sizes;
	}

J
Joao Moreno 已提交
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	movePanel(from: ViewletPanel, to: ViewletPanel): void {
		const fromIndex = firstIndex(this.viewsViewletPanels, panel => panel === from);
		const toIndex = firstIndex(this.viewsViewletPanels, panel => panel === to);

		if (fromIndex < 0 || fromIndex >= this.viewsViewletPanels.length) {
			return;
		}

		if (toIndex < 0 || toIndex >= this.viewsViewletPanels.length) {
			return;
		}

		super.movePanel(from, to);

		const [panel] = this.viewsViewletPanels.splice(fromIndex, 1);
		this.viewsViewletPanels.splice(toIndex, 0, panel);

		for (let order = 0; order < this.viewsViewletPanels.length; order++) {
J
Joao Moreno 已提交
524 525 526 527 528 529 530
			const view = this.viewsStates.get(this.viewsViewletPanels[order].id);

			if (!view) {
				continue;
			}

			view.order = order;
J
Joao Moreno 已提交
531 532 533
		}
	}

534 535 536 537
	private isCurrentlyVisible(viewDescriptor: IViewDescriptor): boolean {
		return !!this.getView(viewDescriptor.id);
	}

538
	private canBeVisible(viewDescriptor: IViewDescriptor): boolean {
539
		const viewstate = this.viewsStates.get(viewDescriptor.id);
S
Sandeep Somavarapu 已提交
540
		if (viewDescriptor.canToggleVisibility && viewstate && viewstate.isHidden) {
541 542
			return false;
		}
543 544 545
		return this.contextKeyService.contextMatchesRules(viewDescriptor.when);
	}

S
#27823  
Sandeep Somavarapu 已提交
546
	private onViewsUpdated(): TPromise<void> {
S
Sandeep Somavarapu 已提交
547
		this.viewHeaderContextMenuListeners = dispose(this.viewHeaderContextMenuListeners);
548

549
		for (const viewDescriptor of this.getViewDescriptorsFromRegistry()) {
S
Sandeep Somavarapu 已提交
550
			const view = this.getView(viewDescriptor.id);
551

S
Sandeep Somavarapu 已提交
552
			if (view) {
J
Joao Moreno 已提交
553
				this.viewHeaderContextMenuListeners.push(DOM.addDisposableListener(view.draggableElement, DOM.EventType.CONTEXT_MENU, e => {
554 555 556 557 558 559
					e.stopPropagation();
					e.preventDefault();
					if (viewDescriptor.canToggleVisibility) {
						this.onContextMenu(new StandardMouseEvent(e), view);
					}
				}));
S
Sandeep Somavarapu 已提交
560 561 562
			}
		}

S
#27823  
Sandeep Somavarapu 已提交
563 564 565
		return this.setVisible(this.isVisible());
	}

S
Sandeep Somavarapu 已提交
566 567 568 569 570 571 572
	private updateHeaders(): void {
		if (this.viewsViewletPanels.length) {
			this.updateTitleArea();
			this.updateViewHeaders();
		}
	}

573
	private onContextMenu(event: StandardMouseEvent, view: ViewsViewletPanel): void {
S
Sandeep Somavarapu 已提交
574 575 576 577 578 579
		event.stopPropagation();
		event.preventDefault();

		let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
		this.contextMenuService.showContextMenu({
			getAnchor: () => anchor,
S
Sandeep Somavarapu 已提交
580
			getActions: () => TPromise.as([<IAction>{
S
Sandeep Somavarapu 已提交
581
				id: `${view.id}.removeView`,
S
Sandeep Somavarapu 已提交
582
				label: nls.localize('hideView', "Hide from Side Bar"),
S
Sandeep Somavarapu 已提交
583 584 585 586 587 588
				enabled: true,
				run: () => this.toggleViewVisibility(view.id)
			}]),
		});
	}

589
	protected isSingleView(): boolean {
590 591 592
		if (!this.showHeaderInTitleWhenSingleView) {
			return false;
		}
S
Sandeep Somavarapu 已提交
593 594 595 596
		if (this.getViewDescriptorsFromRegistry().length === 0) {
			return false;
		}
		if (this.length > 1) {
S
Sandeep Somavarapu 已提交
597 598
			return false;
		}
S
Sandeep Somavarapu 已提交
599 600

		if (ViewLocation.getContributedViewLocation(this.location.id)) {
S
Sandeep Somavarapu 已提交
601
			let visibleViewsCount = 0;
S
Sandeep Somavarapu 已提交
602 603 604 605 606 607 608 609 610 611
			if (this.areExtensionsReady) {
				visibleViewsCount = this.getViewDescriptorsFromRegistry().reduce((visibleViewsCount, v) => visibleViewsCount + (this.canBeVisible(v) ? 1 : 0), 0);
			} else {
				// Check in cache so that view do not jump. See #29609
				this.viewsStates.forEach((viewState, id) => {
					if (!viewState.isHidden) {
						visibleViewsCount++;
					}
				});
			}
S
Sandeep Somavarapu 已提交
612
			return visibleViewsCount === 1;
S
Sandeep Somavarapu 已提交
613
		}
S
Sandeep Somavarapu 已提交
614

S
Sandeep Somavarapu 已提交
615
		return super.isSingleView();
S
Sandeep Somavarapu 已提交
616 617
	}

J
Joao Moreno 已提交
618
	protected getViewDescriptorsFromRegistry(defaultOrder: boolean = false): IViewDescriptor[] {
619 620
		return ViewsRegistry.getViews(this.location)
			.sort((a, b) => {
621 622 623 624
				const viewStateA = this.viewsStates.get(a.id);
				const viewStateB = this.viewsStates.get(b.id);
				const orderA = !defaultOrder && viewStateA ? viewStateA.order : a.order;
				const orderB = !defaultOrder && viewStateB ? viewStateB.order : b.order;
625 626

				if (orderB === void 0 || orderB === null) {
627
					return -1;
S
#27823  
Sandeep Somavarapu 已提交
628
				}
629
				if (orderA === void 0 || orderA === null) {
630
					return 1;
S
#27823  
Sandeep Somavarapu 已提交
631
				}
632

633
				return orderA - orderB;
634
			});
635 636
	}

637 638
	protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewsViewletPanel {
		return this.instantiationService.createInstance(viewDescriptor.ctor, options);
639 640
	}

641 642
	protected get views(): ViewsViewletPanel[] {
		return this.viewsViewletPanels;
S
Sandeep Somavarapu 已提交
643 644
	}

645 646
	protected getView(id: string): ViewsViewletPanel {
		return this.viewsViewletPanels.filter(view => view.id === id)[0];
647
	}
S
#27823  
Sandeep Somavarapu 已提交
648

649 650 651 652 653 654 655
	private snapshotViewsStates(): void {
		for (const view of this.viewsViewletPanels) {
			const currentState = this.viewsStates.get(view.id);
			if (currentState && !this.didLayout) {
				// Do not update to new state if the layout has not happened yet
				return;
			}
656

S
Sandeep Somavarapu 已提交
657 658 659
			const collapsed = !view.isExpanded();
			const order = this.viewsViewletPanels.indexOf(view);
			const panelSize = this.getPanelSize(view);
660
			if (currentState) {
S
Sandeep Somavarapu 已提交
661 662 663 664 665 666 667 668 669 670
				currentState.collapsed = collapsed;
				currentState.size = collapsed ? currentState.size : panelSize;
				currentState.order = order;
			} else {
				this.viewsStates.set(view.id, {
					collapsed,
					size: this.didLayout ? panelSize : void 0,
					isHidden: false,
					order,
				});
671 672
			}
		}
673
	}
J
Joao Moreno 已提交
674 675 676 677 678 679 680 681 682 683 684 685 686
}

export class PersistentViewsViewlet extends ViewsViewlet {

	constructor(
		id: string,
		location: ViewLocation,
		private viewletStateStorageId: string,
		showHeaderInTitleWhenSingleView: boolean,
		@ITelemetryService telemetryService: ITelemetryService,
		@IStorageService storageService: IStorageService,
		@IInstantiationService instantiationService: IInstantiationService,
		@IThemeService themeService: IThemeService,
J
Joao 已提交
687
		@IWorkspaceContextService protected contextService: IWorkspaceContextService,
J
Joao Moreno 已提交
688 689 690 691
		@IContextKeyService contextKeyService: IContextKeyService,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IExtensionService extensionService: IExtensionService
	) {
J
Joao 已提交
692
		super(id, location, showHeaderInTitleWhenSingleView, telemetryService, storageService, instantiationService, themeService, contextKeyService, contextMenuService, extensionService);
S
Sandeep Somavarapu 已提交
693 694 695
	}

	create(parent: Builder): TPromise<void> {
J
Joao Moreno 已提交
696
		this.loadViewsStates();
S
Sandeep Somavarapu 已提交
697
		return super.create(parent);
J
Joao Moreno 已提交
698 699 700 701 702 703 704
	}

	shutdown(): void {
		this.saveViewsStates();
		super.shutdown();
	}

S
Sandeep Somavarapu 已提交
705
	protected saveViewsStates(): void {
J
Joao Moreno 已提交
706 707 708 709
		const viewsStates = {};
		const registeredViewDescriptors = this.getViewDescriptorsFromRegistry();
		this.viewsStates.forEach((viewState, id) => {
			const view = this.getView(id);
J
Joao Moreno 已提交
710

J
Joao Moreno 已提交
711
			if (view) {
S
Sandeep Somavarapu 已提交
712 713 714 715 716 717
				viewsStates[id] = {
					collapsed: !view.isExpanded(),
					size: this.getPanelSize(view),
					isHidden: false,
					order: viewState.order
				};
J
Joao Moreno 已提交
718 719 720 721 722 723 724 725
			} else {
				const viewDescriptor = registeredViewDescriptors.filter(v => v.id === id)[0];
				if (viewDescriptor) {
					viewsStates[id] = viewState;
				}
			}
		});

726
		this.storageService.store(this.viewletStateStorageId, JSON.stringify(viewsStates), this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL);
J
Joao Moreno 已提交
727 728
	}

S
Sandeep Somavarapu 已提交
729
	protected loadViewsStates(): void {
730
		const viewsStates = JSON.parse(this.storageService.get(this.viewletStateStorageId, this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL, '{}'));
J
Joao Moreno 已提交
731 732
		Object.keys(viewsStates).forEach(id => this.viewsStates.set(id, <IViewState>viewsStates[id]));
	}
J
Johannes Rieken 已提交
733
}
S
Sandeep Somavarapu 已提交
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758

export class FileIconThemableWorkbenchTree extends WorkbenchTree {

	constructor(
		container: HTMLElement,
		configuration: ITreeConfiguration,
		options: ITreeOptions,
		@IContextKeyService contextKeyService: IContextKeyService,
		@IListService listService: IListService,
		@IThemeService themeService: IWorkbenchThemeService
	) {
		super(container, configuration, { ...options, ...{ showTwistie: false, twistiePixels: 12 } }, contextKeyService, listService, themeService);

		DOM.addClass(container, 'file-icon-themable-tree');
		DOM.addClass(container, 'show-file-icons');

		const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
			DOM.toggleClass(container, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
			DOM.toggleClass(container, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true);
		};

		this.disposables.push(themeService.onDidFileIconThemeChange(onFileIconThemeChange));
		onFileIconThemeChange(themeService.getFileIconTheme());
	}
}