diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 4209457c0f08a..2dbc57e900f6f 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -21,6 +21,7 @@ import { equals, distinct } from 'vs/base/common/arrays'; import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; import { disposableTimeout, Delayer } from 'vs/base/common/async'; import { isFirefox } from 'vs/base/browser/browser'; +import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; interface IItem { readonly id: string; @@ -198,6 +199,7 @@ export class ListView implements ISpliceable, IDisposable { get onDidScroll(): Event { return this.scrollableElement.onScroll; } get onWillScroll(): Event { return this.scrollableElement.onWillScroll; } + get containerDomNode(): HTMLElement { return this.rowsContainer; } constructor( container: HTMLElement, @@ -273,6 +275,31 @@ export class ListView implements ISpliceable, IDisposable { this.layout(); } + triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { + this.scrollableElement.triggerScrollFromMouseWheelEvent(browserEvent); + } + + updateElementHeight(index: number, size: number): void { + if (this.items[index].size === size) { + return; + } + + const lastRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + + const heightDiff = index < lastRenderRange.start ? size - this.items[index].size : 0; + this.rangeMap.splice(index, 1, [{ size: size }]); + this.items[index].size = size; + + this.render(lastRenderRange, this.lastRenderTop + heightDiff, this.lastRenderHeight, undefined, undefined, true); + + this.eventuallyUpdateScrollDimensions(); + + if (this.supportDynamicHeights) { + this._rerender(this.lastRenderTop, this.lastRenderHeight); + } + return; + } + splice(start: number, deleteCount: number, elements: T[] = []): T[] { if (this.splicing) { throw new Error('Can\'t run recursive splices.'); @@ -516,14 +543,21 @@ export class ListView implements ISpliceable, IDisposable { // Render - private render(renderTop: number, renderHeight: number, renderLeft: number, scrollWidth: number): void { - const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + private render(previousRenderRange: IRange, renderTop: number, renderHeight: number, renderLeft: number | undefined, scrollWidth: number | undefined, updateItemsInDOM: boolean = false): void { const renderRange = this.getRenderRange(renderTop, renderHeight); const rangesToInsert = Range.relativeComplement(renderRange, previousRenderRange); const rangesToRemove = Range.relativeComplement(previousRenderRange, renderRange); const beforeElement = this.getNextToLastElement(rangesToInsert); + if (updateItemsInDOM) { + const rangesToUpdate = Range.intersect(previousRenderRange, renderRange); + + for (let i = rangesToUpdate.start; i < rangesToUpdate.end; i++) { + this.updateItemInDOM(this.items[i], i); + } + } + for (const range of rangesToInsert) { for (let i = range.start; i < range.end; i++) { this.insertItemInDOM(i, beforeElement); @@ -536,10 +570,13 @@ export class ListView implements ISpliceable, IDisposable { } } - this.rowsContainer.style.left = `-${renderLeft}px`; + if (renderLeft !== undefined) { + this.rowsContainer.style.left = `-${renderLeft}px`; + } + this.rowsContainer.style.top = `-${renderTop}px`; - if (this.horizontalScrolling) { + if (this.horizontalScrolling && scrollWidth !== undefined) { this.rowsContainer.style.width = `${Math.max(scrollWidth, this.renderWidth)}px`; } @@ -741,7 +778,8 @@ export class ListView implements ISpliceable, IDisposable { private onScroll(e: ScrollEvent): void { try { - this.render(e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); + const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + this.render(previousRenderRange, e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); if (this.supportDynamicHeights) { this._rerender(e.scrollTop, e.height); @@ -1097,6 +1135,14 @@ export class ListView implements ISpliceable, IDisposable { } const size = item.size; + + if (item.row && item.row.domNode) { + let newSize = item.row.domNode.offsetHeight; + item.size = newSize; + item.lastDynamicHeightWidth = this.renderWidth; + return newSize - size; + } + const row = this.cache.alloc(item.templateId); row.domNode!.style.height = ''; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index aa3febf7c8539..29263345a5eed 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1115,7 +1115,7 @@ export class List implements ISpliceable, IDisposable { private focus: Trait; private selection: Trait; private eventBufferer = new EventBufferer(); - private view: ListView; + protected view: ListView; private spliceable: ISpliceable; private styleController: IStyleController; private typeLabelController?: TypeLabelController; diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 4c34a8bafcd6a..309db05fe2119 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -303,6 +303,10 @@ export abstract class AbstractScrollableElement extends Widget { this._revealOnScroll = value; } + public triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { + this._onMouseWheel(new StandardWheelEvent(browserEvent)); + } + // -------------------- mouse wheel scrolling -------------------- private _setListeningToMouseWheel(shouldListen: boolean): void {