mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Merge pull request #442 from lugoues/feature/configurable-media-key-steps
Add configurable step sizes for volume and brightness keys
This commit is contained in:
commit
756b6b8a34
4 changed files with 192 additions and 57 deletions
|
|
@ -9051,6 +9051,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Brightness fine step" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Brightness HUD" : {
|
"Brightness HUD" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -9127,6 +9130,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Brightness step" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Built-in" : {
|
"Built-in" : {
|
||||||
"comment" : "A label indicating that an animation is a built-in one.",
|
"comment" : "A label indicating that an animation is a built-in one.",
|
||||||
|
|
@ -16870,6 +16876,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Disabled while external display brightness integration is active." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Disabled while external display integration is active — brightness keys are handled by the external app." : {
|
"Disabled while external display integration is active — brightness keys are handled by the external app." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -16898,6 +16907,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Disabled while external display volume integration is active." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Discrete transitions snap between Green (0–60%), Yellow (60–85%), and Red (85–100%)." : {
|
"Discrete transitions snap between Green (0–60%), Yellow (60–85%), and Red (85–100%)." : {
|
||||||
"comment" : "A description of how the color-coded progress bars work when not using smooth transitions.",
|
"comment" : "A description of how the color-coded progress bars work when not using smooth transitions.",
|
||||||
|
|
@ -17175,6 +17187,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Display Overlay" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Display Style" : {
|
"Display Style" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -35276,6 +35291,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Percent change per key press. Fine step applies when holding Shift+Option." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Percentage" : {
|
"Percentage" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -46124,6 +46142,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Step size" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Stop" : {
|
"Stop" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -51018,6 +51039,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Volume fine step" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Volume HUD" : {
|
"Volume HUD" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -51094,6 +51118,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Volume step" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"W " : {
|
"W " : {
|
||||||
"comment" : "Label for the write data point in the disk usage graph.",
|
"comment" : "Label for the write data point in the disk usage graph.",
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case timer
|
case timer
|
||||||
case calendar
|
case calendar
|
||||||
case hudAndOSD
|
case hudAndOSD
|
||||||
|
case systemControls
|
||||||
case battery
|
case battery
|
||||||
case stats
|
case stats
|
||||||
case clipboard
|
case clipboard
|
||||||
|
|
@ -75,7 +76,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
switch self {
|
switch self {
|
||||||
case .general, .appearance: return .core
|
case .general, .appearance: return .core
|
||||||
case .media, .liveActivities, .lockScreen, .devices: return .mediaAndDisplay
|
case .media, .liveActivities, .lockScreen, .devices: return .mediaAndDisplay
|
||||||
case .hudAndOSD, .battery: return .system
|
case .systemControls, .hudAndOSD, .battery: return .system
|
||||||
case .timer, .calendar, .notes: return .productivity
|
case .timer, .calendar, .notes: return .productivity
|
||||||
case .clipboard, .screenAssistant, .colorPicker, .shelf,
|
case .clipboard, .screenAssistant, .colorPicker, .shelf,
|
||||||
.downloads, .shortcuts: return .utilities
|
.downloads, .shortcuts: return .utilities
|
||||||
|
|
@ -96,7 +97,8 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case .extensions: return String(localized: "Extensions")
|
case .extensions: return String(localized: "Extensions")
|
||||||
case .timer: return String(localized: "Timer")
|
case .timer: return String(localized: "Timer")
|
||||||
case .calendar: return String(localized: "Calendar")
|
case .calendar: return String(localized: "Calendar")
|
||||||
case .hudAndOSD: return String(localized: "Controls")
|
case .hudAndOSD: return String(localized: "Display Overlay")
|
||||||
|
case .systemControls: return String(localized: "Controls")
|
||||||
case .battery: return String(localized: "Battery")
|
case .battery: return String(localized: "Battery")
|
||||||
case .stats: return String(localized: "Stats")
|
case .stats: return String(localized: "Stats")
|
||||||
case .clipboard: return String(localized: "Clipboard")
|
case .clipboard: return String(localized: "Clipboard")
|
||||||
|
|
@ -113,27 +115,28 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
|
|
||||||
var systemImage: String {
|
var systemImage: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .general: return String(localized: "gear")
|
case .general: return "gear"
|
||||||
case .liveActivities: return String(localized: "waveform.path.ecg")
|
case .liveActivities: return "waveform.path.ecg"
|
||||||
case .appearance: return String(localized: "paintpalette")
|
case .appearance: return "paintpalette"
|
||||||
case .lockScreen: return String(localized: "lock.laptopcomputer")
|
case .lockScreen: return "lock.laptopcomputer"
|
||||||
case .media: return String(localized: "play.laptopcomputer")
|
case .media: return "play.laptopcomputer"
|
||||||
case .devices: return String(localized: "headphones")
|
case .devices: return "headphones"
|
||||||
case .extensions: return String(localized: "puzzlepiece.extension")
|
case .extensions: return "puzzlepiece.extension"
|
||||||
case .timer: return String(localized: "timer")
|
case .timer: return "timer"
|
||||||
case .calendar: return String(localized: "calendar")
|
case .calendar: return "calendar"
|
||||||
case .hudAndOSD: return String(localized: "dial.medium.fill")
|
case .hudAndOSD: return "rectangle.inset.filled.on.rectangle"
|
||||||
case .battery: return String(localized: "battery.100.bolt")
|
case .systemControls: return "dial.medium.fill"
|
||||||
case .stats: return String(localized: "chart.xyaxis.line")
|
case .battery: return "battery.100.bolt"
|
||||||
case .clipboard: return String(localized: "clipboard")
|
case .stats: return "chart.xyaxis.line"
|
||||||
case .screenAssistant: return String(localized: "brain.head.profile")
|
case .clipboard: return "clipboard"
|
||||||
case .colorPicker: return String(localized: "eyedropper")
|
case .screenAssistant: return "brain.head.profile"
|
||||||
case .downloads: return String(localized: "square.and.arrow.down")
|
case .colorPicker: return "eyedropper"
|
||||||
case .shelf: return String(localized: "books.vertical")
|
case .downloads: return "square.and.arrow.down"
|
||||||
case .shortcuts: return String(localized: "keyboard")
|
case .shelf: return "books.vertical"
|
||||||
case .notes: return String(localized: "note.text")
|
case .shortcuts: return "keyboard"
|
||||||
case .terminal: return String(localized: "apple.terminal")
|
case .notes: return "note.text"
|
||||||
case .about: return String(localized: "info.circle")
|
case .terminal: return "apple.terminal"
|
||||||
|
case .about: return "info.circle"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +151,8 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case .extensions: return Color(red: 0.557, green: 0.353, blue: 0.957)
|
case .extensions: return Color(red: 0.557, green: 0.353, blue: 0.957)
|
||||||
case .timer: return .red
|
case .timer: return .red
|
||||||
case .calendar: return .cyan
|
case .calendar: return .cyan
|
||||||
case .hudAndOSD: return .indigo
|
case .hudAndOSD: return .orange
|
||||||
|
case .systemControls: return .indigo
|
||||||
case .battery: return Color(red: 0.202, green: 0.783, blue: 0.348, opacity: 1.000)
|
case .battery: return Color(red: 0.202, green: 0.783, blue: 0.348, opacity: 1.000)
|
||||||
case .stats: return .teal
|
case .stats: return .teal
|
||||||
case .clipboard: return .mint
|
case .clipboard: return .mint
|
||||||
|
|
@ -491,6 +495,7 @@ struct SettingsView: View {
|
||||||
.lockScreen,
|
.lockScreen,
|
||||||
.devices,
|
.devices,
|
||||||
// System
|
// System
|
||||||
|
.systemControls,
|
||||||
.hudAndOSD,
|
.hudAndOSD,
|
||||||
.battery,
|
.battery,
|
||||||
// Productivity
|
// Productivity
|
||||||
|
|
@ -745,6 +750,12 @@ struct SettingsView: View {
|
||||||
SettingsSearchEntry(tab: .battery, title: "Full battery style", keywords: ["battery", "style", "compact", "standard"], highlightID: SettingsTab.battery.highlightID(for: "Full battery style")),
|
SettingsSearchEntry(tab: .battery, title: "Full battery style", keywords: ["battery", "style", "compact", "standard"], highlightID: SettingsTab.battery.highlightID(for: "Full battery style")),
|
||||||
SettingsSearchEntry(tab: .battery, title: "Full charge threshold", keywords: ["battery", "threshold", "full"], highlightID: SettingsTab.battery.highlightID(for: "Full charge threshold")),
|
SettingsSearchEntry(tab: .battery, title: "Full charge threshold", keywords: ["battery", "threshold", "full"], highlightID: SettingsTab.battery.highlightID(for: "Full charge threshold")),
|
||||||
|
|
||||||
|
// System Controls
|
||||||
|
SettingsSearchEntry(tab: .systemControls, title: "Step size", keywords: ["brightness", "volume", "step",], highlightID: SettingsTab.systemControls.highlightID(for: "Step size")),
|
||||||
|
SettingsSearchEntry(tab: .systemControls, title: "Third-party DDC app integration", keywords: ["ddc", "third party", "external", "display", "betterdisplay", "lunar"], highlightID: SettingsTab.systemControls.highlightID(for: "Third-party DDC app integration")),
|
||||||
|
SettingsSearchEntry(tab: .systemControls, title: "Third-party DDC provider", keywords: ["provider", "betterdisplay", "lunar", "integration", "refresh detection"], highlightID: SettingsTab.systemControls.highlightID(for: "Third-party DDC provider")),
|
||||||
|
SettingsSearchEntry(tab: .systemControls, title: "Enable external volume control listener", keywords: ["external volume", "ddc volume", "betterdisplay volume", "lunar volume", "disable native volume"], highlightID: SettingsTab.systemControls.highlightID(for: "Enable external volume control listener")),
|
||||||
|
|
||||||
// HUDs
|
// HUDs
|
||||||
SettingsSearchEntry(tab: .devices, title: "Show Bluetooth device connections", keywords: ["bluetooth", "hud"], highlightID: SettingsTab.devices.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: .devices, title: "Use circular battery indicator", keywords: ["battery", "circular"], highlightID: SettingsTab.devices.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")),
|
||||||
|
|
@ -767,9 +778,6 @@ struct SettingsView: View {
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Keyboard Backlight OSD", keywords: ["keyboard", "backlight", "osd"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Keyboard Backlight OSD")),
|
SettingsSearchEntry(tab: .hudAndOSD, title: "Keyboard Backlight OSD", keywords: ["keyboard", "backlight", "osd"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Keyboard Backlight OSD")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Material", keywords: ["material", "frosted", "liquid", "glass", "solid", "osd"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Material")),
|
SettingsSearchEntry(tab: .hudAndOSD, title: "Material", keywords: ["material", "frosted", "liquid", "glass", "solid", "osd"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Material")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Icon & Progress Color", keywords: ["color", "icon", "white", "black", "gray", "osd"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Icon & Progress Color")),
|
SettingsSearchEntry(tab: .hudAndOSD, title: "Icon & Progress Color", keywords: ["color", "icon", "white", "black", "gray", "osd"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Icon & Progress Color")),
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Third-party DDC app integration", keywords: ["ddc", "third party", "external", "display", "betterdisplay", "lunar"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Third-party DDC app integration")),
|
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Third-party DDC provider", keywords: ["provider", "betterdisplay", "lunar", "integration", "refresh detection"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Third-party DDC provider")),
|
|
||||||
SettingsSearchEntry(tab: .hudAndOSD, title: "Enable external volume control listener", keywords: ["external volume", "ddc volume", "betterdisplay volume", "lunar volume", "disable native volume"], highlightID: SettingsTab.hudAndOSD.highlightID(for: "Enable external volume control listener")),
|
|
||||||
|
|
||||||
// Media
|
// Media
|
||||||
SettingsSearchEntry(tab: .media, title: "Music Source", keywords: ["media source", "controller"], highlightID: SettingsTab.media.highlightID(for: "Music Source")),
|
SettingsSearchEntry(tab: .media, title: "Music Source", keywords: ["media source", "controller"], highlightID: SettingsTab.media.highlightID(for: "Music Source")),
|
||||||
|
|
@ -976,6 +984,10 @@ struct SettingsView: View {
|
||||||
SettingsForm(tab: .hudAndOSD) {
|
SettingsForm(tab: .hudAndOSD) {
|
||||||
HUDAndOSDSettingsView()
|
HUDAndOSDSettingsView()
|
||||||
}
|
}
|
||||||
|
case .systemControls:
|
||||||
|
SettingsForm(tab: .systemControls) {
|
||||||
|
SystemControlsView()
|
||||||
|
}
|
||||||
case .battery:
|
case .battery:
|
||||||
SettingsForm(tab: .battery) {
|
SettingsForm(tab: .battery) {
|
||||||
Charge()
|
Charge()
|
||||||
|
|
@ -2192,28 +2204,29 @@ private struct HUDAndOSDSettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third-party display integrations (shared across all HUD variants)
|
|
||||||
ExternalDisplayIntegrationsSection()
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
|
||||||
.background(paneBackgroundColor)
|
|
||||||
.navigationTitle("Controls")
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if #unavailable(macOS 26.0), verticalHUDMaterial == .liquid {
|
if #unavailable(macOS 26.0), verticalHUDMaterial == .liquid {
|
||||||
verticalHUDMaterial = .frosted
|
verticalHUDMaterial = .frosted
|
||||||
verticalHUDLiquidGlassCustomizationMode = .standard
|
verticalHUDLiquidGlassCustomizationMode = .standard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||||
|
.navigationTitle("Display Overlay")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - External Display Integrations Settings Section
|
// MARK: - External Display Integrations Settings Section
|
||||||
|
|
||||||
private struct ExternalDisplayIntegrationsSection: View {
|
private struct SystemControlsView: View {
|
||||||
@Default(.enableThirdPartyDDCIntegration) var enableThirdPartyDDCIntegration
|
@Default(.enableThirdPartyDDCIntegration) var enableThirdPartyDDCIntegration
|
||||||
@Default(.thirdPartyDDCProvider) var thirdPartyDDCProvider
|
@Default(.thirdPartyDDCProvider) var thirdPartyDDCProvider
|
||||||
@Default(.enableExternalVolumeControlListener) var enableExternalVolumeControlListener
|
@Default(.enableExternalVolumeControlListener) var enableExternalVolumeControlListener
|
||||||
|
@Default(.volumeStepPercent) var volumeStepPercent
|
||||||
|
@Default(.volumeFineStepPercent) var volumeFineStepPercent
|
||||||
|
@Default(.brightnessStepPercent) var brightnessStepPercent
|
||||||
|
@Default(.brightnessFineStepPercent) var brightnessFineStepPercent
|
||||||
@ObservedObject private var betterDisplayManager = BetterDisplayManager.shared
|
@ObservedObject private var betterDisplayManager = BetterDisplayManager.shared
|
||||||
@ObservedObject private var lunarManager = LunarManager.shared
|
@ObservedObject private var lunarManager = LunarManager.shared
|
||||||
|
|
||||||
|
|
@ -2284,9 +2297,79 @@ private struct ExternalDisplayIntegrationsSection: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
|
Section {
|
||||||
|
Stepper(value: $volumeStepPercent, in: 1...25) {
|
||||||
|
HStack {
|
||||||
|
Text("Volume step")
|
||||||
|
Spacer()
|
||||||
|
Text("\(volumeStepPercent)%")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.monospacedDigit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.settingsHighlight(id: highlightID("Volume step"))
|
||||||
|
.disabled(enableExternalVolumeControlListener)
|
||||||
|
|
||||||
|
Stepper(value: $volumeFineStepPercent, in: 1...25) {
|
||||||
|
HStack {
|
||||||
|
Text("Volume fine step")
|
||||||
|
Spacer()
|
||||||
|
Text("\(volumeFineStepPercent)%")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.monospacedDigit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.settingsHighlight(id: highlightID("Volume fine step"))
|
||||||
|
.disabled(enableExternalVolumeControlListener)
|
||||||
|
|
||||||
|
if enableExternalVolumeControlListener {
|
||||||
|
Text("Disabled while external display volume integration is active.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
Stepper(value: $brightnessStepPercent, in: 1...25) {
|
||||||
|
HStack {
|
||||||
|
Text("Brightness step")
|
||||||
|
Spacer()
|
||||||
|
Text("\(brightnessStepPercent)%")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.monospacedDigit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.settingsHighlight(id: highlightID("Brightness step"))
|
||||||
|
.disabled(enableThirdPartyDDCIntegration)
|
||||||
|
|
||||||
|
Stepper(value: $brightnessFineStepPercent, in: 1...25) {
|
||||||
|
HStack {
|
||||||
|
Text("Brightness fine step")
|
||||||
|
Spacer()
|
||||||
|
Text("\(brightnessFineStepPercent)%")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.monospacedDigit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.settingsHighlight(id: highlightID("Brightness fine step"))
|
||||||
|
.disabled(enableThirdPartyDDCIntegration)
|
||||||
|
|
||||||
|
if enableThirdPartyDDCIntegration {
|
||||||
|
Text("Disabled while external display brightness integration is active.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Step size")
|
||||||
|
} footer: {
|
||||||
|
Text("Percent change per key press. Fine step applies when holding Shift+Option.")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Toggle("Enable third-party DDC app integration", isOn: $enableThirdPartyDDCIntegration)
|
Toggle("Enable third-party DDC app integration", isOn: $enableThirdPartyDDCIntegration)
|
||||||
.settingsHighlight(id: highlightID("Third-party DDC app integration"))
|
.settingsHighlight(id: highlightID("Third-party DDC app integration"))
|
||||||
|
.onChange(of: enableThirdPartyDDCIntegration) { _, _ in
|
||||||
|
enableExternalVolumeControlListener = false
|
||||||
|
}
|
||||||
|
|
||||||
if enableThirdPartyDDCIntegration {
|
if enableThirdPartyDDCIntegration {
|
||||||
Picker("Provider", selection: $thirdPartyDDCProvider) {
|
Picker("Provider", selection: $thirdPartyDDCProvider) {
|
||||||
|
|
@ -2347,6 +2430,8 @@ private struct ExternalDisplayIntegrationsSection: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||||
|
.navigationTitle("Controls")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2502,6 +2587,7 @@ struct HUD: View {
|
||||||
@Default(.enableBrightnessHUD) var enableBrightnessHUD
|
@Default(.enableBrightnessHUD) var enableBrightnessHUD
|
||||||
@Default(.enableKeyboardBacklightHUD) var enableKeyboardBacklightHUD
|
@Default(.enableKeyboardBacklightHUD) var enableKeyboardBacklightHUD
|
||||||
@Default(.enableThirdPartyDDCIntegration) var enableThirdPartyDDCIntegration
|
@Default(.enableThirdPartyDDCIntegration) var enableThirdPartyDDCIntegration
|
||||||
|
@Default(.enableExternalVolumeControlListener) var enableExternalVolumeControlListener
|
||||||
@Default(.systemHUDSensitivity) var systemHUDSensitivity
|
@Default(.systemHUDSensitivity) var systemHUDSensitivity
|
||||||
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
@ObservedObject private var accessibilityPermission = AccessibilityPermissionStore.shared
|
@ObservedObject private var accessibilityPermission = AccessibilityPermissionStore.shared
|
||||||
|
|
@ -2639,7 +2725,6 @@ struct HUD: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Controls")
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
accessibilityPermission.refreshStatus()
|
accessibilityPermission.refreshStatus()
|
||||||
}
|
}
|
||||||
|
|
@ -2672,7 +2757,7 @@ struct Media: View {
|
||||||
@Default(.autoHideInactiveNotchMediaPlayer) private var autoHideInactiveNotchMediaPlayer
|
@Default(.autoHideInactiveNotchMediaPlayer) private var autoHideInactiveNotchMediaPlayer
|
||||||
@Default(.parallaxEffectIntensity) private var parallaxEffectIntensity
|
@Default(.parallaxEffectIntensity) private var parallaxEffectIntensity
|
||||||
|
|
||||||
|
|
||||||
@ObservedObject private var musicManager = MusicManager.shared
|
@ObservedObject private var musicManager = MusicManager.shared
|
||||||
|
|
||||||
private var isAppleMusicActive: Bool {
|
private var isAppleMusicActive: Bool {
|
||||||
|
|
@ -2831,7 +2916,7 @@ struct Media: View {
|
||||||
}
|
}
|
||||||
.settingsHighlight(id: highlightID("Show live canvas in Dynamic Island"))
|
.settingsHighlight(id: highlightID("Show live canvas in Dynamic Island"))
|
||||||
.help("Replaces the artwork tile with the live canvas when the current app provides one, and reuses that moving canvas for the surrounding lighting effect.")
|
.help("Replaces the artwork tile with the live canvas when the current app provides one, and reuses that moving canvas for the surrounding lighting effect.")
|
||||||
|
|
||||||
//Parallax Effect Intensity to control how much parallax is wanted
|
//Parallax Effect Intensity to control how much parallax is wanted
|
||||||
Slider(value: $parallaxEffectIntensity, in: 0...12, step: 1.0) {
|
Slider(value: $parallaxEffectIntensity, in: 0...12, step: 1.0) {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -2842,7 +2927,7 @@ struct Media: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.settingsHighlight(id: highlightID("Enable album art parallax effect"))
|
.settingsHighlight(id: highlightID("Enable album art parallax effect"))
|
||||||
|
|
||||||
Picker("Sneak Peek Style", selection: $sneakPeekStyles){
|
Picker("Sneak Peek Style", selection: $sneakPeekStyles){
|
||||||
ForEach(SneakPeekStyle.allCases) { style in
|
ForEach(SneakPeekStyle.allCases) { style in
|
||||||
Text(style.rawValue).tag(style)
|
Text(style.rawValue).tag(style)
|
||||||
|
|
@ -3314,7 +3399,7 @@ struct CalendarSettings: View {
|
||||||
.disabled(!lockScreenShowCalendarEvent)
|
.disabled(!lockScreenShowCalendarEvent)
|
||||||
.settingsHighlight(id: highlightID("Show start time after event begins"))
|
.settingsHighlight(id: highlightID("Show start time after event begins"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Third-party Calendar Integration
|
// MARK: - Third-party Calendar Integration
|
||||||
Section {
|
Section {
|
||||||
Defaults.Toggle(key: .enableThirdPartyCalendarApp) {
|
Defaults.Toggle(key: .enableThirdPartyCalendarApp) {
|
||||||
|
|
@ -3326,7 +3411,7 @@ struct CalendarSettings: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.settingsHighlight(id: highlightID("Enable third-party calendar app launch"))
|
.settingsHighlight(id: highlightID("Enable third-party calendar app launch"))
|
||||||
|
|
||||||
if enableThirdPartyCalendarApp {
|
if enableThirdPartyCalendarApp {
|
||||||
Picker("Calendar App", selection: $selectedCalendarApp) {
|
Picker("Calendar App", selection: $selectedCalendarApp) {
|
||||||
ForEach(ThirdPartyCalendarApp.allCases) { app in
|
ForEach(ThirdPartyCalendarApp.allCases) { app in
|
||||||
|
|
@ -3342,7 +3427,7 @@ struct CalendarSettings: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.settingsHighlight(id: highlightID("Calendar App"))
|
.settingsHighlight(id: highlightID("Calendar App"))
|
||||||
|
|
||||||
if selectedCalendarApp == .fantastical {
|
if selectedCalendarApp == .fantastical {
|
||||||
Picker("Default View", selection: $fantasticalDefaultView) {
|
Picker("Default View", selection: $fantasticalDefaultView) {
|
||||||
ForEach(FantasticalViewStyle.allCases, id: \.self) { style in
|
ForEach(FantasticalViewStyle.allCases, id: \.self) { style in
|
||||||
|
|
@ -3806,7 +3891,7 @@ struct Shelf: View {
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
if quickShareProvider == "LocalSend" {
|
if quickShareProvider == "LocalSend" {
|
||||||
LocalSendSettingsSection(highlightID: highlightID)
|
LocalSendSettingsSection(highlightID: highlightID)
|
||||||
}
|
}
|
||||||
|
|
@ -3824,10 +3909,10 @@ struct Shelf: View {
|
||||||
|
|
||||||
private struct LocalSendSettingsSection: View {
|
private struct LocalSendSettingsSection: View {
|
||||||
let highlightID: (String) -> String
|
let highlightID: (String) -> String
|
||||||
|
|
||||||
@Default(.localSendDevicePickerGlassMode) private var glassMode
|
@Default(.localSendDevicePickerGlassMode) private var glassMode
|
||||||
@Default(.localSendDevicePickerLiquidGlassVariant) private var liquidGlassVariant
|
@Default(.localSendDevicePickerLiquidGlassVariant) private var liquidGlassVariant
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
Section {
|
||||||
Picker("Device Picker Style", selection: $glassMode) {
|
Picker("Device Picker Style", selection: $glassMode) {
|
||||||
|
|
@ -3836,7 +3921,7 @@ private struct LocalSendSettingsSection: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pickerStyle(.menu)
|
.pickerStyle(.menu)
|
||||||
|
|
||||||
if glassMode == .customLiquid {
|
if glassMode == .customLiquid {
|
||||||
Picker("Liquid Glass Variant", selection: $liquidGlassVariant) {
|
Picker("Liquid Glass Variant", selection: $liquidGlassVariant) {
|
||||||
ForEach(LiquidGlassVariant.allCases) { variant in
|
ForEach(LiquidGlassVariant.allCases) { variant in
|
||||||
|
|
@ -4497,7 +4582,7 @@ struct Appearance: View {
|
||||||
.tag(MirrorShapeEnum.rectangle)
|
.tag(MirrorShapeEnum.rectangle)
|
||||||
}
|
}
|
||||||
.settingsHighlight(id: highlightID("Mirror shape"))
|
.settingsHighlight(id: highlightID("Mirror shape"))
|
||||||
|
|
||||||
if webcamManager.cameraAvailable {
|
if webcamManager.cameraAvailable {
|
||||||
Picker("Mirror Camera", selection: $selectedCameraID) {
|
Picker("Mirror Camera", selection: $selectedCameraID) {
|
||||||
ForEach(webcamManager.availableCameras, id: \.uniqueID) { device in
|
ForEach(webcamManager.availableCameras, id: \.uniqueID) { device in
|
||||||
|
|
@ -7849,14 +7934,14 @@ struct CustomOSDSettings: View {
|
||||||
previewType = .brightness
|
previewType = .brightness
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
|
|
||||||
Button("Backlight") {
|
Button("Backlight") {
|
||||||
previewType = .backlight
|
previewType = .backlight
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
}
|
}
|
||||||
.controlSize(.small)
|
.controlSize(.small)
|
||||||
|
|
||||||
Slider(value: $previewValue, in: 0...1)
|
Slider(value: $previewValue, in: 0...1)
|
||||||
.frame(width: 160)
|
.frame(width: 160)
|
||||||
}
|
}
|
||||||
|
|
@ -7872,7 +7957,6 @@ struct CustomOSDSettings: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Custom OSD")
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
accessibilityPermission.refreshStatus()
|
accessibilityPermission.refreshStatus()
|
||||||
if #unavailable(macOS 26.0), osdMaterial == .liquid {
|
if #unavailable(macOS 26.0), osdMaterial == .liquid {
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,18 @@ final class SystemChangesObserver: MediaKeyInterceptorDelegate {
|
||||||
"headphones"
|
"headphones"
|
||||||
]
|
]
|
||||||
|
|
||||||
private let standardVolumeStep: Float = 1.0 / 16.0
|
private var standardVolumeStep: Float {
|
||||||
private let standardBrightnessStep: Float = 1.0 / 16.0
|
Float(Defaults[.volumeStepPercent]) / 100.0
|
||||||
private let fineStepDivisor: Float = 4.0
|
}
|
||||||
|
private var fineVolumeStep: Float {
|
||||||
|
Float(Defaults[.volumeFineStepPercent]) / 100.0
|
||||||
|
}
|
||||||
|
private var standardBrightnessStep: Float {
|
||||||
|
Float(Defaults[.brightnessStepPercent]) / 100.0
|
||||||
|
}
|
||||||
|
private var fineBrightnessStep: Float {
|
||||||
|
Float(Defaults[.brightnessFineStepPercent]) / 100.0
|
||||||
|
}
|
||||||
|
|
||||||
private var volumeEnabled = false
|
private var volumeEnabled = false
|
||||||
private var brightnessEnabled = false
|
private var brightnessEnabled = false
|
||||||
|
|
@ -157,7 +166,7 @@ final class SystemChangesObserver: MediaKeyInterceptorDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let baseStep = stepSize(for: step, base: standardVolumeStep)
|
let baseStep = volumeStep(for: step)
|
||||||
let delta = direction == .up ? baseStep : -baseStep
|
let delta = direction == .up ? baseStep : -baseStep
|
||||||
volumeController.adjust(by: delta)
|
volumeController.adjust(by: delta)
|
||||||
volumeFeedbackPlayer.playIfNeeded()
|
volumeFeedbackPlayer.playIfNeeded()
|
||||||
|
|
@ -185,7 +194,7 @@ final class SystemChangesObserver: MediaKeyInterceptorDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let baseStep = stepSize(for: step, base: standardBrightnessStep)
|
let baseStep = brightnessStep(for: step)
|
||||||
let delta = direction == .up ? baseStep : -baseStep
|
let delta = direction == .up ? baseStep : -baseStep
|
||||||
if modifiers.contains(.command) && keyboardBacklightEnabled {
|
if modifiers.contains(.command) && keyboardBacklightEnabled {
|
||||||
keyboardBacklightController.adjust(by: delta)
|
keyboardBacklightController.adjust(by: delta)
|
||||||
|
|
@ -336,12 +345,21 @@ final class SystemChangesObserver: MediaKeyInterceptorDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension SystemChangesObserver {
|
private extension SystemChangesObserver {
|
||||||
func stepSize(for step: MediaKeyStep, base: Float) -> Float {
|
func volumeStep(for step: MediaKeyStep) -> Float {
|
||||||
switch step {
|
switch step {
|
||||||
case .standard:
|
case .standard:
|
||||||
return base
|
return standardVolumeStep
|
||||||
case .fine:
|
case .fine:
|
||||||
return base / fineStepDivisor
|
return fineVolumeStep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func brightnessStep(for step: MediaKeyStep) -> Float {
|
||||||
|
switch step {
|
||||||
|
case .standard:
|
||||||
|
return standardBrightnessStep
|
||||||
|
case .fine:
|
||||||
|
return fineBrightnessStep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1133,6 +1133,12 @@ extension Defaults.Keys {
|
||||||
static let enableKeyboardBacklightHUD = Key<Bool>("enableKeyboardBacklightHUD", default: true)
|
static let enableKeyboardBacklightHUD = Key<Bool>("enableKeyboardBacklightHUD", default: true)
|
||||||
static let systemHUDSensitivity = Key<Int>("systemHUDSensitivity", default: 5)
|
static let systemHUDSensitivity = Key<Int>("systemHUDSensitivity", default: 5)
|
||||||
static let playVolumeChangeFeedback = Key<Bool>("playVolumeChangeFeedback", default: false)
|
static let playVolumeChangeFeedback = Key<Bool>("playVolumeChangeFeedback", default: false)
|
||||||
|
|
||||||
|
// Step sizes for hardware media keys (percent of full range, 1-25)
|
||||||
|
static let volumeStepPercent = Key<Int>("volumeStepPercent", default: 6)
|
||||||
|
static let volumeFineStepPercent = Key<Int>("volumeFineStepPercent", default: 2)
|
||||||
|
static let brightnessStepPercent = Key<Int>("brightnessStepPercent", default: 6)
|
||||||
|
static let brightnessFineStepPercent = Key<Int>("brightnessFineStepPercent", default: 2)
|
||||||
|
|
||||||
// MARK: Custom OSD Window Feature
|
// MARK: Custom OSD Window Feature
|
||||||
static let enableCustomOSD = Key<Bool>("enableCustomOSD", default: false)
|
static let enableCustomOSD = Key<Bool>("enableCustomOSD", default: false)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue