Skip to content

Commit af6731b

Browse files
authored
Merge pull request #5685 from Tyriar/5377_scroll
Fix scrolling with touch
2 parents b0118df + 5b9b531 commit af6731b

File tree

5 files changed

+94
-10
lines changed

5 files changed

+94
-10
lines changed

src/browser/CoreBrowserTerminal.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
653653
this._mouseService.bindMouse({
654654
element: this.element!,
655655
screenElement: this.screenElement!,
656-
document: this._document!
656+
document: this._document!,
657+
handleTouchScroll: amount => this._viewport?.handleTouchScroll(amount)
657658
}, disposable => this._register(disposable), () => this.focus());
658659
}
659660

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, IMouseStateService, 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) {

src/browser/services/MouseService.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { CoreMouseAction, CoreMouseButton, CoreMouseEventType, ICoreMouseEvent,
99
import { C0 } from 'common/data/EscapeSequences';
1010
import { toDisposable } from 'common/Lifecycle';
1111
import { ICoreBrowserService, IMouseCoordsService, IMouseService, IMouseServiceTarget, IRenderService, ISelectionService } from './Services';
12+
import { Gesture, EventType as GestureEventType, IGestureEvent } from 'browser/scrollable/touch';
1213

1314
type RequestedMouseEvents = Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener | null>;
1415

@@ -23,6 +24,7 @@ export class MouseService implements IMouseService {
2324

2425
private _lastEvent: ICoreMouseEvent | null = null;
2526
private _wheelPartialScroll: number = 0;
27+
private _touchScrollAccumulator: number = 0;
2628

2729
constructor(
2830
@IRenderService private readonly _renderService: IRenderService,
@@ -82,6 +84,9 @@ export class MouseService implements IMouseService {
8284
*/
8385
register(addDisposableListener(element, 'mousedown', (ev: MouseEvent) => this._handleMouseDown(ctx, ev)));
8486
register(addDisposableListener(element, 'wheel', (ev: WheelEvent) => this._handlePassiveWheel(ctx, ev), { passive: false }));
87+
register(Gesture.addTarget(target.screenElement));
88+
register(addDisposableListener(target.screenElement, GestureEventType.START, () => this._handleTouchStart()));
89+
register(addDisposableListener(target.screenElement, GestureEventType.CHANGE, (e: IGestureEvent) => this._handleTouchChange(ctx, e)));
8590
}
8691

8792
private _sendEvent(ctx: IMouseBindContext, ev: MouseEvent | WheelEvent): boolean {
@@ -264,9 +269,88 @@ export class MouseService implements IMouseService {
264269
}
265270
}
266271

272+
private _handleTouchStart(): void {
273+
this._touchScrollAccumulator = 0;
274+
}
275+
276+
private _handleTouchChange(ctx: IMouseBindContext, e: IGestureEvent): void {
277+
e.preventDefault();
278+
e.stopPropagation();
279+
280+
// When mouse protocol has wheel events active, send as mouse wheel events.
281+
if (ctx.requestedEvents.wheel) {
282+
this._handleTouchScrollAsWheel(ctx, e);
283+
return;
284+
}
285+
286+
// When in alt buffer (no scrollback), send up/down key sequences.
287+
if (!this._bufferService.buffer.hasScrollback) {
288+
this._handleTouchScrollAsKeys(e);
289+
return;
290+
}
291+
292+
// Normal scrollback: delegate to viewport scrolling when available.
293+
ctx.target.handleTouchScroll?.(e.translationY);
294+
}
295+
296+
private _handleTouchScrollAsKeys(e: IGestureEvent): void {
297+
const cellHeight = this._renderService?.dimensions.css.cell.height;
298+
if (!cellHeight) {
299+
return;
300+
}
301+
302+
this._touchScrollAccumulator -= e.translationY;
303+
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
304+
if (lines === 0) {
305+
return;
306+
}
307+
308+
this._touchScrollAccumulator -= lines * cellHeight;
309+
const sequence = C0.ESC
310+
+ (this._coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[')
311+
+ (lines < 0 ? 'A' : 'B');
312+
for (let i = 0; i < Math.abs(lines); i++) {
313+
this._coreService.triggerDataEvent(sequence, true);
314+
}
315+
}
316+
317+
private _handleTouchScrollAsWheel(ctx: IMouseBindContext, e: IGestureEvent): void {
318+
const cellHeight = this._renderService?.dimensions.css.cell.height;
319+
if (!cellHeight) {
320+
return;
321+
}
322+
323+
this._touchScrollAccumulator -= e.translationY;
324+
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
325+
if (lines === 0) {
326+
return;
327+
}
328+
329+
this._touchScrollAccumulator -= lines * cellHeight;
330+
const pos = this._mouseCoordsService.getMouseReportCoords(e, ctx.target.screenElement);
331+
if (!pos) {
332+
return;
333+
}
334+
335+
for (let i = 0; i < Math.abs(lines); i++) {
336+
this._triggerMouseEvent({
337+
col: pos.col,
338+
row: pos.row,
339+
x: pos.x,
340+
y: pos.y,
341+
button: CoreMouseButton.WHEEL,
342+
action: lines < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN,
343+
ctrl: false,
344+
alt: false,
345+
shift: false
346+
});
347+
}
348+
}
349+
267350
public reset(): void {
268351
this._lastEvent = null;
269352
this._wheelPartialScroll = 0;
353+
this._touchScrollAccumulator = 0;
270354
}
271355

272356
private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {

src/browser/services/Services.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export interface IMouseServiceTarget {
6868
element: HTMLElement;
6969
screenElement: HTMLElement;
7070
document: Document;
71+
handleTouchScroll?(amount: number): void;
7172
}
7273

7374
export const IRenderService = createDecorator<IRenderService>('RenderService');

0 commit comments

Comments
 (0)