react-native-screens 5.0.0-alpha.2 → 5.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/Package.swift +87 -0
  2. package/README.md +0 -7
  3. package/android/src/main/cpp/NativeProxy.cpp +30 -6
  4. package/android/src/main/cpp/NativeProxy.h +7 -1
  5. package/android/src/main/java/com/swmansion/rnscreens/common/container/Container.kt +8 -0
  6. package/android/src/main/java/com/swmansion/rnscreens/common/container/ContainerItem.kt +13 -1
  7. package/android/src/main/java/com/swmansion/rnscreens/common/text/ReactTextAppearance.kt +62 -0
  8. package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearance.kt +20 -0
  9. package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearanceDefaults.kt +65 -0
  10. package/android/src/main/java/com/swmansion/rnscreens/helpers/PropIconResolver.kt +17 -23
  11. package/android/src/main/java/com/swmansion/rnscreens/legacy/Screen.kt +3 -1
  12. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStack.kt +2 -0
  13. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetBehaviorController.kt +49 -18
  14. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetDimensionsCoordinator.kt +65 -40
  15. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetKeyboardCoordinator.kt +101 -0
  16. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetNativeDismissCoordinator.kt +0 -6
  17. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetAvailableHeightProvider.kt +44 -0
  18. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialog.kt +35 -3
  19. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialogManager.kt +33 -106
  20. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/interfaces/FormSheetDialogEventEmitter.kt +2 -0
  21. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/model/FormSheetDetents.kt +61 -15
  22. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetAnimatorFactory.kt +8 -9
  23. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDimmingManager.kt +115 -0
  24. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDismissalOrigin.kt +22 -0
  25. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentation.kt +173 -0
  26. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationManager.kt +141 -49
  27. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationState.kt +1 -1
  28. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetStackRegistry.kt +28 -0
  29. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/event/FormSheetDismissEvent.kt +29 -0
  30. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostEventEmitter.kt +7 -0
  31. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostViewManager.kt +2 -0
  32. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderApplicator.kt +369 -58
  33. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderBackPressHandler.kt +11 -0
  34. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt +143 -98
  35. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt +155 -23
  36. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderContentScrimDrawable.kt +36 -0
  37. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderCollapsedTitleGravityMode.kt +6 -0
  38. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfig.kt +170 -167
  39. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigViewManager.kt +385 -12
  40. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationObserver.kt +0 -4
  41. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationProviding.kt +32 -3
  42. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderDelegate.kt +0 -7
  43. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderInvalidationFlags.kt +7 -2
  44. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuApplicator.kt +27 -280
  45. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuController.kt +448 -0
  46. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuDelegate.kt +10 -0
  47. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuMapper.kt +28 -20
  48. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuConfig.kt +3 -55
  49. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuGroupMetadata.kt +1 -10
  50. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuItemConfig.kt +1 -3
  51. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuModel.kt +180 -0
  52. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementOptions.kt +57 -6
  53. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementRawUpdate.kt +3 -1
  54. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementUpdate.kt +1 -0
  55. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuIconResolver.kt +4 -3
  56. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuUpdateQueue.kt +23 -9
  57. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainer.kt +103 -3
  58. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHost.kt +13 -0
  59. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHostViewManager.kt +14 -0
  60. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreen.kt +4 -0
  61. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt +20 -1
  62. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragmentDelegate.kt +12 -0
  63. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceCoordinator.kt +2 -4
  64. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/MenuHelpers.kt +5 -23
  65. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainer.kt +46 -25
  66. package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHost.kt +10 -0
  67. package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHostA11yCoordinator.kt +23 -2
  68. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreen.kt +17 -0
  69. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenFragment.kt +3 -0
  70. package/android/src/main/java/com/swmansion/rnscreens/utils/DimensionUtils.kt +7 -0
  71. package/android/src/main/java/com/swmansion/rnscreens/utils/StyleUtils.kt +22 -0
  72. package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_available_space.xml +8 -0
  73. package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_entire_space.xml +8 -0
  74. package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_available_space.xml +8 -0
  75. package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_entire_space.xml +8 -0
  76. package/cpp/legacy/RNSScreenRemovalListener.cpp +31 -1
  77. package/cpp/legacy/RNSScreenRemovalListener.h +18 -3
  78. package/ios/RNSEnums.h +2 -0
  79. package/ios/conversion/RNSConversions.h +7 -108
  80. package/ios/legacy/RNSFullWindowOverlay.mm +1 -1
  81. package/ios/legacy/RNSModalScreen.mm +1 -1
  82. package/ios/legacy/RNSScreen.mm +8 -19
  83. package/ios/legacy/RNSScreenStackHeaderConfig.mm +3 -1
  84. package/ios/legacy/RNSScreenStackHeaderSubview.mm +1 -1
  85. package/ios/legacy/RNSScreenWindowTraits.mm +5 -13
  86. package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.h +1 -3
  87. package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.mm +11 -22
  88. package/ios/modals/form-sheet/RNSFormSheetContentController.h +1 -1
  89. package/ios/modals/form-sheet/RNSFormSheetContentController.mm +7 -18
  90. package/ios/modals/form-sheet/RNSFormSheetHostComponentView.mm +4 -4
  91. package/ios/modals/form-sheet/RNSFormSheetHostShadowStateProxy.mm +2 -2
  92. package/ios/modals/form-sheet/RNSFormSheetUpdateFlags.h +3 -0
  93. package/ios/safe-area/RNSSafeAreaViewComponentView.mm +2 -2
  94. package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.mm +2 -3
  95. package/ios/split/RNSSplitAppearanceApplicator.h +4 -4
  96. package/ios/split/RNSSplitAppearanceApplicator.mm +53 -56
  97. package/ios/split/RNSSplitHostComponentEventEmitter.h +1 -0
  98. package/ios/split/RNSSplitHostComponentEventEmitter.mm +1 -1
  99. package/ios/split/RNSSplitHostComponentView.h +5 -15
  100. package/ios/split/RNSSplitHostComponentView.mm +55 -6
  101. package/ios/split/RNSSplitHostController.h +25 -6
  102. package/ios/split/RNSSplitHostController.mm +24 -92
  103. package/ios/split/RNSSplitHostProviders.h +67 -0
  104. package/ios/split/RNSSplitScreenComponentView.h +0 -39
  105. package/ios/split/RNSSplitScreenComponentView.mm +27 -10
  106. package/ios/split/RNSSplitScreenController.h +26 -3
  107. package/ios/split/RNSSplitScreenController.mm +13 -30
  108. package/ios/split/RNSSplitScreenShadowStateProxy.h +1 -40
  109. package/ios/split/RNSSplitScreenShadowStateProxy.mm +1 -26
  110. package/ios/split/conversion/RNSConversions-SplitView.h +56 -0
  111. package/ios/{conversion → split/conversion}/RNSConversions-SplitView.mm +4 -1
  112. package/ios/stack/conversion/RNSConversions-Stack.h +32 -0
  113. package/ios/stack/conversion/RNSConversions-Stack.mm +55 -0
  114. package/ios/stack/header/RNSStackHeaderAppearanceMapper.h +26 -0
  115. package/ios/stack/header/RNSStackHeaderAppearanceMapper.mm +88 -0
  116. package/ios/stack/header/RNSStackHeaderConfigComponentView.h +10 -1
  117. package/ios/stack/header/RNSStackHeaderConfigComponentView.mm +67 -35
  118. package/ios/stack/header/RNSStackHeaderConfigDataProviding.h +6 -0
  119. package/ios/stack/header/RNSStackHeaderConfigShadowStateProxy.mm +1 -1
  120. package/ios/stack/header/RNSStackHeaderIconResolver.mm +7 -0
  121. package/ios/stack/header/RNSStackHeaderItemComponentView.h +3 -1
  122. package/ios/stack/header/RNSStackHeaderItemComponentView.mm +15 -2
  123. package/ios/stack/header/RNSStackHeaderItemDataProviding.h +2 -0
  124. package/ios/stack/header/RNSStackHeaderItemShadowStateProxy.mm +1 -1
  125. package/ios/stack/header/RNSStackHeaderItemSpacerComponentView.mm +2 -1
  126. package/ios/stack/header/RNSStackHeaderMenuMapper.mm +4 -0
  127. package/ios/stack/host/RNSStackNavigationBar.h +25 -0
  128. package/ios/stack/host/RNSStackNavigationBar.mm +80 -0
  129. package/ios/stack/host/RNSStackNavigationBarCoordinator.h +8 -0
  130. package/ios/stack/host/RNSStackNavigationBarCoordinator.mm +11 -0
  131. package/ios/stack/host/RNSStackNavigationController.mm +13 -5
  132. package/ios/stack/host/RNSStackOperation.h +0 -2
  133. package/ios/stack/screen/RNSStackScreenComponentView.mm +9 -2
  134. package/ios/stack/screen/RNSStackScreenController.mm +12 -0
  135. package/ios/stack/screen/RNSStackScreenHeaderCoordinator.h +8 -0
  136. package/ios/stack/screen/RNSStackScreenHeaderCoordinator.mm +98 -16
  137. package/ios/tabs/RNSTabBarAppearanceCoordinator.mm +40 -9
  138. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.h +1 -1
  139. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.mm +1 -1
  140. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryContentComponentView.mm +1 -1
  141. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryEventEmitter.mm +1 -1
  142. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryShadowStateProxy.mm +1 -1
  143. package/ios/tabs/conversion/RNSConversions-Tabs.h +78 -0
  144. package/ios/{conversion → tabs/conversion}/RNSConversions-Tabs.mm +8 -71
  145. package/ios/tabs/extensions/RNSTabsHostComponentView+RNSImageLoader.h +1 -1
  146. package/ios/tabs/host/RNSTabsHostComponentView.h +2 -0
  147. package/ios/tabs/host/RNSTabsHostComponentView.mm +10 -3
  148. package/ios/tabs/host/RNSTabsHostEventEmitter.mm +1 -1
  149. package/ios/tabs/screen/RNSTabsScreenComponentView.mm +20 -5
  150. package/ios/utils/RNSDefines.h +3 -0
  151. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js +34 -19
  152. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  153. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js +33 -1
  154. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  155. package/lib/commonjs/components/stack/host/StackHost.js +9 -6
  156. package/lib/commonjs/components/stack/host/StackHost.js.map +1 -1
  157. package/lib/commonjs/components/stack/index.js.map +1 -1
  158. package/lib/commonjs/components/tabs/host/TabsHost.ios.js +1 -0
  159. package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
  160. package/lib/commonjs/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  161. package/lib/commonjs/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  162. package/lib/commonjs/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  163. package/lib/commonjs/fabric/stack/StackHostNativeComponent.js.map +1 -1
  164. package/lib/commonjs/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  165. package/lib/module/components/stack/header/StackHeaderConfig.android.js +35 -20
  166. package/lib/module/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  167. package/lib/module/components/stack/header/StackHeaderConfig.ios.js +34 -2
  168. package/lib/module/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  169. package/lib/module/components/stack/host/StackHost.js +9 -6
  170. package/lib/module/components/stack/host/StackHost.js.map +1 -1
  171. package/lib/module/components/stack/index.js.map +1 -1
  172. package/lib/module/components/tabs/host/TabsHost.ios.js +1 -0
  173. package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
  174. package/lib/module/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  175. package/lib/module/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  176. package/lib/module/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  177. package/lib/module/fabric/stack/StackHostNativeComponent.js.map +1 -1
  178. package/lib/module/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  179. package/lib/typescript/components/modals/form-sheet/FormSheet.types.d.ts +1 -1
  180. package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
  181. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts +616 -21
  182. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
  183. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts.map +1 -1
  184. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts +226 -2
  185. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts.map +1 -1
  186. package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts +2 -2
  187. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts +2 -0
  188. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts.map +1 -1
  189. package/lib/typescript/components/stack/host/StackHost.d.ts +1 -1
  190. package/lib/typescript/components/stack/host/StackHost.d.ts.map +1 -1
  191. package/lib/typescript/components/stack/host/StackHost.types.d.ts +24 -0
  192. package/lib/typescript/components/stack/host/StackHost.types.d.ts.map +1 -1
  193. package/lib/typescript/components/stack/index.d.ts +2 -2
  194. package/lib/typescript/components/stack/index.d.ts.map +1 -1
  195. package/lib/typescript/components/tabs/host/TabsHost.ios.d.ts.map +1 -1
  196. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts +16 -0
  197. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts.map +1 -1
  198. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts +3 -0
  199. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts.map +1 -1
  200. package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts +52 -0
  201. package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts.map +1 -1
  202. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts +25 -1
  203. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts.map +1 -1
  204. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts +2 -0
  205. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts.map +1 -1
  206. package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts +3 -1
  207. package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts.map +1 -1
  208. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +1 -0
  209. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
  210. package/package.json +8 -7
  211. package/react-native.config.js +3 -0
  212. package/src/components/modals/form-sheet/FormSheet.types.ts +1 -1
  213. package/src/components/stack/header/StackHeaderConfig.android.tsx +48 -23
  214. package/src/components/stack/header/StackHeaderConfig.android.types.ts +637 -21
  215. package/src/components/stack/header/StackHeaderConfig.ios.tsx +45 -1
  216. package/src/components/stack/header/StackHeaderConfig.ios.types.ts +238 -2
  217. package/src/components/stack/header/StackHeaderConfig.types.ts +2 -2
  218. package/src/components/stack/header/ios/StackHeaderItem.ios.types.ts +2 -0
  219. package/src/components/stack/host/StackHost.tsx +4 -2
  220. package/src/components/stack/host/StackHost.types.ts +26 -0
  221. package/src/components/stack/index.ts +6 -1
  222. package/src/components/tabs/host/TabsHost.ios.tsx +1 -0
  223. package/src/components/tabs/host/TabsHost.ios.types.ts +16 -0
  224. package/src/components/tabs/host/TabsHost.types.ts +3 -0
  225. package/src/fabric/stack/StackHeaderConfigAndroidNativeComponent.ts +78 -0
  226. package/src/fabric/stack/StackHeaderConfigIOSNativeComponent.ts +32 -0
  227. package/src/fabric/stack/StackHeaderItemIOSNativeComponent.ts +2 -0
  228. package/src/fabric/stack/StackHostNativeComponent.ts +11 -2
  229. package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +1 -0
  230. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingView.kt +0 -83
  231. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewManager.kt +0 -89
  232. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewPointerEvents.kt +0 -18
  233. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuSelectionController.kt +0 -83
  234. package/ios/conversion/RNSConversions-Stack.h +0 -32
  235. package/ios/conversion/RNSConversions-Stack.mm +0 -41
  236. package/ios/conversion/always_false.h +0 -31
  237. /package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.h +0 -0
@@ -1,9 +1,12 @@
1
1
  package com.swmansion.rnscreens.stack.header
2
2
 
3
+ import android.annotation.SuppressLint
3
4
  import android.content.res.ColorStateList
5
+ import android.graphics.Color
6
+ import android.graphics.Typeface
4
7
  import android.graphics.drawable.Drawable
5
- import android.text.TextUtils
6
8
  import android.util.Log
9
+ import android.util.TypedValue
7
10
  import android.view.Gravity
8
11
  import android.view.View
9
12
  import android.view.ViewGroup
@@ -11,10 +14,9 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
11
14
  import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
12
15
  import android.widget.FrameLayout
13
16
  import androidx.appcompat.view.ContextThemeWrapper
14
- import androidx.appcompat.widget.AppCompatTextView
17
+ import androidx.appcompat.widget.AppCompatImageView
15
18
  import androidx.appcompat.widget.Toolbar
16
19
  import androidx.core.graphics.drawable.DrawableCompat
17
- import androidx.core.widget.TextViewCompat
18
20
  import com.google.android.material.R
19
21
  import com.google.android.material.appbar.AppBarLayout
20
22
  import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
@@ -24,11 +26,21 @@ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_
24
26
  import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
25
27
  import com.google.android.material.appbar.CollapsingToolbarLayout
26
28
  import com.google.android.material.appbar.MaterialToolbar
29
+ import com.google.android.material.color.MaterialColors
30
+ import com.google.android.material.shape.MaterialShapeDrawable
31
+ import com.swmansion.rnscreens.common.text.TextAppearance
32
+ import com.swmansion.rnscreens.common.text.TextAppearanceDefaults
27
33
  import com.swmansion.rnscreens.ext.detachFromCurrentParent
28
34
  import com.swmansion.rnscreens.stack.header.appbar.StackHeaderAppBarLayout
35
+ import com.swmansion.rnscreens.stack.header.appbar.StackHeaderContentScrimDrawable
29
36
  import com.swmansion.rnscreens.stack.header.config.StackHeaderConfigurationProviding
37
+ import com.swmansion.rnscreens.stack.header.config.StackHeaderType
30
38
  import com.swmansion.rnscreens.stack.header.subview.StackHeaderSubview
39
+ import com.swmansion.rnscreens.utils.dpToPx
40
+ import com.swmansion.rnscreens.utils.resolveColorAttr
31
41
  import com.swmansion.rnscreens.utils.resolveDrawableAttr
42
+ import com.swmansion.rnscreens.utils.spToPx
43
+ import kotlin.math.roundToInt
32
44
 
33
45
  /**
34
46
  * Builds and applies the Material app bar — type, subviews, title, back button,
@@ -43,7 +55,12 @@ internal class StackHeaderApplicator(
43
55
  coordinatorLayout: StackHeaderCoordinatorLayout,
44
56
  config: StackHeaderConfigurationProviding,
45
57
  ): StackHeaderAppBarLayout {
46
- val appBar = StackHeaderAppBarLayout.create(wrappedContext, config.type)
58
+ val appBar =
59
+ StackHeaderAppBarLayout.create(
60
+ wrappedContext,
61
+ config.type,
62
+ config.collapsedTitleGravityMode,
63
+ )
47
64
 
48
65
  if (config.transparent) {
49
66
  coordinatorLayout.removeContentBehavior()
@@ -84,30 +101,21 @@ internal class StackHeaderApplicator(
84
101
  toolbar.addView(it.view, Toolbar.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.END))
85
102
  }
86
103
 
87
- populateTitleOrCenter(appBar, toolbar, config)
104
+ populateCenterSubview(appBar, toolbar, config)
88
105
  populateBackground(appBar, config)
89
106
  }
90
107
 
91
- private fun populateTitleOrCenter(
108
+ private fun populateCenterSubview(
92
109
  appBar: StackHeaderAppBarLayout,
93
110
  toolbar: Toolbar,
94
111
  config: StackHeaderConfigurationProviding,
95
112
  ) {
96
- val centerSubview = config.centerSubview
97
- if (centerSubview != null) {
98
- if (appBar is StackHeaderAppBarLayout.Small) {
99
- centerSubview.view.detachFromCurrentParent()
100
- toolbar.addView(centerSubview.view, Toolbar.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER_HORIZONTAL))
101
- } else {
102
- Log.e(TAG, "[RNScreens] Center subview is supported only for small header type.")
103
- }
104
- } else if (appBar is StackHeaderAppBarLayout.Small) {
105
- // Small header needs a managed title view because we can't use Toolbar's native
106
- // title — it would be laid out to the leading side of leading subview.
107
- val titleView = createManagedTitleView(toolbar)
108
- appBar.managedTitleView = titleView
109
- val index = if (config.isRTL) 0 else -1
110
- toolbar.addView(titleView, index, Toolbar.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.START))
113
+ val centerSubview = config.centerSubview ?: return
114
+ if (appBar is StackHeaderAppBarLayout.Small) {
115
+ centerSubview.view.detachFromCurrentParent()
116
+ toolbar.addView(centerSubview.view, Toolbar.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER_HORIZONTAL))
117
+ } else {
118
+ Log.e(TAG, "[RNScreens] Center subview is supported only for small header type.")
111
119
  }
112
120
  }
113
121
 
@@ -144,51 +152,205 @@ internal class StackHeaderApplicator(
144
152
  )
145
153
  }
146
154
 
147
- private fun createManagedTitleView(toolbar: Toolbar): AppCompatTextView =
148
- AppCompatTextView(toolbar.context).apply {
149
- setSingleLine()
150
- ellipsize = TextUtils.TruncateAt.END
151
- TextViewCompat.setTextAppearance(
152
- this,
153
- R.style.TextAppearance_Material3_TitleLarge,
154
- )
155
- layoutParams =
156
- Toolbar
157
- .LayoutParams(
158
- WRAP_CONTENT,
159
- WRAP_CONTENT,
160
- Gravity.START,
161
- ).apply {
162
- // TODO: there seems to be a problem with collapsing margins.
163
- // We will expose customization either way but we should
164
- // have consistent behavior and defaults.
165
- marginStart = toolbar.titleMarginStart + toolbar.contentInsetStart
166
- marginEnd = toolbar.titleMarginEnd
167
- topMargin = toolbar.titleMarginTop
168
- bottomMargin = toolbar.titleMarginBottom
169
- }
170
- }
171
-
172
155
  // endregion
173
156
 
174
157
  // region In-place updates
175
158
 
176
- internal fun applyTitle(
159
+ // ctl.setMaxLines() is listed in Material docs in the same way as other props but for some
160
+ // reason it's restricted.
161
+ @SuppressLint("RestrictedApi")
162
+ internal fun applyTitleAndSubtitle(
163
+ appBar: StackHeaderAppBarLayout,
164
+ config: StackHeaderConfigurationProviding,
165
+ isAppBarFullyCollapsed: Boolean,
166
+ ) {
167
+ when (appBar) {
168
+ is StackHeaderAppBarLayout.Small -> {
169
+ appBar.toolbar.title = config.title
170
+ appBar.toolbar.subtitle = config.subtitle
171
+ }
172
+
173
+ is StackHeaderAppBarLayout.Collapsing -> {
174
+ val ctl = appBar.collapsingToolbarLayout
175
+
176
+ ctl.maxLines = config.maxLines
177
+
178
+ ctl.title = config.title
179
+ ctl.subtitle = config.subtitle
180
+
181
+ // setText only recomputes draw offsets within the existing bounds; the
182
+ // title/subtitle vertical split is recomputed only in onLayout. Force a layout
183
+ // so toggling the subtitle at runtime reclaims the title space.
184
+ ctl.requestLayout()
185
+
186
+ // Material re-asserts the collapsed offset after a height change itself, but only
187
+ // when maxLines > 1 (CollapsingToolbarLayout.onMeasure). With maxLines == 1 it never
188
+ // does, so toggling the subtitle at runtime while collapsed leaves a stale offset —
189
+ // setExpanded(false, false) is the closest public equivalent of Material's private
190
+ // maybeSetPendingActionCollapsed().
191
+ if (config.maxLines == 1 && isAppBarFullyCollapsed) {
192
+ appBar.setExpanded(false, false)
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ internal fun applyTitlePositioning(
177
199
  appBar: StackHeaderAppBarLayout,
178
200
  config: StackHeaderConfigurationProviding,
179
201
  ) {
180
202
  when (appBar) {
181
203
  is StackHeaderAppBarLayout.Small -> {
182
- appBar.managedTitleView?.text = config.title
183
- appBar.managedTitleView?.requestLayout()
204
+ appBar.toolbar.isTitleCentered = config.titleCentered
205
+ appBar.toolbar.isSubtitleCentered = config.subtitleCentered
184
206
  }
185
207
 
186
208
  is StackHeaderAppBarLayout.Collapsing -> {
187
- appBar.collapsingToolbarLayout.title = config.title
209
+ appBar.collapsingToolbarLayout.expandedTitleGravity =
210
+ config.expandedTitleHorizontalGravity or config.expandedTitleVerticalGravity
211
+ appBar.collapsingToolbarLayout.collapsedTitleGravity =
212
+ config.collapsedTitleHorizontalGravity or config.collapsedTitleVerticalGravity
188
213
  }
189
214
  }
190
215
  }
191
216
 
217
+ internal fun applyTitleAndSubtitleAppearance(
218
+ appBar: StackHeaderAppBarLayout,
219
+ config: StackHeaderConfigurationProviding,
220
+ ) {
221
+ // Defaults are resolved from wrappedContext, not the widget's own context: MaterialToolbar
222
+ // wraps its context (materialThemeOverlay) in a ContextThemeWrapper whose theme is a
223
+ // one-time copy, so it never sees later setTheme calls on wrappedContext (color scheme
224
+ // changes).
225
+ when (appBar) {
226
+ is StackHeaderAppBarLayout.Small -> {
227
+ val toolbar = appBar.toolbar
228
+
229
+ // Widget.Material3Expressive.Toolbar#{title,subtitle}TextAppearance
230
+ // = @macro/m3_comp_app_bar_small_{title,subtitle}_font. That style sets no text
231
+ // colors, so both come from @macro/m3_comp_app_bar_{title,subtitle}_color.
232
+ applySlot(
233
+ view = toolbar,
234
+ defaults =
235
+ TextAppearanceDefaults.resolve(
236
+ wrappedContext,
237
+ R.attr.textAppearanceTitleLarge,
238
+ R.attr.colorOnSurface,
239
+ ),
240
+ appearance = config.titleAppearance,
241
+ setColor = { toolbar.setTitleTextColor(it) },
242
+ setTypeface = { appBar.titleTextView.typeface = it },
243
+ setTextSizePx = { appBar.titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, it) },
244
+ )
245
+ applySlot(
246
+ view = toolbar,
247
+ defaults =
248
+ TextAppearanceDefaults.resolve(
249
+ wrappedContext,
250
+ R.attr.textAppearanceLabelMedium,
251
+ R.attr.colorOnSurfaceVariant,
252
+ ),
253
+ appearance = config.subtitleAppearance,
254
+ setColor = { toolbar.setSubtitleTextColor(it) },
255
+ setTypeface = { appBar.subtitleTextView.typeface = it },
256
+ setTextSizePx = { appBar.subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, it) },
257
+ )
258
+ }
259
+
260
+ is StackHeaderAppBarLayout.Collapsing -> {
261
+ val ctl = appBar.collapsingToolbarLayout
262
+ val isLarge = appBar.type == StackHeaderType.LARGE
263
+
264
+ // Widget.Material3Expressive.CollapsingToolbar.{Large,Medium}#expandedTitleTextAppearance
265
+ // = @macro/m3_comp_app_bar_{large,medium}_flexible_title_font
266
+ val expandedTitleAttr =
267
+ if (isLarge) R.attr.textAppearanceDisplaySmall else R.attr.textAppearanceHeadlineMedium
268
+
269
+ // …#expandedSubtitleTextAppearance
270
+ // = @macro/m3_comp_app_bar_{large,medium}_flexible_subtitle_font
271
+ val expandedSubtitleAttr =
272
+ if (isLarge) R.attr.textAppearanceTitleMedium else R.attr.textAppearanceLabelLarge
273
+
274
+ // In the calls below, the collapsed appearances and all four colors come from the base
275
+ // Widget.Material3Expressive.CollapsingToolbar: collapsed{Title,Subtitle}TextAppearance
276
+ // = @macro/m3_comp_app_bar_small_{title,subtitle}_font,
277
+ // {collapsed,expanded}{Title,Subtitle}TextColor
278
+ // = @macro/m3_comp_app_bar_{title,subtitle}_color.
279
+
280
+ applySlot(
281
+ view = ctl,
282
+ defaults =
283
+ TextAppearanceDefaults.resolve(wrappedContext, expandedTitleAttr, R.attr.colorOnSurface),
284
+ appearance = config.expandedTitleAppearance,
285
+ setColor = { ctl.setExpandedTitleTextColor(ColorStateList.valueOf(it)) },
286
+ setTypeface = { ctl.setExpandedTitleTypeface(it) },
287
+ setTextSizePx = { ctl.expandedTitleTextSize = it },
288
+ )
289
+ applySlot(
290
+ view = ctl,
291
+ defaults =
292
+ TextAppearanceDefaults.resolve(wrappedContext, R.attr.textAppearanceTitleLarge, R.attr.colorOnSurface),
293
+ appearance = config.collapsedTitleAppearance,
294
+ setColor = { ctl.setCollapsedTitleTextColor(it) },
295
+ setTypeface = { ctl.setCollapsedTitleTypeface(it) },
296
+ setTextSizePx = { ctl.collapsedTitleTextSize = it },
297
+ )
298
+ applySlot(
299
+ view = ctl,
300
+ defaults =
301
+ TextAppearanceDefaults.resolve(wrappedContext, expandedSubtitleAttr, R.attr.colorOnSurfaceVariant),
302
+ appearance = config.expandedSubtitleAppearance,
303
+ setColor = { ctl.setExpandedSubtitleTextColor(ColorStateList.valueOf(it)) },
304
+ setTypeface = { ctl.setExpandedSubtitleTypeface(it) },
305
+ setTextSizePx = { ctl.expandedSubtitleTextSize = it },
306
+ )
307
+ applySlot(
308
+ view = ctl,
309
+ defaults =
310
+ TextAppearanceDefaults.resolve(wrappedContext, R.attr.textAppearanceLabelMedium, R.attr.colorOnSurfaceVariant),
311
+ appearance = config.collapsedSubtitleAppearance,
312
+ setColor = { ctl.setCollapsedSubtitleTextColor(it) },
313
+ setTypeface = { ctl.setCollapsedSubtitleTypeface(it) },
314
+ setTextSizePx = { ctl.collapsedSubtitleTextSize = it },
315
+ )
316
+
317
+ // Layout is necessary e.g. after changing subtitle text size.
318
+ ctl.requestLayout()
319
+ }
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Applies one title/subtitle slot as absolute values: each property is the prop value when
325
+ * set, the Material default otherwise.
326
+ */
327
+ private fun applySlot(
328
+ view: View,
329
+ defaults: TextAppearanceDefaults,
330
+ appearance: TextAppearance,
331
+ setColor: (Int) -> Unit,
332
+ setTypeface: (Typeface) -> Unit,
333
+ setTextSizePx: (Float) -> Unit,
334
+ ) {
335
+ setColor(appearance.color ?: defaults.color)
336
+ setTypeface(appearance.resolveTypeface(defaults.typeface))
337
+ setTextSizePx(appearance.fontSize?.let { view.spToPx(it) } ?: defaults.textSizePx)
338
+ }
339
+
340
+ internal fun applyContentInsets(
341
+ appBar: StackHeaderAppBarLayout,
342
+ config: StackHeaderConfigurationProviding,
343
+ ) {
344
+ val toolbar = appBar.toolbar
345
+
346
+ toolbar.setContentInsetsRelative(
347
+ config.contentInsetStart?.let { toolbar.dpToPx(it).roundToInt() } ?: appBar.defaultContentInsetStart,
348
+ config.contentInsetEnd?.let { toolbar.dpToPx(it).roundToInt() } ?: appBar.defaultContentInsetEnd,
349
+ )
350
+
351
+ toolbar.requestLayout()
352
+ }
353
+
192
354
  internal fun applyBackButton(
193
355
  toolbar: MaterialToolbar,
194
356
  config: StackHeaderConfigurationProviding,
@@ -208,9 +370,15 @@ internal class StackHeaderApplicator(
208
370
  val baseDrawable =
209
371
  config.backButtonIcon
210
372
  ?.let { getResizedDrawable(toolbar, it) }
211
- ?: resolveDefaultBackButtonIcon()
373
+ ?: resolveDefaultBackButtonIcon(toolbar)
374
+
375
+ val tintList =
376
+ buildTintList(
377
+ config.backButtonTintColorNormal,
378
+ config.backButtonTintColorPressed,
379
+ config.backButtonTintColorFocused,
380
+ )
212
381
 
213
- val tintList = resolveBackButtonTintList(config)
214
382
  toolbar.navigationIcon =
215
383
  if (tintList != null && baseDrawable != null) {
216
384
  DrawableCompat.wrap(baseDrawable.mutate()).also {
@@ -223,6 +391,32 @@ internal class StackHeaderApplicator(
223
391
  toolbar.setNavigationOnClickListener { onNavigationIconClick() }
224
392
  }
225
393
 
394
+ internal fun applyOverflowIcon(
395
+ toolbar: MaterialToolbar,
396
+ config: StackHeaderConfigurationProviding,
397
+ ) {
398
+ val baseDrawable =
399
+ config.overflowIcon
400
+ ?.let { getResizedDrawable(toolbar, it) }
401
+ ?: resolveDefaultOverflowIcon(toolbar)
402
+
403
+ val tintList =
404
+ buildTintList(
405
+ config.overflowIconTintColorNormal,
406
+ config.overflowIconTintColorPressed,
407
+ config.overflowIconTintColorFocused,
408
+ )
409
+
410
+ toolbar.overflowIcon =
411
+ if (tintList != null && baseDrawable != null) {
412
+ DrawableCompat.wrap(baseDrawable.mutate()).also {
413
+ DrawableCompat.setTintList(it, tintList)
414
+ }
415
+ } else {
416
+ baseDrawable
417
+ }
418
+ }
419
+
226
420
  internal fun applyScrollFlags(
227
421
  appBar: StackHeaderAppBarLayout,
228
422
  config: StackHeaderConfigurationProviding,
@@ -270,6 +464,109 @@ internal class StackHeaderApplicator(
270
464
  appBar.requestLayout()
271
465
  }
272
466
 
467
+ internal fun applyBackgroundColors(
468
+ appBar: StackHeaderAppBarLayout,
469
+ config: StackHeaderConfigurationProviding,
470
+ ) {
471
+ // Widget.Material3Expressive.AppBarLayout (an empty alias of Widget.Material3.AppBarLayout)
472
+ // #android:background = @macro/m3_comp_app_bar_container_color.
473
+ val backgroundColor =
474
+ config.backgroundColor ?: resolveColorAttr(appBar.context, R.attr.colorSurface)
475
+
476
+ // Both header kinds default to the same token, from different places:
477
+ // small — ...AppBarLayout#liftOnScrollColor
478
+ // = @macro/m3_comp_app_bar_on_scroll_container_color;
479
+ // collapsing — the M3 CollapsingToolbar styles set no contentScrim, so CTL installs its
480
+ // own default for titleCollapseMode=fade (getDefaultContentScrimColorForTitleCollapseFadeMode).
481
+ val scrolledBackgroundColor =
482
+ config.scrolledBackgroundColor
483
+ ?: resolveColorAttr(appBar.context, R.attr.colorSurfaceContainer)
484
+
485
+ val background =
486
+ MaterialShapeDrawable().apply {
487
+ fillColor = ColorStateList.valueOf(backgroundColor)
488
+ }
489
+
490
+ when (appBar) {
491
+ is StackHeaderAppBarLayout.Small -> {
492
+ appBar.background = background
493
+ appBar.setLiftOnScrollColor(ColorStateList.valueOf(scrolledBackgroundColor))
494
+
495
+ // The lift animation runs only on lifted-state changes; jump to the end
496
+ // state when colors change while already lifted. The end state is the
497
+ // scrolled color composited over the background (see Material's
498
+ // initializeLiftOnScrollWithColor), not the raw scrolled color — they
499
+ // differ when the scrolled color is not fully opaque.
500
+ if (appBar.isLifted) {
501
+ background.fillColor =
502
+ ColorStateList.valueOf(
503
+ MaterialColors.layer(backgroundColor, scrolledBackgroundColor),
504
+ )
505
+ }
506
+ }
507
+
508
+ is StackHeaderAppBarLayout.Collapsing -> {
509
+ // Fade collapse mode disables liftOnScroll, but the lift animation only checks the
510
+ // lift color. We set it to null so the fill stays at backgroundColor under the
511
+ // transparent/translucent content scrim.
512
+ appBar.setLiftOnScrollColor(null)
513
+ appBar.background = background
514
+ appBar.collapsingToolbarLayout.contentScrim =
515
+ StackHeaderContentScrimDrawable(scrolledBackgroundColor)
516
+ }
517
+ }
518
+
519
+ applyStatusBarScrim(appBar, config, backgroundColor, scrolledBackgroundColor)
520
+ }
521
+
522
+ private fun applyStatusBarScrim(
523
+ appBar: StackHeaderAppBarLayout,
524
+ config: StackHeaderConfigurationProviding,
525
+ backgroundColor: Int,
526
+ scrolledBackgroundColor: Int,
527
+ ) {
528
+ when (appBar) {
529
+ is StackHeaderAppBarLayout.Small -> {
530
+ val scrimColor = resolveStatusBarScrimColor(config, drivingColor = backgroundColor)
531
+ val autoFollow = scrimColor != null && config.statusBarScrimColor == null
532
+ appBar.setStatusBarScrimSyncEnabled(autoFollow)
533
+ appBar.statusBarForeground?.setTint(
534
+ when {
535
+ scrimColor == null -> Color.TRANSPARENT
536
+ // Same lifted jump as the background fill in applyBackgroundColors.
537
+ autoFollow && appBar.isLifted ->
538
+ MaterialColors.layer(backgroundColor, scrolledBackgroundColor)
539
+ else -> scrimColor
540
+ },
541
+ )
542
+ }
543
+
544
+ is StackHeaderAppBarLayout.Collapsing -> {
545
+ // Defaulting to the content scrim color masks toolbar content passing
546
+ // through the status-bar area (scroll-only flags) and is a visual
547
+ // no-op in the opaque exitUntilCollapsed case. An invisible scrim is
548
+ // normalized to null so the content scrim exclusion can key on scrim
549
+ // presence alone.
550
+ val scrimColor =
551
+ resolveStatusBarScrimColor(config, drivingColor = scrolledBackgroundColor)
552
+ ?.takeIf { Color.alpha(it) > 0 }
553
+ if (scrimColor != null) {
554
+ appBar.collapsingToolbarLayout.setStatusBarScrimColor(scrimColor)
555
+ } else {
556
+ appBar.collapsingToolbarLayout.statusBarScrim = null
557
+ }
558
+ appBar.updateContentScrimExclusion()
559
+ }
560
+ }
561
+ }
562
+
563
+ // The default scrim exists only for an opaque driving color — a translucent
564
+ // scrim would double-composite over the layer it covers.
565
+ private fun resolveStatusBarScrimColor(
566
+ config: StackHeaderConfigurationProviding,
567
+ drivingColor: Int,
568
+ ): Int? = config.statusBarScrimColor ?: drivingColor.takeIf { Color.alpha(it) == 0xFF }
569
+
273
570
  // endregion
274
571
 
275
572
  // region Helpers
@@ -298,7 +595,21 @@ internal class StackHeaderApplicator(
298
595
  }
299
596
  }
300
597
 
301
- private fun resolveDefaultBackButtonIcon(): Drawable? = resolveDrawableAttr(wrappedContext, androidx.appcompat.R.attr.homeAsUpIndicator)
598
+ // Default icon drawables must load through the toolbar's snapshot context, not
599
+ // wrappedContext: on Android 14+ the framework's themed resource cache returns stale
600
+ // entries for a Theme mutated in place by setTheme (ThemeKey dedup + shallow clone),
601
+ // freezing the icon tint after a color scheme change. The toolbar wrapper's theme is
602
+ // created fresh on every rebuild, so its cache entries always match the current palette.
603
+ // Plain attribute resolution (e.g. text appearance defaults) doesn't hit that cache and
604
+ // stays on the live wrappedContext.
605
+ private fun resolveDefaultBackButtonIcon(toolbar: MaterialToolbar): Drawable? =
606
+ resolveDrawableAttr(toolbar.context, androidx.appcompat.R.attr.homeAsUpIndicator)
607
+
608
+ // Mirrors how the toolbar's own overflow button obtains its icon: an AppCompatImageView built
609
+ // with actionOverflowButtonStyle resolves the theme's srcCompat and applies AppCompat's
610
+ // colorControlNormal auto-tint.
611
+ private fun resolveDefaultOverflowIcon(toolbar: MaterialToolbar): Drawable? =
612
+ AppCompatImageView(toolbar.context, null, androidx.appcompat.R.attr.actionOverflowButtonStyle).drawable
302
613
 
303
614
  private fun maybeApplyRTLCollapsingToolbarLayoutWorkaround(
304
615
  coordinatorLayout: StackHeaderCoordinatorLayout,
@@ -340,11 +651,11 @@ internal class StackHeaderApplicator(
340
651
  }
341
652
  }
342
653
 
343
- private fun resolveBackButtonTintList(config: StackHeaderConfigurationProviding): ColorStateList? {
344
- val normal = config.backButtonTintColorNormal
345
- val pressed = config.backButtonTintColorPressed
346
- val focused = config.backButtonTintColorFocused
347
-
654
+ private fun buildTintList(
655
+ normal: Int?,
656
+ pressed: Int?,
657
+ focused: Int?,
658
+ ): ColorStateList? {
348
659
  if (normal == null && pressed == null && focused == null) return null
349
660
 
350
661
  val states = mutableListOf<IntArray>()
@@ -0,0 +1,11 @@
1
+ package com.swmansion.rnscreens.stack.header
2
+
3
+ import com.swmansion.rnscreens.stack.screen.StackScreen
4
+
5
+ /**
6
+ * Receives presses of the native header back button. Implemented by the object
7
+ * managing the navigation state of the stack the pressed header belongs to.
8
+ */
9
+ internal fun interface StackHeaderBackPressHandler {
10
+ fun handleHeaderBackButtonPress(pressedScreen: StackScreen)
11
+ }