feat: Add numerical input and arrow key support for sliders

- Converted menu bar percentage labels into editable text fields.
- Implemented `NSTextFieldDelegate` to handle manual typing with bound clamping (0-100).
- Handled invalid string parsing with graceful fallbacks to the current slider state.
- Added a native rounded bezel effect when the field is actively focused.
- Overrode `control(_:textView:doCommandBy:)` to capture Up/Down arrow presses for live value manipulation.
- Added a `Shift` modifier for arrow keys to toggle between 1% and 10% step increments.
- Ensured all manual inputs immediately sync physical hardware brightness, volume, and contrast via DDC.
This commit is contained in:
Karan Shah 2026-04-18 15:37:16 +05:30
parent fc19ce2bd2
commit 26a1900d7e

View file

@ -3,7 +3,7 @@
import Cocoa
import os.log
class SliderHandler {
class SliderHandler: NSObject, NSTextFieldDelegate {
var slider: MCSlider?
var view: NSView?
var percentageBox: NSTextField?
@ -212,6 +212,7 @@ class SliderHandler {
public init(display: Display?, command: Command, title: String = "", position _: Int = 0) {
self.command = command
self.title = title
super.init()
let slider = SliderHandler.MCSlider(value: 0, minValue: 0, maxValue: 1, target: self, action: #selector(SliderHandler.valueChanged))
let showPercent = prefs.bool(forKey: PrefKey.enableSliderPercent.rawValue)
slider.isEnabled = true
@ -277,11 +278,12 @@ class SliderHandler {
func setupPercentageBox(_ percentageBox: NSTextField) {
percentageBox.font = NSFont.systemFont(ofSize: 12)
percentageBox.isEditable = false
percentageBox.isEditable = true
percentageBox.isBordered = false
percentageBox.drawsBackground = false
percentageBox.alignment = .right
percentageBox.alphaValue = 0.7
percentageBox.delegate = self
}
func valueChangedOtherDisplay(otherDisplay: OtherDisplay, value: Float) {
@ -383,4 +385,47 @@ class SliderHandler {
}
}
}
// MARK: - NSTextFieldDelegate
func controlTextDidBeginEditing(_: Notification) {
self.percentageBox?.isBordered = true
self.percentageBox?.alphaValue = 1.0
}
func controlTextDidEndEditing(_ obj: Notification) {
self.percentageBox?.isBordered = false
self.percentageBox?.alphaValue = 0.7
guard let textField = obj.object as? NSTextField else { return }
let stringValue = textField.stringValue.replacingOccurrences(of: "%", with: "").trimmingCharacters(in: .whitespaces)
if let value = Float(stringValue), let slider = self.slider {
slider.floatValue = max(0, min(100, value)) / 100
self.valueChanged(slider: slider)
} else if let slider = self.slider {
self.setValue(slider.floatValue)
}
}
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
guard commandSelector == #selector(NSResponder.moveUp(_:)) || commandSelector == #selector(NSResponder.moveDown(_:)) else {
return false
}
let stringValue = textView.string.replacingOccurrences(of: "%", with: "").trimmingCharacters(in: .whitespaces)
var currentValue = Float(stringValue) ?? (self.slider?.floatValue ?? 0) * 100
let step: Float = NSEvent.modifierFlags.contains(.shift) ? 10 : 1
currentValue += commandSelector == #selector(NSResponder.moveUp(_:)) ? step : -step
let normalizedValue = max(0, min(100, currentValue))
textView.string = "\(Int(normalizedValue))%"
if let slider = self.slider {
slider.floatValue = normalizedValue / 100
self.valueChanged(slider: slider)
}
return true
}
}