diff --git a/Package.swift b/Package.swift index 1405fb35..1afa4cf2 100644 --- a/Package.swift +++ b/Package.swift @@ -164,6 +164,7 @@ let package = Package( cSettings: [ .unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)), .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), + .define("_WASI_EMULATED_SIGNAL", .when(platforms: [.wasi])), ] ), .target( @@ -171,6 +172,7 @@ let package = Package( cSettings: [ .unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)), .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), + .define("_WASI_EMULATED_SIGNAL", .when(platforms: [.wasi])), ] ), .binaryTarget(name: "CoreServices", path: "PrivateFrameworks/CoreServices.xcframework"), diff --git a/Package@swift-5.10.swift b/Package@swift-5.10.swift index 5a12bb99..8b1f1aef 100644 --- a/Package@swift-5.10.swift +++ b/Package@swift-5.10.swift @@ -160,6 +160,7 @@ let package = Package( cSettings: [ .unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)), .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), + .define("_WASI_EMULATED_SIGNAL", .when(platforms: [.wasi])), ] ), .target( @@ -167,6 +168,7 @@ let package = Package( cSettings: [ .unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)), .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), + .define("_WASI_EMULATED_SIGNAL", .when(platforms: [.wasi])), ] ), .binaryTarget(name: "CoreServices", path: "PrivateFrameworks/CoreServices.xcframework"), diff --git a/Sources/COpenSwiftUICore/include/OpenSwiftUIBase.h b/Sources/COpenSwiftUICore/include/OpenSwiftUIBase.h index 245a789c..f9454d37 100644 --- a/Sources/COpenSwiftUICore/include/OpenSwiftUIBase.h +++ b/Sources/COpenSwiftUICore/include/OpenSwiftUIBase.h @@ -8,6 +8,7 @@ #ifndef OpenSwiftUIBase_h #define OpenSwiftUIBase_h +#include #include #include #include @@ -67,4 +68,6 @@ # define OPENSWIFTUI_SWIFT_NAME(_name) #endif +#define OPENSWIFTUI_ENUM CF_ENUM + #endif /* OpenSwiftUIBase_h */ diff --git a/Sources/COpenSwiftUICore/include/ViewSystem.h b/Sources/COpenSwiftUICore/include/ViewSystem.h new file mode 100644 index 00000000..561c1fd3 --- /dev/null +++ b/Sources/COpenSwiftUICore/include/ViewSystem.h @@ -0,0 +1,18 @@ +// +// ViewSystem.h +// COpenSwiftUICore +// Audited for RELEASE_2024 +// Status: Complete + +#ifndef ViewSystem_h +#define ViewSystem_h + +#include "OpenSwiftUIBase.h" + +typedef OPENSWIFTUI_ENUM(uint8_t, ViewSystem) { + ViewSystemUIView, + ViewSystemNSView, + ViewSystem_2, +}; + +#endif /* ViewSystem_h */ diff --git a/Sources/OpenSwiftUI/Integration/UIKit/UIHostingView.swift b/Sources/OpenSwiftUI/Integration/UIKit/UIHostingView.swift index 9cc18ace..18ff42bf 100644 --- a/Sources/OpenSwiftUI/Integration/UIKit/UIHostingView.swift +++ b/Sources/OpenSwiftUI/Integration/UIKit/UIHostingView.swift @@ -154,6 +154,44 @@ open class _UIHostingView: UIView where Content: View { } extension _UIHostingView: ViewRendererHost { + package var renderingPhase: OpenSwiftUICore.ViewRenderingPhase { + get { + fatalError("TODO") + } + set(newValue) { + fatalError("TODO") + } + } + + package var externalUpdateCount: Int { + get { + fatalError("TODO") + } + set(newValue) { + fatalError("TODO") + } + } + + package func updateEnvironment() { + fatalError("TODO") + } + + package func updateSize() { + fatalError("TODO") + } + + package func updateSafeArea() { + fatalError("TODO") + } + + package func updateScrollableContainerSize() { + fatalError("TODO") + } + + package func renderDisplayList(_ list: DisplayList, asynchronously: Bool, time: Time, nextTime: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time { + fatalError("TODO") + } + package func addImplicitPropertiesNeedingUpdate(to _: inout ViewRendererHostProperties) {} package func updateRootView() { diff --git a/Sources/OpenSwiftUI/Render/UIViewPlatformViewDefinition.swift b/Sources/OpenSwiftUI/Render/UIViewPlatformViewDefinition.swift new file mode 100644 index 00000000..560bdce9 --- /dev/null +++ b/Sources/OpenSwiftUI/Render/UIViewPlatformViewDefinition.swift @@ -0,0 +1,13 @@ +// +// UIViewPlatformViewDefinition.swift +// OpenSwiftUI +// +// Audited for RELEASE_2024 +// Status: WIP + +@_spi(DisplayList_ViewSystem) internal import OpenSwiftUICore + +final class UIViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sendable { + override final class var system: PlatformViewDefinition.System { .uiView } + // TODO +} diff --git a/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift b/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift index d833dd7d..18232d85 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/PreferenceBridge.swift @@ -1,15 +1,17 @@ // // PreferenceBridge.swift -// OpenSwiftUI +// OpenSwiftUICore // -// Audited for RELEASE_2021 -// Status: Complete -// ID: A9FAE381E99529D5274BA37A9BC9B074 +// Audited for RELEASE_2024 +// Status: TO BE AUDITED +// ID: A9FAE381E99529D5274BA37A9BC9B074 (RELEASE_2021) +// ID: DF57A19C61B44C613EB77C1D47FC679A (RELEASE_2024) internal import OpenGraphShims package final class PreferenceBridge { - unowned let viewGraph: ViewGraph + weak var viewGraph: ViewGraph? + var isValid: Bool = true private(set) var children: [Unmanaged] = [] var requestedPreferences = PreferenceKeys() var bridgedViewInputs = PropertyList() @@ -23,9 +25,15 @@ package final class PreferenceBridge { } init() { - viewGraph = GraphHost.currentHost as! ViewGraph + viewGraph = ViewGraph.current } + deinit { + if isValid { invalidate() } + } + + // FIXME: TO BE AUDITED + #if canImport(Darwin) // FIXME: See #39 func addValue(_ value: AnyAttribute, for keyType: AnyPreferenceKey.Type) { struct AddValue: PreferenceKeyVisitor { @@ -48,7 +56,7 @@ package final class PreferenceBridge { } var visitor = AddValue(combiner: combiner, value: value) keyType.visitKey(&visitor) - viewGraph.graphInvalidation(from: value) + viewGraph?.graphInvalidation(from: value) } func removeValue(_ value: AnyAttribute, for keyType: AnyPreferenceKey.Type, isInvalidating: Bool) { @@ -78,7 +86,7 @@ package final class PreferenceBridge { var visitor = RemoveValue(combiner: combiner, value: value) keyType.visitKey(&visitor) if visitor.changed { - viewGraph.graphInvalidation(from: isInvalidating ? nil : value) + viewGraph?.graphInvalidation(from: isInvalidating ? nil : value) } } @@ -92,7 +100,7 @@ package final class PreferenceBridge { ) { combiner in combiner.addChild(keys: keys, values: values) } - viewGraph.graphInvalidation(from: combiner.identifier) + viewGraph?.graphInvalidation(from: combiner.identifier) } func removeHostValue(for keys: Attribute, isInvalidating: Bool) { @@ -112,7 +120,7 @@ package final class PreferenceBridge { hasRemoved = true } if hasRemoved { - viewGraph.graphInvalidation(from: isInvalidating ? nil : keys.identifier) + viewGraph?.graphInvalidation(from: isInvalidating ? nil : keys.identifier) } } diff --git a/Sources/OpenSwiftUICore/Data/Preference/View_Preference.swift b/Sources/OpenSwiftUICore/Data/Preference/View_Preference.swift index edb88b73..f853b3bb 100644 --- a/Sources/OpenSwiftUICore/Data/Preference/View_Preference.swift +++ b/Sources/OpenSwiftUICore/Data/Preference/View_Preference.swift @@ -22,17 +22,3 @@ extension View { modifier(_PreferenceTransformModifier(transform: callback)) } } - -extension EnvironmentValues { - private struct PreferenceBridgeKey: EnvironmentKey { - struct Value { - weak var value: PreferenceBridge? - } - static let defaultValue: Value = Value() - } - - var preferenceBridge: PreferenceBridge? { - get { self[PreferenceBridgeKey.self].value } - set { self[PreferenceBridgeKey.self] = PreferenceBridgeKey.Value(value: newValue) } - } -} diff --git a/Sources/OpenSwiftUICore/EventHandling/InputEvent/HitTesting/ContentShapeKinds.swift b/Sources/OpenSwiftUICore/EventHandling/InputEvent/HitTesting/ContentShapeKinds.swift index 588f29fa..c3e7d2dc 100644 --- a/Sources/OpenSwiftUICore/EventHandling/InputEvent/HitTesting/ContentShapeKinds.swift +++ b/Sources/OpenSwiftUICore/EventHandling/InputEvent/HitTesting/ContentShapeKinds.swift @@ -2,7 +2,7 @@ // ContentShapeKinds.swift // OpenSwiftUI // -// Audited for RELEASE_2023 +// Audited for RELEASE_2024 // Status: Complete import Foundation @@ -55,7 +55,6 @@ public struct ContentShapeKinds: OptionSet, Sendable { /// When using this kind, only the preview shape will be affected. To /// control the shape used to hit-test and start the context menu /// presentation, use the `.interaction` kind. - @available(tvOS 17.0, *) @available(macOS, unavailable) @available(watchOS, unavailable) public static let contextMenuPreview: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 2) @@ -69,10 +68,8 @@ public struct ContentShapeKinds: OptionSet, Sendable { /// This kind does not affect the `onHover` modifier. @available(macOS, unavailable) @available(watchOS, unavailable) - @available(tvOS, unavailable) public static let hoverEffect: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 3) - #if OPENSWIFTUI_SUPPORT_2023_API /// The kind for accessibility visuals and sorting. /// /// Setting a content shape with this kind causes the accessibility frame @@ -85,5 +82,4 @@ public struct ContentShapeKinds: OptionSet, Sendable { /// /// To control the shape for accessibility and hit-testing, use the `interaction` kind. public static let accessibility: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 4) - #endif } diff --git a/Sources/OpenSwiftUICore/Render/DisplayList.GraphicsRenderer.swift b/Sources/OpenSwiftUICore/Render/DisplayList.GraphicsRenderer.swift new file mode 100644 index 00000000..3e331ae1 --- /dev/null +++ b/Sources/OpenSwiftUICore/Render/DisplayList.GraphicsRenderer.swift @@ -0,0 +1,72 @@ +// +// DisplayList.GraphicsRenderer.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: Blocked by RenderBox and GraphicsContext +// ID: EFAEDE41CB8C85EF3A6A18DC05438A3C + +import Foundation + +extension DisplayList { + final package class GraphicsRenderer { + package enum PlatformViewMode { + case ignored + case unsupported + case rendered(update: Bool) + } + + private struct Cache { + var callbacks: [CallbackKey: Void /* RBDisplayListContents */] + var animators: [AnimatorKey: Void /* _DisplayList_AnyEffectAnimator */ ] + + struct CallbackKey: Hashable { + var index: DisplayList.Index.ID + var seed: DisplayList.Seed + var scale: CGFloat + } + + struct AnimatorKey: Hashable { + var index: DisplayList.Index.ID + } + } + + private var oldCache: DisplayList.GraphicsRenderer.Cache + private var newCache: DisplayList.GraphicsRenderer.Cache + var index: DisplayList.Index + var time: Time + var nextTime: Time + var stableIDs: _DisplayList_StableIdentityMap? + var inTransitionGroup: Bool + var stateHashes: [StrongHash] + package var platformViewMode: DisplayList.GraphicsRenderer.PlatformViewMode + + package init(platformViewMode: DisplayList.GraphicsRenderer.PlatformViewMode) { + fatalError("TODO") + } + + package func render(at time: Time, do body: () -> Void) { + fatalError("TODO") + } + + package func renderDisplayList(_ list: DisplayList, at time: Time, in ctx: inout GraphicsContext) { + fatalError("TODO") + } + + package func render(list: DisplayList, in ctx: inout GraphicsContext) { + fatalError("TODO") + } + + package func render(item: DisplayList.Item, in ctx: inout GraphicsContext) { + fatalError("TODO") + } + + package func drawImplicitLayer(in ctx: inout GraphicsContext, content: (inout GraphicsContext) -> Void) { + fatalError("TODO") + } + + package func renderPlatformView(_ view: AnyObject?, in ctx: GraphicsContext, size: CGSize, viewType: any Any.Type) { + fatalError("TODO") + } + } +} diff --git a/Sources/OpenSwiftUICore/Render/DisplayList.ViewRenderer.swift b/Sources/OpenSwiftUICore/Render/DisplayList.ViewRenderer.swift new file mode 100644 index 00000000..4c1465a8 --- /dev/null +++ b/Sources/OpenSwiftUICore/Render/DisplayList.ViewRenderer.swift @@ -0,0 +1,155 @@ +// +// DisplayList.ViewRenderer.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: Blocked by ViewUpdater and ViewRasterizer +// ID: 21FFA3C7D88AC65BB559906758271BFC + +import Foundation + +protocol ViewRendererBase { + var platform: DisplayList.ViewUpdater.Platform { get } + var exportedObject: AnyObject? { get } + func render(rootView: AnyObject, from list: DisplayList, time: Time, version: DisplayList.Version, maxVersion: DisplayList.Version, environment: DisplayList.ViewRenderer.Environment) -> Time + func renderAsync(to list: DisplayList, time: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time? + func destroy(rootView: AnyObject) + var viewCacheIsEmpty: Bool { get } +} + +@_spi(ForOpenSwiftUIOnly) +extension DisplayList { + final public class ViewRenderer { + package struct Environment: Equatable { + package var contentScale: CGFloat + package static let invalid = Environment(contentScale: .zero) + + package init(contentScale: CGFloat) { + self.contentScale = contentScale + } + } + + private enum State { + case none + case updating + case rasterizing + } + + let platform: DisplayList.ViewUpdater.Platform + package var configuration: _RendererConfiguration = .init() + package weak var host: ViewRendererHost? = nil + private var state: State = .none + var renderer: (any ViewRendererBase)? = nil + var configChanged: Bool = true + + package init(platform: DisplayList.ViewUpdater.Platform) { + self.platform = platform + } + + private func updateRenderer(rootView: AnyObject) -> any ViewRendererBase { + guard configChanged else { + return renderer! + } + configChanged = false + let renderStateMatchCheck = switch configuration.renderer { + case .default: state == .updating + case .rasterized: state == .rasterizing + } + if !renderStateMatchCheck { + if let renderer { + renderer.destroy(rootView: rootView) + } + renderer = nil + state = .none + } + if let renderer { + switch configuration.renderer { + case .default: break + case let .rasterized(options): + let rasterizer = renderer as! ViewRasterizer + rasterizer.options = options + rasterizer.renderer.platformViewMode = options.drawsPlatformViews ? .rendered(update: true) : .unsupported + rasterizer.host = host + } + } else { + switch configuration.renderer { + case .default: + let updater = ViewUpdater() + // TODO: ViewUpdater + renderer = updater + state = .updating + case let .rasterized(options): + let rasterizer = ViewRasterizer(platform: platform, host: host, rootView: rootView, options: options) + renderer = rasterizer + state = .rasterizing + } + } + return renderer! + } + + package func exportedObject(rootView: AnyObject) -> AnyObject? { + let renderer = updateRenderer(rootView: rootView) + return renderer.exportedObject + } + + package func render(rootView: AnyObject, from list: DisplayList, time: Time, nextTime: Time, version: DisplayList.Version, maxVersion: DisplayList.Version, environment: DisplayList.ViewRenderer.Environment) -> Time { + let renderer = updateRenderer(rootView: rootView) + let result = renderer.render(rootView: rootView, from: list, time: time, version: version, maxVersion: maxVersion, environment: environment) + let interval = min(nextTime, result) - time + let maxInterval = max(interval, configuration.minFrameInterval) + return time + maxInterval + } + + package func renderAsync(to list: DisplayList, time: Time, nextTime: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time? { + guard !configChanged, let renderer else { + return nil + } + let result = renderer.renderAsync(to: list, time: time, targetTimestamp: targetTimestamp, version: version, maxVersion: maxVersion) + if let result { + let interval = min(nextTime, result) - time + let maxInterval = max(interval, configuration.minFrameInterval) + return time + maxInterval + } else { + return nil + } + } + + package var viewCacheIsEmpty: Bool { + renderer?.viewCacheIsEmpty ?? true + } + } + + private final class ViewRasterizer: ViewRendererBase { + let platform: DisplayList.ViewUpdater.Platform + weak var host: ViewRendererHost? + var drawingView: AnyObject? + var options: _RendererConfiguration.RasterizationOptions + let renderer: DisplayList.GraphicsRenderer + var seed: DisplayList.Seed + var lastContentsScale: CGFloat + + init(platform: DisplayList.ViewUpdater.Platform, host: ViewRendererHost?, rootView: AnyObject, options: _RendererConfiguration.RasterizationOptions) { + fatalError() + } + + var exportedObject: AnyObject? { + platform.definition.getRBLayer(drawingView: drawingView!) + } + + func render(rootView: AnyObject, from list: DisplayList, time: Time, version: DisplayList.Version, maxVersion: DisplayList.Version, environment: DisplayList.ViewRenderer.Environment) -> Time { + fatalError("TODO") + } + + func renderAsync(to list: DisplayList, time: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time? { + fatalError("TODO") + } + + func destroy(rootView: AnyObject) { + fatalError("TODO") + } + + var viewCacheIsEmpty: Bool { + fatalError("TODO") + } + } +} diff --git a/Sources/OpenSwiftUICore/Render/DisplayList.ViewUpdater.swift b/Sources/OpenSwiftUICore/Render/DisplayList.ViewUpdater.swift new file mode 100644 index 00000000..5bc31ddd --- /dev/null +++ b/Sources/OpenSwiftUICore/Render/DisplayList.ViewUpdater.swift @@ -0,0 +1,38 @@ +// +// DisplayList.ViewUpdater.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: WIP + +extension DisplayList { + // FIXME + final package class ViewUpdater: ViewRendererBase { + init() { + fatalError() + } + + init(platform: Platform, exportedObject: AnyObject? = nil, viewCacheIsEmpty: Bool) { + self.platform = platform + self.exportedObject = exportedObject + self.viewCacheIsEmpty = viewCacheIsEmpty + } + + var platform: Platform + + var exportedObject: AnyObject? + + func render(rootView: AnyObject, from list: DisplayList, time: Time, version: DisplayList.Version, maxVersion: DisplayList.Version, environment: DisplayList.ViewRenderer.Environment) -> Time { + .zero + } + + func renderAsync(to list: DisplayList, time: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time? { + nil + } + + func destroy(rootView: AnyObject) { + } + + var viewCacheIsEmpty: Bool + } +} diff --git a/Sources/OpenSwiftUICore/Render/GraphicsContext.swift b/Sources/OpenSwiftUICore/Render/GraphicsContext.swift new file mode 100644 index 00000000..4f0af917 --- /dev/null +++ b/Sources/OpenSwiftUICore/Render/GraphicsContext.swift @@ -0,0 +1,16 @@ +// +// GraphicsContext.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: WIP + +@frozen +public struct GraphicsContext { + @usableFromInline + final class Storage { + + } + + var storage: Storage +} diff --git a/Sources/OpenSwiftUICore/Render/PlatformViewDefinition.swift b/Sources/OpenSwiftUICore/Render/PlatformViewDefinition.swift new file mode 100644 index 00000000..326367d0 --- /dev/null +++ b/Sources/OpenSwiftUICore/Render/PlatformViewDefinition.swift @@ -0,0 +1,93 @@ +// +// PlatformViewDefinition.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: Blocked by PlatformDrawable and GraphicsContext + +internal import COpenSwiftUICore +#if canImport(Darwin) +import QuartzCore +#endif + +@_spi(DisplayList_ViewSystem) +open class PlatformViewDefinition: @unchecked Sendable { + public struct System: Hashable, Sendable { + public static let uiView = PlatformViewDefinition.System(base: .uiView) + public static let nsView = PlatformViewDefinition.System(base: .nsView) + var base: ViewSystem + } + + public enum ViewKind: Sendable { + case inherited + case color + case image + case shape + case shadow + case backdrop + case chameleonColor + case drawing + case compositing + case geometry + case projection + case affine3D + case mask + case platformView + case platformGroup + case platformLayer + case platformEffect + + public var isContainer: Bool { + switch self { + case .inherited, .compositing, .geometry, .projection, .affine3D, .mask, .platformGroup, .platformEffect: + return true + case .color, .image, .shape, .shadow, .backdrop, .chameleonColor, .drawing, .platformView, .platformLayer: + return false + } + } + } + + open class var system: PlatformViewDefinition.System { .init(base: ._2) } + open class func makeView(kind: PlatformViewDefinition.ViewKind) -> AnyObject { fatalError() } + #if canImport(Darwin) + open class func makeLayerView(type: CALayer.Type, kind: PlatformViewDefinition.ViewKind) -> AnyObject { fatalError() } + #endif + open class func makePlatformView(view: AnyObject, kind: PlatformViewDefinition.ViewKind) { fatalError() } + // open class func makeDrawingView(options: PlatformDrawableOptions) -> any PlatformDrawable { fatalError() } + open class func setPath(_ path: Path, shapeView: AnyObject) { fatalError() } + open class func setProjectionTransform(_ transform: ProjectionTransform, projectionView: AnyObject) { fatalError() } + open class func getRBLayer(drawingView: AnyObject) -> AnyObject? { fatalError() } + open class func setIgnoresEvents(_ state: Bool, of view: AnyObject) { fatalError() } + open class func setAllowsWindowActivationEvents(_ value: Bool?, for view: AnyObject) { fatalError() } + open class func setHitTestsAsOpaque(_ value: Bool, for view: AnyObject) { fatalError() } +} + +extension DisplayList.ViewUpdater { + package struct Platform { + let rawValue: UInt + } +} + +extension DisplayList.ViewUpdater.Platform { + package init(definition: PlatformViewDefinition.Type) { + self.init(rawValue: UInt(bitPattern: ObjectIdentifier(definition)) | UInt(definition.system.base.rawValue)) + } + + @inline(__always) + var definition: PlatformViewDefinition.Type { + return unsafeBitCast(rawValue & ~3, to: PlatformViewDefinition.Type.self) + } +} + +extension DisplayList.GraphicsRenderer { + #if canImport(Darwin) + final package func drawPlatformLayer(_ layer: CALayer, in ctx: GraphicsContext, size: CGSize, update: Bool) { + if update { + layer.bounds = CGRect(origin: .zero, size: size) + layer.layoutIfNeeded() + } + fatalError("Blocked by GraphicsContext") + // ctx.drawLayer + } + #endif +} diff --git a/Sources/OpenSwiftUICore/Render/RendererConfiguration.swift b/Sources/OpenSwiftUICore/Render/RendererConfiguration.swift new file mode 100644 index 00000000..adc72148 --- /dev/null +++ b/Sources/OpenSwiftUICore/Render/RendererConfiguration.swift @@ -0,0 +1,44 @@ +// +// RendererConfiguration.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: Complete + +public struct _RendererConfiguration { + public enum Renderer { + case `default` + indirect case rasterized(_ options: _RendererConfiguration.RasterizationOptions = .init()) + } + + public var renderer: _RendererConfiguration.Renderer + public var minFrameInterval: Double + public init(renderer: _RendererConfiguration.Renderer = .default) { + self.renderer = renderer + self.minFrameInterval = .zero + } + + public static func rasterized(_ options: _RendererConfiguration.RasterizationOptions = .init()) -> _RendererConfiguration { + _RendererConfiguration(renderer: .rasterized(options)) + } + + public struct RasterizationOptions { + public var colorMode: ColorRenderingMode = .nonLinear + public var rbColorMode: Int32? = nil + public var rendersAsynchronously: Bool = false + public var isOpaque: Bool = true + public var drawsPlatformViews: Bool = true + public var prefersDisplayCompositing: Bool = false + public var maxDrawableCount: Int = 3 + public init() {} + } +} + +@available(*, unavailable) +extension _RendererConfiguration: Sendable {} + +@available(*, unavailable) +extension _RendererConfiguration.Renderer: Sendable {} + +@available(*, unavailable) +extension _RendererConfiguration.RasterizationOptions: Sendable {} diff --git a/Sources/OpenSwiftUICore/View/ViewGraph.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift similarity index 95% rename from Sources/OpenSwiftUICore/View/ViewGraph.swift rename to Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift index b20f68f6..b56cbe0d 100644 --- a/Sources/OpenSwiftUICore/View/ViewGraph.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift @@ -144,11 +144,11 @@ package final class ViewGraph: GraphHost { sizeThatFitsObserver?.callback(oldCachedSizeThatFits, self.cachedSizeThatFits) } if !requestedOutputs.isEmpty { - delegate?.outputsDidChange(outputs: updatedOutputs) +// delegate?.outputsDidChange(outputs: updatedOutputs) } if needsFocusUpdate { needsFocusUpdate = false - delegate?.focusDidChange() +// delegate?.focusDidChange() } } else { fatalError("TODO") @@ -323,26 +323,26 @@ extension ViewGraph { self.rawValue = rawValue } - static var _0: Outputs { .init(rawValue: 1 << 0) } - static var _1: Outputs { .init(rawValue: 1 << 1) } - static var _2: Outputs { .init(rawValue: 1 << 2) } - static var _3: Outputs { .init(rawValue: 1 << 3) } - static var layout: Outputs { .init(rawValue: 1 << 4) } + 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] + // FIXME fileprivate func addRequestedPreferences(to inputs: inout _ViewInputs) { inputs.preferences.add(HostPreferencesKey.self) - if contains(._0) { + if contains(.displayList) { inputs.preferences.add(DisplayList.Key.self) } - if contains(._2) { + if contains(.viewResponders) { // inputs.preferences.add(ViewRespondersKey.self) } - if contains(._1) { + if contains(.platformItemList) { // inputs.preferences.add(PlatformItemList.Key.self) } - if contains(._3) { -// inputs.preferences.add(AccessibilityNodesKe.self) - } } } } diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraphDelegate.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraphDelegate.swift new file mode 100644 index 00000000..e37075d6 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraphDelegate.swift @@ -0,0 +1,27 @@ +// +// ViewGraphDelegate.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: Complete + +package protocol ViewGraphDelegate: GraphDelegate { + func `as`(_ type: T.Type) -> T? + func modifyViewInputs(_ inputs: inout _ViewInputs) + func updateViewGraph(body: (ViewGraph) -> T) -> T + func rootTransform() -> ViewTransform +} + +@_spi(ForOpenSwiftUIOnly) +extension ViewGraphDelegate { + package func `as`(_ type: T.Type) -> T? { nil } + package func modifyViewInputs(_ inputs: inout _ViewInputs) {} + + public func updateGraph(body: (GraphHost) -> T) -> T { + updateViewGraph { body($0) } + } + + package func rootTransform() -> ViewTransform { + ViewTransform() + } +} diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift new file mode 100644 index 00000000..ecbf339b --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift @@ -0,0 +1,26 @@ +// +// ViewGraphFeature.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: Complete + +package protocol ViewGraphFeature { + mutating func modifyViewInputs(inputs: inout _ViewInputs, graph: ViewGraph) + mutating func modifyViewOutputs(outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) + mutating func uninstantiate(graph: ViewGraph) + mutating func isHiddenForReuseDidChange(graph: ViewGraph) + mutating func allowsAsyncUpdate(graph: ViewGraph) -> Bool? + mutating func needsUpdate(graph: ViewGraph) -> Bool + mutating func update(graph: ViewGraph) +} + +extension ViewGraphFeature { + package mutating func modifyViewInputs(inputs: inout _ViewInputs, graph: ViewGraph) {} + package mutating func modifyViewOutputs(outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) {} + package mutating func uninstantiate(graph: ViewGraph) {} + package mutating func isHiddenForReuseDidChange(graph: ViewGraph) {} + package mutating func allowsAsyncUpdate(graph: ViewGraph) -> Bool? { true } + package mutating func needsUpdate(graph: ViewGraph) -> Bool { false } + package mutating func update(graph: ViewGraph) {} +} diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewRendererHost.swift b/Sources/OpenSwiftUICore/View/Graph/ViewRendererHost.swift new file mode 100644 index 00000000..c4efad8c --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Graph/ViewRendererHost.swift @@ -0,0 +1,317 @@ +// +// ViewRendererHost.swift +// OpenSwiftUICore +// +// Audited for RELEASE_2024 +// Status: WIP +// ID: 76C8A4B3FC8EE0F99045B3425CD62255 + +import Foundation +internal import OpenGraphShims + +// MARK: - ViewRendererHostProperties + +package struct ViewRendererHostProperties: OptionSet { + package let rawValue: UInt16 + package init(rawValue: UInt16) { + self.rawValue = rawValue + } + package static let rootView: ViewRendererHostProperties = .init(rawValue: 1 << 0) + package static let environment: ViewRendererHostProperties = .init(rawValue: 1 << 1) + package static let focusedValues: ViewRendererHostProperties = .init(rawValue: 1 << 2) + package static let transform: ViewRendererHostProperties = .init(rawValue: 1 << 3) + package static let size: ViewRendererHostProperties = .init(rawValue: 1 << 4) + package static let safeArea: ViewRendererHostProperties = .init(rawValue: 1 << 5) + package static let scrollableContainerSize: ViewRendererHostProperties = .init(rawValue: 1 << 6) + package static let focusStore: ViewRendererHostProperties = .init(rawValue: 1 << 7) + package static let accessibilityFocusStore: ViewRendererHostProperties = .init(rawValue: 1 << 8) + package static let focusedItem: ViewRendererHostProperties = .init(rawValue: 1 << 9) + package static let accessibilityFocus: ViewRendererHostProperties = .init(rawValue: 1 << 10) + package static let all: ViewRendererHostProperties = [.rootView, .environment, .focusedValues, .transform, .size, .safeArea, .scrollableContainerSize, .focusStore, .accessibilityFocusStore, .focusedItem, .accessibilityFocus] +} + +// MARK: - ViewRenderingPhase + +package enum ViewRenderingPhase { + case none + case rendering + case renderingAsync +} + +@available(*, unavailable) +extension ViewRenderingPhase: Sendable {} + +// MARK: - ViewRendererHost + +package protocol ViewRendererHost: ViewGraphDelegate { + var viewGraph: ViewGraph { get } + var currentTimestamp: Time { get set } + var propertiesNeedingUpdate: ViewRendererHostProperties { get set } + var renderingPhase: ViewRenderingPhase { get set } + var externalUpdateCount: Int { get set } + func updateRootView() + func updateEnvironment() + func updateFocusedItem() + func updateFocusedValues() + func updateTransform() + func updateSize() + func updateSafeArea() + func updateScrollableContainerSize() + func updateFocusStore() + func updateAccessibilityFocus() + func updateAccessibilityFocusStore() + func updateAccessibilityEnvironment() + func requestUpdate(after delay: Double) + func renderDisplayList(_ list: DisplayList, asynchronously: Bool, time: Time, nextTime: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time + func didRender() +} + +extension ViewRendererHost { + package var isRendering: Bool { + renderingPhase != .none + } + + package func initializeViewGraph() { + // viewGraph.delegate = self + // TODO: Signpost related + } + + package func invalidate() { + // viewGraph.delegate = nil + // TODO: Signpost.viewHost + fatalError("TODO") + } + +// package static func makeRootView(_ view: V) -> ModifiedContent where V: View + package static func makeRootView(_ view: V) -> some View { + view/*.modifier(HitTestBindingModifier())*/ + } + + @_spi(ForOpenSwiftUIOnly) + public func updateViewGraph(body: (ViewGraph) -> T) -> T { + // FIXME + Update.dispatchImmediately { + OGGraph.withoutUpdate { + updateGraph() + return body(viewGraph) + } + } + } + + @_spi(ForOpenSwiftUIOnly) + public func graphDidChange() { + fatalError("TODO") + } + + package func didRender() {} + + @_spi(ForOpenSwiftUIOnly) + public func preferencesDidChange() { + fatalError("TODO") + } + + package func invalidateProperties(_ props: ViewRendererHostProperties, mayDeferUpdate: Bool = true) { + // FIXME +// Update.locked { +// guard !propertiesNeedingUpdate.contains(properties) else { +// return +// } +// propertiesNeedingUpdate.insert(properties) +// viewGraph.setNeedsUpdate(mayDeferUpdate: mayDeferUpdate) +// requestUpdate(after: .zero) +// } + } + + package func updateGraph() { + // FIXME + let properties = propertiesNeedingUpdate + // addImplicitPropertiesNeedingUpdate(to: &properties) + guard !properties.isEmpty else { return } + Update.syncMain { + func update(_ property: ViewRendererHostProperties, body: () -> Void) { + if properties.contains(property) { + propertiesNeedingUpdate.remove(property) + } + body() + } + update(.rootView) { updateRootView() } + // TODO: + } + } + + package func updateTransform() { + fatalError("TODO") + } + + package func render(interval: Double = 0, updateDisplayList: Bool = true, targetTimestamp: Time? = nil) { + // FIXME + Update.dispatchImmediately { + guard !isRendering else { + return + } + let update = { [self] in + currentTimestamp += interval + let time = currentTimestamp + viewGraph.flushTransactions() + // Signpost.renderUpdate + // TODO + viewGraph.updateOutputs(at: time) + } + if Signpost.render.isEnabled { + // TODO: Signpost related + update() + } else { + update() + } + } + } + + package func renderAsync(interval: Double = 0, targetTimestamp: Time?) -> Time? { + fatalError("TODO") + } + + package func advanceTimeForTest(interval: Double) { + fatalError("TODO") + } + + @_spi(Private) + public func preferenceValue(_ key: K.Type) -> K.Value where K: HostPreferenceKey { + fatalError("TODO") + } + + package func idealSize() -> CGSize { fatalError("TODO") } + + package func sizeThatFits(_ proposal: _ProposedSize) -> CGSize { + fatalError("TODO") + } + package func explicitAlignment(of guide: HorizontalAlignment, at size: CGSize) -> CGFloat? { + fatalError("TODO") + } + package func explicitAlignment(of guide: VerticalAlignment, at size: CGSize) -> CGFloat? { + fatalError("TODO") + } + package func alignment(of guide: HorizontalAlignment, at size: CGSize) -> CGFloat { + fatalError("TODO") + } + package func alignment(of guide: VerticalAlignment, at size: CGSize) -> CGFloat { + fatalError("TODO") + } + package var centersRootView: Bool { + get { viewGraph.centersRootView } + set { viewGraph.centersRootView = newValue } + } + +// package var responderNode: ResponderNode? { +// fatalError("TODO") +// } + + package var isRootHost: Bool { + fatalError("TODO") + } + private var enclosingHosts: [ViewRendererHost] { fatalError("TODO") } + package func performExternalUpdate(_ update: () -> Void) { fatalError("TODO") } + package func updateFocusedItem() {} + package func updateFocusedValues() {} + package func updateFocusStore() {} + package func updateAccessibilityFocus() {} + package func updateAccessibilityFocusStore() {} + package func updateAccessibilityEnvironment() {} +} + +// MARK: - ViewRendererHost + Gesture [TODO] + +//package let hostingViewCoordinateSpace: CoordinateSpace.ID + +//extension ViewRendererHost { +// package var nextGestureUpdateTime: Time { +// get +// } +// package func sendEvents(_ events: [EventID : any EventType], rootNode: ResponderNode, at time: Time) -> GesturePhase +// package func resetEvents() +// package func gestureCategory() -> GestureCategory? +// package func setInheritedPhase(_ phase: _GestureInputs.InheritedPhase) +//} + +//extension ViewRendererHost { +// package func sendTestEvents(_ events: [EventID : any EventType]) +// package func resetTestEvents() +//} + +// MARK: - ViewGraph + viewRendererHost + +extension ViewGraph { + package static var viewRendererHost: (any ViewRendererHost)? { + ViewGraph.current.delegate as? ViewRendererHost + } +} + +// MARK: - EnvironmentValues + PreferenceBridge + +extension EnvironmentValues { + private struct PreferenceBridgeKey: EnvironmentKey { + struct Value { + weak var value: PreferenceBridge? + } + static let defaultValue: Value = Value() + } + + package var preferenceBridge: PreferenceBridge? { + get { self[PreferenceBridgeKey.self].value } + set { self[PreferenceBridgeKey.self].value = newValue } + } +} + +// MARK: - ViewRendererHost + rootContentPath [TODO] + +extension ViewRendererHost { + package func rootContentPath(kind: ContentShapeKinds) -> Path { + fatalError("TODO") + } +} + +// MARK: - ViewRendererHost + Graph [TODO] + +extension ViewRendererHost { + package func startProfiling() { + Graph.startProfiling(viewGraph.graph) + } + + package func stopProfiling() { + Graph.stopProfiling(viewGraph.graph) + } + + package func resetProfile() { + // Graph.resetProfile(viewGraph.graph) + } + + package func archiveJSON(name: String? = nil) { + // viewGraph.graph.archiveJSON(name: name) + } +} + +// MARK: - EmptyViewRendererHost + +final package class EmptyViewRendererHost: ViewRendererHost { + package let viewGraph: ViewGraph + package var propertiesNeedingUpdate: ViewRendererHostProperties = [] + package var renderingPhase: ViewRenderingPhase = .none + package var externalUpdateCount: Int = .zero + package var currentTimestamp: Time = .zero + package init(environment: EnvironmentValues = EnvironmentValues()) { + Update.begin() + viewGraph = ViewGraph(rootViewType: EmptyView.self, requestedOutputs: []) + viewGraph.setEnvironment(environment) + initializeViewGraph() + Update.end() + } + package func requestUpdate(after delay: Double) {} + package func updateRootView() {} + package func updateEnvironment() {} + package func updateSize() {} + package func updateSafeArea() {} + package func updateScrollableContainerSize() {} + package func renderDisplayList(_ list: DisplayList, asynchronously: Bool, time: Time, nextTime: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time { + .infinity + } + package func forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {} +} diff --git a/Sources/OpenSwiftUICore/View/ViewGraphDelegate.swift b/Sources/OpenSwiftUICore/View/ViewGraphDelegate.swift deleted file mode 100644 index 7a50732c..00000000 --- a/Sources/OpenSwiftUICore/View/ViewGraphDelegate.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ViewGraphDelegate.swift -// OpenSwiftUI -// -// Audited for RELEASE_2021 -// Status: Complete - -package protocol ViewGraphDelegate: GraphDelegate { - func modifyViewInputs(_ inputs: inout _ViewInputs) - func updateViewGraph(body: (ViewGraph) -> Value) -> Value - func outputsDidChange(outputs: ViewGraph.Outputs) -> () - func focusDidChange() - func rootTransform() -> ViewTransform -} - -extension ViewGraphDelegate { - public func updateGraph(body: (GraphHost) -> V) -> V { - updateViewGraph(body: body) - } - - public func rootTransform() -> ViewTransform { - ViewTransform() - } -} diff --git a/Sources/OpenSwiftUICore/View/ViewRendererHost.swift b/Sources/OpenSwiftUICore/View/ViewRendererHost.swift deleted file mode 100644 index ef91bee2..00000000 --- a/Sources/OpenSwiftUICore/View/ViewRendererHost.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// ViewRendererHost.swift -// OpenSwiftUI -// -// Audited for RELEASE_2021 -// Status: WIP - -internal import OpenGraphShims - -package protocol ViewRendererHost: ViewGraphDelegate { - var viewGraph: ViewGraph { get } - var currentTimestamp: Time { get set } - var propertiesNeedingUpdate: ViewRendererHostProperties { get set } - func addImplicitPropertiesNeedingUpdate(to properties: inout ViewRendererHostProperties) - var isRendering: Bool { get set } - func updateRootView() - func requestUpdate(after: Double) -} - -// MARK: - ViewRendererHost's default implementation for ViewGraphDelegate - -extension ViewRendererHost { - package func updateViewGraph(body: (ViewGraph) -> Value) -> Value { - Update.dispatchImmediately { - OGGraph.withoutUpdate { - updateGraph() - return body(viewGraph) - } - } - } -} - -extension ViewRendererHost { - package func initializeViewGraph() { - viewGraph.delegate = self - // TODO: Signpost related - } - - package func invalidateProperties(_ properties: ViewRendererHostProperties, mayDeferUpdate: Bool) { - Update.locked { - guard !propertiesNeedingUpdate.contains(properties) else { - return - } - propertiesNeedingUpdate.insert(properties) - viewGraph.setNeedsUpdate(mayDeferUpdate: mayDeferUpdate) - requestUpdate(after: .zero) - } - } - - package func startProfiling() { - OGGraph.startProfiling(viewGraph.graph) - } - - package func stopProfiling() { - OGGraph.stopProfiling(viewGraph.graph) - } - - package func render(interval: Double, updateDisplayList: Bool = true) { - Update.dispatchImmediately { - guard !isRendering else { - return - } - let update = { [self] in - currentTimestamp += interval - let time = currentTimestamp - viewGraph.flushTransactions() - // Signpost.renderUpdate - // TODO - viewGraph.updateOutputs(at: time) - } - if Signpost.render.isEnabled { - // TODO: Signpost related - update() - } else { - update() - } - } - } - - package static func makeRootView(_ view: V) -> some View { - view/*.modifier(HitTestBindingModifier())*/ - } - - package func updateGraph() { - var properties = propertiesNeedingUpdate - addImplicitPropertiesNeedingUpdate(to: &properties) - guard !properties.isEmpty else { return } - Update.syncMain { - func update(_ property: ViewRendererHostProperties, body: () -> Void) { - if properties.contains(property) { - propertiesNeedingUpdate.remove(property) - } - body() - } - update(.rootView) { updateRootView() } - // TODO: - } - } - - package func invalidate() { - viewGraph.delegate = nil - // TODO: Signpost.viewHost - } -} - -package struct ViewRendererHostProperties: OptionSet { - package let rawValue: UInt16 - - package init(rawValue: UInt16) { - self.rawValue = rawValue - } - - package static var rootView: ViewRendererHostProperties { ViewRendererHostProperties(rawValue: 1 << 0) } -}