layout.ts 29.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';
14
import { IPartService, Position, ILayoutOptions, 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;
29
const PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY = 0.7;
E
Erich Gamma 已提交
30
const HIDE_SIDEBAR_WIDTH_THRESHOLD = 50;
I
isidor 已提交
31
const HIDE_PANEL_HEIGHT_THRESHOLD = 50;
32
const HIDE_PANEL_WIDTH_THRESHOLD = 100;
33 34 35
const TITLE_BAR_HEIGHT = 22;
const STATUS_BAR_HEIGHT = 22;
const ACTIVITY_BAR_WIDTH = 50;
E
Erich Gamma 已提交
36

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

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

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

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

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

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

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

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

128 129 130
		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 已提交
131

132
		this.layoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
133

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

E
Erich Gamma 已提交
138 139 140
		this.registerSashListeners();
	}

I
isidor 已提交
141 142 143 144 145 146 147 148
	private get activitybarWidth(): number {
		if (this.partService.isVisible(Parts.ACTIVITYBAR_PART)) {
			return this.partLayoutInfo.activitybar.width;
		}

		return 0;
	}

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 183 184 185 186 187
	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 已提交
188 189
		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;
I
isidor 已提交
190 191
		const maxSidebarWidth = this.workbenchSize.width - this.activitybarWidth - editorCountForWidth * MIN_EDITOR_PART_WIDTH - panelMinWidth;
		this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, Math.min(maxSidebarWidth, value));
192 193
	}

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

E
Erich Gamma 已提交
219 220
	private registerSashListeners(): void {
		let startX: number = 0;
I
isidor 已提交
221
		let startY: number = 0;
222
		let startXTwo: number = 0;
223 224 225
		let startSidebarWidth: number;
		let startPanelHeight: number;
		let startPanelWidth: number;
E
Erich Gamma 已提交
226

227 228
		this.toUnbind.push(this.sashXOne.addListener('start', (e: ISashEvent) => {
			startSidebarWidth = this.sidebarWidth;
E
Erich Gamma 已提交
229
			startX = e.startX;
230
		}));
E
Erich Gamma 已提交
231

232 233
		this.toUnbind.push(this.sashY.addListener('start', (e: ISashEvent) => {
			startPanelHeight = this.panelHeight;
I
isidor 已提交
234
			startY = e.startY;
235 236 237 238
		}));

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

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

			// Sidebar visible
250
			if (isSidebarVisible) {
E
Erich Gamma 已提交
251 252

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

				// Otherwise size the sidebar accordingly
				else {
262 263
					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 已提交
264 265 266 267 268
				}
			}

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

			if (doLayout) {
278
				promise.done(() => this.layout(), errors.onUnexpectedError);
E
Erich Gamma 已提交
279
			}
280
		}));
E
Erich Gamma 已提交
281

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

			// Panel visible
289
			if (isPanelVisible) {
290

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

				// Otherwise size the panel accordingly
				else {
301 302
					this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashHeight); // Panel can not become smaller than MIN_PART_HEIGHT
					doLayout = newSashHeight >= this.partLayoutInfo.panel.minHeight;
303 304
				}
			}
I
isidor 已提交
305

306 307
			// Panel hidden
			else {
308
				if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) {
309
					startPanelHeight = 0;
310
					this.panelHeight = this.partLayoutInfo.panel.minHeight;
311
					promise = this.partService.setPanelHidden(false);
312
				}
I
isidor 已提交
313
			}
314

I
isidor 已提交
315
			if (doLayout) {
316
				promise.done(() => this.layout(), errors.onUnexpectedError);
I
isidor 已提交
317
			}
318
		}));
I
isidor 已提交
319

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

358 359 360 361 362
		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 已提交
363
			this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
364 365 366 367 368
		}));

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

370
		this.toUnbind.push(this.sashY.addListener('reset', () => {
I
isidor 已提交
371
			this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT;
M
Maxime Quandalle 已提交
372
			this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
373 374
			this.layout();
		}));
M
Maxime Quandalle 已提交
375

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

		this.toUnbind.push(this.sashXTwo.addListener('reset', () => {
385
			this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT;
386 387 388
			this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
			this.layout();
		}));
E
Erich Gamma 已提交
389 390
	}

391
	private onEditorsChanged(): void {
E
Erich Gamma 已提交
392

393
		// Make sure that we layout properly in case we detect that the sidebar or panel is large enought to cause
E
Erich Gamma 已提交
394 395
		// 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.
396
		if (this.workbenchSize && (this.sidebarWidth || this.panelHeight)) {
E
Erich Gamma 已提交
397
			let visibleEditors = this.editorService.getVisibleEditors().length;
I
isidor 已提交
398
			const panelVertical = this.partService.getPanelPosition() === Position.RIGHT;
399
			if (visibleEditors > 1) {
400
				const sidebarOverflow = this.layoutEditorGroupsVertically && (this.workbenchSize.width - this.sidebarWidth < visibleEditors * MIN_EDITOR_PART_WIDTH);
I
isidor 已提交
401 402
				const panelOverflow = !this.layoutEditorGroupsVertically && !panelVertical && (this.workbenchSize.height - this.panelHeight < visibleEditors * MIN_EDITOR_PART_HEIGHT) ||
					panelVertical && this.layoutEditorGroupsVertically && (this.workbenchSize.width - this.panelWidth < visibleEditors * MIN_EDITOR_PART_WIDTH);
403 404 405 406

				if (sidebarOverflow || panelOverflow) {
					this.layout();
				}
E
Erich Gamma 已提交
407 408 409 410
			}
		}
	}

411 412
	private onGroupOrientationChanged(): void {
		const newLayoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
413

414 415
		const doLayout = this.layoutEditorGroupsVertically !== newLayoutEditorGroupsVertically;
		this.layoutEditorGroupsVertically = newLayoutEditorGroupsVertically;
416 417 418 419 420 421

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

422
	public layout(options?: ILayoutOptions): void {
B
Benjamin Pasero 已提交
423
		this.workbenchSize = this.parent.getClientArea();
E
Erich Gamma 已提交
424

425
		const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
426 427 428 429
		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 已提交
430
		const sidebarPosition = this.partService.getSideBarPosition();
431
		const panelPosition = this.partService.getPanelPosition();
E
Erich Gamma 已提交
432 433

		// Sidebar
I
isidor 已提交
434
		if (this.sidebarWidth === -1) {
435
			this.sidebarWidth = this.workbenchSize.width / 5;
E
Erich Gamma 已提交
436
		}
B
Benjamin Pasero 已提交
437

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

441
		const previousMaxPanelHeight = this.sidebarHeight - MIN_EDITOR_PART_HEIGHT;
442
		this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight;
443
		let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight);
E
Erich Gamma 已提交
444 445

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

I
isidor 已提交
448 449
		// Panel part
		let panelHeight: number;
I
isidor 已提交
450
		let panelWidth: number;
I
isidor 已提交
451
		const editorCountForHeight = this.editorGroupService.getGroupOrientation() === 'horizontal' ? this.editorGroupService.getStacksModel().groups.length : 1;
452
		const editorCountForWidth = this.editorGroupService.getGroupOrientation() === 'vertical' ? this.editorGroupService.getStacksModel().groups.length : 1;
I
isidor 已提交
453
		const maxPanelHeight = sidebarSize.height - editorCountForHeight * MIN_EDITOR_PART_HEIGHT;
454
		const maxPanelWidth = this.workbenchSize.width - activityBarSize.width - sidebarSize.width - editorCountForWidth * MIN_EDITOR_PART_WIDTH;
I
isidor 已提交
455

I
isidor 已提交
456
		if (isPanelHidden) {
I
isidor 已提交
457
			panelHeight = 0;
I
isidor 已提交
458 459 460 461 462 463 464 465 466 467
			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;
468 469 470 471 472 473 474 475 476

			if (options && options.toggleMaximizedPanel) {
				panelHeight = this.panelMaximized ? Math.max(this.partLayoutInfo.panel.minHeight, Math.min(this.panelSizeBeforeMaximized, maxPanelHeight)) : maxPanelHeight;
			}

			this.panelMaximized = panelHeight === maxPanelHeight;
			if (panelHeight / maxPanelHeight < PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY) {
				this.panelSizeBeforeMaximized = panelHeight;
			}
I
isidor 已提交
477
		} else {
I
isidor 已提交
478 479 480 481 482 483
			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;
			}
484 485 486 487 488 489 490 491 492

			if (options && options.toggleMaximizedPanel) {
				panelWidth = this.panelMaximized ? Math.max(this.partLayoutInfo.panel.minWidth, Math.min(this.panelSizeBeforeMaximized, maxPanelWidth)) : maxPanelWidth;
			}

			this.panelMaximized = panelWidth === maxPanelWidth;
			if (panelWidth / maxPanelWidth < PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY) {
				this.panelSizeBeforeMaximized = panelWidth;
			}
I
isidor 已提交
493
		}
I
isidor 已提交
494
		const panelDimension = new Dimension(panelWidth, panelHeight);
I
isidor 已提交
495

E
Erich Gamma 已提交
496 497 498
		// Editor
		let editorSize = {
			width: 0,
I
isidor 已提交
499
			height: 0
E
Erich Gamma 已提交
500 501
		};

I
isidor 已提交
502 503
		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 已提交
504 505

		// Assert Sidebar and Editor Size to not overflow
506 507
		let editorMinWidth = this.partLayoutInfo.editor.minWidth;
		let editorMinHeight = this.partLayoutInfo.editor.minHeight;
E
Erich Gamma 已提交
508 509
		let visibleEditorCount = this.editorService.getVisibleEditors().length;
		if (visibleEditorCount > 1) {
510
			if (this.layoutEditorGroupsVertically) {
511 512 513 514
				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 已提交
515
		}
B
Benjamin Pasero 已提交
516

517 518 519 520 521
		if (editorSize.width < editorMinWidth) {
			let diff = editorMinWidth - editorSize.width;
			editorSize.width = editorMinWidth;
			if (panelPosition === Position.BOTTOM) {
				panelDimension.width = editorMinWidth;
I
isidor 已提交
522 523 524 525
			} else if (panelDimension.width >= diff) {
				const oldWidth = panelDimension.width;
				panelDimension.width = Math.max(MIN_PANEL_PART_WIDTH, panelDimension.width - diff);
				diff = oldWidth - panelDimension.width;
526 527
			}

I
isidor 已提交
528 529 530 531
			if (sidebarSize.width >= diff) {
				sidebarSize.width -= diff;
				sidebarSize.width = Math.max(MIN_SIDEBAR_PART_WIDTH, sidebarSize.width);
			}
532 533 534 535 536 537 538 539 540
		}

		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 已提交
541 542 543

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

I
isidor 已提交
547
		if (!isPanelHidden) {
548 549 550 551 552 553 554
			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 已提交
555 556
		}

E
Erich Gamma 已提交
557 558
		// Workbench
		this.workbenchContainer
559
			.position(0, 0, 0, 0, 'relative')
E
Erich Gamma 已提交
560 561
			.size(this.workbenchSize.width, this.workbenchSize.height);

B
Benjamin Pasero 已提交
562 563 564 565 566 567 568
		// 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 已提交
569 570
		}

571 572 573 574 575 576 577
		// Title Part
		if (isTitlebarHidden) {
			this.titlebar.getContainer().hide();
		} else {
			this.titlebar.getContainer().show();
		}

I
isidor 已提交
578
		// Editor Part and Panel part
E
Erich Gamma 已提交
579
		this.editor.getContainer().size(editorSize.width, editorSize.height);
B
Benjamin Pasero 已提交
580
		this.panel.getContainer().size(panelDimension.width, panelDimension.height);
E
Erich Gamma 已提交
581

I
isidor 已提交
582 583 584 585 586 587 588 589
		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 已提交
590
		} else {
I
isidor 已提交
591 592 593 594 595 596 597
			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 已提交
598 599 600
		}

		// Activity Bar Part
B
Benjamin Pasero 已提交
601
		this.activitybar.getContainer().size(null, activityBarSize.height);
E
Erich Gamma 已提交
602 603
		if (sidebarPosition === Position.LEFT) {
			this.activitybar.getContainer().getHTMLElement().style.right = '';
604
			this.activitybar.getContainer().position(this.titlebarHeight, null, 0, 0);
E
Erich Gamma 已提交
605 606
		} else {
			this.activitybar.getContainer().getHTMLElement().style.left = '';
607
			this.activitybar.getContainer().position(this.titlebarHeight, 0, 0, null);
E
Erich Gamma 已提交
608
		}
B
Benjamin Pasero 已提交
609 610 611 612 613
		if (isActivityBarHidden) {
			this.activitybar.getContainer().hide();
		} else {
			this.activitybar.getContainer().show();
		}
E
Erich Gamma 已提交
614 615 616

		// Sidebar Part
		this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height);
617
		const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0);
E
Erich Gamma 已提交
618
		if (sidebarPosition === Position.LEFT) {
619
			this.sidebar.getContainer().position(this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
E
Erich Gamma 已提交
620
		} else {
621
			this.sidebar.getContainer().position(this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
E
Erich Gamma 已提交
622 623 624
		}

		// Statusbar Part
625
		this.statusbar.getContainer().position(this.workbenchSize.height - this.statusbarHeight);
626 627 628 629 630
		if (isStatusbarHidden) {
			this.statusbar.getContainer().hide();
		} else {
			this.statusbar.getContainer().show();
		}
E
Erich Gamma 已提交
631 632 633 634

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

I
isidor 已提交
635
		// Sashes
636 637 638
		this.sashXOne.layout();
		if (panelPosition === Position.BOTTOM) {
			this.sashXTwo.hide();
639 640
			this.sashY.layout();
			this.sashY.show();
641 642 643
		} else {
			this.sashY.hide();
			this.sashXTwo.layout();
644
			this.sashXTwo.show();
645
		}
E
Erich Gamma 已提交
646 647

		// Propagate to Part Layouts
648
		this.titlebar.layout(new Dimension(this.workbenchSize.width, this.titlebarHeight));
E
Erich Gamma 已提交
649 650
		this.editor.layout(new Dimension(editorSize.width, editorSize.height));
		this.sidebar.layout(sidebarSize);
B
Benjamin Pasero 已提交
651
		this.panel.layout(panelDimension);
652
		this.activitybar.layout(activityBarSize);
E
Erich Gamma 已提交
653 654

		// Propagate to Context View
B
Benjamin Pasero 已提交
655
		this.contextViewService.layout();
E
Erich Gamma 已提交
656 657 658
	}

	public getVerticalSashTop(sash: Sash): number {
659
		return this.titlebarHeight;
E
Erich Gamma 已提交
660 661 662 663
	}

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

666
			if (sidebarPosition === Position.LEFT) {
667
				return this.sidebarWidth + this.activitybarWidth;
668 669
			}

670
			return this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth;
E
Erich Gamma 已提交
671 672
		}

673
		return this.workbenchSize.width - this.panelWidth - (sidebarPosition === Position.RIGHT ? this.sidebarWidth + this.activitybarWidth : 0);
E
Erich Gamma 已提交
674 675 676
	}

	public getVerticalSashHeight(sash: Sash): number {
I
isidor 已提交
677
		return this.sidebarHeight;
E
Erich Gamma 已提交
678 679
	}

I
isidor 已提交
680
	public getHorizontalSashTop(sash: Sash): number {
681 682
		// 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 已提交
683 684 685
	}

	public getHorizontalSashLeft(sash: Sash): number {
686 687 688 689 690
		if (this.partService.getSideBarPosition() === Position.RIGHT) {
			return 0;
		}

		return this.sidebarWidth + this.activitybarWidth;
I
isidor 已提交
691 692 693
	}

	public getHorizontalSashWidth(sash: Sash): number {
I
isidor 已提交
694
		return this.panelWidth;
I
isidor 已提交
695 696
	}

697 698 699 700
	public isPanelMaximized(): boolean {
		return this.panelMaximized;
	}

701
	// change part size along the main axis
B
Benjamin Pasero 已提交
702 703
	public resizePart(part: Parts, sizeChange: number): void {
		const visibleEditors = this.editorService.getVisibleEditors().length;
704 705 706
		const sizeChangePxWidth = this.workbenchSize.width * (sizeChange / 100);
		const sizeChangePxHeight = this.workbenchSize.height * (sizeChange / 100);

B
Benjamin Pasero 已提交
707 708
		let doLayout = false;

709 710
		switch (part) {
			case Parts.SIDEBAR_PART:
711
				this.sidebarWidth = this.sidebarWidth + sizeChangePxWidth; // Sidebar can not become smaller than MIN_PART_WIDTH
712 713 714 715 716 717 718 719

				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:
720 721
				this.panelHeight = this.panelHeight + sizeChangePxHeight;
				this.panelWidth = this.panelWidth + sizeChangePxWidth;
722 723 724 725 726 727 728 729 730
				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 已提交
731 732 733
				} else {
					const stacks = this.editorGroupService.getStacksModel();
					const activeGroup = stacks.positionOfGroup(stacks.activeGroup);
734 735 736 737 738 739 740

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

		if (doLayout) {
B
Benjamin Pasero 已提交
741
			this.layout();
742 743 744
		}
	}

E
Erich Gamma 已提交
745
	public dispose(): void {
M
Martin Aeschlimann 已提交
746 747 748
		if (this.toUnbind) {
			dispose(this.toUnbind);
			this.toUnbind = null;
E
Erich Gamma 已提交
749 750
		}
	}
B
Benjamin Pasero 已提交
751
}