diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 6a726abbff3..6d39cb6d150 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -72,9 +72,6 @@ export class Sash extends EventEmitter { this.$e.on(DOM.EventType.DBLCLICK, (e: MouseEvent) => { this.emit('reset', e); }); this.$e.on(EventType.Start, (e: GestureEvent) => { this.onTouchStart(e); }); - this.orientation = options.orientation || Orientation.VERTICAL; - this.$e.addClass(this.getOrientation()); - this.size = options.baseSize || 5; if (isIPad) { @@ -82,11 +79,7 @@ export class Sash extends EventEmitter { this.$e.addClass('touch'); } - if (this.orientation === Orientation.HORIZONTAL) { - this.$e.size(null, this.size); - } else { - this.$e.size(this.size); - } + this.setOrientation(options.orientation || Orientation.VERTICAL); this.isDisabled = false; this.hidden = false; @@ -97,6 +90,23 @@ export class Sash extends EventEmitter { return this.$e.getHTMLElement(); } + public setOrientation(orientation: Orientation): void { + this.orientation = orientation; + + this.$e.removeClass('horizontal', 'vertical'); + this.$e.addClass(this.getOrientation()); + + if (this.orientation === Orientation.HORIZONTAL) { + this.$e.size(null, this.size); + } else { + this.$e.size(this.size); + } + + if (this.layoutProvider) { + this.layout(); + } + } + private getOrientation(): 'horizontal' | 'vertical' { return this.orientation === Orientation.HORIZONTAL ? 'horizontal' : 'vertical'; } diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 3e4f3944257..f99de5af407 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -487,6 +487,7 @@ export class VSCodeMenu { const fullscreen = new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), accelerator: this.getAccelerator('workbench.action.toggleFullScreen'), click: () => this.windowsService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsService.getWindowCount() > 0 }); const toggleMenuBar = this.createMenuItem(nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar"), 'workbench.action.toggleMenuBar'); const splitEditor = this.createMenuItem(nls.localize({ key: 'miSplitEditor', comment: ['&& denotes a mnemonic'] }, "Split &&Editor"), 'workbench.action.splitEditor'); + const toggleEditorLayout = this.createMenuItem(nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Toggle Editor &&Layout"), 'workbench.action.toggleEditorLayout'); const toggleSidebar = this.createMenuItem(nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar"), 'workbench.action.toggleSidebarVisibility'); const moveSidebar = this.createMenuItem(nls.localize({ key: 'miMoveSidebar', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar"), 'workbench.action.toggleSidebarPosition'); const togglePanel = this.createMenuItem(nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel"), 'workbench.action.togglePanel'); @@ -519,6 +520,7 @@ export class VSCodeMenu { platform.isWindows || platform.isLinux ? toggleMenuBar : void 0, __separator__(), splitEditor, + toggleEditorLayout, moveSidebar, toggleSidebar, togglePanel, @@ -557,9 +559,9 @@ export class VSCodeMenu { const switchGroupMenu = new Menu(); - const focusFirstGroup = this.createMenuItem(nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "&&Left Group"), 'workbench.action.focusFirstEditorGroup'); - const focusSecondGroup = this.createMenuItem(nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "&&Center Group"), 'workbench.action.focusSecondEditorGroup'); - const focusThirdGroup = this.createMenuItem(nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "&&Right Group"), 'workbench.action.focusThirdEditorGroup'); + const focusFirstGroup = this.createMenuItem(nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "&&First Group"), 'workbench.action.focusFirstEditorGroup'); + const focusSecondGroup = this.createMenuItem(nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "&&Second Group"), 'workbench.action.focusSecondEditorGroup'); + const focusThirdGroup = this.createMenuItem(nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "&&Third Group"), 'workbench.action.focusThirdEditorGroup'); const nextGroup = this.createMenuItem(nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group"), 'workbench.action.focusNextGroup'); const previousGroup = this.createMenuItem(nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group"), 'workbench.action.focusPreviousGroup'); diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 2258b5ed91b..2bebb45e399 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -111,17 +111,17 @@ export interface IEditor { */ export enum Position { - /** Opens the editor in the LEFT most position replacing the input currently showing */ - LEFT = 0, + /** Opens the editor in the first position replacing the input currently showing */ + ONE = 0, - /** Opens the editor in the CENTER position replacing the input currently showing */ - CENTER = 1, + /** Opens the editor in the second position replacing the input currently showing */ + TWO = 1, - /** Opens the editor in the RIGHT most position replacing the input currently showing */ - RIGHT = 2 + /** Opens the editor in the third most position replacing the input currently showing */ + THREE = 2 } -export const POSITIONS = [Position.LEFT, Position.CENTER, Position.RIGHT]; +export const POSITIONS = [Position.ONE, Position.TWO, Position.THREE]; export enum Direction { LEFT, diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 1dd52ddbb79..3db78328f40 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -102,13 +102,13 @@ export function toDiagnosticSeverty(value: Severity): types.DiagnosticSeverity { } export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition { - let editorColumn = EditorPosition.LEFT; + let editorColumn = EditorPosition.ONE; if (typeof column !== 'number') { - // stick with LEFT + // stick with ONE } else if (column === types.ViewColumn.Two) { - editorColumn = EditorPosition.CENTER; + editorColumn = EditorPosition.TWO; } else if (column === types.ViewColumn.Three) { - editorColumn = EditorPosition.RIGHT; + editorColumn = EditorPosition.THREE; } return editorColumn; } @@ -117,11 +117,11 @@ export function toViewColumn(position?: EditorPosition): vscode.ViewColumn { if (typeof position !== 'number') { return; } - if (position === EditorPosition.LEFT) { + if (position === EditorPosition.ONE) { return types.ViewColumn.One; - } else if (position === EditorPosition.CENTER) { + } else if (position === EditorPosition.TWO) { return types.ViewColumn.Two; - } else if (position === EditorPosition.RIGHT) { + } else if (position === EditorPosition.THREE) { return types.ViewColumn.Three; } } diff --git a/src/vs/workbench/browser/actions/media/actions.css b/src/vs/workbench/browser/actions/media/actions.css new file mode 100644 index 00000000000..80d772227c2 --- /dev/null +++ b/src/vs/workbench/browser/actions/media/actions.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .toggle-editor-layout { + background-image: url('editor-layout.svg'); +} + +.vs-dark .monaco-workbench .toggle-editor-layout, +.hc-black .monaco-workbench .toggle-editor-layout { + background-image: url('editor-layout-inverse.svg'); +} diff --git a/src/vs/workbench/browser/actions/media/editor-layout-inverse.svg b/src/vs/workbench/browser/actions/media/editor-layout-inverse.svg new file mode 100644 index 00000000000..fe5c9d7b3ee --- /dev/null +++ b/src/vs/workbench/browser/actions/media/editor-layout-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/browser/actions/media/editor-layout.svg b/src/vs/workbench/browser/actions/media/editor-layout.svg new file mode 100644 index 00000000000..8d127ec737d --- /dev/null +++ b/src/vs/workbench/browser/actions/media/editor-layout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/browser/actions/openSettings.ts b/src/vs/workbench/browser/actions/openSettings.ts index 41f45a8f822..3f169a76db7 100644 --- a/src/vs/workbench/browser/actions/openSettings.ts +++ b/src/vs/workbench/browser/actions/openSettings.ts @@ -74,12 +74,12 @@ export class BaseTwoEditorsAction extends Action { return this.createIfNotExists(editableResource, defaultEditableContents).then(() => { return this.editorService.createInput({ resource: editableResource }).then(typedRightHandEditableInput => { const editors = [ - { input: leftHandDefaultInput, position: Position.LEFT, options: { pinned: true } }, - { input: typedRightHandEditableInput, position: Position.CENTER, options: { pinned: true } } + { input: leftHandDefaultInput, position: Position.ONE, options: { pinned: true } }, + { input: typedRightHandEditableInput, position: Position.TWO, options: { pinned: true } } ]; return this.editorService.openEditors(editors).then(() => { - this.editorGroupService.focusGroup(Position.CENTER); + this.editorGroupService.focusGroup(Position.TWO); }); }); }); @@ -153,7 +153,7 @@ export class OpenGlobalSettingsAction extends BaseOpenSettingsAction { const editorCount = this.editorService.getVisibleEditors().length; return this.editorService.createInput({ resource: this.contextService.toResource(WORKSPACE_CONFIG_DEFAULT_PATH) }).then(typedInput => { - return this.editorService.openEditor(typedInput, { pinned: true }, editorCount === 2 ? Position.RIGHT : editorCount === 1 ? Position.CENTER : void 0); + return this.editorService.openEditor(typedInput, { pinned: true }, editorCount === 2 ? Position.THREE : editorCount === 1 ? Position.TWO : void 0); }); }), new Action('neverShowAgain', nls.localize('neverShowAgain', "Don't show again"), null, true, () => { diff --git a/src/vs/workbench/browser/actions/toggleEditorLayout.ts b/src/vs/workbench/browser/actions/toggleEditorLayout.ts new file mode 100644 index 00000000000..0377ee1b746 --- /dev/null +++ b/src/vs/workbench/browser/actions/toggleEditorLayout.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./media/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import { Registry } from 'vs/platform/platform'; +import { Action } from 'vs/base/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export class ToggleEditorLayoutAction extends Action { + + public static ID = 'workbench.action.toggleEditorLayout'; + public static LABEL = nls.localize('toggleEditorLayout', "Toggle Editor Layout"); + + private static editorLayoutConfigurationKey = 'workbench.editor.sideBySideLayout'; + + constructor( + id: string, + label: string, + @IMessageService private messageService: IMessageService, + @IConfigurationService private configurationService: IConfigurationService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + ) { + super(id, label); + + this.class = 'toggle-editor-layout'; + } + + public run(): TPromise { + const editorLayoutVertical = this.configurationService.lookup('workbench.editor.sideBySideLayout').value !== 'horizontal'; + const newEditorLayout = editorLayoutVertical ? 'horizontal' : 'vertical'; + + this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleEditorLayoutAction.editorLayoutConfigurationKey, value: newEditorLayout }).then(null, error => { + this.messageService.show(Severity.Error, error); + }); + + return TPromise.as(null); + } +} + +const registry = Registry.as(Extensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL), 'View: Toggle Editor Layout', nls.localize('view', "View")); \ No newline at end of file diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index a478f827bbe..243f194cb4e 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -20,7 +20,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer const DEFAULT_MIN_PART_WIDTH = 170; const DEFAULT_MIN_PANEL_PART_HEIGHT = 77; -const DEFAULT_MIN_EDITOR_PART_HEIGHT = 170; +const DEFAULT_MIN_EDITOR_PART_HEIGHT = 210; /* 3 x 70px min height of editors when stacked vertically */ const HIDE_SIDEBAR_WIDTH_THRESHOLD = 50; const HIDE_PANEL_HEIGHT_THRESHOLD = 50; diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index a398aab4502..86ac02805bc 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -334,7 +334,7 @@ export interface IEditorInputActionContext { */ export class EditorInputActionContributor extends ActionBarContributor { - // The following data structures are partitioned into arrays of Position (left, center, right) + // The following data structures are partitioned into arrays of Position (one, two, three) private mapEditorInputActionContextToPrimaryActions: { [id: string]: IEditorInputAction[] }[]; private mapEditorInputActionContextToSecondaryActions: { [id: string]: IEditorInputAction[] }[]; diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index c40ec6a532b..bf7e641c0b1 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -30,10 +30,10 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { CloseEditorsInGroupAction, CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, KeepEditorAction, CloseOtherEditorsInGroupAction, OpenToSideAction, - NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInLeftGroupAction, - toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_LEFT_GROUP_PREFIX, - OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInCenterGroupAction, MoveEditorRightInGroupAction, - NAVIGATE_IN_CENTER_GROUP_PREFIX, ShowEditorsInRightGroupAction, NAVIGATE_IN_RIGHT_GROUP_PREFIX, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToLeftGroupAction, MoveEditorToRightGroupAction, MoveEditorLeftInGroupAction + NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInGroupOneAction, + toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_GROUP_ONE_PREFIX, + OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, + NAVIGATE_IN_GROUP_TWO_PREFIX, ShowEditorsInGroupThreeAction, NAVIGATE_IN_GROUP_THREE_PREFIX, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorLeftInGroupAction } from 'vs/workbench/browser/parts/editor/editorActions'; import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands'; @@ -108,6 +108,8 @@ export class QuickOpenActionContributor extends ActionBarContributor { if (entry) { if (!this.openToSideActionInstance) { this.openToSideActionInstance = this.instantiationService.createInstance(OpenToSideAction); + } else { + this.openToSideActionInstance.updateClass(); } actions.push(this.openToSideActionInstance); @@ -131,13 +133,13 @@ actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionCont Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( 'vs/workbench/browser/parts/editor/editorPicker', - 'LeftEditorGroupPicker', - NAVIGATE_IN_LEFT_GROUP_PREFIX, + 'GroupOnePicker', + NAVIGATE_IN_GROUP_ONE_PREFIX, [ { - prefix: NAVIGATE_IN_LEFT_GROUP_PREFIX, + prefix: NAVIGATE_IN_GROUP_ONE_PREFIX, needsEditor: false, - description: nls.localize('leftEditorGroupPicker', "Show Editors in Left Group") + description: nls.localize('groupOnePicker', "Show Editors in First Group") } ] ) @@ -146,13 +148,13 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( 'vs/workbench/browser/parts/editor/editorPicker', - 'CenterEditorGroupPicker', - NAVIGATE_IN_CENTER_GROUP_PREFIX, + 'GroupTwoPicker', + NAVIGATE_IN_GROUP_TWO_PREFIX, [ { - prefix: NAVIGATE_IN_CENTER_GROUP_PREFIX, + prefix: NAVIGATE_IN_GROUP_TWO_PREFIX, needsEditor: false, - description: nls.localize('centerEditorGroupPicker', "Show Editors in Center Group") + description: nls.localize('groupTwoPicker', "Show Editors in Second Group") } ] ) @@ -161,13 +163,13 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( 'vs/workbench/browser/parts/editor/editorPicker', - 'RightEditorGroupPicker', - NAVIGATE_IN_RIGHT_GROUP_PREFIX, + 'GroupThreePicker', + NAVIGATE_IN_GROUP_THREE_PREFIX, [ { - prefix: NAVIGATE_IN_RIGHT_GROUP_PREFIX, + prefix: NAVIGATE_IN_GROUP_THREE_PREFIX, needsEditor: false, - description: nls.localize('rightEditorGroupPicker', "Show Editors in Right Group") + description: nls.localize('groupThreePicker', "Show Editors in Third Group") } ] ) @@ -193,9 +195,9 @@ const category = nls.localize('view', "View"); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }), 'Open Next Recently Used Editor in Group'); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } }), 'Open Previous Recently Used Editor in Group'); registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInLeftGroupAction, ShowEditorsInLeftGroupAction.ID, ShowEditorsInLeftGroupAction.LABEL), 'View: Show Editors in Left Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInCenterGroupAction, ShowEditorsInCenterGroupAction.ID, ShowEditorsInCenterGroupAction.LABEL), 'View: Show Editors in Center Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInRightGroupAction, ShowEditorsInRightGroupAction.ID, ShowEditorsInRightGroupAction.LABEL), 'View: Show Editors in Left Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupOneAction, ShowEditorsInGroupOneAction.ID, ShowEditorsInGroupOneAction.LABEL), 'View: Show Editors in First Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupTwoAction, ShowEditorsInGroupTwoAction.ID, ShowEditorsInGroupTwoAction.LABEL), 'View: Show Editors in Second Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupThreeAction, ShowEditorsInGroupThreeAction.ID, ShowEditorsInGroupThreeAction.LABEL), 'View: Show Editors in Third Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow } }), 'View: Open Next Editor', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow } }), 'View: Open Previous Editor', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category); @@ -209,9 +211,9 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInOtherGro registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category); registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus Left Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSecondGroupAction, FocusSecondGroupAction.ID, FocusSecondGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_2 }), 'View: Focus Center Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusThirdGroupAction, FocusThirdGroupAction.ID, FocusThirdGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_3 }), 'View: Focus Right Editor Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSecondGroupAction, FocusSecondGroupAction.ID, FocusSecondGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_2 }), 'View: Focus Second Editor Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusThirdGroupAction, FocusThirdGroupAction.ID, FocusThirdGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_3 }), 'View: Focus Third Editor Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastEditorInStackAction, FocusLastEditorInStackAction.ID, FocusLastEditorInStackAction.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0 } }), 'View: Focus Last Editor in Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(EvenGroupWidthsAction, EvenGroupWidthsAction.ID, EvenGroupWidthsAction.LABEL), 'View: Even Editor Group Widths', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Sidebar', category); @@ -220,8 +222,8 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupA registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLeftGroupAction, MoveEditorToLeftGroupAction.ID, MoveEditorToLeftGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Group to the Left', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToRightGroupAction, MoveEditorToRightGroupAction.ID, MoveEditorToRightGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Group to the Right', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Previous Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Next Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward'); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 14dfd5f035d..8ebadacb988 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -16,6 +16,7 @@ import { IPartService } from 'vs/workbench/services/part/common/partService'; import { Position, IEditor, Direction, IResourceInput, IEditorInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorGroupService, GroupArrangement } from 'vs/workbench/services/group/common/groupService'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; @@ -70,26 +71,26 @@ export class SplitEditorAction extends Action { switch (editorCount) { - // Open split editor to the right of left one + // Open split editor to the right/bottom of left/top one case 1: - targetPosition = Position.CENTER; + targetPosition = Position.TWO; break; // Special case two editors opened case 2: - // Continue splitting to the right - if (editorToSplit.position === Position.CENTER) { - targetPosition = Position.RIGHT; + // Continue splitting to the right/bottom + if (editorToSplit.position === Position.TWO) { + targetPosition = Position.THREE; } - // Push the center group to the right to make room for the splitted input - else if (editorToSplit.position === Position.LEFT) { + // Push the second group to the right/bottom to make room for the splitted input + else if (editorToSplit.position === Position.ONE) { options.preserveFocus = true; - return this.editorService.openEditor(editorToSplit.input, options, Position.RIGHT).then(() => { - this.editorGroupService.moveGroup(Position.RIGHT, Position.CENTER); - this.editorGroupService.focusGroup(Position.CENTER); + return this.editorService.openEditor(editorToSplit.input, options, Position.THREE).then(() => { + this.editorGroupService.moveGroup(Position.THREE, Position.TWO); + this.editorGroupService.focusGroup(Position.TWO); }); } } @@ -125,7 +126,7 @@ export class NavigateBetweenGroupsAction extends Action { return TPromise.as(false); } - // Cycle to the left and use module to start at 0 again + // Cycle to the left/top and use module to start at 0 again const visibleEditors = this.editorService.getVisibleEditors(); const editorCount = visibleEditors.length; const newIndex = (activeEditor.position + 1) % editorCount; @@ -162,7 +163,7 @@ export class FocusActiveGroupAction extends Action { export class FocusFirstGroupAction extends Action { public static ID = 'workbench.action.focusFirstEditorGroup'; - public static LABEL = nls.localize('focusFirstEditorGroup', "Focus Left Editor Group"); + public static LABEL = nls.localize('focusFirstEditorGroup', "Focus First Editor Group"); constructor( id: string, @@ -176,11 +177,11 @@ export class FocusFirstGroupAction extends Action { public run(): TPromise { - // Find left editor and focus it + // Find left/top editor and focus it const editors = this.editorService.getVisibleEditors(); for (let editor of editors) { - if (editor.position === Position.LEFT) { - this.editorGroupService.focusGroup(Position.LEFT); + if (editor.position === Position.ONE) { + this.editorGroupService.focusGroup(Position.ONE); return TPromise.as(true); } @@ -193,10 +194,10 @@ export class FocusFirstGroupAction extends Action { // For now only support to open files from history to the side if (input instanceof EditorInput) { if (!!getUntitledOrFileResource(input)) { - return this.editorService.openEditor(input, null, Position.LEFT); + return this.editorService.openEditor(input, null, Position.ONE); } } else { - return this.editorService.openEditor(input as IResourceInput, Position.LEFT); + return this.editorService.openEditor(input as IResourceInput, Position.ONE); } } @@ -280,7 +281,7 @@ export abstract class BaseFocusSideGroupAction extends Action { export class FocusSecondGroupAction extends BaseFocusSideGroupAction { public static ID = 'workbench.action.focusSecondEditorGroup'; - public static LABEL = nls.localize('focusSecondEditorGroup', "Focus Center Editor Group"); + public static LABEL = nls.localize('focusSecondEditorGroup', "Focus Second Editor Group"); constructor( id: string, @@ -293,18 +294,18 @@ export class FocusSecondGroupAction extends BaseFocusSideGroupAction { } protected getReferenceEditorSide(): Position { - return Position.LEFT; + return Position.ONE; } protected getTargetEditorSide(): Position { - return Position.CENTER; + return Position.TWO; } } export class FocusThirdGroupAction extends BaseFocusSideGroupAction { public static ID = 'workbench.action.focusThirdEditorGroup'; - public static LABEL = nls.localize('focusThirdEditorGroup', "Focus Right Editor Group"); + public static LABEL = nls.localize('focusThirdEditorGroup', "Focus Third Editor Group"); constructor( id: string, @@ -317,11 +318,11 @@ export class FocusThirdGroupAction extends BaseFocusSideGroupAction { } protected getReferenceEditorSide(): Position { - return Position.CENTER; + return Position.TWO; } protected getTargetEditorSide(): Position { - return Position.RIGHT; + return Position.THREE; } } @@ -348,10 +349,10 @@ export class FocusPreviousGroup extends Action { } - // Find the next position to the left - let nextPosition: Position = Position.LEFT; - if (activeEditor.position === Position.RIGHT) { - nextPosition = Position.CENTER; + // Find the next position to the left/top + let nextPosition: Position = Position.ONE; + if (activeEditor.position === Position.THREE) { + nextPosition = Position.TWO; } // Focus next position if provided @@ -377,22 +378,22 @@ export class FocusNextGroup extends Action { super(id, label); this.navigateActions = []; - this.navigateActions[Position.LEFT] = instantiationService.createInstance(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL); - this.navigateActions[Position.CENTER] = instantiationService.createInstance(FocusSecondGroupAction, FocusSecondGroupAction.ID, FocusSecondGroupAction.LABEL); - this.navigateActions[Position.RIGHT] = instantiationService.createInstance(FocusThirdGroupAction, FocusThirdGroupAction.ID, FocusThirdGroupAction.LABEL); + this.navigateActions[Position.ONE] = instantiationService.createInstance(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL); + this.navigateActions[Position.TWO] = instantiationService.createInstance(FocusSecondGroupAction, FocusSecondGroupAction.ID, FocusSecondGroupAction.LABEL); + this.navigateActions[Position.THREE] = instantiationService.createInstance(FocusThirdGroupAction, FocusThirdGroupAction.ID, FocusThirdGroupAction.LABEL); } public run(event?: any): TPromise { - // Find the next position to the right to use + // Find the next position to the right/bottom to use let nextPosition: Position; const activeEditor = this.editorService.getActiveEditor(); if (!activeEditor) { - nextPosition = Position.LEFT; - } else if (activeEditor.position === Position.LEFT) { - nextPosition = Position.CENTER; - } else if (activeEditor.position === Position.CENTER) { - nextPosition = Position.RIGHT; + nextPosition = Position.ONE; + } else if (activeEditor.position === Position.ONE) { + nextPosition = Position.TWO; + } else if (activeEditor.position === Position.TWO) { + nextPosition = Position.THREE; } // Run the action for the target next position @@ -409,17 +410,25 @@ export class OpenToSideAction extends Action { public static OPEN_TO_SIDE_ID = 'workbench.action.openToSide'; public static OPEN_TO_SIDE_LABEL = nls.localize('openToSide', "Open to the Side"); - constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService) { + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IConfigurationService private configurationService: IConfigurationService + ) { super(OpenToSideAction.OPEN_TO_SIDE_ID, OpenToSideAction.OPEN_TO_SIDE_LABEL); - this.class = 'quick-open-sidebyside'; - this.updateEnablement(); + this.updateClass(); + } + + public updateClass(): void { + const editorLayoutVertical = this.configurationService.lookup('workbench.editor.sideBySideLayout').value !== 'horizontal'; + + this.class = editorLayoutVertical ? 'quick-open-sidebyside-vertical' : 'quick-open-sidebyside-horizontal'; } private updateEnablement(): void { const activeEditor = this.editorService.getActiveEditor(); - this.enabled = (!activeEditor || activeEditor.position !== Position.RIGHT); + this.enabled = (!activeEditor || activeEditor.position !== Position.THREE); } public run(context: any): TPromise { @@ -676,13 +685,13 @@ export class MoveGroupLeftAction extends Action { let position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null; if (typeof position !== 'number') { const activeEditor = this.editorService.getActiveEditor(); - if (activeEditor && (activeEditor.position === Position.CENTER || activeEditor.position === Position.RIGHT)) { + if (activeEditor && (activeEditor.position === Position.TWO || activeEditor.position === Position.THREE)) { position = activeEditor.position; } } if (typeof position === 'number') { - const newPosition = (position === Position.CENTER) ? Position.LEFT : Position.CENTER; + const newPosition = (position === Position.TWO) ? Position.ONE : Position.TWO; // Move group this.editorGroupService.moveGroup(position, newPosition); @@ -712,13 +721,13 @@ export class MoveGroupRightAction extends Action { const activeEditor = this.editorService.getActiveEditor(); const editors = this.editorService.getVisibleEditors(); - if ((editors.length === 2 && activeEditor.position === Position.LEFT) || (editors.length === 3 && activeEditor.position !== Position.RIGHT)) { + if ((editors.length === 2 && activeEditor.position === Position.ONE) || (editors.length === 3 && activeEditor.position !== Position.THREE)) { position = activeEditor.position; } } if (typeof position === 'number') { - const newPosition = (position === Position.LEFT) ? Position.CENTER : Position.RIGHT; + const newPosition = (position === Position.ONE) ? Position.TWO : Position.THREE; // Move group this.editorGroupService.moveGroup(position, newPosition); @@ -754,7 +763,7 @@ export class EvenGroupWidthsAction extends Action { } public run(): TPromise { - this.editorGroupService.arrangeGroups(GroupArrangement.EVEN_WIDTH); + this.editorGroupService.arrangeGroups(GroupArrangement.EVEN); return TPromise.as(false); } @@ -938,55 +947,55 @@ export class ReopenClosedEditorAction extends Action { } } -export const NAVIGATE_IN_LEFT_GROUP_PREFIX = 'edt left '; +export const NAVIGATE_IN_GROUP_ONE_PREFIX = 'edt one '; -export class ShowEditorsInLeftGroupAction extends QuickOpenAction { +export class ShowEditorsInGroupOneAction extends QuickOpenAction { - public static ID = 'workbench.action.showEditorsInLeftGroup'; - public static LABEL = nls.localize('showEditorsInLeftGroup', "Show Editors in Left Group"); + public static ID = 'workbench.action.showEditorsInFirstGroup'; + public static LABEL = nls.localize('showEditorsInFirstGroup', "Show Editors in First Group"); constructor( actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService ) { - super(actionId, actionLabel, NAVIGATE_IN_LEFT_GROUP_PREFIX, quickOpenService); + super(actionId, actionLabel, NAVIGATE_IN_GROUP_ONE_PREFIX, quickOpenService); this.class = 'show-group-editors-action'; } } -export const NAVIGATE_IN_CENTER_GROUP_PREFIX = 'edt center '; +export const NAVIGATE_IN_GROUP_TWO_PREFIX = 'edt two '; -export class ShowEditorsInCenterGroupAction extends QuickOpenAction { +export class ShowEditorsInGroupTwoAction extends QuickOpenAction { - public static ID = 'workbench.action.showEditorsInCenterGroup'; - public static LABEL = nls.localize('showEditorsInCenterGroup', "Show Editors in Center Group"); + public static ID = 'workbench.action.showEditorsInSecondGroup'; + public static LABEL = nls.localize('showEditorsInSecondGroup', "Show Editors in Second Group"); constructor( actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService ) { - super(actionId, actionLabel, NAVIGATE_IN_CENTER_GROUP_PREFIX, quickOpenService); + super(actionId, actionLabel, NAVIGATE_IN_GROUP_TWO_PREFIX, quickOpenService); this.class = 'show-group-editors-action'; } } -export const NAVIGATE_IN_RIGHT_GROUP_PREFIX = 'edt right '; +export const NAVIGATE_IN_GROUP_THREE_PREFIX = 'edt three '; -export class ShowEditorsInRightGroupAction extends QuickOpenAction { +export class ShowEditorsInGroupThreeAction extends QuickOpenAction { - public static ID = 'workbench.action.showEditorsInRightGroup'; - public static LABEL = nls.localize('showEditorsInRightGroup', "Show Editors in Right Group"); + public static ID = 'workbench.action.showEditorsInThirdGroup'; + public static LABEL = nls.localize('showEditorsInThirdGroup', "Show Editors in Third Group"); constructor( actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService ) { - super(actionId, actionLabel, NAVIGATE_IN_RIGHT_GROUP_PREFIX, quickOpenService); + super(actionId, actionLabel, NAVIGATE_IN_GROUP_THREE_PREFIX, quickOpenService); this.class = 'show-group-editors-action'; } @@ -1014,13 +1023,13 @@ export class ShowEditorsInGroupAction extends Action { } switch (stacks.positionOfGroup(context.group)) { - case Position.CENTER: - return this.quickOpenService.show((groupCount === 2) ? NAVIGATE_IN_RIGHT_GROUP_PREFIX : NAVIGATE_IN_CENTER_GROUP_PREFIX); - case Position.RIGHT: - return this.quickOpenService.show(NAVIGATE_IN_RIGHT_GROUP_PREFIX); + case Position.TWO: + return this.quickOpenService.show(NAVIGATE_IN_GROUP_TWO_PREFIX); + case Position.THREE: + return this.quickOpenService.show(NAVIGATE_IN_GROUP_THREE_PREFIX); } - return this.quickOpenService.show(NAVIGATE_IN_LEFT_GROUP_PREFIX); + return this.quickOpenService.show(NAVIGATE_IN_GROUP_ONE_PREFIX); } } @@ -1054,13 +1063,12 @@ export class BaseQuickOpenEditorInGroupAction extends Action { const stacks = this.editorGroupService.getStacksModel(); if (stacks.activeGroup) { const activePosition = stacks.positionOfGroup(stacks.activeGroup); - const count = stacks.groups.length; - let prefix = NAVIGATE_IN_LEFT_GROUP_PREFIX; + let prefix = NAVIGATE_IN_GROUP_ONE_PREFIX; - if (activePosition === Position.CENTER && count === 3) { - prefix = NAVIGATE_IN_CENTER_GROUP_PREFIX; - } else if (activePosition === Position.RIGHT || (activePosition === Position.CENTER && count === 2)) { - prefix = NAVIGATE_IN_RIGHT_GROUP_PREFIX; + if (activePosition === Position.TWO) { + prefix = NAVIGATE_IN_GROUP_TWO_PREFIX; + } else if (activePosition === Position.THREE) { + prefix = NAVIGATE_IN_GROUP_THREE_PREFIX; } this.quickOpenService.show(prefix, { quickNavigateConfiguration: { keybindings: keys } }); @@ -1222,10 +1230,10 @@ export class MoveEditorRightInGroupAction extends Action { } } -export class MoveEditorToLeftGroupAction extends Action { +export class MoveEditorToPreviousGroupAction extends Action { - public static ID = 'workbench.action.moveEditorToLeftGroup'; - public static LABEL = nls.localize('moveEditorToLeftGroup', "Move Editor into Group to the Left"); + public static ID = 'workbench.action.moveEditorToPreviousGroup'; + public static LABEL = nls.localize('moveEditorToPreviousGroup', "Move Editor into Previous Group"); constructor( id: string, @@ -1238,7 +1246,7 @@ export class MoveEditorToLeftGroupAction extends Action { public run(): TPromise { const activeEditor = this.editorService.getActiveEditor(); - if (activeEditor && activeEditor.position !== Position.LEFT) { + if (activeEditor && activeEditor.position !== Position.ONE) { this.editorGroupService.moveEditor(activeEditor.input, activeEditor.position, activeEditor.position - 1); } @@ -1246,10 +1254,10 @@ export class MoveEditorToLeftGroupAction extends Action { } } -export class MoveEditorToRightGroupAction extends Action { +export class MoveEditorToNextGroupAction extends Action { - public static ID = 'workbench.action.moveEditorToRightGroup'; - public static LABEL = nls.localize('moveEditorToRightGroup', "Move Editor into Group to the Right"); + public static ID = 'workbench.action.moveEditorToNextGroup'; + public static LABEL = nls.localize('moveEditorToNextGroup', "Move Editor into Next Group"); constructor( id: string, @@ -1262,7 +1270,7 @@ export class MoveEditorToRightGroupAction extends Action { public run(): TPromise { const activeEditor = this.editorService.getActiveEditor(); - if (activeEditor && activeEditor.position !== Position.RIGHT) { + if (activeEditor && activeEditor.position !== Position.THREE) { this.editorGroupService.moveEditor(activeEditor.input, activeEditor.position, activeEditor.position + 1); } diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index af33b2377d0..6c028b1c8ca 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -127,13 +127,13 @@ function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, activeEditor: newPosition = newPosition + 1; break; case ActiveEditorMovePositioning.FIRST: - newPosition = Position.LEFT; + newPosition = Position.ONE; break; case ActiveEditorMovePositioning.LAST: - newPosition = Position.RIGHT; + newPosition = Position.THREE; break; case ActiveEditorMovePositioning.CENTER: - newPosition = Position.CENTER; + newPosition = Position.TWO; break; case ActiveEditorMovePositioning.POSITION: newPosition = args.value - 1; @@ -211,7 +211,12 @@ function handleCommandDeprecations(): void { 'workbench.files.action.reopenClosedFile': 'workbench.action.reopenClosedEditor', 'workbench.files.action.workingFilesPicker': 'workbench.action.showAllEditors', 'workbench.action.cycleEditor': 'workbench.action.navigateEditorGroups', - 'workbench.action.terminal.focus': 'workbench.action.focusPanel' + 'workbench.action.terminal.focus': 'workbench.action.focusPanel', + 'workbench.action.showEditorsInLeftGroup': 'workbench.action.showEditorsInFirstGroup', + 'workbench.action.showEditorsInCenterGroup': 'workbench.action.showEditorsInSecondGroup', + 'workbench.action.showEditorsInRightGroup': 'workbench.action.showEditorsInThirdGroup', + 'workbench.action.moveEditorToLeftGroup': 'workbench.action.moveEditorToPreviousGroup', + 'workbench.action.moveEditorToRightGroup': 'workbench.action.moveEditorToNextGroup' }; Object.keys(mapDeprecatedCommands).forEach(deprecatedCommandId => { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 7ecba1cbe25..675efbefc8b 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -54,7 +54,7 @@ class ProgressMonitor { } interface IEditorPartUIState { - widthRatio: number[]; + ratio: number[]; } interface IEditorReplacement extends EditorIdentifier { @@ -72,9 +72,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService public _serviceBrand: any; - private static GROUP_LEFT_LABEL = nls.localize('leftGroup', "Left"); - private static GROUP_CENTER_LABEL = nls.localize('centerGroup', "Center"); - private static GROUP_RIGHT_LABEL = nls.localize('rightGroup', "Right"); + private static GROUP_LEFT = nls.localize('groupOneVertical', "Left"); + private static GROUP_CENTER = nls.localize('groupTwoVertical', "Center"); + private static GROUP_RIGHT = nls.localize('groupThreeVertical', "Right"); + private static GROUP_TOP = nls.localize('groupOneHorizontal', "Top"); + private static GROUP_MIDDLE = nls.localize('groupTwoHorizontal', "Middle"); + private static GROUP_BOTTOM = nls.localize('groupThreeHorizontal', "Bottom"); private static EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.uiState'; @@ -83,6 +86,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService private memento: any; private stacks: EditorStacksModel; private previewEditors: boolean; + private layoutVertically: boolean; private _onEditorsChanged: Emitter; private _onEditorsMoved: Emitter; @@ -125,10 +129,15 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService this.stacks = this.instantiationService.createInstance(EditorStacksModel, restoreFromStorage); - const editorConfig = configurationService.getConfiguration().workbench.editor; - this.previewEditors = editorConfig.enablePreview; + const config = configurationService.getConfiguration(); + if (config && config.workbench && config.workbench.editor) { + const editorConfig = config.workbench.editor; - this.telemetryService.publicLog('workbenchEditorConfiguration', editorConfig); + this.previewEditors = editorConfig.enablePreview; + this.layoutVertically = editorConfig.sideBySideLayout !== 'horizontal'; + + this.telemetryService.publicLog('workbenchEditorConfiguration', editorConfig); + } this.registerListeners(); } @@ -140,18 +149,27 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void { - const newPreviewEditors = configuration.workbench.editor.enablePreview; + if (configuration && configuration.workbench && configuration.workbench.editor) { + const editorConfig = configuration.workbench.editor; - // Pin all preview editors of the user chose to disable preview - if (this.previewEditors !== newPreviewEditors && !newPreviewEditors) { - this.stacks.groups.forEach(group => { - if (group.previewEditor) { - this.pinEditor(group, group.previewEditor); - } - }); + // Pin all preview editors of the user chose to disable preview + const newPreviewEditors = editorConfig.enablePreview; + if (this.previewEditors !== newPreviewEditors && !newPreviewEditors) { + this.stacks.groups.forEach(group => { + if (group.previewEditor) { + this.pinEditor(group, group.previewEditor); + } + }); + } + this.previewEditors = newPreviewEditors; + + // Rename groups when layout changes + const newLayoutVertically = editorConfig.sideBySideLayout !== 'horizontal'; + if (newLayoutVertically !== this.layoutVertically) { + this.layoutVertically = newLayoutVertically; + this.renameGroups(); + } } - - this.previewEditors = newPreviewEditors; } private onEditorDirty(identifier: EditorIdentifier): void { @@ -178,14 +196,14 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } public openEditor(input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - public openEditor(input: EditorInput, options?: EditorOptions, position?: Position, widthRatios?: number[]): TPromise; - public openEditor(input: EditorInput, options?: EditorOptions, arg3?: any, widthRatios?: number[]): TPromise { + public openEditor(input: EditorInput, options?: EditorOptions, position?: Position, ratio?: number[]): TPromise; + public openEditor(input: EditorInput, options?: EditorOptions, arg3?: any, ratio?: number[]): TPromise { // Normalize some values if (!options) { options = null; } - // Determine position to open editor in (left, center, right) - const position = this.findPosition(input, options, arg3, widthRatios); + // Determine position to open editor in (one, two, three) + const position = this.findPosition(input, options, arg3, ratio); // Some conditions under which we prevent the request if ( @@ -204,15 +222,15 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } // Opened to the side - if (position !== Position.LEFT) { + if (position !== Position.ONE) { this.telemetryService.publicLog('workbenchSideEditorOpened', { position: position }); } // Open through UI - return this.doOpenEditor(position, descriptor, input, options, widthRatios); + return this.doOpenEditor(position, descriptor, input, options, ratio); } - private doOpenEditor(position: Position, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, widthRatios: number[]): TPromise { + private doOpenEditor(position: Position, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, ratio: number[]): TPromise { // Update stacks: We do this early on before the UI is there because we want our stacks model to have // a consistent view of the editor world and updating it later async after the UI is there will cause @@ -243,7 +261,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService })); // Show editor - return this.doShowEditor(group, descriptor, input, options, widthRatios, monitor).then(editor => { + return this.doShowEditor(group, descriptor, input, options, ratio, monitor).then(editor => { if (!editor) { return TPromise.as(null); // canceled or other error } @@ -253,7 +271,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService }); } - private doShowEditor(group: EditorGroup, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, widthRatios: number[], monitor: ProgressMonitor): TPromise { + private doShowEditor(group: EditorGroup, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, ratio: number[], monitor: ProgressMonitor): TPromise { const position = this.stacks.positionOfGroup(group); const editorAtPosition = this.visibleEditors[position]; @@ -281,7 +299,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } // Show in side by side control - this.sideBySideControl.show(editor, position, options && options.preserveFocus, widthRatios); + this.sideBySideControl.show(editor, position, options && options.preserveFocus, ratio); // Indicate to editor that it is now visible editor.setVisible(true, position); @@ -518,7 +536,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } // Explicitly trigger the focus changed handler because the side by side control will not trigger it unless - // the user is actively changing focus with the mouse from left to right. + // the user is actively changing focus with the mouse from left/top to right/bottom. this.onGroupFocusChanged(); } } @@ -886,9 +904,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService activePosition = this.stacks.positionOfGroup(this.stacks.activeGroup); } - const widthRatios = this.sideBySideControl.getWidthRatios(); + const ratio = this.sideBySideControl.getRatio(); - return this.doOpenEditors(editors, activePosition, widthRatios); + return this.doOpenEditors(editors, activePosition, ratio); } public restoreEditors(): TPromise { @@ -911,43 +929,43 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService const editorState: IEditorPartUIState = this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY]; - return this.doOpenEditors(editors, activePosition, editorState && editorState.widthRatio); + return this.doOpenEditors(editors, activePosition, editorState && editorState.ratio); } - private doOpenEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[], activePosition?: number, widthRatios?: number[]): TPromise { - const leftEditors = editors.filter(e => e.position === Position.LEFT); - const centerEditors = editors.filter(e => e.position === Position.CENTER); - const rightEditors = editors.filter(e => e.position === Position.RIGHT); + private doOpenEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[], activePosition?: number, ratio?: number[]): TPromise { + const positionOneEditors = editors.filter(e => e.position === Position.ONE); + const positionTwoEditors = editors.filter(e => e.position === Position.TWO); + const positionThreeEditors = editors.filter(e => e.position === Position.THREE); - const leftGroup = this.stacks.groupAt(Position.LEFT); - const centerGroup = this.stacks.groupAt(Position.CENTER); - const rightGroup = this.stacks.groupAt(Position.RIGHT); + const groupOne = this.stacks.groupAt(Position.ONE); + const groupTwo = this.stacks.groupAt(Position.TWO); + const groupThree = this.stacks.groupAt(Position.THREE); // Compute the imaginary count if we const all editors open as the way requested - const leftCount = leftEditors.length + (leftGroup ? leftGroup.count : 0); - const centerCount = centerEditors.length + (centerGroup ? centerGroup.count : 0); - const rightCount = rightEditors.length + (rightGroup ? rightGroup.count : 0); + const oneCount = positionOneEditors.length + (groupOne ? groupOne.count : 0); + const twoCount = positionTwoEditors.length + (groupTwo ? groupTwo.count : 0); + const threeCount = positionThreeEditors.length + (groupThree ? groupThree.count : 0); // Validate we do not produce empty groups given our imaginary count model - if ((!leftCount && (centerCount || rightCount) || (!centerCount && rightCount))) { - leftEditors.push(...centerEditors); - leftEditors.push(...rightEditors); - centerEditors.splice(0, centerEditors.length); - rightEditors.splice(0, rightEditors.length); + if ((!oneCount && (twoCount || threeCount) || (!twoCount && threeCount))) { + positionOneEditors.push(...positionTwoEditors); + positionOneEditors.push(...positionThreeEditors); + positionTwoEditors.splice(0, positionTwoEditors.length); + positionThreeEditors.splice(0, positionThreeEditors.length); } // Validate active input if (typeof activePosition !== 'number') { - activePosition = Position.LEFT; + activePosition = Position.ONE; } - // Validate width ratios - const positions = rightEditors.length ? 3 : centerEditors.length ? 2 : 1; - if (!widthRatios || widthRatios.length !== positions) { + // Validate ratios + const positions = positionThreeEditors.length ? 3 : positionTwoEditors.length ? 2 : 1; + if (!ratio || ratio.length !== positions) { if (!this.getVisibleEditors().length) { - widthRatios = (positions === 3) ? [0.33, 0.33, 0.34] : (positions === 2) ? [0.5, 0.5] : [1]; + ratio = (positions === 3) ? [0.33, 0.33, 0.34] : (positions === 2) ? [0.5, 0.5] : [1]; } else { - widthRatios = void 0; + ratio = void 0; } } @@ -962,7 +980,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Open each input respecting the options. Since there can only be one active editor in each // position, we have to pick the first input from each position and add the others as inactive const promises: TPromise[] = []; - [leftEditors.shift(), centerEditors.shift(), rightEditors.shift()].forEach((editor, position) => { + [positionOneEditors.shift(), positionTwoEditors.shift(), positionThreeEditors.shift()].forEach((editor, position) => { if (!editor) { return; // unused position } @@ -981,7 +999,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService options = EditorOptions.create({ preserveFocus }); } - promises.push(this.openEditor(input, options, position, widthRatios)); + promises.push(this.openEditor(input, options, position, ratio)); }); return TPromise.join(promises).then(editors => { @@ -992,7 +1010,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } // Update stacks model for remaining inactive editors - [leftEditors, centerEditors, rightEditors].forEach((editors, index) => { + [positionOneEditors, positionTwoEditors, positionThreeEditors].forEach((editors, index) => { const group = this.stacks.groupAt(index); if (group) { editors.forEach(editor => group.openEditor(editor.input, { pinned: true })); // group could be null if one openeditor call failed! @@ -1106,7 +1124,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService public shutdown(): void { // Persist UI State - const editorState: IEditorPartUIState = { widthRatio: this.sideBySideControl.getWidthRatios() }; + const editorState: IEditorPartUIState = { ratio: this.sideBySideControl.getRatio() }; this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = editorState; // Unload all Instantiated Editors @@ -1160,12 +1178,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService super.dispose(); } - private findPosition(input: EditorInput, options?: EditorOptions, sideBySide?: boolean, widthRatios?: number[]): Position; - private findPosition(input: EditorInput, options?: EditorOptions, desiredPosition?: Position, widthRatios?: number[]): Position; - private findPosition(input: EditorInput, options?: EditorOptions, arg1?: any, widthRatios?: number[]): Position { + private findPosition(input: EditorInput, options?: EditorOptions, sideBySide?: boolean, ratio?: number[]): Position; + private findPosition(input: EditorInput, options?: EditorOptions, desiredPosition?: Position, ratio?: number[]): Position; + private findPosition(input: EditorInput, options?: EditorOptions, arg1?: any, ratio?: number[]): Position { - // With defined width ratios, always trust the provided position - if (widthRatios && types.isNumber(arg1)) { + // With defined ratios, always trust the provided position + if (ratio && types.isNumber(arg1)) { return arg1; } @@ -1173,7 +1191,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService const visibleEditors = this.getVisibleEditors(); const activeEditor = this.getActiveEditor(); if (visibleEditors.length === 0 || !activeEditor) { - return Position.LEFT; // can only be LEFT + return Position.ONE; // can only be ONE } // Respect option to reveal an editor if it is already visible @@ -1189,30 +1207,30 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } } - // Position is unknown: pick last active or LEFT + // Position is unknown: pick last active or ONE if (types.isUndefinedOrNull(arg1) || arg1 === false) { const lastActivePosition = this.sideBySideControl.getActivePosition(); - return lastActivePosition || Position.LEFT; + return lastActivePosition || Position.ONE; } // Position is sideBySide: Find position relative to active editor if (arg1 === true) { switch (activeEditor.position) { - case Position.LEFT: - return Position.CENTER; - case Position.CENTER: - return Position.RIGHT; - case Position.RIGHT: - return null; // Cannot open to the side of the right most editor + case Position.ONE: + return Position.TWO; + case Position.TWO: + return Position.THREE; + case Position.THREE: + return null; // Cannot open to the side of the right/bottom most editor } return null; // Prevent opening to the side } // Position is provided, validate it - if (arg1 === Position.RIGHT && visibleEditors.length === 1) { - return Position.CENTER; + if (arg1 === Position.THREE && visibleEditors.length === 1) { + return Position.TWO; } return arg1; @@ -1244,7 +1262,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Close visible ones second visibleEditors - .sort((a1, a2) => this.stacks.positionOfGroup(a2.group) - this.stacks.positionOfGroup(a1.group)) // reduce layout work by starting right first + .sort((a1, a2) => this.stacks.positionOfGroup(a2.group) - this.stacks.positionOfGroup(a1.group)) // reduce layout work by starting right/bottom first .forEach(visible => this.doCloseEditor(visible.group, visible.editor, false)); // Reset @@ -1260,15 +1278,15 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService if (types.isUndefinedOrNull(arg2)) { const rochade = arg1; switch (rochade) { - case Rochade.CENTER_TO_LEFT: - this.rochade(Position.CENTER, Position.LEFT); + case Rochade.TWO_TO_ONE: + this.rochade(Position.TWO, Position.ONE); break; - case Rochade.RIGHT_TO_CENTER: - this.rochade(Position.RIGHT, Position.CENTER); + case Rochade.THREE_TO_TWO: + this.rochade(Position.THREE, Position.TWO); break; - case Rochade.CENTER_AND_RIGHT_TO_LEFT: - this.rochade(Position.CENTER, Position.LEFT); - this.rochade(Position.RIGHT, Position.CENTER); + case Rochade.TWO_AND_THREE_TO_ONE: + this.rochade(Position.TWO, Position.ONE); + this.rochade(Position.THREE, Position.TWO); } } else { const from = arg1; @@ -1327,22 +1345,22 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService const groups = this.stacks.groups; if (groups.length > 0) { - // LEFT | CENTER | RIGHT + // ONE | TWO | THREE if (groups.length > 2) { - this.stacks.renameGroup(this.stacks.groupAt(Position.LEFT), EditorPart.GROUP_LEFT_LABEL); - this.stacks.renameGroup(this.stacks.groupAt(Position.CENTER), EditorPart.GROUP_CENTER_LABEL); - this.stacks.renameGroup(this.stacks.groupAt(Position.RIGHT), EditorPart.GROUP_RIGHT_LABEL); + this.stacks.renameGroup(this.stacks.groupAt(Position.ONE), this.layoutVertically ? EditorPart.GROUP_LEFT : EditorPart.GROUP_TOP); + this.stacks.renameGroup(this.stacks.groupAt(Position.TWO), this.layoutVertically ? EditorPart.GROUP_CENTER : EditorPart.GROUP_MIDDLE); + this.stacks.renameGroup(this.stacks.groupAt(Position.THREE), this.layoutVertically ? EditorPart.GROUP_RIGHT : EditorPart.GROUP_BOTTOM); } - // LEFT | RIGHT + // ONE | TWO else if (groups.length > 1) { - this.stacks.renameGroup(this.stacks.groupAt(Position.LEFT), EditorPart.GROUP_LEFT_LABEL); - this.stacks.renameGroup(this.stacks.groupAt(Position.CENTER), EditorPart.GROUP_RIGHT_LABEL); + this.stacks.renameGroup(this.stacks.groupAt(Position.ONE), this.layoutVertically ? EditorPart.GROUP_LEFT : EditorPart.GROUP_TOP); + this.stacks.renameGroup(this.stacks.groupAt(Position.TWO), this.layoutVertically ? EditorPart.GROUP_RIGHT : EditorPart.GROUP_BOTTOM); } - // LEFT + // ONE else { - this.stacks.renameGroup(this.stacks.groupAt(Position.LEFT), EditorPart.GROUP_LEFT_LABEL); + this.stacks.renameGroup(this.stacks.groupAt(Position.ONE), this.layoutVertically ? EditorPart.GROUP_LEFT : EditorPart.GROUP_TOP); } } } diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index d2afc71aa7b..4cec36daa7f 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -184,7 +184,7 @@ export abstract class EditorGroupPicker extends BaseEditorPicker { return nls.localize('noResultsFoundInGroup', "No matching opened editor found in group"); } - return nls.localize('noOpenedEditors', "List of opened editors is currently empty"); + return nls.localize('noOpenedEditors', "List of opened editors is currently empty in group"); } public getAutoFocus(searchValue: string, quickNavigateConfiguration: IQuickNavigateConfiguration): IAutoFocus { @@ -214,28 +214,24 @@ export abstract class EditorGroupPicker extends BaseEditorPicker { } } -export class LeftEditorGroupPicker extends EditorGroupPicker { +export class GroupOnePicker extends EditorGroupPicker { protected getPosition(): Position { - return Position.LEFT; + return Position.ONE; } } -export class CenterEditorGroupPicker extends EditorGroupPicker { +export class GroupTwoPicker extends EditorGroupPicker { protected getPosition(): Position { - const stacks = this.editorGroupService.getStacksModel(); - - return stacks.groups.length > 2 ? Position.CENTER : -1; // with 2 groups open, the center one is not available + return Position.TWO; } } -export class RightEditorGroupPicker extends EditorGroupPicker { +export class GroupThreePicker extends EditorGroupPicker { protected getPosition(): Position { - const stacks = this.editorGroupService.getStacksModel(); - - return stacks.groups.length > 2 ? Position.RIGHT : Position.CENTER; + return Position.THREE; } } @@ -259,7 +255,7 @@ export class AllEditorsPicker extends BaseEditorPicker { return nls.localize('noResultsFound', "No matching opened editor found"); } - return nls.localize('noOpenedEditors', "List of opened editors is currently empty"); + return nls.localize('noOpenedEditorsAllGroups', "List of opened editors is currently empty"); } public getAutoFocus(searchValue: string): IAutoFocus { diff --git a/src/vs/workbench/browser/parts/editor/media/sidebyside.css b/src/vs/workbench/browser/parts/editor/media/sidebyside.css index 71c3a8302e8..a41b3022722 100644 --- a/src/vs/workbench/browser/parts/editor/media/sidebyside.css +++ b/src/vs/workbench/browser/parts/editor/media/sidebyside.css @@ -40,12 +40,12 @@ outline-offset: -2px; } -.vs .monaco-workbench > .editor > .content.dragging { +.vs .monaco-workbench > .editor > .content.vertical-layout.dragging { border-left: 1px solid #E7E7E7; border-right: 1px solid #E7E7E7; } -.vs-dark .monaco-workbench > .editor > .content.dragging { +.vs-dark .monaco-workbench > .editor > .content.vertical-layout.dragging { border-left: 1px solid #444; border-right: 1px solid #444; } @@ -55,57 +55,100 @@ box-sizing: border-box; /* use border box to be able to draw a border as separator between editors */ } -.monaco-workbench > .editor > .content > .one-editor-silo.editor-left { +.monaco-workbench > .editor > .content > .one-editor-silo.editor-one { left: 0; + top: 0; } -.monaco-workbench > .editor > .content > .one-editor-silo.editor-right { +.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { right: 0; } +.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + bottom: 0; +} + .monaco-workbench > .editor > .content > .one-editor-silo.dragging { z-index: 2000000; box-sizing: content-box; } -.vs .monaco-workbench > .editor > .content > .one-editor-silo.dragging { +.vs .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging { border-left: 1px solid #E7E7E7; border-right: 1px solid #E7E7E7; } -.vs .monaco-workbench > .editor > .content > .one-editor-silo.editor-center, -.vs .monaco-workbench > .editor > .content > .one-editor-silo.editor-right { +.vs .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging { + border-top: 1px solid #E7E7E7; + border-bottom: 1px solid #E7E7E7; +} + +.vs .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two, +.vs .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { border-left: 1px solid #E7E7E7; } -.vs-dark .monaco-workbench > .editor > .content > .one-editor-silo.dragging { +.vs .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two, +.vs .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + border-top: 1px solid #E7E7E7; +} + +.vs-dark .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging { border-left: 1px solid #444; border-right: 1px solid #444; } -.vs-dark .monaco-workbench > .editor > .content > .one-editor-silo.editor-center, -.vs-dark .monaco-workbench > .editor > .content > .one-editor-silo.editor-right { +.vs-dark .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging { + border-top: 1px solid #444; + border-bottom: 1px solid #444; +} + +.vs-dark .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two, +.vs-dark .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { border-left: 1px solid #444; } -.hc-black .monaco-workbench > .editor > .content > .one-editor-silo.dragging { +.vs-dark .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two, +.vs-dark .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + border-top: 1px solid #444; +} + +.hc-black .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging { border-left: 1px solid #6FC3DF; border-right: 1px solid #6FC3DF; } -.hc-black .monaco-workbench > .editor > .content > .one-editor-silo.editor-center, -.hc-black .monaco-workbench > .editor > .content > .one-editor-silo.editor-right { +.hc-black .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging { + border-top: 1px solid #6FC3DF; + border-bottom: 1px solid #6FC3DF; +} + +.hc-black .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two, +.hc-black .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { border-left: 1px solid #6FC3DF; } -.monaco-workbench > .editor > .content > .one-editor-silo.draggedunder { +.hc-black .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two, +.hc-black .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + border-top: 1px solid #6FC3DF; +} + +.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.draggedunder { transition: left 200ms ease-out; } -.monaco-workbench > .editor > .content > .editor-right.draggedunder { +.monaco-workbench > .editor > .content.vertical-layout > .editor-three.draggedunder { transition-property: right; } +.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.draggedunder { + transition: top 200ms ease-out; +} + +.monaco-workbench > .editor > .content.horizontal-layout > .editor-three.draggedunder { + transition-property: bottom; +} + .monaco-workbench > .editor > .content > .one-editor-silo > .container { height: 100%; } diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-inverse.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-inverse.svg new file mode 100644 index 00000000000..67a2b803962 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal.svg new file mode 100644 index 00000000000..48bc38ae391 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-inverse.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-inverse.svg similarity index 100% rename from src/vs/workbench/browser/parts/editor/media/split-editor-inverse.svg rename to src/vs/workbench/browser/parts/editor/media/split-editor-vertical-inverse.svg diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-vertical.svg similarity index 100% rename from src/vs/workbench/browser/parts/editor/media/split-editor.svg rename to src/vs/workbench/browser/parts/editor/media/split-editor-vertical.svg diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index ebe0a71374d..9b033449630 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -97,13 +97,22 @@ background: url('close-inverse.svg') center center no-repeat; } -.monaco-workbench .split-editor-action { - background: url('split-editor.svg') center center no-repeat; +.monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-vertical.svg') center center no-repeat; } -.vs-dark .monaco-workbench .split-editor-action, -.hc-black .monaco-workbench .split-editor-action { - background: url('split-editor-inverse.svg') center center no-repeat; +.vs-dark .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action, +.hc-black .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-vertical-inverse.svg') center center no-repeat; +} + +.monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-horizontal.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action, +.hc-black .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-horizontal-inverse.svg') center center no-repeat; } .monaco-workbench .show-group-editors-action { diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts index c296391d048..248bdc4d04b 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts @@ -11,7 +11,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import types = require('vs/base/common/types'); import { Dimension, Builder, $ } from 'vs/base/browser/builder'; -import { Sash, ISashEvent, IVerticalSashLayoutProvider } from 'vs/base/browser/ui/sash/sash'; +import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import DOM = require('vs/base/browser/dom'); @@ -39,9 +39,9 @@ import { extractResources } from 'vs/base/browser/dnd'; export enum Rochade { NONE, - CENTER_TO_LEFT, - RIGHT_TO_CENTER, - CENTER_AND_RIGHT_TO_LEFT + TWO_TO_ONE, + THREE_TO_TWO, + TWO_AND_THREE_TO_ONE } export enum ProgressState { @@ -54,7 +54,7 @@ export interface ISideBySideEditorControl { onGroupFocusChanged: Event; - show(editor: BaseEditor, position: Position, preserveActive: boolean, widthRatios?: number[]): void; + show(editor: BaseEditor, position: Position, preserveActive: boolean, ratio?: number[]): void; hide(editor: BaseEditor, position: Position, layoutAndRochade: boolean): Rochade; setActive(editor: BaseEditor): void; @@ -77,22 +77,26 @@ export interface ISideBySideEditorControl { arrangeGroups(arrangement: GroupArrangement): void; - getWidthRatios(): number[]; + getRatio(): number[]; dispose(): void; } /** * Helper class to manage multiple side by side editors for the editor part. */ -export class SideBySideEditorControl implements ISideBySideEditorControl, IVerticalSashLayoutProvider { +export class SideBySideEditorControl implements ISideBySideEditorControl, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider { private static TITLE_AREA_CONTROL_KEY = '__titleAreaControl'; private static PROGRESS_BAR_CONTROL_KEY = '__progressBar'; private static INSTANTIATION_SERVICE_KEY = '__instantiationService'; private static MIN_EDITOR_WIDTH = 170; + private static MIN_EDITOR_HEIGHT = 70; + private static EDITOR_TITLE_HEIGHT = 35; - private static SNAP_TO_MINIMIZED_THRESHOLD = 50; + + private static SNAP_TO_MINIMIZED_THRESHOLD_WIDTH = 50; + private static SNAP_TO_MINIMIZED_THRESHOLD_HEIGHT = 20; private stacks: IEditorStacksModel; @@ -100,15 +104,19 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti private dimension: Dimension; private dragging: boolean; + private layoutVertically: boolean; + private showTabs: boolean; + private showIcons: boolean; + private silos: Builder[]; - private siloWidths: number[]; - private siloInitialRatios: number[]; + private silosSize: number[]; + private silosInitialRatio: number[]; - private leftSash: Sash; - private startLeftContainerWidth: number; + private sashOne: Sash; + private startSiloOneSize: number; - private rightSash: Sash; - private startRightContainerWidth: number; + private sashTwo: Sash; + private startSiloThreeSize: number; private visibleEditors: BaseEditor[]; @@ -141,7 +149,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti this.dimension = new Dimension(0, 0); this.silos = []; - this.siloWidths = []; + this.silosSize = []; this.visibleEditors = []; this.visibleEditorFocusTrackers = []; @@ -152,32 +160,77 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti this.toDispose.push(this.onStacksChangeScheduler); this.stacksChangedBuffer = []; - this.create(this.parent); + this.onConfigurationUpdated(this.configurationService.getConfiguration()); + + this.create(); + this.registerListeners(); } + private get totalSize(): number { + if (!this.dimension || !this.dimension.width || !this.dimension.height) { + return 0; + } + + return this.layoutVertically ? this.dimension.width : this.dimension.height; + } + + private get minSize(): number { + return this.layoutVertically ? SideBySideEditorControl.MIN_EDITOR_WIDTH : SideBySideEditorControl.MIN_EDITOR_HEIGHT; + } + + private get snapToMinimizeThresholdSize(): number { + return this.layoutVertically ? SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD_WIDTH : SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD_HEIGHT; + } + private registerListeners(): void { this.toDispose.push(this.stacks.onModelChanged(e => this.onStacksChanged(e))); - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(e.config))); + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(e.config, true))); this.extensionService.onReady().then(() => this.onExtensionsReady()); } - private onConfigurationUpdated(config: IWorkbenchEditorConfiguration): void { - const {showTabs, showIcons} = this.getConfig(); + private onConfigurationUpdated(config: IWorkbenchEditorConfiguration, refresh?: boolean): void { + if (config.workbench && config.workbench.editor) { + this.showTabs = config.workbench.editor.showTabs; + this.showIcons = config.workbench.editor.showIcons; + this.layoutVertically = (config.workbench.editor.sideBySideLayout !== 'horizontal'); + } else { + this.showTabs = true; + this.showIcons = false; + this.layoutVertically = true; + } + if (!refresh) { + return; // return early if no refresh is needed + } + + // Editor Layout + const verticalLayouting = this.parent.hasClass('vertical-layout'); + if (verticalLayouting !== this.layoutVertically) { + this.parent.removeClass('vertical-layout', 'horizontal-layout'); + this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout'); + + this.sashOne.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL); + this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL); + + // Trigger layout + this.arrangeGroups(GroupArrangement.EVEN); + } + + // Editor Containers POSITIONS.forEach(position => { const titleControl = this.getTitleAreaControl(position); // TItle Container const titleContainer = $(titleControl.getContainer()); - if (showTabs) { + if (this.showTabs) { titleContainer.addClass('tabs'); } else { titleContainer.removeClass('tabs'); } const showingIcons = titleContainer.hasClass('show-file-icons'); - if (showIcons) { + if (this.showIcons) { titleContainer.addClass('show-file-icons'); } else { titleContainer.removeClass('show-file-icons'); @@ -188,14 +241,14 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti const usingTabs = (titleControl instanceof TabsTitleControl); // Recreate title when tabs change - if (usingTabs !== showTabs) { + if (usingTabs !== this.showTabs) { titleControl.dispose(); titleContainer.empty(); this.createTitleControl(this.stacks.groupAt(position), this.silos[position], titleContainer, this.getInstantiationService(position)); } // Refresh title when icons change - else if (showingIcons !== showIcons) { + else if (showingIcons !== this.showIcons) { titleControl.refresh(true); } } @@ -247,7 +300,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti return this._onGroupFocusChanged.event; } - public show(editor: BaseEditor, position: Position, preserveActive: boolean, widthRatios?: number[]): void { + public show(editor: BaseEditor, position: Position, preserveActive: boolean, ratio?: number[]): void { const visibleEditorCount = this.getVisibleEditorCount(); // Store into editor bucket @@ -266,36 +319,36 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti editor.getContainer().build(target); // Adjust layout according to provided ratios (used when restoring multiple editors at once) - if (widthRatios && (widthRatios.length === 2 || widthRatios.length === 3)) { - const hasLayoutInfo = this.dimension && this.dimension.width; + if (ratio && (ratio.length === 2 || ratio.length === 3)) { + const hasLayoutInfo = !!this.totalSize; - // We received width ratios but were not layouted yet. So we keep these ratios for when we layout() + // We received ratios but were not layouted yet. So we keep these ratios for when we layout() if (!hasLayoutInfo) { - this.siloInitialRatios = widthRatios; + this.silosInitialRatio = ratio; } // Adjust layout: -> [!][!] - if (widthRatios.length === 2) { + if (ratio.length === 2) { if (hasLayoutInfo) { - this.siloWidths[position] = this.dimension.width * widthRatios[position]; + this.silosSize[position] = this.totalSize * ratio[position]; } } // Adjust layout: -> [!][!][!] - else if (widthRatios.length === 3) { + else if (ratio.length === 3) { if (hasLayoutInfo) { - this.siloWidths[position] = this.dimension.width * widthRatios[position]; + this.silosSize[position] = this.totalSize * ratio[position]; } - if (this.rightSash.isHidden()) { - this.rightSash.show(); - this.rightSash.layout(); + if (this.sashTwo.isHidden()) { + this.sashTwo.show(); + this.sashTwo.layout(); } } - if (this.leftSash.isHidden()) { - this.leftSash.show(); - this.leftSash.layout(); + if (this.sashOne.isHidden()) { + this.sashOne.show(); + this.sashOne.layout(); } if (hasLayoutInfo) { @@ -305,31 +358,31 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // Adjust layout: -> [!] else if (visibleEditorCount === 0 && this.dimension) { - this.siloWidths[position] = this.dimension.width; + this.silosSize[position] = this.totalSize; this.layoutContainers(); } // Adjust layout: [] -> []|[!] - else if (position === Position.CENTER && this.leftSash.isHidden() && this.rightSash.isHidden() && this.dimension) { - this.siloWidths[Position.LEFT] = this.dimension.width / 2; - this.siloWidths[Position.CENTER] = this.dimension.width - this.siloWidths[Position.LEFT]; + else if (position === Position.TWO && this.sashOne.isHidden() && this.sashTwo.isHidden() && this.dimension) { + this.silosSize[Position.ONE] = this.totalSize / 2; + this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE]; - this.leftSash.show(); - this.leftSash.layout(); + this.sashOne.show(); + this.sashOne.layout(); this.layoutContainers(); } // Adjust layout: []|[] -> []|[]|[!] - else if (position === Position.RIGHT && this.rightSash.isHidden() && this.dimension) { - this.siloWidths[Position.LEFT] = this.dimension.width / 3; - this.siloWidths[Position.CENTER] = this.dimension.width / 3; - this.siloWidths[Position.RIGHT] = this.dimension.width - this.siloWidths[Position.LEFT] - this.siloWidths[Position.CENTER]; + else if (position === Position.THREE && this.sashTwo.isHidden() && this.dimension) { + this.silosSize[Position.ONE] = this.totalSize / 3; + this.silosSize[Position.TWO] = this.totalSize / 3; + this.silosSize[Position.THREE] = this.totalSize - this.silosSize[Position.ONE] - this.silosSize[Position.TWO]; - this.leftSash.layout(); - this.rightSash.show(); - this.rightSash.layout(); + this.sashOne.layout(); + this.sashTwo.show(); + this.sashTwo.layout(); this.layoutContainers(); } @@ -369,34 +422,34 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti if (this.lastActiveEditor !== editor) { this.doSetActive(editor, this.visibleEditors.indexOf(editor)); - // Automatically maximize this position if it has min editor width - if (this.siloWidths[this.lastActivePosition] === SideBySideEditorControl.MIN_EDITOR_WIDTH) { + // Automatically maximize this position if it has min editor size + if (this.silosSize[this.lastActivePosition] === this.minSize) { // Log this fact in telemetry if (this.telemetryService) { this.telemetryService.publicLog('workbenchEditorMaximized'); } - let remainingWidth = this.dimension.width; + let remainingSize = this.totalSize; - // Minimize all other positions to min width + // Minimize all other positions to min size POSITIONS.forEach(p => { if (this.lastActivePosition !== p && !!this.visibleEditors[p]) { - this.siloWidths[p] = SideBySideEditorControl.MIN_EDITOR_WIDTH; - remainingWidth -= this.siloWidths[p]; + this.silosSize[p] = this.minSize; + remainingSize -= this.silosSize[p]; } }); - // Grow focussed position if there is more width to spend - if (remainingWidth > SideBySideEditorControl.MIN_EDITOR_WIDTH) { - this.siloWidths[this.lastActivePosition] = remainingWidth; + // Grow focussed position if there is more size to spend + if (remainingSize > this.minSize) { + this.silosSize[this.lastActivePosition] = remainingSize; - if (!this.leftSash.isHidden()) { - this.leftSash.layout(); + if (!this.sashOne.isHidden()) { + this.sashOne.layout(); } - if (!this.rightSash.isHidden()) { - this.rightSash.layout(); + if (!this.sashTwo.isHidden()) { + this.sashTwo.layout(); } this.layoutContainers(); @@ -411,16 +464,16 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti private focusNextNonMinimized(): void { // If the current focussed editor is minimized, try to focus the next largest editor - if (!types.isUndefinedOrNull(this.lastActivePosition) && this.siloWidths[this.lastActivePosition] === SideBySideEditorControl.MIN_EDITOR_WIDTH) { + if (!types.isUndefinedOrNull(this.lastActivePosition) && this.silosSize[this.lastActivePosition] === this.minSize) { let candidate: Position = null; - let currentWidth = SideBySideEditorControl.MIN_EDITOR_WIDTH; + let currentSize = this.minSize; POSITIONS.forEach(position => { - // Skip current active position and check if the editor is larger than min width + // Skip current active position and check if the editor is larger than min size if (position !== this.lastActivePosition) { - if (this.visibleEditors[position] && this.siloWidths[position] > currentWidth) { + if (this.visibleEditors[position] && this.silosSize[position] > currentSize) { candidate = position; - currentWidth = this.siloWidths[position]; + currentSize = this.silosSize[position]; } } }); @@ -437,8 +490,8 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti const visibleEditorCount = this.getVisibleEditorCount(); - const hasCenter = !!this.visibleEditors[Position.CENTER]; - const hasRight = !!this.visibleEditors[Position.RIGHT]; + const hasEditorInPositionTwo = !!this.visibleEditors[Position.TWO]; + const hasEditorInPositionThree = !!this.visibleEditors[Position.THREE]; // If editor is not showing for position, return if (editor !== this.visibleEditors[position]) { @@ -456,51 +509,51 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // Adjust layout: [x] -> if (visibleEditorCount === 1) { - this.siloWidths[position] = 0; + this.silosSize[position] = 0; - this.leftSash.hide(); - this.rightSash.hide(); + this.sashOne.hide(); + this.sashTwo.hide(); this.layoutContainers(); } // Adjust layout: []|[x] -> [] or [x]|[] -> [] - else if (hasCenter && !hasRight) { - this.siloWidths[Position.LEFT] = this.dimension.width; - this.siloWidths[Position.CENTER] = 0; + else if (hasEditorInPositionTwo && !hasEditorInPositionThree) { + this.silosSize[Position.ONE] = this.totalSize; + this.silosSize[Position.TWO] = 0; - this.leftSash.hide(); - this.rightSash.hide(); + this.sashOne.hide(); + this.sashTwo.hide(); - // Move CENTER to LEFT ([x]|[] -> []) - if (position === Position.LEFT) { - this.rochade(Position.CENTER, Position.LEFT); - result = Rochade.CENTER_TO_LEFT; + // Move TWO to ONE ([x]|[] -> []) + if (position === Position.ONE) { + this.rochade(Position.TWO, Position.ONE); + result = Rochade.TWO_TO_ONE; } this.layoutContainers(); } // Adjust layout: []|[]|[x] -> [ ]|[ ] or []|[x]|[] -> [ ]|[ ] or [x]|[]|[] -> [ ]|[ ] - else if (hasCenter && hasRight) { - this.siloWidths[Position.LEFT] = this.dimension.width / 2; - this.siloWidths[Position.CENTER] = this.dimension.width - this.siloWidths[Position.LEFT]; - this.siloWidths[Position.RIGHT] = 0; + else if (hasEditorInPositionTwo && hasEditorInPositionThree) { + this.silosSize[Position.ONE] = this.totalSize / 2; + this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE]; + this.silosSize[Position.THREE] = 0; - this.leftSash.layout(); - this.rightSash.hide(); + this.sashOne.layout(); + this.sashTwo.hide(); - // Move RIGHT to CENTER ([]|[x]|[] -> [ ]|[ ]) - if (position === Position.CENTER) { - this.rochade(Position.RIGHT, Position.CENTER); - result = Rochade.RIGHT_TO_CENTER; + // Move THREE to TWO ([]|[x]|[] -> [ ]|[ ]) + if (position === Position.TWO) { + this.rochade(Position.THREE, Position.TWO); + result = Rochade.THREE_TO_TWO; } - // Move RIGHT to CENTER and CENTER to LEFT ([x]|[]|[] -> [ ]|[ ]) - else if (position === Position.LEFT) { - this.rochade(Position.CENTER, Position.LEFT); - this.rochade(Position.RIGHT, Position.CENTER); - result = Rochade.CENTER_AND_RIGHT_TO_LEFT; + // Move THREE to TWO and TWO to ONE ([x]|[]|[] -> [ ]|[ ]) + else if (position === Position.ONE) { + this.rochade(Position.TWO, Position.ONE); + this.rochade(Position.THREE, Position.TWO); + result = Rochade.TWO_AND_THREE_TO_ONE; } this.layoutContainers(); @@ -513,18 +566,18 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // Clear old this.doSetActive(null, null); - // Find new active position by taking the next one close to the closed one to the left + // Find new active position by taking the next one close to the closed one to the left/top if (layoutAndRochade) { let newActivePosition: Position; switch (position) { - case Position.LEFT: - newActivePosition = hasCenter ? Position.LEFT : null; + case Position.ONE: + newActivePosition = hasEditorInPositionTwo ? Position.ONE : null; break; - case Position.CENTER: - newActivePosition = Position.LEFT; + case Position.TWO: + newActivePosition = Position.ONE; break; - case Position.RIGHT: - newActivePosition = Position.CENTER; + case Position.THREE: + newActivePosition = Position.TWO; break; } @@ -621,57 +674,57 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti else { // Find new positions - let newLeftPosition: Position; - let newCenterPosition: Position; - let newRightPosition: Position; + let newPositionOne: Position; + let newPositionTwo: Position; + let newPositionThree: Position; - if (from === Position.LEFT) { - newLeftPosition = Position.RIGHT; - newCenterPosition = Position.LEFT; - newRightPosition = Position.CENTER; + if (from === Position.ONE) { + newPositionOne = Position.THREE; + newPositionTwo = Position.ONE; + newPositionThree = Position.TWO; } else { - newLeftPosition = Position.CENTER; - newCenterPosition = Position.RIGHT; - newRightPosition = Position.LEFT; + newPositionOne = Position.TWO; + newPositionTwo = Position.THREE; + newPositionThree = Position.ONE; } // Move containers to new position - const containerPos1 = this.silos[Position.LEFT].child(); - containerPos1.appendTo(this.silos[newLeftPosition]); + const containerPos1 = this.silos[Position.ONE].child(); + containerPos1.appendTo(this.silos[newPositionOne]); - const containerPos2 = this.silos[Position.CENTER].child(); - containerPos2.appendTo(this.silos[newCenterPosition]); + const containerPos2 = this.silos[Position.TWO].child(); + containerPos2.appendTo(this.silos[newPositionTwo]); - const containerPos3 = this.silos[Position.RIGHT].child(); - containerPos3.appendTo(this.silos[newRightPosition]); + const containerPos3 = this.silos[Position.THREE].child(); + containerPos3.appendTo(this.silos[newPositionThree]); // Inform Editors - this.visibleEditors[Position.LEFT].changePosition(newLeftPosition); - this.visibleEditors[Position.CENTER].changePosition(newCenterPosition); - this.visibleEditors[Position.RIGHT].changePosition(newRightPosition); + this.visibleEditors[Position.ONE].changePosition(newPositionOne); + this.visibleEditors[Position.TWO].changePosition(newPositionTwo); + this.visibleEditors[Position.THREE].changePosition(newPositionThree); // Update last active position accordingly - if (this.lastActivePosition === Position.LEFT) { - this.doSetActive(this.lastActiveEditor, newLeftPosition); - } else if (this.lastActivePosition === Position.CENTER) { - this.doSetActive(this.lastActiveEditor, newCenterPosition); - } else if (this.lastActivePosition === Position.RIGHT) { - this.doSetActive(this.lastActiveEditor, newRightPosition); + if (this.lastActivePosition === Position.ONE) { + this.doSetActive(this.lastActiveEditor, newPositionOne); + } else if (this.lastActivePosition === Position.TWO) { + this.doSetActive(this.lastActiveEditor, newPositionTwo); + } else if (this.lastActivePosition === Position.THREE) { + this.doSetActive(this.lastActiveEditor, newPositionThree); } } // Change data structures arrays.move(this.visibleEditors, from, to); arrays.move(this.visibleEditorFocusTrackers, from, to); - arrays.move(this.siloWidths, from, to); + arrays.move(this.silosSize, from, to); // Layout - if (!this.leftSash.isHidden()) { - this.leftSash.layout(); + if (!this.sashOne.isHidden()) { + this.sashOne.layout(); } - if (!this.rightSash.isHidden()) { - this.rightSash.layout(); + if (!this.sashTwo.isHidden()) { + this.sashTwo.layout(); } this.layoutContainers(); @@ -682,7 +735,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti return; // too early } - let availableWidth = this.dimension.width; + let availableSize = this.totalSize; const visibleEditors = this.getVisibleEditorCount(); if (visibleEditors <= 1) { @@ -694,20 +747,20 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti POSITIONS.forEach(position => { if (this.visibleEditors[position]) { if (position !== this.lastActivePosition) { - this.siloWidths[position] = SideBySideEditorControl.MIN_EDITOR_WIDTH; - availableWidth -= SideBySideEditorControl.MIN_EDITOR_WIDTH; + this.silosSize[position] = this.minSize; + availableSize -= this.minSize; } } }); - this.siloWidths[this.lastActivePosition] = availableWidth; + this.silosSize[this.lastActivePosition] = availableSize; } - // Even Widths - else if (arrangement === GroupArrangement.EVEN_WIDTH) { + // Even Sizes + else if (arrangement === GroupArrangement.EVEN) { POSITIONS.forEach(position => { if (this.visibleEditors[position]) { - this.siloWidths[position] = availableWidth / visibleEditors; + this.silosSize[position] = availableSize / visibleEditors; } }); } @@ -715,15 +768,15 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti this.layoutControl(this.dimension); } - public getWidthRatios(): number[] { + public getRatio(): number[] { const ratio: number[] = []; if (this.dimension) { - const fullWidth = this.dimension.width; + const fullSize = this.totalSize; POSITIONS.forEach(position => { if (this.visibleEditors[position]) { - ratio.push(this.siloWidths[position] / fullWidth); + ratio.push(this.silosSize[position] / fullSize); } }); } @@ -739,38 +792,40 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti return this.lastActivePosition; } - private create(parent: Builder): void { + private create(): void { + + // Store layout as class property + this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout'); // Allow to drop into container to open - this.enableDropTarget(parent.getHTMLElement()); + this.enableDropTarget(this.parent.getHTMLElement()); - // Left Silo - this.silos[Position.LEFT] = $(parent).div({ class: 'one-editor-silo editor-left monaco-editor-background' }); + // Silo One + this.silos[Position.ONE] = $(this.parent).div({ class: 'one-editor-silo editor-one monaco-editor-background' }); - // Left Sash - this.leftSash = new Sash(parent.getHTMLElement(), this, { baseSize: 5 }); - this.toDispose.push(this.leftSash.addListener2('start', () => this.onLeftSashDragStart())); - this.toDispose.push(this.leftSash.addListener2('change', (e: ISashEvent) => this.onLeftSashDrag(e))); - this.toDispose.push(this.leftSash.addListener2('end', () => this.onLeftSashDragEnd())); - this.toDispose.push(this.leftSash.addListener2('reset', () => this.onLeftSashReset())); - this.leftSash.hide(); + // Sash One + this.sashOne = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL }); + this.toDispose.push(this.sashOne.addListener2('start', () => this.onSashOneDragStart())); + this.toDispose.push(this.sashOne.addListener2('change', (e: ISashEvent) => this.onSashOneDrag(e))); + this.toDispose.push(this.sashOne.addListener2('end', () => this.onSashOneDragEnd())); + this.toDispose.push(this.sashOne.addListener2('reset', () => this.onSashOneReset())); + this.sashOne.hide(); - // Center Silo - this.silos[Position.CENTER] = $(parent).div({ class: 'one-editor-silo editor-center monaco-editor-background' }); + // Silo Two + this.silos[Position.TWO] = $(this.parent).div({ class: 'one-editor-silo editor-two monaco-editor-background' }); - // Right Sash - this.rightSash = new Sash(parent.getHTMLElement(), this, { baseSize: 5 }); - this.toDispose.push(this.rightSash.addListener2('start', () => this.onRightSashDragStart())); - this.toDispose.push(this.rightSash.addListener2('change', (e: ISashEvent) => this.onRightSashDrag(e))); - this.toDispose.push(this.rightSash.addListener2('end', () => this.onRightSashDragEnd())); - this.toDispose.push(this.rightSash.addListener2('reset', () => this.onRightSashReset())); - this.rightSash.hide(); + // Sash Two + this.sashTwo = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL }); + this.toDispose.push(this.sashTwo.addListener2('start', () => this.onSashTwoDragStart())); + this.toDispose.push(this.sashTwo.addListener2('change', (e: ISashEvent) => this.onSashTwoDrag(e))); + this.toDispose.push(this.sashTwo.addListener2('end', () => this.onSashTwoDragEnd())); + this.toDispose.push(this.sashTwo.addListener2('reset', () => this.onSashTwoReset())); + this.sashTwo.hide(); - // Right Silo - this.silos[Position.RIGHT] = $(parent).div({ class: 'one-editor-silo editor-right monaco-editor-background' }); + // Silo Three + this.silos[Position.THREE] = $(this.parent).div({ class: 'one-editor-silo editor-three monaco-editor-background' }); // For each position - const {showTabs, showIcons} = this.getConfig(); POSITIONS.forEach(position => { const silo = this.silos[position]; @@ -785,10 +840,10 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // Title containers const titleContainer = $(container).div({ 'class': 'title' }); - if (showTabs) { + if (this.showTabs) { titleContainer.addClass('tabs'); } - if (showIcons) { + if (this.showIcons) { titleContainer.addClass('show-file-icons'); } this.hookTitleDragListener(titleContainer); @@ -846,7 +901,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti const groupService = $this.editorGroupService; const splitEditor = (typeof splitTo === 'number'); // TODO@Ben ugly split code should benefit from empty group support once available! - const freeGroup = (stacks.groups.length === 1) ? Position.CENTER : Position.RIGHT; + const freeGroup = (stacks.groups.length === 1) ? Position.TWO : Position.THREE; // Check for transfer from title control const draggedEditor = TitleControl.getDraggedEditor(); @@ -908,15 +963,16 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti function positionOverlay(e: DragEvent, groups: number, position: Position): void { const target = e.target; - const posXOnOverlay = e.offsetX; const overlayIsSplit = typeof overlay.getProperty(splitToPropertyKey) === 'number'; - const overlayWidth = target.clientWidth; - const splitThreshold = overlayIsSplit ? overlayWidth / 5 : overlayWidth / 10; const isCopy = (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh); const draggedEditor = TitleControl.getDraggedEditor(); - const isOverSplitLeft = posXOnOverlay < splitThreshold; - const isOverSplitRight = posXOnOverlay + splitThreshold > overlayWidth; + const overlaySize = $this.layoutVertically ? target.clientWidth : target.clientHeight; + const splitThreshold = overlayIsSplit ? overlaySize / 5 : overlaySize / 10; + + const posOnOverlay = $this.layoutVertically ? e.offsetX : e.offsetY; + const isOverSplitLeftOrUp = posOnOverlay < splitThreshold; + const isOverSplitRightOrBottom = posOnOverlay + splitThreshold > overlaySize; let splitTarget: Position; @@ -929,14 +985,14 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti else if (!isCopy && draggedEditor && draggedEditor.group.count === 1) { const positionOfDraggedEditor = stacks.positionOfGroup(draggedEditor.group); switch (positionOfDraggedEditor) { - case Position.LEFT: - if (position === Position.CENTER && isOverSplitRight) { - splitTarget = Position.CENTER; // allow to move single editor from LEFT to CENTER + case Position.ONE: + if (position === Position.TWO && isOverSplitRightOrBottom) { + splitTarget = Position.TWO; // allow to move single editor from ONE to TWO } break; - case Position.CENTER: - if (position === Position.LEFT && isOverSplitLeft) { - splitTarget = Position.LEFT; // allow to move single editor from CENTER to LEFT + case Position.TWO: + if (position === Position.ONE && isOverSplitLeftOrUp) { + splitTarget = Position.ONE; // allow to move single editor from TWO to ONE } break; default: @@ -946,10 +1002,10 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // Any other case, check for mouse position else { - if (isOverSplitRight) { - splitTarget = (position === Position.LEFT) ? Position.CENTER : Position.RIGHT; - } else if (isOverSplitLeft) { - splitTarget = (position === Position.LEFT) ? Position.LEFT : Position.CENTER; + if (isOverSplitRightOrBottom) { + splitTarget = (position === Position.ONE) ? Position.TWO : Position.THREE; + } else if (isOverSplitLeftOrUp) { + splitTarget = (position === Position.ONE) ? Position.ONE : Position.TWO; } } @@ -962,20 +1018,12 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } // Update overlay styles - if (canSplit && isOverSplitRight) { - overlay.style({ - left: '50%', - width: '50%', - }); - } else if (canSplit && isOverSplitLeft) { - overlay.style({ - width: '50%' - }); + if (canSplit && isOverSplitRightOrBottom) { + overlay.style($this.layoutVertically ? { left: '50%', width: '50%' } : { top: '50%', height: '50%' }); + } else if (canSplit && isOverSplitLeftOrUp) { + overlay.style($this.layoutVertically ? { width: '50%' } : { height: '50%' }); } else { - overlay.style({ - left: '0', - width: '100%' - }); + overlay.style({ left: '0', width: '100%' }); } // Make sure the overlay is visible @@ -987,13 +1035,12 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti function createOverlay(target: HTMLElement): void { if (!overlay) { - const {showTabs} = $this.getConfig(); const containers = $this.visibleEditors.filter(e => !!e).map(e => e.getContainer()); containers.forEach((container, index) => { if (container && DOM.isAncestor(target, container.getHTMLElement())) { overlay = $('div').style({ - top: showTabs ? SideBySideEditorControl.EDITOR_TITLE_HEIGHT + 'px' : 0, - height: showTabs ? `calc(100% - ${SideBySideEditorControl.EDITOR_TITLE_HEIGHT}px` : '100%' + top: $this.showTabs ? SideBySideEditorControl.EDITOR_TITLE_HEIGHT + 'px' : 0, + height: $this.showTabs ? `calc(100% - ${SideBySideEditorControl.EDITOR_TITLE_HEIGHT}px` : '100%' }).id(overlayId); overlay.appendTo(container); @@ -1028,11 +1075,11 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } } - // const a dropped file open inside Code (only if dropped over editor area) + // let a dropped file open inside Code (only if dropped over editor area) this.toDispose.push(DOM.addDisposableListener(node, DOM.EventType.DROP, (e: DragEvent) => { if (e.target === node) { DOM.EventHelper.stop(e, true); - onDrop(e, Position.LEFT); + onDrop(e, Position.ONE); } else { DOM.removeClass(node, 'dropfeedback'); } @@ -1082,9 +1129,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } private createTitleControl(context: IEditorGroup, silo: Builder, container: Builder, instantiationService: IInstantiationService): void { - const {showTabs} = this.getConfig(); - - const titleAreaControl = instantiationService.createInstance(showTabs ? TabsTitleControl : NoTabsTitleControl); + const titleAreaControl = instantiationService.createInstance(this.showTabs ? TabsTitleControl : NoTabsTitleControl); titleAreaControl.create(container.getHTMLElement()); titleAreaControl.setContext(context); titleAreaControl.refresh(true /* instant */); @@ -1092,14 +1137,6 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti silo.child().setProperty(SideBySideEditorControl.TITLE_AREA_CONTROL_KEY, titleAreaControl); // associate with container } - private getConfig(config = this.configurationService.getConfiguration()): { showTabs?: boolean; showIcons?: boolean } { - if (config.workbench && config.workbench.editor) { - return { showTabs: config.workbench.editor.showTabs, showIcons: config.workbench.editor.showIcons }; - } - - return Object.create(null); - } - private findPosition(element: HTMLElement): Position { let parent = element.parentElement; while (parent) { @@ -1145,7 +1182,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // Overlay the editor area with a div to be able to capture all mouse events const overlayDiv = $('div').style({ - top: SideBySideEditorControl.EDITOR_TITLE_HEIGHT + 'px', + top: 0, height: '100%' }).id('monaco-workbench-editor-move-overlay'); overlayDiv.appendTo(this.parent); @@ -1155,8 +1192,8 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti const visibleEditorCount = this.getVisibleEditorCount(); const mouseDownEvent = new StandardMouseEvent(e); - const startX = mouseDownEvent.posx; - let oldNewLeft: number = null; + const startPos = this.layoutVertically ? mouseDownEvent.posx : mouseDownEvent.posy; + let oldNewPos: number = null; this.silos[position].addClass('drag'); this.parent.addClass('drag'); @@ -1166,102 +1203,102 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti DOM.EventHelper.stop(e, false); const mouseMoveEvent = new StandardMouseEvent(e); - const diffX = mouseMoveEvent.posx - startX; - let newLeft: number = null; + const diffPos = (this.layoutVertically ? mouseMoveEvent.posx : mouseMoveEvent.posy) - startPos; + let newPos: number = null; - if (Math.abs(diffX) > 5) { + if (Math.abs(diffPos) > 5) { wasDragged = true; } switch (position) { - // [ ! ]|[ ]: Moves only to the right but not outside of dimension width to the right - case Position.LEFT: { - newLeft = Math.max(-1 /* 1px border accomodation */, Math.min(diffX, this.dimension.width - this.siloWidths[Position.LEFT])); + // [ ! ]|[ ]: Moves only to the right/bottom but not outside of dimension to the right/bottom + case Position.ONE: { + newPos = Math.max(0, Math.min(diffPos, this.totalSize - this.silosSize[Position.ONE])); break; } - case Position.CENTER: { + case Position.TWO: { - // [ ]|[ ! ]: Moves only to the left but not outside of dimension width to the left + // [ ]|[ ! ]: Moves only to the left/top but not outside of dimension to the left/top if (visibleEditorCount === 2) { - newLeft = Math.min(this.siloWidths[Position.LEFT], Math.max(-1 /* 1px border accomodation */, this.siloWidths[Position.LEFT] + diffX)); + newPos = Math.min(this.silosSize[Position.ONE], Math.max(0, this.silosSize[Position.ONE] + diffPos)); } - // [ ]|[ ! ]|[ ]: Moves to left and right but not outside of dimensions width on both sides + // [ ]|[ ! ]|[ ]: Moves to left/top and right/bottom but not outside of dimensions on both sides else { - newLeft = Math.min(this.dimension.width - this.siloWidths[Position.CENTER], Math.max(-1 /* 1px border accomodation */, this.siloWidths[Position.LEFT] + diffX)); + newPos = Math.min(this.totalSize - this.silosSize[Position.TWO], Math.max(0, this.silosSize[Position.ONE] + diffPos)); } break; } - // [ ]|[ ]|[ ! ]: Moves to the right but not outside of dimension width on the left side - case Position.RIGHT: { - newLeft = Math.min(this.siloWidths[Position.LEFT] + this.siloWidths[Position.CENTER], Math.max(-1 /* 1px border accomodation */, this.siloWidths[Position.LEFT] + this.siloWidths[Position.CENTER] + diffX)); + // [ ]|[ ]|[ ! ]: Moves to the right/bottom but not outside of dimension on the left/top side + case Position.THREE: { + newPos = Math.min(this.silosSize[Position.ONE] + this.silosSize[Position.TWO], Math.max(0, this.silosSize[Position.ONE] + this.silosSize[Position.TWO] + diffPos)); break; } } // Return early if position did not change - if (oldNewLeft === newLeft) { + if (oldNewPos === newPos) { return; } - oldNewLeft = newLeft; + oldNewPos = newPos; // Live drag Feedback - const moveTo: Position = this.findMoveTarget(position, diffX); + const moveTo: Position = this.findMoveTarget(position, diffPos); switch (position) { - case Position.LEFT: { - if (moveTo === Position.LEFT || moveTo === null) { - this.silos[Position.CENTER].style({ left: this.siloWidths[Position.LEFT] + 'px', right: 'auto', borderLeftWidth: '1px' }); - this.silos[Position.RIGHT].style({ left: 'auto', right: 0 }); - } else if (moveTo === Position.CENTER) { - this.silos[Position.CENTER].style({ left: 0, right: 'auto', borderLeftWidth: 0 }); - this.silos[Position.CENTER].addClass('draggedunder'); - this.silos[Position.RIGHT].style({ left: 'auto', right: 0 }); - } else if (moveTo === Position.RIGHT) { - this.silos[Position.CENTER].style({ left: 0, right: 'auto' }); - this.silos[Position.RIGHT].style({ left: 'auto', right: this.siloWidths[Position.LEFT] + 'px' }); - this.silos[Position.RIGHT].addClass('draggedunder'); + case Position.ONE: { + if (moveTo === Position.ONE || moveTo === null) { + this.posSilo(Position.TWO, `${this.silosSize[Position.ONE]}px`, 'auto'); + this.posSilo(Position.THREE, 'auto', 0); + } else if (moveTo === Position.TWO) { + this.posSilo(Position.TWO, 0, 'auto'); + this.silos[Position.TWO].addClass('draggedunder'); + this.posSilo(Position.THREE, 'auto', 0); + } else if (moveTo === Position.THREE) { + this.posSilo(Position.TWO, 0, 'auto'); + this.posSilo(Position.THREE, 'auto', `${this.silosSize[Position.ONE]}px`); + this.silos[Position.THREE].addClass('draggedunder'); } break; } - case Position.CENTER: { - if (moveTo === Position.LEFT) { - this.silos[Position.LEFT].style({ left: this.siloWidths[Position.CENTER] + 'px', right: 'auto' }); - this.silos[Position.LEFT].addClass('draggedunder'); - } else if (moveTo === Position.CENTER || moveTo === null) { - this.silos[Position.LEFT].style({ left: 0, right: 'auto' }); - this.silos[Position.RIGHT].style({ left: 'auto', right: 0 }); - } else if (moveTo === Position.RIGHT) { - this.silos[Position.RIGHT].style({ left: 'auto', right: this.siloWidths[Position.CENTER] + 'px' }); - this.silos[Position.RIGHT].addClass('draggedunder'); - this.silos[Position.LEFT].style({ left: 0, right: 'auto' }); + case Position.TWO: { + if (moveTo === Position.ONE) { + this.posSilo(Position.ONE, `${this.silosSize[Position.TWO]}px`, 'auto'); + this.silos[Position.ONE].addClass('draggedunder'); + } else if (moveTo === Position.TWO || moveTo === null) { + this.posSilo(Position.ONE, 0, 'auto'); + this.posSilo(Position.THREE, 'auto', 0); + } else if (moveTo === Position.THREE) { + this.posSilo(Position.THREE, 'auto', `${this.silosSize[Position.TWO]}px`); + this.silos[Position.THREE].addClass('draggedunder'); + this.posSilo(Position.ONE, 0, 'auto'); } break; } - case Position.RIGHT: { - if (moveTo === Position.LEFT) { - this.silos[Position.LEFT].style({ left: this.siloWidths[Position.RIGHT] + 'px', right: 'auto' }); - this.silos[Position.LEFT].addClass('draggedunder'); - } else if (moveTo === Position.CENTER) { - this.silos[Position.LEFT].style({ left: 0, right: 'auto' }); - this.silos[Position.CENTER].style({ left: (this.siloWidths[Position.LEFT] + this.siloWidths[Position.RIGHT]) + 'px', right: 'auto' }); - this.silos[Position.CENTER].addClass('draggedunder'); - } else if (moveTo === Position.RIGHT || moveTo === null) { - this.silos[Position.LEFT].style({ left: 0, right: 'auto' }); - this.silos[Position.CENTER].style({ left: this.siloWidths[Position.LEFT] + 'px', right: 'auto' }); + case Position.THREE: { + if (moveTo === Position.ONE) { + this.posSilo(Position.ONE, `${this.silosSize[Position.THREE]}px`, 'auto'); + this.silos[Position.ONE].addClass('draggedunder'); + } else if (moveTo === Position.TWO) { + this.posSilo(Position.ONE, 0, 'auto'); + this.posSilo(Position.TWO, `${this.silosSize[Position.ONE] + this.silosSize[Position.THREE]}px`, 'auto'); + this.silos[Position.TWO].addClass('draggedunder'); + } else if (moveTo === Position.THREE || moveTo === null) { + this.posSilo(Position.ONE, 0, 'auto'); + this.posSilo(Position.TWO, `${this.silosSize[Position.ONE]}px`, 'auto'); } break; } } // Move the editor to provide feedback to the user and add class - if (newLeft !== null) { - this.silos[position].style({ left: newLeft + 'px' }); + if (newPos !== null) { + this.silos[position].style(this.layoutVertically ? { left: `${newPos}px` } : { top: `${newPos}px` }); this.silos[position].addClass('dragging'); this.parent.addClass('dragging'); } @@ -1283,14 +1320,15 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti this.parent.removeClass('dragging'); this.silos[position].removeClass('dragging'); POSITIONS.forEach(p => this.silos[p].removeClass('draggedunder')); - this.silos[Position.LEFT].style({ left: 0, right: 'auto' }); - this.silos[Position.CENTER].style({ left: 'auto', right: 'auto', borderLeftWidth: '1px' }); - this.silos[Position.RIGHT].style({ left: 'auto', right: 0, borderLeftWidth: '1px' }); + + this.posSilo(Position.ONE, 0, 'auto'); + this.posSilo(Position.TWO, 'auto', 'auto'); + this.posSilo(Position.THREE, 'auto', 0); // Find move target const mouseUpEvent = new StandardMouseEvent(e); - const diffX = mouseUpEvent.posx - startX; - const moveTo: Position = this.findMoveTarget(position, diffX); + const diffPos = (this.layoutVertically ? mouseUpEvent.posx : mouseUpEvent.posy) - startPos; + const moveTo: Position = this.findMoveTarget(position, diffPos); // Move to valid position if any if (moveTo !== null) { @@ -1312,64 +1350,72 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti }); } - private findMoveTarget(position: Position, diffX: number): Position { + private posSilo(pos: number, leftTop: string | number, rightBottom?: string | number): void { + if (this.layoutVertically) { + this.silos[pos].style({ left: leftTop, right: rightBottom }); + } else { + this.silos[pos].style({ top: leftTop, bottom: rightBottom }); + } + } + + private findMoveTarget(position: Position, diffPos: number): Position { const visibleEditorCount = this.getVisibleEditorCount(); switch (position) { - case Position.LEFT: { + case Position.ONE: { // [ ! ]|[] -> []|[ ! ] - if (visibleEditorCount === 2 && (diffX >= this.siloWidths[Position.LEFT] / 2 || diffX >= this.siloWidths[Position.CENTER] / 2)) { - return Position.CENTER; + if (visibleEditorCount === 2 && (diffPos >= this.silosSize[Position.ONE] / 2 || diffPos >= this.silosSize[Position.TWO] / 2)) { + return Position.TWO; } // [ ! ]|[]|[] -> []|[]|[ ! ] - if (visibleEditorCount === 3 && (diffX >= this.siloWidths[Position.LEFT] / 2 + this.siloWidths[Position.CENTER] || diffX >= this.siloWidths[Position.RIGHT] / 2 + this.siloWidths[Position.CENTER])) { - return Position.RIGHT; + if (visibleEditorCount === 3 && (diffPos >= this.silosSize[Position.ONE] / 2 + this.silosSize[Position.TWO] || diffPos >= this.silosSize[Position.THREE] / 2 + this.silosSize[Position.TWO])) { + return Position.THREE; } // [ ! ]|[]|[] -> []|[ ! ]|[] - if (visibleEditorCount === 3 && (diffX >= this.siloWidths[Position.LEFT] / 2 || diffX >= this.siloWidths[Position.CENTER] / 2)) { - return Position.CENTER; + if (visibleEditorCount === 3 && (diffPos >= this.silosSize[Position.ONE] / 2 || diffPos >= this.silosSize[Position.TWO] / 2)) { + return Position.TWO; } break; } - case Position.CENTER: { - if (visibleEditorCount === 2 && diffX > 0) { - return null; // Return early since CENTER cannot be moved to the RIGHT unless there is a RIGHT position + case Position.TWO: { + if (visibleEditorCount === 2 && diffPos > 0) { + return null; // Return early since TWO cannot be moved to the THREE unless there is a THREE position } // []|[ ! ] -> [ ! ]|[] - if (visibleEditorCount === 2 && (Math.abs(diffX) >= this.siloWidths[Position.CENTER] / 2 || Math.abs(diffX) >= this.siloWidths[Position.LEFT] / 2)) { - return Position.LEFT; + if (visibleEditorCount === 2 && (Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2 || Math.abs(diffPos) >= this.silosSize[Position.ONE] / 2)) { + return Position.ONE; } // []|[ ! ]|[] -> [ ! ]|[]|[] - if (visibleEditorCount === 3 && ((diffX < 0 && Math.abs(diffX) >= this.siloWidths[Position.CENTER] / 2) || (diffX < 0 && Math.abs(diffX) >= this.siloWidths[Position.LEFT] / 2))) { - return Position.LEFT; + if (visibleEditorCount === 3 && ((diffPos < 0 && Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2) || (diffPos < 0 && Math.abs(diffPos) >= this.silosSize[Position.ONE] / 2))) { + return Position.ONE; } // []|[ ! ]|[] -> []|[]|[ ! ] - if (visibleEditorCount === 3 && ((diffX > 0 && Math.abs(diffX) >= this.siloWidths[Position.CENTER] / 2) || (diffX > 0 && Math.abs(diffX) >= this.siloWidths[Position.RIGHT] / 2))) { - return Position.RIGHT; + if (visibleEditorCount === 3 && ((diffPos > 0 && Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2) || (diffPos > 0 && Math.abs(diffPos) >= this.silosSize[Position.THREE] / 2))) { + return Position.THREE; } break; } - case Position.RIGHT: { - if (diffX > 0) { - return null; // Return early since RIGHT cannot be moved more to the RIGHT + case Position.THREE: { + if (diffPos > 0) { + return null; // Return early since THREE cannot be moved more to the THREE } // []|[]|[ ! ] -> [ ! ]|[]|[] - if (Math.abs(diffX) >= this.siloWidths[Position.RIGHT] / 2 + this.siloWidths[Position.CENTER] || Math.abs(diffX) >= this.siloWidths[Position.LEFT] / 2 + this.siloWidths[Position.CENTER]) { - return Position.LEFT; + if (Math.abs(diffPos) >= this.silosSize[Position.THREE] / 2 + this.silosSize[Position.TWO] || Math.abs(diffPos) >= this.silosSize[Position.ONE] / 2 + this.silosSize[Position.TWO]) { + return Position.ONE; } // []|[]|[ ! ] -> []|[ ! ]|[] - if (Math.abs(diffX) >= this.siloWidths[Position.RIGHT] / 2 || Math.abs(diffX) >= this.siloWidths[Position.CENTER] / 2) { - return Position.CENTER; + if (Math.abs(diffPos) >= this.silosSize[Position.THREE] / 2 || Math.abs(diffPos) >= this.silosSize[Position.TWO] / 2) { + return Position.TWO; } break; } @@ -1379,167 +1425,172 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } private centerSash(a: Position, b: Position): void { - const sumWidth = this.siloWidths[a] + this.siloWidths[b]; - const meanWidth = sumWidth / 2; - this.siloWidths[a] = meanWidth; - this.siloWidths[b] = sumWidth - meanWidth; + const sumSize = this.silosSize[a] + this.silosSize[b]; + const meanSize = sumSize / 2; + this.silosSize[a] = meanSize; + this.silosSize[b] = sumSize - meanSize; + this.layoutContainers(); } - private onLeftSashDragStart(): void { - this.startLeftContainerWidth = this.siloWidths[Position.LEFT]; + private onSashOneDragStart(): void { + this.startSiloOneSize = this.silosSize[Position.ONE]; } - private onLeftSashDrag(e: ISashEvent): void { - let oldLeftContainerWidth = this.siloWidths[Position.LEFT]; - let newLeftContainerWidth = this.startLeftContainerWidth + e.currentX - e.startX; + private onSashOneDrag(e: ISashEvent): void { + let oldSiloOneSize = this.silosSize[Position.ONE]; + let diffSize = this.layoutVertically ? (e.currentX - e.startX) : (e.currentY - e.startY); + let newSiloOneSize = this.startSiloOneSize + diffSize; // Side-by-Side - if (this.rightSash.isHidden()) { + if (this.sashTwo.isHidden()) { - // []|[ ] : left side can not get smaller than MIN_EDITOR_WIDTH - if (newLeftContainerWidth < SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // []|[ ] : left/top side can not get smaller than the minimal editor size + if (newSiloOneSize < this.minSize) { + newSiloOneSize = this.minSize; } - // [ ]|[] : right side can not get smaller than MIN_EDITOR_WIDTH - else if (this.dimension.width - newLeftContainerWidth < SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = this.dimension.width - SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ ]|[] : right/bottom side can not get smaller than the minimal editor size + else if (this.totalSize - newSiloOneSize < this.minSize) { + newSiloOneSize = this.totalSize - this.minSize; } - // [ <-]|[ ] : left side can snap into minimized - else if (newLeftContainerWidth - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ <-]|[ ] : left/top side can snap into minimized + else if (newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) { + newSiloOneSize = this.minSize; } - // [ ]|[-> ] : right side can snap into minimized - else if (this.dimension.width - newLeftContainerWidth - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = this.dimension.width - SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ ]|[-> ] : right/bottom side can snap into minimized + else if (this.totalSize - newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) { + newSiloOneSize = this.totalSize - this.minSize; } - this.siloWidths[Position.LEFT] = newLeftContainerWidth; - this.siloWidths[Position.CENTER] = this.dimension.width - newLeftContainerWidth; + this.silosSize[Position.ONE] = newSiloOneSize; + this.silosSize[Position.TWO] = this.totalSize - newSiloOneSize; } // Side-by-Side-by-Side else { - // [!]|[ ]|[ ] : left side can not get smaller than MIN_EDITOR_WIDTH - if (newLeftContainerWidth < SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [!]|[ ]|[ ] : left/top side can not get smaller than the minimal editor size + if (newSiloOneSize < this.minSize) { + newSiloOneSize = this.minSize; } - // [ ]|[!]|[ ] : center side can not get smaller than MIN_EDITOR_WIDTH - else if (this.dimension.width - newLeftContainerWidth - this.siloWidths[Position.RIGHT] < SideBySideEditorControl.MIN_EDITOR_WIDTH) { + // [ ]|[!]|[ ] : center side can not get smaller than the minimal editor size + else if (this.totalSize - newSiloOneSize - this.silosSize[Position.THREE] < this.minSize) { - // [ ]|[ ]|[!] : right side can not get smaller than MIN_EDITOR_WIDTH - if (this.dimension.width - newLeftContainerWidth - this.siloWidths[Position.CENTER] < SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = this.dimension.width - (2 * SideBySideEditorControl.MIN_EDITOR_WIDTH); - this.siloWidths[Position.CENTER] = this.siloWidths[Position.RIGHT] = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ ]|[ ]|[!] : right/bottom side can not get smaller than the minimal editor size + if (this.totalSize - newSiloOneSize - this.silosSize[Position.TWO] < this.minSize) { + newSiloOneSize = this.totalSize - (2 * this.minSize); + this.silosSize[Position.TWO] = this.silosSize[Position.THREE] = this.minSize; } - // [ ]|[ ]|[-> ] : right side can snap into minimized - else if (this.dimension.width - newLeftContainerWidth - this.siloWidths[Position.CENTER] - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - this.siloWidths[Position.RIGHT] = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ ]|[ ]|[-> ] : right/bottom side can snap into minimized + else if (this.totalSize - newSiloOneSize - this.silosSize[Position.TWO] - this.snapToMinimizeThresholdSize <= this.minSize) { + this.silosSize[Position.THREE] = this.minSize; } - // [ ]|[ ]|[ ] : right side shrinks + // [ ]|[ ]|[ ] : right/bottom side shrinks else { - this.siloWidths[Position.RIGHT] = this.siloWidths[Position.RIGHT] - (newLeftContainerWidth - oldLeftContainerWidth); + this.silosSize[Position.THREE] = this.silosSize[Position.THREE] - (newSiloOneSize - oldSiloOneSize); } - this.rightSash.layout(); + this.sashTwo.layout(); } - // [ <-]|[ ]|[ ] : left side can snap into minimized - else if (newLeftContainerWidth - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ <-]|[ ]|[ ] : left/top side can snap into minimized + else if (newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) { + newSiloOneSize = this.minSize; } // [ ]|[-> ]|[ ] : center side can snap into minimized - else if (this.dimension.width - this.siloWidths[Position.RIGHT] - newLeftContainerWidth - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newLeftContainerWidth = this.dimension.width - this.siloWidths[Position.RIGHT] - SideBySideEditorControl.MIN_EDITOR_WIDTH; + else if (this.totalSize - this.silosSize[Position.THREE] - newSiloOneSize - this.snapToMinimizeThresholdSize <= this.minSize) { + newSiloOneSize = this.totalSize - this.silosSize[Position.THREE] - this.minSize; } - this.siloWidths[Position.LEFT] = newLeftContainerWidth; - this.siloWidths[Position.CENTER] = this.dimension.width - this.siloWidths[Position.LEFT] - this.siloWidths[Position.RIGHT]; + this.silosSize[Position.ONE] = newSiloOneSize; + this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE] - this.silosSize[Position.THREE]; } // Pass on to containers this.layoutContainers(); } - private onLeftSashDragEnd(): void { - this.leftSash.layout(); - this.rightSash.layout(); // Moving left sash might have also moved right sash, so layout() both + private onSashOneDragEnd(): void { + this.sashOne.layout(); + this.sashTwo.layout(); // Moving sash one might have also moved sash two, so layout() both this.focusNextNonMinimized(); } - private onLeftSashReset(): void { - this.centerSash(Position.LEFT, Position.CENTER); - this.leftSash.layout(); + private onSashOneReset(): void { + this.centerSash(Position.ONE, Position.TWO); + this.sashOne.layout(); } - private onRightSashDragStart(): void { - this.startRightContainerWidth = this.siloWidths[Position.RIGHT]; + private onSashTwoDragStart(): void { + this.startSiloThreeSize = this.silosSize[Position.THREE]; } - private onRightSashDrag(e: ISashEvent): void { - let oldRightContainerWidth = this.siloWidths[Position.RIGHT]; - let newRightContainerWidth = this.startRightContainerWidth - e.currentX + e.startX; + private onSashTwoDrag(e: ISashEvent): void { + let oldSiloThreeSize = this.silosSize[Position.THREE]; + let diffSize = this.layoutVertically ? (-e.currentX + e.startX) : (-e.currentY + e.startY); + let newSiloThreeSize = this.startSiloThreeSize + diffSize; - // [ ]|[ ]|[!] : right side can not get smaller than MIN_EDITOR_WIDTH - if (newRightContainerWidth < SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newRightContainerWidth = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ ]|[ ]|[!] : right/bottom side can not get smaller than the minimal editor size + if (newSiloThreeSize < this.minSize) { + newSiloThreeSize = this.minSize; } - // [ ]|[!]|[ ] : center side can not get smaller than MIN_EDITOR_WIDTH - else if (this.dimension.width - newRightContainerWidth - this.siloWidths[Position.LEFT] < SideBySideEditorControl.MIN_EDITOR_WIDTH) { + // [ ]|[!]|[ ] : center side can not get smaller than the minimal editor size + else if (this.totalSize - newSiloThreeSize - this.silosSize[Position.ONE] < this.minSize) { - // [!]|[ ]|[ ] : left side can not get smaller than MIN_EDITOR_WIDTH - if (this.dimension.width - newRightContainerWidth - this.siloWidths[Position.CENTER] < SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newRightContainerWidth = this.dimension.width - (2 * SideBySideEditorControl.MIN_EDITOR_WIDTH); - this.siloWidths[Position.LEFT] = this.siloWidths[Position.CENTER] = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [!]|[ ]|[ ] : left/top side can not get smaller than the minimal editor size + if (this.totalSize - newSiloThreeSize - this.silosSize[Position.TWO] < this.minSize) { + newSiloThreeSize = this.totalSize - (2 * this.minSize); + this.silosSize[Position.ONE] = this.silosSize[Position.TWO] = this.minSize; } - // [ <-]|[ ]|[ ] : left side can snap into minimized - else if (this.dimension.width - newRightContainerWidth - this.siloWidths[Position.CENTER] - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - this.siloWidths[Position.LEFT] = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ <-]|[ ]|[ ] : left/top side can snap into minimized + else if (this.totalSize - newSiloThreeSize - this.silosSize[Position.TWO] - this.snapToMinimizeThresholdSize <= this.minSize) { + this.silosSize[Position.ONE] = this.minSize; } - // [ ]|[ ]|[ ] : left side shrinks + // [ ]|[ ]|[ ] : left/top side shrinks else { - this.siloWidths[Position.LEFT] = this.siloWidths[Position.LEFT] - (newRightContainerWidth - oldRightContainerWidth); + this.silosSize[Position.ONE] = this.silosSize[Position.ONE] - (newSiloThreeSize - oldSiloThreeSize); } - this.leftSash.layout(); + this.sashOne.layout(); } - // [ ]|[ ]|[-> ] : right side can snap into minimized - else if (newRightContainerWidth - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newRightContainerWidth = SideBySideEditorControl.MIN_EDITOR_WIDTH; + // [ ]|[ ]|[-> ] : right/bottom side can snap into minimized + else if (newSiloThreeSize - this.snapToMinimizeThresholdSize <= this.minSize) { + newSiloThreeSize = this.minSize; } // [ ]|[ <-]|[ ] : center side can snap into minimized - else if (this.dimension.width - this.siloWidths[Position.LEFT] - newRightContainerWidth - SideBySideEditorControl.SNAP_TO_MINIMIZED_THRESHOLD <= SideBySideEditorControl.MIN_EDITOR_WIDTH) { - newRightContainerWidth = this.dimension.width - this.siloWidths[Position.LEFT] - SideBySideEditorControl.MIN_EDITOR_WIDTH; + else if (this.totalSize - this.silosSize[Position.ONE] - newSiloThreeSize - this.snapToMinimizeThresholdSize <= this.minSize) { + newSiloThreeSize = this.totalSize - this.silosSize[Position.ONE] - this.minSize; } - this.siloWidths[Position.RIGHT] = newRightContainerWidth; - this.siloWidths[Position.CENTER] = this.dimension.width - this.siloWidths[Position.LEFT] - this.siloWidths[Position.RIGHT]; + this.silosSize[Position.THREE] = newSiloThreeSize; + this.silosSize[Position.TWO] = this.totalSize - this.silosSize[Position.ONE] - this.silosSize[Position.THREE]; this.layoutContainers(); } - private onRightSashDragEnd(): void { - this.leftSash.layout(); // Moving right sash might have also moved left sash, so layout() both - this.rightSash.layout(); + private onSashTwoDragEnd(): void { + this.sashOne.layout(); // Moving sash one might have also moved sash two, so layout() both + this.sashTwo.layout(); + this.focusNextNonMinimized(); } - private onRightSashReset(): void { - this.centerSash(Position.CENTER, Position.RIGHT); - this.rightSash.layout(); + private onSashTwoReset(): void { + this.centerSash(Position.TWO, Position.THREE); + + this.sashTwo.layout(); } public getVerticalSashTop(sash: Sash): number { @@ -1547,13 +1598,25 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } public getVerticalSashLeft(sash: Sash): number { - return sash === this.leftSash ? this.siloWidths[Position.LEFT] : this.siloWidths[Position.CENTER] + this.siloWidths[Position.LEFT]; + return sash === this.sashOne ? this.silosSize[Position.ONE] : this.silosSize[Position.TWO] + this.silosSize[Position.ONE]; } public getVerticalSashHeight(sash: Sash): number { return this.dimension.height; } + public getHorizontalSashTop(sash: Sash): number { + return sash === this.sashOne ? this.silosSize[Position.ONE] : this.silosSize[Position.TWO] + this.silosSize[Position.ONE]; + } + + public getHorizontalSashLeft(sash: Sash): number { + return 0; + } + + public getHorizontalSashWidth(sash: Sash): number { + return this.dimension.width; + } + public isDragging(): boolean { return this.dragging; } @@ -1578,66 +1641,67 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } // Apply to visible editors - let totalWidth = 0; + let totalSize = 0; // Set preferred dimensions based on ratio to previous dimenions + const oldTotalSize = this.layoutVertically ? oldDimension.width : oldDimension.height; POSITIONS.forEach(position => { if (this.visibleEditors[position]) { - // Keep minimized editors in tact by not letting them grow if we have width to give - if (this.siloWidths[position] !== SideBySideEditorControl.MIN_EDITOR_WIDTH) { - let sashWidthRatio: number; + // Keep minimized editors in tact by not letting them grow if we have size to give + if (this.silosSize[position] !== this.minSize) { + let siloSizeRatio: number; // We have some stored initial ratios when the editor was restored on startup // Use those ratios over anything else but only once. - if (this.siloInitialRatios && types.isNumber(this.siloInitialRatios[position])) { - sashWidthRatio = this.siloInitialRatios[position]; - delete this.siloInitialRatios[position]; // dont use again + if (this.silosInitialRatio && types.isNumber(this.silosInitialRatio[position])) { + siloSizeRatio = this.silosInitialRatio[position]; + delete this.silosInitialRatio[position]; // dont use again } else { - sashWidthRatio = this.siloWidths[position] / oldDimension.width; + siloSizeRatio = this.silosSize[position] / oldTotalSize; } - this.siloWidths[position] = Math.max(Math.round(this.dimension.width * sashWidthRatio), SideBySideEditorControl.MIN_EDITOR_WIDTH); + this.silosSize[position] = Math.max(Math.round(this.totalSize * siloSizeRatio), this.minSize); } - totalWidth += this.siloWidths[position]; + totalSize += this.silosSize[position]; } }); - // Compensate for overflow either through rounding error or min editor width - if (totalWidth > 0) { - let overflow = totalWidth - this.dimension.width; + // Compensate for overflow either through rounding error or min editor size + if (totalSize > 0) { + let overflow = totalSize - this.totalSize; - // We have width to give + // We have size to give if (overflow < 0) { - // Find the first position from left to right that is not minimized - // to give width. This ensures that minimized editors are left like + // Find the first position from left/top to right/bottom that is not minimized + // to give size. This ensures that minimized editors are left like // that if the user chose this layout. let positionToGive: Position = null; POSITIONS.forEach(position => { - if (this.visibleEditors[position] && positionToGive === null && this.siloWidths[position] !== SideBySideEditorControl.MIN_EDITOR_WIDTH) { + if (this.visibleEditors[position] && positionToGive === null && this.silosSize[position] !== this.minSize) { positionToGive = position; } }); if (positionToGive === null) { - positionToGive = Position.LEFT; // maybe all are minimized, so give LEFT the extra width + positionToGive = Position.ONE; // maybe all are minimized, so give ONE the extra size } - this.siloWidths[positionToGive] -= overflow; + this.silosSize[positionToGive] -= overflow; } - // We have width to take + // We have size to take else if (overflow > 0) { POSITIONS.forEach(position => { - const maxCompensation = this.siloWidths[position] - SideBySideEditorControl.MIN_EDITOR_WIDTH; + const maxCompensation = this.silosSize[position] - this.minSize; if (maxCompensation >= overflow) { - this.siloWidths[position] -= overflow; + this.silosSize[position] -= overflow; overflow = 0; } else if (maxCompensation > 0) { const compensation = overflow - maxCompensation; - this.siloWidths[position] -= compensation; + this.silosSize[position] -= compensation; overflow -= compensation; } }); @@ -1645,8 +1709,8 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } // Sash positioning - this.leftSash.layout(); - this.rightSash.layout(); + this.sashOne.layout(); + this.sashTwo.layout(); // Pass on to Editor Containers this.layoutContainers(); @@ -1656,14 +1720,16 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // Layout containers POSITIONS.forEach(position => { - this.silos[position].size(this.siloWidths[position], this.dimension.height); + const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width; + const siloHeight = this.layoutVertically ? this.dimension.height : this.silosSize[position]; + + this.silos[position].size(siloWidth, siloHeight); }); - // Position center depending on visibility of right hand editor - if (this.visibleEditors[Position.RIGHT]) { - this.silos[Position.CENTER].position(null, this.siloWidths[Position.RIGHT]); + if (this.layoutVertically) { + this.silos[Position.TWO].position(0, null, null, this.silosSize[Position.ONE]); } else { - this.silos[Position.CENTER].position(null, this.dimension.width - this.siloWidths[Position.LEFT] - this.siloWidths[Position.CENTER]); + this.silos[Position.TWO].position(this.silosSize[Position.ONE], null, null, 0); } // Visibility @@ -1687,9 +1753,12 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } private layoutEditor(position: Position): void { - const editorWidth = this.siloWidths[position]; - if (editorWidth && this.visibleEditors[position]) { - this.visibleEditors[position].layout(new Dimension(editorWidth, this.dimension.height - SideBySideEditorControl.EDITOR_TITLE_HEIGHT)); + const editorSize = this.silosSize[position]; + if (editorSize && this.visibleEditors[position]) { + const editorWidth = this.layoutVertically ? editorSize : this.dimension.width; + const editorHeight = (this.layoutVertically ? this.dimension.height : this.silosSize[position]) - SideBySideEditorControl.EDITOR_TITLE_HEIGHT; + + this.visibleEditors[position].layout(new Dimension(editorWidth, editorHeight)); } } @@ -1749,8 +1818,8 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti }); // Sash - this.leftSash.dispose(); - this.rightSash.dispose(); + this.sashOne.dispose(); + this.sashTwo.dispose(); // Destroy Container this.silos.forEach(silo => { diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 27acb0ed875..bea6ac51ac4 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -858,6 +858,7 @@ export interface IWorkbenchEditorConfiguration { enablePreview: boolean; enablePreviewFromQuickOpen: boolean; openPositioning: 'left' | 'right' | 'first' | 'last'; + sideBySideLayout: 'vertical' | 'horizontal'; } }; } diff --git a/src/vs/workbench/electron-browser/integration.ts b/src/vs/workbench/electron-browser/integration.ts index ba1f70c1281..c5b11e1d9ad 100644 --- a/src/vs/workbench/electron-browser/integration.ts +++ b/src/vs/workbench/electron-browser/integration.ts @@ -257,7 +257,7 @@ export class ElectronIntegration { return this.editorService.openEditors(resources.map((r, index) => { return { input: r, - position: activeEditor ? activeEditor.position : Position.LEFT + position: activeEditor ? activeEditor.position : Position.ONE }; })); }); diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 3885f87c954..fde40a8bba2 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -74,6 +74,12 @@ configurationRegistry.registerConfiguration({ 'title': nls.localize('workbenchConfigurationTitle', "Workbench"), 'type': 'object', 'properties': { + 'workbench.editor.sideBySideLayout': { + 'type': 'string', + 'enum': ['vertical', 'horizontal'], + 'default': 'vertical', + 'description': nls.localize('sideBySideLayout', "Controls if side by side editors should layout horizontally or vertically.") + }, 'workbench.editor.showTabs': { 'type': 'boolean', 'description': nls.localize('showEditorTabs', "Controls if opened editors should show in tabs or not."), diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 8fc45ce1e22..0b96c5e4235 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -20,6 +20,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Registry } from 'vs/platform/platform'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { IOptions } from 'vs/workbench/common/options'; +import { Position as EditorPosition } from 'vs/platform/editor/common/editor'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorRegistry, Extensions as EditorExtensions, TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; @@ -248,7 +249,7 @@ export class Workbench implements IPartService { return { input: inputWithOptions.input, options: inputWithOptions.options, - position: Position.LEFT + position: EditorPosition.ONE }; }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/dependenciesViewer.ts b/src/vs/workbench/parts/extensions/electron-browser/dependenciesViewer.ts index 50b78944d67..6df43cc01be 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/dependenciesViewer.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/dependenciesViewer.ts @@ -217,7 +217,7 @@ class OpenExtensionToSideAction extends Action { private updateEnablement(): void { const activeEditor = this.editorService.getActiveEditor(); - this.enabled = (!activeEditor || activeEditor.position !== Position.RIGHT); + this.enabled = (!activeEditor || activeEditor.position !== Position.THREE); } run(context: { extension: IExtension }): TPromise { diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 3cbb475730d..a25a19f226f 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -1175,7 +1175,7 @@ export class OpenToSideAction extends Action { private updateEnablement(): void { const activeEditor = this.editorService.getActiveEditor(); - this.enabled = (!activeEditor || activeEditor.position !== Position.RIGHT); + this.enabled = (!activeEditor || activeEditor.position !== Position.THREE); } public run(): TPromise { diff --git a/src/vs/workbench/parts/files/browser/media/fileactions.css b/src/vs/workbench/parts/files/browser/media/fileactions.css index f1acdf53156..d4f12e7e53d 100644 --- a/src/vs/workbench/parts/files/browser/media/fileactions.css +++ b/src/vs/workbench/parts/files/browser/media/fileactions.css @@ -57,13 +57,22 @@ background: url('CollapseAll_inverse.svg') center center no-repeat; } -.monaco-workbench .quick-open-sidebyside { - background-image: url('SplitEditor.svg'); +.monaco-workbench .quick-open-sidebyside-vertical { + background-image: url('split-editor-vertical.svg'); } -.vs-dark .monaco-workbench .quick-open-sidebyside, -.hc-black .monaco-workbench .quick-open-sidebyside { - background-image: url('SplitEditor_inverse.svg'); +.vs-dark .monaco-workbench .quick-open-sidebyside-vertical, +.hc-black .monaco-workbench .quick-open-sidebyside-vertical { + background-image: url('split-editor-vertical-inverse.svg'); +} + +.monaco-workbench .quick-open-sidebyside-horizontal { + background-image: url('split-editor-horizontal.svg'); +} + +.vs-dark .monaco-workbench .quick-open-sidebyside-horizontal, +.hc-black .monaco-workbench .quick-open-sidebyside-horizontal { + background-image: url('split-editor-horizontal-inverse.svg'); } .monaco-workbench .conflict-editor-action.accept-changes { diff --git a/src/vs/workbench/parts/files/browser/media/split-editor-horizontal-inverse.svg b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal-inverse.svg new file mode 100644 index 00000000000..67a2b803962 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/split-editor-horizontal.svg b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal.svg new file mode 100644 index 00000000000..48bc38ae391 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/SplitEditor_inverse.svg b/src/vs/workbench/parts/files/browser/media/split-editor-vertical-inverse.svg similarity index 100% rename from src/vs/workbench/parts/files/browser/media/SplitEditor_inverse.svg rename to src/vs/workbench/parts/files/browser/media/split-editor-vertical-inverse.svg diff --git a/src/vs/workbench/parts/files/browser/media/SplitEditor.svg b/src/vs/workbench/parts/files/browser/media/split-editor-vertical.svg similarity index 70% rename from src/vs/workbench/parts/files/browser/media/SplitEditor.svg rename to src/vs/workbench/parts/files/browser/media/split-editor-vertical.svg index 6eccccfa00b..3eeaf7c5362 100644 --- a/src/vs/workbench/parts/files/browser/media/SplitEditor.svg +++ b/src/vs/workbench/parts/files/browser/media/split-editor-vertical.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/browser/views/openEditorsView.ts index 662b2d2c187..94068d3558c 100644 --- a/src/vs/workbench/parts/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/browser/views/openEditorsView.ts @@ -25,6 +25,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletSer import { Renderer, DataSource, Controller, AccessibilityProvider, ActionProvider, OpenEditor, DragAndDrop } from 'vs/workbench/parts/files/browser/views/openEditorsViewer'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { CloseAllEditorsAction } from 'vs/workbench/browser/parts/editor/editorActions'; +import { ToggleEditorLayoutAction } from 'vs/workbench/browser/actions/toggleEditorLayout'; const $ = dom.$; @@ -77,6 +78,7 @@ export class OpenEditorsView extends AdaptiveCollapsibleViewletView { public getActions(): IAction[] { return [ + this.instantiationService.createInstance(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL), this.instantiationService.createInstance(SaveAllAction, SaveAllAction.ID, SaveAllAction.LABEL), this.instantiationService.createInstance(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL) ]; diff --git a/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts b/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts index 47a3caccbc8..b0bd17d7111 100644 --- a/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts +++ b/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts @@ -104,7 +104,7 @@ export class DirtyFilesTracker implements IWorkbenchContribution { this.pendingDirtyResources = []; const activeEditor = this.editorService.getActiveEditor(); - const activePosition = activeEditor ? activeEditor.position : Position.LEFT; + const activePosition = activeEditor ? activeEditor.position : Position.ONE; // Open this.editorService.openEditors(dirtyNotOpenedResources.map(resource => { diff --git a/src/vs/workbench/services/group/common/groupService.ts b/src/vs/workbench/services/group/common/groupService.ts index f71a46f7f1b..01b4de6b713 100644 --- a/src/vs/workbench/services/group/common/groupService.ts +++ b/src/vs/workbench/services/group/common/groupService.ts @@ -12,7 +12,7 @@ import Event from 'vs/base/common/event'; export enum GroupArrangement { MINIMIZE_OTHERS, - EVEN_WIDTH + EVEN } export const IEditorGroupService = createDecorator('editorGroupService'); diff --git a/src/vs/workbench/test/browser/services.test.ts b/src/vs/workbench/test/browser/services.test.ts index 01dc2a40b26..2a651ef71a7 100644 --- a/src/vs/workbench/test/browser/services.test.ts +++ b/src/vs/workbench/test/browser/services.test.ts @@ -290,7 +290,7 @@ suite('Workbench UI Services', () => { assert(service.getVisibleEditors()[0] === editor); }); - service.openEditor(activeInput, null, Position.LEFT).then((editor) => { + service.openEditor(activeInput, null, Position.ONE).then((editor) => { assert.strictEqual(openedEditorInput, activeInput); assert.strictEqual(openedEditorOptions, null); assert.strictEqual(editor, activeEditor); diff --git a/src/vs/workbench/test/common/editor/editorStacksModel.test.ts b/src/vs/workbench/test/common/editor/editorStacksModel.test.ts index 75f713806ba..d0cc082ff8d 100644 --- a/src/vs/workbench/test/common/editor/editorStacksModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorStacksModel.test.ts @@ -296,9 +296,9 @@ suite('Editor Stacks Model', () => { const group2 = model.openGroup('second'); const group3 = model.openGroup('third'); - assert.equal(Position.LEFT, model.positionOfGroup(group1)); - assert.equal(Position.CENTER, model.positionOfGroup(group2)); - assert.equal(Position.RIGHT, model.positionOfGroup(group3)); + assert.equal(Position.ONE, model.positionOfGroup(group1)); + assert.equal(Position.TWO, model.positionOfGroup(group2)); + assert.equal(Position.THREE, model.positionOfGroup(group3)); }); test('Groups - Rename Group', function () { diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 8c5d2b08734..44e08cbac2e 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -23,6 +23,7 @@ import 'vs/platform/actions/browser/menusExtensionPoint'; import 'vs/workbench/browser/actions/toggleStatusbarVisibility'; import 'vs/workbench/browser/actions/toggleSidebarVisibility'; import 'vs/workbench/browser/actions/toggleSidebarPosition'; +import 'vs/workbench/browser/actions/toggleEditorLayout'; import 'vs/workbench/browser/actions/openSettings'; import 'vs/workbench/browser/actions/configureLocale';