UNPKG

monaco-editor

Version:
462 lines (461 loc) • 22.6 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); } return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); import './media/scrollbars.css'; import * as dom from '../../dom.js'; import { createFastDomNode } from '../../fastDomNode.js'; import { StandardMouseWheelEvent } from '../../mouseEvent.js'; import { HorizontalScrollbar } from './horizontalScrollbar.js'; import { VerticalScrollbar } from './verticalScrollbar.js'; import { Widget } from '../widget.js'; import { TimeoutTimer } from '../../../common/async.js'; import { Emitter } from '../../../common/event.js'; import { dispose } from '../../../common/lifecycle.js'; import * as platform from '../../../common/platform.js'; import { Scrollable } from '../../../common/scrollable.js'; var HIDE_TIMEOUT = 500; var SCROLL_WHEEL_SENSITIVITY = 50; var SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED = true; var MouseWheelClassifierItem = /** @class */ (function () { function MouseWheelClassifierItem(timestamp, deltaX, deltaY) { this.timestamp = timestamp; this.deltaX = deltaX; this.deltaY = deltaY; this.score = 0; } return MouseWheelClassifierItem; }()); var MouseWheelClassifier = /** @class */ (function () { function MouseWheelClassifier() { this._capacity = 5; this._memory = []; this._front = -1; this._rear = -1; } MouseWheelClassifier.prototype.isPhysicalMouseWheel = function () { if (this._front === -1 && this._rear === -1) { // no elements return false; } // 0.5 * last + 0.25 * before last + 0.125 * before before last + ... var remainingInfluence = 1; var score = 0; var iteration = 1; var index = this._rear; do { var influence = (index === this._front ? remainingInfluence : Math.pow(2, -iteration)); remainingInfluence -= influence; score += this._memory[index].score * influence; if (index === this._front) { break; } index = (this._capacity + index - 1) % this._capacity; iteration++; } while (true); return (score <= 0.5); }; MouseWheelClassifier.prototype.accept = function (timestamp, deltaX, deltaY) { var item = new MouseWheelClassifierItem(timestamp, deltaX, deltaY); item.score = this._computeScore(item); if (this._front === -1 && this._rear === -1) { this._memory[0] = item; this._front = 0; this._rear = 0; } else { this._rear = (this._rear + 1) % this._capacity; if (this._rear === this._front) { // Drop oldest this._front = (this._front + 1) % this._capacity; } this._memory[this._rear] = item; } }; /** * A score between 0 and 1 for `item`. * - a score towards 0 indicates that the source appears to be a physical mouse wheel * - a score towards 1 indicates that the source appears to be a touchpad or magic mouse, etc. */ MouseWheelClassifier.prototype._computeScore = function (item) { if (Math.abs(item.deltaX) > 0 && Math.abs(item.deltaY) > 0) { // both axes exercised => definitely not a physical mouse wheel return 1; } var score = 0.5; var prev = (this._front === -1 && this._rear === -1 ? null : this._memory[this._rear]); if (prev) { // const deltaT = item.timestamp - prev.timestamp; // if (deltaT < 1000 / 30) { // // sooner than X times per second => indicator that this is not a physical mouse wheel // score += 0.25; // } // if (item.deltaX === prev.deltaX && item.deltaY === prev.deltaY) { // // equal amplitude => indicator that this is a physical mouse wheel // score -= 0.25; // } } if (Math.abs(item.deltaX - Math.round(item.deltaX)) > 0 || Math.abs(item.deltaY - Math.round(item.deltaY)) > 0) { // non-integer deltas => indicator that this is not a physical mouse wheel score += 0.25; } return Math.min(Math.max(score, 0), 1); }; MouseWheelClassifier.INSTANCE = new MouseWheelClassifier(); return MouseWheelClassifier; }()); export { MouseWheelClassifier }; var AbstractScrollableElement = /** @class */ (function (_super) { __extends(AbstractScrollableElement, _super); function AbstractScrollableElement(element, options, scrollable) { var _this = _super.call(this) || this; _this._onScroll = _this._register(new Emitter()); _this.onScroll = _this._onScroll.event; element.style.overflow = 'hidden'; _this._options = resolveOptions(options); _this._scrollable = scrollable; _this._register(_this._scrollable.onScroll(function (e) { _this._onDidScroll(e); _this._onScroll.fire(e); })); var scrollbarHost = { onMouseWheel: function (mouseWheelEvent) { return _this._onMouseWheel(mouseWheelEvent); }, onDragStart: function () { return _this._onDragStart(); }, onDragEnd: function () { return _this._onDragEnd(); }, }; _this._verticalScrollbar = _this._register(new VerticalScrollbar(_this._scrollable, _this._options, scrollbarHost)); _this._horizontalScrollbar = _this._register(new HorizontalScrollbar(_this._scrollable, _this._options, scrollbarHost)); _this._domNode = document.createElement('div'); _this._domNode.className = 'monaco-scrollable-element ' + _this._options.className; _this._domNode.setAttribute('role', 'presentation'); _this._domNode.style.position = 'relative'; _this._domNode.style.overflow = 'hidden'; _this._domNode.appendChild(element); _this._domNode.appendChild(_this._horizontalScrollbar.domNode.domNode); _this._domNode.appendChild(_this._verticalScrollbar.domNode.domNode); if (_this._options.useShadows) { _this._leftShadowDomNode = createFastDomNode(document.createElement('div')); _this._leftShadowDomNode.setClassName('shadow'); _this._domNode.appendChild(_this._leftShadowDomNode.domNode); _this._topShadowDomNode = createFastDomNode(document.createElement('div')); _this._topShadowDomNode.setClassName('shadow'); _this._domNode.appendChild(_this._topShadowDomNode.domNode); _this._topLeftShadowDomNode = createFastDomNode(document.createElement('div')); _this._topLeftShadowDomNode.setClassName('shadow top-left-corner'); _this._domNode.appendChild(_this._topLeftShadowDomNode.domNode); } _this._listenOnDomNode = _this._options.listenOnDomNode || _this._domNode; _this._mouseWheelToDispose = []; _this._setListeningToMouseWheel(_this._options.handleMouseWheel); _this.onmouseover(_this._listenOnDomNode, function (e) { return _this._onMouseOver(e); }); _this.onnonbubblingmouseout(_this._listenOnDomNode, function (e) { return _this._onMouseOut(e); }); _this._hideTimeout = _this._register(new TimeoutTimer()); _this._isDragging = false; _this._mouseIsOver = false; _this._shouldRender = true; _this._revealOnScroll = true; return _this; } AbstractScrollableElement.prototype.dispose = function () { this._mouseWheelToDispose = dispose(this._mouseWheelToDispose); _super.prototype.dispose.call(this); }; /** * Get the generated 'scrollable' dom node */ AbstractScrollableElement.prototype.getDomNode = function () { return this._domNode; }; AbstractScrollableElement.prototype.getOverviewRulerLayoutInfo = function () { return { parent: this._domNode, insertBefore: this._verticalScrollbar.domNode.domNode, }; }; /** * Delegate a mouse down event to the vertical scrollbar. * This is to help with clicking somewhere else and having the scrollbar react. */ AbstractScrollableElement.prototype.delegateVerticalScrollbarMouseDown = function (browserEvent) { this._verticalScrollbar.delegateMouseDown(browserEvent); }; AbstractScrollableElement.prototype.getScrollDimensions = function () { return this._scrollable.getScrollDimensions(); }; AbstractScrollableElement.prototype.setScrollDimensions = function (dimensions) { this._scrollable.setScrollDimensions(dimensions); }; /** * Update the class name of the scrollable element. */ AbstractScrollableElement.prototype.updateClassName = function (newClassName) { this._options.className = newClassName; // Defaults are different on Macs if (platform.isMacintosh) { this._options.className += ' mac'; } this._domNode.className = 'monaco-scrollable-element ' + this._options.className; }; /** * Update configuration options for the scrollbar. * Really this is Editor.IEditorScrollbarOptions, but base shouldn't * depend on Editor. */ AbstractScrollableElement.prototype.updateOptions = function (newOptions) { var massagedOptions = resolveOptions(newOptions); this._options.handleMouseWheel = massagedOptions.handleMouseWheel; this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity; this._setListeningToMouseWheel(this._options.handleMouseWheel); if (!this._options.lazyRender) { this._render(); } }; // -------------------- mouse wheel scrolling -------------------- AbstractScrollableElement.prototype._setListeningToMouseWheel = function (shouldListen) { var _this = this; var isListening = (this._mouseWheelToDispose.length > 0); if (isListening === shouldListen) { // No change return; } // Stop listening (if necessary) this._mouseWheelToDispose = dispose(this._mouseWheelToDispose); // Start listening (if necessary) if (shouldListen) { var onMouseWheel = function (browserEvent) { var e = new StandardMouseWheelEvent(browserEvent); _this._onMouseWheel(e); }; this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'mousewheel', onMouseWheel)); this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'DOMMouseScroll', onMouseWheel)); } }; AbstractScrollableElement.prototype._onMouseWheel = function (e) { var _a; var classifier = MouseWheelClassifier.INSTANCE; if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) { classifier.accept(Date.now(), e.deltaX, e.deltaY); } // console.log(`${Date.now()}, ${e.deltaY}, ${e.deltaX}`); if (e.deltaY || e.deltaX) { var deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity; var deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity; if (this._options.flipAxes) { _a = [deltaX, deltaY], deltaY = _a[0], deltaX = _a[1]; } // Convert vertical scrolling to horizontal if shift is held, this // is handled at a higher level on Mac var shiftConvert = !platform.isMacintosh && e.browserEvent && e.browserEvent.shiftKey; if ((this._options.scrollYToX || shiftConvert) && !deltaX) { deltaX = deltaY; deltaY = 0; } var futureScrollPosition = this._scrollable.getFutureScrollPosition(); var desiredScrollPosition = {}; if (deltaY) { var desiredScrollTop = futureScrollPosition.scrollTop - SCROLL_WHEEL_SENSITIVITY * deltaY; this._verticalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollTop); } if (deltaX) { var desiredScrollLeft = futureScrollPosition.scrollLeft - SCROLL_WHEEL_SENSITIVITY * deltaX; this._horizontalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollLeft); } // Check that we are scrolling towards a location which is valid desiredScrollPosition = this._scrollable.validateScrollPosition(desiredScrollPosition); if (futureScrollPosition.scrollLeft !== desiredScrollPosition.scrollLeft || futureScrollPosition.scrollTop !== desiredScrollPosition.scrollTop) { var canPerformSmoothScroll = (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED && this._options.mouseWheelSmoothScroll && classifier.isPhysicalMouseWheel()); if (canPerformSmoothScroll) { this._scrollable.setScrollPositionSmooth(desiredScrollPosition); } else { this._scrollable.setScrollPositionNow(desiredScrollPosition); } this._shouldRender = true; } } if (this._options.alwaysConsumeMouseWheel || this._shouldRender) { e.preventDefault(); e.stopPropagation(); } }; AbstractScrollableElement.prototype._onDidScroll = function (e) { this._shouldRender = this._horizontalScrollbar.onDidScroll(e) || this._shouldRender; this._shouldRender = this._verticalScrollbar.onDidScroll(e) || this._shouldRender; if (this._options.useShadows) { this._shouldRender = true; } if (this._revealOnScroll) { this._reveal(); } if (!this._options.lazyRender) { this._render(); } }; /** * Render / mutate the DOM now. * Should be used together with the ctor option `lazyRender`. */ AbstractScrollableElement.prototype.renderNow = function () { if (!this._options.lazyRender) { throw new Error('Please use `lazyRender` together with `renderNow`!'); } this._render(); }; AbstractScrollableElement.prototype._render = function () { if (!this._shouldRender) { return; } this._shouldRender = false; this._horizontalScrollbar.render(); this._verticalScrollbar.render(); if (this._options.useShadows) { var scrollState = this._scrollable.getCurrentScrollPosition(); var enableTop = scrollState.scrollTop > 0; var enableLeft = scrollState.scrollLeft > 0; this._leftShadowDomNode.setClassName('shadow' + (enableLeft ? ' left' : '')); this._topShadowDomNode.setClassName('shadow' + (enableTop ? ' top' : '')); this._topLeftShadowDomNode.setClassName('shadow top-left-corner' + (enableTop ? ' top' : '') + (enableLeft ? ' left' : '')); } }; // -------------------- fade in / fade out -------------------- AbstractScrollableElement.prototype._onDragStart = function () { this._isDragging = true; this._reveal(); }; AbstractScrollableElement.prototype._onDragEnd = function () { this._isDragging = false; this._hide(); }; AbstractScrollableElement.prototype._onMouseOut = function (e) { this._mouseIsOver = false; this._hide(); }; AbstractScrollableElement.prototype._onMouseOver = function (e) { this._mouseIsOver = true; this._reveal(); }; AbstractScrollableElement.prototype._reveal = function () { this._verticalScrollbar.beginReveal(); this._horizontalScrollbar.beginReveal(); this._scheduleHide(); }; AbstractScrollableElement.prototype._hide = function () { if (!this._mouseIsOver && !this._isDragging) { this._verticalScrollbar.beginHide(); this._horizontalScrollbar.beginHide(); } }; AbstractScrollableElement.prototype._scheduleHide = function () { var _this = this; if (!this._mouseIsOver && !this._isDragging) { this._hideTimeout.cancelAndSet(function () { return _this._hide(); }, HIDE_TIMEOUT); } }; return AbstractScrollableElement; }(Widget)); export { AbstractScrollableElement }; var ScrollableElement = /** @class */ (function (_super) { __extends(ScrollableElement, _super); function ScrollableElement(element, options) { var _this = this; options = options || {}; options.mouseWheelSmoothScroll = false; var scrollable = new Scrollable(0, function (callback) { return dom.scheduleAtNextAnimationFrame(callback); }); _this = _super.call(this, element, options, scrollable) || this; _this._register(scrollable); return _this; } ScrollableElement.prototype.setScrollPosition = function (update) { this._scrollable.setScrollPositionNow(update); }; ScrollableElement.prototype.getScrollPosition = function () { return this._scrollable.getCurrentScrollPosition(); }; return ScrollableElement; }(AbstractScrollableElement)); export { ScrollableElement }; var SmoothScrollableElement = /** @class */ (function (_super) { __extends(SmoothScrollableElement, _super); function SmoothScrollableElement(element, options, scrollable) { return _super.call(this, element, options, scrollable) || this; } return SmoothScrollableElement; }(AbstractScrollableElement)); export { SmoothScrollableElement }; var DomScrollableElement = /** @class */ (function (_super) { __extends(DomScrollableElement, _super); function DomScrollableElement(element, options) { var _this = _super.call(this, element, options) || this; _this._element = element; _this.onScroll(function (e) { if (e.scrollTopChanged) { _this._element.scrollTop = e.scrollTop; } if (e.scrollLeftChanged) { _this._element.scrollLeft = e.scrollLeft; } }); _this.scanDomNode(); return _this; } DomScrollableElement.prototype.scanDomNode = function () { // widh, scrollLeft, scrollWidth, height, scrollTop, scrollHeight this.setScrollDimensions({ width: this._element.clientWidth, scrollWidth: this._element.scrollWidth, height: this._element.clientHeight, scrollHeight: this._element.scrollHeight }); this.setScrollPosition({ scrollLeft: this._element.scrollLeft, scrollTop: this._element.scrollTop, }); }; return DomScrollableElement; }(ScrollableElement)); export { DomScrollableElement }; function resolveOptions(opts) { var result = { lazyRender: (typeof opts.lazyRender !== 'undefined' ? opts.lazyRender : false), className: (typeof opts.className !== 'undefined' ? opts.className : ''), useShadows: (typeof opts.useShadows !== 'undefined' ? opts.useShadows : true), handleMouseWheel: (typeof opts.handleMouseWheel !== 'undefined' ? opts.handleMouseWheel : true), flipAxes: (typeof opts.flipAxes !== 'undefined' ? opts.flipAxes : false), alwaysConsumeMouseWheel: (typeof opts.alwaysConsumeMouseWheel !== 'undefined' ? opts.alwaysConsumeMouseWheel : false), scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false), mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1), mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true), arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11), listenOnDomNode: (typeof opts.listenOnDomNode !== 'undefined' ? opts.listenOnDomNode : null), horizontal: (typeof opts.horizontal !== 'undefined' ? opts.horizontal : 1 /* Auto */), horizontalScrollbarSize: (typeof opts.horizontalScrollbarSize !== 'undefined' ? opts.horizontalScrollbarSize : 10), horizontalSliderSize: (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : 0), horizontalHasArrows: (typeof opts.horizontalHasArrows !== 'undefined' ? opts.horizontalHasArrows : false), vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : 1 /* Auto */), verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10), verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false), verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0) }; result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize); result.verticalSliderSize = (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : result.verticalScrollbarSize); // Defaults are different on Macs if (platform.isMacintosh) { result.className += ' mac'; } return result; }