Compare commits
2 commits
149f394ecf
...
20ca403d53
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20ca403d53 | ||
|
|
19e63f0dcd |
|
|
@ -26,6 +26,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
var logWindow: LogWindowController?
|
var logWindow: LogWindowController?
|
||||||
var settingsWindow: SettingsWindowController?
|
var settingsWindow: SettingsWindowController?
|
||||||
|
var tipsWindow: TipsWindowController?
|
||||||
var logBuffer: String = ""
|
var logBuffer: String = ""
|
||||||
|
|
||||||
var brewPath: String?
|
var brewPath: String?
|
||||||
|
|
@ -105,6 +106,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
menu.addItem(NSMenuItem(title: "⚙️ Settings…", action: #selector(showSettings), keyEquivalent: ","))
|
menu.addItem(NSMenuItem(title: "⚙️ Settings…", action: #selector(showSettings), keyEquivalent: ","))
|
||||||
|
menu.addItem(NSMenuItem(title: "💡 Tips", action: #selector(showTips), keyEquivalent: ""))
|
||||||
|
|
||||||
versionItem = NSMenuItem(title: "🏷 Brew: ...", action: nil, keyEquivalent: "")
|
versionItem = NSMenuItem(title: "🏷 Brew: ...", action: nil, keyEquivalent: "")
|
||||||
menu.addItem(versionItem)
|
menu.addItem(versionItem)
|
||||||
|
|
@ -161,6 +163,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
presentWindow(of: settingsWindow)
|
presentWindow(of: settingsWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func showTips() {
|
||||||
|
if tipsWindow == nil {
|
||||||
|
tipsWindow = TipsWindowController()
|
||||||
|
}
|
||||||
|
presentWindow(of: tipsWindow)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Window presentation
|
// MARK: - Window presentation
|
||||||
|
|
||||||
/// Accessory apps never appear in Cmd-Tab and their windows can open
|
/// Accessory apps never appear in Cmd-Tab and their windows can open
|
||||||
|
|
@ -177,7 +186,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
@objc func windowWillClose(_: Notification) {
|
@objc func windowWillClose(_: Notification) {
|
||||||
// async so the closing window's isVisible has flipped to false
|
// async so the closing window's isVisible has flipped to false
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let ourWindows = [self.logWindow?.window, self.settingsWindow?.window]
|
let ourWindows = [self.logWindow?.window, self.settingsWindow?.window, self.tipsWindow?.window]
|
||||||
let stillOpen = ourWindows.compactMap { $0 }.contains { $0.isVisible }
|
let stillOpen = ourWindows.compactMap { $0 }.contains { $0.isVisible }
|
||||||
if !stillOpen {
|
if !stillOpen {
|
||||||
NSApp.setActivationPolicy(.accessory)
|
NSApp.setActivationPolicy(.accessory)
|
||||||
|
|
|
||||||
78
BrewBar/TipsWindowController.swift
Normal file
78
BrewBar/TipsWindowController.swift
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
/// Static "good to know" window for the features a menu can't explain
|
||||||
|
/// by itself. Content lives in the tips array — adding one there is all
|
||||||
|
/// it takes.
|
||||||
|
class TipsWindowController: NSWindowController {
|
||||||
|
let tips: [(title: String, body: String)] = [
|
||||||
|
("⌥ reveals hidden menu items",
|
||||||
|
"With the menu open, hold Option (⌥): Cleanup becomes Cleanup (purge cache), which also empties Homebrew's download cache (brew cleanup --prune=all). Downloads are simply re-fetched when next needed."),
|
||||||
|
|
||||||
|
("Keyboard shortcuts",
|
||||||
|
"While the menu is open: R refreshes, U upgrades formulae & casks, L opens the logs, comma (,) opens Settings and Q quits."),
|
||||||
|
|
||||||
|
("Upgrade only one kind",
|
||||||
|
"When both formulae and casks are outdated, an “Upgrade only…” submenu appears under the main upgrade item to handle just one of the two."),
|
||||||
|
|
||||||
|
("Self-updating apps",
|
||||||
|
"Apps that update themselves (browsers, editors…) can drift from what brew installed, and brew then refuses a normal upgrade. BrewBar detects this and automatically reinstalls the cask instead — you may see reinstall --cask --force in the logs; that is expected."),
|
||||||
|
|
||||||
|
("When something looks off, read the Logs",
|
||||||
|
"Every brew command BrewBar runs is in the Logs window with its full output — errors, warnings and all. It is the first place to look."),
|
||||||
|
]
|
||||||
|
|
||||||
|
convenience init() {
|
||||||
|
let window = NSWindow(
|
||||||
|
contentRect: NSRect(x: 0, y: 0, width: 440, height: 300),
|
||||||
|
styleMask: [.titled, .closable],
|
||||||
|
backing: .buffered,
|
||||||
|
defer: false
|
||||||
|
)
|
||||||
|
window.title = "BrewBar Tips"
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
buildUI()
|
||||||
|
window.center()
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildUI() {
|
||||||
|
guard let contentView = window?.contentView else { return }
|
||||||
|
|
||||||
|
let tipViews: [NSView] = tips.map { tip in
|
||||||
|
let title = NSTextField(labelWithString: tip.title)
|
||||||
|
title.font = .boldSystemFont(ofSize: NSFont.systemFontSize)
|
||||||
|
|
||||||
|
let body = NSTextField(wrappingLabelWithString: tip.body)
|
||||||
|
body.font = .systemFont(ofSize: NSFont.smallSystemFontSize)
|
||||||
|
body.textColor = .secondaryLabelColor
|
||||||
|
body.preferredMaxLayoutWidth = 400
|
||||||
|
|
||||||
|
let tipStack = NSStackView(views: [title, body])
|
||||||
|
tipStack.orientation = .vertical
|
||||||
|
tipStack.alignment = .leading
|
||||||
|
tipStack.spacing = 4
|
||||||
|
return tipStack
|
||||||
|
}
|
||||||
|
|
||||||
|
let stack = NSStackView(views: tipViews)
|
||||||
|
stack.orientation = .vertical
|
||||||
|
stack.alignment = .leading
|
||||||
|
stack.spacing = 16
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue