Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions Muxy/Views/Terminal/GhosttyTerminalNSView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -458,14 +458,7 @@ final class GhosttyTerminalNSView: NSView {

override func mouseDown(with event: NSEvent) {
guard let surface else { return }
let alreadyFirstResponder = window?.firstResponder === self
window?.makeFirstResponder(self)
if alreadyFirstResponder {
ghostty_surface_set_focus(surface, true)
DispatchQueue.main.async { [weak self] in
self?.onFocus?()
}
}
activateFromPointerEvent()
let pt = mousePoint(from: event)
ghostty_surface_mouse_pos(surface, pt.x, pt.y, modsFromEvent(event))
_ = ghostty_surface_mouse_button(surface, GHOSTTY_MOUSE_PRESS, GHOSTTY_MOUSE_LEFT, modsFromEvent(event))
Expand Down Expand Up @@ -570,10 +563,42 @@ final class GhosttyTerminalNSView: NSView {
}

override func scrollWheel(with event: NSEvent) {
if !isFocused,
let focusedView = TerminalViewRegistry.shared.focusedView(excluding: self)
{
focusedView.scrollWithDeltas(
deltaX: event.scrollingDeltaX,
deltaY: event.scrollingDeltaY,
precise: event.hasPreciseScrollingDeltas
)
return
}
scrollWithDeltas(
deltaX: event.scrollingDeltaX,
deltaY: event.scrollingDeltaY,
precise: event.hasPreciseScrollingDeltas
)
}

private func scrollWithDeltas(deltaX: CGFloat, deltaY: CGFloat, precise: Bool) {
guard let surface else { return }
activateFromPointerEvent()
var mods: ghostty_input_scroll_mods_t = 0
if event.hasPreciseScrollingDeltas { mods |= 1 }
ghostty_surface_mouse_scroll(surface, event.scrollingDeltaX, event.scrollingDeltaY, mods)
if precise { mods |= 1 }
ghostty_surface_mouse_scroll(surface, deltaX, deltaY, mods)
}

private func activateFromPointerEvent() {
guard !overlayActive else { return }
let alreadyFirstResponder = window?.firstResponder === self
window?.makeFirstResponder(self)
guard window?.firstResponder === self else { return }
ghostty_surface_set_focus(surface, true)
if alreadyFirstResponder {
DispatchQueue.main.async { [weak self] in
self?.onFocus?()
}
}
}

private func buildKeyEvent(from event: NSEvent, action: ghostty_input_action_e) -> ghostty_input_key_s {
Expand Down
6 changes: 6 additions & 0 deletions Muxy/Views/Terminal/TerminalPane.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ struct TerminalBridge: NSViewRepresentable {
view.envVars = Self.buildEnvVars(paneID: state.id, worktreeKey: key)
}
view.isFocused = focused
if focused {
TerminalViewRegistry.shared.setFocusedPane(state.id)
}
view.overlayActive = overlayActive
view.onFocus = onFocus
view.onProcessExit = onProcessExit
Expand All @@ -152,6 +155,9 @@ struct TerminalBridge: NSViewRepresentable {
nsView.envVars = Self.buildEnvVars(paneID: state.id, worktreeKey: key)
}
nsView.overlayActive = overlayActive
if focused {
TerminalViewRegistry.shared.setFocusedPane(state.id)
}
nsView.onFocus = onFocus
nsView.onProcessExit = onProcessExit
nsView.onSplitRequest = onSplitRequest
Expand Down
16 changes: 16 additions & 0 deletions Muxy/Views/Terminal/TerminalViewRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ final class TerminalViewRegistry {

private var views: [UUID: GhosttyTerminalNSView] = [:]
private var paneIDs: [ObjectIdentifier: UUID] = [:]
private var focusedPaneID: UUID?

private init() {}

Expand All @@ -30,6 +31,9 @@ final class TerminalViewRegistry {
func removeView(for paneID: UUID) {
guard let view = views.removeValue(forKey: paneID) else { return }
paneIDs.removeValue(forKey: ObjectIdentifier(view))
if focusedPaneID == paneID {
focusedPaneID = nil
}
view.tearDown()
}

Expand All @@ -44,6 +48,18 @@ final class TerminalViewRegistry {
func paneID(for view: GhosttyTerminalNSView) -> UUID? {
paneIDs[ObjectIdentifier(view)]
}

func setFocusedPane(_ paneID: UUID) {
focusedPaneID = paneID
}

func focusedView(excluding view: GhosttyTerminalNSView) -> GhosttyTerminalNSView? {
guard let focusedPaneID,
let focusedView = views[focusedPaneID],
focusedView !== view
else { return nil }
return focusedView
}
}

extension TerminalViewRegistry: TerminalViewRemoving {}
Loading