splitview.ts 31.4 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./splitview';
7
import { IDisposable, toDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle';
J
Joao Moreno 已提交
8
import { Event, Emitter } from 'vs/base/common/event';
9 10
import * as types from 'vs/base/common/types';
import * as dom from 'vs/base/browser/dom';
J
Joao Moreno 已提交
11
import { clamp } from 'vs/base/common/numbers';
J
Joao Moreno 已提交
12
import { range, firstIndex, pushToStart, pushToEnd } from 'vs/base/common/arrays';
J
Joao Moreno 已提交
13
import { Sash, Orientation, ISashEvent as IBaseSashEvent, SashState } from 'vs/base/browser/ui/sash/sash';
14
import { Color } from 'vs/base/common/color';
J
Joao Moreno 已提交
15
import { domEvent } from 'vs/base/browser/event';
J
Joao Moreno 已提交
16
export { Orientation } from 'vs/base/browser/ui/sash/sash';
E
Erich Gamma 已提交
17

18 19 20 21 22 23 24 25
export interface ISplitViewStyles {
	separatorBorder: Color;
}

const defaultStyles: ISplitViewStyles = {
	separatorBorder: Color.transparent
};

J
Joao Moreno 已提交
26
export interface ISplitViewOptions<TLayoutContext = undefined> {
J
Joao Moreno 已提交
27 28 29 30 31
	readonly orientation?: Orientation; // default Orientation.VERTICAL
	readonly styles?: ISplitViewStyles;
	readonly orthogonalStartSash?: Sash;
	readonly orthogonalEndSash?: Sash;
	readonly inverseAltBehavior?: boolean;
32
	readonly proportionalLayout?: boolean; // default true,
J
Joao Moreno 已提交
33
	readonly descriptor?: ISplitViewDescriptor<TLayoutContext>;
J
Joao Moreno 已提交
34 35
}

J
Joao Moreno 已提交
36 37 38 39 40 41 42 43 44
/**
 * Only used when `proportionalLayout` is false.
 */
export const enum LayoutPriority {
	Normal,
	Low,
	High
}

J
Joao Moreno 已提交
45
export interface IView<TLayoutContext = undefined> {
J
Joao Moreno 已提交
46
	readonly element: HTMLElement;
J
Joao Moreno 已提交
47 48 49
	readonly minimumSize: number;
	readonly maximumSize: number;
	readonly onDidChange: Event<number | undefined>;
J
Joao Moreno 已提交
50
	readonly priority?: LayoutPriority;
J
Joao Moreno 已提交
51
	readonly snap?: boolean;
J
Joao Moreno 已提交
52
	layout(size: number, offset: number, context: TLayoutContext | undefined): void;
J
Joao Moreno 已提交
53
	setVisible?(visible: boolean): void;
J
Joao Moreno 已提交
54 55
}

J
Joao Moreno 已提交
56
interface ISashEvent {
M
Matt Bierner 已提交
57 58 59 60
	readonly sash: Sash;
	readonly start: number;
	readonly current: number;
	readonly alt: boolean;
E
Erich Gamma 已提交
61 62
}

63 64
type ViewItemSize = number | { cachedVisibleSize: number };

J
Joao Moreno 已提交
65
abstract class ViewItem<TLayoutContext> {
J
Joao Moreno 已提交
66

67
	private _size: number;
J
Joao Moreno 已提交
68 69 70 71 72 73 74 75
	set size(size: number) {
		this._size = size;
	}

	get size(): number {
		return this._size;
	}

76 77
	private _cachedVisibleSize: number | undefined = undefined;
	get cachedVisibleSize(): number | undefined { return this._cachedVisibleSize; }
J
Joao Moreno 已提交
78 79

	get visible(): boolean {
80
		return typeof this._cachedVisibleSize === 'undefined';
J
Joao Moreno 已提交
81 82
	}

J
Joao Moreno 已提交
83
	setVisible(visible: boolean, size?: number): void {
J
Joao Moreno 已提交
84 85 86 87 88
		if (visible === this.visible) {
			return;
		}

		if (visible) {
89 90
			this.size = clamp(this._cachedVisibleSize!, this.viewMinimumSize, this.viewMaximumSize);
			this._cachedVisibleSize = undefined;
J
Joao Moreno 已提交
91
		} else {
J
Joao Moreno 已提交
92
			this._cachedVisibleSize = typeof size === 'number' ? size : this.size;
J
Joao Moreno 已提交
93 94 95 96
			this.size = 0;
		}

		dom.toggleClass(this.container, 'visible', visible);
J
Joao Moreno 已提交
97 98

		if (this.view.setVisible) {
J
Joao Moreno 已提交
99
			this.view.setVisible(visible);
J
Joao Moreno 已提交
100
		}
J
Joao Moreno 已提交
101 102 103
	}

	get minimumSize(): number { return this.visible ? this.view.minimumSize : 0; }
J
Joao Moreno 已提交
104
	get viewMinimumSize(): number { return this.view.minimumSize; }
J
wip  
Joao Moreno 已提交
105

J
Joao Moreno 已提交
106
	get maximumSize(): number { return this.visible ? this.view.maximumSize : 0; }
J
Joao Moreno 已提交
107
	get viewMaximumSize(): number { return this.view.maximumSize; }
J
wip  
Joao Moreno 已提交
108

J
Joao Moreno 已提交
109
	get priority(): LayoutPriority | undefined { return this.view.priority; }
J
Joao Moreno 已提交
110
	get snap(): boolean { return !!this.view.snap; }
J
Joao Moreno 已提交
111

J
Joao Moreno 已提交
112
	set enabled(enabled: boolean) {
113
		this.container.style.pointerEvents = enabled ? '' : 'none';
J
Joao Moreno 已提交
114 115
	}

116 117
	constructor(
		protected container: HTMLElement,
J
Joao Moreno 已提交
118
		private view: IView<TLayoutContext>,
119 120 121 122 123 124
		size: ViewItemSize,
		private disposable: IDisposable
	) {
		if (typeof size === 'number') {
			this._size = size;
			this._cachedVisibleSize = undefined;
S
SteVen Batten 已提交
125
			dom.addClass(container, 'visible');
126 127 128 129
		} else {
			this._size = 0;
			this._cachedVisibleSize = size.cachedVisibleSize;
		}
J
Joao Moreno 已提交
130
	}
J
Joao Moreno 已提交
131

J
Joao Moreno 已提交
132 133 134
	layout(offset: number, layoutContext: TLayoutContext | undefined): void {
		this.layoutContainer(offset);
		this.view.layout(this.size, offset, layoutContext);
J
Joao Moreno 已提交
135 136
	}

J
Joao Moreno 已提交
137
	abstract layoutContainer(offset: number): void;
138

J
Joao Moreno 已提交
139
	dispose(): IView<TLayoutContext> {
J
Joao Moreno 已提交
140 141 142 143 144
		this.disposable.dispose();
		return this.view;
	}
}

J
Joao Moreno 已提交
145
class VerticalViewItem<TLayoutContext> extends ViewItem<TLayoutContext> {
J
Joao Moreno 已提交
146

J
Joao Moreno 已提交
147 148
	layoutContainer(offset: number): void {
		this.container.style.top = `${offset}px`;
J
Joao Moreno 已提交
149 150 151 152
		this.container.style.height = `${this.size}px`;
	}
}

J
Joao Moreno 已提交
153
class HorizontalViewItem<TLayoutContext> extends ViewItem<TLayoutContext> {
J
Joao Moreno 已提交
154

J
Joao Moreno 已提交
155 156
	layoutContainer(offset: number): void {
		this.container.style.left = `${offset}px`;
J
Joao Moreno 已提交
157 158
		this.container.style.width = `${this.size}px`;
	}
E
Erich Gamma 已提交
159 160
}

J
Joao Moreno 已提交
161 162 163
interface ISashItem {
	sash: Sash;
	disposable: IDisposable;
E
Erich Gamma 已提交
164 165
}

J
wip  
Joao Moreno 已提交
166 167 168
interface ISashDragSnapState {
	readonly index: number;
	readonly limitDelta: number;
J
Joao Moreno 已提交
169
	readonly size: number;
J
wip  
Joao Moreno 已提交
170 171
}

J
Joao Moreno 已提交
172 173 174
interface ISashDragState {
	index: number;
	start: number;
J
Joao Moreno 已提交
175
	current: number;
J
Joao Moreno 已提交
176
	sizes: number[];
J
Joao Moreno 已提交
177 178 179
	minDelta: number;
	maxDelta: number;
	alt: boolean;
J
wip  
Joao Moreno 已提交
180 181
	snapBefore: ISashDragSnapState | undefined;
	snapAfter: ISashDragSnapState | undefined;
J
Joao Moreno 已提交
182
	disposable: IDisposable;
183 184
}

185 186 187 188 189
enum State {
	Idle,
	Busy
}

190 191
export type DistributeSizing = { type: 'distribute' };
export type SplitSizing = { type: 'split', index: number };
192 193
export type InvisibleSizing = { type: 'invisible', cachedVisibleSize: number };
export type Sizing = DistributeSizing | SplitSizing | InvisibleSizing;
194 195 196 197

export namespace Sizing {
	export const Distribute: DistributeSizing = { type: 'distribute' };
	export function Split(index: number): SplitSizing { return { type: 'split', index }; }
198
	export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
J
Joao Moreno 已提交
199 200
}

J
Joao Moreno 已提交
201
export interface ISplitViewDescriptor<TLayoutContext> {
202 203 204 205
	size: number;
	views: {
		visible?: boolean;
		size: number;
J
Joao Moreno 已提交
206
		view: IView<TLayoutContext>;
207 208 209
	}[];
}

J
Joao Moreno 已提交
210
export class SplitView<TLayoutContext = undefined> extends Disposable {
S
Sandeep Somavarapu 已提交
211

212
	readonly orientation: Orientation;
J
Joao Moreno 已提交
213
	readonly el: HTMLElement;
214
	private sashContainer: HTMLElement;
215
	private viewContainer: HTMLElement;
J
Joao Moreno 已提交
216
	private size = 0;
J
Joao Moreno 已提交
217
	private layoutContext: TLayoutContext | undefined;
J
Joao Moreno 已提交
218
	private contentSize = 0;
219
	private proportions: undefined | number[] = undefined;
J
Joao Moreno 已提交
220
	private viewItems: ViewItem<TLayoutContext>[] = [];
J
Joao Moreno 已提交
221
	private sashItems: ISashItem[] = [];
J
Joao Moreno 已提交
222
	private sashDragState: ISashDragState | undefined;
223
	private state: State = State.Idle;
I
isidor 已提交
224
	private inverseAltBehavior: boolean;
J
Joao Moreno 已提交
225
	private proportionalLayout: boolean;
E
Erich Gamma 已提交
226

227
	private _onDidSashChange = this._register(new Emitter<number>());
J
Joao Moreno 已提交
228
	readonly onDidSashChange = this._onDidSashChange.event;
229 230

	private _onDidSashReset = this._register(new Emitter<number>());
231
	readonly onDidSashReset = this._onDidSashReset.event;
J
Joao Moreno 已提交
232

J
Joao Moreno 已提交
233 234
	get length(): number {
		return this.viewItems.length;
S
Sandeep Somavarapu 已提交
235
	}
J
Joao Moreno 已提交
236

237
	get minimumSize(): number {
J
Joao Moreno 已提交
238
		return this.viewItems.reduce((r, item) => r + item.minimumSize, 0);
239 240 241
	}

	get maximumSize(): number {
J
Joao Moreno 已提交
242
		return this.length === 0 ? Number.POSITIVE_INFINITY : this.viewItems.reduce((r, item) => r + item.maximumSize, 0);
243 244
	}

245 246 247
	private _orthogonalStartSash: Sash | undefined;
	get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
	set orthogonalStartSash(sash: Sash | undefined) {
248
		for (const sashItem of this.sashItems) {
249
			sashItem.sash.orthogonalStartSash = sash;
250 251
		}

252
		this._orthogonalStartSash = sash;
253 254
	}

255 256 257
	private _orthogonalEndSash: Sash | undefined;
	get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
	set orthogonalEndSash(sash: Sash | undefined) {
258
		for (const sashItem of this.sashItems) {
259
			sashItem.sash.orthogonalEndSash = sash;
260 261
		}

262
		this._orthogonalEndSash = sash;
263 264 265 266 267 268
	}

	get sashes(): Sash[] {
		return this.sashItems.map(s => s.sash);
	}

269 270 271
	private _startSnappingEnabled = true;
	get startSnappingEnabled(): boolean { return this._startSnappingEnabled; }
	set startSnappingEnabled(startSnappingEnabled: boolean) {
272 273 274 275
		if (this._startSnappingEnabled === startSnappingEnabled) {
			return;
		}

276 277 278 279 280 281 282
		this._startSnappingEnabled = startSnappingEnabled;
		this.updateSashEnablement();
	}

	private _endSnappingEnabled = true;
	get endSnappingEnabled(): boolean { return this._endSnappingEnabled; }
	set endSnappingEnabled(endSnappingEnabled: boolean) {
283 284 285 286
		if (this._endSnappingEnabled === endSnappingEnabled) {
			return;
		}

287 288 289 290
		this._endSnappingEnabled = endSnappingEnabled;
		this.updateSashEnablement();
	}

J
Joao Moreno 已提交
291
	constructor(container: HTMLElement, options: ISplitViewOptions<TLayoutContext> = {}) {
292 293
		super();

J
Joao Moreno 已提交
294
		this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
I
isidor 已提交
295
		this.inverseAltBehavior = !!options.inverseAltBehavior;
J
Joao Moreno 已提交
296
		this.proportionalLayout = types.isUndefined(options.proportionalLayout) ? true : !!options.proportionalLayout;
S
Sandeep Somavarapu 已提交
297

J
Joao Moreno 已提交
298 299 300 301
		this.el = document.createElement('div');
		dom.addClass(this.el, 'monaco-split-view2');
		dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal');
		container.appendChild(this.el);
302

303 304
		this.sashContainer = dom.append(this.el, dom.$('.sash-container'));
		this.viewContainer = dom.append(this.el, dom.$('.split-view-container'));
305 306

		this.style(options.styles || defaultStyles);
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321

		// We have an existing set of view, add them now
		if (options.descriptor) {
			this.size = options.descriptor.size;
			options.descriptor.views.forEach((viewDescriptor, index) => {
				const sizing = types.isUndefined(viewDescriptor.visible) || viewDescriptor.visible ? viewDescriptor.size : { type: 'invisible', cachedVisibleSize: viewDescriptor.size } as InvisibleSizing;

				const view = viewDescriptor.view;
				this.doAddView(view, sizing, index, true);
			});

			// Initialize content size and proportions for first layout
			this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
			this.saveProportions();
		}
322 323 324 325 326 327 328 329 330 331
	}

	style(styles: ISplitViewStyles): void {
		if (styles.separatorBorder.isTransparent()) {
			dom.removeClass(this.el, 'separator-border');
			this.el.style.removeProperty('--separator-border');
		} else {
			dom.addClass(this.el, 'separator-border');
			this.el.style.setProperty('--separator-border', styles.separatorBorder.toString());
		}
E
Erich Gamma 已提交
332 333
	}

J
Joao Moreno 已提交
334
	addView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length): void {
335
		this.doAddView(view, size, index, false);
S
#27823  
Sandeep Somavarapu 已提交
336 337
	}

J
Joao Moreno 已提交
338
	removeView(index: number, sizing?: Sizing): IView<TLayoutContext> {
339 340 341 342 343 344
		if (this.state !== State.Idle) {
			throw new Error('Cant modify splitview');
		}

		this.state = State.Busy;

J
Joao Moreno 已提交
345
		if (index < 0 || index >= this.viewItems.length) {
346
			throw new Error('Index out of bounds');
S
#27823  
Sandeep Somavarapu 已提交
347 348
		}

J
Joao Moreno 已提交
349 350
		// Remove view
		const viewItem = this.viewItems.splice(index, 1)[0];
J
Joao Moreno 已提交
351
		const view = viewItem.dispose();
E
Erich Gamma 已提交
352

J
Joao Moreno 已提交
353 354 355 356 357
		// Remove sash
		if (this.viewItems.length >= 1) {
			const sashIndex = Math.max(index - 1, 0);
			const sashItem = this.sashItems.splice(sashIndex, 1)[0];
			sashItem.disposable.dispose();
358
		}
359

J
Joao Moreno 已提交
360
		this.relayout();
361
		this.state = State.Idle;
362

J
Joao Moreno 已提交
363 364
		if (sizing && sizing.type === 'distribute') {
			this.distributeViewSizes();
J
Joao Moreno 已提交
365 366
		}

J
Joao Moreno 已提交
367
		return view;
E
Erich Gamma 已提交
368 369
	}

J
Joao Moreno 已提交
370
	moveView(from: number, to: number): void {
371 372 373 374
		if (this.state !== State.Idle) {
			throw new Error('Cant modify splitview');
		}

375 376
		const cachedVisibleSize = this.getViewCachedVisibleSize(from);
		const sizing = typeof cachedVisibleSize === 'undefined' ? this.getViewSize(from) : Sizing.Invisible(cachedVisibleSize);
377
		const view = this.removeView(from);
378
		this.addView(view, sizing, to);
J
Joao 已提交
379 380
	}

J
Joao Moreno 已提交
381 382 383 384 385 386 387 388 389 390 391 392 393
	swapViews(from: number, to: number): void {
		if (this.state !== State.Idle) {
			throw new Error('Cant modify splitview');
		}

		if (from > to) {
			return this.swapViews(to, from);
		}

		const fromSize = this.getViewSize(from);
		const toSize = this.getViewSize(to);
		const toView = this.removeView(to);
		const fromView = this.removeView(from);
J
Joao Moreno 已提交
394

J
Joao Moreno 已提交
395 396 397 398
		this.addView(toView, fromSize, from);
		this.addView(fromView, toSize, to);
	}

J
Joao Moreno 已提交
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
	isViewVisible(index: number): boolean {
		if (index < 0 || index >= this.viewItems.length) {
			throw new Error('Index out of bounds');
		}

		const viewItem = this.viewItems[index];
		return viewItem.visible;
	}

	setViewVisible(index: number, visible: boolean): void {
		if (index < 0 || index >= this.viewItems.length) {
			throw new Error('Index out of bounds');
		}

		const viewItem = this.viewItems[index];
J
Joao Moreno 已提交
414
		viewItem.setVisible(visible);
J
Joao Moreno 已提交
415 416 417

		this.distributeEmptySpace(index);
		this.layoutViews();
J
Joao Moreno 已提交
418
		this.saveProportions();
J
Joao Moreno 已提交
419 420
	}

421 422 423 424 425 426 427 428 429
	getViewCachedVisibleSize(index: number): number | undefined {
		if (index < 0 || index >= this.viewItems.length) {
			throw new Error('Index out of bounds');
		}

		const viewItem = this.viewItems[index];
		return viewItem.cachedVisibleSize;
	}

J
Joao Moreno 已提交
430
	layout(size: number, layoutContext?: TLayoutContext): void {
J
Joao Moreno 已提交
431 432
		const previousSize = Math.max(this.size, this.contentSize);
		this.size = size;
J
Joao Moreno 已提交
433
		this.layoutContext = layoutContext;
434 435

		if (!this.proportions) {
J
Joao Moreno 已提交
436
			const indexes = range(this.viewItems.length);
J
Joao Moreno 已提交
437 438
			const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.Low);
			const highPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.High);
J
Joao Moreno 已提交
439 440

			this.resize(this.viewItems.length - 1, size - previousSize, undefined, lowPriorityIndexes, highPriorityIndexes);
441 442 443
		} else {
			for (let i = 0; i < this.viewItems.length; i++) {
				const item = this.viewItems[i];
J
Joao Moreno 已提交
444
				item.size = clamp(Math.round(this.proportions[i] * size), item.minimumSize, item.maximumSize);
445
			}
J
Joao Moreno 已提交
446 447 448 449 450
		}

		this.distributeEmptySpace();
		this.layoutViews();
	}
451

J
Joao Moreno 已提交
452
	private saveProportions(): void {
J
Joao Moreno 已提交
453
		if (this.proportionalLayout && this.contentSize > 0) {
J
Joao Moreno 已提交
454
			this.proportions = this.viewItems.map(i => i.size / this.contentSize);
455
		}
S
Sandeep Somavarapu 已提交
456 457
	}

J
Joao Moreno 已提交
458
	private onSashStart({ sash, start, alt }: ISashEvent): void {
J
Joao Moreno 已提交
459 460 461 462
		for (const item of this.viewItems) {
			item.enabled = false;
		}

J
Joao Moreno 已提交
463
		const index = firstIndex(this.sashItems, item => item.sash === sash);
E
Erich Gamma 已提交
464

J
Joao Moreno 已提交
465
		// This way, we can press Alt while we resize a sash, macOS style!
466
		const disposable = combinedDisposable(
J
Joao Moreno 已提交
467 468
			domEvent(document.body, 'keydown')(e => resetSashDragState(this.sashDragState!.current, e.altKey)),
			domEvent(document.body, 'keyup')(() => resetSashDragState(this.sashDragState!.current, false))
469
		);
J
Joao Moreno 已提交
470 471 472

		const resetSashDragState = (start: number, alt: boolean) => {
			const sizes = this.viewItems.map(i => i.size);
J
Joao Moreno 已提交
473
			let minDelta = Number.NEGATIVE_INFINITY;
J
Joao Moreno 已提交
474
			let maxDelta = Number.POSITIVE_INFINITY;
J
Joao Moreno 已提交
475

I
isidor 已提交
476 477 478
			if (this.inverseAltBehavior) {
				alt = !alt;
			}
J
Joao Moreno 已提交
479 480

			if (alt) {
481 482 483 484 485 486 487
				// When we're using the last sash with Alt, we're resizing
				// the view to the left/up, instead of right/down as usual
				// Thus, we must do the inverse of the usual
				const isLastSash = index === this.sashItems.length - 1;

				if (isLastSash) {
					const viewItem = this.viewItems[index];
J
Joao Moreno 已提交
488 489
					minDelta = (viewItem.minimumSize - viewItem.size) / 2;
					maxDelta = (viewItem.maximumSize - viewItem.size) / 2;
490 491
				} else {
					const viewItem = this.viewItems[index + 1];
J
Joao Moreno 已提交
492 493
					minDelta = (viewItem.size - viewItem.maximumSize) / 2;
					maxDelta = (viewItem.size - viewItem.minimumSize) / 2;
494
				}
J
Joao Moreno 已提交
495 496
			}

J
wip  
Joao Moreno 已提交
497 498
			let snapBefore: ISashDragSnapState | undefined;
			let snapAfter: ISashDragSnapState | undefined;
J
Joao Moreno 已提交
499 500 501 502 503

			if (!alt) {
				const upIndexes = range(index, -1);
				const downIndexes = range(index + 1, this.viewItems.length);
				const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].minimumSize - sizes[i]), 0);
J
wip  
Joao Moreno 已提交
504
				const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].viewMaximumSize - sizes[i]), 0);
J
Joao Moreno 已提交
505
				const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].minimumSize), 0);
J
wip  
Joao Moreno 已提交
506
				const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].viewMaximumSize), 0);
J
Joao Moreno 已提交
507 508
				const minDelta = Math.max(minDeltaUp, minDeltaDown);
				const maxDelta = Math.min(maxDeltaDown, maxDeltaUp);
J
Joao Moreno 已提交
509 510
				const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
				const snapAfterIndex = this.findFirstSnapIndex(downIndexes);
J
wip  
Joao Moreno 已提交
511 512 513 514 515 516 517

				if (typeof snapBeforeIndex === 'number') {
					const viewItem = this.viewItems[snapBeforeIndex];
					const halfSize = Math.floor(viewItem.viewMinimumSize / 2);

					snapBefore = {
						index: snapBeforeIndex,
J
Joao Moreno 已提交
518 519
						limitDelta: viewItem.visible ? minDelta - halfSize : minDelta + halfSize,
						size: viewItem.size
J
wip  
Joao Moreno 已提交
520 521 522 523 524 525 526 527 528
					};
				}

				if (typeof snapAfterIndex === 'number') {
					const viewItem = this.viewItems[snapAfterIndex];
					const halfSize = Math.floor(viewItem.viewMinimumSize / 2);

					snapAfter = {
						index: snapAfterIndex,
J
Joao Moreno 已提交
529 530
						limitDelta: viewItem.visible ? maxDelta + halfSize : maxDelta - halfSize,
						size: viewItem.size
J
wip  
Joao Moreno 已提交
531
					};
J
Joao Moreno 已提交
532
				}
J
Joao Moreno 已提交
533 534
			}

J
wip  
Joao Moreno 已提交
535
			this.sashDragState = { start, current: start, index, sizes, minDelta, maxDelta, alt, snapBefore, snapAfter, disposable };
J
Joao Moreno 已提交
536 537 538
		};

		resetSashDragState(start, alt);
J
Joao Moreno 已提交
539
	}
540

J
Joao Moreno 已提交
541
	private onSashChange({ current }: ISashEvent): void {
J
Joao Moreno 已提交
542 543
		const { index, start, sizes, alt, minDelta, maxDelta, snapBefore, snapAfter } = this.sashDragState!;
		this.sashDragState!.current = current;
J
Joao Moreno 已提交
544

J
Joao Moreno 已提交
545
		const delta = current - start;
J
wip  
Joao Moreno 已提交
546
		const newDelta = this.resize(index, delta, sizes, undefined, undefined, minDelta, maxDelta, snapBefore, snapAfter);
J
Joao Moreno 已提交
547 548

		if (alt) {
549
			const isLastSash = index === this.sashItems.length - 1;
J
Joao Moreno 已提交
550
			const newSizes = this.viewItems.map(i => i.size);
551 552
			const viewItemIndex = isLastSash ? index : index + 1;
			const viewItem = this.viewItems[viewItemIndex];
J
Joao Moreno 已提交
553 554
			const newMinDelta = viewItem.size - viewItem.maximumSize;
			const newMaxDelta = viewItem.size - viewItem.minimumSize;
555
			const resizeIndex = isLastSash ? index - 1 : index + 1;
556

557
			this.resize(resizeIndex, -newDelta, newSizes, undefined, undefined, newMinDelta, newMaxDelta);
J
Joao Moreno 已提交
558 559
		}

J
Joao Moreno 已提交
560
		this.distributeEmptySpace();
J
Joao Moreno 已提交
561
		this.layoutViews();
E
Erich Gamma 已提交
562 563
	}

J
Joao Moreno 已提交
564 565
	private onSashEnd(index: number): void {
		this._onDidSashChange.fire(index);
J
Joao Moreno 已提交
566
		this.sashDragState!.disposable.dispose();
J
Joao Moreno 已提交
567
		this.saveProportions();
J
Joao Moreno 已提交
568 569 570 571

		for (const item of this.viewItems) {
			item.enabled = true;
		}
J
Joao Moreno 已提交
572 573
	}

J
Joao Moreno 已提交
574
	private onViewChange(item: ViewItem<TLayoutContext>, size: number | undefined): void {
J
Joao Moreno 已提交
575
		const index = this.viewItems.indexOf(item);
E
Erich Gamma 已提交
576

J
Joao Moreno 已提交
577
		if (index < 0 || index >= this.viewItems.length) {
E
Erich Gamma 已提交
578 579 580
			return;
		}

J
Joao Moreno 已提交
581
		size = typeof size === 'number' ? size : item.size;
J
Joao Moreno 已提交
582
		size = clamp(size, item.minimumSize, item.maximumSize);
583 584 585 586 587 588 589 590 591

		if (this.inverseAltBehavior && index > 0) {
			// In this case, we want the view to grow or shrink both sides equally
			// so we just resize the "left" side by half and let `resize` do the clamping magic
			this.resize(index - 1, Math.floor((item.size - size) / 2));
			this.distributeEmptySpace();
			this.layoutViews();
		} else {
			item.size = size;
592
			this.relayout([index], undefined);
593
		}
E
Erich Gamma 已提交
594 595
	}

J
Joao Moreno 已提交
596
	resizeView(index: number, size: number): void {
597 598 599 600 601 602
		if (this.state !== State.Idle) {
			throw new Error('Cant modify splitview');
		}

		this.state = State.Busy;

J
Joao Moreno 已提交
603
		if (index < 0 || index >= this.viewItems.length) {
E
Erich Gamma 已提交
604 605 606
			return;
		}

607 608 609 610
		const indexes = range(this.viewItems.length).filter(i => i !== index);
		const lowPriorityIndexes = [...indexes.filter(i => this.viewItems[i].priority === LayoutPriority.Low), index];
		const highPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.High);

J
Joao Moreno 已提交
611 612
		const item = this.viewItems[index];
		size = Math.round(size);
613
		size = clamp(size, item.minimumSize, Math.min(item.maximumSize, this.size));
E
Erich Gamma 已提交
614

615 616
		item.size = size;
		this.relayout(lowPriorityIndexes, highPriorityIndexes);
617
		this.state = State.Idle;
618 619
	}

J
Joao Moreno 已提交
620
	distributeViewSizes(): void {
J
Joao Moreno 已提交
621
		const flexibleViewItems: ViewItem<TLayoutContext>[] = [];
622 623 624 625 626 627 628 629 630 631
		let flexibleSize = 0;

		for (const item of this.viewItems) {
			if (item.maximumSize - item.minimumSize > 0) {
				flexibleViewItems.push(item);
				flexibleSize += item.size;
			}
		}

		const size = Math.floor(flexibleSize / flexibleViewItems.length);
J
Joao Moreno 已提交
632

633
		for (const item of flexibleViewItems) {
634
			item.size = clamp(size, item.minimumSize, item.maximumSize);
J
Joao Moreno 已提交
635
		}
636 637 638 639 640 641

		const indexes = range(this.viewItems.length);
		const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.Low);
		const highPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.High);

		this.relayout(lowPriorityIndexes, highPriorityIndexes);
J
Joao Moreno 已提交
642 643
	}

J
Joao Moreno 已提交
644 645 646
	getViewSize(index: number): number {
		if (index < 0 || index >= this.viewItems.length) {
			return -1;
647 648
		}

J
Joao Moreno 已提交
649
		return this.viewItems[index].size;
650 651
	}

J
Joao Moreno 已提交
652
	private doAddView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
		if (this.state !== State.Idle) {
			throw new Error('Cant modify splitview');
		}

		this.state = State.Busy;

		// Add view
		const container = dom.$('.split-view-view');

		if (index === this.viewItems.length) {
			this.viewContainer.appendChild(container);
		} else {
			this.viewContainer.insertBefore(container, this.viewContainer.children.item(index));
		}

		const onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));
		const containerDisposable = toDisposable(() => this.viewContainer.removeChild(container));
		const disposable = combinedDisposable(onChangeDisposable, containerDisposable);

		let viewSize: ViewItemSize;

		if (typeof size === 'number') {
			viewSize = size;
		} else if (size.type === 'split') {
			viewSize = this.getViewSize(size.index) / 2;
		} else if (size.type === 'invisible') {
			viewSize = { cachedVisibleSize: size.cachedVisibleSize };
		} else {
			viewSize = view.minimumSize;
		}

		const item = this.orientation === Orientation.VERTICAL
			? new VerticalViewItem(container, view, viewSize, disposable)
			: new HorizontalViewItem(container, view, viewSize, disposable);

		this.viewItems.splice(index, 0, item);

		// Add sash
		if (this.viewItems.length > 1) {
J
João Moreno 已提交
692
			const sash = this.orientation === Orientation.VERTICAL
J
João Moreno 已提交
693 694 695 696 697 698 699 700 701 702
				? new Sash(this.sashContainer, { getHorizontalSashTop: (sash: Sash) => this.getSashPosition(sash) }, {
					orientation: Orientation.HORIZONTAL,
					orthogonalStartSash: this.orthogonalStartSash,
					orthogonalEndSash: this.orthogonalEndSash
				})
				: new Sash(this.sashContainer, { getVerticalSashLeft: (sash: Sash) => this.getSashPosition(sash) }, {
					orientation: Orientation.VERTICAL,
					orthogonalStartSash: this.orthogonalStartSash,
					orthogonalEndSash: this.orthogonalEndSash
				});
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757

			const sashEventMapper = this.orientation === Orientation.VERTICAL
				? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey })
				: (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey });

			const onStart = Event.map(sash.onDidStart, sashEventMapper);
			const onStartDisposable = onStart(this.onSashStart, this);
			const onChange = Event.map(sash.onDidChange, sashEventMapper);
			const onChangeDisposable = onChange(this.onSashChange, this);
			const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash));
			const onEndDisposable = onEnd(this.onSashEnd, this);

			const onDidResetDisposable = sash.onDidReset(() => {
				const index = firstIndex(this.sashItems, item => item.sash === sash);
				const upIndexes = range(index, -1);
				const downIndexes = range(index + 1, this.viewItems.length);
				const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
				const snapAfterIndex = this.findFirstSnapIndex(downIndexes);

				if (typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible) {
					return;
				}

				if (typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible) {
					return;
				}

				this._onDidSashReset.fire(index);
			});

			const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash);
			const sashItem: ISashItem = { sash, disposable };

			this.sashItems.splice(index - 1, 0, sashItem);
		}

		container.appendChild(view.element);

		let highPriorityIndexes: number[] | undefined;

		if (typeof size !== 'number' && size.type === 'split') {
			highPriorityIndexes = [size.index];
		}

		if (!skipLayout) {
			this.relayout([index], highPriorityIndexes);
		}

		this.state = State.Idle;

		if (!skipLayout && typeof size !== 'number' && size.type === 'distribute') {
			this.distributeViewSizes();
		}
	}

758 759 760 761 762 763 764 765 766
	private relayout(lowPriorityIndexes?: number[], highPriorityIndexes?: number[]): void {
		const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);

		this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndexes, highPriorityIndexes);
		this.distributeEmptySpace();
		this.layoutViews();
		this.saveProportions();
	}

J
Joao Moreno 已提交
767 768 769 770
	private resize(
		index: number,
		delta: number,
		sizes = this.viewItems.map(i => i.size),
J
Joao Moreno 已提交
771 772
		lowPriorityIndexes?: number[],
		highPriorityIndexes?: number[],
J
Joao Moreno 已提交
773
		overloadMinDelta: number = Number.NEGATIVE_INFINITY,
J
Joao Moreno 已提交
774
		overloadMaxDelta: number = Number.POSITIVE_INFINITY,
J
wip  
Joao Moreno 已提交
775 776
		snapBefore?: ISashDragSnapState,
		snapAfter?: ISashDragSnapState
J
Joao Moreno 已提交
777
	): number {
J
Joao Moreno 已提交
778
		if (index < 0 || index >= this.viewItems.length) {
J
Joao Moreno 已提交
779
			return 0;
E
Erich Gamma 已提交
780 781
		}

J
Joao Moreno 已提交
782 783
		const upIndexes = range(index, -1);
		const downIndexes = range(index + 1, this.viewItems.length);
J
Joao Moreno 已提交
784

J
Joao Moreno 已提交
785 786 787 788 789
		if (highPriorityIndexes) {
			for (const index of highPriorityIndexes) {
				pushToStart(upIndexes, index);
				pushToStart(downIndexes, index);
			}
J
Joao Moreno 已提交
790
		}
791

J
Joao Moreno 已提交
792 793 794 795 796
		if (lowPriorityIndexes) {
			for (const index of lowPriorityIndexes) {
				pushToEnd(upIndexes, index);
				pushToEnd(downIndexes, index);
			}
J
Joao Moreno 已提交
797
		}
J
Joao Moreno 已提交
798

J
Joao Moreno 已提交
799 800
		const upItems = upIndexes.map(i => this.viewItems[i]);
		const upSizes = upIndexes.map(i => sizes[i]);
J
Joao Moreno 已提交
801

J
Joao Moreno 已提交
802 803
		const downItems = downIndexes.map(i => this.viewItems[i]);
		const downSizes = downIndexes.map(i => sizes[i]);
E
Erich Gamma 已提交
804

J
Joao Moreno 已提交
805 806 807 808
		const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].minimumSize - sizes[i]), 0);
		const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].maximumSize - sizes[i]), 0);
		const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].minimumSize), 0);
		const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].maximumSize), 0);
J
Joao Moreno 已提交
809 810
		const minDelta = Math.max(minDeltaUp, minDeltaDown, overloadMinDelta);
		const maxDelta = Math.min(maxDeltaDown, maxDeltaUp, overloadMaxDelta);
J
Joao Moreno 已提交
811

J
wip  
Joao Moreno 已提交
812 813 814 815 816 817
		let snapped = false;

		if (snapBefore) {
			const snapView = this.viewItems[snapBefore.index];
			const visible = delta >= snapBefore.limitDelta;
			snapped = visible !== snapView.visible;
J
Joao Moreno 已提交
818
			snapView.setVisible(visible, snapBefore.size);
J
wip  
Joao Moreno 已提交
819
		}
J
Joao Moreno 已提交
820

J
wip  
Joao Moreno 已提交
821 822 823 824
		if (!snapped && snapAfter) {
			const snapView = this.viewItems[snapAfter.index];
			const visible = delta < snapAfter.limitDelta;
			snapped = visible !== snapView.visible;
J
Joao Moreno 已提交
825
			snapView.setVisible(visible, snapAfter.size);
J
wip  
Joao Moreno 已提交
826
		}
J
Joao Moreno 已提交
827

J
wip  
Joao Moreno 已提交
828
		if (snapped) {
J
Joao Moreno 已提交
829 830 831
			return this.resize(index, delta, sizes, lowPriorityIndexes, highPriorityIndexes, overloadMinDelta, overloadMaxDelta);
		}

832
		delta = clamp(delta, minDelta, maxDelta);
J
Joao Moreno 已提交
833

834
		for (let i = 0, deltaUp = delta; i < upItems.length; i++) {
J
Joao Moreno 已提交
835
			const item = upItems[i];
J
Joao Moreno 已提交
836
			const size = clamp(upSizes[i] + deltaUp, item.minimumSize, item.maximumSize);
J
Joao Moreno 已提交
837
			const viewDelta = size - upSizes[i];
E
Erich Gamma 已提交
838

J
Joao Moreno 已提交
839 840 841
			deltaUp -= viewDelta;
			item.size = size;
		}
E
Erich Gamma 已提交
842

843
		for (let i = 0, deltaDown = delta; i < downItems.length; i++) {
J
Joao Moreno 已提交
844
			const item = downItems[i];
J
Joao Moreno 已提交
845
			const size = clamp(downSizes[i] - deltaDown, item.minimumSize, item.maximumSize);
J
Joao Moreno 已提交
846
			const viewDelta = size - downSizes[i];
E
Erich Gamma 已提交
847

J
Joao Moreno 已提交
848 849
			deltaDown += viewDelta;
			item.size = size;
E
Erich Gamma 已提交
850 851
		}

J
Joao Moreno 已提交
852 853 854
		return delta;
	}

J
Joao Moreno 已提交
855 856
	private distributeEmptySpace(lowPriorityIndex?: number): void {
		const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
J
Joao Moreno 已提交
857
		let emptyDelta = this.size - contentSize;
E
Erich Gamma 已提交
858

J
Joao Moreno 已提交
859
		const indexes = range(this.viewItems.length - 1, -1);
860 861 862 863 864 865 866 867 868 869
		const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.Low);
		const highPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.High);

		for (const index of highPriorityIndexes) {
			pushToStart(indexes, index);
		}

		for (const index of lowPriorityIndexes) {
			pushToEnd(indexes, index);
		}
J
Joao Moreno 已提交
870 871 872 873 874 875 876

		if (typeof lowPriorityIndex === 'number') {
			pushToEnd(indexes, lowPriorityIndex);
		}

		for (let i = 0; emptyDelta !== 0 && i < indexes.length; i++) {
			const item = this.viewItems[indexes[i]];
J
Joao Moreno 已提交
877
			const size = clamp(item.size + emptyDelta, item.minimumSize, item.maximumSize);
J
Joao Moreno 已提交
878 879 880 881
			const viewDelta = size - item.size;

			emptyDelta -= viewDelta;
			item.size = size;
E
Erich Gamma 已提交
882
		}
J
Joao Moreno 已提交
883
	}
E
Erich Gamma 已提交
884

J
Joao Moreno 已提交
885
	private layoutViews(): void {
886
		// Save new content size
J
Joao Moreno 已提交
887 888
		this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);

889
		// Layout views
J
Joao Moreno 已提交
890
		let offset = 0;
891 892

		for (const viewItem of this.viewItems) {
J
Joao Moreno 已提交
893 894
			viewItem.layout(offset, this.layoutContext);
			offset += viewItem.size;
895
		}
896 897

		// Layout sashes
J
Joao Moreno 已提交
898
		this.sashItems.forEach(item => item.sash.layout());
899 900
		this.updateSashEnablement();
	}
E
Erich Gamma 已提交
901

902
	private updateSashEnablement(): void {
B
Benjamin Pasero 已提交
903
		let previous = false;
J
Joao Moreno 已提交
904
		const collapsesDown = this.viewItems.map(i => previous = (i.size - i.minimumSize > 0) || previous);
E
Erich Gamma 已提交
905 906

		previous = false;
J
Joao Moreno 已提交
907
		const expandsDown = this.viewItems.map(i => previous = (i.maximumSize - i.size > 0) || previous);
E
Erich Gamma 已提交
908

J
Joao Moreno 已提交
909
		const reverseViews = [...this.viewItems].reverse();
E
Erich Gamma 已提交
910
		previous = false;
J
Joao Moreno 已提交
911
		const collapsesUp = reverseViews.map(i => previous = (i.size - i.minimumSize > 0) || previous).reverse();
E
Erich Gamma 已提交
912 913

		previous = false;
J
Joao Moreno 已提交
914
		const expandsUp = reverseViews.map(i => previous = (i.maximumSize - i.size > 0) || previous).reverse();
E
Erich Gamma 已提交
915

916 917 918 919 920 921
		let position = 0;
		for (let index = 0; index < this.sashItems.length; index++) {
			const { sash } = this.sashItems[index];
			const viewItem = this.viewItems[index];
			position += viewItem.size;

J
Joao Moreno 已提交
922 923
			const min = !(collapsesDown[index] && expandsUp[index + 1]);
			const max = !(expandsDown[index] && collapsesUp[index + 1]);
J
wip  
Joao Moreno 已提交
924

J
Joao Moreno 已提交
925 926 927 928 929 930
			if (min && max) {
				const upIndexes = range(index, -1);
				const downIndexes = range(index + 1, this.viewItems.length);
				const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
				const snapAfterIndex = this.findFirstSnapIndex(downIndexes);

931 932 933
				const snappedBefore = typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible;
				const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible;

934
				if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
J
Joao Moreno 已提交
935
					sash.state = SashState.Minimum;
936
				} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
J
Joao Moreno 已提交
937
					sash.state = SashState.Maximum;
J
wip  
Joao Moreno 已提交
938
				} else {
J
Joao Moreno 已提交
939
					sash.state = SashState.Disabled;
J
wip  
Joao Moreno 已提交
940
				}
J
Joao Moreno 已提交
941 942 943 944
			} else if (min && !max) {
				sash.state = SashState.Minimum;
			} else if (!min && max) {
				sash.state = SashState.Maximum;
J
Joao Moreno 已提交
945
			} else {
J
Joao Moreno 已提交
946
				sash.state = SashState.Enabled;
J
Joao Moreno 已提交
947
			}
948
		}
E
Erich Gamma 已提交
949 950
	}

J
Joao Moreno 已提交
951
	private getSashPosition(sash: Sash): number {
B
Benjamin Pasero 已提交
952
		let position = 0;
E
Erich Gamma 已提交
953

J
Joao Moreno 已提交
954 955 956 957
		for (let i = 0; i < this.sashItems.length; i++) {
			position += this.viewItems[i].size;

			if (this.sashItems[i].sash === sash) {
J
João Moreno 已提交
958
				return position;
J
Joao Moreno 已提交
959
			}
E
Erich Gamma 已提交
960 961
		}

J
Joao Moreno 已提交
962
		return 0;
E
Erich Gamma 已提交
963 964
	}

J
Joao Moreno 已提交
965 966 967
	private findFirstSnapIndex(indexes: number[]): number | undefined {
		// visible views first
		for (const index of indexes) {
J
Joao Moreno 已提交
968 969 970
			const viewItem = this.viewItems[index];

			if (!viewItem.visible) {
J
Joao Moreno 已提交
971 972 973
				continue;
			}

J
Joao Moreno 已提交
974
			if (viewItem.snap) {
J
Joao Moreno 已提交
975 976 977 978 979 980
				return index;
			}
		}

		// then, hidden views
		for (const index of indexes) {
J
Joao Moreno 已提交
981 982 983 984 985 986 987
			const viewItem = this.viewItems[index];

			if (viewItem.visible && viewItem.maximumSize - viewItem.minimumSize > 0) {
				return undefined;
			}

			if (!viewItem.visible && viewItem.snap) {
J
Joao Moreno 已提交
988 989 990 991 992 993 994
				return index;
			}
		}

		return undefined;
	}

J
Joao Moreno 已提交
995
	dispose(): void {
996 997
		super.dispose();

J
Joao Moreno 已提交
998
		this.viewItems.forEach(i => i.dispose());
J
Joao Moreno 已提交
999 1000 1001 1002
		this.viewItems = [];

		this.sashItems.forEach(i => i.disposable.dispose());
		this.sashItems = [];
E
Erich Gamma 已提交
1003 1004
	}
}