提交 d1e4efdc 编写于 作者: J Joao Moreno

tree: do not rerender on collapse state change

上级 ba46974d
......@@ -4,12 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./tree';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IListOptions, List, IIdentityProvider, IMultipleSelectionController } from 'vs/base/browser/ui/list/listWidget';
import { TreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/treeModel';
import { IIterator, empty } from 'vs/base/common/iterator';
import { IDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list';
import { append, $ } from 'vs/base/browser/dom';
import { Event, Relay } from 'vs/base/common/event';
function toTreeListOptions<T>(options?: IListOptions<T>): IListOptions<ITreeNode<T>> {
if (!options) {
......@@ -56,15 +57,22 @@ class TreeDelegate<T> implements IDelegate<ITreeNode<T>> {
interface ITreeListTemplateData<T> {
twistie: HTMLElement;
elementDisposable: IDisposable;
templateData: T;
}
class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeListTemplateData<TTemplateData>> {
readonly templateId: string;
private renderedNodes = new Map<ITreeNode<T>, ITreeListTemplateData<TTemplateData>>();
private disposables: IDisposable[] = [];
constructor(private renderer: IRenderer<T, TTemplateData>) {
constructor(
private renderer: IRenderer<T, TTemplateData>,
onDidChangeCollapseState: Event<ITreeNode<T>>
) {
this.templateId = renderer.templateId;
onDidChangeCollapseState(this.onDidChangeCollapseState, this, this.disposables);
}
renderTemplate(container: HTMLElement): ITreeListTemplateData<TTemplateData> {
......@@ -73,20 +81,39 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis
const contents = append(el, $('.tl-contents'));
const templateData = this.renderer.renderTemplate(contents);
return { twistie, templateData };
return { twistie, elementDisposable: EmptyDisposable, templateData };
}
renderElement(element: ITreeNode<T>, index: number, templateData: ITreeListTemplateData<TTemplateData>): void {
const { twistie } = templateData;
twistie.innerText = element.children.length === 0 ? '' : (element.collapsed ? '' : '');
twistie.style.width = `${10 + element.depth * 10}px`;
renderElement(node: ITreeNode<T>, index: number, templateData: ITreeListTemplateData<TTemplateData>): void {
templateData.elementDisposable.dispose();
this.renderedNodes.set(node, templateData);
templateData.elementDisposable = toDisposable(() => this.renderedNodes.delete(node));
this.renderer.renderElement(element.element, index, templateData.templateData);
templateData.twistie.innerText = node.children.length === 0 ? '' : (node.collapsed ? '' : '');
templateData.twistie.style.width = `${10 + node.depth * 10}px`;
this.renderer.renderElement(node.element, index, templateData.templateData);
}
disposeTemplate(templateData: ITreeListTemplateData<TTemplateData>): void {
this.renderer.disposeTemplate(templateData.templateData);
}
private onDidChangeCollapseState(node: ITreeNode<T>): void {
const templateData = this.renderedNodes.get(node);
if (!templateData) {
return;
}
templateData.twistie.innerText = node.children.length === 0 ? '' : (node.collapsed ? '' : '');
}
dispose(): void {
this.renderedNodes.clear();
this.disposables = dispose(this.disposables);
}
}
function getLocation<T>(node: ITreeNode<T>): number[] {
......@@ -113,11 +140,16 @@ export class Tree<T> implements IDisposable {
options?: IListOptions<T>
) {
const treeDelegate = new TreeDelegate(delegate);
const treeRenderers = renderers.map(r => new TreeRenderer(r));
const onDidChangeCollapseStateRelay = new Relay<ITreeNode<T>>();
const treeRenderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event));
this.disposables.push(...treeRenderers);
const treeOptions = toTreeListOptions(options);
this.view = new List(container, treeDelegate, treeRenderers, treeOptions);
this.model = new TreeModel<T>(this.view);
onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState;
this.view.onMouseClick(this.onMouseClick, this, this.disposables);
}
......
......@@ -8,6 +8,7 @@
import { ISpliceable } from 'vs/base/common/sequence';
import { IIterator, map, collect, iter, empty } from 'vs/base/common/iterator';
import { last } from 'vs/base/common/arrays';
import { Emitter, Event } from 'vs/base/common/event';
export interface ITreeElement<T> {
readonly element: T;
......@@ -93,6 +94,9 @@ export class TreeModel<T> {
visibleCount: 1
};
private _onDidChangeCollapseState = new Emitter<ITreeNode<T>>();
readonly onDidChangeCollapseState: Event<ITreeNode<T>> = this._onDidChangeCollapseState.event;
constructor(private list: ISpliceable<ITreeNode<T>>) { }
splice(location: number[], deleteCount: number, toInsert?: IIterator<ITreeElement<T>> | ITreeElement<T>[]): IIterator<ITreeElement<T>> {
......@@ -125,7 +129,7 @@ export class TreeModel<T> {
}
private _setCollapsed(location: number[], collapsed?: boolean | undefined): void {
let { node, listIndex, visible } = this.findNode(location);
const { node, listIndex, visible } = this.findNode(location);
if (typeof collapsed === 'undefined') {
collapsed = !node.collapsed;
......@@ -143,19 +147,23 @@ export class TreeModel<T> {
if (collapsed) {
const deleteCount = getVisibleCount(node.children);
this.list.splice(listIndex, 1 + deleteCount, [node]);
this.list.splice(listIndex + 1, deleteCount, []);
visibleCountDiff = -deleteCount;
} else {
const toInsert = [node, ...getVisibleNodes(node.children)];
const toInsert = getVisibleNodes(node.children);
this.list.splice(listIndex, 1, toInsert);
visibleCountDiff = toInsert.length - 1;
this.list.splice(listIndex + 1, 0, toInsert);
visibleCountDiff = toInsert.length;
}
while (node) {
node.visibleCount += visibleCountDiff;
node = node.parent;
let mutableNode = node;
while (mutableNode) {
mutableNode.visibleCount += visibleCountDiff;
mutableNode = mutableNode.parent;
}
this._onDidChangeCollapseState.fire(node);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册