Refactor and Rename to CommandNotch
This commit is contained in:
96
Downterm/CommandNotch/Models/NotchViewModel.swift
Normal file
96
Downterm/CommandNotch/Models/NotchViewModel.swift
Normal file
@@ -0,0 +1,96 @@
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
/// Per-screen observable state that drives the notch UI.
|
||||
@MainActor
|
||||
class NotchViewModel: ObservableObject {
|
||||
|
||||
let screenUUID: String
|
||||
|
||||
@Published var notchState: NotchState = .closed
|
||||
@Published var notchSize: CGSize
|
||||
@Published var closedNotchSize: CGSize
|
||||
@Published var isHovering: Bool = false
|
||||
@Published var isCloseTransitionActive: Bool = false
|
||||
|
||||
let terminalManager = TerminalManager.shared
|
||||
|
||||
/// Set by ScreenManager — routes open/close through proper
|
||||
/// window activation so the terminal receives keyboard input.
|
||||
var requestOpen: (() -> Void)?
|
||||
var requestClose: (() -> Void)?
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
@AppStorage(NotchSettings.Keys.openWidth) private var openWidth = NotchSettings.Defaults.openWidth
|
||||
@AppStorage(NotchSettings.Keys.openHeight) private var openHeight = NotchSettings.Defaults.openHeight
|
||||
|
||||
@AppStorage(NotchSettings.Keys.openSpringResponse) private var openSpringResponse = NotchSettings.Defaults.openSpringResponse
|
||||
@AppStorage(NotchSettings.Keys.openSpringDamping) private var openSpringDamping = NotchSettings.Defaults.openSpringDamping
|
||||
@AppStorage(NotchSettings.Keys.closeSpringResponse) private var closeSpringResponse = NotchSettings.Defaults.closeSpringResponse
|
||||
@AppStorage(NotchSettings.Keys.closeSpringDamping) private var closeSpringDamping = NotchSettings.Defaults.closeSpringDamping
|
||||
|
||||
private var closeTransitionTask: Task<Void, Never>?
|
||||
|
||||
var openAnimation: Animation {
|
||||
.spring(response: openSpringResponse, dampingFraction: openSpringDamping)
|
||||
}
|
||||
var closeAnimation: Animation {
|
||||
.spring(response: closeSpringResponse, dampingFraction: closeSpringDamping)
|
||||
}
|
||||
|
||||
init(screenUUID: String) {
|
||||
self.screenUUID = screenUUID
|
||||
let screen = NSScreen.screens.first { $0.displayUUID == screenUUID } ?? NSScreen.main
|
||||
let closed = screen?.closedNotchSize() ?? CGSize(width: 220, height: 32)
|
||||
self.closedNotchSize = closed
|
||||
self.notchSize = closed
|
||||
}
|
||||
|
||||
func open() {
|
||||
notchSize = CGSize(width: openWidth, height: openHeight)
|
||||
notchState = .open
|
||||
}
|
||||
|
||||
func close() {
|
||||
refreshClosedSize()
|
||||
notchSize = closedNotchSize
|
||||
notchState = .closed
|
||||
}
|
||||
|
||||
func refreshClosedSize() {
|
||||
let screen = NSScreen.screens.first { $0.displayUUID == screenUUID } ?? NSScreen.main
|
||||
closedNotchSize = screen?.closedNotchSize() ?? CGSize(width: 220, height: 32)
|
||||
}
|
||||
|
||||
var openNotchSize: CGSize {
|
||||
CGSize(width: openWidth, height: openHeight)
|
||||
}
|
||||
|
||||
var closeInteractionLockDuration: TimeInterval {
|
||||
max(closeSpringResponse + 0.2, 0.35)
|
||||
}
|
||||
|
||||
func beginCloseTransition() {
|
||||
closeTransitionTask?.cancel()
|
||||
isCloseTransitionActive = true
|
||||
|
||||
let delay = closeInteractionLockDuration
|
||||
closeTransitionTask = Task { @MainActor [weak self] in
|
||||
try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
|
||||
guard let self, !Task.isCancelled else { return }
|
||||
self.isCloseTransitionActive = false
|
||||
self.closeTransitionTask = nil
|
||||
}
|
||||
}
|
||||
|
||||
func cancelCloseTransition() {
|
||||
closeTransitionTask?.cancel()
|
||||
closeTransitionTask = nil
|
||||
isCloseTransitionActive = false
|
||||
}
|
||||
|
||||
deinit {
|
||||
closeTransitionTask?.cancel()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user