editorScrollbar.ts 7.6 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

J
Johannes Rieken 已提交
7
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
8
import * as dom from 'vs/base/browser/dom';
J
Johannes Rieken 已提交
9 10
import { ScrollableElementCreationOptions, ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
import { IOverviewRulerLayoutInfo, ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
A
Alex Dima 已提交
11
import { INewScrollPosition } from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
12
import { ClassNames } from 'vs/editor/browser/editorBrowser';
A
Alex Dima 已提交
13
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
14
import { Scrollable } from 'vs/base/common/scrollable';
A
Alex Dima 已提交
15 16 17
import { ViewContext } from 'vs/editor/common/view/viewContext';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
A
Alex Dima 已提交
18
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
E
Erich Gamma 已提交
19

A
Alex Dima 已提交
20
export class EditorScrollbar extends ViewPart {
E
Erich Gamma 已提交
21

22
	private scrollable: Scrollable;
J
Johannes Rieken 已提交
23
	private toDispose: IDisposable[];
A
Alex Dima 已提交
24
	private scrollbar: ScrollableElement;
A
Alex Dima 已提交
25 26 27 28 29 30 31 32 33
	private scrollbarDomNode: FastDomNode<HTMLElement>;

	constructor(
		context: ViewContext,
		scrollable: Scrollable,
		linesContent: FastDomNode<HTMLElement>,
		viewDomNode: FastDomNode<HTMLElement>,
		overflowGuardDomNode: FastDomNode<HTMLElement>
	) {
A
Alex Dima 已提交
34 35
		super(context);

E
Erich Gamma 已提交
36
		this.toDispose = [];
37
		this.scrollable = scrollable;
E
Erich Gamma 已提交
38

A
Alex Dima 已提交
39 40
		const viewInfo = this._context.configuration.editor.viewInfo;
		const configScrollbarOpts = viewInfo.scrollbar;
E
Erich Gamma 已提交
41

A
Alex Dima 已提交
42
		let scrollbarOptions: ScrollableElementCreationOptions = {
A
Alex Dima 已提交
43
			canUseTranslate3d: viewInfo.canUseTranslate3d,
A
Alex Dima 已提交
44
			listenOnDomNode: viewDomNode.domNode,
A
Alex Dima 已提交
45 46 47 48
			className: ClassNames.SCROLLABLE_ELEMENT + ' ' + viewInfo.theme,
			useShadows: false,
			lazyRender: true,

E
Erich Gamma 已提交
49 50
			vertical: configScrollbarOpts.vertical,
			horizontal: configScrollbarOpts.horizontal,
A
Alex Dima 已提交
51 52 53 54 55 56 57 58 59
			verticalHasArrows: configScrollbarOpts.verticalHasArrows,
			horizontalHasArrows: configScrollbarOpts.horizontalHasArrows,
			verticalScrollbarSize: configScrollbarOpts.verticalScrollbarSize,
			verticalSliderSize: configScrollbarOpts.verticalSliderSize,
			horizontalScrollbarSize: configScrollbarOpts.horizontalScrollbarSize,
			horizontalSliderSize: configScrollbarOpts.horizontalSliderSize,
			handleMouseWheel: configScrollbarOpts.handleMouseWheel,
			arrowSize: configScrollbarOpts.arrowSize,
			mouseWheelScrollSensitivity: configScrollbarOpts.mouseWheelScrollSensitivity,
E
Erich Gamma 已提交
60 61
		};

A
Alex Dima 已提交
62
		this.scrollbar = new ScrollableElement(linesContent.domNode, scrollbarOptions, this.scrollable);
A
Alex Dima 已提交
63 64
		PartFingerprints.write(this.scrollbar.getDomNode(), PartFingerprint.ScrollableElement);

E
Erich Gamma 已提交
65 66
		this.toDispose.push(this.scrollbar);

A
Alex Dima 已提交
67 68 69 70
		this.scrollbarDomNode = createFastDomNode(this.scrollbar.getDomNode());
		this.scrollbarDomNode.setPosition('absolute');
		this._setLayout();

E
Erich Gamma 已提交
71 72 73 74
		// When having a zone widget that calls .focus() on one of its dom elements,
		// the browser will try desperately to reveal that dom node, unexpectedly
		// changing the .scrollTop of this.linesContent

A
Alex Dima 已提交
75
		let onBrowserDesperateReveal = (domNode: HTMLElement, lookAtScrollTop: boolean, lookAtScrollLeft: boolean) => {
A
Alex Dima 已提交
76
			const scrollState = this.scrollable.getState();
J
Johannes Rieken 已提交
77
			let newScrollPosition: INewScrollPosition = {};
78

E
Erich Gamma 已提交
79
			if (lookAtScrollTop) {
A
Alex Dima 已提交
80
				let deltaTop = domNode.scrollTop;
E
Erich Gamma 已提交
81
				if (deltaTop) {
A
Alex Dima 已提交
82
					newScrollPosition.scrollTop = scrollState.scrollTop + deltaTop;
E
Erich Gamma 已提交
83 84 85 86 87
					domNode.scrollTop = 0;
				}
			}

			if (lookAtScrollLeft) {
A
Alex Dima 已提交
88
				let deltaLeft = domNode.scrollLeft;
E
Erich Gamma 已提交
89
				if (deltaLeft) {
A
Alex Dima 已提交
90
					newScrollPosition.scrollLeft = scrollState.scrollLeft + deltaLeft;
E
Erich Gamma 已提交
91 92 93
					domNode.scrollLeft = 0;
				}
			}
94

95
			this.scrollable.updateState(newScrollPosition);
E
Erich Gamma 已提交
96 97 98
		};

		// I've seen this happen both on the view dom node & on the lines content dom node.
A
Alex Dima 已提交
99 100 101
		this.toDispose.push(dom.addDisposableListener(viewDomNode.domNode, 'scroll', (e: Event) => onBrowserDesperateReveal(viewDomNode.domNode, true, true)));
		this.toDispose.push(dom.addDisposableListener(linesContent.domNode, 'scroll', (e: Event) => onBrowserDesperateReveal(linesContent.domNode, true, false)));
		this.toDispose.push(dom.addDisposableListener(overflowGuardDomNode.domNode, 'scroll', (e: Event) => onBrowserDesperateReveal(overflowGuardDomNode.domNode, true, false)));
E
Erich Gamma 已提交
102 103 104
	}

	public dispose(): void {
J
Joao Moreno 已提交
105
		this.toDispose = dispose(this.toDispose);
E
Erich Gamma 已提交
106 107
	}

A
Alex Dima 已提交
108 109 110 111 112 113 114 115
	private _setLayout(): void {
		const layoutInfo = this._context.configuration.editor.layoutInfo;

		this.scrollbarDomNode.setLeft(layoutInfo.contentLeft);
		this.scrollbarDomNode.setWidth(layoutInfo.contentWidth + layoutInfo.minimapWidth);
		this.scrollbarDomNode.setHeight(layoutInfo.contentHeight);
	}

A
Alex Dima 已提交
116
	public getOverviewRulerLayoutInfo(): IOverviewRulerLayoutInfo {
117
		return this.scrollbar.getOverviewRulerLayoutInfo();
E
Erich Gamma 已提交
118 119
	}

A
Alex Dima 已提交
120 121
	public getDomNode(): HTMLElement {
		return this.scrollbarDomNode.domNode;
E
Erich Gamma 已提交
122 123
	}

J
Johannes Rieken 已提交
124
	public delegateVerticalScrollbarMouseDown(browserEvent: MouseEvent): void {
125 126
		this.scrollbar.delegateVerticalScrollbarMouseDown(browserEvent);
	}
127 128 129 130

	public getVerticalSliderVerticalCenter(): number {
		return this.scrollbar.getVerticalSliderVerticalCenter();
	}
A
Alex Dima 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

	// --- begin event handlers

	public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
		const viewInfo = this._context.configuration.editor.viewInfo;

		this.scrollbar.updateClassName(ClassNames.SCROLLABLE_ELEMENT + ' ' + viewInfo.theme);
		if (e.viewInfo.scrollbar || e.viewInfo.canUseTranslate3d) {
			let newOpts: ScrollableElementChangeOptions = {
				canUseTranslate3d: viewInfo.canUseTranslate3d,
				handleMouseWheel: viewInfo.scrollbar.handleMouseWheel,
				mouseWheelScrollSensitivity: viewInfo.scrollbar.mouseWheelScrollSensitivity
			};
			this.scrollbar.updateOptions(newOpts);
		}
A
Alex Dima 已提交
146 147 148
		if (e.layoutInfo) {
			this._setLayout();
		}
A
Alex Dima 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
		return true;
	}
	public onCursorPositionChanged(e: viewEvents.ViewCursorPositionChangedEvent): boolean {
		return false;
	}
	public onCursorSelectionChanged(e: viewEvents.ViewCursorSelectionChangedEvent): boolean {
		return false;
	}
	public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
		return false;
	}
	public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
		return false;
	}
	public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean {
		return false;
	}
A
Alex Dima 已提交
166
	public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean {
A
Alex Dima 已提交
167 168
		return false;
	}
A
Alex Dima 已提交
169
	public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean {
A
Alex Dima 已提交
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
		return false;
	}
	public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean {
		return false;
	}
	public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean {
		return false;
	}
	public onRevealRangeRequest(e: viewEvents.ViewRevealRangeRequestEvent): boolean {
		return false;
	}
	public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {
		return true;
	}
	public onScrollRequest(e: viewEvents.ViewScrollRequestEvent): boolean {
		return false;
	}
	public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean {
		return false;
	}
	public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean {
		return false;
	}

	// --- end event handlers

	public prepareRender(ctx: IRenderingContext): void {
		// Nothing to do
	}

	public render(ctx: IRestrictedRenderingContext): void {
		this.scrollbar.renderNow();
	}
E
Erich Gamma 已提交
203
}