RadialGradient.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /// A radial gradient.
  2. public struct RadialGradient: ElementaryView {
  3. /// The gradient represented as an array of color stops, each having a parametric location value.
  4. public let gradient: Gradient
  5. /// The radius at which the first gradient stop will be placed.
  6. ///
  7. /// All space inside this radius gets filled with the color of the first gradient stop.
  8. public let startRadius: Double
  9. /// The radius at which the last gradient stop will be placed.
  10. ///
  11. /// All space outside this radius gets filled with the color of the last gradient stop.
  12. public let endRadius: Double
  13. /// The normalized center point of the gradient in its coordinate space.
  14. public let center: UnitPoint
  15. private static let idealSize = ViewSize(10, 10)
  16. /// Creates a radial gradient from a base gradient.
  17. public init(
  18. gradient: Gradient,
  19. center: UnitPoint,
  20. startRadius: Double,
  21. endRadius: Double
  22. ) {
  23. self.gradient = gradient
  24. self.startRadius = startRadius
  25. self.center = center
  26. self.endRadius = endRadius
  27. }
  28. func asWidget<Backend: BaseAppBackend>(
  29. backend: Backend
  30. ) -> Backend.Widget {
  31. func castBackend_inner<NewBackend: BaseAppBackend & BackendFeatures.RadialGradients>(_ backend: NewBackend) -> NewBackend.Widget {
  32. return backend.createRadialGradientWidget()
  33. }
  34. guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.RadialGradients) else {
  35. fatalError("Backend does not implement BackendFeatures.RadialGradients")
  36. }
  37. return castBackend_inner(castedBackend) as! Backend.Widget
  38. }
  39. public func computeLayout<Backend: BaseAppBackend>(
  40. _ widget: Backend.Widget,
  41. proposedSize: ProposedViewSize,
  42. environment: EnvironmentValues,
  43. backend: Backend
  44. ) -> ViewLayoutResult {
  45. ViewLayoutResult.leafView(
  46. size: proposedSize.replacingUnspecifiedDimensions(by: Self.idealSize)
  47. )
  48. }
  49. func commit<Backend: BaseAppBackend>(
  50. _ widget: Backend.Widget,
  51. layout: ViewLayoutResult,
  52. environment: EnvironmentValues,
  53. backend: Backend
  54. ) {
  55. func castBackend_inner<NewBackend: BaseAppBackend & BackendFeatures.RadialGradients>(_ backend: NewBackend) {
  56. let widget = widget as! NewBackend.Widget
  57. backend.setSize(of: widget, to: layout.size.vector)
  58. backend.updateRadialGradientWidget(
  59. widget,
  60. gradient: self,
  61. withSize: layout.size.vector,
  62. in: environment
  63. )
  64. }
  65. guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.RadialGradients) else {
  66. fatalError("Backend does not implement BackendFeatures.RadialGradients")
  67. }
  68. castBackend_inner(castedBackend)
  69. }
  70. }
  71. extension RadialGradient {
  72. /// Creates a radial gradient from a collection of colors.
  73. public init(
  74. stops: [Gradient.Stop],
  75. center: UnitPoint,
  76. startRadius: Double,
  77. endRadius: Double
  78. ) {
  79. self.init(
  80. gradient: Gradient(stops: stops),
  81. center: center,
  82. startRadius: startRadius,
  83. endRadius: endRadius
  84. )
  85. }
  86. /// Creates a radial gradient from a collection of color stops.
  87. public init(
  88. colors: [Color],
  89. center: UnitPoint,
  90. startRadius: Double,
  91. endRadius: Double
  92. ) {
  93. self.init(
  94. gradient: Gradient(colors: colors),
  95. center: center,
  96. startRadius: startRadius,
  97. endRadius: endRadius
  98. )
  99. }
  100. /// Stops adjusted to accomodate startRadius on backends without native support.
  101. @_spi(Backends) public var adjustedStops: [Gradient.Stop] {
  102. guard startRadius != 0 else { return gradient.stops }
  103. let range = endRadius - startRadius
  104. if range < 0 {
  105. let dividableRange = abs(range) / startRadius
  106. let innerCircle = (startRadius - abs(range)) / startRadius
  107. let invertedStops = gradient.stops.reversed().map { stop in
  108. Gradient.Stop(
  109. color: stop.color,
  110. location: innerCircle + (1.0 - stop.location) * dividableRange
  111. )
  112. }
  113. return invertedStops
  114. }
  115. let dividableRange = range / endRadius
  116. let innerCircle = (endRadius - range) / endRadius
  117. return gradient.stops.map { stop in
  118. Gradient.Stop(
  119. color: stop.color,
  120. location: innerCircle + stop.location * dividableRange
  121. )
  122. }
  123. }
  124. }