@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +31 -6
- package/android/CORE.md +7 -2
- package/android/RECIPES.md +4 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +3 -35
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +101 -87
- package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +76 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -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 +86 -16
- 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 +41 -23
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +24 -20
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +25 -0
- 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 +42 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +29 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +2 -0
- 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 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +4 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +16 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +148 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +24 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +51 -11
- 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 +105 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +123 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +19 -0
- 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 +5 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +85 -27
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +45 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +2 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +4 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +4 -6
- 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/topbar/TitleBar.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +4 -3
- package/cli/app.js +1 -1
- package/cli/project.js +6 -2
- package/cli/setup/index.js +13 -0
- package/cli/setup/steps/appDelegate.js +66 -11
- package/cli/setup/steps/sceneDelegate.js +108 -0
- package/docs-md/architecture/transitions.md +7 -1
- 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/guides/deep-links.md +15 -33
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/index.json +8 -0
- package/docs-md/screen-ui/back-and-gestures.md +6 -0
- package/docs-md/screen-ui/system-ui.md +1 -1
- package/docs-md/start/installation.md +1 -0
- package/ios/Animations/ANNTransitionAnimator.mm +13 -11
- package/ios/Animations/ANNTransitionWait.mm +6 -7
- package/ios/Animations/Elements/ANNElementFinder.h +57 -0
- package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
- package/ios/Animations/Elements/ANNElementTransition.h +7 -5
- package/ios/Animations/Elements/ANNElementTransition.mm +17 -19
- package/ios/Animations/Elements/ANNSharedElementFlight.h +13 -8
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +59 -24
- package/ios/CLAUDE.md +10 -2
- package/ios/CORE.md +3 -2
- package/ios/Contract/ANNCommandAnswer.h +4 -3
- package/ios/Contract/ANNCommandAnswer.mm +12 -2
- package/ios/Contract/ANNCommandRunner.h +2 -2
- package/ios/Contract/ANNCommandRunner.mm +1 -1
- package/ios/Contract/ANNErrorTexts.h +6 -2
- package/ios/Contract/ANNErrorTexts.mm +18 -10
- package/ios/Core/ANNDeepLinkQueue.h +20 -1
- package/ios/Core/ANNDeepLinkQueue.mm +107 -12
- package/ios/Core/ANNErrorHandler.h +8 -3
- package/ios/Core/ANNErrorHandler.mm +17 -1
- package/ios/Core/ANNEventContract.h +246 -0
- package/ios/Core/ANNEventContract.mm +225 -0
- package/ios/Core/ANNEventEmitter.h +5 -1
- package/ios/Core/ANNEventEmitter.mm +117 -80
- package/ios/Core/ANNHinge.mm +6 -4
- package/ios/Core/ANNSceneDelegate.mm +25 -15
- package/ios/Core/ANNSystemUI.h +3 -2
- package/ios/Core/ANNSystemUI.mm +7 -0
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +6 -4
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +1 -1
- package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
- package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
- package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
- package/ios/Layouts/Root/ANNRootCoordinator.mm +12 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
- package/ios/Layouts/Screen/ANNComponentViewController.mm +28 -7
- package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
- package/ios/Layouts/Screen/ANNScreenInsets.mm +6 -4
- package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -6
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -7
- package/ios/Options/ANNSheetOptions.h +4 -1
- package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
- package/ios/RECIPES.md +2 -1
- package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
- package/ios/TurboModules/ANNTurboModule.mm +5 -2
- package/lib/module/ARCHITECTURE.md +7 -4
- package/lib/module/api/app/createNavigation.js +2 -0
- package/lib/module/api/app/createNavigation.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/linking/dispatchedLinks.js +23 -0
- package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
- package/lib/module/interfaces/contract/errors.json +275 -0
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
- package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
- package/lib/typescript/interfaces/Events.d.ts +9 -0
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +27 -18
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +33 -24
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/sideMenu.d.ts +7 -6
- package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +10 -6
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +7 -4
- package/src/api/app/createNavigation.ts +2 -0
- package/src/api/hooks/useNavigation.ts +8 -3
- package/src/api/linking/dispatchedLinks.ts +18 -0
- package/src/interfaces/Events.ts +10 -0
- package/src/interfaces/contract/errors.json +275 -0
- package/src/interfaces/options/animations.ts +27 -18
- package/src/interfaces/options/modals.ts +33 -24
- package/src/interfaces/options/sideMenu.ts +7 -6
- package/src/interfaces/options/system.ts +10 -6
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt
CHANGED
|
@@ -31,6 +31,7 @@ import dev.apexnative.navigation.viewcontrollers.component.ContentReadiness
|
|
|
31
31
|
import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
32
32
|
import dev.apexnative.navigation.viewcontrollers.externalcomponent.ExternalComponentViewController
|
|
33
33
|
import dev.apexnative.navigation.viewcontrollers.modal.ModalCoordinator
|
|
34
|
+
import dev.apexnative.navigation.viewcontrollers.modal.ModalRecord
|
|
34
35
|
import dev.apexnative.navigation.viewcontrollers.modal.ModalViews
|
|
35
36
|
import dev.apexnative.navigation.viewcontrollers.overlay.OverlayCoordinator
|
|
36
37
|
import dev.apexnative.navigation.viewcontrollers.overlay.OverlayRecord
|
|
@@ -38,6 +39,7 @@ import dev.apexnative.navigation.viewcontrollers.overlay.OverlayViews
|
|
|
38
39
|
import dev.apexnative.navigation.viewcontrollers.parent.ParentController
|
|
39
40
|
import dev.apexnative.navigation.viewcontrollers.stack.PreparedPushes
|
|
40
41
|
import dev.apexnative.navigation.viewcontrollers.stack.StackCommands
|
|
42
|
+
import dev.apexnative.navigation.viewcontrollers.stack.StackController
|
|
41
43
|
import dev.apexnative.navigation.viewcontrollers.stack.topbar.BarComponents
|
|
42
44
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
43
45
|
import dev.apexnative.navigation.views.animations.FadeThrough
|
|
@@ -153,6 +155,15 @@ class Navigator(
|
|
|
153
155
|
fun handleBack(listener: CommandListener): Boolean =
|
|
154
156
|
modals.handleBack(listener) ?: (root?.handleBack(listener) == true)
|
|
155
157
|
|
|
158
|
+
/**
|
|
159
|
+
* The stack whose top [handleBack] would take away with the default pop: in the top modal, else in the root; none
|
|
160
|
+
* when back would do anything else.
|
|
161
|
+
*/
|
|
162
|
+
internal fun stackPoppedByBack(): StackController? {
|
|
163
|
+
val modalRoot = modals.backTargetRoot()
|
|
164
|
+
return if (modalRoot != null) modalRoot.stackPoppedByBack() else root?.stackPoppedByBack()
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
/**
|
|
157
168
|
* [layer] may wait to show from now on ([WaitingLayer]): the nodes in it ask it through [isInWaitingLayer] and
|
|
158
169
|
* [shownScreensChanged]. The root's layer is there from the start.
|
|
@@ -214,7 +225,7 @@ class Navigator(
|
|
|
214
225
|
* it itself, `ComponentPresenter`); the system UI follows (only what changed is written).
|
|
215
226
|
*/
|
|
216
227
|
internal fun optionsMerged(node: ViewController<*>, options: Options = Options()) {
|
|
217
|
-
modals.optionsMergedInto(node)
|
|
228
|
+
modals.optionsMergedInto(node, options)
|
|
218
229
|
overlays.optionsMergedInto(node)
|
|
219
230
|
val navigationBar = options.navigationBar
|
|
220
231
|
val padding = navigationBar.drawBehind.hasValue() || navigationBar.backgroundColor.hasValue()
|
|
@@ -264,9 +275,7 @@ class Navigator(
|
|
|
264
275
|
if (!made) return answer.reject(ErrorTexts.notDoneYet("setRoot"))
|
|
265
276
|
val steps = layout.modals.mapIndexed { index, node ->
|
|
266
277
|
val last = index == layout.modals.lastIndex
|
|
267
|
-
|
|
268
|
-
modals.showInChain(node.id, { makeLayer(node, isOverlay = false) }, last, done)
|
|
269
|
-
}
|
|
278
|
+
ModalLayerStep(node, last)
|
|
270
279
|
} + layout.overlays.map { node ->
|
|
271
280
|
RootCoordinator.LayerStep { done ->
|
|
272
281
|
overlays.showInChain(node.id, { makeLayer(node, isOverlay = true) }, done)
|
|
@@ -275,6 +284,35 @@ class Navigator(
|
|
|
275
284
|
roots.setRoot({ factory.create(layout.root) }, answer, steps)
|
|
276
285
|
}
|
|
277
286
|
|
|
287
|
+
// A modal of a `setRoot`'s layout: made with the root and held until its turn, so it renders while the root waits
|
|
288
|
+
// and comes in; made in its turn instead while an id of it still shows (a modal of the old root with that id goes
|
|
289
|
+
// with the old root). A modal that couldn't be made is skipped in its turn.
|
|
290
|
+
private inner class ModalLayerStep(private val node: LayoutNode, private val animated: Boolean) :
|
|
291
|
+
RootCoordinator.LayerStep {
|
|
292
|
+
private var prepared = false
|
|
293
|
+
private var held: ModalRecord? = null
|
|
294
|
+
|
|
295
|
+
override fun prepare() {
|
|
296
|
+
if (LayoutChecks.firstShownId(listOf(node), ::isShownNow) { false } != null) return
|
|
297
|
+
prepared = true
|
|
298
|
+
held = modals.hold(node.id, { factory.create(node) }, animated)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
override fun discard() {
|
|
302
|
+
held?.let(modals::drop)
|
|
303
|
+
held = null
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
override fun show(done: () -> Unit): () -> Unit {
|
|
307
|
+
val record = held
|
|
308
|
+
held = null
|
|
309
|
+
if (record != null) return modals.release(record, done)
|
|
310
|
+
if (!prepared) return modals.showInChain(node.id, { makeLayer(node, isOverlay = false) }, animated, done)
|
|
311
|
+
done()
|
|
312
|
+
return {}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
278
316
|
// A modal or an overlay of a `setRoot`'s layout, made as its turn comes: an id that shows by then fails it.
|
|
279
317
|
private fun makeLayer(node: LayoutNode, isOverlay: Boolean): ViewController<*> {
|
|
280
318
|
val shown = LayoutChecks.firstShownId(listOf(node), ::isShownNow) { isOverlay && isReplacedOverlay(node, it) }
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt
CHANGED
|
@@ -36,7 +36,8 @@ import dev.apexnative.navigation.views.animations.AnimationRules
|
|
|
36
36
|
* The modals over the old root ([ModalsOverRoot]): taken as the `setRoot` is accepted (commands don't find them), given
|
|
37
37
|
* back when it ends without replacing the root, carried with the old root into its transition and destroyed with it,
|
|
38
38
|
* without `ANN.ModalDismissed`. The modals and the overlays of the layout ([LayerStep]) show after the root, one by
|
|
39
|
-
* one, before the answer: a newer `setRoot` stops them.
|
|
39
|
+
* one, before the answer: a newer `setRoot` stops them. Its modals are made with the root ([LayerStep.prepare]) and
|
|
40
|
+
* render unseen while it waits and comes in, so each shows in its turn without a wait of its own.
|
|
40
41
|
*
|
|
41
42
|
* The steps that come later (the wait's timers and readiness signals, the end of the transition, the end of a layer,
|
|
42
43
|
* a stack of the waiting root that changed its top) run under no runner: each goes through one guard, and a failure
|
|
@@ -128,11 +129,20 @@ internal class RootCoordinator(
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
/**
|
|
131
|
-
* A modal or an overlay of the layout of a `setRoot`, shown after its root: [show]
|
|
132
|
-
* its argument once it showed or went (a failure is logged and skipped); what it
|
|
133
|
-
* a newer `setRoot`: it goes without a word.
|
|
132
|
+
* A modal or an overlay of the layout of a `setRoot`, shown after its root: [show] shows it (made then, unless
|
|
133
|
+
* [prepare] made it), and calls its argument once it showed or went (a failure is logged and skipped); what it
|
|
134
|
+
* returns stops it while it waits — a newer `setRoot`: it goes without a word.
|
|
134
135
|
*/
|
|
135
136
|
fun interface LayerStep {
|
|
137
|
+
/**
|
|
138
|
+
* Right after the root is placed, before its wait: a step may make its layer now, so that it renders unseen
|
|
139
|
+
* while the root waits and comes in, and shows without a wait of its own in its turn.
|
|
140
|
+
*/
|
|
141
|
+
fun prepare() = Unit
|
|
142
|
+
|
|
143
|
+
/** The `setRoot` ended before this step's turn: what [prepare] made goes without a word. */
|
|
144
|
+
fun discard() = Unit
|
|
145
|
+
|
|
136
146
|
fun show(done: () -> Unit): () -> Unit
|
|
137
147
|
}
|
|
138
148
|
|
|
@@ -221,6 +231,7 @@ internal class RootCoordinator(
|
|
|
221
231
|
modals.rootAccepted()
|
|
222
232
|
val waitForRender = animationsOf(root).enter.waitForRender
|
|
223
233
|
this.pending = pending
|
|
234
|
+
layers.forEach { it.prepare() }
|
|
224
235
|
firstRoot.rootSet(waitForRender)
|
|
225
236
|
await(pending, waitForRender)
|
|
226
237
|
}
|
|
@@ -367,6 +378,14 @@ internal class RootCoordinator(
|
|
|
367
378
|
if (!done) pending.stopLayer = stop
|
|
368
379
|
}
|
|
369
380
|
|
|
381
|
+
// The layers of [pending] not shown yet go: what they made ahead goes unseen, the others aren't made. Each on its
|
|
382
|
+
// own: one that fails doesn't keep the others.
|
|
383
|
+
private fun discardLayers(pending: Pending) {
|
|
384
|
+
val rest = pending.layers.drop(pending.nextLayer)
|
|
385
|
+
pending.nextLayer = pending.layers.size
|
|
386
|
+
rest.forEach { attempt { it.discard() } }
|
|
387
|
+
}
|
|
388
|
+
|
|
370
389
|
// A new `setRoot` came while this one was on its way.
|
|
371
390
|
private fun supersede() {
|
|
372
391
|
val pending = pending ?: return
|
|
@@ -389,14 +408,15 @@ internal class RootCoordinator(
|
|
|
389
408
|
pending.replaced = null
|
|
390
409
|
modals.replacedRootGone()
|
|
391
410
|
}
|
|
392
|
-
// Its root is the current root: the modal or the overlay that waits now goes without a word, the others
|
|
393
|
-
// aren't made; those shown stay over its root, and go with it when the new root replaces it.
|
|
411
|
+
// Its root is the current root: the modal or the overlay that waits now goes without a word, the others go
|
|
412
|
+
// unseen or aren't made; those shown stay over its root, and go with it when the new root replaces it.
|
|
394
413
|
Phase.Layers -> {
|
|
395
414
|
pending.stopLayer?.invoke()
|
|
396
415
|
pending.stopLayer = null
|
|
397
416
|
releaseState(pending)
|
|
398
417
|
}
|
|
399
418
|
}
|
|
419
|
+
discardLayers(pending)
|
|
400
420
|
pending.answer.reject(ErrorTexts.setRootReplaced(pending.root.id))
|
|
401
421
|
}
|
|
402
422
|
|
|
@@ -426,6 +446,7 @@ internal class RootCoordinator(
|
|
|
426
446
|
pending.wait = null
|
|
427
447
|
when (pending.phase) {
|
|
428
448
|
Phase.Waiting -> {
|
|
449
|
+
discardLayers(pending)
|
|
429
450
|
attempt { takeAway(pending.root) }
|
|
430
451
|
attempt { modals.rootKept() }
|
|
431
452
|
attempt { firstRoot.rootDestroyed() }
|
|
@@ -451,6 +472,7 @@ internal class RootCoordinator(
|
|
|
451
472
|
pending.stopLayer = null
|
|
452
473
|
}
|
|
453
474
|
}
|
|
475
|
+
discardLayers(pending)
|
|
454
476
|
// A root that is up takes the splash away, as at its show.
|
|
455
477
|
if (pending.phase != Phase.Waiting) attempt { onRootShown() }
|
|
456
478
|
attempt { releaseState(pending) }
|
|
@@ -482,6 +504,7 @@ internal class RootCoordinator(
|
|
|
482
504
|
// The root was destroyed while it waited, by something other than a newer `setRoot` or the activity going.
|
|
483
505
|
private fun rootGone(pending: Pending) {
|
|
484
506
|
this.pending = null
|
|
507
|
+
discardLayers(pending)
|
|
485
508
|
releaseState(pending)
|
|
486
509
|
layer.remove(pending.root)
|
|
487
510
|
modals.rootKept()
|
|
@@ -185,6 +185,8 @@ internal class OverlayCoordinator(
|
|
|
185
185
|
|
|
186
186
|
private fun start(make: () -> ViewController<*>, answer: CommandAnswer?, chainDone: (() -> Unit)?): OverlayRecord {
|
|
187
187
|
val root = make()
|
|
188
|
+
// Before the root's view is made: its screens are drawn under the status bar from their first layout.
|
|
189
|
+
root.shownAsOverlay = true
|
|
188
190
|
val record = OverlayRecord(root)
|
|
189
191
|
// A waiting layer before its views join the window: what is in it isn't on screen from its first layout.
|
|
190
192
|
record.screensChanged = { screensChanged(record) }
|
|
@@ -31,9 +31,9 @@ internal object ContainerBackground {
|
|
|
31
31
|
if (!colour.hasValue()) return null
|
|
32
32
|
val color = colour.get(node.activity, Color.TRANSPARENT) ?: Color.TRANSPARENT
|
|
33
33
|
// The screens' band rule (`ScreenSafeArea.backgroundBand`) for a layout, which keeps no room at its top: the
|
|
34
|
-
// status bar's height, but none when it is hidden or the layout is drawn under it
|
|
35
|
-
// a pair.
|
|
36
|
-
val full = options.statusBar.isHiddenOrDrawBehind || node.isInSideMenu || node.isInSheet ||
|
|
34
|
+
// status bar's height, but none when it is hidden or the layout is drawn under it (in an overlay by default),
|
|
35
|
+
// in a side menu, a sheet or a pair.
|
|
36
|
+
val full = options.statusBar.isHiddenOrDrawBehind(node.isInOverlay) || node.isInSideMenu || node.isInSheet ||
|
|
37
37
|
node.parentController is KeepsStatusBarRoom
|
|
38
38
|
val band = if (full) 0 else SystemUiUtils.getStatusBarHeight(node.activity)
|
|
39
39
|
if (band <= 0) return ColorDrawable(color)
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt
CHANGED
|
@@ -8,6 +8,7 @@ import dev.apexnative.navigation.react.CommandListener
|
|
|
8
8
|
import dev.apexnative.navigation.viewcontrollers.bottomtabs.TabBarMotion
|
|
9
9
|
import dev.apexnative.navigation.viewcontrollers.child.ChildController
|
|
10
10
|
import dev.apexnative.navigation.viewcontrollers.child.ChildControllersRegistry
|
|
11
|
+
import dev.apexnative.navigation.viewcontrollers.stack.StackController
|
|
11
12
|
import dev.apexnative.navigation.viewcontrollers.stack.fab.FabPlacement
|
|
12
13
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
13
14
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
@@ -210,6 +211,8 @@ abstract class ParentController<V : ViewGroup>(
|
|
|
210
211
|
|
|
211
212
|
override fun canHandleBack(): Boolean = getCurrentChild()?.canHandleBack() == true
|
|
212
213
|
|
|
214
|
+
override fun stackPoppedByBack(): StackController? = getCurrentChild()?.stackPoppedByBack()
|
|
215
|
+
|
|
213
216
|
override fun destroy() {
|
|
214
217
|
if (isDestroyed) return
|
|
215
218
|
getChildControllers().forEach { it.destroy() }
|
|
@@ -16,6 +16,7 @@ import dev.apexnative.navigation.viewcontrollers.navigator.Navigator
|
|
|
16
16
|
import dev.apexnative.navigation.viewcontrollers.parent.ChildOffsetFromRest
|
|
17
17
|
import dev.apexnative.navigation.viewcontrollers.parent.ParentController
|
|
18
18
|
import dev.apexnative.navigation.viewcontrollers.sidemenu.SideMenuMotion.Side
|
|
19
|
+
import dev.apexnative.navigation.viewcontrollers.stack.StackController
|
|
19
20
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
20
21
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
21
22
|
import dev.apexnative.navigation.views.sidemenu.SideMenuView
|
|
@@ -183,6 +184,9 @@ internal class SideMenuController(
|
|
|
183
184
|
|
|
184
185
|
override fun canHandleBack(): Boolean = motion.canGoBack || center.canHandleBack()
|
|
185
186
|
|
|
187
|
+
// An open menu closes first: no preview of a pop then.
|
|
188
|
+
override fun stackPoppedByBack(): StackController? = if (motion.canGoBack) null else center.stackPoppedByBack()
|
|
189
|
+
|
|
186
190
|
// A button's press addressed to the side menu goes to the center.
|
|
187
191
|
override fun sendOnNavigationButtonPressed(buttonId: String) = center.sendOnNavigationButtonPressed(buttonId)
|
|
188
192
|
|
|
@@ -255,9 +259,7 @@ internal class SideMenuController(
|
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
// The system UI follows the screen the user sees now, at rest: the open menu's, the center's once a close
|
|
258
|
-
// ends. The owner reads it before the next frame, when a menu that closed is hidden already.
|
|
259
|
-
// animation runs this inside `AppearanceDispatcher.withoutWillAppear`: what starts to appear from here would
|
|
260
|
-
// send no WillAppear.
|
|
262
|
+
// ends. The owner reads it before the next frame, when a menu that closed is hidden already.
|
|
261
263
|
override fun shownChanged() {
|
|
262
264
|
navigator?.shownScreensChanged(this@SideMenuController)
|
|
263
265
|
navigator?.systemUi?.invalidate()
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt
CHANGED
|
@@ -16,8 +16,8 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
|
16
16
|
* the drawer calls `onDrawerClosed` only for a drawer that had opened.
|
|
17
17
|
* - The menu screen's events: WillAppear at the side's first movement, DidAppear when it opened, DidDisappear when it
|
|
18
18
|
* rests closed; only while the side menu has appeared ([Host.appeared]) — otherwise they come with the side menu's
|
|
19
|
-
* own appearance (its visible children). An open without animation sends
|
|
20
|
-
*
|
|
19
|
+
* own appearance (its visible children). An open without animation sends both too, WillAppear first, as on iOS:
|
|
20
|
+
* the drawer jumps to its open offset before it calls the open.
|
|
21
21
|
* - At each rest: one state update after the menu's events, the back callback, and the layers that follow the shown
|
|
22
22
|
* screen.
|
|
23
23
|
* - A drawer that doesn't call back (not laid out since it joined the window, or not in one: `DrawerLayout` only
|
|
@@ -246,11 +246,8 @@ internal class SideMenuMotion(
|
|
|
246
246
|
private fun move(side: Side, open: Boolean, animate: Boolean) {
|
|
247
247
|
val drawer = drawer()
|
|
248
248
|
val silent = !drawer.callsBack
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (silent) record(side, open)
|
|
252
|
-
}
|
|
253
|
-
if (open && !animate && host.appeared) AppearanceDispatcher.withoutWillAppear(run) else run()
|
|
249
|
+
if (open) drawer.open(side, animate) else drawer.close(side, animate)
|
|
250
|
+
if (silent) record(side, open)
|
|
254
251
|
}
|
|
255
252
|
|
|
256
253
|
private fun record(side: Side, open: Boolean) {
|
|
@@ -18,15 +18,14 @@ import dev.apexnative.navigation.views.sidemenu.SideMenuView
|
|
|
18
18
|
* `testID`s. Each moment applies its part, from the side menu's resolved options
|
|
19
19
|
* (`SideMenuController.resolveShownOptions`: the current part's chain, the center's `sideMenu` over an open menu's) —
|
|
20
20
|
* at creation everything with the heights, at every appearance of a screen inside everything but the heights, at a
|
|
21
|
-
* merge the fields it names
|
|
21
|
+
* merge the fields it names (a width or a height at once, an open menu too), at a repaint the background and the
|
|
22
|
+
* `testID`s. The locks of `enabled` and the command
|
|
22
23
|
* `visible` are the node's ([SideMenuMotion]). Main thread only.
|
|
23
24
|
*/
|
|
24
25
|
internal class SideMenuPresenter(activity: Activity) : Presenter(activity, Options()) {
|
|
25
|
-
/** The side menu's [view] is made, with [options], resolved: the heights,
|
|
26
|
+
/** The side menu's [view] is made, with [options], resolved: the heights too, which only a merge changes after. */
|
|
26
27
|
fun applyOnCreation(view: SideMenuView, options: Options) {
|
|
27
|
-
|
|
28
|
-
view.setPartHeight(side, SideMenuRules.height(SideMenuRules.sideOf(options.sideMenu, side), ::toPixels))
|
|
29
|
-
}
|
|
28
|
+
applyHeights(view, options.sideMenu)
|
|
30
29
|
applyWidths(view, options.sideMenu)
|
|
31
30
|
applyBackground(view, options)
|
|
32
31
|
applyTestIds(view, options.sideMenu)
|
|
@@ -46,14 +45,18 @@ internal class SideMenuPresenter(activity: Activity) : Presenter(activity, Optio
|
|
|
46
45
|
/**
|
|
47
46
|
* A merge reached the side menu, its own or through a screen inside (`mergeOptions` step 3): the fields [options]
|
|
48
47
|
* name, with the values of the side menu's resolution now, so a screen off what shows changes nothing until it
|
|
49
|
-
* appears. A width
|
|
48
|
+
* appears. A width or a height resizes its menu at once, open or closed, as on iOS.
|
|
50
49
|
*/
|
|
51
50
|
override fun mergeOptions(viewController: ViewController<*>, options: Options) {
|
|
52
51
|
val layout = viewController.peekView() as? SideMenuView ?: return
|
|
53
52
|
val touchesBackground = SideMenuRules.touchesBackground(options)
|
|
54
53
|
val touchesTestIds = SideMenuRules.touchesTestIds(options.sideMenu)
|
|
55
|
-
|
|
54
|
+
val touchesWidths = SideMenuRules.touchesWidths(options.sideMenu)
|
|
55
|
+
val touchesHeights = SideMenuRules.touchesHeights(options.sideMenu)
|
|
56
|
+
if (!touchesBackground && !touchesTestIds && !touchesWidths && !touchesHeights) return
|
|
56
57
|
val shown = viewController.resolveShownOptions(DefaultOptions.current)
|
|
58
|
+
if (touchesWidths) applyWidths(layout, shown.sideMenu)
|
|
59
|
+
if (touchesHeights) applyHeights(layout, shown.sideMenu)
|
|
57
60
|
if (touchesBackground) applyBackground(layout, shown)
|
|
58
61
|
if (touchesTestIds) applyTestIds(layout, shown.sideMenu)
|
|
59
62
|
}
|
|
@@ -71,6 +74,12 @@ internal class SideMenuPresenter(activity: Activity) : Presenter(activity, Optio
|
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
77
|
+
private fun applyHeights(view: SideMenuView, options: SideMenuOptions) {
|
|
78
|
+
for (side in Side.entries) {
|
|
79
|
+
view.setPartHeight(side, SideMenuRules.height(SideMenuRules.sideOf(options, side), ::toPixels))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
74
83
|
// `layout.backgroundColor` on the drawer, its whole height, under the status bar; unset leaves the view as it is,
|
|
75
84
|
// `'NoColor'` clears it.
|
|
76
85
|
private fun applyBackground(view: View, options: Options) {
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt
CHANGED
|
@@ -54,6 +54,13 @@ internal object SideMenuRules {
|
|
|
54
54
|
fun touchesTestIds(merge: SideMenuOptions): Boolean =
|
|
55
55
|
merge.testID.hasValue() || merge.left.testID.hasValue() || merge.right.testID.hasValue()
|
|
56
56
|
|
|
57
|
+
/** A merge names `width` of a side: the menus take their widths again. */
|
|
58
|
+
fun touchesWidths(merge: SideMenuOptions): Boolean = merge.left.width.hasValue() || merge.right.width.hasValue()
|
|
59
|
+
|
|
60
|
+
/** A merge names `height` of a side: the menus take their heights again. */
|
|
61
|
+
fun touchesHeights(merge: SideMenuOptions): Boolean =
|
|
62
|
+
merge.left.height.hasValue() || merge.right.height.hasValue()
|
|
63
|
+
|
|
57
64
|
/** A merge names the background of the side menu (`layout.backgroundColor`). */
|
|
58
65
|
fun touchesBackground(merge: Options): Boolean = merge.layout.backgroundColor.hasValue()
|
|
59
66
|
|
|
@@ -64,7 +71,7 @@ internal object SideMenuRules {
|
|
|
64
71
|
fun wantedWidth(options: SideMenuSideOptions, toPixels: (Double) -> Int): Int? =
|
|
65
72
|
options.width.get(null)?.let { max(0, toPixels(it)) }
|
|
66
73
|
|
|
67
|
-
/** The height of a side, in pixels: `height` (dp) by [toPixels]
|
|
74
|
+
/** The height of a side, in pixels: `height` (dp) by [toPixels]; none: the whole height. */
|
|
68
75
|
fun height(options: SideMenuSideOptions, toPixels: (Double) -> Int): Int? =
|
|
69
76
|
options.height.get(null)?.let { max(0, toPixels(it)) }
|
|
70
77
|
|
|
@@ -26,11 +26,12 @@ import dev.apexnative.navigation.viewcontrollers.navigator.Navigator
|
|
|
26
26
|
import dev.apexnative.navigation.viewcontrollers.parent.ChildOffsetFromRest
|
|
27
27
|
import dev.apexnative.navigation.viewcontrollers.parent.ParentController
|
|
28
28
|
import dev.apexnative.navigation.viewcontrollers.parent.ReturnsScreenFromColumn
|
|
29
|
-
import dev.apexnative.navigation.viewcontrollers.parent.ShowsColumns
|
|
30
29
|
import dev.apexnative.navigation.viewcontrollers.parent.ShownChildren
|
|
30
|
+
import dev.apexnative.navigation.viewcontrollers.parent.ShowsColumns
|
|
31
31
|
import dev.apexnative.navigation.viewcontrollers.stack.StackController
|
|
32
32
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
33
33
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
34
|
+
import kotlin.math.roundToInt
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* A split view: the primary pane beside the secondary one when the width takes both (a tablet, a phone in landscape).
|
|
@@ -365,6 +366,19 @@ class SplitViewController(
|
|
|
365
366
|
return can || super.canHandleBack()
|
|
366
367
|
}
|
|
367
368
|
|
|
369
|
+
// As handleBack: the pane whose back goes first decides; an open sliding pane that would close previews nothing.
|
|
370
|
+
override fun stackPoppedByBack(): StackController? {
|
|
371
|
+
if (!isViewCreated) return super.stackPoppedByBack()
|
|
372
|
+
val layout = getView()
|
|
373
|
+
return when {
|
|
374
|
+
layout.isCompact -> compact?.stackPoppedByBack()
|
|
375
|
+
isSliding && !layout.panes.isOpen -> primary.stackPoppedByBack()
|
|
376
|
+
isSliding -> if (secondary.canHandleBack()) secondary.stackPoppedByBack() else null
|
|
377
|
+
secondary.canHandleBack() -> secondary.stackPoppedByBack()
|
|
378
|
+
else -> primary.stackPoppedByBack()
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
368
382
|
/**
|
|
369
383
|
* How far the pane of [child] is from where it rests: the secondary pane sliding over the primary one rests over
|
|
370
384
|
* it, closed or on its way. Zero for the other children.
|
|
@@ -489,7 +503,7 @@ class SplitViewController(
|
|
|
489
503
|
root.mergeOptions(options)
|
|
490
504
|
}
|
|
491
505
|
|
|
492
|
-
private fun dp(value: Double) = UiUtils.dpToPx(activity, value.toFloat()).
|
|
506
|
+
private fun dp(value: Double) = UiUtils.dpToPx(activity, value.toFloat()).roundToInt()
|
|
493
507
|
|
|
494
508
|
companion object {
|
|
495
509
|
private const val PRIMARY = "primary"
|