89 lines
3.4 KiB
Swift

//
// ForEach.swift
// Adwaita
//
// Created by david-swift on 30.01.24.
//
import CAdw
import LevenshteinTransformations
/// A dynamic list but without a list design in the user interface.
public struct ForEach<Element>: Widget where Element: Identifiable {
/// The dynamic widget elements.
var elements: [Element]
/// The dynamic widget content.
var content: (Element) -> Body
/// Whether the list is horizontal.
var horizontal: Bool
/// Initialize `ForEach`.
public init(_ elements: [Element], horizontal: Bool = false, @ViewBuilder content: @escaping (Element) -> Body) {
self.elements = elements
self.content = content
self.horizontal = horizontal
}
/// Get the widget's view storage.
/// - Parameter modifiers: The view modifiers.
/// - Returns: The view storage.
public func container(modifiers: [(View) -> View]) -> ViewStorage {
let storage = ViewStorage(
gtk_box_new(horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, 0)?.opaque()
)
update(storage, modifiers: modifiers, updateProperties: true)
return storage
}
/// Update the widget's view storage.
/// - Parameters:
/// - storage: The view storage.
/// - modifiers: The view modifiers.
/// - updateProperties: Whether to update the view's properties.
public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) {
var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? []
let old = storage.fields["element"] as? [Element] ?? []
let widget: UnsafeMutablePointer<GtkBox>? = storage.pointer?.cast()
old.identifiableTransform(
to: elements,
functions: .init { index, element in
let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
gtk_box_remove(widget, contentStorage[safe: index]?.pointer?.cast())
gtk_box_insert_child_after(
widget,
child.pointer?.cast(),
contentStorage[safe: index - 1]?.pointer?.cast()
)
contentStorage.remove(at: index)
contentStorage.insert(child, at: index)
} delete: { index in
gtk_box_remove(widget, contentStorage[safe: index]?.pointer?.cast())
contentStorage.remove(at: index)
} insert: { index, element in
let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
gtk_box_insert_child_after(
widget,
child.pointer?.cast(),
contentStorage[safe: index - 1]?.pointer?.cast()
)
contentStorage.insert(child, at: index)
}
)
if updateProperties {
gtk_orientable_set_orientation(
widget?.opaque(),
horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL
)
}
storage.fields["element"] = elements
storage.content[.mainContent] = contentStorage
for (index, element) in elements.enumerated() {
content(element)
.widget(modifiers: modifiers)
.update(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties)
}
}
}