Skip to content

Commit e492250

Browse files
authored
Add automaticPadding API support (#422)
* Add AutomaticPadding support * Add automatic padding tests
1 parent 09ca3bd commit e492250

File tree

4 files changed

+174
-11
lines changed

4 files changed

+174
-11
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// DefaultPaddingUITests.swift
3+
// OpenSwiftUIUITests
4+
5+
import SnapshotTesting
6+
import Testing
7+
8+
@MainActor
9+
@Suite(.snapshots(record: .never, diffTool: diffTool))
10+
struct DefaultPaddingUITests {
11+
// MARK: - Basic Padding
12+
13+
@Test
14+
func defaultAutomaticPadding() {
15+
struct ContentView: View {
16+
var body: some View {
17+
Color.red
18+
._automaticPadding()
19+
.background(Color.blue)
20+
}
21+
}
22+
openSwiftUIAssertSnapshot(of: ContentView())
23+
}
24+
25+
@Test
26+
func customAutomaticPadding() {
27+
struct ContentView: View {
28+
var body: some View {
29+
Color.red
30+
._automaticPadding(
31+
.init(
32+
top: 20,
33+
leading: 20,
34+
bottom: 20,
35+
trailing: 20
36+
)
37+
)
38+
.background(Color.blue)
39+
}
40+
}
41+
openSwiftUIAssertSnapshot(of: ContentView())
42+
}
43+
44+
@Test
45+
func mixAutomaticPadding() {
46+
struct ContentView: View {
47+
var body: some View {
48+
VStack {
49+
Color.red
50+
._automaticPadding()
51+
Color.green
52+
._automaticPadding()
53+
._ignoresAutomaticPadding(false)
54+
Color.blue
55+
._automaticPadding()
56+
._ignoresAutomaticPadding(true)
57+
}
58+
.background(Color.yellow)
59+
}
60+
}
61+
openSwiftUIAssertSnapshot(of: ContentView())
62+
}
63+
}

Sources/OpenSwiftUICore/Layout/FixedSizeLayout.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ extension _FixedSizeLayout: UnaryLayout {
5252
height: vertical ? nil : proposedSize.height)
5353
)
5454
}
55+
56+
package func layoutPriority(child: LayoutProxy) -> Double {
57+
child.layoutPriority
58+
}
5559
}
5660

5761
// MARK: - View + fixedSize [6.4.41]

Sources/OpenSwiftUICore/Layout/Modifier/DefaultPadding.swift

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
// OpenSwiftUICore
44
//
55
// Audited for 6.5.4
6-
// Status: WIP
6+
// Status: Complete
77
// ID: 47C1BD8C61550BB60F4F3D12F752D53D (SwiftUICore)
88

9-
private struct DefaultPaddingKey: EnvironmentKey {
10-
static let defaultValue: EdgeInsets = .init(_all: 16.0)
11-
}
9+
import Foundation
10+
import OpenGraphShims
1211

1312
@available(OpenSwiftUI_v3_0, *)
1413
extension EnvironmentValues {
@@ -19,17 +18,114 @@ extension EnvironmentValues {
1918
}
2019
}
2120

21+
// MARK: - DefaultPaddingKey
22+
23+
private struct DefaultPaddingKey: EnvironmentKey {
24+
static let defaultValue: EdgeInsets = .init(_all: 16.0)
25+
}
26+
2227
@available(OpenSwiftUI_v2_0, *)
2328
extension View {
2429
/// For use by children in containers to disable the automatic padding that
2530
/// those containers apply.
2631
public func _ignoresAutomaticPadding(_ ignoresPadding: Bool) -> some View {
27-
_openSwiftUIUnimplementedFailure()
32+
modifier(IgnoresAutomaticPaddingLayout(ignoresPadding: ignoresPadding))
2833
}
2934

3035
/// Applies explicit padding to a view that allows being disabled by that
3136
/// view using `_ignoresAutomaticPadding`.
3237
public func _automaticPadding(_ edgeInsets: EdgeInsets? = nil) -> some View {
33-
_openSwiftUIUnimplementedFailure()
38+
modifier(AutomaticPaddingViewModifier(padding: edgeInsets))
39+
}
40+
}
41+
42+
// MARK: - AutomaticPaddingViewModifier
43+
44+
private struct AutomaticPaddingViewModifier: MultiViewModifier, PrimitiveViewModifier {
45+
var padding: EdgeInsets?
46+
47+
nonisolated static func _makeView(
48+
modifier: _GraphValue<AutomaticPaddingViewModifier>,
49+
inputs: _ViewInputs,
50+
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
51+
) -> _ViewOutputs {
52+
guard inputs.requestsLayoutComputer || inputs.needsGeometry else {
53+
return body(_Graph(), inputs)
54+
}
55+
let layout = Attribute(PaddingLayout(
56+
modifier: modifier.value,
57+
environment: inputs.environment,
58+
childLayoutComputer: .init()
59+
))
60+
return PaddingLayout.Value.makeDebuggableView(
61+
modifier: _GraphValue(layout),
62+
inputs: inputs,
63+
body: body
64+
)
65+
}
66+
67+
struct PaddingLayout: Rule, AsyncAttribute {
68+
@Attribute var modifier: AutomaticPaddingViewModifier
69+
@Attribute var environment: EnvironmentValues
70+
@OptionalAttribute var childLayoutComputer: LayoutComputer?
71+
72+
typealias Value = ModifiedContent<WrappedLayout, _SafeAreaInsetsModifier>
73+
74+
var value: Value {
75+
let ignoresAutomaticPadding = childLayoutComputer?.ignoresAutomaticPadding() ?? false
76+
let padding: EdgeInsets
77+
if ignoresAutomaticPadding {
78+
padding = .zero
79+
} else {
80+
padding = modifier.padding ?? environment.defaultPadding
81+
}
82+
return WrappedLayout(base: .init(insets: padding))
83+
.concat(_SafeAreaInsetsModifier(insets: padding))
84+
}
85+
86+
struct WrappedLayout: UnaryLayout {
87+
var base: _PaddingLayout
88+
89+
func placement(
90+
of child: LayoutProxy,
91+
in context: PlacementContextType
92+
) -> _Placement {
93+
base.placement(of: child, in: context)
94+
}
95+
96+
func sizeThatFits(
97+
in proposedSize: _ProposedSize,
98+
context: SizeAndSpacingContext,
99+
child: LayoutProxy
100+
) -> CGSize {
101+
base.sizeThatFits(in: proposedSize, context: context, child: child)
102+
}
103+
}
104+
}
105+
}
106+
107+
// MARK: - IgnoresAutomaticPaddingLayout
108+
109+
private struct IgnoresAutomaticPaddingLayout: UnaryLayout {
110+
var ignoresPadding: Bool
111+
112+
func placement(of child: LayoutProxy, in context: PlacementContext) -> _Placement {
113+
_Placement(
114+
proposedSize: context.proposedSize,
115+
aligning: .center,
116+
in: context.size
117+
)
118+
}
119+
120+
func sizeThatFits(in proposedSize: _ProposedSize, context: SizeAndSpacingContext, child: LayoutProxy) -> CGSize {
121+
child.size(in: proposedSize)
122+
}
123+
124+
func layoutPriority(child: LayoutProxy) -> Double {
125+
child.layoutPriority
126+
}
127+
128+
func ignoresAutomaticPadding(child: LayoutProxy) -> Bool {
129+
ignoresPadding
34130
}
35131
}

Sources/OpenSwiftUICore/Layout/UnaryLayout.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ package protocol UnaryLayout: Animatable, MultiViewModifier, PrimitiveViewModifi
3131
}
3232

3333
extension UnaryLayout {
34+
package func spacing(in context: SizeAndSpacingContext, child: LayoutProxy) -> Spacing {
35+
child.spacing()
36+
}
37+
3438
package func layoutPriority(child: LayoutProxy) -> Double {
35-
child.layoutPriority
39+
.zero
3640
}
3741

3842
package func ignoresAutomaticPadding(child: LayoutProxy) -> Bool {
@@ -46,10 +50,6 @@ extension UnaryLayout {
4650
) -> _ViewOutputs {
4751
makeViewImpl(modifier: modifier, inputs: inputs, body: body)
4852
}
49-
50-
package func spacing(in context: SizeAndSpacingContext, child: LayoutProxy) -> Spacing {
51-
child.spacing()
52-
}
5353
}
5454

5555
// MARK: - DerivedLayout [6.4.41]

0 commit comments

Comments
 (0)