diff --git a/Documentation/Reference/README.md b/Documentation/Reference/README.md index 0a89deb..eb9fb5f 100644 --- a/Documentation/Reference/README.md +++ b/Documentation/Reference/README.md @@ -7,6 +7,7 @@ - [View](protocols/View.md) - [Widget](protocols/Widget.md) - [WindowScene](protocols/WindowScene.md) +- [WindowSceneGroup](protocols/WindowSceneGroup.md) ## Structs @@ -19,6 +20,7 @@ - [Text](structs/Text.md) - [UpdateObserver](structs/UpdateObserver.md) - [VStack](structs/VStack.md) +- [Window](structs/Window.md) ## Classes @@ -42,6 +44,8 @@ - [String](extensions/String.md) - [View](extensions/View.md) - [Widget](extensions/Widget.md) +- [WindowScene](extensions/WindowScene.md) +- [WindowSceneGroup](extensions/WindowSceneGroup.md) ## Typealiases diff --git a/Documentation/Reference/classes/GTUIApp.md b/Documentation/Reference/classes/GTUIApp.md index 8d4f8e8..d3c5cde 100644 --- a/Documentation/Reference/classes/GTUIApp.md +++ b/Documentation/Reference/classes/GTUIApp.md @@ -28,3 +28,15 @@ Initialize the GTUI application. ### `onActivate()` The entry point of the application. + +### `showWindow(_:)` + +Focus the window with a certain id. Create the window if it doesn't already exist. +- Parameters: + - id: The window's id. + +### `addWindow(_:)` + +Add a new window with the content of the window with a certain id. +- Parameters: + - id: The window's id. diff --git a/Documentation/Reference/classes/WindowStorage.md b/Documentation/Reference/classes/WindowStorage.md index ce21ca3..ada758c 100644 --- a/Documentation/Reference/classes/WindowStorage.md +++ b/Documentation/Reference/classes/WindowStorage.md @@ -5,6 +5,14 @@ A storage for an app's window. ## Properties +### `id` + +The window's identifier. + +### `destroy` + +Whether the reference to the window should disappear in the next update. + ### `window` The GTUI window. @@ -14,9 +22,10 @@ The GTUI window. The content's storage. ## Methods -### `init(window:view:)` +### `init(id:window:view:)` Initialize a window storage. - Parameters: + - id: The window's identifier. - window: The GTUI window. - view: The content's storage. diff --git a/Documentation/Reference/extensions/Array.md b/Documentation/Reference/extensions/Array.md index 5f1cc6b..b530cdf 100644 --- a/Documentation/Reference/extensions/Array.md +++ b/Documentation/Reference/extensions/Array.md @@ -14,6 +14,11 @@ Update a collection of views with a collection of view storages. - Parameters: - storage: The collection of view storages. +### `windows()` + +Get the content of an array of window scene groups. +- Returns: The array of windows. + ### `checkIndex(_:)` Check if a given index is valid for the array. diff --git a/Documentation/Reference/extensions/WindowScene.md b/Documentation/Reference/extensions/WindowScene.md index 3f945be..82aa4d8 100644 --- a/Documentation/Reference/extensions/WindowScene.md +++ b/Documentation/Reference/extensions/WindowScene.md @@ -2,10 +2,7 @@ # `WindowScene` -## Methods -### `getWindow(app:)` +## Properties +### `body` -Get the `GTUI.Window` with the content. -- Parameters: - - app: The application. -- Returns: The window. +The window scene's body is itself. diff --git a/Documentation/Reference/extensions/WindowSceneGroup.md b/Documentation/Reference/extensions/WindowSceneGroup.md new file mode 100644 index 0000000..9a13c53 --- /dev/null +++ b/Documentation/Reference/extensions/WindowSceneGroup.md @@ -0,0 +1,14 @@ +**EXTENSION** + +# `WindowSceneGroup` + +## Methods +### `windows()` + +Get the windows described by the group. +- Returns: The windows. + +### `update(_:)` + +Update the windows described by the group. +- Parameter storage: The window's storage. diff --git a/Documentation/Reference/protocols/WindowScene.md b/Documentation/Reference/protocols/WindowScene.md index 1d75654..d320f1e 100644 --- a/Documentation/Reference/protocols/WindowScene.md +++ b/Documentation/Reference/protocols/WindowScene.md @@ -2,4 +2,25 @@ # `WindowScene` -A structure conforming to `WindowScene` can be added to an app's `scene`. +A structure representing the content for a certain window type. + +## Properties +### `id` + +The window type's identifier. + +### `open` + +The number of instances of the window at the startup. + +## Methods +### `createWindow(app:)` + +Get the storage for the window. +- Parameter app: The application. +- Returns: The storage. + +### `update(_:)` + +Update a window storage's content. +- Parameter storage: The storage to update. diff --git a/Documentation/Reference/protocols/WindowSceneGroup.md b/Documentation/Reference/protocols/WindowSceneGroup.md new file mode 100644 index 0000000..dfde548 --- /dev/null +++ b/Documentation/Reference/protocols/WindowSceneGroup.md @@ -0,0 +1,10 @@ +**PROTOCOL** + +# `WindowSceneGroup` + +A structure conforming to `WindowScene` can be added to an app's `scene`. + +## Properties +### `body` + +The group's content. diff --git a/Documentation/Reference/structs/Window.md b/Documentation/Reference/structs/Window.md new file mode 100644 index 0000000..0f942b8 --- /dev/null +++ b/Documentation/Reference/structs/Window.md @@ -0,0 +1,52 @@ +**STRUCT** + +# `Window` + +A structure representing a simple window type. + +Note that multiple instances of a window can be opened at the same time. + +## Properties +### `id` + +The window's identifier. + +### `content` + +The window's content. + +### `open` + +Whether an instance of the window type should be opened when the app is starting up. + +## Methods +### `init(id:open:content:)` + +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. + +### `createWindow(app:)` + +Get the storage for the window. +- Parameter app: The application. +- Returns: The storage. + +### `createGTUIWindow(app:)` + +Get the window. +- Parameter app: The application. +- Returns: The window. + +### `getViewStorage(window:)` + +Get the storage of the content view. +- Parameter window: The window. +- Returns: The storage of the content of the window. + +### `update(_:)` + +Update a window storage's content. +- Parameter storage: The storage to update. diff --git a/Icons/HelloWorld.png b/Icons/HelloWorld.png new file mode 100644 index 0000000..562f0ed Binary files /dev/null and b/Icons/HelloWorld.png differ diff --git a/Icons/TwoWindows.png b/Icons/TwoWindows.png new file mode 100644 index 0000000..14ee3d7 Binary files /dev/null and b/Icons/TwoWindows.png differ diff --git a/README.md b/README.md index 4f3f1e1..8fc3134 100644 --- a/README.md +++ b/README.md @@ -108,18 +108,20 @@ brew install libadwaita ### Basics -* [Creating Views][12] +* [Hello World][12] +* [Creating Views][13] +* [Windows][14] ## Thanks ### Dependencies -- [SwiftGui][13] licensed under the [GPL-3.0 license][14] +- [SwiftGui][15] licensed under the [GPL-3.0 license][16] ### Other Thanks -- The [contributors][15] -- [SwiftLint][16] for checking whether code style conventions are violated -- The programming language [Swift][17] -- [SourceDocs][18] used for generating the [docs][19] +- The [contributors][17] +- [SwiftLint][18] for checking whether code style conventions are violated +- The programming language [Swift][19] +- [SourceDocs][20] used for generating the [docs][21] [1]: #goals [2]: #widgets @@ -133,12 +135,14 @@ brew install libadwaita [10]: https://brew.sh [11]: user-manual/GettingStarted.md [12]: user-manual/Basics/CreatingViews.md -[13]: https://github.com/JCWasmx86/SwiftGui -[14]: https://github.com/JCWasmx86/SwiftGui/blob/main/COPYING -[15]: Contributors.md -[16]: https://github.com/realm/SwiftLint -[17]: https://github.com/apple/swift -[18]: https://github.com/SourceDocs/SourceDocs -[19]: Documentation/Reference/README.md +[13] +[14] +[15]: https://github.com/JCWasmx86/SwiftGui +[16]: https://github.com/JCWasmx86/SwiftGui/blob/main/COPYING +[17]: Contributors.md +[18]: https://github.com/realm/SwiftLint +[19]: https://github.com/apple/swift +[20]: https://github.com/SourceDocs/SourceDocs +[21]: Documentation/Reference/README.md [image-1]: Icons/Screenshot.png diff --git a/SUMMARY.md b/SUMMARY.md index 0f5c60b..089dd30 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -5,8 +5,12 @@ ## Basics -* [Creating Views][3] +* [Hello World][3] +* [Creating Views][4] +* [Windows][5] [1]: README.md [2]: user-manual/GettingStarted.md -[3]: user-manual/Basics/CreatingViews.md \ No newline at end of file +[3]: user-manual/Basics/HelloWorld.md +[4]: user-manual/Basics/CreatingViews.md +[5]: user-manual/Basics/Windows.md diff --git a/Sources/Adwaita/Model/Extensions/Array.swift b/Sources/Adwaita/Model/Extensions/Array.swift index 68feabc..912a4e0 100644 --- a/Sources/Adwaita/Model/Extensions/Array.swift +++ b/Sources/Adwaita/Model/Extensions/Array.swift @@ -32,6 +32,16 @@ extension Array where Element == View { } +extension Array where Element == WindowSceneGroup { + + /// Get the content of an array of window scene groups. + /// - Returns: The array of windows. + public func windows() -> [WindowScene] { + flatMap { $0.windows() } + } + +} + extension Array { /// Accesses the element at the specified position safely. diff --git a/Sources/Adwaita/Model/User Interface/App.swift b/Sources/Adwaita/Model/User Interface/App.swift index 6194896..1bec892 100644 --- a/Sources/Adwaita/Model/User Interface/App.swift +++ b/Sources/Adwaita/Model/User Interface/App.swift @@ -46,11 +46,17 @@ extension App { var appInstance = self.init() appInstance.app = GTUIApp(appInstance.id) { appInstance } GTUIApp.updateHandlers.append { - for (windowIndex, window) in appInstance.scene.enumerated() { - if let stored = appInstance.app.sceneStorage[safe: windowIndex] { - window.widget().updateStorage(stored.view) + var removeIndices: [Int] = [] + for (index, window) in appInstance.app.sceneStorage.enumerated() { + if window.destroy { + removeIndices.insert(index, at: 0) + } else if let scene = appInstance.scene.windows().first(where: { $0.id == window.id }) { + scene.update(window) } } + for index in removeIndices { + appInstance.app.sceneStorage.remove(at: index) + } } appInstance.app.run() } diff --git a/Sources/Adwaita/Model/User Interface/GTUIApp.swift b/Sources/Adwaita/Model/User Interface/GTUIApp.swift index 8852b4e..e38b633 100644 --- a/Sources/Adwaita/Model/User Interface/GTUIApp.swift +++ b/Sources/Adwaita/Model/User Interface/GTUIApp.swift @@ -30,13 +30,27 @@ public class GTUIApp: Application { /// The entry point of the application. override public func onActivate() { let body = body() - for windowScene in body.scene { - let window = GTUI.Window(app: self) - let child = windowScene.storage() - let view = child.view - window.setChild(view) - sceneStorage.append(.init(window: window, view: child)) - window.show() + for windowScene in body.scene.windows() { + for _ in 0.. + /// The window type's identifier. + var id: String { get } + /// The number of instances of the window at the startup. + var `open`: Int { get } + /// Get the storage for the window. + /// - Parameter app: The application. + /// - Returns: The storage. + func createWindow(app: GTUIApp) -> WindowStorage + /// Update a window storage's content. + /// - Parameter storage: The storage to update. + func update(_ storage: WindowStorage) + +} + +extension WindowScene { + + /// The window scene's body is itself. + @SceneBuilder public var body: Scene { self } + +} diff --git a/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift b/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift new file mode 100644 index 0000000..41ae5a2 --- /dev/null +++ b/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift @@ -0,0 +1,47 @@ +// +// WindowSceneGroup.swift +// Adwaita +// +// Created by david-swift on 14.09.23. +// + +/// A structure conforming to `WindowScene` can be added to an app's `scene`. +public protocol WindowSceneGroup { + + /// The group's content. + @SceneBuilder var body: Scene { get } + +} + +extension WindowSceneGroup { + + /// Get the windows described by the group. + /// - Returns: The windows. + func windows() -> [WindowScene] { + var content: [WindowScene] = [] + for element in body { + if let window = element as? WindowScene { + content.append(window) + } else { + content += element.windows() + } + } + return content + } + + /// Update the windows described by the group. + /// - Parameter storage: The window's storage. + func update(_ storage: [WindowStorage]) { + for (index, window) in windows().enumerated() { + if let storage = storage[safe: index] { + window.update(storage) + } + } + } + +} + +/// `Scene` is an array of windows. +public typealias Scene = [WindowSceneGroup] +/// A builder for the `Scene` +public typealias SceneBuilder = ArrayBuilder diff --git a/Sources/Adwaita/Model/User Interface/WindowStorage.swift b/Sources/Adwaita/Model/User Interface/WindowStorage.swift index c29ab6e..dc1e04e 100644 --- a/Sources/Adwaita/Model/User Interface/WindowStorage.swift +++ b/Sources/Adwaita/Model/User Interface/WindowStorage.swift @@ -8,18 +8,24 @@ import GTUI /// A storage for an app's window. -class WindowStorage { +public class WindowStorage { + /// The window's identifier. + public var id: String + /// Whether the reference to the window should disappear in the next update. + public var destroy = false /// The GTUI window. - var window: Window + public var window: GTUI.Window /// The content's storage. - var view: ViewStorage + public var view: ViewStorage /// Initialize a window storage. /// - Parameters: + /// - id: The window's identifier. /// - window: The GTUI window. /// - view: The content's storage. - init(window: Window, view: ViewStorage) { + public init(id: String, window: GTUI.Window, view: ViewStorage) { + self.id = id self.window = window self.view = view } diff --git a/Sources/Adwaita/Window/Window.swift b/Sources/Adwaita/Window/Window.swift new file mode 100644 index 0000000..ff7e632 --- /dev/null +++ b/Sources/Adwaita/Window/Window.swift @@ -0,0 +1,71 @@ +// +// Window.swift +// Adwaita +// +// Created by david-swift on 14.09.23. +// + +import GTUI + +/// A structure representing a simple window type. +/// +/// Note that multiple instances of a window can be opened at the same time. +public struct Window: WindowScene { + + /// The window's identifier. + public var id: String + /// The window's content. + var content: (GTUI.Window) -> Body + /// Whether an instance of the window type should be opened when the app is starting up. + public var `open`: Int + + /// 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. + public init(id: String, `open`: Int = 1, @ViewBuilder content: @escaping (GTUI.Window) -> Body) { + self.content = content + self.id = id + self.open = open + } + + /// Get the storage for the window. + /// - Parameter app: The application. + /// - Returns: The storage. + public func createWindow(app: GTUIApp) -> WindowStorage { + let window = createGTUIWindow(app: app) + let storage = getViewStorage(window: window) + let windowStorage = WindowStorage(id: id, window: window, view: storage) + window.observeHide { + windowStorage.destroy = true + return false + } + return windowStorage + } + + /// Get the window. + /// - Parameter app: The application. + /// - Returns: The window. + func createGTUIWindow(app: GTUIApp) -> GTUI.Window { + let window = GTUI.Window(app: app) + window.show() + return window + } + + /// Get the storage of the content view. + /// - Parameter window: The window. + /// - Returns: The storage of the content of the window. + func getViewStorage(window: GTUI.Window) -> ViewStorage { + let storage = content(window).widget().container() + window.setChild(storage.view) + return storage + } + + /// Update a window storage's content. + /// - Parameter storage: The storage to update. + public func update(_ storage: WindowStorage) { + content(storage.window).widget().updateStorage(storage.view) + } + +} diff --git a/Tests/main.swift b/Tests/main.swift index cae1d54..b28c77c 100644 --- a/Tests/main.swift +++ b/Tests/main.swift @@ -16,12 +16,24 @@ struct Counter: App { var app: GTUIApp! var scene: Scene { - CounterWindow() + Window(id: "toggle") { _ in + Button("Add Window") { + app.addWindow("content-view") + } + .padding() + Button("Show Window") { + app.showWindow("content-view") + } + .padding(10, .horizontal.add(.bottom)) + } + Window(id: "content-view", open: 0) { _ in + ContentView() + } } } -struct CounterWindow: WindowScene { +struct ContentView: View { @State private var count = 0 diff --git a/user-manual/Basics/CreatingViews.md b/user-manual/Basics/CreatingViews.md index e31697b..db96e03 100644 --- a/user-manual/Basics/CreatingViews.md +++ b/user-manual/Basics/CreatingViews.md @@ -1,26 +1,13 @@ # Creating Views -This is a beginner tutorial. We will create a simple "Hello, world!" app using _Adwaita_. - -## Create the Swift Package -1. Open your terminal client and navigate to a directory you want to create the package in (e.g. `~/Documents/`). -2. Create a new folder for the package using `mkdir HelloWorld`. -3. Enter the newly created folder using `cd HelloWorld`. -4. Run `swift package init --type executable` for creating a new Swift package. -5. Open the Swift package. If you are using GNOME Builder, click on `Select a Folder…` in the view that appears after opening Builder and open the `HelloWorld` folder. - -## Add the Dependency -1. Open the `Package.swift` file. -2. Add the following line of code after `name: "HelloWorld",`: -``` -dependencies: [.package(url: "https://github.com/david-swift/Adwaita", from: "0.1.0")], -``` +Views are the building blocks of your application. +A view can be as simple as the `Text` widget you have seen in the previous tutorial, or as complex as the whole content of a single window. -## Create the App -1. Navigate to `Sources/main.swift`. -2. An app that uses the _Adwaita_ framework has a structure that conforms to the `App` protocol. The `scene` property returns one or more windows which provide content for display. An `@main` attribute marks it as the entry point of the app. - Replace `print("Hello, world!")` by your first app. We will later define `HelloWindow`: +## Add Views to a Window +You've already seen how to add views to a window: ```swift +import Adwaita + @main struct HelloWorld: App { @@ -28,35 +15,121 @@ struct HelloWorld: App { var app: GTUIApp! var scene: Scene { - HelloWindow() + Window(id: "content") { _ in + // These are the views: + HeaderBar.empty() + Text("Hello, world!") + .padding() + } } } ``` -## Create a View -1. Now, we will define `HelloWindow`. `HelloWindow` is a view, that means it conforms to the `View` protocol. As it additionally is a window, we’ll make it conform to the `WindowScene` which automatically adds conformance to `View`. +In this example, the widgets `HeaderBar` and `Text` are used. +`padding` is a view modifier, a function that modifies a view, which adds some padding around the text. + +## Create Custom Views +While directly adding widgets into the `Window`'s body might work for simple "Hello World" apps, +it can get very messy very quickly. +You can create custom views by declaring types that conform to the `View` protocol: ```swift -struct HelloWindow: WindowScene { +// A custom view named "ContentView": +struct ContentView: View { var view: Body { + HeaderBar.empty() Text("Hello, world!") .padding() } } ``` -2. Run the executable Swift package (in GNOME Builder, press the play button, on the command line, use `swift run`). - You can see that one important component of a window in GNOME is missing: The header bar. -3. If you add another view inside of the `view` property of `HelloWindow`, the views get aligned vertically: + +## Properties +As every structure in Swift, custom views can have properties: ```swift -struct HelloWindow: WindowScene { +struct HelloView: View { + // The property "text": + var text: String var view: Body { - HeaderBar.empty() - Text("Hello, world!") + Text("Hello, \(text)!") + .padding() + } + +} +``` +This view can be called via `HelloView(text: "world")` in another view. + +## State +Whenever you want to modify a property that is stored in the view's structure from your view, +wrap the property with the `@State` property wrapper: +```swift +struct MyView: View { + + // This property can be modified form within the view: + @State private var text = "world" + var view: Body { + Text("Hello, \(text)!") .padding() + Button("Change Text") { + text = Bool.random() ? "world" : "John" + } + .padding(10, [.vertical, .bottom]) + } + +} +``` +In this example, the text property is set whenever you press the "Change Text" button. + +## Change the State in Child Views +You can access state properties in child views in the same way as you would access any other property +if the child view cannot modify the state (`HelloView` is defined above): +```swift +struct MyView: View { + + @State private var text = "world" + var view: Body { + // "HelloView" can read the "text" property: + HelloView(text: text) + Button("Change Text") { + text = Bool.random() ? "world" : "John" + } + .padding(10, [.vertical, .bottom]) + } + +} +``` + +If the child view should be able to modify the state, use the `Binding` property wrapper in the child view +and pass the property with a dollar sign (`$`) to that view. +```swift +struct MyView: View { + + @State private var text = "world" + var view: Body { + HelloView(text: text) + // Pass the editable text property to the child view: + ChangeTextView(text: $text) + } + +} + +struct ChangeTextView: View { + + // Accept the editable text property: + @Binding var text: String + var view: Body { + Button("Change Text") { + // Binding properties can be used the same way as state properties: + text = Bool.random() ? "world" : "John" + } + .padding(10, [.vertical, .bottom]) } } ``` + +Whenever you modify a state property (directly or indirectly through bindings), +the user interface gets automatically updated to reflect that change. diff --git a/user-manual/Basics/HelloWorld.md b/user-manual/Basics/HelloWorld.md new file mode 100644 index 0000000..69dfa46 --- /dev/null +++ b/user-manual/Basics/HelloWorld.md @@ -0,0 +1,72 @@ +# Hello World + +![The "HelloWorld" app][image-1] + +This is a beginner tutorial. We will create a simple "Hello, world!" app using _Adwaita_. + +## Create the Swift Package +1. Open your terminal client and navigate to a directory you want to create the package in (e.g. `~/Documents/`). +2. Create a new folder for the package using `mkdir HelloWorld`. +3. Enter the newly created folder using `cd HelloWorld`. +4. Run `swift package init --type executable` for creating a new Swift package. +5. Open the Swift package. If you are using GNOME Builder, click on `Select a Folder…` in the view that appears after opening Builder and open the `HelloWorld` folder. + +## Add the Dependency +1. Open the `Package.swift` file. +2. Add the following line of code after `name: "HelloWorld",`: +``` +dependencies: [.package(url: "https://github.com/david-swift/Adwaita", from: "0.1.1")], +``` + +## Create the App +1. Navigate to `Sources/main.swift`. +2. An app that uses the _Adwaita_ framework has a structure that conforms to the `App` protocol. The `scene` property returns one or more windows which provide content for display. An `@main` attribute marks it as the entry point of the app. + Replace `print("Hello, world!")` by your first app: +```swift +import Adwaita + +@main +struct HelloWorld: App { + + let id = "io.github.david-swift.HelloWorld" + var app: GTUIApp! + + var scene: Scene { + Window(id: "content") { _ in + Text("Hello, world!") + .padding() + } + } + +} +``` + +## Test the App +1. Run the executable Swift package (in GNOME Builder, press the play button, on the command line, use `swift run`). + You can see that one important component of a window in GNOME is missing: The header bar. + +## Add a Header Bar +1. If you add another view inside of the `Window`'s body, the views get aligned vertically: +```swift +import Adwaita + +@main +struct HelloWorld: App { + + let id = "io.github.david-swift.HelloWorld" + var app: GTUIApp! + + var scene: Scene { + Window(id: "content") { _ in + // Add the header bar: + HeaderBar.empty() + Text("Hello, world!") + .padding() + } + } + +} +``` +2. Run the app. + +[image-1]: ../../Icons/HelloWorld.png diff --git a/user-manual/Basics/Windows.md b/user-manual/Basics/Windows.md new file mode 100644 index 0000000..0c6c362 --- /dev/null +++ b/user-manual/Basics/Windows.md @@ -0,0 +1,130 @@ +# Windows + +![Multiple windows in an app built with _Adwaita_][image-1] + +Windows in _Adwaita_ are not actually single windows in the user interface, +but rather instructions on how to create one type of window. + +## The Simplest Case +In the "HelloWorld" app, we have created a single window app. +Whenever that window was closed using the "x" button, the app terminated. +We can add multiple windows to an app. +Whenever the last one disappears, the app terminates. +```swift +@main +struct HelloWorld: App { + + let id = "io.github.david-swift.HelloWorld" + var app: GTUIApp! + + var scene: Scene { + Window(id: "content") { _ in + HeaderBar.empty() + Text("Hello, world!") + .padding() + } + // Add a second window: + Window(id: "window-2") { _ in + HeaderBar.empty() + Text("Window 2") + .padding() + } + } + +} +``` + +## Showing Windows +Every app contains the property `app`. +You can use this property for running functions that affect the whole app, e.g. quitting the app. +Another use case is showing a window: +```swift +@main +struct HelloWorld: App { + + let id = "io.github.david-swift.HelloWorld" + var app: GTUIApp! + + var scene: Scene { + Window(id: "content") { _ in + HeaderBar.empty() + Text("Hello, world!") + .padding() + } + Window(id: "control") { _ in + HeaderBar.empty() + Button("Show Window") { + // Show the window with the identifier "content": + app.showWindow("content") + } + .padding() + } + } + +} +``` +"Showing" a window means creating an instance of the window type if there isn't one, +or focusing the window that already exists of that type. +It should be used for opening windows that cannot be presented more than once +and for moving a window that is already open into the foreground. + +## Adding Windows +You can call the `addWindow(_:)` function instead of the `showWindow(_:)` +if you want to add and focus another instance of a window type: +```swift +@main +struct HelloWorld: App { + + let id = "io.github.david-swift.HelloWorld" + var app: GTUIApp! + + var scene: Scene { + Window(id: "content") { _ in + HeaderBar.empty() + Text("Hello, world!") + .padding() + } + Window(id: "control") { _ in + HeaderBar.empty() + Button("Add Window") { + // Add a new instance of the "content" window type + app.addWindow("content") + } + .padding() + } + } + +} +``` + +## Customizing the Initial Number of Windows +By default, every window type of the app's scene appears once when the app starts. +It's possible to customize how many windows are being presented at the app's startup: +```swift +@main +struct HelloWorld: App { + + let id = "io.github.david-swift.HelloWorld" + var app: GTUIApp! + + var scene: Scene { + // Open no window of the "content" type + Window(id: "content", open: 0) { _ in + HeaderBar.empty() + Text("Hello, world!") + .padding() + } + // Open two windows of the "control" type + Window(id: "control", open: 2) { _ in + HeaderBar.empty() + Button("Show Window") { + app.addWindow("content") + } + .padding() + } + } + +} +``` + +[image-1]: ../../Icons/TwoWindows.png