180 lines
5.7 KiB
Swift
180 lines
5.7 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: (MacWindow) -> 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 (MacWindow) -> Body
|
|
) {
|
|
self.title = title
|
|
self.content = content
|
|
self.id = id
|
|
self.open = open
|
|
}
|
|
// swiftlint:enable function_default_parameter_at_end
|
|
|
|
/// The window type.
|
|
public struct MacWindow {
|
|
|
|
/// The window.
|
|
var window: NSWindow
|
|
|
|
/// Close the window.
|
|
public func close() {
|
|
window.close()
|
|
}
|
|
|
|
}
|
|
|
|
/// 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(.init(window: window))
|
|
.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 {
|
|
guard let window = storage.pointer as? NSWindow else {
|
|
return
|
|
}
|
|
if let content = storage.content[.mainContent]?.first {
|
|
self.content(.init(window: window)).updateStorage(
|
|
content,
|
|
data: .init(sceneStorage: storage, appStorage: app),
|
|
updateProperties: updateProperties,
|
|
type: MacMainView.self
|
|
)
|
|
}
|
|
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
|
|
}
|
|
|
|
}
|
|
|
|
/// The window type.
|
|
public typealias MacWindow = Window.MacWindow
|