diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 37b33f7e..6ba27b66 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -646,7 +646,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { if (deltaY === 0) { return false; } - const lines = self._consumeWheelEvent(ev as WheelEvent); + const lines = self.coreMouseService.consumeWhellEvent( + ev as WheelEvent, + self._renderService?.dimensions?.device?.cell?.height, + self._coreBrowserService?.dpr + ); if (lines === 0) { return false; } @@ -821,7 +825,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return false; } - const lines = self._consumeWheelEvent(ev as WheelEvent); + const lines = self.coreMouseService.consumeWhellEvent( + ev as WheelEvent, + self._renderService?.dimensions?.device?.cell?.height, + self._coreBrowserService?.dpr + ); if (lines === 0) { return false; } @@ -834,38 +842,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { }, { passive: false })); } - // Stores a partial line amount when scrolling, this is used to keep track of how much of a line - // is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a - // quick fix and could have a more robust solution in place that reset the value when needed. - private _wheelPartialScroll: number = 0; - /** - * Gets the number of pixels scrolled by the mouse event taking into account what type of delta - * is being used. - * @param ev The mouse wheel event. - */ - private _consumeWheelEvent(ev: WheelEvent): number { - // Do nothing if it's not a vertical scroll event - if (ev.deltaY === 0 || ev.shiftKey) { - return 0; - } - - if (!this._coreBrowserService || !this._renderService) { - return 0; - } - // Fallback to WheelEvent.DOM_DELTA_LINE - const targetWheelEventPixels = this._renderService.dimensions.device.cell.height / this._coreBrowserService.dpr; - let amount = 1; - if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { - amount /= targetWheelEventPixels + 0.0; // Prevent integer division - this._wheelPartialScroll += amount; - amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1); - this._wheelPartialScroll %= 1; - } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { - amount *= this._bufferService.rows; - } - return amount; - } /** * Tells the renderer to refresh terminal content between two rows (inclusive) at the next diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 127e1f24..b25a2182 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -66,6 +66,9 @@ export class MockCoreMouseService implements ICoreMouseService { public explainEvents(events: CoreMouseEventType): { [event: string]: boolean } { throw new Error('Method not implemented.'); } + public consumeWhellEvent(ev: WheelEvent, cellHeight: number, dpr: number): number { + return 1; // Return a simple mock value + } } export class MockCharsetService implements ICharsetService { diff --git a/src/common/services/CoreMouseService.ts b/src/common/services/CoreMouseService.ts index f2f02379..a1135a8a 100644 --- a/src/common/services/CoreMouseService.ts +++ b/src/common/services/CoreMouseService.ts @@ -174,6 +174,7 @@ export class CoreMouseService extends Disposable implements ICoreMouseService { private _activeProtocol: string = ''; private _activeEncoding: string = ''; private _lastEvent: ICoreMouseEvent | null = null; + private _wheelPartialScroll: number = 0; private readonly _onProtocolChange = this._register(new Emitter()); public readonly onProtocolChange = this._onProtocolChange.event; @@ -229,6 +230,35 @@ export class CoreMouseService extends Disposable implements ICoreMouseService { this.activeProtocol = 'NONE'; this.activeEncoding = 'DEFAULT'; this._lastEvent = null; + this._wheelPartialScroll = 0; + } + + /** + * Processes a wheel event, accounting for partial scrolls for trackpad, mouse scrolls. + * This prevents hyper-sensitive scrolling in alt buffer. + */ + public consumeWhellEvent(ev: WheelEvent, cellHeight?: number, dpr?: number): number { + // Do nothing if it's not a vertical scroll event + if (ev.deltaY === 0 || ev.shiftKey) { + return 0; + } + + if (cellHeight === undefined || dpr === undefined) { + return 0; + } + + // Fallback to WheelEvent.DOM_DELTA_LINE + const targetWheelEventPixels = cellHeight / dpr; + let amount = 1; + if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { + amount /= targetWheelEventPixels + 0.0; // Prevent integer division + this._wheelPartialScroll += amount; + amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1); + this._wheelPartialScroll %= 1; + } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { + amount *= this._bufferService.rows; + } + return amount; } /** diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 9c3aebf7..e7ccc241 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -58,6 +58,11 @@ export interface ICoreMouseService { * Human readable version of mouse events. */ explainEvents(events: CoreMouseEventType): { [event: string]: boolean }; + + /** + * Process wheel event taking partial scroll into account. + */ + consumeWhellEvent(ev: WheelEvent, cellHeight?: number, dpr?: number): number; } export const ICoreService = createDecorator('CoreService');