add optional outdated count next to the menu bar icon

Off by default. The status item button shows both image and title
(imagePosition = .imageLeft), so the icon stays visible with the count
beside it. Cleared when up to date or when the setting is off; toggling
applies immediately from the cached list without refetching.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
maxsoch 2026-07-06 22:08:51 +02:00
parent 32d81423ab
commit 29b9412a34
2 changed files with 24 additions and 0 deletions

View file

@ -49,6 +49,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
Settings.registerDefaults() Settings.registerDefaults()
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem.button?.imagePosition = .imageLeft // icon stays visible next to the count
setMenuBarIcon("brewbar-uptodate") setMenuBarIcon("brewbar-uptodate")
let menu = NSMenu() let menu = NSMenu()
@ -268,6 +269,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self.updateOutdatedMenu(with: lines) self.updateOutdatedMenu(with: lines)
self.updateStatus(count: lines.count) self.updateStatus(count: lines.count)
self.updateMenuBarCount()
self.notifyIfNeeded(outdated: lines) self.notifyIfNeeded(outdated: lines)
if lines.isEmpty { if lines.isEmpty {
self.setMenuBarIcon("brewbar-uptodate") self.setMenuBarIcon("brewbar-uptodate")
@ -330,6 +332,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
} }
func updateMenuBarCount() {
if Settings.showCountInMenuBar, !cachedOutdated.isEmpty {
statusItem.button?.title = " \(cachedOutdated.count)"
} else {
statusItem.button?.title = ""
}
}
func updateStatus(count: Int) { func updateStatus(count: Int) {
if count == 0 { if count == 0 {
statusMenuItem.title = "✅ All up to date" statusMenuItem.title = "✅ All up to date"

View file

@ -20,6 +20,7 @@ class SettingsWindowController: NSWindowController {
var autoUpgradeFormulaeCheckbox: NSButton! var autoUpgradeFormulaeCheckbox: NSButton!
var autoUpgradeCasksCheckbox: NSButton! var autoUpgradeCasksCheckbox: NSButton!
var notifyCheckbox: NSButton! var notifyCheckbox: NSButton!
var showCountCheckbox: NSButton!
convenience init(appDelegate: AppDelegate) { convenience init(appDelegate: AppDelegate) {
let window = NSWindow( let window = NSWindow(
@ -116,6 +117,12 @@ class SettingsWindowController: NSWindowController {
action: #selector(toggleNotify(_:)) action: #selector(toggleNotify(_:))
) )
showCountCheckbox = NSButton(
checkboxWithTitle: "Show outdated count in the menu bar",
target: self,
action: #selector(toggleShowCount(_:))
)
let stack = NSStackView(views: [ let stack = NSStackView(views: [
frequencyRow, frequencyRow,
customRow, customRow,
@ -126,6 +133,7 @@ class SettingsWindowController: NSWindowController {
caskWarningRow, caskWarningRow,
separator(), separator(),
notifyCheckbox, notifyCheckbox,
showCountCheckbox,
launchAtLoginCheckbox, launchAtLoginCheckbox,
]) ])
stack.orientation = .vertical stack.orientation = .vertical
@ -165,6 +173,7 @@ class SettingsWindowController: NSWindowController {
autoUpgradeFormulaeCheckbox.state = Settings.autoUpgradeFormulae ? .on : .off autoUpgradeFormulaeCheckbox.state = Settings.autoUpgradeFormulae ? .on : .off
autoUpgradeCasksCheckbox.state = Settings.autoUpgradeCasks ? .on : .off autoUpgradeCasksCheckbox.state = Settings.autoUpgradeCasks ? .on : .off
notifyCheckbox.state = Settings.notifyOnNewUpdates ? .on : .off notifyCheckbox.state = Settings.notifyOnNewUpdates ? .on : .off
showCountCheckbox.state = Settings.showCountInMenuBar ? .on : .off
} }
func separator() -> NSBox { func separator() -> NSBox {
@ -225,6 +234,11 @@ class SettingsWindowController: NSWindowController {
} }
} }
@objc func toggleShowCount(_ sender: NSButton) {
Settings.showCountInMenuBar = sender.state == .on
appDelegate?.updateMenuBarCount() // reflect immediately, no refetch needed
}
@objc func toggleLaunchAtLogin(_ sender: NSButton) { @objc func toggleLaunchAtLogin(_ sender: NSButton) {
do { do {
if sender.state == .on { if sender.state == .on {