Skip to content

Commit

Permalink
splitview: add orthogonal size
Browse files Browse the repository at this point in the history
grid: combine layout and orthogonalLayout

fixes #78173
  • Loading branch information
joaomoreno committed Aug 13, 2019
1 parent c23b596 commit 45fec01
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 58 deletions.
85 changes: 50 additions & 35 deletions src/vs/base/browser/ui/grid/gridview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ export class LayoutController implements ILayoutController {
constructor(public isLayoutEnabled: boolean) { }
}

export class MultiplexLayoutController implements ILayoutController {
get isLayoutEnabled(): boolean { return this.layoutControllers.every(l => l.isLayoutEnabled); }
constructor(private layoutControllers: ILayoutController[]) { }
}

export interface IGridViewOptions {
readonly styles?: IGridViewStyles;
readonly proportionalLayout?: boolean; // default true
Expand Down Expand Up @@ -170,6 +175,7 @@ class BranchNode implements ISplitView, IDisposable {

constructor(
readonly orientation: Orientation,
readonly layoutController: ILayoutController,
styles: IGridViewStyles,
readonly proportionalLayout: boolean,
size: number = 0,
Expand All @@ -181,7 +187,7 @@ class BranchNode implements ISplitView, IDisposable {

this.element = $('.monaco-grid-branch-node');
this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout });
this.splitview.layout(size);
this.splitview.layout(size, orthogonalSize);

const onDidSashReset = Event.map(this.splitview.onDidSashReset, i => [i]);
this.splitviewSashResetDisposable = onDidSashReset(this._onDidSashReset.fire, this._onDidSashReset);
Expand All @@ -198,12 +204,19 @@ class BranchNode implements ISplitView, IDisposable {
}
}

layout(size: number): void {
this._orthogonalSize = size;
layout(size: number, orthogonalSize: number | undefined): void {
if (!this.layoutController.isLayoutEnabled) {
return;
}

for (const child of this.children) {
child.orthogonalLayout(size);
if (typeof orthogonalSize !== 'number') {
throw new Error('Invalid state');
}

this._size = orthogonalSize;
this._orthogonalSize = size;

this.splitview.layout(orthogonalSize, size);
}

setVisible(visible: boolean): void {
Expand All @@ -212,11 +225,6 @@ class BranchNode implements ISplitView, IDisposable {
}
}

orthogonalLayout(size: number): void {
this._size = size;
this.splitview.layout(size);
}

addChild(node: Node, size: number | Sizing, index: number): void {
if (index < 0 || index > this.children.length) {
throw new Error('Invalid index');
Expand Down Expand Up @@ -539,12 +547,18 @@ class LeafNode implements ISplitView, IDisposable {
// noop
}

layout(size: number): void {
this._size = size;
layout(size: number, orthogonalSize: number | undefined): void {
if (!this.layoutController.isLayoutEnabled) {
return;
}

if (this.layoutController.isLayoutEnabled) {
this.view.layout(this.width, this.height, orthogonal(this.orientation));
if (typeof orthogonalSize !== 'number') {
throw new Error('Invalid state');
}

this._size = size;
this._orthogonalSize = orthogonalSize;
this.view.layout(this.width, this.height, orthogonal(this.orientation));
}

setVisible(visible: boolean): void {
Expand All @@ -553,22 +567,14 @@ class LeafNode implements ISplitView, IDisposable {
}
}

orthogonalLayout(size: number): void {
this._orthogonalSize = size;

if (this.layoutController.isLayoutEnabled) {
this.view.layout(this.width, this.height, orthogonal(this.orientation));
}
}

dispose(): void { }
}

type Node = BranchNode | LeafNode;

function flipNode<T extends Node>(node: T, size: number, orthogonalSize: number): T {
if (node instanceof BranchNode) {
const result = new BranchNode(orthogonal(node.orientation), node.styles, node.proportionalLayout, size, orthogonalSize);
const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize);

let totalSize = 0;

Expand All @@ -589,7 +595,7 @@ function flipNode<T extends Node>(node: T, size: number, orthogonalSize: number)

return result as T;
} else {
return new LeafNode((node as LeafNode).view, orthogonal(node.orientation), (node as LeafNode).layoutController, orthogonalSize) as T;
return new LeafNode((node as LeafNode).view, orthogonal(node.orientation), node.layoutController, orthogonalSize) as T;
}
}

Expand Down Expand Up @@ -634,8 +640,9 @@ export class GridView implements IDisposable {

const { size, orthogonalSize } = this._root;
this.root = flipNode(this._root, orthogonalSize, size);
this.root.layout(size);
this.root.orthogonalLayout(orthogonalSize);
this.root.layout(size, orthogonalSize);
// this.root.layout(size);
// this.root.orthogonalLayout(orthogonalSize);
}

get width(): number { return this.root.width; }
Expand All @@ -649,14 +656,25 @@ export class GridView implements IDisposable {
private _onDidChange = new Relay<IViewSize | undefined>();
readonly onDidChange = this._onDidChange.event;

/**
* The first layout controller makes sure layout only propagates
* to the views after the very first call to gridview.layout()
*/
private firstLayoutController: LayoutController;
private layoutController: LayoutController;

constructor(options: IGridViewOptions = {}) {
this.element = $('.monaco-grid-view');
this.styles = options.styles || defaultStyles;
this.proportionalLayout = typeof options.proportionalLayout !== 'undefined' ? !!options.proportionalLayout : true;
this.root = new BranchNode(Orientation.VERTICAL, this.styles, this.proportionalLayout);
this.layoutController = options.layoutController || new LayoutController(true);

this.firstLayoutController = new LayoutController(false);
this.layoutController = new MultiplexLayoutController([
this.firstLayoutController,
...(options.layoutController ? [options.layoutController] : [])
]);

this.root = new BranchNode(Orientation.VERTICAL, this.layoutController, this.styles, this.proportionalLayout);
}

style(styles: IGridViewStyles): void {
Expand All @@ -665,9 +683,10 @@ export class GridView implements IDisposable {
}

layout(width: number, height: number): void {
this.firstLayoutController.isLayoutEnabled = true;

const [size, orthogonalSize] = this.root.orientation === Orientation.HORIZONTAL ? [height, width] : [width, height];
this.root.layout(size);
this.root.orthogonalLayout(orthogonalSize);
this.root.layout(size, orthogonalSize);
}

addView(view: IView, size: number | Sizing, location: number[]): void {
Expand All @@ -694,9 +713,8 @@ export class GridView implements IDisposable {

grandParent.removeChild(parentIndex);

const newParent = new BranchNode(parent.orientation, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize);
const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize);
grandParent.addChild(newParent, parent.size, parentIndex);
newParent.orthogonalLayout(parent.orthogonalSize);

const newSibling = new LeafNode(parent.view, grandParent.orientation, this.layoutController, parent.size);
newParent.addChild(newSibling, newSiblingSize, 0);
Expand Down Expand Up @@ -827,9 +845,6 @@ export class GridView implements IDisposable {

fromParent.addChild(toNode, fromSize, fromIndex);
toParent.addChild(fromNode, toSize, toIndex);

fromParent.layout(fromParent.orthogonalSize);
toParent.layout(toParent.orthogonalSize);
}
}

Expand Down
38 changes: 20 additions & 18 deletions src/vs/base/browser/ui/splitview/splitview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ const defaultStyles: ISplitViewStyles = {
};

export interface ISplitViewOptions {
orientation?: Orientation; // default Orientation.VERTICAL
styles?: ISplitViewStyles;
orthogonalStartSash?: Sash;
orthogonalEndSash?: Sash;
inverseAltBehavior?: boolean;
proportionalLayout?: boolean; // default true
readonly orientation?: Orientation; // default Orientation.VERTICAL
readonly styles?: ISplitViewStyles;
readonly orthogonalStartSash?: Sash;
readonly orthogonalEndSash?: Sash;
readonly inverseAltBehavior?: boolean;
readonly proportionalLayout?: boolean; // default true
}

/**
Expand All @@ -48,7 +48,7 @@ export interface IView {
readonly onDidChange: Event<number | undefined>;
readonly priority?: LayoutPriority;
readonly snap?: boolean;
layout(size: number, orientation: Orientation): void;
layout(size: number, orthogonalSize: number | undefined): void;
setVisible?(visible: boolean): void;
}

Expand Down Expand Up @@ -125,13 +125,13 @@ abstract class ViewItem {
dom.addClass(container, 'visible');
}

layout(): void {
layout(_orthogonalSize: number | undefined): void {
this.container.scrollTop = 0;
this.container.scrollLeft = 0;
}

layoutView(orientation: Orientation): void {
this.view.layout(this.size, orientation);
layoutView(orthogonalSize: number | undefined): void {
this.view.layout(this.size, orthogonalSize);
}

dispose(): IView {
Expand All @@ -142,19 +142,19 @@ abstract class ViewItem {

class VerticalViewItem extends ViewItem {

layout(): void {
super.layout();
layout(orthogonalSize: number | undefined): void {
super.layout(orthogonalSize);
this.container.style.height = `${this.size}px`;
this.layoutView(Orientation.VERTICAL);
this.layoutView(orthogonalSize);
}
}

class HorizontalViewItem extends ViewItem {

layout(): void {
super.layout();
layout(orthogonalSize: number | undefined): void {
super.layout(orthogonalSize);
this.container.style.width = `${this.size}px`;
this.layoutView(Orientation.HORIZONTAL);
this.layoutView(orthogonalSize);
}
}

Expand Down Expand Up @@ -205,6 +205,7 @@ export class SplitView extends Disposable {
private sashContainer: HTMLElement;
private viewContainer: HTMLElement;
private size = 0;
private orthogonalSize: number | undefined;
private contentSize = 0;
private proportions: undefined | number[] = undefined;
private viewItems: ViewItem[] = [];
Expand Down Expand Up @@ -475,9 +476,10 @@ export class SplitView extends Disposable {
return viewItem.cachedVisibleSize;
}

layout(size: number): void {
layout(size: number, orthogonalSize?: number): void {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
this.orthogonalSize = orthogonalSize;

if (!this.proportions) {
const indexes = range(this.viewItems.length);
Expand Down Expand Up @@ -820,7 +822,7 @@ export class SplitView extends Disposable {
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);

// Layout views
this.viewItems.forEach(item => item.layout());
this.viewItems.forEach(item => item.layout(this.orthogonalSize));

// Layout sashes
this.sashItems.forEach(item => item.sash.layout());
Expand Down
33 changes: 28 additions & 5 deletions src/vs/base/test/browser/ui/splitview/splitview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as assert from 'assert';
import { Emitter } from 'vs/base/common/event';
import { SplitView, IView, Orientation, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview';
import { SplitView, IView, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview';
import { Sash, SashState } from 'vs/base/browser/ui/sash/sash';

class TestView implements IView {
Expand All @@ -27,7 +27,9 @@ class TestView implements IView {

private _size = 0;
get size(): number { return this._size; }
private _onDidLayout = new Emitter<{ size: number; orientation: Orientation }>();
private _orthogonalSize: number | undefined = 0;
get orthogonalSize(): number | undefined { return this._orthogonalSize; }
private _onDidLayout = new Emitter<{ size: number; orthogonalSize: number | undefined }>();
readonly onDidLayout = this._onDidLayout.event;

private _onDidFocus = new Emitter<void>();
Expand All @@ -41,9 +43,10 @@ class TestView implements IView {
assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size');
}

layout(size: number, orientation: Orientation): void {
layout(size: number, orthogonalSize: number | undefined): void {
this._size = size;
this._onDidLayout.fire({ size, orientation });
this._orthogonalSize = orthogonalSize;
this._onDidLayout.fire({ size, orthogonalSize });
}

focus(): void {
Expand Down Expand Up @@ -523,4 +526,24 @@ suite('Splitview', () => {
view2.dispose();
view1.dispose();
});
});

test('orthogonal size propagates to views', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low);
const splitview = new SplitView(container, { proportionalLayout: false });
splitview.layout(200);

splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
splitview.addView(view3, Sizing.Distribute);

splitview.layout(200, 100);
assert.deepEqual([view1.orthogonalSize, view2.orthogonalSize, view3.orthogonalSize], [100, 100, 100]);

splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
});

0 comments on commit 45fec01

Please sign in to comment.