@apexnative/navigation 1.0.0-alpha.2 → 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 +22 -0
- package/android/CLAUDE.md +21 -2
- package/android/CORE.md +3 -1
- 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/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/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 +74 -8
- 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 +41 -3
- 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 +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +14 -1
- 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 +121 -7
- 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/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/docs-md/architecture/transitions.md +6 -0
- 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 +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/lib/module/api/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.js.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +10 -5
- package/lib/typescript/interfaces/options/animations.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/package.json +1 -1
- package/src/api/hooks/useNavigation.ts +8 -3
- package/src/interfaces/options/animations.ts +10 -5
- package/src/interfaces/options/modals.ts +18 -13
- package/src/interfaces/options/system.ts +6 -3
|
@@ -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
|
|
@@ -71,12 +72,20 @@ internal class StackQueue(
|
|
|
71
72
|
/** The current child (the top of [shown]) changed: a transition started, or an operation ran at once. */
|
|
72
73
|
fun currentChildChanged()
|
|
73
74
|
|
|
75
|
+
/** An operation was accepted: a back gesture's preview that shows ends, the stack as it was. */
|
|
76
|
+
fun operationAccepted() = Unit
|
|
77
|
+
|
|
74
78
|
/**
|
|
75
79
|
* The current child is about to change, right before [currentChildChanged]: [motion] is the default transition
|
|
76
80
|
* 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
|
|
81
|
+
* how the bar shows or hides when the new top wants it otherwise; [travel]: the bar goes with the screens of an
|
|
82
|
+
* animation of its own without elements.
|
|
78
83
|
*/
|
|
79
|
-
fun barTransition(
|
|
84
|
+
fun barTransition(
|
|
85
|
+
motion: StackDisplay.BarMotion?,
|
|
86
|
+
visibility: BarVisibility,
|
|
87
|
+
travel: StackDisplay.BarTravel? = null,
|
|
88
|
+
) = Unit
|
|
80
89
|
|
|
81
90
|
/**
|
|
82
91
|
* The current child is about to change, right before [barTransition]: [move] is how the transition goes, which
|
|
@@ -140,6 +149,9 @@ internal class StackQueue(
|
|
|
140
149
|
private var takenOver = false
|
|
141
150
|
private var destroyed = false
|
|
142
151
|
|
|
152
|
+
/** [node]'s content is made and nothing waits for it to render as a lower screen. */
|
|
153
|
+
fun isRendered(node: Node): Boolean = node !in unrendered && node !in lowerWaits
|
|
154
|
+
|
|
143
155
|
/** The stack as JavaScript sees it, bottom to top. */
|
|
144
156
|
val entries: List<Node>
|
|
145
157
|
get() = entryList
|
|
@@ -199,7 +211,9 @@ internal class StackQueue(
|
|
|
199
211
|
|
|
200
212
|
/**
|
|
201
213
|
* 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`).
|
|
214
|
+
* the user has none. [silent]: no animation and no appearance events (`popToRootOnBlur`). [gesture]: the release of
|
|
215
|
+
* a back gesture whose preview shows the pop already (`StackDisplay.finishBackGesture` ends it, the bar shows the
|
|
216
|
+
* target at once).
|
|
203
217
|
*/
|
|
204
218
|
fun cut(
|
|
205
219
|
target: Node,
|
|
@@ -208,6 +222,7 @@ internal class StackQueue(
|
|
|
208
222
|
what: String,
|
|
209
223
|
value: String?,
|
|
210
224
|
silent: Boolean = false,
|
|
225
|
+
gesture: Boolean = false,
|
|
211
226
|
) {
|
|
212
227
|
val index = entryList.indexOf(target)
|
|
213
228
|
if (destroyed || index < 0) return refuse(answer, null, componentId, emptyList())
|
|
@@ -219,7 +234,7 @@ internal class StackQueue(
|
|
|
219
234
|
(waiting as? ReplaceOp)?.let(::completeAtOnce)
|
|
220
235
|
val showsAbove = target in shownList && shownList.last() !== target
|
|
221
236
|
val op = if (showsAbove) {
|
|
222
|
-
CutOp(seq, answer, componentId, what, target, value, silent)
|
|
237
|
+
CutOp(seq, answer, componentId, what, target, value, silent, gesture)
|
|
223
238
|
} else {
|
|
224
239
|
// A cut that moves nothing more: its answer at its turn.
|
|
225
240
|
NoneOp(seq, answer, null, componentId, what, value, null)
|
|
@@ -341,6 +356,8 @@ internal class StackQueue(
|
|
|
341
356
|
// The table.
|
|
342
357
|
|
|
343
358
|
private fun enqueue(op: Op) {
|
|
359
|
+
// Anything but the release of a back gesture ends its preview first.
|
|
360
|
+
if (!(op is CutOp && op.gesture)) host.operationAccepted()
|
|
344
361
|
queued.addLast(op)
|
|
345
362
|
drain()
|
|
346
363
|
}
|
|
@@ -450,7 +467,7 @@ internal class StackQueue(
|
|
|
450
467
|
val elements = own && AnimationRules.hasElements(push)
|
|
451
468
|
// The bar's row goes with the default transition only; with an animation of its own it changes at once. The tab
|
|
452
469
|
// bar moves with its screens or by its own side.
|
|
453
|
-
tellBars(animated, own, elements, push, TabBarMotion.Move.WithPush, BarMotion.Push)
|
|
470
|
+
tellBars(animated, own, elements, push, TabBarMotion.Move.WithPush, BarMotion.Push, BarTravel.Forward)
|
|
454
471
|
host.currentChildChanged()
|
|
455
472
|
finishTakenOver(ended)
|
|
456
473
|
running += op
|
|
@@ -581,7 +598,9 @@ internal class StackQueue(
|
|
|
581
598
|
val animated = !atOnce && !op.silent && display.canAnimate && !pop.enabled.isFalse
|
|
582
599
|
val own = animated && AnimationRules.stackIsOwn(pop)
|
|
583
600
|
val elements = own && AnimationRules.hasElements(pop)
|
|
584
|
-
|
|
601
|
+
// A back gesture's preview shows the target and its bar already: the bar stays as it is.
|
|
602
|
+
val gesture = op.gesture && animated && !own && leaving.size == 1 && display.hasBackPreview(top, target)
|
|
603
|
+
tellBars(animated, own, elements, pop, TabBarMotion.Move.WithPop, BarMotion.Pop, BarTravel.Back, gesture)
|
|
585
604
|
host.currentChildChanged()
|
|
586
605
|
finishTakenOver(ended)
|
|
587
606
|
if (display.hasView) target.onViewBroughtToFront()
|
|
@@ -592,6 +611,17 @@ internal class StackQueue(
|
|
|
592
611
|
// The screens between the top and the target leave without animation.
|
|
593
612
|
leaving.drop(1).forEach(display::hide)
|
|
594
613
|
if (!animated) return finishCut(op)
|
|
614
|
+
if (gesture) {
|
|
615
|
+
op.animating += top
|
|
616
|
+
op.animating += target
|
|
617
|
+
display.finishBackGesture(
|
|
618
|
+
top,
|
|
619
|
+
target,
|
|
620
|
+
{ step(op) { screenDone(op, top) } },
|
|
621
|
+
{ step(op) { screenDone(op, target) } },
|
|
622
|
+
)
|
|
623
|
+
return
|
|
624
|
+
}
|
|
595
625
|
// Its own: no wait on a pop; the leaving screen starts from where it is.
|
|
596
626
|
if (own) return animate(op, backMoves(top, target, pop, elements, moving))
|
|
597
627
|
animate(op, listOf(Move(top, Motion.PopOut, true), Move(target, Motion.PopReveal, moving)))
|
|
@@ -657,7 +687,7 @@ internal class StackQueue(
|
|
|
657
687
|
val elements = own && !op.asPush && AnimationRules.hasElements(played)
|
|
658
688
|
val motion = if (op.asPush) BarMotion.Push else BarMotion.FadeThrough
|
|
659
689
|
val system = if (op.asPush) TabBarMotion.Move.WithPush else TabBarMotion.Move.Slide
|
|
660
|
-
tellBars(animated, own, elements, played, system, motion)
|
|
690
|
+
tellBars(animated, own, elements, played, system, motion, BarTravel.Forward)
|
|
661
691
|
host.currentChildChanged()
|
|
662
692
|
finishTakenOver(ended)
|
|
663
693
|
op.owned += moved
|
|
@@ -1081,9 +1111,10 @@ internal class StackQueue(
|
|
|
1081
1111
|
private fun awaitsTargets(animation: StackAnimationOptions, enabled: Boolean): Boolean = enabled &&
|
|
1082
1112
|
display.canAnimate && !animation.waitForRender.isFalse && AnimationRules.hasElements(animation)
|
|
1083
1113
|
|
|
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
|
|
1114
|
+
// The bars with a change of the current child. The row goes with the default transition; with an animation of its
|
|
1115
|
+
// own the top bar goes with the screens ([travel]). The tab bar rides with the system's [system] move, slides with an
|
|
1116
|
+
// animation of its own or a side of its own, and is where the new top wants it at once without animation; with
|
|
1117
|
+
// elements every bar changes at once.
|
|
1087
1118
|
private fun tellBars(
|
|
1088
1119
|
animated: Boolean,
|
|
1089
1120
|
own: Boolean,
|
|
@@ -1091,6 +1122,9 @@ internal class StackQueue(
|
|
|
1091
1122
|
animation: StackAnimationOptions,
|
|
1092
1123
|
system: TabBarMotion.Move,
|
|
1093
1124
|
row: BarMotion,
|
|
1125
|
+
travel: BarTravel,
|
|
1126
|
+
// The top bar shows the new top already (a back gesture's preview): it changes at once, the tab bar still moves.
|
|
1127
|
+
barAtOnce: Boolean = false,
|
|
1094
1128
|
) {
|
|
1095
1129
|
val tabs = animation.bottomTabs
|
|
1096
1130
|
val tabsSide = AnimationRules.barSideGiven(tabs.enter) || AnimationRules.barSideGiven(tabs.exit)
|
|
@@ -1100,7 +1134,12 @@ internal class StackQueue(
|
|
|
1100
1134
|
else -> system
|
|
1101
1135
|
}
|
|
1102
1136
|
host.tabBarTransition(move, tabs.takeIf { move == TabBarMotion.Move.Slide })
|
|
1103
|
-
|
|
1137
|
+
val barMoves = animated && !barAtOnce
|
|
1138
|
+
host.barTransition(
|
|
1139
|
+
if (barMoves && !own) row else null,
|
|
1140
|
+
barVisibility(barMoves && !elements, animation.topBar),
|
|
1141
|
+
travel.takeIf { barMoves && own && !elements },
|
|
1142
|
+
)
|
|
1104
1143
|
}
|
|
1105
1144
|
|
|
1106
1145
|
private fun barVisibility(animated: Boolean, side: TopBarEnterExitAnimationOptions): BarVisibility =
|
|
@@ -1209,6 +1248,7 @@ internal class StackQueue(
|
|
|
1209
1248
|
val target: Node,
|
|
1210
1249
|
val value: String?,
|
|
1211
1250
|
val silent: Boolean,
|
|
1251
|
+
val gesture: Boolean,
|
|
1212
1252
|
) : Op(seq, answer, null, componentId, what) {
|
|
1213
1253
|
var leaving: List<Node> = emptyList()
|
|
1214
1254
|
}
|
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()
|
|
@@ -49,7 +49,9 @@ import dev.apexnative.navigation.viewcontrollers.stack.search.StackSearchPresent
|
|
|
49
49
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.IReactView
|
|
50
50
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListener
|
|
51
51
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
52
|
+
import dev.apexnative.navigation.views.animations.AnimationRules
|
|
52
53
|
import dev.apexnative.navigation.views.animations.SideAnimator
|
|
54
|
+
import dev.apexnative.navigation.views.animations.FadeThrough
|
|
53
55
|
import dev.apexnative.navigation.views.animations.SystemTransition
|
|
54
56
|
import dev.apexnative.navigation.views.component.ComponentLayout
|
|
55
57
|
import dev.apexnative.navigation.views.stack.StackLayout
|
|
@@ -124,8 +126,13 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
124
126
|
private var largeTitleShown = false
|
|
125
127
|
private var followed: ScrollEventListener? = null
|
|
126
128
|
private var rowTarget: RowTarget? = null
|
|
129
|
+
// The large title opening with a push from a screen without one; none at rest.
|
|
130
|
+
private var largeTitleOpening: ValueAnimator? = null
|
|
127
131
|
// The picture of the part under the row in a leaving screen's overlay: the screen and the picture.
|
|
128
132
|
private var underRow: Pair<ViewController<*>, Drawable>? = null
|
|
133
|
+
// The bar goes with the screens of a transition of the stack's own, and the screen that leaves with its picture.
|
|
134
|
+
private var travelling = false
|
|
135
|
+
private var travelLeaving: ViewController<*>? = null
|
|
129
136
|
|
|
130
137
|
private enum class Part { Look, Title, Left, Right, Back, LargeTitle, Visible, TestId }
|
|
131
138
|
|
|
@@ -161,12 +168,16 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
161
168
|
* The stack's current child changes to [incoming] ([motion]: the default transition that brings it, none at once):
|
|
162
169
|
* the row's picture is taken before the bar takes the new screen's options. A push from a large title to a screen
|
|
163
170
|
* without one makes the bar lower at once: the part under the row rides with the leaving screen as a picture in its
|
|
164
|
-
* overlay, and the row's picture holds the row only. So does a pop
|
|
171
|
+
* overlay, and the row's picture holds the row only. So does a pop or a fade through (`setStackRoot`) to a screen
|
|
172
|
+
* without a large title or with its large title collapsed: a picture as tall as the large title would hold the bar
|
|
173
|
+
* at its height to the end. With a transition of the stack's own ([travel]) the bar goes with the screens instead
|
|
174
|
+
* ([beginTravel]).
|
|
165
175
|
*/
|
|
166
176
|
fun beginRowTransition(
|
|
167
177
|
motion: BarMotion?,
|
|
168
178
|
incoming: ViewController<*>?,
|
|
169
179
|
visibility: StackDisplay.BarVisibility = StackDisplay.BarVisibility.AT_ONCE,
|
|
180
|
+
travel: StackDisplay.BarTravel? = null,
|
|
170
181
|
) {
|
|
171
182
|
this.motion = motion
|
|
172
183
|
this.visibilityChange = visibility
|
|
@@ -179,14 +190,68 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
179
190
|
rowTarget?.withLargeTitle = !lowers
|
|
180
191
|
if (lowers) pictureUnderRow(bar, leaving)
|
|
181
192
|
rowTransition?.begin(motion)
|
|
193
|
+
endTravel()
|
|
194
|
+
if (travel != null && bar != null && incoming != null) beginTravel(bar, leaving, incoming, travel, visibility)
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
/** The transition that brought the current child ended: the row at rest, the picture under it gone. */
|
|
185
198
|
fun endRowTransition() {
|
|
199
|
+
// A large title still opening is open now.
|
|
200
|
+
largeTitleOpening?.let {
|
|
201
|
+
largeTitleOpening = null
|
|
202
|
+
it.end()
|
|
203
|
+
}
|
|
186
204
|
motion = null
|
|
187
205
|
visibilityChange = StackDisplay.BarVisibility.AT_ONCE
|
|
188
206
|
rowTransition?.end()
|
|
189
207
|
dropPictureUnderRow()
|
|
208
|
+
endTravel()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The bar goes with the screens of a transition of the stack's own, as each screen's bar would: a picture of the
|
|
213
|
+
* bar as it is goes with [leaving], and the bar, which takes [incoming]'s options next, is drawn flat as [incoming]
|
|
214
|
+
* moves — under [leaving] on a pop ([StackDisplay.BarTravel.Back]) (`StackLayout.barTravels`). No picture when the
|
|
215
|
+
* bar doesn't show. Nothing travels when the new top hides the bar by a side of its own
|
|
216
|
+
* (`animations.*.topBar.exit`), which plays instead, or when the bar can't be drawn in software (a `HARDWARE`
|
|
217
|
+
* bitmap in it): the bar takes the options in its place.
|
|
218
|
+
*/
|
|
219
|
+
private fun beginTravel(
|
|
220
|
+
bar: TopBar,
|
|
221
|
+
leaving: ViewController<*>?,
|
|
222
|
+
incoming: ViewController<*>,
|
|
223
|
+
direction: StackDisplay.BarTravel,
|
|
224
|
+
visibility: StackDisplay.BarVisibility,
|
|
225
|
+
) {
|
|
226
|
+
val layout = layout ?: return
|
|
227
|
+
val screen = incoming.peekView()?.takeIf(layout::holds) ?: return
|
|
228
|
+
val leavingView = leaving?.peekView()?.takeIf(layout::holds)
|
|
229
|
+
// The new top hides the bar by a side of its own: the side plays on the bar in its place instead.
|
|
230
|
+
if (host.resolvedOf(incoming).topBar.visible.isFalse && AnimationRules.barSideGiven(visibility.side?.exit)) return
|
|
231
|
+
val shows = bar.visibility == View.VISIBLE && this.visibility?.isHidden != true
|
|
232
|
+
val picture = if (shows && leavingView != null) pictureOfBar(bar) ?: return else null
|
|
233
|
+
travelling = true
|
|
234
|
+
travelLeaving = leaving.takeIf { picture != null }
|
|
235
|
+
// Flat: with an elevation the renderer would draw the bar over every screen and out of its alpha layer.
|
|
236
|
+
bar.isFlat = true
|
|
237
|
+
layout.barTravels(screen, leavingView, picture, direction == StackDisplay.BarTravel.Back)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// The bar's picture where the bar is in the stack, its moves counted.
|
|
241
|
+
private fun pictureOfBar(bar: TopBar): Drawable? {
|
|
242
|
+
val bitmap = bar.picture() ?: return null
|
|
243
|
+
val left = (bar.left + bar.translationX).roundToInt()
|
|
244
|
+
val top = (bar.top + bar.translationY).roundToInt()
|
|
245
|
+
return BitmapDrawable(bar.resources, bitmap).apply { setBounds(left, top, left + bitmap.width, top + bitmap.height) }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// The bar's travel ends: the picture goes, the bar is drawn in its place again with its shadow.
|
|
249
|
+
private fun endTravel() {
|
|
250
|
+
if (!travelling) return
|
|
251
|
+
travelling = false
|
|
252
|
+
travelLeaving = null
|
|
253
|
+
bar?.isFlat = false
|
|
254
|
+
layout?.endBarTravel()
|
|
190
255
|
}
|
|
191
256
|
|
|
192
257
|
/**
|
|
@@ -205,8 +270,9 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
205
270
|
keys.clear()
|
|
206
271
|
}
|
|
207
272
|
shownEntry = entry
|
|
208
|
-
// The screen that left with the picture under the row shows again: the picture goes.
|
|
273
|
+
// The screen that left with the picture under the row, or of the bar, shows again: the picture goes.
|
|
209
274
|
if (underRow?.first === entry) dropPictureUnderRow()
|
|
275
|
+
if (travelLeaving === entry) endTravel()
|
|
210
276
|
val options = host.resolvedOf(entry)
|
|
211
277
|
applyAll(bar, entry, screen, options, merge = false)
|
|
212
278
|
}
|
|
@@ -334,7 +400,9 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
334
400
|
/** The stack is destroyed: every component stops. */
|
|
335
401
|
fun destroy() {
|
|
336
402
|
rowTransition?.end()
|
|
403
|
+
endTravel()
|
|
337
404
|
largeTitleFollower.stop()
|
|
405
|
+
stopLargeTitleOpening()
|
|
338
406
|
collapse?.disableCollapse()
|
|
339
407
|
components.destroyAll()
|
|
340
408
|
cells.clear()
|
|
@@ -370,6 +438,9 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
370
438
|
)
|
|
371
439
|
}
|
|
372
440
|
|
|
441
|
+
/** Whether the bar shows [entry]: its parts are applied, so its place in the layout is [entry]'s. */
|
|
442
|
+
fun shows(entry: ViewController<*>): Boolean = entry === shownEntry
|
|
443
|
+
|
|
373
444
|
/** The height of the search under the row of [entry] when it is the screen the bar shows; 0 otherwise. */
|
|
374
445
|
fun searchHeightOf(entry: ViewController<*>): Int =
|
|
375
446
|
if (entry === shownEntry) bar?.searchAreaHeight ?: 0 else 0
|
|
@@ -633,11 +704,13 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
633
704
|
}
|
|
634
705
|
|
|
635
706
|
// Whether [incoming] shows less of the bar under the row than the large title now: a push to a screen without a
|
|
636
|
-
// large title, or a pop
|
|
707
|
+
// large title, or a pop or a fade through to one without a large title or with its large title collapsed. The lower
|
|
708
|
+
// bar would cut the row's picture.
|
|
637
709
|
private fun lowersBar(motion: BarMotion?, incoming: ViewController<*>, options: Options): Boolean = when (motion) {
|
|
638
710
|
BarMotion.Push -> !largeTitleShownFor(incoming, options)
|
|
639
|
-
BarMotion.Pop
|
|
640
|
-
|
|
711
|
+
BarMotion.Pop, BarMotion.FadeThrough ->
|
|
712
|
+
!largeTitleShownFor(incoming, options) || collapseOf(incoming, options) > 0f
|
|
713
|
+
null -> false
|
|
641
714
|
}
|
|
642
715
|
|
|
643
716
|
// How far [entry]'s large title is collapsed as it shows: by the offset its list had last, unless it doesn't follow
|
|
@@ -675,6 +748,11 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
675
748
|
large.fontSize.get(null), fontKey(large.font), large.collapseOnScroll.get(null), testId).joinToString()
|
|
676
749
|
if (keys[Part.LargeTitle] == key) return
|
|
677
750
|
keys[Part.LargeTitle] = key
|
|
751
|
+
// The default push or fade through from a screen without a large title opens it with the screens, on a bar that
|
|
752
|
+
// shows at rest: the bar's show and its hide on scroll move the same translation.
|
|
753
|
+
val opening = motion.takeIf { it == BarMotion.Push || it == BarMotion.FadeThrough }
|
|
754
|
+
val opens = shown && !largeTitleShown && bar.visibility == View.VISIBLE && bar.translationY == 0f
|
|
755
|
+
stopLargeTitleOpening()
|
|
678
756
|
largeTitleShown = shown
|
|
679
757
|
bar.setLargeTitleShown(shown)
|
|
680
758
|
largeTitleFollower.stop()
|
|
@@ -692,9 +770,39 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
692
770
|
// same frame: back to the screen, after light or dark changed, after a merge.
|
|
693
771
|
if (large.collapseOnScroll.isFalse) scrollOf(entry)?.forgetOffset()
|
|
694
772
|
largeTitleFollower.collapse(collapseOf(entry, options))
|
|
773
|
+
if (opens) opening?.let { openLargeTitle(bar, collapseOf(entry, options), it) }
|
|
695
774
|
if (!large.collapseOnScroll.isFalse) scrollOf(entry)?.let(largeTitleFollower::follow)
|
|
696
775
|
}
|
|
697
776
|
|
|
777
|
+
// The bar grows with the screens' [motion], on its curve and in its time, from the row's height to the large title
|
|
778
|
+
// as the screen's list left it: the large title comes out from under the row as it does when the list scrolls
|
|
779
|
+
// back. No frame shows the tall bar before the screens move.
|
|
780
|
+
private fun openLargeTitle(bar: TopBar, offset: Float, motion: BarMotion) {
|
|
781
|
+
val range = UiUtils.dpToPx(bar.context, LargeTitleView.HEIGHT)
|
|
782
|
+
val to = LargeTitleCollapse.shift(offset, range)
|
|
783
|
+
if (to >= range) return
|
|
784
|
+
bar.setLargeTitleCollapse(range.toInt())
|
|
785
|
+
largeTitleOpening = ValueAnimator.ofFloat(range, to).apply {
|
|
786
|
+
duration = if (motion == BarMotion.FadeThrough) FadeThrough.DURATION else SystemTransition.DURATION
|
|
787
|
+
interpolator = if (motion == BarMotion.FadeThrough) FadeThrough.CURVE else SystemTransition.CURVE
|
|
788
|
+
addUpdateListener { bar.setLargeTitleCollapse((it.animatedValue as Float).toInt()) }
|
|
789
|
+
addListener(object : AnimatorListenerAdapter() {
|
|
790
|
+
override fun onAnimationEnd(animation: Animator) {
|
|
791
|
+
if (largeTitleOpening === animation) largeTitleOpening = null
|
|
792
|
+
}
|
|
793
|
+
})
|
|
794
|
+
start()
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Stopped where it is: what applies next sets the large title.
|
|
799
|
+
private fun stopLargeTitleOpening() {
|
|
800
|
+
val opening = largeTitleOpening ?: return
|
|
801
|
+
largeTitleOpening = null
|
|
802
|
+
opening.removeAllUpdateListeners()
|
|
803
|
+
opening.cancel()
|
|
804
|
+
}
|
|
805
|
+
|
|
698
806
|
// Shown or hidden: with the transition by its side, or by `animate` in a merge.
|
|
699
807
|
private fun applyVisibility(options: Options, merge: Boolean) {
|
|
700
808
|
val visible = !options.topBar.visible.isFalse
|
|
@@ -702,7 +810,10 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
702
810
|
if (keys[Part.Visible] == key) return
|
|
703
811
|
keys[Part.Visible] = key
|
|
704
812
|
val change = visibilityChange
|
|
705
|
-
|
|
813
|
+
// While the bar goes with the screens it shows or hides at once, unless its side of its own plays.
|
|
814
|
+
val travels = !merge && travelling &&
|
|
815
|
+
!AnimationRules.barSideGiven(if (visible) change.side?.enter else change.side?.exit)
|
|
816
|
+
val animated = !options.topBar.animate.isFalse && (merge || change.animated) && !travels
|
|
706
817
|
visibility?.apply(visible, animated, if (merge) null else change.side)
|
|
707
818
|
}
|
|
708
819
|
|
|
@@ -960,8 +1071,11 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
960
1071
|
if (large) View.IMPORTANT_FOR_ACCESSIBILITY_NO else View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
|
961
1072
|
}
|
|
962
1073
|
|
|
1074
|
+
// The list scrolled while the large title opens: it follows the list from there.
|
|
963
1075
|
override fun onScrollOffset(offsetY: Float) {
|
|
964
|
-
if (largeTitleShown)
|
|
1076
|
+
if (!largeTitleShown) return
|
|
1077
|
+
stopLargeTitleOpening()
|
|
1078
|
+
collapse(offsetY)
|
|
965
1079
|
}
|
|
966
1080
|
|
|
967
1081
|
override fun onScrollUp(nextTranslation: Float) = Unit
|
|
@@ -94,6 +94,19 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
94
94
|
open val isInSheet: Boolean
|
|
95
95
|
get() = shownAsSheet || parent?.isInSheet == true
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* The node is the root of an overlay: the overlays set it before the node's view is made, so its screens are drawn
|
|
99
|
+
* under the status bar from their first layout (`StatusBarOptions.isHiddenOrDrawBehind`).
|
|
100
|
+
*/
|
|
101
|
+
internal var shownAsOverlay = false
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The node is in an overlay: it covers the whole window, and is drawn under the status bar by default. It holds from
|
|
105
|
+
* the start of the show, while `OverlayCoordinator.isInOverlay` answers only for the overlays it lists.
|
|
106
|
+
*/
|
|
107
|
+
open val isInOverlay: Boolean
|
|
108
|
+
get() = shownAsOverlay || parent?.isInOverlay == true
|
|
109
|
+
|
|
97
110
|
/**
|
|
98
111
|
* The node is in a side of a side menu (its left or right menu, not its center): the side goes the whole height of
|
|
99
112
|
* the window, and the node keeps no room for the status bar. The layout around answers for it
|
|
@@ -265,6 +278,12 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
265
278
|
/** Whether [handleBack] would go back. */
|
|
266
279
|
open fun canHandleBack(): Boolean = false
|
|
267
280
|
|
|
281
|
+
/**
|
|
282
|
+
* The stack whose top [handleBack] would take away with the default pop, which a back gesture previews; none when
|
|
283
|
+
* back would do anything else (hold, close a menu or a pane, a pop of its own) or nothing.
|
|
284
|
+
*/
|
|
285
|
+
internal open fun stackPoppedByBack(): StackController? = null
|
|
286
|
+
|
|
268
287
|
/** Starts the node's content (a native screen's view, a React screen's surface). */
|
|
269
288
|
open fun start() = Unit
|
|
270
289
|
|