Add widgets and demo application
This commit is contained in:
parent
2611e0c448
commit
cbbf08bd3b
@ -118,14 +118,6 @@ custom_rules:
|
||||
message: 'Spaces should be used instead of tabs.'
|
||||
severity: warning
|
||||
|
||||
string_literals:
|
||||
name: 'String Literals'
|
||||
regex: '(".*")|("""(.|\n)*""")'
|
||||
message: 'String literals should not be used. Disable this rule in String and LocalizedStringResource extensions.'
|
||||
match_kinds:
|
||||
- string
|
||||
severity: warning
|
||||
|
||||
# Thanks to the creator of the SwiftLint rule
|
||||
# "empty_first_line"
|
||||
# https://github.com/coteditor/CotEditor/blob/main/.swiftlint.yml
|
||||
|
||||
@ -14,10 +14,17 @@
|
||||
- [Binding](structs/Binding.md)
|
||||
- [Button](structs/Button.md)
|
||||
- [EitherView](structs/EitherView.md)
|
||||
- [HStack](structs/HStack.md)
|
||||
- [HeaderBar](structs/HeaderBar.md)
|
||||
- [InspectorWrapper](structs/InspectorWrapper.md)
|
||||
- [List](structs/List.md)
|
||||
- [NavigationSplitView](structs/NavigationSplitView.md)
|
||||
- [ScrollView](structs/ScrollView.md)
|
||||
- [State](structs/State.md)
|
||||
- [StateWrapper](structs/StateWrapper.md)
|
||||
- [StatusPage](structs/StatusPage.md)
|
||||
- [Text](structs/Text.md)
|
||||
- [ToolbarView](structs/ToolbarView.md)
|
||||
- [UpdateObserver](structs/UpdateObserver.md)
|
||||
- [VStack](structs/VStack.md)
|
||||
- [Window](structs/Window.md)
|
||||
|
||||
@ -15,7 +15,7 @@ The view's content.
|
||||
|
||||
### `state`
|
||||
|
||||
The view's state (used in `VStack`).
|
||||
The view's state (used in `StateWrapper`).
|
||||
|
||||
## Methods
|
||||
### `init(_:content:state:)`
|
||||
|
||||
@ -2,6 +2,11 @@
|
||||
|
||||
# `Array`
|
||||
|
||||
## Properties
|
||||
### `view`
|
||||
|
||||
The array's view body is the array itself.
|
||||
|
||||
## Methods
|
||||
### `widget()`
|
||||
|
||||
|
||||
@ -10,3 +10,7 @@ A label for main content in a view storage.
|
||||
### `transition`
|
||||
|
||||
A label for the transition data in a GTUI widget's fields.
|
||||
|
||||
### `navigationLabel`
|
||||
|
||||
A label for the navigation label in a GTUI widget's fields.
|
||||
|
||||
@ -44,6 +44,18 @@ Enable or disable the vertical expansion.
|
||||
- Parameter enabled: Whether it is enabled or disabled.
|
||||
- Returns: A view.
|
||||
|
||||
### `halign(_:)`
|
||||
|
||||
Set the horizontal alignment.
|
||||
- Parameter align: The alignment.
|
||||
- Returns: A view.
|
||||
|
||||
### `valign(_:)`
|
||||
|
||||
Set the vertical alignment.
|
||||
- Parameter align: The alignment.
|
||||
- Returns: A view.
|
||||
|
||||
### `frame(minWidth:minHeight:)`
|
||||
|
||||
Set the view's minimal width or height.
|
||||
@ -64,6 +76,40 @@ Set the view's transition.
|
||||
- Parameter transition: The transition.
|
||||
- Returns: A view.
|
||||
|
||||
### `navigationTitle(_:)`
|
||||
|
||||
Set the view's navigation title.
|
||||
- Parameter label: The navigation title.
|
||||
- Returns: A view.
|
||||
|
||||
### `style(_:)`
|
||||
|
||||
Add a style class to the view.
|
||||
- Parameter style: The style class.
|
||||
- Returns: A view.
|
||||
|
||||
### `onAppear(_:)`
|
||||
|
||||
Run a function when the view appears for the first time.
|
||||
- Parameter closure: The function.
|
||||
- Returns: A view.
|
||||
|
||||
### `topToolbar(visible:_:)`
|
||||
|
||||
Add a top toolbar to the view.
|
||||
- Parameters:
|
||||
- toolbar: The toolbar's content.
|
||||
- visible: Whether the toolbar is visible.
|
||||
- Returns: A view.
|
||||
|
||||
### `bottomToolbar(visible:_:)`
|
||||
|
||||
Add a bottom toolbar to the view.
|
||||
- Parameters:
|
||||
- toolbar: The toolbar's content.
|
||||
- visible: Whether the toolbar is visible.
|
||||
- Returns: A view.
|
||||
|
||||
### `onUpdate(_:)`
|
||||
|
||||
Run a function when the view gets an update.
|
||||
|
||||
26
Documentation/Reference/structs/HStack.md
Normal file
26
Documentation/Reference/structs/HStack.md
Normal file
@ -0,0 +1,26 @@
|
||||
**STRUCT**
|
||||
|
||||
# `HStack`
|
||||
|
||||
A horizontal GtkBox equivalent.
|
||||
|
||||
## Properties
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
## Methods
|
||||
### `init(content:)`
|
||||
|
||||
Initialize a `HStack`.
|
||||
- Parameter content: The view content.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameter storage: The view storage.
|
||||
|
||||
### `container()`
|
||||
|
||||
Get a view storage.
|
||||
- Returns: The view storage.
|
||||
46
Documentation/Reference/structs/List.md
Normal file
46
Documentation/Reference/structs/List.md
Normal file
@ -0,0 +1,46 @@
|
||||
**STRUCT**
|
||||
|
||||
# `List`
|
||||
|
||||
A list box widget.
|
||||
|
||||
## Properties
|
||||
### `elements`
|
||||
|
||||
The elements.
|
||||
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
### `selection`
|
||||
|
||||
The identifier of the selected element.
|
||||
|
||||
## Methods
|
||||
### `init(_:selection:content:)`
|
||||
|
||||
Initialize `ForEach`.
|
||||
- Parameters:
|
||||
- elements: The elements.
|
||||
- selection: The identifier of the selected element.
|
||||
- content: The view for an element.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameter storage: The view storage.
|
||||
|
||||
### `container()`
|
||||
|
||||
Get a view storage.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `updateSelection(box:)`
|
||||
|
||||
Update the list's selection.
|
||||
- Parameter box: The list box.
|
||||
|
||||
### `sidebarStyle()`
|
||||
|
||||
Add the "navigation-sidebar" style class.
|
||||
40
Documentation/Reference/structs/NavigationSplitView.md
Normal file
40
Documentation/Reference/structs/NavigationSplitView.md
Normal file
@ -0,0 +1,40 @@
|
||||
**STRUCT**
|
||||
|
||||
# `NavigationSplitView`
|
||||
|
||||
A navigation split view widget.
|
||||
|
||||
## Properties
|
||||
### `sidebar`
|
||||
|
||||
The sidebar's content.
|
||||
|
||||
### `content`
|
||||
|
||||
The split view's main content.
|
||||
|
||||
### `sidebarID`
|
||||
|
||||
The sidebar content's id.
|
||||
|
||||
### `contentID`
|
||||
|
||||
The main content's id.
|
||||
|
||||
## Methods
|
||||
### `init(sidebar:content:)`
|
||||
|
||||
Initialize a navigation split view.
|
||||
- Parameters:
|
||||
- sidebar: The sidebar content.
|
||||
- content: The main content.
|
||||
|
||||
### `container()`
|
||||
|
||||
Get the container of the navigation split view widget.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update the view storage of the navigation split view widget.
|
||||
- Parameter storage: The view storage.
|
||||
26
Documentation/Reference/structs/ScrollView.md
Normal file
26
Documentation/Reference/structs/ScrollView.md
Normal file
@ -0,0 +1,26 @@
|
||||
**STRUCT**
|
||||
|
||||
# `ScrollView`
|
||||
|
||||
A GtkScrolledWindow equivalent.
|
||||
|
||||
## Properties
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
## Methods
|
||||
### `init(content:)`
|
||||
|
||||
Initialize a `ScrollView`.
|
||||
- Parameter content: The view content.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameter storage: The view storage.
|
||||
|
||||
### `container()`
|
||||
|
||||
Get a view storage.
|
||||
- Returns: The view storage.
|
||||
37
Documentation/Reference/structs/StateWrapper.md
Normal file
37
Documentation/Reference/structs/StateWrapper.md
Normal file
@ -0,0 +1,37 @@
|
||||
**STRUCT**
|
||||
|
||||
# `StateWrapper`
|
||||
|
||||
A storage for `@State` properties.
|
||||
|
||||
## Properties
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
### `state`
|
||||
|
||||
The state information (from properties with the `State` wrapper).
|
||||
|
||||
## Methods
|
||||
### `init(content:)`
|
||||
|
||||
Initialize a `StateWrapper`.
|
||||
- Parameter content: The view content.
|
||||
|
||||
### `init(content:state:)`
|
||||
|
||||
Initialize a `StateWrapper`.
|
||||
- Parameters:
|
||||
- content: The view content.
|
||||
- state: The state information.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameter storage: The view storage.
|
||||
|
||||
### `container()`
|
||||
|
||||
Get a view storage.
|
||||
- Returns: The view storage.
|
||||
42
Documentation/Reference/structs/StatusPage.md
Normal file
42
Documentation/Reference/structs/StatusPage.md
Normal file
@ -0,0 +1,42 @@
|
||||
**STRUCT**
|
||||
|
||||
# `StatusPage`
|
||||
|
||||
A status page widget.
|
||||
|
||||
## Properties
|
||||
### `title`
|
||||
|
||||
The title.
|
||||
|
||||
### `description`
|
||||
|
||||
The description.
|
||||
|
||||
### `icon`
|
||||
|
||||
The icon.
|
||||
|
||||
### `content`
|
||||
|
||||
Additional content.
|
||||
|
||||
## Methods
|
||||
### `init(_:icon:description:content:)`
|
||||
|
||||
Initialize a status page widget.
|
||||
- Parameters:
|
||||
- title: The title.
|
||||
- icon: The icon.
|
||||
- description: Additional details.
|
||||
- content: Additional content.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update the view storage of the text widget.
|
||||
- Parameter storage: The view storage.
|
||||
|
||||
### `container()`
|
||||
|
||||
Get the container of the text widget.
|
||||
- Returns: The view storage.
|
||||
37
Documentation/Reference/structs/ToolbarView.md
Normal file
37
Documentation/Reference/structs/ToolbarView.md
Normal file
@ -0,0 +1,37 @@
|
||||
**STRUCT**
|
||||
|
||||
# `ToolbarView`
|
||||
|
||||
A toolbar view widget.
|
||||
|
||||
## Properties
|
||||
### `content`
|
||||
|
||||
The sidebar's content.
|
||||
|
||||
### `toolbar`
|
||||
|
||||
The toolbars.
|
||||
|
||||
### `bottom`
|
||||
|
||||
Whether the toolbars are bottom toolbars.
|
||||
|
||||
### `visible`
|
||||
|
||||
Whether the toolbar is visible.
|
||||
|
||||
### `toolbarID`
|
||||
|
||||
The identifier of the toolbar content.
|
||||
|
||||
## Methods
|
||||
### `container()`
|
||||
|
||||
Get the container of the toolbar view widget.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update the view storage of the toolbar view widget.
|
||||
- Parameter storage: The view storage.
|
||||
@ -9,23 +9,12 @@ A GtkBox equivalent.
|
||||
|
||||
The content.
|
||||
|
||||
### `state`
|
||||
|
||||
The state information (from properties with the `State` wrapper).
|
||||
|
||||
## Methods
|
||||
### `init(content:)`
|
||||
|
||||
Initialize a `VStack`.
|
||||
- Parameter content: The view content.
|
||||
|
||||
### `init(content:state:)`
|
||||
|
||||
Initialize a `VStack`.
|
||||
- Parameters:
|
||||
- content: The view content.
|
||||
- state: The state information.
|
||||
|
||||
### `update(_:)`
|
||||
|
||||
Update a view storage.
|
||||
|
||||
BIN
Icons/Demo.png
Normal file
BIN
Icons/Demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.2 KiB |
@ -26,7 +26,7 @@ let package = Package(
|
||||
dependencies: [.product(name: "GTUI", package: "swiftgui")]
|
||||
),
|
||||
.executableTarget(
|
||||
name: "Counter",
|
||||
name: "Swift Adwaita Demo",
|
||||
dependencies: ["Adwaita"],
|
||||
path: "Tests"
|
||||
)
|
||||
|
||||
113
README.md
113
README.md
@ -27,6 +27,10 @@ struct Example: View {
|
||||
@State private var count = 0
|
||||
|
||||
var view: Body {
|
||||
Text("\(count)")
|
||||
.style("title-1")
|
||||
.padding(50)
|
||||
.topToolbar {
|
||||
HeaderBar.start {
|
||||
Button(icon: .default(icon: .goPrevious)) {
|
||||
count -= 1
|
||||
@ -35,8 +39,7 @@ struct Example: View {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
Text("\(count)")
|
||||
.padding(50)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -46,49 +49,66 @@ Creates a simple counter view:
|
||||
|
||||
![Counter Example][image-1]
|
||||
|
||||
More examples are available in the [Demo app][1]:
|
||||
|
||||
![Demo App][image-2]
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Goals][1]
|
||||
- [Widgets][2]
|
||||
- [Installation][3]
|
||||
- [Usage][4]
|
||||
- [Thanks][5]
|
||||
- [Goals][2]
|
||||
- [Widgets][3]
|
||||
- [Installation][4]
|
||||
- [Usage][5]
|
||||
- [Thanks][6]
|
||||
|
||||
## Goals
|
||||
|
||||
_Adwaita_’s main goal is to provide an easy-to-use interface for creating GNOME apps. The backend should stay as simple as possible, while not limiting the possibilities there are with [Libadwaita][6] and [GTK][7].
|
||||
_Adwaita_’s main goal is to provide an easy-to-use interface for creating GNOME apps. The backend should stay as simple as possible, while not limiting the possibilities there are with [Libadwaita][7] and [GTK][8].
|
||||
|
||||
If you want to use _Adwaita_ in a project, but there are widgets missing, open an [issue on GitHub][8].
|
||||
If you want to use _Adwaita_ in a project, but there are widgets missing, open an [issue on GitHub][9].
|
||||
|
||||
## Widgets
|
||||
|
||||
| Name | Description | Widget |
|
||||
| ---------- | ----------------------------------------------------------------- | ------------ |
|
||||
| -------------------- | ----------------------------------------------------------------- | ---------------------- |
|
||||
| Button | A widget that triggers a function when being clicked. | GtkButton |
|
||||
| EitherView | A widget that displays one of its child views based on a boolean. | GtkStack |
|
||||
| HeaderBar | A widget for creating custom title bars for windows. | GtkHeaderBar |
|
||||
| Text | A widget for displaying a small amount of text. | GtkLabel |
|
||||
| VStack | A widget which arranges child widgets into a single column. | GtkBox |
|
||||
| HStack | A widget which arranges child widgets into a single row. | GtkBox |
|
||||
| List | A widget which arranges child widgets vertically into rows. | GtkListBox |
|
||||
| NavigationSplitView | A widget presenting sidebar and content side by side. | AdwNavigationSplitView |
|
||||
| ScrollView | A container that makes its child scrollable. | GtkScrolledWindow |
|
||||
| StatusPage | A page with an icon, title, and optionally description and widget.| AdwStatusPage |
|
||||
| StateWrapper | A wrapper not affecting the UI which stores state information. | - |
|
||||
|
||||
### View Modifiers
|
||||
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| `inspect(_:)` | Edit the underlying [GTUI][9] widget. |
|
||||
| `inspect(_:)` | Edit the underlying [GTUI][10] widget. |
|
||||
| `padding(_:_:)` | Add empty space around a view. |
|
||||
| `hexpand(_:)` | Enable or disable the horizontal expansion of a view. |
|
||||
| `vexpand(_:)` | Enable or disable the vertical expansion of a view. |
|
||||
| `halign(_:)` | Set the horizontal alignment of a view. |
|
||||
| `valign(_:)` | Set the vertical alignment of a view. |
|
||||
| `frame(minWidth:minHeight:)` | Set the view’s minimal width or height. |
|
||||
| `frame(maxSize:)` | Set the view’s maximal size. |
|
||||
| `transition(_:)` | Assign a transition with the view that is used if it is a direct child of a HeaderBar. |
|
||||
| `transition(_:)` | Assign a transition with the view that is used if it is a direct child of an EitherView. |
|
||||
| `onUpdate(_:)` | Run a function every time a view gets updated. |
|
||||
| `navigationTitle(_:)` | Add a title that is used if the view is a direct child of a NavigationView. |
|
||||
| `style(_:)` | Add a style class to the view. |
|
||||
| `onAppear(_:)` | Run when the view is rendered for the first time. |
|
||||
| `topToolbar(visible:_:)` | Add a native toolbar to the view. Normally, it contains a HeaderBar. |
|
||||
| `bottomToolbar(visible:_:)` | Add a native bottom toolbar to the view. |
|
||||
|
||||
## Installation
|
||||
### Dependencies
|
||||
If you are using a Linux distribution, install `libadwaita-devel` or `libadwaita` (or something similar, based on the package manager) as well as `gtk4-devel`, `gtk4` or similar.
|
||||
|
||||
On macOS, follow these steps:
|
||||
1. Install [Homebrew][10].
|
||||
1. Install [Homebrew][11].
|
||||
2. Install Libadwaita (and thereby GTK 4):
|
||||
```
|
||||
brew install libadwaita
|
||||
@ -104,45 +124,52 @@ brew install libadwaita
|
||||
|
||||
## Usage
|
||||
|
||||
* [Getting Started][11]
|
||||
* [Getting Started][12]
|
||||
|
||||
### Basics
|
||||
|
||||
* [Hello World][12]
|
||||
* [Creating Views][13]
|
||||
* [Windows][14]
|
||||
* [Hello World][13]
|
||||
* [Creating Views][14]
|
||||
* [Windows][15]
|
||||
|
||||
### Advanced
|
||||
|
||||
* [Creating Widgets][16]
|
||||
|
||||
## Thanks
|
||||
|
||||
### Dependencies
|
||||
- [SwiftGui][15] licensed under the [GPL-3.0 license][16]
|
||||
- [SwiftGui][17] licensed under the [GPL-3.0 license][18]
|
||||
|
||||
### Other Thanks
|
||||
- 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]
|
||||
- The [contributors][19]
|
||||
- [SwiftLint][20] for checking whether code style conventions are violated
|
||||
- The programming language [Swift][21]
|
||||
- [SourceDocs][22] used for generating the [docs][23]
|
||||
|
||||
[1]: #goals
|
||||
[2]: #widgets
|
||||
[3]: #installation
|
||||
[4]: #usage
|
||||
[5]: #thanks
|
||||
[6]: https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/index.html
|
||||
[7]: https://docs.gtk.org/gtk4/
|
||||
[8]: https://github.com/david-swift/Adwaita/issues
|
||||
[9]: https://github.com/JCWasmx86/SwiftGui
|
||||
[10]: https://brew.sh
|
||||
[11]: user-manual/GettingStarted.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
|
||||
[1]: Tests/
|
||||
[2]: #goals
|
||||
[3]: #widgets
|
||||
[4]: #installation
|
||||
[5]: #usage
|
||||
[6]: #thanks
|
||||
[7]: https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/index.html
|
||||
[8]: https://docs.gtk.org/gtk4/
|
||||
[9]: https://github.com/david-swift/Adwaita/issues
|
||||
[10]: https://github.com/JCWasmx86/SwiftGui
|
||||
[11]: https://brew.sh
|
||||
[12]: user-manual/GettingStarted.md
|
||||
[13]: user-manual/Basics/HelloWorld.md
|
||||
[14]: user-manual/Basics/CreatingViews.md
|
||||
[15]: user-manual/Basics/Windows.md
|
||||
[16]: user-manual/Advanced/CreatingWidgets.md
|
||||
[17]: https://github.com/JCWasmx86/SwiftGui
|
||||
[18]: https://github.com/JCWasmx86/SwiftGui/blob/main/COPYING
|
||||
[19]: Contributors.md
|
||||
[20]: https://github.com/realm/SwiftLint
|
||||
[21]: https://github.com/apple/swift
|
||||
[22]: https://github.com/SourceDocs/SourceDocs
|
||||
[23]: Documentation/Reference/README.md
|
||||
|
||||
[image-1]: Icons/Screenshot.png
|
||||
[image-2]: Icons/Demo.png
|
||||
|
||||
@ -9,8 +9,13 @@
|
||||
* [Creating Views][4]
|
||||
* [Windows][5]
|
||||
|
||||
## Advanced
|
||||
|
||||
* [Creating Widgets][6]
|
||||
|
||||
[1]: README.md
|
||||
[2]: user-manual/GettingStarted.md
|
||||
[3]: user-manual/Basics/HelloWorld.md
|
||||
[4]: user-manual/Basics/CreatingViews.md
|
||||
[5]: user-manual/Basics/Windows.md
|
||||
[6]: user-manual/Advanced/CreatingWidgets.md
|
||||
|
||||
@ -7,7 +7,10 @@
|
||||
|
||||
import GTUI
|
||||
|
||||
extension Array where Element == View {
|
||||
extension Array: View where Element == View {
|
||||
|
||||
/// The array's view body is the array itself.
|
||||
public var view: Body { self }
|
||||
|
||||
/// Get a widget from a collection of views.
|
||||
/// - Returns: A widget.
|
||||
|
||||
@ -11,5 +11,7 @@ extension String {
|
||||
static var mainContent: Self { "main" }
|
||||
/// A label for the transition data in a GTUI widget's fields.
|
||||
static var transition: Self { "transition" }
|
||||
/// A label for the navigation label in a GTUI widget's fields.
|
||||
static var navigationLabel: Self { "navigation-label" }
|
||||
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ extension View {
|
||||
state[label] = value
|
||||
}
|
||||
}
|
||||
return VStack(content: { view }, state: state)
|
||||
return StateWrapper(content: { view }, state: state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,8 @@ extension View {
|
||||
func updateStorage(_ storage: ViewStorage) {
|
||||
if let widget = self as? Widget {
|
||||
widget.update(storage)
|
||||
} else {
|
||||
StateWrapper { self }.update(storage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ public class ViewStorage {
|
||||
public var view: NativeWidgetPeer
|
||||
/// The view's content.
|
||||
public var content: [String: [ViewStorage]]
|
||||
/// The view's state (used in `VStack`).
|
||||
/// The view's state (used in `StateWrapper`).
|
||||
public var state: [String: StateProtocol]
|
||||
|
||||
/// Initialize a view storage.
|
||||
|
||||
41
Sources/Adwaita/View/HStack.swift
Normal file
41
Sources/Adwaita/View/HStack.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// HStack.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 26.09.23.
|
||||
//
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A horizontal GtkBox equivalent.
|
||||
public struct HStack: Widget {
|
||||
|
||||
/// The content.
|
||||
var content: () -> Body
|
||||
|
||||
/// Initialize a `HStack`.
|
||||
/// - Parameter content: The view content.
|
||||
public init(@ViewBuilder content: @escaping () -> Body) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameter storage: The view storage.
|
||||
public func update(_ storage: ViewStorage) {
|
||||
content().update(storage.content[.mainContent] ?? [])
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Returns: The view storage.
|
||||
public func container() -> ViewStorage {
|
||||
let box: Box = .init(horizontal: true)
|
||||
var content: [ViewStorage] = []
|
||||
for element in self.content() {
|
||||
let widget = element.storage()
|
||||
_ = box.append(widget.view)
|
||||
content.append(widget)
|
||||
}
|
||||
return .init(box, content: [.mainContent: content])
|
||||
}
|
||||
|
||||
}
|
||||
76
Sources/Adwaita/View/List.swift
Normal file
76
Sources/Adwaita/View/List.swift
Normal file
@ -0,0 +1,76 @@
|
||||
//
|
||||
// List.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 25.09.23.
|
||||
//
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A list box widget.
|
||||
public struct List<Element>: Widget where Element: Identifiable {
|
||||
|
||||
/// The elements.
|
||||
var elements: [Element]
|
||||
/// The content.
|
||||
var content: (Element) -> Body
|
||||
/// The identifier of the selected element.
|
||||
@Binding var selection: Element.ID
|
||||
|
||||
/// Initialize `ForEach`.
|
||||
/// - Parameters:
|
||||
/// - elements: The elements.
|
||||
/// - selection: The identifier of the selected element.
|
||||
/// - content: The view for an element.
|
||||
public init(
|
||||
_ elements: [Element],
|
||||
selection: Binding<Element.ID>,
|
||||
@ViewBuilder content: @escaping (Element) -> Body
|
||||
) {
|
||||
self.content = content
|
||||
self.elements = elements
|
||||
self._selection = selection
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameter storage: The view storage.
|
||||
public func update(_ storage: ViewStorage) {
|
||||
if let box = storage.view as? ListBox {
|
||||
updateSelection(box: box)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Returns: The view storage.
|
||||
public func container() -> ViewStorage {
|
||||
let box: ListBox = .init()
|
||||
var content: [ViewStorage] = []
|
||||
for element in elements {
|
||||
let widget = self.content(element).widget().container()
|
||||
_ = box.append(widget.view)
|
||||
content.append(widget)
|
||||
}
|
||||
_ = box.handler {
|
||||
let selection = box.getSelectedRow()
|
||||
if let id = elements[safe: selection]?.id {
|
||||
self.selection = id
|
||||
}
|
||||
}
|
||||
updateSelection(box: box)
|
||||
return .init(box, content: [.mainContent: content])
|
||||
}
|
||||
|
||||
/// Update the list's selection.
|
||||
/// - Parameter box: The list box.
|
||||
func updateSelection(box: ListBox) {
|
||||
if let index = elements.firstIndex(where: { $0.id == selection }) {
|
||||
box.selectRow(at: index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the "navigation-sidebar" style class.
|
||||
public func sidebarStyle() -> View {
|
||||
style("navigation-sidebar")
|
||||
}
|
||||
|
||||
}
|
||||
@ -63,6 +63,20 @@ extension View {
|
||||
inspect { _ = $0?.vexpand() }
|
||||
}
|
||||
|
||||
/// Set the horizontal alignment.
|
||||
/// - Parameter align: The alignment.
|
||||
/// - Returns: A view.
|
||||
public func halign(_ align: Alignment) -> View {
|
||||
inspect { _ = $0?.halign(align) }
|
||||
}
|
||||
|
||||
/// Set the vertical alignment.
|
||||
/// - Parameter align: The alignment.
|
||||
/// - Returns: A view.
|
||||
public func valign(_ align: Alignment) -> View {
|
||||
inspect { _ = $0?.valign(align) }
|
||||
}
|
||||
|
||||
/// Set the view's minimal width or height.
|
||||
/// - Parameters:
|
||||
/// - minWidth: The minimal width.
|
||||
@ -86,4 +100,25 @@ extension View {
|
||||
inspect { $0?.fields[.transition] = transition }
|
||||
}
|
||||
|
||||
/// Set the view's navigation title.
|
||||
/// - Parameter label: The navigation title.
|
||||
/// - Returns: A view.
|
||||
public func navigationTitle(_ label: String) -> View {
|
||||
inspect { $0?.fields[.navigationLabel] = label }
|
||||
}
|
||||
|
||||
/// Add a style class to the view.
|
||||
/// - Parameter style: The style class.
|
||||
/// - Returns: A view.
|
||||
public func style(_ style: String) -> View {
|
||||
inspect { _ = $0?.addStyle(style) }
|
||||
}
|
||||
|
||||
/// Run a function when the view appears for the first time.
|
||||
/// - Parameter closure: The function.
|
||||
/// - Returns: A view.
|
||||
public func onAppear(_ closure: @escaping () -> Void) -> View {
|
||||
inspect { _ in closure() }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
90
Sources/Adwaita/View/Modifiers/ToolbarView.swift
Normal file
90
Sources/Adwaita/View/Modifiers/ToolbarView.swift
Normal file
@ -0,0 +1,90 @@
|
||||
//
|
||||
// ToolbarView.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 24.09.23.
|
||||
//
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A toolbar view widget.
|
||||
struct ToolbarView: Widget {
|
||||
|
||||
/// The sidebar's content.
|
||||
var content: View
|
||||
/// The toolbars.
|
||||
var toolbar: () -> Body
|
||||
/// Whether the toolbars are bottom toolbars.
|
||||
var bottom: Bool
|
||||
/// Whether the toolbar is visible.
|
||||
var visible: Bool
|
||||
|
||||
/// The identifier of the toolbar content.
|
||||
let toolbarID = "toolbar"
|
||||
|
||||
/// Get the container of the toolbar view widget.
|
||||
/// - Returns: The view storage.
|
||||
func container() -> ViewStorage {
|
||||
let content = content.storage()
|
||||
let view = GTUI.ToolbarView(content.view)
|
||||
var toolbarContent: [ViewStorage] = []
|
||||
for item in toolbar() {
|
||||
let storage = item.storage()
|
||||
toolbarContent.append(storage)
|
||||
if bottom {
|
||||
_ = view.addBottomBar(storage.view)
|
||||
} else {
|
||||
_ = view.addTopBar(storage.view)
|
||||
}
|
||||
}
|
||||
if bottom {
|
||||
view.setRevealBottomBar(visible)
|
||||
} else {
|
||||
view.setRevealTopBar(visible)
|
||||
}
|
||||
return .init(view, content: [.mainContent: [content], toolbarID: toolbarContent])
|
||||
}
|
||||
|
||||
/// Update the view storage of the toolbar view widget.
|
||||
/// - Parameter storage: The view storage.
|
||||
func update(_ storage: ViewStorage) {
|
||||
if let mainContent = storage.content[.mainContent]?.first {
|
||||
content.widget().update(mainContent)
|
||||
}
|
||||
if let toolbar = storage.content[toolbarID] {
|
||||
for (index, content) in toolbar.enumerated() {
|
||||
self.toolbar()[safe: index]?.updateStorage(content)
|
||||
}
|
||||
}
|
||||
if let view = storage.view as? GTUI.ToolbarView {
|
||||
if bottom {
|
||||
view.setRevealBottomBar(visible)
|
||||
} else {
|
||||
view.setRevealTopBar(visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
/// Add a top toolbar to the view.
|
||||
/// - Parameters:
|
||||
/// - toolbar: The toolbar's content.
|
||||
/// - visible: Whether the toolbar is visible.
|
||||
/// - Returns: A view.
|
||||
public func topToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> View {
|
||||
ToolbarView(content: self, toolbar: toolbar, bottom: false, visible: visible)
|
||||
}
|
||||
|
||||
/// Add a bottom toolbar to the view.
|
||||
/// - Parameters:
|
||||
/// - toolbar: The toolbar's content.
|
||||
/// - visible: Whether the toolbar is visible.
|
||||
/// - Returns: A view.
|
||||
public func bottomToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> View {
|
||||
ToolbarView(content: self, toolbar: toolbar, bottom: true, visible: visible)
|
||||
}
|
||||
|
||||
}
|
||||
62
Sources/Adwaita/View/NavigationSplitView.swift
Normal file
62
Sources/Adwaita/View/NavigationSplitView.swift
Normal file
@ -0,0 +1,62 @@
|
||||
//
|
||||
// NavigationSplitView.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 24.09.23.
|
||||
//
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A navigation split view widget.
|
||||
public struct NavigationSplitView: Widget {
|
||||
|
||||
/// The sidebar's content.
|
||||
var sidebar: () -> Body
|
||||
/// The split view's main content.
|
||||
var content: () -> Body
|
||||
|
||||
/// The sidebar content's id.
|
||||
let sidebarID = "sidebar"
|
||||
/// The main content's id.
|
||||
let contentID = "content"
|
||||
|
||||
/// Initialize a navigation split view.
|
||||
/// - Parameters:
|
||||
/// - sidebar: The sidebar content.
|
||||
/// - content: The main content.
|
||||
public init(@ViewBuilder sidebar: @escaping () -> Body, @ViewBuilder content: @escaping () -> Body) {
|
||||
self.sidebar = sidebar
|
||||
self.content = content
|
||||
}
|
||||
|
||||
/// Get the container of the navigation split view widget.
|
||||
/// - Returns: The view storage.
|
||||
public func container() -> ViewStorage {
|
||||
let splitView: GTUI.NavigationSplitView = .init()
|
||||
var content: [String: [ViewStorage]] = [:]
|
||||
|
||||
let sidebar = sidebar().widget().container()
|
||||
let label = sidebar.view.fields[.navigationLabel] as? String ?? ""
|
||||
_ = splitView.sidebar(sidebar.view, title: label)
|
||||
content[sidebarID] = [sidebar]
|
||||
|
||||
let mainContent = self.content().widget().container()
|
||||
let mainLabel = mainContent.view.fields[.navigationLabel] as? String ?? ""
|
||||
_ = splitView.content(mainContent.view, title: mainLabel)
|
||||
content[contentID] = [mainContent]
|
||||
|
||||
return .init(splitView, content: content)
|
||||
}
|
||||
|
||||
/// Update the view storage of the navigation split view widget.
|
||||
/// - Parameter storage: The view storage.
|
||||
public func update(_ storage: ViewStorage) {
|
||||
if let storage = storage.content[contentID]?[safe: 0] {
|
||||
content().widget().update(storage)
|
||||
}
|
||||
if let storage = storage.content[sidebarID]?[safe: 0] {
|
||||
sidebar().widget().update(storage)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
Sources/Adwaita/View/ScrollView.swift
Normal file
37
Sources/Adwaita/View/ScrollView.swift
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// ScrollView.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 26.09.23.
|
||||
//
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A GtkScrolledWindow equivalent.
|
||||
public struct ScrollView: Widget {
|
||||
|
||||
/// The content.
|
||||
var content: () -> Body
|
||||
|
||||
/// Initialize a `ScrollView`.
|
||||
/// - Parameter content: The view content.
|
||||
public init(@ViewBuilder content: @escaping () -> Body) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameter storage: The view storage.
|
||||
public func update(_ storage: ViewStorage) {
|
||||
if let first = storage.content[.mainContent]?.first {
|
||||
content().widget().update(first)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Returns: The view storage.
|
||||
public func container() -> ViewStorage {
|
||||
let container = content().widget().container()
|
||||
return .init(Scrolled().setChild(container.view), content: [.mainContent: [container]])
|
||||
}
|
||||
|
||||
}
|
||||
53
Sources/Adwaita/View/StateWrapper.swift
Normal file
53
Sources/Adwaita/View/StateWrapper.swift
Normal file
@ -0,0 +1,53 @@
|
||||
//
|
||||
// StateWrapper.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 26.09.23.
|
||||
//
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A storage for `@State` properties.
|
||||
public struct StateWrapper: Widget {
|
||||
|
||||
/// The content.
|
||||
var content: () -> Body
|
||||
/// The state information (from properties with the `State` wrapper).
|
||||
var state: [String: StateProtocol] = [:]
|
||||
|
||||
/// Initialize a `StateWrapper`.
|
||||
/// - Parameter content: The view content.
|
||||
public init(@ViewBuilder content: @escaping () -> Body) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
/// Initialize a `StateWrapper`.
|
||||
/// - Parameters:
|
||||
/// - content: The view content.
|
||||
/// - state: The state information.
|
||||
init(content: @escaping () -> Body, state: [String: StateProtocol]) {
|
||||
self.content = content
|
||||
self.state = state
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameter storage: The view storage.
|
||||
public func update(_ storage: ViewStorage) {
|
||||
for property in state {
|
||||
if let value = storage.state[property.key]?.value {
|
||||
property.value.value = value
|
||||
}
|
||||
}
|
||||
if let storage = storage.content[.mainContent]?.first {
|
||||
content().widget().update(storage)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Returns: The view storage.
|
||||
public func container() -> ViewStorage {
|
||||
let content = content().widget().container()
|
||||
return .init(content.view, content: [.mainContent: [content]], state: state)
|
||||
}
|
||||
|
||||
}
|
||||
56
Sources/Adwaita/View/StatusPage.swift
Normal file
56
Sources/Adwaita/View/StatusPage.swift
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// StatusPage.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 25.09.23.
|
||||
//
|
||||
|
||||
import GTUI
|
||||
|
||||
/// A status page widget.
|
||||
public struct StatusPage: Widget {
|
||||
|
||||
/// The title.
|
||||
var title: String
|
||||
/// The description.
|
||||
var description: String
|
||||
/// The icon.
|
||||
var icon: Icon
|
||||
/// Additional content.
|
||||
var content: Body
|
||||
|
||||
/// Initialize a status page widget.
|
||||
/// - Parameters:
|
||||
/// - title: The title.
|
||||
/// - icon: The icon.
|
||||
/// - description: Additional details.
|
||||
/// - content: Additional content.
|
||||
public init(_ title: String, icon: Icon, description: String = "", @ViewBuilder content: () -> Body = { [] }) {
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.icon = icon
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
/// Update the view storage of the text widget.
|
||||
/// - Parameter storage: The view storage.
|
||||
public func update(_ storage: ViewStorage) {
|
||||
if let statusPage = storage.view as? GTUI.StatusPage {
|
||||
_ = statusPage.title(title).description(description).icon(icon)
|
||||
}
|
||||
if let storage = storage.content[.mainContent]?.first {
|
||||
content.widget().update(storage)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the container of the text widget.
|
||||
/// - Returns: The view storage.
|
||||
public func container() -> ViewStorage {
|
||||
let child = content.widget().container()
|
||||
return .init(
|
||||
GTUI.StatusPage().title(title).description(description).icon(icon).child(child.view),
|
||||
content: [.mainContent: [child]]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// HeaderBar.swift
|
||||
// Text.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 23.08.23.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// EitherView.swift
|
||||
// VStack.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 23.08.23.
|
||||
@ -12,8 +12,6 @@ public struct VStack: Widget {
|
||||
|
||||
/// The content.
|
||||
var content: () -> Body
|
||||
/// The state information (from properties with the `State` wrapper).
|
||||
var state: [String: StateProtocol] = [:]
|
||||
|
||||
/// Initialize a `VStack`.
|
||||
/// - Parameter content: The view content.
|
||||
@ -21,23 +19,9 @@ public struct VStack: Widget {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
/// Initialize a `VStack`.
|
||||
/// - Parameters:
|
||||
/// - content: The view content.
|
||||
/// - state: The state information.
|
||||
init(content: @escaping () -> Body, state: [String: StateProtocol]) {
|
||||
self.content = content
|
||||
self.state = state
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameter storage: The view storage.
|
||||
public func update(_ storage: ViewStorage) {
|
||||
for property in state {
|
||||
if let value = storage.state[property.key]?.value {
|
||||
property.value.value = value
|
||||
}
|
||||
}
|
||||
content().update(storage.content[.mainContent] ?? [])
|
||||
}
|
||||
|
||||
@ -51,7 +35,7 @@ public struct VStack: Widget {
|
||||
_ = box.append(widget.view)
|
||||
content.append(widget)
|
||||
}
|
||||
return .init(box, content: [.mainContent: content], state: state)
|
||||
return .init(box, content: [.mainContent: content])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
37
Tests/CounterDemo.swift
Normal file
37
Tests/CounterDemo.swift
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// CounterDemo.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 25.09.23.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct CounterDemo: View {
|
||||
|
||||
@State private var count = 0
|
||||
|
||||
var view: Body {
|
||||
description
|
||||
.topToolbar {
|
||||
HeaderBar.start {
|
||||
Button(icon: .default(icon: .goPrevious)) {
|
||||
count -= 1
|
||||
}
|
||||
Button(icon: .default(icon: .goNext)) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private var description: Body {
|
||||
Text("\(count)")
|
||||
.style("title-1")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs
|
||||
68
Tests/Demo.swift
Normal file
68
Tests/Demo.swift
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// Demo.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 25.09.23.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs implicitly_unwrapped_optional no_magic_numbers
|
||||
|
||||
import Adwaita
|
||||
import GTUI
|
||||
|
||||
@main
|
||||
struct Demo: App {
|
||||
|
||||
let id = "io.github.david-swift.Demo"
|
||||
var app: GTUIApp!
|
||||
@State private var toolbar = false
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "main") { window in
|
||||
DemoContent(window: window, app: app)
|
||||
}
|
||||
Window(id: "content", open: 0) { window in
|
||||
Text("This window exists at most once.")
|
||||
.padding()
|
||||
.topToolbar {
|
||||
HeaderBar.empty()
|
||||
}
|
||||
.onAppear {
|
||||
window.setDefaultSize(width: 400, height: 250)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DemoContent: View {
|
||||
|
||||
@State private var selection: Page = .welcome
|
||||
var window: GTUI.Window
|
||||
var app: GTUIApp!
|
||||
|
||||
var view: Body {
|
||||
NavigationSplitView {
|
||||
ScrollView {
|
||||
List(Page.allCases, selection: $selection) { element in
|
||||
Text(element.label)
|
||||
.halign(.start)
|
||||
.padding()
|
||||
}
|
||||
.sidebarStyle()
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar.empty()
|
||||
}
|
||||
.navigationTitle("Demo")
|
||||
} content: {
|
||||
selection.view(app: app)
|
||||
}
|
||||
.onAppear {
|
||||
window.setDefaultSize(width: 650, height: 450)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs implicitly_unwrapped_optional no_magic_numbers
|
||||
40
Tests/Page.swift
Normal file
40
Tests/Page.swift
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// Page.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 25.09.23.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs implicitly_unwrapped_optional
|
||||
|
||||
import Adwaita
|
||||
|
||||
enum Page: String, Identifiable, CaseIterable {
|
||||
|
||||
case welcome
|
||||
case counter
|
||||
case windows
|
||||
|
||||
var id: Self {
|
||||
self
|
||||
}
|
||||
|
||||
var label: String {
|
||||
rawValue.capitalized
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func view(app: GTUIApp!) -> Body {
|
||||
switch self {
|
||||
case .welcome:
|
||||
WelcomeDemo()
|
||||
case .counter:
|
||||
CounterDemo()
|
||||
case .windows:
|
||||
WindowsDemo(app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs implicitly_unwrapped_optional
|
||||
29
Tests/WelcomeDemo.swift
Normal file
29
Tests/WelcomeDemo.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// WelcomeDemo.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 25.09.23.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct WelcomeDemo: View {
|
||||
|
||||
@State private var test = false
|
||||
|
||||
var view: Body {
|
||||
StatusPage(
|
||||
"Swift Adwaita Demo",
|
||||
icon: .default(icon: .gnomeAdwaita1Demo),
|
||||
description: "This is a collection of examples for the Swift Adwaita package."
|
||||
)
|
||||
.topToolbar {
|
||||
HeaderBar.empty()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs
|
||||
34
Tests/WindowsDemo.swift
Normal file
34
Tests/WindowsDemo.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// WindowsDemo.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 25.09.23.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs implicitly_unwrapped_optional no_magic_numbers
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct WindowsDemo: View {
|
||||
|
||||
var app: GTUIApp!
|
||||
|
||||
var view: Body {
|
||||
VStack {
|
||||
Button("Show Window") {
|
||||
app.showWindow("content")
|
||||
}
|
||||
.padding()
|
||||
Button("Add Window") {
|
||||
app.addWindow("main")
|
||||
}
|
||||
.padding(10, .horizontal.add(.bottom))
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar.empty()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs implicitly_unwrapped_optional no_magic_numbers
|
||||
@ -1,72 +0,0 @@
|
||||
//
|
||||
// main.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 05.08.23.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs implicitly_unwrapped_optional no_magic_numbers
|
||||
|
||||
import Adwaita
|
||||
|
||||
@main
|
||||
struct Counter: App {
|
||||
|
||||
let id = "io.github.david-swift.Counter"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
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 ContentView: View {
|
||||
|
||||
@State private var count = 0
|
||||
|
||||
var view: Body {
|
||||
HeaderBar.start {
|
||||
Button(icon: .default(icon: .goPrevious)) {
|
||||
count -= 1
|
||||
}
|
||||
Button(icon: .default(icon: .goNext)) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
description
|
||||
}
|
||||
|
||||
@ViewBuilder private var description: Body {
|
||||
VStack {
|
||||
switch count {
|
||||
case 1:
|
||||
Text("One")
|
||||
.transition(.slideUp)
|
||||
case 0:
|
||||
Text("Zero")
|
||||
default:
|
||||
Text("Hello, world, \(count)!")
|
||||
}
|
||||
}
|
||||
.padding(50)
|
||||
.onUpdate {
|
||||
print(count)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs implicitly_unwrapped_optional no_magic_numbers
|
||||
64
user-manual/Advanced/CreatingWidgets.md
Normal file
64
user-manual/Advanced/CreatingWidgets.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Creating Widgets
|
||||
|
||||
Widgets are special views that do not provide a collection of other views as a content,
|
||||
but have functions that are called when creating or updating the view.
|
||||
Normally, a widget manages a GTK or Libadwaita widget using [SwiftGui][1].
|
||||
|
||||
## Recreate the `Text` widget
|
||||
In this tutorial, we will recreate the text widget.
|
||||
A widget conforms to the `Widget` protocol:
|
||||
```swift
|
||||
struct CustomText: Widget { }
|
||||
```
|
||||
You can add properties to the widget:
|
||||
```swift
|
||||
struct CustomText: Widget {
|
||||
|
||||
var text: String
|
||||
|
||||
}
|
||||
```
|
||||
This widget can be called in a view body using `CustomText(text: "Hello, world!")`.
|
||||
Now, add the two functions required by the protocol:
|
||||
```swift
|
||||
struct CustomText: Widget {
|
||||
|
||||
var text: String
|
||||
|
||||
public func container() -> ViewStorage { }
|
||||
public func update(_ storage: ViewStorage) { }
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## The `container()` Function
|
||||
This function initializes the widget when the widget appears for the first time.
|
||||
It expects a `ViewStorage` as the return type.
|
||||
In our case, this function is very simple:
|
||||
```swift
|
||||
func container() -> ViewStorage {
|
||||
.init(MarkupLabel(self.text))
|
||||
}
|
||||
```
|
||||
`MarkupLabel` is defined in [SwiftGui][1].
|
||||
|
||||
## The `update(_:)` Function
|
||||
Whenever a state of the app changes, the `update(_:)` function of the widget gets called.
|
||||
You get the view storage that you have previously initialized as a parameter.
|
||||
Update the storage to reflect the current state of the widget:
|
||||
```swift
|
||||
func update(_ storage: ViewStorage) {
|
||||
if let label = storage.view as? MarkupLabel {
|
||||
label.setText(text)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Containers
|
||||
Some widgets act as containers that accept other widgets as children.
|
||||
In that case, use the `ViewStorage`'s `content` property for storing their view storages.
|
||||
In the `update(_:)` function, update the children's storages.
|
||||
An example showcasing how to implement containers is the [VStack][2].
|
||||
|
||||
[1]: https://github.com/JCWasmx86/SwiftGui
|
||||
[2]: ../../Sources/Adwaita/View/VStack.swift
|
||||
Loading…
x
Reference in New Issue
Block a user