listWidget.ts 4.8 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./list';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
J
Joao Moreno 已提交
8
import { isNumber } from 'vs/base/common/types';
J
Joao Moreno 已提交
9
import { toggleClass } from 'vs/base/browser/dom';
J
Joao Moreno 已提交
10
import { IDelegate, IRenderer } from './list';
J
Joao Moreno 已提交
11 12 13 14 15 16 17 18 19 20 21 22
import { ListView } from './listView';

interface ITraitTemplateData<D> {
	container: HTMLElement;
	data: D;
}

class TraitRenderer<T, D> implements IRenderer<T, ITraitTemplateData<D>>
{
	private elements: { [id: string]: T };

	constructor(
J
Joao Moreno 已提交
23
		private controller: TraitController,
J
Joao Moreno 已提交
24 25 26 27 28 29 30 31 32 33 34 35
		private renderer: IRenderer<T,D>
	) {}

	public get templateId(): string {
		return this.renderer.templateId;
	}

	renderTemplate(container: HTMLElement): ITraitTemplateData<D> {
		const data = this.renderer.renderTemplate(container);
		return { container, data };
	}

J
Joao Moreno 已提交
36 37 38
	renderElement(element: T, index: number, templateData: ITraitTemplateData<D>): void {
		toggleClass(templateData.container, this.controller.trait, this.controller.contains(index));
		this.renderer.renderElement(element, index, templateData.data);
J
Joao Moreno 已提交
39 40 41 42 43 44 45
	}

	disposeTemplate(templateData: ITraitTemplateData<D>): void {
		return this.renderer.disposeTemplate(templateData.data);
	}
}

J
Joao Moreno 已提交
46
class TraitController {
J
Joao Moreno 已提交
47

J
Joao Moreno 已提交
48
	private indexes: number[];
J
Joao Moreno 已提交
49

J
Joao Moreno 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
	constructor(private _trait: string) {
		this.indexes = [];
	}

	splice(start: number, deleteCount: number, insertCount: number): void {
		const diff = insertCount - deleteCount;
		const end = start + deleteCount;
		const indexes = [];

		for (const index of indexes) {
			if (index >= start && index < end) {
				continue;
			}

			indexes.push(index > start ? index + diff : index);
		}

		this.indexes = indexes;
J
Joao Moreno 已提交
68 69 70 71 72 73
	}

	get trait(): string {
		return this._trait;
	}

J
Joao Moreno 已提交
74 75 76 77
	set(indexes: number[]): number[] {
		const result = this.indexes;
		this.indexes = indexes;
		return result;
J
Joao Moreno 已提交
78 79
	}

J
Joao Moreno 已提交
80 81 82 83
	get(): number[] {
		return this.indexes;
	}

J
Joao Moreno 已提交
84 85 86
	add(index: number): void {
		if (this.contains(index)) {
			return;
J
Joao Moreno 已提交
87
		}
J
Joao Moreno 已提交
88 89

		this.indexes.push(index);
J
Joao Moreno 已提交
90 91
	}

J
Joao Moreno 已提交
92 93
	remove(index: number): void {
		this.indexes = this.indexes.filter(i => i === index);
J
Joao Moreno 已提交
94 95
	}

J
Joao Moreno 已提交
96 97
	contains(index: number): boolean {
		return this.indexes.some(i => i === index);
J
Joao Moreno 已提交
98 99
	}

J
Joao Moreno 已提交
100
	wrapRenderer<T, D>(renderer: IRenderer<T, D>): IRenderer<T, ITraitTemplateData<D>> {
J
Joao Moreno 已提交
101 102 103 104 105 106 107
		return new TraitRenderer<T, D>(this, renderer);
	}
}

export class List<T> implements IDisposable {

	private view: ListView<T>;
J
Joao Moreno 已提交
108 109
	private focus: TraitController;
	private selection: TraitController;
J
Joao Moreno 已提交
110 111 112 113

	constructor(
		container: HTMLElement,
		delegate: IDelegate<T>,
J
Joao Moreno 已提交
114
		renderers: IRenderer<T, any>[]
J
Joao Moreno 已提交
115
	) {
J
Joao Moreno 已提交
116 117
		this.focus = new TraitController('focused');
		this.selection = new TraitController('selected');
J
Joao Moreno 已提交
118 119 120 121 122 123 124 125 126 127 128

		renderers = renderers.map(r => {
			r = this.focus.wrapRenderer(r);
			r = this.selection.wrapRenderer(r);
			return r;
		});

		this.view = new ListView(container, delegate, renderers);
	}

	splice(start: number, deleteCount: number, ...elements: T[]): void {
J
Joao Moreno 已提交
129 130 131
		this.focus.splice(start, deleteCount, elements.length);
		this.selection.splice(start, deleteCount, elements.length);
		this.view.splice(start, deleteCount, ...elements);
J
Joao Moreno 已提交
132 133 134 135 136 137
	}

	get length(): number {
		return this.view.length;
	}

J
Joao Moreno 已提交
138 139 140 141
	get contentHeight(): number {
		return this.view.getScrollHeight();
	}

J
Joao Moreno 已提交
142 143 144 145
	layout(height?: number): void {
		this.view.layout(height);
	}

J
Joao Moreno 已提交
146 147 148
	setSelection(...indexes: number[]): void {
		indexes = indexes.concat(this.selection.set(indexes));
		indexes.forEach(i => this.view.splice(i, 1, this.view.element(i)));
J
Joao Moreno 已提交
149 150
	}

J
Joao Moreno 已提交
151 152 153
	setFocus(...indexes: number[]): void {
		indexes = indexes.concat(this.focus.set(indexes));
		indexes.forEach(i => this.view.splice(i, 1, this.view.element(i)));
J
Joao Moreno 已提交
154 155
	}

J
Joao Moreno 已提交
156 157 158 159
	getFocus(): T[] {
		return this.focus.get().map(i => this.view.element(i));
	}

J
Joao Moreno 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	reveal(index: number, relativeTop?: number): void {
		const scrollTop = this.view.getScrollTop();
		const elementTop = this.view.elementTop(index);
		const elementHeight = this.view.elementHeight(index);

		if (isNumber(relativeTop)) {
			relativeTop = relativeTop < 0 ? 0 : relativeTop;
			relativeTop = relativeTop > 1 ? 1 : relativeTop;

			// y = mx + b
			var m = elementHeight - this.view.height;
			this.view.setScrollTop(m * relativeTop + elementTop);
		} else {
			var viewItemBottom = elementTop + elementHeight;
			var wrapperBottom = scrollTop + this.view.height;

			if (elementTop < scrollTop) {
				this.view.setScrollTop(elementTop);
			} else if (viewItemBottom >= wrapperBottom) {
				this.view.setScrollTop(viewItemBottom - this.view.height);
			}
		}
	}

J
Joao Moreno 已提交
184 185 186 187
	dispose(): void {
		this.view = dispose(this.view);
	}
}