Add support for toasts

Add the new concept of signals
This commit is contained in:
david-swift 2023-11-30 21:14:45 +01:00
parent 5ab9387317
commit 36e61255f8
12 changed files with 288 additions and 23 deletions

View File

@ -13,6 +13,7 @@
## Structs
- [AppearObserver](structs/AppearObserver.md)
- [Binding](structs/Binding.md)
- [Button](structs/Button.md)
- [Clamp](structs/Clamp.md)
@ -28,11 +29,13 @@
- [ModifierStopper](structs/ModifierStopper.md)
- [NavigationSplitView](structs/NavigationSplitView.md)
- [ScrollView](structs/ScrollView.md)
- [Signal](structs/Signal.md)
- [State](structs/State.md)
- [StateWrapper](structs/StateWrapper.md)
- [StatusPage](structs/StatusPage.md)
- [Submenu](structs/Submenu.md)
- [Text](structs/Text.md)
- [ToastOverlay](structs/ToastOverlay.md)
- [ToolbarView](structs/ToolbarView.md)
- [VStack](structs/VStack.md)
- [Window](structs/Window.md)

View File

@ -24,6 +24,12 @@ Get a storage.
### `getModified(modifiers:)`
### `onAppear(_:)`
Run a function when the view appears for the first time.
- Parameter closure: The function.
- Returns: A view.
### `frame(maxSize:)`
Set the view's maximal size.
@ -113,6 +119,24 @@ Run a function when the view gets an update.
Remove all of the content modifiers for the wrapped views.
- Returns: A view.
### `toast(_:signal:)`
Present a toast when the signal gets activated.
- Parameters:
- title: The title of the toast.
- signal: The signal which activates the presentation of a toast.
- Returns: A view.
### `toast(_:signal:button:handler:)`
Present a toast with a button when the signal gets activated.
- Parameters:
- title: The title of the toast.
- signal: The signal which activates the presentation of a toast.
- button: The button's label.
- handler: The handler for the button.
- Returns: A view.
### `topToolbar(visible:_:)`
Add a top toolbar to the view.

View File

@ -0,0 +1,28 @@
**STRUCT**
# `AppearObserver`
A widget which executes a custom code when being rendered for the first time.
## Properties
### `onAppear`
The function.
### `content`
The content.
## Methods
### `container(modifiers:)`
Get the content's container.
- Parameter modifiers: Modify views before being updated.
- Returns: The content's container.
### `update(_:modifiers:)`
Update the content.
- Parameters:
- storage: The content's storage.
- modifiers: Modify views before being updated.

View File

@ -0,0 +1,23 @@
**STRUCT**
# `Signal`
A type that signalizes an action.
## Properties
### `boolean`
An action is signalized by toggling a boolean to `true` and back to `false`.
### `update`
Whether the action has caused an update.
## Methods
### `init()`
Initialize a signal.
### `signal()`
Activate a signal.

View File

@ -0,0 +1,41 @@
**STRUCT**
# `ToastOverlay`
A wrapper around a view presenting toasts.
## Properties
### `signal`
The signal for showing the toast./// Present a toast when the signal gets activated.
### `child`
The child view.
### `title`
The title of the toast.
### `button`
Information about the button if available (label and handler).
## Methods
### `container(modifiers:)`
Get the overlay's view storage.
- Parameter modifiers: The view modifiers.
- Returns: The view storage.
### `update(_:modifiers:)`
Update the overlay's view storage.
- Parameters:
- storage: The view storage.
- modifiers: The view modifiers.
### `setVisibility(_:)`
Add a toast if the signal is active.
- Parameter overlay: The toast overlay.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -86,25 +86,27 @@ If you want to use _Adwaita_ in a project, but there are widgets missing, open a
### View Modifiers
| Syntax | Description |
| ---------------------------- | --------------------------------------------------------------------------------------- |
| `inspect(_:)` | Edit the underlying [Libadwaita][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 views minimal width or height. |
| `frame(maxSize:)` | Set the views maximal size. |
| `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. |
| `modifyContent(_:modify:)` | Replace all occurrences of a certain view type with another view. |
| `stopModifiers()` | Ignore all the `modifyContent(_:modify:)` modifiers from higher above in the view tree. |
| Syntax | Description |
| --------------------------------- | --------------------------------------------------------------------------------------- |
| `inspect(_:)` | Edit the underlying [Libadwaita][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 views minimal width or height. |
| `frame(maxSize:)` | Set the views maximal size. |
| `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. |
| `modifyContent(_:modify:)` | Replace all occurrences of a certain view type with another view. |
| `stopModifiers()` | Ignore all the `modifyContent(_:modify:)` modifiers from higher above in the view tree. |
| `toast(_:signal:)` | Show a toast on top of the view whenever the signal gets activated. |
| `toast(_:signal:button:handler:)` | Show a toast with a button on top of the view whenever the signal gets activated. |
### `Button` Modifiers
| Syntax | Description |

View File

@ -0,0 +1,26 @@
//
// Signal.swift
// Adwaita
//
// Created by david-swift on 30.11.23.
//
/// A type that signalizes an action.
public struct Signal {
/// An action is signalized by toggling a boolean to `true` and back to `false`.
@State var boolean = false
/// Whether the action has caused an update.
public var update: Bool { boolean }
/// Initialize a signal.
public init() { }
/// Activate a signal.
public func signal() {
boolean = true
boolean = false
}
}

View File

@ -0,0 +1,83 @@
//
// ToastOverlay.swift
// Adwaita
//
// Created by david-swift on 30.11.23.
//
import Libadwaita
/// A wrapper around a view presenting toasts.
struct ToastOverlay: Widget {
/// The signal for showing the toast./// Present a toast when the signal gets activated.
var signal: Signal
/// The child view.
var child: View
/// The title of the toast.
var title: String
/// Information about the button if available (label and handler).
var button: (String, () -> Void)?
/// Get the overlay's view storage.
/// - Parameter modifiers: The view modifiers.
/// - Returns: The view storage.
func container(modifiers: [(View) -> View]) -> ViewStorage {
let contentStorage = child.widget(modifiers: modifiers).storage(modifiers: modifiers)
let overlay = Libadwaita.ToastOverlay(contentStorage.view)
setVisibility(overlay)
return .init(overlay, content: [.mainContent: [contentStorage]])
}
/// Update the overlay's view storage.
/// - Parameters:
/// - storage: The view storage.
/// - modifiers: The view modifiers.
func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
if let overlay = storage.view as? Libadwaita.ToastOverlay {
setVisibility(overlay)
}
if let storage = storage.content[.mainContent]?.first {
child.widget(modifiers: modifiers).update(storage, modifiers: modifiers)
}
}
/// Add a toast if the signal is active.
/// - Parameter overlay: The toast overlay.
func setVisibility(_ overlay: Libadwaita.ToastOverlay) {
if signal.update {
let toast = Toast(title)
if let button {
_ = toast
.buttonLabel(button.0)
.buttonHandler(button.1)
}
overlay.addToast(toast)
}
}
}
extension View {
/// Present a toast when the signal gets activated.
/// - Parameters:
/// - title: The title of the toast.
/// - signal: The signal which activates the presentation of a toast.
/// - Returns: A view.
public func toast(_ title: String, signal: Signal) -> View {
ToastOverlay(signal: signal, child: self, title: title)
}
/// Present a toast with a button when the signal gets activated.
/// - Parameters:
/// - title: The title of the toast.
/// - signal: The signal which activates the presentation of a toast.
/// - button: The button's label.
/// - handler: The handler for the button.
/// - Returns: A view.
public func toast(_ title: String, signal: Signal, button: String, handler: @escaping () -> Void) -> View {
ToastOverlay(signal: signal, child: self, title: title, button: (button, handler))
}
}

View File

@ -46,7 +46,8 @@ struct Demo: App {
struct DemoContent: View {
@State private var selection: Page = .transition
@State private var selection: Page = .welcome
@State private var toast: Signal = .init()
var window: GTUIApplicationWindow
var app: GTUIApp!
@ -87,11 +88,12 @@ struct Demo: App {
icon: selection.icon,
description: selection.description
) {
selection.view(app: app)
selection.view(app: app, toast: toast)
}
.topToolbar {
HeaderBar.empty()
}
.toast("This is a toast!", signal: toast)
}
.onAppear {
window.setDefaultSize(width: 650, height: 450)

View File

@ -19,6 +19,7 @@ enum Page: String, Identifiable, CaseIterable {
case transition
case dice
case overlayWindow
case toast
var id: Self {
self
@ -36,7 +37,7 @@ enum Page: String, Identifiable, CaseIterable {
var icon: Libadwaita.Icon? {
switch self {
case .welcome:
return .default(icon: .gnomeAdwaita1Demo)
return .default(icon: .emojiNature)
default:
return nil
}
@ -58,11 +59,13 @@ enum Page: String, Identifiable, CaseIterable {
return "Roll the dice."
case .overlayWindow:
return "A window on top of another window."
case .toast:
return "Show a notification inside of your app."
}
}
@ViewBuilder
func view(app: GTUIApp!) -> Body {
func view(app: GTUIApp!, toast: Signal) -> Body {
switch self {
case .welcome:
[]
@ -78,6 +81,8 @@ enum Page: String, Identifiable, CaseIterable {
DiceDemo()
case .overlayWindow:
OverlayWindowDemo(app: app)
case .toast:
ToastDemo(toast: toast)
}
}

28
Tests/ToastDemo.swift Normal file
View File

@ -0,0 +1,28 @@
//
// ToastDemo.swift
// Adwaita
//
// Created by david-swift on 30.11.23.
//
// swiftlint:disable missing_docs
import Adwaita
struct ToastDemo: View {
var toast: Signal
var view: Body {
VStack {
Button("Add Toast") {
toast.signal()
}
.style("suggested-action")
.frame(maxSize: 100)
}
}
}
// swiftlint:enable missing_docs