// // 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: 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? = 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) } } }