@apexnative/navigation 1.0.0-alpha.3 → 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 +9 -0
- package/android/CLAUDE.md +17 -7
- package/android/CORE.md +8 -3
- package/android/RECIPES.md +9 -3
- 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/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/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +16 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- 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/modal/ModalCoordinator.kt +5 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +6 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +8 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -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 +58 -15
- 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 +16 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +2 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +85 -29
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +2 -2
- 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 +10 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +56 -21
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- 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 +6 -7
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/help/troubleshooting.md +20 -3
- package/docs-md/index.json +4 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +2 -2
- 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/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 +5 -2
- 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/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 +1 -1
- 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 +5 -2
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/topBar.ts +22 -9
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt
CHANGED
|
@@ -240,8 +240,9 @@ internal class RootCoordinator(
|
|
|
240
240
|
/**
|
|
241
241
|
* The activity's layers go: the `setRoot` on its way ends, then the root it replaced, the current root and its root
|
|
242
242
|
* are destroyed, as at any destroy (their events reach only their own JavaScript). [answerPending]: the activity
|
|
243
|
-
* went
|
|
244
|
-
* (
|
|
243
|
+
* went; a `setRoot` whose root still waited resolves with `''` (JavaScript sets the root again in the next
|
|
244
|
+
* activity), one whose root showed (its transition ran, or the modals and overlays of its layout were showing)
|
|
245
|
+
* resolves with the root's id. Otherwise (a reload) it gets no answer.
|
|
245
246
|
*/
|
|
246
247
|
fun destroyAll(answerPending: Boolean) {
|
|
247
248
|
val pending = pending
|
|
@@ -256,7 +257,9 @@ internal class RootCoordinator(
|
|
|
256
257
|
modals.replacedRootGone()
|
|
257
258
|
pending?.let(::releaseState)
|
|
258
259
|
if (pending != null && pending.phase == Phase.Waiting) firstRoot.rootDestroyed()
|
|
259
|
-
if (answerPending
|
|
260
|
+
if (!answerPending || pending == null) return
|
|
261
|
+
// An empty id tells JavaScript the root didn't show: its start waits for the next activity and sets it there.
|
|
262
|
+
pending.answer.resolve(if (pending.phase == Phase.Waiting) "" else pending.root.id)
|
|
260
263
|
}
|
|
261
264
|
|
|
262
265
|
// Waits for the screens that show when the root shows, then runs its transition. Without `waitForRender` one limit
|
|
@@ -124,18 +124,18 @@ internal class OverlayCoordinator(
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
|
-
* `dismissOverlay`: the
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* overlay`.
|
|
127
|
+
* `dismissOverlay`: the overlay [componentId] is in — its root or any screen inside it — goes with every overlay of
|
|
128
|
+
* the same root id: one that waits is cancelled, one that shows goes with its events (both, when a replacement of
|
|
129
|
+
* a shown one waits). [answer] resolves with the overlay's root id, as on iOS. An id no node has: `not shown`; a
|
|
130
|
+
* node in no overlay: `not in an overlay`.
|
|
131
131
|
*/
|
|
132
132
|
fun dismiss(componentId: String, answer: CommandAnswer) {
|
|
133
|
-
|
|
134
|
-
val
|
|
135
|
-
|
|
133
|
+
val node = host.find(componentId) ?: return answer.reject(ErrorTexts.screenNotShown(componentId))
|
|
134
|
+
val rootId = recordOf(node)?.rootId ?: return answer.reject(ErrorTexts.notInOverlay(componentId))
|
|
135
|
+
val matches = records.filter { it.rootId == rootId }
|
|
136
136
|
matches.filter { it.state == OverlayRecord.State.Waiting }.forEach(::cancel)
|
|
137
137
|
val shown = matches.filter { it.state == OverlayRecord.State.Shown }
|
|
138
|
-
if (shown.isEmpty()) return answer.resolve(
|
|
138
|
+
if (shown.isEmpty()) return answer.resolve(rootId)
|
|
139
139
|
shown.dropLast(1).forEach { remove(it, null) }
|
|
140
140
|
remove(shown.last(), answer)
|
|
141
141
|
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt
CHANGED
|
@@ -41,11 +41,18 @@ interface KeepsStatusBarRoom
|
|
|
41
41
|
/** A node that shows screens in its columns (the split view): `showInColumn` and `setColumnShown` go to it. */
|
|
42
42
|
interface ShowsColumns {
|
|
43
43
|
/**
|
|
44
|
-
* Shows [screen] in
|
|
45
|
-
*
|
|
46
|
-
* (
|
|
44
|
+
* Shows [screen] in [column], in place of what it shows; [listener] once it shows, or with the error reason, which
|
|
45
|
+
* names [from], the screen the command comes from. [goesInStack] says whether [screen]'s layout is what a stack
|
|
46
|
+
* takes (a screen, a native screen, a pair), not a container. False when the node doesn't take it now (its compact
|
|
47
|
+
* layout shows): the core pushes the screen over [from] instead.
|
|
47
48
|
*/
|
|
48
|
-
fun showScreen(
|
|
49
|
+
fun showScreen(
|
|
50
|
+
screen: ViewController<*>,
|
|
51
|
+
column: String,
|
|
52
|
+
goesInStack: Boolean,
|
|
53
|
+
from: ViewController<*>,
|
|
54
|
+
listener: CommandListener,
|
|
55
|
+
): Boolean
|
|
49
56
|
|
|
50
57
|
/** Shows or hides [column]; [done] once the columns have stopped moving. */
|
|
51
58
|
fun setColumnShown(column: String, shown: Boolean, done: Runnable)
|
|
@@ -131,7 +131,7 @@ internal class SideMenuController(
|
|
|
131
131
|
|
|
132
132
|
// `mergeOptions` with the side menu's id: its own options, then what a merge does, then the layouts around.
|
|
133
133
|
override fun mergeOptions(options: Options) {
|
|
134
|
-
|
|
134
|
+
mergeOwnOptions(options)
|
|
135
135
|
applyMerge(options)
|
|
136
136
|
performOnParentController { it.mergeChildOptions(SideMenuRules.withoutSideMenu(options), this) }
|
|
137
137
|
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package dev.apexnative.navigation.viewcontrollers.splitview
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
4
6
|
import android.view.WindowInsets
|
|
5
7
|
import androidx.core.graphics.Insets
|
|
6
8
|
import androidx.core.view.WindowInsetsCompat
|
|
@@ -14,6 +16,27 @@ class SplitPanes(context: Context) : SlidingPaneLayout(context) {
|
|
|
14
16
|
/** The panes are side by side. */
|
|
15
17
|
var isSideBySide = false
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Puts [next] in the place of the pane [old], with the structure and the params [old] had. The library wraps the
|
|
21
|
+
* second view it is given in a view of its own that holds back touches (`addView` with one child there), and its
|
|
22
|
+
* `removeView` takes that wrapper away: a plain remove and add would wrap a new primary pane, or give a new
|
|
23
|
+
* secondary one the wrapper's params, and lose its width and weight. So the secondary pane's content changes
|
|
24
|
+
* inside the wrapper, and a new primary pane goes in before the old one leaves, while two children are there.
|
|
25
|
+
*/
|
|
26
|
+
internal fun replacePane(old: View, next: View) {
|
|
27
|
+
val holder = old.parent as? ViewGroup ?: return
|
|
28
|
+
val params = old.layoutParams
|
|
29
|
+
if (holder === this) {
|
|
30
|
+
// A copy: the width that `splitView.width` changes later is the new pane's own.
|
|
31
|
+
addView(next, indexOfChild(old), if (params is LayoutParams) LayoutParams(params) else params)
|
|
32
|
+
removeView(old)
|
|
33
|
+
} else {
|
|
34
|
+
val index = holder.indexOfChild(old)
|
|
35
|
+
holder.removeView(old)
|
|
36
|
+
holder.addView(next, index, params)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
17
40
|
override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets {
|
|
18
41
|
if (!isSideBySide) return super.dispatchApplyWindowInsets(insets)
|
|
19
42
|
val own = onApplyWindowInsets(insets)
|
|
@@ -15,6 +15,7 @@ import dev.apexnative.navigation.options.SplitViewGroupOptions
|
|
|
15
15
|
import dev.apexnative.navigation.options.SplitViewOptions
|
|
16
16
|
import dev.apexnative.navigation.react.CommandListener
|
|
17
17
|
import dev.apexnative.navigation.react.CommandListenerAdapter
|
|
18
|
+
import dev.apexnative.navigation.react.ErrorTexts
|
|
18
19
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
19
20
|
import dev.apexnative.navigation.system.SystemUiOwner
|
|
20
21
|
import dev.apexnative.navigation.utils.TestIds
|
|
@@ -24,6 +25,7 @@ import dev.apexnative.navigation.viewcontrollers.child.ChildControllersRegistry
|
|
|
24
25
|
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackState
|
|
25
26
|
import dev.apexnative.navigation.viewcontrollers.navigator.Navigator
|
|
26
27
|
import dev.apexnative.navigation.viewcontrollers.parent.ChildOffsetFromRest
|
|
28
|
+
import dev.apexnative.navigation.viewcontrollers.parent.OptionsScreen
|
|
27
29
|
import dev.apexnative.navigation.viewcontrollers.parent.ParentController
|
|
28
30
|
import dev.apexnative.navigation.viewcontrollers.parent.ReturnsScreenFromColumn
|
|
29
31
|
import dev.apexnative.navigation.viewcontrollers.parent.ShownChildren
|
|
@@ -45,8 +47,9 @@ class SplitViewController(
|
|
|
45
47
|
presenter: Presenter,
|
|
46
48
|
initialOptions: Options,
|
|
47
49
|
private var split: SplitViewOptions,
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
+
// The panes' content; `showInColumn` may give a pane a new one.
|
|
51
|
+
private var primary: ViewController<*>,
|
|
52
|
+
private var secondary: ViewController<*>,
|
|
50
53
|
private val compact: ViewController<*>?,
|
|
51
54
|
private val eventEmitter: EventEmitter,
|
|
52
55
|
) : ParentController<SplitViewLayout>(activity, childRegistry, id, presenter, initialOptions),
|
|
@@ -421,27 +424,67 @@ class SplitViewController(
|
|
|
421
424
|
}
|
|
422
425
|
|
|
423
426
|
/**
|
|
424
|
-
* Shows [screen] in
|
|
425
|
-
*
|
|
427
|
+
* Shows [screen] in [column] (`primary` or `secondary`; Android has no other) in place of what the pane shows, and
|
|
428
|
+
* brings the pane into view. A stack pane's screens are replaced by it, unless it is a container
|
|
429
|
+
* (![goesInStack]): `columnIsStack`. Any other pane is taken by it, and its content goes. A column the split view
|
|
430
|
+
* doesn't have: `noSuchColumn`. False with the compact layout on screen: the screen goes over [from], the screen
|
|
431
|
+
* the command comes from.
|
|
426
432
|
*/
|
|
427
|
-
override fun showScreen(
|
|
433
|
+
override fun showScreen(
|
|
434
|
+
screen: ViewController<*>,
|
|
435
|
+
column: String,
|
|
436
|
+
goesInStack: Boolean,
|
|
437
|
+
from: ViewController<*>,
|
|
438
|
+
listener: CommandListener,
|
|
439
|
+
): Boolean {
|
|
428
440
|
if (isViewCreated && getView().isCompact) return false
|
|
429
|
-
val
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
441
|
+
val pane = when (column) {
|
|
442
|
+
PRIMARY -> primary
|
|
443
|
+
SECONDARY -> secondary
|
|
444
|
+
else -> null
|
|
433
445
|
}
|
|
434
|
-
|
|
435
|
-
|
|
446
|
+
when {
|
|
447
|
+
pane == null -> listener.onError(ErrorTexts.noSuchColumn(from.id, column))
|
|
448
|
+
pane is StackController && !goesInStack -> listener.onError(ErrorTexts.columnIsStack(from.id, column))
|
|
449
|
+
pane is StackController -> pane.setRoot(listOf(screen), false, object : CommandListenerAdapter() {
|
|
450
|
+
override fun onSuccess(childId: String) {
|
|
451
|
+
updateBackButton()
|
|
452
|
+
bringIntoView(column) { listener.onSuccess(childId) }
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
override fun onError(message: String) = listener.onError(message)
|
|
456
|
+
})
|
|
457
|
+
else -> {
|
|
458
|
+
replacePane(pane, screen)
|
|
436
459
|
updateBackButton()
|
|
437
|
-
|
|
460
|
+
val shownId = ((screen as? OptionsScreen)?.optionsScreen ?: screen).id
|
|
461
|
+
bringIntoView(column) { listener.onSuccess(shownId) }
|
|
438
462
|
}
|
|
439
|
-
|
|
440
|
-
override fun onError(message: String) = listener.onError(message)
|
|
441
|
-
})
|
|
463
|
+
}
|
|
442
464
|
return true
|
|
443
465
|
}
|
|
444
466
|
|
|
467
|
+
// [next] takes the pane of [old], a screen alone or a container, as iOS puts a screen in place of a column's: its
|
|
468
|
+
// view goes where the old one's was, the children shown are told as a move of the panes tells them (DidDisappear
|
|
469
|
+
// of what left, DidAppear of what came), and [old] goes with its screens.
|
|
470
|
+
private fun replacePane(old: ViewController<*>, next: ViewController<*>) {
|
|
471
|
+
next.setParentController(this)
|
|
472
|
+
if (old === primary) primary = next else secondary = next
|
|
473
|
+
peekView()?.let { layout ->
|
|
474
|
+
layout.panes.replacePane(old.getView(), next.getView())
|
|
475
|
+
applyTestIds(primary.getView(), secondary.getView())
|
|
476
|
+
hideCoveredPane()
|
|
477
|
+
shownChildren.moved()
|
|
478
|
+
tellShownChanged()
|
|
479
|
+
}
|
|
480
|
+
old.destroy()
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// The pane of [column] comes into view; [done] once the panes stopped.
|
|
484
|
+
private fun bringIntoView(column: String, done: Runnable) {
|
|
485
|
+
if (column == PRIMARY) setColumnShown(PRIMARY, true, done) else showSecondary(done)
|
|
486
|
+
}
|
|
487
|
+
|
|
445
488
|
private fun showSecondary(done: Runnable) {
|
|
446
489
|
if (!isSliding || getView().panes.isOpen) {
|
|
447
490
|
done.run()
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt
CHANGED
|
@@ -105,33 +105,42 @@ internal class StackCommands(
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
* `showInColumn` (Android): [layout] in
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
108
|
+
* `showInColumn` (Android): [layout] in [column] of the split view of the screen [componentId], or over that screen
|
|
109
|
+
* in its stack. Before any node: the screen is there, else `the screen '<id>' is not shown`; the layout has no id
|
|
110
|
+
* that shows already; out of a split view, the push's check of the layout (a container, which the stack doesn't
|
|
111
|
+
* take); a screen prepared with the id goes. The screen is made at once, a native screen's view too. The split view
|
|
112
|
+
* takes it — in place of a stack column's screens or of a column's content — with the answer once its pane
|
|
113
|
+
* stopped, or rejects (a column it doesn't have, a container for a stack column); with the compact layout on
|
|
114
|
+
* screen, or without a split view, it is pushed over [componentId] in its stack, with the push's checks, answer and
|
|
115
|
+
* errors; neither: `the screen '<id>' is neither in a split view nor in a stack`. Every rejection after the screen
|
|
116
|
+
* was made destroys it. Two calls in a row each make their screen the column's content in turn, and each answers
|
|
117
|
+
* its own id.
|
|
116
118
|
*/
|
|
117
|
-
fun showInColumn(componentId: String, layout: LayoutNode, answer: CommandAnswer, what: String) {
|
|
119
|
+
fun showInColumn(componentId: String, column: String, layout: LayoutNode, answer: CommandAnswer, what: String) {
|
|
118
120
|
val source = find(componentId)?.takeIf { !prepared.isPrepared(it) }
|
|
119
121
|
?: return answer.reject(ErrorTexts.screenNotShown(componentId))
|
|
120
122
|
val shown = LayoutChecks.firstShownId(listOf(layout), ::isShown) { false }
|
|
121
123
|
if (shown != null) return answer.reject(ErrorTexts.alreadyShown(shown))
|
|
122
124
|
val columns = nodeWith<ShowsColumns>(source)
|
|
123
125
|
val stack = source as? StackController ?: source.findParentStack()
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
// Out of a split view it can only be a push: the stack's check comes before any node.
|
|
127
|
+
if (columns == null && stack != null) {
|
|
128
|
+
LayoutChecks.checkStackChild(layout, stack.id)?.let { return answer.reject(it) }
|
|
129
|
+
}
|
|
126
130
|
if (!factory.canMake(layout)) return answer.reject(ErrorTexts.notDoneYet("showInColumn"))
|
|
127
131
|
prepared.discard(layout.id)
|
|
128
132
|
val node = make(layout)
|
|
129
133
|
try {
|
|
130
|
-
|
|
134
|
+
val goesInStack = LayoutChecks.isStackChild(layout)
|
|
135
|
+
if (columns?.showScreen(node, column, goesInStack, source, destroyingOnError(node, answer)) == true) return
|
|
131
136
|
if (stack == null) {
|
|
132
137
|
node.destroy()
|
|
133
138
|
return answer.reject(ErrorTexts.neitherSplitViewNorStack(componentId))
|
|
134
139
|
}
|
|
140
|
+
LayoutChecks.checkStackChild(layout, stack.id)?.let {
|
|
141
|
+
node.destroy()
|
|
142
|
+
return answer.reject(it)
|
|
143
|
+
}
|
|
135
144
|
pushMade(stack, source, node, layout, answer, what)
|
|
136
145
|
} catch (e: Throwable) {
|
|
137
146
|
node.destroy()
|
|
@@ -225,8 +234,9 @@ internal class StackCommands(
|
|
|
225
234
|
fun setStackRoot(componentId: String, layouts: List<LayoutNode>, answer: CommandAnswer, what: String) {
|
|
226
235
|
if (layouts.isEmpty()) return answer.reject(ErrorTexts.noScreensForStack(componentId))
|
|
227
236
|
val stack = stackOf(componentId, answer) ?: return
|
|
228
|
-
// An id of this stack may come again: the new screen replaces it (`setStackRoot` step 3).
|
|
229
|
-
|
|
237
|
+
// An id of an entry of this stack may come again: the new screen replaces it (`setStackRoot` step 3). The
|
|
238
|
+
// stack's own id, and a screen an accepted cut or replace took away, show elsewhere or still go: as on iOS.
|
|
239
|
+
val shown = LayoutChecks.firstShownId(layouts, ::isShown) { isInEntryOf(stack, it.id) }
|
|
230
240
|
if (shown != null) return answer.reject(ErrorTexts.alreadyShown(shown))
|
|
231
241
|
for (layout in layouts) LayoutChecks.checkStackChild(layout, stack.id)?.let { return answer.reject(it) }
|
|
232
242
|
if (!layouts.all(factory::canMake)) return answer.reject(ErrorTexts.notDoneYet("setStackRoot"))
|
|
@@ -357,10 +367,10 @@ internal class StackCommands(
|
|
|
357
367
|
// A node of the registry has [id], and it isn't only prepared for a push (`push` step 1, `preparePush`).
|
|
358
368
|
private fun isShown(id: String): Boolean = registry.find(id)?.let { !prepared.isPrepared(it) } == true
|
|
359
369
|
|
|
360
|
-
// The node with [id] is [stack]
|
|
361
|
-
private fun
|
|
362
|
-
val node = find(id) ?: return false
|
|
363
|
-
return node
|
|
370
|
+
// The node with [id] is in an entry of [stack] as JavaScript sees it (`StackQueue.entries`), not the stack itself.
|
|
371
|
+
private fun isInEntryOf(stack: StackController, id: String): Boolean {
|
|
372
|
+
val node = find(id)?.takeIf { it !== stack } ?: return false
|
|
373
|
+
return stack.entryOf(node)?.let { it in stack.queue.entries } == true
|
|
364
374
|
}
|
|
365
375
|
|
|
366
376
|
// A node of [layout], with its view made now when it has a native screen: its creator's failure rejects the
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt
CHANGED
|
@@ -12,6 +12,7 @@ import dev.apexnative.navigation.options.Options
|
|
|
12
12
|
import dev.apexnative.navigation.options.TopBarOptions
|
|
13
13
|
import dev.apexnative.navigation.react.CommandListener
|
|
14
14
|
import dev.apexnative.navigation.react.ErrorTexts
|
|
15
|
+
import dev.apexnative.navigation.react.PendingCalls
|
|
15
16
|
import dev.apexnative.navigation.react.events.AppearanceDispatcher
|
|
16
17
|
import dev.apexnative.navigation.utils.NavigationLog
|
|
17
18
|
import dev.apexnative.navigation.utils.UiThread
|
|
@@ -21,8 +22,8 @@ import dev.apexnative.navigation.viewcontrollers.child.ChildControllersRegistry
|
|
|
21
22
|
import dev.apexnative.navigation.viewcontrollers.component.ComponentViewController
|
|
22
23
|
import dev.apexnative.navigation.viewcontrollers.component.ContentReadiness
|
|
23
24
|
import dev.apexnative.navigation.viewcontrollers.component.Readiness
|
|
25
|
+
import dev.apexnative.navigation.viewcontrollers.component.RenderWait
|
|
24
26
|
import dev.apexnative.navigation.viewcontrollers.component.RestPlace
|
|
25
|
-
import dev.apexnative.navigation.viewcontrollers.component.ScreenContentFactory
|
|
26
27
|
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackState
|
|
27
28
|
import dev.apexnative.navigation.viewcontrollers.navigator.Navigator
|
|
28
29
|
import dev.apexnative.navigation.viewcontrollers.parent.ContainerBackground
|
|
@@ -81,26 +82,34 @@ class StackController(
|
|
|
81
82
|
|
|
82
83
|
/** The one queue of the stack. */
|
|
83
84
|
// The readiness of the screens reaches the queue's waits on the next turn of the main loop: a signal that comes
|
|
84
|
-
// inside a layout pass or a view's attach moves no view of the container there.
|
|
85
|
+
// inside a layout pass or a view's attach moves no view of the container there. A pushed screen's mount reaches
|
|
86
|
+
// its push behind the calls JavaScript made before it (`PendingCalls`), on a later turn too.
|
|
85
87
|
internal var queue = StackQueue(
|
|
86
88
|
display = StackViews(this),
|
|
87
89
|
readiness = ContentReadiness,
|
|
88
90
|
scheduler = { delay, task -> AppearanceDispatcher.scheduler.schedule(delay, task) },
|
|
89
91
|
host = QueueHost(),
|
|
90
92
|
deliver = { signal -> UiThread.post { signal() } },
|
|
93
|
+
afterCalls = PendingCalls::after,
|
|
91
94
|
)
|
|
92
95
|
private set
|
|
93
96
|
|
|
94
97
|
/**
|
|
95
98
|
* The unit tests run the stack over their own display, readiness and host (views are stubs there), before any
|
|
96
|
-
* screen joins it; the readiness signals come at once there.
|
|
99
|
+
* screen joins it; the readiness signals come at once there, and a push's behind the calls as [afterCalls] says.
|
|
97
100
|
*/
|
|
98
|
-
internal fun useQueueParts(
|
|
101
|
+
internal fun useQueueParts(
|
|
102
|
+
display: StackDisplay,
|
|
103
|
+
readiness: Readiness,
|
|
104
|
+
host: StackQueue.Host?,
|
|
105
|
+
afterCalls: (() -> Unit) -> Unit = { it() },
|
|
106
|
+
) {
|
|
99
107
|
queue = StackQueue(
|
|
100
108
|
display = display,
|
|
101
109
|
readiness = readiness,
|
|
102
110
|
scheduler = { delay, task -> AppearanceDispatcher.scheduler.schedule(delay, task) },
|
|
103
111
|
host = host ?: QueueHost(),
|
|
112
|
+
afterCalls = afterCalls,
|
|
104
113
|
)
|
|
105
114
|
}
|
|
106
115
|
|
|
@@ -470,7 +479,7 @@ class StackController(
|
|
|
470
479
|
|
|
471
480
|
// `mergeOptions` with the stack's id: its own options, what it shows, then the layouts around.
|
|
472
481
|
override fun mergeOptions(options: Options) {
|
|
473
|
-
|
|
482
|
+
mergeOwnOptions(options)
|
|
474
483
|
applyOrKeep(options, null)
|
|
475
484
|
performOnParentController { it.mergeChildOptions(withoutStackGroups(options), this) }
|
|
476
485
|
}
|
|
@@ -730,8 +739,8 @@ class StackController(
|
|
|
730
739
|
if (peekView() != null) topBar.stage(node)
|
|
731
740
|
}
|
|
732
741
|
|
|
733
|
-
override fun
|
|
734
|
-
(node
|
|
742
|
+
override fun hasReactContent(node: ViewController<*>): Boolean =
|
|
743
|
+
RenderWait.waitedScreens(node).any { it is ComponentViewController }
|
|
735
744
|
|
|
736
745
|
override fun log(text: String) {
|
|
737
746
|
Log.e(NavigationLog.TAG, text)
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt
CHANGED
|
@@ -185,7 +185,8 @@ internal interface StackDisplay {
|
|
|
185
185
|
|
|
186
186
|
/**
|
|
187
187
|
* The push of a prepared slot screen: [mounted] as the slot's content mounts into the screen's container (or at the
|
|
188
|
-
* limit of 0.5 s), then [start] on the pre-draw of that frame, while [waiting] holds
|
|
188
|
+
* limit of 0.5 s), then [start] on the pre-draw of that frame, while [waiting] holds; the queue starts the
|
|
189
|
+
* transition from there behind the calls JavaScript made before, on a later turn.
|
|
189
190
|
*/
|
|
190
191
|
fun awaitPrepared(screen: ViewController<*>, waiting: () -> Boolean, mounted: () -> Unit, start: () -> Unit)
|
|
191
192
|
}
|
|
@@ -52,8 +52,12 @@ private typealias Node = ViewController<*>
|
|
|
52
52
|
*
|
|
53
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;
|
|
54
54
|
* the readiness signals reach it through [deliver] (the stack posts them to the next turn of the main loop: a signal
|
|
55
|
-
* that comes inside a layout pass or a view's attach moves no view of the container there). A push
|
|
56
|
-
*
|
|
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
|
|
57
61
|
* interrupts the push, which moves no view into a layer.
|
|
58
62
|
*
|
|
59
63
|
* The transition is the screens' own when the options say so (`AnimationRules.stackIsOwn`): the display plays each
|
|
@@ -66,6 +70,11 @@ internal class StackQueue(
|
|
|
66
70
|
private val scheduler: AppearanceDispatcher.Scheduler,
|
|
67
71
|
private val host: Host,
|
|
68
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() },
|
|
69
78
|
) {
|
|
70
79
|
/** What the queue tells the stack. */
|
|
71
80
|
interface Host {
|
|
@@ -104,10 +113,10 @@ internal class StackQueue(
|
|
|
104
113
|
fun operationEnded(topSettled: Boolean)
|
|
105
114
|
|
|
106
115
|
/**
|
|
107
|
-
* Whether [node]
|
|
108
|
-
*
|
|
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.
|
|
109
118
|
*/
|
|
110
|
-
fun
|
|
119
|
+
fun hasReactContent(node: Node): Boolean = false
|
|
111
120
|
|
|
112
121
|
/** One line for the log: a failure that has no command to answer. */
|
|
113
122
|
fun log(text: String) = Unit
|
|
@@ -427,11 +436,12 @@ internal class StackQueue(
|
|
|
427
436
|
val node = op.node
|
|
428
437
|
val push = resolved(node).animations.push
|
|
429
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.
|
|
430
440
|
val wait = when {
|
|
431
441
|
op.prepared -> Wait.Prepared
|
|
432
442
|
push.waitForRender.isTrue -> Wait.Content
|
|
433
|
-
|
|
434
|
-
else -> Wait.
|
|
443
|
+
push.waitForRender.isFalse || !host.hasReactContent(node) -> Wait.None
|
|
444
|
+
else -> Wait.Limited
|
|
435
445
|
}
|
|
436
446
|
if (wait == Wait.None) return if (op.awaitsTargets) awaitTargets(op) else beginPush(op)
|
|
437
447
|
waiting = op
|
|
@@ -439,18 +449,37 @@ internal class StackQueue(
|
|
|
439
449
|
Wait.Prepared -> awaitPrepared(op)
|
|
440
450
|
Wait.Content -> {
|
|
441
451
|
// Then the components of the bar drawn in advance, 0.5 s at most after the content (a title or a
|
|
442
|
-
// 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.
|
|
443
454
|
display.placeWaiting(node)
|
|
444
|
-
awaitContent(
|
|
455
|
+
awaitContent(
|
|
456
|
+
op,
|
|
457
|
+
node,
|
|
458
|
+
limit = null,
|
|
459
|
+
bar = RenderWait.Bar.AfterContent,
|
|
460
|
+
deliver = afterCalls,
|
|
461
|
+
deliversReady = host.hasReactContent(node),
|
|
462
|
+
)
|
|
445
463
|
}
|
|
446
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.
|
|
447
467
|
display.placeWaiting(node)
|
|
448
|
-
|
|
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
|
+
)
|
|
449
477
|
}
|
|
450
478
|
}
|
|
451
479
|
}
|
|
452
480
|
|
|
453
481
|
private fun beginPush(op: PushOp) {
|
|
482
|
+
op.cancelLimit?.invoke()
|
|
454
483
|
val node = op.node
|
|
455
484
|
val covered = shownList.lastOrNull()
|
|
456
485
|
// The views first: a failure here leaves the lists as they were.
|
|
@@ -505,6 +534,7 @@ internal class StackQueue(
|
|
|
505
534
|
|
|
506
535
|
// Off screen or without a view: at once, without a transition; the top appears when the stack shows.
|
|
507
536
|
private fun pushAtOnce(op: PushOp) {
|
|
537
|
+
op.cancelLimit?.invoke()
|
|
508
538
|
val node = op.node
|
|
509
539
|
val covered = shownList.lastOrNull()
|
|
510
540
|
if (display.hasView) {
|
|
@@ -533,6 +563,7 @@ internal class StackQueue(
|
|
|
533
563
|
if (op.done) return
|
|
534
564
|
op.done = true
|
|
535
565
|
if (waiting === op) stopWaiting(op)
|
|
566
|
+
op.cancelLimit?.invoke()
|
|
536
567
|
queued.remove(op)
|
|
537
568
|
val node = op.node
|
|
538
569
|
entryList.remove(node)
|
|
@@ -799,8 +830,16 @@ internal class StackQueue(
|
|
|
799
830
|
|
|
800
831
|
// The one wait (RenderWait) for [screen], the node the operation brings: at [limit] the content not ready counts
|
|
801
832
|
// as mounted; without one, `waitForRender: true` waits as long as it takes, except for content the readiness store
|
|
802
|
-
// knows nothing of after 1 s, which counts as ready (the same rule as iOS).
|
|
803
|
-
|
|
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
|
+
) {
|
|
804
843
|
val wait = RenderWait(
|
|
805
844
|
readiness = readiness,
|
|
806
845
|
scheduler = scheduler,
|
|
@@ -808,6 +847,7 @@ internal class StackQueue(
|
|
|
808
847
|
bar = bar,
|
|
809
848
|
countsUnknownContent = true,
|
|
810
849
|
deliver = deliver,
|
|
850
|
+
deliversReadyContent = deliversReady,
|
|
811
851
|
step = { work -> step(op) { work() } },
|
|
812
852
|
onEnd = { endWait(op) },
|
|
813
853
|
)
|
|
@@ -828,15 +868,32 @@ internal class StackQueue(
|
|
|
828
868
|
step(op) { wait.retake(RenderWait.waitedScreens(node)) }
|
|
829
869
|
}
|
|
830
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.
|
|
831
874
|
private fun awaitPrepared(op: PushOp) {
|
|
832
875
|
val token = Any()
|
|
833
876
|
op.waitToken = token
|
|
834
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
|
+
}
|
|
835
892
|
display.awaitPrepared(
|
|
836
893
|
node,
|
|
837
|
-
waiting =
|
|
838
|
-
mounted = { step(op) {
|
|
839
|
-
start = {
|
|
894
|
+
waiting = still,
|
|
895
|
+
mounted = { step(op) { markMounted() } },
|
|
896
|
+
start = { afterCalls(start) },
|
|
840
897
|
)
|
|
841
898
|
}
|
|
842
899
|
|
|
@@ -865,7 +922,8 @@ internal class StackQueue(
|
|
|
865
922
|
val token = Any()
|
|
866
923
|
op.waitToken = token
|
|
867
924
|
val still = { !destroyed && !op.done && op.waitToken === token && waiting === op && op.isCurrent() }
|
|
868
|
-
// 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.
|
|
869
927
|
val from = currentChild
|
|
870
928
|
op.stopTargets = display.awaitTargets(animation, from, to, { still() && !op.overLimit }) {
|
|
871
929
|
step(op) { if (still()) endWait(op) }
|
|
@@ -874,6 +932,8 @@ internal class StackQueue(
|
|
|
874
932
|
|
|
875
933
|
private fun stopWaiting(op: Op) {
|
|
876
934
|
if (waiting === op) waiting = null
|
|
935
|
+
op.cancelCap?.invoke()
|
|
936
|
+
op.cancelCap = null
|
|
877
937
|
op.waitToken = null
|
|
878
938
|
op.wait?.cancel()
|
|
879
939
|
op.wait = null
|
|
@@ -1218,9 +1278,12 @@ internal class StackQueue(
|
|
|
1218
1278
|
// It waits for its elements' targets after its content; what stops that wait.
|
|
1219
1279
|
var awaitsTargets = false
|
|
1220
1280
|
var stopTargets: (() -> Unit)? = null
|
|
1221
|
-
// 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.
|
|
1222
1283
|
var overLimit = false
|
|
1223
1284
|
var cancelLimit: (() -> Unit)? = null
|
|
1285
|
+
// The limit of a prepared push's wait, which ends with the wait.
|
|
1286
|
+
var cancelCap: (() -> Unit)? = null
|
|
1224
1287
|
// Its screen came by an animation of its own: it stays at that animation's end values.
|
|
1225
1288
|
var keepsEnd = false
|
|
1226
1289
|
// The listener of a module heard its answer: it hears one only.
|
|
@@ -1295,20 +1358,13 @@ internal class StackQueue(
|
|
|
1295
1358
|
/** How long a lower screen stays placed in the container to make its content, at most. */
|
|
1296
1359
|
const val LOWER_SCREEN_MILLIS = 500L
|
|
1297
1360
|
|
|
1298
|
-
/**
|
|
1299
|
-
|
|
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
|
|
1300
1366
|
|
|
1301
1367
|
// The alpha an arriving screen shows with: its own at rest, never the 0 of its wait.
|
|
1302
1368
|
private const val ARRIVING_ALPHA = 1f
|
|
1303
1369
|
}
|
|
1304
1370
|
}
|
|
1305
|
-
|
|
1306
|
-
/**
|
|
1307
|
-
* The internal switches of the stack. [preparesOwnRootPushes]: the push of a React screen with a root of its own starts
|
|
1308
|
-
* its surface hidden in the stack at its size and insets, and starts its transition once the content rendered, 0.5 s at
|
|
1309
|
-
* most. Off by default («б»: the push starts at once); it is measured at the end on a phone.
|
|
1310
|
-
*/
|
|
1311
|
-
internal object StackSwitches {
|
|
1312
|
-
@Volatile
|
|
1313
|
-
var preparesOwnRootPushes = false
|
|
1314
|
-
}
|