@apexnative/navigation 1.0.0-alpha.3 → 1.0.0-alpha.5
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 +26 -9
- package/android/CORE.md +32 -17
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +25 -9
- 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/AnimationsOptions.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/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +33 -22
- 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/ReactView.kt +39 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +10 -5
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +47 -46
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +34 -7
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -4
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- 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/ArrangementController.kt +3 -2
- 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 +25 -17
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +5 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +45 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +25 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +8 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +8 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +61 -15
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +25 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +2 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +85 -29
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +5 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- 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 +14 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +56 -21
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/cli/doctor/checks/app.js +61 -13
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +6 -7
- package/docs-md/containers/modals.md +2 -2
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/help/troubleshooting.md +22 -5
- package/docs-md/index.json +4 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +24 -2
- package/docs-md/start/installation.md +4 -2
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +18 -13
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +28 -30
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -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 -5
- package/ios/Core/ANNNavigationState.mm +8 -7
- 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/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- 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/ANNPreviewPush.h +5 -5
- package/ios/Layouts/Stack/ANNPreviewPush.mm +85 -22
- 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/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.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 +52 -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/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.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 +7 -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.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +23 -4
- 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/stack.d.ts +9 -0
- package/lib/typescript/interfaces/options/stack.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/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +1 -1
- package/src/api/navigation.ts +4 -1
- package/src/engine/commands.ts +19 -7
- package/src/engine/layout.ts +16 -9
- package/src/engine/options.ts +39 -11
- package/src/engine/places.ts +65 -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/Options.ts +1 -0
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +24 -4
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/topBar.ts +22 -9
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
|
@@ -288,8 +288,8 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
|
|
|
288
288
|
screen.whenContentMounted(
|
|
289
289
|
waiting = waiting,
|
|
290
290
|
mounted = {
|
|
291
|
-
// The frame of the mount: the screen shows transparent and lays out
|
|
292
|
-
//
|
|
291
|
+
// The frame of the mount: the screen shows transparent and lays out. The pre-draw of that frame hands
|
|
292
|
+
// the start to the queue, whose transition begins behind the calls JavaScript made before.
|
|
293
293
|
screen.peekView()?.let {
|
|
294
294
|
it.alpha = 0f
|
|
295
295
|
it.visibility = View.VISIBLE
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt
CHANGED
|
@@ -172,19 +172,20 @@ internal class FabPresenter(
|
|
|
172
172
|
alignStart = options.alignHorizontally.get("right") == "left"
|
|
173
173
|
val source = source
|
|
174
174
|
fab?.let { fab ->
|
|
175
|
-
applyButton(fab, options)
|
|
175
|
+
applyButton(fab, options, mayWait = fab.drawable != null || options.visible.isFalse)
|
|
176
176
|
TestIds.apply(fab, options.testID.takeIf { it.hasValue() }?.get())
|
|
177
177
|
val id = shownId.orEmpty()
|
|
178
178
|
fab.setOnClickListener { press(source, id) }
|
|
179
179
|
}
|
|
180
180
|
menu?.let { menu ->
|
|
181
|
-
applyButton(menu.main, options)
|
|
181
|
+
applyButton(menu.main, options, mayWait = menu.main.drawable != null || options.visible.isFalse)
|
|
182
182
|
TestIds.apply(menu.main, options.testID.takeIf { it.hasValue() }?.get())
|
|
183
183
|
menu.scrimColor = options.scrimColor.get(activity, null)
|
|
184
184
|
menu.setActions(options.actions.orEmpty().map { action ->
|
|
185
185
|
val actionId = action.id.get("")
|
|
186
186
|
Fab(activity, actionId).also { button ->
|
|
187
|
-
|
|
187
|
+
// The actions show only while the menu is open.
|
|
188
|
+
applyButton(button, action, mayWait = !menu.isOpen)
|
|
188
189
|
TestIds.apply(button, action.testID.takeIf { it.hasValue() }?.get())
|
|
189
190
|
button.setOnClickListener {
|
|
190
191
|
press(source, actionId)
|
|
@@ -205,9 +206,10 @@ internal class FabPresenter(
|
|
|
205
206
|
place()
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
|
|
209
|
+
// [mayWait]: the button can take its icon when it is ready (it shows one, or it stays hidden), see [Fab.applyIcon].
|
|
210
|
+
private fun applyButton(button: Fab, options: FabOptions, mayWait: Boolean) {
|
|
209
211
|
val icon = options.icon.takeIf { it.hasValue() }?.get()?.ifEmpty { null }
|
|
210
|
-
if (icon != null) button.applyIcon(icon, options.iconColor)
|
|
212
|
+
if (icon != null) button.applyIcon(icon, options.iconColor, mayWait)
|
|
211
213
|
applyColors(button, options)
|
|
212
214
|
button.setMini(options.size.get("regular") == "mini")
|
|
213
215
|
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt
CHANGED
|
@@ -36,6 +36,8 @@ import dev.apexnative.navigation.views.stack.topbar.search.SuggestionList
|
|
|
36
36
|
class ScreenSearch(
|
|
37
37
|
private val host: Host,
|
|
38
38
|
private val screenId: String,
|
|
39
|
+
/** The sender of the JavaScript the screen was made for: silent once it went. */
|
|
40
|
+
private val emitter: EventEmitter,
|
|
39
41
|
private val context: Context,
|
|
40
42
|
private val loadIcon: (String) -> Drawable?,
|
|
41
43
|
/** A scope the user picks goes into the screen's options, so it stays when they apply again. */
|
|
@@ -45,7 +47,6 @@ class ScreenSearch(
|
|
|
45
47
|
/** The search changed what the bar shows (open or closed, its text): shown again if it is the shown one. */
|
|
46
48
|
fun refresh(search: ScreenSearch)
|
|
47
49
|
fun isShown(search: ScreenSearch): Boolean
|
|
48
|
-
fun emitter(): EventEmitter?
|
|
49
50
|
fun keyboard(view: View, show: Boolean)
|
|
50
51
|
/** The stack's view: a search view goes over it. */
|
|
51
52
|
fun stackView(): ViewGroup?
|
|
@@ -402,7 +403,7 @@ class ScreenSearch(
|
|
|
402
403
|
|
|
403
404
|
// A pick sends searchSuggestionPicked with the suggestion's id; the text stays.
|
|
404
405
|
private fun pickSuggestion(suggestionId: String) {
|
|
405
|
-
|
|
406
|
+
emitter.emitSearchSuggestionPicked(screenId, suggestionId)
|
|
406
407
|
setSuggestions(emptyList())
|
|
407
408
|
}
|
|
408
409
|
|
|
@@ -426,18 +427,15 @@ class ScreenSearch(
|
|
|
426
427
|
private fun sendUpdate(
|
|
427
428
|
focused: Boolean = isActive && (panel?.searchView?.editText ?: searchField.editText).hasFocus(),
|
|
428
429
|
) {
|
|
429
|
-
val emitter = host.emitter() ?: return
|
|
430
430
|
eventTargets().forEach { emitter.emitSearchBarUpdated(it, text, focused, scope()) }
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
private fun sendCancel() {
|
|
434
|
-
val emitter = host.emitter() ?: return
|
|
435
434
|
eventTargets().forEach { emitter.emitSearchBarCancelPressed(it) }
|
|
436
435
|
sendUpdate()
|
|
437
436
|
}
|
|
438
437
|
|
|
439
438
|
private fun sendSubmit() {
|
|
440
|
-
val emitter = host.emitter() ?: return
|
|
441
439
|
eventTargets().forEach { emitter.emitSearchBarSubmitted(it, text, scope()) }
|
|
442
440
|
}
|
|
443
441
|
|
|
@@ -12,9 +12,7 @@ import androidx.appcompat.content.res.AppCompatResources
|
|
|
12
12
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
13
13
|
import androidx.core.view.WindowCompat
|
|
14
14
|
import androidx.core.view.WindowInsetsCompat
|
|
15
|
-
import com.facebook.react.ReactApplication
|
|
16
15
|
import dev.apexnative.navigation.options.SearchBarOptions
|
|
17
|
-
import dev.apexnative.navigation.options.params.Text
|
|
18
16
|
import dev.apexnative.navigation.options.params.ThemeColour
|
|
19
17
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
20
18
|
import dev.apexnative.navigation.utils.dp
|
|
@@ -138,9 +136,6 @@ class StackSearchPresenter(
|
|
|
138
136
|
refresh()
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
override fun emitter(): EventEmitter? =
|
|
142
|
-
(activity.application as? ReactApplication)?.reactHost?.currentReactContext?.let { EventEmitter(it) }
|
|
143
|
-
|
|
144
139
|
override fun keyboard(view: View, show: Boolean) {
|
|
145
140
|
val controller = WindowCompat.getInsetsController(activity.window, view)
|
|
146
141
|
if (show) controller.show(WindowInsetsCompat.Type.ime()) else controller.hide(WindowInsetsCompat.Type.ime())
|
|
@@ -148,10 +143,12 @@ class StackSearchPresenter(
|
|
|
148
143
|
|
|
149
144
|
private fun searchOf(screen: ViewController<*>): ScreenSearch =
|
|
150
145
|
searches.getOrPut(screen.view) {
|
|
151
|
-
// The search of a pair of screens is its primary's: its events name that screen, which keeps the scope
|
|
146
|
+
// The search of a pair of screens is its primary's: its events name that screen, which keeps the scope in
|
|
147
|
+
// its user layer, where a merge that sends the same options again doesn't take it away.
|
|
152
148
|
val owner = (screen as? OptionsScreen)?.optionsScreen ?: screen
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
// Its events go to the JavaScript the screen was made for, and to no other.
|
|
150
|
+
ScreenSearch(this, owner.id, EventEmitter.of(owner.jsGeneration), styleContext(), loadIcon) { scope ->
|
|
151
|
+
owner.changeChoices { it.pickScope(scope) }
|
|
155
152
|
}
|
|
156
153
|
}
|
|
157
154
|
|
|
@@ -5,7 +5,6 @@ import dev.apexnative.navigation.options.Options
|
|
|
5
5
|
import dev.apexnative.navigation.options.TitleComponentOptions
|
|
6
6
|
import dev.apexnative.navigation.options.TopBarBackgroundComponentOptions
|
|
7
7
|
import dev.apexnative.navigation.options.params.Text
|
|
8
|
-
import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
9
8
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.IReactView
|
|
10
9
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
11
10
|
|
|
@@ -15,7 +14,8 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
|
15
14
|
* same root, never made again; another replaces it, and the old one stops at once. A background with the same `name`
|
|
16
15
|
* and `componentId` on several screens is one root, shared; it stops when no screen of the stack names it any more. The
|
|
17
16
|
* roots go with their screen ([screenDestroyed]) or the stack ([destroyAll]). Each root renders at once, without
|
|
18
|
-
* waiting for its layout: [makeRoot]
|
|
17
|
+
* waiting for its layout: [makeRoot] makes it so (`ReactView`'s `rendersAtOnce`), and each answers by its own render
|
|
18
|
+
* when several live roots show one id (`ScreenReadiness`). Main thread only.
|
|
19
19
|
*/
|
|
20
20
|
internal class BarComponents(private val makeRoot: (componentId: String, name: String) -> IReactView) {
|
|
21
21
|
/** A root of the bar: what it renders, and its content. */
|
|
@@ -151,18 +151,10 @@ internal class BarComponents(private val makeRoot: (componentId: String, name: S
|
|
|
151
151
|
backgrounds.remove(key)?.let(::stop)
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
private fun make(key: Key): Root {
|
|
156
|
-
val root = Root(key.name, key.componentId, makeRoot(key.componentId, key.name))
|
|
157
|
-
LiveIds.add(key.componentId)
|
|
158
|
-
return root
|
|
159
|
-
}
|
|
154
|
+
private fun make(key: Key): Root = Root(key.name, key.componentId, makeRoot(key.componentId, key.name))
|
|
160
155
|
|
|
161
|
-
// Stops a root once;
|
|
162
|
-
private fun stop(root: Root)
|
|
163
|
-
root.view.destroy()
|
|
164
|
-
if (LiveIds.remove(root.componentId)) ScreenReadiness.remove(root.componentId)
|
|
165
|
-
}
|
|
156
|
+
// Stops a root once; its content takes its mark of rendering at once with it.
|
|
157
|
+
private fun stop(root: Root) = root.view.destroy()
|
|
166
158
|
|
|
167
159
|
private fun keyOf(name: Text, componentId: Text, id: Text): Key? {
|
|
168
160
|
val componentName = name.get(null)?.takeIf { it.isNotEmpty() } ?: return null
|
|
@@ -170,42 +162,4 @@ internal class BarComponents(private val makeRoot: (componentId: String, name: S
|
|
|
170
162
|
?: return null
|
|
171
163
|
return Key(componentName, rootId)
|
|
172
164
|
}
|
|
173
|
-
|
|
174
|
-
companion object {
|
|
175
|
-
/** Whether a bar root with [id] is alive now, in any stack. */
|
|
176
|
-
fun isLive(id: String): Boolean = LiveIds.has(id)
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Another root that renders at once lives with [id] (a cell of the tabs' custom row): it counts as the bar's
|
|
180
|
-
* roots do, so a root that stops clears the id's readiness only when it was the last one with it.
|
|
181
|
-
*/
|
|
182
|
-
internal fun rootMade(id: String) = LiveIds.add(id)
|
|
183
|
-
|
|
184
|
-
/** A root of [rootMade] stopped; true when no live root has [id] any more. */
|
|
185
|
-
internal fun rootStopped(id: String): Boolean = LiveIds.remove(id)
|
|
186
|
-
|
|
187
|
-
/** The JavaScript went (a reload): no id counts as held by a live root any more, as `ScreenReadiness.reset`. */
|
|
188
|
-
fun reset() = LiveIds.clear()
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// The ids of the bar roots alive now, in every stack: a root that stops clears its id's readiness only when it was
|
|
192
|
-
// the last one with it.
|
|
193
|
-
private object LiveIds {
|
|
194
|
-
private val counts = HashMap<String, Int>()
|
|
195
|
-
|
|
196
|
-
fun has(id: String): Boolean = (counts[id] ?: 0) > 0
|
|
197
|
-
|
|
198
|
-
fun clear() = counts.clear()
|
|
199
|
-
|
|
200
|
-
fun add(id: String) {
|
|
201
|
-
counts[id] = (counts[id] ?: 0) + 1
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// True when no root with the id is left.
|
|
205
|
-
fun remove(id: String): Boolean {
|
|
206
|
-
val count = (counts[id] ?: return true) - 1
|
|
207
|
-
if (count <= 0) counts.remove(id) else counts[id] = count
|
|
208
|
-
return count <= 0
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
165
|
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
package dev.apexnative.navigation.viewcontrollers.stack.topbar
|
|
2
2
|
|
|
3
|
-
import dev.apexnative.navigation.options.ButtonMenuOptions
|
|
4
|
-
import dev.apexnative.navigation.options.ButtonOptions
|
|
5
3
|
import dev.apexnative.navigation.options.Options
|
|
6
|
-
import dev.apexnative.navigation.options.
|
|
4
|
+
import dev.apexnative.navigation.options.UserChoices
|
|
7
5
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
|
-
* The check of a single-selection menu the user picked: written
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* The check of a single-selection menu the user picked: written into the screen's user layer
|
|
9
|
+
* ([ViewController.changeChoices]), never into its own options nor into a menu other screens share (the stack's, the
|
|
10
|
+
* defaults'). It goes over the menu as the screen's options resolve, by the button's id and the item's id, and stays
|
|
11
|
+
* until the app changes that menu's checks or takes the item away. Main thread only.
|
|
14
12
|
*/
|
|
15
13
|
internal object MenuChecks {
|
|
16
14
|
/**
|
|
@@ -19,54 +17,13 @@ internal object MenuChecks {
|
|
|
19
17
|
*/
|
|
20
18
|
fun write(screen: ViewController<*>, resolved: Options, buttonId: String?, itemId: String) {
|
|
21
19
|
val topBar = resolved.topBar
|
|
22
|
-
if (buttonId == null) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
val left = topBar.buttons.left?.any { it.id.get(null) == buttonId } == true
|
|
33
|
-
val list = (if (left) topBar.buttons.left else topBar.buttons.right) ?: return
|
|
34
|
-
val menu = list.firstOrNull { it.id.get(null) == buttonId }?.menu ?: return
|
|
35
|
-
if (!checks(menu, itemId)) return
|
|
36
|
-
screen.changeOptions { own ->
|
|
37
|
-
val ownList = (if (left) own.topBar.buttons.left else own.topBar.buttons.right)
|
|
38
|
-
?: list.map(ButtonOptions::copyWithMenu)
|
|
39
|
-
val button = ownList.firstOrNull { it.id.get(null) == buttonId } ?: return@changeOptions
|
|
40
|
-
val copy = (button.menu ?: menu).copyMenu()
|
|
41
|
-
check(copy, itemId)
|
|
42
|
-
button.menu = copy
|
|
43
|
-
if (left) own.topBar.buttons.left = ownList else own.topBar.buttons.right = ownList
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Whether picking [itemId] moves a check: its level has `singleSelection`. */
|
|
48
|
-
fun checks(menu: ButtonMenuOptions, itemId: String): Boolean = levelOf(menu.items, menu.singleSelection, itemId) == true
|
|
49
|
-
|
|
50
|
-
/** Moves the check of [itemId]'s level to it, in [menu] itself. */
|
|
51
|
-
fun check(menu: ButtonMenuOptions, itemId: String) {
|
|
52
|
-
mark(menu.items, menu.singleSelection, itemId)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Whether the level holding [itemId] has single selection; null when [itemId] isn't in [elements].
|
|
56
|
-
private fun levelOf(elements: List<ButtonMenuOptions.Element>, single: Boolean, itemId: String): Boolean? {
|
|
57
|
-
if (elements.any { it is ButtonMenuOptions.Item && it.id == itemId }) return single
|
|
58
|
-
for (element in elements) {
|
|
59
|
-
if (element !is ButtonMenuOptions.Submenu) continue
|
|
60
|
-
levelOf(element.items, element.singleSelection, itemId)?.let { return it }
|
|
61
|
-
}
|
|
62
|
-
return null
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
private fun mark(elements: List<ButtonMenuOptions.Element>, single: Boolean, itemId: String): Boolean {
|
|
66
|
-
if (elements.any { it is ButtonMenuOptions.Item && it.id == itemId }) {
|
|
67
|
-
if (single) elements.forEach { if (it is ButtonMenuOptions.Item) it.checked = it.id == itemId }
|
|
68
|
-
return true
|
|
69
|
-
}
|
|
70
|
-
return elements.any { it is ButtonMenuOptions.Submenu && mark(it.items, it.singleSelection, itemId) }
|
|
20
|
+
val menu = if (buttonId == null) {
|
|
21
|
+
topBar.overflowMenu
|
|
22
|
+
} else {
|
|
23
|
+
(topBar.buttons.left.orEmpty() + topBar.buttons.right.orEmpty()).firstOrNull { it.id.get(null) == buttonId }
|
|
24
|
+
?.menu
|
|
25
|
+
} ?: return
|
|
26
|
+
if (!UserChoices.checks(menu, itemId)) return
|
|
27
|
+
screen.changeChoices { it.check(buttonId, menu, itemId) }
|
|
71
28
|
}
|
|
72
29
|
}
|
|
@@ -41,7 +41,6 @@ import dev.apexnative.navigation.utils.ImageLoader
|
|
|
41
41
|
import dev.apexnative.navigation.utils.ImageLoadingListenerAdapter
|
|
42
42
|
import dev.apexnative.navigation.utils.TestIds
|
|
43
43
|
import dev.apexnative.navigation.utils.UiUtils
|
|
44
|
-
import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
45
44
|
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackState
|
|
46
45
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay
|
|
47
46
|
import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.BarMotion
|
|
@@ -936,14 +935,10 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
936
935
|
return resolved.component
|
|
937
936
|
}
|
|
938
937
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
// this root's, which answers by its own render.
|
|
944
|
-
if (BarComponents.isLive(componentId)) ScreenReadiness.forgetRender(componentId)
|
|
945
|
-
return ReactView(activity, componentId, name)
|
|
946
|
-
}
|
|
938
|
+
// A component of the bar renders at once, without waiting for its layout. Another live root may show the same id
|
|
939
|
+
// (a component of the stack's options on two screens): each answers by the render of its own surface.
|
|
940
|
+
private fun makeRoot(componentId: String, name: String): IReactView =
|
|
941
|
+
ReactView(activity, componentId, name, rendersAtOnce = true)
|
|
947
942
|
|
|
948
943
|
private fun loadIcon(uri: String): Drawable? {
|
|
949
944
|
var loaded: Drawable? = null
|
|
@@ -1008,8 +1003,16 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
1008
1003
|
button.priority.get(null),
|
|
1009
1004
|
).joinToString("|")
|
|
1010
1005
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1006
|
+
// What the overflow menu shows, its submenus and checks with it: a check the app moves, or one of the user layer
|
|
1007
|
+
// that leaves, fills the menu again.
|
|
1008
|
+
private fun menuKey(menu: ButtonMenuOptions): String = elementsKey(menu.items) + menu.testID
|
|
1009
|
+
|
|
1010
|
+
private fun elementsKey(elements: List<ButtonMenuOptions.Element>): String = elements.joinToString("|") {
|
|
1011
|
+
"${it.title}:${it.icon}:${it.testID}" + when (it) {
|
|
1012
|
+
is ButtonMenuOptions.Item -> ":${it.checked}:${it.disabled}"
|
|
1013
|
+
is ButtonMenuOptions.Submenu -> "[${elementsKey(it.items)}]"
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1013
1016
|
|
|
1014
1017
|
private fun fontKey(font: FontOptions): String =
|
|
1015
1018
|
listOf(font.fontFamily, font.fontStyle, font.fontWeight).joinToString { it.get(null).toString() }
|
|
@@ -6,6 +6,7 @@ import android.view.ViewGroup
|
|
|
6
6
|
import dev.apexnative.navigation.options.DefaultOptions
|
|
7
7
|
import dev.apexnative.navigation.options.Options
|
|
8
8
|
import dev.apexnative.navigation.options.OptionsCache
|
|
9
|
+
import dev.apexnative.navigation.options.UserChoices
|
|
9
10
|
import dev.apexnative.navigation.options.params.Bool
|
|
10
11
|
import dev.apexnative.navigation.options.params.NullBool
|
|
11
12
|
import dev.apexnative.navigation.react.CommandListener
|
|
@@ -30,11 +31,11 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
30
31
|
var initialOptions: Options,
|
|
31
32
|
) {
|
|
32
33
|
/**
|
|
33
|
-
* The node's own options: those it was made with, with every `mergeOptions`
|
|
34
|
-
* Writing them is one of the
|
|
35
|
-
* options, keeps the object (a stripped copy when it has a one-shot option: the
|
|
36
|
-
* makes every resolved result stale. Don't change the object in place:
|
|
37
|
-
* changed copy.
|
|
34
|
+
* The node's own options: those it was made with, with every `mergeOptions` over them; what the user chose in the
|
|
35
|
+
* native UI is not here but in [choices]. Writing them is one of the places the options change ([OptionsCache]):
|
|
36
|
+
* the setter takes away the one-shot options, keeps the object (a stripped copy when it has a one-shot option: the
|
|
37
|
+
* caller's object never changes), and makes every resolved result stale. Don't change the object in place:
|
|
38
|
+
* [changeOptions] or [mergeOptions] write a changed copy.
|
|
38
39
|
*/
|
|
39
40
|
var options: Options = initialOptions.copy().also(Options::clearOneShotOptions)
|
|
40
41
|
set(value) {
|
|
@@ -42,6 +43,10 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
42
43
|
OptionsCache.optionsChanged()
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
// The user layer: what the user chose in the native UI (a menu's check, a search scope), over the node's own
|
|
47
|
+
// options as they resolve ([changeChoices]).
|
|
48
|
+
private val choices = UserChoices()
|
|
49
|
+
|
|
45
50
|
/**
|
|
46
51
|
* The JavaScript the node was made for (`ReloadCoordinator`): the events of the node and its screens reach only
|
|
47
52
|
* that one.
|
|
@@ -156,9 +161,9 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
/**
|
|
159
|
-
* The node's options over those of the layouts around it, without the default options
|
|
160
|
-
* then the node's own. A new object at each call, which the caller may
|
|
161
|
-
* what it shows with: that is [resolveShownOptions].
|
|
164
|
+
* The node's options over those of the layouts around it, without the default options and without the user layer
|
|
165
|
+
* ([changeChoices]): the parents' resolution, then the node's own. A new object at each call, which the caller may
|
|
166
|
+
* change. For a layout of nodes this is not what it shows with: that is [resolveShownOptions].
|
|
162
167
|
*/
|
|
163
168
|
open fun resolveCurrentOptions(): Options {
|
|
164
169
|
val resolved = parent?.resolveChildOptions(this) ?: Options()
|
|
@@ -166,21 +171,21 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
/**
|
|
169
|
-
* The node's options over those of the layouts around it and [defaultOptions]
|
|
170
|
-
* default options ([DefaultOptions.current]) the result is
|
|
171
|
-
* ([OptionsCache]) and the layouts around the node and its current
|
|
172
|
-
* changes it copies it first. Main thread only.
|
|
174
|
+
* The node's options over those of the layouts around it and [defaultOptions], with the user layer over them
|
|
175
|
+
* ([changeChoices]): what it shows with. With the app's default options ([DefaultOptions.current]) the result is
|
|
176
|
+
* kept and given again while no options changed ([OptionsCache]) and the layouts around the node and its current
|
|
177
|
+
* children are the same: it is read-only, whoever changes it copies it first. Main thread only.
|
|
173
178
|
*
|
|
174
179
|
* For a layout of nodes (a stack, tabs, a side menu) it is the layout's own options over those around it, without
|
|
175
180
|
* any child's: not what the layout shows with, which [resolveShownOptions] gives.
|
|
176
181
|
*/
|
|
177
182
|
open fun resolveCurrentOptions(defaultOptions: Options): Options {
|
|
178
|
-
if (defaultOptions !== DefaultOptions.current) return
|
|
183
|
+
if (defaultOptions !== DefaultOptions.current) return withChoices(defaultOptions)
|
|
179
184
|
val generation = OptionsCache.generation
|
|
180
185
|
val along = resolvedAlong()
|
|
181
186
|
val kept = resolved
|
|
182
187
|
if (kept != null && resolvedGeneration == generation && sameNodes(along, resolvedAlong)) return kept
|
|
183
|
-
val result =
|
|
188
|
+
val result = withChoices(defaultOptions)
|
|
184
189
|
resolved = result
|
|
185
190
|
resolvedGeneration = generation
|
|
186
191
|
resolvedAlong = along.map(::WeakReference)
|
|
@@ -196,6 +201,10 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
196
201
|
*/
|
|
197
202
|
open fun resolveShownOptions(defaultOptions: Options): Options = resolveCurrentOptions(defaultOptions)
|
|
198
203
|
|
|
204
|
+
// The resolution in its order: the defaults, the parents, the node's own options, then the user layer over them.
|
|
205
|
+
private fun withChoices(defaultOptions: Options): Options =
|
|
206
|
+
choices.applyTo(resolveCurrentOptions().withDefaultOptions(defaultOptions))
|
|
207
|
+
|
|
199
208
|
// The layouts around this node, nearest first, then its current children down the visible chain.
|
|
200
209
|
private fun resolvedAlong(): List<ViewController<*>> {
|
|
201
210
|
val nodes = ArrayList<ViewController<*>>()
|
|
@@ -216,9 +225,9 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
216
225
|
nodes.size == kept.size && nodes.indices.all { nodes[it] === kept[it].get() }
|
|
217
226
|
|
|
218
227
|
/**
|
|
219
|
-
* Changes the node's own options: [change] gets a copy, which is stored ([options]).
|
|
220
|
-
*
|
|
221
|
-
*
|
|
228
|
+
* Changes the node's own options: [change] gets a copy, which is stored ([options]). It is for the library's own
|
|
229
|
+
* bookkeeping of a merge (a side color written into the node's buttons, the back button's visibility); what the
|
|
230
|
+
* user chose goes through [changeChoices].
|
|
222
231
|
*/
|
|
223
232
|
fun changeOptions(change: (Options) -> Unit) {
|
|
224
233
|
val copy = options.copy()
|
|
@@ -226,14 +235,40 @@ abstract class ViewController<V : ViewGroup>(
|
|
|
226
235
|
options = copy
|
|
227
236
|
}
|
|
228
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Changes the node's user layer: what the user chose in the native UI (a single-selection menu's check, a search
|
|
240
|
+
* scope). It goes over the node's own options as they resolve, by the ids of the buttons, items and scopes, and
|
|
241
|
+
* stays through an unrelated `mergeOptions`, the same values sent again and `setDefaultOptions`; a merge that
|
|
242
|
+
* changes the chosen field takes it away ([mergeOwnOptions]). Every resolved result is stale after it
|
|
243
|
+
* ([OptionsCache]).
|
|
244
|
+
*/
|
|
245
|
+
internal fun changeChoices(change: (UserChoices) -> Unit) {
|
|
246
|
+
change(choices)
|
|
247
|
+
OptionsCache.optionsChanged()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* [delta] of a `mergeOptions` goes over the node's own options ([options]); a choice of the user layer whose field
|
|
252
|
+
* the merge changes, or whose button, item or scope it took away, leaves, so the app's value shows
|
|
253
|
+
* (`UserChoices.yieldTo`). Every `mergeOptions` into a node writes its own options this way.
|
|
254
|
+
*/
|
|
255
|
+
protected fun mergeOwnOptions(delta: Options) {
|
|
256
|
+
val own = options
|
|
257
|
+
val before = if (choices.isEmpty) null else resolveCurrentOptions().withDefaultOptions(DefaultOptions.current)
|
|
258
|
+
options = own.copy().mergeWith(delta)
|
|
259
|
+
before ?: return
|
|
260
|
+
val after = resolveCurrentOptions().withDefaultOptions(DefaultOptions.current)
|
|
261
|
+
if (choices.yieldTo(delta, own, before, after)) OptionsCache.optionsChanged()
|
|
262
|
+
}
|
|
263
|
+
|
|
229
264
|
/**
|
|
230
265
|
* `mergeOptions` with the node's id (`mergeOptions`): [options] go over its own and stay there (one-shot options
|
|
231
|
-
* not kept); what shows applies them ([applyMergedOptions], when the view shows; otherwise as it
|
|
232
|
-
* layouts around it apply their part (`ParentController.mergeChildOptions`, each passes it on).
|
|
233
|
-
* carry the one-shot options to them. Containers that hand the merge to a child override it.
|
|
266
|
+
* not kept, [mergeOwnOptions]); what shows applies them ([applyMergedOptions], when the view shows; otherwise as it
|
|
267
|
+
* appears); the layouts around it apply their part (`ParentController.mergeChildOptions`, each passes it on).
|
|
268
|
+
* [options] still carry the one-shot options to them. Containers that hand the merge to a child override it.
|
|
234
269
|
*/
|
|
235
270
|
open fun mergeOptions(options: Options) {
|
|
236
|
-
|
|
271
|
+
mergeOwnOptions(options)
|
|
237
272
|
if (isViewShown) applyMergedOptions(options)
|
|
238
273
|
performOnParentController { it.mergeChildOptions(options, this) }
|
|
239
274
|
}
|
|
@@ -228,13 +228,15 @@ class BottomTabs(context: Context) : BottomNavigationView(MaterialTheme.context(
|
|
|
228
228
|
applyLongPressesAgain()
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
/** Null: the item has no icon. */
|
|
232
|
+
fun setIcon(index: Int, icon: Drawable?) {
|
|
232
233
|
val item = items.getOrNull(index) ?: return
|
|
233
234
|
if (item.icon == icon) return
|
|
234
235
|
updateIcon(index, item.copy(icon = icon))
|
|
235
236
|
}
|
|
236
237
|
|
|
237
|
-
|
|
238
|
+
/** Null: the item shows its icon when selected too. */
|
|
239
|
+
fun setSelectedIcon(index: Int, icon: Drawable?) {
|
|
238
240
|
val item = items.getOrNull(index) ?: return
|
|
239
241
|
if (item.selectedIcon == icon) return
|
|
240
242
|
updateIcon(index, item.copy(selectedIcon = icon))
|
package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt
CHANGED
|
@@ -22,9 +22,6 @@ open class CustomBottomTabItemView(context: Context) : FrameLayout(context) {
|
|
|
22
22
|
private var state: TabCellState? = null
|
|
23
23
|
private var destroyed = false
|
|
24
24
|
|
|
25
|
-
/** Runs once as the cell stops: what was kept for its content's id goes. */
|
|
26
|
-
internal var onDestroyed: (() -> Unit)? = null
|
|
27
|
-
|
|
28
25
|
/** The content the cell shows, filling it, made with [state] as its first props. */
|
|
29
26
|
internal fun show(view: IReactView, state: TabCellState) {
|
|
30
27
|
content = view
|
|
@@ -68,8 +65,6 @@ open class CustomBottomTabItemView(context: Context) : FrameLayout(context) {
|
|
|
68
65
|
didDisappear()
|
|
69
66
|
destroyed = true
|
|
70
67
|
content?.destroy()
|
|
71
|
-
onDestroyed?.invoke()
|
|
72
|
-
onDestroyed = null
|
|
73
68
|
}
|
|
74
69
|
|
|
75
70
|
/** The props of [state] as React Native takes them. */
|
|
@@ -4,12 +4,11 @@ import android.content.Context
|
|
|
4
4
|
import android.content.res.ColorStateList
|
|
5
5
|
import android.content.res.Configuration
|
|
6
6
|
import android.graphics.Color
|
|
7
|
-
import android.graphics.drawable.Drawable
|
|
8
7
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
9
8
|
import dev.apexnative.navigation.options.params.ThemeColour
|
|
10
|
-
import dev.apexnative.navigation.utils.
|
|
11
|
-
import dev.apexnative.navigation.utils.ImageLoadingListenerAdapter
|
|
9
|
+
import dev.apexnative.navigation.utils.ImageRequests
|
|
12
10
|
import dev.apexnative.navigation.utils.MaterialTheme
|
|
11
|
+
import dev.apexnative.navigation.utils.OptionImages
|
|
13
12
|
import dev.apexnative.navigation.utils.UiUtils
|
|
14
13
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListener
|
|
15
14
|
import androidx.appcompat.R as AppCompatR
|
|
@@ -29,6 +28,7 @@ class Fab(context: Context, val fabId: String) : FloatingActionButton(MaterialTh
|
|
|
29
28
|
// The icon's color of the options: a container color merged later kept the theme's icon color instead.
|
|
30
29
|
private var iconColor: Int? = null
|
|
31
30
|
private var optionRippleColor: Int? = null
|
|
31
|
+
private val icons = ImageRequests<Unit>(OptionImages.source(this.context))
|
|
32
32
|
|
|
33
33
|
init {
|
|
34
34
|
// Material's theme overlay of the button is a copy of the theme, made once: it follows a light/dark switch too.
|
|
@@ -36,14 +36,18 @@ class Fab(context: Context, val fabId: String) : FloatingActionButton(MaterialTh
|
|
|
36
36
|
MaterialTheme.followOverlay(this.context)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* The icon of the options, with its color. [mayWait]: the button can show it when it is ready (it shows an icon
|
|
41
|
+
* already, it is hidden with `visible: false`, or it is an action of a closed menu), so an icon the cache doesn't
|
|
42
|
+
* have loads off the main thread; the last icon asked for wins. Otherwise it loads at once. An icon that doesn't
|
|
43
|
+
* load leaves the button without an icon.
|
|
44
|
+
*/
|
|
45
|
+
fun applyIcon(icon: String, color: ThemeColour, mayWait: Boolean) {
|
|
40
46
|
if (color.hasValue()) iconColor = color.get()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
+
icons.request(Unit, icon, mayWait) { drawable ->
|
|
48
|
+
setImageDrawable(drawable)
|
|
49
|
+
iconColor?.let { imageTintList = ColorStateList.valueOf(it) }
|
|
50
|
+
}
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/** iconColor alone, without a new icon: it did nothing in a merge. */
|