import AppKit import SwiftUI /// Führt das Schaufenster. /// /// Aufgebaut wie `SettingsWindowController` — aus demselben Grund: SwiftUIs /// eigene Fensterszenen kommen bei einer App ohne Menüleiste unzuverlässig an. @MainActor final class GalleryWindowController: NSObject, NSWindowDelegate { private var window: NSWindow? /// Ob Onyx dauerhaft im Dock steht. Bestimmt, wohin nach dem Schließen /// zurückgeschaltet wird. private var keepsDockIcon = false /// Ob das Schaufenster überhaupt erreichbar ist. /// /// Im Debug immer, im Release nur auf ausdrücklichen Wunsch. Es ist /// Werkzeug, kein Programmteil — und ein Menüeintrag, den niemand braucht, /// ist selbst schon Unordnung. /// /// ``` /// defaults write com.scarriffleservices.onyx onyx.gallery -bool YES /// ``` static var isAvailable: Bool { #if DEBUG true #else UserDefaults.standard.bool(forKey: "onyx.gallery") #endif } /// Ob es beim Start gleich aufgehen soll. /// /// Der Werkstatt-Kreislauf ist bauen, starten, hinsehen. Ohne das ist der /// dritte Schritt ein Griff zur Maus und zwei Klicks ins Statusmenü — bei /// jedem Durchlauf. `UserDefaults` liest Startargumente von selbst mit: /// /// ``` /// open Onyx.app --args -onyx.gallery.open YES /// ``` static var opensAtLaunch: Bool { isAvailable && UserDefaults.standard.bool(forKey: "onyx.gallery.open") } func show(model: AppModel) { keepsDockIcon = model.showsDockIcon // Solange ein Fenster offen ist, gehört Onyx in den Dock — sonst ist es // weder über den Dock noch über ⌘-Tab zurückzuholen. NSApp.setActivationPolicy(.regular) if window == nil { let hosting = NSHostingController(rootView: GalleryView(model: model)) let window = NSWindow(contentViewController: hosting) window.title = "Onyx — Schaufenster" window.titlebarAppearsTransparent = true window.titlebarSeparatorStyle = .none window.styleMask = [.titled, .closable, .miniaturizable, .resizable] window.setContentSize(NSSize(width: 1040, height: 720)) window.contentMinSize = NSSize(width: 780, height: 540) window.isReleasedWhenClosed = false window.delegate = self OnyxWindowPlacement.center(window) window.setFrameAutosaveName("onyx.gallery") self.window = window } NSApp.activate(ignoringOtherApps: true) window?.makeKeyAndOrderFront(nil) } /// Der rote Knopf blendet aus, statt zu schließen — wie bei den /// Einstellungen, und aus demselben Grund: Onyx hat kein Hauptfenster, und /// manche Hilfsprogramme beenden eine App, sobald ihr letztes Fenster /// verschwindet. func windowShouldClose(_ sender: NSWindow) -> Bool { hide() return false } private func hide() { window?.orderOut(nil) // Nur zurück in den Hintergrund, wenn wirklich nichts mehr offen ist. // Das Einstellungsfenster kann noch stehen — es hier mit auszublenden // sähe aus wie ein Absturz. let otherVisible = NSApp.windows.contains { $0 !== window && $0.isVisible && $0.styleMask.contains(.titled) } guard !otherVisible else { return } if !keepsDockIcon { NSApp.setActivationPolicy(.accessory) } NSApp.hide(nil) } }