@apexnative/navigation 1.0.0-alpha.2 → 1.0.0-alpha.4
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 +31 -0
- package/android/CLAUDE.md +38 -9
- package/android/CORE.md +11 -4
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +2 -24
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +27 -19
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +17 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +92 -19
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +79 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +29 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +22 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +45 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +35 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +72 -16
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +164 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +136 -40
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +107 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +131 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
- package/cli/doctor/checks/app.js +61 -13
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/docs-md/architecture/transitions.md +12 -7
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +4 -2
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +20 -3
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +8 -2
- package/docs-md/screen-ui/system-ui.md +1 -1
- package/docs-md/start/installation.md +3 -1
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +13 -8
- package/ios/Contract/ANNCommandAnswer.mm +5 -20
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.mm +1 -1
- package/ios/Core/ANNErrorHandler.h +1 -1
- package/ios/Core/ANNErrorHandler.mm +3 -1
- package/ios/Core/ANNEventEmitter.h +1 -1
- package/ios/Core/ANNEventEmitter.mm +2 -2
- package/ios/Core/ANNJavaScriptCalls.h +34 -0
- package/ios/Core/ANNJavaScriptCalls.mm +61 -0
- package/ios/Core/ANNNavigationState.h +7 -3
- package/ios/Core/ANNNavigationState.mm +10 -5
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNRenderWait.h +15 -1
- package/ios/Core/ANNRenderWait.mm +119 -17
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
- package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
- package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
- package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.mm +26 -3
- package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/Image.h +11 -2
- package/ios/Options/Params/Image.mm +15 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +11 -5
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/TopBar/ANNBarButtons.h +9 -5
- package/ios/TopBar/ANNBarButtons.mm +211 -63
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
- package/ios/TopBar/ANNSearchPresenter.mm +170 -19
- package/ios/TopBar/ANNTopBarPresenter.h +5 -0
- package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
- package/ios/TurboModules/ANNTurboModule.mm +39 -21
- package/lib/module/ARCHITECTURE.md +18 -18
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/engine/commands.js +9 -7
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +13 -9
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/options.js +44 -11
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/places.js +44 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/rememberedOptions.js +1 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +22 -12
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +5 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +19 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +15 -7
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +18 -13
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +6 -3
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +18 -18
- package/src/adapters/NativeANNTurboModule.ts +5 -2
- package/src/api/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +9 -4
- package/src/api/navigation.ts +4 -1
- package/src/engine/commands.ts +19 -7
- package/src/engine/layout.ts +16 -9
- package/src/engine/options.ts +39 -11
- package/src/engine/places.ts +56 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/rememberedOptions.ts +4 -1
- package/src/engine/screens.tsx +23 -13
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/Events.ts +19 -10
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +15 -7
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/modals.ts +18 -13
- package/src/interfaces/options/system.ts +6 -3
- package/src/interfaces/options/topBar.ts +22 -9
|
@@ -15,6 +15,7 @@ import dev.apexnative.navigation.viewcontrollers.component.Readiness
|
|
|
15
15
|
import dev.apexnative.navigation.viewcontrollers.component.RenderWait
|
|
16
16
|
import dev.apexnative.navigation.viewcontrollers.parent.OptionsScreen
|
|
17
17
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.BarMotion
|
|
18
|
+
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.BarTravel
|
|
18
19
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.BarVisibility
|
|
19
20
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Custom
|
|
20
21
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Elements
|
|
@@ -51,8 +52,12 @@ private typealias Node = ViewController<*>
|
|
|
51
52
|
*
|
|
52
53
|
* Its waits for a screen to render are the core's one wait ([RenderWait]), on the screen it pushes or sets as the top;
|
|
53
54
|
* the readiness signals reach it through [deliver] (the stack posts them to the next turn of the main loop: a signal
|
|
54
|
-
* that comes inside a layout pass or a view's attach moves no view of the container there). A push
|
|
55
|
-
*
|
|
55
|
+
* that comes inside a layout pass or a view's attach moves no view of the container there). A push of a screen with
|
|
56
|
+
* React content waits for its first mount: [PUSH_WAIT_MILLIS] at most without `waitForRender`, as long as it takes
|
|
57
|
+
* with `true`, not at all with `false`; its signal reaches the wait through [afterCalls], behind the calls JavaScript
|
|
58
|
+
* made in the commit of that mount (its `mergeOptions`), so the transition starts with the options of that commit —
|
|
59
|
+
* content mounted already goes that way too. A push or a `setStackRoot` with elements waits for its targets after
|
|
60
|
+
* that, a wait like the others, within what is left of its limit without `waitForRender`: a cut or a replace during it
|
|
56
61
|
* interrupts the push, which moves no view into a layer.
|
|
57
62
|
*
|
|
58
63
|
* The transition is the screens' own when the options say so (`AnimationRules.stackIsOwn`): the display plays each
|
|
@@ -65,18 +70,31 @@ internal class StackQueue(
|
|
|
65
70
|
private val scheduler: AppearanceDispatcher.Scheduler,
|
|
66
71
|
private val host: Host,
|
|
67
72
|
private val deliver: (() -> Unit) -> Unit = { it() },
|
|
73
|
+
/**
|
|
74
|
+
* How the mount of a pushed screen reaches the push's wait: on the main thread behind every call JavaScript made
|
|
75
|
+
* before it (the stack: `PendingCalls`, on a later turn of the main loop); at once by default.
|
|
76
|
+
*/
|
|
77
|
+
private val afterCalls: (() -> Unit) -> Unit = { it() },
|
|
68
78
|
) {
|
|
69
79
|
/** What the queue tells the stack. */
|
|
70
80
|
interface Host {
|
|
71
81
|
/** The current child (the top of [shown]) changed: a transition started, or an operation ran at once. */
|
|
72
82
|
fun currentChildChanged()
|
|
73
83
|
|
|
84
|
+
/** An operation was accepted: a back gesture's preview that shows ends, the stack as it was. */
|
|
85
|
+
fun operationAccepted() = Unit
|
|
86
|
+
|
|
74
87
|
/**
|
|
75
88
|
* The current child is about to change, right before [currentChildChanged]: [motion] is the default transition
|
|
76
89
|
* the bar's row goes with, none when the change comes at once or with an animation of its own; [visibility] is
|
|
77
|
-
* how the bar shows or hides when the new top wants it otherwise
|
|
90
|
+
* how the bar shows or hides when the new top wants it otherwise; [travel]: the bar goes with the screens of an
|
|
91
|
+
* animation of its own without elements.
|
|
78
92
|
*/
|
|
79
|
-
fun barTransition(
|
|
93
|
+
fun barTransition(
|
|
94
|
+
motion: StackDisplay.BarMotion?,
|
|
95
|
+
visibility: BarVisibility,
|
|
96
|
+
travel: StackDisplay.BarTravel? = null,
|
|
97
|
+
) = Unit
|
|
80
98
|
|
|
81
99
|
/**
|
|
82
100
|
* The current child is about to change, right before [barTransition]: [move] is how the transition goes, which
|
|
@@ -95,10 +113,10 @@ internal class StackQueue(
|
|
|
95
113
|
fun operationEnded(topSettled: Boolean)
|
|
96
114
|
|
|
97
115
|
/**
|
|
98
|
-
* Whether [node]
|
|
99
|
-
*
|
|
116
|
+
* Whether [node] shows React content whose first mount its push waits for: a React screen (a root of its own or
|
|
117
|
+
* a slot), or an entry that shows one. A native screen has none: its push starts at once.
|
|
100
118
|
*/
|
|
101
|
-
fun
|
|
119
|
+
fun hasReactContent(node: Node): Boolean = false
|
|
102
120
|
|
|
103
121
|
/** One line for the log: a failure that has no command to answer. */
|
|
104
122
|
fun log(text: String) = Unit
|
|
@@ -140,6 +158,9 @@ internal class StackQueue(
|
|
|
140
158
|
private var takenOver = false
|
|
141
159
|
private var destroyed = false
|
|
142
160
|
|
|
161
|
+
/** [node]'s content is made and nothing waits for it to render as a lower screen. */
|
|
162
|
+
fun isRendered(node: Node): Boolean = node !in unrendered && node !in lowerWaits
|
|
163
|
+
|
|
143
164
|
/** The stack as JavaScript sees it, bottom to top. */
|
|
144
165
|
val entries: List<Node>
|
|
145
166
|
get() = entryList
|
|
@@ -199,7 +220,9 @@ internal class StackQueue(
|
|
|
199
220
|
|
|
200
221
|
/**
|
|
201
222
|
* A cut to [target], an entry that becomes the top: every entry above it leaves. [answer] gets [value]; a back of
|
|
202
|
-
* the user has none. [silent]: no animation and no appearance events (`popToRootOnBlur`).
|
|
223
|
+
* the user has none. [silent]: no animation and no appearance events (`popToRootOnBlur`). [gesture]: the release of
|
|
224
|
+
* a back gesture whose preview shows the pop already (`StackDisplay.finishBackGesture` ends it, the bar shows the
|
|
225
|
+
* target at once).
|
|
203
226
|
*/
|
|
204
227
|
fun cut(
|
|
205
228
|
target: Node,
|
|
@@ -208,6 +231,7 @@ internal class StackQueue(
|
|
|
208
231
|
what: String,
|
|
209
232
|
value: String?,
|
|
210
233
|
silent: Boolean = false,
|
|
234
|
+
gesture: Boolean = false,
|
|
211
235
|
) {
|
|
212
236
|
val index = entryList.indexOf(target)
|
|
213
237
|
if (destroyed || index < 0) return refuse(answer, null, componentId, emptyList())
|
|
@@ -219,7 +243,7 @@ internal class StackQueue(
|
|
|
219
243
|
(waiting as? ReplaceOp)?.let(::completeAtOnce)
|
|
220
244
|
val showsAbove = target in shownList && shownList.last() !== target
|
|
221
245
|
val op = if (showsAbove) {
|
|
222
|
-
CutOp(seq, answer, componentId, what, target, value, silent)
|
|
246
|
+
CutOp(seq, answer, componentId, what, target, value, silent, gesture)
|
|
223
247
|
} else {
|
|
224
248
|
// A cut that moves nothing more: its answer at its turn.
|
|
225
249
|
NoneOp(seq, answer, null, componentId, what, value, null)
|
|
@@ -341,6 +365,8 @@ internal class StackQueue(
|
|
|
341
365
|
// The table.
|
|
342
366
|
|
|
343
367
|
private fun enqueue(op: Op) {
|
|
368
|
+
// Anything but the release of a back gesture ends its preview first.
|
|
369
|
+
if (!(op is CutOp && op.gesture)) host.operationAccepted()
|
|
344
370
|
queued.addLast(op)
|
|
345
371
|
drain()
|
|
346
372
|
}
|
|
@@ -410,11 +436,12 @@ internal class StackQueue(
|
|
|
410
436
|
val node = op.node
|
|
411
437
|
val push = resolved(node).animations.push
|
|
412
438
|
op.awaitsTargets = awaitsTargets(push, !push.enabled.isFalse)
|
|
439
|
+
// A prepared screen rendered before its push; `false`, and a node without React content, start at once.
|
|
413
440
|
val wait = when {
|
|
414
441
|
op.prepared -> Wait.Prepared
|
|
415
442
|
push.waitForRender.isTrue -> Wait.Content
|
|
416
|
-
|
|
417
|
-
else -> Wait.
|
|
443
|
+
push.waitForRender.isFalse || !host.hasReactContent(node) -> Wait.None
|
|
444
|
+
else -> Wait.Limited
|
|
418
445
|
}
|
|
419
446
|
if (wait == Wait.None) return if (op.awaitsTargets) awaitTargets(op) else beginPush(op)
|
|
420
447
|
waiting = op
|
|
@@ -422,18 +449,37 @@ internal class StackQueue(
|
|
|
422
449
|
Wait.Prepared -> awaitPrepared(op)
|
|
423
450
|
Wait.Content -> {
|
|
424
451
|
// Then the components of the bar drawn in advance, 0.5 s at most after the content (a title or a
|
|
425
|
-
// background with its own `waitForRender: false` not).
|
|
452
|
+
// background with its own `waitForRender: false` not). React content mounted already goes behind the
|
|
453
|
+
// calls too; a native screen starts at once.
|
|
426
454
|
display.placeWaiting(node)
|
|
427
|
-
awaitContent(
|
|
455
|
+
awaitContent(
|
|
456
|
+
op,
|
|
457
|
+
node,
|
|
458
|
+
limit = null,
|
|
459
|
+
bar = RenderWait.Bar.AfterContent,
|
|
460
|
+
deliver = afterCalls,
|
|
461
|
+
deliversReady = host.hasReactContent(node),
|
|
462
|
+
)
|
|
428
463
|
}
|
|
429
464
|
else -> {
|
|
465
|
+
// The screen renders hidden under the top, its signal (or its content mounted already) behind the
|
|
466
|
+
// calls; the bar's components don't count. The 0.5 s hold for the content and the targets together.
|
|
430
467
|
display.placeWaiting(node)
|
|
431
|
-
|
|
468
|
+
op.cancelLimit = scheduler.schedule(PUSH_WAIT_MILLIS) { op.overLimit = true }
|
|
469
|
+
awaitContent(
|
|
470
|
+
op,
|
|
471
|
+
node,
|
|
472
|
+
limit = PUSH_WAIT_MILLIS,
|
|
473
|
+
bar = RenderWait.Bar.None,
|
|
474
|
+
deliver = afterCalls,
|
|
475
|
+
deliversReady = true,
|
|
476
|
+
)
|
|
432
477
|
}
|
|
433
478
|
}
|
|
434
479
|
}
|
|
435
480
|
|
|
436
481
|
private fun beginPush(op: PushOp) {
|
|
482
|
+
op.cancelLimit?.invoke()
|
|
437
483
|
val node = op.node
|
|
438
484
|
val covered = shownList.lastOrNull()
|
|
439
485
|
// The views first: a failure here leaves the lists as they were.
|
|
@@ -450,7 +496,7 @@ internal class StackQueue(
|
|
|
450
496
|
val elements = own && AnimationRules.hasElements(push)
|
|
451
497
|
// The bar's row goes with the default transition only; with an animation of its own it changes at once. The tab
|
|
452
498
|
// bar moves with its screens or by its own side.
|
|
453
|
-
tellBars(animated, own, elements, push, TabBarMotion.Move.WithPush, BarMotion.Push)
|
|
499
|
+
tellBars(animated, own, elements, push, TabBarMotion.Move.WithPush, BarMotion.Push, BarTravel.Forward)
|
|
454
500
|
host.currentChildChanged()
|
|
455
501
|
finishTakenOver(ended)
|
|
456
502
|
running += op
|
|
@@ -488,6 +534,7 @@ internal class StackQueue(
|
|
|
488
534
|
|
|
489
535
|
// Off screen or without a view: at once, without a transition; the top appears when the stack shows.
|
|
490
536
|
private fun pushAtOnce(op: PushOp) {
|
|
537
|
+
op.cancelLimit?.invoke()
|
|
491
538
|
val node = op.node
|
|
492
539
|
val covered = shownList.lastOrNull()
|
|
493
540
|
if (display.hasView) {
|
|
@@ -516,6 +563,7 @@ internal class StackQueue(
|
|
|
516
563
|
if (op.done) return
|
|
517
564
|
op.done = true
|
|
518
565
|
if (waiting === op) stopWaiting(op)
|
|
566
|
+
op.cancelLimit?.invoke()
|
|
519
567
|
queued.remove(op)
|
|
520
568
|
val node = op.node
|
|
521
569
|
entryList.remove(node)
|
|
@@ -581,7 +629,9 @@ internal class StackQueue(
|
|
|
581
629
|
val animated = !atOnce && !op.silent && display.canAnimate && !pop.enabled.isFalse
|
|
582
630
|
val own = animated && AnimationRules.stackIsOwn(pop)
|
|
583
631
|
val elements = own && AnimationRules.hasElements(pop)
|
|
584
|
-
|
|
632
|
+
// A back gesture's preview shows the target and its bar already: the bar stays as it is.
|
|
633
|
+
val gesture = op.gesture && animated && !own && leaving.size == 1 && display.hasBackPreview(top, target)
|
|
634
|
+
tellBars(animated, own, elements, pop, TabBarMotion.Move.WithPop, BarMotion.Pop, BarTravel.Back, gesture)
|
|
585
635
|
host.currentChildChanged()
|
|
586
636
|
finishTakenOver(ended)
|
|
587
637
|
if (display.hasView) target.onViewBroughtToFront()
|
|
@@ -592,6 +642,17 @@ internal class StackQueue(
|
|
|
592
642
|
// The screens between the top and the target leave without animation.
|
|
593
643
|
leaving.drop(1).forEach(display::hide)
|
|
594
644
|
if (!animated) return finishCut(op)
|
|
645
|
+
if (gesture) {
|
|
646
|
+
op.animating += top
|
|
647
|
+
op.animating += target
|
|
648
|
+
display.finishBackGesture(
|
|
649
|
+
top,
|
|
650
|
+
target,
|
|
651
|
+
{ step(op) { screenDone(op, top) } },
|
|
652
|
+
{ step(op) { screenDone(op, target) } },
|
|
653
|
+
)
|
|
654
|
+
return
|
|
655
|
+
}
|
|
595
656
|
// Its own: no wait on a pop; the leaving screen starts from where it is.
|
|
596
657
|
if (own) return animate(op, backMoves(top, target, pop, elements, moving))
|
|
597
658
|
animate(op, listOf(Move(top, Motion.PopOut, true), Move(target, Motion.PopReveal, moving)))
|
|
@@ -657,7 +718,7 @@ internal class StackQueue(
|
|
|
657
718
|
val elements = own && !op.asPush && AnimationRules.hasElements(played)
|
|
658
719
|
val motion = if (op.asPush) BarMotion.Push else BarMotion.FadeThrough
|
|
659
720
|
val system = if (op.asPush) TabBarMotion.Move.WithPush else TabBarMotion.Move.Slide
|
|
660
|
-
tellBars(animated, own, elements, played, system, motion)
|
|
721
|
+
tellBars(animated, own, elements, played, system, motion, BarTravel.Forward)
|
|
661
722
|
host.currentChildChanged()
|
|
662
723
|
finishTakenOver(ended)
|
|
663
724
|
op.owned += moved
|
|
@@ -769,8 +830,16 @@ internal class StackQueue(
|
|
|
769
830
|
|
|
770
831
|
// The one wait (RenderWait) for [screen], the node the operation brings: at [limit] the content not ready counts
|
|
771
832
|
// as mounted; without one, `waitForRender: true` waits as long as it takes, except for content the readiness store
|
|
772
|
-
// knows nothing of after 1 s, which counts as ready (the same rule as iOS).
|
|
773
|
-
|
|
833
|
+
// knows nothing of after 1 s, which counts as ready (the same rule as iOS). A push's signals come through
|
|
834
|
+
// [afterCalls] ([deliver]); with [deliversReady], so does content ready at the start.
|
|
835
|
+
private fun awaitContent(
|
|
836
|
+
op: Op,
|
|
837
|
+
screen: Node,
|
|
838
|
+
limit: Long?,
|
|
839
|
+
bar: RenderWait.Bar,
|
|
840
|
+
deliver: (() -> Unit) -> Unit = this.deliver,
|
|
841
|
+
deliversReady: Boolean = false,
|
|
842
|
+
) {
|
|
774
843
|
val wait = RenderWait(
|
|
775
844
|
readiness = readiness,
|
|
776
845
|
scheduler = scheduler,
|
|
@@ -778,6 +847,7 @@ internal class StackQueue(
|
|
|
778
847
|
bar = bar,
|
|
779
848
|
countsUnknownContent = true,
|
|
780
849
|
deliver = deliver,
|
|
850
|
+
deliversReadyContent = deliversReady,
|
|
781
851
|
step = { work -> step(op) { work() } },
|
|
782
852
|
onEnd = { endWait(op) },
|
|
783
853
|
)
|
|
@@ -798,15 +868,32 @@ internal class StackQueue(
|
|
|
798
868
|
step(op) { wait.retake(RenderWait.waitedScreens(node)) }
|
|
799
869
|
}
|
|
800
870
|
|
|
871
|
+
// The prepared screen's slot mounts (or the display's limit passes), then its transition starts behind the calls
|
|
872
|
+
// JavaScript made before ([afterCalls]): a commit that came after the push (its JavaScript's own limit) brings its
|
|
873
|
+
// `mergeOptions` first. The same 0.5 s from the start hold for both: the transition starts then anyway.
|
|
801
874
|
private fun awaitPrepared(op: PushOp) {
|
|
802
875
|
val token = Any()
|
|
803
876
|
op.waitToken = token
|
|
804
877
|
val node = op.node
|
|
878
|
+
val still = { !destroyed && !op.done && op.waitToken === token && waiting === op && op.isCurrent() }
|
|
879
|
+
var mounted = false
|
|
880
|
+
val markMounted = {
|
|
881
|
+
if (!mounted && !readiness.isReady(node)) readiness.markReady(node)
|
|
882
|
+
mounted = true
|
|
883
|
+
}
|
|
884
|
+
val start = { step(op) { if (still()) endWait(op) } }
|
|
885
|
+
op.cancelCap = scheduler.schedule(PUSH_WAIT_MILLIS) {
|
|
886
|
+
step(op) {
|
|
887
|
+
if (!still()) return@step
|
|
888
|
+
markMounted()
|
|
889
|
+
endWait(op)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
805
892
|
display.awaitPrepared(
|
|
806
893
|
node,
|
|
807
|
-
waiting =
|
|
808
|
-
mounted = { step(op) {
|
|
809
|
-
start = {
|
|
894
|
+
waiting = still,
|
|
895
|
+
mounted = { step(op) { markMounted() } },
|
|
896
|
+
start = { afterCalls(start) },
|
|
810
897
|
)
|
|
811
898
|
}
|
|
812
899
|
|
|
@@ -835,7 +922,8 @@ internal class StackQueue(
|
|
|
835
922
|
val token = Any()
|
|
836
923
|
op.waitToken = token
|
|
837
924
|
val still = { !destroyed && !op.done && op.waitToken === token && waiting === op && op.isCurrent() }
|
|
838
|
-
// Without `waitForRender` a `setStackRoot`
|
|
925
|
+
// Without `waitForRender` the 0.5 s of a `setStackRoot` and of a push of React content hold for the content
|
|
926
|
+
// and the targets together.
|
|
839
927
|
val from = currentChild
|
|
840
928
|
op.stopTargets = display.awaitTargets(animation, from, to, { still() && !op.overLimit }) {
|
|
841
929
|
step(op) { if (still()) endWait(op) }
|
|
@@ -844,6 +932,8 @@ internal class StackQueue(
|
|
|
844
932
|
|
|
845
933
|
private fun stopWaiting(op: Op) {
|
|
846
934
|
if (waiting === op) waiting = null
|
|
935
|
+
op.cancelCap?.invoke()
|
|
936
|
+
op.cancelCap = null
|
|
847
937
|
op.waitToken = null
|
|
848
938
|
op.wait?.cancel()
|
|
849
939
|
op.wait = null
|
|
@@ -1081,9 +1171,10 @@ internal class StackQueue(
|
|
|
1081
1171
|
private fun awaitsTargets(animation: StackAnimationOptions, enabled: Boolean): Boolean = enabled &&
|
|
1082
1172
|
display.canAnimate && !animation.waitForRender.isFalse && AnimationRules.hasElements(animation)
|
|
1083
1173
|
|
|
1084
|
-
// The bars with a change of the current child. The row goes with the default transition
|
|
1085
|
-
//
|
|
1086
|
-
// top wants it at once without animation; with
|
|
1174
|
+
// The bars with a change of the current child. The row goes with the default transition; with an animation of its
|
|
1175
|
+
// own the top bar goes with the screens ([travel]). The tab bar rides with the system's [system] move, slides with an
|
|
1176
|
+
// animation of its own or a side of its own, and is where the new top wants it at once without animation; with
|
|
1177
|
+
// elements every bar changes at once.
|
|
1087
1178
|
private fun tellBars(
|
|
1088
1179
|
animated: Boolean,
|
|
1089
1180
|
own: Boolean,
|
|
@@ -1091,6 +1182,9 @@ internal class StackQueue(
|
|
|
1091
1182
|
animation: StackAnimationOptions,
|
|
1092
1183
|
system: TabBarMotion.Move,
|
|
1093
1184
|
row: BarMotion,
|
|
1185
|
+
travel: BarTravel,
|
|
1186
|
+
// The top bar shows the new top already (a back gesture's preview): it changes at once, the tab bar still moves.
|
|
1187
|
+
barAtOnce: Boolean = false,
|
|
1094
1188
|
) {
|
|
1095
1189
|
val tabs = animation.bottomTabs
|
|
1096
1190
|
val tabsSide = AnimationRules.barSideGiven(tabs.enter) || AnimationRules.barSideGiven(tabs.exit)
|
|
@@ -1100,7 +1194,12 @@ internal class StackQueue(
|
|
|
1100
1194
|
else -> system
|
|
1101
1195
|
}
|
|
1102
1196
|
host.tabBarTransition(move, tabs.takeIf { move == TabBarMotion.Move.Slide })
|
|
1103
|
-
|
|
1197
|
+
val barMoves = animated && !barAtOnce
|
|
1198
|
+
host.barTransition(
|
|
1199
|
+
if (barMoves && !own) row else null,
|
|
1200
|
+
barVisibility(barMoves && !elements, animation.topBar),
|
|
1201
|
+
travel.takeIf { barMoves && own && !elements },
|
|
1202
|
+
)
|
|
1104
1203
|
}
|
|
1105
1204
|
|
|
1106
1205
|
private fun barVisibility(animated: Boolean, side: TopBarEnterExitAnimationOptions): BarVisibility =
|
|
@@ -1179,9 +1278,12 @@ internal class StackQueue(
|
|
|
1179
1278
|
// It waits for its elements' targets after its content; what stops that wait.
|
|
1180
1279
|
var awaitsTargets = false
|
|
1181
1280
|
var stopTargets: (() -> Unit)? = null
|
|
1182
|
-
// The limit of a `setStackRoot` without `waitForRender` passed: its targets get nothing of it any
|
|
1281
|
+
// The limit of a `setStackRoot` or a push without `waitForRender` passed: its targets get nothing of it any
|
|
1282
|
+
// more.
|
|
1183
1283
|
var overLimit = false
|
|
1184
1284
|
var cancelLimit: (() -> Unit)? = null
|
|
1285
|
+
// The limit of a prepared push's wait, which ends with the wait.
|
|
1286
|
+
var cancelCap: (() -> Unit)? = null
|
|
1185
1287
|
// Its screen came by an animation of its own: it stays at that animation's end values.
|
|
1186
1288
|
var keepsEnd = false
|
|
1187
1289
|
// The listener of a module heard its answer: it hears one only.
|
|
@@ -1209,6 +1311,7 @@ internal class StackQueue(
|
|
|
1209
1311
|
val target: Node,
|
|
1210
1312
|
val value: String?,
|
|
1211
1313
|
val silent: Boolean,
|
|
1314
|
+
val gesture: Boolean,
|
|
1212
1315
|
) : Op(seq, answer, null, componentId, what) {
|
|
1213
1316
|
var leaving: List<Node> = emptyList()
|
|
1214
1317
|
}
|
|
@@ -1255,20 +1358,13 @@ internal class StackQueue(
|
|
|
1255
1358
|
/** How long a lower screen stays placed in the container to make its content, at most. */
|
|
1256
1359
|
const val LOWER_SCREEN_MILLIS = 500L
|
|
1257
1360
|
|
|
1258
|
-
/**
|
|
1259
|
-
|
|
1361
|
+
/**
|
|
1362
|
+
* How long a push waits for its screen's first mount without `waitForRender`, and a prepared push for its slot
|
|
1363
|
+
* and the calls before it.
|
|
1364
|
+
*/
|
|
1365
|
+
const val PUSH_WAIT_MILLIS = 500L
|
|
1260
1366
|
|
|
1261
1367
|
// The alpha an arriving screen shows with: its own at rest, never the 0 of its wait.
|
|
1262
1368
|
private const val ARRIVING_ALPHA = 1f
|
|
1263
1369
|
}
|
|
1264
1370
|
}
|
|
1265
|
-
|
|
1266
|
-
/**
|
|
1267
|
-
* The internal switches of the stack. [preparesOwnRootPushes]: the push of a React screen with a root of its own starts
|
|
1268
|
-
* its surface hidden in the stack at its size and insets, and starts its transition once the content rendered, 0.5 s at
|
|
1269
|
-
* most. Off by default («б»: the push starts at once); it is measured at the end on a phone.
|
|
1270
|
-
*/
|
|
1271
|
-
internal object StackSwitches {
|
|
1272
|
-
@Volatile
|
|
1273
|
-
var preparesOwnRootPushes = false
|
|
1274
|
-
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt
CHANGED
|
@@ -16,11 +16,15 @@ import dev.apexnative.navigation.views.animations.SystemTransition
|
|
|
16
16
|
* system's transition between activities (`SystemTransition`: 96 dp along the axis in 450 ms on
|
|
17
17
|
* `fast_out_extra_slow_in`, mirrored right to left), `setStackRoot` is Material's fade through (`FadeThrough`). What
|
|
18
18
|
* leaves starts from where it is; what comes in on a push is hidden explicitly until it fades in, as a delay doesn't
|
|
19
|
-
* apply an animator's first value.
|
|
19
|
+
* apply an animator's first value. The system's windows also extend their edge by the distance they move
|
|
20
|
+
* ([extendsStart], `StackLayout.extendEdge`). Main thread only.
|
|
20
21
|
*/
|
|
21
22
|
internal object StackTransitions {
|
|
22
|
-
/**
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The animator of [view] for [motion]; [fromCurrent] for a screen taken over from a transition still running.
|
|
25
|
+
* [onSlide] runs at each frame of its slide.
|
|
26
|
+
*/
|
|
27
|
+
fun animator(view: View, motion: Motion, fromCurrent: Boolean, onSlide: () -> Unit = {}): Animator {
|
|
24
28
|
val distance = SystemTransition.DISTANCE_DP * view.resources.displayMetrics.density
|
|
25
29
|
// Forward is to the end of the line: right to left, the screens move the other way. The stack's view inherits
|
|
26
30
|
// the app's direction, which `layout.direction` sets on the activity's content (`AppDirection`).
|
|
@@ -30,24 +34,37 @@ internal object StackTransitions {
|
|
|
30
34
|
view.translationX = forward
|
|
31
35
|
view.alpha = 0f
|
|
32
36
|
together(
|
|
33
|
-
slide(view, 0f),
|
|
37
|
+
slide(view, 0f, onSlide),
|
|
34
38
|
fade(view, 1f, SystemTransition.PUSH_FADE_DELAY),
|
|
35
39
|
)
|
|
36
40
|
}
|
|
37
|
-
Motion.PushCover -> slide(view, -forward)
|
|
41
|
+
Motion.PushCover -> slide(view, -forward, onSlide)
|
|
38
42
|
Motion.PopOut -> together(
|
|
39
|
-
slide(view, forward),
|
|
43
|
+
slide(view, forward, onSlide),
|
|
40
44
|
fade(view, 0f, SystemTransition.POP_FADE_DELAY),
|
|
41
45
|
)
|
|
42
46
|
Motion.PopReveal -> {
|
|
43
47
|
if (!fromCurrent) view.translationX = -forward
|
|
44
|
-
if (view.alpha < 1f) together(slide(view, 0f), fade(view, 1f, 0L)) else slide(view, 0f)
|
|
48
|
+
if (view.alpha < 1f) together(slide(view, 0f, onSlide), fade(view, 1f, 0L)) else slide(view, 0f, onSlide)
|
|
45
49
|
}
|
|
46
50
|
Motion.FadeIn -> FadeThrough.enter(view)
|
|
47
51
|
Motion.FadeOut -> FadeThrough.exit(view)
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Whether a screen moving by [motion] extends its start edge (the side it moves away from on a push in and a pop
|
|
57
|
+
* out), its end edge (a push cover, a pop reveal), or none (a fade through: null), as the system's windows do.
|
|
58
|
+
*/
|
|
59
|
+
fun extendsStart(motion: Motion): Boolean? = when (motion) {
|
|
60
|
+
Motion.PushIn, Motion.PopOut -> true
|
|
61
|
+
Motion.PushCover, Motion.PopReveal -> false
|
|
62
|
+
Motion.FadeIn, Motion.FadeOut -> null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Whether [motion] animates the screen's alpha: it draws through a hardware layer meanwhile. */
|
|
66
|
+
fun fades(motion: Motion): Boolean = motion != Motion.PushCover && motion != Motion.PopReveal
|
|
67
|
+
|
|
51
68
|
/**
|
|
52
69
|
* The fade of a screen around elements: in from 0 to 1, out from where it is to 0, linearly in
|
|
53
70
|
* [AnimationRules.SCREEN_FADE]; none for [Fade.None].
|
|
@@ -65,10 +82,11 @@ internal object StackTransitions {
|
|
|
65
82
|
}
|
|
66
83
|
}
|
|
67
84
|
|
|
68
|
-
private fun slide(view: View, to: Float): Animator =
|
|
85
|
+
private fun slide(view: View, to: Float, onFrame: () -> Unit): Animator =
|
|
69
86
|
ObjectAnimator.ofFloat(view, View.TRANSLATION_X, view.translationX, to).apply {
|
|
70
87
|
duration = SystemTransition.DURATION
|
|
71
88
|
interpolator = SystemTransition.CURVE
|
|
89
|
+
addUpdateListener { onFrame() }
|
|
72
90
|
}
|
|
73
91
|
|
|
74
92
|
private fun fade(view: View, to: Float, delay: Long): Animator =
|
|
@@ -9,6 +9,7 @@ import androidx.core.view.WindowInsetsCompat
|
|
|
9
9
|
import androidx.lifecycle.Lifecycle
|
|
10
10
|
import androidx.lifecycle.LifecycleOwner
|
|
11
11
|
import dev.apexnative.navigation.options.LayoutAnimation
|
|
12
|
+
import dev.apexnative.navigation.utils.UiThread
|
|
12
13
|
import dev.apexnative.navigation.utils.whenContentMounted
|
|
13
14
|
import dev.apexnative.navigation.viewcontrollers.component.ComponentViewController
|
|
14
15
|
import dev.apexnative.navigation.viewcontrollers.externalcomponent.ExternalComponentViewController
|
|
@@ -16,6 +17,7 @@ import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Motion
|
|
|
16
17
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
17
18
|
import dev.apexnative.navigation.views.animations.AnimationRules
|
|
18
19
|
import dev.apexnative.navigation.views.animations.SideAnimator
|
|
20
|
+
import dev.apexnative.navigation.views.animations.SystemTransition
|
|
19
21
|
import dev.apexnative.navigation.views.element.ElementTransition
|
|
20
22
|
import dev.apexnative.navigation.views.element.TargetsWait
|
|
21
23
|
import dev.apexnative.navigation.views.stack.StackLayout
|
|
@@ -29,6 +31,11 @@ import dev.apexnative.navigation.views.stack.StackLayout
|
|
|
29
31
|
*/
|
|
30
32
|
internal class StackViews(private val stack: StackController) : StackDisplay {
|
|
31
33
|
private val animators = HashMap<ViewController<*>, Animator>()
|
|
34
|
+
// The preview of the back gesture that runs or ends now, and its two screens.
|
|
35
|
+
private var preview: BackPreview? = null
|
|
36
|
+
private var previewScreens: Pair<ViewController<*>, ViewController<*>>? = null
|
|
37
|
+
// The ends of the two screens of a released gesture whose preview plays its end.
|
|
38
|
+
private var committing: Pair<() -> Unit, () -> Unit>? = null
|
|
32
39
|
// What goes back when a screen's animator ends or stops: the elements its transition carries.
|
|
33
40
|
private val cleanups = HashMap<ViewController<*>, () -> Unit>()
|
|
34
41
|
|
|
@@ -110,11 +117,16 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
|
|
|
110
117
|
override fun animate(screen: ViewController<*>, motion: Motion, fromCurrent: Boolean, done: () -> Unit) {
|
|
111
118
|
stop(screen)
|
|
112
119
|
val view = screen.peekView() ?: return done()
|
|
113
|
-
val
|
|
120
|
+
val layout = layout
|
|
121
|
+
val animator = StackTransitions.animator(view, motion, fromCurrent) { layout?.edgesMoved() }
|
|
122
|
+
cleanups[screen] = dress(view, motion, layout)
|
|
114
123
|
animator.addListener(object : AnimatorListenerAdapter() {
|
|
115
124
|
// Ended or cancelled by anything else than [stop]: the screen's part is done.
|
|
116
125
|
override fun onAnimationEnd(animation: Animator) {
|
|
117
|
-
if (animators[screen] === animator)
|
|
126
|
+
if (animators[screen] === animator) {
|
|
127
|
+
animators.remove(screen)
|
|
128
|
+
cleanups.remove(screen)?.invoke()
|
|
129
|
+
}
|
|
118
130
|
done()
|
|
119
131
|
}
|
|
120
132
|
})
|
|
@@ -122,6 +134,21 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
|
|
|
122
134
|
animator.start()
|
|
123
135
|
}
|
|
124
136
|
|
|
137
|
+
// For the default transition [view] moves as the system's windows do: its edge extended by the distance it moves,
|
|
138
|
+
// and its fade drawn through a hardware layer, so the whole screen isn't drawn again at every frame of it. What
|
|
139
|
+
// undoes both.
|
|
140
|
+
private fun dress(view: View, motion: Motion, layout: StackLayout?): () -> Unit {
|
|
141
|
+
StackTransitions.extendsStart(motion)?.let { start ->
|
|
142
|
+
layout?.extendEdge(view, start, (SystemTransition.DISTANCE_DP * view.resources.displayMetrics.density).toInt())
|
|
143
|
+
}
|
|
144
|
+
val layered = StackTransitions.fades(motion) && view.layerType == View.LAYER_TYPE_NONE
|
|
145
|
+
if (layered) view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
|
146
|
+
return {
|
|
147
|
+
layout?.dropEdge(view)
|
|
148
|
+
if (layered && view.layerType == View.LAYER_TYPE_HARDWARE) view.setLayerType(View.LAYER_TYPE_NONE, null)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
125
152
|
// The elements go into a layer over the screens, and back as the screen's animator ends or stops. A shared element
|
|
126
153
|
// without a duration takes the screen's fade: its own single `alpha`, else 300 ms.
|
|
127
154
|
override fun animateCustom(screen: ViewController<*>, custom: StackDisplay.Custom, done: () -> Unit) {
|
|
@@ -167,7 +194,83 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
|
|
|
167
194
|
done: () -> Unit,
|
|
168
195
|
): () -> Unit = TargetsWait.start(animation, from, to, waiting, done)
|
|
169
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Starts the preview of a back gesture over [top], [target] under it (`BackPreview`): none without the stack's view
|
|
199
|
+
* or a window. [onReveal] shows [target] and its bar as the preview's picture is there.
|
|
200
|
+
*/
|
|
201
|
+
fun startBackPreview(
|
|
202
|
+
top: ViewController<*>,
|
|
203
|
+
target: ViewController<*>,
|
|
204
|
+
fromLeft: Boolean,
|
|
205
|
+
touchY: Float,
|
|
206
|
+
onReveal: () -> Unit,
|
|
207
|
+
): BackPreview? {
|
|
208
|
+
val bottomRoom = top.getBottomInset()
|
|
209
|
+
endPreview()
|
|
210
|
+
val layout = layout ?: return null
|
|
211
|
+
val window = stack.activity.window ?: return null
|
|
212
|
+
val topView = top.peekView() ?: return null
|
|
213
|
+
val targetView = target.peekView() ?: return null
|
|
214
|
+
val started = BackPreview(layout, topView, targetView, fromLeft, touchY, bottomRoom, onReveal)
|
|
215
|
+
preview = started
|
|
216
|
+
previewScreens = top to target
|
|
217
|
+
started.start(window)
|
|
218
|
+
return started
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** The preview of the back gesture went: cancelled and put back, or never shown. */
|
|
222
|
+
fun previewEnded(ended: BackPreview) {
|
|
223
|
+
if (preview !== ended) return
|
|
224
|
+
preview = null
|
|
225
|
+
previewScreens = null
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
override fun hasBackPreview(top: ViewController<*>, target: ViewController<*>): Boolean =
|
|
229
|
+
preview?.isReady == true && previewScreens == top to target
|
|
230
|
+
|
|
231
|
+
override fun finishBackGesture(
|
|
232
|
+
top: ViewController<*>,
|
|
233
|
+
target: ViewController<*>,
|
|
234
|
+
topDone: () -> Unit,
|
|
235
|
+
targetDone: () -> Unit,
|
|
236
|
+
) {
|
|
237
|
+
val running = preview?.takeIf { hasBackPreview(top, target) } ?: run {
|
|
238
|
+
topDone()
|
|
239
|
+
targetDone()
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
committing = topDone to targetDone
|
|
243
|
+
running.commit {
|
|
244
|
+
committing = null
|
|
245
|
+
previewEnded(running)
|
|
246
|
+
topDone()
|
|
247
|
+
targetDone()
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private fun endPreview() {
|
|
252
|
+
val ended = preview ?: return
|
|
253
|
+
preview = null
|
|
254
|
+
previewScreens = null
|
|
255
|
+
committing = null
|
|
256
|
+
ended.endAtOnce()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// A later transition takes one screen of a released gesture's preview: the preview ends where it is, the popped
|
|
260
|
+
// screen stays out of sight until the pop takes it away, and the other screen's part ends on the next turn.
|
|
261
|
+
private fun stopPreview(screen: ViewController<*>) {
|
|
262
|
+
val (top, target) = previewScreens ?: return
|
|
263
|
+
if (screen !== top && screen !== target) return
|
|
264
|
+
val dones = committing
|
|
265
|
+
endPreview()
|
|
266
|
+
if (dones == null) return
|
|
267
|
+
top.peekView()?.alpha = 0f
|
|
268
|
+
val other = if (screen === top) dones.second else dones.first
|
|
269
|
+
UiThread.post { other() }
|
|
270
|
+
}
|
|
271
|
+
|
|
170
272
|
override fun stop(screen: ViewController<*>) {
|
|
273
|
+
stopPreview(screen)
|
|
171
274
|
val animator = animators.remove(screen)
|
|
172
275
|
if (animator != null) {
|
|
173
276
|
animator.removeAllListeners()
|
|
@@ -185,8 +288,8 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
|
|
|
185
288
|
screen.whenContentMounted(
|
|
186
289
|
waiting = waiting,
|
|
187
290
|
mounted = {
|
|
188
|
-
// The frame of the mount: the screen shows transparent and lays out
|
|
189
|
-
//
|
|
291
|
+
// The frame of the mount: the screen shows transparent and lays out. The pre-draw of that frame hands
|
|
292
|
+
// the start to the queue, whose transition begins behind the calls JavaScript made before.
|
|
190
293
|
screen.peekView()?.let {
|
|
191
294
|
it.alpha = 0f
|
|
192
295
|
it.visibility = View.VISIBLE
|