Buttons.swift 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. extension BackendFeatures {
  2. public typealias Buttons = StringLabelButtons & ViewLabelButtons
  3. /// Backend methods for simple buttons.
  4. ///
  5. /// These are used by ``Toggle`` and ``Menu``.
  6. @MainActor
  7. public protocol StringLabelButtons: Core {
  8. /// Creates a labelled button with an action triggered on click/tap.
  9. ///
  10. /// Used by controls in button style like ``Menu``, ``Toggle`` or more constrained result builders.
  11. ///
  12. /// - Returns: A button.
  13. func createSimpleButton() -> Widget
  14. /// Sets a button's label and action.
  15. ///
  16. /// - Parameters:
  17. /// - button: The button to update.
  18. /// - label: The button's label.
  19. /// - environment: The current environment.
  20. /// - action: The action to perform when the button is clicked/tapped.
  21. /// This replaces any existing actions.
  22. func updateSimpleButton(
  23. _ button: Widget,
  24. label: String,
  25. environment: EnvironmentValues,
  26. action: @escaping () -> Void
  27. )
  28. }
  29. /// Backend methods for more complex buttons supporting an arbitrary ``View`` as label.
  30. ///
  31. /// These are used by ``Button``.
  32. @MainActor
  33. public protocol ViewLabelButtons: Core {
  34. /// Creates a button that uses `widget` as its label with an action triggered on click/tap.
  35. ///
  36. /// Predominantly used by ``Button``.
  37. ///
  38. /// - Parameters:
  39. /// - widget: The widget the button should use as label.
  40. ///
  41. /// - Returns: A button.
  42. func createButton(wrapping widget: Widget) -> Widget
  43. /// Sets a button's action and updates the rendered style based on the environment.
  44. ///
  45. /// - Parameters:
  46. /// - button: The button to update.
  47. /// - environment: The current environment.
  48. /// - action: The action to perform when the button is clicked/tapped.
  49. /// This replaces any existing actions.
  50. func updateButton(
  51. _ button: Widget,
  52. environment: EnvironmentValues,
  53. action: @escaping () -> Void
  54. )
  55. /// Buttons are set to label size + padding by SwiftCrossUI.
  56. /// Backends may choose different amounts of padding for different button styles.
  57. ///
  58. /// A padding of (0, 0) is recommended for all styles without a system defined background.
  59. ///
  60. /// - Parameters:
  61. /// - environment: The current environment.
  62. ///
  63. /// - Returns: A vector containing the **total** spacing horizontally and vertically.
  64. func buttonPadding(in environment: EnvironmentValues) -> SIMD2<Int>
  65. /// The default button style that the backend desires.
  66. ///
  67. /// - Returns: The default ``ButtonStyle``.
  68. func defaultButtonStyle() -> ButtonStyle
  69. /// Modifies the environment for the body of a button label.
  70. ///
  71. /// Backends may implement their own to align with the platform's conventions more closely.
  72. /// The default implementation applies SwiftUI-like behavior.
  73. ///
  74. /// - Returns: The modified environment.
  75. func computeButtonLabelEnvironment(
  76. from environment: EnvironmentValues
  77. ) -> EnvironmentValues
  78. }
  79. }
  80. // MARK: - Default Implementations
  81. extension BackendFeatures.ViewLabelButtons {
  82. public func computeButtonLabelEnvironment(
  83. from environment: EnvironmentValues
  84. ) -> EnvironmentValues {
  85. var labelEnvironment = environment
  86. let buttonStyle = environment.resolvedButtonStyle.kind
  87. let deviceClass = environment.backend.deviceClass
  88. if
  89. !environment.isEnabled, buttonStyle == .bordered,
  90. deviceClass == .desktop || deviceClass == .tv
  91. {
  92. labelEnvironment = labelEnvironment.with(
  93. \.foregroundColor,
  94. environment.suggestedForegroundColor.opacity(0.3) // SwiftUI uses tertiary afaict.
  95. )
  96. }
  97. // The disabled opacities and defaults are based on discoveries in SwiftUI.
  98. // Set the default foregroundColor for the label unless overridden.
  99. // Uses the same colors as SwiftUI.
  100. if
  101. buttonStyle == .borderless,
  102. deviceClass == .desktop
  103. {
  104. // Approximately equivalent to Color.secondary in SwiftUI.
  105. let opacity = environment.colorScheme == .dark ? 0.7 : 0.5
  106. labelEnvironment = labelEnvironment.with(
  107. \.foregroundColor,
  108. environment.foregroundColor ?? environment.suggestedForegroundColor.opacity(opacity)
  109. )
  110. }
  111. return labelEnvironment
  112. }
  113. }