@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.
- package/Package.swift +23 -0
- package/README.md +12 -2
- package/Sources/Charts/Charts.swift +65 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +77 -0
- package/Sources/FoundationBridge/Locale.swift +86 -0
- package/Sources/FoundationBridge/Timer.swift +48 -0
- package/Sources/FoundationBridge/UITextChecker.swift +30 -0
- package/Sources/FoundationLite/Common.swift +3 -0
- package/Sources/FoundationLite/FoundationLite.swift +171 -0
- package/Sources/FoundationLite/Interaction.swift +146 -0
- package/Sources/FoundationLite/Timer.swift +48 -0
- package/Sources/SwiftUI/API/Accessibility.swift +132 -0
- package/Sources/SwiftUI/API/Alert.swift +221 -0
- package/Sources/SwiftUI/API/Animation.swift +253 -0
- package/Sources/SwiftUI/API/App.swift +57 -0
- package/Sources/SwiftUI/API/Color.swift +161 -0
- package/Sources/SwiftUI/API/Controls.swift +268 -0
- package/Sources/SwiftUI/API/CustomLayout.swift +403 -0
- package/Sources/SwiftUI/API/Decorations.swift +92 -0
- package/Sources/SwiftUI/API/EditMode.swift +269 -0
- package/Sources/SwiftUI/API/Effects.swift +290 -0
- package/Sources/SwiftUI/API/Environment.swift +355 -0
- package/Sources/SwiftUI/API/Focus.swift +129 -0
- package/Sources/SwiftUI/API/Font.swift +131 -0
- package/Sources/SwiftUI/API/Gauge.swift +289 -0
- package/Sources/SwiftUI/API/Geometry.swift +89 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +124 -0
- package/Sources/SwiftUI/API/Gestures.swift +342 -0
- package/Sources/SwiftUI/API/Grid.swift +188 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +117 -0
- package/Sources/SwiftUI/API/Layout.swift +165 -0
- package/Sources/SwiftUI/API/Lists.swift +276 -0
- package/Sources/SwiftUI/API/Menu.swift +51 -0
- package/Sources/SwiftUI/API/Modifiers.swift +243 -0
- package/Sources/SwiftUI/API/Navigation.swift +371 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +279 -0
- package/Sources/SwiftUI/API/OnChange.swift +48 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +394 -0
- package/Sources/SwiftUI/API/Picker.swift +176 -0
- package/Sources/SwiftUI/API/Presentation.swift +109 -0
- package/Sources/SwiftUI/API/Primitives.swift +390 -0
- package/Sources/SwiftUI/API/Publishers.swift +107 -0
- package/Sources/SwiftUI/API/RowActions.swift +83 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +311 -0
- package/Sources/SwiftUI/API/State.swift +127 -0
- package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
- package/Sources/SwiftUI/API/Tabs.swift +119 -0
- package/Sources/SwiftUI/API/Tasks.swift +35 -0
- package/Sources/SwiftUI/API/Timeline.swift +212 -0
- package/Sources/SwiftUI/API/Toolbar.swift +215 -0
- package/Sources/SwiftUI/API/View.swift +207 -0
- package/Sources/SwiftUI/API/ViewModifier.swift +40 -0
- package/Sources/SwiftUI/API/VisualEffect.swift +82 -0
- package/Sources/SwiftUI/Charts/Axis.swift +319 -0
- package/Sources/SwiftUI/Charts/Chart.swift +646 -0
- package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
- package/Sources/SwiftUI/Charts/Marks.swift +204 -0
- package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
- package/Sources/SwiftUI/Charts/Scales.swift +326 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +83 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +65 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +71 -0
- package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +106 -0
- package/Sources/SwiftUI/Runtime/Reconciler.swift +210 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +1367 -0
- package/Sources/_FoundationCommon/Bundle.swift +82 -0
- package/Sources/_FoundationCommon/UserDefaults.swift +170 -0
- package/Sources/_FoundationCommon/_FoundationCommon.swift +10 -0
- package/index.d.ts +2 -0
- package/index.mjs +16 -0
- package/package.json +28 -4
- 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
|
+
}
|