Improve window management
This commit is contained in:
parent
81426e2e0c
commit
2611e0c448
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
14
Documentation/Reference/extensions/WindowSceneGroup.md
Normal file
14
Documentation/Reference/extensions/WindowSceneGroup.md
Normal file
@ -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.
|
||||
@ -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.
|
||||
|
||||
10
Documentation/Reference/protocols/WindowSceneGroup.md
Normal file
10
Documentation/Reference/protocols/WindowSceneGroup.md
Normal file
@ -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.
|
||||
52
Documentation/Reference/structs/Window.md
Normal file
52
Documentation/Reference/structs/Window.md
Normal file
@ -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.
|
||||
BIN
Icons/HelloWorld.png
Normal file
BIN
Icons/HelloWorld.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
BIN
Icons/TwoWindows.png
Normal file
BIN
Icons/TwoWindows.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
32
README.md
32
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
|
||||
|
||||
@ -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
|
||||
[3]: user-manual/Basics/HelloWorld.md
|
||||
[4]: user-manual/Basics/CreatingViews.md
|
||||
[5]: user-manual/Basics/Windows.md
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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..<windowScene.open {
|
||||
sceneStorage.append(windowScene.createWindow(app: self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Focus the window with a certain id. Create the window if it doesn't already exist.
|
||||
/// - Parameters:
|
||||
/// - id: The window's id.
|
||||
public func showWindow(_ id: String) {
|
||||
sceneStorage.last { $0.id == id && !$0.destroy }?.window.show() ?? addWindow(id)
|
||||
}
|
||||
|
||||
/// Add a new window with the content of the window with a certain id.
|
||||
/// - Parameters:
|
||||
/// - id: The window's id.
|
||||
public func addWindow(_ id: String) {
|
||||
if let window = body().scene.windows().first(where: { $0.id == id }) {
|
||||
sceneStorage.append(window.createWindow(app: self))
|
||||
showWindow(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,26 @@
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A structure conforming to `WindowScene` can be added to an app's `scene`.
|
||||
public protocol WindowScene: View { }
|
||||
/// A structure representing the content for a certain window type.
|
||||
public protocol WindowScene: WindowSceneGroup {
|
||||
|
||||
/// `Scene` is an array of windows.
|
||||
public typealias Scene = [WindowScene]
|
||||
/// A builder for the `Scene`
|
||||
public typealias SceneBuilder = ArrayBuilder<WindowScene>
|
||||
/// 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 }
|
||||
|
||||
}
|
||||
|
||||
47
Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift
Normal file
47
Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift
Normal file
@ -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<WindowSceneGroup>
|
||||
@ -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
|
||||
}
|
||||
|
||||
71
Sources/Adwaita/Window/Window.swift
Normal file
71
Sources/Adwaita/Window/Window.swift
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
72
user-manual/Basics/HelloWorld.md
Normal file
72
user-manual/Basics/HelloWorld.md
Normal file
@ -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
|
||||
130
user-manual/Basics/Windows.md
Normal file
130
user-manual/Basics/Windows.md
Normal file
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user