Merge branch 'feature/tips'
This commit is contained in:
commit
20ca403d53
|
|
@ -26,6 +26,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
var logWindow: LogWindowController?
|
||||
var settingsWindow: SettingsWindowController?
|
||||
var tipsWindow: TipsWindowController?
|
||||
var logBuffer: String = ""
|
||||
|
||||
var brewPath: String?
|
||||
|
|
@ -105,6 +106,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
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: "")
|
||||
menu.addItem(versionItem)
|
||||
|
|
@ -161,6 +163,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
presentWindow(of: settingsWindow)
|
||||
}
|
||||
|
||||
@objc func showTips() {
|
||||
if tipsWindow == nil {
|
||||
tipsWindow = TipsWindowController()
|
||||
}
|
||||
presentWindow(of: tipsWindow)
|
||||
}
|
||||
|
||||
// MARK: - Window presentation
|
||||
|
||||
/// Accessory apps never appear in Cmd-Tab and their windows can open
|
||||
|
|
@ -177,7 +186,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
@objc func windowWillClose(_: Notification) {
|
||||
// async so the closing window's isVisible has flipped to false
|
||||
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 }
|
||||
if !stillOpen {
|
||||
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