fix(terminal): stop wheel replay when mouse reporting is disabled (#8616)
This commit is contained in:
parent
739fce5287
commit
4e670d3e4c
|
|
@ -393,6 +393,7 @@ describe('terminal mouse wheel multiplier', () => {
|
|||
handlers.push(handler)
|
||||
},
|
||||
element: target,
|
||||
modes: { mouseTrackingMode: 'any' },
|
||||
rows: 24
|
||||
},
|
||||
{ getTuiMouseWheelMultiplier: () => 1 }
|
||||
|
|
@ -418,6 +419,81 @@ describe('terminal mouse wheel multiplier', () => {
|
|||
expect(shouldMultiplyTerminalMouseWheel(dispatched[0]!, target)).toBe(false)
|
||||
})
|
||||
|
||||
it('does not replay with a stale active mouse-reporting class', async () => {
|
||||
vi.stubGlobal('WheelEvent', TestWheelEvent)
|
||||
const handlers: ((event: WheelEvent) => boolean)[] = []
|
||||
const target = Object.assign(new EventTarget(), {
|
||||
classList: {
|
||||
contains: (className: string) => className === 'enable-mouse-events'
|
||||
}
|
||||
}) as unknown as EventTarget & HTMLElement
|
||||
const dispatched: WheelEvent[] = []
|
||||
target.addEventListener('wheel', (event) => dispatched.push(event as WheelEvent))
|
||||
const terminal = {
|
||||
attachCustomWheelEventHandler: (handler: (event: WheelEvent) => boolean) => {
|
||||
handlers.push(handler)
|
||||
},
|
||||
element: target,
|
||||
modes: { mouseTrackingMode: 'none' as const },
|
||||
rows: 24
|
||||
}
|
||||
attachTerminalMouseWheelMultiplier(terminal)
|
||||
|
||||
const event = new TestWheelEvent('wheel', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
deltaMode: DOM_DELTA_PIXEL,
|
||||
deltaY: 12
|
||||
}) as WheelEvent
|
||||
Object.defineProperty(event, 'wheelDeltaY', {
|
||||
configurable: true,
|
||||
value: -120
|
||||
})
|
||||
|
||||
expect(handlers[0]?.(event)).toBe(true)
|
||||
await Promise.resolve()
|
||||
|
||||
expect(dispatched).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('discards pending replay when mouse reporting turns off before drain', async () => {
|
||||
vi.stubGlobal('WheelEvent', TestWheelEvent)
|
||||
const handlers: ((event: WheelEvent) => boolean)[] = []
|
||||
const target = Object.assign(new EventTarget(), {
|
||||
classList: {
|
||||
contains: (className: string) => className === 'enable-mouse-events'
|
||||
}
|
||||
}) as unknown as EventTarget & HTMLElement
|
||||
const dispatched: WheelEvent[] = []
|
||||
target.addEventListener('wheel', (event) => dispatched.push(event as WheelEvent))
|
||||
const terminal = {
|
||||
attachCustomWheelEventHandler: (handler: (event: WheelEvent) => boolean) => {
|
||||
handlers.push(handler)
|
||||
},
|
||||
element: target,
|
||||
modes: { mouseTrackingMode: 'any' as 'any' | 'none' },
|
||||
rows: 24
|
||||
}
|
||||
attachTerminalMouseWheelMultiplier(terminal)
|
||||
|
||||
const event = new TestWheelEvent('wheel', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
deltaMode: DOM_DELTA_PIXEL,
|
||||
deltaY: 12
|
||||
}) as WheelEvent
|
||||
Object.defineProperty(event, 'wheelDeltaY', {
|
||||
configurable: true,
|
||||
value: -120
|
||||
})
|
||||
|
||||
expect(handlers[0]?.(event)).toBe(false)
|
||||
terminal.modes.mouseTrackingMode = 'none'
|
||||
await Promise.resolve()
|
||||
|
||||
expect(dispatched).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('replays trackpad-like TUI pixel scrolling with responsive direction reversal', async () => {
|
||||
vi.stubGlobal('WheelEvent', TestWheelEvent)
|
||||
const handlers: ((event: WheelEvent) => boolean)[] = []
|
||||
|
|
@ -434,6 +510,7 @@ describe('terminal mouse wheel multiplier', () => {
|
|||
handlers.push(handler)
|
||||
},
|
||||
element: target,
|
||||
modes: { mouseTrackingMode: 'any' },
|
||||
rows: 24
|
||||
},
|
||||
{ getTuiMouseWheelMultiplier: () => 1 }
|
||||
|
|
@ -486,6 +563,7 @@ describe('terminal mouse wheel multiplier', () => {
|
|||
handlers.push(handler)
|
||||
},
|
||||
element: target,
|
||||
modes: { mouseTrackingMode: 'any' },
|
||||
rows: 24
|
||||
},
|
||||
{ getTuiMouseWheelMultiplier: () => 1 }
|
||||
|
|
@ -520,6 +598,7 @@ describe('terminal mouse wheel multiplier', () => {
|
|||
handlers.push(handler)
|
||||
},
|
||||
element: target,
|
||||
modes: { mouseTrackingMode: 'any' },
|
||||
rows: 24
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ const XTERM_MOUSE_REPORTING_CLASS = 'enable-mouse-events'
|
|||
const REPLAYED_WHEEL_EVENT_PROPERTY = '__orcaReplayedTerminalWheelEvent'
|
||||
const DOM_DELTA_LINE = 1
|
||||
|
||||
type TerminalWheelTarget = Pick<Terminal, 'attachCustomWheelEventHandler' | 'element' | 'rows'>
|
||||
type TerminalWheelTarget = Pick<Terminal, 'attachCustomWheelEventHandler' | 'element' | 'rows'> & {
|
||||
modes: Pick<Terminal['modes'], 'mouseTrackingMode'>
|
||||
}
|
||||
|
||||
type TerminalMouseWheelMultiplierOptions = {
|
||||
getTuiMouseWheelMultiplier?: () => number | undefined
|
||||
|
|
@ -117,7 +119,10 @@ export function shouldMultiplyTerminalMouseWheel(
|
|||
return true
|
||||
}
|
||||
|
||||
function drainTerminalTuiWheelReports(state: TerminalTuiMouseWheelReplayState): void {
|
||||
function drainTerminalTuiWheelReports(
|
||||
state: TerminalTuiMouseWheelReplayState,
|
||||
terminal: TerminalWheelTarget
|
||||
): void {
|
||||
const target = state.pendingTarget
|
||||
const event = state.pendingEvent
|
||||
if (!target || !event || state.pendingReports <= 0) {
|
||||
|
|
@ -125,6 +130,15 @@ function drainTerminalTuiWheelReports(state: TerminalTuiMouseWheelReplayState):
|
|||
return
|
||||
}
|
||||
|
||||
if (terminal.modes.mouseTrackingMode === 'none') {
|
||||
state.pendingReports = 0
|
||||
state.drainScheduled = false
|
||||
state.pendingDirection = 0
|
||||
state.pendingEvent = null
|
||||
state.pendingTarget = null
|
||||
return
|
||||
}
|
||||
|
||||
const reportsToDispatch = state.pendingReports
|
||||
for (let i = 0; i < reportsToDispatch; i += 1) {
|
||||
target.dispatchEvent(cloneWheelReportEvent(event))
|
||||
|
|
@ -138,6 +152,7 @@ function drainTerminalTuiWheelReports(state: TerminalTuiMouseWheelReplayState):
|
|||
|
||||
function queueTerminalTuiWheelReports(
|
||||
state: TerminalTuiMouseWheelReplayState,
|
||||
terminal: TerminalWheelTarget,
|
||||
target: EventTarget,
|
||||
event: WheelEvent,
|
||||
reportCount: number
|
||||
|
|
@ -164,7 +179,7 @@ function queueTerminalTuiWheelReports(
|
|||
// Why: dispatch after xterm returns from the original wheel handler, but do
|
||||
// not frame-cap reports; fullscreen TUIs need the full wheel distance.
|
||||
queueMicrotask(() => {
|
||||
drainTerminalTuiWheelReports(state)
|
||||
drainTerminalTuiWheelReports(state, terminal)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +189,10 @@ export function attachTerminalMouseWheelMultiplier(
|
|||
): void {
|
||||
const replayState = createTerminalTuiMouseWheelReplayState()
|
||||
terminal.attachCustomWheelEventHandler((event) => {
|
||||
if (!shouldMultiplyTerminalMouseWheel(event, terminal.element)) {
|
||||
if (
|
||||
terminal.modes.mouseTrackingMode === 'none' ||
|
||||
!shouldMultiplyTerminalMouseWheel(event, terminal.element)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +213,7 @@ export function attachTerminalMouseWheelMultiplier(
|
|||
rows: terminal.rows
|
||||
}
|
||||
)
|
||||
queueTerminalTuiWheelReports(replayState, target, event, reportCount)
|
||||
queueTerminalTuiWheelReports(replayState, terminal, target, event, reportCount)
|
||||
|
||||
return false
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue