diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 7b4676fee99b01e7b088eff223f0929deabcb2c7..e4e8166b56eff336f5e417253d9622978a6d51ba 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -11,7 +11,7 @@ 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'; -import { IPartService, Position, ILayoutOptions, Parts } from 'vs/workbench/services/part/common/partService'; +import { IPartService, Position, Parts } from 'vs/workbench/services/part/common/partService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -24,9 +24,11 @@ const MIN_SIDEBAR_PART_WIDTH = 170; const MIN_EDITOR_PART_HEIGHT = 70; const MIN_EDITOR_PART_WIDTH = 220; const MIN_PANEL_PART_HEIGHT = 77; -const DEFAULT_PANEL_HEIGHT_COEFFICIENT = 0.4; +const MIN_PANEL_PART_WIDTH = 220; +const DEFAULT_PANEL_SIZE_COEFFICIENT = 0.4; const HIDE_SIDEBAR_WIDTH_THRESHOLD = 50; const HIDE_PANEL_HEIGHT_THRESHOLD = 50; +const HIDE_PANEL_WIDTH_THRESHOLD = 100; const TITLE_BAR_HEIGHT = 22; const STATUS_BAR_HEIGHT = 22; const ACTIVITY_BAR_WIDTH = 50; @@ -35,7 +37,7 @@ interface PartLayoutInfo { titlebar: { height: number; }; activitybar: { width: number; }; sidebar: { minWidth: number; }; - panel: { minHeight: number; }; + panel: { minHeight: number; minWidth: number; }; editor: { minWidth: number; minHeight: number; }; statusbar: { height: number; }; } @@ -45,7 +47,8 @@ interface PartLayoutInfo { */ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider { - private static sashXWidthSettingsKey = 'workbench.sidebar.width'; + private static sashXOneWidthSettingsKey = 'workbench.sidebar.width'; + private static sashXTwoWidthSettingsKey = 'workbench.panel.width'; private static sashYHeightSettingsKey = 'workbench.panel.height'; private parent: Builder; @@ -60,19 +63,15 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal private toUnbind: IDisposable[]; private partLayoutInfo: PartLayoutInfo; private workbenchSize: Dimension; - private sashX: Sash; + private sashXOne: Sash; + private sashXTwo: Sash; private sashY: Sash; - private startSidebarWidth: number; - private sidebarWidth: number; + private _sidebarWidth: number; private sidebarHeight: number; private titlebarHeight: number; - private activitybarWidth: number; private statusbarHeight: number; - private startPanelHeight: number; - private panelHeight: number; - private panelHeightBeforeMaximized: number; - private panelMaximized: boolean; - private panelWidth: number; + private _panelHeight: number; + private _panelWidth: number; private layoutEditorGroupsVertically: boolean; // Take parts as an object bag since instatation service does not have typings for constructors with 9+ arguments @@ -107,10 +106,12 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal this.quickopen = quickopen; this.toUnbind = []; this.partLayoutInfo = this.getPartLayoutInfo(); - this.panelHeightBeforeMaximized = 0; - this.panelMaximized = false; - this.sashX = new Sash(this.workbenchContainer.getHTMLElement(), this, { + this.sashXOne = new Sash(this.workbenchContainer.getHTMLElement(), this, { + baseSize: 5 + }); + + this.sashXTwo = new Sash(this.workbenchContainer.getHTMLElement(), this, { baseSize: 5 }); @@ -119,8 +120,9 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal orientation: Orientation.HORIZONTAL }); - this.sidebarWidth = this.storageService.getInteger(WorkbenchLayout.sashXWidthSettingsKey, StorageScope.GLOBAL, -1); - this.panelHeight = this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, 0); + 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); this.layoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal'); @@ -131,6 +133,59 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal this.registerSashListeners(); } + private get activitybarWidth(): number { + if (this.partService.isVisible(Parts.ACTIVITYBAR_PART)) { + return this.partLayoutInfo.activitybar.width; + } + + return 0; + } + + 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) { + 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)); + } + private getPartLayoutInfo(): PartLayoutInfo { return { titlebar: { @@ -143,7 +198,8 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal minWidth: MIN_SIDEBAR_PART_WIDTH }, panel: { - minHeight: MIN_PANEL_PART_HEIGHT + minHeight: MIN_PANEL_PART_HEIGHT, + minWidth: MIN_PANEL_PART_WIDTH }, editor: { minWidth: MIN_EDITOR_PART_WIDTH, @@ -158,22 +214,31 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal private registerSashListeners(): void { let startX: number = 0; let startY: number = 0; + let startXTwo: number = 0; + let startSidebarWidth: number; + let startPanelHeight: number; + let startPanelWidth: number; - this.sashX.addListener('start', (e: ISashEvent) => { - this.startSidebarWidth = this.sidebarWidth; + this.toUnbind.push(this.sashXOne.addListener('start', (e: ISashEvent) => { + startSidebarWidth = this.sidebarWidth; startX = e.startX; - }); + })); - this.sashY.addListener('start', (e: ISashEvent) => { - this.startPanelHeight = this.panelHeight; + this.toUnbind.push(this.sashY.addListener('start', (e: ISashEvent) => { + startPanelHeight = this.panelHeight; startY = e.startY; - }); + })); - this.sashX.addListener('change', (e: ISashEvent) => { + this.toUnbind.push(this.sashXTwo.addListener('start', (e: ISashEvent) => { + startPanelWidth = this.panelWidth; + startXTwo = e.startX; + })); + + this.toUnbind.push(this.sashXOne.addListener('change', (e: ISashEvent) => { let doLayout = false; let sidebarPosition = this.partService.getSideBarPosition(); let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART); - let newSashWidth = (sidebarPosition === Position.LEFT) ? this.startSidebarWidth + e.currentX - startX : this.startSidebarWidth - e.currentX + startX; + let newSashWidth = (sidebarPosition === Position.LEFT) ? startSidebarWidth + e.currentX - startX : startSidebarWidth - e.currentX + startX; let promise = TPromise.as(null); // Sidebar visible @@ -184,7 +249,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal let dragCompensation = MIN_SIDEBAR_PART_WIDTH - HIDE_SIDEBAR_WIDTH_THRESHOLD; promise = this.partService.setSideBarHidden(true); startX = (sidebarPosition === Position.LEFT) ? Math.max(this.activitybarWidth, e.currentX - dragCompensation) : Math.min(e.currentX + dragCompensation, this.workbenchSize.width - this.activitybarWidth); - this.sidebarWidth = this.startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from + this.sidebarWidth = startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from } // Otherwise size the sidebar accordingly @@ -198,7 +263,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal else { if ((sidebarPosition === Position.LEFT && e.currentX - startX >= this.partLayoutInfo.sidebar.minWidth) || (sidebarPosition === Position.RIGHT && startX - e.currentX >= this.partLayoutInfo.sidebar.minWidth)) { - this.startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX); + startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX); this.sidebarWidth = this.partLayoutInfo.sidebar.minWidth; promise = this.partService.setSideBarHidden(false); } @@ -207,12 +272,12 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal if (doLayout) { promise.done(() => this.layout(), errors.onUnexpectedError); } - }); + })); - this.sashY.addListener('change', (e: ISashEvent) => { + this.toUnbind.push(this.sashY.addListener('change', (e: ISashEvent) => { let doLayout = false; let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART); - let newSashHeight = this.startPanelHeight - (e.currentY - startY); + let newSashHeight = startPanelHeight - (e.currentY - startY); let promise = TPromise.as(null); // Panel visible @@ -223,7 +288,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal let dragCompensation = MIN_PANEL_PART_HEIGHT - HIDE_PANEL_HEIGHT_THRESHOLD; promise = this.partService.setPanelHidden(true); startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation); - this.panelHeight = this.startPanelHeight; // when restoring panel, restore to the panel height we started from + this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from } // Otherwise size the panel accordingly @@ -236,7 +301,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal // Panel hidden else { if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) { - this.startPanelHeight = 0; + startPanelHeight = 0; this.panelHeight = this.partLayoutInfo.panel.minHeight; promise = this.partService.setPanelHidden(false); } @@ -245,29 +310,77 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal if (doLayout) { promise.done(() => this.layout(), errors.onUnexpectedError); } - }); + })); - this.sashX.addListener('end', () => { - this.storageService.store(WorkbenchLayout.sashXWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); - }); + this.toUnbind.push(this.sashXTwo.addListener('change', (e: ISashEvent) => { + let doLayout = false; + let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART); + let newSashWidth = startPanelWidth - (e.currentX - startXTwo); + let promise = TPromise.as(null); + + // Panel visible + if (isPanelVisible) { + + // Automatically hide panel when a certain threshold is met + if (newSashWidth + HIDE_PANEL_WIDTH_THRESHOLD < this.partLayoutInfo.panel.minWidth) { + let dragCompensation = MIN_PANEL_PART_WIDTH - HIDE_PANEL_WIDTH_THRESHOLD; + promise = this.partService.setPanelHidden(true); + startXTwo = Math.min(this.workbenchSize.width - this.activitybarWidth, e.currentX + dragCompensation); + this.panelWidth = startPanelWidth; // when restoring panel, restore to the panel height we started from + } + + // Otherwise size the panel accordingly + else { + this.panelWidth = newSashWidth; + 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); + } + })); + + this.toUnbind.push(this.sashXOne.addListener('end', () => { + this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); + })); - this.sashY.addListener('end', () => { + this.toUnbind.push(this.sashY.addListener('end', () => { this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); - }); + })); + + this.toUnbind.push(this.sashXTwo.addListener('end', () => { + this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); + })); - this.sashY.addListener('reset', () => { - this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_HEIGHT_COEFFICIENT; + this.toUnbind.push(this.sashY.addListener('reset', () => { + this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT; this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); - this.partService.setPanelHidden(false).done(() => this.layout(), errors.onUnexpectedError); - }); + this.layout(); + })); - this.sashX.addListener('reset', () => { + this.toUnbind.push(this.sashXOne.addListener('reset', () => { let activeViewlet = this.viewletService.getActiveViewlet(); let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth(); - this.sidebarWidth = Math.max(MIN_SIDEBAR_PART_WIDTH, optimalWidth || 0); - this.storageService.store(WorkbenchLayout.sashXWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); + this.sidebarWidth = optimalWidth || 0; + this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); this.partService.setSideBarHidden(false).done(() => this.layout(), errors.onUnexpectedError); - }); + })); + + this.toUnbind.push(this.sashXTwo.addListener('reset', () => { + this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT; + this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); + this.layout(); + })); } private onEditorsChanged(): void { @@ -299,7 +412,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal } } - public layout(options?: ILayoutOptions): void { + public layout(): void { this.workbenchSize = this.parent.getClientArea(); const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART); @@ -308,16 +421,13 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal const isStatusbarHidden = !this.partService.isVisible(Parts.STATUSBAR_PART); const isSidebarHidden = !this.partService.isVisible(Parts.SIDEBAR_PART); const sidebarPosition = this.partService.getSideBarPosition(); + const panelPosition = this.partService.getPanelPosition(); // Sidebar - let sidebarWidth: number; - if (isSidebarHidden) { - sidebarWidth = 0; - } else if (this.sidebarWidth !== -1) { - sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.sidebarWidth); + if (this.sidebarWidth !== -1) { + this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.sidebarWidth); } else { - sidebarWidth = this.workbenchSize.width / 5; - this.sidebarWidth = sidebarWidth; + this.sidebarWidth = this.workbenchSize.width / 5; } this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height; @@ -325,60 +435,49 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal const previousMaxPanelHeight = this.sidebarHeight - MIN_EDITOR_PART_HEIGHT; this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight; - let sidebarSize = new Dimension(sidebarWidth, this.sidebarHeight); + let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight); // Activity Bar - this.activitybarWidth = isActivityBarHidden ? 0 : this.partLayoutInfo.activitybar.width; let activityBarSize = new Dimension(this.activitybarWidth, sidebarSize.height); // Panel part let panelHeight: number; + let panelWidth: number; const editorCountForHeight = this.editorGroupService.getGroupOrientation() === 'horizontal' ? this.editorGroupService.getStacksModel().groups.length : 1; const maxPanelHeight = sidebarSize.height - editorCountForHeight * MIN_EDITOR_PART_HEIGHT; + const maxPanelWidth = this.workbenchSize.width - activityBarSize.width - sidebarSize.width - editorCountForHeight * MIN_EDITOR_PART_WIDTH; + if (isPanelHidden) { panelHeight = 0; - } else if (this.panelHeight === previousMaxPanelHeight) { - panelHeight = maxPanelHeight; - } else if (this.panelHeight > 0) { - panelHeight = Math.min(maxPanelHeight, Math.max(this.partLayoutInfo.panel.minHeight, this.panelHeight)); + 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; } else { - panelHeight = sidebarSize.height * DEFAULT_PANEL_HEIGHT_COEFFICIENT; - } - if (options && options.toggleMaximizedPanel) { - panelHeight = this.panelMaximized ? Math.max(this.partLayoutInfo.panel.minHeight, Math.min(this.panelHeightBeforeMaximized, maxPanelHeight)) : maxPanelHeight; - } - this.panelMaximized = panelHeight === maxPanelHeight; - if (panelHeight / maxPanelHeight < 0.7) { - // Remember the previous height only if the panel size is not too large. - // To get a nice minimize effect even if a user dragged the panel sash to maximum. - this.panelHeightBeforeMaximized = panelHeight; + 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; + } } - const panelDimension = new Dimension(this.workbenchSize.width - sidebarSize.width - activityBarSize.width, panelHeight); - this.panelWidth = panelDimension.width; + const panelDimension = new Dimension(panelWidth, panelHeight); // Editor let editorSize = { width: 0, - height: 0, - remainderLeft: 0, - remainderRight: 0 + height: 0 }; - editorSize.width = panelDimension.width; - editorSize.height = sidebarSize.height - panelDimension.height; - - // Sidebar hidden - if (isSidebarHidden) { - editorSize.width = this.workbenchSize.width - activityBarSize.width; - - if (sidebarPosition === Position.LEFT) { - editorSize.remainderLeft = Math.round((this.workbenchSize.width - editorSize.width + activityBarSize.width) / 2); - editorSize.remainderRight = this.workbenchSize.width - editorSize.width - editorSize.remainderLeft; - } else { - editorSize.remainderRight = Math.round((this.workbenchSize.width - editorSize.width + activityBarSize.width) / 2); - editorSize.remainderLeft = this.workbenchSize.width - editorSize.width - editorSize.remainderRight; - } - } + 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); // Assert Sidebar and Editor Size to not overflow let editorMinWidth = this.partLayoutInfo.editor.minWidth; @@ -395,26 +494,35 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal if (editorSize.width < editorMinWidth) { let diff = editorMinWidth - editorSize.width; editorSize.width = editorMinWidth; - panelDimension.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) { + 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); } if (!isSidebarHidden) { this.sidebarWidth = sidebarSize.width; - this.storageService.store(WorkbenchLayout.sashXWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); } if (!isPanelHidden) { - this.panelHeight = panelDimension.height; - this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); + 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); + } } // Workbench @@ -442,16 +550,22 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal this.editor.getContainer().size(editorSize.width, editorSize.height); this.panel.getContainer().size(panelDimension.width, panelDimension.height); - const editorBottom = this.statusbarHeight + panelDimension.height; - if (isSidebarHidden) { - this.editor.getContainer().position(this.titlebarHeight, editorSize.remainderRight, editorBottom, editorSize.remainderLeft); - this.panel.getContainer().position(editorSize.height + this.titlebarHeight, editorSize.remainderRight, this.statusbarHeight, editorSize.remainderLeft); - } else if (sidebarPosition === Position.LEFT) { - this.editor.getContainer().position(this.titlebarHeight, 0, editorBottom, sidebarSize.width + activityBarSize.width); - this.panel.getContainer().position(editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width); + 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); + } } else { - this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width, editorBottom, 0); - this.panel.getContainer().position(editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0); + 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); + } } // Activity Bar Part @@ -471,11 +585,11 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal // Sidebar Part this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height); - + const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0); if (sidebarPosition === Position.LEFT) { - this.sidebar.getContainer().position(this.titlebarHeight, editorSize.width, 0, activityBarSize.width); + this.sidebar.getContainer().position(this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width); } else { - this.sidebar.getContainer().position(this.titlebarHeight, null, 0, editorSize.width); + this.sidebar.getContainer().position(this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth); } // Statusbar Part @@ -490,8 +604,16 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal this.quickopen.layout(this.workbenchSize); // Sashes - this.sashX.layout(); - this.sashY.layout(); + this.sashXOne.layout(); + if (panelPosition === Position.BOTTOM) { + this.sashXTwo.hide(); + this.sashY.layout(); + this.sashY.show(); + } else { + this.sashY.hide(); + this.sashXTwo.layout(); + this.sashXTwo.show(); + } // Propagate to Part Layouts this.titlebar.layout(new Dimension(this.workbenchSize.width, this.titlebarHeight)); @@ -509,14 +631,17 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal } public getVerticalSashLeft(sash: Sash): number { - let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART); let sidebarPosition = this.partService.getSideBarPosition(); + if (sash === this.sashXOne) { - if (sidebarPosition === Position.LEFT) { - return isSidebarVisible ? this.sidebarWidth + this.activitybarWidth : this.activitybarWidth; + if (sidebarPosition === Position.LEFT) { + return this.sidebarWidth + this.activitybarWidth; + } + + return this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth; } - return isSidebarVisible ? this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth : this.workbenchSize.width - this.activitybarWidth; + return this.workbenchSize.width - this.panelWidth - (sidebarPosition === Position.RIGHT ? this.sidebarWidth + this.activitybarWidth : 0); } public getVerticalSashHeight(sash: Sash): number { @@ -529,17 +654,17 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal } public getHorizontalSashLeft(sash: Sash): number { - return this.partService.getSideBarPosition() === Position.LEFT ? this.getVerticalSashLeft(sash) : 0; + if (this.partService.getSideBarPosition() === Position.RIGHT) { + return 0; + } + + return this.sidebarWidth + this.activitybarWidth; } public getHorizontalSashWidth(sash: Sash): number { return this.panelWidth; } - public isPanelMaximized(): boolean { - return this.panelMaximized; - } - // change part size along the main axis public resizePart(part: Parts, sizeChange: number): void { const visibleEditors = this.editorService.getVisibleEditors().length; @@ -547,12 +672,10 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal const sizeChangePxHeight = this.workbenchSize.height * (sizeChange / 100); let doLayout = false; - let newSashSize: number = 0; switch (part) { case Parts.SIDEBAR_PART: - newSashSize = this.sidebarWidth + sizeChangePxWidth; - this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, newSashSize); // Sidebar can not become smaller than MIN_PART_WIDTH + this.sidebarWidth = this.sidebarWidth + sizeChangePxWidth; // Sidebar can not become smaller than MIN_PART_WIDTH if (this.layoutEditorGroupsVertically && (this.workbenchSize.width - this.sidebarWidth < visibleEditors * MIN_EDITOR_PART_WIDTH)) { this.sidebarWidth = (this.workbenchSize.width - visibleEditors * MIN_EDITOR_PART_WIDTH); @@ -561,8 +684,8 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal doLayout = true; break; case Parts.PANEL_PART: - newSashSize = this.panelHeight + sizeChangePxHeight; - this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashSize); + this.panelHeight = this.panelHeight + sizeChangePxHeight; + this.panelWidth = this.panelWidth + sizeChangePxWidth; doLayout = true; break; case Parts.EDITOR_PART: diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts index bbc336984018ec8f1fbf855065452bbc48031e07..2a1a311acfee0aef105014187f0f7a0cb32e13f9 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts @@ -281,7 +281,7 @@ export class CompositeBar implements ICompositeBar { } // Add overflow action as needed - if (visibleCompositesChange && overflows) { + if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) { this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => this.compositeOverflowActionItem.showMenu()); this.compositeOverflowActionItem = this.instantiationService.createInstance( CompositeOverflowActivityActionItem, diff --git a/src/vs/workbench/browser/parts/panel/media/down-inverse.svg b/src/vs/workbench/browser/parts/panel/media/down-inverse.svg deleted file mode 100755 index d436c7e273547f2c4476d62b6753837435b09e5e..0000000000000000000000000000000000000000 --- a/src/vs/workbench/browser/parts/panel/media/down-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/down.svg b/src/vs/workbench/browser/parts/panel/media/down.svg deleted file mode 100755 index 279f3570dc8b7edbe9b550c5380a6cee10a12032..0000000000000000000000000000000000000000 --- a/src/vs/workbench/browser/parts/panel/media/down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panel-bottom-inverse.svg b/src/vs/workbench/browser/parts/panel/media/panel-bottom-inverse.svg new file mode 100755 index 0000000000000000000000000000000000000000..f9aa418241a0e13b51f7ee77bffb1a3d4103bd82 --- /dev/null +++ b/src/vs/workbench/browser/parts/panel/media/panel-bottom-inverse.svg @@ -0,0 +1 @@ +BottomRowOfTwoRows_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panel-bottom.svg b/src/vs/workbench/browser/parts/panel/media/panel-bottom.svg new file mode 100755 index 0000000000000000000000000000000000000000..780681f19e5f8b6bbd5a6a82991816befc3ae62d --- /dev/null +++ b/src/vs/workbench/browser/parts/panel/media/panel-bottom.svg @@ -0,0 +1 @@ +BottomRowOfTwoRows_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panel-right-inverse.svg b/src/vs/workbench/browser/parts/panel/media/panel-right-inverse.svg new file mode 100755 index 0000000000000000000000000000000000000000..428206fb117eefc67cb2621817168cf6a5287fad --- /dev/null +++ b/src/vs/workbench/browser/parts/panel/media/panel-right-inverse.svg @@ -0,0 +1 @@ +RightColumnOfTwoColumns_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panel-right.svg b/src/vs/workbench/browser/parts/panel/media/panel-right.svg new file mode 100755 index 0000000000000000000000000000000000000000..3b7b808f9ab9c07aca5e3b94dc853aa2ade58d8b --- /dev/null +++ b/src/vs/workbench/browser/parts/panel/media/panel-right.svg @@ -0,0 +1 @@ +RightColumnOfTwoColumns_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 042d150e84fb95249c776d8070abe2e41a4cd578..821ae390945348de6669f7bc154165a1dff87ea5 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -13,8 +13,6 @@ } .monaco-workbench > .part.panel .title { - border-top-width: 1px; - border-top-style: solid; padding-right: 0px; height: 35px; display: flex; @@ -22,6 +20,16 @@ justify-content: space-between; } +.monaco-workbench > .part.panel.bottom .title { + border-top-width: 1px; + border-top-style: solid; +} + +.monaco-workbench > .part.panel.right { + border-left-width: 1px; + border-left-style: solid; +} + .monaco-workbench > .part.panel > .composite.title > .title-actions { flex: 0; } @@ -88,22 +96,22 @@ background: url('close.svg') center center no-repeat; } -.monaco-workbench .maximize-panel-action { - background: url('up.svg') center center no-repeat; +.monaco-workbench .move-panel-to-right { + background: url('panel-right.svg') center center no-repeat; } -.vs-dark .monaco-workbench .maximize-panel-action, -.hc-black .monaco-workbench .maximize-panel-action { - background: url('up-inverse.svg') center center no-repeat; +.vs-dark .monaco-workbench .move-panel-to-right, +.hc-black .monaco-workbench .move-panel-to-right { + background: url('panel-right-inverse.svg') center center no-repeat; } -.monaco-workbench .minimize-panel-action { - background: url('down.svg') center center no-repeat; +.monaco-workbench .move-panel-to-bottom { + background: url('panel-bottom.svg') center center no-repeat; } -.vs-dark .monaco-workbench .minimize-panel-action, -.hc-black .monaco-workbench .minimize-panel-action { - background: url('down-inverse.svg') center center no-repeat; +.vs-dark .monaco-workbench .move-panel-to-bottom, +.hc-black .monaco-workbench .move-panel-to-bottom { + background: url('panel-bottom-inverse.svg') center center no-repeat; } .vs-dark .monaco-workbench .hide-panel-action, diff --git a/src/vs/workbench/browser/parts/panel/media/up-inverse.svg b/src/vs/workbench/browser/parts/panel/media/up-inverse.svg deleted file mode 100755 index 33b7f7c65f6c68906bdfd0c4512cdfc714a493cc..0000000000000000000000000000000000000000 --- a/src/vs/workbench/browser/parts/panel/media/up-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/up.svg b/src/vs/workbench/browser/parts/panel/media/up.svg deleted file mode 100755 index 0033a3271638916901d8b00df5f4c1c395543f85..0000000000000000000000000000000000000000 --- a/src/vs/workbench/browser/parts/panel/media/up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 759cb22cbf670a65d7121cf13720451f8150d1c0..866321889e3205972ffdaa8461295f3dcb8ef91b 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -13,10 +13,11 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/actions'; import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ActivityAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; import { IActivity } from 'vs/workbench/common/activity'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export class OpenPanelAction extends Action { @@ -117,32 +118,36 @@ class FocusPanelAction extends Action { } } -export class ToggleMaximizedPanelAction extends Action { +export class TogglePanelPositionAction extends Action { - public static ID = 'workbench.action.toggleMaximizedPanel'; - public static LABEL = nls.localize('toggleMaximizedPanel', "Toggle Maximized Panel"); - private static MAXIMIZE_LABEL = nls.localize('maximizePanel', "Maximize Panel Size"); - private static RESTORE_LABEL = nls.localize('minimizePanel', "Restore Panel Size"); + public static ID = 'workbench.action.togglePanelPosition'; + public static LABEL = nls.localize('toggledPanelPosition', "Toggle Panel Position"); + private static MOVE_TO_RIGHT_LABEL = nls.localize('moveToRight', "Move to Right"); + private static MOVE_TO_BOTTOM_LABEL = nls.localize('moveToBottom', "Move to Bottom"); + private static panelPositionConfigurationKey = 'workbench.panel.location'; private toDispose: IDisposable[]; constructor( id: string, label: string, - @IPartService private partService: IPartService + @IPartService private partService: IPartService, + @IConfigurationService private configurationService: IConfigurationService + ) { - super(id, label, partService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action'); + super(id, label, partService.getPanelPosition() === Position.RIGHT ? 'move-panel-to-bottom' : 'move-panel-to-right'); this.toDispose = []; this.toDispose.push(partService.onEditorLayout(() => { - const maximized = this.partService.isPanelMaximized(); - this.class = maximized ? 'minimize-panel-action' : 'maximize-panel-action'; - this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL; + const positionRight = this.partService.getPanelPosition() === Position.RIGHT; + this.class = positionRight ? 'move-panel-to-bottom' : 'move-panel-to-right'; + this.label = positionRight ? TogglePanelPositionAction.MOVE_TO_BOTTOM_LABEL : TogglePanelPositionAction.MOVE_TO_RIGHT_LABEL; })); } public run(): TPromise { - // Show panel - return this.partService.setPanelHidden(false) - .then(() => this.partService.toggleMaximizedPanel()); + const position = this.partService.getPanelPosition(); + const newPositionValue = (position === Position.BOTTOM) ? 'right' : 'bottom'; + + return this.configurationService.updateValue(TogglePanelPositionAction.panelPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER); } public dispose(): void { @@ -168,5 +173,5 @@ export class PanelActivityAction extends ActivityAction { const actionRegistry = Registry.as(WorkbenchExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_J }), 'View: Toggle Panel', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPanelAction, FocusPanelAction.ID, FocusPanelAction.LABEL), 'View: Focus into Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), 'View: Toggle Maximized Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); \ No newline at end of file +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View")); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 66485230cdf571499f25a60605be9cb43bbf4ca3..949d3135c5272117932ac3d3253aee1b00e91d76 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -22,7 +22,7 @@ import { IMessageService } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ClosePanelAction, ToggleMaximizedPanelAction, PanelActivityAction, OpenPanelAction } from 'vs/workbench/browser/parts/panel/panelActions'; +import { ClosePanelAction, TogglePanelPositionAction, PanelActivityAction, OpenPanelAction } from 'vs/workbench/browser/parts/panel/panelActions'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { activeContrastBorder, focusBorder, contrastBorder, editorBackground, badgeBackground, badgeForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -36,6 +36,7 @@ export class PanelPart extends CompositePart implements IPanelService { public static activePanelSettingsKey = 'workbench.panelpart.activepanelid'; private static readonly PINNED_PANELS = 'workbench.panel.pinnedPanels'; + private static readonly MIN_COMPOSITE_BAR_WIDTH = 50; public _serviceBrand: any; @@ -120,11 +121,12 @@ export class PanelPart extends CompositePart implements IPanelService { return this._onDidCompositeClose.event; } - protected updateStyles(): void { + public updateStyles(): void { super.updateStyles(); const container = this.getContainer(); container.style('background-color', this.getColor(PANEL_BACKGROUND)); + container.style('border-left-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder)); const title = this.getTitleArea(); title.style('border-top-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder)); @@ -175,7 +177,7 @@ export class PanelPart extends CompositePart implements IPanelService { protected getActions(): IAction[] { return [ - this.instantiationService.createInstance(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), + this.instantiationService.createInstance(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), this.instantiationService.createInstance(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL) ]; } @@ -224,7 +226,7 @@ export class PanelPart extends CompositePart implements IPanelService { let availableWidth = this.dimension.width - 8; // take padding into account if (this.toolBar) { // adjust height for global actions showing - availableWidth -= this.toolBar.getContainer().getHTMLElement().offsetWidth; + availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.toolBar.getContainer().getHTMLElement().offsetWidth); } this.compositeBar.layout(new Dimension(availableWidth, this.dimension.height)); } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 63aed0c15c13391c3d04720b29791081af58ce1d..3f29706e8d8ca39f0c4054c8802eefdd3080fa5c 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -222,6 +222,12 @@ let workbenchProperties: { [path: string]: IJSONSchema; } = { 'default': 'left', 'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.") }, + 'workbench.panel.location': { + 'type': 'string', + 'enum': ['bottom', 'right'], + 'default': 'bottom', + 'description': nls.localize('panelLocation', "Controls the location of the panel. It can either show on the bottom or right of the workbench.") + }, 'workbench.statusBar.visible': { 'type': 'boolean', 'default': true, diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index f2d871d7eb69c3294b51e42d780b2262a570391d..d37f4acba1412f9922f7dabd7eebb6bf15d5ff74 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -38,7 +38,7 @@ import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbe import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; import { getServices } from 'vs/platform/instantiation/common/extensions'; -import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; +import { Position, Parts, IPartService } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; @@ -152,6 +152,7 @@ export class Workbench implements IPartService { private static zenModeActiveSettingKey = 'workbench.zenmode.active'; private static sidebarPositionConfigurationKey = 'workbench.sideBar.location'; + private static panelPositionConfigurationKey = 'workbench.panel.location'; private static statusbarVisibleConfigurationKey = 'workbench.statusBar.visible'; private static activityBarVisibleConfigurationKey = 'workbench.activityBar.visible'; @@ -194,6 +195,7 @@ export class Workbench implements IPartService { private statusBarHidden: boolean; private activityBarHidden: boolean; private sideBarPosition: Position; + private panelPosition: Position; private panelHidden: boolean; private editorBackgroundDelayer: Delayer; private closeEmptyWindowScheduler: RunOnceScheduler; @@ -643,6 +645,10 @@ export class Workbench implements IPartService { const sideBarPosition = this.configurationService.getValue(Workbench.sidebarPositionConfigurationKey); this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT; + // Panel position + const panelPosition = this.configurationService.getValue(Workbench.panelPositionConfigurationKey); + this.panelPosition = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; + // Statusbar visibility const statusBarVisible = this.configurationService.getValue(Workbench.statusbarVisibleConfigurationKey); this.statusBarHidden = !statusBarVisible; @@ -886,14 +892,6 @@ export class Workbench implements IPartService { }); } - public toggleMaximizedPanel(): void { - this.workbenchLayout.layout({ toggleMaximizedPanel: true }); - } - - public isPanelMaximized(): boolean { - return this.workbenchLayout.isPanelMaximized(); - } - public getSideBarPosition(): Position { return this.sideBarPosition; } @@ -921,6 +919,30 @@ export class Workbench implements IPartService { this.workbenchLayout.layout(); } + public getPanelPosition(): Position { + return this.panelPosition; + } + + private setPanelPosition(position: Position): void { + if (this.panelHidden) { + this.setPanelHidden(false, true /* Skip Layout */).done(undefined, errors.onUnexpectedError); + } + + const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; + const oldPositionValue = (this.panelPosition === Position.BOTTOM) ? 'bottom' : 'right'; + this.panelPosition = position; + + // Adjust CSS + this.panelPart.getContainer().removeClass(oldPositionValue); + this.panelPart.getContainer().addClass(newPositionValue); + + // Update Styles + this.panelPart.updateStyles(); + + // Layout + this.workbenchLayout.layout(); + } + private setFontAliasing(aliasing: string) { this.fontAliasing = aliasing; @@ -940,9 +962,9 @@ export class Workbench implements IPartService { * Asks the workbench and all its UI components inside to lay out according to * the containers dimension the workbench is living in. */ - public layout(options?: ILayoutOptions): void { + public layout(): void { if (this.isStarted()) { - this.workbenchLayout.layout(options); + this.workbenchLayout.layout(); } } @@ -1076,6 +1098,12 @@ export class Workbench implements IPartService { this.setSideBarPosition(newSidebarPosition); } + const newPanelPositionValue = this.configurationService.getValue(Workbench.panelPositionConfigurationKey); + const newPanelPosition = (newPanelPositionValue === 'right') ? Position.RIGHT : Position.BOTTOM; + if (newPanelPosition !== this.getSideBarPosition()) { + this.setPanelPosition(newPanelPosition); + } + const fontAliasing = this.configurationService.getValue(Workbench.fontAliasingConfigurationKey); if (fontAliasing !== this.fontAliasing) { this.setFontAliasing(fontAliasing); @@ -1190,7 +1218,7 @@ export class Workbench implements IPartService { private createPanelPart(): void { const panelPartContainer = $(this.workbench) .div({ - 'class': ['part', 'panel'], + 'class': ['part', 'panel', this.panelPosition === Position.BOTTOM ? 'bottom' : 'right'], id: Identifiers.PANEL_PART, role: 'complementary' }); diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts index 4e41c5df51c4f056389aadcee9419e7cac708f52..47a45649559b72411a338b725942b5f7290190a0 100644 --- a/src/vs/workbench/services/part/common/partService.ts +++ b/src/vs/workbench/services/part/common/partService.ts @@ -19,11 +19,8 @@ export enum Parts { export enum Position { LEFT, - RIGHT -} - -export interface ILayoutOptions { - toggleMaximizedPanel?: boolean; + RIGHT, + BOTTOM } export const IPartService = createDecorator('partService'); @@ -44,7 +41,7 @@ export interface IPartService { /** * Asks the part service to layout all parts. */ - layout(options?: ILayoutOptions): void; + layout(): void; /** * Asks the part service to if all parts have been created. @@ -92,20 +89,14 @@ export interface IPartService { setPanelHidden(hidden: boolean): TPromise; /** - * Maximizes the panel height if the panel is not already maximized. - * Shrinks the panel to the default starting size if the panel is maximized. - */ - toggleMaximizedPanel(): void; - - /** - * Returns true if the panel is maximized. + * Gets the current side bar position. Note that the sidebar can be hidden too. */ - isPanelMaximized(): boolean; + getSideBarPosition(): Position; /** - * Gets the current side bar position. Note that the sidebar can be hidden too. + * Gets the current panel position. Note that the panel can be hidden too. */ - getSideBarPosition(): Position; + getPanelPosition(): Position; /** * Returns the identifier of the element that contains the workbench. diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index d6aeebc31d33d20d37bfa27ddf359f0d57d02d41..6b3a39d6b6cb74377299177876d39023decba311 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -418,6 +418,10 @@ export class TestPartService implements IPartService { return 0; } + public getPanelPosition() { + return 0; + } + public addClass(clazz: string): void { } public removeClass(clazz: string): void { } public getWorkbenchElementId(): string { return ''; }