Merge from vscode 91e99652cd5fcfc072387c64e151b435e39e8dcf (#6962)

This commit is contained in:
Anthony Dresser
2019-08-26 15:58:42 -07:00
committed by GitHub
parent edf470c8fa
commit 507bae90b7
103 changed files with 1743 additions and 1543 deletions

View File

@@ -7,7 +7,7 @@ import 'vs/css!./gridview';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { Disposable } from 'vs/base/common/lifecycle';
import { tail2 as tail, equals } from 'vs/base/common/arrays';
import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, LayoutController, IGridViewOptions } from './gridview';
import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, IGridViewOptions } from './gridview';
import { Event } from 'vs/base/common/event';
import { InvisibleSizing } from 'vs/base/browser/ui/splitview/splitview';
@@ -217,9 +217,17 @@ export class Grid<T extends IView = IView> extends Disposable {
private didLayout = false;
constructor(view: T, options: IGridOptions = {}) {
constructor(gridview: GridView, options?: IGridOptions);
constructor(view: T, options?: IGridOptions);
constructor(view: T | GridView, options: IGridOptions = {}) {
super();
this.gridview = new GridView(options);
if (view instanceof GridView) {
this.gridview = view;
this.gridview.getViewMap(this.views);
} else {
this.gridview = new GridView(options);
}
this._register(this.gridview);
this._register(this.gridview.onDidSashReset(this.onDidSashReset, this));
@@ -228,7 +236,9 @@ export class Grid<T extends IView = IView> extends Disposable {
? GridViewSizing.Invisible(options.firstViewVisibleCachedSize)
: 0;
this._addView(view, size, [0]);
if (!(view instanceof GridView)) {
this._addView(view, size, [0]);
}
}
style(styles: IGridStyles): void {
@@ -469,12 +479,6 @@ export interface IViewDeserializer<T extends ISerializableView> {
fromJSON(json: any): T;
}
interface InitialLayoutContext<T extends ISerializableView> {
width: number;
height: number;
root: GridBranchNode<T>;
}
export interface ISerializedLeafNode {
type: 'leaf';
data: any;
@@ -567,31 +571,9 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
throw new Error('Invalid JSON: \'height\' property must be a number.');
}
const orientation = json.orientation;
const width = json.width;
const height = json.height;
const box: Box = { top: 0, left: 0, width, height };
const gridview = GridView.deserialize(json, deserializer, options);
const result = new SerializableGrid<T>(gridview, options);
const root = SerializableGrid.deserializeNode(json.root, orientation, box, deserializer) as GridBranchNode<T>;
const firstLeaf = SerializableGrid.getFirstLeaf(root);
if (!firstLeaf) {
throw new Error('Invalid serialized state, first leaf not found');
}
const layoutController = new LayoutController(false);
options = { ...options, layoutController };
if (typeof firstLeaf.cachedVisibleSize === 'number') {
options = { ...options, firstViewVisibleCachedSize: firstLeaf.cachedVisibleSize };
}
const result = new SerializableGrid<T>(firstLeaf.view, options);
result.orientation = orientation;
result.restoreViews(firstLeaf.view, orientation, root);
result.initialLayoutContext = { width, height, root };
layoutController.isLayoutEnabled = true;
return result;
}
@@ -599,7 +581,7 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
* Useful information in order to proportionally restore view sizes
* upon the very first layout call.
*/
private initialLayoutContext: InitialLayoutContext<T> | undefined;
private initialLayoutContext: boolean = true;
serialize(): ISerializedGrid {
return {
@@ -614,65 +596,8 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
super.layout(width, height);
if (this.initialLayoutContext) {
const widthScale = width / this.initialLayoutContext.width;
const heightScale = height / this.initialLayoutContext.height;
this.restoreViewsSize([], this.initialLayoutContext.root, this.orientation, widthScale, heightScale);
this.initialLayoutContext = undefined;
this.gridview.trySet2x2();
}
}
/**
* Recursively restores views which were just deserialized.
*/
private restoreViews(referenceView: T, orientation: Orientation, node: GridNode<T>): void {
if (!isGridBranchNode(node)) {
return;
}
const direction = orientation === Orientation.VERTICAL ? Direction.Down : Direction.Right;
const firstLeaves = node.children.map(c => SerializableGrid.getFirstLeaf(c));
for (let i = 1; i < firstLeaves.length; i++) {
const node = firstLeaves[i];
const size: number | InvisibleSizing = typeof node.cachedVisibleSize === 'number'
? GridViewSizing.Invisible(node.cachedVisibleSize)
: (orientation === Orientation.VERTICAL ? node.box.height : node.box.width);
this.addView(node.view, size, referenceView, direction);
referenceView = node.view;
}
for (let i = 0; i < node.children.length; i++) {
this.restoreViews(firstLeaves[i].view, orthogonal(orientation), node.children[i]);
}
}
/**
* Recursively restores view sizes.
* This should be called only after the very first layout call.
*/
private restoreViewsSize(location: number[], node: GridNode<T>, orientation: Orientation, widthScale: number, heightScale: number): void {
if (!isGridBranchNode(node)) {
return;
}
const scale = orientation === Orientation.VERTICAL ? heightScale : widthScale;
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const childLocation = [...location, i];
if (i < node.children.length - 1) {
const size = orientation === Orientation.VERTICAL
? { height: Math.floor(child.box.height * scale) }
: { width: Math.floor(child.box.width * scale) };
this.gridview.resizeView(childLocation, size);
}
this.restoreViewsSize(childLocation, child, orthogonal(orientation), widthScale, heightScale);
this.initialLayoutContext = false;
}
}
}

View File

@@ -34,6 +34,36 @@ export interface IView {
setVisible?(visible: boolean): void;
}
export interface ISerializableView extends IView {
toJSON(): object;
}
export interface IViewDeserializer<T extends ISerializableView> {
fromJSON(json: any): T;
}
export interface ISerializedLeafNode {
type: 'leaf';
data: any;
size: number;
visible?: boolean;
}
export interface ISerializedBranchNode {
type: 'branch';
data: ISerializedNode[];
size: number;
}
export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode;
export interface ISerializedGridView {
root: ISerializedNode;
orientation: Orientation;
width: number;
height: number;
}
export function orthogonal(orientation: Orientation): Orientation {
return orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
}
@@ -179,18 +209,49 @@ class BranchNode implements ISplitView, IDisposable {
styles: IGridViewStyles,
readonly proportionalLayout: boolean,
size: number = 0,
orthogonalSize: number = 0
orthogonalSize: number = 0,
childDescriptors?: INodeDescriptor[]
) {
this._styles = styles;
this._size = size;
this._orthogonalSize = orthogonalSize;
this.element = $('.monaco-grid-branch-node');
this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout });
this.splitview.layout(size, orthogonalSize);
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);
} else {
// Reconstruction behavior, we want to reconstruct a splitview
const descriptor = {
views: childDescriptors.map(childDescriptor => {
return {
view: childDescriptor.node,
size: childDescriptor.node.size,
visible: childDescriptor.node instanceof LeafNode && childDescriptor.visible !== undefined ? childDescriptor.visible : true
};
}),
size: this.orthogonalSize
};
const options = { proportionalLayout, orientation, styles };
this.children = childDescriptors.map(c => c.node);
this.splitview = new SplitView(this.element, { ...options, descriptor });
this.children.forEach((node, index) => {
// Set up orthogonal sashes for children
node.orthogonalStartSash = this.splitview.sashes[index - 1];
node.orthogonalEndSash = this.splitview.sashes[index];
});
}
const onDidSashReset = Event.map(this.splitview.onDidSashReset, i => [i]);
this.splitviewSashResetDisposable = onDidSashReset(this._onDidSashReset.fire, this._onDidSashReset);
const onDidChildrenSashReset = Event.any(...this.children.map((c, i) => Event.map(c.onDidSashReset, location => [i, ...location])));
this.childrenSashResetDisposable = onDidChildrenSashReset(this._onDidSashReset.fire, this._onDidSashReset);
}
style(styles: IGridViewStyles): void {
@@ -226,7 +287,7 @@ class BranchNode implements ISplitView, IDisposable {
}
}
addChild(node: Node, size: number | Sizing, index: number): void {
addChild(node: Node, size: number | Sizing, index: number, skipLayout?: boolean): void {
if (index < 0 || index > this.children.length) {
throw new Error('Invalid index');
}
@@ -484,9 +545,11 @@ class LeafNode implements ISplitView, IDisposable {
readonly view: IView,
readonly orientation: Orientation,
readonly layoutController: ILayoutController,
orthogonalSize: number
orthogonalSize: number,
size: number = 0
) {
this._orthogonalSize = orthogonalSize;
this._size = size;
this._onDidViewChange = Event.map(this.view.onDidChange, e => e && (this.orientation === Orientation.VERTICAL ? e.width : e.height));
this.onDidChange = Event.any(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event);
@@ -577,6 +640,11 @@ class LeafNode implements ISplitView, IDisposable {
type Node = BranchNode | LeafNode;
export interface INodeDescriptor {
node: Node;
visible?: boolean;
}
function flipNode<T extends Node>(node: T, size: number, orthogonalSize: number): T {
if (node instanceof BranchNode) {
const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize);
@@ -680,6 +748,18 @@ export class GridView implements IDisposable {
this.root = new BranchNode(Orientation.VERTICAL, this.layoutController, this.styles, this.proportionalLayout);
}
getViewMap(map: Map<IView, HTMLElement>, node?: Node): void {
if (!node) {
node = this.root;
}
if (node instanceof BranchNode) {
node.children.forEach(child => this.getViewMap(map, child));
} else {
map.set(node.view, node.element);
}
}
style(styles: IGridViewStyles): void {
this.styles = styles;
this.root.style(styles);
@@ -953,6 +1033,47 @@ export class GridView implements IDisposable {
return this._getViews(node, this.orientation, { top: 0, left: 0, width: this.width, height: this.height });
}
static deserialize<T extends ISerializableView>(json: ISerializedGridView, deserializer: IViewDeserializer<T>, options: IGridViewOptions = {}): GridView {
if (typeof json.orientation !== 'number') {
throw new Error('Invalid JSON: \'orientation\' property must be a number.');
} else if (typeof json.width !== 'number') {
throw new Error('Invalid JSON: \'width\' property must be a number.');
} else if (typeof json.height !== 'number') {
throw new Error('Invalid JSON: \'height\' property must be a number.');
}
const orientation = json.orientation;
const height = json.height;
const result = new GridView(options);
result._deserialize(json.root as ISerializedBranchNode, orientation, deserializer, height);
return result;
}
private _deserialize(root: ISerializedBranchNode, orientation: Orientation, deserializer: IViewDeserializer<ISerializableView>, orthogonalSize: number): void {
this.root = this._deserializeNode(root, orientation, deserializer, orthogonalSize) as BranchNode;
}
private _deserializeNode(node: ISerializedNode, orientation: Orientation, deserializer: IViewDeserializer<ISerializableView>, orthogonalSize: number): Node {
let result: Node;
if (node.type === 'branch') {
const serializedChildren = node.data as ISerializedNode[];
const children = serializedChildren.map(serializedChild => {
return {
node: this._deserializeNode(serializedChild, orthogonal(orientation), deserializer, node.size),
visible: (serializedChild as { visible?: boolean }).visible
} as INodeDescriptor;
});
result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, children);
} else {
result = new LeafNode(deserializer.fromJSON(node.data), orientation, this.layoutController, orthogonalSize, node.size);
}
return result;
}
private _getViews(node: Node, orientation: Orientation, box: Box, cachedVisibleSize?: number): GridNode {
if (node instanceof LeafNode) {
return { view: node.view, box, cachedVisibleSize };