Files
Snaggle/GreenshotMac/App/StatusMenuController.swift
T
2026-09-17 15:15:45 -05:00

128 lines
5.3 KiB
Swift

// StatusMenuController.swift
// GreenshotMac
//
// The menu bar item, equivalent to Greenshot's tray icon and context menu.
import AppKit
@MainActor
final class StatusMenuController: NSObject, NSMenuDelegate {
private let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
private var preferencesController: PreferencesWindowController?
override init() {
super.init()
if let button = statusItem.button {
button.image = NSImage(systemSymbolName: "camera.viewfinder", accessibilityDescription: "GreenshotMac")
button.image?.isTemplate = true
button.toolTip = "GreenshotMac"
}
statusItem.menu = buildMenu()
}
private func buildMenu() -> NSMenu {
let menu = NSMenu()
menu.delegate = self
menu.addItem(item(title: "Capture region",
action: #selector(captureRegion),
hotkey: HotkeyBindings.shared.spec(for: .region)))
menu.addItem(item(title: "Capture window",
action: #selector(captureWindow),
hotkey: HotkeyBindings.shared.spec(for: .window)))
menu.addItem(item(title: "Capture full screen",
action: #selector(captureFullscreen),
hotkey: HotkeyBindings.shared.spec(for: .fullscreen)))
menu.addItem(item(title: "Capture last region",
action: #selector(captureLastRegion),
hotkey: HotkeyBindings.shared.spec(for: .lastRegion)))
menu.addItem(.separator())
menu.addItem(item(title: "Open image from file…", action: #selector(openImageFile), hotkey: nil))
menu.addItem(item(title: "Open last screenshot in Finder", action: #selector(revealLast), hotkey: nil))
menu.addItem(.separator())
let destinationItem = NSMenuItem(title: "Default destination", action: nil, keyEquivalent: "")
destinationItem.submenu = destinationMenu()
menu.addItem(destinationItem)
menu.addItem(item(title: "Preferences…", action: #selector(showPreferences), hotkey: nil))
menu.addItem(.separator())
menu.addItem(item(title: "About GreenshotMac", action: #selector(showAbout), hotkey: nil))
menu.addItem(item(title: "Quit GreenshotMac", action: #selector(quit), hotkey: nil))
return menu
}
private func destinationMenu() -> NSMenu {
let menu = NSMenu()
for destination in DestinationID.allCases {
let menuItem = NSMenuItem(title: destination.title,
action: #selector(selectDestination(_:)),
keyEquivalent: "")
menuItem.target = self
menuItem.representedObject = destination.rawValue
menuItem.state = Configuration.shared.defaultDestination == destination ? .on : .off
menu.addItem(menuItem)
}
return menu
}
private func item(title: String, action: Selector, hotkey: HotkeySpec?) -> NSMenuItem {
let menuItem = NSMenuItem(title: title, action: action, keyEquivalent: "")
menuItem.target = self
if let hotkey, !hotkey.isEmpty {
menuItem.toolTip = HotkeyManager.description(of: hotkey)
}
return menuItem
}
func menuWillOpen(_ menu: NSMenu) {
for menuItem in menu.items where menuItem.title == "Default destination" {
menuItem.submenu = destinationMenu()
}
if let revealItem = menu.items.first(where: { $0.title == "Open last screenshot in Finder" }) {
revealItem.isEnabled = Notifier.mostRecentSavedURL != nil
}
}
// MARK: - Actions
@objc private func captureRegion() { CaptureCoordinator.shared.captureRegion() }
@objc private func captureWindow() { CaptureCoordinator.shared.captureWindow() }
@objc private func captureFullscreen() { CaptureCoordinator.shared.captureFullscreen() }
@objc private func captureLastRegion() { CaptureCoordinator.shared.captureLastRegion() }
@objc private func openImageFile() { CaptureCoordinator.shared.openImageFile() }
@objc private func revealLast() { Notifier.revealMostRecent() }
@objc private func selectDestination(_ sender: NSMenuItem) {
guard let raw = sender.representedObject as? String,
let destination = DestinationID(rawValue: raw) else { return }
Configuration.shared.defaultDestination = destination
}
@objc private func showPreferences() {
if preferencesController == nil {
preferencesController = PreferencesWindowController()
}
preferencesController?.showWindow(nil)
preferencesController?.window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
}
@objc private func showAbout() {
NSApp.activate(ignoringOtherApps: true)
NSApp.orderFrontStandardAboutPanel(options: [
.applicationName: "GreenshotMac",
.credits: NSAttributedString(
string: "A native macOS screenshot tool modelled on Greenshot for Windows.\n"
+ "Licensed under the GNU General Public License v3.",
attributes: [.font: NSFont.systemFont(ofSize: 11)])
])
}
@objc private func quit() {
NSApp.terminate(nil)
}
}