"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts" (16 Sep 2020, 10101 Bytes) of package /linux/misc/vscode-1.49.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*---------------------------------------------------------------------------------------------
    2  *  Copyright (c) Microsoft Corporation. All rights reserved.
    3  *  Licensed under the MIT License. See License.txt in the project root for license information.
    4  *--------------------------------------------------------------------------------------------*/
    5 
    6 import * as dom from 'vs/base/browser/dom';
    7 import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
    8 import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor';
    9 import { IMouseEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent';
   10 import { ScrollbarArrow, ScrollbarArrowOptions } from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
   11 import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
   12 import { ScrollbarVisibilityController } from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
   13 import { Widget } from 'vs/base/browser/ui/widget';
   14 import * as platform from 'vs/base/common/platform';
   15 import { INewScrollPosition, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
   16 
   17 /**
   18  * The orthogonal distance to the slider at which dragging "resets". This implements "snapping"
   19  */
   20 const MOUSE_DRAG_RESET_DISTANCE = 140;
   21 
   22 export interface ISimplifiedMouseEvent {
   23     buttons: number;
   24     posx: number;
   25     posy: number;
   26 }
   27 
   28 export interface ScrollbarHost {
   29     onMouseWheel(mouseWheelEvent: StandardWheelEvent): void;
   30     onDragStart(): void;
   31     onDragEnd(): void;
   32 }
   33 
   34 export interface AbstractScrollbarOptions {
   35     lazyRender: boolean;
   36     host: ScrollbarHost;
   37     scrollbarState: ScrollbarState;
   38     visibility: ScrollbarVisibility;
   39     extraScrollbarClassName: string;
   40     scrollable: Scrollable;
   41 }
   42 
   43 export abstract class AbstractScrollbar extends Widget {
   44 
   45     protected _host: ScrollbarHost;
   46     protected _scrollable: Scrollable;
   47     private _lazyRender: boolean;
   48     protected _scrollbarState: ScrollbarState;
   49     private _visibilityController: ScrollbarVisibilityController;
   50     private _mouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
   51 
   52     public domNode: FastDomNode<HTMLElement>;
   53     public slider!: FastDomNode<HTMLElement>;
   54 
   55     protected _shouldRender: boolean;
   56 
   57     constructor(opts: AbstractScrollbarOptions) {
   58         super();
   59         this._lazyRender = opts.lazyRender;
   60         this._host = opts.host;
   61         this._scrollable = opts.scrollable;
   62         this._scrollbarState = opts.scrollbarState;
   63         this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName));
   64         this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
   65         this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>());
   66         this._shouldRender = true;
   67         this.domNode = createFastDomNode(document.createElement('div'));
   68         this.domNode.setAttribute('role', 'presentation');
   69         this.domNode.setAttribute('aria-hidden', 'true');
   70 
   71         this._visibilityController.setDomNode(this.domNode);
   72         this.domNode.setPosition('absolute');
   73 
   74         this.onmousedown(this.domNode.domNode, (e) => this._domNodeMouseDown(e));
   75     }
   76 
   77     // ----------------- creation
   78 
   79     /**
   80      * Creates the dom node for an arrow & adds it to the container
   81      */
   82     protected _createArrow(opts: ScrollbarArrowOptions): void {
   83         let arrow = this._register(new ScrollbarArrow(opts));
   84         this.domNode.domNode.appendChild(arrow.bgDomNode);
   85         this.domNode.domNode.appendChild(arrow.domNode);
   86     }
   87 
   88     /**
   89      * Creates the slider dom node, adds it to the container & hooks up the events
   90      */
   91     protected _createSlider(top: number, left: number, width: number | undefined, height: number | undefined): void {
   92         this.slider = createFastDomNode(document.createElement('div'));
   93         this.slider.setClassName('slider');
   94         this.slider.setPosition('absolute');
   95         this.slider.setTop(top);
   96         this.slider.setLeft(left);
   97         if (typeof width === 'number') {
   98             this.slider.setWidth(width);
   99         }
  100         if (typeof height === 'number') {
  101             this.slider.setHeight(height);
  102         }
  103         this.slider.setLayerHinting(true);
  104         this.slider.setContain('strict');
  105 
  106         this.domNode.domNode.appendChild(this.slider.domNode);
  107 
  108         this.onmousedown(this.slider.domNode, (e) => {
  109             if (e.leftButton) {
  110                 e.preventDefault();
  111                 this._sliderMouseDown(e, () => { /*nothing to do*/ });
  112             }
  113         });
  114 
  115         this.onclick(this.slider.domNode, e => {
  116             if (e.leftButton) {
  117                 e.stopPropagation();
  118             }
  119         });
  120     }
  121 
  122     // ----------------- Update state
  123 
  124     protected _onElementSize(visibleSize: number): boolean {
  125         if (this._scrollbarState.setVisibleSize(visibleSize)) {
  126             this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
  127             this._shouldRender = true;
  128             if (!this._lazyRender) {
  129                 this.render();
  130             }
  131         }
  132         return this._shouldRender;
  133     }
  134 
  135     protected _onElementScrollSize(elementScrollSize: number): boolean {
  136         if (this._scrollbarState.setScrollSize(elementScrollSize)) {
  137             this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
  138             this._shouldRender = true;
  139             if (!this._lazyRender) {
  140                 this.render();
  141             }
  142         }
  143         return this._shouldRender;
  144     }
  145 
  146     protected _onElementScrollPosition(elementScrollPosition: number): boolean {
  147         if (this._scrollbarState.setScrollPosition(elementScrollPosition)) {
  148             this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
  149             this._shouldRender = true;
  150             if (!this._lazyRender) {
  151                 this.render();
  152             }
  153         }
  154         return this._shouldRender;
  155     }
  156 
  157     // ----------------- rendering
  158 
  159     public beginReveal(): void {
  160         this._visibilityController.setShouldBeVisible(true);
  161     }
  162 
  163     public beginHide(): void {
  164         this._visibilityController.setShouldBeVisible(false);
  165     }
  166 
  167     public render(): void {
  168         if (!this._shouldRender) {
  169             return;
  170         }
  171         this._shouldRender = false;
  172 
  173         this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
  174         this._updateSlider(this._scrollbarState.getSliderSize(), this._scrollbarState.getArrowSize() + this._scrollbarState.getSliderPosition());
  175     }
  176     // ----------------- DOM events
  177 
  178     private _domNodeMouseDown(e: IMouseEvent): void {
  179         if (e.target !== this.domNode.domNode) {
  180             return;
  181         }
  182         this._onMouseDown(e);
  183     }
  184 
  185     public delegateMouseDown(e: IMouseEvent): void {
  186         let domTop = this.domNode.domNode.getClientRects()[0].top;
  187         let sliderStart = domTop + this._scrollbarState.getSliderPosition();
  188         let sliderStop = domTop + this._scrollbarState.getSliderPosition() + this._scrollbarState.getSliderSize();
  189         let mousePos = this._sliderMousePosition(e);
  190         if (sliderStart <= mousePos && mousePos <= sliderStop) {
  191             // Act as if it was a mouse down on the slider
  192             if (e.leftButton) {
  193                 e.preventDefault();
  194                 this._sliderMouseDown(e, () => { /*nothing to do*/ });
  195             }
  196         } else {
  197             // Act as if it was a mouse down on the scrollbar
  198             this._onMouseDown(e);
  199         }
  200     }
  201 
  202     private _onMouseDown(e: IMouseEvent): void {
  203         let offsetX: number;
  204         let offsetY: number;
  205         if (e.target === this.domNode.domNode && typeof e.browserEvent.offsetX === 'number' && typeof e.browserEvent.offsetY === 'number') {
  206             offsetX = e.browserEvent.offsetX;
  207             offsetY = e.browserEvent.offsetY;
  208         } else {
  209             const domNodePosition = dom.getDomNodePagePosition(this.domNode.domNode);
  210             offsetX = e.posx - domNodePosition.left;
  211             offsetY = e.posy - domNodePosition.top;
  212         }
  213         this._setDesiredScrollPositionNow(this._scrollbarState.getDesiredScrollPositionFromOffset(this._mouseDownRelativePosition(offsetX, offsetY)));
  214         if (e.leftButton) {
  215             e.preventDefault();
  216             this._sliderMouseDown(e, () => { /*nothing to do*/ });
  217         }
  218     }
  219 
  220     private _sliderMouseDown(e: IMouseEvent, onDragFinished: () => void): void {
  221         const initialMousePosition = this._sliderMousePosition(e);
  222         const initialMouseOrthogonalPosition = this._sliderOrthogonalMousePosition(e);
  223         const initialScrollbarState = this._scrollbarState.clone();
  224         this.slider.toggleClassName('active', true);
  225 
  226         this._mouseMoveMonitor.startMonitoring(
  227             e.target,
  228             e.buttons,
  229             standardMouseMoveMerger,
  230             (mouseMoveData: IStandardMouseMoveEventData) => {
  231                 const mouseOrthogonalPosition = this._sliderOrthogonalMousePosition(mouseMoveData);
  232                 const mouseOrthogonalDelta = Math.abs(mouseOrthogonalPosition - initialMouseOrthogonalPosition);
  233 
  234                 if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) {
  235                     // The mouse has wondered away from the scrollbar => reset dragging
  236                     this._setDesiredScrollPositionNow(initialScrollbarState.getScrollPosition());
  237                     return;
  238                 }
  239 
  240                 const mousePosition = this._sliderMousePosition(mouseMoveData);
  241                 const mouseDelta = mousePosition - initialMousePosition;
  242                 this._setDesiredScrollPositionNow(initialScrollbarState.getDesiredScrollPositionFromDelta(mouseDelta));
  243             },
  244             () => {
  245                 this.slider.toggleClassName('active', false);
  246                 this._host.onDragEnd();
  247                 onDragFinished();
  248             }
  249         );
  250 
  251         this._host.onDragStart();
  252     }
  253 
  254     private _setDesiredScrollPositionNow(_desiredScrollPosition: number): void {
  255 
  256         let desiredScrollPosition: INewScrollPosition = {};
  257         this.writeScrollPosition(desiredScrollPosition, _desiredScrollPosition);
  258 
  259         this._scrollable.setScrollPositionNow(desiredScrollPosition);
  260     }
  261 
  262     public updateScrollbarSize(scrollbarSize: number): void {
  263         this._updateScrollbarSize(scrollbarSize);
  264         this._scrollbarState.setScrollbarSize(scrollbarSize);
  265         this._shouldRender = true;
  266         if (!this._lazyRender) {
  267             this.render();
  268         }
  269     }
  270 
  271     // ----------------- Overwrite these
  272 
  273     protected abstract _renderDomNode(largeSize: number, smallSize: number): void;
  274     protected abstract _updateSlider(sliderSize: number, sliderPosition: number): void;
  275 
  276     protected abstract _mouseDownRelativePosition(offsetX: number, offsetY: number): number;
  277     protected abstract _sliderMousePosition(e: ISimplifiedMouseEvent): number;
  278     protected abstract _sliderOrthogonalMousePosition(e: ISimplifiedMouseEvent): number;
  279     protected abstract _updateScrollbarSize(size: number): void;
  280 
  281     public abstract writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void;
  282 }