portico/Sources/Portico/App/ApplicationWindow.swift

97 lines
3.3 KiB
Swift

import Adw
import Gtk
/// A window scene that hosts exactly one root ``View``.
///
/// ``ApplicationWindow`` is generic over its content view type, enforcing a single
/// child at compile time. Compose multiple widgets with an explicit
/// container (`VStack`/`HStack`/`Box`).
///
/// The content closure receives the live `Adw.ApplicationWindow` as an
/// imperative escape hatch for configuration not covered by modifiers.
public struct ApplicationWindow<Content: View>: Scene {
/// Builds the window's single root view, given the live application window.
@_spi(Portico) public let content: (Adw.ApplicationWindow) -> Content
/// Creation-time configuration accumulated by the scene modifiers.
@_spi(Portico) public var config = WindowConfig()
public var body: Never { fatalError() }
/// Creates a window hosting the given root view.
///
/// The content closure receives the live `Adw.ApplicationWindow` as an
/// imperative escape hatch for configuration not covered by modifiers.
/// Exactly one view must be returned; use an explicit container
/// (`VStack`/`HStack`/`Box`) for multiple widgets.
public init(content: @escaping (Adw.ApplicationWindow) -> Content) {
self.content = content
}
}
// MARK: - Scene modifiers
public extension ApplicationWindow {
/// Sets the default size of the window, in pixels.
func defaultSize(width: Int32, height: Int32) -> ApplicationWindow {
var c = self
c.config.defaultSize = (width, height)
return c
}
/// Sets the window title.
func title(_ title: String) -> ApplicationWindow {
var c = self
c.config.title = title
return c
}
/// Requests the window to enter fullscreen mode.
func fullscreen(_ enabled: Bool = true) -> ApplicationWindow {
var c = self
c.config.fullscreen = enabled
return c
}
/// Sets whether the window can be resized by the user.
func resizable(_ resizable: Bool = true) -> ApplicationWindow {
var c = self
c.config.resizable = resizable
return c
}
}
@_spi(Portico) extension ApplicationWindow: MountableScene {
/// An `ApplicationWindow` attaches its own window directly, with no
/// conditional wrapping it.
@_spi(Portico) public var identity: SceneIdentity { .leaf }
@_spi(Portico) public func attach(to app: Adw.Application) -> SceneHandle? {
let window = Adw.ApplicationWindow(app: app)
PorticoRuntime._installShutdownGate(on: window)
if let s = config.defaultSize {
window.setDefaultSize(width: s.width, height: s.height)
}
if let t = config.title {
window.setTitle(title: t)
}
if let r = config.resizable {
window.setResizable(resizable: r)
}
if config.fullscreen {
window.fullscreen()
}
let ctx = MountContext()
let child = AnyView(content(window)).makeWidget(ctx)
window.setContent(content: child)
let handle = SceneHandle(window: window, registry: ctx.registry)
_ = window.connectCloseRequest { [weak handle] _ in
handle?.releaseResources()
return false // allow the default close to proceed
}
window.present()
return handle
}
}