|
| 1 | +// |
| 2 | +// Stepper.swift |
| 3 | +// AndroidSwiftUICore |
| 4 | +// |
| 5 | +// A control that increments or decrements a value. The core registers the two |
| 6 | +// edge actions as callbacks; the interpreter renders a label plus − / + buttons. |
| 7 | +// |
| 8 | + |
| 9 | +public struct Stepper<Label: View>: View { |
| 10 | + |
| 11 | + internal let label: Label |
| 12 | + internal let onIncrement: () -> Void |
| 13 | + internal let onDecrement: () -> Void |
| 14 | + |
| 15 | + public init( |
| 16 | + onIncrement: @escaping () -> Void, |
| 17 | + onDecrement: @escaping () -> Void, |
| 18 | + @ViewBuilder label: () -> Label |
| 19 | + ) { |
| 20 | + self.label = label() |
| 21 | + self.onIncrement = onIncrement |
| 22 | + self.onDecrement = onDecrement |
| 23 | + } |
| 24 | + |
| 25 | + public typealias Body = Never |
| 26 | +} |
| 27 | + |
| 28 | +public extension Stepper where Label == Text { |
| 29 | + |
| 30 | + init<S: StringProtocol, V: Strideable>( |
| 31 | + _ title: S, |
| 32 | + value: Binding<V>, |
| 33 | + in bounds: ClosedRange<V>, |
| 34 | + step: V.Stride = 1 |
| 35 | + ) { |
| 36 | + self.init( |
| 37 | + onIncrement: { |
| 38 | + let next = value.wrappedValue.advanced(by: step) |
| 39 | + if next <= bounds.upperBound { value.wrappedValue = next } |
| 40 | + }, |
| 41 | + onDecrement: { |
| 42 | + let previous = value.wrappedValue.advanced(by: -step) |
| 43 | + if previous >= bounds.lowerBound { value.wrappedValue = previous } |
| 44 | + }, |
| 45 | + label: { Text(title) } |
| 46 | + ) |
| 47 | + } |
| 48 | + |
| 49 | + init<S: StringProtocol, V: Strideable>( |
| 50 | + _ title: S, |
| 51 | + value: Binding<V>, |
| 52 | + step: V.Stride = 1 |
| 53 | + ) { |
| 54 | + self.init( |
| 55 | + onIncrement: { value.wrappedValue = value.wrappedValue.advanced(by: step) }, |
| 56 | + onDecrement: { value.wrappedValue = value.wrappedValue.advanced(by: -step) }, |
| 57 | + label: { Text(title) } |
| 58 | + ) |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +extension Stepper: PrimitiveView { |
| 63 | + public func _render(in context: ResolveContext) -> RenderNode { |
| 64 | + let incrementID = context.callbacks.register(.void(onIncrement)) |
| 65 | + let decrementID = context.callbacks.register(.void(onDecrement)) |
| 66 | + return RenderNode( |
| 67 | + type: "Stepper", |
| 68 | + id: context.path, |
| 69 | + props: [ |
| 70 | + "onIncrement": .int(Int(incrementID)), |
| 71 | + "onDecrement": .int(Int(decrementID)), |
| 72 | + ], |
| 73 | + children: Evaluator.resolveChildren(label, context.descending("label")) |
| 74 | + ) |
| 75 | + } |
| 76 | +} |
0 commit comments