@@ -57,6 +57,7 @@ import { AccessibilityManager } from './AccessibilityManager';
|
||||
import { Linkifier } from './Linkifier';
|
||||
import { Emitter, EventUtils, type IEvent } from 'common/Event';
|
||||
import { addDisposableListener } from 'browser/Dom';
|
||||
import { Gesture, EventType as GestureEventType, type IGestureEvent } from 'browser/scrollable/touch';
|
||||
import { MutableDisposable, toDisposable } from 'common/Lifecycle';
|
||||
|
||||
export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
@@ -78,6 +79,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
private _customKeyEventHandler: CustomKeyEventHandler | undefined;
|
||||
private _customWheelEventHandler: CustomWheelEventHandler | undefined;
|
||||
private _touchScrollAccumulator: number = 0;
|
||||
|
||||
// Browser services
|
||||
private readonly _decorationService: DecorationService;
|
||||
@@ -914,9 +916,80 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
return false;
|
||||
}
|
||||
}, { passive: false }));
|
||||
|
||||
// Touch/gesture scrolling support
|
||||
this._register(Gesture.addTarget(this.screenElement!));
|
||||
this._register(addDisposableListener(this.screenElement!, GestureEventType.START, () => {
|
||||
this._touchScrollAccumulator = 0;
|
||||
}));
|
||||
this._register(addDisposableListener(this.screenElement!, GestureEventType.CHANGE, (e: IGestureEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// When mouse protocol has wheel events active, send as mouse wheel events
|
||||
if (requestedEvents.wheel) {
|
||||
this._handleTouchScrollAsWheel(e, sendEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
// When in alt buffer (no scrollback), send up/down key sequences
|
||||
if (!this.buffer.hasScrollback) {
|
||||
this._handleTouchScrollAsKeys(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal scrollback: delegate to viewport
|
||||
this._viewport?.handleTouchScroll(e.translationY);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
private _handleTouchScrollAsKeys(e: IGestureEvent): void {
|
||||
const cellHeight = this._renderService?.dimensions.css.cell.height;
|
||||
if (!cellHeight) return;
|
||||
|
||||
this._touchScrollAccumulator -= e.translationY;
|
||||
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
|
||||
if (lines === 0) return;
|
||||
|
||||
this._touchScrollAccumulator -= lines * cellHeight;
|
||||
const sequence = C0.ESC
|
||||
+ (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[')
|
||||
+ (lines < 0 ? 'A' : 'B');
|
||||
for (let i = 0; i < Math.abs(lines); i++) {
|
||||
this.coreService.triggerDataEvent(sequence, true);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleTouchScrollAsWheel(e: IGestureEvent, sendEvent: (ev: MouseEvent | WheelEvent) => boolean): void {
|
||||
const cellHeight = this._renderService?.dimensions.css.cell.height;
|
||||
if (!cellHeight) return;
|
||||
|
||||
this._touchScrollAccumulator -= e.translationY;
|
||||
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
|
||||
if (lines === 0) return;
|
||||
|
||||
this._touchScrollAccumulator -= lines * cellHeight;
|
||||
|
||||
// Get coordinates from gesture event
|
||||
const pos = this._mouseService?.getMouseReportCoords(e, this.screenElement!);
|
||||
if (!pos) return;
|
||||
|
||||
for (let i = 0; i < Math.abs(lines); i++) {
|
||||
this.coreMouseService.triggerMouseEvent({
|
||||
col: pos.col,
|
||||
row: pos.row,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
button: CoreMouseButton.WHEEL,
|
||||
action: lines < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the renderer to refresh terminal content between two rows (inclusive) at the next
|
||||
* opportunity.
|
||||
|
||||
@@ -8,12 +8,11 @@ import { ViewportConstants } from 'browser/shared/Constants';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { IBufferService, ICoreMouseService, IOptionsService } from 'common/services/Services';
|
||||
import { CoreMouseEventType } from 'common/Types';
|
||||
import { addDisposableListener, scheduleAtNextAnimationFrame } from 'browser/Dom';
|
||||
import { scheduleAtNextAnimationFrame } from 'browser/Dom';
|
||||
import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement';
|
||||
import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions';
|
||||
import { Emitter, EventUtils } from 'common/Event';
|
||||
import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable';
|
||||
import { Gesture, EventType as GestureEventType, type IGestureEvent } from 'browser/scrollable/touch';
|
||||
|
||||
export class Viewport extends Disposable {
|
||||
|
||||
@@ -107,9 +106,6 @@ export class Viewport extends Disposable {
|
||||
|
||||
this._register(this._scrollableElement.onScroll(e => this._handleScroll(e)));
|
||||
|
||||
// Touch/gesture scrolling support
|
||||
this._register(Gesture.addTarget(screenElement));
|
||||
this._register(addDisposableListener(screenElement, GestureEventType.CHANGE, (e: IGestureEvent) => this._handleGestureChange(e)));
|
||||
}
|
||||
|
||||
public scrollLines(disp: number): void {
|
||||
@@ -204,12 +200,10 @@ export class Viewport extends Disposable {
|
||||
this._isHandlingScroll = false;
|
||||
}
|
||||
|
||||
private _handleGestureChange(e: IGestureEvent): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
public handleTouchScroll(translationY: number): void {
|
||||
const pos = this._scrollableElement.getScrollPosition();
|
||||
this._scrollableElement.setScrollPosition({
|
||||
scrollTop: pos.scrollTop - e.translationY
|
||||
scrollTop: pos.scrollTop - translationY
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,8 @@ export interface IGestureEvent extends MouseEvent {
|
||||
translationY: number;
|
||||
pageX: number;
|
||||
pageY: number;
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
tapCount: number;
|
||||
}
|
||||
|
||||
@@ -459,6 +461,8 @@ export class Gesture extends Disposable {
|
||||
evt.translationY = touch.pageY - tail(data.rollingPageY)!;
|
||||
evt.pageX = touch.pageX;
|
||||
evt.pageY = touch.pageY;
|
||||
evt.clientX = touch.clientX;
|
||||
evt.clientY = touch.clientY;
|
||||
this._dispatchEvent(evt);
|
||||
|
||||
if (data.rollingPageX.length > 3) {
|
||||
|
||||
Reference in New Issue
Block a user