PaddingModifier.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. extension View {
  2. /// Adds padding to a view.
  3. ///
  4. /// Separate from ``View/padding(_:_:)`` because overload resolution didn't
  5. /// like the double default parameters.
  6. ///
  7. /// - Parameter amount: The amount of padding to use. If `nil`, a
  8. /// backend-specific default value is used.
  9. public func padding(_ amount: Int? = nil) -> some View {
  10. return padding(.all, amount)
  11. }
  12. /// Adds padding to a view.
  13. ///
  14. /// - Parameters:
  15. /// - edges: The edges to apply the padding to. Defaults to
  16. /// ``Edge/Set/all``.
  17. /// - amount: The amount of padding to use. If `nil`, a backend-specific
  18. /// default value is used.
  19. public func padding(_ edges: Edge.Set = .all, _ amount: Int? = nil) -> some View {
  20. let insets = EdgeInsets.Internal(edges: edges, amount: amount)
  21. return PaddingModifierView(body: TupleView1(self), insets: insets)
  22. }
  23. /// Adds padding to a view with a different amount for each edge.
  24. ///
  25. /// - Parameter insets: The edge insets to use.
  26. public func padding(_ insets: EdgeInsets) -> some View {
  27. return PaddingModifierView(body: TupleView1(self), insets: EdgeInsets.Internal(insets))
  28. }
  29. }
  30. /// Insets for the sides of a rectangle. Generally used to represent view padding.
  31. public struct EdgeInsets: Equatable {
  32. /// The top inset.
  33. public var top: Int
  34. /// The bottom inset.
  35. public var bottom: Int
  36. /// The leading inset.
  37. public var leading: Int
  38. /// The trailing inset.
  39. public var trailing: Int
  40. /// The total inset along each axis.
  41. var axisTotals: SIMD2<Int> {
  42. SIMD2(
  43. leading + trailing,
  44. top + bottom
  45. )
  46. }
  47. /// Constructs edge insets from individual insets.
  48. ///
  49. /// - Parameters:
  50. /// - top: The top inset.
  51. /// - bottom: The bottom inset.
  52. /// - leading: The leading inset.
  53. /// - trailing: The trailing inset.
  54. public init(top: Int = 0, bottom: Int = 0, leading: Int = 0, trailing: Int = 0) {
  55. self.top = top
  56. self.bottom = bottom
  57. self.leading = leading
  58. self.trailing = trailing
  59. }
  60. init(_ insets: Internal, defaultAmount: Int) {
  61. top = insets.top ?? defaultAmount
  62. bottom = insets.bottom ?? defaultAmount
  63. leading = insets.leading ?? defaultAmount
  64. trailing = insets.trailing ?? defaultAmount
  65. }
  66. struct Internal {
  67. var top: Int?
  68. var bottom: Int?
  69. var leading: Int?
  70. var trailing: Int?
  71. }
  72. }
  73. extension EdgeInsets.Internal {
  74. init(edges: Edge.Set, amount: Int?) {
  75. self.top = edges.contains(.top) ? amount : 0
  76. self.bottom = edges.contains(.bottom) ? amount : 0
  77. self.leading = edges.contains(.leading) ? amount : 0
  78. self.trailing = edges.contains(.trailing) ? amount : 0
  79. }
  80. init(_ insets: EdgeInsets) {
  81. top = insets.top
  82. bottom = insets.bottom
  83. leading = insets.leading
  84. trailing = insets.trailing
  85. }
  86. }
  87. /// The implementation for the ``View/padding(_:_:)`` modifier.
  88. struct PaddingModifierView<Child: View>: TypeSafeView {
  89. var body: TupleView1<Child>
  90. /// The insets for each edge.
  91. var insets: EdgeInsets.Internal
  92. func children<Backend: BaseAppBackend>(
  93. backend: Backend,
  94. snapshots: [ViewGraphSnapshotter.NodeSnapshot]?,
  95. environment: EnvironmentValues
  96. ) -> TupleViewChildren1<Child> {
  97. body.children(backend: backend, snapshots: snapshots, environment: environment)
  98. }
  99. func asWidget<Backend: BaseAppBackend>(
  100. _ children: TupleViewChildren1<Child>,
  101. backend: Backend
  102. ) -> Backend.Widget {
  103. let container = backend.createContainer()
  104. backend.insert(children.child0.widget.into(), into: container, at: 0)
  105. return container
  106. }
  107. func computeLayout<Backend: BaseAppBackend>(
  108. _ container: Backend.Widget,
  109. children: TupleViewChildren1<Child>,
  110. proposedSize: ProposedViewSize,
  111. environment: EnvironmentValues,
  112. backend: Backend
  113. ) -> ViewLayoutResult {
  114. // This first block of calculations is somewhat repeated in `commit`,
  115. // make sure to update things in both places.
  116. let insets = EdgeInsets(insets, defaultAmount: backend.defaultPaddingAmount)
  117. let horizontalPadding = Double(insets.leading + insets.trailing)
  118. let verticalPadding = Double(insets.top + insets.bottom)
  119. var childProposal = proposedSize
  120. if let proposedWidth = proposedSize.width {
  121. childProposal.width = max(proposedWidth - horizontalPadding, 0)
  122. }
  123. if let proposedHeight = proposedSize.height {
  124. childProposal.height = max(proposedHeight - verticalPadding, 0)
  125. }
  126. let childResult = children.child0.computeLayout(
  127. with: body.view0,
  128. proposedSize: childProposal,
  129. environment: environment
  130. )
  131. var size = childResult.size
  132. size.width += horizontalPadding
  133. size.height += verticalPadding
  134. return ViewLayoutResult(
  135. size: size,
  136. childResults: [childResult]
  137. )
  138. }
  139. func commit<Backend: BaseAppBackend>(
  140. _ container: Backend.Widget,
  141. children: TupleViewChildren1<Child>,
  142. layout: ViewLayoutResult,
  143. environment: EnvironmentValues,
  144. backend: Backend
  145. ) {
  146. _ = children.child0.commit()
  147. let size = layout.size
  148. backend.setSize(of: container, to: size.vector)
  149. let insets = EdgeInsets(insets, defaultAmount: backend.defaultPaddingAmount)
  150. let childPosition = SIMD2(insets.leading, insets.top)
  151. backend.setPosition(ofChildAt: 0, in: container, to: childPosition)
  152. }
  153. }