@swiftbrowser/swift 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/Package.swift +23 -0
  2. package/README.md +12 -2
  3. package/Sources/Charts/Charts.swift +65 -0
  4. package/Sources/FoundationBridge/FoundationBridge.swift +77 -0
  5. package/Sources/FoundationBridge/Locale.swift +86 -0
  6. package/Sources/FoundationBridge/Timer.swift +48 -0
  7. package/Sources/FoundationBridge/UITextChecker.swift +30 -0
  8. package/Sources/FoundationLite/Common.swift +3 -0
  9. package/Sources/FoundationLite/FoundationLite.swift +171 -0
  10. package/Sources/FoundationLite/Interaction.swift +146 -0
  11. package/Sources/FoundationLite/Timer.swift +48 -0
  12. package/Sources/SwiftUI/API/Accessibility.swift +132 -0
  13. package/Sources/SwiftUI/API/Alert.swift +221 -0
  14. package/Sources/SwiftUI/API/Animation.swift +253 -0
  15. package/Sources/SwiftUI/API/App.swift +57 -0
  16. package/Sources/SwiftUI/API/Color.swift +161 -0
  17. package/Sources/SwiftUI/API/Controls.swift +268 -0
  18. package/Sources/SwiftUI/API/CustomLayout.swift +403 -0
  19. package/Sources/SwiftUI/API/Decorations.swift +92 -0
  20. package/Sources/SwiftUI/API/EditMode.swift +269 -0
  21. package/Sources/SwiftUI/API/Effects.swift +290 -0
  22. package/Sources/SwiftUI/API/Environment.swift +355 -0
  23. package/Sources/SwiftUI/API/Focus.swift +129 -0
  24. package/Sources/SwiftUI/API/Font.swift +131 -0
  25. package/Sources/SwiftUI/API/Gauge.swift +289 -0
  26. package/Sources/SwiftUI/API/Geometry.swift +89 -0
  27. package/Sources/SwiftUI/API/GeometryReader.swift +124 -0
  28. package/Sources/SwiftUI/API/Gestures.swift +342 -0
  29. package/Sources/SwiftUI/API/Grid.swift +188 -0
  30. package/Sources/SwiftUI/API/LabelStyle.swift +117 -0
  31. package/Sources/SwiftUI/API/Layout.swift +165 -0
  32. package/Sources/SwiftUI/API/Lists.swift +276 -0
  33. package/Sources/SwiftUI/API/Menu.swift +51 -0
  34. package/Sources/SwiftUI/API/Modifiers.swift +243 -0
  35. package/Sources/SwiftUI/API/Navigation.swift +371 -0
  36. package/Sources/SwiftUI/API/ObservableObject.swift +279 -0
  37. package/Sources/SwiftUI/API/OnChange.swift +48 -0
  38. package/Sources/SwiftUI/API/Phase8Controls.swift +394 -0
  39. package/Sources/SwiftUI/API/Picker.swift +176 -0
  40. package/Sources/SwiftUI/API/Presentation.swift +109 -0
  41. package/Sources/SwiftUI/API/Primitives.swift +390 -0
  42. package/Sources/SwiftUI/API/Publishers.swift +107 -0
  43. package/Sources/SwiftUI/API/RowActions.swift +83 -0
  44. package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
  45. package/Sources/SwiftUI/API/ShapeStyle.swift +311 -0
  46. package/Sources/SwiftUI/API/State.swift +127 -0
  47. package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
  48. package/Sources/SwiftUI/API/Tabs.swift +119 -0
  49. package/Sources/SwiftUI/API/Tasks.swift +35 -0
  50. package/Sources/SwiftUI/API/Timeline.swift +212 -0
  51. package/Sources/SwiftUI/API/Toolbar.swift +215 -0
  52. package/Sources/SwiftUI/API/View.swift +207 -0
  53. package/Sources/SwiftUI/API/ViewModifier.swift +40 -0
  54. package/Sources/SwiftUI/API/VisualEffect.swift +82 -0
  55. package/Sources/SwiftUI/Charts/Axis.swift +319 -0
  56. package/Sources/SwiftUI/Charts/Chart.swift +646 -0
  57. package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
  58. package/Sources/SwiftUI/Charts/Marks.swift +204 -0
  59. package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
  60. package/Sources/SwiftUI/Charts/Scales.swift +326 -0
  61. package/Sources/SwiftUI/Runtime/Executor.swift +83 -0
  62. package/Sources/SwiftUI/Runtime/Exports.swift +65 -0
  63. package/Sources/SwiftUI/Runtime/JSON.swift +71 -0
  64. package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
  65. package/Sources/SwiftUI/Runtime/Ops.swift +106 -0
  66. package/Sources/SwiftUI/Runtime/Reconciler.swift +210 -0
  67. package/Sources/SwiftUI/Runtime/Runtime.swift +1367 -0
  68. package/Sources/_FoundationCommon/Bundle.swift +82 -0
  69. package/Sources/_FoundationCommon/UserDefaults.swift +170 -0
  70. package/Sources/_FoundationCommon/_FoundationCommon.swift +10 -0
  71. package/index.d.ts +2 -0
  72. package/index.mjs +16 -0
  73. package/package.json +28 -4
  74. package/sync-sources.mjs +19 -0
@@ -0,0 +1,289 @@
1
+ // Gauge and its styles, plus `labelsHidden()`. See docs/ops-protocol.md (Phase 6).
2
+
3
+ // MARK: - Styles
4
+
5
+ /// The appearance of a `Gauge`. The shim's styles are names the renderer draws;
6
+ /// a custom conformance renders as `automatic`.
7
+ public protocol GaugeStyle {}
8
+
9
+ protocol _NamedGaugeStyle {
10
+ var _name: String { get }
11
+ }
12
+
13
+ public struct DefaultGaugeStyle: GaugeStyle, _NamedGaugeStyle, Sendable {
14
+ public init() {}
15
+ var _name: String { "automatic" }
16
+ }
17
+
18
+ public struct LinearCapacityGaugeStyle: GaugeStyle, _NamedGaugeStyle, Sendable {
19
+ public init() {}
20
+ var _name: String { "linearCapacity" }
21
+ }
22
+
23
+ public struct AccessoryLinearGaugeStyle: GaugeStyle, _NamedGaugeStyle, Sendable {
24
+ public init() {}
25
+ var _name: String { "accessoryLinear" }
26
+ }
27
+
28
+ public struct AccessoryCircularGaugeStyle: GaugeStyle, _NamedGaugeStyle, Sendable {
29
+ public init() {}
30
+ var _name: String { "accessoryCircular" }
31
+ }
32
+
33
+ public struct AccessoryCircularCapacityGaugeStyle: GaugeStyle, _NamedGaugeStyle, Sendable {
34
+ public init() {}
35
+ var _name: String { "accessoryCircularCapacity" }
36
+ }
37
+
38
+ extension GaugeStyle where Self == DefaultGaugeStyle {
39
+ public static var automatic: DefaultGaugeStyle { DefaultGaugeStyle() }
40
+ }
41
+ extension GaugeStyle where Self == LinearCapacityGaugeStyle {
42
+ public static var linearCapacity: LinearCapacityGaugeStyle { LinearCapacityGaugeStyle() }
43
+ }
44
+ extension GaugeStyle where Self == AccessoryLinearGaugeStyle {
45
+ public static var accessoryLinear: AccessoryLinearGaugeStyle { AccessoryLinearGaugeStyle() }
46
+ }
47
+ extension GaugeStyle where Self == AccessoryCircularGaugeStyle {
48
+ public static var accessoryCircular: AccessoryCircularGaugeStyle { AccessoryCircularGaugeStyle() }
49
+ }
50
+ extension GaugeStyle where Self == AccessoryCircularCapacityGaugeStyle {
51
+ public static var accessoryCircularCapacity: AccessoryCircularCapacityGaugeStyle { AccessoryCircularCapacityGaugeStyle() }
52
+ }
53
+
54
+ // MARK: - Environment
55
+
56
+ enum _GaugeStyleKey: EnvironmentKey { static let defaultValue = "automatic" }
57
+ enum _LabelsHiddenKey: EnvironmentKey { static let defaultValue = false }
58
+
59
+ extension EnvironmentValues {
60
+ /// The name of the gauge style set by `.gaugeStyle(_:)`.
61
+ var gaugeStyle: String {
62
+ get { self[_GaugeStyleKey.self] }
63
+ set { self[_GaugeStyleKey.self] = newValue }
64
+ }
65
+
66
+ /// Set by `.labelsHidden()`: controls omit their label.
67
+ var labelsHidden: Bool {
68
+ get { self[_LabelsHiddenKey.self] }
69
+ set { self[_LabelsHiddenKey.self] = newValue }
70
+ }
71
+ }
72
+
73
+ extension View {
74
+ public func gaugeStyle<S: GaugeStyle>(_ style: S) -> some View {
75
+ environment(\.gaugeStyle, (style as? any _NamedGaugeStyle)?._name ?? "automatic")
76
+ }
77
+
78
+ /// Hides the labels of controls in this view (gauges, toggles, pickers).
79
+ public func labelsHidden() -> some View {
80
+ environment(\.labelsHidden, true)
81
+ }
82
+ }
83
+
84
+ // MARK: - Gauge
85
+
86
+ /// Shows a value within a range. Kind `gauge`; the children are the label
87
+ /// (unless `EmptyView` or hidden) followed by the current value label.
88
+ public struct Gauge<Label: View, CurrentValueLabel: View, BoundsLabel: View, MarkedValueLabel: View> {
89
+ /// `value` normalized into 0…1 over `bounds`.
90
+ let normalizedValue: Double
91
+ let label: Label
92
+ let currentValueLabel: CurrentValueLabel
93
+
94
+ static func normalize<V: BinaryFloatingPoint>(_ value: V, in bounds: ClosedRange<V>) -> Double {
95
+ let lower = Double(bounds.lowerBound)
96
+ let upper = Double(bounds.upperBound)
97
+ guard upper > lower else { return 0 }
98
+ let fraction = (Double(value) - lower) / (upper - lower)
99
+ return max(0, min(1, fraction))
100
+ }
101
+ }
102
+
103
+ extension Gauge where CurrentValueLabel == EmptyView, BoundsLabel == EmptyView, MarkedValueLabel == EmptyView {
104
+ public init<V: BinaryFloatingPoint>(value: V, in bounds: ClosedRange<V> = 0...1,
105
+ @ViewBuilder label: () -> Label) {
106
+ normalizedValue = Self.normalize(value, in: bounds)
107
+ self.label = label()
108
+ currentValueLabel = EmptyView()
109
+ }
110
+ }
111
+
112
+ extension Gauge where BoundsLabel == EmptyView, MarkedValueLabel == EmptyView {
113
+ public init<V: BinaryFloatingPoint>(value: V, in bounds: ClosedRange<V> = 0...1,
114
+ @ViewBuilder label: () -> Label,
115
+ @ViewBuilder currentValueLabel: () -> CurrentValueLabel) {
116
+ normalizedValue = Self.normalize(value, in: bounds)
117
+ self.label = label()
118
+ self.currentValueLabel = currentValueLabel()
119
+ }
120
+ }
121
+
122
+ extension Gauge where MarkedValueLabel == EmptyView {
123
+ /// Bounds labels are accepted for source compatibility and not rendered.
124
+ public init<V: BinaryFloatingPoint>(value: V, in bounds: ClosedRange<V> = 0...1,
125
+ @ViewBuilder label: () -> Label,
126
+ @ViewBuilder currentValueLabel: () -> CurrentValueLabel,
127
+ @ViewBuilder minimumValueLabel: () -> BoundsLabel,
128
+ @ViewBuilder maximumValueLabel: () -> BoundsLabel) {
129
+ normalizedValue = Self.normalize(value, in: bounds)
130
+ self.label = label()
131
+ self.currentValueLabel = currentValueLabel()
132
+ }
133
+ }
134
+
135
+ extension Gauge: _PrimitiveView {}
136
+ extension Gauge: _HostView {
137
+ var _kind: String { "gauge" }
138
+
139
+ private var hasCurrentValueLabel: Bool { !(CurrentValueLabel.self is EmptyView.Type) }
140
+
141
+ private func hasLabel(in environment: EnvironmentValues) -> Bool {
142
+ !(Label.self is EmptyView.Type) && !environment.labelsHidden
143
+ }
144
+
145
+ func _props(in environment: EnvironmentValues) -> _JSON {
146
+ .object([
147
+ "value": .number(normalizedValue),
148
+ "style": .string(environment.gaugeStyle),
149
+ "hasLabel": .bool(hasLabel(in: environment)),
150
+ "hasCurrentValueLabel": .bool(hasCurrentValueLabel),
151
+ ])
152
+ }
153
+
154
+ var _content: [any View] {
155
+ // `_content` has no environment, so the label goes through a group
156
+ // that reads `labelsHidden` itself and drops the label when set.
157
+ var children: [any View] = []
158
+ if !(Label.self is EmptyView.Type) { children.append(_GaugeLabel(label: label)) }
159
+ if hasCurrentValueLabel { children.append(currentValueLabel) }
160
+ return children
161
+ }
162
+ }
163
+
164
+ // MARK: - ProgressView
165
+
166
+ /// Progress toward a total. A determinate progress view draws as a
167
+ /// `linearCapacity` gauge with its label; an indeterminate one (no value)
168
+ /// has no spinner in the protocol and shows its label, or an hourglass
169
+ /// symbol when it has none.
170
+ public struct ProgressView<Label: View, CurrentValueLabel: View>: View {
171
+ /// Progress in 0…1, nil when indeterminate.
172
+ let fraction: Double?
173
+ let label: Label?
174
+ let currentValueLabel: CurrentValueLabel?
175
+
176
+ public var body: some View {
177
+ if let fraction {
178
+ Gauge(value: fraction, in: 0...1) {
179
+ if let label { label }
180
+ } currentValueLabel: {
181
+ if let currentValueLabel { currentValueLabel }
182
+ }
183
+ .gaugeStyle(.linearCapacity)
184
+ } else if let label {
185
+ label.foregroundStyle(.secondary)
186
+ } else {
187
+ Image(systemName: "hourglass").foregroundStyle(.secondary)
188
+ }
189
+ }
190
+
191
+ static func fraction<V: BinaryFloatingPoint>(_ value: V?, total: V) -> Double? {
192
+ guard let value, total > 0 else { return nil }
193
+ return max(0, min(1, Double(value) / Double(total)))
194
+ }
195
+ }
196
+
197
+ extension ProgressView where Label == EmptyView, CurrentValueLabel == EmptyView {
198
+ public init() {
199
+ fraction = nil
200
+ label = nil
201
+ currentValueLabel = nil
202
+ }
203
+
204
+ public init<V: BinaryFloatingPoint>(value: V?, total: V = 1.0) {
205
+ fraction = Self.fraction(value, total: total)
206
+ label = nil
207
+ currentValueLabel = nil
208
+ }
209
+ }
210
+
211
+ extension ProgressView where Label == Text, CurrentValueLabel == EmptyView {
212
+ public init<S: StringProtocol>(_ title: S) {
213
+ fraction = nil
214
+ label = Text(title)
215
+ currentValueLabel = nil
216
+ }
217
+
218
+ public init(_ titleKey: LocalizedStringKey) {
219
+ fraction = nil
220
+ label = Text(titleKey)
221
+ currentValueLabel = nil
222
+ }
223
+
224
+ public init<S: StringProtocol, V: BinaryFloatingPoint>(_ title: S, value: V?, total: V = 1.0) {
225
+ fraction = Self.fraction(value, total: total)
226
+ label = Text(title)
227
+ currentValueLabel = nil
228
+ }
229
+
230
+ public init<V: BinaryFloatingPoint>(_ titleKey: LocalizedStringKey, value: V?, total: V = 1.0) {
231
+ fraction = Self.fraction(value, total: total)
232
+ label = Text(titleKey)
233
+ currentValueLabel = nil
234
+ }
235
+ }
236
+
237
+ extension ProgressView where CurrentValueLabel == EmptyView {
238
+ public init(@ViewBuilder label: () -> Label) {
239
+ fraction = nil
240
+ self.label = label()
241
+ currentValueLabel = nil
242
+ }
243
+
244
+ public init<V: BinaryFloatingPoint>(value: V?, total: V = 1.0, @ViewBuilder label: () -> Label) {
245
+ fraction = Self.fraction(value, total: total)
246
+ self.label = label()
247
+ currentValueLabel = nil
248
+ }
249
+ }
250
+
251
+ extension ProgressView {
252
+ public init<V: BinaryFloatingPoint>(value: V?, total: V = 1.0, @ViewBuilder label: () -> Label,
253
+ @ViewBuilder currentValueLabel: () -> CurrentValueLabel) {
254
+ fraction = Self.fraction(value, total: total)
255
+ self.label = label()
256
+ self.currentValueLabel = currentValueLabel()
257
+ }
258
+ }
259
+
260
+ /// `.progressViewStyle`: accepted for source compatibility.
261
+ public protocol ProgressViewStyle {}
262
+ public struct DefaultProgressViewStyle: ProgressViewStyle { public init() {} }
263
+ public struct LinearProgressViewStyle: ProgressViewStyle { public init() {} }
264
+ public struct CircularProgressViewStyle: ProgressViewStyle { public init() {} }
265
+ extension ProgressViewStyle where Self == DefaultProgressViewStyle {
266
+ public static var automatic: DefaultProgressViewStyle { DefaultProgressViewStyle() }
267
+ }
268
+ extension ProgressViewStyle where Self == LinearProgressViewStyle {
269
+ public static var linear: LinearProgressViewStyle { LinearProgressViewStyle() }
270
+ }
271
+ extension ProgressViewStyle where Self == CircularProgressViewStyle {
272
+ public static var circular: CircularProgressViewStyle { CircularProgressViewStyle() }
273
+ }
274
+
275
+ extension View {
276
+ public func progressViewStyle<S: ProgressViewStyle>(_ style: S) -> some View { self }
277
+ }
278
+
279
+ /// Contributes the gauge's label unless `labelsHidden` is set in the environment.
280
+ struct _GaugeLabel<Label: View> {
281
+ let label: Label
282
+ @Environment(\.labelsHidden) private var labelsHidden
283
+
284
+ init(label: Label) { self.label = label }
285
+ }
286
+ extension _GaugeLabel: _PrimitiveView {}
287
+ extension _GaugeLabel: _GroupView {
288
+ var _children: [any View] { labelsHidden ? [] : [label] }
289
+ }
@@ -0,0 +1,89 @@
1
+ /// SwiftUI vends `CGFloat` through CoreGraphics. The shim provides its own
2
+ /// alias and geometry types and never imports Foundation (that would link its
3
+ /// 10 MB and ICU into every lean app). An app that also imports Foundation
4
+ /// gets both declarations; the CLI's generated `_SwiftBrowserFoundation.swift`
5
+ /// settles them with typealiases on the shim's (docs/ops-protocol.md, "Phase 11").
6
+ public typealias CGFloat = Double
7
+
8
+ public enum Edge: Int8, CaseIterable, Hashable, Sendable {
9
+ case top, leading, bottom, trailing
10
+
11
+ public struct Set: OptionSet, Sendable {
12
+ public let rawValue: Int8
13
+ public init(rawValue: Int8) { self.rawValue = rawValue }
14
+
15
+ public static let top = Set(rawValue: 1 << 0)
16
+ public static let leading = Set(rawValue: 1 << 1)
17
+ public static let bottom = Set(rawValue: 1 << 2)
18
+ public static let trailing = Set(rawValue: 1 << 3)
19
+ public static let horizontal: Set = [.leading, .trailing]
20
+ public static let vertical: Set = [.top, .bottom]
21
+ public static let all: Set = [.top, .leading, .bottom, .trailing]
22
+ }
23
+ }
24
+
25
+ public struct EdgeInsets: Equatable, Sendable {
26
+ public var top: CGFloat
27
+ public var leading: CGFloat
28
+ public var bottom: CGFloat
29
+ public var trailing: CGFloat
30
+
31
+ public init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) {
32
+ self.top = top
33
+ self.leading = leading
34
+ self.bottom = bottom
35
+ self.trailing = trailing
36
+ }
37
+
38
+ public init() { self.init(top: 0, leading: 0, bottom: 0, trailing: 0) }
39
+
40
+ var _json: _JSON {
41
+ .object(["top": .number(top), "leading": .number(leading),
42
+ "bottom": .number(bottom), "trailing": .number(trailing)])
43
+ }
44
+ }
45
+
46
+ public struct HorizontalAlignment: Equatable, Sendable {
47
+ let name: String
48
+ public static let leading = HorizontalAlignment(name: "leading")
49
+ public static let center = HorizontalAlignment(name: "center")
50
+ public static let trailing = HorizontalAlignment(name: "trailing")
51
+ }
52
+
53
+ public struct VerticalAlignment: Equatable, Sendable {
54
+ let name: String
55
+ public static let top = VerticalAlignment(name: "top")
56
+ public static let center = VerticalAlignment(name: "center")
57
+ public static let bottom = VerticalAlignment(name: "bottom")
58
+ /// Baseline alignment is approximated as `center` by the renderer.
59
+ public static let firstTextBaseline = VerticalAlignment(name: "center")
60
+ public static let lastTextBaseline = VerticalAlignment(name: "center")
61
+ }
62
+
63
+ public struct Alignment: Equatable, Sendable {
64
+ public var horizontal: HorizontalAlignment
65
+ public var vertical: VerticalAlignment
66
+
67
+ public init(horizontal: HorizontalAlignment, vertical: VerticalAlignment) {
68
+ self.horizontal = horizontal
69
+ self.vertical = vertical
70
+ }
71
+
72
+ public static let center = Alignment(horizontal: .center, vertical: .center)
73
+ public static let leading = Alignment(horizontal: .leading, vertical: .center)
74
+ public static let trailing = Alignment(horizontal: .trailing, vertical: .center)
75
+ public static let top = Alignment(horizontal: .center, vertical: .top)
76
+ public static let bottom = Alignment(horizontal: .center, vertical: .bottom)
77
+ public static let topLeading = Alignment(horizontal: .leading, vertical: .top)
78
+ public static let topTrailing = Alignment(horizontal: .trailing, vertical: .top)
79
+ public static let bottomLeading = Alignment(horizontal: .leading, vertical: .bottom)
80
+ public static let bottomTrailing = Alignment(horizontal: .trailing, vertical: .bottom)
81
+
82
+ var _name: String {
83
+ switch (vertical.name, horizontal.name) {
84
+ case ("center", let h): h
85
+ case (let v, "center"): v
86
+ case (let v, let h): v + h.prefix(1).uppercased() + h.dropFirst()
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,124 @@
1
+ // CoreGraphics types, provided by the shim because browser builds have no
2
+ // Foundation (see Geometry.swift).
3
+
4
+ public struct CGPoint: Equatable, Hashable, Sendable {
5
+ public var x: CGFloat
6
+ public var y: CGFloat
7
+ public init(x: CGFloat, y: CGFloat) { self.x = x; self.y = y }
8
+ public init() { self.init(x: 0, y: 0) }
9
+ public static let zero = CGPoint()
10
+ }
11
+
12
+ /// A normalized point in a view's bounds; `(0, 0)` is top leading.
13
+ public struct UnitPoint: Equatable, Hashable, Sendable {
14
+ public var x: CGFloat
15
+ public var y: CGFloat
16
+
17
+ public init(x: CGFloat, y: CGFloat) {
18
+ self.x = x
19
+ self.y = y
20
+ }
21
+
22
+ public init() { self.init(x: 0, y: 0) }
23
+
24
+ public static let zero = UnitPoint(x: 0, y: 0)
25
+ public static let center = UnitPoint(x: 0.5, y: 0.5)
26
+ public static let leading = UnitPoint(x: 0, y: 0.5)
27
+ public static let trailing = UnitPoint(x: 1, y: 0.5)
28
+ public static let top = UnitPoint(x: 0.5, y: 0)
29
+ public static let bottom = UnitPoint(x: 0.5, y: 1)
30
+ public static let topLeading = UnitPoint(x: 0, y: 0)
31
+ public static let topTrailing = UnitPoint(x: 1, y: 0)
32
+ public static let bottomLeading = UnitPoint(x: 0, y: 1)
33
+ public static let bottomTrailing = UnitPoint(x: 1, y: 1)
34
+ }
35
+
36
+ public struct CGSize: Equatable, Hashable, Sendable {
37
+ public var width: CGFloat
38
+ public var height: CGFloat
39
+ public init(width: CGFloat, height: CGFloat) { self.width = width; self.height = height }
40
+ public init() { self.init(width: 0, height: 0) }
41
+ public static let zero = CGSize()
42
+ }
43
+
44
+ public struct CGRect: Equatable, Hashable, Sendable {
45
+ public var origin: CGPoint
46
+ public var size: CGSize
47
+ public init(origin: CGPoint, size: CGSize) { self.origin = origin; self.size = size }
48
+ public init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) {
49
+ self.init(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
50
+ }
51
+ public init() { self.init(origin: .zero, size: .zero) }
52
+ public static let zero = CGRect()
53
+ public var minX: CGFloat { origin.x }
54
+ public var minY: CGFloat { origin.y }
55
+ public var midX: CGFloat { origin.x + size.width / 2 }
56
+ public var midY: CGFloat { origin.y + size.height / 2 }
57
+ public var maxX: CGFloat { origin.x + size.width }
58
+ public var maxY: CGFloat { origin.y + size.height }
59
+ public var width: CGFloat { size.width }
60
+ public var height: CGFloat { size.height }
61
+ public var isEmpty: Bool { size.width <= 0 || size.height <= 0 }
62
+
63
+ /// Shrinks the rectangle by `dx` on each horizontal side and `dy` on each vertical side.
64
+ public func insetBy(dx: CGFloat, dy: CGFloat) -> CGRect {
65
+ CGRect(x: origin.x + dx, y: origin.y + dy, width: size.width - 2 * dx, height: size.height - 2 * dy)
66
+ }
67
+
68
+ public func offsetBy(dx: CGFloat, dy: CGFloat) -> CGRect {
69
+ CGRect(origin: CGPoint(x: origin.x + dx, y: origin.y + dy), size: size)
70
+ }
71
+
72
+ public func contains(_ point: CGPoint) -> Bool {
73
+ point.x >= minX && point.x < maxX && point.y >= minY && point.y < maxY
74
+ }
75
+ }
76
+
77
+ /// The size the renderer laid a `GeometryReader` out at.
78
+ public struct GeometryProxy {
79
+ public let size: CGSize
80
+
81
+ public func frame(in coordinateSpace: CoordinateSpace) -> CGRect {
82
+ CGRect(origin: .zero, size: size)
83
+ }
84
+ }
85
+
86
+ public enum CoordinateSpace: Hashable {
87
+ case global, local
88
+ case named(AnyHashable)
89
+
90
+ public var isGlobal: Bool { self == .global }
91
+ public var isLocal: Bool { self == .local }
92
+ }
93
+ /// `AnyHashable` is not `Sendable`; names are value types in practice.
94
+ extension CoordinateSpace: @unchecked Sendable {}
95
+
96
+ extension View {
97
+ /// Names a coordinate space; accepted for source compatibility (geometry
98
+ /// is always reported in the view's own box).
99
+ public func coordinateSpace<T: Hashable>(name: T) -> some View { self }
100
+ }
101
+
102
+ protocol _GeometryReading {
103
+ func _content(for size: CGSize) -> any View
104
+ }
105
+
106
+ /// A container that reports its size to its content. The size arrives from
107
+ /// the renderer after layout, so the content renders once at zero size and
108
+ /// again with the real size.
109
+ public struct GeometryReader<Content: View> {
110
+ let content: (GeometryProxy) -> Content
111
+
112
+ public init(@ViewBuilder content: @escaping (GeometryProxy) -> Content) {
113
+ self.content = content
114
+ }
115
+ }
116
+ extension GeometryReader: _PrimitiveView {}
117
+ extension GeometryReader: _HostView {
118
+ var _kind: String { "geometry" }
119
+ func _props(in environment: EnvironmentValues) -> _JSON { .object([:]) }
120
+ // The runtime produces the content from the last reported size.
121
+ }
122
+ extension GeometryReader: _GeometryReading {
123
+ func _content(for size: CGSize) -> any View { content(GeometryProxy(size: size)) }
124
+ }