Files
downterm/Downterm/CommandNotch/Components/NotchWindow.swift

79 lines
2.3 KiB
Swift

import AppKit
import SwiftUI
/// Borderless, floating NSPanel that hosts the notch overlay.
/// When the notch is open the window accepts key status so the
/// terminal can receive keyboard input. On resignKey the
/// `onResignKey` closure fires to close the notch.
class NotchWindow: NSPanel {
var isNotchOpen: Bool = false
/// Called when the window loses key status while the notch is open.
var onResignKey: (() -> Void)?
override init(
contentRect: NSRect,
styleMask style: NSWindow.StyleMask,
backing backingStoreType: NSWindow.BackingStoreType,
defer flag: Bool
) {
// Start as a plain borderless utility panel.
// .nonactivatingPanel is NOT included so the window can
// properly accept key status when the notch opens.
super.init(
contentRect: contentRect,
styleMask: [.borderless, .utilityWindow, .nonactivatingPanel],
backing: .buffered,
defer: flag
)
configureWindow()
}
private func configureWindow() {
isOpaque = false
backgroundColor = .clear
isFloatingPanel = true
level = .mainMenu + 3
titleVisibility = .hidden
titlebarAppearsTransparent = true
hasShadow = false
isMovable = false
isMovableByWindowBackground = false
collectionBehavior = [
.canJoinAllSpaces,
.stationary,
.fullScreenAuxiliary,
.ignoresCycle
]
appearance = NSAppearance(named: .darkAqua)
// Accepts mouse events when the app is NOT active so the
// user can click the closed notch to open it.
acceptsMouseMovedEvents = true
}
// MARK: - Key window management
override var canBecomeKey: Bool { isNotchOpen }
override var canBecomeMain: Bool { false }
override func resignKey() {
super.resignKey()
if isNotchOpen {
// Brief async dispatch so the new key window settles first
// avoids closing when we're just transferring focus between
// our own windows (e.g. opening settings).
DispatchQueue.main.async { [weak self] in
guard let self, self.isNotchOpen else { return }
self.onResignKey?()
}
}
}
}