From 2611e0c4489b8ab12861031aa9ca6ec2c4d9ad1e Mon Sep 17 00:00:00 2001 From: david-swift Date: Sun, 24 Sep 2023 15:24:59 +0200 Subject: [PATCH] Improve window management --- Documentation/Reference/README.md | 4 + Documentation/Reference/classes/GTUIApp.md | 12 ++ .../Reference/classes/WindowStorage.md | 11 +- Documentation/Reference/extensions/Array.md | 5 + .../Reference/extensions/WindowScene.md | 9 +- .../Reference/extensions/WindowSceneGroup.md | 14 ++ .../Reference/protocols/WindowScene.md | 23 ++- .../Reference/protocols/WindowSceneGroup.md | 10 ++ Documentation/Reference/structs/Window.md | 52 +++++++ Icons/HelloWorld.png | Bin 0 -> 7651 bytes Icons/TwoWindows.png | Bin 0 -> 47565 bytes README.md | 32 ++-- SUMMARY.md | 8 +- Sources/Adwaita/Model/Extensions/Array.swift | 10 ++ .../Adwaita/Model/User Interface/App.swift | 12 +- .../Model/User Interface/GTUIApp.swift | 28 +++- .../Model/User Interface/WindowScene.swift | 28 +++- .../User Interface/WindowSceneGroup.swift | 47 ++++++ .../Model/User Interface/WindowStorage.swift | 14 +- Sources/Adwaita/Window/Window.swift | 71 +++++++++ Tests/main.swift | 16 +- user-manual/Basics/CreatingViews.md | 143 +++++++++++++----- user-manual/Basics/HelloWorld.md | 72 +++++++++ user-manual/Basics/Windows.md | 130 ++++++++++++++++ 24 files changed, 670 insertions(+), 81 deletions(-) create mode 100644 Documentation/Reference/extensions/WindowSceneGroup.md create mode 100644 Documentation/Reference/protocols/WindowSceneGroup.md create mode 100644 Documentation/Reference/structs/Window.md create mode 100644 Icons/HelloWorld.png create mode 100644 Icons/TwoWindows.png create mode 100644 Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift create mode 100644 Sources/Adwaita/Window/Window.swift create mode 100644 user-manual/Basics/HelloWorld.md create mode 100644 user-manual/Basics/Windows.md 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 0000000000000000000000000000000000000000..53527024078178bec10d221252ba4ee3ce93af15 GIT binary patch literal 7651 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 literal 0 HcmV?d00001 diff --git a/Icons/TwoWindows.png b/Icons/TwoWindows.png new file mode 100644 index 0000000000000000000000000000000000000000..14ee3d75fb8727bc3baa25698001cf4f5abe0a00 GIT binary patch literal 47565 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 4f3f1e1..bc6911c 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 @@ -132,13 +134,15 @@ brew install libadwaita [9]: https://github.com/JCWasmx86/SwiftGui [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 +[12]: user-manual/Basics/HelloWorld.md +[13]: user-manual/Basics/CreatingViews.md +[14]: user-manual/Basics/Windows.md +[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..2210017 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_. +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 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")], -``` - -## 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,29 +15,27 @@ 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`. -```swift -struct HelloWindow: WindowScene { +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. - var view: Body { - 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: +## 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() @@ -60,3 +45,91 @@ struct HelloWindow: WindowScene { } ``` + +## Properties +As every structure in Swift, custom views can have properties: +```swift +struct HelloView: View { + + // The property "text": + var text: String + var view: Body { + 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, .horizontal.add(.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, .horizontal.add(.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, .horizontal.add(.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