react-native-screens 5.0.0-alpha.2 → 5.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +87 -0
- package/README.md +0 -7
- package/android/src/main/cpp/NativeProxy.cpp +30 -6
- package/android/src/main/cpp/NativeProxy.h +7 -1
- package/android/src/main/java/com/swmansion/rnscreens/common/container/Container.kt +8 -0
- package/android/src/main/java/com/swmansion/rnscreens/common/container/ContainerItem.kt +13 -1
- package/android/src/main/java/com/swmansion/rnscreens/common/text/ReactTextAppearance.kt +62 -0
- package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearance.kt +20 -0
- package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearanceDefaults.kt +65 -0
- package/android/src/main/java/com/swmansion/rnscreens/helpers/PropIconResolver.kt +17 -23
- package/android/src/main/java/com/swmansion/rnscreens/legacy/Screen.kt +3 -1
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStack.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetBehaviorController.kt +49 -18
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetDimensionsCoordinator.kt +65 -40
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetKeyboardCoordinator.kt +101 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetNativeDismissCoordinator.kt +0 -6
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetAvailableHeightProvider.kt +44 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialog.kt +35 -3
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialogManager.kt +33 -106
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/interfaces/FormSheetDialogEventEmitter.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/model/FormSheetDetents.kt +61 -15
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetAnimatorFactory.kt +8 -9
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDimmingManager.kt +115 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDismissalOrigin.kt +22 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentation.kt +173 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationManager.kt +141 -49
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationState.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetStackRegistry.kt +28 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/event/FormSheetDismissEvent.kt +29 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostEventEmitter.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostViewManager.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderApplicator.kt +369 -58
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderBackPressHandler.kt +11 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt +143 -98
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt +155 -23
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderContentScrimDrawable.kt +36 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderCollapsedTitleGravityMode.kt +6 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfig.kt +170 -167
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigViewManager.kt +385 -12
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationObserver.kt +0 -4
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationProviding.kt +32 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderDelegate.kt +0 -7
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderInvalidationFlags.kt +7 -2
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuApplicator.kt +27 -280
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuController.kt +448 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuDelegate.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuMapper.kt +28 -20
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuConfig.kt +3 -55
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuGroupMetadata.kt +1 -10
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuItemConfig.kt +1 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuModel.kt +180 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementOptions.kt +57 -6
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementRawUpdate.kt +3 -1
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementUpdate.kt +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuIconResolver.kt +4 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuUpdateQueue.kt +23 -9
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainer.kt +103 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHost.kt +13 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHostViewManager.kt +14 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreen.kt +4 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt +20 -1
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragmentDelegate.kt +12 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceCoordinator.kt +2 -4
- package/android/src/main/java/com/swmansion/rnscreens/tabs/container/MenuHelpers.kt +5 -23
- package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainer.kt +46 -25
- package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHost.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHostA11yCoordinator.kt +23 -2
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreen.kt +17 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenFragment.kt +3 -0
- package/android/src/main/java/com/swmansion/rnscreens/utils/DimensionUtils.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/utils/StyleUtils.kt +22 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_available_space.xml +8 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_entire_space.xml +8 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_available_space.xml +8 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_entire_space.xml +8 -0
- package/cpp/legacy/RNSScreenRemovalListener.cpp +31 -1
- package/cpp/legacy/RNSScreenRemovalListener.h +18 -3
- package/ios/RNSEnums.h +2 -0
- package/ios/conversion/RNSConversions.h +7 -108
- package/ios/legacy/RNSFullWindowOverlay.mm +1 -1
- package/ios/legacy/RNSModalScreen.mm +1 -1
- package/ios/legacy/RNSScreen.mm +8 -19
- package/ios/legacy/RNSScreenStackHeaderConfig.mm +3 -1
- package/ios/legacy/RNSScreenStackHeaderSubview.mm +1 -1
- package/ios/legacy/RNSScreenWindowTraits.mm +5 -13
- package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.h +1 -3
- package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.mm +11 -22
- package/ios/modals/form-sheet/RNSFormSheetContentController.h +1 -1
- package/ios/modals/form-sheet/RNSFormSheetContentController.mm +7 -18
- package/ios/modals/form-sheet/RNSFormSheetHostComponentView.mm +4 -4
- package/ios/modals/form-sheet/RNSFormSheetHostShadowStateProxy.mm +2 -2
- package/ios/modals/form-sheet/RNSFormSheetUpdateFlags.h +3 -0
- package/ios/safe-area/RNSSafeAreaViewComponentView.mm +2 -2
- package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.mm +2 -3
- package/ios/split/RNSSplitAppearanceApplicator.h +4 -4
- package/ios/split/RNSSplitAppearanceApplicator.mm +53 -56
- package/ios/split/RNSSplitHostComponentEventEmitter.h +1 -0
- package/ios/split/RNSSplitHostComponentEventEmitter.mm +1 -1
- package/ios/split/RNSSplitHostComponentView.h +5 -15
- package/ios/split/RNSSplitHostComponentView.mm +55 -6
- package/ios/split/RNSSplitHostController.h +25 -6
- package/ios/split/RNSSplitHostController.mm +24 -92
- package/ios/split/RNSSplitHostProviders.h +67 -0
- package/ios/split/RNSSplitScreenComponentView.h +0 -39
- package/ios/split/RNSSplitScreenComponentView.mm +27 -10
- package/ios/split/RNSSplitScreenController.h +26 -3
- package/ios/split/RNSSplitScreenController.mm +13 -30
- package/ios/split/RNSSplitScreenShadowStateProxy.h +1 -40
- package/ios/split/RNSSplitScreenShadowStateProxy.mm +1 -26
- package/ios/split/conversion/RNSConversions-SplitView.h +56 -0
- package/ios/{conversion → split/conversion}/RNSConversions-SplitView.mm +4 -1
- package/ios/stack/conversion/RNSConversions-Stack.h +32 -0
- package/ios/stack/conversion/RNSConversions-Stack.mm +55 -0
- package/ios/stack/header/RNSStackHeaderAppearanceMapper.h +26 -0
- package/ios/stack/header/RNSStackHeaderAppearanceMapper.mm +88 -0
- package/ios/stack/header/RNSStackHeaderConfigComponentView.h +10 -1
- package/ios/stack/header/RNSStackHeaderConfigComponentView.mm +67 -35
- package/ios/stack/header/RNSStackHeaderConfigDataProviding.h +6 -0
- package/ios/stack/header/RNSStackHeaderConfigShadowStateProxy.mm +1 -1
- package/ios/stack/header/RNSStackHeaderIconResolver.mm +7 -0
- package/ios/stack/header/RNSStackHeaderItemComponentView.h +3 -1
- package/ios/stack/header/RNSStackHeaderItemComponentView.mm +15 -2
- package/ios/stack/header/RNSStackHeaderItemDataProviding.h +2 -0
- package/ios/stack/header/RNSStackHeaderItemShadowStateProxy.mm +1 -1
- package/ios/stack/header/RNSStackHeaderItemSpacerComponentView.mm +2 -1
- package/ios/stack/header/RNSStackHeaderMenuMapper.mm +4 -0
- package/ios/stack/host/RNSStackNavigationBar.h +25 -0
- package/ios/stack/host/RNSStackNavigationBar.mm +80 -0
- package/ios/stack/host/RNSStackNavigationBarCoordinator.h +8 -0
- package/ios/stack/host/RNSStackNavigationBarCoordinator.mm +11 -0
- package/ios/stack/host/RNSStackNavigationController.mm +13 -5
- package/ios/stack/host/RNSStackOperation.h +0 -2
- package/ios/stack/screen/RNSStackScreenComponentView.mm +9 -2
- package/ios/stack/screen/RNSStackScreenController.mm +12 -0
- package/ios/stack/screen/RNSStackScreenHeaderCoordinator.h +8 -0
- package/ios/stack/screen/RNSStackScreenHeaderCoordinator.mm +98 -16
- package/ios/tabs/RNSTabBarAppearanceCoordinator.mm +40 -9
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.h +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.mm +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryContentComponentView.mm +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryEventEmitter.mm +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryShadowStateProxy.mm +1 -1
- package/ios/tabs/conversion/RNSConversions-Tabs.h +78 -0
- package/ios/{conversion → tabs/conversion}/RNSConversions-Tabs.mm +8 -71
- package/ios/tabs/extensions/RNSTabsHostComponentView+RNSImageLoader.h +1 -1
- package/ios/tabs/host/RNSTabsHostComponentView.h +2 -0
- package/ios/tabs/host/RNSTabsHostComponentView.mm +10 -3
- package/ios/tabs/host/RNSTabsHostEventEmitter.mm +1 -1
- package/ios/tabs/screen/RNSTabsScreenComponentView.mm +20 -5
- package/ios/utils/RNSDefines.h +3 -0
- package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js +34 -19
- package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js +33 -1
- package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
- package/lib/commonjs/components/stack/host/StackHost.js +9 -6
- package/lib/commonjs/components/stack/host/StackHost.js.map +1 -1
- package/lib/commonjs/components/stack/index.js.map +1 -1
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js +1 -0
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHostNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
- package/lib/module/components/stack/header/StackHeaderConfig.android.js +35 -20
- package/lib/module/components/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/module/components/stack/header/StackHeaderConfig.ios.js +34 -2
- package/lib/module/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
- package/lib/module/components/stack/host/StackHost.js +9 -6
- package/lib/module/components/stack/host/StackHost.js.map +1 -1
- package/lib/module/components/stack/index.js.map +1 -1
- package/lib/module/components/tabs/host/TabsHost.ios.js +1 -0
- package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHostNativeComponent.js.map +1 -1
- package/lib/module/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
- package/lib/typescript/components/modals/form-sheet/FormSheet.types.d.ts +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts +616 -21
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts +226 -2
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts +2 -2
- package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts +2 -0
- package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/host/StackHost.d.ts +1 -1
- package/lib/typescript/components/stack/host/StackHost.d.ts.map +1 -1
- package/lib/typescript/components/stack/host/StackHost.types.d.ts +24 -0
- package/lib/typescript/components/stack/host/StackHost.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/index.d.ts +2 -2
- package/lib/typescript/components/stack/index.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.ios.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts +16 -0
- package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.types.d.ts +3 -0
- package/lib/typescript/components/tabs/host/TabsHost.types.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts +52 -0
- package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts +25 -1
- package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts +2 -0
- package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts +3 -1
- package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +1 -0
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
- package/package.json +8 -7
- package/react-native.config.js +3 -0
- package/src/components/modals/form-sheet/FormSheet.types.ts +1 -1
- package/src/components/stack/header/StackHeaderConfig.android.tsx +48 -23
- package/src/components/stack/header/StackHeaderConfig.android.types.ts +637 -21
- package/src/components/stack/header/StackHeaderConfig.ios.tsx +45 -1
- package/src/components/stack/header/StackHeaderConfig.ios.types.ts +238 -2
- package/src/components/stack/header/StackHeaderConfig.types.ts +2 -2
- package/src/components/stack/header/ios/StackHeaderItem.ios.types.ts +2 -0
- package/src/components/stack/host/StackHost.tsx +4 -2
- package/src/components/stack/host/StackHost.types.ts +26 -0
- package/src/components/stack/index.ts +6 -1
- package/src/components/tabs/host/TabsHost.ios.tsx +1 -0
- package/src/components/tabs/host/TabsHost.ios.types.ts +16 -0
- package/src/components/tabs/host/TabsHost.types.ts +3 -0
- package/src/fabric/stack/StackHeaderConfigAndroidNativeComponent.ts +78 -0
- package/src/fabric/stack/StackHeaderConfigIOSNativeComponent.ts +32 -0
- package/src/fabric/stack/StackHeaderItemIOSNativeComponent.ts +2 -0
- package/src/fabric/stack/StackHostNativeComponent.ts +11 -2
- package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingView.kt +0 -83
- package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewManager.kt +0 -89
- package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewPointerEvents.kt +0 -18
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuSelectionController.kt +0 -83
- package/ios/conversion/RNSConversions-Stack.h +0 -32
- package/ios/conversion/RNSConversions-Stack.mm +0 -41
- package/ios/conversion/always_false.h +0 -31
- /package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.h +0 -0
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
package com.swmansion.rnscreens.stack.header.toolbar.model
|
|
2
2
|
|
|
3
|
-
import android.graphics.drawable.Drawable
|
|
4
|
-
|
|
5
3
|
internal data class StackHeaderToolbarMenuItemConfig(
|
|
6
4
|
val id: String,
|
|
7
5
|
val title: String?,
|
|
@@ -11,11 +9,11 @@ internal data class StackHeaderToolbarMenuItemConfig(
|
|
|
11
9
|
val hidden: Boolean,
|
|
12
10
|
val disabled: Boolean,
|
|
13
11
|
val showAsAction: StackHeaderToolbarMenuItemShowAsAction,
|
|
14
|
-
val icon: Drawable?,
|
|
15
12
|
val iconTintColorNormal: Int?,
|
|
16
13
|
val iconTintColorPressed: Int?,
|
|
17
14
|
val iconTintColorFocused: Int?,
|
|
18
15
|
val iconTintColorDisabled: Int?,
|
|
16
|
+
val iconSource: StackHeaderToolbarMenuItemIconSource,
|
|
19
17
|
val groupId: String?,
|
|
20
18
|
val itemType: StackHeaderToolbarMenuItemType,
|
|
21
19
|
val initialToggleState: Boolean,
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
package com.swmansion.rnscreens.stack.header.toolbar.model
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Immutable snapshot of a parsed toolbar menu: the config tree plus every
|
|
5
|
+
* derived index the runtime needs. Built and validated in one step by [from],
|
|
6
|
+
* so a successfully constructed model is always internally consistent.
|
|
7
|
+
*/
|
|
8
|
+
internal class StackHeaderToolbarMenuModel private constructor(
|
|
9
|
+
val config: StackHeaderToolbarMenuConfig,
|
|
10
|
+
val elementById: Map<String, StackHeaderToolbarMenuElementConfig>,
|
|
11
|
+
// Element string id <-> int id expected by Android menus.
|
|
12
|
+
val forwardIdMap: Map<String, Int>,
|
|
13
|
+
val reverseIdMap: Map<Int, String>,
|
|
14
|
+
val forwardGroupIdMap: Map<String, Int>,
|
|
15
|
+
val groupMetadata: StackHeaderToolbarMenuGroupMetadata,
|
|
16
|
+
) {
|
|
17
|
+
companion object {
|
|
18
|
+
val EMPTY = from(StackHeaderToolbarMenuConfig(emptyList(), emptyList()))
|
|
19
|
+
|
|
20
|
+
/** Derives all indices and validates [config]; throws on an invalid menu. */
|
|
21
|
+
fun from(config: StackHeaderToolbarMenuConfig): StackHeaderToolbarMenuModel {
|
|
22
|
+
val forwardIdMap = mutableMapOf<String, Int>()
|
|
23
|
+
val reverseIdMap = mutableMapOf<Int, String>()
|
|
24
|
+
var elementCounter = 1
|
|
25
|
+
assignElementIds(config.children, forwardIdMap, reverseIdMap) { elementCounter++ }
|
|
26
|
+
|
|
27
|
+
val forwardGroupIdMap = mutableMapOf<String, Int>()
|
|
28
|
+
var groupCounter = 1
|
|
29
|
+
assignGroupIds(config, forwardGroupIdMap) { groupCounter++ }
|
|
30
|
+
|
|
31
|
+
val itemGroupMap = mutableMapOf<String, String>()
|
|
32
|
+
val groupSingleSelection = mutableMapOf<String, Boolean>()
|
|
33
|
+
val groupMemberItems = mutableMapOf<String, MutableList<String>>()
|
|
34
|
+
collectGroupMetadata(config, itemGroupMap, groupSingleSelection, groupMemberItems)
|
|
35
|
+
|
|
36
|
+
validateRadioInitialSelection(config)
|
|
37
|
+
validateItemTypes(config)
|
|
38
|
+
|
|
39
|
+
val elementById = mutableMapOf<String, StackHeaderToolbarMenuElementConfig>()
|
|
40
|
+
collectElements(config, elementById)
|
|
41
|
+
|
|
42
|
+
return StackHeaderToolbarMenuModel(
|
|
43
|
+
config = config,
|
|
44
|
+
elementById = elementById.toMap(),
|
|
45
|
+
forwardIdMap = forwardIdMap.toMap(),
|
|
46
|
+
reverseIdMap = reverseIdMap.toMap(),
|
|
47
|
+
forwardGroupIdMap = forwardGroupIdMap.toMap(),
|
|
48
|
+
groupMetadata =
|
|
49
|
+
StackHeaderToolbarMenuGroupMetadata(
|
|
50
|
+
itemGroupMap.toMap(),
|
|
51
|
+
groupSingleSelection.toMap(),
|
|
52
|
+
groupMemberItems.mapValues { it.value.toList() },
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private fun assignElementIds(
|
|
60
|
+
elements: List<StackHeaderToolbarMenuElementConfig>,
|
|
61
|
+
forwardIdMap: MutableMap<String, Int>,
|
|
62
|
+
reverseIdMap: MutableMap<Int, String>,
|
|
63
|
+
nextId: () -> Int,
|
|
64
|
+
) {
|
|
65
|
+
for (element in elements) {
|
|
66
|
+
require(element.item.id !in forwardIdMap) {
|
|
67
|
+
"[RNScreens] Duplicate toolbar menu item id: '${element.item.id}'. Item IDs must be unique across the entire menu."
|
|
68
|
+
}
|
|
69
|
+
val nativeId = nextId()
|
|
70
|
+
forwardIdMap[element.item.id] = nativeId
|
|
71
|
+
reverseIdMap[nativeId] = element.item.id
|
|
72
|
+
if (element is StackHeaderToolbarMenuElementConfig.Submenu) {
|
|
73
|
+
assignElementIds(element.menu.children, forwardIdMap, reverseIdMap, nextId)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private fun assignGroupIds(
|
|
79
|
+
menuConfig: StackHeaderToolbarMenuConfig,
|
|
80
|
+
forwardMap: MutableMap<String, Int>,
|
|
81
|
+
nextId: () -> Int,
|
|
82
|
+
) {
|
|
83
|
+
for (group in menuConfig.groups) {
|
|
84
|
+
require(group.groupId !in forwardMap) {
|
|
85
|
+
"[RNScreens] Duplicate toolbar menu group id: '${group.groupId}'. Group IDs must be unique across the entire menu."
|
|
86
|
+
}
|
|
87
|
+
forwardMap[group.groupId] = nextId()
|
|
88
|
+
}
|
|
89
|
+
for (element in menuConfig.children) {
|
|
90
|
+
if (element is StackHeaderToolbarMenuElementConfig.Submenu) {
|
|
91
|
+
assignGroupIds(element.menu, forwardMap, nextId)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private fun collectGroupMetadata(
|
|
97
|
+
config: StackHeaderToolbarMenuConfig,
|
|
98
|
+
itemGroupMap: MutableMap<String, String>,
|
|
99
|
+
groupSingleSelection: MutableMap<String, Boolean>,
|
|
100
|
+
groupMemberItems: MutableMap<String, MutableList<String>>,
|
|
101
|
+
) {
|
|
102
|
+
val localGroupIds = config.groups.map { it.groupId }.toSet()
|
|
103
|
+
for (group in config.groups) {
|
|
104
|
+
groupSingleSelection[group.groupId] = group.singleSelection
|
|
105
|
+
groupMemberItems.getOrPut(group.groupId) { mutableListOf() }
|
|
106
|
+
}
|
|
107
|
+
for (element in config.children) {
|
|
108
|
+
element.item.groupId?.let { gid ->
|
|
109
|
+
require(element is StackHeaderToolbarMenuElementConfig.MenuItem) {
|
|
110
|
+
"[RNScreens] Submenu '${element.item.id}' cannot belong to a group. " +
|
|
111
|
+
"Only elements of type 'menuItem' can have a groupId."
|
|
112
|
+
}
|
|
113
|
+
require(gid in localGroupIds) {
|
|
114
|
+
"[RNScreens] Menu item '${element.item.id}' references group '$gid' " +
|
|
115
|
+
"which is not defined at the same menu level. " +
|
|
116
|
+
"Groups cannot span submenus."
|
|
117
|
+
}
|
|
118
|
+
itemGroupMap[element.item.id] = gid
|
|
119
|
+
groupMemberItems[gid]!!.add(element.item.id)
|
|
120
|
+
}
|
|
121
|
+
if (element is StackHeaderToolbarMenuElementConfig.Submenu) {
|
|
122
|
+
collectGroupMetadata(element.menu, itemGroupMap, groupSingleSelection, groupMemberItems)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private fun validateRadioInitialSelection(menuConfig: StackHeaderToolbarMenuConfig) {
|
|
128
|
+
for (group in menuConfig.groups) {
|
|
129
|
+
if (!group.singleSelection) continue
|
|
130
|
+
var count = 0
|
|
131
|
+
for (element in menuConfig.children) {
|
|
132
|
+
if (element.item.groupId == group.groupId && element.item.initialToggleState) {
|
|
133
|
+
count++
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
require(count <= 1) {
|
|
137
|
+
"[RNScreens] Radio group '${group.groupId}' has $count items with " +
|
|
138
|
+
"initialToggleState=true. At most 1 is allowed for single-selection groups."
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (element in menuConfig.children) {
|
|
142
|
+
if (element is StackHeaderToolbarMenuElementConfig.Submenu) {
|
|
143
|
+
validateRadioInitialSelection(element.menu)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private fun validateItemTypes(menuConfig: StackHeaderToolbarMenuConfig) {
|
|
149
|
+
for (element in menuConfig.children) {
|
|
150
|
+
val item = element.item
|
|
151
|
+
when (item.itemType) {
|
|
152
|
+
StackHeaderToolbarMenuItemType.TOGGLE ->
|
|
153
|
+
require(item.groupId != null) {
|
|
154
|
+
"[RNScreens] Menu item '${item.id}' has itemType=TOGGLE but no groupId. " +
|
|
155
|
+
"Toggle items must belong to a group."
|
|
156
|
+
}
|
|
157
|
+
StackHeaderToolbarMenuItemType.ACTION ->
|
|
158
|
+
require(item.groupId == null) {
|
|
159
|
+
"[RNScreens] Menu item '${item.id}' has itemType=ACTION " +
|
|
160
|
+
"and belongs to a group. Action items cannot belong to groups."
|
|
161
|
+
}
|
|
162
|
+
StackHeaderToolbarMenuItemType.AUTOMATIC -> Unit
|
|
163
|
+
}
|
|
164
|
+
if (element is StackHeaderToolbarMenuElementConfig.Submenu) {
|
|
165
|
+
validateItemTypes(element.menu)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private fun collectElements(
|
|
171
|
+
config: StackHeaderToolbarMenuConfig,
|
|
172
|
+
elements: MutableMap<String, StackHeaderToolbarMenuElementConfig>,
|
|
173
|
+
) {
|
|
174
|
+
for (element in config.children) {
|
|
175
|
+
elements[element.item.id] = element
|
|
176
|
+
if (element is StackHeaderToolbarMenuElementConfig.Submenu) {
|
|
177
|
+
collectElements(element.menu, elements)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.swmansion.rnscreens.stack.header.toolbar.update
|
|
2
2
|
|
|
3
3
|
import android.graphics.drawable.Drawable
|
|
4
|
+
import com.swmansion.rnscreens.stack.header.toolbar.model.StackHeaderToolbarMenuElementConfig
|
|
4
5
|
import com.swmansion.rnscreens.stack.header.toolbar.model.StackHeaderToolbarMenuItemShowAsAction
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -16,12 +17,11 @@ internal data class StackHeaderToolbarMenuElementOptions(
|
|
|
16
17
|
val hidden: Boolean? = null,
|
|
17
18
|
val disabled: Boolean? = null,
|
|
18
19
|
val showAsAction: StackHeaderToolbarMenuItemShowAsAction? = null,
|
|
19
|
-
val icon: StackHeaderToolbarFieldUpdate<Drawable
|
|
20
|
-
val iconTintColorNormal: StackHeaderToolbarFieldUpdate<Int
|
|
21
|
-
val iconTintColorPressed: StackHeaderToolbarFieldUpdate<Int
|
|
22
|
-
val iconTintColorFocused: StackHeaderToolbarFieldUpdate<Int
|
|
23
|
-
val iconTintColorDisabled: StackHeaderToolbarFieldUpdate<Int
|
|
24
|
-
val checked: Boolean? = null,
|
|
20
|
+
val icon: StackHeaderToolbarFieldUpdate<Drawable>? = null,
|
|
21
|
+
val iconTintColorNormal: StackHeaderToolbarFieldUpdate<Int>? = null,
|
|
22
|
+
val iconTintColorPressed: StackHeaderToolbarFieldUpdate<Int>? = null,
|
|
23
|
+
val iconTintColorFocused: StackHeaderToolbarFieldUpdate<Int>? = null,
|
|
24
|
+
val iconTintColorDisabled: StackHeaderToolbarFieldUpdate<Int>? = null,
|
|
25
25
|
val menuTitle: StackHeaderToolbarFieldUpdate<String>? = null,
|
|
26
26
|
) {
|
|
27
27
|
val requiresIconTintColorUpdate: Boolean
|
|
@@ -30,4 +30,55 @@ internal data class StackHeaderToolbarMenuElementOptions(
|
|
|
30
30
|
iconTintColorPressed != null ||
|
|
31
31
|
iconTintColorFocused != null ||
|
|
32
32
|
iconTintColorDisabled != null
|
|
33
|
+
|
|
34
|
+
val isEmpty: Boolean
|
|
35
|
+
get() = this == DEFAULT
|
|
36
|
+
|
|
37
|
+
/** Field-wise merge where [newer]'s non-null fields win. */
|
|
38
|
+
internal fun mergedWith(newer: StackHeaderToolbarMenuElementOptions) =
|
|
39
|
+
StackHeaderToolbarMenuElementOptions(
|
|
40
|
+
title = newer.title ?: title,
|
|
41
|
+
titleCondensed = newer.titleCondensed ?: titleCondensed,
|
|
42
|
+
tooltipText = newer.tooltipText ?: tooltipText,
|
|
43
|
+
accessibilityLabel = newer.accessibilityLabel ?: accessibilityLabel,
|
|
44
|
+
hidden = newer.hidden ?: hidden,
|
|
45
|
+
disabled = newer.disabled ?: disabled,
|
|
46
|
+
showAsAction = newer.showAsAction ?: showAsAction,
|
|
47
|
+
icon = newer.icon ?: icon,
|
|
48
|
+
iconTintColorNormal = newer.iconTintColorNormal ?: iconTintColorNormal,
|
|
49
|
+
iconTintColorPressed = newer.iconTintColorPressed ?: iconTintColorPressed,
|
|
50
|
+
iconTintColorFocused = newer.iconTintColorFocused ?: iconTintColorFocused,
|
|
51
|
+
iconTintColorDisabled = newer.iconTintColorDisabled ?: iconTintColorDisabled,
|
|
52
|
+
menuTitle = newer.menuTitle ?: menuTitle,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
private companion object {
|
|
56
|
+
val DEFAULT = StackHeaderToolbarMenuElementOptions()
|
|
57
|
+
}
|
|
33
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The element's declared configuration as absolute options — every field is
|
|
62
|
+
* Set/Reset, never "leave unchanged". The icon slot is always Reset: parsed
|
|
63
|
+
* configs carry no Drawables; resolved icons overlay this at application time.
|
|
64
|
+
* [StackHeaderToolbarMenuElementOptions.menuTitle] stays "unchanged" for a
|
|
65
|
+
* plain item, which has no submenu header to write.
|
|
66
|
+
*/
|
|
67
|
+
internal fun StackHeaderToolbarMenuElementConfig.toOptions() =
|
|
68
|
+
StackHeaderToolbarMenuElementOptions(
|
|
69
|
+
title = StackHeaderToolbarFieldUpdate.from(item.title),
|
|
70
|
+
titleCondensed = StackHeaderToolbarFieldUpdate.from(item.titleCondensed),
|
|
71
|
+
tooltipText = StackHeaderToolbarFieldUpdate.from(item.tooltipText),
|
|
72
|
+
accessibilityLabel = StackHeaderToolbarFieldUpdate.from(item.accessibilityLabel),
|
|
73
|
+
hidden = item.hidden,
|
|
74
|
+
disabled = item.disabled,
|
|
75
|
+
showAsAction = item.showAsAction,
|
|
76
|
+
icon = StackHeaderToolbarFieldUpdate.Reset,
|
|
77
|
+
iconTintColorNormal = StackHeaderToolbarFieldUpdate.from(item.iconTintColorNormal),
|
|
78
|
+
iconTintColorPressed = StackHeaderToolbarFieldUpdate.from(item.iconTintColorPressed),
|
|
79
|
+
iconTintColorFocused = StackHeaderToolbarFieldUpdate.from(item.iconTintColorFocused),
|
|
80
|
+
iconTintColorDisabled = StackHeaderToolbarFieldUpdate.from(item.iconTintColorDisabled),
|
|
81
|
+
menuTitle =
|
|
82
|
+
(this as? StackHeaderToolbarMenuElementConfig.Submenu)
|
|
83
|
+
?.let { StackHeaderToolbarFieldUpdate.from(it.menuTitle) },
|
|
84
|
+
)
|
|
@@ -4,10 +4,12 @@ import com.swmansion.rnscreens.stack.header.toolbar.model.StackHeaderToolbarMenu
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A single toolbar menu element update carried by an `updateToolbarMenuElements`
|
|
7
|
-
* view command, before its icon (if any) has been resolved.
|
|
7
|
+
* view command, before its icon (if any) has been resolved. `checked` travels
|
|
8
|
+
* outside [options]: it mutates group selections rather than the item itself.
|
|
8
9
|
*/
|
|
9
10
|
internal data class StackHeaderToolbarMenuElementRawUpdate(
|
|
10
11
|
val id: String,
|
|
11
12
|
val options: StackHeaderToolbarMenuElementOptions,
|
|
12
13
|
val iconSource: StackHeaderToolbarMenuItemIconSource?,
|
|
14
|
+
val checked: Boolean?,
|
|
13
15
|
)
|
|
@@ -4,14 +4,15 @@ import android.graphics.drawable.Drawable
|
|
|
4
4
|
import com.swmansion.rnscreens.stack.header.toolbar.model.StackHeaderToolbarMenuItemIconSource
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Loads the icon a menu element's source points at.
|
|
8
8
|
*
|
|
9
9
|
* [onResolved] must be invoked exactly once — synchronously or asynchronously, always on the
|
|
10
|
-
* main thread — with the
|
|
10
|
+
* main thread — with the loaded drawable, or `null` when the source resolves to no icon or
|
|
11
|
+
* fails to load.
|
|
11
12
|
*/
|
|
12
13
|
internal fun interface StackHeaderToolbarMenuIconResolver {
|
|
13
14
|
fun resolve(
|
|
14
15
|
iconSource: StackHeaderToolbarMenuItemIconSource,
|
|
15
|
-
onResolved: (icon:
|
|
16
|
+
onResolved: (icon: Drawable?) -> Unit,
|
|
16
17
|
)
|
|
17
18
|
}
|
|
@@ -23,6 +23,7 @@ internal class StackHeaderToolbarMenuUpdateQueue(
|
|
|
23
23
|
private val pendingBatches = ArrayDeque<List<StackHeaderToolbarMenuElementRawUpdate>>()
|
|
24
24
|
private var isProcessing = false
|
|
25
25
|
private var isTornDown = false
|
|
26
|
+
private var generation = 0
|
|
26
27
|
|
|
27
28
|
internal fun enqueue(batch: List<StackHeaderToolbarMenuElementRawUpdate>) {
|
|
28
29
|
if (batch.isEmpty()) {
|
|
@@ -34,6 +35,18 @@ internal class StackHeaderToolbarMenuUpdateQueue(
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Drops every batch that has not been applied yet, including one whose
|
|
40
|
+
* icons are still resolving — its eventual dispatch becomes a no-op. Used
|
|
41
|
+
* when the menu configuration is replaced: commands sent against the old
|
|
42
|
+
* menu must never touch the new one. The queue stays usable.
|
|
43
|
+
*/
|
|
44
|
+
internal fun clearPending() {
|
|
45
|
+
pendingBatches.clear()
|
|
46
|
+
generation++
|
|
47
|
+
isProcessing = false
|
|
48
|
+
}
|
|
49
|
+
|
|
37
50
|
internal fun tearDown() {
|
|
38
51
|
pendingBatches.clear()
|
|
39
52
|
isTornDown = true
|
|
@@ -48,11 +61,12 @@ internal class StackHeaderToolbarMenuUpdateQueue(
|
|
|
48
61
|
return
|
|
49
62
|
}
|
|
50
63
|
isProcessing = true
|
|
64
|
+
val batchGeneration = generation
|
|
51
65
|
|
|
52
66
|
val updates = arrayOfNulls<StackHeaderToolbarMenuElementUpdate>(batch.size)
|
|
53
67
|
var outstanding = batch.size
|
|
54
68
|
|
|
55
|
-
var
|
|
69
|
+
var inSyncDispatchLoop = true
|
|
56
70
|
|
|
57
71
|
fun dispatchResolvedBatch() {
|
|
58
72
|
pendingBatches.removeFirst()
|
|
@@ -63,20 +77,20 @@ internal class StackHeaderToolbarMenuUpdateQueue(
|
|
|
63
77
|
index: Int,
|
|
64
78
|
options: StackHeaderToolbarMenuElementOptions,
|
|
65
79
|
) {
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
if (isTornDown || batchGeneration != generation) {
|
|
81
|
+
return
|
|
68
82
|
}
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
84
|
+
check(updates[index] == null) {
|
|
85
|
+
"[RNScreens] Unexpected duplicated element resolution."
|
|
72
86
|
}
|
|
73
87
|
|
|
74
|
-
updates[index] = StackHeaderToolbarMenuElementUpdate(batch[index].id, options)
|
|
88
|
+
updates[index] = StackHeaderToolbarMenuElementUpdate(batch[index].id, options, batch[index].checked)
|
|
75
89
|
outstanding -= 1
|
|
76
90
|
|
|
77
91
|
// A synchronous completion is applied by the loop below; only an asynchronous one
|
|
78
92
|
// advances the queue processing from here.
|
|
79
|
-
if (outstanding == 0 && !
|
|
93
|
+
if (outstanding == 0 && !inSyncDispatchLoop) {
|
|
80
94
|
dispatchResolvedBatch()
|
|
81
95
|
processNext()
|
|
82
96
|
}
|
|
@@ -88,11 +102,11 @@ internal class StackHeaderToolbarMenuUpdateQueue(
|
|
|
88
102
|
onElementResolved(index, update.options)
|
|
89
103
|
} else {
|
|
90
104
|
iconResolver.resolve(iconSource) { icon ->
|
|
91
|
-
onElementResolved(index, update.options.copy(icon = icon))
|
|
105
|
+
onElementResolved(index, update.options.copy(icon = StackHeaderToolbarFieldUpdate.from(icon)))
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
108
|
}
|
|
95
|
-
|
|
109
|
+
inSyncDispatchLoop = false
|
|
96
110
|
|
|
97
111
|
if (outstanding > 0) {
|
|
98
112
|
// An icon is still loading asynchronously; its callback will apply the batch
|
|
@@ -2,18 +2,26 @@ package com.swmansion.rnscreens.stack.host
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.content.res.Configuration
|
|
5
6
|
import android.util.Log
|
|
6
7
|
import android.view.ViewGroup
|
|
7
8
|
import android.widget.FrameLayout
|
|
8
9
|
import androidx.fragment.app.Fragment
|
|
9
10
|
import androidx.fragment.app.FragmentManager
|
|
11
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorScheme
|
|
12
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorSchemeCoordinator
|
|
13
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorSchemeListener
|
|
14
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorSchemeProviding
|
|
10
15
|
import com.swmansion.rnscreens.common.container.Container
|
|
16
|
+
import com.swmansion.rnscreens.common.container.ContainerItem
|
|
11
17
|
import com.swmansion.rnscreens.common.container.ParentContainerItemRegistry
|
|
12
18
|
import com.swmansion.rnscreens.ext.isMeasured
|
|
13
19
|
import com.swmansion.rnscreens.helpers.FragmentManagerHelper
|
|
14
20
|
import com.swmansion.rnscreens.helpers.ViewIdGenerator
|
|
21
|
+
import com.swmansion.rnscreens.stack.header.StackHeaderBackPressHandler
|
|
15
22
|
import com.swmansion.rnscreens.stack.screen.StackScreen
|
|
16
23
|
import com.swmansion.rnscreens.stack.screen.StackScreenFragment
|
|
24
|
+
import com.swmansion.rnscreens.stack.screen.StackScreenFragmentDelegate
|
|
17
25
|
import com.swmansion.rnscreens.utils.RNSLog
|
|
18
26
|
import java.lang.ref.WeakReference
|
|
19
27
|
|
|
@@ -23,7 +31,10 @@ internal class StackContainer(
|
|
|
23
31
|
private val delegate: WeakReference<StackContainerDelegate>,
|
|
24
32
|
) : FrameLayout(context),
|
|
25
33
|
Container,
|
|
26
|
-
FragmentManager.OnBackStackChangedListener
|
|
34
|
+
FragmentManager.OnBackStackChangedListener,
|
|
35
|
+
ColorSchemeProviding,
|
|
36
|
+
StackHeaderBackPressHandler,
|
|
37
|
+
StackScreenFragmentDelegate {
|
|
27
38
|
private var fragmentManager: FragmentManager? = null
|
|
28
39
|
|
|
29
40
|
private fun requireFragmentManager(): FragmentManager =
|
|
@@ -51,6 +62,20 @@ internal class StackContainer(
|
|
|
51
62
|
private val fragmentOpExecutor: FragmentOperationExecutor = FragmentOperationExecutor()
|
|
52
63
|
private val fragmentOps: MutableList<FragmentOperation> = arrayListOf()
|
|
53
64
|
|
|
65
|
+
// region Color Scheme
|
|
66
|
+
|
|
67
|
+
private val colorSchemeCoordinator = ColorSchemeCoordinator()
|
|
68
|
+
|
|
69
|
+
internal var colorScheme: ColorScheme by colorSchemeCoordinator::colorScheme
|
|
70
|
+
|
|
71
|
+
override fun getResolvedUiNightMode() = colorSchemeCoordinator.getResolvedUiNightMode()
|
|
72
|
+
|
|
73
|
+
override fun addColorSchemeListener(listener: ColorSchemeListener) = colorSchemeCoordinator.addColorSchemeListener(listener)
|
|
74
|
+
|
|
75
|
+
override fun removeColorSchemeListener(listener: ColorSchemeListener) = colorSchemeCoordinator.removeColorSchemeListener(listener)
|
|
76
|
+
|
|
77
|
+
// endregion
|
|
78
|
+
|
|
54
79
|
init {
|
|
55
80
|
id = ViewIdGenerator.generateViewId()
|
|
56
81
|
}
|
|
@@ -62,6 +87,10 @@ internal class StackContainer(
|
|
|
62
87
|
parentContainerRegistry.attach(this)
|
|
63
88
|
setupFragmentManger()
|
|
64
89
|
|
|
90
|
+
// StackContainer only provides container-level color scheme configuration for its screens
|
|
91
|
+
// but doesn't use any color scheme-dependent views, so we don't need the callback.
|
|
92
|
+
colorSchemeCoordinator.setup(this, null)
|
|
93
|
+
|
|
65
94
|
// Following line works with a couple of assumptions.
|
|
66
95
|
// First, that this view is laid out by our parent view, which is a component view.
|
|
67
96
|
// Component views on new architecture receive their first layout after the view hierarchy is
|
|
@@ -81,8 +110,16 @@ internal class StackContainer(
|
|
|
81
110
|
requireFragmentManager().removeOnBackStackChangedListener(this)
|
|
82
111
|
fragmentManager = null
|
|
83
112
|
parentContainerRegistry.detach(this)
|
|
113
|
+
colorSchemeCoordinator.teardown()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
override fun onConfigurationChanged(newConfig: Configuration?) {
|
|
117
|
+
super.onConfigurationChanged(newConfig)
|
|
118
|
+
colorSchemeCoordinator.onConfigurationChanged(newConfig)
|
|
84
119
|
}
|
|
85
120
|
|
|
121
|
+
override fun onFragmentConfigurationChanged(config: Configuration) = onConfigurationChanged(config)
|
|
122
|
+
|
|
86
123
|
internal fun setupFragmentManger() {
|
|
87
124
|
fragmentManager =
|
|
88
125
|
checkNotNull(FragmentManagerHelper.findFragmentManagerForView(this)) {
|
|
@@ -164,7 +201,8 @@ internal class StackContainer(
|
|
|
164
201
|
}
|
|
165
202
|
|
|
166
203
|
pendingPushOperations.forEach { operation ->
|
|
167
|
-
val newFragment =
|
|
204
|
+
val newFragment =
|
|
205
|
+
createFragmentForScreen(operation.screen, canNavigateBack = stackModel.isNotEmpty())
|
|
168
206
|
|
|
169
207
|
fragmentOps.add(
|
|
170
208
|
AddAndSetAsPrimaryOp(
|
|
@@ -205,7 +243,12 @@ internal class StackContainer(
|
|
|
205
243
|
screen: StackScreen,
|
|
206
244
|
canNavigateBack: Boolean,
|
|
207
245
|
): StackScreenFragment =
|
|
208
|
-
StackScreenFragment(
|
|
246
|
+
StackScreenFragment(
|
|
247
|
+
screen,
|
|
248
|
+
canNavigateBack,
|
|
249
|
+
WeakReference(this),
|
|
250
|
+
backPressHandler = WeakReference(this),
|
|
251
|
+
).also {
|
|
209
252
|
Log.d(TAG, "Created Fragment $it for screen ${screen.screenKey}")
|
|
210
253
|
}
|
|
211
254
|
|
|
@@ -290,6 +333,63 @@ internal class StackContainer(
|
|
|
290
333
|
?.stackScreen
|
|
291
334
|
?.findContentScrollView()
|
|
292
335
|
|
|
336
|
+
// Asked when this container's whole subtree is about to be dismissed (the screen
|
|
337
|
+
// hosting this container is popped) - every item gets a vote, back-to-front, so
|
|
338
|
+
// the deepest preventing screen wins.
|
|
339
|
+
override fun wantsToPreventStackNativeDismiss(): ContainerItem? =
|
|
340
|
+
stackModel
|
|
341
|
+
.asReversed()
|
|
342
|
+
.firstNotNullOfOrNull { it.stackScreen.wantsToPreventStackNativeDismiss() }
|
|
343
|
+
|
|
344
|
+
// endregion
|
|
345
|
+
|
|
346
|
+
// region Header back button
|
|
347
|
+
|
|
348
|
+
override fun handleHeaderBackButtonPress(pressedScreen: StackScreen) {
|
|
349
|
+
val fragmentManager = fragmentManager
|
|
350
|
+
if (fragmentManager == null) {
|
|
351
|
+
Log.w(TAG, "[RNScreens] Ignoring header back button press - container is detached")
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
val topScreen = stackModel.lastOrNull()?.stackScreen
|
|
356
|
+
if (topScreen !== pressedScreen || stackModel.size <= 1) {
|
|
357
|
+
Log.w(
|
|
358
|
+
TAG,
|
|
359
|
+
"[RNScreens] Ignoring header back button press for non-top screen ${pressedScreen.screenKey}",
|
|
360
|
+
)
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// This pop dismisses only the top screen (together with its subtree), therefore
|
|
365
|
+
// only the top item is asked - screens below the top get no vote at this level.
|
|
366
|
+
val vetoingItem = topScreen.wantsToPreventStackNativeDismiss()
|
|
367
|
+
if (vetoingItem != null) {
|
|
368
|
+
// Only a StackScreen can veto - TabsScreen has no own flag and only forwards.
|
|
369
|
+
val vetoingScreen = vetoingItem as? StackScreen
|
|
370
|
+
if (vetoingScreen != null) {
|
|
371
|
+
vetoingScreen.onNativeDismissPrevented()
|
|
372
|
+
} else {
|
|
373
|
+
Log.w(
|
|
374
|
+
TAG,
|
|
375
|
+
"[RNScreens] Unexpected vetoing item type: ${vetoingItem.javaClass.simpleName}",
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
return
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Mirrors FragmentManager's internal OnBackPressedCallback, which also runs
|
|
382
|
+
// popBackStackImmediate. The synchronous pop makes the top-screen guard above
|
|
383
|
+
// reliable against double taps: onBackStackChangeCommitted -> onNativeFragmentPop
|
|
384
|
+
// updates stackModel before this call returns.
|
|
385
|
+
fragmentManager.popBackStackImmediate(
|
|
386
|
+
// key MUST BE present, otherwise the navigation action will be delegated to child primary navigation
|
|
387
|
+
// fragment.
|
|
388
|
+
checkNotNull(pressedScreen.screenKey) { "[RNScreens] Screen key is required" },
|
|
389
|
+
FragmentManager.POP_BACK_STACK_INCLUSIVE,
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
|
|
293
393
|
// endregion
|
|
294
394
|
|
|
295
395
|
companion object {
|
|
@@ -8,6 +8,7 @@ import com.facebook.react.bridge.UIManagerListener
|
|
|
8
8
|
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
9
9
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
10
10
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
11
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorScheme
|
|
11
12
|
import com.swmansion.rnscreens.helpers.getFabricUIManagerNotNull
|
|
12
13
|
import com.swmansion.rnscreens.stack.screen.StackScreen
|
|
13
14
|
import com.swmansion.rnscreens.utils.RNSLog
|
|
@@ -26,6 +27,8 @@ class StackHost(
|
|
|
26
27
|
private val containerUpdateCoordinator = StackContainerUpdateCoordinator()
|
|
27
28
|
private var isLayoutEnqueued = false
|
|
28
29
|
|
|
30
|
+
internal var colorScheme: ColorScheme by container::colorScheme
|
|
31
|
+
|
|
29
32
|
init {
|
|
30
33
|
addView(container)
|
|
31
34
|
|
|
@@ -119,6 +122,16 @@ class StackHost(
|
|
|
119
122
|
refreshLayout()
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
// On window configuration changes ViewRootImpl calls forceLayout() recursively on every view
|
|
126
|
+
// in the hierarchy, bypassing requestLayout(). React views never lay out their children during
|
|
127
|
+
// framework traversals, so nothing would clear the force-layout flags in our subtree — and once
|
|
128
|
+
// they are set, any subsequent requestLayout() from a descendant short-circuits before reaching
|
|
129
|
+
// the override above, permanently disabling the manual pass. Schedule it from here as well.
|
|
130
|
+
override fun forceLayout() {
|
|
131
|
+
super.forceLayout()
|
|
132
|
+
refreshLayout()
|
|
133
|
+
}
|
|
134
|
+
|
|
122
135
|
private fun refreshLayout() {
|
|
123
136
|
if (!isLayoutEnqueued) {
|
|
124
137
|
isLayoutEnqueued = true
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
package com.swmansion.rnscreens.stack.host
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
|
+
import com.facebook.react.bridge.JSApplicationIllegalArgumentException
|
|
4
5
|
import com.facebook.react.module.annotations.ReactModule
|
|
5
6
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
7
|
import com.facebook.react.uimanager.ViewGroupManager
|
|
7
8
|
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
8
9
|
import com.facebook.react.viewmanagers.RNSStackHostManagerDelegate
|
|
9
10
|
import com.facebook.react.viewmanagers.RNSStackHostManagerInterface
|
|
11
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorScheme
|
|
10
12
|
import com.swmansion.rnscreens.stack.screen.StackScreen
|
|
11
13
|
|
|
12
14
|
@ReactModule(name = StackHostViewManager.REACT_CLASS)
|
|
@@ -70,6 +72,18 @@ class StackHostViewManager :
|
|
|
70
72
|
*/
|
|
71
73
|
override fun needsCustomLayoutForChildren() = true
|
|
72
74
|
|
|
75
|
+
override fun setColorScheme(
|
|
76
|
+
view: StackHost,
|
|
77
|
+
value: String?,
|
|
78
|
+
) {
|
|
79
|
+
when (value) {
|
|
80
|
+
null, "inherit" -> view.colorScheme = ColorScheme.INHERIT
|
|
81
|
+
"light" -> view.colorScheme = ColorScheme.LIGHT
|
|
82
|
+
"dark" -> view.colorScheme = ColorScheme.DARK
|
|
83
|
+
else -> throw JSApplicationIllegalArgumentException("[RNScreens] Invalid color scheme: $value.")
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
73
87
|
companion object {
|
|
74
88
|
const val REACT_CLASS = "RNSStackHost"
|
|
75
89
|
}
|
|
@@ -175,4 +175,8 @@ class StackScreen(
|
|
|
175
175
|
override fun resolveNestedContainer(): Container? = containerItemSupport.resolveNestedContainer()
|
|
176
176
|
|
|
177
177
|
override fun findContentScrollView(): ViewGroup? = containerItemSupport.findContentScrollView(this)
|
|
178
|
+
|
|
179
|
+
override fun wantsToPreventStackNativeDismiss(): ContainerItem? =
|
|
180
|
+
resolveNestedContainer()?.wantsToPreventStackNativeDismiss()
|
|
181
|
+
?: takeIf { isPreventNativeDismissEnabled }
|
|
178
182
|
}
|