layout.ts 27.8 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 = 300;
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
I
isidor 已提交
427
		if (this.sidebarWidth === -1) {
428
			this.sidebarWidth = this.workbenchSize.width / 5;
E
Erich Gamma 已提交
429
		}
B
Benjamin Pasero 已提交
430

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

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

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

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

I
isidor 已提交
448
		if (isPanelHidden) {
I
isidor 已提交
449
			panelHeight = 0;
I
isidor 已提交
450 451 452 453 454 455 456 457 458 459 460
			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 已提交
461
		} else {
I
isidor 已提交
462 463 464 465 466 467
			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 已提交
468
		}
I
isidor 已提交
469
		const panelDimension = new Dimension(panelWidth, panelHeight);
I
isidor 已提交
470

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

I
isidor 已提交
477 478
		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 已提交
479 480

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

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
		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 已提交
510 511 512

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

I
isidor 已提交
516
		if (!isPanelHidden) {
517 518 519 520 521 522 523
			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 已提交
524 525
		}

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

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

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

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

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

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

		// Sidebar Part
		this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height);
586
		const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0);
E
Erich Gamma 已提交
587
		if (sidebarPosition === Position.LEFT) {
588
			this.sidebar.getContainer().position(this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
E
Erich Gamma 已提交
589
		} else {
590
			this.sidebar.getContainer().position(this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
E
Erich Gamma 已提交
591 592 593
		}

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

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

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

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

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

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

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

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

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

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

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

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

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

		return this.sidebarWidth + this.activitybarWidth;
I
isidor 已提交
660 661 662
	}

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

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

B
Benjamin Pasero 已提交
672 673
		let doLayout = false;

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

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

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

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

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