editorGroupsControl.ts 79.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';

8
import 'vs/css!./media/editorGroupsControl';
9
import * as arrays from 'vs/base/common/arrays';
M
Matt Bierner 已提交
10
import { Event, Emitter } from 'vs/base/common/event';
J
Johannes Rieken 已提交
11
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
12
import * as types from 'vs/base/common/types';
13
import { Builder, $ } from 'vs/base/browser/builder';
B
Benjamin Pasero 已提交
14
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
J
Johannes Rieken 已提交
15 16
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
17 18
import * as DOM from 'vs/base/browser/dom';
import * as errors from 'vs/base/common/errors';
J
Johannes Rieken 已提交
19 20 21
import { RunOnceScheduler } from 'vs/base/common/async';
import { isMacintosh } from 'vs/base/common/platform';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
22
import { Position } from 'vs/platform/editor/common/editor';
23
import { IEditorGroupService, IEditorTabOptions, GroupArrangement, GroupOrientation } from 'vs/workbench/services/group/common/groupService';
J
Johannes Rieken 已提交
24 25 26
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
27
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
28
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
I
isidor 已提交
29
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier, EditorInput } from 'vs/workbench/common/editor';
B
Benjamin Pasero 已提交
30
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
31
import { IThemeService } from 'vs/platform/theme/common/themeService';
32
import { editorBackground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
33
import { Themable, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BORDER } from 'vs/workbench/common/theme';
34
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
M
Matt Bierner 已提交
35
import { IDisposable } from 'vs/base/common/lifecycle';
36
import { ResourcesDropHandler, LocalSelectionTransfer, DraggedEditorIdentifier } from 'vs/workbench/browser/dnd';
B
Benjamin Pasero 已提交
37
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
S
SrTobi 已提交
38
import { IPartService } from 'vs/workbench/services/part/common/partService';
E
Erich Gamma 已提交
39

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
export class NoOpTitleAreaControl {
	private container = document.createElement('div');

	hasContext(): boolean { return false; }
	allowDragging(element: HTMLElement): boolean { return false; }
	getContainer(): HTMLElement { return this.container; }
	setDragged(dragged: boolean): void { }
	create(parent: HTMLElement): void { }
	refresh(instant?: boolean): void { }
	update(instant?: boolean): void { }
	updateEditorActionsToolbar(): void { }
	layout(dimension: DOM.Dimension): void { }
	dispose(): void { }
	setContext(group: IEditorGroup): void { }
}
I
isidor 已提交
55

E
Erich Gamma 已提交
56 57
export enum Rochade {
	NONE,
58 59 60
	TWO_TO_ONE,
	THREE_TO_TWO,
	TWO_AND_THREE_TO_ONE
E
Erich Gamma 已提交
61 62
}

B
Benjamin Pasero 已提交
63 64 65 66 67 68
export enum ProgressState {
	INFINITE,
	DONE,
	STOP
}

69
export interface IEditorGroupsControl {
70

71
	onGroupFocusChanged: Event<void>;
72

B
Benjamin Pasero 已提交
73
	show(editor: BaseEditor, position: Position, preserveActive: boolean, ratio?: number[]): void;
74
	hide(editor: BaseEditor, position: Position, layoutAndRochade: boolean): Rochade;
75 76 77 78 79 80 81 82 83

	setActive(editor: BaseEditor): void;

	getActiveEditor(): BaseEditor;
	getActivePosition(): Position;

	move(from: Position, to: Position): void;

	isDragging(): boolean;
B
Benjamin Pasero 已提交
84

85
	getInstantiationService(position: Position): IInstantiationService;
86
	getProgressBar(position: Position): ProgressBar;
B
Benjamin Pasero 已提交
87
	updateProgress(position: Position, state: ProgressState): void;
B
Benjamin Pasero 已提交
88
	updateTitleAreas(refreshActive?: boolean): void;
B
Benjamin Pasero 已提交
89

90
	layout(dimension: DOM.Dimension): void;
91 92
	layout(position: Position): void;

93
	arrangeGroups(arrangement: GroupArrangement): void;
94

95 96 97
	setGroupOrientation(orientation: GroupOrientation): void;
	getGroupOrientation(): GroupOrientation;

B
Benjamin Pasero 已提交
98
	resizeGroup(position: Position, groupSizeChange: number): void;
99

B
Benjamin Pasero 已提交
100
	getRatio(): number[];
101

102 103 104
	dispose(): void;
}

B
Benjamin Pasero 已提交
105
interface CenteredEditorLayoutData {
106 107
	leftMarginRatio: number;
	size: number;
108 109
}

110 111
export const POSITIONS = [Position.ONE, Position.TWO, Position.THREE];

E
Erich Gamma 已提交
112 113 114
/**
 * Helper class to manage multiple side by side editors for the editor part.
 */
B
Benjamin Pasero 已提交
115
export class EditorGroupsControl extends Themable implements IEditorGroupsControl, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider {
B
Benjamin Pasero 已提交
116

117
	private static readonly CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY = 'workbench.centerededitorlayout.data';
118

119 120 121
	private static readonly TITLE_AREA_CONTROL_KEY = '__titleAreaControl';
	private static readonly PROGRESS_BAR_CONTROL_KEY = '__progressBar';
	private static readonly INSTANTIATION_SERVICE_KEY = '__instantiationService';
122

123
	private static readonly GOLDEN_RATIO = 0.61;
124 125
	private static readonly MIN_EDITOR_WIDTH = 170;
	private static readonly MIN_EDITOR_HEIGHT = 70;
B
Benjamin Pasero 已提交
126

127
	private static readonly EDITOR_TITLE_HEIGHT = 35;
B
Benjamin Pasero 已提交
128

129
	private static readonly CENTERED_EDITOR_MIN_MARGIN = 10;
S
SrTobi 已提交
130

131 132
	private static readonly SNAP_TO_MINIMIZED_THRESHOLD_WIDTH = 50;
	private static readonly SNAP_TO_MINIMIZED_THRESHOLD_HEIGHT = 20;
E
Erich Gamma 已提交
133

B
Benjamin Pasero 已提交
134 135
	private stacks: IEditorStacksModel;

136
	private parent: HTMLElement;
137
	private dimension: DOM.Dimension;
E
Erich Gamma 已提交
138 139
	private dragging: boolean;

140
	private layoutVertically: boolean;
141

142
	private tabOptions: IEditorTabOptions;
143

B
Benjamin Pasero 已提交
144
	private silos: Builder[];
B
Benjamin Pasero 已提交
145 146
	private silosSize: number[];
	private silosInitialRatio: number[];
147
	private silosMinimized: boolean[];
E
Erich Gamma 已提交
148

B
Benjamin Pasero 已提交
149 150
	private sashOne: Sash;
	private startSiloOneSize: number;
E
Erich Gamma 已提交
151

B
Benjamin Pasero 已提交
152 153
	private sashTwo: Sash;
	private startSiloThreeSize: number;
E
Erich Gamma 已提交
154

155 156 157 158 159 160 161 162
	// if the centered editor layout is activated, the editor inside of silo ONE is centered
	// the silo will then contain:
	// [left margin]|[editor]|[right margin]
	// - The size of the editor is defined by centeredEditorSize
	// - The position is defined by the ratio centeredEditorLeftMarginRatio = left-margin/(left-margin + editor + right-margin).
	// - The two sashes can be used to control the size and position of the editor inside of the silo.
	// - In order to seperate the two sashes from the sashes that control the size of bordering widgets
	//   CENTERED_EDITOR_MIN_MARGIN is forced as a minimum size for the two margins.
S
SrTobi 已提交
163 164 165
	private centeredEditorActive: boolean;
	private centeredEditorSashLeft: Sash;
	private centeredEditorSashRight: Sash;
S
SrTobi 已提交
166
	private centeredEditorPreferredSize: number;
167 168 169
	private centeredEditorLeftMarginRatio: number;
	private centeredEditorDragStartPosition: number;
	private centeredEditorDragStartSize: number;
S
SrTobi 已提交
170

E
Erich Gamma 已提交
171 172 173 174 175
	private visibleEditors: BaseEditor[];

	private lastActiveEditor: BaseEditor;
	private lastActivePosition: Position;

176
	private visibleEditorFocusTrackerDisposable: IDisposable[];
E
Erich Gamma 已提交
177

M
Matt Bierner 已提交
178
	private readonly _onGroupFocusChanged: Emitter<void>;
179

B
Benjamin Pasero 已提交
180 181 182
	private onStacksChangeScheduler: RunOnceScheduler;
	private stacksChangedBuffer: IStacksModelChangeEvent[];

183 184
	private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();

E
Erich Gamma 已提交
185
	constructor(
186
		parent: HTMLElement,
187
		groupOrientation: GroupOrientation,
E
Erich Gamma 已提交
188
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
189
		@IEditorGroupService private editorGroupService: IEditorGroupService,
S
SrTobi 已提交
190
		@IPartService private partService: IPartService,
S
SrTobi 已提交
191
		@IStorageService private storageService: IStorageService,
192
		@IContextKeyService private contextKeyService: IContextKeyService,
193
		@IExtensionService private extensionService: IExtensionService,
J
Joao Moreno 已提交
194
		@IInstantiationService private instantiationService: IInstantiationService,
B
Benjamin Pasero 已提交
195 196
		@IThemeService themeService: IThemeService,
		@ITelemetryService private telemetryService: ITelemetryService
E
Erich Gamma 已提交
197
	) {
B
Benjamin Pasero 已提交
198
		super(themeService);
199

B
Benjamin Pasero 已提交
200 201
		this.stacks = editorGroupService.getStacksModel();

E
Erich Gamma 已提交
202
		this.parent = parent;
203
		this.dimension = new DOM.Dimension(0, 0);
E
Erich Gamma 已提交
204

B
Benjamin Pasero 已提交
205
		this.silos = [];
B
Benjamin Pasero 已提交
206
		this.silosSize = [];
207
		this.silosMinimized = [];
E
Erich Gamma 已提交
208 209

		this.visibleEditors = [];
210
		this.visibleEditorFocusTrackerDisposable = [];
E
Erich Gamma 已提交
211

212
		this._onGroupFocusChanged = new Emitter<void>();
213
		this.toUnbind.push(this._onGroupFocusChanged);
214

B
Benjamin Pasero 已提交
215
		this.onStacksChangeScheduler = new RunOnceScheduler(() => this.handleStacksChanged(), 0);
216
		this.toUnbind.push(this.onStacksChangeScheduler);
B
Benjamin Pasero 已提交
217
		this.stacksChangedBuffer = [];
E
Erich Gamma 已提交
218

I
isidor 已提交
219
		this.updateTabOptions(this.editorGroupService.getTabOptions());
220

B
Benjamin Pasero 已提交
221
		const editorGroupOrientation = groupOrientation || 'vertical';
222 223
		this.layoutVertically = (editorGroupOrientation !== 'horizontal');

224 225
		this.create();

B
Benjamin Pasero 已提交
226
		this.registerListeners();
E
Erich Gamma 已提交
227 228
	}

B
Benjamin Pasero 已提交
229 230 231 232 233 234 235 236
	private get totalSize(): number {
		if (!this.dimension || !this.dimension.width || !this.dimension.height) {
			return 0;
		}

		return this.layoutVertically ? this.dimension.width : this.dimension.height;
	}

B
Benjamin Pasero 已提交
237
	private get minSize(): number {
238
		return this.layoutVertically ? EditorGroupsControl.MIN_EDITOR_WIDTH : EditorGroupsControl.MIN_EDITOR_HEIGHT;
B
Benjamin Pasero 已提交
239 240
	}

241 242 243 244
	private isSiloMinimized(position: number): boolean {
		return this.silosSize[position] === this.minSize && this.silosMinimized[position];
	}

B
Benjamin Pasero 已提交
245
	private enableMinimizedState(): void {
246 247 248
		POSITIONS.forEach(p => this.silosMinimized[p] = this.silosSize[p] === this.minSize);
	}

B
Benjamin Pasero 已提交
249 250 251 252 253 254 255 256
	private updateMinimizedState(): void {
		POSITIONS.forEach(p => {
			if (this.silosSize[p] !== this.minSize) {
				this.silosMinimized[p] = false; // release silo from minimized state if it was sized large enough
			}
		});
	}

B
Benjamin Pasero 已提交
257
	private get snapToMinimizeThresholdSize(): number {
258
		return this.layoutVertically ? EditorGroupsControl.SNAP_TO_MINIMIZED_THRESHOLD_WIDTH : EditorGroupsControl.SNAP_TO_MINIMIZED_THRESHOLD_HEIGHT;
B
Benjamin Pasero 已提交
259 260
	}

B
Benjamin Pasero 已提交
261
	private registerListeners(): void {
262 263
		this.toUnbind.push(this.stacks.onModelChanged(e => this.onStacksChanged(e)));
		this.toUnbind.push(this.editorGroupService.onTabOptionsChanged(options => this.updateTabOptions(options, true)));
264
		this.toUnbind.push(this.extensionService.onDidRegisterExtensions(() => this.onDidRegisterExtensions()));
265 266
	}

267
	private updateTabOptions(tabOptions: IEditorTabOptions, refresh?: boolean): void {
268
		const tabCloseButton = this.tabOptions ? this.tabOptions.tabCloseButton : 'right';
269
		const tabSizing = this.tabOptions ? this.tabOptions.tabSizing : 'fit';
B
Benjamin Pasero 已提交
270
		const iconTheme = this.tabOptions ? this.tabOptions.iconTheme : 'vs-seti';
271

I
isidor 已提交
272
		this.tabOptions = tabOptions;
273 274 275 276 277 278

		if (!refresh) {
			return; // return early if no refresh is needed
		}

		// Editor Containers
279
		POSITIONS.forEach(position => {
280
			const titleControl = this.getTitleAreaControl(position);
281

282
			// Title Container
283
			const titleContainer = $(titleControl.getContainer());
I
isidor 已提交
284
			if (this.tabOptions.showTabs) {
285 286 287 288 289
				titleContainer.addClass('tabs');
			} else {
				titleContainer.removeClass('tabs');
			}

290
			const showingIcons = titleContainer.hasClass('show-file-icons');
I
isidor 已提交
291
			if (this.tabOptions.showIcons) {
292 293 294 295 296
				titleContainer.addClass('show-file-icons');
			} else {
				titleContainer.removeClass('show-file-icons');
			}

297
			// Title Control
298
			if (titleControl) {
299
				const usingTabs = (titleControl instanceof NoOpTitleAreaControl);
300

301
				// Recreate title when tabs change
I
isidor 已提交
302
				if (usingTabs !== this.tabOptions.showTabs) {
303
					titleControl.dispose();
304
					titleContainer.empty();
305
					this.createTitleControl(this.stacks.groupAt(position), this.silos[position], titleContainer, this.getInstantiationService(position));
306
					this.layoutTitleControl(position);
307
				}
308

309
				// Refresh title when layout options change
B
Benjamin Pasero 已提交
310 311 312 313 314 315
				else if (
					showingIcons !== this.tabOptions.showIcons ||
					tabCloseButton !== this.tabOptions.tabCloseButton ||
					tabSizing !== this.tabOptions.tabSizing ||
					iconTheme !== this.tabOptions.iconTheme
				) {
316
					titleControl.refresh();
317
				}
318
			}
319 320 321

			// Update Styles
			this.updateStyles();
322
		});
B
Benjamin Pasero 已提交
323
	}
324

325
	private onDidRegisterExtensions(): void {
326 327

		// Up to date title areas
328
		POSITIONS.forEach(position => this.getTitleAreaControl(position).update());
329 330
	}

331
	private onStacksChanged(e: IStacksModelChangeEvent): void {
B
Benjamin Pasero 已提交
332 333 334 335 336 337 338 339 340 341 342 343
		this.stacksChangedBuffer.push(e);
		this.onStacksChangeScheduler.schedule();
	}

	private handleStacksChanged(): void {

		// Read and reset buffer of events
		const buffer = this.stacksChangedBuffer;
		this.stacksChangedBuffer = [];

		// Up to date context for all title controls
		POSITIONS.forEach(position => {
344 345
			const titleAreaControl = this.getTitleAreaControl(position);
			const context = this.stacks.groupAt(position);
346
			const hasContext = titleAreaControl.hasContext();
347
			titleAreaControl.setContext(context);
348
			if (!context && hasContext) {
349
				titleAreaControl.refresh(); // clear out the control if the context is no longer present and there was a context
350
			}
B
Benjamin Pasero 已提交
351
		});
352

B
Benjamin Pasero 已提交
353
		// Refresh / update if group is visible and has a position
B
Benjamin Pasero 已提交
354 355 356 357 358 359 360 361
		buffer.forEach(e => {
			const position = this.stacks.positionOfGroup(e.group);
			if (position >= 0) { // group could be gone by now because we run from a scheduler with timeout
				if (e.structural) {
					this.getTitleAreaControl(position).refresh();
				} else {
					this.getTitleAreaControl(position).update();
				}
362
			}
B
Benjamin Pasero 已提交
363
		});
364 365
	}

366 367
	public get onGroupFocusChanged(): Event<void> {
		return this._onGroupFocusChanged.event;
368 369
	}

B
Benjamin Pasero 已提交
370
	public show(editor: BaseEditor, position: Position, preserveActive: boolean, ratio?: number[]): void {
B
Benjamin Pasero 已提交
371
		const visibleEditorCount = this.getVisibleEditorCount();
E
Erich Gamma 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384

		// Store into editor bucket
		this.visibleEditors[position] = editor;

		// Store as active unless preserveActive is set
		if (!preserveActive || !this.lastActiveEditor) {
			this.doSetActive(editor, position);
		}

		// Track focus
		this.trackFocus(editor, position);

		// Find target container and build into
385 386
		const target = this.silos[position].child().getHTMLElement();
		target.appendChild(editor.getContainer());
E
Erich Gamma 已提交
387 388

		// Adjust layout according to provided ratios (used when restoring multiple editors at once)
B
Benjamin Pasero 已提交
389
		if (ratio && (ratio.length === 2 || ratio.length === 3)) {
B
Benjamin Pasero 已提交
390
			const hasLayoutInfo = !!this.totalSize;
E
Erich Gamma 已提交
391

B
Benjamin Pasero 已提交
392
			// We received ratios but were not layouted yet. So we keep these ratios for when we layout()
E
Erich Gamma 已提交
393
			if (!hasLayoutInfo) {
B
Benjamin Pasero 已提交
394
				this.silosInitialRatio = ratio;
E
Erich Gamma 已提交
395 396 397
			}

			// Adjust layout: -> [!][!]
B
Benjamin Pasero 已提交
398
			if (ratio.length === 2) {
E
Erich Gamma 已提交
399
				if (hasLayoutInfo) {
B
Benjamin Pasero 已提交
400
					this.silosSize[position] = this.totalSize * ratio[position];
E
Erich Gamma 已提交
401 402 403 404
				}
			}

			// Adjust layout: -> [!][!][!]
B
Benjamin Pasero 已提交
405
			else if (ratio.length === 3) {
E
Erich Gamma 已提交
406
				if (hasLayoutInfo) {
B
Benjamin Pasero 已提交
407
					this.silosSize[position] = this.totalSize * ratio[position];
E
Erich Gamma 已提交
408 409
				}

B
Benjamin Pasero 已提交
410 411 412
				if (this.sashTwo.isHidden()) {
					this.sashTwo.show();
					this.sashTwo.layout();
E
Erich Gamma 已提交
413 414 415
				}
			}

B
Benjamin Pasero 已提交
416 417 418
			if (this.sashOne.isHidden()) {
				this.sashOne.show();
				this.sashOne.layout();
E
Erich Gamma 已提交
419 420 421 422 423 424 425 426 427
			}

			if (hasLayoutInfo) {
				this.layoutContainers();
			}
		}

		// Adjust layout: -> [!]
		else if (visibleEditorCount === 0 && this.dimension) {
B
Benjamin Pasero 已提交
428
			this.silosSize[position] = this.totalSize;
E
Erich Gamma 已提交
429 430 431 432 433

			this.layoutContainers();
		}

		// Adjust layout: [] -> []|[!]
434 435 436
		else if (position === Position.TWO && this.sashOne.isHidden() && this.sashTwo.isHidden() && this.dimension) {
			this.silosSize[Position.ONE] = this.totalSize / 2;
			this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE];
E
Erich Gamma 已提交
437

B
Benjamin Pasero 已提交
438 439
			this.sashOne.show();
			this.sashOne.layout();
E
Erich Gamma 已提交
440 441 442 443 444

			this.layoutContainers();
		}

		// Adjust layout: []|[] -> []|[]|[!]
445 446 447 448
		else if (position === Position.THREE && this.sashTwo.isHidden() && this.dimension) {
			this.silosSize[Position.ONE] = this.totalSize / 3;
			this.silosSize[Position.TWO] = this.totalSize / 3;
			this.silosSize[Position.THREE] = this.totalSize - this.silosSize[Position.ONE] - this.silosSize[Position.TWO];
E
Erich Gamma 已提交
449

B
Benjamin Pasero 已提交
450 451 452
			this.sashOne.layout();
			this.sashTwo.show();
			this.sashTwo.layout();
E
Erich Gamma 已提交
453 454 455 456 457

			this.layoutContainers();
		}

		// Show editor container
458
		DOM.show(editor.getContainer());
E
Erich Gamma 已提交
459 460 461
	}

	private getVisibleEditorCount(): number {
462
		return this.visibleEditors.filter(v => !!v).length;
E
Erich Gamma 已提交
463 464 465 466 467
	}

	private trackFocus(editor: BaseEditor, position: Position): void {

		// In case there is a previous tracker on the position, dispose it first
468 469
		if (this.visibleEditorFocusTrackerDisposable[position]) {
			this.visibleEditorFocusTrackerDisposable[position].dispose();
E
Erich Gamma 已提交
470 471
		}

M
Matt Bierner 已提交
472
		this.visibleEditorFocusTrackerDisposable[position] = editor.onDidFocus(() => {
E
Erich Gamma 已提交
473 474 475 476 477 478 479 480 481 482 483 484
			this.onFocusGained(editor);
		});
	}

	private onFocusGained(editor: BaseEditor): void {
		this.setActive(editor);
	}

	public setActive(editor: BaseEditor): void {

		// Update active editor and position
		if (this.lastActiveEditor !== editor) {
485
			this.doSetActive(editor, this.visibleEditors.indexOf(editor));
E
Erich Gamma 已提交
486

487 488
			// Automatically maximize this position if it is minimized
			if (this.isSiloMinimized(this.lastActivePosition)) {
B
Benjamin Pasero 已提交
489
				let remainingSize = this.totalSize;
490
				let layout = false;
E
Erich Gamma 已提交
491

B
Benjamin Pasero 已提交
492
				// Minimize all other positions to min size
B
Benjamin Pasero 已提交
493
				POSITIONS.forEach(p => {
E
Erich Gamma 已提交
494
					if (this.lastActivePosition !== p && !!this.visibleEditors[p]) {
B
Benjamin Pasero 已提交
495
						this.silosSize[p] = this.minSize;
B
Benjamin Pasero 已提交
496
						remainingSize -= this.silosSize[p];
E
Erich Gamma 已提交
497 498 499
					}
				});

500
				// Grow focused position if there is more size to spend
B
Benjamin Pasero 已提交
501
				if (remainingSize > this.minSize) {
B
Benjamin Pasero 已提交
502
					this.silosSize[this.lastActivePosition] = remainingSize;
E
Erich Gamma 已提交
503

B
Benjamin Pasero 已提交
504 505
					if (!this.sashOne.isHidden()) {
						this.sashOne.layout();
E
Erich Gamma 已提交
506 507
					}

B
Benjamin Pasero 已提交
508 509
					if (!this.sashTwo.isHidden()) {
						this.sashTwo.layout();
E
Erich Gamma 已提交
510 511
					}

512 513 514 515 516
					layout = true;
				}

				// Since we triggered a change in minimized/maximized editors, we need
				// to update our stored state of minimized silos accordingly
B
Benjamin Pasero 已提交
517
				this.enableMinimizedState();
518 519

				if (layout) {
E
Erich Gamma 已提交
520 521 522 523 524
					this.layoutContainers();
				}
			}

			// Re-emit to outside
525
			this._onGroupFocusChanged.fire();
E
Erich Gamma 已提交
526 527 528 529 530
		}
	}

	private focusNextNonMinimized(): void {

531
		// If the current focused editor is minimized, try to focus the next largest editor
532
		if (!types.isUndefinedOrNull(this.lastActivePosition) && this.silosMinimized[this.lastActivePosition]) {
E
Erich Gamma 已提交
533
			let candidate: Position = null;
B
Benjamin Pasero 已提交
534
			let currentSize = this.minSize;
B
Benjamin Pasero 已提交
535
			POSITIONS.forEach(position => {
E
Erich Gamma 已提交
536

B
Benjamin Pasero 已提交
537
				// Skip current active position and check if the editor is larger than min size
E
Erich Gamma 已提交
538
				if (position !== this.lastActivePosition) {
B
Benjamin Pasero 已提交
539
					if (this.visibleEditors[position] && this.silosSize[position] > currentSize) {
E
Erich Gamma 已提交
540
						candidate = position;
B
Benjamin Pasero 已提交
541
						currentSize = this.silosSize[position];
E
Erich Gamma 已提交
542 543 544 545 546 547
					}
				}
			});

			// Focus editor if a candidate has been found
			if (!types.isUndefinedOrNull(candidate)) {
548
				this.editorGroupService.focusGroup(candidate);
E
Erich Gamma 已提交
549 550 551 552
			}
		}
	}

553
	public hide(editor: BaseEditor, position: Position, layoutAndRochade: boolean): Rochade {
E
Erich Gamma 已提交
554 555
		let result = Rochade.NONE;

B
Benjamin Pasero 已提交
556
		const visibleEditorCount = this.getVisibleEditorCount();
E
Erich Gamma 已提交
557

B
Benjamin Pasero 已提交
558 559
		const hasEditorInPositionTwo = !!this.visibleEditors[Position.TWO];
		const hasEditorInPositionThree = !!this.visibleEditors[Position.THREE];
E
Erich Gamma 已提交
560 561 562 563 564 565 566 567 568

		// If editor is not showing for position, return
		if (editor !== this.visibleEditors[position]) {
			return result;
		}

		// Clear Position
		this.clearPosition(position);

569
		// Take editor container offdom and hide
570 571 572 573 574
		const editorContainer = editor.getContainer();
		if (editorContainer.parentNode) {
			editorContainer.parentNode.removeChild(editorContainer);
		}
		DOM.hide(editorContainer);
E
Erich Gamma 已提交
575 576 577 578 579 580

		// Adjust layout and rochade if instructed to do so
		if (layoutAndRochade) {

			// Adjust layout: [x] ->
			if (visibleEditorCount === 1) {
B
Benjamin Pasero 已提交
581
				this.silosSize[position] = 0;
E
Erich Gamma 已提交
582

B
Benjamin Pasero 已提交
583 584
				this.sashOne.hide();
				this.sashTwo.hide();
E
Erich Gamma 已提交
585 586 587 588 589

				this.layoutContainers();
			}

			// Adjust layout: []|[x] -> [] or [x]|[] -> []
B
Benjamin Pasero 已提交
590
			else if (hasEditorInPositionTwo && !hasEditorInPositionThree) {
591 592
				this.silosSize[Position.ONE] = this.totalSize;
				this.silosSize[Position.TWO] = 0;
E
Erich Gamma 已提交
593

B
Benjamin Pasero 已提交
594 595
				this.sashOne.hide();
				this.sashTwo.hide();
E
Erich Gamma 已提交
596

B
Benjamin Pasero 已提交
597
				// Move TWO to ONE ([x]|[] -> [])
598 599 600
				if (position === Position.ONE) {
					this.rochade(Position.TWO, Position.ONE);
					result = Rochade.TWO_TO_ONE;
E
Erich Gamma 已提交
601 602 603 604 605 606
				}

				this.layoutContainers();
			}

			// Adjust layout: []|[]|[x] -> [ ]|[ ] or []|[x]|[] -> [ ]|[ ] or [x]|[]|[] -> [ ]|[ ]
B
Benjamin Pasero 已提交
607
			else if (hasEditorInPositionTwo && hasEditorInPositionThree) {
608 609 610
				this.silosSize[Position.ONE] = this.totalSize / 2;
				this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE];
				this.silosSize[Position.THREE] = 0;
E
Erich Gamma 已提交
611

B
Benjamin Pasero 已提交
612 613
				this.sashOne.layout();
				this.sashTwo.hide();
E
Erich Gamma 已提交
614

B
Benjamin Pasero 已提交
615
				// Move THREE to TWO ([]|[x]|[] -> [ ]|[ ])
616 617 618
				if (position === Position.TWO) {
					this.rochade(Position.THREE, Position.TWO);
					result = Rochade.THREE_TO_TWO;
E
Erich Gamma 已提交
619 620
				}

B
Benjamin Pasero 已提交
621
				// Move THREE to TWO and TWO to ONE ([x]|[]|[] -> [ ]|[ ])
622 623 624 625
				else if (position === Position.ONE) {
					this.rochade(Position.TWO, Position.ONE);
					this.rochade(Position.THREE, Position.TWO);
					result = Rochade.TWO_AND_THREE_TO_ONE;
E
Erich Gamma 已提交
626 627 628 629 630 631 632 633 634 635 636 637
				}

				this.layoutContainers();
			}
		}

		// Automatically pick the next editor as active if any
		if (this.lastActiveEditor === editor) {

			// Clear old
			this.doSetActive(null, null);

B
Benjamin Pasero 已提交
638
			// Find new active position by taking the next one close to the closed one to the left/top
E
Erich Gamma 已提交
639 640 641
			if (layoutAndRochade) {
				let newActivePosition: Position;
				switch (position) {
642
					case Position.ONE:
B
Benjamin Pasero 已提交
643
						newActivePosition = hasEditorInPositionTwo ? Position.ONE : null;
E
Erich Gamma 已提交
644
						break;
645 646
					case Position.TWO:
						newActivePosition = Position.ONE;
E
Erich Gamma 已提交
647
						break;
648 649
					case Position.THREE:
						newActivePosition = Position.TWO;
E
Erich Gamma 已提交
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
						break;
				}

				if (!types.isUndefinedOrNull(newActivePosition)) {
					this.doSetActive(this.visibleEditors[newActivePosition], newActivePosition);
				}
			}
		}

		return result;
	}

	private doSetActive(editor: BaseEditor, newActive: Position): void {
		this.lastActivePosition = newActive;
		this.lastActiveEditor = editor;
	}

	private clearPosition(position: Position): void {

		// Unregister Listeners
670 671 672
		if (this.visibleEditorFocusTrackerDisposable[position]) {
			this.visibleEditorFocusTrackerDisposable[position].dispose();
			this.visibleEditorFocusTrackerDisposable[position] = null;
E
Erich Gamma 已提交
673 674 675 676 677 678 679 680
		}

		// Clear from active editors
		this.visibleEditors[position] = null;
	}

	private rochade(from: Position, to: Position): void {

681 682 683 684 685 686 687 688
		// Move container to new position
		const containerFrom = this.silos[from].child();
		containerFrom.appendTo(this.silos[to]);

		const containerTo = this.silos[to].child();
		containerTo.appendTo(this.silos[from]);

		// Inform editor
B
Benjamin Pasero 已提交
689
		const editor = this.visibleEditors[from];
690
		// editor.changePosition(to);
E
Erich Gamma 已提交
691 692

		// Change data structures
693 694 695
		const listeners = this.visibleEditorFocusTrackerDisposable[from];
		this.visibleEditorFocusTrackerDisposable[to] = listeners;
		this.visibleEditorFocusTrackerDisposable[from] = null;
E
Erich Gamma 已提交
696

697 698 699 700
		const minimizedState = this.silosMinimized[from];
		this.silosMinimized[to] = minimizedState;
		this.silosMinimized[from] = null;

701
		this.visibleEditors[to] = editor;
E
Erich Gamma 已提交
702
		this.visibleEditors[from] = null;
703

E
Erich Gamma 已提交
704 705 706 707 708 709 710 711 712 713 714
		// Update last active position
		if (this.lastActivePosition === from) {
			this.doSetActive(this.lastActiveEditor, to);
		}
	}

	public move(from: Position, to: Position): void {

		// Distance 1: Swap Editors
		if (Math.abs(from - to) === 1) {

715 716 717
			// Move containers to new position
			const containerFrom = this.silos[from].child();
			containerFrom.appendTo(this.silos[to]);
E
Erich Gamma 已提交
718

719 720 721 722
			const containerTo = this.silos[to].child();
			containerTo.appendTo(this.silos[from]);

			// Inform Editors
723 724
			// this.visibleEditors[from].changePosition(to);
			// this.visibleEditors[to].changePosition(from);
E
Erich Gamma 已提交
725 726 727 728 729 730 731 732 733 734 735 736 737

			// Update last active position accordingly
			if (this.lastActivePosition === from) {
				this.doSetActive(this.lastActiveEditor, to);
			} else if (this.lastActivePosition === to) {
				this.doSetActive(this.lastActiveEditor, from);
			}
		}

		// Otherwise Move Editors
		else {

			// Find new positions
B
Benjamin Pasero 已提交
738 739 740
			let newPositionOne: Position;
			let newPositionTwo: Position;
			let newPositionThree: Position;
E
Erich Gamma 已提交
741

742
			if (from === Position.ONE) {
B
Benjamin Pasero 已提交
743 744 745
				newPositionOne = Position.THREE;
				newPositionTwo = Position.ONE;
				newPositionThree = Position.TWO;
E
Erich Gamma 已提交
746
			} else {
B
Benjamin Pasero 已提交
747 748 749
				newPositionOne = Position.TWO;
				newPositionTwo = Position.THREE;
				newPositionThree = Position.ONE;
E
Erich Gamma 已提交
750 751
			}

752
			// Move containers to new position
753
			const containerPos1 = this.silos[Position.ONE].child();
B
Benjamin Pasero 已提交
754
			containerPos1.appendTo(this.silos[newPositionOne]);
755

756
			const containerPos2 = this.silos[Position.TWO].child();
B
Benjamin Pasero 已提交
757
			containerPos2.appendTo(this.silos[newPositionTwo]);
E
Erich Gamma 已提交
758

759
			const containerPos3 = this.silos[Position.THREE].child();
B
Benjamin Pasero 已提交
760
			containerPos3.appendTo(this.silos[newPositionThree]);
E
Erich Gamma 已提交
761

762
			// Inform Editors
763 764 765
			// this.visibleEditors[Position.ONE].changePosition(newPositionOne);
			// this.visibleEditors[Position.TWO].changePosition(newPositionTwo);
			// this.visibleEditors[Position.THREE].changePosition(newPositionThree);
E
Erich Gamma 已提交
766 767

			// Update last active position accordingly
768
			if (this.lastActivePosition === Position.ONE) {
B
Benjamin Pasero 已提交
769
				this.doSetActive(this.lastActiveEditor, newPositionOne);
770
			} else if (this.lastActivePosition === Position.TWO) {
B
Benjamin Pasero 已提交
771
				this.doSetActive(this.lastActiveEditor, newPositionTwo);
772
			} else if (this.lastActivePosition === Position.THREE) {
B
Benjamin Pasero 已提交
773
				this.doSetActive(this.lastActiveEditor, newPositionThree);
E
Erich Gamma 已提交
774 775 776 777 778
			}
		}

		// Change data structures
		arrays.move(this.visibleEditors, from, to);
779
		arrays.move(this.visibleEditorFocusTrackerDisposable, from, to);
B
Benjamin Pasero 已提交
780
		arrays.move(this.silosSize, from, to);
781
		arrays.move(this.silosMinimized, from, to);
E
Erich Gamma 已提交
782 783

		// Layout
B
Benjamin Pasero 已提交
784 785
		if (!this.sashOne.isHidden()) {
			this.sashOne.layout();
E
Erich Gamma 已提交
786 787
		}

B
Benjamin Pasero 已提交
788 789
		if (!this.sashTwo.isHidden()) {
			this.sashTwo.layout();
E
Erich Gamma 已提交
790 791 792
		}

		this.layoutContainers();
793
		this.updateStyles();
E
Erich Gamma 已提交
794 795
	}

796 797 798 799
	public setGroupOrientation(orientation: GroupOrientation): void {
		this.layoutVertically = (orientation !== 'horizontal');

		// Editor Layout
800
		const verticalLayouting = DOM.hasClass(this.parent, 'vertical-layout');
801
		if (verticalLayouting !== this.layoutVertically) {
802 803
			DOM.removeClasses(this.parent, 'vertical-layout', 'horizontal-layout');
			DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
804 805 806 807

			this.sashOne.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
			this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);

808 809 810
			// Update styles
			this.updateStyles();

811
			// Trigger layout
812
			this.arrangeGroups();
813 814 815 816 817 818 819
		}
	}

	public getGroupOrientation(): GroupOrientation {
		return this.layoutVertically ? 'vertical' : 'horizontal';
	}

820
	public arrangeGroups(arrangement?: GroupArrangement): void {
E
Erich Gamma 已提交
821 822 823 824
		if (!this.dimension) {
			return; // too early
		}

B
Benjamin Pasero 已提交
825
		let availableSize = this.totalSize;
B
Benjamin Pasero 已提交
826
		const visibleEditors = this.getVisibleEditorCount();
E
Erich Gamma 已提交
827 828 829 830 831

		if (visibleEditors <= 1) {
			return; // need more editors
		}

832 833 834 835 836 837 838 839 840
		switch (arrangement) {
			case GroupArrangement.MINIMIZE_OTHERS:
				// Minimize Others
				POSITIONS.forEach(position => {
					if (this.visibleEditors[position]) {
						if (position !== this.lastActivePosition) {
							this.silosSize[position] = this.minSize;
							availableSize -= this.minSize;
						}
E
Erich Gamma 已提交
841
					}
842
				});
E
Erich Gamma 已提交
843

844 845 846 847 848 849 850 851 852 853
				this.silosSize[this.lastActivePosition] = availableSize;
				break;
			case GroupArrangement.EVEN:
				// Even Sizes
				POSITIONS.forEach(position => {
					if (this.visibleEditors[position]) {
						this.silosSize[position] = availableSize / visibleEditors;
					}
				});
				break;
854
			default:
855 856 857 858 859 860 861 862 863 864 865 866
				// Minimized editors should remain minimized, others should keep their relative Sizes
				let oldNonMinimizedTotal = 0;
				POSITIONS.forEach(position => {
					if (this.visibleEditors[position]) {
						if (this.silosMinimized[position]) {
							this.silosSize[position] = this.minSize;
							availableSize -= this.minSize;
						} else {
							oldNonMinimizedTotal += this.silosSize[position];
						}
					}
				});
E
Erich Gamma 已提交
867

868 869 870 871 872 873 874
				// Set size for non-minimized editors
				const scaleFactor = availableSize / oldNonMinimizedTotal;
				POSITIONS.forEach(position => {
					if (this.visibleEditors[position] && !this.silosMinimized[position]) {
						this.silosSize[position] *= scaleFactor;
					}
				});
E
Erich Gamma 已提交
875 876
		}

877 878
		// Since we triggered a change in minimized/maximized editors, we need
		// to update our stored state of minimized silos accordingly
B
Benjamin Pasero 已提交
879
		this.enableMinimizedState();
880 881

		// Layout silos
E
Erich Gamma 已提交
882 883 884
		this.layoutControl(this.dimension);
	}

B
Benjamin Pasero 已提交
885
	public getRatio(): number[] {
B
Benjamin Pasero 已提交
886
		const ratio: number[] = [];
E
Erich Gamma 已提交
887 888

		if (this.dimension) {
B
Benjamin Pasero 已提交
889
			const fullSize = this.totalSize;
E
Erich Gamma 已提交
890

B
Benjamin Pasero 已提交
891
			POSITIONS.forEach(position => {
E
Erich Gamma 已提交
892
				if (this.visibleEditors[position]) {
B
Benjamin Pasero 已提交
893
					ratio.push(this.silosSize[position] / fullSize);
E
Erich Gamma 已提交
894 895 896 897 898 899 900
				}
			});
		}

		return ratio;
	}

901
	// Resize the editor/group position - changes main axis
B
Benjamin Pasero 已提交
902
	public resizeGroup(position: Position, groupSizeChange: number): void {
903 904 905 906 907 908 909 910 911 912

		enum VISIBLE_EDITORS {
			ONE = 1,
			TWO = 2,
			THREE = 3
		}

		const visibleEditors = this.getVisibleEditorCount();

		if (visibleEditors <= VISIBLE_EDITORS.ONE) {
B
Benjamin Pasero 已提交
913
			return;
914 915
		}

B
Benjamin Pasero 已提交
916
		const availableSize = this.totalSize;
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
		const activeGroupPosition = this.getActivePosition();

		switch (visibleEditors) {
			case VISIBLE_EDITORS.TWO:
				switch (activeGroupPosition) {
					case Position.ONE:
						this.silosSize[Position.ONE] = this.boundSiloSize(Position.ONE, groupSizeChange);
						this.silosSize[Position.TWO] = availableSize - this.silosSize[Position.ONE];
						break;
					case Position.TWO:
						this.silosSize[Position.TWO] = this.boundSiloSize(Position.TWO, groupSizeChange);
						this.silosSize[Position.ONE] = availableSize - this.silosSize[Position.TWO];
					default:
						break;
				}
				break;
			case VISIBLE_EDITORS.THREE:
				switch (activeGroupPosition) {
					case Position.ONE:
						this.silosSize[Position.ONE] = this.boundSiloSize(Position.ONE, groupSizeChange);
						this.distributeRemainingSilosSize(Position.TWO, Position.THREE, availableSize - this.silosSize[Position.ONE]);
						break;
					case Position.TWO:
						this.silosSize[Position.TWO] = this.boundSiloSize(Position.TWO, groupSizeChange);
						this.distributeRemainingSilosSize(Position.ONE, Position.THREE, availableSize - this.silosSize[Position.TWO]);
						break;
					case Position.THREE:
						this.silosSize[Position.THREE] = this.boundSiloSize(Position.THREE, groupSizeChange);
						this.distributeRemainingSilosSize(Position.ONE, Position.TWO, availableSize - this.silosSize[Position.THREE]);
						break;
					default:
						break;
				}
			default:
				break;
		}

B
Benjamin Pasero 已提交
954
		this.layout(this.dimension);
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
	}

	private boundSiloSize(siloPosition: Position, sizeChangePx: number): number {
		const visibleEditors = this.getVisibleEditorCount();
		let newSiloSize: number = 0;

		newSiloSize = Math.max(this.minSize, this.silosSize[siloPosition] + sizeChangePx);
		newSiloSize = Math.min(newSiloSize, (this.totalSize - this.minSize * (visibleEditors - 1)));

		return newSiloSize;
	}

	private distributeRemainingSilosSize(remPosition1: Position, remPosition2: Position, availableSize: number): void {
		let scaleFactor: number = 0;

		scaleFactor = this.silosSize[remPosition1] / (this.silosSize[remPosition1] + this.silosSize[remPosition2]);
		this.silosSize[remPosition1] = scaleFactor * availableSize;
		this.silosSize[remPosition1] = Math.max(this.silosSize[remPosition1], this.minSize);
		this.silosSize[remPosition1] = Math.min(this.silosSize[remPosition1], (availableSize - this.minSize));
		this.silosSize[remPosition2] = availableSize - this.silosSize[remPosition1];
	}

E
Erich Gamma 已提交
977 978 979 980 981 982 983 984
	public getActiveEditor(): BaseEditor {
		return this.lastActiveEditor;
	}

	public getActivePosition(): Position {
		return this.lastActivePosition;
	}

985 986 987
	private create(): void {

		// Store layout as class property
988
		DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
E
Erich Gamma 已提交
989

990
		// Allow to drop into container to open
991
		this.enableDropTarget(this.parent);
992

B
Benjamin Pasero 已提交
993
		// Silo One
994
		this.silos[Position.ONE] = $(this.parent).div({ class: 'one-editor-silo editor-one' });
E
Erich Gamma 已提交
995

B
Benjamin Pasero 已提交
996
		// Sash One
997
		this.sashOne = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
I
isidor 已提交
998 999 1000 1001
		this.toUnbind.push(this.sashOne.onDidStart(() => this.onSashOneDragStart()));
		this.toUnbind.push(this.sashOne.onDidChange((e: ISashEvent) => this.onSashOneDrag(e)));
		this.toUnbind.push(this.sashOne.onDidEnd(() => this.onSashOneDragEnd()));
		this.toUnbind.push(this.sashOne.onDidReset(() => this.onSashOneReset()));
B
Benjamin Pasero 已提交
1002
		this.sashOne.hide();
E
Erich Gamma 已提交
1003

B
Benjamin Pasero 已提交
1004
		// Silo Two
1005
		this.silos[Position.TWO] = $(this.parent).div({ class: 'one-editor-silo editor-two' });
E
Erich Gamma 已提交
1006

B
Benjamin Pasero 已提交
1007
		// Sash Two
1008
		this.sashTwo = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
I
isidor 已提交
1009 1010 1011 1012
		this.toUnbind.push(this.sashTwo.onDidStart(() => this.onSashTwoDragStart()));
		this.toUnbind.push(this.sashTwo.onDidChange((e: ISashEvent) => this.onSashTwoDrag(e)));
		this.toUnbind.push(this.sashTwo.onDidEnd(() => this.onSashTwoDragEnd()));
		this.toUnbind.push(this.sashTwo.onDidReset(() => this.onSashTwoReset()));
B
Benjamin Pasero 已提交
1013
		this.sashTwo.hide();
E
Erich Gamma 已提交
1014

B
Benjamin Pasero 已提交
1015
		// Silo Three
1016
		this.silos[Position.THREE] = $(this.parent).div({ class: 'one-editor-silo editor-three' });
E
Erich Gamma 已提交
1017

B
Benjamin Pasero 已提交
1018
		// For each position
1019
		POSITIONS.forEach(position => {
B
Benjamin Pasero 已提交
1020 1021
			this.createSilo(position);
		});
B
Benjamin Pasero 已提交
1022

B
Benjamin Pasero 已提交
1023 1024 1025
		// Update Styles
		this.updateStyles();
	}
1026

B
Benjamin Pasero 已提交
1027 1028
	private createSilo(position: Position): void {
		const silo = this.silos[position];
B
Benjamin Pasero 已提交
1029

B
Benjamin Pasero 已提交
1030 1031
		// Containers (they contain everything and can move between silos)
		const container = $(silo).div({ 'class': 'container' });
E
Erich Gamma 已提交
1032

B
Benjamin Pasero 已提交
1033 1034 1035 1036 1037
		// InstantiationServices
		const instantiationService = this.instantiationService.createChild(new ServiceCollection(
			[IContextKeyService, this.contextKeyService.createScoped(container.getHTMLElement())]
		));
		container.setProperty(EditorGroupsControl.INSTANTIATION_SERVICE_KEY, instantiationService); // associate with container
1038

B
Benjamin Pasero 已提交
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
		// Title containers
		const titleContainer = $(container).div({ 'class': 'title' });
		if (this.tabOptions.showTabs) {
			titleContainer.addClass('tabs');
		}
		if (this.tabOptions.showIcons) {
			titleContainer.addClass('show-file-icons');
		}
		this.hookTitleDragListener(titleContainer);

		// Title Control
		this.createTitleControl(this.stacks.groupAt(position), silo, titleContainer, instantiationService);

		// Progress Bar
1053
		const progressBar = new ProgressBar(container.getHTMLElement());
B
Benjamin Pasero 已提交
1054
		this.toUnbind.push(attachProgressBarStyler(progressBar, this.themeService));
1055
		progressBar.hide();
B
Benjamin Pasero 已提交
1056 1057 1058 1059 1060 1061
		container.setProperty(EditorGroupsControl.PROGRESS_BAR_CONTROL_KEY, progressBar); // associate with container

		// Sash for first position to support centered editor layout
		if (position === Position.ONE) {

			// Center Layout stuff
1062 1063 1064 1065 1066 1067
			const registerSashListeners = (sash: Sash) => {
				this.toUnbind.push(sash.onDidStart(() => this.onCenterSashDragStart()));
				this.toUnbind.push(sash.onDidChange((e: ISashEvent) => this.onCenterSashDrag(sash, e)));
				this.toUnbind.push(sash.onDidEnd(() => this.storeCenteredLayoutData()));
				this.toUnbind.push(sash.onDidReset(() => this.resetCenteredEditor()));
			};
B
Benjamin Pasero 已提交
1068 1069
			this.centeredEditorSashLeft = new Sash(container.getHTMLElement(), this, { baseSize: 5, orientation: Orientation.VERTICAL });
			this.centeredEditorSashRight = new Sash(container.getHTMLElement(), this, { baseSize: 5, orientation: Orientation.VERTICAL });
1070 1071
			registerSashListeners(this.centeredEditorSashLeft);
			registerSashListeners(this.centeredEditorSashRight);
1072 1073
			this.centeredEditorSashLeft.hide();
			this.centeredEditorSashRight.hide();
B
Benjamin Pasero 已提交
1074 1075

			this.centeredEditorActive = false;
1076
			this.centeredEditorLeftMarginRatio = 0.5;
B
Benjamin Pasero 已提交
1077 1078

			// Restore centered layout position and size
1079
			const centeredLayoutDataString = this.storageService.get(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.WORKSPACE);
B
Benjamin Pasero 已提交
1080 1081
			if (centeredLayoutDataString) {
				const centeredLayout = <CenteredEditorLayoutData>JSON.parse(centeredLayoutDataString);
1082
				this.centeredEditorLeftMarginRatio = centeredLayout.leftMarginRatio;
S
SrTobi 已提交
1083
				this.centeredEditorPreferredSize = centeredLayout.size;
B
Benjamin Pasero 已提交
1084 1085
			}
		}
1086 1087 1088
	}

	protected updateStyles(): void {
1089
		super.updateStyles();
1090 1091 1092

		// Editor container colors
		this.silos.forEach((silo, index) => {
1093

1094
			// Background
1095
			silo.style('background-color', this.getColor(editorBackground));
1096 1097

			// Border
1098 1099
			silo.style('border-left-color', index > Position.ONE ? (this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder)) : null);
			silo.style('border-top-color', index > Position.ONE ? (this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder)) : null);
1100
		});
1101 1102 1103 1104

		// Title control
		POSITIONS.forEach(position => {
			const container = this.getTitleAreaControl(position).getContainer();
1105
			const borderColor = this.getColor(EDITOR_GROUP_HEADER_TABS_BORDER) || this.getColor(contrastBorder);
1106

B
Benjamin Pasero 已提交
1107
			container.style.backgroundColor = this.getColor(this.tabOptions.showTabs ? EDITOR_GROUP_HEADER_TABS_BACKGROUND : EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND);
1108 1109 1110
			container.style.borderBottomWidth = (borderColor && this.tabOptions.showTabs) ? '1px' : null;
			container.style.borderBottomStyle = (borderColor && this.tabOptions.showTabs) ? 'solid' : null;
			container.style.borderBottomColor = this.tabOptions.showTabs ? borderColor : null;
1111
		});
E
Erich Gamma 已提交
1112 1113
	}

1114
	private enableDropTarget(node: HTMLElement): void {
B
Benjamin Pasero 已提交
1115
		const $this = this;
B
Benjamin Pasero 已提交
1116
		const overlayId = 'monaco-workbench-editor-drop-overlay';
B
wip  
Benjamin Pasero 已提交
1117
		const splitToPropertyKey = 'splitToPosition';
B
Benjamin Pasero 已提交
1118
		const stacks = this.editorGroupService.getStacksModel();
1119

B
Benjamin Pasero 已提交
1120
		let overlay: Builder;
1121 1122 1123 1124 1125 1126

		function cleanUp(): void {
			if (overlay) {
				overlay.destroy();
				overlay = void 0;
			}
B
Benjamin Pasero 已提交
1127

B
Benjamin Pasero 已提交
1128 1129 1130
			POSITIONS.forEach(p => {
				$this.silos[p].removeClass('dragged-over');
			});
1131
		}
1132

1133 1134 1135 1136 1137 1138
		function optionsFromDraggedEditor(identifier: IEditorIdentifier): EditorOptions {

			// When moving an editor, try to preserve as much view state as possible by checking
			// for th editor to be a text editor and creating the options accordingly if so
			let options = EditorOptions.create({ pinned: true });
			const activeEditor = $this.editorService.getActiveEditor();
B
Benjamin Pasero 已提交
1139
			const editor = getCodeEditor(activeEditor.getControl());
1140
			if (editor && activeEditor.group.id === stacks.positionOfGroup(stacks.getGroup(identifier.groupId)) && identifier.editor.matches(activeEditor.input)) {
1141
				options = TextEditorOptions.fromEditor(editor, { pinned: true });
1142 1143 1144 1145 1146
			}

			return options;
		}

1147 1148 1149 1150 1151 1152 1153 1154 1155
		const isCopyDrag = (draggedEditor: IEditorIdentifier, e: DragEvent) => {
			if (draggedEditor && draggedEditor.editor instanceof EditorInput) {
				if (!draggedEditor.editor.supportsSplitEditor()) {
					return false;
				}
			}
			return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
		};

B
wip  
Benjamin Pasero 已提交
1156
		function onDrop(e: DragEvent, position: Position, splitTo?: Position): void {
1157
			$this.updateFromDragAndDrop(node, false);
1158
			cleanUp();
1159

B
Benjamin Pasero 已提交
1160 1161 1162
			const editorService = $this.editorService;
			const groupService = $this.editorGroupService;

B
Benjamin Pasero 已提交
1163
			const splitEditor = (typeof splitTo === 'number');
1164
			const freeGroup = (stacks.groups.length === 1) ? Position.TWO : Position.THREE;
B
Benjamin Pasero 已提交
1165

1166
			// Check for transfer from title control
1167 1168
			if ($this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
				const draggedEditor = $this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
1169
				const isCopy = isCopyDrag(draggedEditor, e);
B
Benjamin Pasero 已提交
1170 1171

				// Copy editor to new location
1172
				if (isCopy) {
B
Benjamin Pasero 已提交
1173
					if (splitEditor) {
1174
						editorService.openEditor(draggedEditor.editor, optionsFromDraggedEditor(draggedEditor), freeGroup).then(() => {
B
Benjamin Pasero 已提交
1175 1176 1177
							if (splitTo !== freeGroup) {
								groupService.moveGroup(freeGroup, splitTo);
							}
B
Benjamin Pasero 已提交
1178
						}).done(null, errors.onUnexpectedError);
B
Benjamin Pasero 已提交
1179
					} else {
1180
						editorService.openEditor(draggedEditor.editor, optionsFromDraggedEditor(draggedEditor), position).done(null, errors.onUnexpectedError);
B
Benjamin Pasero 已提交
1181 1182 1183 1184 1185
					}
				}

				// Move editor to new location
				else {
I
isidor 已提交
1186
					const draggedGroup = stacks.getGroup(draggedEditor.groupId);
1187
					const sourcePosition = stacks.positionOfGroup(draggedGroup);
B
Benjamin Pasero 已提交
1188
					if (splitEditor) {
1189
						if (draggedGroup.count === 1) {
B
Benjamin Pasero 已提交
1190 1191
							groupService.moveGroup(sourcePosition, splitTo);
						} else {
1192
							editorService.openEditor(draggedEditor.editor, optionsFromDraggedEditor(draggedEditor), freeGroup).then(() => {
B
Benjamin Pasero 已提交
1193 1194 1195
								if (splitTo !== freeGroup) {
									groupService.moveGroup(freeGroup, splitTo);
								}
1196
								groupService.moveEditor(draggedEditor.editor, stacks.positionOfGroup(draggedGroup), splitTo);
B
Benjamin Pasero 已提交
1197 1198 1199
							}).done(null, errors.onUnexpectedError);
						}

B
Benjamin Pasero 已提交
1200 1201 1202
					} else {
						groupService.moveEditor(draggedEditor.editor, sourcePosition, position);
					}
1203 1204
				}
			}
B
Benjamin Pasero 已提交
1205

1206 1207
			// Check for URI transfer
			else {
B
Benjamin Pasero 已提交
1208
				const dropHandler = $this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
1209 1210 1211 1212
				dropHandler.handleDrop(e, () => {
					if (splitEditor && splitTo !== freeGroup) {
						groupService.moveGroup(freeGroup, splitTo);
					}
B
Benjamin Pasero 已提交
1213

1214
					groupService.focusGroup(splitEditor ? splitTo : position);
1215
				}, () => splitEditor ? freeGroup : position);
B
Benjamin Pasero 已提交
1216 1217 1218
			}
		}

B
wip  
Benjamin Pasero 已提交
1219 1220
		function positionOverlay(e: DragEvent, groups: number, position: Position): void {
			const target = <HTMLElement>e.target;
B
Benjamin Pasero 已提交
1221
			const overlayIsSplit = typeof overlay.getProperty(splitToPropertyKey) === 'number';
1222
			const draggedEditor = $this.transfer.hasData(DraggedEditorIdentifier.prototype) ? $this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier : void 0;
1223
			const isCopy = isCopyDrag(draggedEditor, e);
B
wip  
Benjamin Pasero 已提交
1224

B
Benjamin Pasero 已提交
1225 1226 1227 1228 1229 1230
			const overlaySize = $this.layoutVertically ? target.clientWidth : target.clientHeight;
			const splitThreshold = overlayIsSplit ? overlaySize / 5 : overlaySize / 10;

			const posOnOverlay = $this.layoutVertically ? e.offsetX : e.offsetY;
			const isOverSplitLeftOrUp = posOnOverlay < splitThreshold;
			const isOverSplitRightOrBottom = posOnOverlay + splitThreshold > overlaySize;
B
Benjamin Pasero 已提交
1231 1232 1233

			let splitTarget: Position;

I
isidor 已提交
1234
			const draggedGroup = stacks.getGroup(draggedEditor.groupId);
1235

B
Benjamin Pasero 已提交
1236
			// No splitting if we reached maximum group count
B
wip  
Benjamin Pasero 已提交
1237
			if (groups === POSITIONS.length) {
B
Benjamin Pasero 已提交
1238
				splitTarget = null;
B
wip  
Benjamin Pasero 已提交
1239 1240
			}

B
Benjamin Pasero 已提交
1241
			// Special splitting if we drag an editor of a group with only one editor
1242 1243
			else if (!isCopy && draggedEditor && draggedGroup.count === 1) {
				const positionOfDraggedEditor = stacks.positionOfGroup(draggedGroup);
B
Benjamin Pasero 已提交
1244
				switch (positionOfDraggedEditor) {
1245 1246
					case Position.ONE:
						if (position === Position.TWO && isOverSplitRightOrBottom) {
B
Benjamin Pasero 已提交
1247
							splitTarget = Position.TWO; // allow to move single editor from ONE to TWO
B
Benjamin Pasero 已提交
1248 1249
						}
						break;
1250 1251
					case Position.TWO:
						if (position === Position.ONE && isOverSplitLeftOrUp) {
B
Benjamin Pasero 已提交
1252
							splitTarget = Position.ONE; // allow to move single editor from TWO to ONE
B
Benjamin Pasero 已提交
1253 1254 1255 1256 1257
						}
						break;
					default:
						splitTarget = null; // splitting not allowed
				}
B
Benjamin Pasero 已提交
1258 1259
			}

B
Benjamin Pasero 已提交
1260 1261
			// Any other case, check for mouse position
			else {
B
Benjamin Pasero 已提交
1262
				if (isOverSplitRightOrBottom) {
1263
					splitTarget = (position === Position.ONE) ? Position.TWO : Position.THREE;
B
Benjamin Pasero 已提交
1264
				} else if (isOverSplitLeftOrUp) {
1265
					splitTarget = (position === Position.ONE) ? Position.ONE : Position.TWO;
B
Benjamin Pasero 已提交
1266
				}
B
wip  
Benjamin Pasero 已提交
1267
			}
B
Benjamin Pasero 已提交
1268

B
Benjamin Pasero 已提交
1269 1270 1271 1272 1273 1274 1275 1276 1277
			// Apply split target
			const canSplit = (typeof splitTarget === 'number');
			if (canSplit) {
				overlay.setProperty(splitToPropertyKey, splitTarget);
			} else {
				overlay.removeProperty(splitToPropertyKey);
			}

			// Update overlay styles
B
Benjamin Pasero 已提交
1278
			if (canSplit && isOverSplitRightOrBottom) {
B
Benjamin Pasero 已提交
1279
				overlay.style($this.layoutVertically ? { left: '50%', width: '50%' } : { top: '50%', height: '50%' });
B
Benjamin Pasero 已提交
1280
			} else if (canSplit && isOverSplitLeftOrUp) {
B
Benjamin Pasero 已提交
1281
				overlay.style($this.layoutVertically ? { width: '50%' } : { height: '50%' });
B
Benjamin Pasero 已提交
1282
			} else {
B
Benjamin Pasero 已提交
1283 1284 1285
				if ($this.layoutVertically) {
					overlay.style({ left: '0', width: '100%' });
				} else {
I
isidor 已提交
1286
					overlay.style({ top: $this.tabOptions.showTabs ? `${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : 0, height: $this.tabOptions.showTabs ? `calc(100% - ${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : '100%' });
B
Benjamin Pasero 已提交
1287
				}
B
Benjamin Pasero 已提交
1288 1289
			}

B
Benjamin Pasero 已提交
1290 1291
			// Make sure the overlay is visible
			overlay.style({ opacity: 1 });
B
Benjamin Pasero 已提交
1292 1293

			// Indicate a drag over is happening
B
Benjamin Pasero 已提交
1294 1295 1296 1297 1298 1299 1300
			POSITIONS.forEach(p => {
				if (p === position) {
					$this.silos[p].addClass('dragged-over');
				} else {
					$this.silos[p].removeClass('dragged-over');
				}
			});
B
wip  
Benjamin Pasero 已提交
1301 1302
		}

B
Benjamin Pasero 已提交
1303 1304
		function createOverlay(target: HTMLElement): void {
			if (!overlay) {
1305
				const containers = $this.visibleEditors.filter(e => !!e).map(e => e.getContainer());
B
wip  
Benjamin Pasero 已提交
1306
				containers.forEach((container, index) => {
1307
					if (container && DOM.isAncestor(target, container)) {
1308
						const activeContrastBorderColor = $this.getColor(activeContrastBorder);
B
Benjamin Pasero 已提交
1309
						overlay = $('div').style({
I
isidor 已提交
1310
							top: $this.tabOptions.showTabs ? `${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : 0,
B
Benjamin Pasero 已提交
1311 1312
							height: $this.tabOptions.showTabs ? `calc(100% - ${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : '100%',
							backgroundColor: $this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND),
1313 1314 1315 1316
							outlineColor: activeContrastBorderColor,
							outlineOffset: activeContrastBorderColor ? '-2px' : null,
							outlineStyle: activeContrastBorderColor ? 'dashed' : null,
							outlineWidth: activeContrastBorderColor ? '2px' : null
B
Benjamin Pasero 已提交
1317
						}).id(overlayId);
B
wip  
Benjamin Pasero 已提交
1318

B
Benjamin Pasero 已提交
1319 1320 1321
						overlay.appendTo(container);

						overlay.on(DOM.EventType.DROP, (e: DragEvent) => {
B
Benjamin Pasero 已提交
1322
							DOM.EventHelper.stop(e, true);
B
wip  
Benjamin Pasero 已提交
1323 1324 1325 1326
							onDrop(e, index, overlay.getProperty(splitToPropertyKey));
						});

						overlay.on(DOM.EventType.DRAG_OVER, (e: DragEvent) => {
B
Benjamin Pasero 已提交
1327 1328 1329

							// update the dropEffect, otherwise it would look like a "move" operation. but only if we are
							// not dragging a tab actually because there we support both moving as well as copying
1330
							if (!$this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
B
Benjamin Pasero 已提交
1331 1332
								e.dataTransfer.dropEffect = 'copy';
							}
1333

B
wip  
Benjamin Pasero 已提交
1334
							positionOverlay(e, containers.length, index);
B
Benjamin Pasero 已提交
1335 1336 1337
						});

						overlay.on([DOM.EventType.DRAG_LEAVE, DOM.EventType.DRAG_END], () => {
1338
							cleanUp();
B
Benjamin Pasero 已提交
1339
						});
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349

						// Under some circumstances we have seen reports where the drop overlay is not being
						// cleaned up and as such the editor area remains under the overlay so that you cannot
						// type into the editor anymore. This seems related to using VMs and DND via host and
						// guest OS, though some users also saw it without VMs.
						// To protect against this issue we always destroy the overlay as soon as we detect a
						// mouse event over it. The delay is used to guarantee we are not interfering with the
						// actual DROP event that can also trigger a mouse over event.
						overlay.once(DOM.EventType.MOUSE_OVER, () => {
							setTimeout(() => {
1350
								cleanUp();
B
Benjamin Pasero 已提交
1351
							}, 300);
1352
						});
B
Benjamin Pasero 已提交
1353 1354
					}
				});
1355
			}
B
Benjamin Pasero 已提交
1356 1357
		}

B
Benjamin Pasero 已提交
1358
		// let a dropped file open inside Code (only if dropped over editor area)
1359
		this.toUnbind.push(DOM.addDisposableListener(node, DOM.EventType.DROP, (e: DragEvent) => {
1360
			if (e.target === node || DOM.isAncestor(e.target as HTMLElement, node)) {
1361
				DOM.EventHelper.stop(e, true);
1362
				onDrop(e, Position.ONE);
1363
			} else {
1364
				this.updateFromDragAndDrop(node, false);
1365
			}
1366 1367
		}));

1368 1369
		// Drag enter
		let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470
1370
		this.toUnbind.push(DOM.addDisposableListener(node, DOM.EventType.DRAG_ENTER, (e: DragEvent) => {
1371
			if (!$this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
1372 1373 1374 1375 1376 1377 1378
				// we used to check for the dragged resources here (via dnd.extractResources()) but this
				// seems to be not possible on Linux and Windows where during DRAG_ENTER the resources
				// are always undefined up until they are dropped when dragged from the tree. The workaround
				// is to check for a datatransfer type being set. See https://github.com/Microsoft/vscode/issues/25789
				if (!e.dataTransfer.types.length) {
					return; // invalid DND
				}
1379 1380
			}

1381
			counter++;
1382
			this.updateFromDragAndDrop(node, true);
B
Benjamin Pasero 已提交
1383 1384 1385 1386

			const target = <HTMLElement>e.target;
			if (target) {
				if (overlay && target.id !== overlayId) {
1387
					cleanUp(); // somehow we managed to move the mouse quickly out of the current overlay, so destroy it
B
Benjamin Pasero 已提交
1388 1389
				}
				createOverlay(target);
1390 1391

				if (overlay) {
1392
					this.updateFromDragAndDrop(node, false); // if we show an overlay, we can remove the drop feedback from the editor background
1393
				}
B
Benjamin Pasero 已提交
1394
			}
1395 1396 1397
		}));

		// Drag leave
1398
		this.toUnbind.push(DOM.addDisposableListener(node, DOM.EventType.DRAG_LEAVE, (e: DragEvent) => {
1399 1400
			counter--;
			if (counter === 0) {
1401
				this.updateFromDragAndDrop(node, false);
1402
			}
1403 1404
		}));

B
Benjamin Pasero 已提交
1405 1406
		// Drag end (also install globally to be safe)
		[node, window].forEach(container => {
1407
			this.toUnbind.push(DOM.addDisposableListener(container, DOM.EventType.DRAG_END, (e: DragEvent) => {
1408
				counter = 0;
1409
				this.updateFromDragAndDrop(node, false);
1410
				cleanUp();
B
Benjamin Pasero 已提交
1411 1412
			}));
		});
1413 1414
	}

1415
	private createTitleControl(context: IEditorGroup, silo: Builder, container: Builder, instantiationService: IInstantiationService): void {
1416
		const titleAreaControl = instantiationService.createInstance<NoOpTitleAreaControl>(this.tabOptions.showTabs ? NoOpTitleAreaControl : NoOpTitleAreaControl);
1417
		titleAreaControl.create(container.getHTMLElement());
B
Benjamin Pasero 已提交
1418
		titleAreaControl.setContext(context);
1419
		titleAreaControl.refresh(true /* instant */);
1420

1421
		silo.child().setProperty(EditorGroupsControl.TITLE_AREA_CONTROL_KEY, titleAreaControl); // associate with container
1422 1423
	}

1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
	private findPosition(element: HTMLElement): Position {
		let parent = element.parentElement;
		while (parent) {
			for (let i = 0; i < POSITIONS.length; i++) {
				const position = POSITIONS[i];
				if (this.silos[position].getHTMLElement() === parent) {
					return position;
				}
			}

			parent = parent.parentElement;
		}

		return null;
	}

	private hookTitleDragListener(titleContainer: Builder): void {
E
Erich Gamma 已提交
1441 1442 1443
		let wasDragged = false;

		// Allow to reorder positions by dragging the title
1444 1445
		titleContainer.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
			const position = this.findPosition(titleContainer.getHTMLElement());
1446
			const titleAreaControl = this.getTitleAreaControl(position);
B
Benjamin Pasero 已提交
1447
			if (!titleAreaControl.allowDragging((e.target || e.srcElement) as HTMLElement)) {
B
Benjamin Pasero 已提交
1448 1449
				return; // return early if we are not in the drag zone of the title widget
			}
E
Erich Gamma 已提交
1450 1451 1452

			// Reset flag
			wasDragged = false;
1453
			titleAreaControl.setDragged(false);
E
Erich Gamma 已提交
1454 1455

			// Return early if there is only one editor active or the user clicked into the toolbar
B
Benjamin Pasero 已提交
1456
			if (this.getVisibleEditorCount() <= 1) {
E
Erich Gamma 已提交
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466
				return;
			}

			// Only allow for first mouse button click!
			if (e.button !== 0) {
				return;
			}

			DOM.EventHelper.stop(e);

1467
			// Overlay the editor area with a div to be able to capture all mouse events
1468
			// Do NOT cover the title area to prevent missing double click events!
B
Benjamin Pasero 已提交
1469
			const overlayDiv = $('div').style({
1470 1471
				top: `${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px`,
				height: `calc(100% - ${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px)`
1472
			}).id('monaco-workbench-editor-move-overlay');
1473
			overlayDiv.appendTo(this.silos[position]);
E
Erich Gamma 已提交
1474 1475 1476 1477

			// Update flag
			this.dragging = true;

B
Benjamin Pasero 已提交
1478 1479
			const visibleEditorCount = this.getVisibleEditorCount();
			const mouseDownEvent = new StandardMouseEvent(e);
B
Benjamin Pasero 已提交
1480 1481
			const startPos = this.layoutVertically ? mouseDownEvent.posx : mouseDownEvent.posy;
			let oldNewPos: number = null;
E
Erich Gamma 已提交
1482

B
Benjamin Pasero 已提交
1483
			this.silos[position].addClass('drag');
E
Erich Gamma 已提交
1484

B
Benjamin Pasero 已提交
1485
			const $window = $(window);
E
Erich Gamma 已提交
1486 1487 1488
			$window.on(DOM.EventType.MOUSE_MOVE, (e: MouseEvent) => {
				DOM.EventHelper.stop(e, false);

B
Benjamin Pasero 已提交
1489
				const mouseMoveEvent = new StandardMouseEvent(e);
B
Benjamin Pasero 已提交
1490 1491
				const diffPos = (this.layoutVertically ? mouseMoveEvent.posx : mouseMoveEvent.posy) - startPos;
				let newPos: number = null;
E
Erich Gamma 已提交
1492

B
Benjamin Pasero 已提交
1493
				if (Math.abs(diffPos) > 5) {
E
Erich Gamma 已提交
1494 1495 1496 1497 1498
					wasDragged = true;
				}

				switch (position) {

B
Benjamin Pasero 已提交
1499
					// [ ! ]|[ ]: Moves only to the right/bottom but not outside of dimension to the right/bottom
1500
					case Position.ONE: {
B
Benjamin Pasero 已提交
1501
						newPos = Math.max(-1 /* 1px border accomodation */, Math.min(diffPos, this.totalSize - this.silosSize[Position.ONE]));
E
Erich Gamma 已提交
1502 1503 1504
						break;
					}

1505
					case Position.TWO: {
E
Erich Gamma 已提交
1506

B
Benjamin Pasero 已提交
1507
						// [ ]|[ ! ]: Moves only to the left/top but not outside of dimension to the left/top
E
Erich Gamma 已提交
1508
						if (visibleEditorCount === 2) {
B
Benjamin Pasero 已提交
1509
							newPos = Math.min(this.silosSize[Position.ONE], Math.max(-1 /* 1px border accomodation */, this.silosSize[Position.ONE] + diffPos));
E
Erich Gamma 已提交
1510 1511
						}

B
Benjamin Pasero 已提交
1512
						// [ ]|[ ! ]|[ ]: Moves to left/top and right/bottom but not outside of dimensions on both sides
E
Erich Gamma 已提交
1513
						else {
B
Benjamin Pasero 已提交
1514
							newPos = Math.min(this.totalSize - this.silosSize[Position.TWO], Math.max(-1 /* 1px border accomodation */, this.silosSize[Position.ONE] + diffPos));
E
Erich Gamma 已提交
1515 1516 1517 1518
						}
						break;
					}

B
Benjamin Pasero 已提交
1519
					// [ ]|[ ]|[ ! ]: Moves to the right/bottom but not outside of dimension on the left/top side
1520
					case Position.THREE: {
B
Benjamin Pasero 已提交
1521
						newPos = Math.min(this.silosSize[Position.ONE] + this.silosSize[Position.TWO], Math.max(-1 /* 1px border accomodation */, this.silosSize[Position.ONE] + this.silosSize[Position.TWO] + diffPos));
E
Erich Gamma 已提交
1522 1523 1524 1525 1526
						break;
					}
				}

				// Return early if position did not change
B
Benjamin Pasero 已提交
1527
				if (oldNewPos === newPos) {
E
Erich Gamma 已提交
1528 1529 1530
					return;
				}

B
Benjamin Pasero 已提交
1531
				oldNewPos = newPos;
E
Erich Gamma 已提交
1532 1533

				// Live drag Feedback
B
Benjamin Pasero 已提交
1534
				const moveTo: Position = this.findMoveTarget(position, diffPos);
E
Erich Gamma 已提交
1535
				switch (position) {
1536 1537
					case Position.ONE: {
						if (moveTo === Position.ONE || moveTo === null) {
B
Benjamin Pasero 已提交
1538
							this.posSilo(Position.TWO, `${this.silosSize[Position.ONE]}px`, 'auto', '1px');
1539 1540
							this.posSilo(Position.THREE, 'auto', 0);
						} else if (moveTo === Position.TWO) {
B
Benjamin Pasero 已提交
1541
							this.posSilo(Position.TWO, 0, 'auto', 0);
1542 1543 1544 1545 1546 1547
							this.silos[Position.TWO].addClass('draggedunder');
							this.posSilo(Position.THREE, 'auto', 0);
						} else if (moveTo === Position.THREE) {
							this.posSilo(Position.TWO, 0, 'auto');
							this.posSilo(Position.THREE, 'auto', `${this.silosSize[Position.ONE]}px`);
							this.silos[Position.THREE].addClass('draggedunder');
E
Erich Gamma 已提交
1548 1549 1550 1551
						}
						break;
					}

1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562
					case Position.TWO: {
						if (moveTo === Position.ONE) {
							this.posSilo(Position.ONE, `${this.silosSize[Position.TWO]}px`, 'auto');
							this.silos[Position.ONE].addClass('draggedunder');
						} else if (moveTo === Position.TWO || moveTo === null) {
							this.posSilo(Position.ONE, 0, 'auto');
							this.posSilo(Position.THREE, 'auto', 0);
						} else if (moveTo === Position.THREE) {
							this.posSilo(Position.THREE, 'auto', `${this.silosSize[Position.TWO]}px`);
							this.silos[Position.THREE].addClass('draggedunder');
							this.posSilo(Position.ONE, 0, 'auto');
E
Erich Gamma 已提交
1563 1564 1565 1566
						}
						break;
					}

1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577
					case Position.THREE: {
						if (moveTo === Position.ONE) {
							this.posSilo(Position.ONE, `${this.silosSize[Position.THREE]}px`, 'auto');
							this.silos[Position.ONE].addClass('draggedunder');
						} else if (moveTo === Position.TWO) {
							this.posSilo(Position.ONE, 0, 'auto');
							this.posSilo(Position.TWO, `${this.silosSize[Position.ONE] + this.silosSize[Position.THREE]}px`, 'auto');
							this.silos[Position.TWO].addClass('draggedunder');
						} else if (moveTo === Position.THREE || moveTo === null) {
							this.posSilo(Position.ONE, 0, 'auto');
							this.posSilo(Position.TWO, `${this.silosSize[Position.ONE]}px`, 'auto');
E
Erich Gamma 已提交
1578 1579 1580 1581 1582 1583
						}
						break;
					}
				}

				// Move the editor to provide feedback to the user and add class
B
Benjamin Pasero 已提交
1584
				if (newPos !== null) {
B
Benjamin Pasero 已提交
1585
					this.posSilo(position, `${newPos}px`);
B
Benjamin Pasero 已提交
1586
					this.updateFromDragging(position, true);
E
Erich Gamma 已提交
1587 1588 1589 1590 1591 1592 1593 1594 1595
				}
			}).once(DOM.EventType.MOUSE_UP, (e: MouseEvent) => {
				DOM.EventHelper.stop(e, false);

				// Destroy overlay
				overlayDiv.destroy();

				// Update flag
				this.dragging = false;
1596 1597 1598
				if (wasDragged) {
					titleAreaControl.setDragged(true);
				}
E
Erich Gamma 已提交
1599 1600

				// Restore styles
B
Benjamin Pasero 已提交
1601
				this.silos[position].removeClass('drag');
B
Benjamin Pasero 已提交
1602
				this.updateFromDragging(position, false);
B
Benjamin Pasero 已提交
1603
				POSITIONS.forEach(p => this.silos[p].removeClass('draggedunder'));
B
Benjamin Pasero 已提交
1604

1605
				this.posSilo(Position.ONE, 0, 'auto');
B
Benjamin Pasero 已提交
1606
				this.posSilo(Position.TWO, 'auto', 'auto', '1px');
1607
				this.posSilo(Position.THREE, 'auto', 0);
E
Erich Gamma 已提交
1608 1609

				// Find move target
B
Benjamin Pasero 已提交
1610
				const mouseUpEvent = new StandardMouseEvent(e);
B
Benjamin Pasero 已提交
1611 1612
				const diffPos = (this.layoutVertically ? mouseUpEvent.posx : mouseUpEvent.posy) - startPos;
				const moveTo: Position = this.findMoveTarget(position, diffPos);
E
Erich Gamma 已提交
1613 1614 1615

				// Move to valid position if any
				if (moveTo !== null) {
B
Benjamin Pasero 已提交
1616 1617 1618
					// TODO@Ben remove me after a while
					/* __GDPR__
						"editorGroupMoved" : {
R
Ramya Achutha Rao 已提交
1619 1620
							"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
							"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
B
Benjamin Pasero 已提交
1621 1622 1623
						}
					*/
					this.telemetryService.publicLog('editorGroupMoved', { source: position, to: moveTo });
1624
					this.editorGroupService.moveGroup(position, moveTo);
E
Erich Gamma 已提交
1625 1626 1627 1628 1629 1630 1631
				}

				// Otherwise layout to restore proper positioning
				else {
					this.layoutContainers();
				}

B
Benjamin Pasero 已提交
1632 1633
				// If not dragging, make editor group active unless already active
				if (!wasDragged && position !== this.getActivePosition()) {
1634
					this.editorGroupService.focusGroup(position);
E
Erich Gamma 已提交
1635 1636 1637 1638 1639 1640 1641
				}

				$window.off('mousemove');
			});
		});
	}

B
Benjamin Pasero 已提交
1642 1643 1644 1645 1646 1647 1648 1649
	private updateFromDragging(position: Position, isDragging: boolean): void {
		const silo = this.silos[position];
		if (silo.hasClass('dragging') === isDragging) {
			return; // avoid repeated work
		}

		let borderColor = null;
		if (isDragging) {
1650
			DOM.addClass(this.parent, 'dragging');
B
Benjamin Pasero 已提交
1651
			silo.addClass('dragging');
1652
			borderColor = this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder);
B
Benjamin Pasero 已提交
1653
		} else {
1654
			DOM.removeClass(this.parent, 'dragging');
B
Benjamin Pasero 已提交
1655 1656 1657 1658 1659
			silo.removeClass('dragging');
		}

		silo.style(this.layoutVertically ? 'border-left-color' : 'border-top-color', borderColor);
		silo.style(this.layoutVertically ? 'border-right-color' : 'border-bottom-color', borderColor);
B
Benjamin Pasero 已提交
1660 1661 1662 1663 1664

		// Back to normal styles once dragging stops
		if (!isDragging) {
			this.updateStyles();
		}
B
Benjamin Pasero 已提交
1665 1666
	}

1667
	private updateFromDragAndDrop(element: HTMLElement, isDraggedOver: boolean): void {
1668
		const background = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
B
Benjamin Pasero 已提交
1669 1670
		element.style.backgroundColor = background;

1671
		const activeContrastBorderColor = this.getColor(activeContrastBorder);
1672 1673 1674 1675 1676 1677
		element.style.outlineColor = isDraggedOver ? activeContrastBorderColor : null;
		element.style.outlineStyle = isDraggedOver && activeContrastBorderColor ? 'dashed' : null;
		element.style.outlineWidth = isDraggedOver && activeContrastBorderColor ? '2px' : null;
		element.style.outlineOffset = isDraggedOver && activeContrastBorderColor ? '-2px' : null;

		DOM.toggleClass(element, 'dragged-over', isDraggedOver);
B
Benjamin Pasero 已提交
1678 1679
	}

B
Benjamin Pasero 已提交
1680 1681
	private posSilo(pos: number, leftTop: string | number, rightBottom?: string | number, borderLeftTopWidth?: string | number): void {
		let style: any;
B
Benjamin Pasero 已提交
1682
		if (this.layoutVertically) {
B
Benjamin Pasero 已提交
1683 1684 1685 1686 1687 1688
			style = { left: leftTop };

			if (typeof rightBottom === 'number' || typeof rightBottom === 'string') {
				style['right'] = rightBottom;
			}

B
Benjamin Pasero 已提交
1689 1690 1691
			if (typeof borderLeftTopWidth === 'number' || typeof borderLeftTopWidth === 'string') {
				style['borderLeftWidth'] = borderLeftTopWidth;
			}
B
Benjamin Pasero 已提交
1692
		} else {
B
Benjamin Pasero 已提交
1693 1694 1695 1696 1697 1698
			style = { top: leftTop };

			if (typeof rightBottom === 'number' || typeof rightBottom === 'string') {
				style['bottom'] = rightBottom;
			}

B
Benjamin Pasero 已提交
1699 1700 1701
			if (typeof borderLeftTopWidth === 'number' || typeof borderLeftTopWidth === 'string') {
				style['borderTopWidth'] = borderLeftTopWidth;
			}
B
Benjamin Pasero 已提交
1702
		}
B
Benjamin Pasero 已提交
1703 1704

		this.silos[pos].style(style);
B
Benjamin Pasero 已提交
1705 1706 1707
	}

	private findMoveTarget(position: Position, diffPos: number): Position {
B
Benjamin Pasero 已提交
1708
		const visibleEditorCount = this.getVisibleEditorCount();
E
Erich Gamma 已提交
1709 1710

		switch (position) {
1711
			case Position.ONE: {
E
Erich Gamma 已提交
1712 1713

				// [ ! ]|[] -> []|[ ! ]
1714 1715
				if (visibleEditorCount === 2 && (diffPos >= this.silosSize[Position.ONE] / 2 || diffPos >= this.silosSize[Position.TWO] / 2)) {
					return Position.TWO;
E
Erich Gamma 已提交
1716 1717 1718
				}

				// [ ! ]|[]|[] -> []|[]|[ ! ]
1719 1720
				if (visibleEditorCount === 3 && (diffPos >= this.silosSize[Position.ONE] / 2 + this.silosSize[Position.TWO] || diffPos >= this.silosSize[Position.THREE] / 2 + this.silosSize[Position.TWO])) {
					return Position.THREE;
E
Erich Gamma 已提交
1721 1722 1723
				}

				// [ ! ]|[]|[] -> []|[ ! ]|[]
1724 1725
				if (visibleEditorCount === 3 && (diffPos >= this.silosSize[Position.ONE] / 2 || diffPos >= this.silosSize[Position.TWO] / 2)) {
					return Position.TWO;
E
Erich Gamma 已提交
1726 1727 1728 1729
				}
				break;
			}

1730
			case Position.TWO: {
B
Benjamin Pasero 已提交
1731
				if (visibleEditorCount === 2 && diffPos > 0) {
B
Benjamin Pasero 已提交
1732
					return null; // Return early since TWO cannot be moved to the THREE unless there is a THREE position
E
Erich Gamma 已提交
1733 1734 1735
				}

				// []|[ ! ] -> [ ! ]|[]
1736 1737
				if (visibleEditorCount === 2 && (Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2 || Math.abs(diffPos) >= this.silosSize[Position.ONE] / 2)) {
					return Position.ONE;
E
Erich Gamma 已提交
1738 1739 1740
				}

				// []|[ ! ]|[] -> [ ! ]|[]|[]
1741 1742
				if (visibleEditorCount === 3 && ((diffPos < 0 && Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2) || (diffPos < 0 && Math.abs(diffPos) >= this.silosSize[Position.ONE] / 2))) {
					return Position.ONE;
E
Erich Gamma 已提交
1743 1744 1745
				}

				// []|[ ! ]|[] -> []|[]|[ ! ]
1746 1747
				if (visibleEditorCount === 3 && ((diffPos > 0 && Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2) || (diffPos > 0 && Math.abs(diffPos) >= this.silosSize[Position.THREE] / 2))) {
					return Position.THREE;
E
Erich Gamma 已提交
1748 1749 1750 1751
				}
				break;
			}

1752
			case Position.THREE: {
B
Benjamin Pasero 已提交
1753
				if (diffPos > 0) {
B
Benjamin Pasero 已提交
1754
					return null; // Return early since THREE cannot be moved more to the THREE
E
Erich Gamma 已提交
1755 1756 1757
				}

				// []|[]|[ ! ] -> [ ! ]|[]|[]
1758 1759
				if (Math.abs(diffPos) >= this.silosSize[Position.THREE] / 2 + this.silosSize[Position.TWO] || Math.abs(diffPos) >= this.silosSize[Position.ONE] / 2 + this.silosSize[Position.TWO]) {
					return Position.ONE;
E
Erich Gamma 已提交
1760 1761 1762
				}

				// []|[]|[ ! ] -> []|[ ! ]|[]
1763 1764
				if (Math.abs(diffPos) >= this.silosSize[Position.THREE] / 2 || Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2) {
					return Position.TWO;
E
Erich Gamma 已提交
1765 1766 1767 1768 1769 1770 1771 1772
				}
				break;
			}
		}

		return null;
	}

M
Maxime Quandalle 已提交
1773
	private centerSash(a: Position, b: Position): void {
B
Benjamin Pasero 已提交
1774 1775 1776 1777 1778
		const sumSize = this.silosSize[a] + this.silosSize[b];
		const meanSize = sumSize / 2;
		this.silosSize[a] = meanSize;
		this.silosSize[b] = sumSize - meanSize;

M
Maxime Quandalle 已提交
1779 1780 1781
		this.layoutContainers();
	}

B
Benjamin Pasero 已提交
1782
	private onSashOneDragStart(): void {
1783
		this.startSiloOneSize = this.silosSize[Position.ONE];
E
Erich Gamma 已提交
1784 1785
	}

B
Benjamin Pasero 已提交
1786
	private onSashOneDrag(e: ISashEvent): void {
1787
		let oldSiloOneSize = this.silosSize[Position.ONE];
B
Benjamin Pasero 已提交
1788 1789
		let diffSize = this.layoutVertically ? (e.currentX - e.startX) : (e.currentY - e.startY);
		let newSiloOneSize = this.startSiloOneSize + diffSize;
E
Erich Gamma 已提交
1790 1791

		// Side-by-Side
B
Benjamin Pasero 已提交
1792
		if (this.sashTwo.isHidden()) {
E
Erich Gamma 已提交
1793

B
Benjamin Pasero 已提交
1794 1795 1796
			// []|[      ] : left/top side can not get smaller than the minimal editor size
			if (newSiloOneSize < this.minSize) {
				newSiloOneSize = this.minSize;
E
Erich Gamma 已提交
1797 1798
			}

B
Benjamin Pasero 已提交
1799 1800 1801
			// [      ]|[] : right/bottom side can not get smaller than the minimal editor size
			else if (this.totalSize - newSiloOneSize < this.minSize) {
				newSiloOneSize = this.totalSize - this.minSize;
E
Erich Gamma 已提交
1802 1803
			}

B
Benjamin Pasero 已提交
1804
			// [ <-]|[      ] : left/top side can snap into minimized
B
Benjamin Pasero 已提交
1805 1806
			else if (newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) {
				newSiloOneSize = this.minSize;
E
Erich Gamma 已提交
1807 1808
			}

B
Benjamin Pasero 已提交
1809
			// [      ]|[-> ] : right/bottom side can snap into minimized
B
Benjamin Pasero 已提交
1810 1811
			else if (this.totalSize - newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) {
				newSiloOneSize = this.totalSize - this.minSize;
E
Erich Gamma 已提交
1812 1813
			}

1814 1815
			this.silosSize[Position.ONE] = newSiloOneSize;
			this.silosSize[Position.TWO] = this.totalSize - newSiloOneSize;
E
Erich Gamma 已提交
1816 1817 1818 1819 1820
		}

		// Side-by-Side-by-Side
		else {

B
Benjamin Pasero 已提交
1821 1822 1823
			// [!]|[      ]|[  ] : left/top side can not get smaller than the minimal editor size
			if (newSiloOneSize < this.minSize) {
				newSiloOneSize = this.minSize;
E
Erich Gamma 已提交
1824 1825
			}

B
Benjamin Pasero 已提交
1826
			// [      ]|[!]|[  ] : center side can not get smaller than the minimal editor size
1827
			else if (this.totalSize - newSiloOneSize - this.silosSize[Position.THREE] < this.minSize) {
E
Erich Gamma 已提交
1828

B
Benjamin Pasero 已提交
1829
				// [      ]|[ ]|[!] : right/bottom side can not get smaller than the minimal editor size
1830
				if (this.totalSize - newSiloOneSize - this.silosSize[Position.TWO] < this.minSize) {
B
Benjamin Pasero 已提交
1831
					newSiloOneSize = this.totalSize - (2 * this.minSize);
1832
					this.silosSize[Position.TWO] = this.silosSize[Position.THREE] = this.minSize;
E
Erich Gamma 已提交
1833 1834
				}

B
Benjamin Pasero 已提交
1835
				// [      ]|[ ]|[-> ] : right/bottom side can snap into minimized
1836 1837
				else if (this.totalSize - newSiloOneSize - this.silosSize[Position.TWO] - this.snapToMinimizeThresholdSize <= this.minSize) {
					this.silosSize[Position.THREE] = this.minSize;
E
Erich Gamma 已提交
1838 1839
				}

B
Benjamin Pasero 已提交
1840
				// [      ]|[ ]|[ ] : right/bottom side shrinks
E
Erich Gamma 已提交
1841
				else {
1842
					this.silosSize[Position.THREE] = this.silosSize[Position.THREE] - (newSiloOneSize - oldSiloOneSize);
E
Erich Gamma 已提交
1843 1844
				}

B
Benjamin Pasero 已提交
1845
				this.sashTwo.layout();
E
Erich Gamma 已提交
1846 1847
			}

B
Benjamin Pasero 已提交
1848
			// [ <-]|[      ]|[  ] : left/top side can snap into minimized
B
Benjamin Pasero 已提交
1849 1850
			else if (newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) {
				newSiloOneSize = this.minSize;
E
Erich Gamma 已提交
1851 1852 1853
			}

			// [      ]|[-> ]|[  ] : center side can snap into minimized
1854 1855
			else if (this.totalSize - this.silosSize[Position.THREE] - newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) {
				newSiloOneSize = this.totalSize - this.silosSize[Position.THREE] - this.minSize;
E
Erich Gamma 已提交
1856 1857
			}

1858 1859
			this.silosSize[Position.ONE] = newSiloOneSize;
			this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE] - this.silosSize[Position.THREE];
E
Erich Gamma 已提交
1860 1861
		}

1862 1863
		// We allow silos to turn into minimized state from user dragging the sash,
		// so we need to update our stored state of minimized silos accordingly
B
Benjamin Pasero 已提交
1864
		this.enableMinimizedState();
1865

E
Erich Gamma 已提交
1866 1867 1868 1869
		// Pass on to containers
		this.layoutContainers();
	}

B
Benjamin Pasero 已提交
1870 1871
	private onSashOneDragEnd(): void {
		this.sashOne.layout();
B
Benjamin Pasero 已提交
1872
		this.sashTwo.layout(); // Moving sash one might have also moved sash two, so layout() both
E
Erich Gamma 已提交
1873 1874 1875
		this.focusNextNonMinimized();
	}

B
Benjamin Pasero 已提交
1876
	private onSashOneReset(): void {
1877
		this.centerSash(Position.ONE, Position.TWO);
B
Benjamin Pasero 已提交
1878
		this.sashOne.layout();
M
Maxime Quandalle 已提交
1879 1880
	}

B
Benjamin Pasero 已提交
1881
	private onSashTwoDragStart(): void {
1882
		this.startSiloThreeSize = this.silosSize[Position.THREE];
E
Erich Gamma 已提交
1883 1884
	}

B
Benjamin Pasero 已提交
1885
	private onSashTwoDrag(e: ISashEvent): void {
1886
		let oldSiloThreeSize = this.silosSize[Position.THREE];
B
Benjamin Pasero 已提交
1887 1888
		let diffSize = this.layoutVertically ? (-e.currentX + e.startX) : (-e.currentY + e.startY);
		let newSiloThreeSize = this.startSiloThreeSize + diffSize;
E
Erich Gamma 已提交
1889

B
Benjamin Pasero 已提交
1890 1891 1892
		// [  ]|[      ]|[!] : right/bottom side can not get smaller than the minimal editor size
		if (newSiloThreeSize < this.minSize) {
			newSiloThreeSize = this.minSize;
E
Erich Gamma 已提交
1893 1894
		}

B
Benjamin Pasero 已提交
1895
		// [      ]|[!]|[  ] : center side can not get smaller than the minimal editor size
1896
		else if (this.totalSize - newSiloThreeSize - this.silosSize[Position.ONE] < this.minSize) {
E
Erich Gamma 已提交
1897

B
Benjamin Pasero 已提交
1898
			// [!]|[ ]|[    ] : left/top side can not get smaller than the minimal editor size
1899
			if (this.totalSize - newSiloThreeSize - this.silosSize[Position.TWO] < this.minSize) {
B
Benjamin Pasero 已提交
1900
				newSiloThreeSize = this.totalSize - (2 * this.minSize);
1901
				this.silosSize[Position.ONE] = this.silosSize[Position.TWO] = this.minSize;
E
Erich Gamma 已提交
1902 1903
			}

B
Benjamin Pasero 已提交
1904
			// [ <-]|[ ]|[    ] : left/top side can snap into minimized
1905 1906
			else if (this.totalSize - newSiloThreeSize - this.silosSize[Position.TWO] - this.snapToMinimizeThresholdSize <= this.minSize) {
				this.silosSize[Position.ONE] = this.minSize;
E
Erich Gamma 已提交
1907 1908
			}

B
Benjamin Pasero 已提交
1909
			// [  ]|[ ]|[   ] : left/top side shrinks
E
Erich Gamma 已提交
1910
			else {
1911
				this.silosSize[Position.ONE] = this.silosSize[Position.ONE] - (newSiloThreeSize - oldSiloThreeSize);
E
Erich Gamma 已提交
1912 1913
			}

B
Benjamin Pasero 已提交
1914
			this.sashOne.layout();
E
Erich Gamma 已提交
1915 1916
		}

B
Benjamin Pasero 已提交
1917
		// [ ]|[      ]|[-> ] : right/bottom side can snap into minimized
B
Benjamin Pasero 已提交
1918 1919
		else if (newSiloThreeSize - this.snapToMinimizeThresholdSize <= this.minSize) {
			newSiloThreeSize = this.minSize;
E
Erich Gamma 已提交
1920 1921 1922
		}

		// [ ]|[ <-]|[      ] : center side can snap into minimized
1923 1924
		else if (this.totalSize - this.silosSize[Position.ONE] - newSiloThreeSize - this.snapToMinimizeThresholdSize <= this.minSize) {
			newSiloThreeSize = this.totalSize - this.silosSize[Position.ONE] - this.minSize;
E
Erich Gamma 已提交
1925 1926
		}

1927 1928
		this.silosSize[Position.THREE] = newSiloThreeSize;
		this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE] - this.silosSize[Position.THREE];
E
Erich Gamma 已提交
1929

1930 1931
		// We allow silos to turn into minimized state from user dragging the sash,
		// so we need to update our stored state of minimized silos accordingly
B
Benjamin Pasero 已提交
1932
		this.enableMinimizedState();
1933 1934

		// Pass on to containers
E
Erich Gamma 已提交
1935 1936 1937
		this.layoutContainers();
	}

B
Benjamin Pasero 已提交
1938
	private onSashTwoDragEnd(): void {
B
Benjamin Pasero 已提交
1939
		this.sashOne.layout(); // Moving sash one might have also moved sash two, so layout() both
B
Benjamin Pasero 已提交
1940 1941
		this.sashTwo.layout();

E
Erich Gamma 已提交
1942 1943 1944
		this.focusNextNonMinimized();
	}

B
Benjamin Pasero 已提交
1945
	private onSashTwoReset(): void {
1946
		this.centerSash(Position.TWO, Position.THREE);
B
Benjamin Pasero 已提交
1947 1948

		this.sashTwo.layout();
M
Maxime Quandalle 已提交
1949 1950
	}

1951
	private get centeredEditorAvailableSize(): number {
1952
		return this.dimension.width - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
1953
	}
B
Benjamin Pasero 已提交
1954

1955
	private get centeredEditorSize(): number {
S
SrTobi 已提交
1956
		return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferredSize);
1957
	}
B
Benjamin Pasero 已提交
1958

1959 1960
	private get centeredEditorPosition(): number {
		return EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN + this.centeredEditorLeftMarginRatio * (this.centeredEditorAvailableSize - this.centeredEditorSize);
S
SrTobi 已提交
1961 1962
	}

1963 1964 1965 1966
	private onCenterSashDragStart(): void {
		this.centeredEditorDragStartPosition = this.centeredEditorPosition;
		this.centeredEditorDragStartSize = this.centeredEditorSize;
	}
S
SrTobi 已提交
1967

1968 1969 1970 1971 1972 1973 1974 1975
	private onCenterSashDrag(sash: Sash, e: ISashEvent): void {
		const sashesCoupled = !e.altKey;
		const delta = sash === this.centeredEditorSashLeft ? e.startX - e.currentX : e.currentX - e.startX;
		const size = this.centeredEditorDragStartSize + (sashesCoupled ? 2 * delta : delta);
		let position = this.centeredEditorDragStartPosition;
		if (sash === this.centeredEditorSashLeft || sashesCoupled) {
			position -= delta;
		}
I
isidor 已提交
1976

1977
		if (size > 3 * this.minSize && size < this.centeredEditorAvailableSize) {
S
SrTobi 已提交
1978
			this.centeredEditorPreferredSize = size;
1979 1980 1981 1982
			position -= EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN;
			position = Math.min(position, this.centeredEditorAvailableSize - this.centeredEditorSize);
			position = Math.max(0, position);
			this.centeredEditorLeftMarginRatio = position / (this.centeredEditorAvailableSize - this.centeredEditorSize);
S
SrTobi 已提交
1983

1984 1985
			this.layoutContainers();
		}
S
SrTobi 已提交
1986 1987
	}

1988
	private storeCenteredLayoutData(): void {
B
Benjamin Pasero 已提交
1989
		const data: CenteredEditorLayoutData = {
1990 1991
			leftMarginRatio: this.centeredEditorLeftMarginRatio,
			size: this.centeredEditorSize
1992
		};
1993
		this.storageService.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.WORKSPACE);
S
SrTobi 已提交
1994 1995
	}

E
Erich Gamma 已提交
1996 1997 1998 1999 2000
	public getVerticalSashTop(sash: Sash): number {
		return 0;
	}

	public getVerticalSashLeft(sash: Sash): number {
S
SrTobi 已提交
2001 2002 2003 2004 2005 2006
		switch (sash) {
			case this.sashOne:
				return this.silosSize[Position.ONE];
			case this.sashTwo:
				return this.silosSize[Position.TWO] + this.silosSize[Position.ONE];
			case this.centeredEditorSashLeft:
2007
				return this.centeredEditorPosition;
S
SrTobi 已提交
2008
			case this.centeredEditorSashRight:
2009
				return this.centeredEditorPosition + this.centeredEditorSize;
S
SrTobi 已提交
2010 2011 2012
			default:
				return 0;
		}
E
Erich Gamma 已提交
2013 2014 2015 2016 2017 2018
	}

	public getVerticalSashHeight(sash: Sash): number {
		return this.dimension.height;
	}

B
Benjamin Pasero 已提交
2019
	public getHorizontalSashTop(sash: Sash): number {
2020
		return sash === this.sashOne ? this.silosSize[Position.ONE] : this.silosSize[Position.TWO] + this.silosSize[Position.ONE];
B
Benjamin Pasero 已提交
2021 2022 2023 2024 2025 2026 2027 2028 2029 2030
	}

	public getHorizontalSashLeft(sash: Sash): number {
		return 0;
	}

	public getHorizontalSashWidth(sash: Sash): number {
		return this.dimension.width;
	}

E
Erich Gamma 已提交
2031 2032 2033 2034
	public isDragging(): boolean {
		return this.dragging;
	}

2035
	public layout(dimension: DOM.Dimension): void;
E
Erich Gamma 已提交
2036 2037
	public layout(position: Position): void;
	public layout(arg: any): void {
2038 2039
		if (arg instanceof DOM.Dimension) {
			this.layoutControl(<DOM.Dimension>arg);
E
Erich Gamma 已提交
2040 2041 2042 2043 2044
		} else {
			this.layoutEditor(<Position>arg);
		}
	}

2045
	private layoutControl(dimension: DOM.Dimension): void {
E
Erich Gamma 已提交
2046 2047 2048 2049 2050 2051 2052 2053 2054
		let oldDimension = this.dimension;
		this.dimension = dimension;

		// Use the current dimension in case an editor was opened before we had any dimension
		if (!oldDimension || !oldDimension.width || !oldDimension.height) {
			oldDimension = dimension;
		}

		// Apply to visible editors
B
Benjamin Pasero 已提交
2055
		let totalSize = 0;
E
Erich Gamma 已提交
2056 2057

		// Set preferred dimensions based on ratio to previous dimenions
2058
		let wasInitialRatioRestored = false;
B
Benjamin Pasero 已提交
2059
		const oldTotalSize = this.layoutVertically ? oldDimension.width : oldDimension.height;
B
Benjamin Pasero 已提交
2060
		POSITIONS.forEach(position => {
E
Erich Gamma 已提交
2061 2062
			if (this.visibleEditors[position]) {

B
Benjamin Pasero 已提交
2063
				// Keep minimized editors in tact by not letting them grow if we have size to give
2064
				if (!this.isSiloMinimized(position)) {
B
Benjamin Pasero 已提交
2065
					let siloSizeRatio: number;
E
Erich Gamma 已提交
2066 2067 2068

					// We have some stored initial ratios when the editor was restored on startup
					// Use those ratios over anything else but only once.
B
Benjamin Pasero 已提交
2069 2070 2071
					if (this.silosInitialRatio && types.isNumber(this.silosInitialRatio[position])) {
						siloSizeRatio = this.silosInitialRatio[position];
						delete this.silosInitialRatio[position]; // dont use again
2072
						wasInitialRatioRestored = true;
E
Erich Gamma 已提交
2073
					} else {
B
Benjamin Pasero 已提交
2074
						siloSizeRatio = this.silosSize[position] / oldTotalSize;
E
Erich Gamma 已提交
2075 2076
					}

B
Benjamin Pasero 已提交
2077
					this.silosSize[position] = Math.max(Math.round(this.totalSize * siloSizeRatio), this.minSize);
E
Erich Gamma 已提交
2078 2079
				}

B
Benjamin Pasero 已提交
2080
				totalSize += this.silosSize[position];
E
Erich Gamma 已提交
2081 2082 2083
			}
		});

2084 2085 2086
		// When restoring from an initial ratio state, we treat editors of min-size as
		// minimized, so we need to update our stored state of minimized silos accordingly
		if (wasInitialRatioRestored) {
B
Benjamin Pasero 已提交
2087
			this.enableMinimizedState();
2088 2089
		}

B
Benjamin Pasero 已提交
2090 2091 2092
		// Compensate for overflow either through rounding error or min editor size
		if (totalSize > 0) {
			let overflow = totalSize - this.totalSize;
E
Erich Gamma 已提交
2093

B
Benjamin Pasero 已提交
2094
			// We have size to give
E
Erich Gamma 已提交
2095 2096
			if (overflow < 0) {

B
Benjamin Pasero 已提交
2097
				// Find the first position from left/top to right/bottom that is not minimized
B
Benjamin Pasero 已提交
2098
				// to give size. This ensures that minimized editors are left like
E
Erich Gamma 已提交
2099 2100 2101
				// that if the user chose this layout.
				let positionToGive: Position = null;
				POSITIONS.forEach(position => {
2102
					if (this.visibleEditors[position] && positionToGive === null && !this.isSiloMinimized(position)) {
E
Erich Gamma 已提交
2103 2104 2105 2106 2107
						positionToGive = position;
					}
				});

				if (positionToGive === null) {
B
Benjamin Pasero 已提交
2108
					positionToGive = Position.ONE; // maybe all are minimized, so give ONE the extra size
E
Erich Gamma 已提交
2109 2110
				}

B
Benjamin Pasero 已提交
2111
				this.silosSize[positionToGive] -= overflow;
E
Erich Gamma 已提交
2112 2113
			}

B
Benjamin Pasero 已提交
2114
			// We have size to take
E
Erich Gamma 已提交
2115
			else if (overflow > 0) {
B
Benjamin Pasero 已提交
2116
				POSITIONS.forEach(position => {
B
Benjamin Pasero 已提交
2117
					const maxCompensation = this.silosSize[position] - this.minSize;
E
Erich Gamma 已提交
2118
					if (maxCompensation >= overflow) {
B
Benjamin Pasero 已提交
2119
						this.silosSize[position] -= overflow;
E
Erich Gamma 已提交
2120 2121
						overflow = 0;
					} else if (maxCompensation > 0) {
2122 2123
						this.silosSize[position] -= maxCompensation;
						overflow -= maxCompensation;
E
Erich Gamma 已提交
2124 2125 2126 2127 2128 2129
					}
				});
			}
		}

		// Sash positioning
B
Benjamin Pasero 已提交
2130 2131
		this.sashOne.layout();
		this.sashTwo.layout();
E
Erich Gamma 已提交
2132 2133 2134 2135 2136 2137 2138 2139

		// Pass on to Editor Containers
		this.layoutContainers();
	}

	private layoutContainers(): void {

		// Layout containers
B
Benjamin Pasero 已提交
2140
		POSITIONS.forEach(position => {
B
Benjamin Pasero 已提交
2141 2142 2143 2144
			const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
			const siloHeight = this.layoutVertically ? this.dimension.height : this.silosSize[position];

			this.silos[position].size(siloWidth, siloHeight);
E
Erich Gamma 已提交
2145 2146
		});

B
Benjamin Pasero 已提交
2147
		if (this.layoutVertically) {
2148
			this.silos[Position.TWO].position(0, null, null, this.silosSize[Position.ONE]);
E
Erich Gamma 已提交
2149
		} else {
2150
			this.silos[Position.TWO].position(this.silosSize[Position.ONE], null, null, 0);
E
Erich Gamma 已提交
2151 2152 2153
		}

		// Visibility
B
Benjamin Pasero 已提交
2154
		POSITIONS.forEach(position => {
B
Benjamin Pasero 已提交
2155 2156 2157 2158
			if (this.visibleEditors[position] && this.silos[position].isHidden()) {
				this.silos[position].show();
			} else if (!this.visibleEditors[position] && !this.silos[position].isHidden()) {
				this.silos[position].hide();
E
Erich Gamma 已提交
2159 2160 2161
			}
		});

B
Benjamin Pasero 已提交
2162
		// Layout centered Editor (only in vertical layout when one group is opened)
I
isidor 已提交
2163 2164
		const doCentering = this.partService.isEditorLayoutCentered() && this.stacks.groups.length === 1 &&
			this.visibleEditors[Position.ONE] && this.visibleEditors[Position.ONE].supportsCenteredLayout();
S
SrTobi 已提交
2165 2166 2167
		if (doCentering && !this.centeredEditorActive) {
			this.centeredEditorSashLeft.show();
			this.centeredEditorSashRight.show();
2168 2169

			// no size set yet. Calculate a default value
S
SrTobi 已提交
2170
			if (!this.centeredEditorPreferredSize) {
2171 2172
				this.resetCenteredEditor(false);
			}
S
SrTobi 已提交
2173 2174 2175 2176 2177
		} else if (!doCentering && this.centeredEditorActive) {
			this.centeredEditorSashLeft.hide();
			this.centeredEditorSashRight.hide();
		}
		this.centeredEditorActive = doCentering;
2178
		this.silos[Position.ONE].setClass('centered', doCentering);
S
SrTobi 已提交
2179 2180 2181 2182 2183 2184

		if (this.centeredEditorActive) {
			this.centeredEditorSashLeft.layout();
			this.centeredEditorSashRight.layout();
		}

2185
		// Layout visible editors
B
Benjamin Pasero 已提交
2186
		POSITIONS.forEach(position => {
E
Erich Gamma 已提交
2187 2188
			this.layoutEditor(position);
		});
2189 2190

		// Layout title controls
2191
		POSITIONS.forEach(position => this.layoutTitleControl(position));
B
Benjamin Pasero 已提交
2192 2193 2194

		// Update minimized state
		this.updateMinimizedState();
E
Erich Gamma 已提交
2195 2196
	}

2197 2198 2199
	private layoutTitleControl(position: Position): void {
		const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;

2200
		this.getTitleAreaControl(position).layout(new DOM.Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
2201 2202
	}

E
Erich Gamma 已提交
2203
	private layoutEditor(position: Position): void {
B
Benjamin Pasero 已提交
2204
		const editorSize = this.silosSize[position];
S
SrTobi 已提交
2205 2206
		const editor = this.visibleEditors[position];
		if (editorSize && editor) {
B
Benjamin Pasero 已提交
2207
			let editorWidth = this.layoutVertically ? editorSize : this.dimension.width;
2208
			let editorHeight = (this.layoutVertically ? this.dimension.height : this.silosSize[position]) - EditorGroupsControl.EDITOR_TITLE_HEIGHT;
2209

S
SrTobi 已提交
2210 2211
			let editorPosition = 0;
			if (this.centeredEditorActive) {
2212 2213
				editorWidth = this.centeredEditorSize;
				editorPosition = this.centeredEditorPosition;
S
SrTobi 已提交
2214 2215
			}

B
Benjamin Pasero 已提交
2216 2217 2218 2219 2220 2221
			if (position !== Position.ONE) {
				if (this.layoutVertically) {
					editorWidth--; // accomodate for 1px left-border in containers TWO, THREE when laying out vertically
				} else {
					editorHeight--; // accomodate for 1px top-border in containers TWO, THREE when laying out horizontally
				}
2222
			}
B
Benjamin Pasero 已提交
2223

2224
			const editorContainer = editor.getContainer();
2225 2226 2227
			editorContainer.style.marginLeft = this.centeredEditorActive ? `${editorPosition}px` : null;
			editorContainer.style.width = this.centeredEditorActive ? `${editorWidth}px` : null;
			editorContainer.style.borderColor = this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null;
2228
			editor.layout(new DOM.Dimension(editorWidth, editorHeight));
S
SrTobi 已提交
2229 2230 2231 2232
		}
	}

	private resetCenteredEditor(layout: boolean = true) {
2233
		this.centeredEditorLeftMarginRatio = 0.5;
S
SrTobi 已提交
2234
		this.centeredEditorPreferredSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
S
SrTobi 已提交
2235 2236
		if (layout) {
			this.layoutContainers();
E
Erich Gamma 已提交
2237
		}
2238
		this.storageService.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.WORKSPACE);
E
Erich Gamma 已提交
2239 2240
	}

2241
	public getInstantiationService(position: Position): IInstantiationService {
2242
		return this.getFromContainer(position, EditorGroupsControl.INSTANTIATION_SERVICE_KEY);
2243 2244
	}

E
Erich Gamma 已提交
2245
	public getProgressBar(position: Position): ProgressBar {
2246
		return this.getFromContainer(position, EditorGroupsControl.PROGRESS_BAR_CONTROL_KEY);
E
Erich Gamma 已提交
2247 2248
	}

2249
	private getTitleAreaControl(position: Position): NoOpTitleAreaControl {
2250
		return this.getFromContainer(position, EditorGroupsControl.TITLE_AREA_CONTROL_KEY);
2251 2252
	}

B
Benjamin Pasero 已提交
2253 2254 2255 2256 2257 2258 2259
	private getFromContainer(position: Position, key: string): any {
		const silo = this.silos[position];

		return silo ? silo.child().getProperty(key) : void 0;
	}

	public updateTitleAreas(refreshActive?: boolean): void {
2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270
		POSITIONS.forEach(position => {
			const group = this.stacks.groupAt(position);
			if (!group) {
				return;
			}

			const titleControl = this.getTitleAreaControl(position);
			if (!titleControl) {
				return;
			}

B
Benjamin Pasero 已提交
2271 2272 2273
			// Make sure the active group is shown in the title
			// and refresh it if we are instructed to refresh it
			if (refreshActive && group.isActive) {
2274 2275 2276 2277
				titleControl.setContext(group);
				titleControl.refresh(true);
			}

B
Benjamin Pasero 已提交
2278
			// Otherwise, just refresh the toolbar
2279 2280 2281 2282 2283 2284
			else {
				titleControl.updateEditorActionsToolbar();
			}
		});
	}

B
Benjamin Pasero 已提交
2285
	public updateProgress(position: Position, state: ProgressState): void {
B
Benjamin Pasero 已提交
2286 2287 2288 2289 2290
		const progressbar = this.getProgressBar(position);
		if (!progressbar) {
			return;
		}

B
Benjamin Pasero 已提交
2291 2292
		switch (state) {
			case ProgressState.INFINITE:
2293
				progressbar.infinite().show();
B
Benjamin Pasero 已提交
2294 2295
				break;
			case ProgressState.DONE:
2296
				progressbar.done().hide();
B
Benjamin Pasero 已提交
2297 2298
				break;
			case ProgressState.STOP:
2299
				progressbar.stop().hide();
B
Benjamin Pasero 已提交
2300 2301 2302 2303
				break;
		}
	}

E
Erich Gamma 已提交
2304
	public dispose(): void {
2305
		super.dispose();
E
Erich Gamma 已提交
2306 2307

		// Positions
B
Benjamin Pasero 已提交
2308
		POSITIONS.forEach(position => {
E
Erich Gamma 已提交
2309 2310 2311
			this.clearPosition(position);
		});

2312
		// Controls
2313
		POSITIONS.forEach(position => {
2314
			this.getTitleAreaControl(position).dispose();
2315
			this.getProgressBar(position).dispose();
E
Erich Gamma 已提交
2316 2317 2318
		});

		// Sash
B
Benjamin Pasero 已提交
2319 2320
		this.sashOne.dispose();
		this.sashTwo.dispose();
E
Erich Gamma 已提交
2321 2322

		// Destroy Container
B
Benjamin Pasero 已提交
2323 2324
		this.silos.forEach(silo => {
			silo.destroy();
E
Erich Gamma 已提交
2325 2326 2327 2328 2329 2330
		});

		this.lastActiveEditor = null;
		this.lastActivePosition = null;
		this.visibleEditors = null;
	}
2331
}