diff --git a/Package.swift b/Package.swift index 7c82eb87c..fd743897a 100644 --- a/Package.swift +++ b/Package.swift @@ -706,6 +706,7 @@ extension [SwiftSetting] { fileprivate static func availabilityMacroSettings(ignoreAvailability: Bool) -> Self { let minimumVersion = "iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, visionOS 1.0" return [ + .enableExperimentalFeature("AvailabilityMacro=SwiftUI_v1_0:\(ignoreAvailability ? minimumVersion : "iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0")"), .enableExperimentalFeature("AvailabilityMacro=OpenSwiftUI_v1_0:\(ignoreAvailability ? minimumVersion : "iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0")"), .enableExperimentalFeature("AvailabilityMacro=OpenSwiftUI_v1_4:\(ignoreAvailability ? minimumVersion : "iOS 13.4, macOS 10.15.4, tvOS 13.4, watchOS 6.2")"), .enableExperimentalFeature("AvailabilityMacro=OpenSwiftUI_v2_0:\(ignoreAvailability ? minimumVersion : "iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0")"), diff --git a/Sources/OpenSwiftUI/View/Configuration/ViewAlias.swift b/Sources/OpenSwiftUI/View/Configuration/ViewAlias.swift index f395c1118..34200146b 100644 --- a/Sources/OpenSwiftUI/View/Configuration/ViewAlias.swift +++ b/Sources/OpenSwiftUI/View/Configuration/ViewAlias.swift @@ -25,6 +25,36 @@ protocol ViewAlias: PrimitiveView { init() } +// Audited for 6.5.4 + +extension ViewAlias { + nonisolated public static func _makeView(view: _GraphValue, inputs: _ViewInputs) -> _ViewOutputs { + var inputs = inputs + guard let source = inputs.popLast(SourceInput.self) else { + return .init() + } + inputs.base.resetCurrentStyleableView() + return source.formula.makeView(view: view, source: source, inputs: inputs) + } + + nonisolated public static func _makeViewList(view: _GraphValue, inputs: _ViewListInputs) -> _ViewListOutputs { + var inputs = inputs + guard let source = inputs.base.popLast(SourceInput.self) else { + return .emptyViewList(inputs: inputs) + } + inputs.base.resetCurrentStyleableView() + return source.formula.makeViewList(view: view, source: source, inputs: inputs) + } + + nonisolated public static func _viewListCount(inputs: _ViewListCountInputs) -> Int? { + var inputs = inputs + guard let source = inputs.popLast(SourceInput.self) else { + return 0 + } + return source.formula.viewListCount(source: source, inputs: inputs) + } +} + // MARK: - View + ViewAlias Extension extension View { diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift index fc068c8cc..c64e1871b 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift @@ -32,10 +32,20 @@ extension DisplayList.Item { switch content.value { case let .placeholder(id: identity): printer.print("(placeholder \(identity))") + case let .color(resolved): + // FIXME + printer.print("(color \(resolved))") default: // TOOD break } + case let .effect(effect, displayList): + // FIXME + printer.push("effect") + for item in displayList.items { + item.print(into: &printer) + } + printer.pop() default: // TODO break @@ -52,10 +62,20 @@ extension DisplayList.Item { switch content.value { case let .placeholder(id: identity): printer.print("@\(identity))") + case let .color(resolved): + // FIXME + printer.print("(c \(resolved))") default: // TOOD break } + case let .effect(effect, displayList): + // FIXME + printer.push("E") + for item in displayList.items { + item.printMinimally(into: &printer) + } + printer.pop() default: // TODO break diff --git a/Tests/OpenSwiftUICoreTests/Layout/ZIndexTests.swift b/Tests/OpenSwiftUICoreTests/Layout/ZIndexTests.swift index 2cda207d6..7eba9cf5d 100644 --- a/Tests/OpenSwiftUICoreTests/Layout/ZIndexTests.swift +++ b/Tests/OpenSwiftUICoreTests/Layout/ZIndexTests.swift @@ -17,5 +17,14 @@ struct ZIndexTests { } } // TODO: Add a test helper to hook into makeViewList and retrieve the zIndex value to verify it + + let graph = ViewGraph( + rootViewType: ContentView.self, + requestedOutputs: [.displayList] + ) + graph.instantiateOutputs() + graph.setRootView(ContentView()) + graph.setProposedSize(CGSize(width: 100, height: 100)) + let (displayList, _) = graph.displayList() } } diff --git a/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayList+StringTests.swift b/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayList+StringTests.swift deleted file mode 100644 index 577fefa63..000000000 --- a/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayList+StringTests.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// DisplayList+StringTests.swift -// OpenSwiftUICoreTests - -import OpenSwiftUICore -import Testing - -struct DisplayList_StringTests { - @Test - func plain() { - let emptyList = DisplayList() - #expect(emptyList.description == "(display-list)") - #expect(emptyList.minimalDescription == "(DL)") - } - - @Test - func emptyItem() { - let item = DisplayList.Item(.empty, frame: .zero, identity: .init(decodedValue: 1), version: .init(decodedValue: 0)) - let d = item.description - print(d) - - #expect(item.description == """ - (display-list-item - (item #:identity 1 #:version 0 - (frame (0.0 0.0; 0.0 0.0)))) - """) - - let list = DisplayList(item) - #expect(list.description == "(display-list)") - #expect(list.minimalDescription == "(DL)") - - let list2 = DisplayList([item]) - #expect(list2.description == """ - (display-list - (item #:identity 1 #:version 0 - (frame (0.0 0.0; 0.0 0.0)))) - """) - #expect(list2.minimalDescription == "(DL(I:1))") - } - - func placeholdItem() { - let item = DisplayList.Item(.content(.init(.placeholder(id: .init(decodedValue: 2)), seed: .init(decodedValue: 4))), frame: .zero, identity: .init(decodedValue: 1), version: .init(decodedValue: 0)) - let expectedDescription = """ - (display-list - (item #:identity 1 #:version 0 #:views true - (frame (0.0 0.0; 0.0 0.0)) - (content-seed 4) - (placeholder #2))) - """ - - let expectedMinimalDescription = """ - (DL(I:1 @#2)) - """ - - - let list = DisplayList(item) - #expect(list.description == expectedDescription) - #expect(list.minimalDescription == expectedMinimalDescription) - - let list2 = DisplayList([item]) - #expect(list2.description == expectedDescription) - #expect(list2.minimalDescription == expectedMinimalDescription) - } - -} diff --git a/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayListPrinterTests.swift b/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayListPrinterTests.swift new file mode 100644 index 000000000..13cfd4a9c --- /dev/null +++ b/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayListPrinterTests.swift @@ -0,0 +1,136 @@ +// +// DisplayListPrinterTests.swift +// OpenSwiftUICoreTests + +import OpenSwiftUICore +import Testing + +struct DisplayListPrinterTests { + @Test + func plain() { + let emptyList = DisplayList() + #expect(emptyList.description == "(display-list)") + #expect(emptyList.minimalDescription == "(DL)") + } + + @Test + func emptyItem() { + let item = DisplayList.Item( + .empty, + frame: .zero, + identity: .init(decodedValue: 1), + version: .init(decodedValue: 0) + ) + let d = item.description + print(d) + + #expect(item.description == """ + (display-list-item + (item #:identity 1 #:version 0 + (frame (0.0 0.0; 0.0 0.0)))) + """) + + let list = DisplayList(item) + #expect(list.description == "(display-list)") + #expect(list.minimalDescription == "(DL)") + + let list2 = DisplayList([item]) + #expect(list2.description == """ + (display-list + (item #:identity 1 #:version 0 + (frame (0.0 0.0; 0.0 0.0)))) + """) + #expect(list2.minimalDescription == "(DL(I:1))") + } + + func placeholderItem() { + let item = DisplayList.Item( + .content(.init( + .placeholder(id: .init(decodedValue: 2)), + seed: .init(decodedValue: 4) + )), + frame: .zero, + identity: .init(decodedValue: 1), + version: .init(decodedValue: 0) + ) + let expectedDescription = """ + (display-list + (item #:identity 1 #:version 0 #:views true + (frame (0.0 0.0; 0.0 0.0)) + (content-seed 4) + (placeholder #2))) + """ + + let expectedMinimalDescription = """ + (DL(I:1 @#2)) + """ + + + let list = DisplayList(item) + #expect(list.description == expectedDescription) + #expect(list.minimalDescription == expectedMinimalDescription) + + let list2 = DisplayList([item]) + #expect(list2.description == expectedDescription) + #expect(list2.minimalDescription == expectedMinimalDescription) + } + + @Test + func colorItem() { + let item = DisplayList.Item( + .content(.init( + .color(.white), + seed: .init(decodedValue: 2)) + ), + frame: .zero, + identity: .init(decodedValue: 1), + version: .init(decodedValue: 0) + ) + #expect(DisplayList(item).description == """ + (display-list + (item #:identity 1 #:version 0 + (frame (0.0 0.0; 0.0 0.0)) + (content-seed 2) + (color #FFFFFFFF))) + """) + #expect(DisplayList(item).minimalDescription == """ + (DL(I:1 (c #FFFFFFFF))) + """) + } + + @Test + func effectItem() { + let colorItem = DisplayList.Item( + .content(.init( + .color(.white), + seed: .init(decodedValue: 2)) + ), + frame: .zero, + identity: .init(decodedValue: 1), + version: .init(decodedValue: 0) + ) + let effectItem = DisplayList.Item( + .effect( + .opacity(1), + .init(colorItem) + ), + frame: .zero, + identity: .init(decodedValue: 4), + version: .init(decodedValue: 3) + ) + // FIXME: opacity value should be printed since it is not canonicalized here + #expect(DisplayList(effectItem).description == """ + (display-list + (item #:identity 4 #:version 3 + (frame (0.0 0.0; 0.0 0.0)) + (effect + (item #:identity 1 #:version 0 + (frame (0.0 0.0; 0.0 0.0)) + (content-seed 2) + (color #FFFFFFFF))))) + """) + #expect(DisplayList(effectItem).minimalDescription == """ + (DL(I:4(E(I:1 (c #FFFFFFFF))))) + """) + } +} diff --git a/Tests/OpenSwiftUICoreTests/View/IDViewTests.swift b/Tests/OpenSwiftUICoreTests/View/IDViewTests.swift index 8654d0467..6f1935e84 100644 --- a/Tests/OpenSwiftUICoreTests/View/IDViewTests.swift +++ b/Tests/OpenSwiftUICoreTests/View/IDViewTests.swift @@ -35,30 +35,21 @@ struct IDViewTests { graph.setRootView(ContentView()) graph.setProposedSize(CGSize(width: 100, height: 100)) let (displayList, _) = graph.displayList() - let expectRegex = try! Regex(#""" \(display-list \(item #:identity \d+ #:version \d+ - \(frame \([^)]+\)\)\)\) - """#) - #expect(displayList.description.contains(expectRegex)) - withKnownIssue("Blocked by DisplayList.print and canonicalize") { - let expectRegex = try! Regex(#""" - \(display-list + \(frame \([^)]+\)\) + \(effect \(item #:identity \d+ #:version \d+ \(frame \([^)]+\)\) - \(effect - \(item #:identity \d+ #:version \d+ - \(frame \([^)]+\)\) - \(content-seed \d+\) - \(color #[0-9A-F]{8}\)\) - \(item #:identity \d+ #:version \d+ - \(frame \([^)]+\)\) - \(content-seed \d+\) - \(color #[0-9A-F]{8}\)\)\)\)\) - """#) - #expect(displayList.description.contains(expectRegex)) - } + \(content-seed \d+\) + \(color #[0-9A-F]{8}\)\) + \(item #:identity \d+ #:version \d+ + \(frame \([^)]+\)\) + \(content-seed \d+\) + \(color #[0-9A-F]{8}\)\)\)\)\) + """#) + #expect(displayList.description.contains(expectRegex)) } #endif } diff --git a/Tests/OpenSwiftUITests/View/Configuration/ViewAliasTests.swift b/Tests/OpenSwiftUITests/View/Configuration/ViewAliasTests.swift new file mode 100644 index 000000000..c5d5c2bf6 --- /dev/null +++ b/Tests/OpenSwiftUITests/View/Configuration/ViewAliasTests.swift @@ -0,0 +1,65 @@ +// +// ViewAliasTests.swift +// OpenSwiftUITests + +import Testing +@testable import OpenSwiftUI +@_spi(ForOpenSwiftUIOnly) +import OpenSwiftUICore +#if os(iOS) || os(visionOS) +import UIKit +#endif + +@MainActor +struct ViewAliasTests { + @Test + func optionalViewAliasDynamicProperty() { + struct ContentView: View { + @OptionalViewAlias + private var alias: ChildView.Alias? + + var body: some View { + ChildView() + .viewAlias(ChildView.Alias.self) { Color.red } + .onAppear { + #expect(alias == nil) + } + } + } + + struct ChildView: View { + struct Alias: ViewAlias {} + + @OptionalViewAlias + private var alias: Alias? + + var body: some View { + Alias() + .onAppear { + #expect(alias != nil) + } + } + } + // How to tirgger onAppear + let graph = ViewGraph( + rootViewType: ContentView.self, + requestedOutputs: [.displayList] + ) + graph.instantiateOutputs() + graph.setRootView(ContentView()) + graph.setProposedSize(CGSize(width: 100, height: 100)) + let (displayList, _) = graph.displayList() + print(displayList.description) + let expectRegex = try! Regex(#""" + \(display-list + \(item #:identity \d+ #:version \d+ + \(frame \([^)]+\)\) + \(effect + \(item #:identity \d+ #:version \d+ + \(frame \([^)]+\)\) + \(content-seed \d+\) + \(color #[0-9A-F]{8}\)\)\)\)\) + """#) + #expect(displayList.description.contains(expectRegex)) + } +} diff --git a/Tests/OpenSwiftUITests/View/ViewAliasTests.swift b/Tests/OpenSwiftUITests/View/ViewAliasTests.swift deleted file mode 100644 index cf9b7058f..000000000 --- a/Tests/OpenSwiftUITests/View/ViewAliasTests.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// ViewAliasTests.swift -// OpenSwiftUITests - -import Testing -@testable import OpenSwiftUI -@_spi(ForOpenSwiftUIOnly) -import OpenSwiftUICore -#if os(iOS) || os(visionOS) -import UIKit -#endif - -// TODO: Add viewAlias related test after style are implemented -@MainActor -struct ViewAliasTests { -// struct CustomView: View { -// struct Alias: ViewAlias { -// init() {} -// } -// } -// @Test -// func optionalViewAliasDynamicProperty() { -// struct ContentView: View { -// @OptionalViewAlias -// private var alias: CustomView.Alias? -// -// var body: some View { -// CustomView() -// .viewAlias(CustomView.Alias.self) { -// Color.red -// } -// } -// } -// #if os(iOS) || os(visionOS) -// let hostingView = _UIHostingView(rootView: ContentView()) -// hostingView.render() -// #endif -// } -}