Tahoe PR: HUD cleanup, force volume keys, slider/CustomHUD fixes

- Add KeyboardVolume.mediaForce and UI for always capturing volume/mute keys
- Clean up CustomHUD windows when displays are cleared
- Fix SliderHandler percentage label update (remove tautology check)
- CustomHUD: RunLoop.common timer, drop unused SwiftUI import
- Widen volume control popup for new menu item; en Main.strings

Made-with: Cursor
This commit is contained in:
Abdul Rehman 2026-04-06 13:59:26 +05:00
parent c6a8fe97b6
commit 8a2e7f1e62
11 changed files with 23 additions and 20 deletions

View file

@ -212,4 +212,6 @@ enum KeyboardVolume: Int {
case custom = 1
case both = 2
case disabled = 3
/// Like `media`, but always captures volume/mute keys even when macOS can control the default output device.
case mediaForce = 4
}

View file

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>7175</string>
<string>7176</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>

View file

@ -161,7 +161,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
func checkPermissions(firstAsk: Bool = false) {
let permissionsRequired: Bool = [KeyboardVolume.media.rawValue, KeyboardVolume.both.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardVolume.rawValue)) || [KeyboardBrightness.media.rawValue, KeyboardBrightness.both.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardBrightness.rawValue))
let permissionsRequired: Bool = [KeyboardVolume.media.rawValue, KeyboardVolume.both.rawValue, KeyboardVolume.mediaForce.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardVolume.rawValue)) || [KeyboardBrightness.media.rawValue, KeyboardBrightness.both.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardBrightness.rawValue))
if !MediaKeyTapManager.readPrivileges(prompt: false), permissionsRequired {
MediaKeyTapManager.acquirePrivileges(firstAsk: firstAsk)
}

View file

@ -3,10 +3,6 @@
import Cocoa
#if swift(>=5.3)
import SwiftUI
#endif
// MARK: - Custom HUD Manager
/// Manages custom HUD windows for brightness/volume display on macOS 26+
@ -181,11 +177,13 @@ class CustomHUDManager {
window.alphaValue = 1.0
window.orderFrontRegardless()
// Schedule fade out after 1.5 seconds
fadeTimers[displayID] = Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false) { [weak self, weak window] _ in
// Schedule fade out after 1.5 seconds (common mode so it still fires during nested run-loop work)
let timer = Timer(timeInterval: 1.5, repeats: false) { [weak self, weak window] _ in
guard let window = window else { return }
self?.fadeOut(window: window)
}
fadeTimers[displayID] = timer
RunLoop.main.add(timer, forMode: .common)
}
private func fadeOut(window: NSWindow) {

View file

@ -311,6 +311,9 @@ class DisplayManager {
}
func clearDisplays() {
for display in self.displays {
CustomHUDManager.shared.cleanupDisplay(display.identifier)
}
self.displays = []
}

View file

@ -150,7 +150,7 @@ class MediaKeyTapManager: MediaKeyTapDelegate {
if [KeyboardBrightness.media.rawValue, KeyboardBrightness.both.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardBrightness.rawValue)) {
keys.append(contentsOf: [.brightnessUp, .brightnessDown])
}
if [KeyboardVolume.media.rawValue, KeyboardVolume.both.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardVolume.rawValue)) {
if [KeyboardVolume.media.rawValue, KeyboardVolume.both.rawValue, KeyboardVolume.mediaForce.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardVolume.rawValue)) {
keys.append(contentsOf: [.mute, .volumeUp, .volumeDown])
}
// Remove brightness keys if no external displays are connected, but only if brightness fine control is not active
@ -166,8 +166,8 @@ class MediaKeyTapManager: MediaKeyTapDelegate {
let keysToDelete: [MediaKey] = [.brightnessUp, .brightnessDown]
keys.removeAll { keysToDelete.contains($0) }
}
// Remove volume related keys if audio device is controllable
if let defaultAudioDevice = app.coreAudio.defaultOutputDevice {
// Remove volume related keys if audio device is controllable (skip when user chose force-capture mode)
if prefs.integer(forKey: PrefKey.keyboardVolume.rawValue) != KeyboardVolume.mediaForce.rawValue, let defaultAudioDevice = app.coreAudio.defaultOutputDevice {
let keysToDelete: [MediaKey] = [.volumeUp, .volumeDown, .mute]
if prefs.integer(forKey: PrefKey.multiKeyboardVolume.rawValue) == MultiKeyboardVolume.audioDeviceNameMatching.rawValue {
if DisplayManager.shared.updateAudioControlTargetDisplays(deviceName: defaultAudioDevice.name) == 0 {

View file

@ -360,9 +360,7 @@ class SliderHandler {
slider.floatValue = value
}
}
if self.percentageBox == self.percentageBox {
self.percentageBox?.stringValue = "" + String(Int(value * 100)) + "%"
}
self.percentageBox?.stringValue = "" + String(Int(value * 100)) + "%"
for display in self.displays {
slider.setHighlightItem(display.identifier, value: value)
if self.command == .brightness, let appleDisplay = display as? AppleDisplay {
@ -418,9 +416,7 @@ class SliderHandler {
} else {
slider.setDisplayHighlightItems(false)
}
if self.percentageBox == self.percentageBox {
self.percentageBox?.stringValue = "" + String(Int(value * 100)) + "%"
}
self.percentageBox?.stringValue = "" + String(Int(value * 100)) + "%"
}
}
}

View file

@ -997,13 +997,14 @@
</gridCell>
<gridCell row="6od-Ek-qTW" column="EiG-65-CTv" xPlacement="leading" id="pCd-dP-Y62">
<popUpButton key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9oZ-JM-bxD">
<rect key="frame" x="217" y="187" width="293" height="25"/>
<rect key="frame" x="217" y="187" width="360" height="25"/>
<popUpButtonCell key="cell" type="push" title="Standard keyboard volume and mute keys" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="1sy-Kd-WL5" id="gTf-PQ-2fA">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="message"/>
<menu key="menu" id="LhY-cM-Msq">
<items>
<menuItem title="Standard keyboard volume and mute keys" state="on" id="1sy-Kd-WL5"/>
<menuItem title="Standard keys (force capture)" tag="4" id="fVk-mR-9Xp"/>
<menuItem title="Custom keyboard shortcuts" tag="1" id="4CG-0I-anB"/>
<menuItem title="Both standard and custom shortcuts" tag="2" id="QDG-SA-mRX">
<modifierMask key="keyEquivalentModifierMask"/>

View file

@ -415,5 +415,8 @@
/* Class = "NSButtonCell"; title = "Show percentages"; ObjectID = "ZUu-MR-XwA"; */
"ZUu-MR-XwA.title" = "Show percentages";
/* Class = "NSMenuItem"; title = "Standard keys (force capture)"; ObjectID = "fVk-mR-9Xp"; */
"fVk-mR-9Xp.title" = "Standard keys (force capture)";
/* Class = "NSTextFieldCell"; title = "Combined dimming switchover point:"; ObjectID = "zv8-pZ-OPy"; */
"zv8-pZ-OPy.title" = "Combined dimming switchover point:";

View file

@ -27,7 +27,7 @@ class OnboardingViewController: NSViewController {
// MARK: - Style
private func setPermissionsButtonState() {
let volumePermissions: Bool = [KeyboardVolume.media.rawValue, KeyboardVolume.both.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardVolume.rawValue))
let volumePermissions: Bool = [KeyboardVolume.media.rawValue, KeyboardVolume.both.rawValue, KeyboardVolume.mediaForce.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardVolume.rawValue))
let brigthnessPermissions: Bool = [KeyboardBrightness.media.rawValue, KeyboardBrightness.both.rawValue].contains(prefs.integer(forKey: PrefKey.keyboardBrightness.rawValue))
let permissionsRequired: Bool = volumePermissions || brigthnessPermissions
let enabled: Bool = !MediaKeyTapManager.readPrivileges(prompt: false) && permissionsRequired

View file

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>7175</string>
<string>7176</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSBackgroundOnly</key>