Atoll/DynamicIsland/components/Notch/DynamicIslandHeader.swift
2025-10-02 23:18:58 +05:30

212 lines
9.4 KiB
Swift

//
// DynamicIslandHeader.swift
// DynamicIsland
//
// Created by Harsh Vardhan Goswami on 04/08/24.
//
import Defaults
import SwiftUI
struct DynamicIslandHeader: View {
@EnvironmentObject var vm: DynamicIslandViewModel
@EnvironmentObject var webcamManager: WebcamManager
@ObservedObject var batteryModel = BatteryStatusViewModel.shared
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
@ObservedObject var clipboardManager = ClipboardManager.shared
@ObservedObject var tvm = TrayDrop.shared
@State private var showClipboardPopover = false
@State private var showColorPickerPopover = false
var body: some View {
HStack(spacing: 0) {
HStack {
if (!tvm.isEmpty || coordinator.alwaysShowTabs) && Defaults[.dynamicShelf] {
TabSelectionView()
} else if vm.notchState == .open {
EmptyView()
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.opacity(vm.notchState == .closed ? 0 : 1)
.blur(radius: vm.notchState == .closed ? 20 : 0)
.animation(.smooth.delay(0.1), value: vm.notchState)
.zIndex(2)
if vm.notchState == .open {
Rectangle()
.fill(NSScreen.screens
.first(where: { $0.localizedName == coordinator.selectedScreen })?.safeAreaInsets.top ?? 0 > 0 ? .black : .clear)
.frame(width: vm.closedNotchSize.width)
.mask {
NotchShape()
}
}
HStack(spacing: 4) {
if vm.notchState == .open {
if Defaults[.showMirror] {
Button(action: {
vm.toggleCameraPreview()
}) {
Capsule()
.fill(.black)
.frame(width: 30, height: 30)
.overlay {
Image(systemName: "web.camera")
.foregroundColor(.white)
.padding()
.imageScale(.medium)
}
}
.buttonStyle(PlainButtonStyle())
}
if Defaults[.enableClipboardManager] && Defaults[.showClipboardIcon] {
Button(action: {
// Switch behavior based on display mode
switch Defaults[.clipboardDisplayMode] {
case .panel:
ClipboardPanelManager.shared.toggleClipboardPanel()
case .popover:
showClipboardPopover.toggle()
}
}) {
Capsule()
.fill(.black)
.frame(width: 30, height: 30)
.overlay {
Image(systemName: "doc.on.clipboard")
.foregroundColor(.white)
.padding()
.imageScale(.medium)
}
}
.buttonStyle(PlainButtonStyle())
.popover(isPresented: $showClipboardPopover, arrowEdge: .bottom) {
ClipboardPopover()
}
.onChange(of: showClipboardPopover) { isActive in
vm.isClipboardPopoverActive = isActive
// If popover was closed, trigger a hover recheck
if !isActive {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
vm.shouldRecheckHover.toggle()
}
}
}
.onAppear {
if Defaults[.enableClipboardManager] && !clipboardManager.isMonitoring {
clipboardManager.startMonitoring()
}
}
}
// ColorPicker button
if Defaults[.enableColorPickerFeature] {
Button(action: {
switch Defaults[.colorPickerDisplayMode] {
case .panel:
ColorPickerPanelManager.shared.toggleColorPickerPanel()
case .popover:
showColorPickerPopover.toggle()
}
}) {
Capsule()
.fill(.black)
.frame(width: 30, height: 30)
.overlay {
Image(systemName: "eyedropper")
.foregroundColor(.white)
.padding()
.imageScale(.medium)
}
}
.buttonStyle(PlainButtonStyle())
.popover(isPresented: $showColorPickerPopover, arrowEdge: .bottom) {
ColorPickerPopover()
}
.onChange(of: showColorPickerPopover) { isActive in
vm.isColorPickerPopoverActive = isActive
// If popover was closed, trigger a hover recheck
if !isActive {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
vm.shouldRecheckHover.toggle()
}
}
}
}
if Defaults[.settingsIconInNotch] {
Button(action: {
SettingsWindowController.shared.showWindow()
}) {
Capsule()
.fill(.black)
.frame(width: 30, height: 30)
.overlay {
Image(systemName: "gear")
.foregroundColor(.white)
.padding()
.imageScale(.medium)
}
}
.buttonStyle(PlainButtonStyle())
}
// Screen Recording Indicator
if Defaults[.enableScreenRecordingDetection] && Defaults[.showRecordingIndicator] {
RecordingIndicator()
.frame(width: 30, height: 30) // Same size as other header elements
}
if Defaults[.showBatteryIndicator] {
DynamicIslandBatteryView(
batteryWidth: 30,
isCharging: batteryModel.isCharging,
isInLowPowerMode: batteryModel.isInLowPowerMode,
isPluggedIn: batteryModel.isPluggedIn,
levelBattery: batteryModel.levelBattery,
maxCapacity: batteryModel.maxCapacity,
timeToFullCharge: batteryModel.timeToFullCharge,
isForNotification: false
)
}
}
}
.font(.system(.headline, design: .rounded))
.frame(maxWidth: .infinity, alignment: .trailing)
.opacity(vm.notchState == .closed ? 0 : 1)
.blur(radius: vm.notchState == .closed ? 20 : 0)
.animation(.smooth.delay(0.1), value: vm.notchState)
.zIndex(2)
}
.foregroundColor(.gray)
.environmentObject(vm)
.onChange(of: coordinator.shouldToggleClipboardPopover) { _ in
// Only toggle if clipboard is enabled
if Defaults[.enableClipboardManager] {
switch Defaults[.clipboardDisplayMode] {
case .panel:
ClipboardPanelManager.shared.toggleClipboardPanel()
case .popover:
showClipboardPopover.toggle()
}
}
}
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("ToggleClipboardPopover"))) { _ in
// Handle keyboard shortcut for popover mode
if Defaults[.enableClipboardManager] && Defaults[.clipboardDisplayMode] == .popover {
showClipboardPopover.toggle()
}
}
}
}
#Preview {
DynamicIslandHeader()
.environmentObject(DynamicIslandViewModel())
.environmentObject(WebcamManager.shared)
}