Path: blob/main/src/vs/base/browser/ui/scrollbar/scrollableElement.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { getZoomFactor, isChrome } from '../../browser.js';6import * as dom from '../../dom.js';7import { FastDomNode, createFastDomNode } from '../../fastDomNode.js';8import { IMouseEvent, IMouseWheelEvent, StandardWheelEvent } from '../../mouseEvent.js';9import { ScrollbarHost } from './abstractScrollbar.js';10import { HorizontalScrollbar } from './horizontalScrollbar.js';11import { ScrollableElementChangeOptions, ScrollableElementCreationOptions, ScrollableElementResolvedOptions } from './scrollableElementOptions.js';12import { VerticalScrollbar } from './verticalScrollbar.js';13import { Widget } from '../widget.js';14import { TimeoutTimer } from '../../../common/async.js';15import { Emitter, Event } from '../../../common/event.js';16import { IDisposable, dispose } from '../../../common/lifecycle.js';17import * as platform from '../../../common/platform.js';18import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from '../../../common/scrollable.js';19import './media/scrollbars.css';2021const HIDE_TIMEOUT = 500;22const SCROLL_WHEEL_SENSITIVITY = 50;23const SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED = true;2425export interface IOverviewRulerLayoutInfo {26parent: HTMLElement;27insertBefore: HTMLElement;28}2930class MouseWheelClassifierItem {31public timestamp: number;32public deltaX: number;33public deltaY: number;34public score: number;3536constructor(timestamp: number, deltaX: number, deltaY: number) {37this.timestamp = timestamp;38this.deltaX = deltaX;39this.deltaY = deltaY;40this.score = 0;41}42}4344export class MouseWheelClassifier {4546public static readonly INSTANCE = new MouseWheelClassifier();4748private readonly _capacity: number;49private _memory: MouseWheelClassifierItem[];50private _front: number;51private _rear: number;5253constructor() {54this._capacity = 5;55this._memory = [];56this._front = -1;57this._rear = -1;58}5960public isPhysicalMouseWheel(): boolean {61if (this._front === -1 && this._rear === -1) {62// no elements63return false;64}6566// 0.5 * last + 0.25 * 2nd last + 0.125 * 3rd last + ...67let remainingInfluence = 1;68let score = 0;69let iteration = 1;7071let index = this._rear;72do {73const influence = (index === this._front ? remainingInfluence : Math.pow(2, -iteration));74remainingInfluence -= influence;75score += this._memory[index].score * influence;7677if (index === this._front) {78break;79}8081index = (this._capacity + index - 1) % this._capacity;82iteration++;83} while (true);8485return (score <= 0.5);86}8788public acceptStandardWheelEvent(e: StandardWheelEvent): void {89if (isChrome) {90const targetWindow = dom.getWindow(e.browserEvent);91const pageZoomFactor = getZoomFactor(targetWindow);92// On Chrome, the incoming delta events are multiplied with the OS zoom factor.93// The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account.94this.accept(Date.now(), e.deltaX * pageZoomFactor, e.deltaY * pageZoomFactor);95} else {96this.accept(Date.now(), e.deltaX, e.deltaY);97}98}99100public accept(timestamp: number, deltaX: number, deltaY: number): void {101let previousItem = null;102const item = new MouseWheelClassifierItem(timestamp, deltaX, deltaY);103104if (this._front === -1 && this._rear === -1) {105this._memory[0] = item;106this._front = 0;107this._rear = 0;108} else {109previousItem = this._memory[this._rear];110111this._rear = (this._rear + 1) % this._capacity;112if (this._rear === this._front) {113// Drop oldest114this._front = (this._front + 1) % this._capacity;115}116this._memory[this._rear] = item;117}118119item.score = this._computeScore(item, previousItem);120}121122/**123* A score between 0 and 1 for `item`.124* - a score towards 0 indicates that the source appears to be a physical mouse wheel125* - a score towards 1 indicates that the source appears to be a touchpad or magic mouse, etc.126*/127private _computeScore(item: MouseWheelClassifierItem, previousItem: MouseWheelClassifierItem | null): number {128129if (Math.abs(item.deltaX) > 0 && Math.abs(item.deltaY) > 0) {130// both axes exercised => definitely not a physical mouse wheel131return 1;132}133134let score: number = 0.5;135136if (!this._isAlmostInt(item.deltaX) || !this._isAlmostInt(item.deltaY)) {137// non-integer deltas => indicator that this is not a physical mouse wheel138score += 0.25;139}140141// Non-accelerating scroll => indicator that this is a physical mouse wheel142// These can be identified by seeing whether they are the module of one another.143if (previousItem) {144const absDeltaX = Math.abs(item.deltaX);145const absDeltaY = Math.abs(item.deltaY);146147const absPreviousDeltaX = Math.abs(previousItem.deltaX);148const absPreviousDeltaY = Math.abs(previousItem.deltaY);149150// Min 1 to avoid division by zero, module 1 will still be 0.151const minDeltaX = Math.max(Math.min(absDeltaX, absPreviousDeltaX), 1);152const minDeltaY = Math.max(Math.min(absDeltaY, absPreviousDeltaY), 1);153154const maxDeltaX = Math.max(absDeltaX, absPreviousDeltaX);155const maxDeltaY = Math.max(absDeltaY, absPreviousDeltaY);156157const isSameModulo = (maxDeltaX % minDeltaX === 0 && maxDeltaY % minDeltaY === 0);158if (isSameModulo) {159score -= 0.5;160}161}162163return Math.min(Math.max(score, 0), 1);164}165166private _isAlmostInt(value: number): boolean {167const epsilon = Number.EPSILON * 100; // Use a small tolerance factor for floating-point errors168const delta = Math.abs(Math.round(value) - value);169return (delta < 0.01 + epsilon);170}171}172173export abstract class AbstractScrollableElement extends Widget {174175private readonly _options: ScrollableElementResolvedOptions;176protected readonly _scrollable: Scrollable;177private readonly _verticalScrollbar: VerticalScrollbar;178private readonly _horizontalScrollbar: HorizontalScrollbar;179private readonly _domNode: HTMLElement;180181private readonly _leftShadowDomNode: FastDomNode<HTMLElement> | null;182private readonly _topShadowDomNode: FastDomNode<HTMLElement> | null;183private readonly _topLeftShadowDomNode: FastDomNode<HTMLElement> | null;184185private readonly _listenOnDomNode: HTMLElement;186187private _mouseWheelToDispose: IDisposable[];188189private _isDragging: boolean;190private _mouseIsOver: boolean;191192private readonly _hideTimeout: TimeoutTimer;193private _shouldRender: boolean;194195private _revealOnScroll: boolean;196197private _inertialTimeout: TimeoutTimer | null = null;198private _inertialSpeed: { X: number; Y: number } = { X: 0, Y: 0 };199200private readonly _onScroll = this._register(new Emitter<ScrollEvent>());201public get onScroll(): Event<ScrollEvent> { return this._onScroll.event; }202203private readonly _onWillScroll = this._register(new Emitter<ScrollEvent>());204public get onWillScroll(): Event<ScrollEvent> { return this._onWillScroll.event; }205206public get options(): Readonly<ScrollableElementResolvedOptions> {207return this._options;208}209210protected constructor(element: HTMLElement, options: ScrollableElementCreationOptions, scrollable: Scrollable) {211super();212element.style.overflow = 'hidden';213this._options = resolveOptions(options);214this._scrollable = scrollable;215216this._register(this._scrollable.onScroll((e) => {217this._onWillScroll.fire(e);218this._onDidScroll(e);219this._onScroll.fire(e);220}));221222const scrollbarHost: ScrollbarHost = {223onMouseWheel: (mouseWheelEvent: StandardWheelEvent) => this._onMouseWheel(mouseWheelEvent),224onDragStart: () => this._onDragStart(),225onDragEnd: () => this._onDragEnd(),226};227this._verticalScrollbar = this._register(new VerticalScrollbar(this._scrollable, this._options, scrollbarHost));228this._horizontalScrollbar = this._register(new HorizontalScrollbar(this._scrollable, this._options, scrollbarHost));229230this._domNode = document.createElement('div');231this._domNode.className = 'monaco-scrollable-element ' + this._options.className;232this._domNode.setAttribute('role', 'presentation');233this._domNode.style.position = 'relative';234this._domNode.style.overflow = 'hidden';235this._domNode.appendChild(element);236this._domNode.appendChild(this._horizontalScrollbar.domNode.domNode);237this._domNode.appendChild(this._verticalScrollbar.domNode.domNode);238239if (this._options.useShadows) {240this._leftShadowDomNode = createFastDomNode(document.createElement('div'));241this._leftShadowDomNode.setClassName('shadow');242this._domNode.appendChild(this._leftShadowDomNode.domNode);243244this._topShadowDomNode = createFastDomNode(document.createElement('div'));245this._topShadowDomNode.setClassName('shadow');246this._domNode.appendChild(this._topShadowDomNode.domNode);247248this._topLeftShadowDomNode = createFastDomNode(document.createElement('div'));249this._topLeftShadowDomNode.setClassName('shadow');250this._domNode.appendChild(this._topLeftShadowDomNode.domNode);251} else {252this._leftShadowDomNode = null;253this._topShadowDomNode = null;254this._topLeftShadowDomNode = null;255}256257this._listenOnDomNode = this._options.listenOnDomNode || this._domNode;258259this._mouseWheelToDispose = [];260this._setListeningToMouseWheel(this._options.handleMouseWheel);261262this.onmouseover(this._listenOnDomNode, (e) => this._onMouseOver(e));263this.onmouseleave(this._listenOnDomNode, (e) => this._onMouseLeave(e));264265this._hideTimeout = this._register(new TimeoutTimer());266this._isDragging = false;267this._mouseIsOver = false;268269this._shouldRender = true;270271this._revealOnScroll = true;272}273274public override dispose(): void {275this._mouseWheelToDispose = dispose(this._mouseWheelToDispose);276if (this._inertialTimeout) {277this._inertialTimeout.dispose();278this._inertialTimeout = null;279}280super.dispose();281}282283/**284* Get the generated 'scrollable' dom node285*/286public getDomNode(): HTMLElement {287return this._domNode;288}289290public getOverviewRulerLayoutInfo(): IOverviewRulerLayoutInfo {291return {292parent: this._domNode,293insertBefore: this._verticalScrollbar.domNode.domNode,294};295}296297/**298* Delegate a pointer down event to the vertical scrollbar.299* This is to help with clicking somewhere else and having the scrollbar react.300*/301public delegateVerticalScrollbarPointerDown(browserEvent: PointerEvent): void {302this._verticalScrollbar.delegatePointerDown(browserEvent);303}304305public getScrollDimensions(): IScrollDimensions {306return this._scrollable.getScrollDimensions();307}308309public setScrollDimensions(dimensions: INewScrollDimensions): void {310this._scrollable.setScrollDimensions(dimensions, false);311}312313/**314* Update the class name of the scrollable element.315*/316public updateClassName(newClassName: string): void {317this._options.className = newClassName;318// Defaults are different on Macs319if (platform.isMacintosh) {320this._options.className += ' mac';321}322this._domNode.className = 'monaco-scrollable-element ' + this._options.className;323}324325/**326* Update configuration options for the scrollbar.327*/328public updateOptions(newOptions: ScrollableElementChangeOptions): void {329if (typeof newOptions.handleMouseWheel !== 'undefined') {330this._options.handleMouseWheel = newOptions.handleMouseWheel;331this._setListeningToMouseWheel(this._options.handleMouseWheel);332}333if (typeof newOptions.mouseWheelScrollSensitivity !== 'undefined') {334this._options.mouseWheelScrollSensitivity = newOptions.mouseWheelScrollSensitivity;335}336if (typeof newOptions.fastScrollSensitivity !== 'undefined') {337this._options.fastScrollSensitivity = newOptions.fastScrollSensitivity;338}339if (typeof newOptions.scrollPredominantAxis !== 'undefined') {340this._options.scrollPredominantAxis = newOptions.scrollPredominantAxis;341}342if (typeof newOptions.horizontal !== 'undefined') {343this._options.horizontal = newOptions.horizontal;344}345if (typeof newOptions.vertical !== 'undefined') {346this._options.vertical = newOptions.vertical;347}348if (typeof newOptions.horizontalScrollbarSize !== 'undefined') {349this._options.horizontalScrollbarSize = newOptions.horizontalScrollbarSize;350}351if (typeof newOptions.verticalScrollbarSize !== 'undefined') {352this._options.verticalScrollbarSize = newOptions.verticalScrollbarSize;353}354if (typeof newOptions.scrollByPage !== 'undefined') {355this._options.scrollByPage = newOptions.scrollByPage;356}357this._horizontalScrollbar.updateOptions(this._options);358this._verticalScrollbar.updateOptions(this._options);359360if (!this._options.lazyRender) {361this._render();362}363}364365public setRevealOnScroll(value: boolean) {366this._revealOnScroll = value;367}368369public delegateScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {370this._onMouseWheel(new StandardWheelEvent(browserEvent));371}372373private async _periodicSync(): Promise<void> {374let scheduleAgain = false;375376if (this._inertialSpeed.X !== 0 || this._inertialSpeed.Y !== 0) {377this._scrollable.setScrollPositionNow({378scrollTop: this._scrollable.getCurrentScrollPosition().scrollTop - this._inertialSpeed.Y * 100,379scrollLeft: this._scrollable.getCurrentScrollPosition().scrollLeft - this._inertialSpeed.X * 100380});381this._inertialSpeed.X *= 0.9;382this._inertialSpeed.Y *= 0.9;383if (Math.abs(this._inertialSpeed.X) < 0.01) {384this._inertialSpeed.X = 0;385}386if (Math.abs(this._inertialSpeed.Y) < 0.01) {387this._inertialSpeed.Y = 0;388}389390scheduleAgain = (this._inertialSpeed.X !== 0 || this._inertialSpeed.Y !== 0);391}392393if (scheduleAgain) {394if (!this._inertialTimeout) {395this._inertialTimeout = new TimeoutTimer();396}397this._inertialTimeout.cancelAndSet(() => this._periodicSync(), 1000 / 60);398} else {399this._inertialTimeout?.dispose();400this._inertialTimeout = null;401}402}403404// -------------------- mouse wheel scrolling --------------------405406private _setListeningToMouseWheel(shouldListen: boolean): void {407const isListening = (this._mouseWheelToDispose.length > 0);408409if (isListening === shouldListen) {410// No change411return;412}413414// Stop listening (if necessary)415this._mouseWheelToDispose = dispose(this._mouseWheelToDispose);416417// Start listening (if necessary)418if (shouldListen) {419const onMouseWheel = (browserEvent: IMouseWheelEvent) => {420this._onMouseWheel(new StandardWheelEvent(browserEvent));421};422423this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false }));424}425}426427private _onMouseWheel(e: StandardWheelEvent): void {428if (e.browserEvent?.defaultPrevented) {429return;430}431432const classifier = MouseWheelClassifier.INSTANCE;433if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) {434classifier.acceptStandardWheelEvent(e);435}436437// useful for creating unit tests:438// console.log(`${Date.now()}, ${e.deltaY}, ${e.deltaX}`);439440let didScroll = false;441442if (e.deltaY || e.deltaX) {443let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity;444let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity;445446if (this._options.scrollPredominantAxis) {447if (this._options.scrollYToX && deltaX + deltaY === 0) {448// when configured to map Y to X and we both see449// no dominant axis and X and Y are competing with450// identical values into opposite directions, we451// ignore the delta as we cannot make a decision then452deltaX = deltaY = 0;453} else if (Math.abs(deltaY) >= Math.abs(deltaX)) {454deltaX = 0;455} else {456deltaY = 0;457}458}459460if (this._options.flipAxes) {461[deltaY, deltaX] = [deltaX, deltaY];462}463464// Convert vertical scrolling to horizontal if shift is held, this465// is handled at a higher level on Mac466const shiftConvert = !platform.isMacintosh && e.browserEvent && e.browserEvent.shiftKey;467if ((this._options.scrollYToX || shiftConvert) && !deltaX) {468deltaX = deltaY;469deltaY = 0;470}471472if (e.browserEvent && e.browserEvent.altKey) {473// fastScrolling474deltaX = deltaX * this._options.fastScrollSensitivity;475deltaY = deltaY * this._options.fastScrollSensitivity;476}477478const futureScrollPosition = this._scrollable.getFutureScrollPosition();479480let desiredScrollPosition: INewScrollPosition = {};481if (deltaY) {482const deltaScrollTop = SCROLL_WHEEL_SENSITIVITY * deltaY;483// Here we convert values such as -0.3 to -1 or 0.3 to 1, otherwise low speed scrolling will never scroll484const desiredScrollTop = futureScrollPosition.scrollTop - (deltaScrollTop < 0 ? Math.floor(deltaScrollTop) : Math.ceil(deltaScrollTop));485this._verticalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollTop);486}487if (deltaX) {488const deltaScrollLeft = SCROLL_WHEEL_SENSITIVITY * deltaX;489// Here we convert values such as -0.3 to -1 or 0.3 to 1, otherwise low speed scrolling will never scroll490const desiredScrollLeft = futureScrollPosition.scrollLeft - (deltaScrollLeft < 0 ? Math.floor(deltaScrollLeft) : Math.ceil(deltaScrollLeft));491this._horizontalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollLeft);492}493494// Check that we are scrolling towards a location which is valid495desiredScrollPosition = this._scrollable.validateScrollPosition(desiredScrollPosition);496497if (this._options.inertialScroll && (deltaX || deltaY)) {498let startPeriodic = false;499// Only start periodic if it's not running500if (this._inertialSpeed.X === 0 && this._inertialSpeed.Y === 0) {501startPeriodic = true;502}503this._inertialSpeed.Y = (deltaY < 0 ? -1 : 1) * (Math.abs(deltaY) ** 1.02);504this._inertialSpeed.X = (deltaX < 0 ? -1 : 1) * (Math.abs(deltaX) ** 1.02);505if (startPeriodic) {506this._periodicSync();507}508}509510if (futureScrollPosition.scrollLeft !== desiredScrollPosition.scrollLeft || futureScrollPosition.scrollTop !== desiredScrollPosition.scrollTop) {511512const canPerformSmoothScroll = (513SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED514&& this._options.mouseWheelSmoothScroll515&& classifier.isPhysicalMouseWheel()516);517518if (canPerformSmoothScroll) {519this._scrollable.setScrollPositionSmooth(desiredScrollPosition);520} else {521this._scrollable.setScrollPositionNow(desiredScrollPosition);522}523524didScroll = true;525}526}527528let consumeMouseWheel = didScroll;529if (!consumeMouseWheel && this._options.alwaysConsumeMouseWheel) {530consumeMouseWheel = true;531}532if (!consumeMouseWheel && this._options.consumeMouseWheelIfScrollbarIsNeeded && (this._verticalScrollbar.isNeeded() || this._horizontalScrollbar.isNeeded())) {533consumeMouseWheel = true;534}535536if (consumeMouseWheel) {537e.preventDefault();538e.stopPropagation();539}540}541542private _onDidScroll(e: ScrollEvent): void {543this._shouldRender = this._horizontalScrollbar.onDidScroll(e) || this._shouldRender;544this._shouldRender = this._verticalScrollbar.onDidScroll(e) || this._shouldRender;545546if (this._options.useShadows) {547this._shouldRender = true;548}549550if (this._revealOnScroll) {551this._reveal();552}553554if (!this._options.lazyRender) {555this._render();556}557}558559/**560* Render / mutate the DOM now.561* Should be used together with the ctor option `lazyRender`.562*/563public renderNow(): void {564if (!this._options.lazyRender) {565throw new Error('Please use `lazyRender` together with `renderNow`!');566}567568this._render();569}570571private _render(): void {572if (!this._shouldRender) {573return;574}575576this._shouldRender = false;577578this._horizontalScrollbar.render();579this._verticalScrollbar.render();580581if (this._options.useShadows) {582const scrollState = this._scrollable.getCurrentScrollPosition();583const enableTop = scrollState.scrollTop > 0;584const enableLeft = scrollState.scrollLeft > 0;585586const leftClassName = (enableLeft ? ' left' : '');587const topClassName = (enableTop ? ' top' : '');588const topLeftClassName = (enableLeft || enableTop ? ' top-left-corner' : '');589this._leftShadowDomNode!.setClassName(`shadow${leftClassName}`);590this._topShadowDomNode!.setClassName(`shadow${topClassName}`);591this._topLeftShadowDomNode!.setClassName(`shadow${topLeftClassName}${topClassName}${leftClassName}`);592}593}594595// -------------------- fade in / fade out --------------------596597private _onDragStart(): void {598this._isDragging = true;599this._reveal();600}601602private _onDragEnd(): void {603this._isDragging = false;604this._hide();605}606607private _onMouseLeave(e: IMouseEvent): void {608this._mouseIsOver = false;609this._hide();610}611612private _onMouseOver(e: IMouseEvent): void {613this._mouseIsOver = true;614this._reveal();615}616617private _reveal(): void {618this._verticalScrollbar.beginReveal();619this._horizontalScrollbar.beginReveal();620this._scheduleHide();621}622623private _hide(): void {624if (!this._mouseIsOver && !this._isDragging) {625this._verticalScrollbar.beginHide();626this._horizontalScrollbar.beginHide();627}628}629630private _scheduleHide(): void {631if (!this._mouseIsOver && !this._isDragging) {632this._hideTimeout.cancelAndSet(() => this._hide(), HIDE_TIMEOUT);633}634}635}636637export class ScrollableElement extends AbstractScrollableElement {638639constructor(element: HTMLElement, options: ScrollableElementCreationOptions) {640options = options || {};641options.mouseWheelSmoothScroll = false;642const scrollable = new Scrollable({643forceIntegerValues: true,644smoothScrollDuration: 0,645scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(dom.getWindow(element), callback)646});647super(element, options, scrollable);648this._register(scrollable);649}650651public setScrollPosition(update: INewScrollPosition): void {652this._scrollable.setScrollPositionNow(update);653}654655public getScrollPosition(): IScrollPosition {656return this._scrollable.getCurrentScrollPosition();657}658}659660export class SmoothScrollableElement extends AbstractScrollableElement {661662constructor(element: HTMLElement, options: ScrollableElementCreationOptions, scrollable: Scrollable) {663super(element, options, scrollable);664}665666public setScrollPosition(update: INewScrollPosition & { reuseAnimation?: boolean }): void {667if (update.reuseAnimation) {668this._scrollable.setScrollPositionSmooth(update, update.reuseAnimation);669} else {670this._scrollable.setScrollPositionNow(update);671}672}673674public getScrollPosition(): IScrollPosition {675return this._scrollable.getCurrentScrollPosition();676}677678}679680export class DomScrollableElement extends AbstractScrollableElement {681682private _element: HTMLElement;683684constructor(element: HTMLElement, options: ScrollableElementCreationOptions) {685options = options || {};686options.mouseWheelSmoothScroll = false;687const scrollable = new Scrollable({688forceIntegerValues: false, // See https://github.com/microsoft/vscode/issues/139877689smoothScrollDuration: 0,690scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(dom.getWindow(element), callback)691});692super(element, options, scrollable);693this._register(scrollable);694this._element = element;695this._register(this.onScroll((e) => {696if (e.scrollTopChanged) {697this._element.scrollTop = e.scrollTop;698}699if (e.scrollLeftChanged) {700this._element.scrollLeft = e.scrollLeft;701}702}));703this.scanDomNode();704}705706public setScrollPosition(update: INewScrollPosition): void {707this._scrollable.setScrollPositionNow(update);708}709710public getScrollPosition(): IScrollPosition {711return this._scrollable.getCurrentScrollPosition();712}713714public scanDomNode(): void {715// width, scrollLeft, scrollWidth, height, scrollTop, scrollHeight716this.setScrollDimensions({717width: this._element.clientWidth,718scrollWidth: this._element.scrollWidth,719height: this._element.clientHeight,720scrollHeight: this._element.scrollHeight721});722this.setScrollPosition({723scrollLeft: this._element.scrollLeft,724scrollTop: this._element.scrollTop,725});726}727}728729function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableElementResolvedOptions {730const result: ScrollableElementResolvedOptions = {731lazyRender: (typeof opts.lazyRender !== 'undefined' ? opts.lazyRender : false),732className: (typeof opts.className !== 'undefined' ? opts.className : ''),733useShadows: (typeof opts.useShadows !== 'undefined' ? opts.useShadows : true),734handleMouseWheel: (typeof opts.handleMouseWheel !== 'undefined' ? opts.handleMouseWheel : true),735flipAxes: (typeof opts.flipAxes !== 'undefined' ? opts.flipAxes : false),736consumeMouseWheelIfScrollbarIsNeeded: (typeof opts.consumeMouseWheelIfScrollbarIsNeeded !== 'undefined' ? opts.consumeMouseWheelIfScrollbarIsNeeded : false),737alwaysConsumeMouseWheel: (typeof opts.alwaysConsumeMouseWheel !== 'undefined' ? opts.alwaysConsumeMouseWheel : false),738scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false),739mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1),740fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5),741scrollPredominantAxis: (typeof opts.scrollPredominantAxis !== 'undefined' ? opts.scrollPredominantAxis : true),742mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true),743inertialScroll: (typeof opts.inertialScroll !== 'undefined' ? opts.inertialScroll : false),744arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11),745746listenOnDomNode: (typeof opts.listenOnDomNode !== 'undefined' ? opts.listenOnDomNode : null),747748horizontal: (typeof opts.horizontal !== 'undefined' ? opts.horizontal : ScrollbarVisibility.Auto),749horizontalScrollbarSize: (typeof opts.horizontalScrollbarSize !== 'undefined' ? opts.horizontalScrollbarSize : 10),750horizontalSliderSize: (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : 0),751horizontalHasArrows: (typeof opts.horizontalHasArrows !== 'undefined' ? opts.horizontalHasArrows : false),752753vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto),754verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10),755verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false),756verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0),757758scrollByPage: (typeof opts.scrollByPage !== 'undefined' ? opts.scrollByPage : false)759};760761result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize);762result.verticalSliderSize = (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : result.verticalScrollbarSize);763764// Defaults are different on Macs765if (platform.isMacintosh) {766result.className += ' mac';767}768769return result;770}771772773