Skip to content
This repository was archived by the owner on Oct 17, 2024. It is now read-only.

Commit 81e489c

Browse files
committed
Add ForEach widget for dynamic GtkBox
1 parent 8d05d22 commit 81e489c

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

Documentation/Reference/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
- [EntryRow](structs/EntryRow.md)
3535
- [ExpanderRow](structs/ExpanderRow.md)
3636
- [FileDialog](structs/FileDialog.md)
37+
- [ForEach](structs/ForEach.md)
3738
- [Form](structs/Form.md)
3839
- [HStack](structs/HStack.md)
3940
- [HeaderBar](structs/HeaderBar.md)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
**STRUCT**
2+
3+
# `ForEach`
4+
5+
A dynamic list but without a list design in the user interface.
6+
7+
## Properties
8+
### `elements`
9+
10+
The dynamic widget elements.
11+
12+
### `content`
13+
14+
The dynamic widget content.
15+
16+
### `horizontal`
17+
18+
Whether the list is horizontal.
19+
20+
## Methods
21+
### `init(_:horizontal:content:)`
22+
23+
Initialize `ForEach`.
24+
25+
### `container(modifiers:)`
26+
27+
Get the widget's view storage.
28+
- Parameter modifiers: The view modifiers.
29+
- Returns: The view storage.
30+
31+
### `update(_:modifiers:updateProperties:)`
32+
33+
Update the widget's view storage.
34+
- Parameters:
35+
- storage: The view storage.
36+
- modifiers: The view modifiers.
37+
- updateProperties: Whether to update the view's properties.

Sources/Adwaita/View/ForEach.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// ForEach.swift
3+
// Adwaita
4+
//
5+
// Created by david-swift on 30.01.24.
6+
//
7+
8+
import CAdw
9+
import LevenshteinTransformations
10+
11+
/// A dynamic list but without a list design in the user interface.
12+
public struct ForEach<Element>: Widget where Element: Identifiable {
13+
14+
/// The dynamic widget elements.
15+
var elements: [Element]
16+
/// The dynamic widget content.
17+
var content: (Element) -> Body
18+
/// Whether the list is horizontal.
19+
var horizontal: Bool
20+
21+
/// Initialize `ForEach`.
22+
public init(_ elements: [Element], horizontal: Bool = false, @ViewBuilder content: @escaping (Element) -> Body) {
23+
self.elements = elements
24+
self.content = content
25+
self.horizontal = horizontal
26+
}
27+
28+
/// Get the widget's view storage.
29+
/// - Parameter modifiers: The view modifiers.
30+
/// - Returns: The view storage.
31+
public func container(modifiers: [(View) -> View]) -> ViewStorage {
32+
let storage = ViewStorage(
33+
gtk_box_new(horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, 0)?.opaque()
34+
)
35+
update(storage, modifiers: modifiers, updateProperties: true)
36+
return storage
37+
}
38+
39+
/// Update the widget's view storage.
40+
/// - Parameters:
41+
/// - storage: The view storage.
42+
/// - modifiers: The view modifiers.
43+
/// - updateProperties: Whether to update the view's properties.
44+
public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) {
45+
var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? []
46+
let old = storage.fields["element"] as? [Element] ?? []
47+
let widget: UnsafeMutablePointer<GtkBox>? = storage.pointer?.cast()
48+
old.identifiableTransform(
49+
to: elements,
50+
functions: .init { index, element in
51+
let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
52+
gtk_box_remove(widget, contentStorage[safe: index]?.pointer?.cast())
53+
gtk_box_insert_child_after(widget, child.pointer?.cast(), contentStorage[safe: index]?.pointer?.cast())
54+
contentStorage.remove(at: index)
55+
contentStorage.insert(child, at: index)
56+
} delete: { index in
57+
gtk_box_remove(widget, contentStorage[safe: index]?.pointer?.cast())
58+
contentStorage.remove(at: index)
59+
} insert: { index, element in
60+
let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
61+
gtk_box_insert_child_after(widget, child.pointer?.cast(), contentStorage[safe: index]?.pointer?.cast())
62+
contentStorage.insert(child, at: index)
63+
}
64+
)
65+
if updateProperties {
66+
gtk_orientable_set_orientation(
67+
widget?.opaque(),
68+
horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL
69+
)
70+
}
71+
storage.fields["element"] = elements
72+
storage.content[.mainContent] = contentStorage
73+
for (index, element) in elements.enumerated() {
74+
content(element)
75+
.widget(modifiers: modifiers)
76+
.update(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties)
77+
}
78+
}
79+
80+
}

user-manual/Information/Widgets.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Widgets
22

3-
This is an overview of the available widgets and other components in _Adwaita_ that are not auto-generated.
3+
This is an overview of the available widgets and other components in _Adwaita_ that are not auto-generated or that are wrappers for easily accessing auto-generated widgets.
44
There are many more widgets available using auto-generation. Learn [how to use them.](AutoGeneratedWidgets.md)
55

66
| Name | Description | Widget |
@@ -10,6 +10,7 @@ There are many more widgets available using auto-generation. Learn [how to use t
1010
| HStack | A widget which arranges child widgets into a single row. | GtkBox |
1111
| Toggle | A button with two possible states, on and off. | GtkToggleButton |
1212
| List | A widget which arranges child widgets vertically into rows. | GtkListBox |
13+
| ForEach | Arrange dynamic widgets vertically or horizontally. | GtkBox |
1314
| NavigationSplitView | A widget presenting sidebar and content side by side. | AdwNavigationSplitView |
1415
| ScrollView | A container that makes its child scrollable. | GtkScrolledWindow |
1516
| ViewSwitcher | A control for switching between different views. | AdwViewSwitcher |

0 commit comments

Comments
 (0)