|
| 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 | +} |
0 commit comments