EnvironmentValues.swift 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. import Foundation
  2. /// The environment used when constructing scenes and views. Each scene or view
  3. /// gets to modify the environment before passing it on to its children, which
  4. /// is the basis of many view modifiers.
  5. public struct EnvironmentValues {
  6. /// A font resolution context derived from the current environment.
  7. ///
  8. /// Essentially just a subset of the environment.
  9. @MainActor
  10. public var fontResolutionContext: Font.Context {
  11. Font.Context(
  12. overlay: fontOverlay,
  13. deviceClass: backend.deviceClass,
  14. resolveTextStyle: { backend.resolveTextStyle($0) }
  15. )
  16. }
  17. /// The current font resolved to a form suitable for rendering.
  18. ///
  19. /// Just a helper method for our own backends. We haven't made this public
  20. /// because it would be weird to have two pretty equivalent ways of resolving
  21. /// fonts.
  22. @MainActor
  23. @_spi(Backends) public var resolvedFont: Font.Resolved {
  24. font.resolve(in: fontResolutionContext)
  25. }
  26. /// The suggested foreground color for backends to use.
  27. ///
  28. /// Backends don't neccessarily have to obey this when
  29. /// ``EnvironmentValues/foregroundColor`` is `nil`.
  30. public var suggestedForegroundColor: Color {
  31. foregroundColor ?? colorScheme.defaultForegroundColor
  32. }
  33. /// Called by view graph nodes when they resize due to an internal state
  34. /// change and end up changing size.
  35. ///
  36. /// Each view graph node sets its own handler when passing the environment
  37. /// on to its children, setting up a bottom-up update chain up which resize
  38. /// events can propagate.
  39. @_spi(Backends) public var onResize: @MainActor (_ newSize: ViewSize) -> Void
  40. /// Backing storage for extensible subscript
  41. private var values: [ObjectIdentifier: Any]
  42. /// An internal environment value used to control whether layout caching is
  43. /// enabled or not.
  44. ///
  45. /// This is set to `true` when computing non-final layouts. E.g. when a stack
  46. /// computes the minimum and maximum sizes of its children, it should enable
  47. /// layout caching because those updates are guaranteed to be non-final. The
  48. /// reason that we can't cache on non-final updates is that the last layout
  49. /// proposal received by each view must be its intended final proposal.
  50. var allowLayoutCaching: Bool = false
  51. /// Backing storage for observable subscript
  52. private var observableObjects: [ObjectIdentifier: any ObservableObject]
  53. /// Gets an environment value given an environment key's metatype.
  54. ///
  55. /// - Parameter key: The type of the key.
  56. /// - Returns: The environment value associated with `key`, or the key's
  57. /// default value if it hasn't been set in the environment yet.
  58. public subscript<T: EnvironmentKey>(_ key: T.Type) -> T.Value {
  59. get {
  60. values[ObjectIdentifier(T.self), default: T.defaultValue] as! T.Value
  61. }
  62. set {
  63. values[ObjectIdentifier(T.self)] = newValue
  64. }
  65. }
  66. public subscript<T: ObservableObject>(observable key: T.Type) -> T? {
  67. get {
  68. guard let value = observableObjects[ObjectIdentifier(T.self)] as? T? else {
  69. let message =
  70. "EnvironmentValues type mismatch: value for key '\(T.self).self' doesn't match expected type '\(T.self)'"
  71. fatalError(message)
  72. }
  73. return value
  74. }
  75. set {
  76. observableObjects[ObjectIdentifier(T.self)] = newValue
  77. }
  78. }
  79. /// Brings the current window forward.
  80. ///
  81. /// This is not guaranteed to always bring the window to the top (due
  82. /// to focus stealing prevention).
  83. @MainActor
  84. func bringWindowForward() {
  85. func activate<Backend: BaseAppBackend>(with backend: Backend) {
  86. backend.activate(window: window as! Backend.Window)
  87. }
  88. activate(with: backend)
  89. }
  90. /// The backend in use.
  91. ///
  92. /// Mustn't change throughout the app's lifecycle.
  93. let backend: any BaseAppBackend
  94. /// Presents an 'Open file' dialog fit for selecting a single file.
  95. ///
  96. /// Displays as a modal for the current window, or the entire app if
  97. /// accessed outside of a scene's view graph (in which case the backend
  98. /// can decide whether to make it an app modal, a standalone window, or a
  99. /// modal for a window of its choosing).
  100. ///
  101. /// - Important: GtkBackend, Gtk3Backend, and WinUIBackend will only
  102. /// enable _either_ files or directories for selection, but won't
  103. /// enable both types in a single dialog.
  104. @MainActor
  105. @available(tvOS, unavailable, message: "tvOS does not provide file system access")
  106. public var chooseFile: PresentSingleFileOpenDialogAction {
  107. PresentSingleFileOpenDialogAction(
  108. backend: backend,
  109. window: MainActorBox(value: window)
  110. )
  111. }
  112. /// Presents a 'Save file' dialog fit for selecting a save destination.
  113. ///
  114. /// Displays as a modal for the current window, or the entire app if
  115. /// accessed outside of a scene's view graph (in which case the backend
  116. /// can decide whether to make it an app modal, a standalone window, or a
  117. /// window of its choosing).
  118. @MainActor
  119. public var chooseFileSaveDestination: PresentFileSaveDialogAction {
  120. PresentFileSaveDialogAction(
  121. backend: backend,
  122. window: MainActorBox(value: window)
  123. )
  124. }
  125. /// Presents an alert for the current window, or the entire app if accessed
  126. /// outside of a scene's view graph (in which case the backend can decide
  127. /// whether to make it an app modal, a standalone window, or a modal for a
  128. /// window of its choosing).
  129. @MainActor
  130. public var presentAlert: PresentAlertAction {
  131. PresentAlertAction(environment: self)
  132. }
  133. /// Opens a URL with the default application.
  134. ///
  135. /// May present an application picker if multiple applications are registered
  136. /// for the given URL protocol.
  137. ///
  138. /// `nil` on platforms that don't support opening external URLS (none at the
  139. /// moment).
  140. @MainActor
  141. public var openURL: OpenURLAction {
  142. OpenURLAction(backend: backend)
  143. }
  144. /// Opens a window with the specified ID.
  145. @MainActor
  146. public var openWindow: OpenWindowAction {
  147. OpenWindowAction(environment: self)
  148. }
  149. /// Closes the enclosing window.
  150. @MainActor
  151. public var dismissWindow: DismissWindowAction {
  152. DismissWindowAction(
  153. backend: backend,
  154. window: MainActorBox(value: window)
  155. )
  156. }
  157. /// Reveals a file in the system's file manager.
  158. ///
  159. /// This opens the file's enclosing directory and highlights the file.
  160. ///
  161. /// `nil` on platforms that don't support revealing files, e.g. iOS.
  162. @MainActor
  163. public var revealFile: RevealFileAction? {
  164. RevealFileAction(backend: backend)
  165. }
  166. /// Whether the backend can have multiple windows open at once. Mobile
  167. /// backends generally can't.
  168. @MainActor
  169. public var supportsMultipleWindows: Bool {
  170. backend.supportsMultipleWindows
  171. }
  172. /// The display styles supported by ``DatePicker``. ``datePickerStyle`` must be one of these.
  173. public let supportedDatePickerStyles: [DatePickerStyle]
  174. /// Checks whether a picker style is supported by the current backend.
  175. @MainActor
  176. public var isPickerStyleSupported: PickerSupportedAction {
  177. PickerSupportedAction(backend: backend)
  178. }
  179. /// Creates the default environment.
  180. ///
  181. /// - Parameters:
  182. /// - backend: The app's backend.
  183. @_spi(Backends) public init<Backend: BaseAppBackend>(backend: Backend) {
  184. self.backend = backend
  185. onResize = { _ in }
  186. values = [:]
  187. observableObjects = [:]
  188. if let backend = backend as? any BackendFeatures.DatePickers {
  189. self.supportedDatePickerStyles = backend.supportedDatePickerStyles
  190. } else {
  191. self.supportedDatePickerStyles = [.automatic]
  192. }
  193. }
  194. /// Returns a copy of the environment with the specified property set to the
  195. /// provided new value.
  196. ///
  197. /// - Parameters:
  198. /// - keyPath: A key path to the property to set.
  199. /// - newValue: The new value of the property.
  200. /// - Returns: A copy of the environment with the specified property set to
  201. /// `newValue`.
  202. public func with<T>(_ keyPath: WritableKeyPath<Self, T>, _ newValue: T) -> Self {
  203. var environment = self
  204. environment[keyPath: keyPath] = newValue
  205. return environment
  206. }
  207. }
  208. extension EnvironmentValues {
  209. /// The app storage provider to use for `@AppStorage` property wrappers.
  210. private struct __Key_appStorageProvider: EnvironmentKey {
  211. static let defaultValue: any AppStorageProvider = DefaultAppStorageProvider()
  212. }
  213. public var appStorageProvider: any AppStorageProvider {
  214. get { self[__Key_appStorageProvider.self] }
  215. set { self[__Key_appStorageProvider.self] = newValue }
  216. }
  217. /// The current stack orientation.
  218. ///
  219. /// Inherited by ``ForEach`` and ``Group`` so that they can be used without
  220. /// affecting layout.
  221. private struct __Key_layoutOrientation: EnvironmentKey {
  222. static let defaultValue: Orientation = .vertical
  223. }
  224. public var layoutOrientation: Orientation {
  225. get { self[__Key_layoutOrientation.self] }
  226. set { self[__Key_layoutOrientation.self] = newValue }
  227. }
  228. /// The current stack alignment.
  229. ///
  230. /// Inherited by ``ForEach`` and ``Group`` so that they can be used without
  231. /// affecting layout.
  232. private struct __Key_layoutAlignment: EnvironmentKey {
  233. static let defaultValue: StackAlignment = .center
  234. }
  235. public var layoutAlignment: StackAlignment {
  236. get { self[__Key_layoutAlignment.self] }
  237. set { self[__Key_layoutAlignment.self] = newValue }
  238. }
  239. /// The current stack spacing.
  240. ///
  241. /// Inherited by ``ForEach`` and ``Group`` so that they can be used without
  242. /// affecting layout.
  243. private struct __Key_layoutSpacing: EnvironmentKey {
  244. static let defaultValue: Int = 10
  245. }
  246. public var layoutSpacing: Int {
  247. get { self[__Key_layoutSpacing.self] }
  248. set { self[__Key_layoutSpacing.self] = newValue }
  249. }
  250. /// The current font.
  251. private struct __Key_font: EnvironmentKey {
  252. static let defaultValue: Font = .body
  253. }
  254. public var font: Font {
  255. get { self[__Key_font.self] }
  256. set { self[__Key_font.self] = newValue }
  257. }
  258. /// A font overlay storing font modifications.
  259. ///
  260. /// If these conflict with the font's internal overlay, these win out.
  261. ///
  262. /// We keep this separate overlay for modifiers because we want modifiers to
  263. /// be persisted even if the developer sets a custom font further down the
  264. /// view hierarchy.
  265. private struct __Key_fontOverlay: EnvironmentKey {
  266. static let defaultValue: Font.Overlay = Font.Overlay()
  267. }
  268. internal var fontOverlay: Font.Overlay {
  269. get { self[__Key_fontOverlay.self] }
  270. set { self[__Key_fontOverlay.self] = newValue }
  271. }
  272. /// How lines should be aligned relative to each other when line wrapped.
  273. private struct __Key_multilineTextAlignment: EnvironmentKey {
  274. static let defaultValue: HorizontalAlignment = .leading
  275. }
  276. public var multilineTextAlignment: HorizontalAlignment {
  277. get { self[__Key_multilineTextAlignment.self] }
  278. set { self[__Key_multilineTextAlignment.self] = newValue }
  279. }
  280. /// Whether to override the case of displayed ``Text`` views.
  281. ///
  282. /// `nil` displays the text without any case changes.
  283. private struct __Key_textCase: EnvironmentKey {
  284. static let defaultValue: Text.Case? = nil
  285. }
  286. public var textCase: Text.Case? {
  287. get { self[__Key_textCase.self] }
  288. set { self[__Key_textCase.self] = newValue }
  289. }
  290. /// The current color scheme of the current view scope.
  291. private struct __Key_colorScheme: EnvironmentKey {
  292. static let defaultValue: ColorScheme = .light
  293. }
  294. public var colorScheme: ColorScheme {
  295. get { self[__Key_colorScheme.self] }
  296. set { self[__Key_colorScheme.self] = newValue }
  297. }
  298. /// The foreground color.
  299. ///
  300. /// `nil` means that the default foreground color of the current color scheme
  301. /// should be used.
  302. private struct __Key_foregroundColor: EnvironmentKey {
  303. static let defaultValue: Color? = nil
  304. }
  305. public var foregroundColor: Color? {
  306. get { self[__Key_foregroundColor.self] }
  307. set { self[__Key_foregroundColor.self] = newValue }
  308. }
  309. /// Called when a text field gets submitted (usually due to the user
  310. /// pressing Enter/Return).
  311. private struct __Key_onSubmit: EnvironmentKey {
  312. static let defaultValue: (@MainActor @Sendable () -> Void)? = nil
  313. }
  314. public var onSubmit: (@MainActor @Sendable () -> Void)? {
  315. get { self[__Key_onSubmit.self] }
  316. set { self[__Key_onSubmit.self] = newValue }
  317. }
  318. /// The scale factor of the current window.
  319. private struct __Key_windowScaleFactor: EnvironmentKey {
  320. static let defaultValue: Double = 1
  321. }
  322. public var windowScaleFactor: Double {
  323. get { self[__Key_windowScaleFactor.self] }
  324. set { self[__Key_windowScaleFactor.self] = newValue }
  325. }
  326. /// The type of input that text fields represent.
  327. ///
  328. /// This affects autocomplete suggestions, and on devices with no physical keyboard, which
  329. /// on-screen keyboard to use.
  330. ///
  331. /// - Warning: Do not use this in place of validation, even if you only plan on supporting
  332. /// mobile devices, as this does not restrict copy-paste and many mobile devices support
  333. /// Bluetooth keyboards.
  334. private struct __Key_textContentType: EnvironmentKey {
  335. static let defaultValue: TextContentType = .text
  336. }
  337. public var textContentType: TextContentType {
  338. get { self[__Key_textContentType.self] }
  339. set { self[__Key_textContentType.self] = newValue }
  340. }
  341. /// The way that scrollable content interacts with the software keyboard.
  342. private struct __Key_scrollDismissesKeyboardMode: EnvironmentKey {
  343. static let defaultValue: ScrollDismissesKeyboardMode = .automatic
  344. }
  345. public var scrollDismissesKeyboardMode: ScrollDismissesKeyboardMode {
  346. get { self[__Key_scrollDismissesKeyboardMode.self] }
  347. set { self[__Key_scrollDismissesKeyboardMode.self] = newValue }
  348. }
  349. /// The style of list to use.
  350. private struct __Key_listStyle: EnvironmentKey {
  351. static let defaultValue: ListStyle = .default
  352. }
  353. @_spi(Backends) public var listStyle: ListStyle {
  354. get { self[__Key_listStyle.self] }
  355. set { self[__Key_listStyle.self] = newValue }
  356. }
  357. /// The style of toggle to use.
  358. private struct __Key_toggleStyle: EnvironmentKey {
  359. static let defaultValue: ToggleStyle = .button
  360. }
  361. public var toggleStyle: ToggleStyle {
  362. get { self[__Key_toggleStyle.self] }
  363. set { self[__Key_toggleStyle.self] = newValue }
  364. }
  365. /// Whether the text should be selectable.
  366. ///
  367. /// Set by ``View/textSelectionEnabled(_:)``.
  368. private struct __Key_isTextSelectionEnabled: EnvironmentKey {
  369. static let defaultValue: Bool = false
  370. }
  371. public var isTextSelectionEnabled: Bool {
  372. get { self[__Key_isTextSelectionEnabled.self] }
  373. set { self[__Key_isTextSelectionEnabled.self] = newValue }
  374. }
  375. /// The resizing behaviour of windows.
  376. ///
  377. /// Set by ``Window/windowResizability(_:)->Scene``.
  378. private struct __Key_windowResizability: EnvironmentKey {
  379. static let defaultValue: WindowResizability = .automatic
  380. }
  381. internal var windowResizability: WindowResizability {
  382. get { self[__Key_windowResizability.self] }
  383. set { self[__Key_windowResizability.self] = newValue }
  384. }
  385. /// The default launch behavior of windows.
  386. ///
  387. /// Set by ``Window/defaultLaunchBehavior(_:)->Scene``.
  388. private struct __Key_defaultLaunchBehavior: EnvironmentKey {
  389. static let defaultValue: SceneLaunchBehavior = .automatic
  390. }
  391. internal var defaultLaunchBehavior: SceneLaunchBehavior {
  392. get { self[__Key_defaultLaunchBehavior.self] }
  393. set { self[__Key_defaultLaunchBehavior.self] = newValue }
  394. }
  395. /// The default size of windows.
  396. ///
  397. /// Defaults to 900x450.
  398. ///
  399. /// Set by ``Window/defaultSize(width:height:)->Scene``.
  400. private struct __Key_defaultWindowSize: EnvironmentKey {
  401. static let defaultValue: SIMD2<Int> = SIMD2(900, 450)
  402. }
  403. internal var defaultWindowSize: SIMD2<Int> {
  404. get { self[__Key_defaultWindowSize.self] }
  405. set { self[__Key_defaultWindowSize.self] = newValue }
  406. }
  407. /// The menu ordering to use.
  408. private struct __Key_menuOrder: EnvironmentKey {
  409. static let defaultValue: MenuOrder = .automatic
  410. }
  411. public var menuOrder: MenuOrder {
  412. get { self[__Key_menuOrder.self] }
  413. set { self[__Key_menuOrder.self] = newValue }
  414. }
  415. /// Backing store for ``EnvironmentValues/openWindowFunctionsByID``.
  416. /// Used to resolve "non-sendable type" warnings in Swift 5 and errors in Swift 6 language mode.
  417. private struct __Key_openWindowFunctionsByIDStore: EnvironmentKey {
  418. static let defaultValue: UncheckedSendable<Box<[String: @MainActor () -> Void]>> = UncheckedSendable(wrappedValue: Box([:]))
  419. }
  420. private var openWindowFunctionsByIDStore: UncheckedSendable<Box<[String: @MainActor () -> Void]>> {
  421. get { self[__Key_openWindowFunctionsByIDStore.self] }
  422. set { self[__Key_openWindowFunctionsByIDStore.self] = newValue }
  423. }
  424. /// A mapping of window IDs to functions that open the corresponding windows.
  425. internal var openWindowFunctionsByID: Box<[String: @MainActor () -> Void]> {
  426. get {
  427. openWindowFunctionsByIDStore.wrappedValue
  428. }
  429. set {
  430. openWindowFunctionsByIDStore.wrappedValue = newValue
  431. }
  432. }
  433. /// The app's lifecycle phase.
  434. ///
  435. /// Unlike in SwiftUI, where the app's lifecycle phase can only be accessed
  436. /// by using `@Environment(\.scenePhase)` directly on the ``App`` struct, this
  437. /// environment value can be accessed from anywhere within the application.
  438. private struct __Key_appPhase: EnvironmentKey {
  439. static let defaultValue: AppPhase = .active
  440. }
  441. public package(set) var appPhase: AppPhase {
  442. get { self[__Key_appPhase.self] }
  443. set { self[__Key_appPhase.self] = newValue }
  444. }
  445. /// The current scene's lifecycle phase.
  446. ///
  447. /// - Important: Unlike SwiftUI, this environment value cannot be accessed from
  448. /// outside a scene. If you need to access the phase of the entire application,
  449. /// use ``appPhase`` instead.
  450. public package(set) var scenePhase: ScenePhase {
  451. get {
  452. guard let phase = self[__Key_scenePhase.self] else {
  453. if window != nil {
  454. // If there's a window but no scenePhase, we assume that the
  455. // backend is actively trying to _set_ the scene phase; return
  456. // a dummy value to prevent a crash.
  457. return .inactive
  458. }
  459. fatalError(
  460. """
  461. 'scenePhase' accessed from outside a scene (most likely \
  462. with an @Environment property on the App struct); you \
  463. probably meant to use 'appPhase' instead
  464. """
  465. )
  466. }
  467. return phase
  468. }
  469. set { self[__Key_scenePhase.self] = newValue }
  470. }
  471. private struct __Key_scenePhase: EnvironmentKey {
  472. static let defaultValue: ScenePhase? = nil
  473. }
  474. /// Backing store for ``EnvironmentValues/window``.
  475. /// Used to resolve "non-sendable type" warnings in Swift 5 and errors in Swift 6 language mode.
  476. private struct __Key_windowStore: EnvironmentKey {
  477. static let defaultValue: UncheckedSendable<Any?> = UncheckedSendable(wrappedValue: nil)
  478. }
  479. private var windowStore: UncheckedSendable<Any?> {
  480. get { self[__Key_windowStore.self] }
  481. set { self[__Key_windowStore.self] = newValue }
  482. }
  483. /// The backend's representation of the window that the current view is
  484. /// in, if any.
  485. ///
  486. /// This is a very internal detail that should never get exposed to users.
  487. @_spi(Backends) public var window: Any? {
  488. get {
  489. windowStore.wrappedValue
  490. }
  491. set {
  492. windowStore.wrappedValue = newValue
  493. }
  494. }
  495. /// Backing store for ``EnvironmentValues/sheet``.
  496. /// Used to resolve "non-sendable type" warnings in Swift 5 and errors in Swift 6 language mode.
  497. private struct __Key_sheetStore: EnvironmentKey {
  498. static let defaultValue: UncheckedSendable<Any?> = UncheckedSendable(wrappedValue: nil)
  499. }
  500. private var sheetStore: UncheckedSendable<Any?> {
  501. get { self[__Key_sheetStore.self] }
  502. set { self[__Key_sheetStore.self] = newValue }
  503. }
  504. /// The backend's representation of the sheet that the current view is
  505. /// in, if any.
  506. ///
  507. /// This is a very internal detail that should never get exposed to users.
  508. @_spi(Backends) public var sheet: Any? {
  509. get {
  510. sheetStore.wrappedValue
  511. }
  512. set {
  513. sheetStore.wrappedValue = newValue
  514. }
  515. }
  516. /// The current calendar that views should use when handling dates.
  517. private struct __Key_calendar: EnvironmentKey {
  518. static let defaultValue: Calendar = .current
  519. }
  520. public var calendar: Calendar {
  521. get { self[__Key_calendar.self] }
  522. set { self[__Key_calendar.self] = newValue }
  523. }
  524. /// The current time zone that views should use when handling dates.
  525. private struct __Key_timeZone: EnvironmentKey {
  526. static let defaultValue: TimeZone = .current
  527. }
  528. public var timeZone: TimeZone {
  529. get { self[__Key_timeZone.self] }
  530. set { self[__Key_timeZone.self] = newValue }
  531. }
  532. /// The current locale.
  533. private struct __Key_locale: EnvironmentKey {
  534. static let defaultValue: Locale = .current
  535. }
  536. public var locale: Locale {
  537. get { self[__Key_locale.self] }
  538. set { self[__Key_locale.self] = newValue }
  539. }
  540. /// The display style used by ``Picker``.
  541. private struct __Key_pickerStyle: EnvironmentKey {
  542. static let defaultValue: any PickerStyle = .automatic
  543. }
  544. public var pickerStyle: any PickerStyle {
  545. get { self[__Key_pickerStyle.self] }
  546. set { self[__Key_pickerStyle.self] = newValue }
  547. }
  548. /// The display style used by ``DatePicker``.
  549. private struct __Key_datePickerStyle: EnvironmentKey {
  550. static let defaultValue: DatePickerStyle = .automatic
  551. }
  552. public var datePickerStyle: DatePickerStyle {
  553. get { self[__Key_datePickerStyle.self] }
  554. set { self[__Key_datePickerStyle.self] = newValue }
  555. }
  556. /// Whether user interaction is enabled.
  557. ///
  558. /// Set by ``View/disabled(_:)``.
  559. private struct __Key_isEnabled: EnvironmentKey {
  560. static let defaultValue: Bool = true
  561. }
  562. public var isEnabled: Bool {
  563. get { self[__Key_isEnabled.self] }
  564. set { self[__Key_isEnabled.self] = newValue }
  565. }
  566. /// The number of lines text can occupy and whether to reserve that space.
  567. private struct __Key_lineLimitSettings: EnvironmentKey {
  568. static let defaultValue: LineLimit? = nil
  569. }
  570. public var lineLimitSettings: LineLimit? {
  571. get { self[__Key_lineLimitSettings.self] }
  572. set { self[__Key_lineLimitSettings.self] = newValue }
  573. }
  574. /// The maximum number of lines that text can occupy in a view.
  575. public var lineLimit: Int? {
  576. lineLimitSettings?.limit
  577. }
  578. /// Whether the current device has a circular screen. Primarily Android smart watches.
  579. private struct __Key_isCircularScreen: EnvironmentKey {
  580. static let defaultValue: Bool = false
  581. }
  582. public var isCircularScreen: Bool {
  583. get { self[__Key_isCircularScreen.self] }
  584. set { self[__Key_isCircularScreen.self] = newValue }
  585. }
  586. /// The display style used by ``Button``.
  587. private struct __Key_buttonStyle: EnvironmentKey {
  588. static let defaultValue: ButtonStyle? = nil
  589. }
  590. public var buttonStyle: ButtonStyle? {
  591. get { self[__Key_buttonStyle.self] }
  592. set { self[__Key_buttonStyle.self] = newValue }
  593. }
  594. /// The default button style as declared by the backend.
  595. @MainActor
  596. public var defaultButtonStyle: ButtonStyle {
  597. backend.defaultButtonStyle()
  598. }
  599. /// The resolved ``ButtonStyle``. Either ``buttonStyle``, or ``defaultButtonStyle`` if nil.
  600. @MainActor
  601. public var resolvedButtonStyle: ButtonStyle {
  602. buttonStyle ?? defaultButtonStyle
  603. }
  604. /// The amount of padding that the current backend applies to the labels of buttons with the current ``ButtonStyle``.
  605. @MainActor
  606. public var buttonPadding: SIMD2<Int> {
  607. backend.buttonPadding(in: self)
  608. }
  609. /// The device class of the current device.
  610. @MainActor
  611. public var deviceClass: DeviceClass { backend.deviceClass }
  612. }
  613. extension EnvironmentValues {
  614. func applyingTextTransforms(to string: String) -> String {
  615. var string = string
  616. switch textCase {
  617. case .lowercase: string = string.lowercased()
  618. case .uppercase: string = string.uppercased()
  619. case nil: break
  620. }
  621. return string
  622. }
  623. }
  624. /// A key that can be used to extend the environment with new properties.
  625. public protocol EnvironmentKey<Value> {
  626. /// The type of value the key can hold.
  627. associatedtype Value
  628. /// The default value for the key.
  629. static var defaultValue: Value { get }
  630. }