Skip to content

Commit 1c2db85

Browse files
authored
fix: Mac window becomes unmovable after pane rearrangement (#10133)
On main, after rearranging panes, the window becomes permanently immovable. Grab handles temporarily set `window.isMovable = false` on hover to prevent window dragging from interfering with pane drags (fixing [#10110](#10110)), but the restoration logic failed when views were destroyed during pane rearrangement (which happens each time a pane is rearranged). The previous approach managed `window.isMovable` state across the view lifecycle: 1. `mouseEntered` → saved and disabled `window.isMovable` 2. View removed during rearrangement → `mouseExited` never fired 3. `deinit` ran with `self.window` already nil → restoration failed 4. Window stuck with `isMovable = false` Instead of managing window state, prevent the mouseDown event from reaching the window's drag handler by overriding mouse event handling in the grab handle view. Per [Apple's Event Handling Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/HandlingMouseEvents.html): > Custom NSView objects should not invoke super in their implementations of NSResponder mouse-event-handling methods such as mouseDown:, mouseDragged: and mouseUp: unless it is known that the inherited implementation provides some needed functionality. This eliminates all state management while solving both the original issue (#10110) and the new bug. AI disclosure: claude code found and wrote the fix. I tested it manually to see that it works. I pressed claude quite hard here to come up with the best fix, and looked at documentation to understand what the fix was doing. It seems like this is a better approach overall to preventing the main window from being dragged when grabbing the Surface Drag handle.
2 parents 9a21e56 + c384cd0 commit 1c2db85

File tree

1 file changed

+17
-17
lines changed

1 file changed

+17
-17
lines changed

macos/Sources/Ghostty/Surface View/SurfaceDragSource.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,30 @@ extension Ghostty {
105105
/// Whether the current drag was cancelled by pressing escape.
106106
private var dragCancelledByEscape: Bool = false
107107

108-
/// Original value of `window.isMovable` to restore
109-
/// when the mouse exits.
110-
private var isWindowMovable: Bool?
111-
112108
deinit {
113109
if let escapeMonitor {
114110
NSEvent.removeMonitor(escapeMonitor)
115111
}
116112
}
117-
113+
114+
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
115+
// Ensure this view gets the mouse event before window dragging handlers
116+
return true
117+
}
118+
119+
override func mouseDown(with event: NSEvent) {
120+
// Consume the mouseDown event to prevent it from propagating to the
121+
// window's drag handler. This fixes issue #10110 where grab handles
122+
// would drag the window instead of initiating pane drags.
123+
// Don't call super - the drag will be initiated in mouseDragged.
124+
}
125+
118126
override func updateTrackingAreas() {
119127
super.updateTrackingAreas()
120-
128+
121129
// To update our tracking area we just recreate it all.
122130
trackingAreas.forEach { removeTrackingArea($0) }
123-
131+
124132
// Add our tracking area for mouse events
125133
addTrackingArea(NSTrackingArea(
126134
rect: bounds,
@@ -135,18 +143,10 @@ extension Ghostty {
135143
}
136144

137145
override func mouseEntered(with event: NSEvent) {
138-
// Temporarily disable `isMovable` to fix
139-
// https://github.com/ghostty-org/ghostty/issues/10110
140-
isWindowMovable = window?.isMovable
141-
window?.isMovable = false
142146
onHoverChanged?(true)
143147
}
144148

145149
override func mouseExited(with event: NSEvent) {
146-
if let isWindowMovable {
147-
window?.isMovable = isWindowMovable
148-
self.isWindowMovable = nil
149-
}
150150
onHoverChanged?(false)
151151
}
152152

@@ -237,7 +237,7 @@ extension Ghostty {
237237
NSEvent.removeMonitor(escapeMonitor)
238238
self.escapeMonitor = nil
239239
}
240-
240+
241241
if operation == [] && !dragCancelledByEscape {
242242
let endsInWindow = NSApplication.shared.windows.contains { window in
243243
window.isVisible && window.frame.contains(screenPoint)
@@ -250,7 +250,7 @@ extension Ghostty {
250250
)
251251
}
252252
}
253-
253+
254254
isTracking = false
255255
onDragStateChanged?(false)
256256
}

0 commit comments

Comments
 (0)