@xterm/xterm 5.6.0-beta.43 → 5.6.0-beta.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/css/xterm.css +65 -4
  2. package/lib/xterm.js +1 -1
  3. package/lib/xterm.js.map +1 -1
  4. package/lib/xterm.mjs +34 -7
  5. package/lib/xterm.mjs.map +4 -4
  6. package/package.json +2 -2
  7. package/src/browser/CoreBrowserTerminal.ts +53 -68
  8. package/src/browser/Types.ts +4 -1
  9. package/src/browser/Viewport.ts +142 -370
  10. package/src/browser/decorations/OverviewRulerRenderer.ts +25 -35
  11. package/src/browser/renderer/shared/CharAtlasUtils.ts +4 -0
  12. package/src/browser/services/ThemeService.ts +10 -1
  13. package/src/browser/shared/Constants.ts +8 -0
  14. package/src/common/CoreTerminal.ts +7 -13
  15. package/src/common/Types.ts +0 -6
  16. package/src/common/services/BufferService.ts +2 -2
  17. package/src/common/services/CoreMouseService.ts +2 -0
  18. package/src/common/services/Services.ts +10 -3
  19. package/src/vs/base/browser/browser.ts +141 -0
  20. package/src/vs/base/browser/canIUse.ts +49 -0
  21. package/src/vs/base/browser/dom.ts +2369 -0
  22. package/src/vs/base/browser/fastDomNode.ts +316 -0
  23. package/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
  24. package/src/vs/base/browser/iframe.ts +135 -0
  25. package/src/vs/base/browser/keyboardEvent.ts +213 -0
  26. package/src/vs/base/browser/mouseEvent.ts +229 -0
  27. package/src/vs/base/browser/touch.ts +372 -0
  28. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
  29. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
  30. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +718 -0
  31. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
  32. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
  33. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
  34. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
  35. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
  36. package/src/vs/base/browser/ui/widget.ts +57 -0
  37. package/src/vs/base/browser/window.ts +14 -0
  38. package/src/vs/base/common/arrays.ts +887 -0
  39. package/src/vs/base/common/arraysFind.ts +202 -0
  40. package/src/vs/base/common/assert.ts +71 -0
  41. package/src/vs/base/common/async.ts +1992 -0
  42. package/src/vs/base/common/cancellation.ts +148 -0
  43. package/src/vs/base/common/charCode.ts +450 -0
  44. package/src/vs/base/common/collections.ts +140 -0
  45. package/src/vs/base/common/decorators.ts +130 -0
  46. package/src/vs/base/common/equals.ts +146 -0
  47. package/src/vs/base/common/errors.ts +303 -0
  48. package/src/vs/base/common/event.ts +1762 -0
  49. package/src/vs/base/common/functional.ts +32 -0
  50. package/src/vs/base/common/hash.ts +316 -0
  51. package/src/vs/base/common/iterator.ts +159 -0
  52. package/src/vs/base/common/keyCodes.ts +526 -0
  53. package/src/vs/base/common/keybindings.ts +284 -0
  54. package/src/vs/base/common/lazy.ts +47 -0
  55. package/src/vs/base/common/lifecycle.ts +801 -0
  56. package/src/vs/base/common/linkedList.ts +142 -0
  57. package/src/vs/base/common/map.ts +202 -0
  58. package/src/vs/base/common/numbers.ts +98 -0
  59. package/src/vs/base/common/observable.ts +76 -0
  60. package/src/vs/base/common/observableInternal/api.ts +31 -0
  61. package/src/vs/base/common/observableInternal/autorun.ts +281 -0
  62. package/src/vs/base/common/observableInternal/base.ts +489 -0
  63. package/src/vs/base/common/observableInternal/debugName.ts +145 -0
  64. package/src/vs/base/common/observableInternal/derived.ts +428 -0
  65. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
  66. package/src/vs/base/common/observableInternal/logging.ts +328 -0
  67. package/src/vs/base/common/observableInternal/promise.ts +209 -0
  68. package/src/vs/base/common/observableInternal/utils.ts +610 -0
  69. package/src/vs/base/common/platform.ts +281 -0
  70. package/src/vs/base/common/scrollable.ts +522 -0
  71. package/src/vs/base/common/sequence.ts +34 -0
  72. package/src/vs/base/common/stopwatch.ts +43 -0
  73. package/src/vs/base/common/strings.ts +557 -0
  74. package/src/vs/base/common/symbols.ts +9 -0
  75. package/src/vs/base/common/uint.ts +59 -0
  76. package/src/vs/patches/nls.ts +90 -0
  77. package/src/vs/typings/base-common.d.ts +20 -0
  78. package/src/vs/typings/require.d.ts +42 -0
  79. package/src/vs/typings/thenable.d.ts +12 -0
  80. package/src/vs/typings/vscode-globals-nls.d.ts +36 -0
  81. package/src/vs/typings/vscode-globals-product.d.ts +33 -0
  82. package/typings/xterm.d.ts +25 -1
@@ -0,0 +1,372 @@
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 DomUtils from 'vs/base/browser/dom';
7
+ import { mainWindow } from 'vs/base/browser/window';
8
+ import * as arrays from 'vs/base/common/arrays';
9
+ import { memoize } from 'vs/base/common/decorators';
10
+ import { Event as EventUtils } from 'vs/base/common/event';
11
+ import { Disposable, IDisposable, markAsSingleton, toDisposable } from 'vs/base/common/lifecycle';
12
+ import { LinkedList } from 'vs/base/common/linkedList';
13
+
14
+ export namespace EventType {
15
+ export const Tap = '-xterm-gesturetap';
16
+ export const Change = '-xterm-gesturechange';
17
+ export const Start = '-xterm-gesturestart';
18
+ export const End = '-xterm-gesturesend';
19
+ export const Contextmenu = '-xterm-gesturecontextmenu';
20
+ }
21
+
22
+ interface TouchData {
23
+ id: number;
24
+ initialTarget: EventTarget;
25
+ initialTimeStamp: number;
26
+ initialPageX: number;
27
+ initialPageY: number;
28
+ rollingTimestamps: number[];
29
+ rollingPageX: number[];
30
+ rollingPageY: number[];
31
+ }
32
+
33
+ export interface GestureEvent extends MouseEvent {
34
+ initialTarget: EventTarget | undefined;
35
+ translationX: number;
36
+ translationY: number;
37
+ pageX: number;
38
+ pageY: number;
39
+ tapCount: number;
40
+ }
41
+
42
+ interface Touch {
43
+ identifier: number;
44
+ screenX: number;
45
+ screenY: number;
46
+ clientX: number;
47
+ clientY: number;
48
+ pageX: number;
49
+ pageY: number;
50
+ radiusX: number;
51
+ radiusY: number;
52
+ rotationAngle: number;
53
+ force: number;
54
+ target: Element;
55
+ }
56
+
57
+ interface TouchList {
58
+ [i: number]: Touch;
59
+ length: number;
60
+ item(index: number): Touch;
61
+ identifiedTouch(id: number): Touch;
62
+ }
63
+
64
+ interface TouchEvent extends Event {
65
+ touches: TouchList;
66
+ targetTouches: TouchList;
67
+ changedTouches: TouchList;
68
+ }
69
+
70
+ export class Gesture extends Disposable {
71
+
72
+ private static readonly SCROLL_FRICTION = -0.005;
73
+ private static INSTANCE: Gesture;
74
+ private static readonly HOLD_DELAY = 700;
75
+
76
+ private dispatched = false;
77
+ private readonly targets = new LinkedList<HTMLElement>();
78
+ private readonly ignoreTargets = new LinkedList<HTMLElement>();
79
+ private handle: IDisposable | null;
80
+
81
+ private readonly activeTouches: { [id: number]: TouchData };
82
+
83
+ private _lastSetTapCountTime: number;
84
+
85
+ private static readonly CLEAR_TAP_COUNT_TIME = 400; // ms
86
+
87
+
88
+ private constructor() {
89
+ super();
90
+
91
+ this.activeTouches = {};
92
+ this.handle = null;
93
+ this._lastSetTapCountTime = 0;
94
+
95
+ this._register(EventUtils.runAndSubscribe(DomUtils.onDidRegisterWindow, ({ window, disposables }) => {
96
+ disposables.add(DomUtils.addDisposableListener(window.document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e), { passive: false }));
97
+ disposables.add(DomUtils.addDisposableListener(window.document, 'touchend', (e: TouchEvent) => this.onTouchEnd(window, e)));
98
+ disposables.add(DomUtils.addDisposableListener(window.document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e), { passive: false }));
99
+ }, { window: mainWindow, disposables: this._store }));
100
+ }
101
+
102
+ public static addTarget(element: HTMLElement): IDisposable {
103
+ if (!Gesture.isTouchDevice()) {
104
+ return Disposable.None;
105
+ }
106
+ if (!Gesture.INSTANCE) {
107
+ Gesture.INSTANCE = markAsSingleton(new Gesture());
108
+ }
109
+
110
+ const remove = Gesture.INSTANCE.targets.push(element);
111
+ return toDisposable(remove);
112
+ }
113
+
114
+ public static ignoreTarget(element: HTMLElement): IDisposable {
115
+ if (!Gesture.isTouchDevice()) {
116
+ return Disposable.None;
117
+ }
118
+ if (!Gesture.INSTANCE) {
119
+ Gesture.INSTANCE = markAsSingleton(new Gesture());
120
+ }
121
+
122
+ const remove = Gesture.INSTANCE.ignoreTargets.push(element);
123
+ return toDisposable(remove);
124
+ }
125
+
126
+ @memoize
127
+ static isTouchDevice(): boolean {
128
+ // `'ontouchstart' in window` always evaluates to true with typescript's modern typings. This causes `window` to be
129
+ // `never` later in `window.navigator`. That's why we need the explicit `window as Window` cast
130
+ return 'ontouchstart' in mainWindow || navigator.maxTouchPoints > 0;
131
+ }
132
+
133
+ public override dispose(): void {
134
+ if (this.handle) {
135
+ this.handle.dispose();
136
+ this.handle = null;
137
+ }
138
+
139
+ super.dispose();
140
+ }
141
+
142
+ private onTouchStart(e: TouchEvent): void {
143
+ const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
144
+
145
+ if (this.handle) {
146
+ this.handle.dispose();
147
+ this.handle = null;
148
+ }
149
+
150
+ for (let i = 0, len = e.targetTouches.length; i < len; i++) {
151
+ const touch = e.targetTouches.item(i);
152
+
153
+ this.activeTouches[touch.identifier] = {
154
+ id: touch.identifier,
155
+ initialTarget: touch.target,
156
+ initialTimeStamp: timestamp,
157
+ initialPageX: touch.pageX,
158
+ initialPageY: touch.pageY,
159
+ rollingTimestamps: [timestamp],
160
+ rollingPageX: [touch.pageX],
161
+ rollingPageY: [touch.pageY]
162
+ };
163
+
164
+ const evt = this.newGestureEvent(EventType.Start, touch.target);
165
+ evt.pageX = touch.pageX;
166
+ evt.pageY = touch.pageY;
167
+ this.dispatchEvent(evt);
168
+ }
169
+
170
+ if (this.dispatched) {
171
+ e.preventDefault();
172
+ e.stopPropagation();
173
+ this.dispatched = false;
174
+ }
175
+ }
176
+
177
+ private onTouchEnd(targetWindow: Window, e: TouchEvent): void {
178
+ const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
179
+
180
+ const activeTouchCount = Object.keys(this.activeTouches).length;
181
+
182
+ for (let i = 0, len = e.changedTouches.length; i < len; i++) {
183
+
184
+ const touch = e.changedTouches.item(i);
185
+
186
+ if (!this.activeTouches.hasOwnProperty(String(touch.identifier))) {
187
+ console.warn('move of an UNKNOWN touch', touch);
188
+ continue;
189
+ }
190
+
191
+ const data = this.activeTouches[touch.identifier],
192
+ holdTime = Date.now() - data.initialTimeStamp;
193
+
194
+ if (holdTime < Gesture.HOLD_DELAY
195
+ && Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)!) < 30
196
+ && Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)!) < 30) {
197
+
198
+ const evt = this.newGestureEvent(EventType.Tap, data.initialTarget);
199
+ evt.pageX = arrays.tail(data.rollingPageX)!;
200
+ evt.pageY = arrays.tail(data.rollingPageY)!;
201
+ this.dispatchEvent(evt);
202
+
203
+ } else if (holdTime >= Gesture.HOLD_DELAY
204
+ && Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)!) < 30
205
+ && Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)!) < 30) {
206
+
207
+ const evt = this.newGestureEvent(EventType.Contextmenu, data.initialTarget);
208
+ evt.pageX = arrays.tail(data.rollingPageX)!;
209
+ evt.pageY = arrays.tail(data.rollingPageY)!;
210
+ this.dispatchEvent(evt);
211
+
212
+ } else if (activeTouchCount === 1) {
213
+ const finalX = arrays.tail(data.rollingPageX)!;
214
+ const finalY = arrays.tail(data.rollingPageY)!;
215
+
216
+ const deltaT = arrays.tail(data.rollingTimestamps)! - data.rollingTimestamps[0];
217
+ const deltaX = finalX - data.rollingPageX[0];
218
+ const deltaY = finalY - data.rollingPageY[0];
219
+
220
+ // We need to get all the dispatch targets on the start of the inertia event
221
+ const dispatchTo = [...this.targets].filter(t => data.initialTarget instanceof Node && t.contains(data.initialTarget));
222
+ this.inertia(targetWindow, dispatchTo, timestamp, // time now
223
+ Math.abs(deltaX) / deltaT, // speed
224
+ deltaX > 0 ? 1 : -1, // x direction
225
+ finalX, // x now
226
+ Math.abs(deltaY) / deltaT, // y speed
227
+ deltaY > 0 ? 1 : -1, // y direction
228
+ finalY // y now
229
+ );
230
+ }
231
+
232
+
233
+ this.dispatchEvent(this.newGestureEvent(EventType.End, data.initialTarget));
234
+ // forget about this touch
235
+ delete this.activeTouches[touch.identifier];
236
+ }
237
+
238
+ if (this.dispatched) {
239
+ e.preventDefault();
240
+ e.stopPropagation();
241
+ this.dispatched = false;
242
+ }
243
+ }
244
+
245
+ private newGestureEvent(type: string, initialTarget?: EventTarget): GestureEvent {
246
+ const event = document.createEvent('CustomEvent') as unknown as GestureEvent;
247
+ event.initEvent(type, false, true);
248
+ event.initialTarget = initialTarget;
249
+ event.tapCount = 0;
250
+ return event;
251
+ }
252
+
253
+ private dispatchEvent(event: GestureEvent): void {
254
+ if (event.type === EventType.Tap) {
255
+ const currentTime = (new Date()).getTime();
256
+ let setTapCount = 0;
257
+ if (currentTime - this._lastSetTapCountTime > Gesture.CLEAR_TAP_COUNT_TIME) {
258
+ setTapCount = 1;
259
+ } else {
260
+ setTapCount = 2;
261
+ }
262
+
263
+ this._lastSetTapCountTime = currentTime;
264
+ event.tapCount = setTapCount;
265
+ } else if (event.type === EventType.Change || event.type === EventType.Contextmenu) {
266
+ // tap is canceled by scrolling or context menu
267
+ this._lastSetTapCountTime = 0;
268
+ }
269
+
270
+ if (event.initialTarget instanceof Node) {
271
+ for (const ignoreTarget of this.ignoreTargets) {
272
+ if (ignoreTarget.contains(event.initialTarget)) {
273
+ return;
274
+ }
275
+ }
276
+
277
+ const targets: [number, HTMLElement][] = [];
278
+ for (const target of this.targets) {
279
+ if (target.contains(event.initialTarget)) {
280
+ let depth = 0;
281
+ let now: Node | null = event.initialTarget;
282
+ while (now && now !== target) {
283
+ depth++;
284
+ now = now.parentElement;
285
+ }
286
+ targets.push([depth, target]);
287
+ }
288
+ }
289
+
290
+ targets.sort((a, b) => a[0] - b[0]);
291
+
292
+ for (const [_, target] of targets) {
293
+ target.dispatchEvent(event);
294
+ this.dispatched = true;
295
+ }
296
+ }
297
+ }
298
+
299
+ private inertia(targetWindow: Window, dispatchTo: readonly EventTarget[], t1: number, vX: number, dirX: number, x: number, vY: number, dirY: number, y: number): void {
300
+ this.handle = DomUtils.scheduleAtNextAnimationFrame(targetWindow, () => {
301
+ const now = Date.now();
302
+
303
+ // velocity: old speed + accel_over_time
304
+ const deltaT = now - t1;
305
+ let delta_pos_x = 0, delta_pos_y = 0;
306
+ let stopped = true;
307
+
308
+ vX += Gesture.SCROLL_FRICTION * deltaT;
309
+ vY += Gesture.SCROLL_FRICTION * deltaT;
310
+
311
+ if (vX > 0) {
312
+ stopped = false;
313
+ delta_pos_x = dirX * vX * deltaT;
314
+ }
315
+
316
+ if (vY > 0) {
317
+ stopped = false;
318
+ delta_pos_y = dirY * vY * deltaT;
319
+ }
320
+
321
+ // dispatch translation event
322
+ const evt = this.newGestureEvent(EventType.Change);
323
+ evt.translationX = delta_pos_x;
324
+ evt.translationY = delta_pos_y;
325
+ dispatchTo.forEach(d => d.dispatchEvent(evt));
326
+
327
+ if (!stopped) {
328
+ this.inertia(targetWindow, dispatchTo, now, vX, dirX, x + delta_pos_x, vY, dirY, y + delta_pos_y);
329
+ }
330
+ });
331
+ }
332
+
333
+ private onTouchMove(e: TouchEvent): void {
334
+ const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
335
+
336
+ for (let i = 0, len = e.changedTouches.length; i < len; i++) {
337
+
338
+ const touch = e.changedTouches.item(i);
339
+
340
+ if (!this.activeTouches.hasOwnProperty(String(touch.identifier))) {
341
+ console.warn('end of an UNKNOWN touch', touch);
342
+ continue;
343
+ }
344
+
345
+ const data = this.activeTouches[touch.identifier];
346
+
347
+ const evt = this.newGestureEvent(EventType.Change, data.initialTarget);
348
+ evt.translationX = touch.pageX - arrays.tail(data.rollingPageX)!;
349
+ evt.translationY = touch.pageY - arrays.tail(data.rollingPageY)!;
350
+ evt.pageX = touch.pageX;
351
+ evt.pageY = touch.pageY;
352
+ this.dispatchEvent(evt);
353
+
354
+ // only keep a few data points, to average the final speed
355
+ if (data.rollingPageX.length > 3) {
356
+ data.rollingPageX.shift();
357
+ data.rollingPageY.shift();
358
+ data.rollingTimestamps.shift();
359
+ }
360
+
361
+ data.rollingPageX.push(touch.pageX);
362
+ data.rollingPageY.push(touch.pageY);
363
+ data.rollingTimestamps.push(timestamp);
364
+ }
365
+
366
+ if (this.dispatched) {
367
+ e.preventDefault();
368
+ e.stopPropagation();
369
+ this.dispatched = false;
370
+ }
371
+ }
372
+ }
@@ -0,0 +1,303 @@
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 { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode';
8
+ import { GlobalPointerMoveMonitor } from 'vs/base/browser/globalPointerMoveMonitor';
9
+ import { 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 POINTER_DRAG_RESET_DISTANCE = 140;
21
+
22
+ export interface ISimplifiedPointerEvent {
23
+ buttons: number;
24
+ pageX: number;
25
+ pageY: 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
+ scrollByPage: boolean;
42
+ }
43
+
44
+ export abstract class AbstractScrollbar extends Widget {
45
+
46
+ protected _host: ScrollbarHost;
47
+ protected _scrollable: Scrollable;
48
+ protected _scrollByPage: boolean;
49
+ private _lazyRender: boolean;
50
+ protected _scrollbarState: ScrollbarState;
51
+ protected _visibilityController: ScrollbarVisibilityController;
52
+ private _pointerMoveMonitor: GlobalPointerMoveMonitor;
53
+
54
+ public domNode: FastDomNode<HTMLElement>;
55
+ public slider!: FastDomNode<HTMLElement>;
56
+
57
+ protected _shouldRender: boolean;
58
+
59
+ constructor(opts: AbstractScrollbarOptions) {
60
+ super();
61
+ this._lazyRender = opts.lazyRender;
62
+ this._host = opts.host;
63
+ this._scrollable = opts.scrollable;
64
+ this._scrollByPage = opts.scrollByPage;
65
+ this._scrollbarState = opts.scrollbarState;
66
+ this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName));
67
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
68
+ this._pointerMoveMonitor = this._register(new GlobalPointerMoveMonitor());
69
+ this._shouldRender = true;
70
+ this.domNode = createFastDomNode(document.createElement('div'));
71
+ this.domNode.setAttribute('role', 'presentation');
72
+ this.domNode.setAttribute('aria-hidden', 'true');
73
+
74
+ this._visibilityController.setDomNode(this.domNode);
75
+ this.domNode.setPosition('absolute');
76
+
77
+ this._register(dom.addDisposableListener(this.domNode.domNode, dom.EventType.POINTER_DOWN, (e: PointerEvent) => this._domNodePointerDown(e)));
78
+ }
79
+
80
+ // ----------------- creation
81
+
82
+ /**
83
+ * Creates the dom node for an arrow & adds it to the container
84
+ */
85
+ protected _createArrow(opts: ScrollbarArrowOptions): void {
86
+ const arrow = this._register(new ScrollbarArrow(opts));
87
+ this.domNode.domNode.appendChild(arrow.bgDomNode);
88
+ this.domNode.domNode.appendChild(arrow.domNode);
89
+ }
90
+
91
+ /**
92
+ * Creates the slider dom node, adds it to the container & hooks up the events
93
+ */
94
+ protected _createSlider(top: number, left: number, width: number | undefined, height: number | undefined): void {
95
+ this.slider = createFastDomNode(document.createElement('div'));
96
+ this.slider.setClassName('slider');
97
+ this.slider.setPosition('absolute');
98
+ this.slider.setTop(top);
99
+ this.slider.setLeft(left);
100
+ if (typeof width === 'number') {
101
+ this.slider.setWidth(width);
102
+ }
103
+ if (typeof height === 'number') {
104
+ this.slider.setHeight(height);
105
+ }
106
+ this.slider.setLayerHinting(true);
107
+ this.slider.setContain('strict');
108
+
109
+ this.domNode.domNode.appendChild(this.slider.domNode);
110
+
111
+ this._register(dom.addDisposableListener(
112
+ this.slider.domNode,
113
+ dom.EventType.POINTER_DOWN,
114
+ (e: PointerEvent) => {
115
+ if (e.button === 0) {
116
+ e.preventDefault();
117
+ this._sliderPointerDown(e);
118
+ }
119
+ }
120
+ ));
121
+
122
+ this.onclick(this.slider.domNode, e => {
123
+ if (e.leftButton) {
124
+ e.stopPropagation();
125
+ }
126
+ });
127
+ }
128
+
129
+ // ----------------- Update state
130
+
131
+ protected _onElementSize(visibleSize: number): boolean {
132
+ if (this._scrollbarState.setVisibleSize(visibleSize)) {
133
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
134
+ this._shouldRender = true;
135
+ if (!this._lazyRender) {
136
+ this.render();
137
+ }
138
+ }
139
+ return this._shouldRender;
140
+ }
141
+
142
+ protected _onElementScrollSize(elementScrollSize: number): boolean {
143
+ if (this._scrollbarState.setScrollSize(elementScrollSize)) {
144
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
145
+ this._shouldRender = true;
146
+ if (!this._lazyRender) {
147
+ this.render();
148
+ }
149
+ }
150
+ return this._shouldRender;
151
+ }
152
+
153
+ protected _onElementScrollPosition(elementScrollPosition: number): boolean {
154
+ if (this._scrollbarState.setScrollPosition(elementScrollPosition)) {
155
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
156
+ this._shouldRender = true;
157
+ if (!this._lazyRender) {
158
+ this.render();
159
+ }
160
+ }
161
+ return this._shouldRender;
162
+ }
163
+
164
+ // ----------------- rendering
165
+
166
+ public beginReveal(): void {
167
+ this._visibilityController.setShouldBeVisible(true);
168
+ }
169
+
170
+ public beginHide(): void {
171
+ this._visibilityController.setShouldBeVisible(false);
172
+ }
173
+
174
+ public render(): void {
175
+ if (!this._shouldRender) {
176
+ return;
177
+ }
178
+ this._shouldRender = false;
179
+
180
+ this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
181
+ this._updateSlider(this._scrollbarState.getSliderSize(), this._scrollbarState.getArrowSize() + this._scrollbarState.getSliderPosition());
182
+ }
183
+ // ----------------- DOM events
184
+
185
+ private _domNodePointerDown(e: PointerEvent): void {
186
+ if (e.target !== this.domNode.domNode) {
187
+ return;
188
+ }
189
+ this._onPointerDown(e);
190
+ }
191
+
192
+ public delegatePointerDown(e: PointerEvent): void {
193
+ const domTop = this.domNode.domNode.getClientRects()[0].top;
194
+ const sliderStart = domTop + this._scrollbarState.getSliderPosition();
195
+ const sliderStop = domTop + this._scrollbarState.getSliderPosition() + this._scrollbarState.getSliderSize();
196
+ const pointerPos = this._sliderPointerPosition(e);
197
+ if (sliderStart <= pointerPos && pointerPos <= sliderStop) {
198
+ // Act as if it was a pointer down on the slider
199
+ if (e.button === 0) {
200
+ e.preventDefault();
201
+ this._sliderPointerDown(e);
202
+ }
203
+ } else {
204
+ // Act as if it was a pointer down on the scrollbar
205
+ this._onPointerDown(e);
206
+ }
207
+ }
208
+
209
+ private _onPointerDown(e: PointerEvent): void {
210
+ let offsetX: number;
211
+ let offsetY: number;
212
+ if (e.target === this.domNode.domNode && typeof e.offsetX === 'number' && typeof e.offsetY === 'number') {
213
+ offsetX = e.offsetX;
214
+ offsetY = e.offsetY;
215
+ } else {
216
+ const domNodePosition = dom.getDomNodePagePosition(this.domNode.domNode);
217
+ offsetX = e.pageX - domNodePosition.left;
218
+ offsetY = e.pageY - domNodePosition.top;
219
+ }
220
+
221
+ const offset = this._pointerDownRelativePosition(offsetX, offsetY);
222
+ this._setDesiredScrollPositionNow(
223
+ this._scrollByPage
224
+ ? this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset)
225
+ : this._scrollbarState.getDesiredScrollPositionFromOffset(offset)
226
+ );
227
+
228
+ if (e.button === 0) {
229
+ // left button
230
+ e.preventDefault();
231
+ this._sliderPointerDown(e);
232
+ }
233
+ }
234
+
235
+ private _sliderPointerDown(e: PointerEvent): void {
236
+ if (!e.target || !(e.target instanceof Element)) {
237
+ return;
238
+ }
239
+ const initialPointerPosition = this._sliderPointerPosition(e);
240
+ const initialPointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(e);
241
+ const initialScrollbarState = this._scrollbarState.clone();
242
+ this.slider.toggleClassName('active', true);
243
+
244
+ this._pointerMoveMonitor.startMonitoring(
245
+ e.target,
246
+ e.pointerId,
247
+ e.buttons,
248
+ (pointerMoveData: PointerEvent) => {
249
+ const pointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(pointerMoveData);
250
+ const pointerOrthogonalDelta = Math.abs(pointerOrthogonalPosition - initialPointerOrthogonalPosition);
251
+
252
+ if (platform.isWindows && pointerOrthogonalDelta > POINTER_DRAG_RESET_DISTANCE) {
253
+ // The pointer has wondered away from the scrollbar => reset dragging
254
+ this._setDesiredScrollPositionNow(initialScrollbarState.getScrollPosition());
255
+ return;
256
+ }
257
+
258
+ const pointerPosition = this._sliderPointerPosition(pointerMoveData);
259
+ const pointerDelta = pointerPosition - initialPointerPosition;
260
+ this._setDesiredScrollPositionNow(initialScrollbarState.getDesiredScrollPositionFromDelta(pointerDelta));
261
+ },
262
+ () => {
263
+ this.slider.toggleClassName('active', false);
264
+ this._host.onDragEnd();
265
+ }
266
+ );
267
+
268
+ this._host.onDragStart();
269
+ }
270
+
271
+ private _setDesiredScrollPositionNow(_desiredScrollPosition: number): void {
272
+
273
+ const desiredScrollPosition: INewScrollPosition = {};
274
+ this.writeScrollPosition(desiredScrollPosition, _desiredScrollPosition);
275
+
276
+ this._scrollable.setScrollPositionNow(desiredScrollPosition);
277
+ }
278
+
279
+ public updateScrollbarSize(scrollbarSize: number): void {
280
+ this._updateScrollbarSize(scrollbarSize);
281
+ this._scrollbarState.setScrollbarSize(scrollbarSize);
282
+ this._shouldRender = true;
283
+ if (!this._lazyRender) {
284
+ this.render();
285
+ }
286
+ }
287
+
288
+ public isNeeded(): boolean {
289
+ return this._scrollbarState.isNeeded();
290
+ }
291
+
292
+ // ----------------- Overwrite these
293
+
294
+ protected abstract _renderDomNode(largeSize: number, smallSize: number): void;
295
+ protected abstract _updateSlider(sliderSize: number, sliderPosition: number): void;
296
+
297
+ protected abstract _pointerDownRelativePosition(offsetX: number, offsetY: number): number;
298
+ protected abstract _sliderPointerPosition(e: ISimplifiedPointerEvent): number;
299
+ protected abstract _sliderOrthogonalPointerPosition(e: ISimplifiedPointerEvent): number;
300
+ protected abstract _updateScrollbarSize(size: number): void;
301
+
302
+ public abstract writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void;
303
+ }