From 4dcc69fc10412f41340511f410763ba4c0dfe3f7 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sat, 16 May 2026 12:17:49 +0800 Subject: [PATCH 1/3] Add DisplayList.properties support --- .../Render/DisplayList/DisplayList.swift | 44 +++++++++++++++++++ .../DisplayList/DisplayListPrinter.swift | 4 -- .../Render/DisplayList/DisplayListTests.swift | 35 +++++++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift index 4647ad690..fa8473b93 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift @@ -661,6 +661,36 @@ extension DisplayList.Item { } } } + + package var properties: DisplayList.Properties { + switch value { + case .empty: + return [] + case let .content(content): + if case let .flattened(list, _, _) = content.value { + return list.properties + } else { + return [] + } + case let .effect(effect, list): + var properties = list.properties + switch effect { + case let .properties(effectProperties): + properties.formUnion(effectProperties) + case let .mask(mask, _): + properties.formUnion(mask.properties) + case let .interpolatorLayer(group, _): + properties.formUnion(group.properties) + default: + break + } + return properties + case let .states(states): + return states.reduce(into: []) { properties, state in + properties.formUnion(state.1.properties) + } + } + } } // TODO @@ -716,6 +746,10 @@ extension DisplayList { [] } + var properties: Properties { + [] + } + func nextUpdate(after _: Time) -> Time { .infinity } @@ -742,6 +776,10 @@ extension DisplayList { override func nextUpdate(after time: Time) -> Time { layer.nextUpdate(after: time) } + + override var properties: Properties { + layer.properties + } } struct InterpolatorLayer { @@ -783,6 +821,12 @@ extension DisplayList { func nextUpdate(after _: Time) -> Time { removed.isEmpty ? contents.nextTime : time } + + var properties: Properties { + removed.reduce(into: contents.list.properties) { properties, removed in + properties.formUnion(removed.contents.list.properties) + } + } } } diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift index c64e1871b..72a54eb15 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListPrinter.swift @@ -7,10 +7,6 @@ // ID: 11125C146A81D1913BFBD53B89D010C6 (SwiftUICore) extension DisplayList.Item { - // TODO - // var features: DisplayList.Features { [] } - var properties: DisplayList.Properties { [] } - fileprivate func print(into printer: inout SExpPrinter) { printer.push("item") if identity.value != .zero { diff --git a/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayListTests.swift b/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayListTests.swift index d809d6bbf..ef7444652 100644 --- a/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayListTests.swift +++ b/Tests/OpenSwiftUICoreTests/Render/DisplayList/DisplayListTests.swift @@ -26,6 +26,41 @@ struct DisplayListTests { #expect(combineVersion.value == 999) } + @Test + func properties() { + func item(_ value: DisplayList.Item.Value) -> DisplayList.Item { + DisplayList.Item( + value, + frame: .zero, + identity: .init(decodedValue: 1), + version: .init(decodedValue: 1) + ) + } + + let privacyItem = item(.effect(.properties(.privacySensitive), .init())) + let ignoresEventsItem = item(.effect(.properties(.ignoresEvents), .init())) + let privacyList = DisplayList(privacyItem) + let ignoresEventsList = DisplayList(ignoresEventsItem) + + #expect(privacyList.properties == .privacySensitive) + #expect(ignoresEventsList.properties == .ignoresEvents) + + let maskItem = item(.effect(.mask(privacyList), ignoresEventsList)) + #expect(DisplayList(maskItem).properties == [.privacySensitive, .ignoresEvents]) + + let flattenedItem = item(.content(.init( + .flattened(privacyList, .zero, .init()), + seed: .init(decodedValue: 1) + ))) + #expect(DisplayList(flattenedItem).properties == .privacySensitive) + + let statesItem = item(.states([ + (StrongHash(of: 1), privacyList), + (StrongHash(of: 2), ignoresEventsList), + ])) + #expect(DisplayList(statesItem).properties == [.privacySensitive, .ignoresEvents]) + } + @Suite struct DisplayListItemMatchesTopLevelStructureTests { enum TestCase { From 2372fcc8c5d00ffca2aa629da977c6f57d0c97da Mon Sep 17 00:00:00 2001 From: Kyle Date: Sat, 16 May 2026 18:04:58 +0800 Subject: [PATCH 2/3] Update DisplayList.init and append --- .../Render/DisplayList/DisplayList.swift | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift index fa8473b93..8373f5ecd 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift @@ -2,7 +2,7 @@ // DisplayList.swift // OpenSwiftUICore // -// Audited for 6.0.87 +// Audited for 6.5.4 // Status: WIP // ID: F37E3733E490AA5E3BDC045E3D34D9F8 (SwiftUICore) @@ -68,13 +68,11 @@ package struct DisplayList: Equatable { } package init(_ item: Item) { - // TODO - switch item.value { - case .empty: + if case .empty = item.value { items = [] features = [] properties = [] - default: + } else { items = [item] features = item.features properties = item.properties @@ -82,7 +80,6 @@ package struct DisplayList: Equatable { } package init(_ items: [Item]) { - // TOOD var features: Features = [] var properties: Properties = [] for item in items { @@ -95,13 +92,18 @@ package struct DisplayList: Equatable { } package mutating func append(_ item: Item) { - _openSwiftUIUnimplementedFailure() + if case .empty = item.value { + return + } + items.append(item) + features.formUnion(item.features) + properties.formUnion(item.properties) } package mutating func append(contentsOf other: DisplayList) { - // TODO - items.append(contentsOf: other.items) -// _openSwiftUIUnimplementedFailure() + for item in other.items { + append(item) + } } package var isEmpty: Bool { From aa5dac588665acb8572d4f5238400ca6c21458d6 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sat, 16 May 2026 20:43:55 +0800 Subject: [PATCH 3/3] Update DisplayList API --- .../Render/DisplayList/DisplayList.swift | 113 +++++++++++++++++- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift index 8373f5ecd..ee6f19de3 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift @@ -106,11 +106,48 @@ package struct DisplayList: Equatable { } } + package mutating func reserveCapacity(_ count: Int) { + items.reserveCapacity(count) + } + + package mutating func transform(_ body: (inout Item) -> Void) { + features = [] + properties = [] + guard !items.isEmpty else { + return + } + var transformedFeatures: Features = [] + var transformedProperties: Properties = [] + for index in items.indices { + body(&items[index]) + transformedFeatures.formUnion(items[index].features) + transformedProperties.formUnion(items[index].properties) + } + features = transformedFeatures + properties = transformedProperties + } + + package mutating func translate(by offset: CGSize, version: Version) { + guard !items.isEmpty else { + return + } + for index in items.indices { + items[index].frame.origin += offset + items[index].version.combine(with: version) + } + } + package var isEmpty: Bool { items.isEmpty } - - // TODO + + package var version: Version { + var version = Version() + for item in items { + version.combine(with: item.version) + } + return version + } package func nextUpdate(after time: Time) -> Time { guard !features.contains(.animations) else { @@ -127,6 +164,76 @@ package struct DisplayList: Equatable { } return nextUpdate } + + @discardableResult + package func forEachIdentity(_ body: (Identity, inout Bool) -> Void) -> Bool { + for item in items { + if item.identity != .none { + var stop = false + body(item.identity, &stop) + guard !stop else { + return false + } + } + switch item.value { + case let .content(content): + if case let .flattened(list, _, _) = content.value { + guard list.forEachIdentity(body) else { + return false + } + } + case let .effect(effect, list): + if case let .mask(mask, _) = effect { + guard mask.forEachIdentity(body) else { + return false + } + } + guard list.forEachIdentity(body) else { + return false + } + case let .states(states): + for (_, list) in states { + guard list.forEachIdentity(body) else { + return false + } + } + case .empty: + break + } + } + return true + } + + package func forEachRBDisplayList(_ body: (any ORBDisplayListContents) -> Void) { + for item in items { + switch item.value { + case let .content(content): + switch content.value { + case let .flattened(list, _, _): + list.forEachRBDisplayList(body) + case let .drawing(rbList, _, _): + body(rbList) + default: + break + } + case let .effect(effect, list): + if case let .mask(mask, _) = effect { + mask.forEachRBDisplayList(body) + } + list.forEachRBDisplayList(body) + case let .states(states): + for (_, list) in states { + list.forEachRBDisplayList(body) + } + case .empty: + break + } + } + } + + package static func == (a: DisplayList, b: DisplayList) -> Bool { + a.items == b.items && a.features == b.features && a.properties == b.properties + } } @available(*, unavailable) @@ -313,7 +420,6 @@ extension DisplayList { case rotation(_RotationEffect.Data) case rotation3D(_Rotation3DEffect.Data) - @inline(__always) package var affineTransform: CGAffineTransform? { switch self { case let .affine(transform): @@ -325,7 +431,6 @@ extension DisplayList { } } - @inline(__always) package var projectionTransform: ProjectionTransform? { switch self { case let .projection(transform):