@apexnative/navigation 1.0.0-alpha.5 → 1.0.0-alpha.6

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 (182) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +1 -1
  3. package/android/CLAUDE.md +14 -12
  4. package/android/CORE.md +9 -6
  5. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +36 -5
  6. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
  7. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +3 -3
  8. package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +0 -2
  9. package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
  10. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +12 -49
  11. package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
  12. package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -6
  13. package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
  14. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +9 -7
  15. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
  16. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +13 -10
  17. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
  18. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
  19. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +7 -8
  20. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
  21. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
  22. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
  23. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +119 -8
  24. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +7 -0
  25. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
  26. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -0
  27. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
  28. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
  29. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
  30. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +4 -0
  31. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +3 -0
  32. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
  33. package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
  34. package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
  35. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
  36. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
  37. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +9 -2
  38. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +11 -0
  39. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +318 -25
  40. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +18 -20
  41. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +17 -25
  42. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +15 -22
  43. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +12 -9
  44. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +84 -35
  45. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +49 -40
  46. package/docs-md/architecture/transitions.md +4 -2
  47. package/docs-md/containers/modals.md +2 -0
  48. package/docs-md/containers/overlays.md +1 -1
  49. package/docs-md/containers/side-menu.md +1 -1
  50. package/docs-md/containers/tabs.md +2 -0
  51. package/docs-md/flows/bottom-sheets.md +1 -1
  52. package/docs-md/fundamentals/starting-the-app.md +1 -1
  53. package/docs-md/help/glossary.md +2 -2
  54. package/docs-md/help/troubleshooting.md +21 -0
  55. package/docs-md/index.json +12 -0
  56. package/docs-md/migration/from-expo-router.md +1 -1
  57. package/docs-md/migration/from-react-native-navigation.md +1 -1
  58. package/docs-md/migration/from-react-navigation.md +2 -2
  59. package/docs-md/screen-ui/back-and-gestures.md +14 -0
  60. package/docs-md/screen-ui/bar-buttons.md +4 -4
  61. package/docs-md/screen-ui/search.md +1 -1
  62. package/docs-md/screen-ui/system-ui.md +16 -11
  63. package/docs-md/screen-ui/toolbar-and-fab.md +2 -2
  64. package/docs-md/screen-ui/top-bar.md +8 -4
  65. package/ios/Animations/ANNFrameDriver.mm +2 -0
  66. package/ios/Animations/Elements/ANNSharedElementFlight.mm +70 -4
  67. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  68. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  69. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  70. package/ios/Animations/Interpolators/ANNViewTracks.h +6 -4
  71. package/ios/Animations/Interpolators/ANNViewTracks.mm +49 -9
  72. package/ios/CLAUDE.md +17 -15
  73. package/ios/CORE.md +24 -20
  74. package/ios/Contract/ANNCommandRunner.h +8 -7
  75. package/ios/Contract/ANNCommandRunner.mm +21 -27
  76. package/ios/Contract/ANNLayoutNode.h +3 -3
  77. package/ios/Contract/ANNLayoutReader.h +3 -3
  78. package/ios/Core/ANNJavaScriptCalls.h +11 -12
  79. package/ios/Core/ANNJavaScriptCalls.mm +7 -16
  80. package/ios/Core/ANNReloadCoordinator.mm +4 -8
  81. package/ios/Core/ApexNavigation+Internal.h +1 -1
  82. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  83. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  84. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
  85. package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
  86. package/ios/Layouts/Screen/ANNComponentViewController.mm +9 -22
  87. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  88. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  89. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  90. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  91. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  92. package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
  93. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  94. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  95. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  96. package/ios/Layouts/Stack/ANNPreviewPush.mm +57 -5
  97. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  98. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  99. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
  100. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  101. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
  102. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
  103. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  104. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  105. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  106. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  107. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  108. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  109. package/ios/Options/ANNOptions.h +18 -0
  110. package/ios/Options/ANNOptions.mm +112 -1
  111. package/ios/Options/ANNOptionsReader.mm +110 -0
  112. package/ios/Options/Params/ANNParam.h +15 -0
  113. package/ios/Options/Params/ANNParam.mm +61 -0
  114. package/ios/Options/Params/Color.h +3 -0
  115. package/ios/Options/Params/Color.mm +21 -0
  116. package/ios/Options/Params/Image.h +4 -0
  117. package/ios/Options/Params/Image.mm +20 -0
  118. package/ios/React/ANNReactView.mm +6 -25
  119. package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
  120. package/ios/React/ANNSurfaceHostingView.h +6 -0
  121. package/ios/React/ANNSurfaceHostingView.mm +116 -0
  122. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  123. package/ios/TopBar/ANNBarAppearance.h +2 -1
  124. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  125. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  126. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  127. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  128. package/ios/TopBar/ANNBarButtons.h +5 -3
  129. package/ios/TopBar/ANNBarButtons.mm +186 -17
  130. package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
  131. package/ios/TopBar/ANNMainList.h +3 -0
  132. package/ios/TopBar/ANNMainList.mm +4 -0
  133. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  134. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  135. package/ios/TopBar/ANNSearchPresenter.mm +2 -2
  136. package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
  137. package/ios/TopBar/ANNTopBarPresenter.h +3 -2
  138. package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
  139. package/ios/TurboModules/ANNTurboModule.mm +20 -16
  140. package/lib/module/ARCHITECTURE.md +8 -4
  141. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  142. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  143. package/lib/module/api/options/OptionsChain.js +2 -2
  144. package/lib/module/engine/commands.js +21 -1
  145. package/lib/module/engine/commands.js.map +1 -1
  146. package/lib/module/engine/screens.js +29 -4
  147. package/lib/module/engine/screens.js.map +1 -1
  148. package/lib/typescript/api/app/createNavigation.d.ts +3 -3
  149. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  150. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  151. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  152. package/lib/typescript/engine/commands.d.ts.map +1 -1
  153. package/lib/typescript/engine/screens.d.ts +20 -6
  154. package/lib/typescript/engine/screens.d.ts.map +1 -1
  155. package/lib/typescript/interfaces/options/animations.d.ts +12 -7
  156. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  157. package/lib/typescript/interfaces/options/bottomTabs.d.ts +18 -5
  158. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  159. package/lib/typescript/interfaces/options/buttons.d.ts +8 -8
  160. package/lib/typescript/interfaces/options/system.d.ts +8 -5
  161. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  162. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  163. package/lib/typescript/interfaces/options/topBar.d.ts +32 -29
  164. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  165. package/package.json +1 -1
  166. package/src/ARCHITECTURE.md +8 -4
  167. package/src/api/app/createNavigation.ts +3 -3
  168. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  169. package/src/api/options/OptionsChain.ts +2 -2
  170. package/src/engine/commands.ts +30 -6
  171. package/src/engine/screens.tsx +29 -8
  172. package/src/interfaces/options/animations.ts +12 -7
  173. package/src/interfaces/options/bottomTabs.ts +18 -5
  174. package/src/interfaces/options/buttons.ts +8 -8
  175. package/src/interfaces/options/system.ts +8 -5
  176. package/src/interfaces/options/toolbar.ts +2 -2
  177. package/src/interfaces/options/topBar.ts +32 -29
  178. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
  179. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
  180. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  181. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  182. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
@@ -1,70 +0,0 @@
1
- package dev.apexnative.navigation.react
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import dev.apexnative.navigation.react.events.EventContract
6
- import dev.apexnative.navigation.react.events.EventEmitter
7
- import java.util.concurrent.ConcurrentHashMap
8
-
9
- /**
10
- * The safe area of each screen, in dp: how much of its content the bars and the system cover on each side. The padding
11
- * is the part of it the screen's safe area view keeps its content out of: the insets on the edges the screen's options
12
- * give. JavaScript gets the rest, what still covers the content (0 on a padded edge): it reads it from its own thread
13
- * for a screen's first render, and gets every change as `ANN.ScreenInsetsChanged`.
14
- */
15
- object ScreenInsets {
16
- data class Insets(val top: Double, val right: Double, val bottom: Double, val left: Double)
17
-
18
- private val insets = ConcurrentHashMap<String, Insets>()
19
- // Main thread only.
20
- private val padding = HashMap<String, Insets>()
21
- private val paddingListeners = HashMap<String, MutableSet<() -> Unit>>()
22
-
23
- /** The padding of the screen's safe area view; none before the screen was laid out. */
24
- fun padding(screenId: String): Insets? = padding[screenId]
25
-
26
- /** Calls `listener` when the padding of the screen changes; returns the function that stops it. */
27
- fun listenToPadding(screenId: String, listener: () -> Unit): () -> Unit {
28
- paddingListeners.getOrPut(screenId) { LinkedHashSet() }.add(listener)
29
- return {
30
- paddingListeners[screenId]?.let {
31
- it.remove(listener)
32
- if (it.isEmpty()) paddingListeners.remove(screenId)
33
- }
34
- }
35
- }
36
-
37
- /**
38
- * Keeps the screen's insets and padding, and tells when they changed through [emitter], the one of the JavaScript
39
- * the screen was made for: silent once it went.
40
- */
41
- fun update(screenId: String, value: Insets, paddingValue: Insets, emitter: EventEmitter) {
42
- if (padding.put(screenId, paddingValue) != paddingValue) {
43
- paddingListeners[screenId]?.toList()?.forEach { it() }
44
- }
45
- // JavaScript gets what still covers the content: the edges the safe area view keeps it off are 0.
46
- val uncovered = Insets(
47
- top = (value.top - paddingValue.top).coerceAtLeast(0.0),
48
- right = (value.right - paddingValue.right).coerceAtLeast(0.0),
49
- bottom = (value.bottom - paddingValue.bottom).coerceAtLeast(0.0),
50
- left = (value.left - paddingValue.left).coerceAtLeast(0.0),
51
- )
52
- if (insets.put(screenId, uncovered) == uncovered) return
53
- emitter.emitScreenInsetsChanged(screenId, toMap(uncovered))
54
- }
55
-
56
- /** `{ top, right, bottom, left }`, empty before the screen was laid out. Any thread. */
57
- fun get(screenId: String): WritableMap = insets[screenId]?.let(::toMap) ?: Arguments.createMap()
58
-
59
- fun remove(screenId: String) {
60
- insets.remove(screenId)
61
- padding.remove(screenId)
62
- }
63
-
64
- private fun toMap(value: Insets): WritableMap = Arguments.createMap().apply {
65
- putDouble(EventContract.ScreenInsetsChanged.TOP, value.top)
66
- putDouble(EventContract.ScreenInsetsChanged.RIGHT, value.right)
67
- putDouble(EventContract.ScreenInsetsChanged.BOTTOM, value.bottom)
68
- putDouble(EventContract.ScreenInsetsChanged.LEFT, value.left)
69
- }
70
- }
@@ -1,223 +0,0 @@
1
- package dev.apexnative.navigation.viewcontrollers.bottomtabs
2
-
3
- import android.app.Activity
4
- import android.app.Application
5
- import android.os.Bundle
6
- import android.view.View
7
- import android.view.ViewGroup
8
- import android.view.ViewTreeObserver
9
- import androidx.core.view.ViewCompat
10
- import androidx.core.view.WindowInsetsCompat
11
- import dev.apexnative.navigation.NavigationActivity
12
- import dev.apexnative.navigation.utils.UiUtils
13
- import dev.apexnative.navigation.viewcontrollers.modal.ModalContainer
14
- import dev.apexnative.navigation.views.bottomtabs.BottomTabsLayout
15
- import dev.apexnative.navigation.views.bottomtabs.CustomTabRow
16
- import java.util.WeakHashMap
17
-
18
- /**
19
- * Puts the custom rows of the tabs in their place, as it works now: an observer of every activity of the process that
20
- * touches only the navigation's activities; on each layout pass of such a window it finds the tabs with a row through
21
- * the view tree, and puts each row in the window's content right above the layer holding its tabs (after the rows
22
- * already there) — tabs in a modal: in the modal's own view, right above its root's view (or its sheet), so the row
23
- * moves with the modal and newer modals cover it —, over the bar as [CustomTabRowGeometry] says — applied only when it
24
- * changed, so UI tests reach idle. A row whose tabs left the window leaves it too, and comes back to its place as they
25
- * do. Before each frame the row takes its tabs' alpha (a root that waits under the old one is transparent) and the
26
- * motion of the bar's container, and stays out of accessibility and touches while they wait. Main thread only.
27
- */
28
- internal object CustomTabRowPlacer : Application.ActivityLifecycleCallbacks {
29
- private var registered = false
30
- // The listeners of each observed activity's window.
31
- private val observed = WeakHashMap<Activity, Listeners>()
32
- // The rows placed at the last layout pass of each activity, with their tabs.
33
- private val rows = WeakHashMap<CustomTabRow, BottomTabsLayout>()
34
-
35
- private class Listeners(
36
- val observer: ViewTreeObserver,
37
- val layout: ViewTreeObserver.OnGlobalLayoutListener,
38
- val draw: ViewTreeObserver.OnPreDrawListener,
39
- )
40
-
41
- /** [activity] has tabs with a row: its window is observed from now on, and the process's later activities. */
42
- fun track(activity: Activity) {
43
- if (!registered) {
44
- registered = true
45
- activity.application?.registerActivityLifecycleCallbacks(this)
46
- }
47
- observe(activity)
48
- }
49
-
50
- /** The tabs of [row] go: the row leaves the window. */
51
- fun forget(row: CustomTabRow) {
52
- rows.remove(row)
53
- (row.parent as? ViewGroup)?.removeView(row)
54
- }
55
-
56
- override fun onActivityResumed(activity: Activity) = observe(activity)
57
-
58
- override fun onActivityDestroyed(activity: Activity) {
59
- val listeners = observed.remove(activity) ?: return
60
- if (!listeners.observer.isAlive) return
61
- listeners.observer.removeOnGlobalLayoutListener(listeners.layout)
62
- listeners.observer.removeOnPreDrawListener(listeners.draw)
63
- }
64
-
65
- override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
66
-
67
- override fun onActivityStarted(activity: Activity) = Unit
68
-
69
- override fun onActivityPaused(activity: Activity) = Unit
70
-
71
- override fun onActivityStopped(activity: Activity) = Unit
72
-
73
- override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
74
-
75
- // Only the navigation's activities: another activity's views are never looked for.
76
- private fun observe(activity: Activity) {
77
- if (activity !is NavigationActivity || observed.containsKey(activity)) return
78
- val decor = activity.window?.decorView ?: return
79
- val observer = decor.viewTreeObserver
80
- val layout = ViewTreeObserver.OnGlobalLayoutListener { place(activity) }
81
- val draw = ViewTreeObserver.OnPreDrawListener {
82
- follow(activity)
83
- true
84
- }
85
- observer.addOnGlobalLayoutListener(layout)
86
- observer.addOnPreDrawListener(draw)
87
- observed[activity] = Listeners(observer, layout, draw)
88
- }
89
-
90
- private fun contentOf(activity: Activity): ViewGroup? =
91
- activity.window?.decorView?.findViewById(android.R.id.content)
92
-
93
- // Each layout pass: the tabs with a row in the content, and the rows in their places.
94
- private fun place(activity: NavigationActivity) {
95
- val content = contentOf(activity) ?: return
96
- val found = ArrayList<BottomTabsLayout>()
97
- collect(content, found)
98
- val placed = found.mapNotNull { tabs -> tabs.row?.also { place(activity, content, tabs, it) } }.toSet()
99
- // A row whose tabs left the window leaves it too.
100
- rows.entries.filter { (row, tabs) -> row !in placed && tabs.context === activity }.forEach { (row, _) ->
101
- (row.parent as? ViewGroup)?.removeView(row)
102
- }
103
- }
104
-
105
- private fun collect(view: View, found: MutableList<BottomTabsLayout>) {
106
- if (view is CustomTabRow) return
107
- if (view is BottomTabsLayout && view.row != null) found += view
108
- if (view is ViewGroup) for (index in 0 until view.childCount) collect(view.getChildAt(index), found)
109
- }
110
-
111
- private fun place(activity: NavigationActivity, content: ViewGroup, tabs: BottomTabsLayout, row: CustomTabRow) {
112
- rows[row] = tabs
113
- val (host, layer) = placeIn(content, tabs) ?: return
114
- if (row.parent !== host) {
115
- (row.parent as? ViewGroup)?.removeView(row)
116
- // Margins of the host's own kind: the host converts them (a frame or a coordinator layout).
117
- host.addView(row, indexAbove(host, layer, row), ViewGroup.MarginLayoutParams(0, 0))
118
- row.place = null
119
- } else if (host.indexOfChild(row) != indexAbove(host, layer, row)) {
120
- val params = row.layoutParams
121
- host.removeView(row)
122
- host.addView(row, indexAbove(host, layer, row), params)
123
- }
124
- row.showWith(tabs.bar)
125
- if (tabs.bar.visibility == View.GONE || tabs.bar.width == 0) return
126
- val place = placeOf(activity, host, tabs, row)
127
- if (!CustomTabRowGeometry.needsPlacing(row.place, place)) return
128
- row.place = place
129
- val params = row.layoutParams as? ViewGroup.MarginLayoutParams ?: return
130
- params.width = place.right - place.left
131
- params.height = place.bottom - place.top
132
- params.leftMargin = place.left
133
- params.topMargin = place.top
134
- row.layoutParams = params
135
- }
136
-
137
- // Over the bar: its edges less the margins, its bottom, the height of the content and the margin below it, in the
138
- // coordinates of the row's [host].
139
- private fun placeOf(
140
- activity: Activity,
141
- host: ViewGroup,
142
- tabs: BottomTabsLayout,
143
- row: CustomTabRow,
144
- ): CustomTabRowGeometry.Place {
145
- val bar = IntArray(2).also(tabs.bar::getLocationInWindow)
146
- val origin = IntArray(2).also(host::getLocationInWindow)
147
- val options = row.options
148
- val margin = UiUtils.dpToPixels(
149
- activity,
150
- options.horizontalMargin.get(CustomTabRowGeometry.HORIZONTAL_MARGIN_DP.toDouble()),
151
- )
152
- val height = options.height.get(null)?.let { UiUtils.dpToPixels(activity, it) }
153
- val extra = UiUtils.dpToPixels(activity, CustomTabRowGeometry.EXTRA_HEIGHT_DP.toDouble())
154
- // Where the bar rests: its container's motion (a ride, a slide, the hide on scroll) the row takes before each
155
- // frame as its own translation.
156
- val left = bar[0] - origin[0] - tabs.container.translationX.toInt()
157
- val top = bar[1] - origin[1] - tabs.container.translationY.toInt()
158
- // Never over the navigation bar's buttons: the bar is above them, and so is the row.
159
- val navigationBar = if ((activity as? NavigationActivity)?.isEdgeToEdge == true) navigationBarOf(host) else 0
160
- return CustomTabRowGeometry.place(
161
- barLeft = left,
162
- barRight = left + tabs.bar.width,
163
- barBottom = minOf(top + tabs.bar.height, host.height - navigationBar),
164
- margin = margin,
165
- content = CustomTabRowGeometry.content(tabs.bar.height, height, extra),
166
- bottomMargin = UiUtils.dpToPixels(activity, options.bottomMargin.get(0.0)),
167
- )
168
- }
169
-
170
- // The navigation bar at the bottom of the window, through the compat insets on every version.
171
- private fun navigationBarOf(view: View): Int =
172
- ViewCompat.getRootWindowInsets(view)?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
173
-
174
- // Where the row of [tabs] goes, and the child there it goes right above: tabs in a modal, the modal's own view and
175
- // the view in it that holds them (its root's, or its sheet's container); else the content and the layer of the
176
- // root or of the overlays. A sheet's drag moves it without a layout: the row doesn't follow a drag.
177
- private fun placeIn(content: ViewGroup, tabs: View): Pair<ViewGroup, View>? {
178
- var current: View = tabs
179
- while (true) {
180
- val parent = current.parent as? ViewGroup ?: return null
181
- if (parent is ModalContainer || parent === content) return parent to current
182
- current = parent
183
- }
184
- }
185
-
186
- // Right above [layer], after the rows already there.
187
- private fun indexAbove(host: ViewGroup, layer: View, row: CustomTabRow): Int {
188
- var index = host.indexOfChild(layer) + 1
189
- while (index < host.childCount) {
190
- val child = host.getChildAt(index)
191
- if (child !is CustomTabRow || child === row) break
192
- index++
193
- }
194
- return index
195
- }
196
-
197
- // Before each frame: the row takes the alpha of its tabs up to the view it is in and the container's translation,
198
- // and leaves accessibility while they wait to show.
199
- private fun follow(activity: Activity) {
200
- val content = contentOf(activity) ?: return
201
- for ((row, tabs) in rows) {
202
- val host = row.parent as? View ?: continue
203
- if (host !== content && host !is ModalContainer) continue
204
- var alpha = tabs.container.alpha
205
- var current: View? = tabs
206
- while (current != null && current !== host) {
207
- alpha *= current.alpha
208
- current = current.parent as? View
209
- }
210
- if (row.alpha != alpha) row.alpha = alpha
211
- // The row goes with the bar's container: a ride, a slide, the hide on scroll.
212
- val container = tabs.container
213
- if (row.translationX != container.translationX) row.translationX = container.translationX
214
- if (row.translationY != container.translationY) row.translationY = container.translationY
215
- val mode = if (row.hiddenFromAccessibility()) {
216
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
217
- } else {
218
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
219
- }
220
- if (row.importantForAccessibility != mode) row.importantForAccessibility = mode
221
- }
222
- }
223
- }
@@ -1,30 +0,0 @@
1
- #import "ANNEventEmitter.h"
2
- #import <UIKit/UIKit.h>
3
-
4
- NS_ASSUME_NONNULL_BEGIN
5
-
6
- /// Posted on the main queue when the padding of a screen's safe area view changed; the object is the screen id.
7
- extern NSNotificationName const ANNScreenSafeAreaPaddingDidChangeNotification;
8
-
9
- /// The safe area of each screen: how much of its content the bars and the system cover on each side. The padding is
10
- /// the part of it the screen's safe area view keeps its content out of: the insets on the edges the screen's options
11
- /// give. JavaScript gets the rest, what still covers the content (0 on a padded edge): it reads it from its own thread
12
- /// for a screen's first render, and gets every change as `ANN.ScreenInsetsChanged`. Main queue, but the reads.
13
- @interface ANNScreenInsets : NSObject
14
-
15
- /// Keeps the screen's insets and padding, and tells when they changed.
16
- + (void)setInsets:(UIEdgeInsets)insets
17
- padding:(UIEdgeInsets)padding
18
- forScreenId:(NSString *)screenId
19
- eventEmitter:(ANNEventEmitter *)eventEmitter;
20
- /// The padding of the screen's safe area view; none before the screen was laid out. Main queue.
21
- + (UIEdgeInsets)paddingForScreenId:(NSString *)screenId;
22
- /// `{ top, right, bottom, left }`, or nil before the screen was laid out. Any thread.
23
- + (nullable NSDictionary<NSString *, NSNumber *> *)insetsForScreenId:(NSString *)screenId;
24
- + (void)removeScreenId:(NSString *)screenId;
25
- /// Forgets every screen: after a reload the new JavaScript counts its ids anew. Main queue.
26
- + (void)removeAll;
27
-
28
- @end
29
-
30
- NS_ASSUME_NONNULL_END
@@ -1,70 +0,0 @@
1
- #import "ANNScreenInsets.h"
2
- #import "ANNEventContract.h"
3
-
4
- NSNotificationName const ANNScreenSafeAreaPaddingDidChangeNotification = @"ANNScreenSafeAreaPaddingDidChange";
5
-
6
- static NSMutableDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *ANNScreenInsetsById;
7
- // Main queue only.
8
- static NSMutableDictionary<NSString *, NSValue *> *ANNScreenPaddingById;
9
-
10
- @implementation ANNScreenInsets
11
-
12
- + (void)initialize {
13
- if (self == ANNScreenInsets.class) {
14
- ANNScreenInsetsById = [NSMutableDictionary new];
15
- ANNScreenPaddingById = [NSMutableDictionary new];
16
- }
17
- }
18
-
19
- + (void)setInsets:(UIEdgeInsets)insets
20
- padding:(UIEdgeInsets)padding
21
- forScreenId:(NSString *)screenId
22
- eventEmitter:(ANNEventEmitter *)eventEmitter {
23
- NSValue *paddingValue = [NSValue valueWithUIEdgeInsets:padding];
24
- if (![ANNScreenPaddingById[screenId] isEqualToValue:paddingValue]) {
25
- ANNScreenPaddingById[screenId] = paddingValue;
26
- [NSNotificationCenter.defaultCenter postNotificationName:ANNScreenSafeAreaPaddingDidChangeNotification
27
- object:screenId];
28
- }
29
- // JavaScript gets what still covers the content: the edges the safe area view keeps it off are 0.
30
- const auto &keys = ANNEventContract.ScreenInsetsChanged;
31
- NSDictionary<NSString *, NSNumber *> *value = @{
32
- keys.top : @(MAX(insets.top - padding.top, 0)),
33
- keys.right : @(MAX(insets.right - padding.right, 0)),
34
- keys.bottom : @(MAX(insets.bottom - padding.bottom, 0)),
35
- keys.left : @(MAX(insets.left - padding.left, 0)),
36
- };
37
- @synchronized(ANNScreenInsetsById) {
38
- if ([ANNScreenInsetsById[screenId] isEqualToDictionary:value]) {
39
- return;
40
- }
41
- ANNScreenInsetsById[screenId] = value;
42
- }
43
- [eventEmitter sendScreenInsetsChanged:screenId insets:value];
44
- }
45
-
46
- + (NSDictionary<NSString *, NSNumber *> *)insetsForScreenId:(NSString *)screenId {
47
- @synchronized(ANNScreenInsetsById) {
48
- return ANNScreenInsetsById[screenId];
49
- }
50
- }
51
-
52
- + (UIEdgeInsets)paddingForScreenId:(NSString *)screenId {
53
- return ANNScreenPaddingById[screenId].UIEdgeInsetsValue;
54
- }
55
-
56
- + (void)removeScreenId:(NSString *)screenId {
57
- [ANNScreenPaddingById removeObjectForKey:screenId];
58
- @synchronized(ANNScreenInsetsById) {
59
- [ANNScreenInsetsById removeObjectForKey:screenId];
60
- }
61
- }
62
-
63
- + (void)removeAll {
64
- [ANNScreenPaddingById removeAllObjects];
65
- @synchronized(ANNScreenInsetsById) {
66
- [ANNScreenInsetsById removeAllObjects];
67
- }
68
- }
69
-
70
- @end