mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
fixes #38628
This commit is contained in:
@@ -3,14 +3,14 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { toObject, assign, getOrDefault } from 'vs/base/common/objects';
|
||||
import { assign, getOrDefault } from 'vs/base/common/objects';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollEvent, ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { RangeMap, IRange, relativeComplement, each } from './rangeMap';
|
||||
import { RangeMap, IRange, relativeComplement } from './rangeMap';
|
||||
import { IDelegate, IRenderer, ISpliceable } from './list';
|
||||
import { RowCache, IRow } from './rowCache';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
@@ -72,7 +72,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
private itemId: number;
|
||||
private rangeMap: RangeMap;
|
||||
private cache: RowCache<T>;
|
||||
private renderers: { [templateId: string]: IRenderer<T, any>; };
|
||||
private renderers = new Map<string, IRenderer<T, any>>();
|
||||
private lastRenderTop: number;
|
||||
private lastRenderHeight: number;
|
||||
private _domNode: HTMLElement;
|
||||
@@ -90,7 +90,11 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
this.items = [];
|
||||
this.itemId = 0;
|
||||
this.rangeMap = new RangeMap();
|
||||
this.renderers = toObject<IRenderer<T, any>>(renderers, r => r.templateId);
|
||||
|
||||
for (const renderer of renderers) {
|
||||
this.renderers.set(renderer.templateId, renderer);
|
||||
}
|
||||
|
||||
this.cache = new RowCache(this.renderers);
|
||||
|
||||
this.lastRenderTop = 0;
|
||||
@@ -127,7 +131,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[] = []): T[] {
|
||||
const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
each(previousRenderRange, i => this.removeItemFromDOM(this.items[i]));
|
||||
|
||||
for (let i = previousRenderRange.start; i < previousRenderRange.end; i++) {
|
||||
this.removeItemFromDOM(this.items[i]);
|
||||
}
|
||||
|
||||
const inserted = elements.map<IItem<T>>(element => ({
|
||||
id: String(this.itemId++),
|
||||
@@ -140,9 +147,11 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
this.rangeMap.splice(start, deleteCount, ...inserted);
|
||||
|
||||
const deleted = this.items.splice(start, deleteCount, ...inserted);
|
||||
|
||||
const renderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
each(renderRange, i => this.insertItemInDOM(this.items[i], i));
|
||||
|
||||
for (let i = renderRange.start; i < renderRange.end; i++) {
|
||||
this.insertItemInDOM(this.items[i], i);
|
||||
}
|
||||
|
||||
const scrollHeight = this.getContentHeight();
|
||||
this.rowsContainer.style.height = `${scrollHeight}px`;
|
||||
@@ -200,8 +209,17 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
const rangesToInsert = relativeComplement(renderRange, previousRenderRange);
|
||||
const rangesToRemove = relativeComplement(previousRenderRange, renderRange);
|
||||
|
||||
rangesToInsert.forEach(range => each(range, i => this.insertItemInDOM(this.items[i], i)));
|
||||
rangesToRemove.forEach(range => each(range, i => this.removeItemFromDOM(this.items[i])));
|
||||
for (const range of rangesToInsert) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
this.insertItemInDOM(this.items[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
for (const range of rangesToRemove) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
this.removeItemFromDOM(this.items[i], );
|
||||
}
|
||||
}
|
||||
|
||||
if (canUseTranslate3d() && !isWindows /* Windows: translate3d breaks subpixel-antialias (ClearType) unless a background is defined */) {
|
||||
const transform = `translate3d(0px, -${renderTop}px, 0px)`;
|
||||
@@ -226,7 +244,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
this.rowsContainer.appendChild(item.row.domNode);
|
||||
}
|
||||
|
||||
const renderer = this.renderers[item.templateId];
|
||||
const renderer = this.renderers.get(item.templateId);
|
||||
item.row.domNode.style.top = `${this.elementTop(index)}px`;
|
||||
item.row.domNode.style.height = `${item.size}px`;
|
||||
item.row.domNode.setAttribute('data-index', `${index}`);
|
||||
|
||||
@@ -30,7 +30,9 @@ class CombinedSpliceable<T> implements ISpliceable<T> {
|
||||
constructor(private spliceables: ISpliceable<T>[]) { }
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
this.spliceables.forEach(s => s.splice(start, deleteCount, elements));
|
||||
for (const spliceable of this.spliceables) {
|
||||
spliceable.splice(start, deleteCount, elements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,15 +76,19 @@ class TraitRenderer<T> implements IRenderer<T, ITraitTemplateData>
|
||||
}
|
||||
|
||||
renderIndexes(indexes: number[]): void {
|
||||
this.rendered
|
||||
.filter(({ index }) => indexes.indexOf(index) > -1)
|
||||
.forEach(({ index, templateData }) => this.trait.renderIndex(index, templateData.container));
|
||||
for (const { index, templateData } of this.rendered) {
|
||||
if (indexes.indexOf(index) > -1) {
|
||||
this.trait.renderIndex(index, templateData.container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number): void {
|
||||
this.rendered
|
||||
.filter(({ index }) => index >= start && index < start + deleteCount)
|
||||
.forEach(({ templateData }) => templateData.elementDisposable.dispose());
|
||||
for (const { index, templateData } of this.rendered) {
|
||||
if (index >= start && index < start + deleteCount) {
|
||||
templateData.elementDisposable.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ITraitTemplateData): void {
|
||||
@@ -583,11 +589,19 @@ class PipelineRenderer<T> implements IRenderer<T, any> {
|
||||
}
|
||||
|
||||
renderElement(element: T, index: number, templateData: any[]): void {
|
||||
this.renderers.forEach((r, i) => r.renderElement(element, index, templateData[i]));
|
||||
let i = 0;
|
||||
|
||||
for (const renderer of this.renderers) {
|
||||
renderer.renderElement(element, index, templateData[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: any[]): void {
|
||||
this.renderers.forEach((r, i) => r.disposeTemplate(templateData[i]));
|
||||
let i = 0;
|
||||
|
||||
for (const renderer of this.renderers) {
|
||||
renderer.disposeTemplate(templateData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,6 +703,10 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[] = []): void {
|
||||
if (deleteCount === 0 && elements.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements));
|
||||
}
|
||||
|
||||
|
||||
@@ -56,12 +56,6 @@ export function relativeComplement(one: IRange, other: IRange): IRange[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function each(range: IRange, fn: (index: number) => void): void {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
fn(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intersection between a ranged group and a range.
|
||||
* Returns `[]` if the intersection is empty.
|
||||
|
||||
@@ -23,11 +23,9 @@ function removeFromParent(element: HTMLElement): void {
|
||||
|
||||
export class RowCache<T> implements IDisposable {
|
||||
|
||||
private cache: { [templateId: string]: IRow[]; };
|
||||
private cache = new Map<string, IRow[]>();
|
||||
|
||||
constructor(private renderers: { [templateId: string]: IRenderer<T, any>; }) {
|
||||
this.cache = Object.create(null);
|
||||
}
|
||||
constructor(private renderers: Map<string, IRenderer<T, any>>) { }
|
||||
|
||||
/**
|
||||
* Returns a row either by creating a new one or reusing
|
||||
@@ -38,7 +36,7 @@ export class RowCache<T> implements IDisposable {
|
||||
|
||||
if (!result) {
|
||||
const domNode = $('.monaco-list-row');
|
||||
const renderer = this.renderers[templateId];
|
||||
const renderer = this.renderers.get(templateId);
|
||||
const templateData = renderer.renderTemplate(domNode);
|
||||
result = { domNode, templateId, templateData };
|
||||
}
|
||||
@@ -67,27 +65,36 @@ export class RowCache<T> implements IDisposable {
|
||||
}
|
||||
|
||||
private getTemplateCache(templateId: string): IRow[] {
|
||||
return this.cache[templateId] || (this.cache[templateId] = []);
|
||||
let result = this.cache.get(templateId);
|
||||
|
||||
if (!result) {
|
||||
result = [];
|
||||
this.cache.set(templateId, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private garbageCollect(): void {
|
||||
if (this.cache) {
|
||||
Object.keys(this.cache).forEach(templateId => {
|
||||
this.cache[templateId].forEach(cachedRow => {
|
||||
const renderer = this.renderers[templateId];
|
||||
renderer.disposeTemplate(cachedRow.templateData);
|
||||
cachedRow.domNode = null;
|
||||
cachedRow.templateData = null;
|
||||
});
|
||||
|
||||
delete this.cache[templateId];
|
||||
});
|
||||
if (!this.renderers) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cache.forEach((cachedRows, templateId) => {
|
||||
for (const cachedRow of cachedRows) {
|
||||
const renderer = this.renderers[templateId];
|
||||
renderer.disposeTemplate(cachedRow.templateData);
|
||||
cachedRow.domNode = null;
|
||||
cachedRow.templateData = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.garbageCollect();
|
||||
this.cache = null;
|
||||
this.cache.clear();
|
||||
this.renderers = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user