diff --git a/.codecov.yml b/.codecov.yml index 1f15d08a0..4b8c5086b 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,9 +6,9 @@ coverage: default: target: auto threshold: 5 - patch: - default: - target: auto - threshold: 5 + patch: off + # default: + # target: auto + # threshold: 5 ignore: - Tests diff --git a/Package.resolved b/Package.resolved index 10844b86c..758db548b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "3ec5ceae50a3ce95edc97ef8b27b3a65cb61320642aa5cc535bb5d4690cb4230", + "originHash" : "1cf308da26a892a1962d7acfd0aeae3c95ac506674d47f80a07474eb8476d770", "pins" : [ { "identity" : "darwinprivateframeworks", diff --git a/Package.swift b/Package.swift index 8ae814135..2e3339dd1 100644 --- a/Package.swift +++ b/Package.swift @@ -314,15 +314,23 @@ if useLocalDeps { package.dependencies += [ .package(path: "../OpenGraph"), .package(path: "../OpenBox"), - .package(path: "../DarwinPrivateFrameworks"), ] + if attributeGraphCondition || renderBoxCondition { + package.dependencies.append( + .package(path: "../DarwinPrivateFrameworks") + ) + } } else { package.dependencies += [ // FIXME: on Linux platform: OG contains unsafe build flags which prevents us using version dependency .package(url: "https://github.com/OpenSwiftUIProject/OpenGraph", branch: "main"), .package(url: "https://github.com/OpenSwiftUIProject/OpenBox", branch: "main"), - .package(url: "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", branch: "main"), ] + if attributeGraphCondition || renderBoxCondition { + package.dependencies.append( + .package(url: "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", branch: "main") + ) + } } #if os(macOS) diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift index fd7e4e98b..90d3c5f49 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift @@ -3,7 +3,7 @@ // OpenSwiftUICore // // Audited for iOS 18.0 -// Status: Blocked by ViewGraph +// Status: Complete // ID: A9FAE381E99529D5274BA37A9BC9B074 (SwiftUI) // ID: DF57A19C61B44C613EB77C1D47FC679A (SwiftUICore) @@ -31,12 +31,12 @@ package final class PreferenceBridge { 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() - // } + for child in children { + let viewGraph = child.takeRetainedValue() + viewGraph.invalidatePreferenceBridge() + child.release() + } + viewGraph = nil isValid = false } @@ -109,10 +109,11 @@ package final class PreferenceBridge { } package func removedStateDidChange() { - // TODO: Blocked by ViewGraph - // for child in children { - // let viewGraph = child.takeUnretainedValue() - // } + for child in children { + let viewGraph = child.takeUnretainedValue() + viewGraph.updateRemovedState() + child.release() + } } #if canImport(Darwin) diff --git a/Sources/OpenSwiftUICore/Graph/GraphHost.swift b/Sources/OpenSwiftUICore/Graph/GraphHost.swift index 8f2550282..d31c29c58 100644 --- a/Sources/OpenSwiftUICore/Graph/GraphHost.swift +++ b/Sources/OpenSwiftUICore/Graph/GraphHost.swift @@ -4,8 +4,8 @@ // // Audited for iOS 18.0 // Status: Blocked by transactions -// ID: 30C09FF16BC95EC5173809B57186CAC3 (RELEASE_2021) -// ID: F9F204BD2F8DB167A76F17F3FB1B3335 (RELEASE_2024) +// ID: 30C09FF16BC95EC5173809B57186CAC3 (SwiftUI) +// ID: F9F204BD2F8DB167A76F17F3FB1B3335 (SwiftUICore) import OpenSwiftUI_SPI package import OpenGraphShims @@ -329,7 +329,38 @@ extension GraphHost { } package final func updateRemovedState() { - preconditionFailure("TODO") + let isRemoved: Bool + let removedState: RemovedState + + if self.removedState.isEmpty { + if let parentHost { + let state = parentHost.removedState + isRemoved = state.contains(.unattached) + removedState = state + } else { + isRemoved = false + removedState = [] + } + } else { + isRemoved = true + removedState = self.removedState + } + let isHiddenForReuse = removedState.contains(.hiddenForReuse) + + if isRemoved != data.isRemoved { + if isRemoved { + rootSubgraph.willRemove() + // TODO: OGSubgraphRemoveChild + } else { + // TODO: OGSubgraphAddChild + rootSubgraph.didReinsert() + } + data.isRemoved = isRemoved + } + if isHiddenForReuse != data.isHiddenForReuse { + data.isHiddenForReuse = isHiddenForReuse + isHiddenForReuseDidChange() + } } // MARK: - GraphHost + Transaction @@ -582,7 +613,7 @@ private final class GlobalTransaction { } } -// MARK: - Graph + Extension +// MARK: - Graph + GraphHost extension Graph { package func graphHost() -> GraphHost { diff --git a/Sources/OpenSwiftUICore/Layout/Edge/EdgeInsets.swift b/Sources/OpenSwiftUICore/Layout/Edge/EdgeInsets.swift index 4f500999c..f6ed71952 100644 --- a/Sources/OpenSwiftUICore/Layout/Edge/EdgeInsets.swift +++ b/Sources/OpenSwiftUICore/Layout/Edge/EdgeInsets.swift @@ -16,7 +16,6 @@ public struct EdgeInsets: Equatable { public var trailing: CGFloat @inlinable - @inline(__always) public init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) { self.top = top self.leading = leading @@ -25,11 +24,12 @@ public struct EdgeInsets: Equatable { } @inlinable - @inline(__always) public init() { self.init(top: 0, leading: 0, bottom: 0, trailing: 0) } + package static var zero: EdgeInsets { EdgeInsets() } + @inline(__always) init(_ value: CGFloat, edges: Edge.Set) { self.init( diff --git a/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift b/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift index ec224fe72..e7406a698 100644 --- a/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift +++ b/Sources/OpenSwiftUICore/View/Debug/ViewDebug.swift @@ -97,7 +97,15 @@ extension View { } } -extension ViewModifier { +extension ViewModifier { + package static func makeDebuggableView( + modifier: _GraphValue, + inputs: _ViewInputs, + body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs + ) -> _ViewOutputs { + preconditionFailure("TODO") + } + static func makeDebuggableViewList( modifier: _GraphValue, inputs: _ViewListInputs, diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift index 7a43f76d4..871c82300 100644 --- a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift @@ -2,231 +2,239 @@ // ViewGraph.swift // OpenSwiftUI // -// Audited for iOS 15.5 +// Audited for iOS 18.0 // Status: WIP -// ID: D63C4EB7F2B205694B6515509E76E98B +// ID: D63C4EB7F2B205694B6515509E76E98B (SwiftUI) +// ID: 7D9EDEF832940A362646A6E979F296C8 (SwiftUICore) -import OpenGraphShims +package import OpenGraphShims +#if canImport(Darwin) import Foundation +#else +package import Foundation +#endif +import OpenSwiftUI_SPI package final class ViewGraph: GraphHost { - @inline(__always) - static var current: ViewGraph { GraphHost.currentHost as! ViewGraph } + package struct Outputs: OptionSet { + package let rawValue: UInt8 + + package init(rawValue: UInt8) { + self.rawValue = rawValue + } + + package static let displayList: ViewGraph.Outputs = .init(rawValue: 1 << 0) + package static let platformItemList: ViewGraph.Outputs = .init(rawValue: 1 << 1) + package static let viewResponders: ViewGraph.Outputs = .init(rawValue: 1 << 2) + package static let layout: ViewGraph.Outputs = .init(rawValue: 1 << 4) + package static let focus: ViewGraph.Outputs = .init(rawValue: 1 << 5) + package static let all: ViewGraph.Outputs = .init(rawValue: 0xFF) + package static let defaults: ViewGraph.Outputs = [.displayList, .viewResponders, .layout, .focus] + + @inline(__always) + fileprivate func addRequestedPreferences(to inputs: inout _ViewInputs) { + inputs.preferences.add(HostPreferencesKey.self) + if contains(.platformItemList) { + inputs.preferences.add(DisplayList.Key.self) + } + if contains(.viewResponders) { + // inputs.preferences.add(ViewRespondersKey.self) + } + } + } let rootViewType: Any.Type let makeRootView: (AnyAttribute, _ViewInputs) -> _ViewOutputs - weak var delegate: ViewGraphDelegate? - var centersRootView: Bool = true - let rootView: AnyAttribute + + package weak var delegate: (any ViewGraphDelegate)? = nil + + // private var features: ViewGraphFeaturesBuffer = .init() + + package var centersRootView: Bool = true + + package let rootView: AnyAttribute + @Attribute var rootTransform: ViewTransform - @Attribute var zeroPoint: ViewOrigin - // TODO - @Attribute var proposedSize: ViewSize - // TODO + @Attribute package var transform: ViewTransform + @Attribute package var zeroPoint: ViewOrigin + @Attribute package var proposedSize: ViewSize + @Attribute package var safeAreaInsets: _SafeAreaInsetsModifier + @Attribute var rootGeometry: ViewGeometry @Attribute var position: ViewOrigin @Attribute var dimensions: ViewSize - @Attribute var updateSeed: UInt32 - // TODO - @Attribute var defaultLayoutComputer: LayoutComputer - // TODO - var cachedSizeThatFits: CGSize = .invalidValue - var sizeThatFitsObserver: SizeThatFitsObserver? { - didSet { - guard let _ = sizeThatFitsObserver else { + + @OptionalAttribute var scrollableContainerSize: ViewSize? + + @Attribute var gestureTime: Time + // @Attribute var gestureEvents: [EventID : EventType] + // @Attribute var inheritedPhase: _GestureInputs.InheritedPhase + @Attribute var gestureResetSeed: UInt32 + // @OptionalAttribute var rootPhase: GesturePhase? + // @OptionalAttribute package var gestureDebug: GestureDebug.Data? + // @OptionalAttribute package var gestureCategory: GestureCategory? + @Attribute package var gesturePreferenceKeys: PreferenceKeys + var eventSubgraph: Subgraph? + + @Attribute package var defaultLayoutComputer: LayoutComputer + // @WeakAttribute var rootResponders: [ViewResponder]? + @WeakAttribute var rootLayoutComputer: LayoutComputer? + @WeakAttribute var rootDisplayList: (DisplayList, DisplayList.Version)? + + // package var sizeThatFitsObservers: ViewGraphGeometryObservers = .init() + + package var accessibilityEnabled: Bool = false + + package var requestedOutputs: Outputs + var disabledOutputs: Outputs = [] + + private var mainUpdates: Int = 0 + + // MARK: - ViewGraph + NextUpdate + + package struct NextUpdate { + package private(set) var time: Time = .infinity + private var _interval: Double = .infinity + package var interval: Double { + _interval.isFinite ? .zero : _interval + } + package private(set) var reasons: Set = [] + + package mutating func at(_ next: Time) { + time = next < time ? next : time + } + + package mutating func maxVelocity(_ velocity: CGFloat) { + guard velocity >= 160 else { return } - guard requestedOutputs.contains(.layout) else { - preconditionFailure("Cannot use sizeThatFits without layout output") + let interval = velocity < 320 ? 1 / 80.0 : 1 / 120.0 + let highFrameRateReason: UInt32 = _HighFrameRateReasonMake(0) + _interval = min(interval, _interval) + reasons.insert(highFrameRateReason) + } + + package mutating func interval(_ interval: Double, reason: UInt32? = nil) { + if interval == .zero { + if _interval > 1 / 60 { + _interval = .infinity + } + } else { + _interval = min(interval, _interval) + } + if let reason { + reasons.insert(reason) } } } - var requestedOutputs: Outputs - var disabledOutputs: Outputs = [] - var mainUpdates: Int = 0 - var needsFocusUpdate: Bool = false - var nextUpdate: (views: NextUpdate, gestures: NextUpdate) = (NextUpdate(time: .infinity), NextUpdate(time: .infinity)) + + package var nextUpdate: (views: NextUpdate, gestures: NextUpdate) = (NextUpdate(), NextUpdate()) + private weak var _preferenceBridge: PreferenceBridge? + package var preferenceBridge: PreferenceBridge? { get { _preferenceBridge } - set { setPreferenceBridge(to: newValue, isInvalidating: false) } + set { setPreferenceBridge(to: newValue) } } #if canImport(Darwin) // FIXME: See #39 var bridgedPreferences: [(AnyPreferenceKey.Type, AnyAttribute)] = [] #endif - // TODO - package init(rootViewType: Body.Type, requestedOutputs: Outputs) { + package static var current: ViewGraph { GraphHost.currentHost as! ViewGraph } + + package init(rootViewType: Root.Type = Root.self, requestedOutputs: ViewGraph.Outputs = Outputs.defaults) where Root: View { #if canImport(Darwin) self.rootViewType = rootViewType self.requestedOutputs = requestedOutputs - let data = GraphHost.Data() OGSubgraph.current = data.globalSubgraph - rootView = Attribute(type: Body.self).identifier + rootView = Attribute(type: Root.self).identifier _rootTransform = Attribute(RootTransform()) + _transform = _rootTransform _zeroPoint = Attribute(value: ViewOrigin()) - // TODO _proposedSize = Attribute(value: .zero) - // TODO - _rootGeometry = Attribute(RootGeometry(proposedSize: _proposedSize)) + _scrollableContainerSize = requestedOutputs.contains(.layout) ? OptionalAttribute(Attribute(value: .zero)) : OptionalAttribute() + _safeAreaInsets = Attribute(value: _SafeAreaInsetsModifier(elements: [.init(regions: .container, insets: .zero)])) + _defaultLayoutComputer = Attribute(value: .defaultValue) + _gestureTime = Attribute(value: .zero) + // _gestureEvents + // _inheritedPhase + _gestureResetSeed = Attribute(value: .zero) + _gesturePreferenceKeys = Attribute(value: .init()) + _rootGeometry = Attribute(RootGeometry(proposedSize: _proposedSize, safeAreaInsets: OptionalAttribute(_safeAreaInsets))) _position = _rootGeometry.origin() _dimensions = _rootGeometry.size() - _updateSeed = Attribute(value: .zero) - // TODO - _defaultLayoutComputer = Attribute(value: .defaultValue) - // FIXME - makeRootView = { view, inputs in - let rootView = _GraphValue(view.unsafeCast(to: Body.self)) - return Body._makeView(view: rootView, inputs: inputs) + makeRootView = { [_zeroPoint, _proposedSize, _safeAreaInsets] view, inputs in + // FIXME + _ = _zeroPoint + _ = _proposedSize + return _SafeAreaInsetsModifier.makeDebuggableView(modifier: _GraphValue(_safeAreaInsets), inputs: inputs) { _, inputs in + let rootView = _GraphValue(view.unsafeCast(to: Root.self)) + return Root.makeDebuggableView(view: rootView, inputs: inputs) + } } super.init(data: data) - OGSubgraph.current = nil + Subgraph.current = nil #else - preconditionFailure("TOOD") + preconditionFailure("#39") #endif } deinit { + // FIXME removePreferenceOutlets(isInvalidating: true) } - - @inline(__always) - func updateOutputs(at time: Time) { - beginNextUpdate(at: time) - updateOutputs() - } - - private func beginNextUpdate(at time: Time) { - setTime(time) - updateSeed &+= 1 - mainUpdates = graph.mainUpdates - } - - private func updateOutputs() { - #if canImport(Darwin) - instantiateIfNeeded() - - let oldCachedSizeThatFits = cachedSizeThatFits - - var preferencesChanged = false - var observedSizeThatFitsChanged = false - var updatedOutputs: Outputs = [] - - var counter1 = 0 - repeat { - counter1 &+= 1 - inTransaction = true - var counter2 = 0 - repeat { - let conts = continuations - continuations = [] - for continuation in conts { - continuation() - } - counter2 &+= 1 - data.globalSubgraph.update(flags: .active) - } while (continuations.count != 0 && counter2 != 8) - inTransaction = false - preferencesChanged = preferencesChanged || updatePreferences() - observedSizeThatFitsChanged = observedSizeThatFitsChanged || updateObservedSizeThatFits() - updatedOutputs.formUnion(updateRequestedOutputs()) - } while (data.globalSubgraph.isDirty(1) && counter1 != 8) - guard preferencesChanged || observedSizeThatFitsChanged || !updatedOutputs.isEmpty || needsFocusUpdate else { - return - } - if Thread.isMainThread { - if preferencesChanged { - delegate?.preferencesDidChange() - } - if observedSizeThatFitsChanged { - sizeThatFitsObserver?.callback(oldCachedSizeThatFits, self.cachedSizeThatFits) - } - if !requestedOutputs.isEmpty { -// delegate?.outputsDidChange(outputs: updatedOutputs) - } - if needsFocusUpdate { - needsFocusUpdate = false -// delegate?.focusDidChange() - } - } else { - preconditionFailure("TODO") - } - mainUpdates &-= 1 - #endif - } - - private func updateObservedSizeThatFits() -> Bool { - // TODO - return false - } - - private func updateRequestedOutputs() -> Outputs { - // TODO - return [] - } - - func clearPreferenceBridge() { - setPreferenceBridge(to: nil, isInvalidating: true) - } - - private func makePreferenceOutlets(outputs: _ViewOutputs) { - // TODO - } - - private func removePreferenceOutlets(isInvalidating: Bool) { - // TODO - } + override package var graphDelegate: GraphDelegate? { delegate } - package func setRootView(_ view: V) { - #if canImport(Darwin) - @Attribute(identifier: rootView) - var rootView: V - rootView = view - #endif - } + override package var parentHost: GraphHost? { preferenceBridge?.viewGraph } - // MARK: - Override Methods - - override package var graphDelegate: GraphDelegate? { delegate } - override package var parentHost: GraphHost? { - // TODO: _preferenceBridge - nil - } + // TODO: ViewGraphFeature + // package func append(feature: T) where T: ViewGraphFeature + // package subscript(feature: T.Type) -> UnsafeMutablePointer? where T: ViewGraphFeature override package func instantiateOutputs() { #if canImport(Darwin) - let outputs = self.data.globalSubgraph.apply { - let graphInputs = graphInputs - + let outputs = globalSubgraph.apply { var inputs = _ViewInputs( graphInputs, position: $position, size: $dimensions, - transform: $rootTransform, + transform: $transform, containerPosition: $zeroPoint, hostPreferenceKeys: data.$hostPreferenceKeys ) if requestedOutputs.contains(.layout) { - // FIXME - // inputs.base.options.formUnion(.init(rawValue: 0xe2)) + inputs.base.options.formUnion([.viewRequestsLayoutComputer, .viewNeedsGeometry]) + inputs.scrollableContainerSize = _scrollableContainerSize } requestedOutputs.addRequestedPreferences(to: &inputs) - _preferenceBridge?.wrapInputs(&inputs) - _ViewDebug.instantiateIfNeeded() - delegate?.modifyViewInputs(&inputs) - // TODO + if let preferenceBridge { + preferenceBridge.wrapInputs(&inputs) + } + _ViewDebug.instantiateIfNeeded() // FIXME + if _VariableFrameDurationIsSupported() { + if !inputs.base.options.contains(.supportsVariableFrameDuration) { + inputs.base.options.formUnion(.supportsVariableFrameDuration) + } + } + if let delegate { + delegate.modifyViewInputs(&inputs) + } + if inputs.base.options.contains(.viewNeedsGeometry) { + // inputs.makeRootMatchedGeometryScope() + } + inputs.base.pushStableType(rootViewType) $rootGeometry.mutateBody( as: RootGeometry.self, invalidating: true ) { rootGeometry in - inputs.withMutableCachedEnviroment { - rootGeometry.$layoutDirection = $0.attribute(keyPath: \.layoutDirection) - } + rootGeometry.$layoutDirection = inputs.mapEnvironment(\.layoutDirection) } - // TOOD - return makeRootView(rootView, inputs) + // TODO: features related + let outputs = makeRootView(rootView, inputs) + // TODO: features related + return outputs } $rootGeometry.mutateBody( as: RootGeometry.self, @@ -234,8 +242,20 @@ package final class ViewGraph: GraphHost { ) { rootGeometry in rootGeometry.$childLayoutComputer = outputs.layoutComputer } - // TODO - // hostPreferenceValues.projectedValue = outputs.hostPreferences + if requestedOutputs.contains(.displayList) { + if let displayList = outputs.preferences[DisplayList.Key.self] { + _rootDisplayList = WeakAttribute(rootSubgraph.apply { + Attribute(RootDisplayList(content: displayList, time: data.$time)) + }) + } + } + if requestedOutputs.contains(.viewResponders) { + // _rootResponders = WeakAttribute(outputs.preferences[ViewRespondersKey.self]) + } + if requestedOutputs.contains(.layout) { + _rootLayoutComputer = WeakAttribute(outputs.layoutComputer) + } + hostPreferenceValues = WeakAttribute(outputs.preferences[HostPreferencesKey.self]) makePreferenceOutlets(outputs: outputs) #endif } @@ -243,98 +263,232 @@ package final class ViewGraph: GraphHost { override package func uninstantiateOutputs() { #if canImport(Darwin) removePreferenceOutlets(isInvalidating: false) + // TODO: features $rootGeometry.mutateBody( as: RootGeometry.self, invalidating: true ) { rootGeometry in - rootGeometry.$layoutDirection = nil rootGeometry.$childLayoutComputer = nil + rootGeometry.$layoutDirection = nil } -// $rootPlatformList = nil + $rootLayoutComputer = nil // $rootResponders = nil -// $rootAccessibilityNodes = nil -// $rootLayoutComputer = nil -// $rootDisplayList = nil + $rootDisplayList = nil hostPreferenceValues = WeakAttribute() #endif } override package func timeDidChange() { - nextUpdate.views = NextUpdate(time: .infinity) + nextUpdate.views = NextUpdate() } override package func isHiddenForReuseDidChange() { + preconditionFailure("TODO") + } + + private func makePreferenceOutlets(outputs: _ViewOutputs) { // TODO } -} - -extension ViewGraph { - fileprivate func setPreferenceBridge(to bridge: PreferenceBridge?, isInvalidating: Bool) { + + @inline(__always) + private func removePreferenceOutlets(isInvalidating: Bool) { // TODO } } extension ViewGraph { - struct NextUpdate { - var time: Time - var _interval: Double - var reasons: Set - - @inline(__always) - init(time: Time) { - self.time = time - _interval = .infinity - reasons = [] + package func setRootView(_ view: Root) where Root: View { + #if canImport(Darwin) + @Attribute(identifier: rootView) + var rootView: Root + rootView = view + #endif + } + + package func setSize(_ size: ViewSize) { + let hasChange = $proposedSize.setValue(size) + if hasChange { + delegate?.graphDidChange() } - - // TODO: AnimatorState.nextUpdate - mutating func interval(_ value: Double, reason: UInt32?) { - if value == .zero { - if _interval > 1 / 60 { - _interval = .infinity - } - } else { - _interval = min(value, _interval) - } - if let reason { - reasons.insert(reason) - } + } + + package func setProposedSize(_ size: CGSize) { + let hasChange = $proposedSize.setValue(ViewSize.fixed(size)) + if hasChange { + delegate?.graphDidChange() + } + } + + package var size: ViewSize { + proposedSize + } + + @discardableResult + package func setSafeAreaInsets(_ insets: EdgeInsets) -> Bool { + setSafeAreaInsets([.init(regions: .container, insets: insets)]) + } + + @discardableResult + package func setSafeAreaInsets(_ elts: [SafeAreaInsets.Element]) -> Bool { + let hasChange = $safeAreaInsets.setValue(.init(elements: elts)) + if hasChange { + delegate?.graphDidChange() + } + return hasChange + } + + package func setScrollableContainerSize(_ size: ViewSize) { + guard let $scrollableContainerSize else { + return + } + let hasChange = $scrollableContainerSize.setValue(size) + if hasChange { + delegate?.graphDidChange() } } + + @discardableResult + package func invalidateTransform() -> Bool { + preconditionFailure("TODO") + } } extension ViewGraph { - package struct Outputs: OptionSet { - package let rawValue: UInt8 + package var updateRequiredMainThread: Bool { + graph.mainUpdates != mainUpdates + } + + package func updateOutputs(at time: Time) { + beginNextUpdate(at: time) + updateOutputs(async: false) + } + + package func updateOutputsAsync(at time: Time) -> (list: DisplayList, version: DisplayList.Version)? { + beginNextUpdate(at: time) + preconditionFailure("TODO") + } + + package func displayList() -> (DisplayList, DisplayList.Version) { + preconditionFailure("TODO") + } + + private func beginNextUpdate(at time: Time) { + setTime(time) + data.updateSeed &+= 1 + mainUpdates = graph.mainUpdates + } + + // FIXME + private func updateOutputs(async: Bool) { + #if canImport(Darwin) + instantiateIfNeeded() - package init(rawValue: UInt8) { - self.rawValue = rawValue - } + // let oldCachedSizeThatFits = cachedSizeThatFits - package static let displayList: ViewGraph.Outputs = .init(rawValue: 1 << 0) - package static let platformItemList: ViewGraph.Outputs = .init(rawValue: 1 << 1) - package static let viewResponders: ViewGraph.Outputs = .init(rawValue: 1 << 2) - package static let layout: ViewGraph.Outputs = .init(rawValue: 1 << 4) - package static let focus: ViewGraph.Outputs = .init(rawValue: 1 << 5) - package static let all: ViewGraph.Outputs = .init(rawValue: 0xFF) - package static let defaults: ViewGraph.Outputs = [.displayList, .viewResponders, .layout, .focus] + var preferencesChanged = false + var observedSizeThatFitsChanged = false + var updatedOutputs: Outputs = [] - // FIXME - fileprivate func addRequestedPreferences(to inputs: inout _ViewInputs) { - inputs.preferences.add(HostPreferencesKey.self) - if contains(.displayList) { - inputs.preferences.add(DisplayList.Key.self) - } - if contains(.viewResponders) { -// inputs.preferences.add(ViewRespondersKey.self) - } - if contains(.platformItemList) { -// inputs.preferences.add(PlatformItemList.Key.self) + var counter1 = 0 + repeat { + counter1 &+= 1 + inTransaction = true + var counter2 = 0 + repeat { + let conts = continuations + continuations = [] + for continuation in conts { + continuation() + } + counter2 &+= 1 + data.globalSubgraph.update(flags: .active) + } while (continuations.count != 0 && counter2 != 8) + inTransaction = false + preferencesChanged = preferencesChanged || updatePreferences() + observedSizeThatFitsChanged = observedSizeThatFitsChanged || updateObservedSizeThatFits() + updatedOutputs.formUnion(updateRequestedOutputs()) + } while (data.globalSubgraph.isDirty(1) && counter1 != 8) + +// guard preferencesChanged || observedSizeThatFitsChanged || !updatedOutputs.isEmpty || needsFocusUpdate else { +// return +// } +// if Thread.isMainThread { +// if preferencesChanged { +// delegate?.preferencesDidChange() +// } +// if observedSizeThatFitsChanged { +// sizeThatFitsObserver?.callback(oldCachedSizeThatFits, self.cachedSizeThatFits) +// } +// if !requestedOutputs.isEmpty { +//// delegate?.outputsDidChange(outputs: updatedOutputs) +// } +// if needsFocusUpdate { +// needsFocusUpdate = false +//// delegate?.focusDidChange() +// } +// } else { +// preconditionFailure("TODO") +// } +// mainUpdates &-= 1 + #endif + } + + private func updateObservedSizeThatFits() -> Bool { + // TODO + return false + } + + private func updateRequestedOutputs() -> Outputs { + // TODO + return [] + } +} + +// TODO + +extension ViewGraph { + package func invalidatePreferenceBridge() { + setPreferenceBridge(to: nil, isInvalidating: true) + } + + @inline(__always) + private func setPreferenceBridge(to preferenceBridge: PreferenceBridge?, isInvalidating: Bool = false) { + guard _preferenceBridge !== preferenceBridge else { return } + #if canImport(Darwin) + if let preferenceBridge = _preferenceBridge { + for (src, key) in bridgedPreferences { + preferenceBridge.removeValue(key, for: src, isInvalidating: isInvalidating) } + bridgedPreferences = [] + preferenceBridge.removeHostValues(for: data.$hostPreferenceKeys, isInvalidating: isInvalidating) + preferenceBridge.removeChild(self) + } + #endif + _preferenceBridge = nil + if isInstantiated { + uninstantiate(immediately: isInvalidating) } + _preferenceBridge = preferenceBridge + if let preferenceBridge = _preferenceBridge { + preferenceBridge.addChild(self) + } + updateRemovedState() + } +} + +// MARK: - RootDisplayList + +private struct RootDisplayList: Rule, AsyncAttribute { + @Attribute var content: DisplayList + @Attribute var time: Time + + var value: (DisplayList, DisplayList.Version) { + preconditionFailure("TODO") } } +// MARK: - RootTransform + private struct RootTransform: Rule { var value: ViewTransform { guard let delegate = ViewGraph.current.delegate else { @@ -344,11 +498,26 @@ private struct RootTransform: Rule { } } -struct RootGeometry: Rule, AsyncAttribute { - @OptionalAttribute var layoutDirection: LayoutDirection? - @Attribute var proposedSize: ViewSize - @OptionalAttribute var safeAreaInsets: _SafeAreaInsetsModifier? - @OptionalAttribute var childLayoutComputer: LayoutComputer? +// MARK: - RootGeometry + +package struct RootGeometry: Rule, AsyncAttribute { + @OptionalAttribute package var layoutDirection: LayoutDirection? + @Attribute package var proposedSize: ViewSize + @OptionalAttribute package var safeAreaInsets: _SafeAreaInsetsModifier? + @OptionalAttribute package var childLayoutComputer: LayoutComputer? + + package init( + layoutDirection: OptionalAttribute = .init(), + proposedSize: Attribute, + safeAreaInsets: OptionalAttribute<_SafeAreaInsetsModifier> = .init(), + childLayoutComputer: OptionalAttribute = .init() + ) { + _layoutDirection = layoutDirection + _proposedSize = proposedSize + _safeAreaInsets = safeAreaInsets + _childLayoutComputer = childLayoutComputer + } + // |←--------------------------proposedSize.value.width--------------------------→| (0, 0) // ┌──────────────────────────────────────────────────────────────────────────────┐ ┌─────────> x @@ -381,7 +550,7 @@ struct RootGeometry: Rule, AsyncAttribute { // │ └────────────────────────────────────────────────────────────────────┘ | x: i.l+(p.width-f.width)*0.5=34 // | | y: i.t+(p.height-f.height)*0.5=14 // └──────────────────────────────────────────────────────────────────────────────┘ - var value: ViewGeometry { + package var value: ViewGeometry { preconditionFailure("TODO") // let layoutComputer = childLayoutComputer ?? .defaultValue // let insets = safeAreaInsets?.insets ?? EdgeInsets() @@ -411,3 +580,11 @@ struct RootGeometry: Rule, AsyncAttribute { // ) } } + +// MARK: - Graph + ViewGraph + +extension Graph { + package func viewGraph() -> ViewGraph { + unsafeBitCast(context, to: ViewGraph.self) + } +} diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraphDelegate.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraphDelegate.swift index a9728ba3b..5b76aeca5 100644 --- a/Sources/OpenSwiftUICore/View/Graph/ViewGraphDelegate.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraphDelegate.swift @@ -9,6 +9,7 @@ package protocol ViewGraphDelegate: GraphDelegate { func `as`(_ type: T.Type) -> T? func modifyViewInputs(_ inputs: inout _ViewInputs) func updateViewGraph(body: (ViewGraph) -> T) -> T + func requestUpdate(after: Double) -> () func rootTransform() -> ViewTransform } diff --git a/Sources/OpenSwiftUICore/View/Input/ViewInputs.swift b/Sources/OpenSwiftUICore/View/Input/ViewInputs.swift index e1a4afd49..e57a40843 100644 --- a/Sources/OpenSwiftUICore/View/Input/ViewInputs.swift +++ b/Sources/OpenSwiftUICore/View/Input/ViewInputs.swift @@ -244,18 +244,6 @@ extension _ViewInputs { body(&base) } - // MARK: - base.cachedEnvironment - - @inline(__always) - func withCachedEnviroment(_ body: (CachedEnvironment) -> R) -> R { - body(base.cachedEnvironment.wrappedValue) - } - - @inline(__always) - func withMutableCachedEnviroment(_ body: (inout CachedEnvironment) -> R) -> R { - body(&base.cachedEnvironment.wrappedValue) - } - @inline(__always) func detechedEnvironmentInputs() -> Self { var newInputs = self diff --git a/Sources/OpenSwiftUI_SPI/OpenSwiftUITargetConditionals.h b/Sources/OpenSwiftUI_SPI/OpenSwiftUITargetConditionals.h index 91a9a2316..efba3b268 100644 --- a/Sources/OpenSwiftUI_SPI/OpenSwiftUITargetConditionals.h +++ b/Sources/OpenSwiftUI_SPI/OpenSwiftUITargetConditionals.h @@ -146,6 +146,7 @@ #define OPENSWIFTUI_TARGET_OS_WATCH TARGET_OS_WATCH #define OPENSWIFTUI_TARGET_OS_TV TARGET_OS_TV #define OPENSWIFTUI_TARGET_OS_MACCATALYST TARGET_OS_MACCATALYST +#define OPENSWIFTUI_TARGET_OS_SIMULATOR TARGET_OS_SIMULATOR #else // iOS, watchOS, and tvOS are not supported #define OPENSWIFTUI_TARGET_OS_IPHONE 0 @@ -153,6 +154,7 @@ #define OPENSWIFTUI_TARGET_OS_WATCH 0 #define OPENSWIFTUI_TARGET_OS_TV 0 #define OPENSWIFTUI_TARGET_OS_MACCATALYST 0 +#define OPENSWIFTUI_TARGET_OS_SIMULATOR 0 #endif /* OpenSwiftUI Addition End */ diff --git a/Sources/OpenSwiftUI_SPI/Util/RenderUtil.c b/Sources/OpenSwiftUI_SPI/Util/RenderUtil.c new file mode 100644 index 000000000..3c525e9ab --- /dev/null +++ b/Sources/OpenSwiftUI_SPI/Util/RenderUtil.c @@ -0,0 +1,22 @@ +// +// RenderUtil.c +// OpenSwiftUI_SPI + +#include "RenderUtil.h" + +uint32_t _HighFrameRateReasonMake(uint32_t value) { + return value | 0x270000; +} + +bool _VariableFrameDurationIsSupported() { + #if OPENSWIFTUI_TARGET_OS_IPHONE && !OPENSWIFTUI_TARGET_OS_SIMULATOR + static bool supported; + static dispatch_once_t once; + dispatch_once(&once, ^{ + supported = false; // MGIsDeviceOneOfType + }); + return supported; + #else + return false; + #endif +} diff --git a/Sources/OpenSwiftUI_SPI/Util/RenderUtil.h b/Sources/OpenSwiftUI_SPI/Util/RenderUtil.h new file mode 100644 index 000000000..295bbe874 --- /dev/null +++ b/Sources/OpenSwiftUI_SPI/Util/RenderUtil.h @@ -0,0 +1,13 @@ +// +// RenderUtil.h +// OpenSwiftUI_SPI + +#ifndef RenderUtil_h +#define RenderUtil_h + +#include "OpenSwiftUIBase.h" + +uint32_t _HighFrameRateReasonMake(uint32_t value); +bool _VariableFrameDurationIsSupported(); + +#endif /* RenderUtil_h */