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

tree: introduce filter

上级 fe8d9d24
......@@ -15,13 +15,13 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { tail2 } from 'vs/base/common/arrays';
function toTreeListOptions<T>(options?: IListOptions<T>): IListOptions<ITreeNode<T>> {
function toTreeListOptions<T>(options?: IListOptions<T>): IListOptions<ITreeNode<T, any>> {
if (!options) {
return undefined;
}
let identityProvider: IIdentityProvider<ITreeNode<T>> | undefined = undefined;
let multipleSelectionController: IMultipleSelectionController<ITreeNode<T>> | undefined = undefined;
let identityProvider: IIdentityProvider<ITreeNode<T, any>> | undefined = undefined;
let multipleSelectionController: IMultipleSelectionController<ITreeNode<T, any>> | undefined = undefined;
if (options.identityProvider) {
identityProvider = el => options.identityProvider(el.element);
......@@ -45,15 +45,15 @@ function toTreeListOptions<T>(options?: IListOptions<T>): IListOptions<ITreeNode
};
}
class TreeDelegate<T> implements IVirtualDelegate<ITreeNode<T>> {
class TreeDelegate<T> implements IVirtualDelegate<ITreeNode<T, any>> {
constructor(private delegate: IVirtualDelegate<T>) { }
getHeight(element: ITreeNode<T>): number {
getHeight(element: ITreeNode<T, any>): number {
return this.delegate.getHeight(element.element);
}
getTemplateId(element: ITreeNode<T>): string {
getTemplateId(element: ITreeNode<T, any>): string {
return this.delegate.getTemplateId(element.element);
}
}
......@@ -64,7 +64,7 @@ interface ITreeListTemplateData<T> {
templateData: T;
}
function renderTwistie<T>(node: ITreeNode<T>, twistie: HTMLElement): void {
function renderTwistie<T>(node: ITreeNode<T, any>, twistie: HTMLElement): void {
if (node.children.length === 0 && !node.collapsible) {
twistie.innerText = '';
} else {
......@@ -72,15 +72,15 @@ function renderTwistie<T>(node: ITreeNode<T>, twistie: HTMLElement): void {
}
}
class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeListTemplateData<TTemplateData>> {
class TreeRenderer<T, TFilterData, TTemplateData> implements IRenderer<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>> {
readonly templateId: string;
private renderedNodes = new Map<ITreeNode<T>, ITreeListTemplateData<TTemplateData>>();
private renderedNodes = new Map<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>>();
private disposables: IDisposable[] = [];
constructor(
private renderer: IRenderer<T, TTemplateData>,
onDidChangeCollapseState: Event<ITreeNode<T>>
onDidChangeCollapseState: Event<ITreeNode<T, TFilterData>>
) {
this.templateId = renderer.templateId;
onDidChangeCollapseState(this.onDidChangeCollapseState, this, this.disposables);
......@@ -96,7 +96,7 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis
return { twistie, count, templateData };
}
renderElement(node: ITreeNode<T>, index: number, templateData: ITreeListTemplateData<TTemplateData>): void {
renderElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>): void {
this.renderedNodes.set(node, templateData);
templateData.twistie.style.width = `${10 + node.depth * 10}px`;
......@@ -106,7 +106,7 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis
this.renderer.renderElement(node.element, index, templateData.templateData);
}
disposeElement(node: ITreeNode<T>): void {
disposeElement(node: ITreeNode<T, TFilterData>): void {
this.renderedNodes.delete(node);
}
......@@ -114,7 +114,7 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis
this.renderer.disposeTemplate(templateData.templateData);
}
private onDidChangeCollapseState(node: ITreeNode<T>): void {
private onDidChangeCollapseState(node: ITreeNode<T, TFilterData>): void {
const templateData = this.renderedNodes.get(node);
if (!templateData) {
......@@ -137,10 +137,10 @@ function isInputElement(e: HTMLElement): boolean {
export interface ITreeOptions<T> extends IListOptions<T> { }
export class Tree<T> implements IDisposable {
export class Tree<T, TFilterData = void> implements IDisposable {
private view: List<ITreeNode<T>>;
private model: TreeModel<T>;
private view: List<ITreeNode<T, TFilterData>>;
private model: TreeModel<T, TFilterData>;
private disposables: IDisposable[] = [];
constructor(
......@@ -151,14 +151,14 @@ export class Tree<T> implements IDisposable {
) {
const treeDelegate = new TreeDelegate(delegate);
const onDidChangeCollapseStateRelay = new Relay<ITreeNode<T>>();
const onDidChangeCollapseStateRelay = new Relay<ITreeNode<T, TFilterData>>();
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);
this.model = new TreeModel<T, TFilterData>(this.view);
onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState;
this.view.onMouseClick(this.onMouseClick, this, this.disposables);
......@@ -176,7 +176,7 @@ export class Tree<T> implements IDisposable {
return this.model.splice(location, deleteCount, toInsert);
}
private onMouseClick(e: IListMouseEvent<ITreeNode<T>>): void {
private onMouseClick(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
const node = e.element;
const location = getNodeLocation(node);
......
......@@ -16,28 +16,47 @@ export interface ITreeElement<T> {
readonly collapsed?: boolean;
}
export interface ITreeNode<T> {
readonly parent: ITreeNode<T> | undefined;
export interface ITreeNode<T, TFilterData = void> {
readonly parent: ITreeNode<T, TFilterData> | undefined;
readonly element: T;
readonly children: ITreeNode<T>[];
readonly children: ITreeNode<T, TFilterData>[];
readonly depth: number;
readonly collapsible: boolean;
readonly collapsed: boolean;
readonly visibleCount: number;
readonly visible: boolean;
readonly filterData: TFilterData | undefined;
}
interface IMutableTreeNode<T> extends ITreeNode<T> {
readonly parent: IMutableTreeNode<T> | undefined;
readonly children: IMutableTreeNode<T>[];
interface IMutableTreeNode<T, TFilterData> extends ITreeNode<T, TFilterData> {
readonly parent: IMutableTreeNode<T, TFilterData> | undefined;
readonly children: IMutableTreeNode<T, TFilterData>[];
collapsed: boolean;
visibleCount: number;
visible: boolean;
filterData: TFilterData | undefined;
}
function visibleCountReducer<T>(result: number, node: IMutableTreeNode<T>): number {
export const enum Visibility {
Hidden,
Visible,
Recurse // TODO@joao come up with a better name
}
export interface IFilterResult<TFilterData> {
visibility: Visibility;
data: TFilterData;
}
export interface IFilter<T, TFilterData> {
filter(element: T): Visibility | IFilterResult<TFilterData>;
}
function visibleCountReducer<T>(result: number, node: IMutableTreeNode<T, any>): number {
return result + (node.collapsed ? 1 : node.visibleCount);
}
function getVisibleCount<T>(nodes: IMutableTreeNode<T>[]): number {
function getVisibleCount<T>(nodes: IMutableTreeNode<T, any>[]): number {
return nodes.reduce(visibleCountReducer, 0);
}
......@@ -45,11 +64,11 @@ function getVisibleCount<T>(nodes: IMutableTreeNode<T>[]): number {
* Recursively updates the visibleCount of a subtree, while collecting
* all the visible nodes in an array.
*/
function updateVisibleCount<T>(node: IMutableTreeNode<T>): ITreeNode<T>[] {
function updateVisibleCount<T, TFilterData>(node: IMutableTreeNode<T, TFilterData>): ITreeNode<T, TFilterData>[] {
const previousVisibleCount = node.visibleCount;
const result: ITreeNode<T>[] = [];
const result: ITreeNode<T, TFilterData>[] = [];
function _updateVisibleCount(node: IMutableTreeNode<T>): number {
function _updateVisibleCount(node: IMutableTreeNode<T, TFilterData>): number {
result.push(node);
node.visibleCount = 1;
......@@ -85,10 +104,10 @@ function getTreeElementIterator<T>(elements: Iterator<ITreeElement<T>> | ITreeEl
}
}
function treeElementToNode<T>(treeElement: ITreeElement<T>, parent: IMutableTreeNode<T>, visible: boolean, treeListElements: ITreeNode<T>[]): IMutableTreeNode<T> {
function treeElementToNode<T, TFilterData>(treeElement: ITreeElement<T>, parent: IMutableTreeNode<T, TFilterData>, visible: boolean, treeListElements: ITreeNode<T, TFilterData>[]): IMutableTreeNode<T, TFilterData> {
const depth = parent.depth + 1;
const { element, collapsible, collapsed } = treeElement;
const node = { parent, element, children: [], depth, collapsible: !!collapsible, collapsed: !!collapsed, visibleCount: 1 };
const node = { parent, element, children: [], depth, collapsible: !!collapsible, collapsed: !!collapsed, visibleCount: 1, visible: true, filterData: undefined };
if (visible) {
treeListElements.push(node);
......@@ -105,14 +124,14 @@ function treeElementToNode<T>(treeElement: ITreeElement<T>, parent: IMutableTree
return node;
}
function treeNodeToElement<T>(node: IMutableTreeNode<T>): ITreeElement<T> {
function treeNodeToElement<T>(node: IMutableTreeNode<T, any>): ITreeElement<T> {
const { element, collapsed } = node;
const children = Iterator.map(Iterator.fromArray(node.children), treeNodeToElement);
return { element, children, collapsed };
}
export function getNodeLocation<T>(node: ITreeNode<T>): number[] {
export function getNodeLocation<T>(node: ITreeNode<T, any>): number[] {
const location = [];
while (node.parent) {
......@@ -123,22 +142,24 @@ export function getNodeLocation<T>(node: ITreeNode<T>): number[] {
return location.reverse();
}
export class TreeModel<T> {
export class TreeModel<T, TFilterData = void> {
private root: IMutableTreeNode<T> = {
private root: IMutableTreeNode<T, TFilterData> = {
parent: undefined,
element: undefined,
children: [],
depth: 0,
collapsible: false,
collapsed: false,
visibleCount: 1
visibleCount: 1,
visible: true,
filterData: undefined
};
private _onDidChangeCollapseState = new Emitter<ITreeNode<T>>();
readonly onDidChangeCollapseState: Event<ITreeNode<T>> = this._onDidChangeCollapseState.event;
private _onDidChangeCollapseState = new Emitter<ITreeNode<T, TFilterData>>();
readonly onDidChangeCollapseState: Event<ITreeNode<T, TFilterData>> = this._onDidChangeCollapseState.event;
constructor(private list: ISpliceable<ITreeNode<T>>) { }
constructor(private list: ISpliceable<ITreeNode<T, TFilterData>>) { }
splice(location: number[], deleteCount: number, toInsert?: ISequence<ITreeElement<T>>): Iterator<ITreeElement<T>> {
if (location.length === 0) {
......@@ -146,7 +167,7 @@ export class TreeModel<T> {
}
const { parentNode, listIndex, visible } = this.findParentNode(location);
const treeListElementsToInsert: ITreeNode<T>[] = [];
const treeListElementsToInsert: ITreeNode<T, TFilterData>[] = [];
const elementsToInsert = getTreeElementIterator(toInsert);
const nodesToInsert = Iterator.collect(Iterator.map(elementsToInsert, el => treeElementToNode(el, parentNode, visible, treeListElementsToInsert)));
const lastIndex = location[location.length - 1];
......@@ -176,7 +197,7 @@ export class TreeModel<T> {
this._setCollapsed(node, listIndex, visible);
}
private _setCollapsed(node: IMutableTreeNode<T>, listIndex: number, visible: boolean, collapsed?: boolean | undefined): boolean {
private _setCollapsed(node: IMutableTreeNode<T, TFilterData>, listIndex: number, visible: boolean, collapsed?: boolean | undefined): boolean {
if (!node.collapsible) {
return false;
}
......@@ -223,7 +244,7 @@ export class TreeModel<T> {
return this.findNode(location).node.collapsed;
}
private findNode(location: number[]): { node: IMutableTreeNode<T>, listIndex: number, visible: boolean } {
private findNode(location: number[]): { node: IMutableTreeNode<T, TFilterData>, listIndex: number, visible: boolean } {
const { parentNode, listIndex, visible } = this.findParentNode(location);
const index = location[location.length - 1];
......@@ -236,7 +257,7 @@ export class TreeModel<T> {
return { node, listIndex, visible };
}
private findParentNode(location: number[], node: IMutableTreeNode<T> = this.root, listIndex: number = 0, visible = true): { parentNode: IMutableTreeNode<T>; listIndex: number; visible: boolean; } {
private findParentNode(location: number[], node: IMutableTreeNode<T, TFilterData> = this.root, listIndex: number = 0, visible = true): { parentNode: IMutableTreeNode<T, TFilterData>; listIndex: number; visible: boolean; } {
const [index, ...rest] = location;
if (index < 0 || index > node.children.length) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册