layout.ts 27.7 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
const MIN_PANEL_PART_WIDTH = 220;
I
isidor 已提交
28
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;
69
	private _sidebarWidth: number;
I
isidor 已提交
70
	private sidebarHeight: number;
71 72
	private titlebarHeight: number;
	private statusbarHeight: number;
73 74
	private _panelHeight: number;
	private _panelWidth: number;
75
	private layoutEditorGroupsVertically: boolean;
E
Erich Gamma 已提交
76

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

110 111 112 113 114
		this.sashXOne = new Sash(this.workbenchContainer.getHTMLElement(), this, {
			baseSize: 5
		});

		this.sashXTwo = new Sash(this.workbenchContainer.getHTMLElement(), this, {
E
Erich Gamma 已提交
115 116
			baseSize: 5
		});
117

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

123 124 125
		this._sidebarWidth = this.storageService.getInteger(WorkbenchLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, -1);
		this._panelHeight = this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, 0);
		this._panelWidth = this.storageService.getInteger(WorkbenchLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, 0);
E
Erich Gamma 已提交
126

127
		this.layoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
128

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

E
Erich Gamma 已提交
133 134 135
		this.registerSashListeners();
	}

I
isidor 已提交
136 137 138 139 140 141 142 143
	private get activitybarWidth(): number {
		if (this.partService.isVisible(Parts.ACTIVITYBAR_PART)) {
			return this.partLayoutInfo.activitybar.width;
		}

		return 0;
	}

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	private get panelHeight(): number {
		const panelPosition = this.partService.getPanelPosition();
		if (panelPosition === Position.RIGHT) {
			return this.sidebarHeight;
		}

		return this._panelHeight;
	}

	private set panelHeight(value: number) {
		const editorCountForHeight = this.editorGroupService.getGroupOrientation() === 'horizontal' ? this.editorGroupService.getStacksModel().groups.length : 1;
		const maxPanelHeight = this.sidebarHeight - editorCountForHeight * MIN_EDITOR_PART_HEIGHT;
		this._panelHeight = Math.min(maxPanelHeight, Math.max(this.partLayoutInfo.panel.minHeight, value));
	}

	private get panelWidth(): number {
		const panelPosition = this.partService.getPanelPosition();
		if (panelPosition === Position.BOTTOM) {
			return this.workbenchSize.width - this.activitybarWidth - this.sidebarWidth;
		}

		return this._panelWidth;
	}

	private set panelWidth(value: number) {
		const editorCountForWidth = this.editorGroupService.getGroupOrientation() === 'vertical' ? this.editorGroupService.getStacksModel().groups.length : 1;
		const maxPanelWidth = this.workbenchSize.width - editorCountForWidth * MIN_EDITOR_PART_WIDTH - this.sidebarWidth - this.activitybarWidth;
		this._panelWidth = Math.min(maxPanelWidth, Math.max(this.partLayoutInfo.panel.minWidth, value));
	}

	private get sidebarWidth(): number {
		if (this.partService.isVisible(Parts.SIDEBAR_PART)) {
			return this._sidebarWidth;
		}

		return 0;
	}

	private set sidebarWidth(value: number) {
I
isidor 已提交
183 184 185 186
		const editorCountForWidth = this.editorGroupService.getGroupOrientation() === 'vertical' ? this.editorGroupService.getStacksModel().groups.length : 1;
		const panelMinWidth = this.partService.getPanelPosition() === Position.RIGHT && this.partService.isVisible(Parts.PANEL_PART) ? MIN_PANEL_PART_WIDTH : 0;
		const maxSidebarWidth = this.workbenchSize.width - this.activitybarWidth - editorCountForWidth * MIN_EDITOR_PART_WIDTH - panelMinWidth;
		this._sidebarWidth = Math.min(maxSidebarWidth, Math.max(this.partLayoutInfo.sidebar.minWidth, value));
187 188
	}

189 190 191 192 193 194 195 196 197 198 199 200
	private getPartLayoutInfo(): PartLayoutInfo {
		return {
			titlebar: {
				height: TITLE_BAR_HEIGHT
			},
			activitybar: {
				width: ACTIVITY_BAR_WIDTH
			},
			sidebar: {
				minWidth: MIN_SIDEBAR_PART_WIDTH
			},
			panel: {
I
isidor 已提交
201 202
				minHeight: MIN_PANEL_PART_HEIGHT,
				minWidth: MIN_PANEL_PART_WIDTH
203 204 205 206 207 208 209 210 211 212 213
			},
			editor: {
				minWidth: MIN_EDITOR_PART_WIDTH,
				minHeight: MIN_EDITOR_PART_HEIGHT
			},
			statusbar: {
				height: STATUS_BAR_HEIGHT
			}
		};
	}

E
Erich Gamma 已提交
214 215
	private registerSashListeners(): void {
		let startX: number = 0;
I
isidor 已提交
216
		let startY: number = 0;
217
		let startXTwo: number = 0;
218 219 220
		let startSidebarWidth: number;
		let startPanelHeight: number;
		let startPanelWidth: number;
E
Erich Gamma 已提交
221

222 223
		this.toUnbind.push(this.sashXOne.addListener('start', (e: ISashEvent) => {
			startSidebarWidth = this.sidebarWidth;
E
Erich Gamma 已提交
224
			startX = e.startX;
225
		}));
E
Erich Gamma 已提交
226

227 228
		this.toUnbind.push(this.sashY.addListener('start', (e: ISashEvent) => {
			startPanelHeight = this.panelHeight;
I
isidor 已提交
229
			startY = e.startY;
230 231 232 233
		}));

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

237
		this.toUnbind.push(this.sashXOne.addListener('change', (e: ISashEvent) => {
E
Erich Gamma 已提交
238 239
			let doLayout = false;
			let sidebarPosition = this.partService.getSideBarPosition();
240
			let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
241
			let newSashWidth = (sidebarPosition === Position.LEFT) ? startSidebarWidth + e.currentX - startX : startSidebarWidth - e.currentX + startX;
242
			let promise = TPromise.as<void>(null);
E
Erich Gamma 已提交
243 244

			// Sidebar visible
245
			if (isSidebarVisible) {
E
Erich Gamma 已提交
246 247

				// Automatically hide side bar when a certain threshold is met
248 249
				if (newSashWidth + HIDE_SIDEBAR_WIDTH_THRESHOLD < this.partLayoutInfo.sidebar.minWidth) {
					let dragCompensation = MIN_SIDEBAR_PART_WIDTH - HIDE_SIDEBAR_WIDTH_THRESHOLD;
250
					promise = this.partService.setSideBarHidden(true);
251
					startX = (sidebarPosition === Position.LEFT) ? Math.max(this.activitybarWidth, e.currentX - dragCompensation) : Math.min(e.currentX + dragCompensation, this.workbenchSize.width - this.activitybarWidth);
252
					this.sidebarWidth = startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from
E
Erich Gamma 已提交
253 254 255 256
				}

				// Otherwise size the sidebar accordingly
				else {
257 258
					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 已提交
259 260 261 262 263
				}
			}

			// Sidebar hidden
			else {
264 265
				if ((sidebarPosition === Position.LEFT && e.currentX - startX >= this.partLayoutInfo.sidebar.minWidth) ||
					(sidebarPosition === Position.RIGHT && startX - e.currentX >= this.partLayoutInfo.sidebar.minWidth)) {
266
					startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX);
267
					this.sidebarWidth = this.partLayoutInfo.sidebar.minWidth;
268
					promise = this.partService.setSideBarHidden(false);
E
Erich Gamma 已提交
269 270 271 272
				}
			}

			if (doLayout) {
273
				promise.done(() => this.layout(), errors.onUnexpectedError);
E
Erich Gamma 已提交
274
			}
275
		}));
E
Erich Gamma 已提交
276

277
		this.toUnbind.push(this.sashY.addListener('change', (e: ISashEvent) => {
I
isidor 已提交
278
			let doLayout = false;
279
			let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART);
280
			let newSashHeight = startPanelHeight - (e.currentY - startY);
281
			let promise = TPromise.as<void>(null);
I
isidor 已提交
282 283

			// Panel visible
284
			if (isPanelVisible) {
285

286
				// Automatically hide panel when a certain threshold is met
287 288
				if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) {
					let dragCompensation = MIN_PANEL_PART_HEIGHT - HIDE_PANEL_HEIGHT_THRESHOLD;
289
					promise = this.partService.setPanelHidden(true);
290
					startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation);
291
					this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from
292 293 294 295
				}

				// Otherwise size the panel accordingly
				else {
296 297
					this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashHeight); // Panel can not become smaller than MIN_PART_HEIGHT
					doLayout = newSashHeight >= this.partLayoutInfo.panel.minHeight;
298 299
				}
			}
I
isidor 已提交
300

301 302
			// Panel hidden
			else {
303
				if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) {
304
					startPanelHeight = 0;
305
					this.panelHeight = this.partLayoutInfo.panel.minHeight;
306
					promise = this.partService.setPanelHidden(false);
307
				}
I
isidor 已提交
308
			}
309

I
isidor 已提交
310
			if (doLayout) {
311
				promise.done(() => this.layout(), errors.onUnexpectedError);
I
isidor 已提交
312
			}
313
		}));
I
isidor 已提交
314

315
		this.toUnbind.push(this.sashXTwo.addListener('change', (e: ISashEvent) => {
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
			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 {
334
					this.panelWidth = newSashWidth;
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
					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);
			}
351
		}));
M
Maxime Quandalle 已提交
352

353 354 355 356 357
		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 已提交
358
			this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
359 360 361 362 363
		}));

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

365
		this.toUnbind.push(this.sashY.addListener('reset', () => {
I
isidor 已提交
366
			this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT;
M
Maxime Quandalle 已提交
367
			this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
368 369
			this.layout();
		}));
M
Maxime Quandalle 已提交
370

371
		this.toUnbind.push(this.sashXOne.addListener('reset', () => {
M
Maxime Quandalle 已提交
372 373
			let activeViewlet = this.viewletService.getActiveViewlet();
			let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth();
374
			this.sidebarWidth = optimalWidth || 0;
375
			this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
376
			this.partService.setSideBarHidden(false).done(() => this.layout(), errors.onUnexpectedError);
377 378 379
		}));

		this.toUnbind.push(this.sashXTwo.addListener('reset', () => {
380
			this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT;
381 382 383
			this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
			this.layout();
		}));
E
Erich Gamma 已提交
384 385
	}

386
	private onEditorsChanged(): void {
E
Erich Gamma 已提交
387

388
		// Make sure that we layout properly in case we detect that the sidebar or panel is large enought to cause
E
Erich Gamma 已提交
389 390
		// 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.
391
		if (this.workbenchSize && (this.sidebarWidth || this.panelHeight)) {
E
Erich Gamma 已提交
392
			let visibleEditors = this.editorService.getVisibleEditors().length;
393
			if (visibleEditors > 1) {
394 395
				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);
396 397 398 399

				if (sidebarOverflow || panelOverflow) {
					this.layout();
				}
E
Erich Gamma 已提交
400 401 402 403
			}
		}
	}

404 405
	private onGroupOrientationChanged(): void {
		const newLayoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
406

407 408
		const doLayout = this.layoutEditorGroupsVertically !== newLayoutEditorGroupsVertically;
		this.layoutEditorGroupsVertically = newLayoutEditorGroupsVertically;
409 410 411 412 413 414

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

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

418
		const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
419 420 421 422
		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 已提交
423
		const sidebarPosition = this.partService.getSideBarPosition();
424
		const panelPosition = this.partService.getPanelPosition();
E
Erich Gamma 已提交
425 426

		// Sidebar
427 428
		if (this.sidebarWidth !== -1) {
			this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.sidebarWidth);
E
Erich Gamma 已提交
429
		} else {
430
			this.sidebarWidth = this.workbenchSize.width / 5;
E
Erich Gamma 已提交
431
		}
B
Benjamin Pasero 已提交
432

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

436
		const previousMaxPanelHeight = this.sidebarHeight - MIN_EDITOR_PART_HEIGHT;
437
		this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight;
438
		let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight);
E
Erich Gamma 已提交
439 440

		// Activity Bar
441
		let activityBarSize = new Dimension(this.activitybarWidth, sidebarSize.height);
E
Erich Gamma 已提交
442

I
isidor 已提交
443 444
		// Panel part
		let panelHeight: number;
I
isidor 已提交
445
		let panelWidth: number;
I
isidor 已提交
446 447
		const editorCountForHeight = this.editorGroupService.getGroupOrientation() === 'horizontal' ? this.editorGroupService.getStacksModel().groups.length : 1;
		const maxPanelHeight = sidebarSize.height - editorCountForHeight * MIN_EDITOR_PART_HEIGHT;
I
isidor 已提交
448 449
		const maxPanelWidth = this.workbenchSize.width - activityBarSize.width - sidebarSize.width - editorCountForHeight * MIN_EDITOR_PART_WIDTH;

I
isidor 已提交
450
		if (isPanelHidden) {
I
isidor 已提交
451
			panelHeight = 0;
I
isidor 已提交
452 453 454 455 456 457 458 459 460 461 462
			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;
			}

			panelWidth = this.workbenchSize.width - sidebarSize.width - activityBarSize.width;
I
isidor 已提交
463
		} else {
I
isidor 已提交
464 465 466 467 468 469
			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 已提交
470
		}
I
isidor 已提交
471
		const panelDimension = new Dimension(panelWidth, panelHeight);
I
isidor 已提交
472

E
Erich Gamma 已提交
473 474 475
		// Editor
		let editorSize = {
			width: 0,
I
isidor 已提交
476
			height: 0
E
Erich Gamma 已提交
477 478
		};

I
isidor 已提交
479 480
		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 已提交
481 482

		// Assert Sidebar and Editor Size to not overflow
483 484
		let editorMinWidth = this.partLayoutInfo.editor.minWidth;
		let editorMinHeight = this.partLayoutInfo.editor.minHeight;
E
Erich Gamma 已提交
485 486
		let visibleEditorCount = this.editorService.getVisibleEditors().length;
		if (visibleEditorCount > 1) {
487
			if (this.layoutEditorGroupsVertically) {
488 489 490 491
				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 已提交
492
		}
B
Benjamin Pasero 已提交
493

494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
		if (editorSize.width < editorMinWidth) {
			let diff = editorMinWidth - editorSize.width;
			editorSize.width = editorMinWidth;
			if (panelPosition === Position.BOTTOM) {
				panelDimension.width = editorMinWidth;
			}

			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 已提交
512 513 514

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

I
isidor 已提交
518
		if (!isPanelHidden) {
519 520 521 522 523 524 525
			if (panelPosition === Position.BOTTOM) {
				this.panelHeight = panelDimension.height;
				this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
			} else {
				this.panelWidth = panelDimension.width;
				this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
			}
I
isidor 已提交
526 527
		}

E
Erich Gamma 已提交
528 529
		// Workbench
		this.workbenchContainer
530
			.position(0, 0, 0, 0, 'relative')
E
Erich Gamma 已提交
531 532
			.size(this.workbenchSize.width, this.workbenchSize.height);

B
Benjamin Pasero 已提交
533 534 535 536 537 538 539
		// 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 已提交
540 541
		}

542 543 544 545 546 547 548
		// Title Part
		if (isTitlebarHidden) {
			this.titlebar.getContainer().hide();
		} else {
			this.titlebar.getContainer().show();
		}

I
isidor 已提交
549
		// Editor Part and Panel part
E
Erich Gamma 已提交
550
		this.editor.getContainer().size(editorSize.width, editorSize.height);
B
Benjamin Pasero 已提交
551
		this.panel.getContainer().size(panelDimension.width, panelDimension.height);
E
Erich Gamma 已提交
552

I
isidor 已提交
553 554 555 556 557 558 559 560
		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 已提交
561
		} else {
I
isidor 已提交
562 563 564 565 566 567 568
			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 已提交
569 570 571
		}

		// Activity Bar Part
B
Benjamin Pasero 已提交
572
		this.activitybar.getContainer().size(null, activityBarSize.height);
E
Erich Gamma 已提交
573 574
		if (sidebarPosition === Position.LEFT) {
			this.activitybar.getContainer().getHTMLElement().style.right = '';
575
			this.activitybar.getContainer().position(this.titlebarHeight, null, 0, 0);
E
Erich Gamma 已提交
576 577
		} else {
			this.activitybar.getContainer().getHTMLElement().style.left = '';
578
			this.activitybar.getContainer().position(this.titlebarHeight, 0, 0, null);
E
Erich Gamma 已提交
579
		}
B
Benjamin Pasero 已提交
580 581 582 583 584
		if (isActivityBarHidden) {
			this.activitybar.getContainer().hide();
		} else {
			this.activitybar.getContainer().show();
		}
E
Erich Gamma 已提交
585 586 587 588 589

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

		if (sidebarPosition === Position.LEFT) {
590
			this.sidebar.getContainer().position(this.titlebarHeight, editorSize.width, 0, activityBarSize.width);
E
Erich Gamma 已提交
591
		} else {
592
			this.sidebar.getContainer().position(this.titlebarHeight, null, 0, editorSize.width);
E
Erich Gamma 已提交
593 594 595
		}

		// Statusbar Part
596
		this.statusbar.getContainer().position(this.workbenchSize.height - this.statusbarHeight);
597 598 599 600 601
		if (isStatusbarHidden) {
			this.statusbar.getContainer().hide();
		} else {
			this.statusbar.getContainer().show();
		}
E
Erich Gamma 已提交
602 603 604 605

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

I
isidor 已提交
606
		// Sashes
607 608 609
		this.sashXOne.layout();
		if (panelPosition === Position.BOTTOM) {
			this.sashXTwo.hide();
610 611
			this.sashY.layout();
			this.sashY.show();
612 613 614
		} else {
			this.sashY.hide();
			this.sashXTwo.layout();
615
			this.sashXTwo.show();
616
		}
E
Erich Gamma 已提交
617 618

		// Propagate to Part Layouts
619
		this.titlebar.layout(new Dimension(this.workbenchSize.width, this.titlebarHeight));
E
Erich Gamma 已提交
620 621
		this.editor.layout(new Dimension(editorSize.width, editorSize.height));
		this.sidebar.layout(sidebarSize);
B
Benjamin Pasero 已提交
622
		this.panel.layout(panelDimension);
623
		this.activitybar.layout(activityBarSize);
E
Erich Gamma 已提交
624 625

		// Propagate to Context View
B
Benjamin Pasero 已提交
626
		this.contextViewService.layout();
E
Erich Gamma 已提交
627 628 629
	}

	public getVerticalSashTop(sash: Sash): number {
630
		return this.titlebarHeight;
E
Erich Gamma 已提交
631 632 633 634
	}

	public getVerticalSashLeft(sash: Sash): number {
		let sidebarPosition = this.partService.getSideBarPosition();
635
		if (sash === this.sashXOne) {
E
Erich Gamma 已提交
636

637
			if (sidebarPosition === Position.LEFT) {
638
				return this.sidebarWidth + this.activitybarWidth;
639 640
			}

641
			return this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth;
E
Erich Gamma 已提交
642 643
		}

644
		return this.workbenchSize.width - this.panelWidth - (sidebarPosition === Position.RIGHT ? this.sidebarWidth + this.activitybarWidth : 0);
E
Erich Gamma 已提交
645 646 647
	}

	public getVerticalSashHeight(sash: Sash): number {
I
isidor 已提交
648
		return this.sidebarHeight;
E
Erich Gamma 已提交
649 650
	}

I
isidor 已提交
651
	public getHorizontalSashTop(sash: Sash): number {
652 653
		// 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 已提交
654 655 656
	}

	public getHorizontalSashLeft(sash: Sash): number {
657 658 659 660 661
		if (this.partService.getSideBarPosition() === Position.RIGHT) {
			return 0;
		}

		return this.sidebarWidth + this.activitybarWidth;
I
isidor 已提交
662 663 664
	}

	public getHorizontalSashWidth(sash: Sash): number {
I
isidor 已提交
665
		return this.panelWidth;
I
isidor 已提交
666 667
	}

668
	// change part size along the main axis
B
Benjamin Pasero 已提交
669 670
	public resizePart(part: Parts, sizeChange: number): void {
		const visibleEditors = this.editorService.getVisibleEditors().length;
671 672 673
		const sizeChangePxWidth = this.workbenchSize.width * (sizeChange / 100);
		const sizeChangePxHeight = this.workbenchSize.height * (sizeChange / 100);

B
Benjamin Pasero 已提交
674 675
		let doLayout = false;

676 677
		switch (part) {
			case Parts.SIDEBAR_PART:
678
				this.sidebarWidth = this.sidebarWidth + sizeChangePxWidth; // Sidebar can not become smaller than MIN_PART_WIDTH
679 680 681 682 683 684 685 686

				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:
687 688
				this.panelHeight = this.panelHeight + sizeChangePxHeight;
				this.panelWidth = this.panelWidth + sizeChangePxWidth;
689 690 691 692 693 694 695 696 697
				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 已提交
698 699 700
				} else {
					const stacks = this.editorGroupService.getStacksModel();
					const activeGroup = stacks.positionOfGroup(stacks.activeGroup);
701 702 703 704 705 706 707

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

		if (doLayout) {
B
Benjamin Pasero 已提交
708
			this.layout();
709 710 711
		}
	}

E
Erich Gamma 已提交
712
	public dispose(): void {
M
Martin Aeschlimann 已提交
713 714 715
		if (this.toUnbind) {
			dispose(this.toUnbind);
			this.toUnbind = null;
E
Erich Gamma 已提交
716 717
		}
	}
B
Benjamin Pasero 已提交
718
}