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

View File

@ -16,15 +16,28 @@ public protocol AnyView {
extension AnyView { extension AnyView {
/// Get the view's debug tree. /// 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. /// - Returns: A textual description.
public func getDebugTree<WidgetType>(parameters: Bool, type: WidgetType.Type) -> String { public func getDebugTree<WidgetType>(
if let body = self as? Body { parameters: Bool,
type: WidgetType.Type,
modifiers: [(AnyView) -> AnyView] = []
) -> String {
if let body = getModified(modifiers: modifiers) as? Body {
return body.getBodyDebugTree(parameters: parameters, type: type) return body.getBodyDebugTree(parameters: parameters, type: type)
} else if let widget = getModified(modifiers: modifiers) as? Widget {
return widget.getViewDescription(parameters: parameters, type: type)
} }
return """ return """
\(Self.self) { \(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, updateProperties: Bool,
type: WidgetType.Type type: WidgetType.Type
) { ) {
let modified = getModified(modifiers: modifiers) widget(modifiers: modifiers)
if let widget = modified as? Widget { .update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
widget.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
} else {
Wrapper { viewContent }
.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
}
} }
/// Get a storage. /// Get a storage.
@ -75,7 +83,7 @@ extension AnyView {
if let peer = modified as? Widget { if let peer = modified as? Widget {
return peer return peer
} }
return Wrapper { viewContent } return Wrapper { viewContent.map { $0.getModified(modifiers: modifiers) } }
} }
/// Whether the view can be rendered in a certain environment. /// 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. /// - type: The type of the widgets.
/// - Returns: The view storage. /// - Returns: The view storage.
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage { 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 @ViewBuilder
var testContent: Body { var testContent: Body {
Backend2.TestWidget2() Backend2.TestWidget2()
Backend1.TestWidget1() Backend1.TestWidget3()
} }
} }
@ -25,10 +25,10 @@ struct TestView: SimpleView {
} }
print(DemoView().getDebugTree(parameters: true, type: Backend1.BackendWidget.self)) let backendType = Backend1.BackendWidget.self
print(DemoView().getDebugTree(parameters: true, type: Backend2.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 { 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 { } 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 { } public protocol BackendWidget: Widget { }
} }