diff --git a/src/Terminal.ts b/src/Terminal.ts index 731846f2..f8610100 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1037,8 +1037,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // the shell for example this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => { if (this.mouseEvents) return; - this.viewport.onWheel(ev); - return this.cancel(ev); + if (!this.viewport.onWheel(ev)) { + return this.cancel(ev); + } })); this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => { @@ -1049,8 +1050,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => { if (this.mouseEvents) return; - this.viewport.onTouchMove(ev); - return this.cancel(ev); + if (!this.viewport.onTouchMove(ev)) { + return this.cancel(ev); + } })); } diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 9a406a08..920b93db 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -410,13 +410,13 @@ export class MockViewport implements IViewport { onThemeChange(colors: IColorSet): void { throw new Error('Method not implemented.'); } - onWheel(ev: WheelEvent): void { + onWheel(ev: WheelEvent): boolean { throw new Error('Method not implemented.'); } onTouchStart(ev: TouchEvent): void { throw new Error('Method not implemented.'); } - onTouchMove(ev: TouchEvent): void { + onTouchMove(ev: TouchEvent): boolean { throw new Error('Method not implemented.'); } syncScrollArea(): void { } diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index 3f3a7637..894beb36 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -37,9 +37,9 @@ export interface IViewport extends IDisposable { scrollBarWidth: number; syncScrollArea(): void; getLinesScrolled(ev: WheelEvent): number; - onWheel(ev: WheelEvent): void; + onWheel(ev: WheelEvent): boolean; onTouchStart(ev: TouchEvent): void; - onTouchMove(ev: TouchEvent): void; + onTouchMove(ev: TouchEvent): boolean; onThemeChange(colors: IColorSet): void; } diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 9625588d..4f9363d9 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -152,20 +152,36 @@ export class Viewport extends Disposable implements IViewport { this._scrollLines(diff, true); } + /** + * Handles bubbling of scroll event in case the viewport has reached top or bottom + * @param ev The scroll event. + * @param amount The amount scrolled + */ + private _bubbleScroll(ev: Event, amount: number): boolean { + const scrollPosFromTop = this._viewportElement.scrollTop + this._lastRecordedViewportHeight; + if ((amount < 0 && this._viewportElement.scrollTop !== 0) || + (amount > 0 && scrollPosFromTop < this._lastRecordedBufferHeight)) { + if (ev.cancelable) { + ev.preventDefault(); + } + return false; + } + return true; + } + /** * Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual * scrolling to `onScroll`, this event needs to be attached manually by the consumer of * `Viewport`. * @param ev The mouse wheel event. */ - public onWheel(ev: WheelEvent): void { + public onWheel(ev: WheelEvent): boolean { const amount = this._getPixelsScrolled(ev); if (amount === 0) { - return; + return false; } this._viewportElement.scrollTop += amount; - // Prevent the page from scrolling when the terminal scrolls - ev.preventDefault(); + return this._bubbleScroll(ev, amount); } private _getPixelsScrolled(ev: WheelEvent): number { @@ -220,13 +236,13 @@ export class Viewport extends Disposable implements IViewport { * Handles the touchmove event, scrolling the viewport if the position shifted. * @param ev The touch event. */ - public onTouchMove(ev: TouchEvent): void { + public onTouchMove(ev: TouchEvent): boolean { const deltaY = this._lastTouchY - ev.touches[0].pageY; this._lastTouchY = ev.touches[0].pageY; if (deltaY === 0) { - return; + return false; } this._viewportElement.scrollTop += deltaY; - ev.preventDefault(); + return this._bubbleScroll(ev, deltaY); } }