@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
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt
CHANGED
|
@@ -33,6 +33,7 @@ import dev.apexnative.navigation.viewcontrollers.externalcomponent.ExternalCompo
|
|
|
33
33
|
import dev.apexnative.navigation.viewcontrollers.modal.ModalCoordinator
|
|
34
34
|
import dev.apexnative.navigation.viewcontrollers.modal.ModalRecord
|
|
35
35
|
import dev.apexnative.navigation.viewcontrollers.modal.ModalViews
|
|
36
|
+
import dev.apexnative.navigation.viewcontrollers.modal.SheetBack
|
|
36
37
|
import dev.apexnative.navigation.viewcontrollers.overlay.OverlayCoordinator
|
|
37
38
|
import dev.apexnative.navigation.viewcontrollers.overlay.OverlayRecord
|
|
38
39
|
import dev.apexnative.navigation.viewcontrollers.overlay.OverlayViews
|
|
@@ -40,7 +41,6 @@ import dev.apexnative.navigation.viewcontrollers.parent.ParentController
|
|
|
40
41
|
import dev.apexnative.navigation.viewcontrollers.stack.PreparedPushes
|
|
41
42
|
import dev.apexnative.navigation.viewcontrollers.stack.StackCommands
|
|
42
43
|
import dev.apexnative.navigation.viewcontrollers.stack.StackController
|
|
43
|
-
import dev.apexnative.navigation.viewcontrollers.stack.topbar.BarComponents
|
|
44
44
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
45
45
|
import dev.apexnative.navigation.views.animations.FadeThrough
|
|
46
46
|
import dev.apexnative.navigation.views.animations.SideAnimator
|
|
@@ -105,7 +105,10 @@ class Navigator(
|
|
|
105
105
|
readiness = ContentReadiness,
|
|
106
106
|
scheduler = scheduler,
|
|
107
107
|
firstRoot = firstRootDraw,
|
|
108
|
-
onRootChanged = {
|
|
108
|
+
onRootChanged = {
|
|
109
|
+
root = it
|
|
110
|
+
BackCallbackState.update(this)
|
|
111
|
+
},
|
|
109
112
|
onRootShown = {
|
|
110
113
|
onRootShown?.invoke()
|
|
111
114
|
BackCallbackState.update(this)
|
|
@@ -142,10 +145,21 @@ class Navigator(
|
|
|
142
145
|
|
|
143
146
|
/**
|
|
144
147
|
* Whether back has something to do in the navigation: the mirror of [handleBack], without acting — the top modal
|
|
145
|
-
* first, then the root. The back callback asks it
|
|
148
|
+
* first, then the root. The back callback asks it whenever what back does can have changed (`BackCallbackState`).
|
|
146
149
|
*/
|
|
147
150
|
fun canHandleBack(): Boolean = modals.canHandleBack() ?: (root?.canHandleBack() == true)
|
|
148
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Whether back stays in the app also when the navigation has nothing to do: the layout back acts on — the top
|
|
154
|
+
* modal's, else the root — shows a screen with `hardwareBackButton.systemAnimation: false`. The system then doesn't
|
|
155
|
+
* play its animation of going home, and the press does the activity's own back ([handleBack] is false). The back
|
|
156
|
+
* callback asks it with [canHandleBack] (`BackCallbackState`).
|
|
157
|
+
*/
|
|
158
|
+
fun keepsBackFromSystem(): Boolean {
|
|
159
|
+
val layout = modals.backTargetRoot() ?: root ?: return false
|
|
160
|
+
return layout.resolveShownOptions(DefaultOptions.current).hardwareBackButton.systemAnimation.isFalse
|
|
161
|
+
}
|
|
162
|
+
|
|
149
163
|
/**
|
|
150
164
|
* The navigation's step of back, after JavaScript's listeners didn't take it: the top modal first (its content,
|
|
151
165
|
* else it closes or holds), else the root does the first thing it can (a stack's top leaves or holds, an open side
|
|
@@ -164,6 +178,9 @@ class Navigator(
|
|
|
164
178
|
return if (modalRoot != null) modalRoot.stackPoppedByBack() else root?.stackPoppedByBack()
|
|
165
179
|
}
|
|
166
180
|
|
|
181
|
+
/** The sheet [handleBack] would dismiss, as a drag down of the user would; none when back would do anything else. */
|
|
182
|
+
internal fun sheetDismissedByBack(): SheetBack? = modals.backTargetSheet()
|
|
183
|
+
|
|
167
184
|
/**
|
|
168
185
|
* [layer] may wait to show from now on ([WaitingLayer]): the nodes in it ask it through [isInWaitingLayer] and
|
|
169
186
|
* [shownScreensChanged]. The root's layer is there from the start.
|
|
@@ -187,12 +204,14 @@ class Navigator(
|
|
|
187
204
|
* A layout of [node] (a stack, the tabs, a side menu, a split view, a pair) changed the screens it shows: when it
|
|
188
205
|
* is in a layer that still waits, the layer waits for the screens it shows now, and no longer for one that went
|
|
189
206
|
* ([WaitingLayer.shownScreensChanged]); so does the operation of its stack that waits for [node] as its entry (a
|
|
190
|
-
* pushed pair); a modal's sheet takes the hold of the screen it shows now
|
|
207
|
+
* pushed pair); a modal's sheet takes the hold of the screen it shows now; what back does is computed again (the
|
|
208
|
+
* tab, the pane or the menu back goes to, and the options of the screen it shows).
|
|
191
209
|
*/
|
|
192
210
|
internal fun shownScreensChanged(node: ViewController<*>) {
|
|
193
211
|
waitingLayerOf(node)?.shownScreensChanged()
|
|
194
212
|
node.findParentStack()?.queue?.shownScreensChanged(node)
|
|
195
213
|
modals.visibleScreenChanged(node)
|
|
214
|
+
BackCallbackState.update(this)
|
|
196
215
|
}
|
|
197
216
|
|
|
198
217
|
// The waiting layer [node] is in, up through the layouts around it; none.
|
|
@@ -435,7 +454,8 @@ class Navigator(
|
|
|
435
454
|
destroyLayers(answerPending = false)
|
|
436
455
|
// Nothing the old screens told of their readiness holds for the screens of the new JavaScript.
|
|
437
456
|
ScreenReadiness.reset()
|
|
438
|
-
|
|
457
|
+
// Without layers back leaves the app until the new JavaScript sets its root.
|
|
458
|
+
BackCallbackState.update(this)
|
|
439
459
|
}
|
|
440
460
|
|
|
441
461
|
/**
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt
CHANGED
|
@@ -488,8 +488,8 @@ internal class RootCoordinator(
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
// The navigation state goes once, when the root is in place and its layers have shown, right before the answer:
|
|
491
|
-
// what the old root's destroy, the root's appearance and each layer ask for meanwhile waits (
|
|
492
|
-
// per `setRoot`, before the old root goes.
|
|
491
|
+
// what the old root's destroy, the root's appearance and each layer ask for meanwhile waits (the hold of
|
|
492
|
+
// NavigationState). Taken once per `setRoot`, before the old root goes.
|
|
493
493
|
private fun holdState(pending: Pending) {
|
|
494
494
|
if (pending.holdsState) return
|
|
495
495
|
pending.holdsState = true
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt
CHANGED
|
@@ -129,12 +129,9 @@ abstract class ParentController<V : ViewGroup>(
|
|
|
129
129
|
open fun leavesTopEdgeFree(child: ViewController<*>): Boolean =
|
|
130
130
|
parentController?.leavesTopEdgeFree(this) ?: false
|
|
131
131
|
|
|
132
|
-
/**
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*/
|
|
136
|
-
open fun leavesBottomEdgeFree(child: ViewController<*>, screen: ViewController<*> = child): Boolean =
|
|
137
|
-
parentController?.leavesBottomEdgeFree(this, screen) ?: false
|
|
132
|
+
/** Whether a large title shows over [child]: its stack shows the bar with one. No by default. */
|
|
133
|
+
open fun showsLargeTitleOver(child: ViewController<*>): Boolean =
|
|
134
|
+
parentController?.showsLargeTitleOver(this) ?: false
|
|
138
135
|
|
|
139
136
|
/**
|
|
140
137
|
* The tab bar at rest under [child]: where a stack's FAB keeps clear of it, by the options of [screen], the stack's
|
|
@@ -153,6 +153,9 @@ class SplitViewController(
|
|
|
153
153
|
|
|
154
154
|
private fun onWidthChanged(layout: SplitViewLayout) {
|
|
155
155
|
primary.getView().visibility = if (primaryHidden && layout.isWide == true) View.GONE else View.VISIBLE
|
|
156
|
+
// Compact, side by side or one over the other decides what back does from this measure on: the callback only
|
|
157
|
+
// takes its value here, nothing moves; the panes' end below computes it again.
|
|
158
|
+
BackCallbackState.update()
|
|
156
159
|
// Where a later `compactTopColumn` puts the pane over the other at this switch: true open, false closed.
|
|
157
160
|
val settle = if (measured && isSliding) laterTopColumn?.let { it == SECONDARY } else null
|
|
158
161
|
if (!measured) {
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt
CHANGED
|
@@ -2,18 +2,18 @@ package dev.apexnative.navigation.viewcontrollers.stack
|
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
4
|
import com.facebook.react.uimanager.PixelUtil.pxToDp
|
|
5
|
-
import dev.apexnative.navigation.react.
|
|
5
|
+
import dev.apexnative.navigation.react.ScreenEnvironment
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Where a screen prepared for a push (`preparePush`) shows, in dp: the size of the container its slot fills and the
|
|
9
9
|
* padding of its safe area view, as the push lays it out. JavaScript renders the screen with them before the push, and
|
|
10
10
|
* the slot and the safe area view start their states with them.
|
|
11
11
|
*/
|
|
12
|
-
internal data class PreparedFrame(val width: Float, val height: Float, val padding:
|
|
12
|
+
internal data class PreparedFrame(val width: Float, val height: Float, val padding: ScreenEnvironment.Insets) {
|
|
13
13
|
companion object {
|
|
14
14
|
/** The size of [container] as its slot measures it (`SlotView`), so the slot finds it equal. */
|
|
15
15
|
@JvmStatic
|
|
16
|
-
fun of(container: View, padding:
|
|
16
|
+
fun of(container: View, padding: ScreenEnvironment.Insets) =
|
|
17
17
|
PreparedFrame(container.width.toFloat().pxToDp(), container.height.toFloat().pxToDp(), padding)
|
|
18
18
|
}
|
|
19
19
|
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt
CHANGED
|
@@ -11,7 +11,7 @@ import dev.apexnative.navigation.options.DefaultOptions
|
|
|
11
11
|
import dev.apexnative.navigation.react.CommandAnswer
|
|
12
12
|
import dev.apexnative.navigation.react.CommandListener
|
|
13
13
|
import dev.apexnative.navigation.react.ErrorTexts
|
|
14
|
-
import dev.apexnative.navigation.react.
|
|
14
|
+
import dev.apexnative.navigation.react.ScreenEnvironment
|
|
15
15
|
import dev.apexnative.navigation.slots.SlotScreenView
|
|
16
16
|
import dev.apexnative.navigation.utils.NavigationLog
|
|
17
17
|
import dev.apexnative.navigation.viewcontrollers.child.ChildControllersRegistry
|
|
@@ -318,7 +318,7 @@ internal class StackCommands(
|
|
|
318
318
|
layoutNow(stackView, screenView)
|
|
319
319
|
node.updateSafeArea()
|
|
320
320
|
val content = node.content?.asView()
|
|
321
|
-
val padding =
|
|
321
|
+
val padding = ScreenEnvironment.padding(node.id)
|
|
322
322
|
val noInsets = content == null || ViewCompat.getRootWindowInsets(content) == null
|
|
323
323
|
if (noInsets || padding == null || content.width <= 0 || content.height <= 0) {
|
|
324
324
|
prepared.discard(node.id)
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt
CHANGED
|
@@ -270,8 +270,8 @@ class StackController(
|
|
|
270
270
|
|
|
271
271
|
/**
|
|
272
272
|
* The default pop of the top is what back does now and it can be previewed: two entries or more, nothing running
|
|
273
|
-
* or waiting, no open search, the top doesn't hold back
|
|
274
|
-
* has its view, and the stack shows.
|
|
273
|
+
* or waiting, no open search, the top doesn't hold back, pops with the default transition and doesn't turn the
|
|
274
|
+
* preview off (`animations.pop.preview: false`), the screen under it has its view, and the stack shows.
|
|
275
275
|
*/
|
|
276
276
|
override fun stackPoppedByBack(): StackController? {
|
|
277
277
|
if (topBar.isSearchOpen() || !queue.isAtRest) return null
|
|
@@ -282,9 +282,8 @@ class StackController(
|
|
|
282
282
|
if (!queue.isRendered(target) || backGesture != null) return null
|
|
283
283
|
val options = resolvedOf(top)
|
|
284
284
|
val pop = options.animations.pop
|
|
285
|
-
if (options.hardwareBackButton.popStackOnPress.isFalse || pop.enabled.isFalse
|
|
286
|
-
|
|
287
|
-
}
|
|
285
|
+
if (options.hardwareBackButton.popStackOnPress.isFalse || pop.enabled.isFalse) return null
|
|
286
|
+
if (pop.preview.isFalse || AnimationRules.stackIsOwn(pop)) return null
|
|
288
287
|
val display = queue.display
|
|
289
288
|
return this.takeIf { display.isOnScreen && display.canAnimate }
|
|
290
289
|
}
|
|
@@ -595,6 +594,10 @@ class StackController(
|
|
|
595
594
|
override fun leavesTopEdgeFree(child: ViewController<*>): Boolean =
|
|
596
595
|
TopBarRoom.leavesTopEdgeFree(resolvedOf(child), largeTitleShown(child)) || super.leavesTopEdgeFree(child)
|
|
597
596
|
|
|
597
|
+
/** A large title shows over [child] in this stack's bar. */
|
|
598
|
+
override fun showsLargeTitleOver(child: ViewController<*>): Boolean =
|
|
599
|
+
largeTitleShown(child) || super.showsLargeTitleOver(child)
|
|
600
|
+
|
|
598
601
|
/** A screen under another doesn't compute its safe area again; the prepared and waiting do. */
|
|
599
602
|
override fun isTopOfStack(child: ViewController<*>): Boolean =
|
|
600
603
|
child === queue.currentChild || child === queue.waitingScreen || isPrepared(child)
|
|
@@ -694,15 +697,20 @@ class StackController(
|
|
|
694
697
|
private fun optionsScreen(node: ViewController<*>): ViewController<*> =
|
|
695
698
|
(node as? OptionsScreen)?.optionsScreen ?: node
|
|
696
699
|
|
|
700
|
+
// What back does is computed again on each change of the queue (BackCallbackState): its entries change as an
|
|
701
|
+
// operation is accepted, the screen whose options count as its current child changes (a push that waited for its
|
|
702
|
+
// screen begins after its command), and an operation ends or rejects.
|
|
697
703
|
private inner class QueueHost : StackQueue.Host {
|
|
698
704
|
override fun operationAccepted() {
|
|
699
705
|
backGesture?.abort()
|
|
706
|
+
BackCallbackState.update()
|
|
700
707
|
}
|
|
701
708
|
|
|
702
709
|
// The bar takes the options of the new top at once, also without appearance events.
|
|
703
710
|
override fun currentChildChanged() {
|
|
704
711
|
updateBar()
|
|
705
712
|
navigator?.shownScreensChanged(this@StackController)
|
|
713
|
+
BackCallbackState.update()
|
|
706
714
|
}
|
|
707
715
|
|
|
708
716
|
// Before the bar takes the new top's options: the row's picture or the bar's travel, and how it shows or hides.
|
|
@@ -15,6 +15,7 @@ import dev.apexnative.navigation.viewcontrollers.component.ComponentViewControll
|
|
|
15
15
|
import dev.apexnative.navigation.viewcontrollers.externalcomponent.ExternalComponentViewController
|
|
16
16
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Motion
|
|
17
17
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
18
|
+
import dev.apexnative.navigation.views.animations.AnimationLayer
|
|
18
19
|
import dev.apexnative.navigation.views.animations.AnimationRules
|
|
19
20
|
import dev.apexnative.navigation.views.animations.SideAnimator
|
|
20
21
|
import dev.apexnative.navigation.views.animations.SystemTransition
|
|
@@ -157,6 +158,8 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
|
|
|
157
158
|
custom.alpha?.let { view.alpha = it }
|
|
158
159
|
val own = custom.side?.let { SideAnimator.build(view, it, SideAnimator.Base(alpha = custom.alpha)) }
|
|
159
160
|
val fade = if (own == null) StackTransitions.fade(view, custom.fade) else null
|
|
161
|
+
// A fade of the whole screen draws it through a hardware layer, as the default transition does ([dress]).
|
|
162
|
+
for (animator in listOfNotNull(own, fade)) AnimationLayer.during(view, animator)
|
|
160
163
|
val elements = custom.elements?.let { elements ->
|
|
161
164
|
val layout = layout ?: return@let null
|
|
162
165
|
val fadeLength = custom.side?.takeIf(AnimationRules::isSingleAlpha)?.let(AnimationRules::lengthOf)
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt
CHANGED
|
@@ -36,6 +36,8 @@ import dev.apexnative.navigation.views.stack.topbar.search.SuggestionList
|
|
|
36
36
|
class ScreenSearch(
|
|
37
37
|
private val host: Host,
|
|
38
38
|
private val screenId: String,
|
|
39
|
+
/** The sender of the JavaScript the screen was made for: silent once it went. */
|
|
40
|
+
private val emitter: EventEmitter,
|
|
39
41
|
private val context: Context,
|
|
40
42
|
private val loadIcon: (String) -> Drawable?,
|
|
41
43
|
/** A scope the user picks goes into the screen's options, so it stays when they apply again. */
|
|
@@ -45,7 +47,6 @@ class ScreenSearch(
|
|
|
45
47
|
/** The search changed what the bar shows (open or closed, its text): shown again if it is the shown one. */
|
|
46
48
|
fun refresh(search: ScreenSearch)
|
|
47
49
|
fun isShown(search: ScreenSearch): Boolean
|
|
48
|
-
fun emitter(): EventEmitter?
|
|
49
50
|
fun keyboard(view: View, show: Boolean)
|
|
50
51
|
/** The stack's view: a search view goes over it. */
|
|
51
52
|
fun stackView(): ViewGroup?
|
|
@@ -402,7 +403,7 @@ class ScreenSearch(
|
|
|
402
403
|
|
|
403
404
|
// A pick sends searchSuggestionPicked with the suggestion's id; the text stays.
|
|
404
405
|
private fun pickSuggestion(suggestionId: String) {
|
|
405
|
-
|
|
406
|
+
emitter.emitSearchSuggestionPicked(screenId, suggestionId)
|
|
406
407
|
setSuggestions(emptyList())
|
|
407
408
|
}
|
|
408
409
|
|
|
@@ -426,18 +427,15 @@ class ScreenSearch(
|
|
|
426
427
|
private fun sendUpdate(
|
|
427
428
|
focused: Boolean = isActive && (panel?.searchView?.editText ?: searchField.editText).hasFocus(),
|
|
428
429
|
) {
|
|
429
|
-
val emitter = host.emitter() ?: return
|
|
430
430
|
eventTargets().forEach { emitter.emitSearchBarUpdated(it, text, focused, scope()) }
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
private fun sendCancel() {
|
|
434
|
-
val emitter = host.emitter() ?: return
|
|
435
434
|
eventTargets().forEach { emitter.emitSearchBarCancelPressed(it) }
|
|
436
435
|
sendUpdate()
|
|
437
436
|
}
|
|
438
437
|
|
|
439
438
|
private fun sendSubmit() {
|
|
440
|
-
val emitter = host.emitter() ?: return
|
|
441
439
|
eventTargets().forEach { emitter.emitSearchBarSubmitted(it, text, scope()) }
|
|
442
440
|
}
|
|
443
441
|
|
|
@@ -12,7 +12,6 @@ import androidx.appcompat.content.res.AppCompatResources
|
|
|
12
12
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
13
13
|
import androidx.core.view.WindowCompat
|
|
14
14
|
import androidx.core.view.WindowInsetsCompat
|
|
15
|
-
import com.facebook.react.ReactApplication
|
|
16
15
|
import dev.apexnative.navigation.options.SearchBarOptions
|
|
17
16
|
import dev.apexnative.navigation.options.params.ThemeColour
|
|
18
17
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
@@ -137,9 +136,6 @@ class StackSearchPresenter(
|
|
|
137
136
|
refresh()
|
|
138
137
|
}
|
|
139
138
|
|
|
140
|
-
override fun emitter(): EventEmitter? =
|
|
141
|
-
(activity.application as? ReactApplication)?.reactHost?.currentReactContext?.let { EventEmitter(it) }
|
|
142
|
-
|
|
143
139
|
override fun keyboard(view: View, show: Boolean) {
|
|
144
140
|
val controller = WindowCompat.getInsetsController(activity.window, view)
|
|
145
141
|
if (show) controller.show(WindowInsetsCompat.Type.ime()) else controller.hide(WindowInsetsCompat.Type.ime())
|
|
@@ -150,7 +146,8 @@ class StackSearchPresenter(
|
|
|
150
146
|
// The search of a pair of screens is its primary's: its events name that screen, which keeps the scope in
|
|
151
147
|
// its user layer, where a merge that sends the same options again doesn't take it away.
|
|
152
148
|
val owner = (screen as? OptionsScreen)?.optionsScreen ?: screen
|
|
153
|
-
|
|
149
|
+
// Its events go to the JavaScript the screen was made for, and to no other.
|
|
150
|
+
ScreenSearch(this, owner.id, EventEmitter.of(owner.jsGeneration), styleContext(), loadIcon) { scope ->
|
|
154
151
|
owner.changeChoices { it.pickScope(scope) }
|
|
155
152
|
}
|
|
156
153
|
}
|
|
@@ -5,7 +5,6 @@ import dev.apexnative.navigation.options.Options
|
|
|
5
5
|
import dev.apexnative.navigation.options.TitleComponentOptions
|
|
6
6
|
import dev.apexnative.navigation.options.TopBarBackgroundComponentOptions
|
|
7
7
|
import dev.apexnative.navigation.options.params.Text
|
|
8
|
-
import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
9
8
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.IReactView
|
|
10
9
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
11
10
|
|
|
@@ -15,7 +14,8 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
|
15
14
|
* same root, never made again; another replaces it, and the old one stops at once. A background with the same `name`
|
|
16
15
|
* and `componentId` on several screens is one root, shared; it stops when no screen of the stack names it any more. The
|
|
17
16
|
* roots go with their screen ([screenDestroyed]) or the stack ([destroyAll]). Each root renders at once, without
|
|
18
|
-
* waiting for its layout: [makeRoot]
|
|
17
|
+
* waiting for its layout: [makeRoot] makes it so (`ReactView`'s `rendersAtOnce`), and each answers by its own render
|
|
18
|
+
* when several live roots show one id (`ScreenReadiness`). Main thread only.
|
|
19
19
|
*/
|
|
20
20
|
internal class BarComponents(private val makeRoot: (componentId: String, name: String) -> IReactView) {
|
|
21
21
|
/** A root of the bar: what it renders, and its content. */
|
|
@@ -151,18 +151,10 @@ internal class BarComponents(private val makeRoot: (componentId: String, name: S
|
|
|
151
151
|
backgrounds.remove(key)?.let(::stop)
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
private fun make(key: Key): Root {
|
|
156
|
-
val root = Root(key.name, key.componentId, makeRoot(key.componentId, key.name))
|
|
157
|
-
LiveIds.add(key.componentId)
|
|
158
|
-
return root
|
|
159
|
-
}
|
|
154
|
+
private fun make(key: Key): Root = Root(key.name, key.componentId, makeRoot(key.componentId, key.name))
|
|
160
155
|
|
|
161
|
-
// Stops a root once;
|
|
162
|
-
private fun stop(root: Root)
|
|
163
|
-
root.view.destroy()
|
|
164
|
-
if (LiveIds.remove(root.componentId)) ScreenReadiness.remove(root.componentId)
|
|
165
|
-
}
|
|
156
|
+
// Stops a root once; its content takes its mark of rendering at once with it.
|
|
157
|
+
private fun stop(root: Root) = root.view.destroy()
|
|
166
158
|
|
|
167
159
|
private fun keyOf(name: Text, componentId: Text, id: Text): Key? {
|
|
168
160
|
val componentName = name.get(null)?.takeIf { it.isNotEmpty() } ?: return null
|
|
@@ -170,42 +162,4 @@ internal class BarComponents(private val makeRoot: (componentId: String, name: S
|
|
|
170
162
|
?: return null
|
|
171
163
|
return Key(componentName, rootId)
|
|
172
164
|
}
|
|
173
|
-
|
|
174
|
-
companion object {
|
|
175
|
-
/** Whether a bar root with [id] is alive now, in any stack. */
|
|
176
|
-
fun isLive(id: String): Boolean = LiveIds.has(id)
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Another root that renders at once lives with [id] (a cell of the tabs' custom row): it counts as the bar's
|
|
180
|
-
* roots do, so a root that stops clears the id's readiness only when it was the last one with it.
|
|
181
|
-
*/
|
|
182
|
-
internal fun rootMade(id: String) = LiveIds.add(id)
|
|
183
|
-
|
|
184
|
-
/** A root of [rootMade] stopped; true when no live root has [id] any more. */
|
|
185
|
-
internal fun rootStopped(id: String): Boolean = LiveIds.remove(id)
|
|
186
|
-
|
|
187
|
-
/** The JavaScript went (a reload): no id counts as held by a live root any more, as `ScreenReadiness.reset`. */
|
|
188
|
-
fun reset() = LiveIds.clear()
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// The ids of the bar roots alive now, in every stack: a root that stops clears its id's readiness only when it was
|
|
192
|
-
// the last one with it.
|
|
193
|
-
private object LiveIds {
|
|
194
|
-
private val counts = HashMap<String, Int>()
|
|
195
|
-
|
|
196
|
-
fun has(id: String): Boolean = (counts[id] ?: 0) > 0
|
|
197
|
-
|
|
198
|
-
fun clear() = counts.clear()
|
|
199
|
-
|
|
200
|
-
fun add(id: String) {
|
|
201
|
-
counts[id] = (counts[id] ?: 0) + 1
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// True when no root with the id is left.
|
|
205
|
-
fun remove(id: String): Boolean {
|
|
206
|
-
val count = (counts[id] ?: return true) - 1
|
|
207
|
-
if (count <= 0) counts.remove(id) else counts[id] = count
|
|
208
|
-
return count <= 0
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
165
|
}
|
|
@@ -41,7 +41,6 @@ import dev.apexnative.navigation.utils.ImageLoader
|
|
|
41
41
|
import dev.apexnative.navigation.utils.ImageLoadingListenerAdapter
|
|
42
42
|
import dev.apexnative.navigation.utils.TestIds
|
|
43
43
|
import dev.apexnative.navigation.utils.UiUtils
|
|
44
|
-
import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
45
44
|
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackState
|
|
46
45
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay
|
|
47
46
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.BarMotion
|
|
@@ -936,14 +935,10 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
936
935
|
return resolved.component
|
|
937
936
|
}
|
|
938
937
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
// this root's, which answers by its own render.
|
|
944
|
-
if (BarComponents.isLive(componentId)) ScreenReadiness.forgetRender(componentId)
|
|
945
|
-
return ReactView(activity, componentId, name)
|
|
946
|
-
}
|
|
938
|
+
// A component of the bar renders at once, without waiting for its layout. Another live root may show the same id
|
|
939
|
+
// (a component of the stack's options on two screens): each answers by the render of its own surface.
|
|
940
|
+
private fun makeRoot(componentId: String, name: String): IReactView =
|
|
941
|
+
ReactView(activity, componentId, name, rendersAtOnce = true)
|
|
947
942
|
|
|
948
943
|
private fun loadIcon(uri: String): Drawable? {
|
|
949
944
|
var loaded: Drawable? = null
|
|
@@ -13,8 +13,8 @@ import java.lang.ref.WeakReference
|
|
|
13
13
|
* tag) is in the screen, found once per tag —, not the horizontal ones, nor those of a modal, an overlay or another
|
|
14
14
|
* screen; a slot screen's lists are in its container too. While a list is dragged, each follower gets the drag as a
|
|
15
15
|
* translation to follow; when the drag ends, the way to settle; at every scroll event, the drags' and the inertia's,
|
|
16
|
-
* the offset. It listens to React Native's
|
|
17
|
-
*
|
|
16
|
+
* the offset. It listens to React Native's scroll views ([ScrollEvents]) only while it has followers and its screen's
|
|
17
|
+
* view. Released with the screen. Main thread only.
|
|
18
18
|
*/
|
|
19
19
|
class ScrollEventListener() {
|
|
20
20
|
/** Follows the content while it is dragged. */
|
|
@@ -105,15 +105,12 @@ class ScrollEventListener() {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
* Listens to React Native's scroll
|
|
109
|
-
*
|
|
110
|
-
* window.
|
|
108
|
+
* Listens to React Native's scroll views when followers wait for them and it doesn't yet, once it has the screen's
|
|
109
|
+
* view to tell its own lists by; asked again as the screen joins a window.
|
|
111
110
|
*/
|
|
112
111
|
internal fun attachIfNeeded() {
|
|
113
|
-
if (released || stopListening != null || registrations.isEmpty()) return
|
|
114
|
-
|
|
115
|
-
stopListening = ScrollEvents.listen(context, ::onScrollEvent)
|
|
116
|
-
if (stopListening == null) ScrollEvents.waitForDispatcher(this)
|
|
112
|
+
if (released || stopListening != null || registrations.isEmpty() || scope?.get() == null) return
|
|
113
|
+
stopListening = ScrollEvents.listen(::onScrollEvent)
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
/** The screen is destroyed: no follower hears it any more. */
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package dev.apexnative.navigation.views.animations
|
|
2
|
+
|
|
3
|
+
import android.animation.Animator
|
|
4
|
+
import android.animation.AnimatorListenerAdapter
|
|
5
|
+
import android.animation.AnimatorSet
|
|
6
|
+
import android.animation.ObjectAnimator
|
|
7
|
+
import android.view.View
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A hardware layer on a view for the time of an animator that fades it. A view whose `alpha` is below 1 and whose
|
|
11
|
+
* rendering overlaps (any `ViewGroup` by default) is drawn into an offscreen buffer at every frame; through a layer it
|
|
12
|
+
* is drawn once and the GPU only composites it. The layer is taken as the animator starts and only from
|
|
13
|
+
* `LAYER_TYPE_NONE` (a layer someone else set stays theirs), and given back as it ends or is cancelled. A pure
|
|
14
|
+
* translation or scale needs none: RenderThread draws it. The stack's default transition has its own
|
|
15
|
+
* (`StackViews.dress`). Main thread only.
|
|
16
|
+
*/
|
|
17
|
+
internal object AnimationLayer {
|
|
18
|
+
private const val ALPHA = "alpha"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* [view] draws through a hardware layer while [animator] plays, when [animator] changes the `alpha` of [view] and
|
|
22
|
+
* the view's rendering overlaps; [animator] itself, for chaining.
|
|
23
|
+
*/
|
|
24
|
+
fun during(view: View, animator: Animator): Animator {
|
|
25
|
+
if (!changesAlpha(animator, view)) return animator
|
|
26
|
+
val hold = Hold({ view.layerType }, { view.setLayerType(it, null) })
|
|
27
|
+
animator.addListener(object : AnimatorListenerAdapter() {
|
|
28
|
+
override fun onAnimationStart(animation: Animator) {
|
|
29
|
+
hold.take(changesAlpha = true, overlapping = view.hasOverlappingRendering())
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override fun onAnimationCancel(animation: Animator) = hold.release()
|
|
33
|
+
|
|
34
|
+
override fun onAnimationEnd(animation: Animator) = hold.release()
|
|
35
|
+
})
|
|
36
|
+
return animator
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Whether an animation takes a layer: it fades, its view's rendering overlaps, and the view has no layer. */
|
|
40
|
+
fun takes(changesAlpha: Boolean, overlapping: Boolean, layerType: Int): Boolean =
|
|
41
|
+
changesAlpha && overlapping && layerType == View.LAYER_TYPE_NONE
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The layer one animation took on one view, through [layerType] and [setLayerType]: taken at most once at a time,
|
|
45
|
+
* given back once, and only while it is still the hardware layer it set.
|
|
46
|
+
*/
|
|
47
|
+
class Hold(private val layerType: () -> Int, private val setLayerType: (Int) -> Unit) {
|
|
48
|
+
/** Whether the animation holds the view's layer now. */
|
|
49
|
+
var isTaken = false
|
|
50
|
+
private set
|
|
51
|
+
|
|
52
|
+
/** The animation starts: the layer, when [takes] says so. */
|
|
53
|
+
fun take(changesAlpha: Boolean, overlapping: Boolean) {
|
|
54
|
+
if (isTaken || !takes(changesAlpha, overlapping, layerType())) return
|
|
55
|
+
setLayerType(View.LAYER_TYPE_HARDWARE)
|
|
56
|
+
isTaken = true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The animation ended or stopped: the layer goes, unless someone changed it meanwhile. */
|
|
60
|
+
fun release() {
|
|
61
|
+
if (!isTaken) return
|
|
62
|
+
isTaken = false
|
|
63
|
+
if (layerType() == View.LAYER_TYPE_HARDWARE) setLayerType(View.LAYER_TYPE_NONE)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Whether [animator], or an animator in it, writes the alpha of [view].
|
|
68
|
+
private fun changesAlpha(animator: Animator, view: View): Boolean = when (animator) {
|
|
69
|
+
is AnimatorSet -> animator.childAnimations.any { changesAlpha(it, view) }
|
|
70
|
+
is ObjectAnimator -> animator.target === view && animator.values.orEmpty().any { it.propertyName == ALPHA }
|
|
71
|
+
else -> false
|
|
72
|
+
}
|
|
73
|
+
}
|
package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt
CHANGED
|
@@ -18,6 +18,18 @@ internal object ValueInterpolation {
|
|
|
18
18
|
else -> from + fraction * (to - from)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* An angle in degrees at [fraction] of the way along the shorter arc, as iOS turns a shared element: 0 → 270 turns
|
|
23
|
+
* back by 90, 170 → −170 turns on by 20; half a turn goes on. At the end of the time exactly [to].
|
|
24
|
+
*/
|
|
25
|
+
fun angle(from: Float, to: Float, fraction: Float, time: Float = fraction): Float {
|
|
26
|
+
if (time <= 0f) return from
|
|
27
|
+
if (time >= 1f) return to
|
|
28
|
+
val forward = ((to - from) % FULL_TURN + FULL_TURN) % FULL_TURN
|
|
29
|
+
val turn = if (forward > HALF_TURN) forward - FULL_TURN else forward
|
|
30
|
+
return from + fraction * turn
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
/**
|
|
22
34
|
* A color (an sRGB color int, as React Native keeps a background; none is transparent) at [fraction] of the way
|
|
23
35
|
* from [from] to [to]: L, a and b in Lab (D65), the alpha linear, the result rounded; at the end of the time
|
|
@@ -90,4 +102,6 @@ internal object ValueInterpolation {
|
|
|
90
102
|
private const val WHITE_Z = 108.883
|
|
91
103
|
private const val EPSILON = 0.008856
|
|
92
104
|
private const val KAPPA = 903.3
|
|
105
|
+
private const val FULL_TURN = 360f
|
|
106
|
+
private const val HALF_TURN = 180f
|
|
93
107
|
}
|