Skip to content

Commit 6e4f705

Browse files
authored
Add Divider support (#646)
1 parent f2ad77f commit 6e4f705

File tree

11 files changed

+394
-18
lines changed

11 files changed

+394
-18
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// DividerUITests.swift
3+
// OpenSwiftUIUITests
4+
5+
import Testing
6+
import SnapshotTesting
7+
8+
@MainActor
9+
@Suite(.snapshots(record: .never, diffTool: diffTool))
10+
struct DividerUITests {
11+
12+
// MARK: - Basic Spacer in HStack
13+
14+
@Test("TODO: Fix colorScheme issue")
15+
func dividerWithColorScheme() {
16+
struct ContentView: View {
17+
var body: some View {
18+
HStack {
19+
VStack {
20+
VStack {
21+
Color.red
22+
Divider()
23+
Color.blue
24+
}
25+
HStack {
26+
Color.red
27+
Divider()
28+
Color.blue
29+
}
30+
}.colorScheme(.light)
31+
VStack {
32+
HStack {
33+
Color.red
34+
Divider()
35+
Color.blue
36+
}
37+
VStack {
38+
Color.red
39+
Divider()
40+
Color.blue
41+
}
42+
}.colorScheme(.dark)
43+
}
44+
.background(Color.green)
45+
}
46+
}
47+
withKnownIssue("Path/Shape is not implemented") {
48+
openSwiftUIAssertSnapshot(of: ContentView())
49+
}
50+
}
51+
}

Scripts/build.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,10 @@ OPENSWIFTUI_ROOT="$(dirname $(dirname $(filepath $0)))"
99

1010
cd $OPENSWIFTUI_ROOT
1111

12+
# Set OPENSWIFTUI_LIB_SWIFT_PATH on Linux if swiftly is installed
13+
if [[ "$(uname)" == "Linux" ]] && command -v swiftly &> /dev/null && [[ -z "$OPENSWIFTUI_LIB_SWIFT_PATH" ]]; then
14+
export OPENSWIFTUI_LIB_SWIFT_PATH="$(swiftly use --print-location)/usr/lib/swift"
15+
echo "Set OPENSWIFTUI_LIB_SWIFT_PATH=$OPENSWIFTUI_LIB_SWIFT_PATH"
16+
fi
17+
1218
swift build
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// DefaultDividerStyle.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for 6.5.4
6+
// Status: Complete
7+
8+
// MARK: - DefaultDividerStyle
9+
10+
extension DividerStyle where Self == DefaultDividerStyle {
11+
static var `default`: DefaultDividerStyle {
12+
.init()
13+
}
14+
}
15+
16+
struct DefaultDividerStyle: DividerStyle {
17+
func makeBody(configuration: Configuration) -> some View {
18+
Divider().dividerStyle(.plain)
19+
}
20+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// Divider.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for 6.5.4
6+
// Status: Complete
7+
// ID: A41482AADD0929733C3343B5E142E952 (SwiftUI)
8+
9+
import OpenAttributeGraphShims
10+
@_spi(ForOpenSwiftUIOnly)
11+
public import OpenSwiftUICore
12+
13+
// MARK: - Divider
14+
15+
/// A visual element that can be used to separate other content.
16+
///
17+
/// When contained in a stack, the divider extends across the minor axis of the
18+
/// stack, or horizontally when not in a stack.
19+
@available(OpenSwiftUI_v1_0, *)
20+
public struct Divider: View, UnaryView, PrimitiveView {
21+
22+
public init() {
23+
_openSwiftUIEmptyStub()
24+
}
25+
26+
nonisolated public static func _makeView(
27+
view: _GraphValue<Self>,
28+
inputs: _ViewInputs
29+
) -> _ViewOutputs {
30+
var newInputs = inputs
31+
if inputs.preferences.requiresPlatformItemList {
32+
newInputs.preferences.requiresPlatformItemList = false
33+
newInputs.requestedTextRepresentation = nil
34+
}
35+
let orientation = inputs.stackOrientation
36+
let child = Attribute(
37+
Child(
38+
orientation: orientation,
39+
dynamicOrientation: orientation == nil ? inputs.dynamicStackOrientation : .init()
40+
)
41+
)
42+
var outputs = ResolvedDivider.makeDebuggableView(
43+
view: .init(child),
44+
inputs: newInputs
45+
)
46+
if let representation = inputs.requestedDividerRepresentation,
47+
representation.shouldMakeRepresentation(inputs: inputs) {
48+
representation.makeRepresentation(inputs: inputs, outputs: &outputs)
49+
}
50+
return outputs
51+
}
52+
53+
private struct Child: Rule {
54+
var orientation: Axis?
55+
@OptionalAttribute var dynamicOrientation: Axis??
56+
57+
var value: ResolvedDivider {
58+
let axis: Axis
59+
if let orientation {
60+
axis = orientation
61+
} else if let dynamicOrientation {
62+
axis = dynamicOrientation ?? .vertical
63+
} else {
64+
axis = .vertical
65+
}
66+
return ResolvedDivider(configuration: .init(orientation: axis.otherAxis))
67+
}
68+
}
69+
}
70+
71+
@available(*, unavailable)
72+
extension Divider: Sendable {}
73+
74+
// MARK: - PlatformDividerRepresentable
75+
76+
package protocol PlatformDividerRepresentable {
77+
static func shouldMakeRepresentation(
78+
inputs: _ViewInputs
79+
) -> Bool
80+
81+
static func makeRepresentation(
82+
inputs: _ViewInputs,
83+
outputs: inout _ViewOutputs
84+
)
85+
}
86+
87+
extension _ViewInputs {
88+
package var requestedDividerRepresentation: (any PlatformDividerRepresentable.Type)? {
89+
get { base.requestedDividerRepresentation }
90+
set { base.requestedDividerRepresentation = newValue }
91+
}
92+
}
93+
94+
extension _GraphInputs {
95+
private struct DividerRepresentationKey: GraphInput {
96+
static var defaultValue: (any PlatformDividerRepresentable.Type)? { nil }
97+
}
98+
99+
package var requestedDividerRepresentation: (any PlatformDividerRepresentable.Type)? {
100+
get { self[DividerRepresentationKey.self] }
101+
set { self[DividerRepresentationKey.self] = newValue }
102+
}
103+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// DividerStyle.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for 6.5.4
6+
// Status: Complete
7+
8+
public import OpenSwiftUICore
9+
10+
// MARK: - DividerStyle
11+
12+
@_spi(Private)
13+
@available(OpenSwiftUI_v3_0, *)
14+
@MainActor
15+
@preconcurrency
16+
public protocol DividerStyle {
17+
associatedtype Body: View
18+
19+
@ViewBuilder
20+
func makeBody(configuration: Configuration) -> Body
21+
22+
typealias Configuration = DividerStyleConfiguration
23+
}
24+
25+
// MARK: - DividerStyleConfiguration
26+
27+
@_spi(Private)
28+
@available(OpenSwiftUI_v3_0, *)
29+
public struct DividerStyleConfiguration {
30+
public var orientation: Axis
31+
}
32+
33+
@_spi(Private)
34+
@available(*, unavailable)
35+
extension DividerStyleConfiguration: Sendable {}
36+
37+
// MARK: - ResolvedDivider
38+
39+
struct ResolvedDivider: StyleableView {
40+
static var defaultStyleModifier: DividerStyleModifier = .init(style: .default)
41+
42+
var configuration: DividerStyleConfiguration
43+
}
44+
45+
// MARK: - DividerStyleModifier
46+
47+
@_spi(Private)
48+
@available(OpenSwiftUI_v3_0, *)
49+
extension View {
50+
nonisolated public func dividerStyle<S>(_ style: S) -> some View where S: DividerStyle {
51+
modifier(DividerStyleModifier(style: style))
52+
}
53+
}
54+
55+
struct DividerStyleModifier<S>: StyleModifier where S: DividerStyle {
56+
var style: S
57+
58+
init(style: S) {
59+
self.style = style
60+
}
61+
62+
func styleBody(configuration: DividerStyleConfiguration) -> some View {
63+
style.makeBody(configuration: configuration)
64+
}
65+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//
2+
// PlainDividerStyle.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for 6.5.4
6+
// Status: Blocked by Shape
7+
// ID: 4E7A7B805FC8F5CEEF99A1E333CA7AA7 (SwiftUI)
8+
9+
import OpenCoreGraphicsShims
10+
@_spi(Private)
11+
import OpenSwiftUICore
12+
13+
// MARK: - PlainDividerStyle
14+
15+
extension DividerStyle where Self == PlainDividerStyle {
16+
static var plain: PlainDividerStyle {
17+
.init()
18+
}
19+
}
20+
21+
struct PlainDividerStyle: DividerStyle {
22+
@Environment(\.dividerThickness)
23+
private var thickness: CGFloat
24+
25+
func makeBody(configuration: DividerStyleConfiguration) -> some View {
26+
// TODO: Shape is not implemented yet
27+
Color(provider: PlainDividerShapeStyle())
28+
// DividerShape(base: Rectangle())
29+
// .fill(PlainDividerShapeStyle())
30+
.frame(
31+
width: configuration.orientation == .horizontal ? nil : thickness,
32+
height: configuration.orientation == .horizontal ? thickness : nil
33+
)
34+
}
35+
}
36+
37+
// MARK: - PlainDividerShapeStyle
38+
39+
struct PlainDividerShapeStyle: ShapeStyle, ColorProvider {
40+
private static let sharedColor = Color(provider: PlainDividerShapeStyle())
41+
42+
func _apply(to shape: inout _ShapeStyle_Shape) {
43+
if shape.environment.isVisionEnabled {
44+
SeparatorShapeStyle()._apply(to: &shape)
45+
} else {
46+
let color: Color
47+
if shape.environment.backgroundMaterial != nil {
48+
color = Color.quaternary
49+
} else {
50+
color = Self.sharedColor
51+
}
52+
color._apply(to: &shape)
53+
}
54+
}
55+
56+
func resolve(in environment: EnvironmentValues) -> Color.Resolved {
57+
if environment.colorScheme == .dark {
58+
Color.Resolved(red: 84 / 255, green: 84 / 255, blue: 88 / 255, opacity: 0.6)
59+
} else {
60+
Color.Resolved(red: 60 / 255, green: 60 / 255, blue: 67 / 255, opacity: 0.29)
61+
}
62+
}
63+
}
64+
65+
// MARK: - DividerShape
66+
67+
private struct DividerShape<S>: Shape where S: Shape {
68+
var base: S
69+
70+
nonisolated func path(in rect: CGRect) -> Path {
71+
base.path(in: rect)
72+
}
73+
74+
nonisolated static var role: ShapeRole {
75+
.separator
76+
}
77+
78+
nonisolated var layoutDirectionBehavior: LayoutDirectionBehavior {
79+
base.layoutDirectionBehavior
80+
}
81+
}

Sources/OpenSwiftUICore/Layout/Separator/Divider.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.

Sources/OpenSwiftUICore/Layout/Separator/Spacer.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@ extension PrimitiveSpacer {
289289
) -> _ViewOutputs {
290290
var outputs = _ViewOutputs()
291291
if inputs.requestsLayoutComputer {
292-
293292
let computer = if let orientation = axis ?? inputs.stackOrientation {
294293
SpacerLayoutComputer(
295294
spacer: view.value,

Sources/OpenSwiftUICore/Shape/ShapeView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ public struct _ShapeView<Content, Style>: View, UnaryView, ShapeStyledLeafView,
136136
self.fillStyle = fillStyle
137137
}
138138

139-
nonisolated public static func _makeView(view: _GraphValue<_ShapeView<Content, Style>>, inputs: _ViewInputs) -> _ViewOutputs {
139+
nonisolated public static func _makeView(
140+
view: _GraphValue<Self>,
141+
inputs: _ViewInputs
142+
) -> _ViewOutputs {
140143
_openSwiftUIUnimplementedFailure()
141144
}
142145

Sources/OpenSwiftUICore/View/Input/ViewInputs.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,15 @@ public struct _ViewInputs {
211211
@available(*, unavailable)
212212
extension _ViewInputs: Sendable {}
213213

214-
// MARK: - DynamicStackOrientation [6.0.87]
214+
// MARK: - DynamicStackOrientation [6.5.4]
215215

216216
package struct DynamicStackOrientation: ViewInput {
217217
package static let defaultValue: OptionalAttribute<Axis?> = .init()
218218
}
219219

220220
extension _ViewInputs {
221221
@inline(__always)
222-
var dynamicStackOrientation: OptionalAttribute<Axis?> {
222+
package var dynamicStackOrientation: OptionalAttribute<Axis?> {
223223
get { self[DynamicStackOrientation.self] }
224224
set { self[DynamicStackOrientation.self] = newValue }
225225
}

0 commit comments

Comments
 (0)