vOOice/VoiceInk/Views/Recorder/MiniRecorderPanel.swift
2025-08-06 23:15:06 +05:45

84 lines
2.8 KiB
Swift

import SwiftUI
import AppKit
class MiniRecorderPanel: NSPanel {
override var canBecomeKey: Bool { false }
override var canBecomeMain: Bool { false }
init(contentRect: NSRect) {
super.init(
contentRect: contentRect,
styleMask: [.nonactivatingPanel, .fullSizeContentView],
backing: .buffered,
defer: false
)
configurePanel()
}
private func configurePanel() {
isFloatingPanel = true
level = .floating
hidesOnDeactivate = false
collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
isMovable = true
isMovableByWindowBackground = true
backgroundColor = .clear
isOpaque = false
hasShadow = false
titlebarAppearsTransparent = true
titleVisibility = .hidden
standardWindowButton(.closeButton)?.isHidden = true
}
static func calculateWindowMetrics(expanded: Bool = false) -> NSRect {
guard let screen = NSScreen.main else {
return NSRect(x: 0, y: 0, width: expanded ? 160 : 70, height: 34)
}
let compactWidth: CGFloat = 100
let expandedWidth: CGFloat = 160
let width = expanded ? expandedWidth : compactWidth
let height: CGFloat = 34
let padding: CGFloat = 24
let visibleFrame = screen.visibleFrame
let centerX = visibleFrame.midX - 5
let xPosition = centerX - (width / 2)
let yPosition = visibleFrame.minY + padding
return NSRect(
x: xPosition,
y: yPosition,
width: width,
height: height
)
}
func show() {
let metrics = MiniRecorderPanel.calculateWindowMetrics(expanded: false)
setFrame(metrics, display: true)
orderFrontRegardless()
}
func expandWindow(completion: (() -> Void)? = nil) {
let expandedMetrics = MiniRecorderPanel.calculateWindowMetrics(expanded: true)
NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.25
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
animator().setFrame(expandedMetrics, display: true)
}, completionHandler: completion)
}
func collapseWindow(completion: (() -> Void)? = nil) {
let compactMetrics = MiniRecorderPanel.calculateWindowMetrics(expanded: false)
NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.25
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
animator().setFrame(compactMetrics, display: true)
}, completionHandler: completion)
}
func hide(completion: @escaping () -> Void) {
completion()
}
}