Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Centered editor layout #52064

Merged
merged 12 commits into from
Jun 19, 2018
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
}
}