Skip to content

Commit

Permalink
Add: NavigatorCreator
Browse files Browse the repository at this point in the history
  • Loading branch information
Velord committed Feb 6, 2024
1 parent b30152a commit 43020f5
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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) }
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()
Expand All @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -112,16 +112,16 @@ 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

public val lastItem: Screen by derivedStateOf {
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>()
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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") }
Expand All @@ -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
Expand All @@ -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)
}
}

This file was deleted.

0 comments on commit 43020f5

Please sign in to comment.