InspectModifier.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /// A point at which a view's underlying widget can be inspected.
  2. public struct InspectionPoints: OptionSet, RawRepresentable, Hashable, Sendable {
  3. public var rawValue: UInt32
  4. public init(rawValue: UInt32) {
  5. self.rawValue = rawValue
  6. }
  7. public static let onCreate = Self(rawValue: 1 << 0)
  8. public static let beforeUpdate = Self(rawValue: 1 << 1)
  9. public static let afterUpdate = Self(rawValue: 1 << 2)
  10. }
  11. /// The `View.inspect(_:_:)` family of modifiers is implemented within each
  12. /// backend. Make sure to import your chosen backend in any files where you
  13. /// need to inspect a widget. This type simply supports the implementation of
  14. /// those backend-specific modifiers.
  15. @_spi(Backends) public struct InspectView<Child: View> {
  16. var child: TupleView1<Child>
  17. var inspectionPoints: InspectionPoints
  18. var action: @MainActor (_ widget: AnyWidget, _ children: any ViewGraphNodeChildren) -> Void
  19. public init<WidgetType>(
  20. child: Child,
  21. inspectionPoints: InspectionPoints,
  22. action: @escaping @MainActor @Sendable (WidgetType) -> Void
  23. ) {
  24. self.child = TupleView1(child)
  25. self.inspectionPoints = inspectionPoints
  26. self.action = { widget, _ in
  27. action(widget.into())
  28. }
  29. }
  30. public init<WidgetType, Children: ViewGraphNodeChildren>(
  31. child: Child,
  32. inspectionPoints: InspectionPoints,
  33. action: @escaping @MainActor @Sendable (WidgetType, Children) -> Void
  34. ) {
  35. self.child = TupleView1(child)
  36. self.inspectionPoints = inspectionPoints
  37. self.action = { widget, children in
  38. action(widget.into(), children as! Children)
  39. }
  40. }
  41. }
  42. extension InspectView: View {
  43. public var body: some View { EmptyView() }
  44. public func asWidget<Backend: BaseAppBackend>(
  45. _ children: any ViewGraphNodeChildren,
  46. backend: Backend
  47. ) -> Backend.Widget {
  48. let widget = child.asWidget(children, backend: backend)
  49. let children = children as! TupleView1<Child>.Children
  50. if inspectionPoints.contains(.onCreate) {
  51. action(children.child0.widget, children.child0.getChildren())
  52. }
  53. return widget
  54. }
  55. public func children<Backend: BaseAppBackend>(
  56. backend: Backend,
  57. snapshots: [ViewGraphSnapshotter.NodeSnapshot]?,
  58. environment: EnvironmentValues
  59. ) -> any ViewGraphNodeChildren {
  60. child.children(backend: backend, snapshots: snapshots, environment: environment)
  61. }
  62. public func layoutableChildren<Backend: BaseAppBackend>(
  63. backend: Backend,
  64. children: any ViewGraphNodeChildren
  65. ) -> [LayoutSystem.LayoutableChild] {
  66. child.layoutableChildren(backend: backend, children: children)
  67. }
  68. public func computeLayout<Backend: BaseAppBackend>(
  69. _ widget: Backend.Widget,
  70. children: any ViewGraphNodeChildren,
  71. proposedSize: ProposedViewSize,
  72. environment: EnvironmentValues,
  73. backend: Backend
  74. ) -> ViewLayoutResult {
  75. let children = children as! TupleView1<Child>.Children
  76. if inspectionPoints.contains(.beforeUpdate) {
  77. action(children.child0.widget, children.child0.getChildren())
  78. }
  79. let result = child.computeLayout(
  80. widget,
  81. children: children,
  82. proposedSize: proposedSize,
  83. environment: environment,
  84. backend: backend
  85. )
  86. return result
  87. }
  88. public func commit<Backend: BaseAppBackend>(
  89. _ widget: Backend.Widget,
  90. children: any ViewGraphNodeChildren,
  91. layout: ViewLayoutResult,
  92. environment: EnvironmentValues,
  93. backend: Backend
  94. ) {
  95. child.commit(
  96. widget,
  97. children: children,
  98. layout: layout,
  99. environment: environment,
  100. backend: backend
  101. )
  102. let children = children as! TupleView1<Child>.Children
  103. if inspectionPoints.contains(.afterUpdate) {
  104. action(children.child0.widget, children.child0.getChildren())
  105. }
  106. }
  107. }
  108. /// The `View.inspectWindow(_:)` modifier is implemented within each backend.
  109. /// Make sure to import your chosen backend in any files where you need to
  110. /// inspect a native window. This type simply supports the implementation of
  111. /// those backend-specified modifiers.
  112. @_spi(Backends) public struct InspectWindowView<Child: View> {
  113. @Environment(\.window) var window
  114. var child: Child
  115. var action: @MainActor (_ window: Any) -> Void
  116. public init<WindowType>(
  117. child: Child,
  118. action: @escaping @MainActor @Sendable (WindowType) -> Void
  119. ) {
  120. self.child = child
  121. self.action = { window in
  122. action(window as! WindowType)
  123. }
  124. }
  125. }
  126. extension InspectWindowView: View {
  127. public var body: some View {
  128. child.onCommit {
  129. action(window!)
  130. }
  131. }
  132. }