diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index b373b3bf46ed68e7974f538dd63bec0e8d652dad..09cc5afa4575f373b98a0d5045ffaa7bd0ccf51e 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -11,7 +11,7 @@ import { append, $, toggleClass } from 'vs/base/browser/dom'; import { Event, Relay } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; function asListOptions(options?: IAbstractTreeOptions): IListOptions> | undefined { @@ -74,11 +74,11 @@ class TreeRenderer implements IListRenderer, - onDidChangeCollapseState: Event> + onDidChangeCollapseState: Event> ) { this.templateId = renderer.templateId; - onDidChangeCollapseState(this.onDidChangeNodeTwistieState, this, this.disposables); + Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables); if (renderer.onDidChangeTwistieState) { renderer.onDidChangeTwistieState(this.onDidChangeTwistieState, this, this.disposables); @@ -204,7 +204,7 @@ export abstract class AbstractTree implements IDisposable get onDidFocus(): Event { return this.view.onDidFocus; } get onDidBlur(): Event { return this.view.onDidBlur; } - get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } + get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } get onDidChangeRenderNodeCount(): Event> { return this.model.onDidChangeRenderNodeCount; } get onDidDispose(): Event { return this.view.onDidDispose; } @@ -217,7 +217,7 @@ export abstract class AbstractTree implements IDisposable ) { const treeDelegate = new ComposedTreeDelegate>(delegate); - const onDidChangeCollapseStateRelay = new Relay>(); + const onDidChangeCollapseStateRelay = new Relay>(); const treeRenderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event)); this.disposables.push(...treeRenderers); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index c7dd1f4055b71c1a1cd6138a383a939faa4fa37d..4616054d67d73526e6fb95fa44568f7e5b645d8e 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -6,7 +6,7 @@ import { ComposedTreeDelegate, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { timeout, always } from 'vs/base/common/async'; @@ -193,7 +193,6 @@ export class AsyncDataTree, TFilterData = void> imple get onDidChangeFocus(): Event> { return Event.map(this.tree.onDidChangeFocus, asTreeEvent); } get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } get onDidOpen(): Event> { return Event.map(this.tree.onDidOpen, asTreeEvent); } - get onDidChangeCollapseState(): Event { return Event.map(this.tree.onDidChangeCollapseState, e => e.element!.element!); } private readonly _onDidResolveChildren = new Emitter>(); readonly onDidResolveChildren: Event> = this._onDidResolveChildren.event; @@ -496,9 +495,13 @@ export class AsyncDataTree, TFilterData = void> imple } } - private _onDidChangeCollapseState(treeNode: ITreeNode, any>): void { - if (!treeNode.collapsed && treeNode.element.state === AsyncDataTreeNodeState.Uninitialized) { - this.refreshNode(treeNode.element, false, ChildrenResolutionReason.Expand); + private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void { + if (!node.collapsed && node.element.state === AsyncDataTreeNodeState.Uninitialized) { + if (deep) { + this.collapse(node.element.element!); + } else { + this.refreshNode(node.element, false, ChildrenResolutionReason.Expand); + } } } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 2611366ba38ee733f38ea14b43044f0f4a330076..3df1cdee8e851801e3be87d3dc75e61e3d28271b 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { Iterator, ISequence } from 'vs/base/common/iterator'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; import { tail2 } from 'vs/base/common/arrays'; -import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; interface IMutableTreeNode extends ITreeNode { readonly parent: IMutableTreeNode | undefined; @@ -48,10 +48,10 @@ export class IndexTreeModel, TFilterData = voi readonly rootRef = []; private root: IMutableTreeNode; - private eventBufferer = new EventBufferer(); // TODO@joao is this really necessary + private eventBufferer = new EventBufferer(); - private _onDidChangeCollapseState = new Emitter>(); - readonly onDidChangeCollapseState: Event> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event); + private _onDidChangeCollapseState = new Emitter>(); + readonly onDidChangeCollapseState: Event> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event); private _onDidChangeRenderNodeCount = new Emitter>(); readonly onDidChangeRenderNodeCount: Event> = this.eventBufferer.wrapEvent(this._onDidChangeRenderNodeCount.event); @@ -63,6 +63,8 @@ export class IndexTreeModel, TFilterData = voi this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault; this.filter = options.filter; + // this.onDidChangeCollapseState(node => console.log(node.collapsed, node)); + this.root = { parent: undefined, element: rootElement, @@ -140,11 +142,13 @@ export class IndexTreeModel, TFilterData = voi collapsed = !node.collapsed; } - return this._setCollapsed(node, listIndex, revealed, collapsed, recursive || false); + return this.eventBufferer.bufferEvents(() => { + return this._setCollapsed(node, listIndex, revealed, collapsed!, recursive || false); + }); } private _setCollapsed(node: IMutableTreeNode, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean { - const result = this._setNodeCollapsed(node, collapsed, recursive); + const result = this._setNodeCollapsed(node, collapsed, recursive, false); if (!revealed || !node.visible) { return result; @@ -152,25 +156,26 @@ export class IndexTreeModel, TFilterData = voi const previousRenderNodeCount = node.renderNodeCount; const toInsert = this.updateNodeAfterCollapseChange(node); - const deleteCount = previousRenderNodeCount - (listIndex === -1 ? 0 : 1); - this.list.splice(listIndex + 1, deleteCount, toInsert.slice(1)); - this._onDidChangeCollapseState.fire(node); return result; } - private _setNodeCollapsed(node: IMutableTreeNode, collapsed: boolean, recursive: boolean): boolean { + private _setNodeCollapsed(node: IMutableTreeNode, collapsed: boolean, recursive: boolean, deep: boolean): boolean { let result = node.collapsible && node.collapsed !== collapsed; if (node.collapsible) { node.collapsed = collapsed; + + if (result) { + this._onDidChangeCollapseState.fire({ node, deep }); + } } if (recursive) { for (const child of node.children) { - result = this._setNodeCollapsed(child, collapsed, true) || result; + result = this._setNodeCollapsed(child, collapsed, true, true) || result; } } diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 7d5509446b2c83ac62528171b1e42d89410c4bae..c8cc6dbc25380790e0963d5d9b555a1308b002fb 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterator'; import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; -import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { sorter?: ITreeSorter; @@ -21,14 +21,14 @@ export class ObjectTreeModel, TFilterData extends Non private nodes = new Map>(); private sorter?: ITreeSorter>; - readonly onDidChangeCollapseState: Event>; + readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; get size(): number { return this.nodes.size; } constructor(list: ISpliceable>, options: IObjectTreeModelOptions = {}) { this.model = new IndexTreeModel(list, null, options); - this.onDidChangeCollapseState = this.model.onDidChangeCollapseState as Event>; + this.onDidChangeCollapseState = this.model.onDidChangeCollapseState as Event>; this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount as Event>; if (options.sorter) { diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 1614677ea8909fa75661cec4e399c1cbee67792f..74fb75b681702e520180ae48bb03f414d689d9d8 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -89,9 +89,14 @@ export interface ITreeNode { readonly filterData: TFilterData | undefined; } +export interface ICollapseStateChangeEvent { + node: ITreeNode; + deep: boolean; +} + export interface ITreeModel { readonly rootRef: TRef; - readonly onDidChangeCollapseState: Event>; + readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; getListIndex(location: TRef): number;