@apexnative/navigation 1.0.0-alpha.4 → 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.
- package/CHANGELOG.md +38 -0
- package/README.md +1 -1
- package/android/CLAUDE.md +23 -14
- package/android/CORE.md +32 -19
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +60 -13
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +28 -13
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +39 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -51
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +30 -39
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +34 -7
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +22 -21
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +119 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +7 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +45 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +25 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +2 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +4 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +11 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +318 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +18 -20
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +17 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +15 -22
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +84 -35
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +49 -40
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +4 -2
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +1 -1
- package/docs-md/containers/side-menu.md +1 -1
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/bottom-sheets.md +1 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +23 -2
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +1 -1
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +36 -0
- package/docs-md/screen-ui/bar-buttons.md +4 -4
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +16 -11
- package/docs-md/screen-ui/toolbar-and-fab.md +2 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +1 -1
- package/ios/Animations/ANNFrameDriver.mm +2 -0
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +70 -4
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +6 -4
- package/ios/Animations/Interpolators/ANNViewTracks.mm +49 -9
- package/ios/CLAUDE.md +17 -15
- package/ios/CORE.md +30 -26
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +25 -12
- package/ios/Contract/ANNCommandRunner.h +8 -7
- package/ios/Contract/ANNCommandRunner.mm +21 -27
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNJavaScriptCalls.h +11 -12
- package/ios/Core/ANNJavaScriptCalls.mm +7 -16
- package/ios/Core/ANNNavigationState.h +4 -6
- package/ios/Core/ANNNavigationState.mm +0 -4
- package/ios/Core/ANNReloadCoordinator.mm +4 -8
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
- package/ios/Layouts/Screen/ANNComponentViewController.mm +9 -22
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +18 -0
- package/ios/Options/ANNOptions.mm +112 -1
- package/ios/Options/ANNOptionsReader.mm +110 -0
- package/ios/Options/Params/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/Image.h +4 -0
- package/ios/Options/Params/Image.mm +20 -0
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
- package/ios/React/ANNSurfaceHostingView.h +6 -0
- package/ios/React/ANNSurfaceHostingView.mm +116 -0
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +5 -3
- package/ios/TopBar/ANNBarButtons.mm +186 -17
- package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +2 -2
- package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
- package/ios/TopBar/ANNTopBarPresenter.h +3 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
- package/ios/TurboModules/ANNTurboModule.mm +20 -16
- package/lib/module/ARCHITECTURE.md +8 -4
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/engine/commands.js +21 -1
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/places.js +8 -0
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/screens.js +29 -4
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +3 -3
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +2 -0
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +20 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +30 -9
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +18 -5
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +8 -8
- package/lib/typescript/interfaces/options/stack.d.ts +9 -0
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +8 -5
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +32 -29
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +8 -4
- package/src/api/app/createNavigation.ts +3 -3
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/engine/commands.ts +30 -6
- package/src/engine/places.ts +9 -0
- package/src/engine/screens.tsx +29 -8
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/options/animations.ts +31 -9
- package/src/interfaces/options/bottomTabs.ts +18 -5
- package/src/interfaces/options/buttons.ts +8 -8
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/system.ts +8 -5
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +32 -29
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
|
@@ -31,7 +31,12 @@ class SlotScreenView(
|
|
|
31
31
|
private val whenMounted = mutableListOf<Runnable>()
|
|
32
32
|
private val whenRendered = mutableListOf<Runnable>()
|
|
33
33
|
private val mountedHandlers = LinkedHashMap<String, Runnable>()
|
|
34
|
-
private var
|
|
34
|
+
private var listening = false
|
|
35
|
+
|
|
36
|
+
// The safe area view of a slot renders in the app's one React tree: every signal of the id is the screen's.
|
|
37
|
+
private val readiness = object : ScreenReadiness.Listener {
|
|
38
|
+
override fun onContentRendered() = checkReadiness()
|
|
39
|
+
}
|
|
35
40
|
private var mounted = false
|
|
36
41
|
private var readyTold = false
|
|
37
42
|
private var destroyed = false
|
|
@@ -105,14 +110,9 @@ class SlotScreenView(
|
|
|
105
110
|
if (readyTold) return listener.run()
|
|
106
111
|
whenRendered += listener
|
|
107
112
|
if (isReady()) return checkReadiness()
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
override fun onContentRendered() = checkReadiness()
|
|
113
|
-
}
|
|
114
|
-
readinessListener = readiness
|
|
115
|
-
ScreenReadiness.setListener(componentId, readiness)
|
|
113
|
+
if (listening) return
|
|
114
|
+
listening = true
|
|
115
|
+
ScreenReadiness.addListener(componentId, this, readiness)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
override fun whenSlotContentMounted(listener: Runnable) {
|
|
@@ -151,8 +151,8 @@ class SlotScreenView(
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
private fun stopListening() {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
ScreenReadiness.removeListener(componentId,
|
|
154
|
+
if (!listening) return
|
|
155
|
+
listening = false
|
|
156
|
+
ScreenReadiness.removeListener(componentId, this)
|
|
157
157
|
}
|
|
158
158
|
}
|
|
@@ -26,16 +26,3 @@ val View.borderRadius: Float
|
|
|
26
26
|
*/
|
|
27
27
|
internal fun resolveBorderRadius(radius: LengthPercentage, width: Int, density: Float): Float =
|
|
28
28
|
(radius.resolve(width / density) * density).coerceAtLeast(0f)
|
|
29
|
-
|
|
30
|
-
/** The scale the parents of [view] give it, along x and along y: a view in a scaled parent shows smaller. */
|
|
31
|
-
fun computeInheritedScale(view: View): Pair<Float, Float> {
|
|
32
|
-
var scaleX = 1f
|
|
33
|
-
var scaleY = 1f
|
|
34
|
-
var parent = view.parent as? View
|
|
35
|
-
while (parent != null) {
|
|
36
|
-
scaleX *= parent.scaleX
|
|
37
|
-
scaleY *= parent.scaleY
|
|
38
|
-
parent = parent.parent as? View
|
|
39
|
-
}
|
|
40
|
-
return scaleX to scaleY
|
|
41
|
-
}
|
|
@@ -9,7 +9,8 @@ import androidx.window.layout.WindowMetricsCalculator
|
|
|
9
9
|
import dev.apexnative.navigation.NavigationActivity
|
|
10
10
|
import dev.apexnative.navigation.options.DefaultOptions
|
|
11
11
|
import dev.apexnative.navigation.options.Options
|
|
12
|
-
import dev.apexnative.navigation.react.
|
|
12
|
+
import dev.apexnative.navigation.react.ScreenEnvironment
|
|
13
|
+
import dev.apexnative.navigation.react.events.EventEmitter
|
|
13
14
|
import dev.apexnative.navigation.utils.SystemUiUtils
|
|
14
15
|
import dev.apexnative.navigation.utils.WindowFolds
|
|
15
16
|
import dev.apexnative.navigation.viewcontrollers.child.ChildController
|
|
@@ -145,12 +146,14 @@ class ArrangementController internal constructor(
|
|
|
145
146
|
val overlay = plan.layout == ArrangementPlan.Layout.OVERLAY
|
|
146
147
|
val single = plan.layout == ArrangementPlan.Layout.SINGLE
|
|
147
148
|
if (primary is ComponentViewController) {
|
|
148
|
-
val state =
|
|
149
|
-
|
|
149
|
+
val state = ScreenEnvironment.Arrangement(PRIMARY, layout, front = overlay, hidden = false)
|
|
150
|
+
val changes = ScreenEnvironment.changesOf(EventEmitter.of(primary.jsGeneration))
|
|
151
|
+
ScreenEnvironment.setArrangement(primary.id, state, notify, changes)
|
|
150
152
|
}
|
|
151
153
|
if (secondary is ComponentViewController) {
|
|
152
|
-
val state =
|
|
153
|
-
|
|
154
|
+
val state = ScreenEnvironment.Arrangement(SECONDARY, layout, front = false, hidden = single)
|
|
155
|
+
val changes = ScreenEnvironment.changesOf(EventEmitter.of(secondary.jsGeneration))
|
|
156
|
+
ScreenEnvironment.setArrangement(secondary.id, state, notify, changes)
|
|
154
157
|
}
|
|
155
158
|
}
|
|
156
159
|
|
|
@@ -243,9 +246,9 @@ class ArrangementController internal constructor(
|
|
|
243
246
|
// The stack keeps the bar's title, buttons and search by the view of its screen on top: they go with the pair.
|
|
244
247
|
if (!isDestroyed && peekView() != null) performOnParentController { it.onChildDestroyed(this) }
|
|
245
248
|
folds?.removeListener(onFoldsChanged)
|
|
246
|
-
ArrangementStates.remove(primary.id)
|
|
247
|
-
ArrangementStates.remove(secondary.id)
|
|
248
249
|
shownChildren.containerDisappeared()
|
|
250
|
+
// The screens go with the pair: each clears its own environment, its arrangement too, unless its id is
|
|
251
|
+
// another's now.
|
|
249
252
|
super.destroy()
|
|
250
253
|
}
|
|
251
254
|
|
|
@@ -2,6 +2,7 @@ package dev.apexnative.navigation.viewcontrollers.bottomtabs
|
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
4
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListener
|
|
5
|
+
import dev.apexnative.navigation.views.bottomtabs.BottomTabsLayout
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* bottomTabs.hideOnScroll: the tabs hide after a drag that scrolls the screen's content down and come back after one
|
|
@@ -43,9 +44,10 @@ class BottomTabsCollapseBehavior(private val container: () -> View?) :
|
|
|
43
44
|
override fun onHide() {
|
|
44
45
|
val view = container() ?: return
|
|
45
46
|
hiddenByScroll = true
|
|
46
|
-
// Out of the screen with what is under it
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Out of the screen with what is under it (a compact bar floats over its bottom margin and the navigation bar)
|
|
48
|
+
// and the custom row that rises above it.
|
|
49
|
+
val distance = (view.parent as? BottomTabsLayout)?.hideDistance ?: view.height
|
|
50
|
+
view.animate().translationY(distance.toFloat()).setDuration(DURATION).start()
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
private companion object {
|
|
@@ -26,16 +26,15 @@ import dev.apexnative.navigation.utils.scrollToTop
|
|
|
26
26
|
import dev.apexnative.navigation.viewcontrollers.child.ChildControllersRegistry
|
|
27
27
|
import dev.apexnative.navigation.viewcontrollers.component.ComponentViewController
|
|
28
28
|
import dev.apexnative.navigation.viewcontrollers.component.RestPlace
|
|
29
|
-
import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
30
29
|
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackState
|
|
31
30
|
import dev.apexnative.navigation.viewcontrollers.navigator.Navigator
|
|
32
31
|
import dev.apexnative.navigation.viewcontrollers.parent.ContainerBackground
|
|
33
32
|
import dev.apexnative.navigation.viewcontrollers.parent.ParentController
|
|
34
33
|
import dev.apexnative.navigation.viewcontrollers.stack.StackController
|
|
35
34
|
import dev.apexnative.navigation.viewcontrollers.stack.fab.FabPlacement
|
|
36
|
-
import dev.apexnative.navigation.viewcontrollers.stack.topbar.BarComponents
|
|
37
35
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
38
36
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
37
|
+
import dev.apexnative.navigation.views.animations.AnimationLayer
|
|
39
38
|
import dev.apexnative.navigation.views.animations.FadeThrough
|
|
40
39
|
import dev.apexnative.navigation.views.bottomtabs.BottomTabItem
|
|
41
40
|
import dev.apexnative.navigation.views.bottomtabs.BottomTabsLayout
|
|
@@ -201,7 +200,9 @@ class BottomTabsController(
|
|
|
201
200
|
}
|
|
202
201
|
})
|
|
203
202
|
val set = AnimatorSet()
|
|
204
|
-
set
|
|
203
|
+
// Each tab fades whole through a hardware layer; a switch ended early ([set]'s end) gives them back too.
|
|
204
|
+
val enter = FadeThrough.enter(coming)
|
|
205
|
+
set.playTogether(AnimationLayer.during(going, exit), AnimationLayer.during(coming, enter))
|
|
205
206
|
set.addListener(object : AnimatorListenerAdapter() {
|
|
206
207
|
override fun onAnimationEnd(animation: Animator) {
|
|
207
208
|
set.removeAllListeners()
|
|
@@ -352,8 +353,8 @@ class BottomTabsController(
|
|
|
352
353
|
|
|
353
354
|
/**
|
|
354
355
|
* Selects the tab at [index]: the bar shows it at once; on screen the tab it left fades through to it (Material's
|
|
355
|
-
* fade through)
|
|
356
|
-
* history of back follows.
|
|
356
|
+
* fade through); off screen, or when the new tab's `bottomTabs.switchAnimation` is `'none'`, one container becomes
|
|
357
|
+
* invisible and the other visible at once; then [switched]. The history of back follows.
|
|
357
358
|
*/
|
|
358
359
|
private fun select(index: Int, how: How) {
|
|
359
360
|
if (index == selectedIndex || index !in tabs.indices || isDestroyed) return
|
|
@@ -379,8 +380,11 @@ class BottomTabsController(
|
|
|
379
380
|
// A layer that waits for the tabs waits for the tab they show now.
|
|
380
381
|
navigator?.shownScreensChanged(this)
|
|
381
382
|
if (onScreen) AppearanceDispatcher.beginTransition(listOf(shown))
|
|
383
|
+
// From code and by back the state goes at once, as on iOS: `selectTab` resolves on it, and the command after
|
|
384
|
+
// it must act in this tab, not in the one still fading out.
|
|
385
|
+
if (how != How.Press) AppearanceDispatcher.reportState()
|
|
382
386
|
if (views == null) return switched(index, previous, how, onScreen, hasViews = false)
|
|
383
|
-
endSwitch = views.switch(shown, left, animated = onScreen) {
|
|
387
|
+
endSwitch = views.switch(shown, left, animated = onScreen && fadesThrough(shown)) {
|
|
384
388
|
endSwitch = null
|
|
385
389
|
if (!isDestroyed) switched(index, previous, how, onScreen, hasViews = true)
|
|
386
390
|
}
|
|
@@ -390,8 +394,8 @@ class BottomTabsController(
|
|
|
390
394
|
* The switch to the tab at [index] from [previous] ended: what went disappears, what came appears — unless the tabs
|
|
391
395
|
* stopped showing during the fade (a modal over them): it appears when they show again; then for a press
|
|
392
396
|
* `ANN.BottomTabSelected` right after the new screen's DidAppear and the left tab's `popToRootOnBlur`, with the state
|
|
393
|
-
* as one update after them; from code and by back, `popToRootOnBlur` and the state
|
|
394
|
-
* started on screen.
|
|
397
|
+
* as one update after them; from code and by back, `popToRootOnBlur` and the state, which went at the start too
|
|
398
|
+
* ([select]). [onScreen]: the switch started on screen.
|
|
395
399
|
*/
|
|
396
400
|
private fun switched(index: Int, previous: Int, how: How, onScreen: Boolean, hasViews: Boolean) {
|
|
397
401
|
val left = tabs[previous]
|
|
@@ -426,6 +430,11 @@ class BottomTabsController(
|
|
|
426
430
|
if (screen == null) finish() else AppearanceDispatcher.afterDidAppear(screen, finish)
|
|
427
431
|
}
|
|
428
432
|
|
|
433
|
+
// `bottomTabs.switchAnimation` of the tab that shows, its shown screen's over its own, the tabs' and the defaults:
|
|
434
|
+
// `'platform'`, the default, fades through; `'none'` switches at once.
|
|
435
|
+
private fun fadesThrough(tab: ViewController<*>): Boolean =
|
|
436
|
+
tab.resolveShownOptions(DefaultOptions.current).bottomTabs.switchAnimation.get(null) != "none"
|
|
437
|
+
|
|
429
438
|
// A switch still running ends at once and a press's `ANN.BottomTabSelected` still waiting goes, before anything
|
|
430
439
|
// newer on these tabs.
|
|
431
440
|
private fun flushPendingSelected() {
|
|
@@ -547,9 +556,6 @@ class BottomTabsController(
|
|
|
547
556
|
return TabsRoom.tabsTop(room(), screenOptions(screen), bottom)
|
|
548
557
|
}
|
|
549
558
|
|
|
550
|
-
override fun leavesBottomEdgeFree(child: ViewController<*>, screen: ViewController<*>): Boolean =
|
|
551
|
-
TabsRoom.leavesBottomEdgeFree(screenOptions(screen)) || super.leavesBottomEdgeFree(child, screen)
|
|
552
|
-
|
|
553
559
|
override fun tabsAtRestOf(child: ViewController<*>, screen: ViewController<*>): FabPlacement.Tabs? {
|
|
554
560
|
if (peekView() == null) return super.tabsAtRestOf(child, screen)
|
|
555
561
|
return TabsRoom.fabTabs(room(), screenOptions(screen))
|
|
@@ -630,7 +636,6 @@ class BottomTabsController(
|
|
|
630
636
|
layout.row = row
|
|
631
637
|
layout.bar.customRowOptionsListener = { applyRow(row, it) }
|
|
632
638
|
layout.bar.customRowOptions = resolveShownOptions(DefaultOptions.current).bottomTabs.customRow.copy()
|
|
633
|
-
CustomTabRowPlacer.track(activity)
|
|
634
639
|
}
|
|
635
640
|
|
|
636
641
|
private fun applyRow(row: CustomTabRow, options: BottomTabsCustomRowOptions) {
|
|
@@ -660,15 +665,12 @@ class BottomTabsController(
|
|
|
660
665
|
private fun makeCell(index: Int): CustomBottomTabItemView {
|
|
661
666
|
val component = itemOptions(index).component
|
|
662
667
|
val componentId = component.componentId.get(null) ?: "$id.$index"
|
|
663
|
-
ScreenReadiness.setRendersAtOnce(componentId, true)
|
|
664
|
-
// Another live root has the id (the same layout set again before the old one went): what it rendered isn't
|
|
665
|
-
// this one's; the id's readiness goes only with its last root (as the bar's roots).
|
|
666
|
-
if (BarComponents.isLive(componentId)) ScreenReadiness.forgetRender(componentId)
|
|
667
|
-
BarComponents.rootMade(componentId)
|
|
668
668
|
val cell = CustomBottomTabItemView(activity)
|
|
669
669
|
val state = cellState(index)
|
|
670
|
-
|
|
671
|
-
|
|
670
|
+
// Another live root may have the id (the same layout set again before the old one went): each answers by the
|
|
671
|
+
// render of its own surface, and the id's readiness goes with its last root.
|
|
672
|
+
val root = ReactView(activity, componentId, component.name.get(""), cell.bundleOf(state), rendersAtOnce = true)
|
|
673
|
+
cell.show(root, state)
|
|
672
674
|
return cell
|
|
673
675
|
}
|
|
674
676
|
|
|
@@ -685,7 +687,7 @@ class BottomTabsController(
|
|
|
685
687
|
|
|
686
688
|
/**
|
|
687
689
|
* Destroys the tabs once: every tab and the registry first, then the cells (each stops its root once), the row out
|
|
688
|
-
* of the
|
|
690
|
+
* of the tabs' view, the scroll it followed. A press's event still waiting goes with them.
|
|
689
691
|
*/
|
|
690
692
|
override fun destroy() {
|
|
691
693
|
if (isDestroyed) return
|
|
@@ -696,7 +698,6 @@ class BottomTabsController(
|
|
|
696
698
|
endSwitch?.invoke()
|
|
697
699
|
peekView()?.let { layout ->
|
|
698
700
|
layout.bar.destroyCustomItemViews()
|
|
699
|
-
layout.row?.let(CustomTabRowPlacer::forget)
|
|
700
701
|
layout.row = null
|
|
701
702
|
}
|
|
702
703
|
cells = emptyList()
|
|
@@ -49,6 +49,22 @@ internal object CustomTabRowGeometry {
|
|
|
49
49
|
content = content,
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The row over the bar as the tabs' view lays it out: the bar's [barLeft], [barRight] and [barBottom] in that
|
|
54
|
+
* view, whose height is [tabsHeight] and whose bottom [navigationBar] pixels lie under the navigation bar — the
|
|
55
|
+
* row's bottom never goes over them —, with [margin], [content] and [bottomMargin] as [place] takes them.
|
|
56
|
+
*/
|
|
57
|
+
fun inTabs(
|
|
58
|
+
barLeft: Int,
|
|
59
|
+
barRight: Int,
|
|
60
|
+
barBottom: Int,
|
|
61
|
+
tabsHeight: Int,
|
|
62
|
+
navigationBar: Int,
|
|
63
|
+
margin: Int,
|
|
64
|
+
content: Int,
|
|
65
|
+
bottomMargin: Int,
|
|
66
|
+
): Place = place(barLeft, barRight, minOf(barBottom, tabsHeight - navigationBar), margin, content, bottomMargin)
|
|
67
|
+
|
|
52
68
|
/**
|
|
53
69
|
* The part of the row's frame at [place] below its visible part, in pixels: `bottomMargin`. The row draws and takes
|
|
54
70
|
* touches only above it.
|
|
@@ -61,6 +77,12 @@ internal object CustomTabRowGeometry {
|
|
|
61
77
|
*/
|
|
62
78
|
fun needsPlacing(last: Place?, next: Place): Boolean = last != next
|
|
63
79
|
|
|
80
|
+
/**
|
|
81
|
+
* The row's own lift over its [elevation] while the bar's container stands at [barZ]: as high as the container, so
|
|
82
|
+
* the row, after it in the tabs' view, draws over it and takes the touches first even under a raised bar.
|
|
83
|
+
*/
|
|
84
|
+
fun lift(barZ: Float, elevation: Float): Float = (barZ - elevation).coerceAtLeast(0f)
|
|
85
|
+
|
|
64
86
|
/**
|
|
65
87
|
* The row's background: `customRow.backgroundColor` ([color]; `null` and `'NoColor'` are none); without it
|
|
66
88
|
* `backgroundEffect: 'none'` ([effect]) is clear, any other effect or none the theme's `android:colorBackground`
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt
CHANGED
|
@@ -58,11 +58,6 @@ internal object TabsRoom {
|
|
|
58
58
|
fun tabsTop(bar: Bar, screen: Screen, tabsBottom: Int): Int? =
|
|
59
59
|
if (screen.hidden) null else tabsBottom - bar.reach - bar.rowOvershoot
|
|
60
60
|
|
|
61
|
-
/**
|
|
62
|
-
* The safe area of the screen leaves its bottom edge free: the bar hides on scroll and shows for it.
|
|
63
|
-
*/
|
|
64
|
-
fun leavesBottomEdgeFree(screen: Screen): Boolean = screen.hideOnScroll && !screen.hidden
|
|
65
|
-
|
|
66
61
|
/**
|
|
67
62
|
* The bar at rest under a stack's FAB: also when the screen is under it (`drawBehind`, `hideOnScroll`); none when
|
|
68
63
|
* the screen hides it.
|
|
@@ -8,12 +8,12 @@ import androidx.core.view.ViewCompat
|
|
|
8
8
|
import androidx.core.view.WindowInsetsCompat
|
|
9
9
|
import dev.apexnative.navigation.options.DefaultOptions
|
|
10
10
|
import dev.apexnative.navigation.options.Options
|
|
11
|
-
import dev.apexnative.navigation.react.ArrangementStates
|
|
12
11
|
import dev.apexnative.navigation.react.ReactView
|
|
13
|
-
import dev.apexnative.navigation.react.
|
|
12
|
+
import dev.apexnative.navigation.react.ScreenEnvironment
|
|
14
13
|
import dev.apexnative.navigation.react.ScreenRegions
|
|
15
14
|
import dev.apexnative.navigation.react.events.AppearanceDispatcher
|
|
16
15
|
import dev.apexnative.navigation.react.events.ComponentType
|
|
16
|
+
import dev.apexnative.navigation.react.events.EventEmitter
|
|
17
17
|
import dev.apexnative.navigation.slots.SlotScreenView
|
|
18
18
|
import dev.apexnative.navigation.system.SystemUiOwner
|
|
19
19
|
import dev.apexnative.navigation.viewcontrollers.child.ChildController
|
|
@@ -156,7 +156,7 @@ class ComponentViewController(
|
|
|
156
156
|
shown = layout.isShown || prepared,
|
|
157
157
|
layoutPending = !prepared && isLayoutPending(content),
|
|
158
158
|
topOfStack = parent?.isTopOfStack(this) ?: true,
|
|
159
|
-
hasSafeArea =
|
|
159
|
+
hasSafeArea = ScreenEnvironment.padding(id) != null,
|
|
160
160
|
)
|
|
161
161
|
if (!measures) return
|
|
162
162
|
val windowInsets = ViewCompat.getRootWindowInsets(content) ?: return
|
|
@@ -175,10 +175,15 @@ class ComponentViewController(
|
|
|
175
175
|
val edges = ScreenSafeArea.paddedEdges(
|
|
176
176
|
safeAreaEdges = resolveCurrentOptions(DefaultOptions.current).layout.safeAreaEdges,
|
|
177
177
|
topFree = parent?.leavesTopEdgeFree(this) == true,
|
|
178
|
-
|
|
178
|
+
largeTitle = parent?.showsLargeTitleOver(this) == true,
|
|
179
179
|
)
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
// The events of the screen go to the JavaScript it was made for, and to no other.
|
|
181
|
+
val emitter = EventEmitter.of(jsGeneration)
|
|
182
|
+
val padding = ScreenSafeArea.padding(insets, edges)
|
|
183
|
+
val uncovered = ScreenSafeArea.uncovered(insets, padding)
|
|
184
|
+
ScreenEnvironment.setInsets(id, uncovered, padding, ScreenEnvironment.changesOf(emitter))
|
|
185
|
+
val area = Rect(place.left, place.top, place.right, place.bottom)
|
|
186
|
+
ScreenRegions.update(activity, id, area, windowInsets, emitter)
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
// A layout asked for on the content or on a view around it: it isn't in its place yet.
|
|
@@ -241,9 +246,7 @@ class ComponentViewController(
|
|
|
241
246
|
}
|
|
242
247
|
setParentController(null)
|
|
243
248
|
if (isIdTakenByAnother) return
|
|
244
|
-
|
|
245
|
-
ScreenRegions.remove(id)
|
|
246
|
-
ArrangementStates.remove(id)
|
|
249
|
+
ScreenEnvironment.remove(id)
|
|
247
250
|
ScreenReadiness.remove(id)
|
|
248
251
|
}
|
|
249
252
|
|
|
@@ -5,7 +5,7 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
|
5
5
|
/**
|
|
6
6
|
* Whether a screen's content rendered, which a command waits for before the screen comes in: a new root (`setRoot`),
|
|
7
7
|
* a push (unless `waitForRender: false`), the new top of a `setStackRoot`. A wait never listens to the readiness store
|
|
8
|
-
* itself: the content keeps
|
|
8
|
+
* itself: the content keeps its own listener of its id and tells every waiter. Main thread only.
|
|
9
9
|
*/
|
|
10
10
|
internal interface Readiness {
|
|
11
11
|
/** The content of [screen] rendered; a screen without content to wait for is ready. */
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt
CHANGED
|
@@ -1,93 +1,128 @@
|
|
|
1
1
|
package dev.apexnative.navigation.viewcontrollers.component
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The readiness of each screen by its id: the safe area view of a screen (`ANNSafeAreaView`) tells it here that it
|
|
5
|
-
* laid out in its place and that the screen's content rendered into it, and asks whether the
|
|
6
|
-
* all. The screen
|
|
7
|
-
* thread only.
|
|
4
|
+
* The readiness of each screen by its id: the safe area view of a screen (`ANNSafeAreaView`) tells it here that it
|
|
5
|
+
* has one, that it is laid out in its place and that the screen's content rendered into it, and asks whether the
|
|
6
|
+
* screen waits for that at all. The content of a screen or of a component listens by the id: no view walks up or down
|
|
7
|
+
* the views to find the other. Main thread only.
|
|
8
|
+
*
|
|
9
|
+
* As on iOS (`ANNScreenReadiness`), what a safe area view said is kept: a listener that comes later hears it at once.
|
|
10
|
+
* Several listeners may wait on one id (the roots of a bar component that several screens show with one id), each
|
|
11
|
+
* under a key of its own, which removes it. Each signal carries its source, the React surface of the safe area view
|
|
12
|
+
* that gave it, and a listener hears only the sources it [accepts][Listener.accepts]: React Native never gives a
|
|
13
|
+
* surface's id twice in a process, so the late signal of another root with the same id (a root of the JavaScript that
|
|
14
|
+
* went, a destroyed screen, another live root of a bar component) never makes a new root ready.
|
|
8
15
|
*/
|
|
9
16
|
object ScreenReadiness {
|
|
10
|
-
/**
|
|
17
|
+
/** The source of a signal that names none: a listener that accepts every source hears it. */
|
|
18
|
+
const val NO_SOURCE = -1
|
|
19
|
+
|
|
20
|
+
/** What the content of a screen or of a component hears from the safe area view of its id. */
|
|
11
21
|
interface Listener {
|
|
22
|
+
/**
|
|
23
|
+
* Whether a signal of [source] (the id of the React surface of the safe area view that gave it) is this
|
|
24
|
+
* listener's: by default every one is.
|
|
25
|
+
*/
|
|
26
|
+
fun accepts(source: Int): Boolean = true
|
|
27
|
+
|
|
12
28
|
/** The safe area view is laid out in its place, with a size. */
|
|
13
|
-
fun onLaidOut()
|
|
29
|
+
fun onLaidOut() = Unit
|
|
14
30
|
|
|
15
31
|
/** The screen rendered its content into its safe area view, also when it renders nothing (`null`). */
|
|
16
32
|
fun onContentRendered()
|
|
17
33
|
}
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
// What the safe area views of one id said: that one exists, and the sources that are laid out and rendered.
|
|
36
|
+
private class Entry {
|
|
37
|
+
var wrapped = false
|
|
38
|
+
val laidOut = HashSet<Int>()
|
|
39
|
+
val rendered = HashSet<Int>()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private val entries = HashMap<String, Entry>()
|
|
43
|
+
private val listeners = HashMap<String, LinkedHashMap<Any, Listener>>()
|
|
44
|
+
// The roots that render at once, by id: an id renders at once while one of them lives.
|
|
45
|
+
private val atOnce = HashMap<String, MutableSet<Any>>()
|
|
24
46
|
|
|
25
47
|
/**
|
|
26
48
|
* Whether the screen [screenId] waits for its safe area view to be laid out before its content renders: every
|
|
27
|
-
* screen does, as on iOS, unless
|
|
28
|
-
*
|
|
49
|
+
* screen does, as on iOS, unless a root of the id renders at once ([setRendersAtOnce]) — a React component of a
|
|
50
|
+
* bar or a tab item, whose size comes from its content.
|
|
29
51
|
*/
|
|
30
|
-
fun waitsForReadiness(screenId: String): Boolean = screenId
|
|
52
|
+
fun waitsForReadiness(screenId: String): Boolean = atOnce[screenId].isNullOrEmpty()
|
|
31
53
|
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The root [key] of [screenId] renders at once (a component of a bar, a tab item), or stopped. The id renders at
|
|
56
|
+
* once while a root marks it; when the last one stops, what was said of the id goes too, so a later root with it
|
|
57
|
+
* starts anew.
|
|
58
|
+
*/
|
|
59
|
+
fun setRendersAtOnce(screenId: String, key: Any, rendersAtOnce: Boolean) {
|
|
60
|
+
if (rendersAtOnce) {
|
|
61
|
+
atOnce.getOrPut(screenId, ::HashSet).add(key)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
val keys = atOnce[screenId] ?: return
|
|
65
|
+
if (!keys.remove(key) || keys.isNotEmpty()) return
|
|
66
|
+
atOnce.remove(screenId)
|
|
67
|
+
entries.remove(screenId)
|
|
35
68
|
}
|
|
36
69
|
|
|
37
70
|
/** The screen [screenId] has a safe area view: it is rendered only once that view says so. */
|
|
38
71
|
fun wrapped(screenId: String) {
|
|
39
|
-
wrapped
|
|
72
|
+
entryOf(screenId).wrapped = true
|
|
40
73
|
}
|
|
41
74
|
|
|
42
|
-
/** The safe area view of [screenId] is laid out in its place, with a size. */
|
|
43
|
-
fun laidOut(screenId: String) {
|
|
44
|
-
|
|
75
|
+
/** The safe area view of [screenId], in the React surface [source], is laid out in its place, with a size. */
|
|
76
|
+
fun laidOut(screenId: String, source: Int = NO_SOURCE) {
|
|
77
|
+
val entry = entryOf(screenId)
|
|
78
|
+
entry.wrapped = true
|
|
79
|
+
if (entry.laidOut.add(source)) tell(screenId, source, Listener::onLaidOut)
|
|
45
80
|
}
|
|
46
81
|
|
|
47
|
-
/** The screen [screenId] rendered its content into its safe area view. */
|
|
48
|
-
fun contentRendered(screenId: String) {
|
|
49
|
-
|
|
82
|
+
/** The screen [screenId] rendered its content into its safe area view, in the React surface [source]. */
|
|
83
|
+
fun contentRendered(screenId: String, source: Int = NO_SOURCE) {
|
|
84
|
+
val entry = entryOf(screenId)
|
|
85
|
+
entry.wrapped = true
|
|
86
|
+
if (entry.rendered.add(source)) tell(screenId, source, Listener::onContentRendered)
|
|
50
87
|
}
|
|
51
88
|
|
|
52
89
|
/**
|
|
53
|
-
* Whether the screen [screenId] rendered its content into its safe area view
|
|
54
|
-
* anything of
|
|
90
|
+
* Whether the screen [screenId] rendered its content into its safe area view, in a source [listener] accepts (any
|
|
91
|
+
* source without one); null when no safe area view told anything of the id (a screen without one, of the old API:
|
|
92
|
+
* its content decides by its own children).
|
|
55
93
|
*/
|
|
56
|
-
fun isContentRendered(screenId: String
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
else -> null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** [listener] hears the signals of [screenId] from now on; null stops it. */
|
|
63
|
-
fun setListener(screenId: String, listener: Listener?) {
|
|
64
|
-
if (listener == null) listeners.remove(screenId) else listeners[screenId] = listener
|
|
94
|
+
fun isContentRendered(screenId: String, listener: Listener? = null): Boolean? {
|
|
95
|
+
val entry = entries[screenId] ?: return null
|
|
96
|
+
return entry.rendered.any { listener?.accepts(it) ?: true }
|
|
65
97
|
}
|
|
66
98
|
|
|
67
99
|
/**
|
|
68
|
-
* [listener]
|
|
69
|
-
*
|
|
100
|
+
* [listener] hears the signals of [screenId] under [key] (its owner) until [removeListener] with the key: a key
|
|
101
|
+
* has one listener per id, the other keys keep theirs. What the safe area views said before reaches it at once, in
|
|
102
|
+
* order, for the sources it accepts.
|
|
70
103
|
*/
|
|
71
|
-
fun
|
|
72
|
-
|
|
104
|
+
fun addListener(screenId: String, key: Any, listener: Listener) {
|
|
105
|
+
listeners.getOrPut(screenId, ::LinkedHashMap)[key] = listener
|
|
106
|
+
val entry = entries[screenId] ?: return
|
|
107
|
+
if (entry.laidOut.any(listener::accepts) && isListening(screenId, key, listener)) listener.onLaidOut()
|
|
108
|
+
if (entry.rendered.any(listener::accepts) && isListening(screenId, key, listener)) listener.onContentRendered()
|
|
73
109
|
}
|
|
74
110
|
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
laidOut -= screenId
|
|
81
|
-
rendered -= screenId
|
|
111
|
+
/** The listener of [key] stops hearing [screenId]; those of other keys (a later root with the id) keep theirs. */
|
|
112
|
+
fun removeListener(screenId: String, key: Any) {
|
|
113
|
+
val keys = listeners[screenId] ?: return
|
|
114
|
+
keys.remove(key)
|
|
115
|
+
if (keys.isEmpty()) listeners.remove(screenId)
|
|
82
116
|
}
|
|
83
117
|
|
|
84
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* The screen [screenId] owns the id from now on (made), or went while no other node has it (destroyed): what its
|
|
120
|
+
* safe area views said and the marks of rendering at once go, so a later screen with the id starts anew. The
|
|
121
|
+
* listeners stay with their keys, which remove them.
|
|
122
|
+
*/
|
|
85
123
|
fun remove(screenId: String) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
laidOut -= screenId
|
|
89
|
-
rendered -= screenId
|
|
90
|
-
listeners -= screenId
|
|
124
|
+
entries.remove(screenId)
|
|
125
|
+
atOnce.remove(screenId)
|
|
91
126
|
}
|
|
92
127
|
|
|
93
128
|
/**
|
|
@@ -95,10 +130,22 @@ object ScreenReadiness {
|
|
|
95
130
|
* "rendered" kept for a screen of the old one would make its new screen with the same id ready at once.
|
|
96
131
|
*/
|
|
97
132
|
fun reset() {
|
|
98
|
-
|
|
99
|
-
wrapped.clear()
|
|
100
|
-
laidOut.clear()
|
|
101
|
-
rendered.clear()
|
|
133
|
+
entries.clear()
|
|
102
134
|
listeners.clear()
|
|
135
|
+
atOnce.clear()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private fun entryOf(screenId: String): Entry = entries.getOrPut(screenId, ::Entry)
|
|
139
|
+
|
|
140
|
+
private fun isListening(screenId: String, key: Any, listener: Listener): Boolean =
|
|
141
|
+
listeners[screenId]?.get(key) === listener
|
|
142
|
+
|
|
143
|
+
// Each listener of the id that accepts the source, as they are now: one that a listener before it removed hears
|
|
144
|
+
// nothing.
|
|
145
|
+
private fun tell(screenId: String, source: Int, signal: (Listener) -> Unit) {
|
|
146
|
+
val now = listeners[screenId]?.entries?.toList() ?: return
|
|
147
|
+
for ((key, listener) in now) {
|
|
148
|
+
if (isListening(screenId, key, listener) && listener.accepts(source)) signal(listener)
|
|
149
|
+
}
|
|
103
150
|
}
|
|
104
151
|
}
|