Add binding properties
This commit is contained in:
parent
17718471e9
commit
6289e518e5
@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// Property.swift
|
||||||
|
// Meta
|
||||||
|
//
|
||||||
|
// Created by david-swift on 16.09.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Assign an observing and updating closure to a widget's binding property.
|
||||||
|
///
|
||||||
|
/// This will be used if you do not provide a custom ``Widget/update(_:data:updateProperties:type:)`` method
|
||||||
|
/// or call the ``Widget/updateProperties(_:updateProperties:)`` method in your custom update method.
|
||||||
|
@propertyWrapper
|
||||||
|
public struct BindingProperty<Value, Pointer>: BindingPropertyProtocol {
|
||||||
|
|
||||||
|
/// The wrapped binding.
|
||||||
|
public var wrappedValue: Binding<Value>
|
||||||
|
/// Observe the UI element.
|
||||||
|
var observe: (Pointer, Binding<Value>, ViewStorage) -> Void
|
||||||
|
/// Set the UI element's property.
|
||||||
|
var setValue: (Pointer, Value, ViewStorage) -> Void
|
||||||
|
|
||||||
|
/// Initialize a property.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - wrappedValue: The wrapped value.
|
||||||
|
/// - getProperty: Get the property from the UI.
|
||||||
|
/// - setProperty: The function applying the property to the UI.
|
||||||
|
/// - pointer: The type of the pointer.
|
||||||
|
public init(
|
||||||
|
wrappedValue: Binding<Value>,
|
||||||
|
observe: @escaping (Pointer, Binding<Value>, ViewStorage) -> Void,
|
||||||
|
set setValue: @escaping (Pointer, Value, ViewStorage) -> Void,
|
||||||
|
pointer: Pointer.Type
|
||||||
|
) {
|
||||||
|
self.wrappedValue = wrappedValue
|
||||||
|
self.observe = observe
|
||||||
|
self.setValue = setValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a property.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - wrappedValue: The wrapped value.
|
||||||
|
/// - getProperty: Get the property from the UI.
|
||||||
|
/// - setProperty: The function applying the property to the UI.
|
||||||
|
/// - pointer: The type of the pointer.
|
||||||
|
public init(
|
||||||
|
wrappedValue: Binding<Value>,
|
||||||
|
observe: @escaping (Pointer, Binding<Value>) -> Void,
|
||||||
|
set setValue: @escaping (Pointer, Value) -> Void,
|
||||||
|
pointer: Pointer.Type
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
wrappedValue: wrappedValue,
|
||||||
|
observe: { pointer, value, _ in observe(pointer, value) },
|
||||||
|
set: { pointer, value, _ in setValue(pointer, value) },
|
||||||
|
pointer: pointer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The binding property protocol.
|
||||||
|
protocol BindingPropertyProtocol {
|
||||||
|
|
||||||
|
/// The binding's wrapped value.
|
||||||
|
associatedtype Value
|
||||||
|
/// The storage's pointer.
|
||||||
|
associatedtype Pointer
|
||||||
|
|
||||||
|
/// The wrapped value.
|
||||||
|
var wrappedValue: Binding<Value> { get }
|
||||||
|
/// Observe the property.
|
||||||
|
var observe: (Pointer, Binding<Value>, ViewStorage) -> Void { get }
|
||||||
|
/// Set the property.
|
||||||
|
var setValue: (Pointer, Value, ViewStorage) -> Void { get }
|
||||||
|
|
||||||
|
}
|
||||||
@ -58,33 +58,6 @@ public struct Property<Value, Pointer>: PropertyProtocol {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign an updating closure to a widget's property.
|
|
||||||
///
|
|
||||||
/// This will be used if you do not provide a custom ``Widget/update(_:data:updateProperties:type:)`` method
|
|
||||||
/// or call the ``Widget/updateProperties(_:updateProperties:)`` method in your custom update method.
|
|
||||||
@propertyWrapper
|
|
||||||
public struct ViewProperty<Pointer, ViewPointer>: ViewPropertyProtocol {
|
|
||||||
|
|
||||||
/// The wrapped value.
|
|
||||||
public var wrappedValue: Body = []
|
|
||||||
/// Set the view.
|
|
||||||
var setView: (Pointer, ViewPointer) -> Void
|
|
||||||
|
|
||||||
/// Initialize a property.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - setView: Set the view.
|
|
||||||
/// - pointer: The pointer type of the parent view (usually a concrete view type).
|
|
||||||
/// - subview: The pointer type of the child view (usually a protocol, view class, or similar).
|
|
||||||
public init(
|
|
||||||
set setView: @escaping (Pointer, ViewPointer) -> Void,
|
|
||||||
pointer: Pointer.Type,
|
|
||||||
subview: ViewPointer.Type
|
|
||||||
) {
|
|
||||||
self.setView = setView
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Property where Value: OptionalProtocol {
|
extension Property where Value: OptionalProtocol {
|
||||||
|
|
||||||
/// Initialize a property.
|
/// Initialize a property.
|
||||||
@ -143,23 +116,6 @@ protocol PropertyProtocol {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The view property protocol.
|
|
||||||
///
|
|
||||||
/// Do not use for wrapper widgets.
|
|
||||||
protocol ViewPropertyProtocol {
|
|
||||||
|
|
||||||
/// The type of the view's pointer.
|
|
||||||
associatedtype Pointer
|
|
||||||
/// The type of the view's content.
|
|
||||||
associatedtype ViewPointer
|
|
||||||
|
|
||||||
/// The wrapped value.
|
|
||||||
var wrappedValue: Body { get }
|
|
||||||
/// Set the view.
|
|
||||||
var setView: (Pointer, ViewPointer) -> Void { get }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The update strategy for properties.
|
/// The update strategy for properties.
|
||||||
public enum UpdateStrategy {
|
public enum UpdateStrategy {
|
||||||
|
|
||||||
@ -228,6 +184,9 @@ extension Widget {
|
|||||||
initViewProperty(value, view: subview, parent: storage)
|
initViewProperty(value, view: subview, parent: storage)
|
||||||
storage.content[property.label ?? .mainContent] = [subview]
|
storage.content[property.label ?? .mainContent] = [subview]
|
||||||
}
|
}
|
||||||
|
if let value = property.value as? any BindingPropertyProtocol {
|
||||||
|
initBindingProperty(value, parent: storage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +205,16 @@ extension Widget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize a binding property.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - value: The property.
|
||||||
|
/// - parent: The view storage.
|
||||||
|
func initBindingProperty<Property>(_ value: Property, parent: ViewStorage) where Property: BindingPropertyProtocol {
|
||||||
|
if let view = parent.pointer as? Property.Pointer {
|
||||||
|
value.observe(view, value.wrappedValue, parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the properties wrapped with ``Property``.
|
/// Update the properties wrapped with ``Property``.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - storage: The storage to update.
|
/// - storage: The storage to update.
|
||||||
@ -292,6 +261,9 @@ extension Widget {
|
|||||||
let storage = storage.content[property.label ?? .mainContent]?.first {
|
let storage = storage.content[property.label ?? .mainContent]?.first {
|
||||||
value.wrappedValue.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
|
value.wrappedValue.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
|
||||||
}
|
}
|
||||||
|
if let value = property.value as? any BindingPropertyProtocol {
|
||||||
|
setBindingProperty(property: value, storage: storage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,6 +340,22 @@ extension Widget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply a binding property to the framework.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - property: The property.
|
||||||
|
/// - storage: The view storage.
|
||||||
|
func setBindingProperty<Property>(
|
||||||
|
property: Property,
|
||||||
|
storage: ViewStorage
|
||||||
|
) where Property: BindingPropertyProtocol {
|
||||||
|
if let optional = property.wrappedValue.wrappedValue as? any OptionalProtocol, optional.optionalValue == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let pointer = storage.pointer as? Property.Pointer {
|
||||||
|
property.setValue(pointer, property.wrappedValue.wrappedValue, storage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A protocol for values which can be optional.
|
/// A protocol for values which can be optional.
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// Property.swift
|
||||||
|
// Meta
|
||||||
|
//
|
||||||
|
// Created by david-swift on 16.09.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Assign an updating closure to a widget's property.
|
||||||
|
///
|
||||||
|
/// This will be used if you do not provide a custom ``Widget/update(_:data:updateProperties:type:)`` method
|
||||||
|
/// or call the ``Widget/updateProperties(_:updateProperties:)`` method in your custom update method.
|
||||||
|
@propertyWrapper
|
||||||
|
public struct ViewProperty<Pointer, ViewPointer>: ViewPropertyProtocol {
|
||||||
|
|
||||||
|
/// The wrapped value.
|
||||||
|
public var wrappedValue: Body = []
|
||||||
|
/// Set the view.
|
||||||
|
var setView: (Pointer, ViewPointer) -> Void
|
||||||
|
|
||||||
|
/// Initialize a property.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - setView: Set the view.
|
||||||
|
/// - pointer: The pointer type of the parent view (usually a concrete view type).
|
||||||
|
/// - subview: The pointer type of the child view (usually a protocol, view class, or similar).
|
||||||
|
public init(
|
||||||
|
set setView: @escaping (Pointer, ViewPointer) -> Void,
|
||||||
|
pointer: Pointer.Type,
|
||||||
|
subview: ViewPointer.Type
|
||||||
|
) {
|
||||||
|
self.setView = setView
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The view property protocol.
|
||||||
|
///
|
||||||
|
/// Do not use for wrapper widgets.
|
||||||
|
protocol ViewPropertyProtocol {
|
||||||
|
|
||||||
|
/// The type of the view's pointer.
|
||||||
|
associatedtype Pointer
|
||||||
|
/// The type of the view's content.
|
||||||
|
associatedtype ViewPointer
|
||||||
|
|
||||||
|
/// The wrapped value.
|
||||||
|
var wrappedValue: Body { get }
|
||||||
|
/// Set the view.
|
||||||
|
var setView: (Pointer, ViewPointer) -> Void { get }
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user