pommedoro/Sources/Pommedoro/SettingsMenu.swift

150 lines
5.1 KiB
Swift

import AppKit
// MARK: - Slider Menu Item View
class SliderMenuItemView: NSView {
let slider: NSSlider
let valueLabel: NSTextField
private let titleLabel: NSTextField
var onChanged: ((Double) -> Void)?
init(title: String, value: Double, minValue: Double, maxValue: Double, width: CGFloat = 280) {
titleLabel = NSTextField(labelWithString: title)
slider = NSSlider(value: value, minValue: minValue, maxValue: maxValue,
target: nil, action: nil)
valueLabel = NSTextField(labelWithString: String(format: "%.2fx", value))
super.init(frame: NSRect(x: 0, y: 0, width: width, height: 50))
titleLabel.font = NSFont.systemFont(ofSize: 12, weight: .medium)
titleLabel.textColor = .labelColor
titleLabel.frame = NSRect(x: 16, y: 28, width: width - 32, height: 16)
addSubview(titleLabel)
let sliderW = width - 80
slider.frame = NSRect(x: 16, y: 4, width: sliderW, height: 20)
slider.target = self
slider.action = #selector(sliderMoved)
slider.isContinuous = true
addSubview(slider)
valueLabel.font = NSFont.monospacedSystemFont(ofSize: 11, weight: .regular)
valueLabel.textColor = .secondaryLabelColor
valueLabel.alignment = .right
valueLabel.frame = NSRect(x: sliderW + 20, y: 4, width: 44, height: 20)
addSubview(valueLabel)
}
required init?(coder: NSCoder) { fatalError() }
@objc private func sliderMoved() {
let v = slider.doubleValue
valueLabel.stringValue = String(format: "%.2fx", v)
onChanged?(v)
}
func update(value: Double) {
slider.doubleValue = value
valueLabel.stringValue = String(format: "%.2fx", value)
}
}
// MARK: - Color Menu Item View
class ColorMenuItemView: NSView {
let colorWell: NSColorWell
private let titleLabel: NSTextField
var onChanged: ((NSColor) -> Void)?
init(title: String, color: NSColor, width: CGFloat = 280) {
titleLabel = NSTextField(labelWithString: title)
colorWell = NSColorWell(frame: .zero)
super.init(frame: NSRect(x: 0, y: 0, width: width, height: 34))
titleLabel.font = NSFont.systemFont(ofSize: 12, weight: .medium)
titleLabel.textColor = .labelColor
titleLabel.frame = NSRect(x: 16, y: 7, width: 120, height: 16)
addSubview(titleLabel)
colorWell.frame = NSRect(x: 140, y: 4, width: 40, height: 26)
colorWell.color = color
colorWell.target = self
colorWell.action = #selector(colorPicked)
addSubview(colorWell)
}
required init?(coder: NSCoder) { fatalError() }
@objc private func colorPicked() {
onChanged?(colorWell.color)
}
func update(color: NSColor) {
colorWell.color = color
}
}
// MARK: - Settings Menu Builder
class SettingsMenuBuilder {
private var intensityView: SliderMenuItemView?
private var durationView: SliderMenuItemView?
private var colorView: ColorMenuItemView?
/// Build settings items and append them to the given menu as a submenu
func buildSettingsSubmenu() -> NSMenuItem {
let submenu = NSMenu(title: "Settings")
// Color
let cv = ColorMenuItemView(title: "Gradient Color:", color: Settings.shared.gradientColor)
cv.onChanged = { color in Settings.shared.gradientColor = color }
colorView = cv
let colorItem = NSMenuItem()
colorItem.view = cv
submenu.addItem(colorItem)
submenu.addItem(NSMenuItem.separator())
// Intensity
let iv = SliderMenuItemView(title: "Intensity", value: Settings.shared.intensityScale,
minValue: 0.25, maxValue: 2.0)
iv.onChanged = { val in Settings.shared.intensityScale = val }
intensityView = iv
let intensityItem = NSMenuItem()
intensityItem.view = iv
submenu.addItem(intensityItem)
submenu.addItem(NSMenuItem.separator())
// Blink Duration
let dv = SliderMenuItemView(title: "Blink Duration", value: Settings.shared.blinkDurationScale,
minValue: 0.25, maxValue: 3.0)
dv.onChanged = { val in Settings.shared.blinkDurationScale = val }
durationView = dv
let durationItem = NSMenuItem()
durationItem.view = dv
submenu.addItem(durationItem)
submenu.addItem(NSMenuItem.separator())
// Reset
let resetItem = NSMenuItem(title: "Reset to Defaults", action: #selector(resetDefaults), keyEquivalent: "")
resetItem.target = self
submenu.addItem(resetItem)
let parentItem = NSMenuItem(title: "Settings", action: nil, keyEquivalent: ",")
parentItem.submenu = submenu
return parentItem
}
func refreshValues() {
let s = Settings.shared
intensityView?.update(value: s.intensityScale)
durationView?.update(value: s.blinkDurationScale)
colorView?.update(color: s.gradientColor)
}
@objc private func resetDefaults() {
Settings.shared.resetToDefaults()
refreshValues()
}
}