提交 91ceb9d4 编写于 作者: J Joao Moreno

tree.autoExpandSingleChildren

fixes #64888
上级 f6a7ab12
......@@ -277,9 +277,10 @@ function asTreeContextMenuEvent<T>(event: IListContextMenuEvent<ITreeNode<T, any
}
export interface IAbstractTreeOptions<T, TFilterData = void> extends IListOptions<T> {
collapseByDefault?: boolean; // defaults to false
filter?: ITreeFilter<T, TFilterData>;
readonly collapseByDefault?: boolean; // defaults to false
readonly filter?: ITreeFilter<T, TFilterData>;
readonly dnd?: ITreeDragAndDrop<T>;
readonly autoExpandSingleChildren?: boolean;
}
export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable {
......
......@@ -183,6 +183,7 @@ class AsyncDataTreeNodeListDragAndDrop<TInput, T> implements IListDragAndDrop<IA
function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOptions<T, TFilterData>): IObjectTreeOptions<IAsyncDataTreeNode<TInput, T>, TFilterData> | undefined {
return options && {
...options,
collapseByDefault: true,
identityProvider: options.identityProvider && {
getId(el) {
return options.identityProvider!.getId(el.element as T);
......@@ -230,6 +231,7 @@ function asTreeElement<TInput, T>(node: IAsyncDataTreeNode<TInput, T>): ITreeEle
export interface IAsyncDataTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
identityProvider?: IIdentityProvider<T>;
sorter?: ITreeSorter<T>;
autoExpandSingleChildren?: boolean;
}
export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable {
......@@ -239,6 +241,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
private readonly nodes = new Map<null | T, IAsyncDataTreeNode<TInput, T>>();
private readonly refreshPromises = new Map<IAsyncDataTreeNode<TInput, T>, CancelablePromise<T[]>>();
private readonly identityProvider?: IIdentityProvider<T>;
private readonly autoExpandSingleChildren: boolean;
private readonly _onDidChangeNodeState = new Emitter<IAsyncDataTreeNode<TInput, T>>();
......@@ -261,14 +264,14 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
private dataSource: IAsyncDataSource<TInput, T>,
options?: IAsyncDataTreeOptions<T, TFilterData>
options: IAsyncDataTreeOptions<T, TFilterData> = {}
) {
this.identityProvider = options && options.identityProvider;
this.identityProvider = options.identityProvider;
this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren;
const objectTreeDelegate = new ComposedTreeDelegate<TInput | T, IAsyncDataTreeNode<TInput, T>>(delegate);
const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event));
const objectTreeOptions = asObjectTreeOptions<TInput, T, TFilterData>(options) || {};
objectTreeOptions.collapseByDefault = true;
this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
......@@ -371,7 +374,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
async expand(element: T, recursive: boolean = false): Promise<boolean> {
const node = this.getDataNode(element);
if (!this.tree.isCollapsed(node === this.root ? null : node)) {
if (node !== this.root && !this.tree.isCollapsed(node === this.root ? null : node)) {
return false;
}
......@@ -536,48 +539,59 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
return always(result, () => this.currentRefreshCalls.delete(node));
}
private doRefresh(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
private async doRefresh(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
const hasChildren = !!this.dataSource.hasChildren(node.element!);
if (!hasChildren) {
this.setChildren(node, [], recursive);
return Promise.resolve();
} else if (node !== this.root && (!this.tree.isCollapsible(node) || this.tree.isCollapsed(node))) {
return;
}
if (node !== this.root && (!this.tree.isCollapsible(node) || this.tree.isCollapsed(node))) {
node.state = AsyncDataTreeNodeState.Uninitialized;
return Promise.resolve();
} else {
node.state = AsyncDataTreeNodeState.Loading;
return;
}
node.state = AsyncDataTreeNodeState.Loading;
this._onDidChangeNodeState.fire(node);
const slowTimeout = timeout(800);
slowTimeout.then(() => {
node.state = AsyncDataTreeNodeState.Slow;
this._onDidChangeNodeState.fire(node);
}, _ => null);
const slowTimeout = timeout(800);
try {
const children = await this.doGetChildren(node);
slowTimeout.cancel();
node.state = AsyncDataTreeNodeState.Loaded;
this._onDidChangeNodeState.fire(node);
slowTimeout.then(() => {
node.state = AsyncDataTreeNodeState.Slow;
this._onDidChangeNodeState.fire(node);
}, _ => null);
this.setChildren(node, children, recursive);
return Promise.resolve(this.doGetChildren(node))
.then(children => {
slowTimeout.cancel();
node.state = AsyncDataTreeNodeState.Loaded;
this._onDidChangeNodeState.fire(node);
if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) {
const treeNode = this.tree.getNode(node);
const visibleChildren = treeNode.children.filter(node => node.visible);
this.setChildren(node, children, recursive);
}, err => {
if (isPromiseCanceledError(err)) {
return Promise.resolve(null);
}
if (visibleChildren.length === 1) {
this.tree.expand(visibleChildren[0].element, false);
}
}
} catch (err) {
if (isPromiseCanceledError(err)) {
return;
}
slowTimeout.cancel();
node.state = AsyncDataTreeNodeState.Uninitialized;
this._onDidChangeNodeState.fire(node);
slowTimeout.cancel();
node.state = AsyncDataTreeNodeState.Uninitialized;
this._onDidChangeNodeState.fire(node);
if (node !== this.root) {
this.tree.collapse(node === this.root ? null : node);
}
if (node !== this.root) {
this.tree.collapse(node === this.root ? null : node);
}
return Promise.reject(err);
});
throw err;
}
}
......
......@@ -39,8 +39,9 @@ function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility {
}
export interface IIndexTreeModelOptions<T, TFilterData> {
collapseByDefault?: boolean; // defaults to false
filter?: ITreeFilter<T, TFilterData>;
readonly collapseByDefault?: boolean; // defaults to false
readonly filter?: ITreeFilter<T, TFilterData>;
readonly autoExpandSingleChildren?: boolean;
}
export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = void> implements ITreeModel<T, TFilterData, number[]> {
......@@ -58,10 +59,12 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
private collapseByDefault: boolean;
private filter?: ITreeFilter<T, TFilterData>;
private autoExpandSingleChildren: boolean;
constructor(private list: ISpliceable<ITreeNode<T, TFilterData>>, rootElement: T, options: IIndexTreeModelOptions<T, TFilterData> = {}) {
this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault;
this.filter = options.filter;
this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren;
// this.onDidChangeCollapseState(node => console.log(node.collapsed, node));
......@@ -140,18 +143,45 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
}
setCollapsed(location: number[], collapsed?: boolean, recursive?: boolean): boolean {
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
const node = this.getTreeNode(location);
if (typeof collapsed === 'undefined') {
collapsed = !node.collapsed;
}
return this.eventBufferer.bufferEvents(() => {
return this._setCollapsed(node, listIndex, revealed, collapsed!, recursive || false);
});
return this.eventBufferer.bufferEvents(() => this._setCollapsed(location, collapsed!, recursive));
}
private _setCollapsed(location: number[], collapsed: boolean, recursive?: boolean): boolean {
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
const result = this._setListNodeCollapsed(node, listIndex, revealed, collapsed!, recursive || false);
if (this.autoExpandSingleChildren && !collapsed! && !recursive) {
let onlyVisibleChildIndex = -1;
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
if (child.visible) {
if (onlyVisibleChildIndex > -1) {
onlyVisibleChildIndex = -1;
break;
} else {
onlyVisibleChildIndex = i;
}
}
}
if (onlyVisibleChildIndex > -1) {
this._setCollapsed([...location, onlyVisibleChildIndex], false, false);
}
}
return result;
}
private _setCollapsed(node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean {
private _setListNodeCollapsed(node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean {
const result = this._setNodeCollapsed(node, collapsed, recursive, false);
if (!revealed || !node.visible) {
......
......@@ -10,7 +10,7 @@ import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> {
sorter?: ITreeSorter<T>;
readonly sorter?: ITreeSorter<T>;
}
export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {
......
......@@ -251,7 +251,8 @@ export class ExplorerView extends ViewletPanel {
multipleSelectionSupport: true,
filter: this.filter,
sorter: this.instantiationService.createInstance(FileSorter),
dnd: this.instantiationService.createInstance(FileDragAndDrop)
dnd: this.instantiationService.createInstance(FileDragAndDrop),
autoExpandSingleChildren: true
}, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService);
this.disposables.push(this.tree);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册