diff --git a/Sources/OpenSwiftUICore/Data/Protobuf/ProtobufEncoder.swift b/Sources/OpenSwiftUICore/Data/Protobuf/ProtobufEncoder.swift index f0f0fc340..09d08112c 100644 --- a/Sources/OpenSwiftUICore/Data/Protobuf/ProtobufEncoder.swift +++ b/Sources/OpenSwiftUICore/Data/Protobuf/ProtobufEncoder.swift @@ -35,6 +35,10 @@ package struct ProtobufEncoder { /// User-defined information. package var userInfo: [CodingUserInfoKey: Any] = [:] + + package var archiveHost: (any AnyArchivedViewHost)? { + userInfo[ArchivedViewCore.archivedViewHostKey] as? any AnyArchivedViewHost + } /// Takes the encoded data. /// diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift index 42e0a0449..f91a6fd59 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList_StableIdentity.swift @@ -27,7 +27,7 @@ package struct _DisplayList_StableIdentityMap { set { map[index] = newValue } } package mutating func formUnion(_ other: _DisplayList_StableIdentityMap) { - map.merge(other.map) { old, _ in old } + map.merge(other.map.lazy.map { ($0.key, $0.value) }) { old, _ in old } } } @@ -38,11 +38,25 @@ final package class _DisplayList_StableIdentityRoot { package init() {} package final subscript(index: _DisplayList_Identity) -> _DisplayList_StableIdentity? { - if let map { - return map[index] - } else { - _openSwiftUIUnimplementedFailure() + // TBA + if map == nil { + var map = _DisplayList_StableIdentityMap() + var scopeIndex = scopes.startIndex + while scopeIndex < scopes.endIndex { + guard let value = scopes[scopeIndex].value else { + let lastIndex = scopes.index(before: scopes.endIndex) + if scopeIndex != lastIndex { + scopes.swapAt(scopeIndex, lastIndex) + } + scopes.removeLast() + continue + } + map.formUnion(value.map) + scopes.formIndex(after: &scopeIndex) + } + self.map = map } + return map?[index] } } diff --git a/Sources/OpenSwiftUICore/Render/PlatformDrawable.swift b/Sources/OpenSwiftUICore/Render/PlatformDrawable.swift index a5541a8e2..7c8875a51 100644 --- a/Sources/OpenSwiftUICore/Render/PlatformDrawable.swift +++ b/Sources/OpenSwiftUICore/Render/PlatformDrawable.swift @@ -57,16 +57,18 @@ public struct PlatformDrawableContent: @unchecked Sendable { // MARK: - PlatformDrawableContent.State public struct State { - package var mode: DisplayList.GraphicsRenderer.PlatformViewMode = .unsupported + package var mode: DisplayList.GraphicsRenderer.PlatformViewMode package var _renderer: DisplayList.GraphicsRenderer? package init() { - _openSwiftUIEmptyStub() + mode = .unsupported + _renderer = nil } package init(platformViewMode: DisplayList.GraphicsRenderer.PlatformViewMode) { mode = platformViewMode + _renderer = nil } package mutating func renderer() -> DisplayList.GraphicsRenderer { diff --git a/Sources/OpenSwiftUICore/Render/PlatformViewFactory.swift b/Sources/OpenSwiftUICore/Render/PlatformViewFactory.swift index 623210aec..68dff648d 100644 --- a/Sources/OpenSwiftUICore/Render/PlatformViewFactory.swift +++ b/Sources/OpenSwiftUICore/Render/PlatformViewFactory.swift @@ -3,6 +3,7 @@ // OpenSwiftUICore // // Audited for 6.5.4 +// Status: Blocked by RB // ID: 7A45621CE16223183E03CAC88E8C5E60 (SwiftUICore) package import Foundation @@ -28,7 +29,7 @@ extension AnyViewFactory where Self: View { } } -// MARK: - PlatformLayerFactory [TODO] +// MARK: - PlatformLayerFactory package protocol PlatformLayerFactory: AnyViewFactory { var platformLayerType: CALayer.Type { get } @@ -48,12 +49,16 @@ extension PlatformLayerFactory { size: CGSize, renderer: DisplayList.GraphicsRenderer ) { - // TODO: render.platformViewMode - _openSwiftUIUnimplementedFailure() + if case .ignored = renderer.platformViewMode { + return + } + Log.externalWarning("Unable to render flattened version of \(viewType).") + // TODO: GraphicsContext + ctx.renderMissingPlatformView(size: size) } } -// MARK: - PlatformViewFactory [TODO] +// MARK: - PlatformViewFactory package protocol PlatformViewFactory: AnyViewFactory { func makePlatformView() -> AnyObject? @@ -75,8 +80,12 @@ extension PlatformViewFactory { size: CGSize, renderer: DisplayList.GraphicsRenderer ) { - // TODO: render.platformViewMode - _openSwiftUIUnimplementedFailure() + if case .ignored = renderer.platformViewMode { + return + } + Log.externalWarning("Unable to render flattened version of \(viewType).") + // TODO: GraphicsContext + ctx.renderMissingPlatformView(size: size) } package var features: DisplayList.Features { @@ -118,8 +127,8 @@ extension PlatformGroupFactory { size: CGSize, renderer: DisplayList.GraphicsRenderer ) { - // TODO: RB - _openSwiftUIUnimplementedFailure() + var ctx = ctx + renderer.render(list: list, in: &ctx) } package var features: DisplayList.Features { @@ -152,8 +161,15 @@ extension RendererLeafView where Self: _DisplayList_ViewFactory { package struct ViewDecoders { package typealias DecodableViewFactory = Decodable & AnyViewFactory + fileprivate struct DecodableFactoryDecoder { + var decode: (Data, ProtobufDecoder) throws -> any AnyViewFactory + } + @AtomicBox - private static var shared: ViewDecoders = .init() + fileprivate static var shared: ViewDecoders = .init() + + @AtomicBox + fileprivate static var factoryDecoders: [String: DecodableFactoryDecoder] = [:] package static func registerDecodableFactoryType( _ factoryType: T.Type, @@ -170,16 +186,22 @@ package struct ViewDecoders { forID id: String ) where T: Decodable, T: AnyViewFactory { shared.decodableFactoryTypes[id] = factoryType - _openSwiftUIUnimplementedFailure() + factoryDecoders[id] = DecodableFactoryDecoder { data, decoder in + try decoder.value(fromBinaryPlist: data, type: T.self) + } } package static func registerStandard(_ body: () -> Void) { body() } - private var decodableFactoryTypes: [String: any DecodableViewFactory.Type] = [:] + fileprivate static func factoryDecoder(forID id: String) -> DecodableFactoryDecoder? { + factoryDecoders[id] + } + + fileprivate var decodableFactoryTypes: [String: any DecodableViewFactory.Type] = [:] - private var hasRegisteredStandardDecoders = false + fileprivate var hasRegisteredStandardDecoders = false } // MARK: - EmptyViewFactory [TODO] @@ -213,9 +235,10 @@ extension EmptyViewFactory: PlatformLayerFactory { GraphicsContext.renderingTo( cgContext: ctx, environment: .init(), - deviceScale: .zero - ) { _ in - _openSwiftUIUnimplementedFailure() + deviceScale: nil + ) { context in + // TODO: RB setup + context.renderMissingPlatformView(size: bounds.size) } } @@ -239,7 +262,8 @@ extension EmptyViewFactory: PlatformLayerFactory { size: CGSize, renderer: DisplayList.GraphicsRenderer ) { - _openSwiftUIUnimplementedFailure() + // TODO: RB + ctx.renderMissingPlatformView(size: size) } } @@ -261,7 +285,8 @@ extension EmptyViewFactory: PlatformViewFactory { size: CGSize, renderer: DisplayList.GraphicsRenderer ) { - _openSwiftUIUnimplementedFailure() + // TODO: RB + ctx.renderMissingPlatformView(size: size) } } @@ -279,7 +304,7 @@ extension EmptyViewFactory: PlatformGroupFactory { } package func platformGroupContainer(_ view: AnyObject) -> AnyObject { - _openSwiftUIUnimplementedFailure() + view } package func renderPlatformGroup( @@ -288,7 +313,8 @@ extension EmptyViewFactory: PlatformGroupFactory { size: CGSize, renderer: DisplayList.GraphicsRenderer ) { - _openSwiftUIUnimplementedFailure() + // TODO: RB + ctx.renderMissingPlatformView(size: size) } } @@ -309,14 +335,36 @@ struct CodableViewFactory: ProtobufMessage { } init(from decoder: inout ProtobufDecoder) throws { - _openSwiftUIUnimplementedFailure() + var id: String? + var data = Data() + while let field = try decoder.nextField() { + switch field.tag { + case 1: + id = try decoder.stringField(field) + case 2: + data = try decoder.messageField(field) + default: + try decoder.skipField(field) + } + } + guard let id else { + factory = EmptyViewFactory() + return + } + guard let factoryDecoder = ViewDecoders.factoryDecoder(forID: id) else { + throw Error.missingView(id) + } + factory = try factoryDecoder.decode(data, decoder) } func encode(to encoder: inout ProtobufEncoder) throws { guard let encoding = factory.encoding() else { - // TODO: encoder.archiveHost - _openSwiftUIUnimplementedFailure() + encoder.archiveHost?.failedToEncodeView(type: factory.viewType) + return } - _openSwiftUIUnimplementedFailure() + try encoder.stringField(1, encoding.id) + let data = try encoder.binaryPlistData(for: encoding.data) + try encoder.messageField(2, data) } + } diff --git a/Sources/OpenSwiftUICore/View/Archive/ArchivedViewCore.swift b/Sources/OpenSwiftUICore/View/Archive/ArchivedViewCore.swift new file mode 100644 index 000000000..d981c939f --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Archive/ArchivedViewCore.swift @@ -0,0 +1,91 @@ +// +// ArchivedViewCore.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete +// ID: D016D0E3769EA092A45BCB1F998387D5 (SwiftUICore) + +package import CoreText_Private +package import Foundation + +// MARK: - ArchivedViewCore + +package struct ArchivedViewCore { + package static let majorVersion: Int = 13 + package static let archivedViewHostKey = CodingUserInfoKey(rawValue: "org.OpenSwiftUIProject.OpenSwiftUI.ArchivedViewHost")! + package static let archiveOptionsKey = CodingUserInfoKey(rawValue: "org.OpenSwiftUIProject.OpenSwiftUI.ArchivedViewInput")! + package static let rbEncoderSetKey = CodingUserInfoKey(rawValue: "org.OpenSwiftUIProject.OpenSwiftUI.RBEncoderSet")! + + package struct Metadata: Codable { + package var majorVersion: Int + package var stateAttachments: [Int] + package var stableIDsAttachment: Int? + package var dataAttachment: Int? + package var archiveID: UUID + package var deploymentVersion: ArchivedViewInput.DeploymentVersion + + package var preferredBundleLanguage: String? = Bundle.main.preferredLocalizations.first + @CodableRawRepresentable + package var preferredCompositionLanguage: CTCompositionLanguage = OpenSwiftUI_CTParagraphStyleGetCompositionLanguageForLanguage(nil) + + package init( + majorVersion: Int = ArchivedViewCore.majorVersion, + stateAttachments: [Int] = [], + stableIDAttachment: Int? = nil, + dataAttachment: Int? = nil, + archiveID: UUID = .init(), + deploymentVersion: ArchivedViewInput.DeploymentVersion = .current + ) { + self.majorVersion = majorVersion + self.stateAttachments = stateAttachments + self.stableIDsAttachment = stableIDAttachment + self.dataAttachment = dataAttachment + self.archiveID = archiveID + self.deploymentVersion = deploymentVersion + } + + package func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(majorVersion, forKey: .majorVersion) + try container.encode(stateAttachments, forKey: .stateAttachments) + try container.encodeIfPresent(stableIDsAttachment, forKey: .stableIDsAttachment) + try container.encodeIfPresent(dataAttachment, forKey: .dataAttachment) + try container.encode(archiveID, forKey: .archiveID) + try container.encode(deploymentVersion, forKey: .deploymentVersion) + try container.encodeIfPresent(preferredBundleLanguage, forKey: .preferredBundleLanguage) + try container.encode(_preferredCompositionLanguage, forKey: .preferredCompositionLanguage) + } + + private enum CodingKeys: String, CodingKey { + case majorVersion + case stateAttachments + case stableIDsAttachment + case dataAttachment + case archiveID + case deploymentVersion + case preferredBundleLanguage + case preferredCompositionLanguage + } + } +} + +extension ArchivedViewCore.Metadata { + package init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + majorVersion = try container.decode(Int.self, forKey: .majorVersion) + stateAttachments = try container.decode([Int].self, forKey: .stateAttachments) + stableIDsAttachment = try container.decodeIfPresent(Int.self, forKey: .stableIDsAttachment) + dataAttachment = try container.decodeIfPresent(Int.self, forKey: .dataAttachment) + archiveID = try container.decode(UUID.self, forKey: .archiveID) + deploymentVersion = try container.decodeIfPresent( + ArchivedViewInput.DeploymentVersion.self, + forKey: .deploymentVersion + ) ?? .oldest + preferredBundleLanguage = try container.decodeIfPresent(String.self, forKey: .preferredBundleLanguage) + _preferredCompositionLanguage = try container.decodeIfPresent( + CodableRawRepresentable.self, + forKey: .preferredCompositionLanguage + ) ?? CodableRawRepresentable(.unset) + } +} diff --git a/Sources/OpenSwiftUICore/View/Archive/ArchivedViewHost.swift b/Sources/OpenSwiftUICore/View/Archive/ArchivedViewHost.swift new file mode 100644 index 000000000..cbeead783 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Archive/ArchivedViewHost.swift @@ -0,0 +1,123 @@ +// +// ArchivedViewHost.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Blocked by ArchiveWriter + +public import Foundation +public import OpenCoreGraphicsShims +#if canImport(UniformTypeIdentifiers) +public import UniformTypeIdentifiers +#endif + +#if !canImport(CoreGraphics) +public typealias CGDataConsumer = OpaquePointer +#endif + +// MARK: - _ArchivedViewHostDelegate + +@_spi(Private) +@available(OpenSwiftUI_v2_0, *) +public protocol _ArchivedViewHostDelegate { + mutating func viewDataNeedsUpdate() + + mutating func failedToEncodeView(type: any Any.Type) + + mutating func filteredImage(_ image: CGImage) throws -> CGImage + + #if canImport(UniformTypeIdentifiers) + func preferredImageType(for image: CGImage) -> UTType? + #endif +} + +// MARK: - ArchivedViewHostStates + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +public protocol ArchivedViewHostStates { + var count: Int { get } + + mutating func updateState(at index: Int, proxy: ArchivedViewStateProxy) throws + + func auxiliaryData() throws -> Data? +} + +// MARK: - ArchivedViewStateProxy [TODO] + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +public struct ArchivedViewStateProxy { + var writer: ArchiveWriter + + package init(writer: ArchiveWriter) { + self.writer = writer + } + + public func addAttachment(data: Data) throws -> Int { + // TODO: ArchiveWriter + _openSwiftUIUnimplementedFailure() + } + + public func addAttachment(encoder: (CGDataConsumer) throws -> Void) throws -> Int { + // TODO: ArchiveWriter + _openSwiftUIUnimplementedFailure() + } +} + +@_spi(Private) +@available(*, unavailable) +extension ArchivedViewStateProxy: Sendable {} + +// MARK: - ArchiveWriter [FIXME] + +package class ArchiveWriter {} + +// MARK: - _ArchivedViewHostDelegate + Default implementation + +@_spi(Private) +@available(OpenSwiftUI_v2_0, *) +extension _ArchivedViewHostDelegate { + public mutating func failedToEncodeView(type: any Any.Type) { + Log.externalWarning("Failed to serialize view of type: \(type)") + } + + public mutating func filteredImage(_ image: CGImage) throws -> CGImage { + image + } + + #if canImport(UniformTypeIdentifiers) + public func preferredImageType(for image: CGImage) -> UTType? { + nil + } + #endif +} + +// MARK: - AnyArchivedViewHost + +package protocol AnyArchivedViewHost { + func failedToEncodeView(type: any Any.Type) + + func filteredImage(_ image: CGImage) throws -> CGImage + + #if canImport(UniformTypeIdentifiers) + var allowedImageTypes: Set { get } + + func imageType(for image: CGImage) -> UTType? + #endif +} + +extension _DisplayList_StableIdentityMap { + package mutating func addIDs( + from list: DisplayList, + root: DisplayList.StableIdentityRoot + ) { + list.forEachIdentity { identity, _ in + guard let stableID = root[identity] else { + Log.internalError("missing stable ID \(identity)") + return + } + map[identity] = stableID + } + } +} diff --git a/Sources/OpenSwiftUI_SPI/Shims/CoreText/Private/CTCompositionLanguage.h b/Sources/OpenSwiftUI_SPI/Shims/CoreText/Private/CTCompositionLanguage.h index 7b9c2b14d..4a463a01a 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/CoreText/Private/CTCompositionLanguage.h +++ b/Sources/OpenSwiftUI_SPI/Shims/CoreText/Private/CTCompositionLanguage.h @@ -7,13 +7,10 @@ #include "OpenSwiftUIBase.h" #if __has_include() - #import -#import - -CF_ASSUME_NONNULL_BEGIN +#endif -typedef CF_ENUM(uint8_t, CTCompositionLanguage) { +typedef OPENSWIFTUI_ENUM(uint8_t, CTCompositionLanguage) { kCTCompositionLanguageUnset, kCTCompositionLanguageNone, kCTCompositionLanguageJapanese, @@ -21,6 +18,22 @@ typedef CF_ENUM(uint8_t, CTCompositionLanguage) { kCTCompositionLanguageTraditionalChinese, }; -CF_ASSUME_NONNULL_END +OPENSWIFTUI_EXTERN_C_BEGIN + +#if __has_include() + +CTCompositionLanguage CTParagraphStyleGetCompositionLanguageForLanguage(CFStringRef language); + +static inline CTCompositionLanguage OpenSwiftUI_CTParagraphStyleGetCompositionLanguageForLanguage(CFStringRef language) { + return CTParagraphStyleGetCompositionLanguageForLanguage(language); +} + +#else + +static inline CTCompositionLanguage OpenSwiftUI_CTParagraphStyleGetCompositionLanguageForLanguage(CFStringRef language) { + return kCTCompositionLanguageUnset; +} + +#endif -#endif /* CoreText.h */ +OPENSWIFTUI_EXTERN_C_END diff --git a/Tests/OpenSwiftUICoreTests/Layout/Geometry/LayoutTraitsTests.swift b/Tests/OpenSwiftUICoreTests/Layout/Geometry/LayoutTraitsTests.swift index 005d5f406..a770965bf 100644 --- a/Tests/OpenSwiftUICoreTests/Layout/Geometry/LayoutTraitsTests.swift +++ b/Tests/OpenSwiftUICoreTests/Layout/Geometry/LayoutTraitsTests.swift @@ -88,7 +88,7 @@ struct LayoutTraitsTests { if expectedFailure { #if compiler(>=6.3) // ST-12 #if !os(iOS) && !os(visionOS) - await #expect(processExitsWith: .failure) { [min, idea, max] in + await #expect(processExitsWith: .failure) { [min, ideal, max] in _ = Dimension(min: min, ideal: ideal, max: max) var d = Dimension.fixed(.zero) d.max = ideal