Skip to content
This repository has been archived by the owner on Oct 17, 2024. It is now read-only.

Commit

Permalink
Add binding properties
Browse files Browse the repository at this point in the history
  • Loading branch information
david-swift committed Sep 16, 2024
1 parent 1771847 commit 6289e51
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 44 deletions.
76 changes: 76 additions & 0 deletions Sources/Model/User Interface/View/Properties/BindingProperty.swift
Original file line number Diff line number Diff line change
@@ -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 }

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

/// Initialize a property.
Expand Down Expand Up @@ -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.
public enum UpdateStrategy {

Expand Down Expand Up @@ -228,6 +184,9 @@ extension Widget {
initViewProperty(value, view: subview, parent: storage)
storage.content[property.label ?? .mainContent] = [subview]
}
if let value = property.value as? any BindingPropertyProtocol {
initBindingProperty(value, parent: storage)
}
}
}

Expand All @@ -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``.
/// - Parameters:
/// - storage: The storage to update.
Expand Down Expand Up @@ -292,6 +261,9 @@ extension Widget {
let storage = storage.content[property.label ?? .mainContent]?.first {
value.wrappedValue.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
}
if let value = property.value as? any BindingPropertyProtocol {
setBindingProperty(property: value, storage: storage)
}
}
}

Expand Down Expand Up @@ -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.
Expand Down
50 changes: 50 additions & 0 deletions Sources/Model/User Interface/View/Properties/ViewProperty.swift
Original file line number Diff line number Diff line change
@@ -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 }

}

0 comments on commit 6289e51

Please sign in to comment.