Skip to content

Commit 38913e4

Browse files
committed
Fix scrolling with touch
Part of #5377
1 parent e14746e commit 38913e4

File tree

3 files changed

+80
-9
lines changed

3 files changed

+80
-9
lines changed

src/browser/CoreBrowserTerminal.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { AccessibilityManager } from './AccessibilityManager';
5757
import { Linkifier } from './Linkifier';
5858
import { Emitter, EventUtils, type IEvent } from 'common/Event';
5959
import { addDisposableListener } from 'browser/Dom';
60+
import { Gesture, EventType as GestureEventType, type IGestureEvent } from 'browser/scrollable/touch';
6061
import { MutableDisposable, toDisposable } from 'common/Lifecycle';
6162

6263
export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
@@ -78,6 +79,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
7879

7980
private _customKeyEventHandler: CustomKeyEventHandler | undefined;
8081
private _customWheelEventHandler: CustomWheelEventHandler | undefined;
82+
private _touchScrollAccumulator: number = 0;
8183

8284
// Browser services
8385
private readonly _decorationService: DecorationService;
@@ -914,8 +916,79 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
914916
return false;
915917
}
916918
}, { passive: false }));
919+
920+
// Touch/gesture scrolling support
921+
this._register(Gesture.addTarget(this.screenElement!));
922+
this._register(addDisposableListener(this.screenElement!, GestureEventType.START, () => {
923+
this._touchScrollAccumulator = 0;
924+
}));
925+
this._register(addDisposableListener(this.screenElement!, GestureEventType.CHANGE, (e: IGestureEvent) => {
926+
e.preventDefault();
927+
e.stopPropagation();
928+
929+
// When mouse protocol has wheel events active, send as mouse wheel events
930+
if (requestedEvents.wheel) {
931+
this._handleTouchScrollAsWheel(e, sendEvent);
932+
return;
933+
}
934+
935+
// When in alt buffer (no scrollback), send up/down key sequences
936+
if (!this.buffer.hasScrollback) {
937+
this._handleTouchScrollAsKeys(e);
938+
return;
939+
}
940+
941+
// Normal scrollback: delegate to viewport
942+
this._viewport?.handleTouchScroll(e.translationY);
943+
}));
944+
}
945+
946+
947+
private _handleTouchScrollAsKeys(e: IGestureEvent): void {
948+
const cellHeight = this._renderService?.dimensions.css.cell.height;
949+
if (!cellHeight) return;
950+
951+
this._touchScrollAccumulator -= e.translationY;
952+
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
953+
if (lines === 0) return;
954+
955+
this._touchScrollAccumulator -= lines * cellHeight;
956+
const sequence = C0.ESC
957+
+ (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[')
958+
+ (lines < 0 ? 'A' : 'B');
959+
for (let i = 0; i < Math.abs(lines); i++) {
960+
this.coreService.triggerDataEvent(sequence, true);
961+
}
917962
}
918963

964+
private _handleTouchScrollAsWheel(e: IGestureEvent, sendEvent: (ev: MouseEvent | WheelEvent) => boolean): void {
965+
const cellHeight = this._renderService?.dimensions.css.cell.height;
966+
if (!cellHeight) return;
967+
968+
this._touchScrollAccumulator -= e.translationY;
969+
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
970+
if (lines === 0) return;
971+
972+
this._touchScrollAccumulator -= lines * cellHeight;
973+
974+
// Get coordinates from gesture event
975+
const pos = this._mouseService?.getMouseReportCoords(e, this.screenElement!);
976+
if (!pos) return;
977+
978+
for (let i = 0; i < Math.abs(lines); i++) {
979+
this.coreMouseService.triggerMouseEvent({
980+
col: pos.col,
981+
row: pos.row,
982+
x: pos.x,
983+
y: pos.y,
984+
button: CoreMouseButton.WHEEL,
985+
action: lines < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN,
986+
ctrl: false,
987+
alt: false,
988+
shift: false
989+
});
990+
}
991+
}
919992

920993
/**
921994
* Tells the renderer to refresh terminal content between two rows (inclusive) at the next

src/browser/Viewport.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ import { ViewportConstants } from 'browser/shared/Constants';
88
import { Disposable, toDisposable } from 'common/Lifecycle';
99
import { IBufferService, ICoreMouseService, IOptionsService } from 'common/services/Services';
1010
import { CoreMouseEventType } from 'common/Types';
11-
import { addDisposableListener, scheduleAtNextAnimationFrame } from 'browser/Dom';
11+
import { scheduleAtNextAnimationFrame } from 'browser/Dom';
1212
import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement';
1313
import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions';
1414
import { Emitter, EventUtils } from 'common/Event';
1515
import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable';
16-
import { Gesture, EventType as GestureEventType, type IGestureEvent } from 'browser/scrollable/touch';
1716

1817
export class Viewport extends Disposable {
1918

@@ -107,9 +106,6 @@ export class Viewport extends Disposable {
107106

108107
this._register(this._scrollableElement.onScroll(e => this._handleScroll(e)));
109108

110-
// Touch/gesture scrolling support
111-
this._register(Gesture.addTarget(screenElement));
112-
this._register(addDisposableListener(screenElement, GestureEventType.CHANGE, (e: IGestureEvent) => this._handleGestureChange(e)));
113109
}
114110

115111
public scrollLines(disp: number): void {
@@ -204,12 +200,10 @@ export class Viewport extends Disposable {
204200
this._isHandlingScroll = false;
205201
}
206202

207-
private _handleGestureChange(e: IGestureEvent): void {
208-
e.preventDefault();
209-
e.stopPropagation();
203+
public handleTouchScroll(translationY: number): void {
210204
const pos = this._scrollableElement.getScrollPosition();
211205
this._scrollableElement.setScrollPosition({
212-
scrollTop: pos.scrollTop - e.translationY
206+
scrollTop: pos.scrollTop - translationY
213207
});
214208
}
215209
}

src/browser/scrollable/touch.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ export interface IGestureEvent extends MouseEvent {
153153
translationY: number;
154154
pageX: number;
155155
pageY: number;
156+
clientX: number;
157+
clientY: number;
156158
tapCount: number;
157159
}
158160

@@ -459,6 +461,8 @@ export class Gesture extends Disposable {
459461
evt.translationY = touch.pageY - tail(data.rollingPageY)!;
460462
evt.pageX = touch.pageX;
461463
evt.pageY = touch.pageY;
464+
evt.clientX = touch.clientX;
465+
evt.clientY = touch.clientY;
462466
this._dispatchEvent(evt);
463467

464468
if (data.rollingPageX.length > 3) {

0 commit comments

Comments
 (0)