Fix views with multiple backends not updating

This commit is contained in:
david-swift 2024-06-18 15:47:03 +02:00
parent 133a4b02da
commit c114bd32e9
6 changed files with 89 additions and 46 deletions

View File

@ -18,13 +18,7 @@ extension Array: AnyView where Element == AnyView {
public func getBodyDebugTree<WidgetType>(parameters: Bool, type: WidgetType.Type) -> String {
var description = ""
for view in self where view.renderable(type: type) {
let viewDescription: String
if let widget = view as? Widget {
viewDescription = widget.getViewDescription(parameters: parameters, type: type)
} else {
viewDescription = view.getDebugTree(parameters: parameters, type: type)
}
description += viewDescription + "\n"
description += view.getDebugTree(parameters: parameters, type: type) + "\n"
}
if !description.isEmpty {
description.removeLast()
@ -61,7 +55,7 @@ extension Array: AnyView where Element == AnyView {
updateProperties: Bool,
type: WidgetType.Type
) {
for (index, element) in enumerated() where element.renderable(type: type) {
for (index, element) in filter({ $0.renderable(type: type) }).enumerated() {
if let storage = storage[safe: index] {
element
.widget(modifiers: modifiers)
@ -70,26 +64,15 @@ extension Array: AnyView where Element == AnyView {
}
}
}
extension Array where Element == String {
/// Get the C version of the array.
var cArray: UnsafePointer<UnsafePointer<CChar>?>? {
let cStrings = self.map { $0.utf8CString }
let cStringPointers = cStrings.map { $0.withUnsafeBufferPointer { $0.baseAddress } }
let optionalCStringPointers = cStringPointers + [nil]
var optionalCStringPointersCopy = optionalCStringPointers
optionalCStringPointersCopy.withUnsafeMutableBufferPointer { bufferPointer in
bufferPointer.baseAddress?.advanced(by: cStrings.count).pointee = nil
}
let flatArray = optionalCStringPointersCopy.compactMap { $0 }
let pointer = UnsafeMutablePointer<UnsafePointer<CChar>?>.allocate(capacity: flatArray.count + 1)
for (index, element) in flatArray.enumerated() {
pointer.advanced(by: index).pointee = element
}
pointer.advanced(by: flatArray.count).pointee = nil
return UnsafePointer(pointer)
/// Get the view storages of a collection of views.
/// - Parameters:
/// - modifiers: Modify views before generating the storages.
/// - type: The type of the widgets.
public func storages<WidgetType>(
modifiers: [(AnyView) -> AnyView],
type: WidgetType.Type
) -> [ViewStorage] {
compactMap { $0.renderable(type: type) ? $0.storage(modifiers: [], type: type) : nil }
}
}

View File

@ -16,15 +16,28 @@ public protocol AnyView {
extension AnyView {
/// Get the view's debug tree.
/// - Parameter parameters: Whether the widget parameters should be included in the debug tree.
/// - Parameters:
/// - parameters: Whether the widget parameters should be included in the debug tree.
/// - type: The widget type.
/// - modifiers: Modify the view before getting the debug tree.
/// - Returns: A textual description.
public func getDebugTree<WidgetType>(parameters: Bool, type: WidgetType.Type) -> String {
if let body = self as? Body {
public func getDebugTree<WidgetType>(
parameters: Bool,
type: WidgetType.Type,
modifiers: [(AnyView) -> AnyView] = []
) -> String {
if let body = getModified(modifiers: modifiers) as? Body {
return body.getBodyDebugTree(parameters: parameters, type: type)
} else if let widget = getModified(modifiers: modifiers) as? Widget {
return widget.getViewDescription(parameters: parameters, type: type)
}
return """
\(Self.self) {
\(indented: viewContent.getBodyDebugTree(parameters: parameters, type: type))
\(indented: viewContent
.map { view in
view.getModified(modifiers: modifiers)
}
.getBodyDebugTree(parameters: parameters, type: type))
}
"""
}
@ -49,13 +62,8 @@ extension AnyView {
updateProperties: Bool,
type: WidgetType.Type
) {
let modified = getModified(modifiers: modifiers)
if let widget = modified as? Widget {
widget.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
} else {
Wrapper { viewContent }
.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
}
widget(modifiers: modifiers)
.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
}
/// Get a storage.
@ -75,7 +83,7 @@ extension AnyView {
if let peer = modified as? Widget {
return peer
}
return Wrapper { viewContent }
return Wrapper { viewContent.map { $0.getModified(modifiers: modifiers) } }
}
/// Whether the view can be rendered in a certain environment.

View File

@ -51,7 +51,7 @@ public struct Wrapper: ConvenienceWidget {
/// - type: The type of the widgets.
/// - Returns: The view storage.
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
ViewStorage(nil, content: [.mainContent: content.map { $0.storage(modifiers: [], type: type) }])
.init(nil, content: [.mainContent: content.storages(modifiers: modifiers, type: type)])
}
}

View File

@ -12,7 +12,7 @@ struct DemoView: SimpleView {
@ViewBuilder
var testContent: Body {
Backend2.TestWidget2()
Backend1.TestWidget1()
Backend1.TestWidget3()
}
}
@ -25,10 +25,10 @@ struct TestView: SimpleView {
}
print(DemoView().getDebugTree(parameters: true, type: Backend1.BackendWidget.self))
print(DemoView().getDebugTree(parameters: true, type: Backend2.BackendWidget.self))
let backendType = Backend1.BackendWidget.self
let storage = DemoView().storage(modifiers: [], type: Backend1.BackendWidget.self)
print(DemoView().getDebugTree(parameters: true, type: backendType))
let storage = DemoView().storage(modifiers: [], type: backendType)
for _ in 0...2 {
DemoView().updateStorage(storage, modifiers: [], updateProperties: true, type: Backend2.BackendWidget.self)
DemoView().updateStorage(storage, modifiers: [], updateProperties: true, type: backendType)
}

View File

@ -28,6 +28,32 @@ public enum Backend1 {
}
public struct TestWidget3: BackendWidget {
public init() { }
public var debugTreeContent: [(String, body: Body)] {
[]
}
public var debugTreeParameters: [(String, value: CustomStringConvertible)] {
[]
}
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
print("Init test widget 3")
let storage = ViewStorage(nil)
storage.fields["test"] = 0
return storage
}
public func update<WidgetType>(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type) {
print("Update test widget 3 (#\(storage.fields["test"] ?? ""))")
storage.fields["test"] = (storage.fields["test"] as? Int ?? 0) + 1
}
}
public protocol BackendWidget: Widget { }
}

View File

@ -28,6 +28,32 @@ public enum Backend2 {
}
public struct TestWidget4: BackendWidget {
public init() { }
public var debugTreeContent: [(String, body: Body)] {
[]
}
public var debugTreeParameters: [(String, value: CustomStringConvertible)] {
[]
}
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
print("Init test widget 4")
let storage = ViewStorage(nil)
storage.fields["test"] = 0
return storage
}
public func update<WidgetType>(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type) {
print("Update test widget 4 (#\(storage.fields["test"] ?? ""))")
storage.fields["test"] = (storage.fields["test"] as? Int ?? 0) + 1
}
}
public protocol BackendWidget: Widget { }
}