Implement view updating system
This commit is contained in:
parent
3c17404dc6
commit
c15839de3b
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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) }])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 { }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 { }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user