Skip to content

Commit 73e1eb0

Browse files
authored
Add initial rendering support for NSHostingView (#291)
* Add initial rendering support for NSHostingView * Fix file headers
1 parent 8fdbd2f commit 73e1eb0

File tree

11 files changed

+274
-6
lines changed

11 files changed

+274
-6
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// OpenSwiftUI+NSView.h
3+
// COpenSwiftUI
4+
//
5+
// Audited for macOS 15.0
6+
// Status: WIP
7+
8+
#ifndef OpenSwiftUI_NSView_h
9+
#define OpenSwiftUI_NSView_h
10+
11+
#include "OpenSwiftUIBase.h"
12+
13+
#if OPENSWIFTUI_TARGET_OS_OSX
14+
15+
#import <AppKit/AppKit.h>
16+
17+
OPENSWIFTUI_ASSUME_NONNULL_BEGIN
18+
19+
@interface NSView (OpenSwiftUI)
20+
21+
@property (strong, nullable) NSView *maskView;
22+
23+
- (void)setFlipped:(BOOL)flipped;
24+
25+
@end
26+
27+
OPENSWIFTUI_ASSUME_NONNULL_END
28+
29+
#endif /* OPENSWIFTUI_TARGET_OS_OSX */
30+
31+
#endif /* OpenSwiftUI_NSView_h */
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// OpenSwiftUI+NSView.m
3+
// COpenSwiftUI
4+
//
5+
// Audited for macOS 15.0
6+
// Status: WIP
7+
8+
#import "OpenSwiftUI+NSView.h"
9+
10+
#if OPENSWIFTUI_TARGET_OS_OSX
11+
12+
#endif
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// CAChameleonLayer.h
3+
// COpenSwiftUI
4+
//
5+
// Audited for macOS 15.0
6+
// Status: WIP
7+
8+
#include "OpenSwiftUIBase.h"
9+
10+
#if OPENSWIFTUI_TARGET_OS_OSX
11+
12+
#import <QuartzCore/QuartzCore.h>
13+
14+
OPENSWIFTUI_ASSUME_NONNULL_BEGIN
15+
16+
@interface CAChameleonLayer : CALayer
17+
18+
@end
19+
20+
OPENSWIFTUI_ASSUME_NONNULL_END
21+
22+
#endif /* OPENSWIFTUI_TARGET_OS_OSX */
23+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//
2+
// CAChameleonLayer.m
3+
// COpenSwiftUI
4+
//
5+
// Audited for macOS 15.0
6+
// Status: WIP
7+
8+
#import "CAChameleonLayer.h"
9+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// NSGraphicsView.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for macOS 15.0
6+
// Status: WIP
7+
8+
#if os(macOS)
9+
10+
import OpenSwiftUI_SPI
11+
import AppKit
12+
13+
final class _NSGraphicsView: NSView {
14+
var recursiveIgnoreHitTest: Bool = false
15+
16+
var customAcceptsFirstMouse: Bool?
17+
18+
override init(frame frameRect: NSRect) {
19+
super.init(frame: frameRect)
20+
}
21+
22+
required init?(coder: NSCoder) {
23+
super.init(coder: coder)
24+
}
25+
}
26+
27+
#endif

Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSHostingView.swift

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ open class NSHostingView<Content>: NSView, XcodeViewDebugDataProvider where Cont
127127
// TODO
128128
wantsLayer = true
129129
// TODO
130+
renderer.host = self
130131
HostingViewRegistry.shared.add(self)
131132
// TODO
132133
Update.end()
@@ -182,7 +183,7 @@ open class NSHostingView<Content>: NSView, XcodeViewDebugDataProvider where Cont
182183
context.allowsImplicitAnimation = false
183184
isUpdating = true
184185
// TODO
185-
render()
186+
render(targetTimestamp: Time())
186187
// TODO
187188
isUpdating = false
188189
// TODO
@@ -318,8 +319,67 @@ extension NSHostingView: ViewRendererHost {
318319

319320
package func updateScrollableContainerSize() {}
320321

321-
package func renderDisplayList(_ list: DisplayList, asynchronously: Bool, time: Time, nextTime: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time {
322-
.infinity
322+
package func renderDisplayList(
323+
_ list: DisplayList,
324+
asynchronously: Bool,
325+
time: Time,
326+
nextTime: Time,
327+
targetTimestamp: Time?,
328+
version: DisplayList.Version,
329+
maxVersion: DisplayList.Version
330+
) -> Time {
331+
func render() -> Time {
332+
let scale = window?.screen?.backingScaleFactor ?? 1
333+
let environment = DisplayList.ViewRenderer.Environment(contentsScale: scale, opaqueBackground: isOpaque)
334+
#if canImport(SwiftUI, _underlyingVersion: 6.0.87) && _OPENSWIFTUI_SWIFTUI_RENDER
335+
336+
return renderer.swiftUI_render(
337+
rootView: self,
338+
from: list,
339+
time: time,
340+
nextTime: nextTime,
341+
version: version,
342+
maxVersion: maxVersion,
343+
environment: environment
344+
)
345+
346+
#else
347+
return renderer.render(
348+
rootView: self,
349+
from: list,
350+
time: time,
351+
nextTime: nextTime,
352+
version: version,
353+
maxVersion: maxVersion,
354+
environment: environment
355+
)
356+
#endif
357+
}
358+
359+
if asynchronously {
360+
if let renderedTime = renderer.renderAsync(
361+
to: list,
362+
time: time,
363+
nextTime: nextTime,
364+
targetTimestamp: targetTimestamp,
365+
version: version,
366+
maxVersion: maxVersion
367+
) {
368+
return renderedTime
369+
} else {
370+
var renderedTime = nextTime
371+
Update.syncMain {
372+
renderedTime = render()
373+
}
374+
return renderedTime
375+
}
376+
} else {
377+
var renderedTime = nextTime
378+
Update.syncMain {
379+
renderedTime = render()
380+
}
381+
return renderedTime
382+
}
323383
}
324384

325385
package func updateRootView() {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// NSInheritedView.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for macOS 15.0
6+
// Status: WIP
7+
8+
#if os(macOS)
9+
10+
import OpenSwiftUI_SPI
11+
import AppKit
12+
13+
final class _NSInheritedView: NSView {
14+
var hitTestsAsOpaque: Bool = false
15+
16+
override init(frame frameRect: NSRect) {
17+
super.init(frame: frameRect)
18+
}
19+
20+
required init?(coder: NSCoder) {
21+
super.init(coder: coder)
22+
}
23+
}
24+
25+
#endif
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// NSProjectionView.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for macOS 15.0
6+
// Status: WIP
7+
8+
#if os(macOS)
9+
10+
import OpenSwiftUI_SPI
11+
import AppKit
12+
13+
final class _NSProjectionView: NSView {
14+
15+
override var wantsUpdateLayer: Bool { true }
16+
17+
override init(frame frameRect: NSRect) {
18+
super.init(frame: frameRect)
19+
}
20+
21+
required init?(coder: NSCoder) {
22+
super.init(coder: coder)
23+
}
24+
}
25+
26+
#endif

Sources/OpenSwiftUI/Integration/Render/AppKit/NSViewPlatformViewDefinition.swift

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,54 @@
88
#if os(macOS)
99
@_spi(DisplayList_ViewSystem) import OpenSwiftUICore
1010
import AppKit
11+
import OpenSwiftUISymbolDualTestsSupport
12+
import COpenSwiftUI
1113

1214
// TODO
1315
final class NSViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sendable {
1416
override final class var system: PlatformViewDefinition.System { .nsView }
1517

1618
override static func makeView(kind: PlatformViewDefinition.ViewKind) -> AnyObject {
17-
// TODO
18-
return NSView()
19+
let view: NSView
20+
switch kind {
21+
case .chameleonColor:
22+
return makeLayerView(type: CAChameleonLayer.self, kind: kind)
23+
case .projection:
24+
view = _NSProjectionView()
25+
case .mask:
26+
view = _NSGraphicsView()
27+
view.mask = _NSInheritedView()
28+
initView(view.mask!, kind: kind)
29+
default:
30+
view = kind.isContainer ? _NSInheritedView() : _NSGraphicsView()
31+
}
32+
initView(view, kind: kind)
33+
return view
1934
}
2035

36+
private static func initView(_ view: NSView, kind: PlatformViewDefinition.ViewKind) {
37+
view.wantsLayer = true
38+
39+
if kind != .platformView && kind != .platformGroup {
40+
view.setFlipped(true)
41+
view.autoresizesSubviews = false
42+
// TODO - UnifiedHitTestingFeature.isEnabled
43+
// setIgnoreHitTest: true
44+
}
45+
46+
switch kind {
47+
case .color, .image, .shape:
48+
view.layer?.edgeAntialiasingMask = [.layerTopEdge, .layerBottomEdge, .layerLeftEdge, .layerRightEdge]
49+
view.layer?.allowsEdgeAntialiasing = true
50+
break
51+
case .geometry, .projection, .mask:
52+
view.layer?.allowsGroupOpacity = false
53+
view.layer?.allowsGroupBlending = false
54+
default:
55+
break
56+
}
57+
}
58+
2159
override static func makeLayerView(type: CALayer.Type, kind: PlatformViewDefinition.ViewKind) -> AnyObject {
2260
preconditionFailure("TODO")
2361
}

Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewRenderer.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,23 @@ extension DisplayList {
2222
final public class ViewRenderer {
2323
package struct Environment: Equatable {
2424
package var contentsScale: CGFloat
25-
25+
26+
#if os(macOS)
27+
package var opaqueBackground: Bool = false
28+
#endif
29+
2630
package static let invalid = Environment(contentsScale: .zero)
2731

2832
package init(contentsScale: CGFloat) {
2933
self.contentsScale = contentsScale
3034
}
35+
36+
#if os(macOS)
37+
package init(contentsScale: CGFloat, opaqueBackground: Bool) {
38+
self.contentsScale = contentsScale
39+
self.opaqueBackground = opaqueBackground
40+
}
41+
#endif
3142
}
3243

3344
let platform: DisplayList.ViewUpdater.Platform

Sources/OpenSwiftUICore/Render/RendererLeafView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,14 @@ private struct LeafDisplayList<V>: StatefulRule, CustomStringConvertible where V
139139
)
140140
item.canonicalize(options: options)
141141
#if _OPENSWIFTUI_SWIFTUI_RENDER
142+
142143
// FIXME: Remove me after Layout system is implemented
144+
#if os(macOS)
145+
item.frame = CGRect(x: 0, y: 0, width: 500, height: 300)
146+
#elseif os(iOS)
143147
item.frame = CGRect(x: 0, y: 100.333, width: 402, height: 739)
148+
#endif
149+
144150
#endif
145151
value = DisplayList(item)
146152
}

0 commit comments

Comments
 (0)