diff --git a/Sources/COpenSwiftUI/AppKit/OpenSwiftUI+NSView.h b/Sources/COpenSwiftUI/AppKit/OpenSwiftUI+NSView.h new file mode 100644 index 00000000..d980469a --- /dev/null +++ b/Sources/COpenSwiftUI/AppKit/OpenSwiftUI+NSView.h @@ -0,0 +1,31 @@ +// +// OpenSwiftUI+NSView.h +// COpenSwiftUI +// +// Audited for macOS 15.0 +// Status: WIP + +#ifndef OpenSwiftUI_NSView_h +#define OpenSwiftUI_NSView_h + +#include "OpenSwiftUIBase.h" + +#if OPENSWIFTUI_TARGET_OS_OSX + +#import + +OPENSWIFTUI_ASSUME_NONNULL_BEGIN + +@interface NSView (OpenSwiftUI) + +@property (strong, nullable) NSView *maskView; + +- (void)setFlipped:(BOOL)flipped; + +@end + +OPENSWIFTUI_ASSUME_NONNULL_END + +#endif /* OPENSWIFTUI_TARGET_OS_OSX */ + +#endif /* OpenSwiftUI_NSView_h */ diff --git a/Sources/COpenSwiftUI/AppKit/OpenSwiftUI+NSView.m b/Sources/COpenSwiftUI/AppKit/OpenSwiftUI+NSView.m new file mode 100644 index 00000000..a36d4ec0 --- /dev/null +++ b/Sources/COpenSwiftUI/AppKit/OpenSwiftUI+NSView.m @@ -0,0 +1,12 @@ +// +// OpenSwiftUI+NSView.m +// COpenSwiftUI +// +// Audited for macOS 15.0 +// Status: WIP + +#import "OpenSwiftUI+NSView.h" + +#if OPENSWIFTUI_TARGET_OS_OSX + +#endif diff --git a/Sources/COpenSwiftUI/QuartzCore/CAChameleonLayer.h b/Sources/COpenSwiftUI/QuartzCore/CAChameleonLayer.h new file mode 100644 index 00000000..97536bb5 --- /dev/null +++ b/Sources/COpenSwiftUI/QuartzCore/CAChameleonLayer.h @@ -0,0 +1,23 @@ +// +// CAChameleonLayer.h +// COpenSwiftUI +// +// Audited for macOS 15.0 +// Status: WIP + +#include "OpenSwiftUIBase.h" + +#if OPENSWIFTUI_TARGET_OS_OSX + +#import + +OPENSWIFTUI_ASSUME_NONNULL_BEGIN + +@interface CAChameleonLayer : CALayer + +@end + +OPENSWIFTUI_ASSUME_NONNULL_END + +#endif /* OPENSWIFTUI_TARGET_OS_OSX */ + diff --git a/Sources/COpenSwiftUI/QuartzCore/CAChameleonLayer.m b/Sources/COpenSwiftUI/QuartzCore/CAChameleonLayer.m new file mode 100644 index 00000000..785f294a --- /dev/null +++ b/Sources/COpenSwiftUI/QuartzCore/CAChameleonLayer.m @@ -0,0 +1,9 @@ +// +// CAChameleonLayer.m +// COpenSwiftUI +// +// Audited for macOS 15.0 +// Status: WIP + +#import "CAChameleonLayer.h" + diff --git a/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSGraphicsView.swift b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSGraphicsView.swift new file mode 100644 index 00000000..bb3ec8e3 --- /dev/null +++ b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSGraphicsView.swift @@ -0,0 +1,27 @@ +// +// NSGraphicsView.swift +// OpenSwiftUI +// +// Audited for macOS 15.0 +// Status: WIP + +#if os(macOS) + +import OpenSwiftUI_SPI +import AppKit + +final class _NSGraphicsView: NSView { + var recursiveIgnoreHitTest: Bool = false + + var customAcceptsFirstMouse: Bool? + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } +} + +#endif diff --git a/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSHostingView.swift b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSHostingView.swift index 35caa88b..9cec921b 100644 --- a/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSHostingView.swift +++ b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSHostingView.swift @@ -127,6 +127,7 @@ open class NSHostingView: NSView, XcodeViewDebugDataProvider where Cont // TODO wantsLayer = true // TODO + renderer.host = self HostingViewRegistry.shared.add(self) // TODO Update.end() @@ -182,7 +183,7 @@ open class NSHostingView: NSView, XcodeViewDebugDataProvider where Cont context.allowsImplicitAnimation = false isUpdating = true // TODO - render() + render(targetTimestamp: Time()) // TODO isUpdating = false // TODO @@ -318,8 +319,67 @@ extension NSHostingView: ViewRendererHost { package func updateScrollableContainerSize() {} - package func renderDisplayList(_ list: DisplayList, asynchronously: Bool, time: Time, nextTime: Time, targetTimestamp: Time?, version: DisplayList.Version, maxVersion: DisplayList.Version) -> Time { - .infinity + package func renderDisplayList( + _ list: DisplayList, + asynchronously: Bool, + time: Time, + nextTime: Time, + targetTimestamp: Time?, + version: DisplayList.Version, + maxVersion: DisplayList.Version + ) -> Time { + func render() -> Time { + let scale = window?.screen?.backingScaleFactor ?? 1 + let environment = DisplayList.ViewRenderer.Environment(contentsScale: scale, opaqueBackground: isOpaque) + #if canImport(SwiftUI, _underlyingVersion: 6.0.87) && _OPENSWIFTUI_SWIFTUI_RENDER + + return renderer.swiftUI_render( + rootView: self, + from: list, + time: time, + nextTime: nextTime, + version: version, + maxVersion: maxVersion, + environment: environment + ) + + #else + return renderer.render( + rootView: self, + from: list, + time: time, + nextTime: nextTime, + version: version, + maxVersion: maxVersion, + environment: environment + ) + #endif + } + + if asynchronously { + if let renderedTime = renderer.renderAsync( + to: list, + time: time, + nextTime: nextTime, + targetTimestamp: targetTimestamp, + version: version, + maxVersion: maxVersion + ) { + return renderedTime + } else { + var renderedTime = nextTime + Update.syncMain { + renderedTime = render() + } + return renderedTime + } + } else { + var renderedTime = nextTime + Update.syncMain { + renderedTime = render() + } + return renderedTime + } } package func updateRootView() { diff --git a/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSInheritedView.swift b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSInheritedView.swift new file mode 100644 index 00000000..c80b7346 --- /dev/null +++ b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSInheritedView.swift @@ -0,0 +1,25 @@ +// +// NSInheritedView.swift +// OpenSwiftUI +// +// Audited for macOS 15.0 +// Status: WIP + +#if os(macOS) + +import OpenSwiftUI_SPI +import AppKit + +final class _NSInheritedView: NSView { + var hitTestsAsOpaque: Bool = false + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } +} + +#endif diff --git a/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSProjectionView.swift b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSProjectionView.swift new file mode 100644 index 00000000..f5497c38 --- /dev/null +++ b/Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSProjectionView.swift @@ -0,0 +1,26 @@ +// +// NSProjectionView.swift +// OpenSwiftUI +// +// Audited for macOS 15.0 +// Status: WIP + +#if os(macOS) + +import OpenSwiftUI_SPI +import AppKit + +final class _NSProjectionView: NSView { + + override var wantsUpdateLayer: Bool { true } + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } +} + +#endif diff --git a/Sources/OpenSwiftUI/Integration/Render/AppKit/NSViewPlatformViewDefinition.swift b/Sources/OpenSwiftUI/Integration/Render/AppKit/NSViewPlatformViewDefinition.swift index 7afc54ea..aee03f7a 100644 --- a/Sources/OpenSwiftUI/Integration/Render/AppKit/NSViewPlatformViewDefinition.swift +++ b/Sources/OpenSwiftUI/Integration/Render/AppKit/NSViewPlatformViewDefinition.swift @@ -8,16 +8,54 @@ #if os(macOS) @_spi(DisplayList_ViewSystem) import OpenSwiftUICore import AppKit +import OpenSwiftUISymbolDualTestsSupport +import COpenSwiftUI // TODO final class NSViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sendable { override final class var system: PlatformViewDefinition.System { .nsView } override static func makeView(kind: PlatformViewDefinition.ViewKind) -> AnyObject { - // TODO - return NSView() + let view: NSView + switch kind { + case .chameleonColor: + return makeLayerView(type: CAChameleonLayer.self, kind: kind) + case .projection: + view = _NSProjectionView() + case .mask: + view = _NSGraphicsView() + view.mask = _NSInheritedView() + initView(view.mask!, kind: kind) + default: + view = kind.isContainer ? _NSInheritedView() : _NSGraphicsView() + } + initView(view, kind: kind) + return view } + private static func initView(_ view: NSView, kind: PlatformViewDefinition.ViewKind) { + view.wantsLayer = true + + if kind != .platformView && kind != .platformGroup { + view.setFlipped(true) + view.autoresizesSubviews = false + // TODO - UnifiedHitTestingFeature.isEnabled + // setIgnoreHitTest: true + } + + switch kind { + case .color, .image, .shape: + view.layer?.edgeAntialiasingMask = [.layerTopEdge, .layerBottomEdge, .layerLeftEdge, .layerRightEdge] + view.layer?.allowsEdgeAntialiasing = true + break + case .geometry, .projection, .mask: + view.layer?.allowsGroupOpacity = false + view.layer?.allowsGroupBlending = false + default: + break + } + } + override static func makeLayerView(type: CALayer.Type, kind: PlatformViewDefinition.ViewKind) -> AnyObject { preconditionFailure("TODO") } diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewRenderer.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewRenderer.swift index c51395db..63ade70d 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewRenderer.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewRenderer.swift @@ -22,12 +22,23 @@ extension DisplayList { final public class ViewRenderer { package struct Environment: Equatable { package var contentsScale: CGFloat - + + #if os(macOS) + package var opaqueBackground: Bool = false + #endif + package static let invalid = Environment(contentsScale: .zero) package init(contentsScale: CGFloat) { self.contentsScale = contentsScale } + + #if os(macOS) + package init(contentsScale: CGFloat, opaqueBackground: Bool) { + self.contentsScale = contentsScale + self.opaqueBackground = opaqueBackground + } + #endif } let platform: DisplayList.ViewUpdater.Platform diff --git a/Sources/OpenSwiftUICore/Render/RendererLeafView.swift b/Sources/OpenSwiftUICore/Render/RendererLeafView.swift index 13d9e85a..efe1ebf0 100644 --- a/Sources/OpenSwiftUICore/Render/RendererLeafView.swift +++ b/Sources/OpenSwiftUICore/Render/RendererLeafView.swift @@ -139,8 +139,14 @@ private struct LeafDisplayList: StatefulRule, CustomStringConvertible where V ) item.canonicalize(options: options) #if _OPENSWIFTUI_SWIFTUI_RENDER + // FIXME: Remove me after Layout system is implemented + #if os(macOS) + item.frame = CGRect(x: 0, y: 0, width: 500, height: 300) + #elseif os(iOS) item.frame = CGRect(x: 0, y: 100.333, width: 402, height: 739) + #endif + #endif value = DisplayList(item) }