// // Window.swift // MacBackend // // Created by david-swift on 14.09.23. // import AppKit /// A structure representing an application window type. /// /// Note that it may be possible to open multiple instances of a window at the same time. public struct Window: MacSceneElement { /// The window's identifier. public var id: String /// The window's content. var content: Body /// Whether an instance of the window type should be opened when the app is starting up. var `open`: Int /// The window's title. var title: String /// Whether the window is miniaturizable. var miniaturizable = true /// Whether the window is resizable. var resizable = true /// The window's width. var width: Binding? /// The window's height. var height: Binding? // swiftlint:disable function_default_parameter_at_end /// Create a window type with a certain identifier and user interface. /// - Parameters: /// - id: The identifier. /// - open: The number of instances of the window type when the app is starting. /// - content: The window's content. /// - title: The window's title. public init(_ title: String = "", id: String, `open`: Int = 1, @ViewBuilder content: @escaping () -> Body) { self.title = title self.content = content() self.id = id self.open = open } // swiftlint:enable function_default_parameter_at_end /// Set up the initial scene storages. /// - Parameter app: The app storage. public func setupInitialContainers(app: Storage) where Storage: AppStorage { for _ in 0..(app: Storage) -> SceneStorage where Storage: AppStorage { let window = NSWindow() let storage = SceneStorage(id: id, pointer: window) { window.makeKeyAndOrderFront(nil) } NotificationCenter.default .addObserver(forName: NSWindow.willCloseNotification, object: window, queue: nil) { _ in storage.destroy = true } let content = content.storage(data: .init(sceneStorage: storage, appStorage: app), type: MacMainView.self) if let pointer = content.pointer as? NSView { window.contentView = pointer } storage.content[.mainContent] = [content] window.styleMask = [.titled, .closable, .fullSizeContentView, .resizable, .miniaturizable] update(storage, app: app, updateProperties: true) window.setFrame( .init(origin: .zero, size: .init(width: width?.wrappedValue ?? -1, height: height?.wrappedValue ?? -1)), display: true ) return storage } /// Update the stored content. /// - Parameters: /// - storage: The storage to update. /// - app: The app storage. /// - updateProperties: Whether to update the view's properties. public func update( _ storage: SceneStorage, app: Storage, updateProperties: Bool ) where Storage: AppStorage { if let content = storage.content[.mainContent]?.first { self.content.updateStorage( content, data: .init(sceneStorage: storage, appStorage: app), updateProperties: updateProperties, type: MacMainView.self ) } guard let window = storage.pointer as? NSWindow else { return } guard updateProperties else { return } let previousState = storage.previousState as? Self if previousState?.title != title { window.title = title } if previousState?.miniaturizable != miniaturizable { if miniaturizable { window.styleMask.insert(.miniaturizable) } else { window.styleMask.remove(.miniaturizable) } } if previousState?.resizable != resizable { if resizable { window.styleMask.insert(.resizable) } else { window.styleMask.remove(.resizable) } } storage.previousState = self } /// The window's width and height. /// - Parameters: /// - width: The width. /// - height: The height. /// - Returns: The window. public func frame(width: Binding? = nil, height: Binding? = nil) -> Self { var newSelf = self newSelf.width = width newSelf.height = height return newSelf } /// Whether the window is miniaturizable. /// - Parameter miniaturizable: Whether the window is miniaturizable. /// - Returns: The window. public func miniaturizable(_ miniaturizable: Bool = true) -> Self { var newSelf = self newSelf.miniaturizable = miniaturizable return newSelf } /// Whether the window is resizable. /// - Parameter resizable: Whether the window is resizable. /// - Returns: The window. public func resizable(_ resizable: Bool = true) -> Self { var newSelf = self newSelf.resizable = resizable return newSelf } }