| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import * as dom from '../../dom.js';
- import { createFastDomNode } from '../../fastDomNode.js';
- import { GlobalPointerMoveMonitor } from '../../globalPointerMoveMonitor.js';
- import { ScrollbarArrow } from './scrollbarArrow.js';
- import { ScrollbarVisibilityController } from './scrollbarVisibilityController.js';
- import { Widget } from '../widget.js';
- import * as platform from '../../../common/platform.js';
- /**
- * The orthogonal distance to the slider at which dragging "resets". This implements "snapping"
- */
- const POINTER_DRAG_RESET_DISTANCE = 140;
- export class AbstractScrollbar extends Widget {
- constructor(opts) {
- super();
- this._lazyRender = opts.lazyRender;
- this._host = opts.host;
- this._scrollable = opts.scrollable;
- this._scrollByPage = opts.scrollByPage;
- this._scrollbarState = opts.scrollbarState;
- this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName));
- this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
- this._pointerMoveMonitor = this._register(new GlobalPointerMoveMonitor());
- this._shouldRender = true;
- this.domNode = createFastDomNode(document.createElement('div'));
- this.domNode.setAttribute('role', 'presentation');
- this.domNode.setAttribute('aria-hidden', 'true');
- this._visibilityController.setDomNode(this.domNode);
- this.domNode.setPosition('absolute');
- this._register(dom.addDisposableListener(this.domNode.domNode, dom.EventType.POINTER_DOWN, (e) => this._domNodePointerDown(e)));
- }
- // ----------------- creation
- /**
- * Creates the dom node for an arrow & adds it to the container
- */
- _createArrow(opts) {
- const arrow = this._register(new ScrollbarArrow(opts));
- this.domNode.domNode.appendChild(arrow.bgDomNode);
- this.domNode.domNode.appendChild(arrow.domNode);
- }
- /**
- * Creates the slider dom node, adds it to the container & hooks up the events
- */
- _createSlider(top, left, width, height) {
- this.slider = createFastDomNode(document.createElement('div'));
- this.slider.setClassName('slider');
- this.slider.setPosition('absolute');
- this.slider.setTop(top);
- this.slider.setLeft(left);
- if (typeof width === 'number') {
- this.slider.setWidth(width);
- }
- if (typeof height === 'number') {
- this.slider.setHeight(height);
- }
- this.slider.setLayerHinting(true);
- this.slider.setContain('strict');
- this.domNode.domNode.appendChild(this.slider.domNode);
- this._register(dom.addDisposableListener(this.slider.domNode, dom.EventType.POINTER_DOWN, (e) => {
- if (e.button === 0) {
- e.preventDefault();
- this._sliderPointerDown(e);
- }
- }));
- this.onclick(this.slider.domNode, e => {
- if (e.leftButton) {
- e.stopPropagation();
- }
- });
- }
- // ----------------- Update state
- _onElementSize(visibleSize) {
- if (this._scrollbarState.setVisibleSize(visibleSize)) {
- this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
- this._shouldRender = true;
- if (!this._lazyRender) {
- this.render();
- }
- }
- return this._shouldRender;
- }
- _onElementScrollSize(elementScrollSize) {
- if (this._scrollbarState.setScrollSize(elementScrollSize)) {
- this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
- this._shouldRender = true;
- if (!this._lazyRender) {
- this.render();
- }
- }
- return this._shouldRender;
- }
- _onElementScrollPosition(elementScrollPosition) {
- if (this._scrollbarState.setScrollPosition(elementScrollPosition)) {
- this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
- this._shouldRender = true;
- if (!this._lazyRender) {
- this.render();
- }
- }
- return this._shouldRender;
- }
- // ----------------- rendering
- beginReveal() {
- this._visibilityController.setShouldBeVisible(true);
- }
- beginHide() {
- this._visibilityController.setShouldBeVisible(false);
- }
- render() {
- if (!this._shouldRender) {
- return;
- }
- this._shouldRender = false;
- this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
- this._updateSlider(this._scrollbarState.getSliderSize(), this._scrollbarState.getArrowSize() + this._scrollbarState.getSliderPosition());
- }
- // ----------------- DOM events
- _domNodePointerDown(e) {
- if (e.target !== this.domNode.domNode) {
- return;
- }
- this._onPointerDown(e);
- }
- delegatePointerDown(e) {
- const domTop = this.domNode.domNode.getClientRects()[0].top;
- const sliderStart = domTop + this._scrollbarState.getSliderPosition();
- const sliderStop = domTop + this._scrollbarState.getSliderPosition() + this._scrollbarState.getSliderSize();
- const pointerPos = this._sliderPointerPosition(e);
- if (sliderStart <= pointerPos && pointerPos <= sliderStop) {
- // Act as if it was a pointer down on the slider
- if (e.button === 0) {
- e.preventDefault();
- this._sliderPointerDown(e);
- }
- }
- else {
- // Act as if it was a pointer down on the scrollbar
- this._onPointerDown(e);
- }
- }
- _onPointerDown(e) {
- let offsetX;
- let offsetY;
- if (e.target === this.domNode.domNode && typeof e.offsetX === 'number' && typeof e.offsetY === 'number') {
- offsetX = e.offsetX;
- offsetY = e.offsetY;
- }
- else {
- const domNodePosition = dom.getDomNodePagePosition(this.domNode.domNode);
- offsetX = e.pageX - domNodePosition.left;
- offsetY = e.pageY - domNodePosition.top;
- }
- const offset = this._pointerDownRelativePosition(offsetX, offsetY);
- this._setDesiredScrollPositionNow(this._scrollByPage
- ? this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset)
- : this._scrollbarState.getDesiredScrollPositionFromOffset(offset));
- if (e.button === 0) {
- // left button
- e.preventDefault();
- this._sliderPointerDown(e);
- }
- }
- _sliderPointerDown(e) {
- if (!e.target || !(e.target instanceof Element)) {
- return;
- }
- const initialPointerPosition = this._sliderPointerPosition(e);
- const initialPointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(e);
- const initialScrollbarState = this._scrollbarState.clone();
- this.slider.toggleClassName('active', true);
- this._pointerMoveMonitor.startMonitoring(e.target, e.pointerId, e.buttons, (pointerMoveData) => {
- const pointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(pointerMoveData);
- const pointerOrthogonalDelta = Math.abs(pointerOrthogonalPosition - initialPointerOrthogonalPosition);
- if (platform.isWindows && pointerOrthogonalDelta > POINTER_DRAG_RESET_DISTANCE) {
- // The pointer has wondered away from the scrollbar => reset dragging
- this._setDesiredScrollPositionNow(initialScrollbarState.getScrollPosition());
- return;
- }
- const pointerPosition = this._sliderPointerPosition(pointerMoveData);
- const pointerDelta = pointerPosition - initialPointerPosition;
- this._setDesiredScrollPositionNow(initialScrollbarState.getDesiredScrollPositionFromDelta(pointerDelta));
- }, () => {
- this.slider.toggleClassName('active', false);
- this._host.onDragEnd();
- });
- this._host.onDragStart();
- }
- _setDesiredScrollPositionNow(_desiredScrollPosition) {
- const desiredScrollPosition = {};
- this.writeScrollPosition(desiredScrollPosition, _desiredScrollPosition);
- this._scrollable.setScrollPositionNow(desiredScrollPosition);
- }
- updateScrollbarSize(scrollbarSize) {
- this._updateScrollbarSize(scrollbarSize);
- this._scrollbarState.setScrollbarSize(scrollbarSize);
- this._shouldRender = true;
- if (!this._lazyRender) {
- this.render();
- }
- }
- isNeeded() {
- return this._scrollbarState.isNeeded();
- }
- }
|