Implement view updating system

This commit is contained in:
david-swift 2024-06-17 09:11:20 +02:00
parent 3c17404dc6
commit c15839de3b
9 changed files with 82 additions and 89 deletions

View File

@ -15,9 +15,9 @@ extension Array: AnyView where Element == AnyView {
/// Get the debug tree for an array of views.
/// - Parameter parameters: Whether the widget parameters should be visible in the tree.
/// - Returns: The tree.
public func getBodyDebugTree<ViewType>(parameters: Bool, type: ViewType.Type) -> String {
public func getBodyDebugTree<WidgetType>(parameters: Bool, type: WidgetType.Type) -> String {
var description = ""
for view in self where view as? ViewType != nil {
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)
@ -45,7 +45,6 @@ extension Array: AnyView where Element == AnyView {
modified[safe: index] = modifier(view)
}
}
// TODO: Is wrapper correct choice?
return Wrapper { modified }
}
}
@ -55,12 +54,13 @@ extension Array: AnyView where Element == AnyView {
/// - storage: The collection of view storages.
/// - modifiers: Modify views before being updated.
/// - updateProperties: Whether to update properties.
public func update(_ storage: [ViewStorage], modifiers: [(AnyView) -> AnyView], updateProperties: Bool) {
for (index, element) in enumerated() {
/// - type: The type of the widgets.
public func update<WidgetType>(_ storage: [ViewStorage], modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type) {
for (index, element) in enumerated() where element.renderable(type: type) {
if let storage = storage[safe: index] {
element
.widget(modifiers: modifiers)
.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties)
.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
}
}
}

View File

@ -18,7 +18,7 @@ extension AnyView {
/// Get the view's debug tree.
/// - Parameter parameters: Whether the widget parameters should be included in the debug tree.
/// - Returns: A textual description.
public func getDebugTree<ViewType>(parameters: Bool, type: ViewType.Type) -> String {
public func getDebugTree<WidgetType>(parameters: Bool, type: WidgetType.Type) -> String {
if let body = self as? Body {
return body.getBodyDebugTree(parameters: parameters, type: type)
}
@ -42,21 +42,24 @@ extension AnyView {
/// - storage: The storage.
/// - modifiers: Modify views before being updated.
/// - updateProperties: Whether to update properties.
public func updateStorage(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool) {
/// - type: The type of the widgets.
public func updateStorage<WidgetType>(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type) {
let modified = getModified(modifiers: modifiers)
if let widget = modified as? Widget {
widget.update(storage, modifiers: modifiers, updateProperties: updateProperties)
widget.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
} else {
Wrapper { viewContent }
.update(storage, modifiers: modifiers, updateProperties: updateProperties)
.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
}
}
/// Get a storage.
/// - Parameter modifiers: Modify views before being updated.
/// - Parameters:
/// - modifiers: Modify views before being updated.
/// - type: The widget types.
/// - Returns: The storage.
public func storage(modifiers: [(AnyView) -> AnyView]) -> ViewStorage {
widget(modifiers: modifiers).container(modifiers: modifiers)
public func storage<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
widget(modifiers: modifiers).container(modifiers: modifiers, type: type)
}
/// Wrap the view into a widget.
@ -70,6 +73,11 @@ extension AnyView {
return Wrapper { viewContent }
}
/// Whether the view can be rendered in a certain environment.
func renderable<WidgetType>(type: WidgetType.Type) -> Bool {
self as? WidgetType != nil || self as? SimpleView != nil || self as? View != nil
}
}
/// `Body` is an array of views.

View File

@ -53,40 +53,6 @@ public enum ViewBuilder {
component
}
// TODO: Add support for optionals, building either
/*
/// Enables support for `if` statements without an `else`.
/// - Parameter component: An optional component.
/// - Returns: A nonoptional component.
public static func buildOptional(_ component: Component?) -> Component {
.element(
Bin()
.child {
if let component {
buildFinalResult(component)
} else {
[]
}
}
.visible(component != nil)
)
}
/// Enables support for `if`-`else` and `switch` statements.
/// - Parameter component: A component.
/// - Returns: The component.
public static func buildEither(first component: Component) -> Component {
.element(ViewStack(id: true) { _ in buildFinalResult(component) })
}
/// Enables support for `if`-`else` and `switch` statements.
/// - Parameter component: A component.
/// - Returns: The component.
public static func buildEither(second component: Component) -> Component {
.element(ViewStack(id: false) { _ in buildFinalResult(component) })
}
*/
/// Convert a component to an array of elements.
/// - Parameter component: The component to convert.
/// - Returns: The generated array of elements.

View File

@ -13,14 +13,17 @@ public protocol Widget: AnyView {
/// The debug tree's content.
var debugTreeContent: [(String, body: Body)] { get }
/// The view storage.
/// - Parameter modifiers: Modify views before being updated.
func container(modifiers: [(AnyView) -> AnyView]) -> ViewStorage
/// - Parameters:
/// - modifiers: Modify views before being updated.
/// - type: The type of the widgets.
func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage
/// Update the stored content.
/// - Parameters:
/// - storage: The storage to update.
/// - modifiers: Modify views before being updated
/// - updateProperties: Whether to update the view's properties.
func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool)
/// - type: The type of the widgets.
func update<WidgetType>(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type)
}
@ -30,7 +33,7 @@ extension Widget {
public var viewContent: Body { [] }
/// A description of the view.
public func getViewDescription<ViewType>(parameters: Bool, type: ViewType.Type) -> String {
public func getViewDescription<WidgetType>(parameters: Bool, type: WidgetType.Type) -> String {
var content = ""
for element in debugTreeContent {
if content.isEmpty {

View File

@ -50,7 +50,8 @@ public struct StateWrapper: Widget {
/// - storage: The view storage.
/// - modifiers: Modify views before being updated.
/// - updateProperties: Whether to update properties.
public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool) {
/// - type: The type of the widgets.
public func update<WidgetType>(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type) {
var updateProperties = storage.fields[updateID] as? Bool ?? false
storage.fields[updateID] = false
for property in state {
@ -65,15 +66,17 @@ public struct StateWrapper: Widget {
if let storage = storage.content[.mainContent]?.first {
content()
.widget(modifiers: modifiers)
.update(storage, modifiers: modifiers, updateProperties: updateProperties)
.update(storage, modifiers: modifiers, updateProperties: updateProperties, type: type)
}
}
/// Get a view storage.
/// - Parameter modifiers: Modify views before being updated.
/// - Parameters:
/// - modifiers: Modify views before being updated.
/// - type: The type of the widgets.
/// - Returns: The view storage.
public func container(modifiers: [(AnyView) -> AnyView]) -> ViewStorage {
let content = content().widget(modifiers: modifiers).container(modifiers: modifiers)
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
let content = content().storage(modifiers: modifiers, type: type)
let storage = ViewStorage(content.pointer, content: [.mainContent: [content]])
storage.state = state
observe(storage: storage)

View File

@ -32,20 +32,21 @@ public struct Wrapper: Widget {
/// - storage: The view storage.
/// - modifiers: Modify views before being updated.
/// - updateProperties: Whether to update properties.
public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool) {
if let storage = storage.content[.mainContent]?.first {
content
.widget(modifiers: modifiers)
.update(storage, modifiers: modifiers, updateProperties: updateProperties)
/// - type: The widget types.
public func update<WidgetType>(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type) {
guard let storages = storage.content[.mainContent] else {
return
}
content.update(storages, modifiers: modifiers, updateProperties: updateProperties, type: type)
}
/// Get a view storage.
/// - Parameter modifiers: Modify views before being updated.
/// - Parameters:
/// - modifiers: Modify views before being updated.
/// - type: The type of the widgets.
/// - Returns: The view storage.
public func container(modifiers: [(AnyView) -> AnyView]) -> ViewStorage {
let content = content.widget(modifiers: modifiers).container(modifiers: modifiers)
return .init(content.pointer, content: [.mainContent: [content]])
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
ViewStorage(nil, content: [.mainContent: content.map { $0.storage(modifiers: [], type: type) }])
}
}

View File

@ -4,17 +4,31 @@ import SampleBackends
struct DemoView: SimpleView {
var view: Body {
Backend1.TestWidget()
Backend1.TestWidget1()
TestView()
testContent
}
@ViewBuilder
var testContent: Body {
Backend2.TestWidget()
Backend1.TestWidget()
Backend2.TestWidget2()
Backend1.TestWidget1()
}
}
print(DemoView().getDebugTree(parameters: true, type: Backend1.BackendView.self))
print(DemoView().getDebugTree(parameters: true, type: Backend2.BackendView.self))
struct TestView: SimpleView {
var view: Body {
[]
}
}
print(DemoView().getDebugTree(parameters: true, type: Backend1.BackendWidget.self))
print(DemoView().getDebugTree(parameters: true, type: Backend2.BackendWidget.self))
let storage = DemoView().storage(modifiers: [], type: Backend1.BackendWidget.self)
for _ in 0...2 {
DemoView().updateStorage(storage, modifiers: [], updateProperties: true, type: Backend2.BackendWidget.self)
}

View File

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

View File

@ -2,7 +2,7 @@ import Meta
public enum Backend2 {
public struct TestWidget: BackendWidget {
public struct TestWidget2: BackendWidget {
public init() { }
@ -14,21 +14,20 @@ public enum Backend2 {
[]
}
public func container(modifiers: [(AnyView) -> AnyView]) -> ViewStorage {
print("Init Content")
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
print("Init test widget 2")
let storage = ViewStorage(nil)
storage.fields["test"] = 0
return storage
}
public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool) {
storage.fields["test"] = storage.fields["tests"] as? Int ?? 0 + 1
public func update<WidgetType>(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: WidgetType.Type) {
print("Update test widget 2 (#\(storage.fields["test"] ?? ""))")
storage.fields["test"] = (storage.fields["test"] as? Int ?? 0) + 1
}
}
public protocol BackendView: AnyView { }
public protocol BackendWidget: BackendView, Widget { }
public protocol BackendWidget: Widget { }
}
}