Skip to content

Update CachedEnvironment #310

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 9 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
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ private struct UnsupportedDisplayList: Rule {
var value: DisplayList {
let version = DisplayList.Version(forUpdate: ())
let seed = DisplayList.Seed(version)
let position = position.value
let containerPosition = containerPosition.value
let position = position
let containerPosition = containerPosition
let size = size.value
preconditionFailure("TODO: EmptyViewFactory")
}
Expand Down
316 changes: 196 additions & 120 deletions Sources/OpenSwiftUICore/Data/Environment/CachedEnvironment.swift
Original file line number Diff line number Diff line change
@@ -1,62 +1,60 @@
//
// CachedEnvironment.swift
// OpenSwiftUI
// OpenSwiftUICore
//
// Audited for iOS 15.5
// Status: WIP
// ID: C424ABD9FC88B2DFD0B7B2319F2E7987
// ID: C424ABD9FC88B2DFD0B7B2319F2E7987 (SwiftUI)
// ID: B62A4B04AF9F1325924A089D63071424 (SwiftUICore)

import Foundation
package import Foundation
package import OpenGraphShims

// MARK: - CachedEnvironment [6.4.41]

package struct CachedEnvironment {
package var environment: Attribute<EnvironmentValues>
private var items: [Item]
private var constants: [HashableConstant: AnyAttribute]
// private var animatedFrame: AnimatedFrame?
// private var resolvedFgStyles: [ResolvedFgStyle : Swift<_ShapeStyle_Resolved.ResolvedFg>]

package init(_ environment: Attribute<EnvironmentValues>) {
self.environment = environment
items = []
constants = [:]
// animatedFrame = nil
// resolvedFgStyles = [:] FIXME: 0x100: ?
}

package mutating func attribute<Value>(keyPath: KeyPath<EnvironmentValues, Value>) -> Attribute<Value> {
if let item = items.first(where: { $0.key == keyPath }) {
return Attribute(identifier: item.value)
} else {
let value = environment[keyPath: keyPath]
items.append(Item(key: keyPath, value: value.identifier))
return value
}
}

package mutating func intern<Value>(_ value: Value, id: Int) -> Attribute<Value> {
let constant = HashableConstant(value, id: id)
if let identifier = constants[constant] {
return Attribute(identifier: identifier)
} else {
let attribute = Attribute(value: value)
constants[constant] = attribute.identifier
return attribute
package private(set) var environment: Attribute<EnvironmentValues>

package struct ID: Hashable { // FIXME
package var base: UniqueID

package init() {
self.base = .init()
}
}

func animatedPosition(for inputs: _ViewInputs) -> Attribute<ViewOrigin> {
preconditionFailure("TODO")

private struct MapItem {
var key: CachedEnvironment.ID
var value: AnyAttribute
}

func animatedSize(for inputs: _ViewInputs) -> Attribute<ViewSize> {
preconditionFailure("TODO")

private var mapItems: [MapItem]

private var animatedFrame: AnimatedFrame?

private var resolvedShapeStyles: [ResolvedShapeStyles: Attribute<ShapeStyle.Pack>]

struct PlatformCache {}

private var platformCache: PlatformCache

@inline(__always)
init(_ environment: Attribute<EnvironmentValues>) {
self.environment = environment
self.mapItems = []
self.animatedFrame = nil
self.resolvedShapeStyles = [:]
self.platformCache = PlatformCache()
}

func animatedCGSize(for inputs: _ViewInputs) -> Attribute<CGSize> {
preconditionFailure("TODO")

package mutating func attribute<T>(id: CachedEnvironment.ID, _ body: (EnvironmentValues) -> T) -> Attribute<T> {
guard let item = mapItems.first(where: { $0.key == id }) else {
// Blocked by OG's Map
// preconditionFailure("TODO")
return Attribute(value: body(environment.value))
}
return item.value.unsafeCast(to: T.self)
}

// func resolvedForegroundStyle() {}

func resolvedShapeStyles(
Expand All @@ -69,87 +67,165 @@ package struct CachedEnvironment {
}

extension CachedEnvironment {
private struct Item {
var key: PartialKeyPath<EnvironmentValues>
var value: AnyAttribute
private mutating func withAnimatedFrame<T>(for inputs: _ViewInputs, body: (inout AnimatedFrame) -> T) -> T {
let transaction = inputs.geometryTransaction()
let pixelLength = attribute(id: .pixelLength) { $0.pixelLength }
if let animatedFrame,
animatedFrame.position == inputs.position,
animatedFrame.size == inputs.size,
animatedFrame.pixelLength == pixelLength,
animatedFrame.time == inputs.time,
animatedFrame.transaction == transaction,
animatedFrame.viewPhase == inputs.viewPhase {
// Reuse existing animated frame
} else {
animatedFrame = AnimatedFrame(
inputs: inputs,
pixelLength: pixelLength,
environment: environment
)
}
return body(&animatedFrame!)
}
}

private protocol _Constant {
func hash(into hasher: inout Hasher)
func isEqual(to other: _Constant) -> Bool
}

private struct Constant<Value>: _Constant {
let value: Value
let id: Int

init(_ value: Value, id: Int) {
self.value = value
self.id = id
package mutating func animatedPosition(for inputs: _ViewInputs) -> Attribute<ViewOrigin> {
guard inputs.needsGeometry else {
return inputs.position
}
return withAnimatedFrame(for: inputs) { $0.animatedPosition() }
}

func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(Value.self))
hasher.combine(id)

package mutating func animatedSize(for inputs: _ViewInputs) -> Attribute<ViewSize> {
guard inputs.needsGeometry else {
return inputs.size
}
return withAnimatedFrame(for: inputs) { $0.animatedSize() }
}

func isEqual(to other: _Constant) -> Bool {
let otherConstant = other as? Constant<Value>
return otherConstant.map { id == $0.id } ?? false

package mutating func animatedCGSize(for inputs: _ViewInputs) -> Attribute<CGSize> {
guard inputs.needsGeometry else {
return inputs.size.cgSize
}
return withAnimatedFrame(for: inputs) { $0.animatedCGSize() }
}
}

private struct HashableConstant: Hashable {
let value: _Constant

init<Value>(_ value: Value, id: Int) {
self.value = Constant(value, id: id)
}

func hash(into hasher: inout Hasher) {
value.hash(into: &hasher)
}

static func == (lhs: HashableConstant, rhs: HashableConstant) -> Bool {
lhs.value.isEqual(to: rhs.value)
// MARK: - ResolvedShapeStyles [6.4.41] [WIP]

private struct ResolvedShapeStyles: Hashable {
let environment: Attribute<EnvironmentValues>
let time: Attribute<Time>
let transaction: Attribute<Transaction>
let viewPhase: Attribute<_GraphInputs.Phase>
let mode: OptionalAttribute<_ShapeStyle_ResolverMode>
let role: ShapeRole
let animationsDisabled: Bool
}

// MARK: - CachedEnvironment.AnimatedFrame [6.4.41]

extension CachedEnvironment.AnimatedFrame {
package init(
inputs: _ViewInputs,
pixelLength: Attribute<CGFloat>,
environment: Attribute<EnvironmentValues>
) {
let frameAttribute: Attribute<ViewFrame>
if inputs.supportsVFD {
let attribute = AnimatableFrameAttributeVFD(
position: inputs.position,
size: inputs.size,
pixelLength: pixelLength,
environment: environment,
phase: inputs.viewPhase,
time: inputs.time,
transaction: inputs.transaction,
animationsDisabled: inputs.base.animationsDisabled
)
frameAttribute = Attribute(attribute)
} else {
let attribute = AnimatableFrameAttribute(
position: inputs.position,
size: inputs.size,
pixelLength: pixelLength,
environment: environment,
phase: inputs.viewPhase,
time: inputs.time,
transaction: inputs.transaction,
animationsDisabled: inputs.base.animationsDisabled
)
frameAttribute = Attribute(attribute)
}
frameAttribute.flags = .active
self.init(
inputs: inputs.base,
position: inputs.position,
size: inputs.size,
pixelLength: pixelLength,
animatedFrame: frameAttribute,
environment: environment
)
}
}

//private struct AnimatedFrame {
// let position: Attribute<ViewOrigin>
// let size: Attribute<ViewSize>
// let pixelLength: Attribute<CGFloat>
// let time: Attribute<Time>
// let transaction: Attribute<Transaction>
// let viewPhase: Attribute<_GraphInputs.Phase>
// let animatedFrame: Attribute<ViewFrame>
// private var _animatedPosition: Attribute<ViewOrigin>?
// private var _animatedSize: Attribute<ViewSize>?
//
// mutating func animatePosition() -> Attribute<ViewOrigin> {
// guard let _animatedPosition else {
// // FIXME
// let animatePosition = animatedFrame.unsafeOffset(
// at: 0,
// as:ViewOrigin.self
// )
// _animatedPosition = animatePosition
// return animatePosition
// }
// return _animatedPosition
// }
//
// mutating func animateSize() -> Attribute<ViewSize> {
// guard let _animatedSize else {
// // FIXME
// let animatedSize = animatedFrame.unsafeOffset(
// at: MemoryLayout<ViewOrigin>.size,
// as: ViewSize.self
// )
// _animatedSize = animatedSize
// return animatedSize
// }
// return _animatedSize
// }
//}
extension CachedEnvironment {
package struct AnimatedFrame {
package let position: Attribute<ViewOrigin>
package let size: Attribute<ViewSize>
package let pixelLength: Attribute<CGFloat>
package let time: Attribute<Time>
package let transaction: Attribute<Transaction>
package let viewPhase: Attribute<_GraphInputs.Phase>
package let animatedFrame: Attribute<ViewFrame>
private var _animatedPosition: Attribute<ViewOrigin>?
private var _animatedSize: Attribute<ViewSize>?
private var _animatedCGSize: Attribute<CGSize>?

package init(
inputs: _GraphInputs,
position: Attribute<ViewOrigin>,
size: Attribute<ViewSize>,
pixelLength: Attribute<CGFloat>,
animatedFrame: Attribute<ViewFrame>,
environment: Attribute<EnvironmentValues>
) {
self.position = position
self.size = size
self.pixelLength = pixelLength
self.time = inputs.time
self.transaction = inputs.transaction
self.viewPhase = inputs.phase
self.animatedFrame = animatedFrame
}

package mutating func animatedPosition() -> Attribute<ViewOrigin> {
if let _animatedPosition {
return _animatedPosition
} else {
let animatedPosition = animatedFrame[keyPath: \.origin]
_animatedPosition = animatedPosition
return animatedPosition
}
}

package mutating func animatedSize() -> Attribute<ViewSize> {
if let _animatedSize {
return _animatedSize
} else {
let animatedSize = animatedFrame[keyPath: \.size]
_animatedSize = animatedSize
return animatedSize
}
}

package mutating func animatedCGSize() -> Attribute<CGSize> {
if let _animatedCGSize {
return _animatedCGSize
} else {
let animatedCGSize = animatedFrame[keyPath: \.size.cgSize]
_animatedCGSize = animatedCGSize
return animatedCGSize
}
}
}
}
Loading