Skip to content

Commit ffe44d8

Browse files
authored
Add CAHostingLayerPlatformDefinition (#788)
1 parent 9c71bdb commit ffe44d8

File tree

8 files changed

+305
-6
lines changed

8 files changed

+305
-6
lines changed

Sources/OpenSwiftUI/Render/DisplayList/UIKitDisplayList.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class UIViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sen
2626
case .mask:
2727
view = _UIGraphicsView()
2828
view.mask = _UIInheritedView()
29-
initView(view.mask!, kind: kind)
29+
initView(view.mask!, kind: .inherited)
3030
default:
3131
view = kind.isContainer ? _UIInheritedView() : _UIGraphicsView()
3232
}
@@ -95,13 +95,12 @@ final class UIViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sen
9595
view.layer.anchorPoint = .zero
9696
switch kind {
9797
case .color, .image, .shape:
98-
view.layer.allowsEdgeAntialiasing = true
99-
break
100-
case .geometry, .projection, .affine3D, .mask, .platformEffect:
98+
let layer = view.layer
99+
layer.allowsEdgeAntialiasing = true
100+
case .inherited, .geometry, .projection, .affine3D, .mask, .platformEffect:
101101
let layer = view.layer
102102
layer.allowsGroupOpacity = false
103103
layer.allowsGroupBlending = false
104-
break
105104
default:
106105
break
107106
}
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
//
2+
// CAHostingLayerPlatformDefinition.swift
3+
// OpenSwiftUICore
4+
//
5+
// Audited for 6.5.4
6+
// Status: Complete
7+
// ID: E2A63CF3FB15FAD08FBE4CE6D0C83E51 (SwiftUICore)
8+
9+
#if canImport(QuartzCore)
10+
import OpenSwiftUI_SPI
11+
import QuartzCore
12+
import QuartzCore_Private
13+
import OpenRenderBoxShims
14+
15+
// MARK: - CAHostingLayerPlatformDefinition
16+
17+
final class CAHostingLayerPlatformDefinition: PlatformViewDefinition, @unchecked Sendable {
18+
override static var system: PlatformViewDefinition.System { .caLayer }
19+
20+
override static func makeView(kind: PlatformViewDefinition.ViewKind) -> AnyObject {
21+
let layer = CALayer()
22+
if kind == .mask {
23+
let maskLayer = CALayer()
24+
layer.mask = maskLayer
25+
initLayer(layer.mask!, kind: .inherited)
26+
}
27+
initLayer(layer, kind: kind)
28+
return layer
29+
}
30+
31+
override static func makeLayerView(type: CALayer.Type, kind: PlatformViewDefinition.ViewKind) -> AnyObject {
32+
let layer = type.init()
33+
initLayer(layer, kind: kind)
34+
return layer
35+
}
36+
37+
override static func makePlatformView(view: AnyObject, kind: PlatformViewDefinition.ViewKind) {
38+
let layer = view as! CALayer
39+
Self.initLayer(layer, kind: kind)
40+
}
41+
42+
override static func makeDrawingView(options: PlatformDrawableOptions) -> any PlatformDrawable {
43+
let layer: CALayer & PlatformDrawable
44+
if options.isAccelerated && ORBDevice.isSupported() {
45+
layer = RBDrawingLayer(options: options)
46+
} else {
47+
layer = CGDrawingLayer(options: options)
48+
}
49+
layer.contentsGravity = .topLeft
50+
initLayer(layer, kind: .drawing)
51+
return layer
52+
}
53+
54+
override static func setPath(_ path: Path, shapeView: AnyObject) {
55+
_openSwiftUIEmptyStub()
56+
}
57+
58+
override static func setProjectionTransform(_ transform: ProjectionTransform, projectionView: AnyObject) {
59+
let layer = projectionView as! CALayer
60+
layer.transform = CATransform3D(transform)
61+
}
62+
63+
override static func getRBLayer(drawingView: AnyObject) -> AnyObject? {
64+
drawingView as? ORBLayer
65+
}
66+
67+
override static func setIgnoresEvents(_ state: Bool, of view: AnyObject) {
68+
let layer = unsafeBitCast(view, to: CALayer.self)
69+
layer.allowsHitTesting = !state
70+
}
71+
72+
private static func initLayer(_ layer: CALayer, kind: PlatformViewDefinition.ViewKind) {
73+
layer.delegate = CAPlatformLayerDelegate.shared
74+
layer.anchorPoint = .zero
75+
switch kind {
76+
case .color, .image, .shape:
77+
layer.allowsEdgeAntialiasing = true
78+
case .inherited, .geometry, .projection, .affine3D, .mask:
79+
layer.allowsGroupOpacity = false
80+
layer.allowsGroupBlending = false
81+
default:
82+
break
83+
}
84+
}
85+
}
86+
87+
// MARK: - CAPlatformLayerDelegate
88+
89+
final class CAPlatformLayerDelegate: NSObject, CALayerDelegate {
90+
static let shared = CAPlatformLayerDelegate()
91+
92+
func action(for layer: CALayer, forKey event: String) -> (any CAAction)? {
93+
_CANullAction()
94+
}
95+
}
96+
97+
// MARK: - CGDrawingLayer
98+
99+
private final class CGDrawingLayer: CALayer, PlatformDrawable {
100+
var content: PlatformDrawableContent = .init()
101+
var state: PlatformDrawableContent.State = .init()
102+
var options: PlatformDrawableOptions {
103+
didSet {
104+
guard oldValue != options else { return }
105+
updateOptions()
106+
}
107+
}
108+
109+
init(options: PlatformDrawableOptions) {
110+
self.options = options
111+
super.init()
112+
updateOptions()
113+
}
114+
115+
@available(*, unavailable)
116+
required init?(coder: NSCoder) {
117+
fatalError("init(coder:) has not been implemented")
118+
}
119+
120+
private func updateOptions() {
121+
isOpaque = options.isOpaque
122+
contentsFormat = options.caLayerContentsFormat
123+
}
124+
125+
static var allowsContentsMultiplyColor: Bool { true }
126+
127+
func update(content: PlatformDrawableContent?, required: Bool) -> Bool {
128+
if let content {
129+
self.content = content
130+
}
131+
setNeedsDisplay()
132+
return true
133+
}
134+
135+
func makeAsyncUpdate(
136+
content: PlatformDrawableContent,
137+
required: Bool,
138+
layer: CALayer,
139+
bounds: CGRect
140+
) -> (() -> Void)? {
141+
nil
142+
}
143+
144+
func setContentsScale(_ scale: CGFloat) {
145+
contentsScale = scale
146+
}
147+
148+
func drawForTesting(in displayList: ORBDisplayList) {
149+
var state = PlatformDrawableContent.State()
150+
content.draw(in: displayList, size: bounds.size, state: &state)
151+
}
152+
153+
override func draw(in ctx: CGContext) {
154+
content.draw(
155+
in: ctx,
156+
size: bounds.size,
157+
contentsScale: contentsScale,
158+
state: &state
159+
)
160+
}
161+
}
162+
163+
// MARK: - RBDrawingLayer
164+
165+
private final class RBDrawingLayer: ORBLayer, PlatformDrawable {
166+
var options: PlatformDrawableOptions {
167+
didSet {
168+
guard oldValue != options else { return }
169+
updateOptions()
170+
}
171+
}
172+
173+
private struct State {
174+
var content: PlatformDrawableContent = .init()
175+
var renderer: PlatformDrawableContent.State = .init()
176+
}
177+
178+
@AtomicBox
179+
private var state: RBDrawingLayer.State = .init()
180+
181+
init(options: PlatformDrawableOptions) {
182+
self.options = options
183+
super.init()
184+
updateOptions()
185+
}
186+
187+
@available(*, unavailable)
188+
required init?(coder: NSCoder) {
189+
fatalError("init(coder:) has not been implemented")
190+
}
191+
192+
private func updateOptions() {
193+
isOpaque = options.isOpaque
194+
options.update(rbLayer: self)
195+
}
196+
197+
static var allowsContentsMultiplyColor: Bool { false }
198+
199+
func update(content: PlatformDrawableContent?, required: Bool) -> Bool {
200+
guard required || !options.rendersAsynchronously || isDrawableAvailable else {
201+
return false
202+
}
203+
if let content {
204+
state.content = content
205+
}
206+
setNeedsDisplay()
207+
return true
208+
}
209+
210+
func makeAsyncUpdate(
211+
content: PlatformDrawableContent,
212+
required: Bool,
213+
layer: CALayer,
214+
bounds: CGRect
215+
) -> (() -> Void)? {
216+
guard required || !options.rendersAsynchronously || isDrawableAvailable else {
217+
return nil
218+
}
219+
return { [self] in
220+
state.content = content
221+
display(withBounds: bounds) { displayList in
222+
self.draw(in: displayList, size: bounds.size)
223+
}
224+
}
225+
}
226+
227+
func setContentsScale(_ scale: CGFloat) {
228+
contentsScale = scale
229+
}
230+
231+
func drawForTesting(in displayList: ORBDisplayList) {
232+
var s = PlatformDrawableContent.State()
233+
state.content.draw(in: displayList, size: bounds.size, state: &s)
234+
}
235+
236+
private func draw(in displayList: ORBDisplayList, size: CGSize) {
237+
var renderer = $state.access { state in
238+
let saved = state.renderer
239+
state.renderer = PlatformDrawableContent.State()
240+
return saved
241+
}
242+
let content = state.content
243+
content.draw(in: displayList, size: size, state: &renderer)
244+
$state.access { state in
245+
state.renderer = renderer
246+
}
247+
}
248+
}
249+
250+
#endif

Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ open class PlatformViewDefinition: @unchecked Sendable {
1919
public struct System: Hashable, Sendable {
2020
public static let uiView = PlatformViewDefinition.System(base: .uiView)
2121
public static let nsView = PlatformViewDefinition.System(base: .nsView)
22+
static let caLayer = PlatformViewDefinition.System(base: .caLayer)
23+
2224
var base: ViewSystem
2325
}
2426

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// CANullAction.h
3+
// OpenSwiftUI_SPI
4+
//
5+
// Status: Complete
6+
7+
#ifndef CANullAction_h
8+
#define CANullAction_h
9+
10+
#include "OpenSwiftUIBase.h"
11+
12+
#if __has_include(<QuartzCore/QuartzCore.h>)
13+
14+
#import <QuartzCore/QuartzCore.h>
15+
16+
/// Returns kCFNull as a CAAction to suppress implicit layer animations.
17+
OPENSWIFTUI_EXPORT
18+
id<CAAction> _Nonnull _CANullAction(void);
19+
20+
#endif /* __has_include(<QuartzCore/QuartzCore.h>) */
21+
22+
#endif /* CANullAction_h */
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// CANullAction.m
3+
// OpenSwiftUI_SPI
4+
//
5+
// Status: Complete
6+
7+
#import "CANullAction.h"
8+
9+
#if __has_include(<QuartzCore/QuartzCore.h>)
10+
11+
id<CAAction> _CANullAction(void) {
12+
return (id<CAAction>)kCFNull;
13+
}
14+
15+
#endif /* __has_include(<QuartzCore/QuartzCore.h>) */

Sources/OpenSwiftUI_SPI/Shims/QuartzCore/CALayerPrivate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ typedef NSString *CALayerContentsScaling NS_TYPED_ENUM;
2121
@property (nonatomic, assign) BOOL allowsDisplayCompositing_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(allowsDisplayCompositing);
2222
@property (nonatomic, assign, readonly) BOOL hasBeenCommitted_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(hasBeenCommitted);
2323
@property (nonatomic, assign) BOOL allowsGroupBlending_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(allowsGroupBlending);
24+
@property (nonatomic, assign) BOOL allowsHitTesting_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(allowsHitTesting);
2425

2526
@property (nonatomic, assign) uint64_t openSwiftUI_viewTestProperties;
2627

Sources/OpenSwiftUI_SPI/Shims/QuartzCore/CALayerPrivate.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ - (void)setAllowsGroupBlending_openswiftui_safe_wrapper:(BOOL)allows {
4141
func(self, selector, allows);
4242
}
4343

44+
- (BOOL)allowsHitTesting_openswiftui_safe_wrapper {
45+
OPENSWIFTUI_SAFE_WRAPPER_IMP(BOOL, @"allowsHitTesting", YES);
46+
return func(self, selector);
47+
}
48+
49+
- (void)setAllowsHitTesting_openswiftui_safe_wrapper:(BOOL)allows {
50+
OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"setAllowsHitTesting:", , BOOL);
51+
func(self, selector, allows);
52+
}
53+
4454
- (uint64_t)openSwiftUI_viewTestProperties {
4555
NSNumber *properties = [self valueForKey:@"_viewTestProperties"];
4656
return properties.integerValue;

Sources/OpenSwiftUI_SPI/module.modulemap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module OpenSwiftUI_SPI {
2-
umbrella "Overlay/CoreGraphics"
2+
umbrella "Overlay"
33
export *
44

55
module Util {

0 commit comments

Comments
 (0)