Skip to content

Update UnaryLayout #311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/OpenSwiftUICore/Graph/GraphInputs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public struct _GraphInputs {
get { cachedEnvironment.wrappedValue.environment }
set {
cachedEnvironment = MutableBox(CachedEnvironment(newValue))
changedDebugProperties.formUnion(.environment)
changedDebugProperties.insert(.environment)
}
}

Expand Down
22 changes: 1 addition & 21 deletions Sources/OpenSwiftUICore/Layout/GeometryReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,4 @@
// GeometryReader.swift
// OpenSwiftUICore
//
// Status: WIP

extension UnaryLayout where Self.PlacementContextType == _PositionAwarePlacementContext {
package static func makeViewImpl(
modifier: _GraphValue<Self>,
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
preconditionFailure("TODO")
}
}

extension UnaryLayout where Self.PlacementContextType == PlacementContext {
package static func makeViewImpl(
modifier: _GraphValue<Self>,
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
preconditionFailure("TODO")
}
}
// Status: Empty
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
//
// UnaryLayoutComputer.swift
// OpenSwiftUICore
//
// Status: Blocked by _PositionAwarePlacementContext
// ID: 1C3B77B617AD058A6802F719E38F5D79 (SwiftUICore?)

import Foundation
package import OpenGraphShims

// MARK: - UnaryLayout + _PositionAwarePlacementContext [6.4.41] [TODO]

extension UnaryLayout where Self.PlacementContextType == _PositionAwarePlacementContext {
package static func makeViewImpl(
modifier: _GraphValue<Self>,
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
preconditionFailure("TODO")
}
}

// MARK: - UnaryLayout + PlacementContext [6.4.41]

extension UnaryLayout where PlacementContextType == PlacementContext {
package static func makeViewImpl(
modifier: _GraphValue<Self>,
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
guard inputs.requestsLayoutComputer || inputs.needsGeometry else {
return body(_Graph(), inputs)
}
let animatedModifier = makeAnimatable(value: modifier, inputs: inputs.base)
let computer = Attribute( UnaryLayoutComputer(
layout: animatedModifier,
environment: inputs.environment,
childLayoutComputer: OptionalAttribute()
))
var inputs = inputs
let geometry: Attribute<ViewGeometry>!
if inputs.needsGeometry {
geometry = Attribute(UnaryChildGeometry<Self>(
parentSize: inputs.size,
layoutDirection: inputs.layoutDirection,
parentLayoutComputer: computer
))
inputs.size = geometry.size()
inputs.position = Attribute(LayoutPositionQuery(
parentPosition: inputs.position,
localPosition: geometry.origin()
))
inputs.requestsLayoutComputer = true
} else {
geometry = nil
}
let requestsLayoutComputer = inputs.requestsLayoutComputer
var outputs = body(_Graph(), inputs)
if inputs.needsGeometry {
computer.mutateBody(as: UnaryLayoutComputer<Self>.self, invalidating: true) { computer in
computer.$childLayoutComputer = outputs.layoutComputer
}
geometry.mutateBody(as: UnaryChildGeometry<Self>.self, invalidating: true) { geometry in
geometry.$childLayoutComputer = outputs.layoutComputer
}
}
if requestsLayoutComputer {
outputs.layoutComputer = computer
}
return outputs
}
}

private struct UnaryChildGeometry<L>: Rule, AsyncAttribute, CustomStringConvertible
where L: UnaryLayout, L.PlacementContextType == PlacementContext {
@Attribute var parentSize: ViewSize
@Attribute var layoutDirection: LayoutDirection
@Attribute var parentLayoutComputer: LayoutComputer
@OptionalAttribute var childLayoutComputer: LayoutComputer?

var value: ViewGeometry {
let parentSize = parentSize
let computer = parentLayoutComputer
let placement = computer.withMutableEngine(type: UnaryLayoutEngine<L>.self) { engine in
engine.childPlacement(at: parentSize)
}
let childProxy = LayoutProxy(
context: AnyRuleContext(context),
layoutComputer: $childLayoutComputer
)
return childProxy.finallyPlaced(
at: placement,
in: parentSize.value,
layoutDirection: layoutDirection
)
}

var description: String {
"\(L.self) → ChildGeometry"
}
}

private struct UnaryLayoutComputer<L>: StatefulRule, AsyncAttribute, CustomStringConvertible
where L: UnaryLayout, L.PlacementContextType == PlacementContext {
@Attribute var layout: L
@Attribute var environment: EnvironmentValues
@OptionalAttribute var childLayoutComputer: LayoutComputer?

typealias Value = LayoutComputer

mutating func updateValue() {
let context = AnyRuleContext(context)
let engine = UnaryLayoutEngine(
layout: layout,
layoutContext: SizeAndSpacingContext(context: context, environment: $environment),
child: LayoutProxy(context: context, layoutComputer: $childLayoutComputer)
)
update(to: engine)
}

var description: String {
"\(L.self) → LayoutComputer"
}
}

private struct UnaryLayoutEngine<L>: LayoutEngine
where L: UnaryLayout, L.PlacementContextType == PlacementContext {
let layout: L

let layoutContext: SizeAndSpacingContext

let child: LayoutProxy

var dimensionsCache: ViewSizeCache = .init()

var placementCache: Cache3<ViewSize, _Placement> = .init()

init(layout: L, layoutContext: SizeAndSpacingContext, child: LayoutProxy) {
self.layout = layout
self.layoutContext = layoutContext
self.child = child
}

mutating func childPlacement(at size: ViewSize) -> _Placement {
let layout = layout
let child = child
let context = PlacementContext(base: layoutContext, parentSize: size)
return placementCache.get(size) {
layout.placement(of: child, in: context)
}
}

func layoutPriority() -> Double {
layout.layoutPriority(child: child)
}

func ignoresAutomaticPadding() -> Bool {
layout.ignoresAutomaticPadding(child: child)
}

func spacing() -> Spacing {
layout.spacing(in: layoutContext, child: child)
}

mutating func sizeThatFits(_ proposedSize: _ProposedSize) -> CGSize {
let layout = layout
let context = layoutContext
let child = child
return dimensionsCache.get(proposedSize) {
layout.sizeThatFits(
in: proposedSize,
context: context,
child: child
)
}
}

mutating func explicitAlignment(_ k: AlignmentKey, at viewSize: ViewSize) -> CGFloat? {
let placement = childPlacement(at: viewSize)
let dimensions = child.dimensions(in: placement.proposedSize_)
let alignment = child.explicitAlignment(k, at: dimensions.size)
if let alignment {
return placement.frameOrigin(childSize: dimensions.size.value)[k.axis] + alignment
} else {
return alignment
}
}
}

// MARK: - LayoutPositionQuery [6.4.41]

package struct LayoutPositionQuery: Rule, AsyncAttribute {
@Attribute private var parentPosition: ViewOrigin
@Attribute private var localPosition: ViewOrigin

package init(
parentPosition: Attribute<ViewOrigin>,
localPosition: Attribute<ViewOrigin>,
) {
_parentPosition = parentPosition
_localPosition = localPosition
}

package var value: ViewOrigin {
let localPosition = localPosition
let parentPosition = parentPosition
return ViewOrigin(
x: localPosition.x + parentPosition.x,
y: localPosition.y + parentPosition.y
)
}
}
30 changes: 13 additions & 17 deletions Sources/OpenSwiftUICore/Tracing/LayoutTrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,12 @@ package struct LayoutTrace {
preconditionFailure("TODO")
}

func traceCacheLookup(_ proposal: _ProposedSize, _ hit: Bool) {
guard let recorder else {
return
}
recorder.cacheLookup = (proposal, hit)
}

func traceCacheLookup(_ proposal: CGSize, _ hit: Bool) {
guard let recorder else {
return
}
recorder.cacheLookup = (.init(proposal), hit)
}

func traceChildGeometries(_ attribute: AnyAttribute?, at parentSize: ViewSize, origin: CGPoint, _ block: () -> [ViewGeometry]) -> [ViewGeometry] {
preconditionFailure("TODO")
activateFrameIfNeeded()
CoreGlue.shared.startChildGeometries(.init(recorder: self, parentSize: parentSize, origin: origin, attributeID: attribute?.rawValue ?? .zero))
let geometries = block()
CoreGlue.shared.endChildGeometries(.init(recorder: self, geometries: geometries))
return geometries
}

func traceContentDescription(_ attribute: AnyAttribute?, _ description: String) {
Expand All @@ -88,12 +78,18 @@ extension LayoutTrace {

@inline(__always)
package static func traceCacheLookup(_ proposal: _ProposedSize, _ hit: Bool) {
recorder!.traceCacheLookup(proposal, hit)
guard let recorder else {
return
}
recorder.cacheLookup = (proposal, hit)
}

@inline(__always)
package static func traceCacheLookup(_ proposal: CGSize, _ hit: Bool) {
recorder!.traceCacheLookup(proposal, hit)
guard let recorder else {
return
}
recorder.cacheLookup = (.init(proposal), hit)
}

@inline(__always)
Expand Down
10 changes: 6 additions & 4 deletions Sources/OpenSwiftUICore/Util/CoreGlue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,27 +141,29 @@ extension CoreGlue {
}

public struct StartChildGeometriesParameters {
// package var recorder: LayoutTrace.Recorder
package var recorder: LayoutTrace.Recorder

package var parentSize: ViewSize

package var origin: CGPoint

package var attributeID: UInt32

package init(parentSize: ViewSize, origin: CGPoint, attributeID: UInt32) {
package init(recorder: LayoutTrace.Recorder, parentSize: ViewSize, origin: CGPoint, attributeID: UInt32) {
self.recorder = recorder
self.parentSize = parentSize
self.origin = origin
self.attributeID = attributeID
}
}

public struct EndChildGeometriesParameters {
// package var recorder: LayoutTrace.Recorder
package var recorder: LayoutTrace.Recorder

package var geometries: [ViewGeometry]

package init(geometries: [ViewGeometry]) {
package init(recorder: LayoutTrace.Recorder, geometries: [ViewGeometry]) {
self.recorder = recorder
self.geometries = geometries
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/OpenSwiftUICore/View/Input/ViewInputs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public struct _ViewInputs {
get { base.phase }
set {
base.phase = newValue
base.changedDebugProperties.formUnion(.phase)
base.changedDebugProperties.insert(.phase)
}
}

Expand All @@ -65,21 +65,21 @@ public struct _ViewInputs {

package var transform: Attribute<ViewTransform> {
didSet {
base.changedDebugProperties.formUnion(.transform)
base.changedDebugProperties.insert(.transform)
}
}

package var position: Attribute<ViewOrigin> {
didSet {
base.changedDebugProperties.formUnion(.position)
base.changedDebugProperties.insert(.position)
}
}

package var containerPosition: Attribute<ViewOrigin>

package var size: Attribute<ViewSize> {
didSet {
base.changedDebugProperties.formUnion(.size)
base.changedDebugProperties.insert(.size)
}
}

Expand Down
4 changes: 1 addition & 3 deletions Sources/OpenSwiftUICore/View/Input/ViewOutputs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ public struct _ViewOutputs {
}
set {
_layoutComputer = OptionalAttribute(newValue)
if !preferences.debugProperties.contains(.layoutComputer) {
preferences.debugProperties.formUnion(.layoutComputer)
}
preferences.debugProperties.insert(.layoutComputer)
}
}

Expand Down