Add support for grouping controls with forms
This commit is contained in:
parent
1e6143acce
commit
4dc5d07409
@ -16,15 +16,20 @@
|
||||
## Structs
|
||||
|
||||
- [AboutWindow](structs/AboutWindow.md)
|
||||
- [ActionRow](structs/ActionRow.md)
|
||||
- [AppearObserver](structs/AppearObserver.md)
|
||||
- [Banner](structs/Banner.md)
|
||||
- [Binding](structs/Binding.md)
|
||||
- [Button](structs/Button.md)
|
||||
- [Carousel](structs/Carousel.md)
|
||||
- [Clamp](structs/Clamp.md)
|
||||
- [ComboRow](structs/ComboRow.md)
|
||||
- [Container](structs/Container.md)
|
||||
- [ContentModifier](structs/ContentModifier.md)
|
||||
- [EntryRow](structs/EntryRow.md)
|
||||
- [FileDialog](structs/FileDialog.md)
|
||||
- [Form](structs/Form.md)
|
||||
- [FormSection](structs/FormSection.md)
|
||||
- [HStack](structs/HStack.md)
|
||||
- [HeaderBar](structs/HeaderBar.md)
|
||||
- [InspectorWrapper](structs/InspectorWrapper.md)
|
||||
@ -39,10 +44,12 @@
|
||||
- [ProgressBar](structs/ProgressBar.md)
|
||||
- [ScrollView](structs/ScrollView.md)
|
||||
- [Signal](structs/Signal.md)
|
||||
- [SpinRow](structs/SpinRow.md)
|
||||
- [State](structs/State.md)
|
||||
- [StateWrapper](structs/StateWrapper.md)
|
||||
- [StatusPage](structs/StatusPage.md)
|
||||
- [Submenu](structs/Submenu.md)
|
||||
- [SwitchRow](structs/SwitchRow.md)
|
||||
- [Text](structs/Text.md)
|
||||
- [ToastOverlay](structs/ToastOverlay.md)
|
||||
- [Toggle](structs/Toggle.md)
|
||||
|
||||
@ -177,3 +177,8 @@ Add a bottom toolbar to the view.
|
||||
- toolbar: The toolbar's content.
|
||||
- visible: Whether the toolbar is visible.
|
||||
- Returns: A view.
|
||||
|
||||
### `verticalCenter()`
|
||||
|
||||
Wrap the view in a vertical stack and center vertically.
|
||||
- Returns: The view.
|
||||
|
||||
72
Documentation/Reference/structs/ActionRow.md
Normal file
72
Documentation/Reference/structs/ActionRow.md
Normal file
@ -0,0 +1,72 @@
|
||||
**STRUCT**
|
||||
|
||||
# `ActionRow`
|
||||
|
||||
A form content row showing a title and optionally a subtitle and widgets.
|
||||
|
||||
## Properties
|
||||
### `title`
|
||||
|
||||
The title.
|
||||
|
||||
### `subtitle`
|
||||
|
||||
The subtitle.
|
||||
|
||||
### `prefix`
|
||||
|
||||
The prefix.
|
||||
|
||||
### `suffix`
|
||||
|
||||
The suffix.
|
||||
|
||||
### `prefixID`
|
||||
|
||||
The identifier for the prefix content.
|
||||
|
||||
### `suffixID`
|
||||
|
||||
The identifier for the suffix content.
|
||||
|
||||
## Methods
|
||||
### `init(_:)`
|
||||
|
||||
Initialize an action row.
|
||||
- Parameter title: The row's title.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(row:)`
|
||||
|
||||
Update the action row.
|
||||
- Parameter row: The action row.
|
||||
|
||||
### `subtitle(_:)`
|
||||
|
||||
Set the action row's subtitle.
|
||||
- Parameter subtitle: The subtitle.
|
||||
- Returns: The action row.
|
||||
|
||||
### `prefix(_:)`
|
||||
|
||||
Set the action row's prefix view.
|
||||
- Parameter prefix: The prefix.
|
||||
- Returns: The action row.
|
||||
|
||||
### `suffix(_:)`
|
||||
|
||||
Set the action row's suffix view.
|
||||
- Parameter suffix: The suffix.
|
||||
- Returns: The action row.
|
||||
87
Documentation/Reference/structs/ComboRow.md
Normal file
87
Documentation/Reference/structs/ComboRow.md
Normal file
@ -0,0 +1,87 @@
|
||||
**STRUCT**
|
||||
|
||||
# `ComboRow`
|
||||
|
||||
A row for selecting an element out of a list of elements.
|
||||
|
||||
## Properties
|
||||
### `title`
|
||||
|
||||
The title.
|
||||
|
||||
### `selection`
|
||||
|
||||
The selected element.
|
||||
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
### `subtitle`
|
||||
|
||||
The subtitle.
|
||||
|
||||
### `prefix`
|
||||
|
||||
The prefix.
|
||||
|
||||
### `suffix`
|
||||
|
||||
The suffix.
|
||||
|
||||
### `prefixID`
|
||||
|
||||
The identifier for the prefix content.
|
||||
|
||||
### `suffixID`
|
||||
|
||||
The identifier for the suffix content.
|
||||
|
||||
### `elementsID`
|
||||
|
||||
The identifier for the elements.
|
||||
|
||||
## Methods
|
||||
### `init(_:selection:values:)`
|
||||
|
||||
Initialize a combo row.
|
||||
- Parameters:
|
||||
- title: The row's title.
|
||||
- selection: The selected value.
|
||||
- values: The available values.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(row:)`
|
||||
|
||||
Update the combo row.
|
||||
- Parameter row: The combo row.
|
||||
|
||||
### `subtitle(_:)`
|
||||
|
||||
Set the combo row's subtitle.
|
||||
- Parameter subtitle: The subtitle.
|
||||
- Returns: The combo row.
|
||||
|
||||
### `prefix(_:)`
|
||||
|
||||
Set the combo row's prefix view.
|
||||
- Parameter prefix: The prefix.
|
||||
- Returns: The combo row.
|
||||
|
||||
### `suffix(_:)`
|
||||
|
||||
Set the combo row's suffix view.
|
||||
- Parameter suffix: The suffix.
|
||||
- Returns: The combo row.
|
||||
87
Documentation/Reference/structs/EntryRow.md
Normal file
87
Documentation/Reference/structs/EntryRow.md
Normal file
@ -0,0 +1,87 @@
|
||||
**STRUCT**
|
||||
|
||||
# `EntryRow`
|
||||
|
||||
A form content row accepting text input.
|
||||
|
||||
## Properties
|
||||
### `title`
|
||||
|
||||
The title.
|
||||
|
||||
### `text`
|
||||
|
||||
The text.
|
||||
|
||||
### `prefix`
|
||||
|
||||
The prefix.
|
||||
|
||||
### `suffix`
|
||||
|
||||
The suffix.
|
||||
|
||||
### `onSubmit`
|
||||
|
||||
The handler that gets executed when the user submits the content.
|
||||
|
||||
### `password`
|
||||
|
||||
Whether the password entry row should be used.
|
||||
|
||||
### `prefixID`
|
||||
|
||||
The identifier for the prefix content.
|
||||
|
||||
### `suffixID`
|
||||
|
||||
The identifier for the suffix content.
|
||||
|
||||
## Methods
|
||||
### `init(_:text:)`
|
||||
|
||||
Initialize an entry row.
|
||||
- Parameters:
|
||||
- title: The row's title.
|
||||
- text: The text.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(row:)`
|
||||
|
||||
Update the entry row.
|
||||
- Parameter row: The entry row.
|
||||
|
||||
### `onSubmit(_:)`
|
||||
|
||||
Set the entry row's subtitle.
|
||||
- Parameter subtitle: The subtitle.
|
||||
- Returns: The entry row.
|
||||
|
||||
### `prefix(_:)`
|
||||
|
||||
Set the entry row's prefix view.
|
||||
- Parameter prefix: The prefix.
|
||||
- Returns: The entry row.
|
||||
|
||||
### `suffix(_:)`
|
||||
|
||||
Set the entry row's suffix view.
|
||||
- Parameter suffix: The suffix.
|
||||
- Returns: The entry row.
|
||||
|
||||
### `secure()`
|
||||
|
||||
Let the user securely enter private text.
|
||||
- Returns: The entry row.
|
||||
29
Documentation/Reference/structs/Form.md
Normal file
29
Documentation/Reference/structs/Form.md
Normal file
@ -0,0 +1,29 @@
|
||||
**STRUCT**
|
||||
|
||||
# `Form`
|
||||
|
||||
A list with no dynamic content styled as a boxed list.
|
||||
|
||||
## Properties
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
## Methods
|
||||
### `init(content:)`
|
||||
|
||||
Initialize a `Form`.
|
||||
- Parameter content: The view content, usually different kind of rows.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
64
Documentation/Reference/structs/FormSection.md
Normal file
64
Documentation/Reference/structs/FormSection.md
Normal file
@ -0,0 +1,64 @@
|
||||
**STRUCT**
|
||||
|
||||
# `FormSection`
|
||||
|
||||
A section usually groups forms.
|
||||
|
||||
## Properties
|
||||
### `title`
|
||||
|
||||
The title.
|
||||
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
### `description`
|
||||
|
||||
The description.
|
||||
|
||||
### `suffix`
|
||||
|
||||
The suffix.
|
||||
|
||||
### `suffixID`
|
||||
|
||||
The identifier for the suffix content.
|
||||
|
||||
## Methods
|
||||
### `init(_:content:)`
|
||||
|
||||
Initialize a form section.
|
||||
- Parameters:
|
||||
- title: The title.
|
||||
- content: The content, usually one or more forms.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(group:)`
|
||||
|
||||
Update the form section.
|
||||
- Parameter group: The form section.
|
||||
|
||||
### `description(_:)`
|
||||
|
||||
Set the form section's description.
|
||||
- Parameter description: The description.
|
||||
- Returns: The form section.
|
||||
|
||||
### `suffix(_:)`
|
||||
|
||||
Set the form section's suffix view.
|
||||
- Parameter suffix: The suffix.
|
||||
- Returns: The form section.
|
||||
102
Documentation/Reference/structs/SpinRow.md
Normal file
102
Documentation/Reference/structs/SpinRow.md
Normal file
@ -0,0 +1,102 @@
|
||||
**STRUCT**
|
||||
|
||||
# `SpinRow`
|
||||
|
||||
A spin row lets the user select an integer in a certain range.
|
||||
|
||||
## Properties
|
||||
### `title`
|
||||
|
||||
The title.
|
||||
|
||||
### `value`
|
||||
|
||||
The selected value.
|
||||
|
||||
### `min`
|
||||
|
||||
The minimum value.
|
||||
|
||||
### `max`
|
||||
|
||||
The maximum value.
|
||||
|
||||
### `step`
|
||||
|
||||
The increase/decrease step.
|
||||
|
||||
### `subtitle`
|
||||
|
||||
The subtitle.
|
||||
|
||||
### `prefix`
|
||||
|
||||
The prefix.
|
||||
|
||||
### `suffix`
|
||||
|
||||
The suffix.
|
||||
|
||||
### `prefixID`
|
||||
|
||||
The identifier for the prefix content.
|
||||
|
||||
### `suffixID`
|
||||
|
||||
The identifier for the suffix content.
|
||||
|
||||
### `configID`
|
||||
|
||||
The identifier of the configuration field.
|
||||
|
||||
## Methods
|
||||
### `init(_:value:min:max:)`
|
||||
|
||||
Initialize a spin row.
|
||||
- Parameters:
|
||||
- title: The row's title.
|
||||
- value: The selected value.
|
||||
- min: The minimum value.
|
||||
- max: The maximum value.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(row:)`
|
||||
|
||||
Update the spin row.
|
||||
- Parameter row: The spin row.
|
||||
|
||||
### `subtitle(_:)`
|
||||
|
||||
Set the spin row's subtitle.
|
||||
- Parameter subtitle: The subtitle.
|
||||
- Returns: The spin row.
|
||||
|
||||
### `prefix(_:)`
|
||||
|
||||
Set the spin row's prefix view.
|
||||
- Parameter prefix: The prefix.
|
||||
- Returns: The spin row.
|
||||
|
||||
### `suffix(_:)`
|
||||
|
||||
Set the spin row's suffix view.
|
||||
- Parameter suffix: The suffix.
|
||||
- Returns: The spin row.
|
||||
|
||||
### `step(_:)`
|
||||
|
||||
Set the difference a single click on the increase/decrease buttons makes.
|
||||
- Parameter step: The increase/decrease step.
|
||||
- Returns: The spin row.
|
||||
78
Documentation/Reference/structs/SwitchRow.md
Normal file
78
Documentation/Reference/structs/SwitchRow.md
Normal file
@ -0,0 +1,78 @@
|
||||
**STRUCT**
|
||||
|
||||
# `SwitchRow`
|
||||
|
||||
A row representing a boolean state.
|
||||
|
||||
## Properties
|
||||
### `title`
|
||||
|
||||
The title.
|
||||
|
||||
### `isOn`
|
||||
|
||||
Whether the switch is activated.
|
||||
|
||||
### `subtitle`
|
||||
|
||||
The subtitle.
|
||||
|
||||
### `prefix`
|
||||
|
||||
The prefix.
|
||||
|
||||
### `suffix`
|
||||
|
||||
The suffix.
|
||||
|
||||
### `prefixID`
|
||||
|
||||
The identifier for the prefix content.
|
||||
|
||||
### `suffixID`
|
||||
|
||||
The identifier for the suffix content.
|
||||
|
||||
## Methods
|
||||
### `init(_:isOn:)`
|
||||
|
||||
Initialize a switch row.
|
||||
- Parameters:
|
||||
- title: The row's title.
|
||||
- isOn: Whether the switch is on.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `update(row:)`
|
||||
|
||||
Update the switch row.
|
||||
- Parameter row: The switch row.
|
||||
|
||||
### `subtitle(_:)`
|
||||
|
||||
Set the switch row's subtitle.
|
||||
- Parameter subtitle: The subtitle.
|
||||
- Returns: The switch row.
|
||||
|
||||
### `prefix(_:)`
|
||||
|
||||
Set the switch row's prefix view.
|
||||
- Parameter prefix: The prefix.
|
||||
- Returns: The switch row.
|
||||
|
||||
### `suffix(_:)`
|
||||
|
||||
Set the switch row's suffix view.
|
||||
- Parameter suffix: The suffix.
|
||||
- Returns: The switch row.
|
||||
@ -17,6 +17,10 @@ The button's icon.
|
||||
|
||||
Whether the toggle is on.
|
||||
|
||||
### `isCheckButton`
|
||||
|
||||
Whether to use GtkCheckButton instead of GtkToggleButton
|
||||
|
||||
## Methods
|
||||
### `init(_:icon:isOn:)`
|
||||
|
||||
@ -50,3 +54,13 @@ Get a button's view storage.
|
||||
|
||||
Update the toggle's state.
|
||||
- Parameter toggle: The toggle.
|
||||
|
||||
### `updateState(toggle:)`
|
||||
|
||||
Update the check button's state.
|
||||
- Parameter toggle: The toggle.
|
||||
|
||||
### `checkButton()`
|
||||
|
||||
Use the check button style.
|
||||
- Returns: The toggle.
|
||||
|
||||
@ -18,7 +18,7 @@ let package = Package(
|
||||
)
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/AparokshaUI/Libadwaita", from: "0.1.5"),
|
||||
.package(url: "https://github.com/AparokshaUI/Libadwaita", from: "0.1.6"),
|
||||
.package(
|
||||
url: "https://github.com/david-swift/LevenshteinTransformations",
|
||||
from: "0.1.1"
|
||||
|
||||
99
Sources/Adwaita/View/Forms/ActionRow.swift
Normal file
99
Sources/Adwaita/View/Forms/ActionRow.swift
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
// ActionRow.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 03.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A form content row showing a title and optionally a subtitle and widgets.
|
||||
public struct ActionRow: Widget {
|
||||
|
||||
/// The title.
|
||||
var title: String
|
||||
/// The subtitle.
|
||||
var subtitle = ""
|
||||
/// The prefix.
|
||||
var prefix: Body = []
|
||||
/// The suffix.
|
||||
var suffix: Body = []
|
||||
|
||||
/// The identifier for the prefix content.
|
||||
let prefixID = "prefix"
|
||||
/// The identifier for the suffix content.
|
||||
let suffixID = "suffix"
|
||||
|
||||
/// Initialize an action row.
|
||||
/// - Parameter title: The row's title.
|
||||
public init(_ title: String) {
|
||||
self.title = title
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - modifiers: Modify views before being updated.
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
if let row = storage.view as? Libadwaita.ActionRow {
|
||||
update(row: row)
|
||||
}
|
||||
if let prefixStorage = storage.content[prefixID]?.first {
|
||||
prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
|
||||
}
|
||||
if let suffixStorage = storage.content[suffixID]?.first {
|
||||
suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let row: Libadwaita.ActionRow = .init(title: title, subtitle: subtitle)
|
||||
let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
if !prefix.isEmpty {
|
||||
_ = row.addPrefix(prefixContent.view)
|
||||
}
|
||||
if !suffix.isEmpty {
|
||||
_ = row.addSuffix(suffixContent.view)
|
||||
}
|
||||
return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
|
||||
}
|
||||
|
||||
/// Update the action row.
|
||||
/// - Parameter row: The action row.
|
||||
func update(row: Libadwaita.ActionRow) {
|
||||
_ = row.title(title)
|
||||
_ = row.subtitle(subtitle)
|
||||
}
|
||||
|
||||
/// Set the action row's subtitle.
|
||||
/// - Parameter subtitle: The subtitle.
|
||||
/// - Returns: The action row.
|
||||
public func subtitle(_ subtitle: String) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.subtitle = subtitle
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the action row's prefix view.
|
||||
/// - Parameter prefix: The prefix.
|
||||
/// - Returns: The action row.
|
||||
public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.prefix = prefix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the action row's suffix view.
|
||||
/// - Parameter suffix: The suffix.
|
||||
/// - Returns: The action row.
|
||||
public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.suffix = suffix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
}
|
||||
132
Sources/Adwaita/View/Forms/ComboRow.swift
Normal file
132
Sources/Adwaita/View/Forms/ComboRow.swift
Normal file
@ -0,0 +1,132 @@
|
||||
//
|
||||
// ComboRow.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 04.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A row for selecting an element out of a list of elements.
|
||||
public struct ComboRow<Element>: Widget
|
||||
where Element: CustomStringConvertible, Element: Identifiable, Element: Equatable {
|
||||
|
||||
/// The title.
|
||||
var title: String
|
||||
/// The selected element.
|
||||
@Binding var selection: Element.ID
|
||||
/// The content.
|
||||
var content: [Element]
|
||||
/// The subtitle.
|
||||
var subtitle = ""
|
||||
/// The prefix.
|
||||
var prefix: Body = []
|
||||
/// The suffix.
|
||||
var suffix: Body = []
|
||||
|
||||
/// The identifier for the prefix content.
|
||||
let prefixID = "prefix"
|
||||
/// The identifier for the suffix content.
|
||||
let suffixID = "suffix"
|
||||
|
||||
/// The identifier for the elements.
|
||||
let elementsID = "elements"
|
||||
|
||||
/// Initialize a combo row.
|
||||
/// - Parameters:
|
||||
/// - title: The row's title.
|
||||
/// - selection: The selected value.
|
||||
/// - values: The available values.
|
||||
public init(_ title: String, selection: Binding<Element.ID>, values: [Element]) {
|
||||
self.title = title
|
||||
self._selection = selection
|
||||
content = values
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - modifiers: Modify views before being updated.
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
if let row = storage.view as? Libadwaita.ComboRow {
|
||||
update(row: row)
|
||||
}
|
||||
if let prefixStorage = storage.content[prefixID]?.first {
|
||||
prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
|
||||
}
|
||||
if let suffixStorage = storage.content[suffixID]?.first {
|
||||
suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let row: Libadwaita.ComboRow = .init(title: title, subtitle: subtitle)
|
||||
let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
if !prefix.isEmpty {
|
||||
_ = row.addPrefix(prefixContent.view)
|
||||
}
|
||||
if !suffix.isEmpty {
|
||||
_ = row.addSuffix(suffixContent.view)
|
||||
}
|
||||
update(row: row)
|
||||
_ = row.onChange {
|
||||
if let element = content.first(where: { $0.description == row.selected() }) {
|
||||
selection = element.id
|
||||
}
|
||||
}
|
||||
return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
|
||||
}
|
||||
|
||||
/// Update the combo row.
|
||||
/// - Parameter row: The combo row.
|
||||
func update(row: Libadwaita.ComboRow) {
|
||||
_ = row.title(title)
|
||||
_ = row.subtitle(subtitle)
|
||||
let oldElements = row.fields[elementsID] as? [Element] ?? []
|
||||
if oldElements != content {
|
||||
for element in oldElements {
|
||||
row.remove(at: 0)
|
||||
}
|
||||
for element in content {
|
||||
row.append(element.description)
|
||||
}
|
||||
}
|
||||
let index = content.firstIndex { $0.id == selection } ?? 0
|
||||
if row.selected() != content[safe: index]?.description {
|
||||
row.select(at: index)
|
||||
}
|
||||
row.fields[elementsID] = content
|
||||
}
|
||||
|
||||
/// Set the combo row's subtitle.
|
||||
/// - Parameter subtitle: The subtitle.
|
||||
/// - Returns: The combo row.
|
||||
public func subtitle(_ subtitle: String) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.subtitle = subtitle
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the combo row's prefix view.
|
||||
/// - Parameter prefix: The prefix.
|
||||
/// - Returns: The combo row.
|
||||
public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.prefix = prefix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the combo row's suffix view.
|
||||
/// - Parameter suffix: The suffix.
|
||||
/// - Returns: The combo row.
|
||||
public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.suffix = suffix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
}
|
||||
128
Sources/Adwaita/View/Forms/EntryRow.swift
Normal file
128
Sources/Adwaita/View/Forms/EntryRow.swift
Normal file
@ -0,0 +1,128 @@
|
||||
//
|
||||
// EntryRow.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 04.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A form content row accepting text input.
|
||||
public struct EntryRow: Widget {
|
||||
|
||||
/// The title.
|
||||
var title: String
|
||||
/// The text.
|
||||
@Binding var text: String
|
||||
/// The prefix.
|
||||
var prefix: Body = []
|
||||
/// The suffix.
|
||||
var suffix: Body = []
|
||||
/// The handler that gets executed when the user submits the content.
|
||||
var onSubmit: (() -> Void)?
|
||||
/// Whether the password entry row should be used.
|
||||
var password = false
|
||||
|
||||
/// The identifier for the prefix content.
|
||||
let prefixID = "prefix"
|
||||
/// The identifier for the suffix content.
|
||||
let suffixID = "suffix"
|
||||
|
||||
/// Initialize an entry row.
|
||||
/// - Parameters:
|
||||
/// - title: The row's title.
|
||||
/// - text: The text.
|
||||
public init(_ title: String, text: Binding<String>) {
|
||||
self.title = title
|
||||
self._text = text
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - modifiers: Modify views before being updated.
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
if let row = storage.view as? Libadwaita.EntryRow {
|
||||
update(row: row)
|
||||
}
|
||||
if let prefixStorage = storage.content[prefixID]?.first {
|
||||
prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
|
||||
}
|
||||
if let suffixStorage = storage.content[suffixID]?.first {
|
||||
suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let row: Libadwaita.EntryRow
|
||||
if password {
|
||||
row = PasswordEntryRow(title: title)
|
||||
} else {
|
||||
row = .init(title: title)
|
||||
}
|
||||
let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
if !prefix.isEmpty {
|
||||
_ = row.addPrefix(prefixContent.view)
|
||||
}
|
||||
if !suffix.isEmpty {
|
||||
_ = row.addSuffix(suffixContent.view)
|
||||
}
|
||||
_ = row.changeHandler {
|
||||
text = row.contents()
|
||||
}
|
||||
update(row: row)
|
||||
return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
|
||||
}
|
||||
|
||||
/// Update the entry row.
|
||||
/// - Parameter row: The entry row.
|
||||
func update(row: Libadwaita.EntryRow) {
|
||||
_ = row.title(title)
|
||||
if row.contents() != text {
|
||||
row.setContents(text)
|
||||
}
|
||||
if let onSubmit {
|
||||
_ = row.submitHandler(onSubmit)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the entry row's subtitle.
|
||||
/// - Parameter subtitle: The subtitle.
|
||||
/// - Returns: The entry row.
|
||||
public func onSubmit(_ onSubmit: @escaping () -> Void) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.onSubmit = onSubmit
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the entry row's prefix view.
|
||||
/// - Parameter prefix: The prefix.
|
||||
/// - Returns: The entry row.
|
||||
public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.prefix = prefix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the entry row's suffix view.
|
||||
/// - Parameter suffix: The suffix.
|
||||
/// - Returns: The entry row.
|
||||
public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.suffix = suffix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Let the user securely enter private text.
|
||||
/// - Returns: The entry row.
|
||||
public func secure() -> Self {
|
||||
var newSelf = self
|
||||
newSelf.password = true
|
||||
return newSelf
|
||||
}
|
||||
|
||||
}
|
||||
47
Sources/Adwaita/View/Forms/Form.swift
Normal file
47
Sources/Adwaita/View/Forms/Form.swift
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// Form.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 03.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A list with no dynamic content styled as a boxed list.
|
||||
public struct Form: Widget {
|
||||
|
||||
/// The content.
|
||||
var content: () -> Body
|
||||
|
||||
/// Initialize a `Form`.
|
||||
/// - Parameter content: The view content, usually different kind of rows.
|
||||
public init(@ViewBuilder content: @escaping () -> Body) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - modifiers: Modify views before being updated.
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
content().update(storage.content[.mainContent] ?? [], modifiers: modifiers)
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let form: ListBox = .init()
|
||||
_ = form
|
||||
.noSelection()
|
||||
.addStyle("boxed-list")
|
||||
var content: [ViewStorage] = []
|
||||
for element in self.content() {
|
||||
let widget = element.storage(modifiers: modifiers)
|
||||
_ = form.append(widget.view)
|
||||
content.append(widget)
|
||||
}
|
||||
return .init(form, content: [.mainContent: content])
|
||||
}
|
||||
|
||||
}
|
||||
89
Sources/Adwaita/View/Forms/FormSection.swift
Normal file
89
Sources/Adwaita/View/Forms/FormSection.swift
Normal file
@ -0,0 +1,89 @@
|
||||
//
|
||||
// FormSection.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 03.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A section usually groups forms.
|
||||
public struct FormSection: Widget {
|
||||
|
||||
/// The title.
|
||||
var title: String
|
||||
/// The content.
|
||||
var content: Body
|
||||
/// The description.
|
||||
var description = ""
|
||||
/// The suffix.
|
||||
var suffix: Body = []
|
||||
|
||||
/// The identifier for the suffix content.
|
||||
let suffixID = "suffix"
|
||||
|
||||
/// Initialize a form section.
|
||||
/// - Parameters:
|
||||
/// - title: The title.
|
||||
/// - content: The content, usually one or more forms.
|
||||
public init(_ title: String, @ViewBuilder content: () -> Body) {
|
||||
self.title = title
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - modifiers: Modify views before being updated.
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
if let group = storage.view as? Libadwaita.PreferencesGroup {
|
||||
update(group: group)
|
||||
}
|
||||
if let storage = storage.content[.mainContent]?.first {
|
||||
content.widget(modifiers: modifiers).update(storage, modifiers: modifiers)
|
||||
}
|
||||
if let suffixStorage = storage.content[suffixID]?.first {
|
||||
suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let group: Libadwaita.PreferencesGroup = .init(name: title, description: description)
|
||||
let content = content.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
group.add(content.view)
|
||||
if !suffix.isEmpty {
|
||||
_ = group.headerSuffix(suffixContent.view)
|
||||
}
|
||||
return .init(group, content: [.mainContent: [content], suffixID: [suffixContent]])
|
||||
}
|
||||
|
||||
/// Update the form section.
|
||||
/// - Parameter group: The form section.
|
||||
func update(group: Libadwaita.PreferencesGroup) {
|
||||
_ = group.title(title)
|
||||
_ = group.description(description)
|
||||
}
|
||||
|
||||
/// Set the form section's description.
|
||||
/// - Parameter description: The description.
|
||||
/// - Returns: The form section.
|
||||
public func description(_ description: String) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.description = description
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the form section's suffix view.
|
||||
/// - Parameter suffix: The suffix.
|
||||
/// - Returns: The form section.
|
||||
public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.suffix = suffix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
}
|
||||
146
Sources/Adwaita/View/Forms/SpinRow.swift
Normal file
146
Sources/Adwaita/View/Forms/SpinRow.swift
Normal file
@ -0,0 +1,146 @@
|
||||
//
|
||||
// SpinRow.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 04.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A spin row lets the user select an integer in a certain range.
|
||||
public struct SpinRow: Widget {
|
||||
|
||||
/// The title.
|
||||
var title: String
|
||||
/// The selected value.
|
||||
@Binding var value: Int
|
||||
/// The minimum value.
|
||||
var min: Int
|
||||
/// The maximum value.
|
||||
var max: Int
|
||||
/// The increase/decrease step.
|
||||
var step = 1
|
||||
/// The subtitle.
|
||||
var subtitle = ""
|
||||
/// The prefix.
|
||||
var prefix: Body = []
|
||||
/// The suffix.
|
||||
var suffix: Body = []
|
||||
|
||||
/// The identifier for the prefix content.
|
||||
let prefixID = "prefix"
|
||||
/// The identifier for the suffix content.
|
||||
let suffixID = "suffix"
|
||||
|
||||
/// The identifier of the configuration field.
|
||||
let configID = "config"
|
||||
|
||||
/// Initialize a spin row.
|
||||
/// - Parameters:
|
||||
/// - title: The row's title.
|
||||
/// - value: The selected value.
|
||||
/// - min: The minimum value.
|
||||
/// - max: The maximum value.
|
||||
public init(_ title: String, value: Binding<Int>, min: Int, max: Int) {
|
||||
self.title = title
|
||||
self._value = value
|
||||
self.min = min
|
||||
self.max = max
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - modifiers: Modify views before being updated.
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
if let row = storage.view as? Libadwaita.SpinRow {
|
||||
update(row: row)
|
||||
}
|
||||
if let prefixStorage = storage.content[prefixID]?.first {
|
||||
prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
|
||||
}
|
||||
if let suffixStorage = storage.content[suffixID]?.first {
|
||||
suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let row: Libadwaita.SpinRow = .init(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
min: .init(min),
|
||||
max: .init(max),
|
||||
step: .init(step)
|
||||
)
|
||||
row.fields[configID] = (min, max, step)
|
||||
let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
if !prefix.isEmpty {
|
||||
_ = row.addPrefix(prefixContent.view)
|
||||
}
|
||||
if !suffix.isEmpty {
|
||||
_ = row.addSuffix(suffixContent.view)
|
||||
}
|
||||
_ = row.onChange {
|
||||
value = .init(row.getValue().rounded())
|
||||
}
|
||||
update(row: row)
|
||||
return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
|
||||
}
|
||||
|
||||
// swiftlint:disable large_tuple
|
||||
/// Update the spin row.
|
||||
/// - Parameter row: The spin row.
|
||||
func update(row: Libadwaita.SpinRow) {
|
||||
_ = row.title(title)
|
||||
_ = row.subtitle(subtitle)
|
||||
if row.fields[configID] as? (Int, Int, Int) ?? (0, 0, 0) != (min, max, step) {
|
||||
_ = row.configuration(min: .init(min), max: .init(max), step: .init(step))
|
||||
row.fields[configID] = (min, max, step)
|
||||
}
|
||||
if row.getValue() != .init(value) {
|
||||
row.setValue(.init(value))
|
||||
}
|
||||
}
|
||||
// swiftlint:enable large_tuple
|
||||
|
||||
/// Set the spin row's subtitle.
|
||||
/// - Parameter subtitle: The subtitle.
|
||||
/// - Returns: The spin row.
|
||||
public func subtitle(_ subtitle: String) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.subtitle = subtitle
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the spin row's prefix view.
|
||||
/// - Parameter prefix: The prefix.
|
||||
/// - Returns: The spin row.
|
||||
public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.prefix = prefix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the spin row's suffix view.
|
||||
/// - Parameter suffix: The suffix.
|
||||
/// - Returns: The spin row.
|
||||
public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.suffix = suffix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the difference a single click on the increase/decrease buttons makes.
|
||||
/// - Parameter step: The increase/decrease step.
|
||||
/// - Returns: The spin row.
|
||||
public func step(_ step: Int) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.step = step
|
||||
return newSelf
|
||||
}
|
||||
|
||||
}
|
||||
111
Sources/Adwaita/View/Forms/SwitchRow.swift
Normal file
111
Sources/Adwaita/View/Forms/SwitchRow.swift
Normal file
@ -0,0 +1,111 @@
|
||||
//
|
||||
// SwitchRow.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 04.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A row representing a boolean state.
|
||||
public struct SwitchRow: Widget {
|
||||
|
||||
/// The title.
|
||||
var title: String
|
||||
/// Whether the switch is activated.
|
||||
@Binding var isOn: Bool
|
||||
/// The subtitle.
|
||||
var subtitle = ""
|
||||
/// The prefix.
|
||||
var prefix: Body = []
|
||||
/// The suffix.
|
||||
var suffix: Body = []
|
||||
|
||||
/// The identifier for the prefix content.
|
||||
let prefixID = "prefix"
|
||||
/// The identifier for the suffix content.
|
||||
let suffixID = "suffix"
|
||||
|
||||
/// Initialize a switch row.
|
||||
/// - Parameters:
|
||||
/// - title: The row's title.
|
||||
/// - isOn: Whether the switch is on.
|
||||
public init(_ title: String, isOn: Binding<Bool>) {
|
||||
self.title = title
|
||||
self._isOn = isOn
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - modifiers: Modify views before being updated.
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
if let row = storage.view as? Libadwaita.SwitchRow {
|
||||
update(row: row)
|
||||
}
|
||||
if let prefixStorage = storage.content[prefixID]?.first {
|
||||
prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
|
||||
}
|
||||
if let suffixStorage = storage.content[suffixID]?.first {
|
||||
suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let row: Libadwaita.SwitchRow = .init(title: title, subtitle: subtitle)
|
||||
let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
|
||||
if !prefix.isEmpty {
|
||||
_ = row.addPrefix(prefixContent.view)
|
||||
}
|
||||
if !suffix.isEmpty {
|
||||
_ = row.addSuffix(suffixContent.view)
|
||||
}
|
||||
_ = row.onChange {
|
||||
isOn = row.getActive()
|
||||
}
|
||||
update(row: row)
|
||||
return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
|
||||
}
|
||||
|
||||
/// Update the switch row.
|
||||
/// - Parameter row: The switch row.
|
||||
func update(row: Libadwaita.SwitchRow) {
|
||||
_ = row.title(title)
|
||||
_ = row.subtitle(subtitle)
|
||||
if row.getActive() != isOn {
|
||||
row.setActive(isOn)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the switch row's subtitle.
|
||||
/// - Parameter subtitle: The subtitle.
|
||||
/// - Returns: The switch row.
|
||||
public func subtitle(_ subtitle: String) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.subtitle = subtitle
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the switch row's prefix view.
|
||||
/// - Parameter prefix: The prefix.
|
||||
/// - Returns: The switch row.
|
||||
public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.prefix = prefix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set the switch row's suffix view.
|
||||
/// - Parameter suffix: The suffix.
|
||||
/// - Returns: The switch row.
|
||||
public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.suffix = suffix()
|
||||
return newSelf
|
||||
}
|
||||
|
||||
}
|
||||
17
Sources/Adwaita/View/Modifiers/View+.swift
Normal file
17
Sources/Adwaita/View/Modifiers/View+.swift
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// View+.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 03.01.24.
|
||||
//
|
||||
|
||||
extension View {
|
||||
|
||||
/// Wrap the view in a vertical stack and center vertically.
|
||||
/// - Returns: The view.
|
||||
public func verticalCenter() -> View {
|
||||
VStack { self }
|
||||
.valign(.center)
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,6 +16,8 @@ public struct Toggle: Widget {
|
||||
var icon: Icon?
|
||||
/// Whether the toggle is on.
|
||||
@Binding var isOn: Bool
|
||||
/// Whether to use GtkCheckButton instead of GtkToggleButton
|
||||
var isCheckButton = false
|
||||
|
||||
// swiftlint:disable function_default_parameter_at_end
|
||||
/// Initialize a toggle button.
|
||||
@ -23,7 +25,7 @@ public struct Toggle: Widget {
|
||||
/// - label: The button's label.
|
||||
/// - icon: The button's icon.
|
||||
/// - isOn: Whether the toggle is on.
|
||||
public init(_ label: String? = nil, icon: Icon, isOn: Binding<Bool>) {
|
||||
public init(_ label: String? = nil, icon: Icon? = nil, isOn: Binding<Bool>) {
|
||||
self.label = label
|
||||
self.icon = icon
|
||||
self._isOn = isOn
|
||||
@ -46,6 +48,8 @@ public struct Toggle: Widget {
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
|
||||
if let toggle = storage.view as? Libadwaita.ToggleButton {
|
||||
updateState(toggle: toggle)
|
||||
} else if let toggle = storage.view as? Libadwaita.CheckButton {
|
||||
updateState(toggle: toggle)
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,12 +57,21 @@ public struct Toggle: Widget {
|
||||
/// - Parameter modifiers: Modify views before being updated.
|
||||
/// - Returns: The button's view storage.
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
let toggle: Libadwaita.ToggleButton = .init(label ?? "")
|
||||
updateState(toggle: toggle)
|
||||
toggle.handler {
|
||||
self.isOn.toggle()
|
||||
if isCheckButton {
|
||||
let toggle: Libadwaita.CheckButton = .init(label ?? "")
|
||||
updateState(toggle: toggle)
|
||||
_ = toggle.handler {
|
||||
self.isOn.toggle()
|
||||
}
|
||||
return .init(toggle)
|
||||
} else {
|
||||
let toggle: Libadwaita.ToggleButton = .init(label ?? "")
|
||||
updateState(toggle: toggle)
|
||||
_ = toggle.handler {
|
||||
self.isOn.toggle()
|
||||
}
|
||||
return .init(toggle)
|
||||
}
|
||||
return .init(toggle)
|
||||
}
|
||||
|
||||
/// Update the toggle's state.
|
||||
@ -72,4 +85,21 @@ public struct Toggle: Widget {
|
||||
toggle.setActive(isOn)
|
||||
}
|
||||
|
||||
/// Update the check button's state.
|
||||
/// - Parameter toggle: The toggle.
|
||||
func updateState(toggle: Libadwaita.CheckButton) {
|
||||
if let label {
|
||||
toggle.setLabel(label)
|
||||
}
|
||||
toggle.setActive(isOn)
|
||||
}
|
||||
|
||||
/// Use the check button style.
|
||||
/// - Returns: The toggle.
|
||||
public func checkButton() -> Self {
|
||||
var newSelf = self
|
||||
newSelf.isCheckButton = true
|
||||
return newSelf
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -56,6 +56,12 @@ struct Demo: App {
|
||||
.defaultSize(width: 600, height: 400)
|
||||
.resizable(false)
|
||||
.title("View Switcher Demo")
|
||||
Window(id: "form-demo", open: 0) { _ in
|
||||
FormDemo.WindowContent()
|
||||
}
|
||||
.closeShortcut()
|
||||
.defaultSize(width: 400, height: 250)
|
||||
.title("Form Demo")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
88
Tests/FormDemo.swift
Normal file
88
Tests/FormDemo.swift
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// FormDemo.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 03.01.24.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs no_magic_numbers
|
||||
|
||||
import Adwaita
|
||||
import Libadwaita
|
||||
|
||||
struct FormDemo: View {
|
||||
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
VStack {
|
||||
Button("View Demo") {
|
||||
app.showWindow("form-demo")
|
||||
}
|
||||
.style("suggested-action")
|
||||
.frame(maxSize: 100)
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowContent: View {
|
||||
|
||||
@State private var text = "They also have a subtitle"
|
||||
@State private var password = "Password"
|
||||
@State private var value = 0
|
||||
@State private var isOn = true
|
||||
@State private var selection = "Hello"
|
||||
|
||||
let values: [ListDemo.Element] = [.init(id: "Hello"), .init(id: "World")]
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
VStack {
|
||||
Form {
|
||||
ActionRow("Rows have a title")
|
||||
.subtitle(text)
|
||||
ActionRow("Rows can have suffix widgets")
|
||||
.suffix {
|
||||
Button("Action") { }
|
||||
.verticalCenter()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
FormSection("Entry Rows") {
|
||||
Form {
|
||||
EntryRow("Entry Row", text: $text)
|
||||
.suffix {
|
||||
Button(icon: .default(icon: .editCopy)) { Clipboard.copy(text) }
|
||||
.style("flat")
|
||||
.verticalCenter()
|
||||
}
|
||||
EntryRow("Password", text: $password)
|
||||
.secure()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
rowDemo("Spin Rows", row: SpinRow("Spin Row", value: $value, min: 0, max: 100))
|
||||
rowDemo("Switch Rows", row: SwitchRow("Switch Row", isOn: $isOn))
|
||||
rowDemo("Combo Rows", row: ComboRow("Combo Row", selection: $selection, values: values))
|
||||
}
|
||||
.padding()
|
||||
.frame(maxSize: 400)
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar.empty()
|
||||
}
|
||||
}
|
||||
|
||||
func rowDemo(_ title: String, row: View) -> View {
|
||||
FormSection(title) {
|
||||
Form {
|
||||
row
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs no_magic_numbers
|
||||
@ -45,9 +45,10 @@ struct ListDemo: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct Element: Identifiable {
|
||||
struct Element: Identifiable, CustomStringConvertible, Equatable {
|
||||
|
||||
var id: String
|
||||
var description: String { id }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ enum Page: String, Identifiable, CaseIterable, Codable {
|
||||
case list
|
||||
case carousel
|
||||
case viewSwitcher
|
||||
case form
|
||||
|
||||
var id: Self {
|
||||
self
|
||||
@ -72,6 +73,8 @@ enum Page: String, Identifiable, CaseIterable, Codable {
|
||||
return "Scroll horizontally on a touchpad or touchscreen, or scroll down on your mouse wheel."
|
||||
case .viewSwitcher:
|
||||
return "Switch the window's view."
|
||||
case .form:
|
||||
return "Group controls used for data entry."
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +104,8 @@ enum Page: String, Identifiable, CaseIterable, Codable {
|
||||
CarouselDemo()
|
||||
case .viewSwitcher:
|
||||
ViewSwitcherDemo(app: app)
|
||||
case .form:
|
||||
FormDemo(app: app)
|
||||
}
|
||||
}
|
||||
// swiftlint:enable cyclomatic_complexity
|
||||
|
||||
@ -23,6 +23,13 @@ This is an overview of the available widgets and other components in _Adwaita_.
|
||||
| ProgressBar | A bar showing a progress. | GtkProgressBar |
|
||||
| Banner | A bar showing contextual information. | AdwBanner |
|
||||
| StateWrapper | A wrapper not affecting the UI which stores state information. | - |
|
||||
| FormSection | A titled section, usually containing one or multiple forms. | AdwPreferencesGroup |
|
||||
| Form | A static boxed list, usually containing one or multiple rows. | GtkListBox |
|
||||
| ActionRow | The most basic row displaying text and optionally other views. | AdwActionRow |
|
||||
| ComboRow | A row displaying an array, letting the user choose one element. | AdwComboRow |
|
||||
| EntryRow | A row for text input. | AdwEntryRow |
|
||||
| SpinRow | A row for selecting an integer in a range. | AdwSpinRow |
|
||||
| SwitchRow | A row controlling a simple boolean value. | AdwSwitchRow |
|
||||
|
||||
### View Modifiers
|
||||
|
||||
@ -51,6 +58,7 @@ This is an overview of the available widgets and other components in _Adwaita_.
|
||||
| `overlay(_:)` | Overlay a view with another view. |
|
||||
| `insensitive(_:)` | Make a view unable to detect actions. This is especially useful for overlays. |
|
||||
| `onClick(handler:)` | Run a function when the user clicks on the widget. |
|
||||
| `verticalCenter()` | Wrap a view in a `VStack` and center vertically. |
|
||||
|
||||
### `Button` Modifiers
|
||||
| Syntax | Description |
|
||||
@ -63,6 +71,11 @@ This is an overview of the available widgets and other components in _Adwaita_.
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `headerBarTitle(view:)` | Customize the title view in the header bar. |
|
||||
|
||||
### `Toggle` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `checkButton()` | Use a check button design instead of a toggle. |
|
||||
|
||||
### `List` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
@ -78,11 +91,54 @@ This is an overview of the available widgets and other components in _Adwaita_.
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `wideDesign(_:)` | Whether the wide view switcher design is used. |
|
||||
|
||||
## `Banner` Modifiers
|
||||
### `Banner` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `button(_:handler)` | Show the banner's button and set its title and handler. |
|
||||
|
||||
### `FormSection` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `description(_:)` | Set the section's description. |
|
||||
| `suffix(_:)` | Set the section's suffix view. |
|
||||
|
||||
### `ActionRow` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `subtitle(_:)` | Set the row's subtitle. |
|
||||
| `prefix(_:)` | Set the row's prefix view. |
|
||||
| `suffix(_:)` | Set the row's suffix view. |
|
||||
|
||||
### `ComboRow` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `subtitle(_:)` | Set the row's subtitle. |
|
||||
| `prefix(_:)` | Set the row's prefix view. |
|
||||
| `suffix(_:)` | Set the row's suffix view. |
|
||||
|
||||
### `EntryRow` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `onSubmit(_:)` | Add a submit button to the entry row. Run the provided closure when it gets pressed. |
|
||||
| `prefix(_:)` | Set the row's prefix view. |
|
||||
| `suffix(_:)` | Set the row's suffix view. |
|
||||
| `secure()` | Use the secure design for password inputs etc. |
|
||||
|
||||
### `SpinRow` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `subtitle(_:)` | Set the row's subtitle. |
|
||||
| `prefix(_:)` | Set the row's prefix view. |
|
||||
| `suffix(_:)` | Set the row's suffix view. |
|
||||
| `step(_:)` | Set the increase/decrease rate of the buttons. |
|
||||
|
||||
### `SwitchRow` Modifiers
|
||||
| Syntax | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `subtitle(_:)` | Set the row's subtitle. |
|
||||
| `prefix(_:)` | Set the row's prefix view. |
|
||||
| `suffix(_:)` | Set the row's suffix view. |
|
||||
|
||||
### Window Types
|
||||
| Name | Description | Widget |
|
||||
| -------------------- | ----------------------------------------------------------------- | ---------------------- |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user