layout.ts 26.5 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.
 *--------------------------------------------------------------------------------------------*/
'use strict';

7
import { Dimension, Builder } from 'vs/base/browser/builder';
8 9
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
J
Johannes Rieken 已提交
10 11 12 13
import { Part } from 'vs/workbench/browser/part';
import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
I
isidor 已提交
14
import { IPartService, Position, Parts } from 'vs/workbench/services/part/common/partService';
B
Benjamin Pasero 已提交
15
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Johannes Rieken 已提交
16 17 18 19
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
20
import { getZoomFactor } from 'vs/base/browser/browser';
21
import { IThemeService } from 'vs/platform/theme/common/themeService';
E
Erich Gamma 已提交
22

23 24 25 26
const MIN_SIDEBAR_PART_WIDTH = 170;
const MIN_EDITOR_PART_HEIGHT = 70;
const MIN_EDITOR_PART_WIDTH = 220;
const MIN_PANEL_PART_HEIGHT = 77;
I
isidor 已提交
27 28
const MIN_PANEL_PART_WIDTH = 150;
const DEFAULT_PANEL_SIZE_COEFFICIENT = 0.4;
E
Erich Gamma 已提交
29
const HIDE_SIDEBAR_WIDTH_THRESHOLD = 50;
I
isidor 已提交
30
const HIDE_PANEL_HEIGHT_THRESHOLD = 50;
31
const HIDE_PANEL_WIDTH_THRESHOLD = 100;
32 33 34
const TITLE_BAR_HEIGHT = 22;
const STATUS_BAR_HEIGHT = 22;
const ACTIVITY_BAR_WIDTH = 50;
E
Erich Gamma 已提交
35

36
interface PartLayoutInfo {
37 38
	titlebar: { height: number; };
	activitybar: { width: number; };
E
Erich Gamma 已提交
39
	sidebar: { minWidth: number; };
I
isidor 已提交
40
	panel: { minHeight: number; minWidth: number; };
41
	editor: { minWidth: number; minHeight: number; };
E
Erich Gamma 已提交
42 43 44 45
	statusbar: { height: number; };
}

/**
B
Benjamin Pasero 已提交
46
 * The workbench layout is responsible to lay out all parts that make the Workbench.
E
Erich Gamma 已提交
47
 */
I
isidor 已提交
48
export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider {
E
Erich Gamma 已提交
49

50 51
	private static sashXOneWidthSettingsKey = 'workbench.sidebar.width';
	private static sashXTwoWidthSettingsKey = 'workbench.panel.width';
I
isidor 已提交
52
	private static sashYHeightSettingsKey = 'workbench.panel.height';
E
Erich Gamma 已提交
53 54 55

	private parent: Builder;
	private workbenchContainer: Builder;
56
	private titlebar: Part;
E
Erich Gamma 已提交
57 58 59
	private activitybar: Part;
	private editor: Part;
	private sidebar: Part;
B
Benjamin Pasero 已提交
60
	private panel: Part;
E
Erich Gamma 已提交
61 62
	private statusbar: Part;
	private quickopen: QuickOpenController;
M
Martin Aeschlimann 已提交
63
	private toUnbind: IDisposable[];
64
	private partLayoutInfo: PartLayoutInfo;
E
Erich Gamma 已提交
65
	private workbenchSize: Dimension;
66 67
	private sashXOne: Sash;
	private sashXTwo: Sash;
I
isidor 已提交
68
	private sashY: Sash;
E
Erich Gamma 已提交
69
	private sidebarWidth: number;
I
isidor 已提交
70
	private sidebarHeight: number;
71 72 73
	private titlebarHeight: number;
	private activitybarWidth: number;
	private statusbarHeight: number;
I
isidor 已提交
74
	private panelHeight: number;
75
	private panelHeightBeforeMaximized: number;
I
isidor 已提交
76
	private panelWidth: number;
77
	private layoutEditorGroupsVertically: boolean;
E
Erich Gamma 已提交
78

I
isidor 已提交
79
	// Take parts as an object bag since instatation service does not have typings for constructors with 9+ arguments
E
Erich Gamma 已提交
80 81 82
	constructor(
		parent: Builder,
		workbenchContainer: Builder,
I
isidor 已提交
83
		parts: {
84
			titlebar: Part,
I
isidor 已提交
85 86 87 88 89 90
			activitybar: Part,
			editor: Part,
			sidebar: Part,
			panel: Part,
			statusbar: Part
		},
E
Erich Gamma 已提交
91 92 93 94
		quickopen: QuickOpenController,
		@IStorageService private storageService: IStorageService,
		@IContextViewService private contextViewService: IContextViewService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
95
		@IEditorGroupService private editorGroupService: IEditorGroupService,
M
Martin Aeschlimann 已提交
96
		@IPartService private partService: IPartService,
M
Maxime Quandalle 已提交
97
		@IViewletService private viewletService: IViewletService,
98
		@IThemeService themeService: IThemeService
E
Erich Gamma 已提交
99 100 101
	) {
		this.parent = parent;
		this.workbenchContainer = workbenchContainer;
102
		this.titlebar = parts.titlebar;
I
isidor 已提交
103 104 105
		this.activitybar = parts.activitybar;
		this.editor = parts.editor;
		this.sidebar = parts.sidebar;
B
Benjamin Pasero 已提交
106
		this.panel = parts.panel;
I
isidor 已提交
107
		this.statusbar = parts.statusbar;
E
Erich Gamma 已提交
108 109
		this.quickopen = quickopen;
		this.toUnbind = [];
110
		this.partLayoutInfo = this.getPartLayoutInfo();
I
isidor 已提交
111
		this.panelHeightBeforeMaximized = 0;
112

113 114 115 116 117
		this.sashXOne = new Sash(this.workbenchContainer.getHTMLElement(), this, {
			baseSize: 5
		});

		this.sashXTwo = new Sash(this.workbenchContainer.getHTMLElement(), this, {
E
Erich Gamma 已提交
118 119
			baseSize: 5
		});
120

I
isidor 已提交
121
		this.sashY = new Sash(this.workbenchContainer.getHTMLElement(), this, {
I
isidor 已提交
122
			baseSize: 4,
I
isidor 已提交
123 124
			orientation: Orientation.HORIZONTAL
		});
E
Erich Gamma 已提交
125

126
		this.sidebarWidth = this.storageService.getInteger(WorkbenchLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, -1);
I
isidor 已提交
127
		this.panelHeight = this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, 0);
128
		this.panelWidth = this.storageService.getInteger(WorkbenchLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, 0);
E
Erich Gamma 已提交
129

130
		this.layoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
131

132
		this.toUnbind.push(themeService.onThemeChange(_ => this.layout()));
133
		this.toUnbind.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
134
		this.toUnbind.push(editorGroupService.onGroupOrientationChanged(e => this.onGroupOrientationChanged()));
M
Martin Aeschlimann 已提交
135

E
Erich Gamma 已提交
136 137 138
		this.registerSashListeners();
	}

139 140 141 142 143 144 145 146 147 148 149 150
	private getPartLayoutInfo(): PartLayoutInfo {
		return {
			titlebar: {
				height: TITLE_BAR_HEIGHT
			},
			activitybar: {
				width: ACTIVITY_BAR_WIDTH
			},
			sidebar: {
				minWidth: MIN_SIDEBAR_PART_WIDTH
			},
			panel: {
I
isidor 已提交
151 152
				minHeight: MIN_PANEL_PART_HEIGHT,
				minWidth: MIN_PANEL_PART_WIDTH
153 154 155 156 157 158 159 160 161 162 163
			},
			editor: {
				minWidth: MIN_EDITOR_PART_WIDTH,
				minHeight: MIN_EDITOR_PART_HEIGHT
			},
			statusbar: {
				height: STATUS_BAR_HEIGHT
			}
		};
	}

E
Erich Gamma 已提交
164 165
	private registerSashListeners(): void {
		let startX: number = 0;
I
isidor 已提交
166
		let startY: number = 0;
167
		let startXTwo: number = 0;
168 169 170
		let startSidebarWidth: number;
		let startPanelHeight: number;
		let startPanelWidth: number;
E
Erich Gamma 已提交
171

172 173
		this.toUnbind.push(this.sashXOne.addListener('start', (e: ISashEvent) => {
			startSidebarWidth = this.sidebarWidth;
E
Erich Gamma 已提交
174
			startX = e.startX;
175
		}));
E
Erich Gamma 已提交
176

177 178
		this.toUnbind.push(this.sashY.addListener('start', (e: ISashEvent) => {
			startPanelHeight = this.panelHeight;
I
isidor 已提交
179
			startY = e.startY;
180 181 182 183
		}));

		this.toUnbind.push(this.sashXTwo.addListener('start', (e: ISashEvent) => {
			startPanelWidth = this.panelWidth;
184
			startXTwo = e.startX;
185
		}));
I
isidor 已提交
186

187
		this.toUnbind.push(this.sashXOne.addListener('change', (e: ISashEvent) => {
E
Erich Gamma 已提交
188 189
			let doLayout = false;
			let sidebarPosition = this.partService.getSideBarPosition();
190
			let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
191
			let newSashWidth = (sidebarPosition === Position.LEFT) ? startSidebarWidth + e.currentX - startX : startSidebarWidth - e.currentX + startX;
192
			let promise = TPromise.as<void>(null);
E
Erich Gamma 已提交
193 194

			// Sidebar visible
195
			if (isSidebarVisible) {
E
Erich Gamma 已提交
196 197

				// Automatically hide side bar when a certain threshold is met
198 199
				if (newSashWidth + HIDE_SIDEBAR_WIDTH_THRESHOLD < this.partLayoutInfo.sidebar.minWidth) {
					let dragCompensation = MIN_SIDEBAR_PART_WIDTH - HIDE_SIDEBAR_WIDTH_THRESHOLD;
200
					promise = this.partService.setSideBarHidden(true);
201
					startX = (sidebarPosition === Position.LEFT) ? Math.max(this.activitybarWidth, e.currentX - dragCompensation) : Math.min(e.currentX + dragCompensation, this.workbenchSize.width - this.activitybarWidth);
202
					this.sidebarWidth = startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from
E
Erich Gamma 已提交
203 204 205 206
				}

				// Otherwise size the sidebar accordingly
				else {
207 208
					this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, newSashWidth); // Sidebar can not become smaller than MIN_PART_WIDTH
					doLayout = newSashWidth >= this.partLayoutInfo.sidebar.minWidth;
E
Erich Gamma 已提交
209 210 211 212 213
				}
			}

			// Sidebar hidden
			else {
214 215
				if ((sidebarPosition === Position.LEFT && e.currentX - startX >= this.partLayoutInfo.sidebar.minWidth) ||
					(sidebarPosition === Position.RIGHT && startX - e.currentX >= this.partLayoutInfo.sidebar.minWidth)) {
216
					startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX);
217
					this.sidebarWidth = this.partLayoutInfo.sidebar.minWidth;
218
					promise = this.partService.setSideBarHidden(false);
E
Erich Gamma 已提交
219 220 221 222
				}
			}

			if (doLayout) {
223
				promise.done(() => this.layout(), errors.onUnexpectedError);
E
Erich Gamma 已提交
224
			}
225
		}));
E
Erich Gamma 已提交
226

227
		this.toUnbind.push(this.sashY.addListener('change', (e: ISashEvent) => {
I
isidor 已提交
228
			let doLayout = false;
229
			let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART);
230
			let newSashHeight = startPanelHeight - (e.currentY - startY);
231
			let promise = TPromise.as<void>(null);
I
isidor 已提交
232 233

			// Panel visible
234
			if (isPanelVisible) {
235

236
				// Automatically hide panel when a certain threshold is met
237 238
				if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) {
					let dragCompensation = MIN_PANEL_PART_HEIGHT - HIDE_PANEL_HEIGHT_THRESHOLD;
239
					promise = this.partService.setPanelHidden(true);
240
					startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation);
241
					this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from
242 243 244 245
				}

				// Otherwise size the panel accordingly
				else {
246 247
					this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashHeight); // Panel can not become smaller than MIN_PART_HEIGHT
					doLayout = newSashHeight >= this.partLayoutInfo.panel.minHeight;
248 249
				}
			}
I
isidor 已提交
250

251 252
			// Panel hidden
			else {
253
				if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) {
254
					startPanelHeight = 0;
255
					this.panelHeight = this.partLayoutInfo.panel.minHeight;
256
					promise = this.partService.setPanelHidden(false);
257
				}
I
isidor 已提交
258
			}
259

I
isidor 已提交
260
			if (doLayout) {
261
				promise.done(() => this.layout(), errors.onUnexpectedError);
I
isidor 已提交
262
			}
263
		}));
I
isidor 已提交
264

265
		this.toUnbind.push(this.sashXTwo.addListener('change', (e: ISashEvent) => {
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
			let doLayout = false;
			let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART);
			let newSashWidth = startPanelWidth - (e.currentX - startXTwo);
			let promise = TPromise.as<void>(null);

			// Panel visible
			if (isPanelVisible) {

				// Automatically hide panel when a certain threshold is met
				if (newSashWidth + HIDE_PANEL_WIDTH_THRESHOLD < this.partLayoutInfo.panel.minWidth) {
					let dragCompensation = MIN_PANEL_PART_WIDTH - HIDE_PANEL_WIDTH_THRESHOLD;
					promise = this.partService.setPanelHidden(true);
					startXTwo = Math.min(this.workbenchSize.width - this.activitybarWidth, e.currentX + dragCompensation);
					this.panelWidth = startPanelWidth; // when restoring panel, restore to the panel height we started from
				}

				// Otherwise size the panel accordingly
				else {
					this.panelWidth = Math.max(this.partLayoutInfo.panel.minWidth, newSashWidth); // Panel can not become smaller than MIN_PART_HEIGHT
					doLayout = newSashWidth >= this.partLayoutInfo.panel.minWidth;
				}
			}

			// Panel hidden
			else {
				if (startXTwo - e.currentX >= this.partLayoutInfo.panel.minWidth) {
					startPanelWidth = 0;
					this.panelWidth = this.partLayoutInfo.panel.minWidth;
					promise = this.partService.setPanelHidden(false);
				}
			}

			if (doLayout) {
				promise.done(() => this.layout(), errors.onUnexpectedError);
			}
301
		}));
M
Maxime Quandalle 已提交
302

303 304 305 306 307
		this.toUnbind.push(this.sashXOne.addListener('end', () => {
			this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
		}));

		this.toUnbind.push(this.sashY.addListener('end', () => {
I
isidor 已提交
308
			this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
309 310 311 312 313
		}));

		this.toUnbind.push(this.sashXTwo.addListener('end', () => {
			this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
		}));
M
Maxime Quandalle 已提交
314

315
		this.toUnbind.push(this.sashY.addListener('reset', () => {
I
isidor 已提交
316
			this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT;
M
Maxime Quandalle 已提交
317
			this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
318 319
			this.layout();
		}));
M
Maxime Quandalle 已提交
320

321
		this.toUnbind.push(this.sashXOne.addListener('reset', () => {
M
Maxime Quandalle 已提交
322 323
			let activeViewlet = this.viewletService.getActiveViewlet();
			let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth();
324
			this.sidebarWidth = Math.max(MIN_SIDEBAR_PART_WIDTH, optimalWidth || 0);
325
			this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
326
			this.partService.setSideBarHidden(false).done(() => this.layout(), errors.onUnexpectedError);
327 328 329
		}));

		this.toUnbind.push(this.sashXTwo.addListener('reset', () => {
330
			this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT;
331 332 333
			this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
			this.layout();
		}));
E
Erich Gamma 已提交
334 335
	}

336
	private onEditorsChanged(): void {
E
Erich Gamma 已提交
337

338
		// Make sure that we layout properly in case we detect that the sidebar or panel is large enought to cause
E
Erich Gamma 已提交
339 340
		// multiple opened editors to go below minimal size. The fix is to trigger a layout for any editor
		// input change that falls into this category.
341
		if (this.workbenchSize && (this.sidebarWidth || this.panelHeight)) {
E
Erich Gamma 已提交
342
			let visibleEditors = this.editorService.getVisibleEditors().length;
343
			if (visibleEditors > 1) {
344 345
				const sidebarOverflow = this.layoutEditorGroupsVertically && (this.workbenchSize.width - this.sidebarWidth < visibleEditors * MIN_EDITOR_PART_WIDTH);
				const panelOverflow = !this.layoutEditorGroupsVertically && (this.workbenchSize.height - this.panelHeight < visibleEditors * MIN_EDITOR_PART_HEIGHT);
346 347 348 349

				if (sidebarOverflow || panelOverflow) {
					this.layout();
				}
E
Erich Gamma 已提交
350 351 352 353
			}
		}
	}

354 355
	private onGroupOrientationChanged(): void {
		const newLayoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
356

357 358
		const doLayout = this.layoutEditorGroupsVertically !== newLayoutEditorGroupsVertically;
		this.layoutEditorGroupsVertically = newLayoutEditorGroupsVertically;
359 360 361 362 363 364

		if (doLayout) {
			this.layout();
		}
	}

I
isidor 已提交
365
	public layout(): void {
B
Benjamin Pasero 已提交
366
		this.workbenchSize = this.parent.getClientArea();
E
Erich Gamma 已提交
367

368
		const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
369 370 371 372
		const isTitlebarHidden = !this.partService.isVisible(Parts.TITLEBAR_PART);
		const isPanelHidden = !this.partService.isVisible(Parts.PANEL_PART);
		const isStatusbarHidden = !this.partService.isVisible(Parts.STATUSBAR_PART);
		const isSidebarHidden = !this.partService.isVisible(Parts.SIDEBAR_PART);
I
isidor 已提交
373
		const sidebarPosition = this.partService.getSideBarPosition();
374
		const panelPosition = this.partService.getPanelPosition();
E
Erich Gamma 已提交
375 376 377 378 379 380

		// Sidebar
		let sidebarWidth: number;
		if (isSidebarHidden) {
			sidebarWidth = 0;
		} else if (this.sidebarWidth !== -1) {
381
			sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.sidebarWidth);
E
Erich Gamma 已提交
382 383 384 385
		} else {
			sidebarWidth = this.workbenchSize.width / 5;
			this.sidebarWidth = sidebarWidth;
		}
B
Benjamin Pasero 已提交
386

387 388
		this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height;
		this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / getZoomFactor(); // adjust for zoom prevention
E
Erich Gamma 已提交
389

390
		const previousMaxPanelHeight = this.sidebarHeight - MIN_EDITOR_PART_HEIGHT;
391
		this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight;
I
isidor 已提交
392
		let sidebarSize = new Dimension(sidebarWidth, this.sidebarHeight);
E
Erich Gamma 已提交
393 394

		// Activity Bar
395
		this.activitybarWidth = isActivityBarHidden ? 0 : this.partLayoutInfo.activitybar.width;
396
		let activityBarSize = new Dimension(this.activitybarWidth, sidebarSize.height);
E
Erich Gamma 已提交
397

I
isidor 已提交
398 399
		// Panel part
		let panelHeight: number;
I
isidor 已提交
400
		let panelWidth: number;
I
isidor 已提交
401 402
		const editorCountForHeight = this.editorGroupService.getGroupOrientation() === 'horizontal' ? this.editorGroupService.getStacksModel().groups.length : 1;
		const maxPanelHeight = sidebarSize.height - editorCountForHeight * MIN_EDITOR_PART_HEIGHT;
I
isidor 已提交
403 404
		const maxPanelWidth = this.workbenchSize.width - activityBarSize.width - sidebarSize.width - editorCountForHeight * MIN_EDITOR_PART_WIDTH;

I
isidor 已提交
405
		if (isPanelHidden) {
I
isidor 已提交
406
			panelHeight = 0;
I
isidor 已提交
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
			panelWidth = 0;
		} else if (panelPosition === Position.BOTTOM) {
			if (this.panelHeight === previousMaxPanelHeight) {
				panelHeight = maxPanelHeight;
			} else if (this.panelHeight > 0) {
				panelHeight = Math.min(maxPanelHeight, Math.max(this.partLayoutInfo.panel.minHeight, this.panelHeight));
			} else {
				panelHeight = sidebarSize.height * DEFAULT_PANEL_SIZE_COEFFICIENT;
			}
			if (panelHeight / maxPanelHeight < 0.7) {
				// Remember the previous height only if the panel size is not too large.
				// To get a nice minimize effect even if a user dragged the panel sash to maximum.
				this.panelHeightBeforeMaximized = panelHeight;
			}

			panelWidth = this.workbenchSize.width - sidebarSize.width - activityBarSize.width;
I
isidor 已提交
423
		} else {
I
isidor 已提交
424 425 426 427 428 429
			panelHeight = sidebarSize.height;
			if (this.panelWidth > 0) {
				panelWidth = Math.min(maxPanelWidth, Math.max(this.partLayoutInfo.panel.minWidth, this.panelWidth));
			} else {
				panelWidth = (this.workbenchSize.width - activityBarSize.width - sidebarSize.width) * DEFAULT_PANEL_SIZE_COEFFICIENT;
			}
I
isidor 已提交
430
		}
I
isidor 已提交
431
		const panelDimension = new Dimension(panelWidth, panelHeight);
I
isidor 已提交
432

E
Erich Gamma 已提交
433 434 435
		// Editor
		let editorSize = {
			width: 0,
I
isidor 已提交
436
			height: 0
E
Erich Gamma 已提交
437 438
		};

I
isidor 已提交
439 440
		editorSize.width = this.workbenchSize.width - sidebarSize.width - activityBarSize.width - (panelPosition === Position.RIGHT ? panelDimension.width : 0);
		editorSize.height = sidebarSize.height - (panelPosition === Position.BOTTOM ? panelDimension.height : 0);
E
Erich Gamma 已提交
441 442

		// Assert Sidebar and Editor Size to not overflow
443 444
		let editorMinWidth = this.partLayoutInfo.editor.minWidth;
		let editorMinHeight = this.partLayoutInfo.editor.minHeight;
E
Erich Gamma 已提交
445 446
		let visibleEditorCount = this.editorService.getVisibleEditors().length;
		if (visibleEditorCount > 1) {
447
			if (this.layoutEditorGroupsVertically) {
448 449 450 451
				editorMinWidth *= visibleEditorCount; // when editors layout vertically, multiply the min editor width by number of visible editors
			} else {
				editorMinHeight *= visibleEditorCount; // when editors layout horizontally, multiply the min editor height by number of visible editors
			}
E
Erich Gamma 已提交
452
		}
B
Benjamin Pasero 已提交
453

454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
		if (editorSize.width < editorMinWidth) {
			let diff = editorMinWidth - editorSize.width;
			editorSize.width = editorMinWidth;
			if (panelPosition === Position.BOTTOM) {
				panelDimension.width = editorMinWidth;
			} else {
				// TODO@Isidor might need to reduce panel size and only then recude sidebar size
			}

			sidebarSize.width -= diff;
			sidebarSize.width = Math.max(MIN_SIDEBAR_PART_WIDTH, sidebarSize.width);
		}

		if (editorSize.height < editorMinHeight && panelPosition === Position.BOTTOM) {
			let diff = editorMinHeight - editorSize.height;
			editorSize.height = editorMinHeight;

			panelDimension.height -= diff;
			panelDimension.height = Math.max(MIN_PANEL_PART_HEIGHT, panelDimension.height);
		}
E
Erich Gamma 已提交
474 475 476

		if (!isSidebarHidden) {
			this.sidebarWidth = sidebarSize.width;
477
			this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
E
Erich Gamma 已提交
478 479
		}

I
isidor 已提交
480 481
		if (!isPanelHidden) {
			this.panelHeight = panelDimension.height;
482
			this.panelWidth = panelDimension.width;
I
isidor 已提交
483
			this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
484
			this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
I
isidor 已提交
485 486
		}

E
Erich Gamma 已提交
487 488
		// Workbench
		this.workbenchContainer
489
			.position(0, 0, 0, 0, 'relative')
E
Erich Gamma 已提交
490 491
			.size(this.workbenchSize.width, this.workbenchSize.height);

B
Benjamin Pasero 已提交
492 493 494 495 496 497 498
		// Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case.
		const workbenchContainer = this.workbenchContainer.getHTMLElement();
		if (workbenchContainer.scrollTop > 0) {
			workbenchContainer.scrollTop = 0;
		}
		if (workbenchContainer.scrollLeft > 0) {
			workbenchContainer.scrollLeft = 0;
E
Erich Gamma 已提交
499 500
		}

501 502 503 504 505 506 507
		// Title Part
		if (isTitlebarHidden) {
			this.titlebar.getContainer().hide();
		} else {
			this.titlebar.getContainer().show();
		}

I
isidor 已提交
508
		// Editor Part and Panel part
E
Erich Gamma 已提交
509
		this.editor.getContainer().size(editorSize.width, editorSize.height);
B
Benjamin Pasero 已提交
510
		this.panel.getContainer().size(panelDimension.width, panelDimension.height);
E
Erich Gamma 已提交
511

I
isidor 已提交
512 513 514 515 516 517 518 519
		if (panelPosition === Position.BOTTOM) {
			if (sidebarPosition === Position.LEFT) {
				this.editor.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
				this.panel.getContainer().position(editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
			} else {
				this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
				this.panel.getContainer().position(editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
			}
E
Erich Gamma 已提交
520
		} else {
I
isidor 已提交
521 522 523 524 525 526 527
			if (sidebarPosition === Position.LEFT) {
				this.editor.getContainer().position(this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
				this.panel.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
			} else {
				this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
				this.panel.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
			}
E
Erich Gamma 已提交
528 529 530
		}

		// Activity Bar Part
B
Benjamin Pasero 已提交
531
		this.activitybar.getContainer().size(null, activityBarSize.height);
E
Erich Gamma 已提交
532 533
		if (sidebarPosition === Position.LEFT) {
			this.activitybar.getContainer().getHTMLElement().style.right = '';
534
			this.activitybar.getContainer().position(this.titlebarHeight, null, 0, 0);
E
Erich Gamma 已提交
535 536
		} else {
			this.activitybar.getContainer().getHTMLElement().style.left = '';
537
			this.activitybar.getContainer().position(this.titlebarHeight, 0, 0, null);
E
Erich Gamma 已提交
538
		}
B
Benjamin Pasero 已提交
539 540 541 542 543
		if (isActivityBarHidden) {
			this.activitybar.getContainer().hide();
		} else {
			this.activitybar.getContainer().show();
		}
E
Erich Gamma 已提交
544 545 546 547 548

		// Sidebar Part
		this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height);

		if (sidebarPosition === Position.LEFT) {
549
			this.sidebar.getContainer().position(this.titlebarHeight, editorSize.width, 0, activityBarSize.width);
E
Erich Gamma 已提交
550
		} else {
551
			this.sidebar.getContainer().position(this.titlebarHeight, null, 0, editorSize.width);
E
Erich Gamma 已提交
552 553 554
		}

		// Statusbar Part
555
		this.statusbar.getContainer().position(this.workbenchSize.height - this.statusbarHeight);
556 557 558 559 560
		if (isStatusbarHidden) {
			this.statusbar.getContainer().hide();
		} else {
			this.statusbar.getContainer().show();
		}
E
Erich Gamma 已提交
561 562 563 564

		// Quick open
		this.quickopen.layout(this.workbenchSize);

I
isidor 已提交
565
		// Sashes
566 567 568 569 570 571 572 573
		this.sashXOne.layout();
		if (panelPosition === Position.BOTTOM) {
			this.sashY.layout();
			this.sashXTwo.hide();
		} else {
			this.sashY.hide();
			this.sashXTwo.layout();
		}
E
Erich Gamma 已提交
574 575

		// Propagate to Part Layouts
576
		this.titlebar.layout(new Dimension(this.workbenchSize.width, this.titlebarHeight));
E
Erich Gamma 已提交
577 578
		this.editor.layout(new Dimension(editorSize.width, editorSize.height));
		this.sidebar.layout(sidebarSize);
B
Benjamin Pasero 已提交
579
		this.panel.layout(panelDimension);
580
		this.activitybar.layout(activityBarSize);
E
Erich Gamma 已提交
581 582

		// Propagate to Context View
B
Benjamin Pasero 已提交
583
		this.contextViewService.layout();
E
Erich Gamma 已提交
584 585 586
	}

	public getVerticalSashTop(sash: Sash): number {
587
		return this.titlebarHeight;
E
Erich Gamma 已提交
588 589 590
	}

	public getVerticalSashLeft(sash: Sash): number {
591
		let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
E
Erich Gamma 已提交
592
		let sidebarPosition = this.partService.getSideBarPosition();
593
		if (sash === this.sashXOne) {
E
Erich Gamma 已提交
594

595 596 597 598 599
			if (sidebarPosition === Position.LEFT) {
				return isSidebarVisible ? this.sidebarWidth + this.activitybarWidth : this.activitybarWidth;
			}

			return isSidebarVisible ? this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth : this.workbenchSize.width - this.activitybarWidth;
E
Erich Gamma 已提交
600 601
		}

602
		return this.workbenchSize.width - this.panelWidth - (sidebarPosition === Position.RIGHT && isSidebarVisible ? this.sidebarWidth + this.activitybarWidth : 0);
E
Erich Gamma 已提交
603 604 605
	}

	public getVerticalSashHeight(sash: Sash): number {
I
isidor 已提交
606
		return this.sidebarHeight;
E
Erich Gamma 已提交
607 608
	}

I
isidor 已提交
609
	public getHorizontalSashTop(sash: Sash): number {
610 611
		// Horizontal sash should be a bit lower than the editor area, thus add 2px #5524
		return 2 + (this.partService.isVisible(Parts.PANEL_PART) ? this.sidebarHeight - this.panelHeight + this.titlebarHeight : this.sidebarHeight + this.titlebarHeight);
I
isidor 已提交
612 613 614
	}

	public getHorizontalSashLeft(sash: Sash): number {
615
		return this.partService.getSideBarPosition() === Position.LEFT ? this.getVerticalSashLeft(sash) : 0;
I
isidor 已提交
616 617 618
	}

	public getHorizontalSashWidth(sash: Sash): number {
I
isidor 已提交
619
		return this.panelWidth;
I
isidor 已提交
620 621
	}

622
	// change part size along the main axis
B
Benjamin Pasero 已提交
623 624
	public resizePart(part: Parts, sizeChange: number): void {
		const visibleEditors = this.editorService.getVisibleEditors().length;
625 626 627
		const sizeChangePxWidth = this.workbenchSize.width * (sizeChange / 100);
		const sizeChangePxHeight = this.workbenchSize.height * (sizeChange / 100);

B
Benjamin Pasero 已提交
628 629 630
		let doLayout = false;
		let newSashSize: number = 0;

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
		switch (part) {
			case Parts.SIDEBAR_PART:
				newSashSize = this.sidebarWidth + sizeChangePxWidth;
				this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, newSashSize); // Sidebar can not become smaller than MIN_PART_WIDTH

				if (this.layoutEditorGroupsVertically && (this.workbenchSize.width - this.sidebarWidth < visibleEditors * MIN_EDITOR_PART_WIDTH)) {
					this.sidebarWidth = (this.workbenchSize.width - visibleEditors * MIN_EDITOR_PART_WIDTH);
				}

				doLayout = true;
				break;
			case Parts.PANEL_PART:
				newSashSize = this.panelHeight + sizeChangePxHeight;
				this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashSize);
				doLayout = true;
				break;
			case Parts.EDITOR_PART:
				// If we have one editor we can cheat and resize sidebar with the negative delta
				const visibleEditorCount = this.editorService.getVisibleEditors().length;

				if (visibleEditorCount === 1) {
					this.sidebarWidth = this.sidebarWidth - sizeChangePxWidth;
					doLayout = true;
B
Benjamin Pasero 已提交
654 655 656
				} else {
					const stacks = this.editorGroupService.getStacksModel();
					const activeGroup = stacks.positionOfGroup(stacks.activeGroup);
657 658 659 660 661 662 663

					this.editorGroupService.resizeGroup(activeGroup, sizeChangePxWidth);
					doLayout = false;
				}
		}

		if (doLayout) {
B
Benjamin Pasero 已提交
664
			this.layout();
665 666 667
		}
	}

E
Erich Gamma 已提交
668
	public dispose(): void {
M
Martin Aeschlimann 已提交
669 670 671
		if (this.toUnbind) {
			dispose(this.toUnbind);
			this.toUnbind = null;
E
Erich Gamma 已提交
672 673
		}
	}
B
Benjamin Pasero 已提交
674
}