Skip to content

Commit

Permalink
Merge pull request #52064 from Microsoft/isidorn/centeredLayout
Browse files Browse the repository at this point in the history
Centered editor layout
  • Loading branch information
isidorn authored Jun 19, 2018
2 parents 04cc579 + 8527801 commit 6a1ca40
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 39 deletions.
8 changes: 8 additions & 0 deletions src/vs/base/browser/ui/centered/centeredViewLayout.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

.monaco-workbench > .part.editor > .content .centered-view-layout {
height: 100%;
}
132 changes: 132 additions & 0 deletions src/vs/base/browser/ui/centered/centeredViewLayout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import 'vs/css!./centeredViewLayout';

import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview';
import { $ } from 'vs/base/browser/dom';
import { Event, mapEvent, anyEvent } from 'vs/base/common/event';
import { IView } from 'vs/base/browser/ui/grid/gridview';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';

function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView {
return {
element: view.element,
maximumSize: view.maximumWidth,
minimumSize: view.minimumWidth,
onDidChange: mapEvent(view.onDidChange, widthAndHeight => widthAndHeight && widthAndHeight.width),
layout: size => view.layout(size, getHeight())
};
}

export interface CenteredViewState {
leftMarginRatio: number;
rightMarginRatio: number;
}

const GOLDEN_RATIO = {
leftMarginRatio: 0.1909,
rightMarginRatio: 0.1909
};

export class CenteredViewLayout {

private splitView: SplitView;
private element: HTMLElement;
private width: number = 0;
private height: number = 0;
private style: ISplitViewStyles;
private didLayout = false;
private splitViewDisposable: IDisposable[] = [];

constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = GOLDEN_RATIO) {
this.container.appendChild(this.view.element);
}

layout(width: number, height: number): void {
this.width = width;
this.height = height;
if (this.splitView) {
this.splitView.layout(width);
if (!this.didLayout) {
this.splitView.resizeView(0, this.state.leftMarginRatio * this.width);
this.splitView.resizeView(2, this.state.rightMarginRatio * this.width);
}
} else {
this.view.layout(width, height);
}
this.didLayout = true;
}

isActive(): boolean {
return !!this.splitView;
}

styles(style: ISplitViewStyles): void {
this.style = style;
if (this.splitView) {
this.splitView.style(this.style);
}
}

resetView(view: IView): void {
this.view = view;
if (this.splitView) {
const size = this.splitView.getViewSize(1);
this.splitView.removeView(1);
this.splitView.addView(toSplitViewView(this.view, () => this.height), size, 1);
this.splitView.distributeViewSizes();
}
}

activate(active: boolean): void {
if (active === !!this.splitView) {
return;
}

if (active) {
this.element = $('.centered-view-layout');
this.container.removeChild(this.view.element);
this.container.appendChild(this.element);
this.splitView = new SplitView(this.element, {
inverseAltBehavior: true,
orientation: Orientation.HORIZONTAL,
styles: this.style
});

const onDidSizesChange = anyEvent(this.splitView.onDidSashChange, this.splitView.onDidSashReset);
this.splitViewDisposable.push(onDidSizesChange(() => {
this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width;
this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width;
}));

this.splitView.layout(this.width);

const getEmptyView = () => ({
element: $('.centered-layout-margin'),
layout: () => undefined,
minimumSize: 40,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
});

this.splitView.addView(toSplitViewView(this.view, () => this.height), 0);
this.splitView.addView(getEmptyView(), this.state.leftMarginRatio * this.width, 0);
this.splitView.addView(getEmptyView(), this.state.rightMarginRatio * this.width, 2);
} else {
this.splitViewDisposable = dispose(this.splitViewDisposable);
this.splitView.dispose();
this.splitView = undefined;
this.container.removeChild(this.element);
this.container.appendChild(this.view.element);
}
}

dispose(): void {
if (this.splitView) {
this.splitView.dispose();
}
}
}
42 changes: 38 additions & 4 deletions src/vs/workbench/browser/parts/editor/editorPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/
import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTarget';
import { localize } from 'vs/nls';
import { Color } from 'vs/base/common/color';
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
import { IView } from 'vs/base/browser/ui/grid/gridview';

interface IEditorPartUIState {
serializedGrid: ISerializedGrid;
Expand All @@ -46,6 +48,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
_serviceBrand: any;

private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.state';
private static readonly EDITOR_PART_CENTERED_VIEW_STORAGE_KEY = 'editorpart.centeredview';

//#region Events

Expand Down Expand Up @@ -77,6 +80,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
private _preferredSize: Dimension;

private memento: object;
private globalMemento: object;

private _partOptions: IEditorPartOptions;

private _activeGroup: IEditorGroupView;
Expand All @@ -90,6 +95,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
private whenRestoredComplete: TValueCallback<void>;

private previousUIState: IEditorPartUIState;
private centeredViewLayout: CenteredViewLayout;

constructor(
id: string,
Expand All @@ -106,6 +112,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor

this._partOptions = getEditorPartOptions(this.configurationService.getValue<IWorkbenchEditorConfiguration>());
this.memento = this.getMemento(this.storageService, Scope.WORKSPACE);
this.globalMemento = this.getMemento(this.storageService, Scope.GLOBAL);

this._whenRestored = new TPromise(resolve => {
this.whenRestoredComplete = resolve;
Expand Down Expand Up @@ -699,7 +706,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
protected updateStyles(): void {
this.container.style.backgroundColor = this.getColor(editorBackground);

this.gridWidget.style({ separatorBorder: this.gridSeparatorBorder });
const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder };
this.gridWidget.style(separatorBorderStyle);
this.centeredViewLayout.styles(separatorBorderStyle);
}

createContentArea(parent: HTMLElement): HTMLElement {
Expand All @@ -709,15 +718,36 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
addClass(this.container, 'content');
parent.appendChild(this.container);

// Grid control
// Grid control with center layout
this.doCreateGridControl();
this.centeredViewLayout = new CenteredViewLayout(this.container, this.getGridAsView(), this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]);

// Drop support
this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container));

return this.container;
}

private getGridAsView(): IView {
return {
element: this.gridWidget.element,
layout: (width, height) => this.gridWidget.layout(width, height),
minimumWidth: this.gridWidget.minimumWidth,
maximumWidth: this.gridWidget.maximumWidth,
minimumHeight: this.gridWidget.minimumHeight,
maximumHeight: this.gridWidget.minimumHeight,
onDidChange: this.gridWidget.onDidChange
};
}

centerLayout(active: boolean): void {
this.centeredViewLayout.activate(active);
}

isLayoutCentered(): boolean {
return this.centeredViewLayout.isActive();
}

private doCreateGridControl(): void {

// Grid Widget (with previous UI state)
Expand Down Expand Up @@ -809,7 +839,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.gridWidget = gridWidget;

if (gridWidget) {
this.container.appendChild(gridWidget.element);
if (this.centeredViewLayout) {
this.centeredViewLayout.resetView(this.getGridAsView());
}
this._onDidSizeConstraintsChange.input = gridWidget.onDidChange;
}

Expand Down Expand Up @@ -967,7 +999,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor

// Layout Grid
try {
this.gridWidget.layout(this.dimension.width, this.dimension.height);
this.centeredViewLayout.layout(this.dimension.width, this.dimension.height);
} catch (error) {
this.gridError(error);
}
Expand All @@ -992,6 +1024,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState;
}
}
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = this.centeredViewLayout.state;

// Forward to all groups
this.groupViews.forEach(group => group.shutdown());
Expand All @@ -1009,6 +1042,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
if (this.gridWidget) {
this.gridWidget.dispose();
}
this.centeredViewLayout.dispose();

super.dispose();
}
Expand Down
41 changes: 6 additions & 35 deletions src/vs/workbench/electron-browser/workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { PreferencesService } from 'vs/workbench/services/preferences/browser/preferencesService';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService, GroupDirection, preferredSideBySideGroupDirection, GroupOrientation } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, GroupDirection, preferredSideBySideGroupDirection } from 'vs/workbench/services/group/common/editorGroupsService';
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
import { IExtensionUrlHandler, ExtensionUrlHandler } from 'vs/platform/url/electron-browser/inactiveExtensionUrlHandler';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
Expand Down Expand Up @@ -227,7 +227,6 @@ export class Workbench extends Disposable implements IPartService {
private panelHidden: boolean;
private menubarHidden: boolean;
private zenMode: IZenMode;
private centeredEditorLayoutActive: boolean;
private fontAliasing: FontAliasingOption;
private hasInitialFilesToOpen: boolean;

Expand Down Expand Up @@ -714,7 +713,7 @@ export class Workbench extends Disposable implements IPartService {

// Restore Forced Editor Center Mode
if (this.storageService.getBoolean(Workbench.centeredEditorLayoutActiveStorageKey, StorageScope.WORKSPACE, false)) {
this.centeredEditorLayoutActive = true;
this.centerEditorLayout(true);
}

const onRestored = (error?: Error): IWorkbenchStartedInfo => {
Expand Down Expand Up @@ -872,9 +871,6 @@ export class Workbench extends Disposable implements IPartService {
wasPanelVisible: false,
transitionDisposeables: []
};

// Centered Editor Layout
this.centeredEditorLayoutActive = false;
}

private setPanelPositionFromStorageOrConfig() {
Expand Down Expand Up @@ -1293,39 +1289,14 @@ export class Workbench extends Disposable implements IPartService {
}

isEditorLayoutCentered(): boolean {
return this.centeredEditorLayoutActive;
return this.editorPart.isLayoutCentered();
}

// TODO@ben support centered editor layout using empty groups or not? functionality missing:
// - resize sashes left and right in sync
// - IEditorInput.supportsCenteredEditorLayout() no longer supported
// - should we just allow to enter layout even if groups > 1? what does it then mean to be
// actively in centered editor layout though?
centerEditorLayout(active: boolean, skipLayout?: boolean): void {
this.centeredEditorLayoutActive = active;
this.storageService.store(Workbench.centeredEditorLayoutActiveStorageKey, this.centeredEditorLayoutActive, StorageScope.WORKSPACE);
this.storageService.store(Workbench.centeredEditorLayoutActiveStorageKey, active, StorageScope.WORKSPACE);

// Enter Centered Editor Layout
if (active) {
if (this.editorGroupService.count === 1) {
const activeGroup = this.editorGroupService.activeGroup;
this.editorGroupService.addGroup(activeGroup, GroupDirection.LEFT);
this.editorGroupService.addGroup(activeGroup, GroupDirection.RIGHT);

this.editorGroupService.applyLayout({ groups: [{ size: 0.2 }, { size: 0.6 }, { size: 0.2 }], orientation: GroupOrientation.HORIZONTAL });
}
}

// Leave Centered Editor Layout
else {
if (this.editorGroupService.count === 3) {
this.editorGroupService.groups.forEach(group => {
if (group.count === 0) {
this.editorGroupService.removeGroup(group);
}
});
}
}
this.editorPart.centerLayout(active);

if (!skipLayout) {
this.layout();
Expand Down Expand Up @@ -1514,4 +1485,4 @@ export class Workbench extends Disposable implements IPartService {
}

//#endregion
}
}

0 comments on commit 6a1ca40

Please sign in to comment.