StyledShape.swift 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /// A shape that has style information attached to it, including color and
  2. /// stroke style.
  3. public protocol StyledShape: Shape {
  4. /// The shape's stroke color.
  5. var strokeColor: Color? { get }
  6. /// The shape's fill color.
  7. var fillColor: Color? { get }
  8. /// The shape's stroke style.
  9. var strokeStyle: StrokeStyle? { get }
  10. }
  11. struct StyledShapeImpl<Base: Shape>: Sendable {
  12. var base: Base
  13. var strokeColor: Color?
  14. var fillColor: Color?
  15. var strokeStyle: StrokeStyle?
  16. init(
  17. base: Base,
  18. strokeColor: Color? = nil,
  19. fillColor: Color? = nil,
  20. strokeStyle: StrokeStyle? = nil
  21. ) {
  22. self.base = base
  23. if let styledBase = base as? any StyledShape {
  24. self.strokeColor = strokeColor ?? styledBase.strokeColor
  25. self.fillColor = fillColor ?? styledBase.fillColor
  26. self.strokeStyle = strokeStyle ?? styledBase.strokeStyle
  27. } else {
  28. self.strokeColor = strokeColor
  29. self.fillColor = fillColor
  30. self.strokeStyle = strokeStyle
  31. }
  32. }
  33. }
  34. extension StyledShapeImpl: StyledShape {
  35. func path(in bounds: Path.Rect) -> Path {
  36. return base.path(in: bounds)
  37. }
  38. func size(fitting proposal: ProposedViewSize) -> ViewSize {
  39. return base.size(fitting: proposal)
  40. }
  41. }
  42. extension Shape {
  43. public func fill(_ color: Color) -> some StyledShape {
  44. StyledShapeImpl(base: self, fillColor: color)
  45. }
  46. public func stroke(_ color: Color, style: StrokeStyle? = nil) -> some StyledShape {
  47. StyledShapeImpl(base: self, strokeColor: color, strokeStyle: style)
  48. }
  49. }
  50. extension StyledShape {
  51. @MainActor
  52. public func computeLayout<Backend: BaseAppBackend>(
  53. _ widget: Backend.Widget,
  54. children: any ViewGraphNodeChildren,
  55. proposedSize: ProposedViewSize,
  56. environment: EnvironmentValues,
  57. backend: Backend
  58. ) -> ViewLayoutResult {
  59. let size = size(fitting: proposedSize)
  60. return ViewLayoutResult.leafView(size: size)
  61. }
  62. @MainActor
  63. func commit<Backend: BaseAppBackend>(
  64. _ widget: Backend.Widget,
  65. children: any ViewGraphNodeChildren,
  66. layout: ViewLayoutResult,
  67. environment: EnvironmentValues,
  68. backend: Backend
  69. ) {
  70. func castBackend_inner<NewBackend: BaseAppBackend & BackendFeatures.Paths>(_ backend: NewBackend) {
  71. let widget = widget as! NewBackend.Widget
  72. let bounds = Path.Rect(
  73. x: 0.0,
  74. y: 0.0,
  75. width: layout.size.width,
  76. height: layout.size.height
  77. )
  78. let path = path(in: bounds)
  79. let storage = children as! ShapeStorage
  80. let pointsChanged = storage.oldPath?.actions != path.actions
  81. storage.oldPath = path
  82. let backendPath = storage.backendPath as! NewBackend.Path
  83. backend.updatePath(
  84. backendPath,
  85. path,
  86. bounds: bounds,
  87. pointsChanged: pointsChanged,
  88. environment: environment
  89. )
  90. backend.setSize(of: widget, to: layout.size.vector)
  91. backend.renderPath(
  92. backendPath,
  93. container: widget,
  94. strokeColor: (strokeColor ?? .clear).resolve(in: environment),
  95. fillColor: (fillColor ?? .clear).resolve(in: environment),
  96. overrideStrokeStyle: strokeStyle
  97. )
  98. }
  99. guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.Paths) else {
  100. fatalError("Backend does not implement BackendFeatures.Paths")
  101. }
  102. castBackend_inner(castedBackend)
  103. }
  104. }