mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 11:08:31 -05:00
Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3 (#6516)
* Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3 * fix tests
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IView, IViewSize } from 'vs/base/browser/ui/grid/gridview';
|
||||
import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid';
|
||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ 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, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize } from './gridview';
|
||||
import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize } from './gridview';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { InvisibleSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
export { Orientation, Sizing as GridViewSizing } from './gridview';
|
||||
export { Orientation, Sizing as GridViewSizing, IViewSize, orthogonal, LayoutPriority } from './gridview';
|
||||
|
||||
export const enum Direction {
|
||||
Up,
|
||||
@@ -28,9 +29,15 @@ function oppositeDirection(direction: Direction): Direction {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IView extends IGridViewView {
|
||||
readonly preferredHeight?: number;
|
||||
readonly preferredWidth?: number;
|
||||
}
|
||||
|
||||
export interface GridLeafNode<T extends IView> {
|
||||
readonly view: T;
|
||||
readonly box: Box;
|
||||
readonly cachedVisibleSize: number | undefined;
|
||||
}
|
||||
|
||||
export interface GridBranchNode<T extends IView> {
|
||||
@@ -173,16 +180,23 @@ function getGridLocation(element: HTMLElement): number[] {
|
||||
return [...getGridLocation(ancestor), index];
|
||||
}
|
||||
|
||||
export const enum Sizing {
|
||||
Distribute = 'distribute',
|
||||
Split = 'split'
|
||||
export type DistributeSizing = { type: 'distribute' };
|
||||
export type SplitSizing = { type: 'split' };
|
||||
export type InvisibleSizing = { type: 'invisible', cachedVisibleSize: number };
|
||||
export type Sizing = DistributeSizing | SplitSizing | InvisibleSizing;
|
||||
|
||||
export namespace Sizing {
|
||||
export const Distribute: DistributeSizing = { type: 'distribute' };
|
||||
export const Split: SplitSizing = { type: 'split' };
|
||||
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
|
||||
}
|
||||
|
||||
export interface IGridStyles extends IGridViewStyles { }
|
||||
|
||||
export interface IGridOptions {
|
||||
styles?: IGridStyles;
|
||||
proportionalLayout?: boolean;
|
||||
readonly styles?: IGridStyles;
|
||||
readonly proportionalLayout?: boolean;
|
||||
readonly firstViewVisibleCachedSize?: number;
|
||||
}
|
||||
|
||||
export class Grid<T extends IView = IView> extends Disposable {
|
||||
@@ -208,9 +222,13 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
this.gridview = new GridView(options);
|
||||
this._register(this.gridview);
|
||||
|
||||
this._register(this.gridview.onDidSashReset(this.doResetViewSize, this));
|
||||
this._register(this.gridview.onDidSashReset(this.onDidSashReset, this));
|
||||
|
||||
this._addView(view, 0, [0]);
|
||||
const size: number | GridViewSizing = typeof options.firstViewVisibleCachedSize === 'number'
|
||||
? GridViewSizing.Invisible(options.firstViewVisibleCachedSize)
|
||||
: 0;
|
||||
|
||||
this._addView(view, size, [0]);
|
||||
}
|
||||
|
||||
style(styles: IGridStyles): void {
|
||||
@@ -241,10 +259,12 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
|
||||
let viewSize: number | GridViewSizing;
|
||||
|
||||
if (size === Sizing.Split) {
|
||||
if (typeof size === 'number') {
|
||||
viewSize = size;
|
||||
} else if (size.type === 'split') {
|
||||
const [, index] = tail(referenceLocation);
|
||||
viewSize = GridViewSizing.Split(index);
|
||||
} else if (size === Sizing.Distribute) {
|
||||
} else if (size.type === 'distribute') {
|
||||
viewSize = GridViewSizing.Distribute;
|
||||
} else {
|
||||
viewSize = size;
|
||||
@@ -264,7 +284,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
}
|
||||
|
||||
const location = this.getViewLocation(view);
|
||||
this.gridview.removeView(location, sizing === Sizing.Distribute ? GridViewSizing.Distribute : undefined);
|
||||
this.gridview.removeView(location, (sizing && sizing.type === 'distribute') ? GridViewSizing.Distribute : undefined);
|
||||
this.views.delete(view);
|
||||
}
|
||||
|
||||
@@ -320,7 +340,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
}
|
||||
|
||||
getViews(): GridBranchNode<T> {
|
||||
return this.gridview.getViews() as GridBranchNode<T>;
|
||||
return this.gridview.getView() as GridBranchNode<T>;
|
||||
}
|
||||
|
||||
getNeighborViews(view: T, direction: Direction, wrap: boolean = false): T[] {
|
||||
@@ -355,8 +375,36 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
return getGridLocation(element);
|
||||
}
|
||||
|
||||
private doResetViewSize(location: number[]): void {
|
||||
const [parentLocation,] = tail(location);
|
||||
private onDidSashReset(location: number[]): void {
|
||||
const resizeToPreferredSize = (location: number[]): boolean => {
|
||||
const node = this.gridview.getView(location) as GridNode<T>;
|
||||
|
||||
if (isGridBranchNode(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const direction = getLocationOrientation(this.orientation, location);
|
||||
const size = direction === Orientation.HORIZONTAL ? node.view.preferredWidth : node.view.preferredHeight;
|
||||
|
||||
if (typeof size !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const viewSize = direction === Orientation.HORIZONTAL ? { width: Math.round(size) } : { height: Math.round(size) };
|
||||
this.gridview.resizeView(location, viewSize);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (resizeToPreferredSize(location)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [parentLocation, index] = tail(location);
|
||||
|
||||
if (resizeToPreferredSize([...parentLocation, index + 1])) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gridview.distributeViewSizes(parentLocation);
|
||||
}
|
||||
}
|
||||
@@ -379,6 +427,7 @@ export interface ISerializedLeafNode {
|
||||
type: 'leaf';
|
||||
data: object | null;
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
export interface ISerializedBranchNode {
|
||||
@@ -402,6 +451,10 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
||||
const size = orientation === Orientation.VERTICAL ? node.box.width : node.box.height;
|
||||
|
||||
if (!isGridBranchNode(node)) {
|
||||
if (typeof node.cachedVisibleSize === 'number') {
|
||||
return { type: 'leaf', data: node.view.toJSON(), size: node.cachedVisibleSize, visible: false };
|
||||
}
|
||||
|
||||
return { type: 'leaf', data: node.view.toJSON(), size };
|
||||
}
|
||||
|
||||
@@ -426,25 +479,26 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
||||
throw new Error('Invalid JSON: \'size\' property of node must be a number.');
|
||||
}
|
||||
|
||||
const childSize = child.type === 'leaf' && child.visible === false ? 0 : child.size;
|
||||
const childBox: Box = orientation === Orientation.HORIZONTAL
|
||||
? { top: box.top, left: box.left + offset, width: child.size, height: box.height }
|
||||
: { top: box.top + offset, left: box.left, width: box.width, height: child.size };
|
||||
? { top: box.top, left: box.left + offset, width: childSize, height: box.height }
|
||||
: { top: box.top + offset, left: box.left, width: box.width, height: childSize };
|
||||
|
||||
children.push(SerializableGrid.deserializeNode(child, orthogonal(orientation), childBox, deserializer));
|
||||
offset += child.size;
|
||||
offset += childSize;
|
||||
}
|
||||
|
||||
return { children, box };
|
||||
|
||||
} else if (json.type === 'leaf') {
|
||||
const view: T = deserializer.fromJSON(json.data);
|
||||
return { view, box };
|
||||
return { view, box, cachedVisibleSize: json.visible === false ? json.size : undefined };
|
||||
}
|
||||
|
||||
throw new Error('Invalid JSON: \'type\' property must be either \'branch\' or \'leaf\'.');
|
||||
}
|
||||
|
||||
private static getFirstLeaf<T extends IView>(node: GridNode<T>): GridLeafNode<T> | undefined {
|
||||
private static getFirstLeaf<T extends IView>(node: GridNode<T>): GridLeafNode<T> {
|
||||
if (!isGridBranchNode(node)) {
|
||||
return node;
|
||||
}
|
||||
@@ -473,6 +527,10 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
||||
throw new Error('Invalid serialized state, first leaf not found');
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -522,13 +580,16 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
||||
const firstLeaves = node.children.map(c => SerializableGrid.getFirstLeaf(c));
|
||||
|
||||
for (let i = 1; i < firstLeaves.length; i++) {
|
||||
const size = orientation === Orientation.VERTICAL ? firstLeaves[i]!.box.height : firstLeaves[i]!.box.width;
|
||||
this.addView(firstLeaves[i]!.view, size, referenceView, direction);
|
||||
referenceView = firstLeaves[i]!.view;
|
||||
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]);
|
||||
this.restoreViews(firstLeaves[i].view, orthogonal(orientation), node.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ export interface Box {
|
||||
export interface GridLeafNode {
|
||||
readonly view: IView;
|
||||
readonly box: Box;
|
||||
readonly cachedVisibleSize: number | undefined;
|
||||
}
|
||||
|
||||
export interface GridBranchNode {
|
||||
@@ -343,6 +344,14 @@ class BranchNode implements ISplitView, IDisposable {
|
||||
this.splitview.setViewVisible(index, visible);
|
||||
}
|
||||
|
||||
getChildCachedVisibleSize(index: number): number | undefined {
|
||||
if (index < 0 || index >= this.children.length) {
|
||||
throw new Error('Invalid index');
|
||||
}
|
||||
|
||||
return this.splitview.getViewCachedVisibleSize(index);
|
||||
}
|
||||
|
||||
private onDidChildrenChange(): void {
|
||||
const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined);
|
||||
this.childrenChangeDisposable.dispose();
|
||||
@@ -424,6 +433,9 @@ class LeafNode implements ISplitView, IDisposable {
|
||||
private _size: number = 0;
|
||||
get size(): number { return this._size; }
|
||||
|
||||
private _cachedVisibleSize: number | undefined;
|
||||
get cachedVisibleSize(): number | undefined { return this._cachedVisibleSize; }
|
||||
|
||||
private _orthogonalSize: number;
|
||||
get orthogonalSize(): number { return this._orthogonalSize; }
|
||||
|
||||
@@ -528,6 +540,12 @@ class LeafNode implements ISplitView, IDisposable {
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
if (visible) {
|
||||
this._cachedVisibleSize = undefined;
|
||||
} else {
|
||||
this._cachedVisibleSize = this._size;
|
||||
}
|
||||
|
||||
if (this.view.setVisible) {
|
||||
this.view.setVisible(visible);
|
||||
}
|
||||
@@ -658,6 +676,14 @@ export class GridView implements IDisposable {
|
||||
} else {
|
||||
const [, grandParent] = tail(pathToParent);
|
||||
const [, parentIndex] = tail(rest);
|
||||
|
||||
let newSiblingSize: number | Sizing = 0;
|
||||
|
||||
const newSiblingCachedVisibleSize = grandParent.getChildCachedVisibleSize(parentIndex);
|
||||
if (typeof newSiblingCachedVisibleSize === 'number') {
|
||||
newSiblingSize = Sizing.Invisible(newSiblingCachedVisibleSize);
|
||||
}
|
||||
|
||||
grandParent.removeChild(parentIndex);
|
||||
|
||||
const newParent = new BranchNode(parent.orientation, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize);
|
||||
@@ -665,7 +691,7 @@ export class GridView implements IDisposable {
|
||||
newParent.orthogonalLayout(parent.orthogonalSize);
|
||||
|
||||
const newSibling = new LeafNode(parent.view, grandParent.orientation, parent.size);
|
||||
newParent.addChild(newSibling, 0, 0);
|
||||
newParent.addChild(newSibling, newSiblingSize, 0);
|
||||
|
||||
if (typeof size !== 'number' && size.type === 'split') {
|
||||
size = Sizing.Split(0);
|
||||
@@ -877,13 +903,16 @@ export class GridView implements IDisposable {
|
||||
parent.setChildVisible(index, visible);
|
||||
}
|
||||
|
||||
getViews(): GridBranchNode {
|
||||
return this._getViews(this.root, this.orientation, { top: 0, left: 0, width: this.width, height: this.height }) as GridBranchNode;
|
||||
getView(): GridBranchNode;
|
||||
getView(location?: number[]): GridNode;
|
||||
getView(location?: number[]): GridNode {
|
||||
const node = location ? this.getNode(location)[1] : this._root;
|
||||
return this._getViews(node, this.orientation, { top: 0, left: 0, width: this.width, height: this.height });
|
||||
}
|
||||
|
||||
private _getViews(node: Node, orientation: Orientation, box: Box): GridNode {
|
||||
if (node instanceof LeafNode) {
|
||||
return { view: node.view, box };
|
||||
return { view: node.view, box, cachedVisibleSize: node.cachedVisibleSize };
|
||||
}
|
||||
|
||||
const children: GridNode[] = [];
|
||||
|
||||
@@ -236,7 +236,7 @@ class KeyboardController<T> implements IDisposable {
|
||||
private view: ListView<T>,
|
||||
options: IListOptions<T>
|
||||
) {
|
||||
const multipleSelectionSupport = !(options.multipleSelectionSupport === false);
|
||||
const multipleSelectionSupport = options.multipleSelectionSupport !== false;
|
||||
|
||||
this.openController = options.openController || DefaultOpenController;
|
||||
|
||||
|
||||
@@ -190,4 +190,4 @@ export class RangeMap {
|
||||
dispose() {
|
||||
this.groups = null!; // StrictNullOverride: nulling out ok in dispose
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/scrollbars';
|
||||
import { isEdgeOrIE } from 'vs/base/browser/browser';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
@@ -312,7 +313,7 @@ export abstract class AbstractScrollableElement extends Widget {
|
||||
this._onMouseWheel(new StandardWheelEvent(browserEvent));
|
||||
};
|
||||
|
||||
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'mousewheel', onMouseWheel));
|
||||
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,11 @@ interface ISashEvent {
|
||||
alt: boolean;
|
||||
}
|
||||
|
||||
type ViewItemSize = number | { cachedVisibleSize: number };
|
||||
|
||||
abstract class ViewItem {
|
||||
|
||||
private _size: number;
|
||||
set size(size: number) {
|
||||
this._size = size;
|
||||
}
|
||||
@@ -69,10 +72,11 @@ abstract class ViewItem {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
private cachedSize: number | undefined = undefined;
|
||||
private _cachedVisibleSize: number | undefined = undefined;
|
||||
get cachedVisibleSize(): number | undefined { return this._cachedVisibleSize; }
|
||||
|
||||
get visible(): boolean {
|
||||
return typeof this.cachedSize === 'undefined';
|
||||
return typeof this._cachedVisibleSize === 'undefined';
|
||||
}
|
||||
|
||||
set visible(visible: boolean) {
|
||||
@@ -81,10 +85,10 @@ abstract class ViewItem {
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
this.size = this.cachedSize!;
|
||||
this.cachedSize = undefined;
|
||||
this.size = clamp(this._cachedVisibleSize!, this.viewMinimumSize, this.viewMaximumSize);
|
||||
this._cachedVisibleSize = undefined;
|
||||
} else {
|
||||
this.cachedSize = this.size;
|
||||
this._cachedVisibleSize = this.size;
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
@@ -104,7 +108,20 @@ abstract class ViewItem {
|
||||
get priority(): LayoutPriority | undefined { return this.view.priority; }
|
||||
get snap(): boolean { return !!this.view.snap; }
|
||||
|
||||
constructor(protected container: HTMLElement, private view: IView, private _size: number, private disposable: IDisposable) {
|
||||
constructor(
|
||||
protected container: HTMLElement,
|
||||
private view: IView,
|
||||
size: ViewItemSize,
|
||||
private disposable: IDisposable
|
||||
) {
|
||||
if (typeof size === 'number') {
|
||||
this._size = size;
|
||||
this._cachedVisibleSize = undefined;
|
||||
} else {
|
||||
this._size = 0;
|
||||
this._cachedVisibleSize = size.cachedVisibleSize;
|
||||
}
|
||||
|
||||
dom.addClass(container, 'visible');
|
||||
}
|
||||
|
||||
@@ -166,11 +183,13 @@ enum State {
|
||||
|
||||
export type DistributeSizing = { type: 'distribute' };
|
||||
export type SplitSizing = { type: 'split', index: number };
|
||||
export type Sizing = DistributeSizing | SplitSizing;
|
||||
export type InvisibleSizing = { type: 'invisible', cachedVisibleSize: number };
|
||||
export type Sizing = DistributeSizing | SplitSizing | InvisibleSizing;
|
||||
|
||||
export namespace Sizing {
|
||||
export const Distribute: DistributeSizing = { type: 'distribute' };
|
||||
export function Split(index: number): SplitSizing { return { type: 'split', index }; }
|
||||
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
|
||||
}
|
||||
|
||||
export class SplitView extends Disposable {
|
||||
@@ -279,12 +298,14 @@ export class SplitView extends Disposable {
|
||||
const containerDisposable = toDisposable(() => this.viewContainer.removeChild(container));
|
||||
const disposable = combinedDisposable(onChangeDisposable, containerDisposable);
|
||||
|
||||
let viewSize: number;
|
||||
let viewSize: ViewItemSize;
|
||||
|
||||
if (typeof size === 'number') {
|
||||
viewSize = size;
|
||||
} else if (size.type === 'split') {
|
||||
viewSize = this.getViewSize(size.index) / 2;
|
||||
} else if (size.type === 'invisible') {
|
||||
viewSize = { cachedVisibleSize: size.cachedVisibleSize };
|
||||
} else {
|
||||
viewSize = view.minimumSize;
|
||||
}
|
||||
@@ -315,7 +336,24 @@ export class SplitView extends Disposable {
|
||||
const onChangeDisposable = onChange(this.onSashChange, this);
|
||||
const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash));
|
||||
const onEndDisposable = onEnd(this.onSashEnd, this);
|
||||
const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash)));
|
||||
|
||||
const onDidResetDisposable = sash.onDidReset(() => {
|
||||
const index = firstIndex(this.sashItems, item => item.sash === sash);
|
||||
const upIndexes = range(index, -1);
|
||||
const downIndexes = range(index + 1, this.viewItems.length);
|
||||
const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
|
||||
const snapAfterIndex = this.findFirstSnapIndex(downIndexes);
|
||||
|
||||
if (typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._onDidSashReset.fire(index);
|
||||
});
|
||||
|
||||
const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash);
|
||||
const sashItem: ISashItem = { sash, disposable };
|
||||
@@ -420,6 +458,15 @@ export class SplitView extends Disposable {
|
||||
this.layoutViews();
|
||||
}
|
||||
|
||||
getViewCachedVisibleSize(index: number): number | undefined {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
throw new Error('Index out of bounds');
|
||||
}
|
||||
|
||||
const viewItem = this.viewItems[index];
|
||||
return viewItem.cachedVisibleSize;
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
const previousSize = Math.max(this.size, this.contentSize);
|
||||
this.size = size;
|
||||
@@ -600,10 +647,19 @@ export class SplitView extends Disposable {
|
||||
}
|
||||
|
||||
distributeViewSizes(): void {
|
||||
const size = Math.floor(this.size / this.viewItems.length);
|
||||
const flexibleViewItems: ViewItem[] = [];
|
||||
let flexibleSize = 0;
|
||||
|
||||
for (let i = 0; i < this.viewItems.length; i++) {
|
||||
const item = this.viewItems[i];
|
||||
for (const item of this.viewItems) {
|
||||
if (item.maximumSize - item.minimumSize > 0) {
|
||||
flexibleViewItems.push(item);
|
||||
flexibleSize += item.size;
|
||||
}
|
||||
}
|
||||
|
||||
const size = Math.floor(flexibleSize / flexibleViewItems.length);
|
||||
|
||||
for (const item of flexibleViewItems) {
|
||||
item.size = clamp(size, item.minimumSize, item.maximumSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -1183,7 +1183,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
private _options: IAbstractTreeOptions<T, TFilterData> = {}
|
||||
) {
|
||||
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);
|
||||
|
||||
@@ -320,7 +320,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
get onDidFocus(): Event<void> { return this.tree.onDidFocus; }
|
||||
get onDidBlur(): Event<void> { return this.tree.onDidBlur; }
|
||||
|
||||
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T>, TFilterData>> { return this.tree.onDidChangeCollapseState; }
|
||||
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T> | null, TFilterData>> { return this.tree.onDidChangeCollapseState; }
|
||||
|
||||
get onDidUpdateOptions(): Event<IAsyncDataTreeOptionsUpdate> { return this.tree.onDidUpdateOptions; }
|
||||
|
||||
@@ -340,7 +340,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
private dataSource: IAsyncDataSource<TInput, T>,
|
||||
options: IAsyncDataTreeOptions<T, TFilterData> = {}
|
||||
) {
|
||||
|
||||
56
src/vs/base/browser/ui/tree/compressedObjectTree.ts
Normal file
56
src/vs/base/browser/ui/tree/compressedObjectTree.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Iterator, ISequence } from 'vs/base/common/iterator';
|
||||
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { ISpliceable } from 'vs/base/common/sequence';
|
||||
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { CompressedTreeModel, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
|
||||
|
||||
export interface IObjectTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
|
||||
sorter?: ITreeSorter<T>;
|
||||
}
|
||||
|
||||
export class CompressedObjectTree<T extends NonNullable<any>, TFilterData = void> extends AbstractTree<ICompressedTreeNode<T> | null, TFilterData, T | null> {
|
||||
|
||||
protected model: CompressedTreeModel<T, TFilterData>;
|
||||
|
||||
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<ICompressedTreeNode<T> | null, TFilterData>> { return this.model.onDidChangeCollapseState; }
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<ICompressedTreeNode<T>>,
|
||||
renderers: ITreeRenderer<ICompressedTreeNode<T>, TFilterData, any>[],
|
||||
options: IObjectTreeOptions<ICompressedTreeNode<T>, TFilterData> = {}
|
||||
) {
|
||||
super(container, delegate, renderers, options);
|
||||
}
|
||||
|
||||
setChildren(
|
||||
element: T | null,
|
||||
children?: ISequence<ITreeElement<T>>
|
||||
): Iterator<ITreeElement<T | null>> {
|
||||
return this.model.setChildren(element, children);
|
||||
}
|
||||
|
||||
rerender(element?: T): void {
|
||||
if (element === undefined) {
|
||||
this.view.rerender();
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.rerender(element);
|
||||
}
|
||||
|
||||
resort(element: T, recursive = true): void {
|
||||
this.model.resort(element, recursive);
|
||||
}
|
||||
|
||||
protected createModel(view: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, options: IObjectTreeOptions<ICompressedTreeNode<T>, TFilterData>): ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
|
||||
return new CompressedTreeModel(view, options);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence';
|
||||
import { Iterator, ISequence } from 'vs/base/common/iterator';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IObjectTreeModelOptions, ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
|
||||
import { IObjectTreeModelOptions, ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
|
||||
|
||||
export interface ICompressedTreeElement<T> extends ITreeElement<T> {
|
||||
readonly children?: Iterator<ICompressedTreeElement<T>> | ICompressedTreeElement<T>[];
|
||||
@@ -80,9 +80,9 @@ export function splice<T>(treeElement: ICompressedTreeElement<T>, element: T, ch
|
||||
};
|
||||
}
|
||||
|
||||
export interface ICompressedObjectTreeModelOptions<T, TFilterData> extends IObjectTreeModelOptions<ICompressedTreeNode<T>, TFilterData> { }
|
||||
export interface ICompressedTreeModelOptions<T, TFilterData> extends IObjectTreeModelOptions<ICompressedTreeNode<T>, TFilterData> { }
|
||||
|
||||
export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
|
||||
export class CompressedTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<ICompressedTreeNode<T> | null, TFilterData, T | null> {
|
||||
|
||||
readonly rootRef = null;
|
||||
|
||||
@@ -95,7 +95,7 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
|
||||
|
||||
get size(): number { return this.nodes.size; }
|
||||
|
||||
constructor(list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, options: ICompressedObjectTreeModelOptions<T, TFilterData> = {}) {
|
||||
constructor(list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>, options: ICompressedTreeModelOptions<T, TFilterData> = {}) {
|
||||
this.model = new ObjectTreeModel(list, options);
|
||||
}
|
||||
|
||||
@@ -289,11 +289,11 @@ function createNodeMapper<T, TFilterData>(elementMapper: ElementMapper<T>): Node
|
||||
return node => mapNode(elementMapper, node);
|
||||
}
|
||||
|
||||
export interface ILinearCompressedObjectTreeModelOptions<T, TFilterData> extends ICompressedObjectTreeModelOptions<T, TFilterData> {
|
||||
export interface ICompressedObjectTreeModelOptions<T, TFilterData> extends ICompressedTreeModelOptions<T, TFilterData> {
|
||||
readonly elementMapper?: ElementMapper<T>;
|
||||
}
|
||||
|
||||
export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {
|
||||
export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements IObjectTreeModel<T, TFilterData> {
|
||||
|
||||
readonly rootRef = null;
|
||||
|
||||
@@ -317,15 +317,25 @@ export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilter
|
||||
|
||||
private mapElement: ElementMapper<T | null>;
|
||||
private mapNode: NodeMapper<T | null, TFilterData>;
|
||||
private model: CompressedObjectTreeModel<T, TFilterData>;
|
||||
private model: CompressedTreeModel<T, TFilterData>;
|
||||
|
||||
constructor(
|
||||
list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>,
|
||||
options: ILinearCompressedObjectTreeModelOptions<T, TFilterData> = {}
|
||||
options: ICompressedObjectTreeModelOptions<T, TFilterData> = {}
|
||||
) {
|
||||
this.mapElement = options.elementMapper || DefaultElementMapper;
|
||||
this.mapNode = createNodeMapper(this.mapElement);
|
||||
this.model = new CompressedObjectTreeModel(list, options);
|
||||
this.model = new CompressedTreeModel(list, options);
|
||||
}
|
||||
|
||||
setChildren(
|
||||
element: T | null,
|
||||
children: ISequence<ITreeElement<T>> | undefined
|
||||
): Iterator<ITreeElement<T>> {
|
||||
this.model.setChildren(element, children);
|
||||
|
||||
// TODO
|
||||
return Iterator.empty();
|
||||
}
|
||||
|
||||
getListIndex(location: T | null): number {
|
||||
@@ -401,4 +411,8 @@ export class LinearCompressedObjectTreeModel<T extends NonNullable<any>, TFilter
|
||||
refilter(): void {
|
||||
return this.model.refilter();
|
||||
}
|
||||
|
||||
resort(element: T | null = null, recursive = true): void {
|
||||
return this.model.resort(element, recursive);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
private dataSource: IDataSource<TInput, T>,
|
||||
options: IDataTreeOptions<T, TFilterData> = {}
|
||||
) {
|
||||
|
||||
@@ -20,7 +20,7 @@ export class IndexTree<T, TFilterData = void> extends AbstractTree<T, TFilterDat
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
private rootElement: T,
|
||||
options: IIndexTreeOptions<T, TFilterData> = {}
|
||||
) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Iterator, ISequence } from 'vs/base/common/iterator';
|
||||
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { ISpliceable } from 'vs/base/common/sequence';
|
||||
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
|
||||
import { ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
@@ -17,14 +17,14 @@ export interface IObjectTreeOptions<T, TFilterData = void> extends IAbstractTree
|
||||
|
||||
export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends AbstractTree<T | null, TFilterData, T | null> {
|
||||
|
||||
protected model: ObjectTreeModel<T, TFilterData>;
|
||||
protected model: IObjectTreeModel<T, TFilterData>;
|
||||
|
||||
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T, TFilterData>> { return this.model.onDidChangeCollapseState; }
|
||||
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T | null, TFilterData>> { return this.model.onDidChangeCollapseState; }
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
options: IObjectTreeOptions<T, TFilterData> = {}
|
||||
) {
|
||||
super(container, delegate, renderers, options);
|
||||
@@ -32,11 +32,9 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
|
||||
|
||||
setChildren(
|
||||
element: T | null,
|
||||
children?: ISequence<ITreeElement<T>>,
|
||||
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
|
||||
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
|
||||
children?: ISequence<ITreeElement<T>>
|
||||
): Iterator<ITreeElement<T | null>> {
|
||||
return this.model.setChildren(element, children, onDidCreateNode, onDidDeleteNode);
|
||||
return this.model.setChildren(element, children);
|
||||
}
|
||||
|
||||
rerender(element?: T): void {
|
||||
|
||||
@@ -10,12 +10,19 @@ import { Event } from 'vs/base/common/event';
|
||||
import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
|
||||
export type ITreeNodeCallback<T, TFilterData> = (node: ITreeNode<T, TFilterData>) => void;
|
||||
|
||||
export interface IObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> extends ITreeModel<T | null, TFilterData, T | null> {
|
||||
setChildren(element: T | null, children: ISequence<ITreeElement<T>> | undefined): Iterator<ITreeElement<T>>;
|
||||
resort(element?: T | null, recursive?: boolean): void;
|
||||
}
|
||||
|
||||
export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> {
|
||||
readonly sorter?: ITreeSorter<T>;
|
||||
readonly identityProvider?: IIdentityProvider<T>;
|
||||
}
|
||||
|
||||
export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {
|
||||
export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements IObjectTreeModel<T, TFilterData> {
|
||||
|
||||
readonly rootRef = null;
|
||||
|
||||
@@ -51,8 +58,8 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
|
||||
setChildren(
|
||||
element: T | null,
|
||||
children: ISequence<ITreeElement<T>> | undefined,
|
||||
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
|
||||
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
|
||||
onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
|
||||
onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
|
||||
): Iterator<ITreeElement<T>> {
|
||||
const location = this.getElementLocation(element);
|
||||
return this._setChildren(location, this.preserveCollapseState(children), onDidCreateNode, onDidDeleteNode);
|
||||
@@ -61,8 +68,8 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
|
||||
private _setChildren(
|
||||
location: number[],
|
||||
children: ISequence<ITreeElement<T>> | undefined,
|
||||
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
|
||||
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
|
||||
onDidCreateNode?: ITreeNodeCallback<T, TFilterData>,
|
||||
onDidDeleteNode?: ITreeNodeCallback<T, TFilterData>
|
||||
): Iterator<ITreeElement<T>> {
|
||||
const insertedElements = new Set<T | null>();
|
||||
const insertedElementIds = new Set<string>();
|
||||
|
||||
Reference in New Issue
Block a user