这是indexloc提供的服务,不要输入任何密码
Skip to content

Add PaneNavigationState to MultiPaneDisplayScope and PaneScope #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.tunjid.treenav.compose.MultiPaneDisplay
import com.tunjid.treenav.compose.MultiPaneDisplayState
import com.tunjid.treenav.compose.PaneScope
import com.tunjid.treenav.compose.PaneState
import com.tunjid.treenav.compose.PaneNavigationState
import com.tunjid.treenav.compose.moveablesharedelement.MovableSharedElementHostState
import com.tunjid.treenav.compose.moveablesharedelement.MovableSharedElementScope
import com.tunjid.treenav.compose.moveablesharedelement.PaneMovableSharedElementScope
Expand Down Expand Up @@ -113,6 +114,9 @@ private class ThreePaneMovableSharedElementScope<Destination : Node>(
override val sharedTransitionScope: SharedTransitionScope
get() = delegate.sharedTransitionScope

override val paneNavigationState: PaneNavigationState<ThreePane, Destination>
get() = delegate.paneScope.paneNavigationState

override val transition: Transition<EnterExitState>
get() = delegate.paneScope.transition

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ import kotlinx.coroutines.CancellationException
interface MultiPaneDisplayScope<Pane, Destination : Node> {

/**
* All possible panes in the [MultiPaneDisplayScope].
* Provides the pane navigation state for the [MultiPaneDisplay].
*/
val panes: Collection<Pane>
val paneNavigationState: PaneNavigationState<Pane, Destination>

/**
* Renders the given [Destination] in the provided [Pane].
Expand All @@ -64,20 +64,6 @@ interface MultiPaneDisplayScope<Pane, Destination : Node> {
fun Destination(
pane: Pane,
)

/**
* Provides the set of adaptations in the provided [Pane].
*/
fun adaptationsIn(
pane: Pane,
): Set<Adaptation>

/**
* Returns the [Destination] in the provided [Pane].
*/
fun destinationIn(
pane: Pane,
): Destination?
}

/**
Expand Down Expand Up @@ -130,7 +116,7 @@ fun <NavigationState : Node, Destination : Node, Pane> MultiPaneDisplay(
}

val initialPanedNavigationState = remember {
SlotBasedPanedNavigationState.initial<Pane, Destination>(slots = slots)
SlotBasedPaneNavigationState.initial<Pane, Destination>(slots = slots)
.adaptTo(
slots = slots,
panesToDestinations = panesToDestinations.value,
Expand Down Expand Up @@ -209,7 +195,7 @@ private class MultiPanePaneSceneStrategy<NavigationState : Node, Destination : N
private val state: MultiPaneDisplayState<NavigationState, Destination, Pane>,
private val slots: Set<Slot>,
private val backStatus: () -> BackStatus,
private val currentPanedNavigationState: () -> SlotBasedPanedNavigationState<Pane, Destination>,
private val currentPanedNavigationState: () -> SlotBasedPaneNavigationState<Pane, Destination>,
private val content: @Composable (MultiPaneDisplayScope<Pane, Destination>.() -> Unit),
) : SceneStrategy<Destination> {

Expand Down Expand Up @@ -280,7 +266,7 @@ private class MultiPaneDisplayScene<Pane, Destination : Node>(
private val sceneKey: MultiPaneSceneKey,
private val destination: Destination,
private val slots: Set<Slot>,
private val currentPanedNavigationState: SlotBasedPanedNavigationState<Pane, Destination>,
private val currentPanedNavigationState: SlotBasedPaneNavigationState<Pane, Destination>,
backStatus: () -> BackStatus,
private val panesToDestinations: @Composable (Destination) -> Map<Pane, Destination?>,
private val scopeContent: @Composable (MultiPaneDisplayScope<Pane, Destination>.() -> Unit),
Expand Down Expand Up @@ -324,23 +310,20 @@ private class MultiPaneDisplayScene<Pane, Destination : Node>(

@Stable
class PaneDestinationMultiPaneDisplayScope<Pane, Destination : Node>(
panedNavigationState: State<SlotBasedPanedNavigationState<Pane, Destination>>,
panedNavigationState: State<SlotBasedPaneNavigationState<Pane, Destination>>,
private val currentEntries: () -> List<NavEntry<Destination>>,
private val backStatus: () -> BackStatus,
) : MultiPaneDisplayScope<Pane, Destination> {

private val panedNavigationState by panedNavigationState

override val panes: Collection<Pane>
get() = panedNavigationState.panesToDestinations.keys
override val paneNavigationState by panedNavigationState

@Composable
override fun Destination(pane: Pane) {
val id = panedNavigationState.destinationFor(pane)?.id
val id = paneNavigationState.destinationIn(pane)?.id
val entry = currentEntries().firstOrNull { it.id == id } ?: return

val paneState = remember(panedNavigationState.identityHash()) {
panedNavigationState.slotFor(pane)?.let(panedNavigationState::paneStateFor)
val paneState = remember(paneNavigationState.identityHash()) {
paneNavigationState.slotFor(pane)?.let(paneNavigationState::paneStateFor)
} ?: return

val animatedContentScope = LocalNavAnimatedContentScope.current
Expand All @@ -350,6 +333,7 @@ private class MultiPaneDisplayScene<Pane, Destination : Node>(
backStatus = backStatus,
paneState = paneState,
animatedContentScope = animatedContentScope,
navigationState = { paneNavigationState },
)
}.also {
it.paneState = paneState
Expand All @@ -361,35 +345,21 @@ private class MultiPaneDisplayScene<Pane, Destination : Node>(
entry.Content()
}
}

override fun adaptationsIn(pane: Pane): Set<Adaptation> =
panedNavigationState.adaptationsIn(pane)

override fun destinationIn(pane: Pane): Destination? =
panedNavigationState.destinationFor(pane)
}
}

@Stable
private class NonRenderingMultiPaneDisplayScope<Pane, Destination : Node>(
panedNavigationState: State<SlotBasedPanedNavigationState<Pane, Destination>>,
panedNavigationState: State<SlotBasedPaneNavigationState<Pane, Destination>>,
) : MultiPaneDisplayScope<Pane, Destination> {

private val panedNavigationState by panedNavigationState

override val panes: Collection<Pane>
get() = panedNavigationState.panesToDestinations.keys
override val paneNavigationState by panedNavigationState

@Composable
override fun Destination(pane: Pane) = throw IllegalStateException(
"This MultiPaneDisplayScope cannot render panes"
)

override fun adaptationsIn(pane: Pane): Set<Adaptation> =
panedNavigationState.adaptationsIn(pane)

override fun destinationIn(pane: Pane): Destination? =
panedNavigationState.destinationFor(pane)
}

private fun <NavigationState : Node> MultiPaneDisplayState<NavigationState, *, *>.findNavigationStateMatching(
Expand All @@ -406,11 +376,11 @@ private fun <NavigationState : Node> MultiPaneDisplayState<NavigationState, *, *
* Keep track of changes to the paned navigation state.
*/
@Composable
private fun <Destination : Node, Pane> SlotBasedPanedNavigationState<Pane, Destination>.rememberUpdatedPanedNavigationState(
private fun <Destination : Node, Pane> SlotBasedPaneNavigationState<Pane, Destination>.rememberUpdatedPanedNavigationState(
backStackIds: List<String>,
panesToDestinations: Map<Pane, Destination?>,
slots: Set<Slot>
): State<SlotBasedPanedNavigationState<Pane, Destination>> =
): State<SlotBasedPaneNavigationState<Pane, Destination>> =
remember {
mutableStateOf(this)
}.also {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import kotlin.jvm.JvmInline
@Stable
interface PaneScope<Pane, Destination : Node> : AnimatedVisibilityScope {

/**
* Provides the pane navigation state that created this [PaneScope].
*/
val paneNavigationState: PaneNavigationState<Pane, Destination>

/**
* Provides information about the adaptive context that created this [PaneScope].
*/
Expand Down Expand Up @@ -60,13 +65,17 @@ interface PaneScope<Pane, Destination : Node> : AnimatedVisibilityScope {
@Stable
internal class AnimatedPaneScope<Pane, Destination : Node>(
val backStatus: () -> BackStatus,
val navigationState: () -> PaneNavigationState<Pane, Destination>,
paneState: PaneState<Pane, Destination>,
animatedContentScope: AnimatedContentScope,
) : PaneScope<Pane, Destination>, AnimatedVisibilityScope by animatedContentScope {

private val isEntering
get() = transition.targetState == EnterExitState.Visible

override val paneNavigationState: PaneNavigationState<Pane, Destination>
get() = navigationState()

override var paneState by mutableStateOf(paneState)

override val isActive: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,33 @@ import androidx.compose.runtime.Immutable
import com.tunjid.treenav.Node
import com.tunjid.treenav.compose.Adaptation.Change.contains

interface PaneNavigationState<Pane, Destination : Node> {

/**
* The id of the [Destination] that produced this [PaneNavigationState].
*/
val destinationId: String

/**
* Provides the set of adaptations in the provided [Pane].
*/
fun adaptationsIn(
pane: Pane,
): Set<Adaptation>

/**
* Returns the [Destination] in the provided [Pane].
*/
fun destinationIn(
pane: Pane,
): Destination?
}

/**
* Data structure for managing navigation as it adapts to various layout configurations
*/
@Immutable
internal data class SlotBasedPanedNavigationState<Pane, Destination : Node>(
internal data class SlotBasedPaneNavigationState<Pane, Destination : Node>(
/**
* True if this navigation change is as a result of popping the backStack.
*/
Expand All @@ -49,11 +71,11 @@ internal data class SlotBasedPanedNavigationState<Pane, Destination : Node>(
* A set of node ids that may be returned to.
*/
val backStackIds: List<String>,
) {
) : PaneNavigationState<Pane, Destination> {
companion object {
internal fun <Pane, Destination : Node> initial(
slots: Collection<Slot>,
): SlotBasedPanedNavigationState<Pane, Destination> = SlotBasedPanedNavigationState(
): SlotBasedPaneNavigationState<Pane, Destination> = SlotBasedPaneNavigationState(
isPop = false,
swapAdaptations = emptySet(),
panesToDestinations = emptyMap(),
Expand All @@ -65,10 +87,13 @@ internal data class SlotBasedPanedNavigationState<Pane, Destination : Node>(
)
}

override val destinationId: String
get() = backStackIds.last()

internal fun paneStateFor(
slot: Slot,
): PaneState<Pane, Destination> {
val node = destinationFor(slot)
val node = destinationIn(slot)
val pane = node?.let(::paneFor)
return SlotPaneState(
slot = slot,
Expand All @@ -91,7 +116,7 @@ internal data class SlotBasedPanedNavigationState<Pane, Destination : Node>(
if (paneDestination?.id == destination.id) pane else null
}

private fun destinationFor(
private fun destinationIn(
slot: Slot,
): Destination? = destinationIdsToAdaptiveSlots.firstNotNullOfOrNull { (nodeId, nodeSlot) ->
if (nodeSlot == slot) panesToDestinations.firstNotNullOfOrNull { (_, node) ->
Expand All @@ -101,17 +126,18 @@ internal data class SlotBasedPanedNavigationState<Pane, Destination : Node>(
else null
}

fun destinationFor(
override fun destinationIn(
pane: Pane,
): Destination? = panesToDestinations[pane]

fun adaptationsIn(
override fun adaptationsIn(
pane: Pane,
): Set<Adaptation> {
val adaptations = when {
swapAdaptations.any { pane in it } -> swapAdaptations.filterTo(mutableSetOf()) {
pane in it
}

else -> when (panesToDestinations[pane]?.id) {
previousPanesToDestinations[pane]?.id -> SameAdaptations
else -> ChangeAdaptations
Expand All @@ -128,11 +154,11 @@ private val ChangeAdaptations = setOf(Adaptation.Change)
* A method that adapts changes in navigation to different panes while allowing for them
* to be animated easily.
*/
internal fun <Pane, Destination : Node> SlotBasedPanedNavigationState<Pane, Destination>.adaptTo(
internal fun <Pane, Destination : Node> SlotBasedPaneNavigationState<Pane, Destination>.adaptTo(
slots: Set<Slot>,
panesToDestinations: Map<Pane, Destination?>,
backStackIds: List<String>,
): SlotBasedPanedNavigationState<Pane, Destination> {
): SlotBasedPaneNavigationState<Pane, Destination> {
val previous = this

val previouslyUsedSlots = previous.destinationIdsToAdaptiveSlots
Expand Down Expand Up @@ -185,8 +211,8 @@ internal fun <Pane, Destination : Node> SlotBasedPanedNavigationState<Pane, Dest
nodeIdsToAdaptiveSlots[nodeId] = availableSlots.first().also(availableSlots::remove)
}

return SlotBasedPanedNavigationState(
isPop = backStackIds.let popCheck@{ ids ->
return SlotBasedPaneNavigationState(
isPop = backStackIds.let popCheck@{ ids ->
if (ids.size >= previous.backStackIds.size) return@popCheck false
if (ids.isEmpty()) return@popCheck true

Expand All @@ -201,7 +227,7 @@ internal fun <Pane, Destination : Node> SlotBasedPanedNavigationState<Pane, Dest
else -> swapAdaptations
},
previousPanesToDestinations = previous.panesToDestinations.keys.associateWith(
valueSelector = previous::destinationFor
valueSelector = previous::destinationIn
),
destinationIdsToAdaptiveSlots = nodeIdsToAdaptiveSlots,
backStackIds = backStackIds,
Expand Down
Loading