@apexnative/navigation 1.0.0-alpha.2 → 1.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/android/CLAUDE.md +38 -9
- package/android/CORE.md +11 -4
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +2 -24
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +27 -19
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +17 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +92 -19
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +79 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +29 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +22 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +45 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +35 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +72 -16
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +164 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +136 -40
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +107 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +131 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
- package/cli/doctor/checks/app.js +61 -13
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/docs-md/architecture/transitions.md +12 -7
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +4 -2
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +20 -3
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +8 -2
- package/docs-md/screen-ui/system-ui.md +1 -1
- package/docs-md/start/installation.md +3 -1
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +13 -8
- package/ios/Contract/ANNCommandAnswer.mm +5 -20
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.mm +1 -1
- package/ios/Core/ANNErrorHandler.h +1 -1
- package/ios/Core/ANNErrorHandler.mm +3 -1
- package/ios/Core/ANNEventEmitter.h +1 -1
- package/ios/Core/ANNEventEmitter.mm +2 -2
- package/ios/Core/ANNJavaScriptCalls.h +34 -0
- package/ios/Core/ANNJavaScriptCalls.mm +61 -0
- package/ios/Core/ANNNavigationState.h +7 -3
- package/ios/Core/ANNNavigationState.mm +10 -5
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNRenderWait.h +15 -1
- package/ios/Core/ANNRenderWait.mm +119 -17
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
- package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
- package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
- package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.mm +26 -3
- package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/Image.h +11 -2
- package/ios/Options/Params/Image.mm +15 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +11 -5
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/TopBar/ANNBarButtons.h +9 -5
- package/ios/TopBar/ANNBarButtons.mm +211 -63
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
- package/ios/TopBar/ANNSearchPresenter.mm +170 -19
- package/ios/TopBar/ANNTopBarPresenter.h +5 -0
- package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
- package/ios/TurboModules/ANNTurboModule.mm +39 -21
- package/lib/module/ARCHITECTURE.md +18 -18
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/engine/commands.js +9 -7
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +13 -9
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/options.js +44 -11
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/places.js +44 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/rememberedOptions.js +1 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +22 -12
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +5 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +19 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +15 -7
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +18 -13
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +6 -3
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +18 -18
- package/src/adapters/NativeANNTurboModule.ts +5 -2
- package/src/api/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +9 -4
- package/src/api/navigation.ts +4 -1
- package/src/engine/commands.ts +19 -7
- package/src/engine/layout.ts +16 -9
- package/src/engine/options.ts +39 -11
- package/src/engine/places.ts +56 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/rememberedOptions.ts +4 -1
- package/src/engine/screens.tsx +23 -13
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/Events.ts +19 -10
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +15 -7
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/modals.ts +18 -13
- package/src/interfaces/options/system.ts +6 -3
- package/src/interfaces/options/topBar.ts +22 -9
|
@@ -29,11 +29,12 @@ import dev.apexnative.navigation.utils.dp
|
|
|
29
29
|
import kotlin.math.roundToInt
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* A modal shown as a Material bottom sheet (`modal.sheet`
|
|
33
|
-
* [BottomSheetBehavior], in the modals' CoordinatorLayout, over a scrim. It stops at
|
|
34
|
-
* `medium` at half the height, `large` at the full height under the status bar, a number
|
|
35
|
-
* user drags it between them.
|
|
36
|
-
*
|
|
32
|
+
* A modal shown as a Material bottom sheet (`pageSheet`, `formSheet`, `popover`, or `modal.sheet` without a style): the
|
|
33
|
+
* modal's view in a container with a [BottomSheetBehavior], in the modals' CoordinatorLayout, over a scrim. It stops at
|
|
34
|
+
* the detents of its options — `medium` at half the height, `large` at the full height under the status bar, a number
|
|
35
|
+
* at that height in dp — and the user drags it between them. In a window wider than [MAX_WIDTH_DP] it stands in the
|
|
36
|
+
* middle at that width. A drag down past the smallest one closes it, unless `swipeToDismiss` is false: then the sheet
|
|
37
|
+
* stays, and the try is told. The screen under it stays in place.
|
|
37
38
|
*/
|
|
38
39
|
internal class ModalSheet(
|
|
39
40
|
private val layout: CoordinatorLayout,
|
|
@@ -96,11 +97,19 @@ internal class ModalSheet(
|
|
|
96
97
|
outline.setRoundRect(0, 0, view.width, view.height + radius.roundToInt(), radius)
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
|
-
// The sheet is under the status bar: its screens leave no room for it at their top.
|
|
100
|
+
// The sheet is under the status bar: its screens leave no room for it at their top. In the middle of a wide
|
|
101
|
+
// window it reaches no side, and keeps room only for the part of a side bar that is over it.
|
|
100
102
|
ViewCompat.setOnApplyWindowInsetsListener(container) { _, insets ->
|
|
101
103
|
val types = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
|
|
102
104
|
val bars = insets.getInsets(types)
|
|
103
|
-
|
|
105
|
+
val laidOut = container.width > 0
|
|
106
|
+
val left = if (laidOut) (bars.left - container.left).coerceAtLeast(0) else bars.left
|
|
107
|
+
val right = if (laidOut) (bars.right - (layout.width - container.right)).coerceAtLeast(0) else bars.right
|
|
108
|
+
WindowInsetsCompat.Builder(insets).setInsets(types, Insets.of(left, 0, right, bars.bottom)).build()
|
|
109
|
+
}
|
|
110
|
+
// Its sides move with the window's width (a rotation, a resize): the room for the side bars follows.
|
|
111
|
+
container.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
|
|
112
|
+
if (left != oldLeft || right != oldRight) container.post { ViewCompat.requestApplyInsets(container) }
|
|
104
113
|
}
|
|
105
114
|
container.addView(content, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
|
|
106
115
|
container.addView(grabber, FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.TOP or Gravity.CENTER_HORIZONTAL))
|
|
@@ -120,7 +129,12 @@ internal class ModalSheet(
|
|
|
120
129
|
layout.addView(scrim, CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
|
|
121
130
|
// As tall as its largest detent (maxHeight), not moved down by a margin: the behavior counts its states from
|
|
122
131
|
// where the sheet is laid out.
|
|
123
|
-
|
|
132
|
+
// In a wide window it stands in the middle, as wide as a Material modal sheet at most.
|
|
133
|
+
behavior.maxWidth = MAX_WIDTH_DP.dp
|
|
134
|
+
val params = CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
|
135
|
+
params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
|
|
136
|
+
params.behavior = behavior
|
|
137
|
+
layout.addView(container, params)
|
|
124
138
|
shown = onShown
|
|
125
139
|
update(sheetOptions, swipeToDismiss, merge = null)
|
|
126
140
|
selected = sheetOptions.selectedDetent?.takeIf { it in detents }
|
|
@@ -334,10 +348,13 @@ internal class ModalSheet(
|
|
|
334
348
|
}
|
|
335
349
|
}
|
|
336
350
|
|
|
337
|
-
|
|
351
|
+
companion object {
|
|
352
|
+
/** The widest a sheet is, in dp: the Material modal bottom sheet's `maxWidth`. */
|
|
353
|
+
const val MAX_WIDTH_DP = 640
|
|
354
|
+
|
|
338
355
|
/** The dim of a Material modal bottom sheet's scrim. */
|
|
339
|
-
const val SCRIM_ALPHA = 0.32f
|
|
340
|
-
const val GRAB_AREA_DP = 48
|
|
341
|
-
const val ATTEMPT_DISTANCE_DP = 48
|
|
356
|
+
private const val SCRIM_ALPHA = 0.32f
|
|
357
|
+
private const val GRAB_AREA_DP = 48
|
|
358
|
+
private const val ATTEMPT_DISTANCE_DP = 48
|
|
342
359
|
}
|
|
343
360
|
}
|
|
@@ -4,9 +4,9 @@ import android.animation.Animator
|
|
|
4
4
|
import android.animation.AnimatorListenerAdapter
|
|
5
5
|
import android.animation.AnimatorSet
|
|
6
6
|
import android.animation.ObjectAnimator
|
|
7
|
-
import android.animation.PropertyValuesHolder
|
|
8
7
|
import android.animation.TimeInterpolator
|
|
9
8
|
import android.content.Context
|
|
9
|
+
import android.view.Gravity
|
|
10
10
|
import android.view.View
|
|
11
11
|
import android.view.ViewGroup
|
|
12
12
|
import android.view.animation.LinearInterpolator
|
|
@@ -17,6 +17,7 @@ import androidx.core.view.WindowInsetsCompat
|
|
|
17
17
|
import dev.apexnative.navigation.options.ModalAnimationOptions
|
|
18
18
|
import dev.apexnative.navigation.options.Options
|
|
19
19
|
import dev.apexnative.navigation.utils.TestIds
|
|
20
|
+
import dev.apexnative.navigation.utils.dp
|
|
20
21
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
21
22
|
import dev.apexnative.navigation.views.animations.AnimationRules
|
|
22
23
|
import dev.apexnative.navigation.views.animations.SideAnimator
|
|
@@ -62,9 +63,15 @@ internal class ModalViews(
|
|
|
62
63
|
try {
|
|
63
64
|
val view = record.root.view
|
|
64
65
|
(view.parent as? ViewGroup)?.removeView(view)
|
|
65
|
-
// A sheet's content waits at the sheet's full height, under the status bar.
|
|
66
|
+
// A sheet's content waits at the sheet's full height, under the status bar, and at its width.
|
|
66
67
|
val params = CoordinatorLayout.LayoutParams(MATCH, MATCH)
|
|
67
|
-
if (record.isSheet)
|
|
68
|
+
if (record.isSheet) {
|
|
69
|
+
params.topMargin = statusBarTop()
|
|
70
|
+
params.gravity = Gravity.CENTER_HORIZONTAL
|
|
71
|
+
// The modals layer is hidden while it has no modal: the window's content gives the width.
|
|
72
|
+
val maxWidth = ModalSheet.MAX_WIDTH_DP.dp
|
|
73
|
+
if (((modalsLayer.parent as? View)?.width ?: 0) > maxWidth) params.width = maxWidth
|
|
74
|
+
}
|
|
68
75
|
container.addView(view, params)
|
|
69
76
|
modalsLayer.addView(container, FrameLayout.LayoutParams(MATCH, MATCH))
|
|
70
77
|
} catch (e: Throwable) {
|
|
@@ -100,14 +107,13 @@ internal class ModalViews(
|
|
|
100
107
|
container.translationY = 0f
|
|
101
108
|
return playOwn(record, parts, motion, showing = true, onEnd)
|
|
102
109
|
}
|
|
110
|
+
// The system's slide over the window below: the modal comes up whole, without a fade.
|
|
103
111
|
val distance = distanceOf(container)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
109
|
-
animator.interpolator = TimeInterpolator { ModalAnimations.decelerate(it) }
|
|
110
|
-
return play(parts, animator, onEnd)
|
|
112
|
+
container.alpha = 1f
|
|
113
|
+
container.translationY = distance
|
|
114
|
+
val animator = ObjectAnimator.ofFloat(container, View.TRANSLATION_Y, distance, 0f)
|
|
115
|
+
animator.interpolator = TimeInterpolator { ModalAnimations.linearOutSlowIn(it) }
|
|
116
|
+
return play(parts, animator, ModalAnimations.SHOW_DURATION, onEnd)
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
// The root's view leaves the container and goes into the sheet's, in one turn; the sheet slides to its first
|
|
@@ -163,13 +169,11 @@ internal class ModalViews(
|
|
|
163
169
|
if (!animated || !seen) return none(onEnd)
|
|
164
170
|
if (motion != null) return playOwn(record, parts, motion, showing = false, onEnd)
|
|
165
171
|
val container = parts.container
|
|
166
|
-
val animator = ObjectAnimator.
|
|
167
|
-
container,
|
|
168
|
-
PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, container.translationY, distanceOf(container)),
|
|
169
|
-
PropertyValuesHolder.ofFloat(View.ALPHA, container.alpha, 0f),
|
|
172
|
+
val animator = ObjectAnimator.ofFloat(
|
|
173
|
+
container, View.TRANSLATION_Y, container.translationY, distanceOf(container),
|
|
170
174
|
)
|
|
171
|
-
animator.interpolator = TimeInterpolator { ModalAnimations.
|
|
172
|
-
return play(parts, animator, onEnd)
|
|
175
|
+
animator.interpolator = TimeInterpolator { ModalAnimations.accelerate(it) }
|
|
176
|
+
return play(parts, animator, ModalAnimations.DISMISS_DURATION, onEnd)
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
override fun awaitTargets(
|
|
@@ -355,8 +359,8 @@ internal class ModalViews(
|
|
|
355
359
|
private fun viewOf(node: ViewController<*>): View? =
|
|
356
360
|
parts.entries.firstOrNull { it.key.root === node }?.value?.container ?: node.peekView()
|
|
357
361
|
|
|
358
|
-
private fun play(parts: Parts, animator: Animator, onEnd: () -> Unit): () -> Unit {
|
|
359
|
-
animator.duration =
|
|
362
|
+
private fun play(parts: Parts, animator: Animator, duration: Long, onEnd: () -> Unit): () -> Unit {
|
|
363
|
+
animator.duration = duration
|
|
360
364
|
animator.addListener(object : AnimatorListenerAdapter() {
|
|
361
365
|
override fun onAnimationEnd(animation: Animator) {
|
|
362
366
|
if (parts.animator === animation) parts.animator = null
|
|
@@ -41,6 +41,10 @@ internal object BackCallbackState {
|
|
|
41
41
|
entries.removeAll { it.navigator.get().let { attached -> attached == null || attached === navigator } }
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** JavaScript listens to back (`BackHandler`): it hears a press before the navigation does. */
|
|
45
|
+
val hasJsListeners: Boolean
|
|
46
|
+
get() = jsListeners
|
|
47
|
+
|
|
44
48
|
/** `setBackListeners` of JavaScript; false again when its JavaScript goes. */
|
|
45
49
|
fun setJsListeners(active: Boolean) {
|
|
46
50
|
jsListeners = active
|
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.
|
|
@@ -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) }
|
|
@@ -401,9 +439,10 @@ class Navigator(
|
|
|
401
439
|
}
|
|
402
440
|
|
|
403
441
|
/**
|
|
404
|
-
* The activity is destroyed: every layer goes, with the events of a destroy; a `setRoot`
|
|
405
|
-
* `''` (JavaScript sets its root again in the next activity),
|
|
406
|
-
* wait for a modal with their roots' ids.
|
|
442
|
+
* The activity is destroyed: every layer goes, with the events of a destroy; a `setRoot` whose root still waited
|
|
443
|
+
* resolves with `''` (JavaScript sets its root again in the next activity), one whose root showed resolves with
|
|
444
|
+
* its id, and a modal's or an overlay's show and the commands that wait for a modal resolve with their roots' ids.
|
|
445
|
+
* Main thread only.
|
|
407
446
|
*/
|
|
408
447
|
internal fun destroyWithActivity() {
|
|
409
448
|
destroyLayers(answerPending = true)
|
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
|
}
|
|
@@ -229,8 +240,9 @@ internal class RootCoordinator(
|
|
|
229
240
|
/**
|
|
230
241
|
* The activity's layers go: the `setRoot` on its way ends, then the root it replaced, the current root and its root
|
|
231
242
|
* are destroyed, as at any destroy (their events reach only their own JavaScript). [answerPending]: the activity
|
|
232
|
-
* went
|
|
233
|
-
* (
|
|
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.
|
|
234
246
|
*/
|
|
235
247
|
fun destroyAll(answerPending: Boolean) {
|
|
236
248
|
val pending = pending
|
|
@@ -245,7 +257,9 @@ internal class RootCoordinator(
|
|
|
245
257
|
modals.replacedRootGone()
|
|
246
258
|
pending?.let(::releaseState)
|
|
247
259
|
if (pending != null && pending.phase == Phase.Waiting) firstRoot.rootDestroyed()
|
|
248
|
-
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)
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
// Waits for the screens that show when the root shows, then runs its transition. Without `waitForRender` one limit
|
|
@@ -367,6 +381,14 @@ internal class RootCoordinator(
|
|
|
367
381
|
if (!done) pending.stopLayer = stop
|
|
368
382
|
}
|
|
369
383
|
|
|
384
|
+
// The layers of [pending] not shown yet go: what they made ahead goes unseen, the others aren't made. Each on its
|
|
385
|
+
// own: one that fails doesn't keep the others.
|
|
386
|
+
private fun discardLayers(pending: Pending) {
|
|
387
|
+
val rest = pending.layers.drop(pending.nextLayer)
|
|
388
|
+
pending.nextLayer = pending.layers.size
|
|
389
|
+
rest.forEach { attempt { it.discard() } }
|
|
390
|
+
}
|
|
391
|
+
|
|
370
392
|
// A new `setRoot` came while this one was on its way.
|
|
371
393
|
private fun supersede() {
|
|
372
394
|
val pending = pending ?: return
|
|
@@ -389,14 +411,15 @@ internal class RootCoordinator(
|
|
|
389
411
|
pending.replaced = null
|
|
390
412
|
modals.replacedRootGone()
|
|
391
413
|
}
|
|
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.
|
|
414
|
+
// Its root is the current root: the modal or the overlay that waits now goes without a word, the others go
|
|
415
|
+
// unseen or aren't made; those shown stay over its root, and go with it when the new root replaces it.
|
|
394
416
|
Phase.Layers -> {
|
|
395
417
|
pending.stopLayer?.invoke()
|
|
396
418
|
pending.stopLayer = null
|
|
397
419
|
releaseState(pending)
|
|
398
420
|
}
|
|
399
421
|
}
|
|
422
|
+
discardLayers(pending)
|
|
400
423
|
pending.answer.reject(ErrorTexts.setRootReplaced(pending.root.id))
|
|
401
424
|
}
|
|
402
425
|
|
|
@@ -426,6 +449,7 @@ internal class RootCoordinator(
|
|
|
426
449
|
pending.wait = null
|
|
427
450
|
when (pending.phase) {
|
|
428
451
|
Phase.Waiting -> {
|
|
452
|
+
discardLayers(pending)
|
|
429
453
|
attempt { takeAway(pending.root) }
|
|
430
454
|
attempt { modals.rootKept() }
|
|
431
455
|
attempt { firstRoot.rootDestroyed() }
|
|
@@ -451,6 +475,7 @@ internal class RootCoordinator(
|
|
|
451
475
|
pending.stopLayer = null
|
|
452
476
|
}
|
|
453
477
|
}
|
|
478
|
+
discardLayers(pending)
|
|
454
479
|
// A root that is up takes the splash away, as at its show.
|
|
455
480
|
if (pending.phase != Phase.Waiting) attempt { onRootShown() }
|
|
456
481
|
attempt { releaseState(pending) }
|
|
@@ -482,6 +507,7 @@ internal class RootCoordinator(
|
|
|
482
507
|
// The root was destroyed while it waited, by something other than a newer `setRoot` or the activity going.
|
|
483
508
|
private fun rootGone(pending: Pending) {
|
|
484
509
|
this.pending = null
|
|
510
|
+
discardLayers(pending)
|
|
485
511
|
releaseState(pending)
|
|
486
512
|
layer.remove(pending.root)
|
|
487
513
|
modals.rootKept()
|
|
@@ -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
|
}
|
|
@@ -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) }
|
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)
|
|
@@ -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
|
|
@@ -130,7 +131,7 @@ internal class SideMenuController(
|
|
|
130
131
|
|
|
131
132
|
// `mergeOptions` with the side menu's id: its own options, then what a merge does, then the layouts around.
|
|
132
133
|
override fun mergeOptions(options: Options) {
|
|
133
|
-
|
|
134
|
+
mergeOwnOptions(options)
|
|
134
135
|
applyMerge(options)
|
|
135
136
|
performOnParentController { it.mergeChildOptions(SideMenuRules.withoutSideMenu(options), this) }
|
|
136
137
|
}
|
|
@@ -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
|
|
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)
|