From c57e43ae1a708930590fd24d9d70ef0855cab75f Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Sep 2025 19:50:11 +0800 Subject: [PATCH 1/4] Add ViewStyle API --- .../View/Style/ViewStyle.swift | 229 ++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 Sources/OpenSwiftUICore/View/Style/ViewStyle.swift diff --git a/Sources/OpenSwiftUICore/View/Style/ViewStyle.swift b/Sources/OpenSwiftUICore/View/Style/ViewStyle.swift new file mode 100644 index 000000000..fc4eb38e3 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Style/ViewStyle.swift @@ -0,0 +1,229 @@ +// +// ViewStyle.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete +// ID: AC03956538119E2820390436C305EBF1 (SwiftUI) +// ID: AC59074524C298808AAD87A4737AEFFC (SwiftUICore) + +import OpenAttributeGraphShims + +// MARK: - StyleableView + +package protocol StyleableView: View where Configuration == DefaultStyleModifier.StyleConfiguration { + associatedtype Configuration + + associatedtype DefaultStyleModifier: StyleModifier + + var configuration: Configuration { get } + + static var defaultStyleModifier: DefaultStyleModifier { get } +} + +// MARK: - StyleModifier + +package protocol StyleModifier: MultiViewModifier, PrimitiveViewModifier { + associatedtype Style + + associatedtype StyleConfiguration + + associatedtype StyleBody: View + + init(style: Style) + + var style: Style { get set } + + func styleBody(configuration: StyleConfiguration) -> StyleBody +} + +// MARK: - AnyStyleModifierType + +private protocol AnyStyleModifierType { + static func makeView( + view: _GraphValue, + modifier: AnyStyleModifier, + inputs: _ViewInputs + ) -> _ViewOutputs where V: StyleableView + + static func makeViewList( + view: _GraphValue, + modifier: AnyStyleModifier, + inputs: _ViewListInputs + ) -> _ViewListOutputs where V: StyleableView + + static func viewListCount(inputs: _ViewListCountInputs) -> Int? +} + +// MARK: - AutomaticStyleOverrideModifier + +struct AutomaticStyleOverrideModifier: PrimitiveViewModifier, _GraphInputsModifier where M: StyleModifier { + var styleModifier: M + + init(_: I.Type, modifier: M) { + self.styleModifier = modifier + } + + static func _makeInputs(modifier: _GraphValue, inputs: inout _GraphInputs) { + inputs[StyleOverrideInput.self] = AnyStyleModifier( + value: modifier[offset: { .of(&$0.styleModifier) }].value.identifier, + _type: StyleModifierType.self + ) + } +} + +// MARK: - StyleOverrideInput + +private struct StyleOverrideInput: ViewInput { + static var defaultValue: AnyStyleModifier? { + nil + } +} + +// MARK: - MakeDefaultRepresentation + +private struct MakeDefaultRepresentation: Rule where V: StyleableView { + @Attribute var view: V + + typealias Value = ModifiedContent + + var value: Value { + view.modifier(V.defaultStyleModifier) + } +} + +// MARK: - AnyStyleModifier + +struct AnyStyleModifier { + var value: AnyAttribute + let _type: Any.Type +} + +// MARK: - StyleInput + +private struct StyleInput: ViewInput { + static var defaultValue: Stack { + .init() + } +} + +// MARK: - MakeResolvedRepresentation + +private struct MakeResolvedRepresentation: Rule where V: StyleableView { + @Attribute var view: V + + var value: V.Body { + view.body + } +} + +// MARK: - StyleModifierType + +private struct StyleModifierType: AnyStyleModifierType where M: StyleModifier { + static func makeView( + view: _GraphValue, + modifier: AnyStyleModifier, + inputs: _ViewInputs + ) -> _ViewOutputs where V: StyleableView { + var inputs = inputs + let fields = DynamicPropertyCache.fields(of: M.Style.self) + let (styleBody, buffer) = makeStyleBody( + view: view, + modifier: modifier, + inputs: &inputs.base, + fields: fields + ) + let outputs = M.StyleBody.makeDebuggableView( + view: styleBody, + inputs: inputs + ) + if let buffer { + buffer.traceMountedProperties( + to: view, + fields: fields + ) + } + return outputs + } + + static func makeStyleBody( + view: _GraphValue, + modifier: AnyStyleModifier, + inputs: inout _GraphInputs, + fields: DynamicPropertyCache.Fields + ) -> (_GraphValue, _DynamicPropertyBuffer?) where V: StyleableView { + if Semantics.ViewStylesMustBeValueTypes.isEnabled { + guard Metadata(M.Style.self).isValueType else { + preconditionFailure("styles must be value types (either a struct or an enum); \(M.Style.self) is a class.") + } + } + let styleModifier = modifier.value.unsafeCast(to: M.self) + let accessor = StyleBodyAccessor( + view: view.value, + styleModifier: styleModifier + ) + return accessor + .makeBody( + container: .init(styleModifier[offset: { .of(&$0.style) }]), + inputs: &inputs, + fields: fields + ) + } + + static func makeViewList( + view: _GraphValue, + modifier: AnyStyleModifier, + inputs: _ViewListInputs + ) -> _ViewListOutputs where V: StyleableView { + var inputs = inputs + let fields = DynamicPropertyCache.fields(of: M.Style.self) + let (styleBody, buffer) = makeStyleBody( + view: view, + modifier: modifier, + inputs: &inputs.base, + fields: fields + ) + let outputs = M.StyleBody.makeDebuggableViewList( + view: styleBody, + inputs: inputs + ) + if let buffer { + buffer.traceMountedProperties( + to: view, + fields: fields + ) + } + return outputs + } + + static func viewListCount(inputs: _ViewListCountInputs) -> Int? { + M.StyleBody._viewListCount(inputs: inputs) + } +} + +// MARK: - StyleableViewContextInput + +private struct StyleableViewContextInput: ViewInput { + static var defaultValue: Any.Type? { nil } +} + +// MARK: - StyleBodyAccessor + +private struct StyleBodyAccessor: BodyAccessor where V: StyleableView, M: StyleModifier { + @Attribute var view: V + @Attribute var styleModifier: M + + typealias Container = M.Style + + typealias Body = M.StyleBody + + func updateBody(of container: Container, changed: Bool) { + let (view, viewChanged) = $view.changedValue() + guard changed || viewChanged else { + return + } + setBody { + styleModifier.styleBody(configuration: view.configuration as! M.StyleConfiguration) + } + } +} From 90e5815b5fdb6a4b18187c1fd2409ab1411ec756 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Sep 2025 21:00:37 +0800 Subject: [PATCH 2/4] Update StyleableView and StyleModifier attribute implementation --- .../View/Style/ViewStyle.swift | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/Sources/OpenSwiftUICore/View/Style/ViewStyle.swift b/Sources/OpenSwiftUICore/View/Style/ViewStyle.swift index fc4eb38e3..9aa3ef029 100644 --- a/Sources/OpenSwiftUICore/View/Style/ViewStyle.swift +++ b/Sources/OpenSwiftUICore/View/Style/ViewStyle.swift @@ -21,6 +21,74 @@ package protocol StyleableView: View where Configuration == DefaultStyleModifier static var defaultStyleModifier: DefaultStyleModifier { get } } +extension StyleableView { + package var body: some View { self } + + nonisolated package static func _makeView( + view: _GraphValue, + inputs: _ViewInputs + ) -> _ViewOutputs { + var inputs = inputs + guard inputs.base.isCurrentStyleableView(Self.self) else { + inputs.base.setCurrentStyleableView(Self.self) + return Body._makeView( + view: _GraphValue(MakeResolvedRepresentation(view: view.value)), + inputs: inputs + ) + } + guard let modifier = inputs.base.popLast(StyleInput.self) else { + return MakeDefaultRepresentation.Value.makeDebuggableView( + view: _GraphValue(MakeDefaultRepresentation(view: view.value)), + inputs: inputs + ) + } + return (modifier._type as! AnyStyleModifierType.Type).makeView( + view: view, + modifier: modifier, + inputs: inputs + ) + } + + nonisolated package static func _makeViewList( + view: _GraphValue, + inputs: _ViewListInputs + ) -> _ViewListOutputs { + var inputs = inputs + guard inputs.base.isCurrentStyleableView(Self.self) else { + inputs.base.setCurrentStyleableView(Self.self) + return Body._makeViewList( + view: _GraphValue(MakeResolvedRepresentation(view: view.value)), + inputs: inputs + ) + } + guard let modifier = inputs.base.popLast(StyleInput.self) else { + return MakeDefaultRepresentation.Value.makeDebuggableViewList( + view: _GraphValue(MakeDefaultRepresentation(view: view.value)), + inputs: inputs + ) + } + return (modifier._type as! AnyStyleModifierType.Type).makeViewList( + view: view, + modifier: modifier, + inputs: inputs + ) + } + + nonisolated package static func _viewListCount( + inputs: _ViewListCountInputs + ) -> Int? { + var inputs = inputs + guard inputs.isCurrentStyleableView(Self.self) else { + inputs.setCurrentStyleableView(Self.self) + return Body._viewListCount(inputs: inputs) + } + guard let modifier = inputs.popLast(StyleInput.self) else { + return MakeDefaultRepresentation.Value._viewListCount(inputs: inputs) + } + return (modifier._type as! AnyStyleModifierType.Type).viewListCount(inputs: inputs) + } +} + // MARK: - StyleModifier package protocol StyleModifier: MultiViewModifier, PrimitiveViewModifier { @@ -37,6 +105,57 @@ package protocol StyleModifier: MultiViewModifier, PrimitiveViewModifier { func styleBody(configuration: StyleConfiguration) -> StyleBody } +extension StyleModifier { + nonisolated package static func _makeView( + modifier: _GraphValue, + inputs: _ViewInputs, + body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs + ) -> _ViewOutputs { + var inputs = inputs + let override = inputs[StyleOverrideInput