Files
LockInBroMacOS/FloatingPanel.swift

90 lines
2.7 KiB
Swift
Raw Permalink Normal View History

2026-03-29 00:58:22 -04:00
// FloatingPanel.swift Always-on-top NSPanel that hosts the floating HUD
import AppKit
import SwiftUI
// MARK: - Panel subclass
final class FloatingPanel: NSPanel {
init() {
super.init(
contentRect: NSRect(x: 0, y: 0, width: 320, height: 140),
styleMask: [.titled, .fullSizeContentView, .nonactivatingPanel, .utilityWindow],
backing: .buffered,
defer: false
)
// Always float above other windows, including full-screen apps
level = .floating
collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .stationary]
// Drag anywhere on the panel body
isMovableByWindowBackground = true
isMovable = true
// Hide the standard title bar chrome
titleVisibility = .hidden
titlebarAppearsTransparent = true
// Transparent background so the SwiftUI material shows through
backgroundColor = .clear
isOpaque = false
// Don't activate the app when clicked (user keeps focus on their work)
becomesKeyOnlyIfNeeded = true
}
2026-04-01 16:10:30 -05:00
// Allow the panel to become key so buttons inside it can receive clicks.
// Combined with .nonactivatingPanel, this lets buttons work without
// stealing focus from the user's active app.
override var canBecomeKey: Bool { true }
2026-03-29 00:58:22 -04:00
}
// MARK: - Controller
@MainActor
final class FloatingPanelController {
static let shared = FloatingPanelController()
private var panel: FloatingPanel?
private init() {}
func show(session: SessionManager) {
if panel == nil {
let p = FloatingPanel()
let hud = FloatingHUDView()
.environment(session)
2026-04-01 16:10:30 -05:00
// NSHostingController gives proper preferredContentSize tracking so the
// panel auto-resizes as SwiftUI content grows or shrinks.
let controller = NSHostingController(rootView: hud)
p.contentViewController = controller
// Position: top-right of the main screen, just below the menu bar.
// Anchor the top edge so the panel grows downward as content expands.
2026-03-29 00:58:22 -04:00
if let screen = NSScreen.main {
let margin: CGFloat = 16
let x = screen.visibleFrame.maxX - 320 - margin
2026-04-01 16:10:30 -05:00
// Place top edge just below the menu bar
let topY = screen.visibleFrame.maxY - margin
p.setFrameTopLeftPoint(NSPoint(x: x, y: topY))
2026-03-29 00:58:22 -04:00
} else {
p.center()
}
panel = p
}
panel?.orderFront(nil)
}
func hide() {
panel?.orderOut(nil)
}
/// Call when the session fully ends to release the panel
func close() {
panel?.close()
panel = nil
}
}