mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Update Settings and Decoupled Bluetooth
This commit is contained in:
parent
7e9fc2ecb8
commit
97b30858ca
6 changed files with 156 additions and 49 deletions
|
|
@ -164,12 +164,10 @@ class DynamicIslandViewCoordinator: ObservableObject {
|
||||||
resolvedDuration = duration
|
resolvedDuration = duration
|
||||||
}
|
}
|
||||||
sneakPeekDuration = resolvedDuration
|
sneakPeekDuration = resolvedDuration
|
||||||
if type != .music && type != .timer && type != .reminder {
|
let bypassedTypes: [SneakContentType] = [.music, .timer, .reminder, .bluetoothAudio]
|
||||||
// close()
|
if !bypassedTypes.contains(type) && !Defaults[.enableSystemHUD] {
|
||||||
if !Defaults[.enableSystemHUD] {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
withAnimation(.smooth) {
|
withAnimation(.smooth) {
|
||||||
self.sneakPeek.show = status
|
self.sneakPeek.show = status
|
||||||
|
|
|
||||||
|
|
@ -3667,6 +3667,10 @@
|
||||||
"Battery fully charged" : {
|
"Battery fully charged" : {
|
||||||
"comment" : "Battery is full"
|
"comment" : "Battery is full"
|
||||||
},
|
},
|
||||||
|
"Battery Indicator Styling" : {
|
||||||
|
"comment" : "A section header in the Settings app that lists options for customizing the appearance of the battery indicator.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Battery Information" : {
|
"Battery Information" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"ar" : {
|
"ar" : {
|
||||||
|
|
@ -5599,8 +5603,8 @@
|
||||||
"comment" : "An explanatory text that appears when the user tries to enable color-coded color gradients in Segmented progress bars. It instructs the user to switch to Hierarchical or Gradient progress bars to enable these features.",
|
"comment" : "An explanatory text that appears when the user tries to enable color-coded color gradients in Segmented progress bars. It instructs the user to switch to Hierarchical or Gradient progress bars to enable these features.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Color-Coded Progress Bars" : {
|
"Color-coded fills are unavailable in Segmented mode. Switch to Hierarchical or Gradient inside Controls › Dynamic Island to adjust advanced options." : {
|
||||||
"comment" : "A section header that lists the options for color-coded progress bars.",
|
"comment" : "A message explaining that color-coded battery indicators are unavailable in segmented progress bar style. It also includes a suggestion to switch to a more advanced progress bar style to adjust advanced options.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"ColorPicker Disabled" : {
|
"ColorPicker Disabled" : {
|
||||||
|
|
@ -6827,6 +6831,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Devices" : {
|
||||||
|
"comment" : "The title of the \"Devices\" section in the settings menu.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Diagnostics" : {
|
"Diagnostics" : {
|
||||||
"comment" : "A section header in the Lock Screen Settings view, describing diagnostics-related functionality.",
|
"comment" : "A section header in the Lock Screen Settings view, describing diagnostics-related functionality.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
|
@ -7459,6 +7467,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Dynamic Island Progress Bars" : {
|
||||||
|
"comment" : "A section header that lists the options for customizing the appearance of the progress bars displayed by Dynamic Island.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"dynamic.island" : {
|
"dynamic.island" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"fr" : {
|
"fr" : {
|
||||||
|
|
@ -10214,6 +10226,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"HUDs" : {
|
"HUDs" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"ar" : {
|
"ar" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
|
|
@ -10312,9 +10325,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"HUDs & OSD" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Icon & Progress Color" : {
|
"Icon & Progress Color" : {
|
||||||
"comment" : "A picker that lets users choose the color scheme for the icon and progress bar in the OSD.",
|
"comment" : "A picker that lets users choose the color scheme for the icon and progress bar in the OSD.",
|
||||||
|
|
@ -17658,6 +17668,9 @@
|
||||||
"Smooth transitions blend Green (0–60%), Yellow (60–85%), and Red (85–100%) through the entire fill." : {
|
"Smooth transitions blend Green (0–60%), Yellow (60–85%), and Red (85–100%) through the entire fill." : {
|
||||||
"comment" : "A description of how the color-coding feature works.",
|
"comment" : "A description of how the color-coding feature works.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Smooth transitions blend Green (0–60%), Yellow (60–85%), and Red (85–100%) through the entire fill. Adjust gradient behavior from Controls › Dynamic Island." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Sneak peek duration" : {
|
"Sneak peek duration" : {
|
||||||
"comment" : "A label describing the duration of the \"sneak peek\" feature in the calendar settings.",
|
"comment" : "A label describing the duration of the \"sneak peek\" feature in the calendar settings.",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case appearance
|
case appearance
|
||||||
case lockScreen
|
case lockScreen
|
||||||
case media
|
case media
|
||||||
|
case devices
|
||||||
case timer
|
case timer
|
||||||
case calendar
|
case calendar
|
||||||
case hudAndOSD
|
case hudAndOSD
|
||||||
|
|
@ -45,9 +46,10 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case .appearance: return "Appearance"
|
case .appearance: return "Appearance"
|
||||||
case .lockScreen: return "Lock Screen"
|
case .lockScreen: return "Lock Screen"
|
||||||
case .media: return "Media"
|
case .media: return "Media"
|
||||||
|
case .devices: return "Devices"
|
||||||
case .timer: return "Timer"
|
case .timer: return "Timer"
|
||||||
case .calendar: return "Calendar"
|
case .calendar: return "Calendar"
|
||||||
case .hudAndOSD: return "HUDs & OSD"
|
case .hudAndOSD: return "Controls"
|
||||||
case .battery: return "Battery"
|
case .battery: return "Battery"
|
||||||
case .stats: return "Stats"
|
case .stats: return "Stats"
|
||||||
case .clipboard: return "Clipboard"
|
case .clipboard: return "Clipboard"
|
||||||
|
|
@ -67,6 +69,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case .appearance: return "paintpalette"
|
case .appearance: return "paintpalette"
|
||||||
case .lockScreen: return "lock.laptopcomputer"
|
case .lockScreen: return "lock.laptopcomputer"
|
||||||
case .media: return "play.laptopcomputer"
|
case .media: return "play.laptopcomputer"
|
||||||
|
case .devices: return "headphones"
|
||||||
case .timer: return "timer"
|
case .timer: return "timer"
|
||||||
case .calendar: return "calendar"
|
case .calendar: return "calendar"
|
||||||
case .hudAndOSD: return "dial.medium.fill"
|
case .hudAndOSD: return "dial.medium.fill"
|
||||||
|
|
@ -89,6 +92,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case .appearance: return .purple
|
case .appearance: return .purple
|
||||||
case .lockScreen: return .orange
|
case .lockScreen: return .orange
|
||||||
case .media: return .green
|
case .media: return .green
|
||||||
|
case .devices: return .cyan
|
||||||
case .timer: return .red
|
case .timer: return .red
|
||||||
case .calendar: return .cyan
|
case .calendar: return .cyan
|
||||||
case .hudAndOSD: return .indigo
|
case .hudAndOSD: return .indigo
|
||||||
|
|
@ -377,6 +381,7 @@ struct SettingsView: View {
|
||||||
.appearance,
|
.appearance,
|
||||||
.lockScreen,
|
.lockScreen,
|
||||||
.media,
|
.media,
|
||||||
|
.devices,
|
||||||
.timer,
|
.timer,
|
||||||
.calendar,
|
.calendar,
|
||||||
.hudAndOSD,
|
.hudAndOSD,
|
||||||
|
|
@ -591,11 +596,11 @@ struct SettingsView: View {
|
||||||
SettingsSearchEntry(tab: .battery, title: "Play low battery alert sound", keywords: ["low battery", "alert", "sound"], highlightID: SettingsTab.battery.highlightID(for: "Play low battery alert sound")),
|
SettingsSearchEntry(tab: .battery, title: "Play low battery alert sound", keywords: ["low battery", "alert", "sound"], highlightID: SettingsTab.battery.highlightID(for: "Play low battery alert sound")),
|
||||||
|
|
||||||
// HUDs
|
// HUDs
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Show Bluetooth device connections", keywords: ["bluetooth", "hud"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Show Bluetooth device connections")),
|
SettingsSearchEntry(tab: .devices, title: "Show Bluetooth device connections", keywords: ["bluetooth", "hud"], highlightID: SettingsTab.devices.highlightID(for: "Show Bluetooth device connections")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Use circular battery indicator", keywords: ["battery", "circular"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Use circular battery indicator")),
|
SettingsSearchEntry(tab: .devices, title: "Use circular battery indicator", keywords: ["battery", "circular"], highlightID: SettingsTab.devices.highlightID(for: "Use circular battery indicator")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Show battery percentage text in HUD", keywords: ["battery text"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Show battery percentage text in HUD")),
|
SettingsSearchEntry(tab: .devices, title: "Show battery percentage text in HUD", keywords: ["battery text"], highlightID: SettingsTab.devices.highlightID(for: "Show battery percentage text in HUD")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Scroll device name in HUD", keywords: ["marquee", "device name"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Scroll device name in HUD")),
|
SettingsSearchEntry(tab: .devices, title: "Scroll device name in HUD", keywords: ["marquee", "device name"], highlightID: SettingsTab.devices.highlightID(for: "Scroll device name in HUD")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Color-coded battery display", keywords: ["color", "battery"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Color-coded battery display")),
|
SettingsSearchEntry(tab: .devices, title: "Color-coded battery display", keywords: ["color", "battery"], highlightID: SettingsTab.devices.highlightID(for: "Color-coded battery display")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Color-coded volume display", keywords: ["volume", "color"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Color-coded volume display")),
|
SettingsSearchEntry(tab: .hudAndOSD, title: "Color-coded volume display", keywords: ["volume", "color"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Color-coded volume display")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Smooth color transitions", keywords: ["gradient", "smooth"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Smooth color transitions")),
|
SettingsSearchEntry(tab: .hudAndOSD, title: "Smooth color transitions", keywords: ["gradient", "smooth"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Smooth color transitions")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Show percentages beside progress bars", keywords: ["percentages", "progress"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Show percentages beside progress bars")),
|
SettingsSearchEntry(tab: .hudAndOSD, title: "Show percentages beside progress bars", keywords: ["percentages", "progress"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Show percentages beside progress bars")),
|
||||||
|
|
@ -747,6 +752,10 @@ struct SettingsView: View {
|
||||||
SettingsForm(tab: .media) {
|
SettingsForm(tab: .media) {
|
||||||
Media()
|
Media()
|
||||||
}
|
}
|
||||||
|
case .devices:
|
||||||
|
SettingsForm(tab: .devices) {
|
||||||
|
DevicesSettingsView()
|
||||||
|
}
|
||||||
case .timer:
|
case .timer:
|
||||||
SettingsForm(tab: .timer) {
|
SettingsForm(tab: .timer) {
|
||||||
TimerSettings()
|
TimerSettings()
|
||||||
|
|
@ -1572,7 +1581,7 @@ private struct HUDAndOSDSettingsView: View {
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("HUDs & OSD")
|
.navigationTitle("Controls")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1629,6 +1638,63 @@ private struct HUDSelectionCard<Preview: View>: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct DevicesSettingsView: View {
|
||||||
|
@Default(.progressBarStyle) var progressBarStyle
|
||||||
|
|
||||||
|
private func highlightID(_ title: String) -> String {
|
||||||
|
SettingsTab.devices.highlightID(for: title)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var colorCodingDisabled: Bool {
|
||||||
|
progressBarStyle == .segmented
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
Defaults.Toggle("Show Bluetooth device connections", key: .showBluetoothDeviceConnections)
|
||||||
|
.settingsHighlight(id: highlightID("Show Bluetooth device connections"))
|
||||||
|
Defaults.Toggle("Use circular battery indicator", key: .useCircularBluetoothBatteryIndicator)
|
||||||
|
.settingsHighlight(id: highlightID("Use circular battery indicator"))
|
||||||
|
Defaults.Toggle("Show battery percentage text in HUD", key: .showBluetoothBatteryPercentageText)
|
||||||
|
.settingsHighlight(id: highlightID("Show battery percentage text in HUD"))
|
||||||
|
Defaults.Toggle("Scroll device name in HUD", key: .showBluetoothDeviceNameMarquee)
|
||||||
|
.settingsHighlight(id: highlightID("Scroll device name in HUD"))
|
||||||
|
} header: {
|
||||||
|
Text("Bluetooth Audio Devices")
|
||||||
|
} footer: {
|
||||||
|
Text("Displays a HUD notification when Bluetooth audio devices (headphones, AirPods, speakers) connect, showing device name and battery level.")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Defaults.Toggle("Color-coded battery display", key: .useColorCodedBatteryDisplay)
|
||||||
|
.disabled(colorCodingDisabled)
|
||||||
|
.settingsHighlight(id: highlightID("Color-coded battery display"))
|
||||||
|
} header: {
|
||||||
|
Text("Battery Indicator Styling")
|
||||||
|
} footer: {
|
||||||
|
if progressBarStyle == .segmented {
|
||||||
|
Text("Color-coded fills are unavailable in Segmented mode. Switch to Hierarchical or Gradient inside Controls › Dynamic Island to adjust advanced options.")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
} else if Defaults[.useSmoothColorGradient] {
|
||||||
|
Text("Smooth transitions blend Green (0–60%), Yellow (60–85%), and Red (85–100%) through the entire fill. Adjust gradient behavior from Controls › Dynamic Island.")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
} else {
|
||||||
|
Text("Discrete transitions snap between Green (0–60%), Yellow (60–85%), and Red (85–100%).")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.top, 10)
|
||||||
|
.navigationTitle("Devices")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct HUD: View {
|
struct HUD: View {
|
||||||
@EnvironmentObject var vm: DynamicIslandViewModel
|
@EnvironmentObject var vm: DynamicIslandViewModel
|
||||||
@Default(.inlineHUD) var inlineHUD
|
@Default(.inlineHUD) var inlineHUD
|
||||||
|
|
@ -1649,6 +1715,10 @@ struct HUD: View {
|
||||||
accessibilityPermission.isAuthorized
|
accessibilityPermission.isAuthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var colorCodingDisabled: Bool {
|
||||||
|
progressBarStyle == .segmented
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
if !hasAccessibilityPermission {
|
if !hasAccessibilityPermission {
|
||||||
|
|
@ -1680,27 +1750,6 @@ struct HUD: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Defaults.Toggle("Show Bluetooth device connections", key: .showBluetoothDeviceConnections)
|
|
||||||
.settingsHighlight(id: highlightID("Show Bluetooth device connections"))
|
|
||||||
Defaults.Toggle("Use circular battery indicator", key: .useCircularBluetoothBatteryIndicator)
|
|
||||||
.settingsHighlight(id: highlightID("Use circular battery indicator"))
|
|
||||||
Defaults.Toggle("Show battery percentage text in HUD", key: .showBluetoothBatteryPercentageText)
|
|
||||||
.settingsHighlight(id: highlightID("Show battery percentage text in HUD"))
|
|
||||||
Defaults.Toggle("Scroll device name in HUD", key: .showBluetoothDeviceNameMarquee)
|
|
||||||
.settingsHighlight(id: highlightID("Scroll device name in HUD"))
|
|
||||||
} header: {
|
|
||||||
Text("Bluetooth Audio Devices")
|
|
||||||
} footer: {
|
|
||||||
Text("Displays a HUD notification when Bluetooth audio devices (headphones, AirPods, speakers) connect, showing device name and battery level.")
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
|
||||||
let colorCodingDisabled = progressBarStyle == .segmented
|
|
||||||
Defaults.Toggle("Color-coded battery display", key: .useColorCodedBatteryDisplay)
|
|
||||||
.disabled(colorCodingDisabled)
|
|
||||||
.settingsHighlight(id: highlightID("Color-coded battery display"))
|
|
||||||
Defaults.Toggle("Color-coded volume display", key: .useColorCodedVolumeDisplay)
|
Defaults.Toggle("Color-coded volume display", key: .useColorCodedVolumeDisplay)
|
||||||
.disabled(colorCodingDisabled)
|
.disabled(colorCodingDisabled)
|
||||||
.settingsHighlight(id: highlightID("Color-coded volume display"))
|
.settingsHighlight(id: highlightID("Color-coded volume display"))
|
||||||
|
|
@ -1713,9 +1762,9 @@ struct HUD: View {
|
||||||
Defaults.Toggle("Show percentages beside progress bars", key: .showProgressPercentages)
|
Defaults.Toggle("Show percentages beside progress bars", key: .showProgressPercentages)
|
||||||
.settingsHighlight(id: highlightID("Show percentages beside progress bars"))
|
.settingsHighlight(id: highlightID("Show percentages beside progress bars"))
|
||||||
} header: {
|
} header: {
|
||||||
Text("Color-Coded Progress Bars")
|
Text("Dynamic Island Progress Bars")
|
||||||
} footer: {
|
} footer: {
|
||||||
if progressBarStyle == .segmented {
|
if colorCodingDisabled {
|
||||||
Text("Color-coded fills and smooth gradients are unavailable in Segmented mode. Switch to Hierarchical or Gradient to adjust these options.")
|
Text("Color-coded fills and smooth gradients are unavailable in Segmented mode. Switch to Hierarchical or Gradient to adjust these options.")
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
|
|
@ -1765,7 +1814,7 @@ struct HUD: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("HUDs")
|
.navigationTitle("Controls")
|
||||||
.onAppear {
|
.onAppear {
|
||||||
accessibilityPermission.refreshStatus()
|
accessibilityPermission.refreshStatus()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1456,6 +1456,14 @@ class BluetoothAudioManager: ObservableObject {
|
||||||
func refreshConnectedDeviceBatteries() {
|
func refreshConnectedDeviceBatteries() {
|
||||||
refreshBatteryLevelsForConnectedDevices()
|
refreshBatteryLevelsForConnectedDevices()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func activeDeviceIconSymbol() -> String? {
|
||||||
|
if let prioritizedDevice = connectedDevices.last ?? lastConnectedDevice {
|
||||||
|
return prioritizedDevice.deviceType.sfSymbol
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - CoreBluetooth Battery Reader
|
// MARK: - CoreBluetooth Battery Reader
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,8 @@ final class SystemChangesObserver: MediaKeyInterceptorDelegate {
|
||||||
// Send to Vertical HUD if enabled
|
// Send to Vertical HUD if enabled
|
||||||
if Defaults[.enableVerticalHUD] {
|
if Defaults[.enableVerticalHUD] {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
VerticalHUDWindowManager.shared.show(type: .volume, value: CGFloat(value))
|
let icon = BluetoothAudioManager.shared.activeDeviceIconSymbol() ?? ""
|
||||||
|
VerticalHUDWindowManager.shared.show(type: .volume, value: CGFloat(value), icon: icon)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,12 @@ final class VerticalHUDWindowManager {
|
||||||
Defaults.publisher(.verticalHUDWidth, options: []).sink(receiveValue: updateBlock).store(in: &cancellables)
|
Defaults.publisher(.verticalHUDWidth, options: []).sink(receiveValue: updateBlock).store(in: &cancellables)
|
||||||
Defaults.publisher(.verticalHUDPadding, options: []).sink(receiveValue: updateBlock).store(in: &cancellables)
|
Defaults.publisher(.verticalHUDPadding, options: []).sink(receiveValue: updateBlock).store(in: &cancellables)
|
||||||
Defaults.publisher(.verticalHUDInteractive, options: []).sink(receiveValue: updateBlock).store(in: &cancellables)
|
Defaults.publisher(.verticalHUDInteractive, options: []).sink(receiveValue: updateBlock).store(in: &cancellables)
|
||||||
|
|
||||||
|
Defaults.publisher(.enableVerticalHUD, options: []).sink { [weak self] change in
|
||||||
|
Task { @MainActor [weak self] in
|
||||||
|
self?.handleEnablementChange(change.newValue)
|
||||||
|
}
|
||||||
|
}.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateWindowLayout() {
|
private func updateWindowLayout() {
|
||||||
|
|
@ -53,7 +59,6 @@ final class VerticalHUDWindowManager {
|
||||||
let width = Defaults[.verticalHUDWidth]
|
let width = Defaults[.verticalHUDWidth]
|
||||||
let height = Defaults[.verticalHUDHeight]
|
let height = Defaults[.verticalHUDHeight]
|
||||||
let padding = Defaults[.verticalHUDPadding]
|
let padding = Defaults[.verticalHUDPadding]
|
||||||
let interactive = Defaults[.verticalHUDInteractive]
|
|
||||||
|
|
||||||
// Add padding for shadow and elastic stretch
|
// Add padding for shadow and elastic stretch
|
||||||
// We use 120px total padding (60px per side) to absolutely guarantee no clipping.
|
// We use 120px total padding (60px per side) to absolutely guarantee no clipping.
|
||||||
|
|
@ -77,10 +82,42 @@ final class VerticalHUDWindowManager {
|
||||||
let newFrame = NSRect(x: x, y: y, width: totalWidth, height: totalHeight)
|
let newFrame = NSRect(x: x, y: y, width: totalWidth, height: totalHeight)
|
||||||
|
|
||||||
window.nsWindow.setFrame(newFrame, display: true, animate: true)
|
window.nsWindow.setFrame(newFrame, display: true, animate: true)
|
||||||
window.nsWindow.ignoresMouseEvents = !interactive
|
applyInteractivity(window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func handleEnablementChange(_ isEnabled: Bool) {
|
||||||
|
if isEnabled {
|
||||||
|
updateWindowLayout()
|
||||||
|
} else {
|
||||||
|
teardownWindows()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func applyInteractivity(_ window: OSDWindow, visibleOverride: Bool? = nil) {
|
||||||
|
let isVisible = visibleOverride ?? (window.nsWindow.alphaValue > 0.01)
|
||||||
|
let shouldAllowInteraction = Defaults[.enableVerticalHUD] && Defaults[.verticalHUDInteractive] && isVisible
|
||||||
|
window.nsWindow.ignoresMouseEvents = !shouldAllowInteraction
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateWindowInteractivity() {
|
||||||
|
for window in windows.values {
|
||||||
|
applyInteractivity(window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func teardownWindows() {
|
||||||
|
hideWorkItem?.cancel()
|
||||||
|
hideWorkItem = nil
|
||||||
|
|
||||||
|
for window in windows.values {
|
||||||
|
window.nsWindow.orderOut(nil)
|
||||||
|
window.nsWindow.ignoresMouseEvents = true
|
||||||
|
}
|
||||||
|
|
||||||
|
windows.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
func show(type: SneakContentType, value: CGFloat, icon: String = "") {
|
func show(type: SneakContentType, value: CGFloat, icon: String = "") {
|
||||||
guard Defaults[.enableVerticalHUD] else { return }
|
guard Defaults[.enableVerticalHUD] else { return }
|
||||||
|
|
||||||
|
|
@ -94,6 +131,7 @@ final class VerticalHUDWindowManager {
|
||||||
|
|
||||||
// Ensure visible
|
// Ensure visible
|
||||||
windowContext.nsWindow.orderFrontRegardless()
|
windowContext.nsWindow.orderFrontRegardless()
|
||||||
|
applyInteractivity(windowContext, visibleOverride: true)
|
||||||
|
|
||||||
NSAnimationContext.runAnimationGroup { context in
|
NSAnimationContext.runAnimationGroup { context in
|
||||||
context.duration = animationDuration
|
context.duration = animationDuration
|
||||||
|
|
@ -161,8 +199,8 @@ final class VerticalHUDWindowManager {
|
||||||
win.contentView = hostingView
|
win.contentView = hostingView
|
||||||
win.alphaValue = 0
|
win.alphaValue = 0
|
||||||
|
|
||||||
// Allow mouse interaction if enabled
|
// Default to ignoring events until explicitly shown
|
||||||
win.ignoresMouseEvents = !Defaults[.verticalHUDInteractive]
|
win.ignoresMouseEvents = true
|
||||||
|
|
||||||
SkyLightOperator.shared.delegateWindow(win)
|
SkyLightOperator.shared.delegateWindow(win)
|
||||||
|
|
||||||
|
|
@ -197,8 +235,8 @@ final class VerticalHUDWindowManager {
|
||||||
NSAnimationContext.runAnimationGroup { context in
|
NSAnimationContext.runAnimationGroup { context in
|
||||||
context.duration = animationDuration
|
context.duration = animationDuration
|
||||||
window.nsWindow.animator().alphaValue = 0
|
window.nsWindow.animator().alphaValue = 0
|
||||||
} completionHandler: {
|
} completionHandler: { [weak self] in
|
||||||
// Keep window around
|
self?.applyInteractivity(window, visibleOverride: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue