1
0

Menus.swift 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. extension BackendFeatures {
  2. /// Backend methods for menu buttons.
  3. ///
  4. /// - Important: You only need to write a conformance to _one of_
  5. /// ``AttachedMenus`` or ``PopoverMenus``, depending on what you use as
  6. /// your ``MenuButtons/menuImplementationStyle-4blzf`` (that is, what would work best
  7. /// for your backend's underlying UI framework).
  8. @MainActor
  9. public protocol MenuButtons<Menu>: Core, StringLabelButtons {
  10. /// The underlying menu type. Can be a wrapper or subclass.
  11. associatedtype Menu
  12. /// How the backend handles rendering of menu buttons.
  13. ///
  14. /// This affects which menu-related methods are called.
  15. ///
  16. /// This requirement is automatically implemented for backends that conform to exactly
  17. /// one of ``BackendFeatures/PopoverMenus`` or ``BackendFeatures/AttachedMenus``.
  18. ///
  19. /// ## See Also
  20. /// - ``MenuImplementationStyle``
  21. var menuImplementationStyle: MenuImplementationStyle<Widget, Menu> { get }
  22. /// Creates a popover menu (the sort you often see when right clicking on
  23. /// apps).
  24. ///
  25. /// The menu won't be visible when first created.
  26. ///
  27. /// - Returns: A popover menu.
  28. func createPopoverMenu() -> Menu
  29. /// Updates a popover menu's content and appearance.
  30. ///
  31. /// - Parameters:
  32. /// - menu: The menu to update.
  33. /// - content: The menu content.
  34. /// - environment: The current environment.
  35. func updatePopoverMenu(
  36. _ menu: Menu,
  37. content: ResolvedMenu,
  38. environment: EnvironmentValues
  39. )
  40. }
  41. /// Backend methods for menus that are simply attached to an existing
  42. /// button widget.
  43. @MainActor
  44. public protocol AttachedMenus<Widget, Menu>: MenuButtons {
  45. /// Sets a button's label and menu.
  46. ///
  47. /// Only used when ``BackendFeatures/MenuButtons/menuImplementationStyle`` is
  48. /// ``MenuImplementationStyle/menuButton``.
  49. ///
  50. /// - Parameters:
  51. /// - button: The button to update.
  52. /// - label: The button's label.
  53. /// - menu: The menu to show when the button is clicked/tapped.
  54. /// - environment: The current environment.
  55. func updateButton(
  56. _ button: Widget,
  57. label: String,
  58. menu: Menu,
  59. environment: EnvironmentValues
  60. )
  61. }
  62. /// Backend methods for menus which need a separate widget to be created.
  63. @MainActor
  64. public protocol PopoverMenus<Widget, Menu>: MenuButtons {
  65. /// Shows the popover menu at a position relative to the given widget.
  66. ///
  67. /// Only used when ``BackendFeatures/MenuButtons/menuImplementationStyle`` is
  68. /// ``MenuImplementationStyle/dynamicPopover``.
  69. ///
  70. /// - Parameters:
  71. /// - menu: The menu to show.
  72. /// - position: The position to show the menu at, relative to `widget`.
  73. /// - widget: The widget to attach `menu` to.
  74. /// - handleClose: The action performed when the menu is closed.
  75. func showPopoverMenu(
  76. _ menu: Menu,
  77. at position: SIMD2<Int>,
  78. relativeTo widget: Widget,
  79. closeHandler handleClose: @escaping () -> Void
  80. )
  81. }
  82. }
  83. // MARK: Default Implementations
  84. extension BackendFeatures.MenuButtons where Self: BackendFeatures.PopoverMenus {
  85. /// The default implementation of ``BackendFeatures/MenuButtons/menuImplementationStyle-4blzf``
  86. /// for backends that implement ``BackendFeatures/PopoverMenus``.
  87. ///
  88. /// This simply returns `.dynamicPopover(self)`. You should very rarely have
  89. /// to override this.
  90. public var menuImplementationStyle: MenuImplementationStyle<Widget, Menu> {
  91. .dynamicPopover(self)
  92. }
  93. }
  94. extension BackendFeatures.MenuButtons where Self: BackendFeatures.AttachedMenus {
  95. /// The default implementation of ``BackendFeatures/MenuButtons/menuImplementationStyle-4blzf``
  96. /// for backends that implement ``BackendFeatures/AttachedMenus``.
  97. ///
  98. /// This simply returns `.menuButton(self)`. You should very rarely have
  99. /// to override this.
  100. public var menuImplementationStyle: MenuImplementationStyle<Widget, Menu> {
  101. .menuButton(self)
  102. }
  103. }
  104. // NB: The default implementations below serve to provide more helpful error messages when
  105. // the two `menuImplementationStyle` implementations above conflict or when neither of them
  106. // can be used -- i.e. when both (or neither) of `PopoverMenus` and `AttachedMenus` are
  107. // conformed to.
  108. extension BackendFeatures.MenuButtons where Self: BackendFeatures.PopoverMenus,
  109. Self: BackendFeatures.AttachedMenus
  110. {
  111. @available(
  112. *,
  113. unavailable,
  114. message: """
  115. you should only conform to one of 'PopoverMenus' or 'AttachedMenus'. Implement \
  116. 'menuImplementationStyle' manually if conforming to both is intentional
  117. """
  118. )
  119. public var menuImplementationStyle: MenuImplementationStyle<Widget, Menu> {
  120. fatalError("unavailable default implementation of 'menuImplementationStyle'")
  121. }
  122. }
  123. extension BackendFeatures.MenuButtons {
  124. @available(
  125. *,
  126. unavailable,
  127. message: """
  128. you need to conform to one of 'PopoverMenus' or 'AttachedMenus' for full 'MenuButtons' conformance
  129. """
  130. )
  131. public var menuImplementationStyle: MenuImplementationStyle<Widget, Menu> {
  132. fatalError("unavailable default implementation of 'menuImplementationStyle'")
  133. }
  134. }