Skip to content

Commit 8501b3f

Browse files
authored
Add ShapeStyle support (#108)
* Add ShapeStyle and AnyShapeStyle * Add ContentShapeKinds and ContentResponder * Add BitVector implementation * Fix linux build issue
1 parent b250b61 commit 8501b3f

File tree

12 files changed

+630
-42
lines changed

12 files changed

+630
-42
lines changed

Docs/Version.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
RELEASE_2021 - iOS 15.5 - macOS 12.7.1
44

55
RELEASE_2023 - iOS 17.4 - macOS 14.4.1
6+
7+
<!--RELEASE_2024 - iOS 18.0 Beta 1 - macOS 15.0 Beta 1-->
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// BitVector64.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for RELEASE_2021
6+
// Status: WIP
7+
8+
struct BitVector64: OptionSet {
9+
var rawValue: UInt64
10+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// ContentResponder.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for RELEASE_2021
6+
// Status: WIP
7+
8+
import Foundation
9+
internal import COpenSwiftUI
10+
11+
protocol ContentResponder {
12+
func contains(points: [CGPoint], size: CGSize) -> BitVector64
13+
func contentPath(size: CGSize) -> Path
14+
func contentPath(size: CGSize, kind: ContentShapeKinds) -> Path
15+
}
16+
17+
extension ContentResponder {
18+
func contains(points: [CGPoint], size: CGSize) -> BitVector64 {
19+
guard !points.isEmpty else { return BitVector64() }
20+
#if OPENSWIFTUI_RELEASE_2024
21+
// TODO: mapBool
22+
#else
23+
fatalError("TODO")
24+
#endif
25+
}
26+
27+
func contentPath(size: CGSize) -> Path {
28+
Path(CGRect(origin: .zero, size: size))
29+
}
30+
31+
func contentPath(size: CGSize, kind: ContentShapeKinds) -> Path {
32+
if kind == .interaction {
33+
return contentPath(size: size)
34+
} else {
35+
let semantics = Semantics.v3
36+
let shouldReturnEmptyPath = if let forced = Semantics.forced {
37+
forced >= semantics
38+
} else {
39+
dyld_program_sdk_at_least(.init(semantics: semantics))
40+
}
41+
if shouldReturnEmptyPath {
42+
return Path()
43+
} else {
44+
return contentPath(size: size)
45+
}
46+
}
47+
}
48+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// ContentShapeKinds.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for RELEASE_2023
6+
// Status: Complete
7+
8+
import Foundation
9+
10+
/// A kind for the content shape of a view.
11+
///
12+
/// The kind is used by the system to influence various effects, hit-testing,
13+
/// and more.
14+
public struct ContentShapeKinds: OptionSet, Sendable {
15+
16+
/// The corresponding value of the raw type.
17+
///
18+
/// A new instance initialized with `rawValue` will be equivalent to this
19+
/// instance. For example:
20+
///
21+
/// enum PaperSize: String {
22+
/// case A4, A5, Letter, Legal
23+
/// }
24+
///
25+
/// let selectedSize = PaperSize.Letter
26+
/// print(selectedSize.rawValue)
27+
/// // Prints "Letter"
28+
///
29+
/// print(selectedSize == PaperSize(rawValue: selectedSize.rawValue)!)
30+
/// // Prints "true"
31+
public var rawValue: Int
32+
33+
/// Creates a content shape kind.
34+
public init(rawValue: Int) {
35+
self.rawValue = rawValue
36+
}
37+
38+
/// The kind for hit-testing and accessibility.
39+
///
40+
/// Setting a content shape with this kind causes the view to hit-test
41+
/// using the specified shape.
42+
public static let interaction: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 0)
43+
44+
/// The kind for drag and drop previews.
45+
///
46+
/// When using this kind, only the preview shape is affected. To control the
47+
/// shape used to hit-test and start the drag preview, use the `interaction`
48+
/// kind.
49+
@available(watchOS, unavailable)
50+
@available(tvOS, unavailable)
51+
public static let dragPreview: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 1)
52+
53+
/// The kind for context menu previews.
54+
///
55+
/// When using this kind, only the preview shape will be affected. To
56+
/// control the shape used to hit-test and start the context menu
57+
/// presentation, use the `.interaction` kind.
58+
@available(tvOS 17.0, *)
59+
@available(macOS, unavailable)
60+
@available(watchOS, unavailable)
61+
public static let contextMenuPreview: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 2)
62+
63+
/// The kind for hover effects.
64+
///
65+
/// When using this kind, only the preview shape is affected. To control
66+
/// the shape used to hit-test and start the effect, use the `interaction`
67+
/// kind.
68+
///
69+
/// This kind does not affect the `onHover` modifier.
70+
@available(macOS, unavailable)
71+
@available(watchOS, unavailable)
72+
@available(tvOS, unavailable)
73+
public static let hoverEffect: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 3)
74+
75+
#if OPENSWIFTUI_SUPPORT_2023_API
76+
/// The kind for accessibility visuals and sorting.
77+
///
78+
/// Setting a content shape with this kind causes the accessibility frame
79+
/// and path of the view's underlying accessibility element to match the
80+
/// shape without adjusting the hit-testing shape, updating the visual focus
81+
/// ring that assistive apps, such as VoiceOver, draw, as well as how the
82+
/// element is sorted. Updating the accessibility shape is only required if
83+
/// the shape or size used to hit-test significantly diverges from the visual
84+
/// shape of the view.
85+
///
86+
/// To control the shape for accessibility and hit-testing, use the `interaction` kind.
87+
public static let accessibility: ContentShapeKinds = ContentShapeKinds(rawValue: 1 << 4)
88+
#endif
89+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// TODO
2+
public struct Color {}

Sources/OpenSwiftUI/View/Shape/ShapeStyle.swift

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// AnyShapeStyle.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for RELEASE_2021
6+
// Status: Complete
7+
// ID: ABC85937500395B09974756E9F651929
8+
9+
import Foundation
10+
internal import OpenGraphShims
11+
12+
/// A type-erased ShapeStyle value.
13+
@frozen
14+
public struct AnyShapeStyle: ShapeStyle {
15+
@usableFromInline
16+
@frozen
17+
package struct Storage: Equatable {
18+
package var box: AnyShapeStyleBox
19+
20+
@usableFromInline
21+
package static func == (lhs: Storage, rhs: Storage) -> Bool {
22+
if lhs.box === rhs.box {
23+
return true
24+
} else {
25+
return lhs.box.isEqual(to: rhs.box)
26+
}
27+
}
28+
}
29+
30+
package var storage: Storage
31+
32+
/// Create an instance from `style`.
33+
public init<S>(_ style: S) where S: ShapeStyle {
34+
storage = .init(box: ShapeStyleBox(style))
35+
}
36+
37+
public func _apply(to shape: inout _ShapeStyle_Shape) {
38+
storage.box.apply(to: &shape)
39+
}
40+
41+
public static func _apply(to type: inout _ShapeStyle_ShapeType) {
42+
type.result = .none
43+
}
44+
45+
#if OPENSWIFTUI_SUPPORT_2023_API
46+
public typealias Resolved = Never
47+
#endif
48+
}
49+
50+
/// Abstract base class for type-erased ShapeStyle storage.
51+
@usableFromInline
52+
package class AnyShapeStyleBox {
53+
package func apply(to shape: inout _ShapeStyle_Shape) {}
54+
package func isEqual(to other: AnyShapeStyleBox) -> Bool { false }
55+
}
56+
57+
// ID: ABC85937500395B09974756E9F651929
58+
private final class ShapeStyleBox<S>: AnyShapeStyleBox where S: ShapeStyle {
59+
let base: S
60+
61+
init(_ base: S) {
62+
self.base = base
63+
}
64+
65+
override func apply(to shape: inout _ShapeStyle_Shape) {
66+
base._apply(to: &shape)
67+
}
68+
69+
override func isEqual(to other: AnyShapeStyleBox) -> Bool {
70+
let otherBox = other as? Self
71+
return otherBox.map { compareValues(base, $0.base) } ?? false
72+
}
73+
}

0 commit comments

Comments
 (0)