diff --git a/voyager-navigator/src/androidMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.android.kt b/voyager-navigator/src/androidMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.android.kt index 1509a96c..824709d0 100644 --- a/voyager-navigator/src/androidMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.android.kt +++ b/voyager-navigator/src/androidMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.android.kt @@ -1,26 +1,15 @@ package cafe.adriel.voyager.navigator import android.os.Parcelable -import androidx.compose.runtime.saveable.SaveableStateHolder import androidx.compose.runtime.saveable.listSaver -import cafe.adriel.voyager.core.annotation.ExperimentalVoyagerApi -import cafe.adriel.voyager.core.annotation.InternalVoyagerApi import cafe.adriel.voyager.core.screen.Screen /** * Navigator Saver that forces all Screens be [Parcelable], if not, it will throw a exception while trying to save * the navigator state. */ -@OptIn(ExperimentalVoyagerApi::class, InternalVoyagerApi::class) public fun parcelableNavigatorSaver( - createNavigator: NavigatorCreator = { - screenCollection: List<Screen>, - s: String, - saveableStateHolder: SaveableStateHolder, - navigatorDisposeBehavior: NavigatorDisposeBehavior, - navigator: Navigator?, -> - NavigatorDefault(screenCollection, s, saveableStateHolder, navigatorDisposeBehavior, navigator) - } + navigatorCreator: NavigatorCreator = summonNavigatorCreator() ): NavigatorSaver<Any> = NavigatorSaver { _, key, stateHolder, disposeBehavior, parent -> listSaver( @@ -41,7 +30,7 @@ public fun parcelableNavigatorSaver( screenAsParcelables }, - restore = { items -> createNavigator(items as List<Screen>, key, stateHolder, disposeBehavior, parent) } + restore = { items -> navigatorCreator(items as List<Screen>, key, stateHolder, disposeBehavior, parent) } ) } diff --git a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorDefault.kt b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/DefaultNavigator.kt similarity index 92% rename from voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorDefault.kt rename to voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/DefaultNavigator.kt index f34f2215..8f4b58b0 100644 --- a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorDefault.kt +++ b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/DefaultNavigator.kt @@ -6,7 +6,7 @@ import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.stack.Stack import cafe.adriel.voyager.core.stack.toMutableStateStack -public class NavigatorDefault @InternalVoyagerApi constructor( +public class DefaultNavigator @InternalVoyagerApi constructor( screens: List<Screen>, key: String, stateHolder: SaveableStateHolder, diff --git a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorExtended.kt b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/ExtendedNavigator.kt similarity index 86% rename from voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorExtended.kt rename to voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/ExtendedNavigator.kt index 839d8e9a..ff73ee0a 100644 --- a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorExtended.kt +++ b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/ExtendedNavigator.kt @@ -14,13 +14,15 @@ import cafe.adriel.voyager.core.stack.toMutableStateStack public data class StackLastAction<Item>( val invoker: Item?, - val event: StackEvent, + val event: StackEvent ) public fun <Item> StackLastAction<Item>?.isPush(): Boolean = this?.event == StackEvent.Push public fun <Item> StackLastAction<Item>?.isPop(): Boolean = this?.event == StackEvent.Pop +public fun <Item> StackLastAction<Item>?.isReplace(): Boolean = this?.event == StackEvent.Replace +public fun <Item> StackLastAction<Item>?.isIdle(): Boolean = this?.event == StackEvent.Idle -public class NavigatorExtended @InternalVoyagerApi constructor( +public class ExtendedNavigator @InternalVoyagerApi constructor( screens: List<Screen>, key: String, stateHolder: SaveableStateHolder, @@ -58,7 +60,7 @@ public class NavigatorExtended @InternalVoyagerApi constructor( } override fun pop(): Boolean { - if(canPop) { + if (canPop) { lastAction = StackLastAction(lastItemOrNull, StackEvent.Pop) } return stack.pop() @@ -83,4 +85,9 @@ public class NavigatorExtended @InternalVoyagerApi constructor( lastAction = StackLastAction(lastItemOrNull, StackEvent.Push) stack.plusAssign(items) } + + override fun clearEvent() { + lastAction = StackLastAction(lastItemOrNull, StackEvent.Idle) + stack.clearEvent() + } } diff --git a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/Navigator.kt b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/Navigator.kt index c7c3850f..6d74dcf8 100644 --- a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/Navigator.kt +++ b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/Navigator.kt @@ -103,7 +103,7 @@ public fun Navigator( } } -public abstract class Navigator( +public abstract class Navigator @InternalVoyagerApi constructor( screens: List<Screen>, @InternalVoyagerApi public val key: String, private val stateHolder: SaveableStateHolder, @@ -112,6 +112,9 @@ public abstract class Navigator( private val stack: Stack<Screen> = screens.toMutableStateStack(minSize = 1) ) : Stack<Screen> by stack { + @ExperimentalVoyagerApi + public abstract var lastAction: StackLastAction<Screen>? + public val level: Int = parent?.level?.inc() ?: 0 @@ -119,9 +122,6 @@ public abstract class Navigator( lastItemOrNull ?: error("Navigator has no screen") } - @ExperimentalVoyagerApi - public abstract var lastAction: StackLastAction<Screen>? - private val stateKeys: ThreadSafeSet<String> = ThreadSafeSet() internal val children = ThreadSafeMap<NavigatorKey, Navigator>() @@ -157,6 +157,18 @@ public abstract class Navigator( ) } + public fun popUntilRoot() { + popUntilRoot(this) + } + + private tailrec fun popUntilRoot(navigator: Navigator) { + navigator.popAll() + + if (navigator.parent != null) { + popUntilRoot(navigator.parent) + } + } + @InternalVoyagerApi public fun dispose( screen: Screen @@ -171,28 +183,8 @@ public abstract class Navigator( stateKeys -= key } } - - public fun popUntilRoot() { - popUntilRoot(this) - } - - private tailrec fun popUntilRoot(navigator: Navigator) { - navigator.popAll() - - if (navigator.parent != null) { - popUntilRoot(navigator.parent) - } - } } -public typealias NavigatorCreator = ( - screens: List<Screen>, - key: String, - stateHolder: SaveableStateHolder, - disposeBehavior: NavigatorDisposeBehavior, - parent: Navigator? -) -> Navigator - public data class NavigatorDisposeBehavior( val disposeNestedNavigators: Boolean = true, val disposeSteps: Boolean = true diff --git a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorCreator.kt b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorCreator.kt new file mode 100644 index 00000000..ae46b418 --- /dev/null +++ b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorCreator.kt @@ -0,0 +1,35 @@ +package cafe.adriel.voyager.navigator + +import androidx.compose.runtime.saveable.SaveableStateHolder +import cafe.adriel.voyager.core.annotation.ExperimentalVoyagerApi +import cafe.adriel.voyager.core.screen.Screen + +public typealias NavigatorCreator = ( + screens: List<Screen>, + key: String, + stateHolder: SaveableStateHolder, + disposeBehavior: NavigatorDisposeBehavior, + parent: Navigator? +) -> Navigator + +// Choose your Navigator +public fun summonNavigatorCreator(): NavigatorCreator = DefaultNavigatorCreator() + +private fun DefaultNavigatorCreator(): NavigatorCreator = { + screenCollection: List<Screen>, + s: String, + saveableStateHolder: SaveableStateHolder, + navigatorDisposeBehavior: NavigatorDisposeBehavior, + navigator: Navigator?, -> + DefaultNavigator(screenCollection, s, saveableStateHolder, navigatorDisposeBehavior, navigator) +} + +@ExperimentalVoyagerApi +private fun ExtendedNavigatorCreator(): NavigatorCreator = { + screenCollection: List<Screen>, + s: String, + saveableStateHolder: SaveableStateHolder, + navigatorDisposeBehavior: NavigatorDisposeBehavior, + navigator: Navigator?, -> + ExtendedNavigator(screenCollection, s, saveableStateHolder, navigatorDisposeBehavior, navigator) +} diff --git a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.kt b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.kt index c045fec4..ca16ae9f 100644 --- a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.kt +++ b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/NavigatorSaver.kt @@ -33,10 +33,12 @@ public fun interface NavigatorSaver<Saveable : Any> { * * If you want to use only Parcelable and want a NavigatorSaver that forces the use Parcelable, you can use [parcelableNavigatorSaver]. */ -public fun defaultNavigatorSaver(): NavigatorSaver<Any> = +public fun defaultNavigatorSaver( + navigatorCreator: NavigatorCreator = summonNavigatorCreator() +): NavigatorSaver<Any> = NavigatorSaver { _, key, stateHolder, disposeBehavior, parent -> listSaver( save = { navigator -> navigator.items }, - restore = { items -> NavigatorDefault(items, key, stateHolder, disposeBehavior, parent) } + restore = { items -> navigatorCreator(items, key, stateHolder, disposeBehavior, parent) } ) } diff --git a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/internal/NavigatorSaverInternal.kt b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/internal/NavigatorSaverInternal.kt index 6547bc27..268bfd49 100644 --- a/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/internal/NavigatorSaverInternal.kt +++ b/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/internal/NavigatorSaverInternal.kt @@ -10,8 +10,8 @@ import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigatorSaver import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.NavigatorCreator -import cafe.adriel.voyager.navigator.NavigatorDefault import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior +import cafe.adriel.voyager.navigator.summonNavigatorCreator internal val LocalNavigatorStateHolder: ProvidableCompositionLocal<SaveableStateHolder> = staticCompositionLocalOf { error("LocalNavigatorStateHolder not initialized") } @@ -22,14 +22,7 @@ internal fun rememberNavigator( key: String, disposeBehavior: NavigatorDisposeBehavior, parent: Navigator?, - createNavigator: NavigatorCreator = { - screenCollection: List<Screen>, - s: String, - saveableStateHolder: SaveableStateHolder, - navigatorDisposeBehavior: NavigatorDisposeBehavior, - navigator: Navigator?, -> - NavigatorDefault(screenCollection, s, saveableStateHolder, navigatorDisposeBehavior, navigator) - } + navigatorCreator: NavigatorCreator = summonNavigatorCreator() ): Navigator { val stateHolder = LocalNavigatorStateHolder.current val navigatorSaver = LocalNavigatorSaver.current @@ -38,6 +31,6 @@ internal fun rememberNavigator( } return rememberSaveable(saver = saver, key = key) { - createNavigator(screens, key, stateHolder, disposeBehavior, parent) + navigatorCreator(screens, key, stateHolder, disposeBehavior, parent) } } diff --git a/voyager-tab-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/tab/TabNavigatorExtended.kt b/voyager-tab-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/tab/TabNavigatorExtended.kt deleted file mode 100644 index 2a22ae7b..00000000 --- a/voyager-tab-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/tab/TabNavigatorExtended.kt +++ /dev/null @@ -1,30 +0,0 @@ -package cafe.adriel.voyager.navigator.tab - -import androidx.compose.runtime.Composable -import cafe.adriel.voyager.navigator.Navigator - -public class TabNavigatorExtended internal constructor( - internal val navigator: Navigator -) { - - public var current: Tab - get() = navigator.lastItem as Tab - private set(value) {} - - public fun setCurrent(invoker: Tab, newTab: Tab) { - // TODO: sdfsdfd - //navigator.replaceAll(invoker, newTab) - navigator.replaceAll(newTab) - current = newTab - } - - @Composable - public fun saveableState( - key: String, - tab: Tab = current, - content: @Composable () -> Unit - ) { - navigator.saveableState(key, tab, content = content) - } -} -