replace interval submenu with a Settings window (frequency)
New SettingsWindowController (programmatic NSWindow, same pattern as
LogWindowController) with a preset popup and a custom-minutes field for
the refresh frequency. Changes apply immediately, per macOS convention.
The ⏱ Refresh Interval submenu, its custom text-field-in-menu view and
the two backing actions are removed in favor of "⚙️ Settings…" (Cmd+,).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
be49480732
commit
98d8cbe2a9
|
|
@ -20,6 +20,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
var versionItem: NSMenuItem!
|
||||
|
||||
var logWindow: LogWindowController?
|
||||
var settingsWindow: SettingsWindowController?
|
||||
var logBuffer: String = ""
|
||||
|
||||
var brewPath: String?
|
||||
|
|
@ -68,40 +69,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let intervalItem = NSMenuItem(title: "⏱ Refresh Interval", action: nil, keyEquivalent: "")
|
||||
let intervalSubmenu = NSMenu()
|
||||
|
||||
let options: [(String, TimeInterval)] = [
|
||||
("1 heure", 3600),
|
||||
("6 heures", 21600),
|
||||
]
|
||||
|
||||
for (label, interval) in options {
|
||||
let item = NSMenuItem(title: label, action: #selector(setRefreshInterval(_:)), keyEquivalent: "")
|
||||
item.representedObject = interval
|
||||
intervalSubmenu.addItem(item)
|
||||
}
|
||||
|
||||
let customMenuItem = NSMenuItem()
|
||||
let customView = NSView(frame: NSRect(x: 0, y: 0, width: 200, height: 30))
|
||||
|
||||
let textField = NSTextField(frame: NSRect(x: 10, y: 4, width: 120, height: 22))
|
||||
textField.placeholderString = "Secondes..."
|
||||
textField.stringValue = "\(Int(refreshInterval))"
|
||||
|
||||
let confirmButton = NSButton(frame: NSRect(x: 138, y: 4, width: 52, height: 22))
|
||||
confirmButton.title = "✓ Set"
|
||||
confirmButton.bezelStyle = .rounded
|
||||
confirmButton.target = self
|
||||
confirmButton.action = #selector(applyCustomInterval(_:))
|
||||
|
||||
customView.addSubview(textField)
|
||||
customView.addSubview(confirmButton)
|
||||
customMenuItem.view = customView
|
||||
intervalSubmenu.addItem(customMenuItem)
|
||||
|
||||
intervalItem.submenu = intervalSubmenu
|
||||
menu.addItem(intervalItem)
|
||||
menu.addItem(NSMenuItem(title: "⚙️ Settings…", action: #selector(showSettings), keyEquivalent: ","))
|
||||
|
||||
versionItem = NSMenuItem(title: "🏷 Brew: ...", action: nil, keyEquivalent: "")
|
||||
menu.addItem(versionItem)
|
||||
|
|
@ -141,20 +109,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
@objc func setRefreshInterval(_ sender: NSMenuItem) {
|
||||
guard let interval = sender.representedObject as? TimeInterval else { return }
|
||||
refreshInterval = interval // ← le set sauvegarde dans UserDefaults
|
||||
restartTimer()
|
||||
}
|
||||
|
||||
@objc func applyCustomInterval(_ sender: NSButton) {
|
||||
guard let view = sender.superview,
|
||||
let textField = view.subviews.first(where: { $0 is NSTextField }) as? NSTextField,
|
||||
let seconds = Int(textField.stringValue), seconds > 0 else { return }
|
||||
|
||||
refreshInterval = TimeInterval(seconds)
|
||||
restartTimer()
|
||||
statusItem.menu?.cancelTracking()
|
||||
@objc func showSettings() {
|
||||
if settingsWindow == nil {
|
||||
settingsWindow = SettingsWindowController(appDelegate: self)
|
||||
}
|
||||
settingsWindow?.syncUI()
|
||||
settingsWindow?.showWindow(nil)
|
||||
// accessory app: without activation the window can open behind others
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
settingsWindow?.window?.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
|
||||
func resolveBrewPath() -> String {
|
||||
|
|
|
|||
127
BrewBar/SettingsWindowController.swift
Normal file
127
BrewBar/SettingsWindowController.swift
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import Cocoa
|
||||
|
||||
class SettingsWindowController: NSWindowController {
|
||||
weak var appDelegate: AppDelegate?
|
||||
|
||||
let presets: [(label: String, interval: TimeInterval)] = [
|
||||
("30 minutes", 1800),
|
||||
("1 hour", 3600),
|
||||
("6 hours", 21600),
|
||||
("12 hours", 43200),
|
||||
("24 hours", 86400),
|
||||
]
|
||||
|
||||
var intervalPopup: NSPopUpButton!
|
||||
var customMinutesField: NSTextField!
|
||||
|
||||
convenience init(appDelegate: AppDelegate) {
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 380, height: 200),
|
||||
styleMask: [.titled, .closable],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
window.title = "BrewBar Settings"
|
||||
// the controller keeps the window alive; without this, closing the
|
||||
// window would deallocate it and reopening would crash
|
||||
window.isReleasedWhenClosed = false
|
||||
|
||||
self.init(window: window)
|
||||
self.appDelegate = appDelegate
|
||||
|
||||
buildUI()
|
||||
syncUI()
|
||||
window.center()
|
||||
}
|
||||
|
||||
// MARK: - Layout
|
||||
|
||||
func buildUI() {
|
||||
guard let contentView = window?.contentView else { return }
|
||||
|
||||
intervalPopup = NSPopUpButton()
|
||||
for preset in presets {
|
||||
intervalPopup.addItem(withTitle: preset.label)
|
||||
}
|
||||
intervalPopup.addItem(withTitle: "Custom")
|
||||
intervalPopup.target = self
|
||||
intervalPopup.action = #selector(presetPicked(_:))
|
||||
|
||||
customMinutesField = NSTextField()
|
||||
customMinutesField.placeholderString = "60"
|
||||
customMinutesField.alignment = .right
|
||||
customMinutesField.target = self
|
||||
customMinutesField.action = #selector(customMinutesEntered(_:))
|
||||
customMinutesField.widthAnchor.constraint(equalToConstant: 60).isActive = true
|
||||
|
||||
let frequencyRow = NSStackView(views: [
|
||||
NSTextField(labelWithString: "Check for updates every:"),
|
||||
intervalPopup,
|
||||
])
|
||||
frequencyRow.orientation = .horizontal
|
||||
|
||||
let customRow = NSStackView(views: [
|
||||
customMinutesField,
|
||||
NSTextField(labelWithString: "minutes"),
|
||||
])
|
||||
customRow.orientation = .horizontal
|
||||
|
||||
let stack = NSStackView(views: [
|
||||
frequencyRow,
|
||||
customRow,
|
||||
])
|
||||
stack.orientation = .vertical
|
||||
stack.alignment = .leading
|
||||
stack.spacing = 12
|
||||
|
||||
stack.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(stack)
|
||||
NSLayoutConstraint.activate([
|
||||
stack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
|
||||
stack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
|
||||
stack.trailingAnchor.constraint(lessThanOrEqualTo: contentView.trailingAnchor, constant: -20),
|
||||
stack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
|
||||
])
|
||||
|
||||
contentView.layoutSubtreeIfNeeded()
|
||||
window?.setContentSize(contentView.fittingSize)
|
||||
}
|
||||
|
||||
/// Push current values into the controls. Called on every open, since
|
||||
/// values may have changed while the window was closed.
|
||||
func syncUI() {
|
||||
guard let appDelegate else { return }
|
||||
|
||||
let interval = appDelegate.refreshInterval
|
||||
if let presetIndex = presets.firstIndex(where: { $0.interval == interval }) {
|
||||
intervalPopup.selectItem(at: presetIndex)
|
||||
} else {
|
||||
intervalPopup.selectItem(at: presets.count) // Custom
|
||||
}
|
||||
customMinutesField.stringValue = "\(Int(interval / 60))"
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc func presetPicked(_ sender: NSPopUpButton) {
|
||||
guard sender.indexOfSelectedItem < presets.count else {
|
||||
window?.makeFirstResponder(customMinutesField)
|
||||
return
|
||||
}
|
||||
applyInterval(presets[sender.indexOfSelectedItem].interval)
|
||||
}
|
||||
|
||||
@objc func customMinutesEntered(_ sender: NSTextField) {
|
||||
guard let minutes = Int(sender.stringValue), minutes > 0 else {
|
||||
syncUI() // reject invalid input by restoring the current value
|
||||
return
|
||||
}
|
||||
applyInterval(TimeInterval(minutes * 60))
|
||||
}
|
||||
|
||||
func applyInterval(_ interval: TimeInterval) {
|
||||
appDelegate?.refreshInterval = interval
|
||||
appDelegate?.restartTimer()
|
||||
syncUI()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue