david-swift e989bea14e
All checks were successful
Deploy Docs / publish (push) Successful in 2m38s
Initial commit
2024-12-02 22:14:13 +01:00

158 lines
5.3 KiB
Swift

//
// 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<Int>?
/// The window's height.
var height: Binding<Int>?
// 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<Storage>(app: Storage) where Storage: AppStorage {
for _ in 0..<open {
let container = container(app: app)
container.show()
app.storage.sceneStorage.append(container)
}
}
/// The scene storage.
/// - Parameter app: The app storage.
public func container<Storage>(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>(
_ 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<Int>? = nil, height: Binding<Int>? = 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
}
}