Skip to content

Commit 2338cbd

Browse files
authored
Fix: Scroll event leak after scrolling to the top of a text widget #3990 (#4231)
1 parent 83aa887 commit 2338cbd

File tree

1 file changed

+49
-30
lines changed

1 file changed

+49
-30
lines changed

src/composables/widgets/useStringWidget.ts

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { app } from '@/scripts/app'
88
import { type ComfyWidgetConstructorV2 } from '@/scripts/widgets'
99
import { useSettingStore } from '@/stores/settingStore'
1010

11+
const TRACKPAD_DETECTION_THRESHOLD = 50
12+
1113
function addMultilineWidget(
1214
node: LGraphNode,
1315
name: string,
@@ -54,38 +56,55 @@ function addMultilineWidget(
5456
}
5557
})
5658

57-
/** Timer reference. `null` when the timer completes. */
58-
let ignoreEventsTimer: ReturnType<typeof setTimeout> | null = null
59-
/** Total number of events ignored since the timer started. */
60-
let ignoredEvents = 0
61-
62-
// Pass wheel events to the canvas when appropriate
6359
inputEl.addEventListener('wheel', (event: WheelEvent) => {
64-
if (!Object.is(event.deltaX, -0)) return
65-
66-
// If the textarea has focus, require more effort to activate pass-through
67-
const multiplier = document.activeElement === inputEl ? 2 : 1
68-
const maxScrollHeight = inputEl.scrollHeight - inputEl.clientHeight
69-
70-
if (
71-
(event.deltaY < 0 && inputEl.scrollTop === 0) ||
72-
(event.deltaY > 0 && inputEl.scrollTop === maxScrollHeight)
73-
) {
74-
// Attempting to scroll past the end of the textarea
75-
if (!ignoreEventsTimer || ignoredEvents > 25 * multiplier) {
76-
app.canvas.processMouseWheel(event)
77-
} else {
78-
ignoredEvents++
79-
}
80-
} else if (event.deltaY !== 0) {
81-
// Start timer whenever a successful scroll occurs
82-
ignoredEvents = 0
83-
if (ignoreEventsTimer) clearTimeout(ignoreEventsTimer)
84-
85-
ignoreEventsTimer = setTimeout(() => {
86-
ignoreEventsTimer = null
87-
}, 800 * multiplier)
60+
const gesturesEnabled = useSettingStore().get(
61+
'LiteGraph.Pointer.TrackpadGestures'
62+
)
63+
const deltaX = event.deltaX
64+
const deltaY = event.deltaY
65+
66+
const canScrollY = inputEl.scrollHeight > inputEl.clientHeight
67+
const isHorizontal = Math.abs(deltaX) > Math.abs(deltaY)
68+
69+
// Prevent pinch zoom from zooming the page
70+
if (event.ctrlKey) {
71+
event.preventDefault()
72+
event.stopPropagation()
73+
app.canvas.processMouseWheel(event)
74+
return
8875
}
76+
77+
// Detect if this is likely a trackpad gesture vs mouse wheel
78+
// Trackpads usually have deltaX or smaller deltaY values (< TRACKPAD_DETECTION_THRESHOLD)
79+
// Mouse wheels typically have larger discrete deltaY values (>= TRACKPAD_DETECTION_THRESHOLD)
80+
const isLikelyTrackpad =
81+
Math.abs(deltaX) > 0 || Math.abs(deltaY) < TRACKPAD_DETECTION_THRESHOLD
82+
83+
// Trackpad gestures: when enabled, trackpad panning goes to canvas
84+
if (gesturesEnabled && isLikelyTrackpad) {
85+
event.preventDefault()
86+
event.stopPropagation()
87+
app.canvas.processMouseWheel(event)
88+
return
89+
}
90+
91+
// When gestures disabled: horizontal always goes to canvas (no horizontal scroll in textarea)
92+
if (isHorizontal) {
93+
event.preventDefault()
94+
event.stopPropagation()
95+
app.canvas.processMouseWheel(event)
96+
return
97+
}
98+
99+
// Vertical scrolling when gestures disabled: let textarea scroll if scrollable
100+
if (canScrollY) {
101+
event.stopPropagation()
102+
return
103+
}
104+
105+
// If textarea can't scroll vertically, pass to canvas
106+
event.preventDefault()
107+
app.canvas.processMouseWheel(event)
89108
})
90109

91110
return widget

0 commit comments

Comments
 (0)