diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index d8a004c7647..fc0141b11ac 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -42,7 +42,7 @@ function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { get maximumSize() { return view.maximumWidth; }, get minimumSize() { return view.minimumWidth; }, onDidChange: Event.map(view.onDidChange, e => e && e.width), - layout: size => view.layout(size, getHeight()) + layout: (size, offset) => view.layout(size, getHeight(), 0, offset) }; } @@ -81,7 +81,7 @@ export class CenteredViewLayout implements IDisposable { this.resizeMargins(); } } else { - this.view.layout(width, height); + this.view.layout(width, height, 0, 0); } this.didLayout = true; } diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index ddf7ff59664..35d519b22b1 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -30,7 +30,7 @@ export interface IView { readonly onDidChange: Event; readonly priority?: LayoutPriority; readonly snap?: boolean; - layout(width: number, height: number): void; + layout(width: number, height: number, top: number, left: number): void; setVisible?(visible: boolean): void; } @@ -117,11 +117,16 @@ export interface IGridViewOptions { readonly layoutController?: ILayoutController; } -class BranchNode implements ISplitView, IDisposable { +interface ILayoutContext { + readonly orthogonalSize: number; + readonly orthogonalOffset: number; +} + +class BranchNode implements ISplitView, IDisposable { readonly element: HTMLElement; readonly children: Node[] = []; - private splitview: SplitView; + private splitview: SplitView; private _size: number; get size(): number { return this._size; } @@ -129,6 +134,12 @@ class BranchNode implements ISplitView, IDisposable { private _orthogonalSize: number; get orthogonalSize(): number { return this._orthogonalSize; } + private _offset: number = 0; + get offset(): number { return this._offset; } + + private _orthogonalOffset: number = 0; + get orthogonalOffset(): number { return this._orthogonalOffset; } + private _styles: IGridViewStyles; get styles(): IGridViewStyles { return this._styles; } @@ -221,7 +232,7 @@ class BranchNode implements ISplitView, IDisposable { if (!childDescriptors) { // Normal behavior, we have no children yet, just set up the splitview this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout }); - this.splitview.layout(size, orthogonalSize); + this.splitview.layout(size, { orthogonalSize, orthogonalOffset: 0 }); } else { // Reconstruction behavior, we want to reconstruct a splitview const descriptor = { @@ -268,20 +279,23 @@ class BranchNode implements ISplitView, IDisposable { } } - layout(size: number, orthogonalSize: number | undefined): void { + layout(size: number, offset: number, ctx: ILayoutContext | undefined): void { if (!this.layoutController.isLayoutEnabled) { return; } - if (typeof orthogonalSize !== 'number') { + if (typeof ctx === 'undefined') { throw new Error('Invalid state'); } // branch nodes should flip the normal/orthogonal directions - this._size = orthogonalSize; + this._size = ctx.orthogonalSize; this._orthogonalSize = size; - this.splitview.layout(orthogonalSize, size); + this.splitview.layout(ctx.orthogonalSize, { + orthogonalSize: size, + orthogonalOffset: offset + }); } setVisible(visible: boolean): void { @@ -511,7 +525,7 @@ class BranchNode implements ISplitView, IDisposable { } } -class LeafNode implements ISplitView, IDisposable { +class LeafNode implements ISplitView, IDisposable { private _size: number = 0; get size(): number { return this._size; } @@ -519,6 +533,12 @@ class LeafNode implements ISplitView, IDisposable { private _orthogonalSize: number; get orthogonalSize(): number { return this._orthogonalSize; } + private _offset: number = 0; + get offset(): number { return this._offset; } + + private _orthogonalOffset: number = 0; + get orthogonalOffset(): number { return this._orthogonalOffset; } + readonly onDidSashReset: Event = Event.None; private _onDidLinkedWidthNodeChange = new Relay(); @@ -565,6 +585,14 @@ class LeafNode implements ISplitView, IDisposable { return this.orientation === Orientation.HORIZONTAL ? this.size : this.orthogonalSize; } + get top(): number { + return this.orientation === Orientation.HORIZONTAL ? this.offset : this.orthogonalOffset; + } + + get left(): number { + return this.orientation === Orientation.HORIZONTAL ? this.orthogonalOffset : this.offset; + } + get element(): HTMLElement { return this.view.element; } @@ -617,18 +645,20 @@ class LeafNode implements ISplitView, IDisposable { // noop } - layout(size: number, orthogonalSize: number | undefined): void { + layout(size: number, offset: number, ctx: ILayoutContext | undefined): void { if (!this.layoutController.isLayoutEnabled) { return; } - if (typeof orthogonalSize !== 'number') { + if (typeof ctx === 'undefined') { throw new Error('Invalid state'); } this._size = size; - this._orthogonalSize = orthogonalSize; - this.view.layout(this.width, this.height); + this._offset = offset; + this._orthogonalSize = ctx.orthogonalSize; + this._orthogonalOffset = ctx.orthogonalOffset; + this.view.layout(this.width, this.height, this.top, this.left); } setVisible(visible: boolean): void { @@ -715,7 +745,7 @@ export class GridView implements IDisposable { const { size, orthogonalSize } = this._root; this.root = flipNode(this._root, orthogonalSize, size); - this.root.layout(size, orthogonalSize); + this.root.layout(size, 0, { orthogonalSize, orthogonalOffset: 0 }); } get width(): number { return this.root.width; } @@ -771,7 +801,7 @@ export class GridView implements IDisposable { this.firstLayoutController.isLayoutEnabled = true; const [size, orthogonalSize] = this.root.orientation === Orientation.HORIZONTAL ? [height, width] : [width, height]; - this.root.layout(size, orthogonalSize); + this.root.layout(size, 0, { orthogonalSize, orthogonalOffset: 0 }); } addView(view: IView, size: number | Sizing, location: number[]): void { diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index b79435b63d6..2d566e23417 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -23,14 +23,14 @@ const defaultStyles: ISplitViewStyles = { separatorBorder: Color.transparent }; -export interface ISplitViewOptions { +export interface ISplitViewOptions { readonly orientation?: Orientation; // default Orientation.VERTICAL readonly styles?: ISplitViewStyles; readonly orthogonalStartSash?: Sash; readonly orthogonalEndSash?: Sash; readonly inverseAltBehavior?: boolean; readonly proportionalLayout?: boolean; // default true, - readonly descriptor?: ISplitViewDescriptor; + readonly descriptor?: ISplitViewDescriptor; } /** @@ -42,14 +42,14 @@ export const enum LayoutPriority { High } -export interface IView { +export interface IView { readonly element: HTMLElement; readonly minimumSize: number; readonly maximumSize: number; readonly onDidChange: Event; readonly priority?: LayoutPriority; readonly snap?: boolean; - layout(size: number, orthogonalSize: number | undefined): void; + layout(size: number, offset: number, context: TLayoutContext | undefined): void; setVisible?(visible: boolean): void; } @@ -62,7 +62,7 @@ interface ISashEvent { type ViewItemSize = number | { cachedVisibleSize: number }; -abstract class ViewItem { +abstract class ViewItem { private _size: number; set size(size: number) { @@ -115,7 +115,7 @@ abstract class ViewItem { constructor( protected container: HTMLElement, - private view: IView, + private view: IView, size: ViewItemSize, private disposable: IDisposable ) { @@ -129,31 +129,31 @@ abstract class ViewItem { } } - layout(position: number, orthogonalSize: number | undefined): void { - this.layoutContainer(position); - this.view.layout(this.size, orthogonalSize); + layout(offset: number, layoutContext: TLayoutContext | undefined): void { + this.layoutContainer(offset); + this.view.layout(this.size, offset, layoutContext); } - abstract layoutContainer(position: number): void; + abstract layoutContainer(offset: number): void; - dispose(): IView { + dispose(): IView { this.disposable.dispose(); return this.view; } } -class VerticalViewItem extends ViewItem { +class VerticalViewItem extends ViewItem { - layoutContainer(position: number): void { - this.container.style.top = `${position}px`; + layoutContainer(offset: number): void { + this.container.style.top = `${offset}px`; this.container.style.height = `${this.size}px`; } } -class HorizontalViewItem extends ViewItem { +class HorizontalViewItem extends ViewItem { - layoutContainer(position: number): void { - this.container.style.left = `${position}px`; + layoutContainer(offset: number): void { + this.container.style.left = `${offset}px`; this.container.style.width = `${this.size}px`; } } @@ -198,26 +198,26 @@ export namespace Sizing { export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; } } -export interface ISplitViewDescriptor { +export interface ISplitViewDescriptor { size: number; views: { visible?: boolean; size: number; - view: IView; + view: IView; }[]; } -export class SplitView extends Disposable { +export class SplitView extends Disposable { readonly orientation: Orientation; readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; private size = 0; - private orthogonalSize: number | undefined; + private layoutContext: TLayoutContext | undefined; private contentSize = 0; private proportions: undefined | number[] = undefined; - private viewItems: ViewItem[] = []; + private viewItems: ViewItem[] = []; private sashItems: ISashItem[] = []; private sashDragState: ISashDragState | undefined; private state: State = State.Idle; @@ -280,7 +280,7 @@ export class SplitView extends Disposable { this.updateSashEnablement(); } - constructor(container: HTMLElement, options: ISplitViewOptions = {}) { + constructor(container: HTMLElement, options: ISplitViewOptions = {}) { super(); this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation; @@ -323,11 +323,11 @@ export class SplitView extends Disposable { } } - addView(view: IView, size: number | Sizing, index = this.viewItems.length): void { + addView(view: IView, size: number | Sizing, index = this.viewItems.length): void { this.doAddView(view, size, index, false); } - removeView(index: number, sizing?: Sizing): IView { + removeView(index: number, sizing?: Sizing): IView { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); } @@ -419,10 +419,10 @@ export class SplitView extends Disposable { return viewItem.cachedVisibleSize; } - layout(size: number, orthogonalSize?: number): void { + layout(size: number, layoutContext?: TLayoutContext): void { const previousSize = Math.max(this.size, this.contentSize); this.size = size; - this.orthogonalSize = orthogonalSize; + this.layoutContext = layoutContext; if (!this.proportions) { const indexes = range(this.viewItems.length); @@ -563,7 +563,7 @@ export class SplitView extends Disposable { } } - private onViewChange(item: ViewItem, size: number | undefined): void { + private onViewChange(item: ViewItem, size: number | undefined): void { const index = this.viewItems.indexOf(item); if (index < 0 || index >= this.viewItems.length) { @@ -610,7 +610,7 @@ export class SplitView extends Disposable { } distributeViewSizes(): void { - const flexibleViewItems: ViewItem[] = []; + const flexibleViewItems: ViewItem[] = []; let flexibleSize = 0; for (const item of this.viewItems) { @@ -641,7 +641,7 @@ export class SplitView extends Disposable { return this.viewItems[index].size; } - private doAddView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void { + private doAddView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); } @@ -875,11 +875,11 @@ export class SplitView extends Disposable { this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); // Layout views - let position = 0; + let offset = 0; for (const viewItem of this.viewItems) { - viewItem.layout(position, this.orthogonalSize); - position += viewItem.size; + viewItem.layout(offset, this.layoutContext); + offset += viewItem.size; } // Layout sashes diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 264a2bac86b..2620db0b7d8 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -8,7 +8,7 @@ import { Emitter } from 'vs/base/common/event'; 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 { +class TestView implements IView { private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; @@ -43,7 +43,7 @@ class TestView implements IView { assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size'); } - layout(size: number, orthogonalSize: number | undefined): void { + layout(size: number, _offset: number, orthogonalSize: number | undefined): void { this._size = size; this._orthogonalSize = orthogonalSize; this._onDidLayout.fire({ size, orthogonalSize }); @@ -527,11 +527,11 @@ suite('Splitview', () => { view1.dispose(); }); - test('orthogonal size propagates to views', () => { + test('context 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 }); + const splitview = new SplitView(container, { proportionalLayout: false }); splitview.layout(200); splitview.addView(view1, Sizing.Distribute); diff --git a/test/splitview/public/index.html b/test/splitview/public/index.html index 2df71316146..0951af8ea52 100644 --- a/test/splitview/public/index.html +++ b/test/splitview/public/index.html @@ -18,7 +18,10 @@ color: white; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-weight: bold; - font-size: 30px; + font-size: 20px; + display: flex; + justify-content: center; + align-items: center; } @@ -65,9 +68,8 @@ this.setVisible(true); } - layout(width, height, orientation) { - console.log(`layout@${this.label}`); - this.element.style.lineHeight = `${height}px`; + layout(width, height, top, left) { + this.element.innerHTML = `(${top}, ${left})
(${width}, ${height})`; } setVisible(visible) {