Fix terminal pane trackpad scroll routing
This commit is contained in:
parent
b0bf4ef514
commit
eeb6122e3c
2 changed files with 79 additions and 10 deletions
|
|
@ -3700,23 +3700,23 @@ extension Notification.Name {
|
|||
private final class GhosttyScrollView: NSScrollView {
|
||||
weak var surfaceView: GhosttyNSView?
|
||||
|
||||
// Keep keyboard routing on the terminal surface; this wrapper is viewport plumbing.
|
||||
override var acceptsFirstResponder: Bool { false }
|
||||
|
||||
override func scrollWheel(with event: NSEvent) {
|
||||
guard let surfaceView else {
|
||||
super.scrollWheel(with: event)
|
||||
return
|
||||
}
|
||||
|
||||
if let surface = surfaceView.terminalSurface?.surface,
|
||||
ghostty_surface_mouse_captured(surface) {
|
||||
GhosttyNSView.focusLog("GhosttyScrollView.scrollWheel: mouseCaptured -> surface scroll")
|
||||
if window?.firstResponder !== surfaceView {
|
||||
window?.makeFirstResponder(surfaceView)
|
||||
}
|
||||
surfaceView.scrollWheel(with: event)
|
||||
} else {
|
||||
GhosttyNSView.focusLog("GhosttyScrollView.scrollWheel: super scroll")
|
||||
super.scrollWheel(with: event)
|
||||
// Route wheel gestures to the terminal surface so Ghostty handles scrollback.
|
||||
// Letting NSScrollView consume these events moves the wrapper viewport itself,
|
||||
// which causes pane-content drift instead of terminal scrollback movement.
|
||||
GhosttyNSView.focusLog("GhosttyScrollView.scrollWheel: surface scroll")
|
||||
if window?.firstResponder !== surfaceView {
|
||||
window?.makeFirstResponder(surfaceView)
|
||||
}
|
||||
surfaceView.scrollWheel(with: event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6571,6 +6571,75 @@ final class CommandPaletteOverlayPromotionPolicyTests: XCTestCase {
|
|||
|
||||
@MainActor
|
||||
final class GhosttySurfaceOverlayTests: XCTestCase {
|
||||
private final class ScrollProbeSurfaceView: GhosttyNSView {
|
||||
private(set) var scrollWheelCallCount = 0
|
||||
|
||||
override func scrollWheel(with event: NSEvent) {
|
||||
scrollWheelCallCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
func testTrackpadScrollRoutesToTerminalSurfaceAndPreservesKeyboardFocusPath() {
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 360, height: 240),
|
||||
styleMask: [.titled, .closable],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
defer { window.orderOut(nil) }
|
||||
|
||||
guard let contentView = window.contentView else {
|
||||
XCTFail("Expected content view")
|
||||
return
|
||||
}
|
||||
|
||||
let surfaceView = ScrollProbeSurfaceView(frame: NSRect(x: 0, y: 0, width: 160, height: 120))
|
||||
let hostedView = GhosttySurfaceScrollView(surfaceView: surfaceView)
|
||||
hostedView.frame = contentView.bounds
|
||||
hostedView.autoresizingMask = [.width, .height]
|
||||
contentView.addSubview(hostedView)
|
||||
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.displayIfNeeded()
|
||||
contentView.layoutSubtreeIfNeeded()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.05))
|
||||
|
||||
guard let scrollView = hostedView.subviews.first(where: { $0 is NSScrollView }) as? NSScrollView else {
|
||||
XCTFail("Expected hosted terminal scroll view")
|
||||
return
|
||||
}
|
||||
XCTAssertFalse(
|
||||
scrollView.acceptsFirstResponder,
|
||||
"Host scroll view should not become first responder and steal terminal shortcuts"
|
||||
)
|
||||
|
||||
_ = window.makeFirstResponder(nil)
|
||||
|
||||
guard let cgEvent = CGEvent(
|
||||
scrollWheelEvent2Source: nil,
|
||||
units: .pixel,
|
||||
wheelCount: 2,
|
||||
wheel1: 0,
|
||||
wheel2: -12,
|
||||
wheel3: 0
|
||||
), let scrollEvent = NSEvent(cgEvent: cgEvent) else {
|
||||
XCTFail("Expected scroll wheel event")
|
||||
return
|
||||
}
|
||||
|
||||
scrollView.scrollWheel(with: scrollEvent)
|
||||
|
||||
XCTAssertEqual(
|
||||
surfaceView.scrollWheelCallCount,
|
||||
1,
|
||||
"Trackpad wheel events should be forwarded directly to Ghostty surface scrolling"
|
||||
)
|
||||
XCTAssertTrue(
|
||||
window.firstResponder === surfaceView,
|
||||
"Scroll wheel handling should keep keyboard focus on terminal surface"
|
||||
)
|
||||
}
|
||||
|
||||
func testInactiveOverlayVisibilityTracksRequestedState() {
|
||||
let hostedView = GhosttySurfaceScrollView(
|
||||
surfaceView: GhosttyNSView(frame: NSRect(x: 0, y: 0, width: 80, height: 50))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue