feat: add SwiftUI macOS main window demo with CI
swift-macos-arm64 / swift-macos-arm64 (push) Failing after 11s

This commit is contained in:
Gremlin
2026-03-21 15:50:21 +01:00
commit cf2bfc3aa8
9 changed files with 477 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
import SwiftUI
struct FeatureCard: View {
let title: String
let systemImage: String
let detail: String
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Label(title, systemImage: systemImage)
.font(.headline)
Text(detail)
.font(.subheadline)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(.quaternary.opacity(0.45), in: RoundedRectangle(cornerRadius: 14, style: .continuous))
}
}
struct ContentView: View {
@Environment(\.openWindow) private var openWindow
@State private var documentTitle = "Swift Window Demo"
@State private var width: Double = 980
@State private var height: Double = 640
@State private var showSheet = false
@State private var showDialog = false
var body: some View {
NavigationSplitView {
List {
Label("Overview", systemImage: "macwindow")
Label("Window Controls", systemImage: "slider.horizontal.3")
Label("Secondary Window", systemImage: "uiwindow.split.2x1")
Label("Dialogs", systemImage: "square.and.pencil")
}
.navigationTitle("Capabilities")
.listStyle(.sidebar)
} detail: {
ScrollView {
VStack(alignment: .leading, spacing: 22) {
VStack(alignment: .leading, spacing: 8) {
Text("Main Window Demo")
.font(.system(size: 34, weight: .bold))
Text("A current-style SwiftUI macOS app that exercises real desktop window behaviors instead of just rendering a static view.")
.foregroundStyle(.secondary)
}
Grid(horizontalSpacing: 16, verticalSpacing: 16) {
GridRow {
FeatureCard(title: "Resizable", systemImage: "arrow.up.left.and.arrow.down.right", detail: "Set a target content size and apply it to the frontmost app window.")
FeatureCard(title: "Secondary Window", systemImage: "macwindow.on.rectangle", detail: "Open a separate utility-style scene to prove multi-window support.")
}
GridRow {
FeatureCard(title: "Sheet + Dialog", systemImage: "rectangle.portrait.and.arrow.right", detail: "Trigger standard macOS sheets and confirmation dialogs.")
FeatureCard(title: "Tray Integration", systemImage: "menubar.rectangle", detail: "Use the menu-bar item to reopen the main window at any time.")
}
}
GroupBox("Window controls") {
VStack(alignment: .leading, spacing: 14) {
TextField("Window title", text: $documentTitle)
.textFieldStyle(.roundedBorder)
HStack {
VStack(alignment: .leading) {
Text("Width: \(Int(width))")
Slider(value: $width, in: 700...1400, step: 10)
}
VStack(alignment: .leading) {
Text("Height: \(Int(height))")
Slider(value: $height, in: 480...960, step: 10)
}
}
HStack(spacing: 12) {
Button("Apply Size") {
WindowCommandCenter.resizeMainWindow(width: width, height: height)
}
Button("Center Window") {
WindowCommandCenter.centerMainWindow()
}
Button("Toggle Full Screen") {
WindowCommandCenter.toggleFullScreen()
}
}
}
.padding(.top, 4)
}
HStack(spacing: 12) {
Button("Open Utility Window") {
openWindow(id: "inspector")
}
Button("Show Sheet") {
showSheet = true
}
Button("Show Confirmation") {
showDialog = true
}
}
}
.padding(24)
}
.navigationTitle(documentTitle)
.sheet(isPresented: $showSheet) {
SheetDemoView()
}
.confirmationDialog("Test confirmation dialog", isPresented: $showDialog, titleVisibility: .visible) {
Button("Acknowledge") {}
Button("Cancel", role: .cancel) {}
} message: {
Text("This validates standard app-window attached dialogs on macOS.")
}
}
}
}
struct SheetDemoView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Attached Sheet")
.font(.title.bold())
Text("This sheet is attached to the main app window and demonstrates standard macOS document-style presentation.")
.foregroundStyle(.secondary)
HStack {
Spacer()
Button("Close") { dismiss() }
.keyboardShortcut(.defaultAction)
}
}
.padding(24)
.frame(minWidth: 420, minHeight: 180)
}
}
#Preview {
ContentView()
}
+168
View File
@@ -0,0 +1,168 @@
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)
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)
}
}
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 TrayController: NSObject {
private var statusItem: NSStatusItem?
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()
let openItem = NSMenuItem(title: "Open Main Window", action: #selector(MenuActionBox.performAction), keyEquivalent: "o")
openItem.target = MenuActionBox {
WindowCommandCenter.showMainWindow()
}
menu.addItem(openItem)
let centerItem = NSMenuItem(title: "Center Main Window", action: #selector(MenuActionBox.performAction), keyEquivalent: "c")
centerItem.target = MenuActionBox {
WindowCommandCenter.centerMainWindow()
}
menu.addItem(centerItem)
menu.addItem(.separator())
let quitItem = NSMenuItem(title: "Quit", action: #selector(MenuActionBox.performAction), keyEquivalent: "q")
quitItem.target = MenuActionBox {
NSApp.terminate(nil)
}
menu.addItem(quitItem)
objc_setAssociatedObject(openItem, UnsafeRawPointer(bitPattern: 1)!, openItem.target, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_setAssociatedObject(centerItem, UnsafeRawPointer(bitPattern: 2)!, centerItem.target, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_setAssociatedObject(quitItem, UnsafeRawPointer(bitPattern: 3)!, quitItem.target, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
item.menu = menu
statusItem = item
}
}
final class MenuActionBox: NSObject {
private let action: () -> Void
init(action: @escaping () -> Void) {
self.action = action
}
@objc func performAction() {
action()
}
}
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")
}
.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")
}
}
Window("Inspector", id: "inspector") {
InspectorView()
}
.defaultSize(width: 360, height: 220)
.windowResizability(.contentSize)
}
}