diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index b850cbdaa6b..29f29c411e5 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -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'; @@ -47,7 +45,7 @@ const MouseEventTypes = [ 'contextmenu' ]; -export class ListView implements IScrollable, IDisposable { +export class ListView implements IDisposable { private items: IItem[]; private itemId: number; @@ -55,16 +53,14 @@ export class ListView implements IScrollable, IDisposable { private cache: RowCache; private renderers: { [templateId: string]: IRenderer; }; - 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(); - private _lastScrollEvent: ScrollEvent; private toDispose: IDisposable[]; @@ -79,10 +75,8 @@ export class ListView implements IScrollable, IDisposable { this.renderers = toObject, IRenderer>(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'; @@ -92,18 +86,21 @@ export class ListView 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(); } @@ -139,9 +136,10 @@ export class ListView 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); } @@ -151,7 +149,7 @@ export class ListView implements IScrollable, IDisposable { } get renderHeight(): number { - return this._renderHeight; + return this.scrollableElement.getHeight(); } element(index: number): T { @@ -175,22 +173,16 @@ export class ListView 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 @@ -199,30 +191,30 @@ export class ListView 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[] { const result: IItemRange[] = []; - 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; @@ -260,45 +252,18 @@ export class ListView 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 diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index e53dab37e13..8facec26665 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -198,7 +198,7 @@ export class List implements IDisposable { } get contentHeight(): number { - return this.view.getScrollHeight(); + return this.view.getContentHeight(); } layout(height?: number): void { diff --git a/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts b/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts index fee52268e05..b43129606cc 100644 --- a/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts +++ b/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts @@ -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 = { @@ -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'); @@ -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(); } }); } @@ -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(); } }); } @@ -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(); } }); } @@ -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(); } } } diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 5a04450c5cf..c3e625b85f0 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -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" @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; diff --git a/src/vs/base/browser/ui/scrollbar/domNodeScrollable.ts b/src/vs/base/browser/ui/scrollbar/domNodeScrollable.ts deleted file mode 100644 index 0e7c8be3ea8..00000000000 --- a/src/vs/base/browser/ui/scrollbar/domNodeScrollable.ts +++ /dev/null @@ -1,73 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as DomUtils from 'vs/base/browser/dom'; -import {Gesture} from 'vs/base/browser/touch'; -import {Disposable, IDisposable} from 'vs/base/common/lifecycle'; -import {IScrollable, ScrollEvent} from 'vs/base/common/scrollable'; -import {Emitter} from 'vs/base/common/event'; - -export class DomNodeScrollable extends Disposable implements IScrollable { - - private _domNode: HTMLElement; - private _gestureHandler: Gesture; - private _onScroll = this._register(new Emitter()); - private _lastScrollEvent: ScrollEvent; - - constructor(domNode: HTMLElement) { - super(); - this._domNode = domNode; - this._gestureHandler = this._register(new Gesture(this._domNode)); - - this._lastScrollEvent = new ScrollEvent(this.getScrollTop(), this.getScrollLeft(), this.getScrollWidth(), this.getScrollHeight()); - - this._register(DomUtils.addDisposableListener(this._domNode, 'scroll', (e) => { - this._emitScrollEvent(); - })); - } - - public onContentsDimensions(): void { - this._emitScrollEvent(); - } - - private _emitScrollEvent(): void { - this._lastScrollEvent = this._lastScrollEvent.create(this.getScrollTop(), this.getScrollLeft(), this.getScrollWidth(), this.getScrollHeight()); - this._onScroll.fire(this._lastScrollEvent); - } - - public dispose() { - this._domNode = null; - super.dispose(); - } - - public getScrollHeight(): number { - return this._domNode.scrollHeight; - } - - public getScrollWidth(): number { - return this._domNode.scrollWidth; - } - - public getScrollLeft(): number { - return this._domNode.scrollLeft; - } - - public setScrollLeft(scrollLeft: number): void { - this._domNode.scrollLeft = scrollLeft; - } - - public getScrollTop(): number { - return this._domNode.scrollTop; - } - - public setScrollTop(scrollTop: number): void { - this._domNode.scrollTop = scrollTop; - } - - public addScrollListener(callback: (v:ScrollEvent) => void): IDisposable { - return this._onScroll.event(callback); - } -} diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 207ff7273e1..abc765bf420 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -9,14 +9,14 @@ import {AbstractScrollbar, ScrollbarHost, IMouseMoveEventData} from 'vs/base/bro import {IMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent'; import {IDomNodePosition} from 'vs/base/browser/dom'; import {ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; -import {DelegateScrollable} from 'vs/base/common/scrollable'; +import {Scrollable, ScrollEvent} from 'vs/base/common/scrollable'; import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState'; import {ARROW_IMG_SIZE} from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import {Visibility} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController'; export class HorizontalScrollbar extends AbstractScrollbar { - constructor(scrollable: DelegateScrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { super({ forbidTranslate3dUse: options.forbidTranslate3dUse, lazyRender: options.lazyRender, @@ -77,6 +77,13 @@ export class HorizontalScrollbar extends AbstractScrollbar { this.domNode.setBottom(0); } + public onDidScroll(e:ScrollEvent): boolean { + this._shouldRender = this._onElementScrollSize(e.scrollWidth) || this._shouldRender; + this._shouldRender = this._onElementScrollPosition(e.scrollLeft) || this._shouldRender; + this._shouldRender = this._onElementSize(e.width) || this._shouldRender; + return this._shouldRender; + } + protected _mouseDownRelativePosition(e: IMouseEvent, domNodePosition: IDomNodePosition): number { return e.posx - domNodePosition.left; } @@ -94,6 +101,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { } protected _setScrollPosition(scrollPosition: number) { - this._scrollable.setScrollLeft(scrollPosition); + this._scrollable.updateState({ + scrollLeft: scrollPosition + }); } } diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 5f613551a2d..6b51e5bd1bc 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -14,11 +14,12 @@ import {VerticalScrollbar} from 'vs/base/browser/ui/scrollbar/verticalScrollbar' import {ScrollableElementCreationOptions, ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import {visibilityFromString} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController'; import {IDisposable, dispose} from 'vs/base/common/lifecycle'; -import {IScrollable, DelegateScrollable} from 'vs/base/common/scrollable'; +import {Scrollable, ScrollEvent, INewScrollState} from 'vs/base/common/scrollable'; import {Widget} from 'vs/base/browser/ui/widget'; import {TimeoutTimer} from 'vs/base/common/async'; import {FastDomNode, createFastDomNode} from 'vs/base/browser/styleMutator'; import {ScrollbarHost} from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; +import Event, {Emitter} from 'vs/base/common/event'; const HIDE_TIMEOUT = 500; const SCROLL_WHEEL_SENSITIVITY = 50; @@ -28,15 +29,10 @@ export interface IOverviewRulerLayoutInfo { insertBefore: HTMLElement; } -export interface IDimensions { - width: number; - height: number; -} - export class ScrollableElement extends Widget { private _options: ScrollableElementResolvedOptions; - private _scrollable: DelegateScrollable; + private _scrollable: Scrollable; private _verticalScrollbar: VerticalScrollbar; private _horizontalScrollbar: HorizontalScrollbar; private _domNode: HTMLElement; @@ -49,19 +45,27 @@ export class ScrollableElement extends Widget { private _mouseWheelToDispose: IDisposable[]; - private _onElementDimensionsTimeout: TimeoutTimer; private _isDragging: boolean; private _mouseIsOver: boolean; private _hideTimeout: TimeoutTimer; private _shouldRender: boolean; - constructor(element: HTMLElement, scrollable:IScrollable, options: ScrollableElementCreationOptions, dimensions: IDimensions = null) { + private _onScroll = this._register(new Emitter()); + public onScroll: Event = this._onScroll.event; + + constructor(element: HTMLElement, options: ScrollableElementCreationOptions) { super(); element.style.overflow = 'hidden'; this._options = resolveOptions(options); - this._scrollable = this._register(new DelegateScrollable(scrollable, () => this._onScroll())); + this._scrollable = this._register(new Scrollable()); + this._register(this._scrollable.onScroll((e) => { + this._onDidScroll(e); + this._onScroll.fire(e); + })); + + // this._scrollable = this._register(new DelegateScrollable(scrollable, () => this._onScroll())); let scrollbarHost:ScrollbarHost = { onMouseWheel: (mouseWheelEvent: StandardMouseWheelEvent) => this._onMouseWheel(mouseWheelEvent), @@ -102,16 +106,11 @@ export class ScrollableElement extends Widget { this.onmouseover(this._listenOnDomNode, (e) => this._onMouseOver(e)); this.onnonbubblingmouseout(this._listenOnDomNode, (e) => this._onMouseOut(e)); - this._onElementDimensionsTimeout = this._register(new TimeoutTimer()); this._hideTimeout = this._register(new TimeoutTimer()); this._isDragging = false; this._mouseIsOver = false; - this.onElementDimensions(dimensions, true); - this._shouldRender = true; - this._shouldRender = this._horizontalScrollbar.onElementScrollSize(this._scrollable.getScrollWidth()) || this._shouldRender; - this._shouldRender = this._verticalScrollbar.onElementScrollSize(this._scrollable.getScrollHeight()) || this._shouldRender; } public dispose(): void { @@ -141,29 +140,28 @@ export class ScrollableElement extends Widget { this._verticalScrollbar.delegateMouseDown(browserEvent); } - /** - * Let the scrollable element know that the generated dom node's width / height might have changed. - */ - public onElementDimensions(dimensions: IDimensions = null, synchronous: boolean = false): void { - if (synchronous) { - this._actualElementDimensions(dimensions); - this._onElementDimensionsTimeout.cancel(); - } else { - this._onElementDimensionsTimeout.cancelAndSet(() => this._actualElementDimensions(dimensions), 0); - } + public updateState(newState:INewScrollState): void { + this._scrollable.updateState(newState); } - private _actualElementDimensions(dimensions: IDimensions = null): void { - if (!dimensions) { - dimensions = { - width: this._domNode.clientWidth, - height: this._domNode.clientHeight - }; - } - let width = Math.round(dimensions.width); - let height = Math.round(dimensions.height); - this._shouldRender = this._verticalScrollbar.onElementSize(height) || this._shouldRender; - this._shouldRender = this._horizontalScrollbar.onElementSize(width) || this._shouldRender; + public getWidth(): number { + return this._scrollable.getWidth(); + } + public getScrollWidth(): number { + return this._scrollable.getScrollWidth(); + } + public getScrollLeft(): number { + return this._scrollable.getScrollLeft(); + } + + public getHeight(): number { + return this._scrollable.getHeight(); + } + public getScrollHeight(): number { + return this._scrollable.getScrollHeight(); + } + public getScrollTop(): number { + return this._scrollable.getScrollTop(); } /** @@ -281,16 +279,9 @@ export class ScrollableElement extends Widget { e.stopPropagation(); } - private _onScroll(): void { - let scrollHeight = this._scrollable.getScrollHeight(); - let scrollTop = this._scrollable.getScrollTop(); - let scrollWidth = this._scrollable.getScrollWidth(); - let scrollLeft = this._scrollable.getScrollLeft(); - - this._shouldRender = this._horizontalScrollbar.onElementScrollSize(scrollWidth) || this._shouldRender; - this._shouldRender = this._verticalScrollbar.onElementScrollSize(scrollHeight) || this._shouldRender; - this._shouldRender = this._verticalScrollbar.onElementScrollPosition(scrollTop) || this._shouldRender; - this._shouldRender = this._horizontalScrollbar.onElementScrollPosition(scrollLeft) || this._shouldRender; + private _onDidScroll(e:ScrollEvent): void { + this._shouldRender = this._horizontalScrollbar.onDidScroll(e) || this._shouldRender; + this._shouldRender = this._verticalScrollbar.onDidScroll(e) || this._shouldRender; if (this._options.useShadows) { this._shouldRender = true; @@ -375,6 +366,38 @@ export class ScrollableElement extends Widget { } } +export class DomScrollableElement extends ScrollableElement { + + private _element: HTMLElement; + + constructor(element: HTMLElement, options: ScrollableElementCreationOptions) { + super(element, options); + this._element = element; + this.onScroll((e) => { + if (e.scrollTopChanged) { + this._element.scrollTop = e.scrollTop; + } + if (e.scrollLeftChanged) { + this._element.scrollLeft = e.scrollLeft; + } + }); + this.scanDomNode(); + } + + public scanDomNode(): void { + // widh, scrollLeft, scrollWidth, height, scrollTop, scrollHeight + this.updateState({ + width: this._element.clientWidth, + scrollWidth: this._element.scrollWidth, + scrollLeft: this._element.scrollLeft, + + height: this._element.clientHeight, + scrollHeight: this._element.scrollHeight, + scrollTop: this._element.scrollTop, + }); + } +} + function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableElementResolvedOptions { let result: ScrollableElementResolvedOptions = { forbidTranslate3dUse: (typeof opts.forbidTranslate3dUse !== 'undefined' ? opts.forbidTranslate3dUse : false), diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index 566c899bf42..a7e91d33fc9 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -9,14 +9,14 @@ import {AbstractScrollbar, ScrollbarHost, IMouseMoveEventData} from 'vs/base/bro import {IMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent'; import {IDomNodePosition} from 'vs/base/browser/dom'; import {ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; -import {DelegateScrollable} from 'vs/base/common/scrollable'; +import {Scrollable, ScrollEvent} from 'vs/base/common/scrollable'; import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState'; import {ARROW_IMG_SIZE} from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import {Visibility} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController'; export class VerticalScrollbar extends AbstractScrollbar { - constructor(scrollable: DelegateScrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { super({ forbidTranslate3dUse: options.forbidTranslate3dUse, lazyRender: options.lazyRender, @@ -78,6 +78,13 @@ export class VerticalScrollbar extends AbstractScrollbar { this.domNode.setTop(0); } + public onDidScroll(e:ScrollEvent): boolean { + this._shouldRender = this._onElementScrollSize(e.scrollHeight) || this._shouldRender; + this._shouldRender = this._onElementScrollPosition(e.scrollTop) || this._shouldRender; + this._shouldRender = this._onElementSize(e.height) || this._shouldRender; + return this._shouldRender; + } + protected _mouseDownRelativePosition(e: IMouseEvent, domNodePosition: IDomNodePosition): number { return e.posy - domNodePosition.top; } @@ -95,6 +102,8 @@ export class VerticalScrollbar extends AbstractScrollbar { } protected _setScrollPosition(scrollPosition: number): void { - this._scrollable.setScrollTop(scrollPosition); + this._scrollable.updateState({ + scrollTop: scrollPosition + }); } } diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index f12be9fcaca..b1852116e91 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -4,119 +4,149 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {Disposable, IDisposable} from 'vs/base/common/lifecycle'; +import {Disposable} from 'vs/base/common/lifecycle'; +import Event, {Emitter} from 'vs/base/common/event'; -export interface IScrollable { - getScrollHeight():number; - getScrollWidth():number; - getScrollLeft():number; - setScrollLeft(scrollLeft:number); - getScrollTop():number; - setScrollTop(scrollTop:number); - addScrollListener(callback:(newValues:ScrollEvent)=>void): IDisposable; -} - -export class ScrollEvent { - _scrollEventTrait: void; - - scrollTop: number; - scrollLeft: number; +export interface ScrollEvent { + width: number; scrollWidth: number; - scrollHeight: number; + scrollLeft: number; - scrollTopChanged: boolean; - scrollLeftChanged: boolean; + height: number; + scrollHeight: number; + scrollTop: number; + + widthChanged: boolean; scrollWidthChanged: boolean; + scrollLeftChanged: boolean; + + heightChanged: boolean; scrollHeightChanged: boolean; - - constructor(scrollTop:number, scrollLeft:number, scrollWidth:number, scrollHeight:number, scrollTopChanged = false, scrollLeftChanged = false, scrollWidthChanged = false, scrollHeightChanged = false) { - this.scrollTop = Math.round(scrollTop); - this.scrollLeft = Math.round(scrollLeft); - this.scrollWidth = Math.round(scrollWidth); - this.scrollHeight = Math.round(scrollHeight); - - this.scrollTopChanged = scrollTopChanged; - this.scrollLeftChanged = scrollLeftChanged; - this.scrollWidthChanged = scrollWidthChanged; - this.scrollHeightChanged = scrollHeightChanged; - } - - public create(scrollTop:number, scrollLeft:number, scrollWidth:number, scrollHeight:number): ScrollEvent { - return new ScrollEvent( - scrollTop, scrollLeft, scrollWidth, scrollHeight, - scrollTop !== this.scrollTop, - scrollLeft !== this.scrollLeft, - scrollWidth !== this.scrollWidth, - scrollHeight !== this.scrollHeight - ); - } + scrollTopChanged: boolean; } -export class ScrollableValues { - _scrollableValuesTrait: void; +export interface INewScrollState { + width?: number; + scrollWidth?: number; + scrollLeft?: number; - scrollTop: number; - scrollLeft: number; - scrollWidth: number; - scrollHeight: number; - - constructor(scrollTop:number, scrollLeft:number, scrollWidth:number, scrollHeight:number) { - this.scrollTop = Math.round(scrollTop); - this.scrollLeft = Math.round(scrollLeft); - this.scrollWidth = Math.round(scrollWidth); - this.scrollHeight = Math.round(scrollHeight); - } - - public equals(other:ScrollEvent): boolean { - return ( - this.scrollTop === other.scrollTop - && this.scrollLeft === other.scrollLeft - && this.scrollWidth === other.scrollWidth - && this.scrollHeight === other.scrollHeight - ); - } + height?: number; + scrollHeight?: number; + scrollTop?: number; } -export class DelegateScrollable extends Disposable { +export class Scrollable extends Disposable { - private _actual:IScrollable; - private _onChange:()=>void; + _scrollableTrait: void; - private _values: ScrollableValues; + private _width: number; + private _scrollWidth: number; + private _scrollLeft: number; - constructor(actual:IScrollable, onChange:()=>void) { + private _height: number; + private _scrollHeight: number; + private _scrollTop: number; + + private _onScroll = this._register(new Emitter()); + public onScroll: Event = this._onScroll.event; + + constructor() { super(); - this._actual = actual; - this._onChange = onChange; - this._values = new ScrollableValues(this._actual.getScrollTop(), this._actual.getScrollLeft(), this._actual.getScrollWidth(), this._actual.getScrollHeight()); - this._register(this._actual.addScrollListener((newValues) => this._update(newValues))); + this._width = 0; + this._scrollWidth = 0; + this._scrollLeft = 0; + + this._height = 0; + this._scrollHeight = 0; + this._scrollTop = 0; } - public dispose(): void { - super.dispose(); + public getWidth(): number { + return this._width; + } + public getScrollWidth(): number { + return this._scrollWidth; + } + public getScrollLeft(): number { + return this._scrollLeft; } - private _update(e:ScrollEvent): void { - if (this._values.equals(e)) { + public getHeight(): number { + return this._height; + } + public getScrollHeight(): number { + return this._scrollHeight; + } + public getScrollTop(): number { + return this._scrollTop; + } + + public updateState(newState:INewScrollState): void { + let width = (typeof newState.width !== 'undefined' ? newState.width|0 : this._width); + let scrollWidth = (typeof newState.scrollWidth !== 'undefined' ? newState.scrollWidth|0 : this._scrollWidth); + let scrollLeft = (typeof newState.scrollLeft !== 'undefined' ? newState.scrollLeft|0 : this._scrollLeft); + + let height = (typeof newState.height !== 'undefined' ? newState.height|0 : this._height); + let scrollHeight = (typeof newState.scrollHeight !== 'undefined' ? newState.scrollHeight|0 : this._scrollHeight); + let scrollTop = (typeof newState.scrollTop !== 'undefined' ? newState.scrollTop|0 : this._scrollTop); + + if (width < 0) { + width = 0; + } + if (scrollLeft + width > scrollWidth) { + scrollLeft = scrollWidth - width; + } + if (scrollLeft < 0) { + scrollLeft = 0; + } + + if (height < 0) { + height = 0; + } + if (scrollTop + height > scrollHeight) { + scrollTop = scrollHeight - height; + } + if (scrollTop < 0) { + scrollTop = 0; + } + + let widthChanged = (this._width !== width); + let scrollWidthChanged = (this._scrollWidth !== scrollWidth); + let scrollLeftChanged = (this._scrollLeft !== scrollLeft); + + let heightChanged = (this._height !== height); + let scrollHeightChanged = (this._scrollHeight !== scrollHeight); + let scrollTopChanged = (this._scrollTop !== scrollTop); + + if (!widthChanged && !scrollWidthChanged && !scrollLeftChanged && !heightChanged && !scrollHeightChanged && !scrollTopChanged) { return; } - this._values = new ScrollableValues(e.scrollTop, e.scrollLeft, e.scrollWidth, e.scrollHeight); + this._width = width; + this._scrollWidth = scrollWidth; + this._scrollLeft = scrollLeft; - this._onChange(); - } + this._height = height; + this._scrollHeight = scrollHeight; + this._scrollTop = scrollTop; - public getScrollTop():number { return this._values.scrollTop; } - public getScrollLeft():number { return this._values.scrollLeft; } - public getScrollWidth():number { return this._values.scrollWidth; } - public getScrollHeight():number { return this._values.scrollHeight; } + this._onScroll.fire({ + width: this._width, + scrollWidth: this._scrollWidth, + scrollLeft: this._scrollLeft, - public setScrollTop(scrollTop:number): void { - this._actual.setScrollTop(scrollTop); - } + height: this._height, + scrollHeight: this._scrollHeight, + scrollTop: this._scrollTop, - public setScrollLeft(scrollLeft:number): void { - this._actual.setScrollLeft(scrollLeft); + widthChanged: widthChanged, + scrollWidthChanged: scrollWidthChanged, + scrollLeftChanged: scrollLeftChanged, + + heightChanged: heightChanged, + scrollHeightChanged: scrollHeightChanged, + scrollTopChanged: scrollTopChanged, + }); } } diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 1a374664279..8cd9b64428e 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -21,7 +21,6 @@ import ScrollableElementImpl = require('vs/base/browser/ui/scrollbar/scrollableE import { HeightMap } from 'vs/base/parts/tree/browser/treeViewModel'; import _ = require('vs/base/parts/tree/browser/tree'); import { IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; -import {IScrollable, ScrollEvent} from 'vs/base/common/scrollable'; import {KeyCode} from 'vs/base/common/keyCodes'; export interface IRow { @@ -393,7 +392,7 @@ function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction): b } } -export class TreeView extends HeightMap implements IScrollable { +export class TreeView extends HeightMap { static BINDING = 'monaco-tree-row'; static LOADING_DECORATION_DELAY = 800; @@ -414,11 +413,8 @@ export class TreeView extends HeightMap implements IScrollable { private lastPointerType:string; private lastClickTimeStamp: number = 0; - private _viewHeight: number; - private renderTop: number; - private renderHeight: number; - private _scrollTop: number; - private _lastScrollEvent: ScrollEvent; + private lastRenderTop: number; + private lastRenderHeight: number; private inputItem: ViewItem; private items: { [id: string]: ViewItem; }; @@ -490,13 +486,17 @@ export class TreeView extends HeightMap implements IScrollable { this.wrapper = document.createElement('div'); this.wrapper.className = 'monaco-tree-wrapper'; - this.scrollableElement = new ScrollableElementImpl.ScrollableElement(this.wrapper, this, { + this.scrollableElement = new ScrollableElementImpl.ScrollableElement(this.wrapper, { forbidTranslate3dUse: true, horizontal: 'hidden', vertical: context.options.verticalScrollMode || 'auto', useShadows: context.options.useShadows, saveLastScrollTimeOnClassName: 'monaco-tree-row' }); + this.scrollableElement.onScroll((e) => { + this.render(e.scrollTop, e.height); + this.emit('scroll', e); // TODO@Joao: is anyone interested in this event? + }); if(Browser.isIE11orEarlier) { this.wrapper.style.msTouchAction = 'none'; @@ -551,12 +551,8 @@ export class TreeView extends HeightMap implements IScrollable { this.domNode.appendChild(this.scrollableElement.getDomNode()); container.appendChild(this.domNode); - this._scrollTop = 0; - this._viewHeight = 0; - 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.didJustPressContextMenuKey = false; @@ -598,8 +594,6 @@ export class TreeView extends HeightMap implements IScrollable { public onVisible(): void { this.scrollTop = this.onHiddenScrollTop; this.onHiddenScrollTop = null; - this.scrollableElement.onElementDimensions(); - this._emitScrollEvent(); this.setupMSGesture(); } @@ -624,23 +618,15 @@ export class TreeView extends HeightMap implements IScrollable { } this.viewHeight = height || DOM.getContentHeight(this.wrapper); // render - this.scrollTop = this.scrollTop; // render - - this.scrollableElement.onElementDimensions(); - this._emitScrollEvent(); } private render(scrollTop: number, viewHeight: number): void { - var scrollBottom = scrollTop + viewHeight; - var thisScrollBottom = this.scrollTop + this.viewHeight; var i: number; var stop: number; var renderTop = scrollTop; - renderTop = Math.max(renderTop, 0); - - var renderBottom = scrollBottom; - var thisRenderBottom = thisScrollBottom === 0 ? 0 : thisScrollBottom; + var renderBottom = scrollTop + viewHeight; + var thisRenderBottom = this.lastRenderTop + this.lastRenderHeight; // when view scrolls down, start rendering from the renderBottom for (i = this.indexAfter(renderBottom) - 1, stop = this.indexAt(Math.max(thisRenderBottom, renderTop)); i >= stop; i--) { @@ -648,17 +634,17 @@ export class TreeView extends HeightMap implements IScrollable { } // when view scrolls up, start rendering from either this.renderTop or renderBottom - for (i = Math.min(this.indexAt(this.renderTop), this.indexAfter(renderBottom)) - 1, stop = this.indexAt(renderTop); i >= stop; i--) { + for (i = Math.min(this.indexAt(this.lastRenderTop), this.indexAfter(renderBottom)) - 1, stop = this.indexAt(renderTop); i >= stop; i--) { this.insertItemInDOM( this.itemAtIndex(i)); } // when view scrolls down, start unrendering from renderTop - for (i = this.indexAt(this.renderTop), stop = Math.min(this.indexAt(renderTop), this.indexAfter(thisRenderBottom)); i < stop; i++) { + for (i = this.indexAt(this.lastRenderTop), stop = Math.min(this.indexAt(renderTop), this.indexAfter(thisRenderBottom)); i < stop; i++) { this.removeItemFromDOM( this.itemAtIndex(i)); } // when view scrolls up, start unrendering from either renderBottom this.renderTop - for (i = Math.max(this.indexAfter(renderBottom), this.indexAt(this.renderTop)), stop = this.indexAfter(thisRenderBottom); i < stop; i++) { + for (i = Math.max(this.indexAfter(renderBottom), this.indexAt(this.lastRenderTop)), stop = this.indexAfter(thisRenderBottom); i < stop; i++) { this.removeItemFromDOM( this.itemAtIndex(i)); } @@ -668,8 +654,8 @@ export class TreeView extends HeightMap implements IScrollable { this.rowsContainer.style.top = (topItem.top - renderTop) + 'px'; } - this.renderTop = renderTop; - this.renderHeight = renderBottom - renderTop; + this.lastRenderTop = renderTop; + this.lastRenderHeight = renderBottom - renderTop; } public setModel(newModel: Model.TreeModel): void { @@ -752,7 +738,6 @@ export class TreeView extends HeightMap implements IScrollable { } this.scrollTop = scrollTop; - this._emitScrollEvent(); } public focusNextPage(eventPayload?:any): void { @@ -806,61 +791,25 @@ export class TreeView extends HeightMap implements IScrollable { } public get viewHeight() { - return this._viewHeight; + return this.scrollableElement.getHeight(); } public set viewHeight(viewHeight: number) { - this.render(this.scrollTop, viewHeight); - this._viewHeight = viewHeight; - } - - // IScrollable - - public getScrollHeight():number { - return this.getTotalHeight(); - } - - public getScrollWidth():number { - return 0; - } - - public getScrollLeft():number { - return 0; - } - - public setScrollLeft(scrollLeft:number): void { - // noop + this.scrollableElement.updateState({ + height: viewHeight, + scrollHeight: this.getTotalHeight() + }); } public get scrollTop(): number { - return this._scrollTop; - } - - public getScrollTop(): number { - return this._scrollTop; + return this.scrollableElement.getScrollTop(); } public set scrollTop(scrollTop: number) { - this.setScrollTop(scrollTop); - } - - public setScrollTop(scrollTop: number): void { - scrollTop = Math.min(scrollTop, this.getTotalHeight() - this.viewHeight); - scrollTop = Math.max(scrollTop, 0); - - this.render(scrollTop, this.viewHeight); - this._scrollTop = scrollTop; - - this._emitScrollEvent(); - } - - private _emitScrollEvent(): void { - this._lastScrollEvent = this._lastScrollEvent.create(this.getScrollTop(), this.getScrollLeft(), this.getScrollWidth(), this.getScrollHeight()); - this.emit('scroll', this._lastScrollEvent); - } - - public addScrollListener(callback:(v:ScrollEvent)=>void): Lifecycle.IDisposable { - return this.addListener2('scroll', callback); + this.scrollableElement.updateState({ + scrollTop: scrollTop, + scrollHeight: this.getTotalHeight() + }); } public getScrollPosition(): number { @@ -1614,7 +1563,7 @@ export class TreeView extends HeightMap implements IScrollable { // Helpers private shouldBeRendered(item: ViewItem): boolean { - return item.top < this.renderTop + this.renderHeight && item.top + item.height > this.renderTop; + return item.top < this.lastRenderTop + this.lastRenderHeight && item.top + item.height > this.lastRenderTop; } private getItemAround(element: HTMLElement): ViewItem { diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 3a9989844e7..3c5be5ef5c8 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -98,8 +98,10 @@ class MsPointerHandler extends MouseHandler implements IDisposable { } private _onGestureChange(e:IThrottledGestureEvent): void { - this.viewHelper.setScrollTop(this.viewHelper.getScrollTop() - e.translationY); - this.viewHelper.setScrollLeft(this.viewHelper.getScrollLeft() - e.translationX); + this.viewHelper.setScrollPosition({ + scrollLeft: this.viewHelper.getScrollLeft() - e.translationX, + scrollTop: this.viewHelper.getScrollTop() - e.translationY, + }); } public dispose(): void { @@ -177,8 +179,10 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { } private _onGestureChange(e:IThrottledGestureEvent): void { - this.viewHelper.setScrollTop(this.viewHelper.getScrollTop() - e.translationY); - this.viewHelper.setScrollLeft(this.viewHelper.getScrollLeft() - e.translationX); + this.viewHelper.setScrollPosition({ + scrollLeft: this.viewHelper.getScrollLeft() - e.translationX, + scrollTop: this.viewHelper.getScrollTop() - e.translationY, + }); } public dispose(): void { @@ -220,9 +224,11 @@ class TouchHandler extends MouseHandler { } } - private onChange(event:GestureEvent): void { - this.viewHelper.setScrollTop(this.viewHelper.getScrollTop() - event.translationY); - this.viewHelper.setScrollLeft(this.viewHelper.getScrollLeft() - event.translationX); + private onChange(e:GestureEvent): void { + this.viewHelper.setScrollPosition({ + scrollLeft: this.viewHelper.getScrollLeft() - e.translationX, + scrollTop: this.viewHelper.getScrollTop() - e.translationY, + }); } } diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 622f1e5d145..f4699829ee7 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -22,12 +22,14 @@ export interface IOverlayWidgetData { } export interface ICodeEditorHelper { - getScrollTop(): number; - setScrollTop(scrollTop:number): void; - getScrollLeft(): number; - setScrollLeft(scrollLeft:number): void; - getScrollHeight(): number; getScrollWidth(): number; + getScrollLeft(): number; + + getScrollHeight(): number; + getScrollTop(): number; + + setScrollPosition(position:editorCommon.INewScrollPosition): void; + getVerticalOffsetForPosition(lineNumber:number, column:number): number; delegateVerticalScrollbarMouseDown(browserEvent:MouseEvent): void; getOffsetForColumn(lineNumber:number, column:number): number; @@ -47,10 +49,10 @@ export interface IPointerHandlerHelper { focusTextArea(): void; isDirty(): boolean; - getScrollTop(): number; - setScrollTop(scrollTop:number): void; getScrollLeft(): number; - setScrollLeft(scrollLeft:number): void; + getScrollTop(): number; + + setScrollPosition(position:editorCommon.INewScrollPosition): void; isAfterLines(verticalOffset:number): boolean; getLineNumberAtVerticalOffset(verticalOffset: number): number; @@ -236,12 +238,13 @@ export interface IScrollingProvider { // This is for the glyphs, line numbers, etc. getScrolledTopFromAbsoluteTop(top:number): number; - getScrollHeight(): number; getScrollWidth(): number; getScrollLeft(): number; - setScrollLeft(scrollLeft:number): void; + + getScrollHeight(): number; getScrollTop(): number; - setScrollTop(scrollTop:number): void; + + setScrollPosition(position:editorCommon.INewScrollPosition): void; } export interface IVerticalLayoutProvider { diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index c28c0357377..874f8e39a8f 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -299,29 +299,24 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp return (this.accumulatedModelEvents.length > 0); }, - getScrollTop: () => { - if (this._isDisposed) { - throw new Error('ViewImpl.pointerHandler.getScrollTop: View is disposed'); - } - return this.layoutProvider.getScrollTop(); - }, - setScrollTop: (scrollTop: number) => { - if (this._isDisposed) { - throw new Error('ViewImpl.pointerHandler.setScrollTop: View is disposed'); - } - this.layoutProvider.setScrollTop(scrollTop); - }, getScrollLeft: () => { if (this._isDisposed) { throw new Error('ViewImpl.pointerHandler.getScrollLeft: View is disposed'); } return this.layoutProvider.getScrollLeft(); }, - setScrollLeft: (scrollLeft: number) => { + getScrollTop: () => { if (this._isDisposed) { - throw new Error('ViewImpl.pointerHandler.setScrollLeft: View is disposed'); + throw new Error('ViewImpl.pointerHandler.getScrollTop: View is disposed'); } - this.layoutProvider.setScrollLeft(scrollLeft); + return this.layoutProvider.getScrollTop(); + }, + + setScrollPosition: (position:editorCommon.INewScrollPosition) => { + if (this._isDisposed) { + throw new Error('ViewImpl.pointerHandler.setScrollPosition: View is disposed'); + } + this.layoutProvider.setScrollPosition(position); }, isAfterLines: (verticalOffset: number) => { @@ -522,17 +517,11 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp public getCodeEditorHelper(): editorBrowser.ICodeEditorHelper { if (!this.codeEditorHelper) { this.codeEditorHelper = { - getScrollTop: () => { + getScrollWidth: () => { if (this._isDisposed) { - throw new Error('ViewImpl.codeEditorHelper.getScrollTop: View is disposed'); + throw new Error('ViewImpl.codeEditorHelper.getScrollWidth: View is disposed'); } - return this.layoutProvider.getScrollTop(); - }, - setScrollTop: (scrollTop: number) => { - if (this._isDisposed) { - throw new Error('ViewImpl.codeEditorHelper.setScrollTop: View is disposed'); - } - this.layoutProvider.setScrollTop(scrollTop); + return this.layoutProvider.getScrollWidth(); }, getScrollLeft: () => { if (this._isDisposed) { @@ -540,24 +529,27 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp } return this.layoutProvider.getScrollLeft(); }, - setScrollLeft: (scrollLeft: number) => { - if (this._isDisposed) { - throw new Error('ViewImpl.codeEditorHelper.setScrollLeft: View is disposed'); - } - this.layoutProvider.setScrollLeft(scrollLeft); - }, + getScrollHeight: () => { if (this._isDisposed) { throw new Error('ViewImpl.codeEditorHelper.getScrollHeight: View is disposed'); } return this.layoutProvider.getScrollHeight(); }, - getScrollWidth: () => { + getScrollTop: () => { if (this._isDisposed) { - throw new Error('ViewImpl.codeEditorHelper.getScrollWidth: View is disposed'); + throw new Error('ViewImpl.codeEditorHelper.getScrollTop: View is disposed'); } - return this.layoutProvider.getScrollWidth(); + return this.layoutProvider.getScrollTop(); }, + + setScrollPosition: (position:editorCommon.INewScrollPosition) => { + if (this._isDisposed) { + throw new Error('ViewImpl.codeEditorHelper.setScrollPosition: View is disposed'); + } + this.layoutProvider.setScrollPosition(position); + }, + getVerticalOffsetForPosition: (modelLineNumber:number, modelColumn:number) => { if (this._isDisposed) { throw new Error('ViewImpl.codeEditorHelper.getVerticalOffsetForPosition: View is disposed'); diff --git a/src/vs/editor/browser/viewLayout/layoutProvider.ts b/src/vs/editor/browser/viewLayout/layoutProvider.ts index 16c5ca1f2e5..4ec1ece7d8d 100644 --- a/src/vs/editor/browser/viewLayout/layoutProvider.ts +++ b/src/vs/editor/browser/viewLayout/layoutProvider.ts @@ -6,7 +6,6 @@ import {IDisposable} from 'vs/base/common/lifecycle'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import {EditorScrollable} from 'vs/editor/common/viewLayout/editorScrollable'; import {LinesLayout} from 'vs/editor/common/viewLayout/linesLayout'; import {ViewEventHandler} from 'vs/editor/common/viewModel/viewEventHandler'; import {ILayoutProvider} from 'vs/editor/browser/editorBrowser'; @@ -21,7 +20,6 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa private model:editorCommon.IViewModel; private scrollManager:ScrollManager; private linesLayout: LinesLayout; - private scrollable: EditorScrollable; constructor(configuration:editorCommon.IConfiguration, model:editorCommon.IViewModel, privateViewEventBus:editorCommon.IViewEventBus, linesContent:HTMLElement, viewDomNode:HTMLElement, overflowGuardDomNode:HTMLElement) { super(); @@ -30,10 +28,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa this.privateViewEventBus = privateViewEventBus; this.model = model; - this.scrollable = new EditorScrollable(); - this.scrollable.setWidth(this.configuration.editor.layoutInfo.contentWidth); - this.scrollable.setHeight(this.configuration.editor.layoutInfo.contentHeight); - this.scrollManager = new ScrollManager(this.scrollable, configuration, privateViewEventBus, linesContent, viewDomNode, overflowGuardDomNode); + this.scrollManager = new ScrollManager(configuration, privateViewEventBus, linesContent, viewDomNode, overflowGuardDomNode); this.configuration.setLineCount(this.model.getLineCount()); @@ -44,7 +39,6 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa public dispose(): void { this.scrollManager.dispose(); - this.scrollable.dispose(); } private updateLineCount(): void { @@ -82,9 +76,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa public onConfigurationChanged(e:editorCommon.IConfigurationChangedEvent): boolean { this.linesLayout.onConfigurationChanged(e); if (e.layoutInfo) { - this.scrollable.setWidth(this.configuration.editor.layoutInfo.contentWidth); - this.scrollable.setHeight(this.configuration.editor.layoutInfo.contentHeight); - this.scrollManager.onSizeProviderLayoutChanged(); + this.scrollManager.onLayoutInfoChanged(); this._emitLayoutChangedEvent(); } this._updateHeight(); @@ -92,7 +84,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa } private _updateHeight(): void { - this.scrollable.setScrollHeight(this.getTotalHeight()); + this.scrollManager.setScrollHeight(this.getTotalHeight()); } // ---- end view event handlers @@ -101,10 +93,10 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa public getCurrentViewport(): editorCommon.Viewport { return new editorCommon.Viewport( - this.scrollable.getScrollTop(), - this.scrollable.getScrollLeft(), - this.scrollable.getWidth(), - this.scrollable.getHeight() + this.scrollManager.getScrollTop(), + this.scrollManager.getScrollLeft(), + this.scrollManager.getWidth(), + this.scrollManager.getHeight() ); } @@ -130,7 +122,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa public onMaxLineWidthChanged(maxLineWidth:number): void { var newScrollWidth = this._computeScrollWidth(maxLineWidth, this.getCurrentViewport().width); - this.scrollable.setScrollWidth(newScrollWidth); + this.scrollManager.setScrollWidth(newScrollWidth); // The height might depend on the fact that there is a horizontal scrollbar or not this._updateHeight(); @@ -139,13 +131,13 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa // ---- view state public saveState(): editorCommon.IViewState { - var scrollTop = this.scrollable.getScrollTop(); + var scrollTop = this.scrollManager.getScrollTop(); var firstLineNumberInViewport = this.linesLayout.getLineNumberAtOrAfterVerticalOffset(scrollTop); var whitespaceAboveFirstLine = this.linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(firstLineNumberInViewport); return { scrollTop: scrollTop, scrollTopWithoutViewZones: scrollTop - whitespaceAboveFirstLine, - scrollLeft: this.scrollable.getScrollLeft() + scrollLeft: this.scrollManager.getScrollLeft() }; } @@ -154,11 +146,12 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa if (typeof state.scrollTopWithoutViewZones === 'number' && !this.linesLayout.hasWhitespace()) { restoreScrollTop = state.scrollTopWithoutViewZones; } - this.scrollable.setScrollTop(restoreScrollTop); - this.scrollable.setScrollLeft(state.scrollLeft); + this.scrollManager.setScrollPosition({ + scrollLeft: state.scrollLeft, + scrollTop: restoreScrollTop + }); } - // ---- IVerticalLayoutProvider public addWhitespace(afterLineNumber:number, ordinal:number, height:number): number { @@ -184,7 +177,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa } public getTotalHeight(): number { var reserveHorizontalScrollbarHeight = 0; - if (this.scrollable.getScrollWidth() > this.scrollable.getWidth()) { + if (this.scrollManager.getScrollWidth() > this.scrollManager.getWidth()) { reserveHorizontalScrollbarHeight = this.configuration.editor.scrollbar.horizontalScrollbarSize; } return this.linesLayout.getTotalHeight(this.getCurrentViewport(), reserveHorizontalScrollbarHeight); @@ -217,26 +210,24 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa public delegateVerticalScrollbarMouseDown(browserEvent:MouseEvent): void { this.scrollManager.delegateVerticalScrollbarMouseDown(browserEvent); } - public getScrollHeight(): number { - return this.scrollable.getScrollHeight(); - } public getScrollWidth(): number { - return this.scrollable.getScrollWidth(); + return this.scrollManager.getScrollWidth(); } public getScrollLeft(): number { - return this.scrollable.getScrollLeft(); + return this.scrollManager.getScrollLeft(); } - public setScrollLeft(scrollLeft:number): void { - this.scrollable.setScrollLeft(scrollLeft); + public getScrollHeight(): number { + return this.scrollManager.getScrollHeight(); } public getScrollTop(): number { - return this.scrollable.getScrollTop(); + return this.scrollManager.getScrollTop(); } - public setScrollTop(scrollTop:number): void { - this.scrollable.setScrollTop(scrollTop); + + public setScrollPosition(position:editorCommon.INewScrollPosition): void { + this.scrollManager.setScrollPosition(position); } public getScrolledTopFromAbsoluteTop(top:number): number { - return top - this.scrollable.getScrollTop(); + return top - this.scrollManager.getScrollTop(); } public renderScrollbar(): void { diff --git a/src/vs/editor/browser/viewLayout/scrollManager.ts b/src/vs/editor/browser/viewLayout/scrollManager.ts index 17d7c8c977a..5ca440e3819 100644 --- a/src/vs/editor/browser/viewLayout/scrollManager.ts +++ b/src/vs/editor/browser/viewLayout/scrollManager.ts @@ -8,8 +8,7 @@ import {IDisposable, dispose} from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import {ScrollableElementCreationOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import {IOverviewRulerLayoutInfo, ScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import {EventType, IConfiguration, IConfigurationChangedEvent, IScrollEvent, IViewEventBus} from 'vs/editor/common/editorCommon'; -import {EditorScrollable} from 'vs/editor/common/viewLayout/editorScrollable'; +import {EventType, IConfiguration, IConfigurationChangedEvent, IScrollEvent, IViewEventBus, INewScrollPosition} from 'vs/editor/common/editorCommon'; import {ClassNames} from 'vs/editor/browser/editorBrowser'; function addPropertyIfPresent(src:any, dst:any, prop:string): void { @@ -24,21 +23,15 @@ export class ScrollManager implements IDisposable { private privateViewEventBus:IViewEventBus; private toDispose:IDisposable[]; - private scrollable: EditorScrollable; private linesContent: HTMLElement; private scrollbar: ScrollableElement; - constructor(scrollable:EditorScrollable, configuration:IConfiguration, privateViewEventBus:IViewEventBus, linesContent:HTMLElement, viewDomNode:HTMLElement, overflowGuardDomNode:HTMLElement) { + constructor(configuration:IConfiguration, privateViewEventBus:IViewEventBus, linesContent:HTMLElement, viewDomNode:HTMLElement, overflowGuardDomNode:HTMLElement) { this.toDispose = []; - this.scrollable = scrollable; this.configuration = configuration; this.privateViewEventBus = privateViewEventBus; this.linesContent = linesContent; - this.toDispose.push(this.scrollable.addScrollListener((e:IScrollEvent) => { - this.privateViewEventBus.emit(EventType.ViewScrollChanged, e); - })); - var configScrollbarOpts = this.configuration.editor.scrollbar; var scrollbarOptions:ScrollableElementCreationOptions = { @@ -60,12 +53,12 @@ export class ScrollManager implements IDisposable { addPropertyIfPresent(configScrollbarOpts, scrollbarOptions, 'arrowSize'); addPropertyIfPresent(configScrollbarOpts, scrollbarOptions, 'mouseWheelScrollSensitivity'); - - this.scrollbar = new ScrollableElement(linesContent, this.scrollable, scrollbarOptions, { - width: this.configuration.editor.layoutInfo.contentWidth, - height: this.configuration.editor.layoutInfo.contentHeight, - }); + this.scrollbar = new ScrollableElement(linesContent, scrollbarOptions); + this.onLayoutInfoChanged(); this.toDispose.push(this.scrollbar); + this.toDispose.push(this.scrollbar.onScroll((e:IScrollEvent) => { + this.privateViewEventBus.emit(EventType.ViewScrollChanged, e); + })); this.toDispose.push(this.configuration.onDidChange((e:IConfigurationChangedEvent) => { this.scrollbar.updateClassName(this.configuration.editor.theme); @@ -79,10 +72,12 @@ export class ScrollManager implements IDisposable { // changing the .scrollTop of this.linesContent var onBrowserDesperateReveal = (domNode:HTMLElement, lookAtScrollTop:boolean, lookAtScrollLeft:boolean) => { + let newScrollPosition:INewScrollPosition = {}; + if (lookAtScrollTop) { var deltaTop = domNode.scrollTop; if (deltaTop) { - this.scrollable.setScrollTop(this.scrollable.getScrollTop() + deltaTop); + newScrollPosition.scrollTop = this.getScrollTop() + deltaTop; domNode.scrollTop = 0; } } @@ -90,10 +85,12 @@ export class ScrollManager implements IDisposable { if (lookAtScrollLeft) { var deltaLeft = domNode.scrollLeft; if (deltaLeft) { - this.scrollable.setScrollLeft(this.scrollable.getScrollLeft() + deltaLeft); + newScrollPosition.scrollLeft = this.getScrollLeft() + deltaLeft; domNode.scrollLeft = 0; } } + + this.setScrollPosition(newScrollPosition); }; // I've seen this happen both on the view dom node & on the lines content dom node. @@ -110,32 +107,57 @@ export class ScrollManager implements IDisposable { this.scrollbar.renderNow(); } - public onSizeProviderLayoutChanged(): void { - if (this.scrollbar) { - this.scrollbar.onElementDimensions({ - width: this.configuration.editor.layoutInfo.contentWidth, - height: this.configuration.editor.layoutInfo.contentHeight, - }); - } + public onLayoutInfoChanged(): void { + this.scrollbar.updateState({ + width: this.configuration.editor.layoutInfo.contentWidth, + height: this.configuration.editor.layoutInfo.contentHeight + }); } public getOverviewRulerLayoutInfo(): IOverviewRulerLayoutInfo { - if (this.scrollbar) { - return this.scrollbar.getOverviewRulerLayoutInfo(); - } - return null; + return this.scrollbar.getOverviewRulerLayoutInfo(); } public getScrollbarContainerDomNode(): HTMLElement { - if (this.scrollbar) { - return this.scrollbar.getDomNode(); - } - return this.linesContent; + return this.scrollbar.getDomNode(); } public delegateVerticalScrollbarMouseDown(browserEvent:MouseEvent): void { - if (this.scrollbar) { - this.scrollbar.delegateVerticalScrollbarMouseDown(browserEvent); - } + this.scrollbar.delegateVerticalScrollbarMouseDown(browserEvent); + } + + public getWidth(): number { + return this.scrollbar.getWidth(); + } + public getScrollWidth(): number { + return this.scrollbar.getScrollWidth(); + } + public getScrollLeft(): number { + return this.scrollbar.getScrollLeft(); + } + + public getHeight(): number { + return this.scrollbar.getHeight(); + } + public getScrollHeight(): number { + return this.scrollbar.getScrollHeight(); + } + public getScrollTop(): number { + return this.scrollbar.getScrollTop(); + } + + public setScrollPosition(position:INewScrollPosition): void { + this.scrollbar.updateState(position); + } + + public setScrollHeight(scrollHeight:number): void { + this.scrollbar.updateState({ + scrollHeight: scrollHeight + }); + } + public setScrollWidth(scrollWidth:number): void { + this.scrollbar.updateState({ + scrollWidth: scrollWidth + }); } } diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 65691ceac2c..de1c7b8b978 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -146,7 +146,9 @@ export class ViewLines extends ViewLayer { this._lastCursorRevealRangeHorizontallyEvent = e; } - this._layoutProvider.setScrollTop(newScrollTop); + this._layoutProvider.setScrollPosition({ + scrollTop: newScrollTop + }); return true; } @@ -154,7 +156,9 @@ export class ViewLines extends ViewLayer { public onCursorScrollRequest(e:editorCommon.IViewScrollRequestEvent): boolean { let currentScrollTop = this._layoutProvider.getScrollTop(); let newScrollTop = currentScrollTop + e.deltaLines * this._lineHeight; - this._layoutProvider.setScrollTop(newScrollTop); + this._layoutProvider.setScrollPosition({ + scrollTop: newScrollTop + }); return true; } @@ -387,7 +391,9 @@ export class ViewLines extends ViewLayer { } // set `scrollLeft` - this._layoutProvider.setScrollLeft(newScrollLeft.scrollLeft); + this._layoutProvider.setScrollPosition({ + scrollLeft: newScrollLeft.scrollLeft + }); } // (4) handle scrolling diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 7307308e107..0b9ecc59f23 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -130,16 +130,25 @@ export class CodeEditorWidget extends CommonCodeEditor implements editorBrowser. return this._view.getCenteredRangeInViewport(); } - public setScrollTop(newScrollTop:number): void { + public getScrollWidth(): number { if (!this.hasView) { - return; + return -1; } - if (typeof newScrollTop !== 'number') { - throw new Error('Invalid arguments'); + return this._view.getCodeEditorHelper().getScrollWidth(); + } + public getScrollLeft(): number { + if (!this.hasView) { + return -1; } - this._view.getCodeEditorHelper().setScrollTop(newScrollTop); + return this._view.getCodeEditorHelper().getScrollLeft(); } + public getScrollHeight(): number { + if (!this.hasView) { + return -1; + } + return this._view.getCodeEditorHelper().getScrollHeight(); + } public getScrollTop(): number { if (!this.hasView) { return -1; @@ -147,13 +156,6 @@ export class CodeEditorWidget extends CommonCodeEditor implements editorBrowser. return this._view.getCodeEditorHelper().getScrollTop(); } - public delegateVerticalScrollbarMouseDown(browserEvent:MouseEvent): void { - if (!this.hasView) { - return; - } - this._view.getCodeEditorHelper().delegateVerticalScrollbarMouseDown(browserEvent); - } - public setScrollLeft(newScrollLeft:number): void { if (!this.hasView) { return; @@ -161,28 +163,33 @@ export class CodeEditorWidget extends CommonCodeEditor implements editorBrowser. if (typeof newScrollLeft !== 'number') { throw new Error('Invalid arguments'); } - this._view.getCodeEditorHelper().setScrollLeft(newScrollLeft); + this._view.getCodeEditorHelper().setScrollPosition({ + scrollLeft: newScrollLeft + }); + } + public setScrollTop(newScrollTop:number): void { + if (!this.hasView) { + return; + } + if (typeof newScrollTop !== 'number') { + throw new Error('Invalid arguments'); + } + this._view.getCodeEditorHelper().setScrollPosition({ + scrollTop: newScrollTop + }); + } + public setScrollPosition(position: editorCommon.INewScrollPosition): void { + if (!this.hasView) { + return; + } + this._view.getCodeEditorHelper().setScrollPosition(position); } - public getScrollLeft(): number { + public delegateVerticalScrollbarMouseDown(browserEvent:MouseEvent): void { if (!this.hasView) { - return -1; + return; } - return this._view.getCodeEditorHelper().getScrollLeft(); - } - - public getScrollWidth(): number { - if (!this.hasView) { - return -1; - } - return this._view.getCodeEditorHelper().getScrollWidth(); - } - - public getScrollHeight(): number { - if (!this.hasView) { - return -1; - } - return this._view.getCodeEditorHelper().getScrollHeight(); + this._view.getCodeEditorHelper().delegateVerticalScrollbarMouseDown(browserEvent); } public saveViewState(): editorCommon.ICodeEditorViewState { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 8af52c3c380..5e5a472a6b9 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -813,8 +813,10 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif return; } this._isHandlingScrollEvent = true; - this.modifiedEditor.setScrollLeft(e.scrollLeft); - this.modifiedEditor.setScrollTop(e.scrollTop); + this.modifiedEditor.setScrollPosition({ + scrollLeft: e.scrollLeft, + scrollTop: e.scrollTop + }); this._isHandlingScrollEvent = false; } @@ -823,8 +825,10 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif return; } this._isHandlingScrollEvent = true; - this.originalEditor.setScrollLeft(e.scrollLeft); - this.originalEditor.setScrollTop(e.scrollTop); + this.originalEditor.setScrollPosition({ + scrollLeft: e.scrollLeft, + scrollTop: e.scrollTop + }); this._isHandlingScrollEvent = false; } diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts index 86b979540e8..2fdbd80ec3a 100644 --- a/src/vs/editor/common/commonCodeEditor.ts +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -477,20 +477,17 @@ export abstract class CommonCodeEditor extends EventEmitter implements IActionPr this.cursor.setSelections('api', ranges); } - public abstract setScrollTop(newScrollTop:number): void; + public abstract getScrollWidth(): number; + public abstract getScrollLeft(): number; + public abstract getScrollHeight(): number; public abstract getScrollTop(): number; public abstract setScrollLeft(newScrollLeft:number): void; - - public abstract getScrollLeft(): number; - - public abstract getScrollWidth(): number; - - public abstract getScrollHeight(): number; + public abstract setScrollTop(newScrollTop:number): void; + public abstract setScrollPosition(position: editorCommon.INewScrollPosition): void; public abstract saveViewState(): editorCommon.ICodeEditorViewState; - public abstract restoreViewState(state:editorCommon.IEditorViewState): void; public onVisible(): void { diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index ef41223fa60..c2e3fdb5223 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -2756,6 +2756,11 @@ export interface IScrollEvent { scrollHeightChanged: boolean; } +export interface INewScrollPosition { + scrollLeft?: number; + scrollTop?: number; +} + export interface IViewLinesDeletedEvent { /** * At what line the deletion began (inclusive). @@ -3308,9 +3313,18 @@ export interface ICommonCodeEditor extends IEditor { setValue(newValue: string): void; /** - * Change the scrollTop of the editor's viewport. + * Get the scrollWidth of the editor's viewport. */ - setScrollTop(newScrollTop: number): void; + getScrollWidth(): number; + /** + * Get the scrollLeft of the editor's viewport. + */ + getScrollLeft(): number; + + /** + * Get the scrollHeight of the editor's viewport. + */ + getScrollHeight(): number; /** * Get the scrollTop of the editor's viewport. */ @@ -3321,19 +3335,13 @@ export interface ICommonCodeEditor extends IEditor { */ setScrollLeft(newScrollLeft: number): void; /** - * Get the scrollLeft of the editor's viewport. + * Change the scrollTop of the editor's viewport. */ - getScrollLeft(): number; - + setScrollTop(newScrollTop: number): void; /** - * Get the scrollWidth of the editor's viewport. + * Change the scroll position of the editor's viewport. */ - getScrollWidth(): number; - - /** - * Get the scrollHeight of the editor's viewport. - */ - getScrollHeight(): number; + setScrollPosition(position: INewScrollPosition): void; /** * Get an action that is a contribution to this editor. diff --git a/src/vs/editor/common/viewLayout/editorScrollable.ts b/src/vs/editor/common/viewLayout/editorScrollable.ts deleted file mode 100644 index 158a0cafbb9..00000000000 --- a/src/vs/editor/common/viewLayout/editorScrollable.ts +++ /dev/null @@ -1,180 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import {EventEmitter} from 'vs/base/common/eventEmitter'; -import {IDisposable} from 'vs/base/common/lifecycle'; -import {IScrollable, ScrollEvent} from 'vs/base/common/scrollable'; - -export class EditorScrollable extends EventEmitter implements IScrollable { - - private scrollTop: number; - private scrollLeft: number; - private scrollWidth: number; - private scrollHeight: number; - private width: number; - private height: number; - private _lastScrollEvent: ScrollEvent; - - constructor() { - super([ - EditorScrollable._SCROLL_EVENT - ]); - - this.scrollTop = 0; - this.scrollLeft = 0; - this.scrollWidth = 0; - this.scrollHeight = 0; - this.width = 0; - this.height = 0; - - this._lastScrollEvent = new ScrollEvent(this.getScrollTop(), this.getScrollLeft(), this.getScrollWidth(), this.getScrollHeight()); - } - - public dispose(): void { - super.dispose(); - } - - // ------------ (visible) width - - public getWidth(): number { - return this.width; - } - - public setWidth(width: number): void { - width = Math.floor(width); - if (width < 0) { - width = 0; - } - - if (this.width !== width) { - this.width = width; - - // Revalidate - this.setScrollWidth(this.scrollWidth); - this.setScrollLeft(this.scrollLeft); - } - } - - // ------------ scroll width - - public getScrollWidth(): number { - return this.scrollWidth; - } - - public setScrollWidth(scrollWidth:number): void { - scrollWidth = Math.floor(scrollWidth); - if (scrollWidth < this.width) { - scrollWidth = this.width; - } - - if (this.scrollWidth !== scrollWidth) { - this.scrollWidth = scrollWidth; - - // Revalidate - this.setScrollLeft(this.scrollLeft); - - this._emitScrollEvent(); - } - } - - // ------------ scroll left - - public getScrollLeft(): number { - return this.scrollLeft; - } - - public setScrollLeft(scrollLeft:number): void { - scrollLeft = Math.floor(scrollLeft); - if (scrollLeft < 0) { - scrollLeft = 0; - } - if (scrollLeft + this.width > this.scrollWidth) { - scrollLeft = this.scrollWidth - this.width; - } - - if (this.scrollLeft !== scrollLeft) { - this.scrollLeft = scrollLeft; - - this._emitScrollEvent(); - } - } - - // ------------ (visible) height - - public getHeight(): number { - return this.height; - } - - public setHeight(height: number): void { - height = Math.floor(height); - if (height < 0) { - height = 0; - } - - if (this.height !== height) { - this.height = height; - - // Revalidate - this.setScrollHeight(this.scrollHeight); - this.setScrollTop(this.scrollTop); - } - } - - // ------------ scroll height - - public getScrollHeight(): number { - return this.scrollHeight; - } - - public setScrollHeight(scrollHeight: number): void { - scrollHeight = Math.floor(scrollHeight); - if (scrollHeight < this.height) { - scrollHeight = this.height; - } - - if (this.scrollHeight !== scrollHeight) { - this.scrollHeight = scrollHeight; - - // Revalidate - this.setScrollTop(this.scrollTop); - - this._emitScrollEvent(); - } - } - - // ------------ scroll top - - public getScrollTop(): number { - return this.scrollTop; - } - - public setScrollTop(scrollTop:number): void { - scrollTop = Math.floor(scrollTop); - if (scrollTop < 0) { - scrollTop = 0; - } - if (scrollTop + this.height > this.scrollHeight) { - scrollTop = this.scrollHeight - this.height; - } - - if (this.scrollTop !== scrollTop) { - this.scrollTop = scrollTop; - - this._emitScrollEvent(); - } - } - - // ------------ events - - static _SCROLL_EVENT = 'scroll'; - private _emitScrollEvent(): void { - this._lastScrollEvent = this._lastScrollEvent.create(this.getScrollTop(), this.getScrollLeft(), this.getScrollWidth(), this.getScrollHeight()); - this.emit(EditorScrollable._SCROLL_EVENT, this._lastScrollEvent); - } - public addScrollListener(listener: (e:ScrollEvent) => void): IDisposable { - return this.addListener2(EditorScrollable._SCROLL_EVENT, listener); - } -} diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index a67f23885f9..896af2b9be0 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -17,7 +17,7 @@ import {addClass, append, emmet as $, hide, removeClass, show, toggleClass} from import {HighlightedLabel} from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import {IDelegate, IFocusChangeEvent, IRenderer, ISelectionChangeEvent} from 'vs/base/browser/ui/list/list'; import {List} from 'vs/base/browser/ui/list/listWidget'; -import {ScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import {DomScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IKeybindingContextKey, IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; @@ -28,7 +28,6 @@ import {CONTEXT_SUGGESTION_SUPPORTS_ACCEPT_ON_KEY} from '../common/suggest'; import {CompletionItem, CompletionModel} from './completionModel'; import {ICancelEvent, ISuggestEvent, ITriggerEvent, SuggestModel} from './suggestModel'; import {alert} from 'vs/base/browser/ui/aria/aria'; -import {DomNodeScrollable} from 'vs/base/browser/ui/scrollbar/domNodeScrollable'; interface ISuggestionTemplateData { root: HTMLElement; @@ -182,8 +181,7 @@ class SuggestionDetails { private el: HTMLElement; private title: HTMLElement; private back: HTMLElement; - private scrollable: DomNodeScrollable; - private scrollbar: ScrollableElement; + private scrollbar: DomScrollableElement; private body: HTMLElement; private type: HTMLElement; private docs: HTMLElement; @@ -196,8 +194,7 @@ class SuggestionDetails { this.back = append(header, $('span.go-back.octicon.octicon-mail-reply')); this.back.title = nls.localize('goback', "Go back"); this.body = $('.body'); - this.scrollable = new DomNodeScrollable(this.body); - this.scrollbar = new ScrollableElement(this.body, this.scrollable, {}); + this.scrollbar = new DomScrollableElement(this.body, {}); append(this.el, this.scrollbar.getDomNode()); this.type = append(this.body, $('p.type')); this.docs = append(this.body, $('p.docs')); @@ -231,8 +228,7 @@ class SuggestionDetails { this.widget.toggleDetails(); }; - this.scrollbar.onElementDimensions(); - this.scrollable.onContentsDimensions(); + this.scrollbar.scanDomNode(); this.ariaLabel = strings.format('{0}\n{1}\n{2}', item.suggestion.label || '', item.suggestion.typeLabel || '', item.suggestion.documentationLabel || ''); } @@ -259,7 +255,6 @@ class SuggestionDetails { dispose(): void { this.scrollbar.dispose(); - this.scrollable.dispose(); this.el.parentElement.removeChild(this.el); this.el = null; diff --git a/src/vs/editor/test/common/mocks/mockCodeEditor.ts b/src/vs/editor/test/common/mocks/mockCodeEditor.ts index 8771ecc3830..ea0e41ecb73 100644 --- a/src/vs/editor/test/common/mocks/mockCodeEditor.ts +++ b/src/vs/editor/test/common/mocks/mockCodeEditor.ts @@ -24,17 +24,25 @@ export class MockCodeEditor extends CommonCodeEditor { return new MockConfiguration(options); } public getCenteredRangeInViewport(): editorCommon.IEditorRange { return null; } - public setScrollTop(newScrollTop:number): void { } - public getScrollTop(): number { return 0; } - public setScrollLeft(newScrollLeft:number): void { } - public getScrollLeft(): number { return 0; } + public getScrollWidth(): number { return 0; } + public getScrollLeft(): number { return 0; } + public getScrollHeight(): number { return 0; } + public getScrollTop(): number { return 0; } + + public setScrollLeft(newScrollLeft:number): void { } + public setScrollTop(newScrollTop:number): void { } + public setScrollPosition(position: editorCommon.INewScrollPosition): void { } + public saveViewState(): editorCommon.ICodeEditorViewState { return null; } public restoreViewState(state:editorCommon.IEditorViewState): void { } + public layout(dimension?:editorCommon.IDimension): void { } + public focus(): void { } public isFocused(): boolean { return true; } + protected _enableEmptySelectionClipboard(): boolean { return false; } protected _createView(): void { } protected _getViewInternalEventBus(): IEventEmitter { return new EventEmitter(); } diff --git a/src/vs/editor/test/common/viewLayout/editorScrollable.test.ts b/src/vs/editor/test/common/viewLayout/editorScrollable.test.ts deleted file mode 100644 index b9ab9bbd887..00000000000 --- a/src/vs/editor/test/common/viewLayout/editorScrollable.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as assert from 'assert'; -import {EditorScrollable} from 'vs/editor/common/viewLayout/editorScrollable'; - -suite('Editor ViewLayout - EditorScrollable', () => { - - function assertScrollState(scrollable:EditorScrollable, scrollTop:number, scrollLeft:number, width:number, height:number, scrollWidth:number, scrollHeight:number) { - assert.equal(scrollable.getScrollTop(), scrollTop); - assert.equal(scrollable.getScrollLeft(), scrollLeft); - assert.equal(scrollable.getScrollWidth(), scrollWidth); - assert.equal(scrollable.getScrollHeight(), scrollHeight); - assert.equal(scrollable.getWidth(), width); - assert.equal(scrollable.getHeight(), height); - } - - test('EditorScrollable', () => { - var scrollable = new EditorScrollable(); - - scrollable.setWidth(100); - scrollable.setHeight(100); - - assertScrollState(scrollable, 0, 0, 100, 100, 100, 100); - - // Make it vertically scrollable - scrollable.setScrollHeight(1000); - assertScrollState(scrollable, 0, 0, 100, 100, 100, 1000); - - // Scroll vertically... - scrollable.setScrollTop(10); - assertScrollState(scrollable, 10, 0, 100, 100, 100, 1000); - scrollable.setScrollTop(900); - assertScrollState(scrollable, 900, 0, 100, 100, 100, 1000); - scrollable.setScrollTop(-1); - assertScrollState(scrollable, 0, 0, 100, 100, 100, 1000); - scrollable.setScrollTop(901); - assertScrollState(scrollable, 900, 0, 100, 100, 100, 1000); - scrollable.setScrollTop(9001); - assertScrollState(scrollable, 900, 0, 100, 100, 100, 1000); - - // Increase vertical size => scrollTop should readjust - scrollable.setHeight(200); - assertScrollState(scrollable, 800, 0, 100, 200, 100, 1000); - - // Reset height & scrollHeight - scrollable.setScrollHeight(100); - assertScrollState(scrollable, 0, 0, 100, 200, 100, 200); - scrollable.setHeight(100); - assertScrollState(scrollable, 0, 0, 100, 100, 100, 200); - scrollable.setScrollHeight(100); - assertScrollState(scrollable, 0, 0, 100, 100, 100, 100); - - // Make it vertically scrollable - scrollable.setScrollWidth(1000); - assertScrollState(scrollable, 0, 0, 100, 100, 1000, 100); - - // Scroll horizontally... - scrollable.setScrollLeft(10); - assertScrollState(scrollable, 0, 10, 100, 100, 1000, 100); - scrollable.setScrollLeft(900); - assertScrollState(scrollable, 0, 900, 100, 100, 1000, 100); - scrollable.setScrollLeft(-1); - assertScrollState(scrollable, 0, 0, 100, 100, 1000, 100); - scrollable.setScrollLeft(901); - assertScrollState(scrollable, 0, 900, 100, 100, 1000, 100); - scrollable.setScrollLeft(9001); - assertScrollState(scrollable, 0, 900, 100, 100, 1000, 100); - - // Increase horizontal size => scrollLeft should readjust - scrollable.setWidth(200); - assertScrollState(scrollable, 0, 800, 200, 100, 1000, 100); - - // Validate with / height - scrollable.setWidth(-1); - assertScrollState(scrollable, 0, 800, 0, 100, 1000, 100); - scrollable.setScrollWidth(-1); - assertScrollState(scrollable, 0, 0, 0, 100, 0, 100); - scrollable.setHeight(-1); - assertScrollState(scrollable, 0, 0, 0, 0, 0, 100); - scrollable.setScrollHeight(-1); - assertScrollState(scrollable, 0, 0, 0, 0, 0, 0); - }); -}); diff --git a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts index 6ca6027fd27..1ae8b918f0d 100644 --- a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts @@ -12,14 +12,13 @@ import URI from 'vs/base/common/uri'; import {Sash, ISashEvent, IVerticalSashLayoutProvider} from 'vs/base/browser/ui/sash/sash'; import {Dimension, Builder, $} from 'vs/base/browser/builder'; import {ResourceViewer} from 'vs/base/browser/ui/resourceviewer/resourceViewer'; -import {ScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import {DomScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement'; import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor'; import {EditorInput, EditorOptions} from 'vs/workbench/common/editor'; import {BinaryEditorModel} from 'vs/workbench/common/editor/binaryEditorModel'; import {DiffEditorModel} from 'vs/workbench/common/editor/diffEditorModel'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; -import {DomNodeScrollable} from 'vs/base/browser/ui/scrollbar/domNodeScrollable'; /** * An implementation of editor for diffing binary files like images or videos. @@ -31,11 +30,9 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas private static MIN_CONTAINER_WIDTH = 100; private leftBinaryContainer: Builder; - private leftScrollable: DomNodeScrollable; - private leftScrollbar: ScrollableElement; + private leftScrollbar: DomScrollableElement; private rightBinaryContainer: Builder; - private rightScrollable: DomNodeScrollable; - private rightScrollbar: ScrollableElement; + private rightScrollbar: DomScrollableElement; private sash: Sash; private dimension: Dimension; private leftContainerWidth: number; @@ -61,8 +58,7 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas this.leftBinaryContainer.tabindex(0); // enable focus support from the editor part (do not remove) // Left Custom Scrollbars - this.leftScrollable = new DomNodeScrollable(leftBinaryContainerElement); - this.leftScrollbar = new ScrollableElement(leftBinaryContainerElement, this.leftScrollable, { horizontal: 'hidden', vertical: 'hidden' }); + this.leftScrollbar = new DomScrollableElement(leftBinaryContainerElement, { horizontal: 'hidden', vertical: 'hidden' }); parent.getHTMLElement().appendChild(this.leftScrollbar.getDomNode()); $(this.leftScrollbar.getDomNode()).addClass('binarydiff-left'); @@ -80,8 +76,7 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas this.rightBinaryContainer.tabindex(0); // enable focus support from the editor part (do not remove) // Right Custom Scrollbars - this.rightScrollable = new DomNodeScrollable(rightBinaryContainerElement); - this.rightScrollbar = new ScrollableElement(rightBinaryContainerElement, this.rightScrollable, { horizontal: 'hidden', vertical: 'hidden' }); + this.rightScrollbar = new DomScrollableElement(rightBinaryContainerElement, { horizontal: 'hidden', vertical: 'hidden' }); parent.getHTMLElement().appendChild(this.rightScrollbar.getDomNode()); $(this.rightScrollbar.getDomNode()).addClass('binarydiff-right'); } @@ -132,9 +127,9 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas // Pass to ResourceViewer let container = isOriginal ? this.leftBinaryContainer : this.rightBinaryContainer; - let scrollable = isOriginal ? this.leftScrollable : this.rightScrollable; + let scrollbar = isOriginal ? this.leftScrollbar : this.rightScrollbar; - ResourceViewer.show(name, resource, container, scrollable); + ResourceViewer.show(name, resource, container, scrollbar); } public clearInput(): void { @@ -169,13 +164,11 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas // Size left container this.leftBinaryContainer.size(this.leftContainerWidth, this.dimension.height); - this.leftScrollbar.onElementDimensions(); - this.leftScrollable.onContentsDimensions(); + this.leftScrollbar.scanDomNode(); // Size right container this.rightBinaryContainer.size(this.dimension.width - this.leftContainerWidth, this.dimension.height); - this.rightScrollbar.onElementDimensions(); - this.rightScrollable.onContentsDimensions(); + this.rightScrollbar.scanDomNode(); } private onSashDragStart(): void { @@ -228,9 +221,7 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas // Dispose Scrollbar this.leftScrollbar.dispose(); - this.leftScrollable.dispose(); this.rightScrollbar.dispose(); - this.rightScrollable.dispose(); // Destroy Container this.leftBinaryContainer.destroy();