-
Notifications
You must be signed in to change notification settings - Fork 59
Update UIKitDisplayList and AppKitDisplayList #787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // | ||
| // AllowsWindowActivationEventsResponder.swift | ||
| // OpenSwiftUI | ||
| // | ||
| // Audited for 6.5.4 | ||
| // Status: WIP | ||
| // ID: 302179F1EB9AE99B83C6A183C0B4143E (?) | ||
|
|
||
| #if os(macOS) | ||
| // TODO | ||
| class AllowsWindowActivationEventsResponder {} | ||
|
|
||
| // MARK: - AllowsWindowActivationEventsKey | ||
|
|
||
| private struct AllowsWindowActivationEventsKey: EnvironmentKey { | ||
| static var defaultValue: Bool? | ||
| } | ||
|
|
||
| extension EnvironmentValues { | ||
| var allowsWindowActivationEvents: Bool? { | ||
| get { self[AllowsWindowActivationEventsKey.self] } | ||
| set { self[AllowsWindowActivationEventsKey.self] = newValue } | ||
| } | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,22 +2,25 @@ | |
| // AppKitDisplayList.swift | ||
| // OpenSwiftUI | ||
| // | ||
| // Audited for 6.0.87 | ||
| // Status: WIP | ||
| // Audited for 6.5.4 | ||
| // Status: Complete | ||
| // ID: 33EEAA67E0460DA84AE814EA027152BA (SwiftUI) | ||
|
|
||
| #if os(macOS) | ||
| @_spi(DisplayList_ViewSystem) import OpenSwiftUICore | ||
| @_spi(ForOpenSwiftUIOnly) | ||
| @_spi(DisplayList_ViewSystem) | ||
| import OpenSwiftUICore | ||
| import AppKit | ||
| import OpenSwiftUISymbolDualTestsSupport | ||
| import COpenSwiftUI | ||
| import QuartzCore_Private | ||
| import OpenRenderBoxShims | ||
| import OpenSwiftUI_SPI | ||
|
|
||
| // MARK: - NSViewPlatformViewDefinition [TODO] | ||
| // MARK: - NSViewPlatformViewDefinition | ||
|
|
||
| final class NSViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sendable { | ||
| override final class var system: PlatformViewDefinition.System { .nsView } | ||
| override static var system: PlatformViewDefinition.System { .nsView } | ||
|
|
||
| override static func makeView(kind: PlatformViewDefinition.ViewKind) -> AnyObject { | ||
| let view: NSView | ||
|
|
@@ -27,47 +30,25 @@ final class NSViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sen | |
| case .projection: | ||
| view = _NSProjectionView() | ||
| case .mask: | ||
| view = _NSGraphicsView() | ||
| view.mask = _NSInheritedView() | ||
| initView(view.mask!, kind: kind) | ||
| view = _NSInheritedView() | ||
| let maskView = _NSInheritedView() | ||
| view.mask = maskView | ||
| initView(maskView, kind: .inherited) | ||
| 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 | ||
| } | ||
| } | ||
|
|
||
| // Audited for 6.5.4 | ||
| override static func makeLayerView(type: CALayer.Type, kind: PlatformViewDefinition.ViewKind) -> AnyObject { | ||
| let cls: NSView.Type | ||
| if kind == .shape { | ||
| switch kind { | ||
| case .shape: | ||
| cls = _NSShapeHitTestingView.self | ||
| } else if kind == .platformLayer { | ||
| case .platformLayer: | ||
| cls = _NSPlatformLayerView.self | ||
| } else { | ||
| default: | ||
| cls = kind.isContainer ? _NSInheritedView.self : _NSGraphicsView.self | ||
| } | ||
| let view = cls.init() | ||
|
|
@@ -78,37 +59,97 @@ final class NSViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sen | |
| return view | ||
| } | ||
|
|
||
| override class func makePlatformView(view: AnyObject, kind: PlatformViewDefinition.ViewKind) { | ||
| override static func makePlatformView(view: AnyObject, kind: PlatformViewDefinition.ViewKind) { | ||
| Self.initView(view as! NSView, kind: kind) | ||
| } | ||
|
|
||
| override static func makeDrawingView(options: PlatformDrawableOptions) -> any PlatformDrawable { | ||
| let view: NSView & PlatformDrawable | ||
| if options.isAccelerated && ORBDevice.isSupported() { | ||
| view = RBDrawingView(options: options) | ||
| } else { | ||
| view = CGDrawingView(options: options) | ||
| } | ||
| view.layerContentsRedrawPolicy = .onSetNeedsDisplay | ||
| view.layerContentsPlacement = .topLeft | ||
| initView(view, kind: .drawing) | ||
| return view | ||
| } | ||
|
|
||
| override static func setPath(_ path: Path, shapeView: AnyObject) { | ||
| let view = unsafeBitCast(shapeView, to: _NSShapeHitTestingView.self) | ||
| guard let view = shapeView as? _NSShapeHitTestingView else { return } | ||
| view.path = path | ||
| } | ||
|
|
||
| override class func setProjectionTransform(_ transform: ProjectionTransform, projectionView: AnyObject) { | ||
| override static func setProjectionTransform(_ transform: ProjectionTransform, projectionView: AnyObject) { | ||
| guard let view = projectionView as? _NSProjectionView else { | ||
| return | ||
| } | ||
| view.projectionTransform = transform | ||
| view.layer?.transform = .init(transform) | ||
| view.layer?.transform = CATransform3D(transform) | ||
| } | ||
|
|
||
| override static func getRBLayer(drawingView: AnyObject) -> AnyObject? { | ||
| guard let rbView = drawingView as? RBDrawingView else { return nil } | ||
| return rbView.layer | ||
| } | ||
|
|
||
| override static func setIgnoresEvents(_ state: Bool, of view: AnyObject) { | ||
| guard !ResponderBasedHitTesting.enabled else { return } | ||
| if UnifiedHitTestingFeature.isEnabled { | ||
| if let customizing = view as? RecursiveIgnoreHitTestCustomizing { | ||
| customizing.recursiveIgnoreHitTest = state | ||
| } | ||
| } else { | ||
| let view = unsafeBitCast(view, to: NSView.self) | ||
| view.ignoreHitTest = state | ||
| } | ||
| } | ||
|
|
||
| override static func setAllowsWindowActivationEvents(_ value: Bool?, for view: AnyObject) { | ||
| guard !ResponderBasedHitTesting.enabled else { return } | ||
| if let customizing = view as? AcceptsFirstMouseCustomizing { | ||
| customizing.customAcceptsFirstMouse = value | ||
| } | ||
| } | ||
|
|
||
| override class func setAllowsWindowActivationEvents(_ value: Bool?, for view: AnyObject) { | ||
| _openSwiftUIUnimplementedWarning() | ||
| override static func setHitTestsAsOpaque(_ value: Bool, for view: AnyObject) { | ||
| guard !ResponderBasedHitTesting.enabled else { return } | ||
| if let customizing = view as? HitTestsAsOpaqueCustomizing { | ||
| customizing.hitTestsAsOpaque = value | ||
| } | ||
| } | ||
|
|
||
| override class func setHitTestsAsOpaque(_ value: Bool, for view: AnyObject) { | ||
| _openSwiftUIUnimplementedWarning() | ||
| private static func initView(_ view: NSView, kind: PlatformViewDefinition.ViewKind) { | ||
| view.wantsLayer = true | ||
| if kind != .platformView && kind != .platformGroup { | ||
| view.setFlipped(true) | ||
| view.autoresizesSubviews = false | ||
| view.clipsToBounds = false | ||
| if !UnifiedHitTestingFeature.isEnabled { | ||
| view.ignoreHitTest = true | ||
| } | ||
| } | ||
| switch kind { | ||
| case .color, .image, .shape: | ||
| let layer = view.layer! | ||
| layer.edgeAntialiasingMask = [.layerTopEdge, .layerBottomEdge, .layerLeftEdge, .layerRightEdge] | ||
| layer.allowsEdgeAntialiasing = true | ||
| case .inherited, .geometry, .projection, .mask: | ||
| let layer = view.layer! | ||
| layer.allowsGroupOpacity = false | ||
| layer.allowsGroupBlending = false | ||
| default: | ||
| break | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // MARK: - _NSGraphicsView | ||
|
|
||
| typealias PlatformGraphicsView = _NSGraphicsView | ||
|
|
||
| class _NSGraphicsView: NSView { | ||
| class _NSGraphicsView: NSView, RecursiveIgnoreHitTestCustomizing, AcceptsFirstMouseCustomizing { | ||
| var recursiveIgnoreHitTest: Bool = false | ||
|
|
||
| var customAcceptsFirstMouse: Bool? | ||
|
|
@@ -120,13 +161,43 @@ class _NSGraphicsView: NSView { | |
| required init?(coder: NSCoder) { | ||
| super.init(coder: coder) | ||
| } | ||
|
|
||
| override func hitTest(_ point: NSPoint) -> NSView? { | ||
| guard !ResponderBasedHitTesting.enabled else { | ||
| return super.hitTest(point) | ||
| } | ||
| if UnifiedHitTestingFeature.isEnabled { | ||
| guard !recursiveIgnoreHitTest, | ||
| alphaValue >= ViewResponder.minOpacityForHitTest else { | ||
| return nil | ||
| } | ||
| return super.hitTest(point) | ||
| } else { | ||
| guard !ignoreHitTest, frame.contains(point) else { | ||
| return nil | ||
| } | ||
| return self | ||
| } | ||
| } | ||
|
|
||
| override func _nextResponder(for event: NSEvent?) -> NSResponder? { | ||
| nextResponder | ||
| } | ||
|
|
||
| override func acceptsFirstMouse(for event: NSEvent?) -> Bool { | ||
| guard !ResponderBasedHitTesting.enabled, | ||
| let value = effectiveAcceptsFirstMouse else { | ||
| return super.acceptsFirstMouse(for: event) | ||
| } | ||
| return value | ||
| } | ||
| } | ||
|
|
||
| // MARK: - _NSInheritedView | ||
|
|
||
| typealias PlatformInheritedView = _NSInheritedView | ||
|
|
||
| class _NSInheritedView: _NSGraphicsView { | ||
| class _NSInheritedView: _NSGraphicsView, HitTestsAsOpaqueCustomizing { | ||
| var hitTestsAsOpaque: Bool = false | ||
|
|
||
| override init(frame frameRect: NSRect) { | ||
|
|
@@ -136,11 +207,30 @@ class _NSInheritedView: _NSGraphicsView { | |
| required init?(coder: NSCoder) { | ||
| super.init(coder: coder) | ||
| } | ||
|
|
||
| override func hitTest(_ point: NSPoint) -> NSView? { | ||
| guard !ResponderBasedHitTesting.enabled, | ||
| UnifiedHitTestingFeature.isEnabled else { | ||
| return super.hitTest(point) | ||
| } | ||
| guard !isHiddenOrHasHiddenAncestor, !recursiveIgnoreHitTest else { | ||
| return nil | ||
| } | ||
| for subview in subviews.reversed() { | ||
| let convertedPoint = convert(point, from: superview) | ||
| if let result = subview.hitTest(convertedPoint) { | ||
| return result | ||
| } | ||
| } | ||
| guard hitTestsAsOpaque, frame.contains(point) else { | ||
| return nil | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Severity: high 🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage. |
||
| return self | ||
| } | ||
| } | ||
|
|
||
| // MARK: - _NSProjectionView [6.5.4] | ||
| // MARK: - _NSProjectionView | ||
|
|
||
| @objc | ||
| private class _NSProjectionView: _NSInheritedView { | ||
| var projectionTransform: ProjectionTransform | ||
|
|
||
|
|
@@ -158,11 +248,11 @@ private class _NSProjectionView: _NSInheritedView { | |
|
|
||
| override func _updateLayerGeometryFromView() { | ||
| super._updateLayerGeometryFromView() | ||
| layer?.transform = .init(projectionTransform) | ||
| layer?.transform = CATransform3D(projectionTransform) | ||
| } | ||
| } | ||
|
|
||
| // MARK: - _NSShapeHitTestingView [WIP] | ||
| // MARK: - _NSShapeHitTestingView | ||
|
|
||
| @objc | ||
| private class _NSShapeHitTestingView: _NSGraphicsView { | ||
|
|
@@ -179,15 +269,22 @@ private class _NSShapeHitTestingView: _NSGraphicsView { | |
| } | ||
|
|
||
| override func hitTest(_ point: NSPoint) -> NSView? { | ||
| // path.contains(, eoFill: false) | ||
| _openSwiftUIUnimplementedWarning() | ||
| return nil | ||
| guard !ResponderBasedHitTesting.enabled else { | ||
| return super.hitTest(point) | ||
| } | ||
| if UnifiedHitTestingFeature.isEnabled, super.hitTest(point) == nil { | ||
| return nil | ||
| } | ||
| let point = convert(point, from: superview) | ||
| guard path.contains(point, eoFill: false) else { | ||
| return nil | ||
| } | ||
| return self | ||
| } | ||
| } | ||
|
|
||
| // MARK: - _NSPlatformLayerView | ||
|
|
||
| @objc | ||
| private class _NSPlatformLayerView: _NSGraphicsView { | ||
| override init(frame frameRect: NSRect) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| super.init(frame: frameRect) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NSView.hitTest(_:)receivespointin the receiver’s local coordinates, so checkingframe.contains(point)(superview coordinates) can mis-evaluate hit-testing when the view’s frame origin isn’t (0,0).Severity: high
Other Locations
Sources/OpenSwiftUI/Render/DisplayList/AppKitDisplayList.swift:232🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.