diff --git a/Sources/OpenSwiftUI/Integration/PlatformItemList/PlatformItemList.swift b/Sources/OpenSwiftUI/Integration/PlatformItemList/PlatformItemList.swift index 28d5b6960..aaa662cda 100644 --- a/Sources/OpenSwiftUI/Integration/PlatformItemList/PlatformItemList.swift +++ b/Sources/OpenSwiftUI/Integration/PlatformItemList/PlatformItemList.swift @@ -79,7 +79,7 @@ package struct PlatformItemList { var mergedContentItem: Item { // FIXME - items[0] + items.first ?? Item() } mutating func modify(_ body: (inout Item) -> Void) { diff --git a/Sources/OpenSwiftUI/Integration/PlatformItemList/WidgetAuxiliaryViewMetadata.swift b/Sources/OpenSwiftUI/Integration/PlatformItemList/WidgetAuxiliaryViewMetadata.swift new file mode 100644 index 000000000..81c246e9d --- /dev/null +++ b/Sources/OpenSwiftUI/Integration/PlatformItemList/WidgetAuxiliaryViewMetadata.swift @@ -0,0 +1,1021 @@ +// +// WidgetAuxiliaryViewMetadata.swift +// OpenSwiftUI +// +// Audited for 6.5.4 +// Status: WIP (Blocked by SymbolEffect + PlatformImageCodable) +// ID: 5D203C4BCF4ED90873E64430FDF30283 (SwiftUI) + +public import Foundation +public import CoreFoundation +import OpenAttributeGraphShims +import UIFoundation_Private +@_spi(ForOpenSwiftUIOnly) +@_spi(Private) +import OpenSwiftUICore +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +public import AppKit +#elseif canImport(UIKit) +public import UIKit +#endif + +// MARK: - WidgetAuxiliaryViewMetadata [WIP] + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +public struct WidgetAuxiliaryViewMetadata { + + // MARK: - WidgetAuxiliaryViewMetadata.Text + + public struct Text { + public struct Metadata { + public enum Kind { + case string(String) + case graphic(Graphic) + case dateAbsolute(Date, OpenSwiftUICore.Text.DateStyle) + case dateInterval(DateInterval) + case dateCurrent(String, Bool, TimeZone?) + case dateTimer(DateInterval, TimeInterval?, Bool) + @available(OpenSwiftUI_v6_0, *) + case timedata(TimeDataFormattingContainer) + } + + public var kind: Kind + + public var range: NSRange + + public var color: Color? + + public var features: [CFDictionary]? + + @available(OpenSwiftUI_v5_0, *) + public var textScale: OpenSwiftUICore.Text.Scale? + } + + public var text: NSAttributedString + + public var metadata: [Metadata] { + var result: [Metadata] = [] + var previousResolvableTextSegment: ResolvableTextSegmentAttribute.Value? + text.enumerateAttributes( + in: text.range, + options: [] + ) { attributes, range, _ in + let resolvableTextSegment = attributes[.resolvableTextSegment] as? ResolvableTextSegmentAttribute.Value + guard resolvableTextSegment == nil || + resolvableTextSegment != previousResolvableTextSegment else { + return + } + previousResolvableTextSegment = resolvableTextSegment + let kind: Metadata.Kind + if let specialMetadata = Self.extractSpecialMetadata(from: attributes) { + kind = specialMetadata + } else { + kind = .string(text.attributedSubstring(from: range).string) + } + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + let color = (attributes[.foregroundColor] as? NSColor).map(Color.init) + #elseif canImport(UIKit) + let color = (attributes[.foregroundColor] as? UIColor).map(Color.init) + #else + let color: Color? = nil + #endif + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + let font = attributes[.font] as? NSFont + let features = font?.fontDescriptor.object(forKey: .featureSettings) as? [NSDictionary] + #elseif canImport(UIKit) + let font = attributes[.font] as? UIFont + let features = font?.fontDescriptor.object(forKey: .featureSettings) as? [NSDictionary] + #else + let features: [CFDictionary]? = nil + #endif + let textScale = (attributes[._textScale] as? String).flatMap(OpenSwiftUICore.Text.Scale.init) + result.append( + Metadata( + kind: kind, + range: range, + color: color, + features: features, + textScale: textScale + ) + ) + } + return result + } + + fileprivate static func extractSpecialMetadata( + from attributes: [NSAttributedString.Key: Any] + ) -> Metadata.Kind? { + #if canImport(AppKit) || canImport(UIKit) + if let attachment = attributes[.attachment] as? NSTextAttachment { + return metadataKind(from: attachment) + } + #endif + if let absoluteDate = attributes[ResolvableAbsoluteDate.attribute] as? ResolvableAbsoluteDate { + return .dateAbsolute(absoluteDate.date, absoluteDate.style) + } + if let dateInterval = attributes[ResolvableDateInterval.attribute] as? ResolvableDateInterval { + return .dateInterval(dateInterval.interval) + } + if let dateCurrent = attributes[ResolvableCurrentDate.attribute] as? ResolvableCurrentDate { + let dateFormat: String + let isTemplate: Bool + switch dateCurrent.dateFormat { + case let .format(format): + dateFormat = format + isTemplate = false + case let .template(template): + dateFormat = template + isTemplate = true + @unknown default: _openSwiftUIUnreachableCode() + } + return .dateCurrent(dateFormat, isTemplate, dateCurrent.timeZone) + } + if let dateTimer = attributes[ResolvableTimer.attribute] as? ResolvableTimer { + return .dateTimer(dateTimer.interval, dateTimer.pause, dateTimer.countdown) + } + if let timeData = attributes[TimeDataFormatting.attribute] as? any ResolvableStringAttribute, + let container = TimeDataFormattingContainer(resolvable: timeData) { + return .timedata(container) + } + return nil + } + + #if canImport(AppKit) || canImport(UIKit) + private static func metadataKind( + from attachment: NSTextAttachment + ) -> Metadata.Kind? { + if let contents = attachment.contents { + guard let metadata = try? PropertyListDecoder().decode( + WidgetAuxiliaryViewMetadata.self, + from: contents + ) else { + Log.internalWarning("text attachment contents of unknown type") + return nil + } + guard let graphic = metadata.graphic else { + return nil + } + return .graphic(graphic) + } else if let image = attachment.image { + return .graphic(.image(image)) + } else { + Log.internalWarning("text attachment does not have contents or image") + return nil + } + } + #endif + } + + // MARK: - WidgetAuxiliaryViewMetadata.Graphic [WIP] + + public enum Graphic { + case named(Named) + + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + case image(NSImage) + #elseif canImport(UIKit) + case image(UIImage) + #else + case image(NSObject) + #endif + + var isSymbol: Bool { + guard case let .named(named) = self else { + return false + } + return named.isSymbol + } + + public struct Named { + public enum Location { + case bundle(URL) + case system(Bool) + } + + public var name: String + + public var location: Location + + public var value: Float? + + var isSymbol: Bool + + fileprivate var _colors: [Color.Resolved]? + + public var colors: [Color]? { + _colors?.map(Color.init) + } + + fileprivate var _tintColor: Color.Resolved? + + @available(OpenSwiftUI_v6_0, *) + public var tintColor: Color? { + _tintColor.map(Color.init) + } + + private var _mode: SymbolRenderingMode.Storage? + + public var symbolRenderingMode: SymbolRenderingMode? { + _mode.map { SymbolRenderingMode(storage: $0) } + } + + // TODO: SymbolEffect +// @ProtobufCodable +// private var _symbolEffects: SymbolEffectArray +// @available(OpenSwiftUI_v6_0, *) +// public var symbolEffects: [SymbolEffect] { +// _symbolEffects.effects.map { SymbolEffect(base: $0) } +// } + + @ProtobufCodable + private var _contentTransition: ContentTransition + + @available(OpenSwiftUI_v6_0, *) + public var contentTransition: ContentTransition { + _contentTransition + } + + // TBA + init(_ named: Image.NamedResolved, _ resolvedImage: Image.Resolved?) { + let environment = named.environment +// _symbolEffects = SymbolEffectArray(effects: environment.symbolEffects) + _contentTransition = environment.contentTransition + guard !environment.shouldRedactSymbolImages else { + name = "square.fill" + location = .system(false) + value = nil + isSymbol = true + _colors = environment._effectiveForegroundColor.map { + [$0.resolve(in: environment)] + } ?? [] + _mode = .monochrome + return + } + name = named.name + switch named.location { + case let .bundle(bundle): + location = .bundle(bundle.bundleURL) + if case .vectorGlyph? = resolvedImage?.image.contents { + isSymbol = true + } else { + isSymbol = false + } + case .system: + location = .system(false) + isSymbol = true + case .privateSystem: + location = .system(true) + isSymbol = true + } + value = named.value + _mode = named.symbolRenderingMode + if _mode == nil, + case let .vectorGlyph(glyph)? = resolvedImage?.image.contents { + _mode = glyph.renderingMode + } + + if let resolvedImage { + let levels: Int = { + let resolverMode = resolvedImage.styleResolverMode + var levels = Int(resolverMode.foregroundLevels) + if !resolverMode.options.contains(.foregroundPalette) { + if named.isTemplate { + levels = 1 + } else { + levels = levels == 0 ? 0 : 1 + } + } + return levels + }() + if levels == 0 { + _colors = nil + } else if let foregroundStyle = environment.foregroundStyle { + var shape = _ShapeStyle_Shape( + operation: .resolveStyle( + name: .foreground, + levels: 0 ..< levels + ), + environment: environment + ) + foregroundStyle._apply(to: &shape) + var colors: [Color.Resolved] = [] + for style in shape.stylePack[.foreground] { + guard let color = style.color else { + break + } + colors.append(color) + } + _colors = colors + } else { + _colors = [] + } + } else { + _colors = named.isTemplate ? [] : nil + } + _tintColor = environment.tintColor?.resolve(in: environment) + } + } + } + + // MARK: - WidgetAuxiliaryViewMetadata.Progress + + public struct Progress { + public enum Kind { + case absolute(Double?, Bool) + case date(ClosedRange, Bool) + } + + public var kind: Kind + + @MutableBox + private var labelBox: WidgetAuxiliaryViewMetadata? + + public var label: WidgetAuxiliaryViewMetadata? { + get { labelBox } + set { labelBox = newValue } + } + + @MutableBox + private var currentValueLabelBox: WidgetAuxiliaryViewMetadata? + + public var currentValueLabel: WidgetAuxiliaryViewMetadata? { + get { currentValueLabelBox } + set { currentValueLabelBox = newValue } + } + + private var _tint: ResolvedGradient? + + public var tint: Gradient? { + _tint.map(Gradient.init) + } + + init( + kind: Kind, + label: WidgetAuxiliaryViewMetadata?, + currentValueLabel: WidgetAuxiliaryViewMetadata?, + tint: ResolvedGradient? + ) { + self.kind = kind + labelBox = label + currentValueLabelBox = currentValueLabel + _tint = tint + } + } + + // MARK: - WidgetAuxiliaryViewMetadata.Gauge + + public struct Gauge { + public var value: Double + + @MutableBox + private var labelBox: WidgetAuxiliaryViewMetadata? + + public var label: WidgetAuxiliaryViewMetadata? { + get { labelBox } + set { labelBox = newValue } + } + + @MutableBox + private var currentValueLabelBox: WidgetAuxiliaryViewMetadata? + + public var currentValueLabel: WidgetAuxiliaryViewMetadata? { + get { currentValueLabelBox } + set { currentValueLabelBox = newValue } + } + + @MutableBox + private var minimumValueLabelBox: WidgetAuxiliaryViewMetadata? + + public var minimumValueLabel: WidgetAuxiliaryViewMetadata? { + get { minimumValueLabelBox } + set { minimumValueLabelBox = newValue } + } + + @MutableBox + private var maximumValueLabelBox: WidgetAuxiliaryViewMetadata? + + public var maximumValueLabel: WidgetAuxiliaryViewMetadata? { + get { maximumValueLabelBox } + set { maximumValueLabelBox = newValue } + } + + private var _tint: ResolvedGradient? + + public var tint: Gradient? { + _tint.map(Gradient.init) + } + + init( + value: Double, + label: WidgetAuxiliaryViewMetadata?, + currentValueLabel: WidgetAuxiliaryViewMetadata?, + minimumValueLabel: WidgetAuxiliaryViewMetadata?, + maximumValueLabel: WidgetAuxiliaryViewMetadata?, + tint: ResolvedGradient? + ) { + self.value = value + labelBox = label + currentValueLabelBox = currentValueLabel + minimumValueLabelBox = minimumValueLabel + maximumValueLabelBox = maximumValueLabel + _tint = tint + } + } + + // MARK: - WidgetAuxiliaryViewMetadata.Accessibility + + public struct Accessibility { + public var label: String? + + public var value: String? + + public var identifier: String? + + @available(OpenSwiftUI_v6_0, *) + public var hint: String? + } + + public private(set) var metadataText: WidgetAuxiliaryViewMetadata.Text? + + @available(OpenSwiftUI_v5_0, *) + public private(set) var metadataSecondaryText: WidgetAuxiliaryViewMetadata.Text? + + public private(set) var graphic: WidgetAuxiliaryViewMetadata.Graphic? + + public private(set) var fallbacks: [WidgetAuxiliaryViewMetadata]? + + public private(set) var progress: Progress? + + public private(set) var gauge: Gauge? + + public private(set) var url: URL? + + public private(set) var accessibility: Accessibility? + + public init(progress: Progress?) { + self.progress = progress + } + + public init(gauge: Gauge?) { + self.gauge = gauge + } + + @available(*, deprecated) + public init(fallbacks: [WidgetAuxiliaryViewMetadata]?) { + self.fallbacks = fallbacks + } + + init( + item: PlatformItemList.Item?, + url: URL?, + accessibility: Accessibility?, + child: WidgetAuxiliaryViewMetadata? + ) { + self.metadataText = item?.text.map { Text(text: $0) } + self.metadataSecondaryText = item?.secondaryText.map { Text(text: $0) } + if let namedResolvedImage = item?.namedResolvedImage { + self.graphic = .named(.init(namedResolvedImage, item?.resolvedImage)) + } else { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + self.graphic = (item?.resolvedImage?.basePlatformItemImage as? NSImage).map(Graphic.image) + #elseif canImport(UIKit) + self.graphic = (item?.resolvedImage?.basePlatformItemImage as? UIImage).map(Graphic.image) + #else + self.graphic = (item?.resolvedImage?.basePlatformItemImage as? NSObject).map(Graphic.image) + #endif + } + self.url = url + self.accessibility = accessibility + + guard let child else { + return + } + self.metadataText = metadataText ?? child.metadataText + self.metadataSecondaryText = metadataSecondaryText ?? child.metadataSecondaryText + self.graphic = graphic ?? child.graphic + self.fallbacks = fallbacks ?? child.fallbacks + self.progress = progress ?? child.progress + self.gauge = gauge ?? child.gauge + self.url = self.url ?? child.url + self.accessibility = self.accessibility ?? child.accessibility + } +} + +// MARK: - SymbolEffectArray [TODO] + +//private struct SymbolEffectArray: CodableByProtobuf, Equatable { +// var effects: [_SymbolEffect] +// +// init(effects: [_SymbolEffect.Identified]) { +// self.effects = effects.map(\.effect) +// } +// +// func encode(to encoder: inout ProtobufEncoder) throws { +// for effect in effects { +// try encoder.messageField(1, effect) +// } +// } +// +// init(from decoder: inout ProtobufDecoder) throws { +// effects = [] +// while let field = try decoder.nextField() { +// switch field.tag { +// case 1: +// effects.append(try decoder.messageField(field)) +// default: +// try decoder.skipField(field) +// } +// } +// } +//} + +// MARK: - TimeDataFormattingContainer + representation + +extension TimeDataFormattingContainer { + public func representation( + for version: _ArchivedViewStates.DeploymentVersion + ) -> WidgetAuxiliaryViewMetadata.Text.Metadata.Kind? { + let representation = representation(for: version.base) + var attributes: [NSAttributedString.Key: Any] = [:] + attributes[type(of: representation).attribute] = representation + return WidgetAuxiliaryViewMetadata.Text.extractSpecialMetadata(from: attributes) + } +} + +// MARK: - WidgetAuxiliaryViewMetadata + tint + +@_spi(Private) +extension WidgetAuxiliaryViewMetadata { + public var tint: Color? { + guard let graphic, + case let .named(named) = graphic else { + return nil + } + return named.tintColor + } + + public var resolvedTint: Color.Resolved? { + guard let graphic, + case let .named(named) = graphic else { + return nil + } + return named._tintColor + } + + package static func tint(from env: EnvironmentValues) -> ResolvedGradient? { + guard let tint = env.tint else { + return nil + } + // TODO: Gradient related + _openSwiftUIUnimplementedWarning() + return nil +// if let gradient = tint.resolveGradient(in: env) { +// return gradient +// } +// guard let color = tint.fallbackColor(in: env) else { +// return nil +// } +// return Gradient(colors: [color]).resolve(in: env) + } +} + +// MARK: - WidgetAuxiliaryViewMetadata + Sendable + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Gauge: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Graphic: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Graphic.Named: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Graphic.Named.Location: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Accessibility: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Progress: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Progress.Kind: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Text: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Text.Metadata: Sendable {} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Text.Metadata.Kind: Sendable {} + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +extension WidgetAuxiliaryViewMetadata { + @available(*, deprecated, message: "Use metadataText instead") + public var text: NSAttributedString? { + metadataText?.text + } + + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + @available(*, deprecated, message: "Use graphic instead") + public var image: NSImage? { + guard let graphic, + case let .image(image) = graphic else { + return nil + } + return image + } + #elseif canImport(UIKit) + @available(*, deprecated, message: "Use graphic instead") + public var image: UIImage? { + guard let graphic, + case let .image(image) = graphic else { + return nil + } + return image + } + #else + @available(*, deprecated, message: "Use graphic instead") + public var image: NSObject? { + guard let graphic, + case let .image(image) = graphic else { + return nil + } + return image + } + #endif +} + +// MARK: - WidgetAuxiliaryViewMetadata + Codable [TODO] + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +extension WidgetAuxiliaryViewMetadata: Codable { + public func encode(to encoder: any Encoder) throws { + _openSwiftUIUnimplementedFailure() + } + + public init(from decoder: any Decoder) throws { + _openSwiftUIUnimplementedFailure() + } +} + +// MARK: - WidgetAuxiliaryViewMetadata + CustomDebugStringConvertible + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata: CustomDebugStringConvertible { + public var debugDescription: String { + var content = "" + func append(_ value: T?) where T: CustomDebugStringConvertible { + guard let value else { + return + } + content += "\n\t\(value.debugDescription)" + } + append(metadataText) + append(graphic) + append(url) + append(accessibility) + append(gauge) + append(progress) + return "WidgetAuxiliaryViewMetadata(\(content)\n)" + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Text: CustomDebugStringConvertible { + public var debugDescription: String { + "Text(\"\(text.string)\")" + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Graphic: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case let .named(named): + "Graphic(\(named.debugDescription))" + case let .image(image): + "Graphic(\(image.debugDescription))" + } + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Graphic.Named: CustomDebugStringConvertible { + public var debugDescription: String { + let locationDescription: String + switch location { + case let .bundle(url): + locationDescription = "bundle(\(url.description))" + case let .system(isPublic): + locationDescription = isPublic ? "system" : "internal" + } + return "Named(name: \(name), location: \(locationDescription), value: \(value?.description ?? "--"), colors: \(colors?.debugDescription ?? "[]"))" + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Accessibility: CustomDebugStringConvertible { + public var debugDescription: String { + "Accessibility(label: \(label ?? "nil"), value: \(value ?? "nil"), identifier: \(identifier ?? "nil") )" + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Gauge: CustomDebugStringConvertible { + public var debugDescription: String { + "Gauge(value: \(value))" + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Progress: CustomDebugStringConvertible { + public var debugDescription: String { + "Progress(\(kind.debugDescription))" + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Progress.Kind: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case let .absolute(completed, indeterminate): + "absolute(completed: \(completed?.description ?? "nil"), indeterminate: \(indeterminate))\"" + case let .date(range, countdown): + "date(\(range.debugDescription), countdown: \(countdown))" + } + } +} + +// MARK: - WidgetAuxiliaryViewMetadata + Equatable + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Graphic.Named.Location: Equatable {} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Graphic.Named: Equatable {} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension WidgetAuxiliaryViewMetadata.Graphic: Equatable {} + +// MARK: - WidgetAuxiliaryViewMetadata.Key + +@_spi(Private) +extension WidgetAuxiliaryViewMetadata { + public struct Key: HostPreferenceKey { + public static var defaultValue: WidgetAuxiliaryViewMetadata? + + public static func reduce( + value: inout WidgetAuxiliaryViewMetadata?, + nextValue: () -> WidgetAuxiliaryViewMetadata? + ) { + value = WidgetAuxiliaryViewMetadata.reduce(value, nextValue()) + } + } + + public static func reduce( + _ lhs: WidgetAuxiliaryViewMetadata?, + _ rhs: WidgetAuxiliaryViewMetadata? + ) -> WidgetAuxiliaryViewMetadata? { + guard var result = lhs else { + return rhs + } + guard let rhs else { + return result + } + if let graphic = rhs.graphic { + result.graphic = graphic + } + if let fallbacks = rhs.fallbacks { + result.fallbacks = fallbacks + } + if let metadataText = rhs.metadataText { + result.metadataText = metadataText + } + if let accessibility = rhs.accessibility { + result.accessibility = accessibility + } + return result + } +} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadata.Key: Sendable {} + +// MARK: - WidgetAuxiliaryViewMetadataModifier + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +public struct WidgetAuxiliaryViewMetadataModifier: PrimitiveViewModifier, UnaryViewModifier where Content: View { + var content: Content + + public init(@ViewBuilder content: () -> Content) { + self.content = content() + } + + nonisolated public static func _makeView( + modifier: _GraphValue, + inputs: _ViewInputs, + body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs + ) -> _ViewOutputs { + let content = modifier.value[offset: { .of(&$0.content) }] + var contentInputs = inputs.withoutGeometryDependencies + var hostKeys = PreferenceKeys() + hostKeys.add(WidgetAuxiliaryViewMetadata.Key.self) + contentInputs.hasWidgetMetadata = true + contentInputs.preferences = PreferencesInputs( + hostKeys: inputs.intern(hostKeys, id: .defaultValue) + ) + contentInputs.addPlatformItemListKey( + flags: WidgetMetadataPlatformItemListFlags.self, + editOperation: .replace + ) + contentInputs.preferences.add(WidgetAuxiliaryViewMetadata.Key.self) + contentInputs.preferences.add(WidgetAuxiliaryURLPreferenceKey.self) + contentInputs.needsDisplayListAccessibility = true + contentInputs.environment = Attribute(value: contentInputs.environment.value) + contentInputs.preferences.add(AccessibilityAttachment.Key.self) + + let contentOutputs = Content._makeView( + view: .init(content), + inputs: contentInputs + ) + let platformItemList = contentOutputs.preferences.platformItemList + ?? inputs.intern(PlatformItemList(items: []), id: .defaultValue) + let preferenceWriter = AuxiliaryViewMetadataPreferenceWriter( + metadata: OptionalAttribute( + contentOutputs.preferences[WidgetAuxiliaryViewMetadata.Key.self] + ), + url: OptionalAttribute( + contentOutputs.preferences[WidgetAuxiliaryURLPreferenceKey.self] + ), + accessibilityAttachment: OptionalAttribute( + contentOutputs.preferences[AccessibilityAttachment.Key.self] + ), + environmentValues: contentInputs.environment, + platformItemList: platformItemList, + idiom: contentInputs.base.interfaceIdiom + ) + var outputs = body(_Graph(), inputs) + outputs.preferences.makePreferenceWriter( + inputs: inputs.preferences, + key: WidgetAuxiliaryViewMetadata.Key.self, + value: Attribute(preferenceWriter) + ) + return outputs + } +} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryViewMetadataModifier: Sendable {} + +// MARK: - AuxiliaryViewMetadataPreferenceWriter + +private struct AuxiliaryViewMetadataPreferenceWriter: Rule { + @OptionalAttribute var metadata: WidgetAuxiliaryViewMetadata?? + @OptionalAttribute var url: URL?? + @OptionalAttribute var accessibilityAttachment: AccessibilityAttachment.Tree? + @Attribute var environmentValues: EnvironmentValues + @Attribute var platformItemList: PlatformItemList + var idiom: AnyInterfaceIdiom + + var value: WidgetAuxiliaryViewMetadata? { + WidgetAuxiliaryViewMetadata( + item: platformItemList.mergedContentItem, + url: url ?? nil, + accessibility: accessibilityAttachment?.metadataAccessibility( + in: environmentValues, + idiom: idiom + ), + child: metadata ?? nil + ) + } +} + +extension AccessibilityAttachment.Tree { + func metadataAccessibility( + in environment: EnvironmentValues, + idiom: AnyInterfaceIdiom + ) -> WidgetAuxiliaryViewMetadata.Accessibility? { + // TODO: Accessibility is not implemented + _openSwiftUIUnimplementedWarning() + return nil + } +} + +// MARK: - WidgetAuxiliaryURLPreferenceKey + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +public struct WidgetAuxiliaryURLPreferenceKey: PreferenceKey { + public static var defaultValue: URL? + + public static func reduce(value: inout URL?, nextValue: () -> URL?) { + value = value ?? nextValue() + } +} + +@_spi(Private) +@available(*, unavailable) +extension WidgetAuxiliaryURLPreferenceKey: Sendable {} + +// MARK: - WidgetAuxiliaryTextImagePreference + +struct WidgetAuxiliaryTextImagePreference { + var list: PlatformItemList? +} + +// MARK: - LazyWidgetAuxiliaryMetadataTextImage + +struct LazyWidgetAuxiliaryMetadataTextImage: StatefulRule where Content: View { + var subgraph: Subgraph + @Attribute var content: Content + let inputs: _ViewInputs + @OptionalAttribute var textImagePref: WidgetAuxiliaryTextImagePreference?? + + init( + flags _: _AttributeType.Flags.Type, + content: Attribute, + inputs: _ViewInputs + ) { + self.subgraph = Subgraph.current! + self._content = content + self.inputs = inputs + self._textImagePref = OptionalAttribute() + } + + typealias Value = WidgetAuxiliaryTextImagePreference? + + mutating func updateValue() { + if !hasValue { + _textImagePref = subgraph.apply { + makeTextImage() + } + } + value = textImagePref ?? nil + } + + private func makeTextImage() -> OptionalAttribute { + var inputs = inputs + inputs.addPlatformItemListKey( + flags: WidgetMetadataPlatformItemListFlags.self, + editOperation: .replace + ) + let outputs = Content._makeView( + view: _GraphValue($content), + inputs: inputs + ) + return OptionalAttribute( + Attribute( + WidgetAuxiliaryMetadataTextImageWriter( + list: WeakAttribute(outputs.preferences.platformItemList) + ) + ) + ) + } +} + +// MARK: - WidgetAuxiliaryMetadataTextImageWriter + +struct WidgetAuxiliaryMetadataTextImageWriter: AsyncAttribute, Rule { + @WeakAttribute var list: PlatformItemList? + + var value: WidgetAuxiliaryTextImagePreference? { + guard let list else { + return nil + } + return WidgetAuxiliaryTextImagePreference(list: list) + } +} diff --git a/Sources/OpenSwiftUI/View/ArchivedView/ArchivedViewState.swift b/Sources/OpenSwiftUI/View/ArchivedView/ArchivedViewState.swift new file mode 100644 index 000000000..203a9bea3 --- /dev/null +++ b/Sources/OpenSwiftUI/View/ArchivedView/ArchivedViewState.swift @@ -0,0 +1,58 @@ +// +// ArchivedViewStates.swift +// OpenSwiftUI +// +// Audited for 6.5.4 +// Status: WIP + +import Foundation +@_spi(ForOpenSwiftUIOnly) +import OpenSwiftUICore + +// MARK: - _ArchivedViewStates [TODO] + +@_spi(Private) +@available(OpenSwiftUI_v2_0, *) +public struct _ArchivedViewStates {} + +// MARK: - _ArchivedViewStates.DeploymentVersion + +@_spi(Private) +extension _ArchivedViewStates { + @available(OpenSwiftUI_v6_0, *) + public struct DeploymentVersion: Hashable, Comparable, Codable, Sendable { + var base: ArchivedViewInput.DeploymentVersion + + init(base: ArchivedViewInput.DeploymentVersion) { + self.base = base + } + + public static let v5: DeploymentVersion = .init(base: .v5) + + public static let v6: DeploymentVersion = .init(base: .v6) + + @_alwaysEmitIntoClient + public static var current: DeploymentVersion { .v6 } + + public static func < ( + lhs: DeploymentVersion, + rhs: DeploymentVersion + ) -> Bool { + lhs.base < rhs.base + } + } +} + +@_spi(Private) +@available(OpenSwiftUI_v6_0, *) +extension _ArchivedViewStates.DeploymentVersion { + public func encode(to encoder: any Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(base.rawValue) + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.singleValueContainer() + base = .init(rawValue: try container.decode(Int8.self)) + } +} diff --git a/Sources/OpenSwiftUI/View/LabelContent/AccessibilityLabeledContent.swift b/Sources/OpenSwiftUI/View/LabelContent/AccessibilityLabeledContent.swift index fed386ef7..c0ed23732 100644 --- a/Sources/OpenSwiftUI/View/LabelContent/AccessibilityLabeledContent.swift +++ b/Sources/OpenSwiftUI/View/LabelContent/AccessibilityLabeledContent.swift @@ -7,6 +7,7 @@ // ID: 28161D0154DF546094400EFEC8044F4B (SwiftUI) import OpenAttributeGraphShims +@_spi(Private) import OpenSwiftUICore // MARK: - AccessibilityLabeledContentModifier @@ -129,8 +130,36 @@ struct ResolvedPresentation: Rule { struct AccessibilityAttachmentModifier {} // FIXME -enum AccessibilityAttachment { - enum Tree {} +enum AccessibilityAttachment {} + +extension AccessibilityAttachment { + enum Tree { + case leaf(AccessibilityAttachment) + case branch([Tree]) + case empty + } + + struct Key: HostPreferenceKey { + static let defaultValue: Tree = .empty + + static func reduce(value: inout Tree, nextValue: () -> Tree) { + let nextValue = nextValue() + switch (value, nextValue) { + case (_, .empty): + break + case (.empty, _): + value = nextValue + case let (.branch(lhs), .branch(rhs)): + value = .branch(lhs + rhs) + case let (.branch(lhs), _): + value = .branch(lhs + [nextValue]) + case let (_, .branch(rhs)): + value = .branch([value] + rhs) + default: + value = .branch([value, nextValue]) + } + } + } } // FIXME diff --git a/Sources/OpenSwiftUICore/Animation/Transition/ContentTransition.swift b/Sources/OpenSwiftUICore/Animation/Transition/ContentTransition.swift index bb2a4944f..2f4e499e3 100644 --- a/Sources/OpenSwiftUICore/Animation/Transition/ContentTransition.swift +++ b/Sources/OpenSwiftUICore/Animation/Transition/ContentTransition.swift @@ -515,6 +515,17 @@ package struct ContentTransitionEffect: _RendererEffect { } } +// FIXME +extension ContentTransition: ProtobufMessage { + package func encode(to encoder: inout ProtobufEncoder) throws { + _openSwiftUIUnimplementedFailure() + } + + package init(from decoder: inout ProtobufDecoder) throws { + _openSwiftUIUnimplementedFailure() + } +} + // FIXME: ORB package enum ORBTransitionMethod: Int { diff --git a/Sources/OpenSwiftUICore/Graphic/Gradient/ResolvedGradient.swift b/Sources/OpenSwiftUICore/Graphic/Gradient/ResolvedGradient.swift index c643c36bc..5c8dda891 100644 --- a/Sources/OpenSwiftUICore/Graphic/Gradient/ResolvedGradient.swift +++ b/Sources/OpenSwiftUICore/Graphic/Gradient/ResolvedGradient.swift @@ -18,4 +18,13 @@ package struct ResolvedGradient: Equatable { } // FIXME -public struct Gradient {} +public struct Gradient { + package init(_ resolved: ResolvedGradient) { +// self.init( +// stops: resolved.stops.map { +// Stop(color: Color($0.color), location: $0.location) +// } +// ) + } + +}