Skip to content

Commit

Permalink
Simplify the usage of scrollableElement
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Apr 30, 2016
1 parent 83c5096 commit 6cf0724
Show file tree
Hide file tree
Showing 25 changed files with 513 additions and 838 deletions.
97 changes: 31 additions & 66 deletions src/vs/base/browser/ui/list/listView.ts
Expand Up @@ -3,8 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IScrollable, ScrollEvent } from 'vs/base/common/scrollable';
import { Emitter } from 'vs/base/common/event';
import { toObject, assign } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Gesture } from 'vs/base/browser/touch';
Expand Down Expand Up @@ -47,24 +45,22 @@ const MouseEventTypes = [
'contextmenu'
];

export class ListView<T> implements IScrollable, IDisposable {
export class ListView<T> implements IDisposable {

private items: IItem<T>[];
private itemId: number;
private rangeMap: RangeMap;
private cache: RowCache<T>;
private renderers: { [templateId: string]: IRenderer<T, any>; };

private renderTop: number;
private _renderHeight: number;
private lastRenderTop: number;
private lastRenderHeight: number;

private _domNode: HTMLElement;
private gesture: Gesture;
private rowsContainer: HTMLElement;
private scrollableElement: ScrollableElement;

private _onScroll = new Emitter<ScrollEvent>();
private _lastScrollEvent: ScrollEvent;

private toDispose: IDisposable[];

Expand All @@ -79,10 +75,8 @@ export class ListView<T> implements IScrollable, IDisposable {
this.renderers = toObject<IRenderer<T, any>, IRenderer<T, any>>(renderers, r => r.templateId);
this.cache = new RowCache(this.renderers);

this.renderTop = 0;
this._renderHeight = 0;

this._lastScrollEvent = new ScrollEvent(this.getScrollTop(), this.getScrollLeft(), this.getScrollWidth(), this.getScrollHeight());
this.lastRenderTop = 0;
this.lastRenderHeight = 0;

this._domNode = document.createElement('div');
this._domNode.className = 'monaco-list';
Expand All @@ -92,18 +86,21 @@ export class ListView<T> implements IScrollable, IDisposable {
this.rowsContainer.className = 'monaco-list-rows';
this.gesture = new Gesture(this.rowsContainer);

this.scrollableElement = new ScrollableElement(this.rowsContainer, this, {
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
forbidTranslate3dUse: true,
horizontal: 'hidden',
vertical: 'auto',
useShadows: false,
saveLastScrollTimeOnClassName: 'monaco-list-row'
});
this.scrollableElement.onScroll((e) => {
this.render(e.scrollTop, e.height);
});

this._domNode.appendChild(this.scrollableElement.getDomNode());
container.appendChild(this._domNode);

this.toDispose = [this.rangeMap, this.gesture, this.scrollableElement, this._onScroll];
this.toDispose = [this.rangeMap, this.gesture, this.scrollableElement];

this.layout();
}
Expand Down Expand Up @@ -139,9 +136,10 @@ export class ListView<T> implements IScrollable, IDisposable {
}
}

this.rowsContainer.style.height = `${ this.rangeMap.size }px`;
this.setScrollTop(this.renderTop);
this._emitScrollEvent();
this.rowsContainer.style.height = `${ this.getContentHeight() }px`;
this.scrollableElement.updateState({
scrollHeight: this.getContentHeight()
});

return deleted.map(i => i.element);
}
Expand All @@ -151,7 +149,7 @@ export class ListView<T> implements IScrollable, IDisposable {
}

get renderHeight(): number {
return this._renderHeight;
return this.scrollableElement.getHeight();
}

element(index: number): T {
Expand All @@ -175,22 +173,16 @@ export class ListView<T> implements IScrollable, IDisposable {
}

layout(height?: number): void {
this.setRenderHeight(height || DOM.getContentHeight(this._domNode));
this.setScrollTop(this.renderTop);
this.scrollableElement.onElementDimensions();
this._emitScrollEvent();
this.scrollableElement.updateState({
height: height || DOM.getContentHeight(this._domNode)
});
}

// Render

private setRenderHeight(viewHeight: number) {
this.render(this.renderTop, viewHeight);
this._renderHeight = viewHeight;
}

private render(renderTop: number, renderHeight: number): void {
const renderBottom = renderTop + renderHeight;
const thisRenderBottom = this.renderTop + this._renderHeight;
const thisRenderBottom = this.lastRenderTop + this.lastRenderHeight;
let i: number, stop: number;

// when view scrolls down, start rendering from the renderBottom
Expand All @@ -199,30 +191,30 @@ export class ListView<T> implements IScrollable, IDisposable {
}

// when view scrolls up, start rendering from either this.renderTop or renderBottom
for (i = Math.min(this.rangeMap.indexAt(this.renderTop), this.rangeMap.indexAfter(renderBottom)) - 1, stop = this.rangeMap.indexAt(renderTop); i >= stop; i--) {
for (i = Math.min(this.rangeMap.indexAt(this.lastRenderTop), this.rangeMap.indexAfter(renderBottom)) - 1, stop = this.rangeMap.indexAt(renderTop); i >= stop; i--) {
this.insertItemInDOM(this.items[i], i);
}

// when view scrolls down, start unrendering from renderTop
for (i = this.rangeMap.indexAt(this.renderTop), stop = Math.min(this.rangeMap.indexAt(renderTop), this.rangeMap.indexAfter(thisRenderBottom)); i < stop; i++) {
for (i = this.rangeMap.indexAt(this.lastRenderTop), stop = Math.min(this.rangeMap.indexAt(renderTop), this.rangeMap.indexAfter(thisRenderBottom)); i < stop; i++) {
this.removeItemFromDOM(this.items[i]);
}

// when view scrolls up, start unrendering from either renderBottom this.renderTop
for (i = Math.max(this.rangeMap.indexAfter(renderBottom), this.rangeMap.indexAt(this.renderTop)), stop = this.rangeMap.indexAfter(thisRenderBottom); i < stop; i++) {
for (i = Math.max(this.rangeMap.indexAfter(renderBottom), this.rangeMap.indexAt(this.lastRenderTop)), stop = this.rangeMap.indexAfter(thisRenderBottom); i < stop; i++) {
this.removeItemFromDOM(this.items[i]);
}

this.rowsContainer.style.transform = `translate3d(0px, -${ renderTop }px, 0px)`;
this.renderTop = renderTop;
this._renderHeight = renderBottom - renderTop;
this.lastRenderTop = renderTop;
this.lastRenderHeight = renderBottom - renderTop;
}

private getRenderedItemRanges(): IItemRange<T>[] {
const result: IItemRange<T>[] = [];
const renderBottom = this.renderTop + this._renderHeight;
const renderBottom = this.lastRenderTop + this.lastRenderHeight;

let start = this.renderTop;
let start = this.lastRenderTop;
let index = this.rangeMap.indexAt(start);
let item = this.items[index];
let end = -1;
Expand Down Expand Up @@ -260,45 +252,18 @@ export class ListView<T> implements IScrollable, IDisposable {
item.row = null;
}

// IScrollable

getScrollHeight(): number {
getContentHeight(): number {
return this.rangeMap.size;
}

getScrollWidth(): number {
return 0;
}

getScrollLeft(): number {
return 0;
}

setScrollLeft(scrollLeft: number): void {
// noop
}

getScrollTop(): number {
return this.renderTop;
return this.scrollableElement.getScrollTop();
}

setScrollTop(scrollTop: number): void {
scrollTop = Math.min(scrollTop, this.getScrollHeight() - this._renderHeight);
scrollTop = Math.max(scrollTop, 0);

this.render(scrollTop, this._renderHeight);
this.renderTop = scrollTop;

this._emitScrollEvent();
}

private _emitScrollEvent(): void {
this._lastScrollEvent = this._lastScrollEvent.create(this.getScrollTop(), this.getScrollLeft(), this.getScrollWidth(), this.getScrollHeight());
this._onScroll.fire(this._lastScrollEvent);
}

addScrollListener(callback: (v:ScrollEvent)=>void): IDisposable {
return this._onScroll.event(callback);
this.scrollableElement.updateState({
scrollTop: scrollTop
});
}

// Events
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/ui/list/listWidget.ts
Expand Up @@ -198,7 +198,7 @@ export class List<T> implements IDisposable {
}

get contentHeight(): number {
return this.view.getScrollHeight();
return this.view.getContentHeight();
}

layout(height?: number): void {
Expand Down
20 changes: 10 additions & 10 deletions src/vs/base/browser/ui/resourceviewer/resourceViewer.ts
Expand Up @@ -12,7 +12,7 @@ import URI from 'vs/base/common/uri';
import paths = require('vs/base/common/paths');
import {Builder, $} from 'vs/base/browser/builder';
import DOM = require('vs/base/browser/dom');
import {DomNodeScrollable} from 'vs/base/browser/ui/scrollbar/domNodeScrollable';
import {DomScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement';

// Known media mimes that we can handle
const mapExtToMediaMimes = {
Expand Down Expand Up @@ -69,7 +69,7 @@ const mapExtToMediaMimes = {
*/
export class ResourceViewer {

public static show(name: string, resource: URI, container: Builder, scrollable?: DomNodeScrollable): void {
public static show(name: string, resource: URI, container: Builder, scrollbar?: DomScrollableElement): void {

// Ensure CSS class
$(container).addClass('monaco-resource-viewer');
Expand All @@ -93,8 +93,8 @@ export class ResourceViewer {
.img({
src: resource.toString() + '?' + new Date().getTime() // We really want to avoid the browser from caching this resource, so we add a fake query param that is unique
}).on(DOM.EventType.LOAD, () => {
if (scrollable) {
scrollable.onContentsDimensions();
if (scrollbar) {
scrollbar.scanDomNode();
}
});
}
Expand Down Expand Up @@ -124,8 +124,8 @@ export class ResourceViewer {
text: nls.localize('missingAudioSupport', "Sorry but playback of audio files is not supported."),
controls: 'controls'
}).on(DOM.EventType.LOAD, () => {
if (scrollable) {
scrollable.onContentsDimensions();
if (scrollbar) {
scrollbar.scanDomNode();
}
});
}
Expand All @@ -141,8 +141,8 @@ export class ResourceViewer {
text: nls.localize('missingVideoSupport', "Sorry but playback of video files is not supported."),
controls: 'controls'
}).on(DOM.EventType.LOAD, () => {
if (scrollable) {
scrollable.onContentsDimensions();
if (scrollbar) {
scrollbar.scanDomNode();
}
});
}
Expand All @@ -156,8 +156,8 @@ export class ResourceViewer {
text: nls.localize('nativeBinaryError', "The file cannot be displayed in the editor because it is either binary, very large or uses an unsupported text encoding.")
});

if (scrollable) {
scrollable.onContentsDimensions();
if (scrollbar) {
scrollbar.scanDomNode();
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts
Expand Up @@ -14,7 +14,7 @@ import {FastDomNode, createFastDomNode} from 'vs/base/browser/styleMutator';
import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState';
import {ScrollbarArrow, ScrollbarArrowOptions} from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
import {Visibility, ScrollbarVisibilityController} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
import {DelegateScrollable} from 'vs/base/common/scrollable';
import {Scrollable} from 'vs/base/common/scrollable';

/**
* The orthogonal distance to the slider at which dragging "resets". This implements "snapping"
Expand All @@ -40,14 +40,14 @@ export interface AbstractScrollbarOptions {
scrollbarState: ScrollbarState;
visibility: Visibility;
extraScrollbarClassName: string;
scrollable: DelegateScrollable;
scrollable: Scrollable;
}

export abstract class AbstractScrollbar extends Widget {

protected _forbidTranslate3dUse: boolean;
protected _host: ScrollbarHost;
protected _scrollable: DelegateScrollable;
protected _scrollable: Scrollable;
private _lazyRender: boolean;
private _scrollbarState: ScrollbarState;
private _visibilityController: ScrollbarVisibilityController;
Expand Down Expand Up @@ -110,7 +110,7 @@ export abstract class AbstractScrollbar extends Widget {

// ----------------- Update state

public onElementSize(visibleSize: number): boolean {
protected _onElementSize(visibleSize: number): boolean {
if (this._scrollbarState.setVisibleSize(visibleSize)) {
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
this._shouldRender = true;
Expand All @@ -121,7 +121,7 @@ export abstract class AbstractScrollbar extends Widget {
return this._shouldRender;
}

public onElementScrollSize(elementScrollSize: number): boolean {
protected _onElementScrollSize(elementScrollSize: number): boolean {
if (this._scrollbarState.setScrollSize(elementScrollSize)) {
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
this._shouldRender = true;
Expand All @@ -132,7 +132,7 @@ export abstract class AbstractScrollbar extends Widget {
return this._shouldRender;
}

public onElementScrollPosition(elementScrollPosition: number): boolean {
protected _onElementScrollPosition(elementScrollPosition: number): boolean {
if (this._scrollbarState.setScrollPosition(elementScrollPosition)) {
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
this._shouldRender = true;
Expand Down Expand Up @@ -237,7 +237,7 @@ export abstract class AbstractScrollbar extends Widget {
let newScrollPosition = this._getScrollPosition();

if (oldScrollPosition !== newScrollPosition) {
this.onElementScrollPosition(this._getScrollPosition());
this._onElementScrollPosition(this._getScrollPosition());
return true;
}
return false;
Expand Down

0 comments on commit 6cf0724

Please sign in to comment.