diff --git a/Package.resolved b/Package.resolved index 89bce8d42..9a01d3bd7 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "6ae2798ced7417c65233e0130814db449aa933d9f98ec74d935b06b494949ef9", + "originHash" : "5c37246fc3829f800b0ccd442988c364c2beeffc296cdc8b4f7ce1585eebc14a", "pins" : [ { "identity" : "opengraph", @@ -7,7 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/OpenGraph", "state" : { "branch" : "main", - "revision" : "93e713f1357fc04ca917635d09460dbf42b663b6" + "revision" : "f2b16a1145acadd47c708d528f7eb1047d412f78" } }, { diff --git a/Package.swift b/Package.swift index 046b5bb9d..6bf2bd76d 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,7 @@ let development = envEnable("OPENSWIFTUI_DEVELOPMENT", default: false) // Xcode use clang as linker which supports "-iframework" while SwiftPM use swiftc as linker which supports "-Fsystem" let systemFrameworkSearchFlag = isXcodeEnv ? "-iframework" : "-Fsystem" -let releaseVersion = Context.environment["OPENSWIFTUI_TARGET_RELEASE"].flatMap { Int($0) } ?? 2021 +let releaseVersion = Context.environment["OPENSWIFTUI_TARGET_RELEASE"].flatMap { Int($0) } ?? 2024 let platforms: [SupportedPlatform] = switch releaseVersion { case 2024: // iOS 18.0 [ diff --git a/Sources/OpenSwiftUICore/View/InterfaceIdiom.swift b/Sources/OpenSwiftUI/View/InterfaceIdiom/InterfaceIdiom.swift similarity index 80% rename from Sources/OpenSwiftUICore/View/InterfaceIdiom.swift rename to Sources/OpenSwiftUI/View/InterfaceIdiom/InterfaceIdiom.swift index 354f1bb3e..0c7049194 100644 --- a/Sources/OpenSwiftUICore/View/InterfaceIdiom.swift +++ b/Sources/OpenSwiftUI/View/InterfaceIdiom/InterfaceIdiom.swift @@ -2,9 +2,11 @@ // InterfaceIdiom.swift // OpenSwiftUI // -// Audited for iOS 15.5 +// Audited for iOS 15.0 // Status: Complete -// ID: 2FFD16F575FFD9B8AC17BCAE09549F2 +// ID: 2FFD16F575FFD9B8AC17BCAE09549F23 (SwiftUI) + +#if OPENSWIFTUI_RELEASE_2021 // MARK: InterfaceIdiomType @@ -112,3 +114,27 @@ extension UIUserInterfaceIdiom { } } #endif + +#elseif OPENSWIFTUI_RELEASE_2024 + +#if os(iOS) || os(tvOS) +import OpenSwiftUICore +import UIKit + +extension UIUserInterfaceIdiom { + var idiom: AnyInterfaceIdiom? { + switch rawValue { + case UIUserInterfaceIdiom.phone.rawValue: return AnyInterfaceIdiom(.phone) + case UIUserInterfaceIdiom.pad.rawValue: return AnyInterfaceIdiom(.pad) + case UIUserInterfaceIdiom.tv.rawValue: return AnyInterfaceIdiom(.tv) + case 4: return AnyInterfaceIdiom(.watch) // There is no UIUserInterfaceIdiom.watch exposed currently + case UIUserInterfaceIdiom.carPlay.rawValue: return AnyInterfaceIdiom(.carPlay) + case UIUserInterfaceIdiom.mac.rawValue: return AnyInterfaceIdiom(.mac) + case UIUserInterfaceIdiom.vision.rawValue: return AnyInterfaceIdiom(.vision) + default: return nil + } + } +} +#endif + +#endif diff --git a/Sources/OpenSwiftUICore/Data/Environment/CachedEnvironment.swift b/Sources/OpenSwiftUICore/Data/Environment/CachedEnvironment.swift index 6d11db538..66db15c49 100644 --- a/Sources/OpenSwiftUICore/Data/Environment/CachedEnvironment.swift +++ b/Sources/OpenSwiftUICore/Data/Environment/CachedEnvironment.swift @@ -57,13 +57,17 @@ package struct CachedEnvironment { #endif } -// func animatePosition(for inputs: _ViewInputs) -> Attribute { -// fatalError("TODO") -// } -// -// func animateSize(for inputs: _ViewInputs) -> Attribute { -// fatalError("TODO") -// } + func animatedPosition(for inputs: _ViewInputs) -> Attribute { + fatalError("TODO") + } + + func animatedSize(for inputs: _ViewInputs) -> Attribute { + fatalError("TODO") + } + + func animatedCGSize(for inputs: _ViewInputs) -> Attribute { + fatalError("TODO") + } // func resolvedForegroundStyle() {} } diff --git a/Sources/OpenSwiftUICore/Data/Environment/EnvironmentValues+IsVisionEnabledKey.swift b/Sources/OpenSwiftUICore/Data/Environment/EnvironmentValues+IsVisionEnabledKey.swift new file mode 100644 index 000000000..76b26192c --- /dev/null +++ b/Sources/OpenSwiftUICore/Data/Environment/EnvironmentValues+IsVisionEnabledKey.swift @@ -0,0 +1,25 @@ +// +// EnvironmentValues+IsVisionEnabledKey.swift +// OpenSwiftUICore +// +// Audited for iOS 18.0 +// Status: Complete + +extension EnvironmentValues { + package var isVisionEnabled: Bool { + get { + #if os(macOS) + false + #else + self[IsVisionEnabledKey.self] + #endif + } + set { self[IsVisionEnabledKey.self] = newValue } + } +} + +package struct IsVisionEnabledKey: EnvironmentKey { + package static let defaultValue: Bool = false + + package typealias Value = Bool +} diff --git a/Sources/OpenSwiftUICore/Data/Other/VersionSeed.swift b/Sources/OpenSwiftUICore/Data/Other/VersionSeed.swift index 66679bb31..265ddb5ac 100644 --- a/Sources/OpenSwiftUICore/Data/Other/VersionSeed.swift +++ b/Sources/OpenSwiftUICore/Data/Other/VersionSeed.swift @@ -1,6 +1,6 @@ // // VersionSeed.swift -// OpenSwiftUI +// OpenSwiftUICore // // Audited for iOS 18.0 // Status: Complete @@ -64,8 +64,6 @@ package struct VersionSeed: CustomStringConvertible { } } -// ID: 1B00D77CE2C80F9C0F5A59FDEA30ED6B (RELEASE_2021) -// ID: F99DF4753FB5F5765C388695646E450B (RELEASE_2024) private func merge32(_ a: UInt32, _ b: UInt32) -> UInt32 { let a = UInt64(a) let b = UInt64(b) diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift index 6c0c4cd2c..fd7e4e98b 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift @@ -3,20 +3,20 @@ // OpenSwiftUICore // // Audited for iOS 18.0 -// Status: TO BE AUDITED -// ID: A9FAE381E99529D5274BA37A9BC9B074 (RELEASE_2021) -// ID: DF57A19C61B44C613EB77C1D47FC679A (RELEASE_2024) +// Status: Blocked by ViewGraph +// ID: A9FAE381E99529D5274BA37A9BC9B074 (SwiftUI) +// ID: DF57A19C61B44C613EB77C1D47FC679A (SwiftUICore) -import OpenGraphShims +package import OpenGraphShims package final class PreferenceBridge { - weak var viewGraph: ViewGraph? - var isValid: Bool = true - private(set) var children: [Unmanaged] = [] + package weak var viewGraph: ViewGraph? + private var isValid: Bool = true + private var children: [Unmanaged] = [] var requestedPreferences = PreferenceKeys() var bridgedViewInputs = PropertyList() - @WeakAttribute var hostPreferenceKeys: PreferenceKeys? - @WeakAttribute var hostPreferencesCombiner: PreferenceList? + @WeakAttribute private var hostPreferenceKeys: PreferenceKeys? + @WeakAttribute private var hostPreferencesCombiner: PreferenceList? private var bridgedPreferences: [BridgedPreference] = [] struct BridgedPreference { @@ -24,18 +24,99 @@ package final class PreferenceBridge { var combiner: AnyWeakAttribute } - init() { + package init() { viewGraph = ViewGraph.current } + package func invalidate() { + requestedPreferences = PreferenceKeys() + bridgedViewInputs = PropertyList() + // TODO: Blocked by ViewGraph + // for child in children { + // let viewGraph = child.takeRetainedValue() + // viewGraph.setPreferenceBridge(to: nil, isInvalidating: true) + // child.release() + // } + isValid = false + } + deinit { if isValid { invalidate() } } - // FIXME: TO BE AUDITED + package func wrapInputs(_ inputs: inout _ViewInputs) { + #if canImport(Darwin) + inputs.customInputs = bridgedViewInputs + for key in requestedPreferences { + inputs.preferences.keys.add(key) + } + inputs.preferences.hostKeys = Attribute(MergePreferenceKeys(lhs: inputs.preferences.hostKeys, rhs: _hostPreferenceKeys)) + #endif + } + + package func wrapOutputs(_ outputs: inout PreferencesOutputs, inputs: _ViewInputs) { + #if canImport(Darwin) + bridgedViewInputs = inputs.customInputs + for key in inputs.preferences.keys { + if key == _AnyPreferenceKey.self { + let combiner = Attribute( + HostPreferencesCombiner( + keys: inputs.preferences.hostKeys, + values: Attribute(identifier: outputs[anyKey: _AnyPreferenceKey.self] ?? .nil) + ) + ) + outputs[anyKey: key] = combiner.identifier + _hostPreferenceKeys = WeakAttribute(inputs.preferences.hostKeys) + _hostPreferencesCombiner = WeakAttribute(combiner) + } else { + struct MakeCombiner: PreferenceKeyVisitor { + var result: AnyAttribute? + + mutating func visit(key: K.Type) where K : PreferenceKey { + let combiner = PreferenceCombiner(attributes: []) + result = Attribute(combiner).identifier + } + } + guard outputs[anyKey: key] == nil else { + break + } + var combiner = MakeCombiner() + key.visitKey(&combiner) + guard let result = combiner.result else { + break + } + requestedPreferences.add(key) + bridgedPreferences.append(BridgedPreference(key: key, combiner: AnyWeakAttribute(result))) + outputs[anyKey: key] = result + } + } + #endif + + } + + package func addChild(_ child: ViewGraph) { + guard !children.contains(where: { $0.takeUnretainedValue() === child }) else { + return + } + children.append(Unmanaged.passUnretained(child)) + } + + package func removeChild(_ child: ViewGraph) { + guard let index = children.firstIndex(where: { $0.takeUnretainedValue() === child }) else { + return + } + children.remove(at: index) + } - #if canImport(Darwin) // FIXME: See #39 - func addValue(_ value: AnyAttribute, for keyType: AnyPreferenceKey.Type) { + package func removedStateDidChange() { + // TODO: Blocked by ViewGraph + // for child in children { + // let viewGraph = child.takeUnretainedValue() + // } + } + + #if canImport(Darwin) + package func addValue(_ src: AnyAttribute, for key: any AnyPreferenceKey.Type) { struct AddValue: PreferenceKeyVisitor { var combiner: AnyAttribute var value: AnyAttribute @@ -48,18 +129,16 @@ package final class PreferenceBridge { } } } - guard let bridgedPreference = bridgedPreferences.first(where: { $0.key == keyType }) else { - return - } - guard let combiner = bridgedPreference.combiner.attribute else { - return - } - var visitor = AddValue(combiner: combiner, value: value) - keyType.visitKey(&visitor) - viewGraph?.graphInvalidation(from: value) + guard let viewGraph, + let bridgedPreference = bridgedPreferences.first(where: { $0.key == key }), + let combiner = bridgedPreference.combiner.attribute + else { return } + var visitor = AddValue(combiner: combiner, value: src) + key.visitKey(&visitor) + viewGraph.graphInvalidation(from: src) } - - func removeValue(_ value: AnyAttribute, for keyType: AnyPreferenceKey.Type, isInvalidating: Bool) { + + package func removeValue(_ src: AnyAttribute, for key: any AnyPreferenceKey.Type, isInvalidating: Bool = false) { struct RemoveValue: PreferenceKeyVisitor { var combiner: AnyAttribute var value: AnyAttribute @@ -77,36 +156,54 @@ package final class PreferenceBridge { } } } - guard let bridgedPreference = bridgedPreferences.first(where: { $0.key == keyType }) else { - return - } - guard let combiner = bridgedPreference.combiner.attribute else { - return - } - var visitor = RemoveValue(combiner: combiner, value: value) - keyType.visitKey(&visitor) + guard let viewGraph, + let bridgedPreference = bridgedPreferences.first(where: { $0.key == key }), + let combiner = bridgedPreference.combiner.attribute + else { return } + var visitor = RemoveValue(combiner: combiner, value: src) + key.visitKey(&visitor) if visitor.changed { - viewGraph?.graphInvalidation(from: isInvalidating ? nil : value) + viewGraph.graphInvalidation(from: isInvalidating ? nil : src) } } - - func addHostValue(_ values: Attribute, for keys: Attribute) { - guard let combiner = $hostPreferencesCombiner else { - return - } + #endif + + package func updateHostValues(_ keys: Attribute) { + #if canImport(Darwin) + guard let viewGraph else { return } + viewGraph.graphInvalidation(from: keys.identifier) + #endif + } + + package func addHostValues(_ values: WeakAttribute, for keys: Attribute) { + #if canImport(Darwin) + guard let viewGraph, + let combiner = $hostPreferencesCombiner + else { return } combiner.mutateBody( as: HostPreferencesCombiner.self, invalidating: true ) { combiner in combiner.addChild(keys: keys, values: values) } - viewGraph?.graphInvalidation(from: combiner.identifier) + viewGraph.graphInvalidation(from: keys.identifier) + #endif } - - func removeHostValue(for keys: Attribute, isInvalidating: Bool) { - guard let combiner = $hostPreferencesCombiner else { + + package func addHostValues(_ values: OptionalAttribute, for keys: Attribute) { + #if canImport(Darwin) + guard let attribute = values.attribute else { return } + addHostValues(WeakAttribute(attribute), for: keys) + #endif + } + + package func removeHostValues(for keys: Attribute, isInvalidating: Bool = false) { + #if canImport(Darwin) + guard let viewGraph, + let combiner = $hostPreferencesCombiner + else { return } var hasRemoved = false combiner.mutateBody( as: HostPreferencesCombiner.self, @@ -120,91 +217,26 @@ package final class PreferenceBridge { hasRemoved = true } if hasRemoved { - viewGraph?.graphInvalidation(from: isInvalidating ? nil : keys.identifier) - } - } - - /// Append `child` to `children` property if `child` has not been on `children`. - /// - Parameter child: The ``ViewGraph`` instance to be added - func addChild(_ child: ViewGraph) { - guard !children.contains(where: { $0.takeUnretainedValue() === child }) else { - return - } - children.append(.passUnretained(child)) - } - - /// Remove `child` from `children` property if `child` has been on `children` - /// - Parameter child: The ``ViewGraph`` instance to be removed - func removeChild(_ child: ViewGraph) { - guard let index = children.firstIndex(where: { $0.takeUnretainedValue() === child }) else { - return - } - children.remove(at: index) - } - - func removedStateDidChange() { - for child in children { - let viewGraph = child.takeUnretainedValue() - viewGraph.updateRemovedState() + viewGraph.graphInvalidation(from: isInvalidating ? nil : keys.identifier) } + #endif } - - func wrapInputs(_ inputs: inout _ViewInputs) { - inputs.withMutableCustomInputs { $0 = bridgedViewInputs } - inputs.preferences.merge(requestedPreferences) - inputs.preferences.hostKeys = Attribute(MergePreferenceKeys(lhs: inputs.preferences.hostKeys, rhs: _hostPreferenceKeys)) - } - - func wrapOutputs(_ outputs: inout PreferencesOutputs, inputs: _ViewInputs) { - struct MakeCombiner: PreferenceKeyVisitor { - var result: AnyAttribute? - - mutating func visit(key _: Key.Type) where Key: PreferenceKey { - result = Attribute(PreferenceCombiner(attributes: [])).identifier - } - } - inputs.withCustomInputs { bridgedViewInputs = $0 } - for key in inputs.preferences.keys { - if key == _AnyPreferenceKey.self { - let combiner = Attribute(HostPreferencesCombiner( - keys: inputs.preferences.hostKeys, - values: OptionalAttribute(base: AnyOptionalAttribute(outputs[anyKey: key])), - children: [] - )) - outputs.hostPreferences = combiner - $hostPreferenceKeys = inputs.preferences.hostKeys - $hostPreferencesCombiner = combiner - } else { - guard !outputs.contains(key) else { - continue - } - var visitor = MakeCombiner() - key.visitKey(&visitor) - guard let combiner = visitor.result else { - continue - } - if !requestedPreferences.contains(key) { - requestedPreferences.add(key) - } - bridgedPreferences.append(BridgedPreference(key: key, combiner: AnyWeakAttribute(combiner))) - outputs[anyKey: key] = combiner - } - } - } - #endif } +// MARK: - MergePreferenceKeys + private struct MergePreferenceKeys: Rule, AsyncAttribute { @Attribute var lhs: PreferenceKeys @WeakAttribute var rhs: PreferenceKeys? var value: PreferenceKeys { -// var result = lhs -// guard let rhs else { -// return result -// } -// result.merge(rhs) -// return result - fatalError("TODO") + var result = lhs + guard let rhs else { + return lhs + } + for key in rhs.keys { + result.add(key) + } + return result } } diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferenceCombiner.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferenceCombiner.swift new file mode 100644 index 000000000..b18d7f5bf --- /dev/null +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferenceCombiner.swift @@ -0,0 +1,212 @@ +// +// PreferenceCombiner.swift +// OpenSwiftUICore +// +// Audited for iOS 18.0 +// Status: Complete +// ID: 59D15989E597719355BF0EAE6CB41FF9 (SwiftUI) +// ID: EAF68AEE5E08E2f44FEB886FE6A27001 (SwiftUICore) + +package import OpenGraphShims + +// MARK: - PreferenceCombiner + +package struct PreferenceCombiner: Rule, AsyncAttribute, CustomStringConvertible where K: PreferenceKey { + package var attributes: [WeakAttribute] + + package init() { + attributes = [] + } + + package init(attributes: [Attribute]) { + self.attributes = attributes.map { WeakAttribute($0) } + } + + package static var initialValue: K.Value? { + K.defaultValue + } + + package var value: K.Value { + var value = K.defaultValue + var initialValue = true + for attribute in attributes { + if initialValue { + value = attribute.value ?? K.defaultValue + } else { + K.reduce(value: &value) { + attribute.value ?? K.defaultValue + } + } + initialValue = false + } + return value + } + + package var description: String { + "∪ \(K.readableName)" + } +} + +// MARK: - PairwisePreferenceCombinerVisitor + +package struct PairwisePreferenceCombinerVisitor: PreferenceKeyVisitor { + package let outputs: (_ViewOutputs, _ViewOutputs) + package var result: _ViewOutputs = _ViewOutputs() + + package init(outputs: (_ViewOutputs, _ViewOutputs)) { + self.outputs = outputs + } + + package mutating func visit(key: K.Type) where K: PreferenceKey { + let values = (outputs.0[key], outputs.1[key]) + if let first = values.0, let second = values.1 { + result[key] = Attribute(PairPreferenceCombiner(attributes: (first, second))) + } else if let value = values.0 { + result[key] = value + } else if let value = values.1 { + result[key] = value + } + } +} + +// MARK: - MultiPreferenceCombinerVisitor + +package struct MultiPreferenceCombinerVisitor: PreferenceKeyVisitor { + package let outputs: [PreferencesOutputs] + package var result: PreferencesOutputs + + init(outputs: [PreferencesOutputs], result: PreferencesOutputs) { + self.outputs = outputs + self.result = result + } + + package mutating func visit(key: K.Type) where K: PreferenceKey { + let values = outputs.compactMap { $0[K.self] } + switch values.count { + case 0: break + case 1: result[key] = values[0] + case 2: result[key] = Attribute(PairPreferenceCombiner(attributes: (values[0], values[1]))) + default: result[key] = Attribute(PreferenceCombiner(attributes: values)) + } + } +} + +// MARK: - PairPreferenceCombiner + +private struct PairPreferenceCombiner: Rule, AsyncAttribute where K: PreferenceKey{ + private var attributes: (Attribute, Attribute) + + init(attributes: (Attribute, Attribute)) { + self.attributes = attributes + } + + var value: K.Value { + var value = attributes.0.value + K.reduce(value: &value) { attributes.1.value } + return value + } +} + +// MARK: - PreferencesAggregator + +package struct PreferencesAggregator: Rule, AsyncAttribute where K: PreferenceKey { + package var attributes: [WeakAttribute] + + package init(attributes: [Attribute]) { + self.attributes = attributes.map { WeakAttribute($0) } + } + + package var value: [K.Value] { + attributes.map { $0.value ?? K.defaultValue } + } +} + +// MARK: - HostPreferencesCombiner + +package struct HostPreferencesCombiner: Rule, AsyncAttribute { + @Attribute var keys: PreferenceKeys + @OptionalAttribute var values: PreferenceList? + var children: [Child] + + package init(keys: Attribute, values: Attribute?) { + _keys = keys + _values = OptionalAttribute(values) + children = [] + } + + struct Child { + @WeakAttribute var keys: PreferenceKeys? + @WeakAttribute var values: PreferenceList? + } + + package mutating func addChild(keys: Attribute, values: WeakAttribute) { + let child = Child(keys: WeakAttribute(keys), values: values) + if let index = children.firstIndex(where: { $0.$keys == keys }) { + children[index] = child + } else { + children.append(child) + } + } + + package mutating func removeChild(keys: Attribute) -> Bool { + guard let index = children.firstIndex(where: { $0.$keys == keys }) else { + return false + } + children.remove(at: index) + return true + } + + private struct CombineValues: PreferenceKeyVisitor { + var children: [Child] + var values: PreferenceList + + mutating func visit(key: Key.Type) { + guard !values.contains(key) else { + return + } + var value = Key.defaultValue + var seed = VersionSeed.empty + guard !children.isEmpty else { + return + } + var initialValue = true + for child in children { + guard let keys = child.$keys, + !keys.value.contains(key) else { + continue + } + guard let values = child.$values, + let listValue = values.value.valueIfPresent(for: key) else { + continue + } + if initialValue { + value = listValue.value + seed = listValue.seed + } else { + Key.reduce(value: &value) { + seed.merge(listValue.seed) + return listValue.value + } + } + initialValue = false + } + if !initialValue { + values[key] = PreferenceList.Value(value: value, seed: seed) + } + } + } + + package var value: PreferenceList { + let values = values ?? PreferenceList() + guard !children.isEmpty else { + return values + } + var visitor = CombineValues(children: children, values: values) + let keys = keys + guard !keys.isEmpty else { + return values + } + keys.forEach { $0.visitKey(&visitor) } + return visitor.values + } +} diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferenceKey.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferenceKey.swift index 12916856a..8421ffafd 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferenceKey.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferenceKey.swift @@ -4,7 +4,6 @@ // // Audited for iOS 18.0 // Status: Complete -// ID: 3D8838A231BB2CC7FC00E7880D8B2FC4 // MARK: - PreferenceKey diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferenceList.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferenceList.swift index 79b892f1a..c91432c74 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferenceList.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferenceList.swift @@ -1,11 +1,13 @@ // // PreferenceList.swift -// OpenSwiftUI +// OpenSwiftUICore // -// Audited for iOS 15.5 -// Status: WIP -// ID: C1C63C2F6F2B9F3EB30DD747F0605FBD (RELEASE_2021) -// ID: 7B694C05291EA7AF22785AB458D1BC2F (RELEASE_2024) +// Audited for iOS 18.0 +// Status: Copmlete +// ID: C1C63C2F6F2B9F3EB30DD747F0605FBD (SwiftUI) +// ID: 7B694C05291EA7AF22785AB458D1BC2F (SwiftUICore) + +// MARK: - PreferenceList package struct PreferenceList: CustomStringConvertible { private var first: PreferenceNode? @@ -23,40 +25,33 @@ package struct PreferenceList: CustomStringConvertible { } } - // TODO: TO BE AUDITED package subscript(key: K.Type) -> Value where K: PreferenceKey{ get { guard let first, - let node = first.find(key: key) else { + let node = first.find(key) else { return Value(value: key.defaultValue, seed: .empty) } return Value(value: node.value, seed: node.seed) } set { if let first, - let _ = first.find(key: key) { + let _ = first.find(key) { removeValue(for: key) } first = _PreferenceNode(value: newValue.value, seed: newValue.seed, next: first) } } - // TODO: TO BE AUDITED - package func valueIfPresent(for _: K.Type) -> Value? where K: PreferenceKey { - guard let first else { - return nil - } - return first.find(key: K.self).map { node in + package func valueIfPresent(for key: K.Type = K.self) -> Value? where K: PreferenceKey { + first?.find(key).map { node in Value(value: node.value, seed: node.seed) } } - // TODO: TO BE AUDITED package func contains(_ key: K.Type) -> Bool where K: PreferenceKey { - first?.find(key: key) != nil + first?.find(key) != nil } - // TODO: TO BE AUDITED package mutating func removeValue(for key: K.Type) where K: PreferenceKey { let first = first self.first = nil @@ -68,8 +63,7 @@ package struct PreferenceList: CustomStringConvertible { } } - // TODO: TO BE AUDITED - package mutating func modifyValue(for key: K.Type, transform: Value <(inout K.Value) -> Void>) where K: PreferenceKey { + package mutating func modifyValue(for key: K.Type, transform: Value<(inout K.Value) -> Void>) where K: PreferenceKey { var value = self[key] value.seed.merge(transform.seed) transform.value(&value.value) @@ -77,15 +71,15 @@ package struct PreferenceList: CustomStringConvertible { first = _PreferenceNode(value: value.value, seed: value.seed, next: first) } - // TODO: TO BE AUDITED package func mayNotBeEqual(to other: PreferenceList) -> Bool { - // TODO - return false + guard first !== other.first else { + return false + } + return !seed.matches(other.seed) } package var seed: VersionSeed { first?.mergedSeed ?? .empty } - // TODO: TO BE AUDITED package mutating func combine(with other: PreferenceList) { guard let otherFirst = other.first else { return @@ -141,7 +135,8 @@ package struct PreferenceList: CustomStringConvertible { } } -// TODO: TO BE AUDITED +// MARK: - PreferenceNode + private class PreferenceNode: CustomStringConvertible { let keyType: Any.Type let seed: VersionSeed @@ -161,30 +156,25 @@ private class PreferenceNode: CustomStringConvertible { self.next = next } + @inlinable final func forEach(_ body: (PreferenceNode) -> Void) { - var node = self - repeat { + var currentNode: PreferenceNode? = self + while let node = currentNode { body(node) - guard let next = node.next else { - break - } - node = next - } while true + currentNode = node.next + } } - final func find(key: Key.Type) -> _PreferenceNode? { - var node = self - repeat { - if node.keyType == key { - return node as? _PreferenceNode - } else { - if let next = node.next { - node = next - } else { - break - } + @inlinable + final func find(_ key: K.Type = K.self) -> _PreferenceNode? where K: PreferenceKey { + var currentNode: PreferenceNode? = self + while let node = currentNode { + guard node.keyType == key else { + currentNode = node.next + continue } - } while true + return (node as! _PreferenceNode) + } return nil } @@ -195,33 +185,34 @@ private class PreferenceNode: CustomStringConvertible { var description: String { fatalError() } } -// TODO: TO BE AUDITED -private class _PreferenceNode: PreferenceNode { - let value: Key.Value +// MARK: - PreferenceNode + +private class _PreferenceNode: PreferenceNode where K: PreferenceKey { + let value: K.Value - init(value: Key.Value, seed: VersionSeed, next: PreferenceNode?) { + init(value: K.Value, seed: VersionSeed, next: PreferenceNode?) { self.value = value - super.init(keyType: Key.self, seed: seed, next: next) + super.init(keyType: K.self, seed: seed, next: next) } override func find(from: PreferenceNode?) -> PreferenceNode? { - from?.find(key: Key.self) + from?.find(K.self) } override func combine(from: PreferenceNode?, next: PreferenceNode?) -> PreferenceNode? { var currentNode = from while let node = currentNode { - if keyType == node.keyType { - var value = self.value - var seed = self.seed - Key.reduce(value: &value) { - seed.merge(node.seed) - return (node as! _PreferenceNode).value - } - return _PreferenceNode(value: value, seed: seed, next: next) - } else { + guard keyType == node.keyType else { currentNode = node.next + continue + } + var value = self.value + var seed = self.seed + K.reduce(value: &value) { + seed.merge(node.seed) + return (node as! _PreferenceNode).value } + return _PreferenceNode(value: value, seed: seed, next: next) } return nil } @@ -230,9 +221,9 @@ private class _PreferenceNode: PreferenceNode { _PreferenceNode(value: value, seed: seed, next: next) } - override class var _includesRemovedValues: Bool { Key._includesRemovedValues } + override class var _includesRemovedValues: Bool { K._includesRemovedValues } override var description: String { - "\(Key.self) = \(value)" + "\(K.self) = \(value)" } } diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferencesCombiner.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferencesCombiner.swift deleted file mode 100644 index c65eabb22..000000000 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferencesCombiner.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// PreferencesCombiner.swift -// OpenSwiftUI -// -// Audited for iOS 15.5 -// Status: Complete -// ID: 59D15989E597719355BF0EAE6CB41FF9 - -import OpenGraphShims - -struct PreferenceCombiner: Rule, AsyncAttribute { - var attributes: [WeakAttribute] - - init(attributes: [Attribute]) { - self.attributes = attributes.map { WeakAttribute($0) } - } - - var value: Key.Value { - var value = Key.defaultValue - var initialValue = true - for attribute in attributes { - if initialValue { - value = attribute.value ?? Key.defaultValue - } else { - Key.reduce(value: &value) { - attribute.value ?? Key.defaultValue - } - } - initialValue = false - } - return value - } -} - -struct HostPreferencesCombiner: Rule, AsyncAttribute { - @Attribute var keys: PreferenceKeys - @OptionalAttribute var values: PreferenceList? - var children: [Child] - - struct Child { - @WeakAttribute var keys: PreferenceKeys? - @WeakAttribute var values: PreferenceList? - - init(keys: Attribute, values: Attribute) { - _keys = WeakAttribute(keys) - _values = WeakAttribute(values) - } - } - - #if canImport(Darwin) // FIXME: See #39 - mutating func addChild(keys: Attribute, values: Attribute) { - let child = Child(keys: keys, values: values) - if let index = children.firstIndex(where: { $0.$keys == keys }) { - children[index] = child - } else { - children.append(child) - } - } - #endif - - private struct CombineValues: PreferenceKeyVisitor { - var children: [Child] - var values: PreferenceList - - mutating func visit(key: Key.Type) { - guard !values.contains(key) else { - return - } - var value = Key.defaultValue - var seed = VersionSeed.empty - guard !children.isEmpty else { - return - } - var initialValue = true - for child in children { - guard let keys = child.$keys, - !keys.value.contains(key) else { - continue - } - guard let values = child.$values, - let listValue = values.value.valueIfPresent(for: key) else { - continue - } - if initialValue { - value = listValue.value - seed = listValue.seed - } else { - Key.reduce(value: &value) { - seed.merge(listValue.seed) - return listValue.value - } - } - initialValue = false - } - if !initialValue { - values[key] = PreferenceList.Value(value: value, seed: seed) - } - } - } - - var value: PreferenceList { - let values = values ?? PreferenceList() - guard !children.isEmpty else { - return values - } - var visitor = CombineValues(children: children, values: values) - let keys = keys - for key in keys { - key.visitKey(&visitor) - } - return visitor.values - } -} - -private struct PairPreferenceCombiner: Rule, AsyncAttribute { - private var attributes: (Attribute, Attribute) - - init(attributes: (Attribute, Attribute)) { - self.attributes = attributes - } - - var value: Key.Value { - var value = attributes.0.value - Key.reduce(value: &value) { attributes.1.value } - return value - } -} - -struct PairwisePreferenceCombinerVisitor: PreferenceKeyVisitor { - let outputs: (_ViewOutputs, _ViewOutputs) - var result: _ViewOutputs - - mutating func visit(key _: Key.Type) { - let values = (outputs.0[Key.self], outputs.1[Key.self]) - - if let value1 = values.0, let value2 = values.1 { - result[Key.self] = Attribute(PairPreferenceCombiner(attributes: (value1, value2))) - } else if let value = values.0 { - result[Key.self] = value - } else if let value = values.1 { - result[Key.self] = value - } - } -} - -struct MultiPreferenceCombinerVisitor: PreferenceKeyVisitor { - let outputs: [PreferencesOutputs] - var result: PreferencesOutputs - - mutating func visit(key _: Key.Type) { - let values = outputs.compactMap { $0[Key.self] } - switch values.count { - case 0: break - case 1: result[Key.self] = values[0] - case 2: result[Key.self] = Attribute(PairPreferenceCombiner(attributes: (values[0], values[1]))) - default: result[Key.self] = Attribute(PreferenceCombiner(attributes: values)) - } - } -} diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferencesInputs.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferencesInputs.swift index 61d7c00a8..adbc86ed5 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferencesInputs.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferencesInputs.swift @@ -1,36 +1,67 @@ // // PreferencesInputs.swift -// OpenSwiftUI +// OpenSwiftUICore // -// Audited for iOS 15.5 +// Audited for iOS 18.0 // Status: Complete -import OpenGraphShims +package import OpenGraphShims -struct PreferencesInputs { - private(set) var keys: PreferenceKeys - var hostKeys: Attribute +package struct PreferencesInputs { + package var keys: PreferenceKeys + package var hostKeys: Attribute - @inline(__always) - init(hostKeys: Attribute) { + @inlinable + package init(hostKeys: Attribute) { self.keys = PreferenceKeys() self.hostKeys = hostKeys } + + @inlinable + package mutating func remove(_ key: K.Type) where K: PreferenceKey { + keys.remove(key) + } - mutating func add(_ key: Key.Type) { + @inlinable + package mutating func add(_ key: K.Type) where K: PreferenceKey { keys.add(key) } - mutating func remove(_ key: Key.Type) { - keys.remove(key) + @inlinable + package func contains(_ key: K.Type) -> Bool where K: PreferenceKey { + keys.contains(key) } - func contains(_ key: Key.Type) -> Bool { - keys.contains(key) + @inlinable + package func contains(_ key: K.Type, includeHostPreferences: Bool) -> Bool where K: PreferenceKey { + let result = keys.contains(key) + guard !result, includeHostPreferences else { + return result + } + guard K._isReadableByHost else { + return false + } + return keys.contains(_AnyPreferenceKey.self) } - @inline(__always) - mutating func merge(_ preferenceKeys: PreferenceKeys) { - // keys.merge(preferenceKeys) + package func makeIndirectOutputs() -> PreferencesOutputs { + struct AddPreference: PreferenceKeyVisitor { + var outputs: PreferencesOutputs + + mutating func visit(key: K.Type) where K: PreferenceKey { + let source = ViewGraph.current.intern(K.defaultValue, for: K.self, id: .preferenceKeyDefault) + + @IndirectAttribute(source: source) + var indirect: K.Value + + outputs.appendPreference(key: K.self, value: $indirect) + } + } + + var visitor = AddPreference(outputs: PreferencesOutputs()) + for key in keys { + key.visitKey(&visitor) + } + return visitor.outputs } } diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferencesOutputs.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferencesOutputs.swift index 4ba931b95..0b1f316ad 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferencesOutputs.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferencesOutputs.swift @@ -1,113 +1,117 @@ // // PreferencesOutputs.swift -// OpenSwiftUI +// OpenSwiftUICore // -// Audited for iOS 15.5 +// Audited for iOS 18.0 // Status: Complete -// ID: A948213B3F0A65E8491149A582CA5C71 -import OpenGraphShims +package import OpenGraphShims -struct PreferencesOutputs { - private var preferences: [KeyValue] = [] - private var debugProperties: _ViewDebug.Properties = [] - - func contains(_: Key.Type) -> Bool { - contains(_AnyPreferenceKey.self) - } - - func contains(_ key: AnyPreferenceKey.Type) -> Bool { - preferences.contains { $0.key == key } - } - - #if canImport(Darwin) // FIXME: See #39 - subscript(_: Key.Type) -> Attribute? { - get { - let value = self[anyKey: _AnyPreferenceKey.self] - return value.map { Attribute(identifier: $0) } - } - set { - self[anyKey: _AnyPreferenceKey.self] = newValue?.identifier - } +package struct PreferencesOutputs { + private var preferences: [KeyValue] + package var debugProperties: _ViewDebug.Properties + + @inlinable + package init() { + preferences = [] + debugProperties = [] } - - subscript(anyKey keyType: AnyPreferenceKey.Type) -> AnyAttribute? { - get { preferences.first { $0.key == keyType }?.value } + + #if canImport(Darwin) + subscript(anyKey key: AnyPreferenceKey.Type) -> AnyAttribute? { + get { preferences.first { $0.key == key }?.value } set { - if keyType == _AnyPreferenceKey.self { + if key == _AnyPreferenceKey.self { if !debugProperties.contains(.displayList) { debugProperties.formUnion(.displayList) } } - if let index = preferences.firstIndex(where: { $0.key == keyType }) { + if let index = preferences.firstIndex(where: { $0.key == key }) { if let newValue { preferences[index].value = newValue } else { preferences.remove(at: index) } } else { - if let newValue { - preferences.append(KeyValue(key: keyType, value: newValue)) + newValue.map { + preferences.append(KeyValue(key: key, value: $0)) } } } } - - @inline(__always) - func forEach(body: ( - _ key: AnyPreferenceKey.Type, - _ value: AnyAttribute - ) throws -> Void - ) rethrows { - try preferences.forEach { try body($0.key, $0.value) } + #endif + + subscript(key: K.Type) -> Attribute? where K: PreferenceKey { + get { + #if canImport(Darwin) + let value = self[anyKey: _AnyPreferenceKey.self] + return value.map { Attribute(identifier: $0) } + #else + fatalError("See #39") + #endif + } + set { + #if canImport(Darwin) + self[anyKey: _AnyPreferenceKey.self] = newValue.map { $0.identifier } + #else + fatalError("See #39") + #endif + } } - @inline(__always) - func first(where predicate: ( - _ key: AnyPreferenceKey.Type, - _ value: AnyAttribute - ) throws -> Bool - ) rethrows -> (key: AnyPreferenceKey.Type, value: AnyAttribute)? { - try preferences - .first { try predicate($0.key, $0.value) } - .map { ($0.key, $0.value) } + package mutating func appendPreference(key: K.Type, value: Attribute) where K: PreferenceKey{ + #if canImport(Darwin) + preferences.append(KeyValue(key: _AnyPreferenceKey.self, value: value.identifier)) + #else + fatalError("See #39") + #endif + } + + #if canImport(Darwin) + package func forEachPreference(_ body: (any AnyPreferenceKey.Type, AnyAttribute) -> Void) { + preferences.forEach { body($0.key, $0.value) } } + #endif - #else - subscript(_: Key.Type) -> Attribute? { - get { fatalError("See #39") } - set { fatalError("See #39") } + #if canImport(Darwin) + package func setIndirectDependencies(_ dependency: AnyAttribute?) { + preferences.forEach { + $0.value.indirectDependency = dependency + } } #endif - mutating func appendPreference(key: Key.Type, value: Attribute) { + package func attachIndirectOutputs(to childOutputs: PreferencesOutputs) { + #if canImport(Darwin) + for preference in preferences { + for childPreference in childOutputs.preferences where childPreference.key == preference.key { + preference.value.source = childPreference.value + } + } + #endif + } + + package func detachIndirectOutputs() { #if canImport(Darwin) - preferences.append(KeyValue(key: _AnyPreferenceKey.self, value: value.identifier)) + struct ResetPreference: PreferenceKeyVisitor { + var destination: AnyAttribute + func visit(key: K.Type) where K: PreferenceKey { + destination.source = .nil + } + } + for keyValue in preferences { + var visitor = ResetPreference(destination: keyValue.value) + keyValue.key.visitKey(&visitor) + } #endif } } extension PreferencesOutputs { private struct KeyValue { - var key: AnyPreferenceKey.Type - #if canImport(Darwin) // FIXME: See #39 + var key: any AnyPreferenceKey.Type + #if canImport(Darwin) var value: AnyAttribute #endif } } - -extension _ViewOutputs { - @inline(__always) - var hostPreferences: Attribute? { - get { self[HostPreferencesKey.self] } - set { self[HostPreferencesKey.self] = newValue } - } -} - -extension PreferencesOutputs { - @inline(__always) - var hostPreferences: Attribute? { - get { self[HostPreferencesKey.self] } - set { self[HostPreferencesKey.self] = newValue } - } -} diff --git a/Sources/OpenSwiftUICore/Data/Preference/View_Indirect.swift b/Sources/OpenSwiftUICore/Data/Preference/View_Indirect.swift new file mode 100644 index 000000000..c60245a7b --- /dev/null +++ b/Sources/OpenSwiftUICore/Data/Preference/View_Indirect.swift @@ -0,0 +1,61 @@ +// +// View_Indirect.swift +// OpenSwiftUICore +// +// Audited for iOS 18.0 +// Status: Complete + +#if canImport(Darwin) +package import OpenGraphShims +#else +import OpenGraphShims +#endif + +extension _ViewInputs { + func makeIndirectOutputs() -> _ViewOutputs { + let indirectPreferenceOutputs = preferences.makeIndirectOutputs() + var outputs = _ViewOutputs() + outputs.preferences = indirectPreferenceOutputs + guard requestsLayoutComputer else { + return outputs + } + #if canImport(Darwin) + let defaultLayoutComputer = CoreGlue.shared.makeDefaultLayoutComputer().value + @IndirectAttribute(source: defaultLayoutComputer) + var indirect: LayoutComputer + outputs.layoutComputer = $indirect + #endif + return outputs + } +} + +extension _ViewOutputs { + #if canImport(Darwin) + package func setIndirectDependencies(_ dependency: AnyAttribute?) { + preferences.setIndirectDependencies(dependency) + if let target = layoutComputer?.identifier { + target.indirectDependency = dependency + } + } + #endif + + + package func attachIndirectOutputs(to childOutputs: _ViewOutputs) { + #if canImport(Darwin) + preferences.attachIndirectOutputs(to: childOutputs.preferences) + if let target = layoutComputer?.identifier, + let source = childOutputs.layoutComputer?.identifier { + target.source = source + } + #endif + } + + package func detachIndirectOutputs() { + #if canImport(Darwin) + preferences.detachIndirectOutputs() + if let target = layoutComputer?.identifier { + target.source = .nil + } + #endif + } +} diff --git a/Sources/OpenSwiftUICore/Graph/GraphHost.swift b/Sources/OpenSwiftUICore/Graph/GraphHost.swift index 2f65d0f96..db0f8ae26 100644 --- a/Sources/OpenSwiftUICore/Graph/GraphHost.swift +++ b/Sources/OpenSwiftUICore/Graph/GraphHost.swift @@ -219,6 +219,7 @@ open class GraphHost: CustomReflectable { case defaultValue3D case failedValue case placeholder + case preferenceKeyDefault } package final func intern(_ value: T, for type: Any.Type = T.self, id: ConstantID) -> Attribute { diff --git a/Sources/OpenSwiftUICore/Graph/GraphInputs.swift b/Sources/OpenSwiftUICore/Graph/GraphInputs.swift index 95cdf637e..4b3bf686a 100644 --- a/Sources/OpenSwiftUICore/Graph/GraphInputs.swift +++ b/Sources/OpenSwiftUICore/Graph/GraphInputs.swift @@ -4,7 +4,6 @@ // // Audited for iOS 18.0 // Status: WIP -// ID: 9FF97745734808976F608CE0DC13C39C package import OpenGraphShims @@ -137,7 +136,9 @@ public struct _GraphInputs { get { cachedEnvironment.wrappedValue.environment } set { cachedEnvironment.wrappedValue = CachedEnvironment(newValue) - changedDebugProperties.insert(.environment) + if !changedDebugProperties.contains(.environment) { + changedDebugProperties.formUnion(.environment) + } } } @@ -307,23 +308,4 @@ extension _GraphInputs { // return newInputs fatalError("TO BE REMOVED") } - - // MARK: - changedDebugProperties - - @inline(__always) - package func withEmptyChangedDebugPropertiesInputs(_ body: (_GraphInputs) -> R) -> R { -// var inputs = self -// inputs.changedDebugProperties = [] -// return body(inputs) - fatalError("TO BE REMOVED") - } - - // MARK: - options - - @inline(__always) - package var enableLayout: Bool { - false -// get { options.contains(.enableLayout) } - // TODO: setter - } } diff --git a/Sources/OpenSwiftUICore/Graph/GraphReusable.swift b/Sources/OpenSwiftUICore/Graph/GraphReuse.swift similarity index 96% rename from Sources/OpenSwiftUICore/Graph/GraphReusable.swift rename to Sources/OpenSwiftUICore/Graph/GraphReuse.swift index 50921e911..d9c9ec0e1 100644 --- a/Sources/OpenSwiftUICore/Graph/GraphReusable.swift +++ b/Sources/OpenSwiftUICore/Graph/GraphReuse.swift @@ -1,10 +1,10 @@ // -// GraphReusable.swift +// GraphReuse.swift // OpenSwiftUICore // // Audited for iOS 18.0 // Status: Blocked by _GraphInputs -// ID: 3E2D3733C4CBF57EC1EA761D02CE8317 +// ID: 3E2D3733C4CBF57EC1EA761D02CE8317 (SwiftUICore) package import OpenGraphShims @@ -96,7 +96,7 @@ import Foundation import os.log #endif -private enum EnableGraphReuseLogging: UserDefaultKeyedFeature { +private struct EnableGraphReuseLogging: UserDefaultKeyedFeature { static var key: String { "org.OpenSwiftUIProject.OpenSwiftUI.GraphReuseLogging" } static var cachedValue: Bool? } diff --git a/Sources/OpenSwiftUICore/Graphic/Color/ColorResolved.swift b/Sources/OpenSwiftUICore/Graphic/Color/ColorResolved.swift index f22a43b64..a5bb2652f 100644 --- a/Sources/OpenSwiftUICore/Graphic/Color/ColorResolved.swift +++ b/Sources/OpenSwiftUICore/Graphic/Color/ColorResolved.swift @@ -5,11 +5,7 @@ // Audited for iOS 18.0 // Status: WIP -#if canImport(Darwin) -package import Foundation -#else import Foundation -#endif import OpenSwiftUI_SPI // MARK: - Color.Resolved @@ -315,32 +311,3 @@ func sRGBToLinear(_ sRGB: Float) -> Float { } return sRGB > 0 ? result : -result } - -#if canImport(Darwin) - -// MARK: - Color.Resolved + platformColor - -extension Color.Resolved { - package init?(platformColor: AnyObject) { - var red: CGFloat = 0 - var green: CGFloat = 0 - var blue: CGFloat = 0 - var alpha: CGFloat = 0 - let result = CoreColorPlatformColorGetComponents(system: isAppKitBased() ? .appKit : .uiKit, color: platformColor, red: &red, green: &green, blue: &blue, alpha: &alpha) - if result { - self.init(red: Float(red), green: Float(green), blue: Float(blue), opacity: Float(alpha)) - } else { - return nil - } - } - - // ID: 4330A474F53D66045762501ED6F8A749 - private static let cache: ObjectCache = ObjectCache { resolved in - CoreColor.platformColor(resolvedColor: resolved)! - } - - package var kitColor: NSObject { - Self.cache[self] - } -} -#endif diff --git a/Sources/OpenSwiftUICore/Graphic/Color/CoreColor+Extension.swift b/Sources/OpenSwiftUICore/Graphic/Color/CoreColor+Extension.swift deleted file mode 100644 index d14d3cac5..000000000 --- a/Sources/OpenSwiftUICore/Graphic/Color/CoreColor+Extension.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// CoreColor+Extension.swift -// OpenSwiftUICore -// -// Audited for iOS 18.0 -// Status: Complete - -#if canImport(Darwin) -package import Foundation -import OpenSwiftUI_SPI - -extension CoreColor { - package static func platformColor(resolvedColor: Color.Resolved) -> NSObject? { - platformColor(red: CGFloat(resolvedColor.red), green: CGFloat(resolvedColor.green), blue: CGFloat(resolvedColor.blue), alpha: CGFloat(resolvedColor.opacity)) - } - - package static func platformColor(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> NSObject? { - CorePlatformColorForRGBA(system: isAppKitBased() ? .appKit : .uiKit, red: red, green: green, blue: blue, alpha: alpha) - } -} - -#endif diff --git a/Sources/OpenSwiftUICore/Graphic/Color/CoreColor.swift b/Sources/OpenSwiftUICore/Graphic/Color/CoreColor.swift new file mode 100644 index 000000000..8241207ca --- /dev/null +++ b/Sources/OpenSwiftUICore/Graphic/Color/CoreColor.swift @@ -0,0 +1,47 @@ +// +// CoreColor.swift +// OpenSwiftUICore +// +// Audited for iOS 18.0 +// Status: Complete + +#if canImport(Darwin) +package import Foundation +import OpenSwiftUI_SPI + +// MARK: - Color.Resolved + platformColor + +extension Color.Resolved { + package init?(platformColor: AnyObject) { + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + let result = CoreColorPlatformColorGetComponents(system: isAppKitBased() ? .appKit : .uiKit, color: platformColor, red: &red, green: &green, blue: &blue, alpha: &alpha) + if result { + self.init(red: Float(red), green: Float(green), blue: Float(blue), opacity: Float(alpha)) + } else { + return nil + } + } + + private static let cache: ObjectCache = ObjectCache { resolved in + CoreColor.platformColor(resolvedColor: resolved)! + } + + package var kitColor: NSObject { + Self.cache[self] + } +} + +extension CoreColor { + package static func platformColor(resolvedColor: Color.Resolved) -> NSObject? { + platformColor(red: CGFloat(resolvedColor.red), green: CGFloat(resolvedColor.green), blue: CGFloat(resolvedColor.blue), alpha: CGFloat(resolvedColor.opacity)) + } + + package static func platformColor(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> NSObject? { + CorePlatformColorForRGBA(system: isAppKitBased() ? .appKit : .uiKit, red: red, green: green, blue: blue, alpha: alpha) + } +} + +#endif diff --git a/Sources/OpenSwiftUICore/Layout/LayoutAdjustments/Edge/SafeAreaInsets.swift b/Sources/OpenSwiftUICore/Layout/LayoutAdjustments/Edge/SafeAreaInsets.swift index 315ef24a6..17b754ecf 100644 --- a/Sources/OpenSwiftUICore/Layout/LayoutAdjustments/Edge/SafeAreaInsets.swift +++ b/Sources/OpenSwiftUICore/Layout/LayoutAdjustments/Edge/SafeAreaInsets.swift @@ -1,5 +1,5 @@ // TODO -struct SafeAreaInsets { +package struct SafeAreaInsets { var space: UniqueID var elements: [Element] var next: OptionalValue diff --git a/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/LayoutComputer.swift b/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/LayoutComputer.swift index d61e8e0cd..607e3c153 100644 --- a/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/LayoutComputer.swift +++ b/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/LayoutComputer.swift @@ -8,8 +8,8 @@ import Foundation // TODO: -struct LayoutComputer: Equatable { - static func == (lhs: LayoutComputer, rhs: LayoutComputer) -> Bool { +package struct LayoutComputer: Equatable { + package static func == (lhs: LayoutComputer, rhs: LayoutComputer) -> Bool { lhs.seed == rhs.seed /*&& lhs.delegate == rhs.delegate*/ } diff --git a/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewOrigin.swift b/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewOrigin.swift index ea1fe8f98..d8737a207 100644 --- a/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewOrigin.swift +++ b/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewOrigin.swift @@ -5,9 +5,9 @@ // Audited for iOS 15.5 // Status: Complete -import Foundation +package import Foundation -struct ViewOrigin: Equatable { +package struct ViewOrigin: Equatable { var value: CGPoint @inline(__always) @@ -15,7 +15,7 @@ struct ViewOrigin: Equatable { } extension ViewOrigin: Animatable { - var animatableData: AnimatablePair { + package var animatableData: AnimatablePair { get { .init(value.x, value.y) } set { value = .init(x: newValue.first, y: newValue.second) } } diff --git a/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewSize.swift b/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewSize.swift index 0df83c240..bd6d41989 100644 --- a/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewSize.swift +++ b/Sources/OpenSwiftUICore/Layout/LayoutFundamentals/internal/ViewSize.swift @@ -7,7 +7,7 @@ import Foundation -struct ViewSize: Equatable { +package struct ViewSize: Equatable { var value: CGSize var _proposal: CGSize diff --git a/Sources/OpenSwiftUICore/Modifier/AppearanceActionModifier.swift b/Sources/OpenSwiftUICore/Modifier/AppearanceActionModifier.swift index 0a2779a25..ff027d511 100644 --- a/Sources/OpenSwiftUICore/Modifier/AppearanceActionModifier.swift +++ b/Sources/OpenSwiftUICore/Modifier/AppearanceActionModifier.swift @@ -27,7 +27,7 @@ public struct _AppearanceActionModifier: PrimitiveViewModifier { inputs: _ViewInputs, body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs ) -> _ViewOutputs { - let effect = AppearanceEffect(modifier: modifier.value, phase: inputs.phase) + let effect = AppearanceEffect(modifier: modifier.value, phase: inputs.viewPhase) let attribute = Attribute(effect) attribute.flags = [.active, .removable] return body(_Graph(), inputs) diff --git a/Sources/OpenSwiftUICore/View/AnyView.swift b/Sources/OpenSwiftUICore/View/AnyView.swift index eaac2b031..92777664a 100644 --- a/Sources/OpenSwiftUICore/View/AnyView.swift +++ b/Sources/OpenSwiftUICore/View/AnyView.swift @@ -61,10 +61,10 @@ public struct AnyView: PrimitiveView { let parent = OGSubgraph.current! let container = AnyViewContainer(view: view.value, inputs: inputs, outputs: outputs, parentSubgraph: parent) let containerAttribute = Attribute(container) - outputs.forEach { key, value in + outputs.forEachPreference { key, value in value.indirectDependency = containerAttribute.identifier } - if let layoutComputer = outputs.$layoutComputer { + if let layoutComputer = outputs.layoutComputer { layoutComputer.identifier.indirectDependency = containerAttribute.identifier } return outputs diff --git a/Sources/OpenSwiftUICore/View/CoreGlue/CoreGlue.swift b/Sources/OpenSwiftUICore/View/CoreGlue/CoreGlue.swift new file mode 100644 index 000000000..5435dae16 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/CoreGlue/CoreGlue.swift @@ -0,0 +1,35 @@ +// +// CoreGlue.swift +// OpenSwiftUICore +// +// Audited for iOS 18.0 +// Status: Empty + +#if canImport(Darwin) + +public import Foundation +package import OpenGraphShims + +@_spi(ForOpenSwiftUIOnly) +@objc(OpenSwiftUICoreGlue) +open class CoreGlue: NSObject { + package static var shared: CoreGlue = CoreGlue() // FIXME + + + open func makeDefaultLayoutComputer() -> MakeDefaultLayoutComputerResult { + fatalError("TODO") + } +} + +@_spi(ForOpenSwiftUIOnly) +extension CoreGlue { + public struct MakeDefaultLayoutComputerResult { + package var value: Attribute + + package init(value: Attribute) { + self.value = value + } + } +} + +#endif diff --git a/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift b/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift index 95cb263ca..8b947a763 100644 --- a/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift +++ b/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift @@ -148,9 +148,13 @@ extension _ViewDebug { ) -> _ViewOutputs { var inputs = inputs OGSubgraph.beginTreeElement(value: view.value, flags: 0) - var outputs = inputs.withEmptyChangedDebugPropertiesInputs { inputs in - body(view, inputs) - } + // FIXME +// var outputs = inputs.withEmptyChangedDebugPropertiesInputs { inputs in +// body(view, inputs) +// } + inputs.changedDebugProperties = [] + var outputs = body(view, inputs) + if OGSubgraph.shouldRecordTree { _ViewDebug.reallyWrap(&outputs, value: view, inputs: &inputs) OGSubgraph.endTreeElement(value: view.value) diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift index 698e35825..bc380472b 100644 --- a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift @@ -199,14 +199,14 @@ package final class ViewGraph: GraphHost { #if canImport(Darwin) let outputs = self.data.globalSubgraph.apply { let graphInputs = graphInputs + var inputs = _ViewInputs( - base: graphInputs, - preferences: PreferencesInputs(hostKeys: data.$hostPreferenceKeys), - transform: $rootTransform, + graphInputs, position: $position, - containerPosition: $zeroPoint, size: $dimensions, - safeAreaInsets: OptionalAttribute() + transform: $rootTransform, + containerPosition: $zeroPoint, + hostPreferenceKeys: data.$hostPreferenceKeys ) if requestedOutputs.contains(.layout) { // FIXME @@ -232,10 +232,10 @@ package final class ViewGraph: GraphHost { as: RootGeometry.self, invalidating: true ) { rootGeometry in - rootGeometry.$childLayoutComputer = outputs.$layoutComputer + rootGeometry.$childLayoutComputer = outputs.layoutComputer } // TODO - hostPreferenceValues.projectedValue = outputs.hostPreferences + // hostPreferenceValues.projectedValue = outputs.hostPreferences makePreferenceOutlets(outputs: outputs) #endif } @@ -274,18 +274,6 @@ extension ViewGraph { } } -extension PreferenceBridge { - func invalidate() { - requestedPreferences = PreferenceKeys() - bridgedViewInputs = PropertyList() - for child in children { - let viewGraph = child.takeRetainedValue() - viewGraph.setPreferenceBridge(to: nil, isInvalidating: true) - child.release() - } - } -} - extension ViewGraph { struct NextUpdate { var time: Time diff --git a/Sources/OpenSwiftUICore/View/Input/ViewInputPredicate.swift b/Sources/OpenSwiftUICore/View/Input/ViewInputPredicate.swift new file mode 100644 index 000000000..616d92acd --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Input/ViewInputPredicate.swift @@ -0,0 +1,138 @@ +// +// ViewInputPredicate.swift +// OpenSwiftUICore +// +// Audited for iOS 18.0 +// Status: Complete + +// MARK: - ViewInputPredicate + +package protocol ViewInputPredicate { + static func evaluate(inputs: _GraphInputs) -> Bool +} + +// MARK: - ViewInputFlag + +package protocol ViewInputFlag: ViewInputPredicate, _GraphInputsModifier { + associatedtype Input: ViewInput where Input.Value: Equatable + static var value: Input.Value { get } + init() +} + +extension ViewInputFlag { + package static func evaluate(inputs: _GraphInputs) -> Bool { + inputs[Input.self] == value + } + + package static func _makeInputs(modifier _: _GraphValue, inputs: inout _GraphInputs) { + inputs[Input.self] = value + } +} + +extension ViewInput where Self: ViewInputFlag { + package typealias Input = Self +} + +// MARK: - ViewInputBoolFlag + +package protocol ViewInputBoolFlag: ViewInput, ViewInputFlag where Value == Bool {} + + +extension ViewInputBoolFlag { + @inlinable + package static var defaultValue: Bool { false } + + @inlinable + package static var value: Bool { true } +} + +// MARK: - ViewInputPredicate + Extension + +extension ViewInputPredicate { + package static prefix func ! (predicate: Self) -> some ViewInputPredicate { + InvertedViewInputPredicate() + } + + package static func || (lhs: Self, rhs: Other) -> some ViewInputPredicate where Other: ViewInputPredicate { + OrOperationViewInputPredicate() + } + + package typealias Inverted = InvertedViewInputPredicate +} + +// MARK: - InvertedViewInputPredicate + +package struct InvertedViewInputPredicate: ViewInputPredicate where Base: ViewInputPredicate { + package static func evaluate(inputs: _GraphInputs) -> Bool { + !Base.evaluate(inputs: inputs) + } +} + +extension InvertedViewInputPredicate where Base: Feature { + package static var isEnabled: Bool { + !Base.isEnabled + } +} +extension InvertedViewInputPredicate: ViewInputBoolFlag, ViewInputFlag, _GraphInputsModifier, ViewInput, GraphInput, PropertyKey where Base: ViewInputBoolFlag { + @inlinable + package static var value: Bool { false } + + @inlinable + package init() {} + + package typealias Value = Bool +} + +// MARK: - OrOperationViewInputPredicate + +package struct OrOperationViewInputPredicate: ViewInputPredicate where Left: ViewInputPredicate, Right: ViewInputPredicate { + package static func evaluate(inputs: _GraphInputs) -> Bool { + Left.evaluate(inputs: inputs) || Right.evaluate(inputs: inputs) + } + + @inlinable + package init() {} +} + +// MARK: - AndOperationViewInputPredicate + +package struct AndOperationViewInputPredicate: ViewInputPredicate where Left: ViewInputPredicate, Right: ViewInputPredicate { + package static func evaluate(inputs: _GraphInputs) -> Bool { + Left.evaluate(inputs: inputs) && Right.evaluate(inputs: inputs) + } + + @inlinable + package init() {} +} + +package struct TypesMatch: ViewInputPredicate { + package static func evaluate(inputs: _GraphInputs) -> Bool { + Left.self == Right.self + } + + @inlinable + package init() {} +} + +package struct IsVisionEnabledPredicate: ViewInputPredicate { + package static func evaluate(inputs: _GraphInputs) -> Bool { + #if os(macOS) + false + #else + inputs.interfaceIdiom.accepts(.vision) + #endif + } + + package init() {} +} + +extension _ViewInputs { + package var isVisionEnabled: Bool { + IsVisionEnabledPredicate.evaluate(inputs: base) + } +} +extension _ViewListInputs { + package var isVisionEnabled: Bool { + IsVisionEnabledPredicate.evaluate(inputs: base) + } +} diff --git a/Sources/OpenSwiftUICore/View/Input/ViewInputs.swift b/Sources/OpenSwiftUICore/View/Input/ViewInputs.swift new file mode 100644 index 000000000..14b5c509c --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Input/ViewInputs.swift @@ -0,0 +1,266 @@ +// +// ViewInputs.swift +// OpenSwiftUICore +// +// Audited for iOS 18.0 +// Status: Complete + +#if !canImport(Darwin) +package import Foundation +#endif +package import OpenGraphShims + +package typealias ViewPhase = _GraphInputs.Phase + +package protocol ViewInput: GraphInput {} + +/// The input (aka inherited) attributes supplied to each view. Most +/// view types will only actually wire a small number of these into +/// their node. Doesn't include the view itself, which is passed +/// separately. +public struct _ViewInputs { + package var base: _GraphInputs + + package var preferences: PreferencesInputs + + package var customInputs: PropertyList { + get { base.customInputs } + set { base.customInputs = newValue } + } + + package subscript(input: T.Type) -> T.Value where T : ViewInput { + get { base[input] } + set { base[input] = newValue } + } + + package subscript(input: T.Type) -> T.Value where T : ViewInput, T.Value : GraphReusable { + get { base[input] } + set { base[input] = newValue } + } + + package var time: Attribute