forked from aparoksha/adwaita-swift
Improve window management
This commit is contained in:
parent
81426e2e0c
commit
2611e0c448
@ -7,6 +7,7 @@
|
|||||||
- [View](protocols/View.md)
|
- [View](protocols/View.md)
|
||||||
- [Widget](protocols/Widget.md)
|
- [Widget](protocols/Widget.md)
|
||||||
- [WindowScene](protocols/WindowScene.md)
|
- [WindowScene](protocols/WindowScene.md)
|
||||||
|
- [WindowSceneGroup](protocols/WindowSceneGroup.md)
|
||||||
|
|
||||||
## Structs
|
## Structs
|
||||||
|
|
||||||
@ -19,6 +20,7 @@
|
|||||||
- [Text](structs/Text.md)
|
- [Text](structs/Text.md)
|
||||||
- [UpdateObserver](structs/UpdateObserver.md)
|
- [UpdateObserver](structs/UpdateObserver.md)
|
||||||
- [VStack](structs/VStack.md)
|
- [VStack](structs/VStack.md)
|
||||||
|
- [Window](structs/Window.md)
|
||||||
|
|
||||||
## Classes
|
## Classes
|
||||||
|
|
||||||
@ -42,6 +44,8 @@
|
|||||||
- [String](extensions/String.md)
|
- [String](extensions/String.md)
|
||||||
- [View](extensions/View.md)
|
- [View](extensions/View.md)
|
||||||
- [Widget](extensions/Widget.md)
|
- [Widget](extensions/Widget.md)
|
||||||
|
- [WindowScene](extensions/WindowScene.md)
|
||||||
|
- [WindowSceneGroup](extensions/WindowSceneGroup.md)
|
||||||
|
|
||||||
## Typealiases
|
## Typealiases
|
||||||
|
|
||||||
|
|||||||
@ -28,3 +28,15 @@ Initialize the GTUI application.
|
|||||||
### `onActivate()`
|
### `onActivate()`
|
||||||
|
|
||||||
The entry point of the application.
|
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.
|
A storage for an app's window.
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
### `id`
|
||||||
|
|
||||||
|
The window's identifier.
|
||||||
|
|
||||||
|
### `destroy`
|
||||||
|
|
||||||
|
Whether the reference to the window should disappear in the next update.
|
||||||
|
|
||||||
### `window`
|
### `window`
|
||||||
|
|
||||||
The GTUI window.
|
The GTUI window.
|
||||||
@ -14,9 +22,10 @@ The GTUI window.
|
|||||||
The content's storage.
|
The content's storage.
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
### `init(window:view:)`
|
### `init(id:window:view:)`
|
||||||
|
|
||||||
Initialize a window storage.
|
Initialize a window storage.
|
||||||
- Parameters:
|
- Parameters:
|
||||||
|
- id: The window's identifier.
|
||||||
- window: The GTUI window.
|
- window: The GTUI window.
|
||||||
- view: The content's storage.
|
- view: The content's storage.
|
||||||
|
|||||||
@ -14,6 +14,11 @@ Update a collection of views with a collection of view storages.
|
|||||||
- Parameters:
|
- Parameters:
|
||||||
- storage: The collection of view storages.
|
- storage: The collection of view storages.
|
||||||
|
|
||||||
|
### `windows()`
|
||||||
|
|
||||||
|
Get the content of an array of window scene groups.
|
||||||
|
- Returns: The array of windows.
|
||||||
|
|
||||||
### `checkIndex(_:)`
|
### `checkIndex(_:)`
|
||||||
|
|
||||||
Check if a given index is valid for the array.
|
Check if a given index is valid for the array.
|
||||||
|
|||||||
@ -2,10 +2,7 @@
|
|||||||
|
|
||||||
# `WindowScene`
|
# `WindowScene`
|
||||||
|
|
||||||
## Methods
|
## Properties
|
||||||
### `getWindow(app:)`
|
### `body`
|
||||||
|
|
||||||
Get the `GTUI.Window` with the content.
|
The window scene's body is itself.
|
||||||
- Parameters:
|
|
||||||
- app: The application.
|
|
||||||
- Returns: The window.
|
|
||||||
|
|||||||
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`
|
# `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
|
### Basics
|
||||||
|
|
||||||
* [Creating Views][12]
|
* [Hello World][12]
|
||||||
|
* [Creating Views][13]
|
||||||
|
* [Windows][14]
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
- [SwiftGui][13] licensed under the [GPL-3.0 license][14]
|
- [SwiftGui][15] licensed under the [GPL-3.0 license][16]
|
||||||
|
|
||||||
### Other Thanks
|
### Other Thanks
|
||||||
- The [contributors][15]
|
- The [contributors][17]
|
||||||
- [SwiftLint][16] for checking whether code style conventions are violated
|
- [SwiftLint][18] for checking whether code style conventions are violated
|
||||||
- The programming language [Swift][17]
|
- The programming language [Swift][19]
|
||||||
- [SourceDocs][18] used for generating the [docs][19]
|
- [SourceDocs][20] used for generating the [docs][21]
|
||||||
|
|
||||||
[1]: #goals
|
[1]: #goals
|
||||||
[2]: #widgets
|
[2]: #widgets
|
||||||
@ -132,13 +134,15 @@ brew install libadwaita
|
|||||||
[9]: https://github.com/JCWasmx86/SwiftGui
|
[9]: https://github.com/JCWasmx86/SwiftGui
|
||||||
[10]: https://brew.sh
|
[10]: https://brew.sh
|
||||||
[11]: user-manual/GettingStarted.md
|
[11]: user-manual/GettingStarted.md
|
||||||
[12]: user-manual/Basics/CreatingViews.md
|
[12]: user-manual/Basics/HelloWorld.md
|
||||||
[13]: https://github.com/JCWasmx86/SwiftGui
|
[13]: user-manual/Basics/CreatingViews.md
|
||||||
[14]: https://github.com/JCWasmx86/SwiftGui/blob/main/COPYING
|
[14]: user-manual/Basics/Windows.md
|
||||||
[15]: Contributors.md
|
[15]: https://github.com/JCWasmx86/SwiftGui
|
||||||
[16]: https://github.com/realm/SwiftLint
|
[16]: https://github.com/JCWasmx86/SwiftGui/blob/main/COPYING
|
||||||
[17]: https://github.com/apple/swift
|
[17]: Contributors.md
|
||||||
[18]: https://github.com/SourceDocs/SourceDocs
|
[18]: https://github.com/realm/SwiftLint
|
||||||
[19]: Documentation/Reference/README.md
|
[19]: https://github.com/apple/swift
|
||||||
|
[20]: https://github.com/SourceDocs/SourceDocs
|
||||||
|
[21]: Documentation/Reference/README.md
|
||||||
|
|
||||||
[image-1]: Icons/Screenshot.png
|
[image-1]: Icons/Screenshot.png
|
||||||
|
|||||||
@ -5,8 +5,12 @@
|
|||||||
|
|
||||||
## Basics
|
## Basics
|
||||||
|
|
||||||
* [Creating Views][3]
|
* [Hello World][3]
|
||||||
|
* [Creating Views][4]
|
||||||
|
* [Windows][5]
|
||||||
|
|
||||||
[1]: README.md
|
[1]: README.md
|
||||||
[2]: user-manual/GettingStarted.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 {
|
extension Array {
|
||||||
|
|
||||||
/// Accesses the element at the specified position safely.
|
/// Accesses the element at the specified position safely.
|
||||||
|
|||||||
@ -46,11 +46,17 @@ extension App {
|
|||||||
var appInstance = self.init()
|
var appInstance = self.init()
|
||||||
appInstance.app = GTUIApp(appInstance.id) { appInstance }
|
appInstance.app = GTUIApp(appInstance.id) { appInstance }
|
||||||
GTUIApp.updateHandlers.append {
|
GTUIApp.updateHandlers.append {
|
||||||
for (windowIndex, window) in appInstance.scene.enumerated() {
|
var removeIndices: [Int] = []
|
||||||
if let stored = appInstance.app.sceneStorage[safe: windowIndex] {
|
for (index, window) in appInstance.app.sceneStorage.enumerated() {
|
||||||
window.widget().updateStorage(stored.view)
|
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()
|
appInstance.app.run()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,13 +30,27 @@ public class GTUIApp: Application {
|
|||||||
/// The entry point of the application.
|
/// The entry point of the application.
|
||||||
override public func onActivate() {
|
override public func onActivate() {
|
||||||
let body = body()
|
let body = body()
|
||||||
for windowScene in body.scene {
|
for windowScene in body.scene.windows() {
|
||||||
let window = GTUI.Window(app: self)
|
for _ in 0..<windowScene.open {
|
||||||
let child = windowScene.storage()
|
sceneStorage.append(windowScene.createWindow(app: self))
|
||||||
let view = child.view
|
}
|
||||||
window.setChild(view)
|
}
|
||||||
sceneStorage.append(.init(window: window, view: child))
|
}
|
||||||
window.show()
|
|
||||||
|
/// 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
|
import GTUI
|
||||||
|
|
||||||
/// A structure conforming to `WindowScene` can be added to an app's `scene`.
|
/// A structure representing the content for a certain window type.
|
||||||
public protocol WindowScene: View { }
|
public protocol WindowScene: WindowSceneGroup {
|
||||||
|
|
||||||
/// `Scene` is an array of windows.
|
/// The window type's identifier.
|
||||||
public typealias Scene = [WindowScene]
|
var id: String { get }
|
||||||
/// A builder for the `Scene`
|
/// The number of instances of the window at the startup.
|
||||||
public typealias SceneBuilder = ArrayBuilder<WindowScene>
|
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
|
import GTUI
|
||||||
|
|
||||||
/// A storage for an app's window.
|
/// 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.
|
/// The GTUI window.
|
||||||
var window: Window
|
public var window: GTUI.Window
|
||||||
/// The content's storage.
|
/// The content's storage.
|
||||||
var view: ViewStorage
|
public var view: ViewStorage
|
||||||
|
|
||||||
/// Initialize a window storage.
|
/// Initialize a window storage.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
|
/// - id: The window's identifier.
|
||||||
/// - window: The GTUI window.
|
/// - window: The GTUI window.
|
||||||
/// - view: The content's storage.
|
/// - 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.window = window
|
||||||
self.view = view
|
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 app: GTUIApp!
|
||||||
|
|
||||||
var scene: Scene {
|
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
|
@State private var count = 0
|
||||||
|
|
||||||
|
|||||||
@ -1,26 +1,13 @@
|
|||||||
# Creating Views
|
# 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
|
## Add Views to a Window
|
||||||
1. Open your terminal client and navigate to a directory you want to create the package in (e.g. `~/Documents/`).
|
You've already seen how to add views to a window:
|
||||||
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`:
|
|
||||||
```swift
|
```swift
|
||||||
|
import Adwaita
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct HelloWorld: App {
|
struct HelloWorld: App {
|
||||||
|
|
||||||
@ -28,29 +15,27 @@ struct HelloWorld: App {
|
|||||||
var app: GTUIApp!
|
var app: GTUIApp!
|
||||||
|
|
||||||
var scene: Scene {
|
var scene: Scene {
|
||||||
HelloWindow()
|
Window(id: "content") { _ in
|
||||||
|
// These are the views:
|
||||||
|
HeaderBar.empty()
|
||||||
|
Text("Hello, world!")
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create a View
|
In this example, the widgets `HeaderBar` and `Text` are used.
|
||||||
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`.
|
`padding` is a view modifier, a function that modifies a view, which adds some padding around the text.
|
||||||
```swift
|
|
||||||
struct HelloWindow: WindowScene {
|
|
||||||
|
|
||||||
var view: Body {
|
## Create Custom Views
|
||||||
Text("Hello, world!")
|
While directly adding widgets into the `Window`'s body might work for simple "Hello World" apps,
|
||||||
.padding()
|
it can get very messy very quickly.
|
||||||
}
|
You can create custom views by declaring types that conform to the `View` protocol:
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
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:
|
|
||||||
```swift
|
```swift
|
||||||
struct HelloWindow: WindowScene {
|
// A custom view named "ContentView":
|
||||||
|
struct ContentView: View {
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
HeaderBar.empty()
|
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