swift-tests/SwiftTrayDemo/SwiftTrayDemoApp.swift
Gremlin 44b59d50c9
All checks were successful
swift-macos-arm64 / swift-macos-arm64 (push) Successful in 11s
feat: make tray menu actions work and add functional demo sections
2026-03-21 17:01:43 +01:00

201 lines
6.4 KiB
Swift

import SwiftUI
import AppKit
import ObjectiveC
enum WindowCommandCenter {
static func mainWindow() -> NSWindow? {
NSApp.windows.first { $0.identifier?.rawValue == "main-window" } ?? NSApp.windows.first
}
static func resizeMainWindow(width: Double, height: Double) {
guard let window = mainWindow() else { return }
var frame = window.frame
frame.size = NSSize(width: width, height: height)
window.setFrame(frame, display: true, animate: true)
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
}
static func centerMainWindow() {
guard let window = mainWindow() else { return }
window.center()
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
}
static func toggleFullScreen() {
guard let window = mainWindow() else { return }
NSApp.activate(ignoringOtherApps: true)
window.toggleFullScreen(nil)
}
static func showMainWindow() {
guard let window = mainWindow() else { return }
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
}
static func renameMainWindow(to title: String) {
guard let window = mainWindow() else { return }
window.title = title
}
static func setMainWindowAlpha(_ value: Double) {
guard let window = mainWindow() else { return }
window.alphaValue = CGFloat(value)
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
}
}
final class WindowAccessor: NSView {
var onResolve: ((NSWindow) -> Void)?
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
if let window {
onResolve?(window)
}
}
}
struct MainWindowConfigurator: NSViewRepresentable {
func makeNSView(context: Context) -> NSView {
let view = WindowAccessor()
view.onResolve = { window in
window.identifier = NSUserInterfaceItemIdentifier("main-window")
window.title = "Swift Window Demo"
window.setContentSize(NSSize(width: 980, height: 640))
window.center()
window.styleMask.insert(.resizable)
window.styleMask.insert(.miniaturizable)
window.toolbarStyle = .unified
window.titlebarAppearsTransparent = false
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}
final class MenuActionBox: NSObject {
private let action: () -> Void
init(action: @escaping () -> Void) {
self.action = action
}
@objc func performAction() {
action()
}
}
final class TrayController: NSObject {
private var statusItem: NSStatusItem?
private var retainedTargets: [MenuActionBox] = []
func install() {
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
item.button?.image = NSImage(systemSymbolName: "menubar.rectangle", accessibilityDescription: "Swift Window Demo")
item.button?.toolTip = "Swift Window Demo"
let menu = NSMenu()
menu.autoenablesItems = false
let openTarget = MenuActionBox {
WindowCommandCenter.showMainWindow()
}
let openItem = NSMenuItem(title: "Open Main Window", action: #selector(MenuActionBox.performAction), keyEquivalent: "o")
openItem.target = openTarget
openItem.isEnabled = true
menu.addItem(openItem)
let centerTarget = MenuActionBox {
WindowCommandCenter.centerMainWindow()
}
let centerItem = NSMenuItem(title: "Center Main Window", action: #selector(MenuActionBox.performAction), keyEquivalent: "c")
centerItem.target = centerTarget
centerItem.isEnabled = true
menu.addItem(centerItem)
let fullscreenTarget = MenuActionBox {
WindowCommandCenter.toggleFullScreen()
}
let fullscreenItem = NSMenuItem(title: "Toggle Full Screen", action: #selector(MenuActionBox.performAction), keyEquivalent: "f")
fullscreenItem.target = fullscreenTarget
fullscreenItem.isEnabled = true
menu.addItem(fullscreenItem)
menu.addItem(.separator())
let quitTarget = MenuActionBox {
NSApp.terminate(nil)
}
let quitItem = NSMenuItem(title: "Quit", action: #selector(MenuActionBox.performAction), keyEquivalent: "q")
quitItem.target = quitTarget
quitItem.isEnabled = true
menu.addItem(quitItem)
retainedTargets = [openTarget, centerTarget, fullscreenTarget, quitTarget]
item.menu = menu
statusItem = item
}
}
final class AppDelegate: NSObject, NSApplicationDelegate {
private let trayController = TrayController()
func applicationDidFinishLaunching(_ notification: Notification) {
trayController.install()
}
}
struct InspectorView: View {
var body: some View {
VStack(alignment: .leading, spacing: 14) {
Text("Utility Window")
.font(.title2.bold())
Text("This secondary scene proves the app supports more than one real macOS window.")
.foregroundStyle(.secondary)
Divider()
Label("Independent scene", systemImage: "macwindow.on.rectangle")
Label("Useful for inspectors, logs, or tools", systemImage: "sidebar.right")
Label("Opened from both sidebar actions and tray-driven workflows", systemImage: "menubar.rectangle")
}
.padding(20)
.frame(minWidth: 360, minHeight: 220)
}
}
@main
struct SwiftTrayDemoApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
var body: some Scene {
WindowGroup("Swift Window Demo") {
ContentView()
.background(MainWindowConfigurator())
}
.defaultSize(width: 980, height: 640)
.commands {
CommandGroup(after: .windowArrangement) {
Button("Center Main Window") {
WindowCommandCenter.centerMainWindow()
}
.keyboardShortcut("0")
Button("Toggle Full Screen") {
WindowCommandCenter.toggleFullScreen()
}
.keyboardShortcut("f")
}
}
Window("Inspector", id: "inspector") {
InspectorView()
}
.defaultSize(width: 360, height: 220)
.windowResizability(.contentSize)
}
}