Slider.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /// A control for selecting a value from a bounded range of numerical values.
  2. public struct Slider: ElementaryView, View {
  3. /// The ideal width of a Slider.
  4. private static let idealWidth: Double = 100
  5. /// A binding to the current value.
  6. private var value: Binding<Double>?
  7. /// The slider's selectable range of values.
  8. private var range: ClosedRange<Double>
  9. /// The number of decimal places used when displaying the value.
  10. private var decimalPlaces: Int
  11. @available(*, deprecated, renamed: "init(value:in:)")
  12. public init<T: BinaryInteger>(_ value: Binding<T>? = nil, minimum: T, maximum: T) {
  13. self.init(value: value, in: minimum...maximum)
  14. }
  15. @available(*, deprecated, renamed: "init(value:in:)")
  16. public init<T: BinaryFloatingPoint>(_ value: Binding<T>? = nil, minimum: T, maximum: T) {
  17. self.init(value: value, in: minimum...maximum)
  18. }
  19. /// Creates a slider to select a value in a range.
  20. ///
  21. /// - Parameters:
  22. /// - value: A binding to the current value.
  23. /// - range: The slider's selectable range of values.
  24. public init<T: BinaryInteger>(value: Binding<T>? = nil, in range: ClosedRange<T>) {
  25. if let value {
  26. self.value = Binding<Double>(
  27. get: {
  28. return Double(value.wrappedValue)
  29. },
  30. set: { newValue in
  31. value.wrappedValue = T(newValue.rounded())
  32. }
  33. )
  34. }
  35. self.range = Double(range.lowerBound)...Double(range.upperBound)
  36. decimalPlaces = 0
  37. }
  38. /// Creates a slider to select a value in a range.
  39. ///
  40. /// - Parameters:
  41. /// - value: A binding to the current value.
  42. /// - range: The slider's range of values.
  43. public init<T: BinaryFloatingPoint>(value: Binding<T>? = nil, in range: ClosedRange<T>) {
  44. if let value {
  45. self.value = Binding<Double>(
  46. get: {
  47. return Double(value.wrappedValue)
  48. },
  49. set: { newValue in
  50. value.wrappedValue = T(newValue)
  51. }
  52. )
  53. }
  54. self.range = Double(range.lowerBound)...Double(range.upperBound)
  55. decimalPlaces = 2
  56. }
  57. func asWidget<Backend: BaseAppBackend>(backend: Backend) -> Backend.Widget {
  58. return backend.createSlider()
  59. }
  60. func computeLayout<Backend: BaseAppBackend>(
  61. _ widget: Backend.Widget,
  62. proposedSize: ProposedViewSize,
  63. environment: EnvironmentValues,
  64. backend: Backend
  65. ) -> ViewLayoutResult {
  66. // TODO: Don't rely on naturalSize for minimum size so that we can get Slider sizes without
  67. // relying on the widget.
  68. let naturalSize = backend.naturalSize(of: widget)
  69. let size = ViewSize(
  70. max(Double(naturalSize.x), proposedSize.width ?? Self.idealWidth),
  71. Double(naturalSize.y)
  72. )
  73. // TODO: Allow backends to specify their own ideal slider widths.
  74. return ViewLayoutResult.leafView(size: size)
  75. }
  76. func commit<Backend: BaseAppBackend>(
  77. _ widget: Backend.Widget,
  78. layout: ViewLayoutResult,
  79. environment: EnvironmentValues,
  80. backend: Backend
  81. ) {
  82. backend.updateSlider(
  83. widget,
  84. minimum: range.lowerBound,
  85. maximum: range.upperBound,
  86. decimalPlaces: decimalPlaces,
  87. environment: environment
  88. ) { newValue in
  89. if let value, value.wrappedValue != newValue {
  90. value.wrappedValue = newValue
  91. }
  92. }
  93. if let value = value?.wrappedValue {
  94. backend.setValue(ofSlider: widget, to: value)
  95. }
  96. backend.setSize(of: widget, to: layout.size.vector)
  97. }
  98. }