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

Use "decorators" to match nav3 #37

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 4 commits into from
Jun 28, 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 @@ -25,22 +25,22 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tunjid.treenav.Node
import com.tunjid.treenav.compose.threepane.ThreePane
import com.tunjid.treenav.compose.transforms.PaneTransform
import com.tunjid.treenav.compose.transforms.paneMappingTransform
import com.tunjid.treenav.compose.panedecorators.PaneDecorator
import com.tunjid.treenav.compose.panedecorators.paneMappingDecorator

/**
* An [PaneTransform] that selectively displays panes for a [ThreePane] layout
* An [PaneDecorator] that selectively displays panes for a [ThreePane] layout
* based on the space available determined by the [windowWidthState].
*
* @param windowWidthState provides the current width of the display in Dp.
*/
fun <NavigationState : Node, Destination : Node>
threePanedAdaptiveTransform(
threePaneAdaptiveDecorator(
windowWidthState: State<Dp>,
secondaryPaneBreakPoint: State<Dp> = mutableStateOf(SECONDARY_PANE_MIN_WIDTH_BREAKPOINT_DP),
tertiaryPaneBreakPoint: State<Dp> = mutableStateOf(TERTIARY_PANE_MIN_WIDTH_BREAKPOINT_DP),
): PaneTransform<NavigationState, Destination, ThreePane> =
paneMappingTransform { destination, destinationPaneMapper ->
): PaneDecorator<NavigationState, Destination, ThreePane> =
paneMappingDecorator { destination, destinationPaneMapper ->
val showSecondary by remember {
derivedStateOf { windowWidthState.value >= secondaryPaneBreakPoint.value }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ import com.tunjid.treenav.compose.moveablesharedelement.MovableSharedElementScop
import com.tunjid.treenav.compose.moveablesharedelement.PaneMovableSharedElementScope
import com.tunjid.treenav.compose.moveablesharedelement.rememberPaneMovableSharedElementScope
import com.tunjid.treenav.compose.threepane.ThreePane
import com.tunjid.treenav.compose.transforms.PaneTransform
import com.tunjid.treenav.compose.transforms.paneRenderTransform
import com.tunjid.treenav.compose.panedecorators.PaneDecorator
import com.tunjid.treenav.compose.panedecorators.paneRenderDecorator

/**
* A [PaneTransform] that applies semantics of movable shared elements to
* A [PaneDecorator] that applies semantics of movable shared elements to
* [ThreePane] layouts.
*
* It is an opinionated implementation that always shows the movable shared element in
Expand All @@ -60,10 +60,10 @@ import com.tunjid.treenav.compose.transforms.paneRenderTransform
* There should be one instance of this per [MultiPaneDisplay].
*/
fun <NavigationState : Node, Destination : Node>
threePanedMovableSharedElementTransform(
threePanedMovableSharedElementDecorator(
movableSharedElementHostState: MovableSharedElementHostState<ThreePane, Destination>,
): PaneTransform<NavigationState, Destination, ThreePane> =
paneRenderTransform { destination, destinationPaneMapper ->
): PaneDecorator<NavigationState, Destination, ThreePane> =
paneRenderDecorator { destination, destinationPaneMapper ->
val delegate = rememberPaneMovableSharedElementScope(
movableSharedElementHostState = movableSharedElementHostState
)
Expand All @@ -82,7 +82,7 @@ fun <NavigationState : Node, Destination : Node>
/**
* Requires that this [PaneScope] is a [MovableSharedElementScope] specifically configured for
* [ThreePane] layouts and returns it. This only succeeds if the [MultiPaneDisplayState] has the
* [threePanedMovableSharedElementTransform] applied to it.
* [threePanedMovableSharedElementDecorator] applied to it.
*
* In the case this [PaneScope] is not the [MovableSharedElementScope] requested, an exception
* will be thrown.
Expand All @@ -96,7 +96,7 @@ fun <Destination : Node> PaneScope<
"""
The current PaneScope (${this::class.qualifiedName}) is not an instance of
a ThreePaneMovableSharedElementScope. You must configure your ThreePane MultiPaneDisplay with
threePanedMovableSharedElementTransform().
threePaneAdaptiveDecorator().
""".trimIndent()
}
return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import com.tunjid.treenav.Node
import com.tunjid.treenav.compose.navigation3.runtime.NavEntry
import com.tunjid.treenav.compose.transforms.PaneMappingTransform
import com.tunjid.treenav.compose.transforms.PaneTransform
import com.tunjid.treenav.compose.transforms.PaneRenderTransform
import com.tunjid.treenav.compose.panedecorators.PaneMappingDecorator
import com.tunjid.treenav.compose.panedecorators.PaneDecorator
import com.tunjid.treenav.compose.panedecorators.PaneRenderDecorator

/**
* Class for configuring a [MultiPaneDisplay] for selecting, adapting and placing navigation
Expand Down Expand Up @@ -115,14 +115,14 @@ class MultiPaneDisplayState<NavigationState : Node, Destination : Node, Pane> in
* @param destinationTransform a transform of the [navigationState] to its current destination.
* @param popTransform a transform of the [navigationState] when back is pressed.
* @param onPopped an action to perform when the navigation state has been popped to a new state.
* @param entryProvider provides the [PaneTransform]s and content needed to render
* @param entryProvider provides the [PaneDecorator]s and content needed to render
* a [Destination] in its pane.
* @param transforms a list of transforms applied to every [Destination] before it is
* @param paneDecorators a list of decorators applied to every [Destination] before it is
* rendered in its pane. Order matters; they are applied from last to first.
*/
fun <NavigationState : Node, Destination : Node, Pane> MultiPaneDisplayState(
panes: List<Pane>,
transforms: List<PaneTransform<NavigationState, Destination, Pane>>,
paneDecorators: List<PaneDecorator<NavigationState, Destination, Pane>>,
navigationState: State<NavigationState>,
backStackTransform: (NavigationState) -> List<Destination>,
destinationTransform: (NavigationState) -> Destination,
Expand All @@ -132,7 +132,7 @@ fun <NavigationState : Node, Destination : Node, Pane> MultiPaneDisplayState(
NoContentTransform
},
entryProvider: (Destination) -> PaneEntry<Pane, Destination>,
) = transforms.fold(
) = paneDecorators.fold(
initial = MultiPaneDisplayState(
panes = panes,
navigationState = navigationState,
Expand All @@ -154,7 +154,7 @@ fun <NavigationState : Node, Destination : Node, Pane> MultiPaneDisplayState(

private operator fun <NavigationState : Node, Destination : Node, Pane>
MultiPaneDisplayState<NavigationState, Destination, Pane>.plus(
transform: PaneTransform<NavigationState, Destination, Pane>,
transform: PaneDecorator<NavigationState, Destination, Pane>,
): MultiPaneDisplayState<NavigationState, Destination, Pane> =
MultiPaneDisplayState(
panes = panes,
Expand All @@ -166,21 +166,21 @@ private operator fun <NavigationState : Node, Destination : Node, Pane>
transitionSpec = transitionSpec,
paneEntryProvider = paneEntryProvider,
destinationPanes = when (transform) {
is PaneMappingTransform -> { destination ->
is PaneMappingDecorator -> { destination ->
transform.toPanesAndDestinations(
destination = destination,
previousTransform = destinationPanes,
previousDecorator = destinationPanes,
)
}

else -> destinationPanes
},
destinationContent = when (transform) {
is PaneRenderTransform -> { paneEntry, destination ->
is PaneRenderDecorator -> { paneEntry, destination ->
with(transform) {
Render(
destination = destination,
previousTransform = previous@{ innerDestination ->
previousDecorator = previous@{ innerDestination ->
destinationContent(
this@previous,
paneEntry,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.tunjid.treenav.compose.transforms
package com.tunjid.treenav.compose.panedecorators

import androidx.compose.runtime.Composable
import com.tunjid.treenav.Node
Expand All @@ -9,49 +9,49 @@ import com.tunjid.treenav.compose.PaneScope
/**
* Provides APIs for adjusting what is presented in a [MultiPaneDisplay].
*/
sealed interface PaneTransform<in NavigationState : Node, Destination : Node, Pane>
sealed interface PaneDecorator<in NavigationState : Node, Destination : Node, Pane>

/**
* A [PaneTransform] that allows for changing which [Destination] shows in which [Pane].
* A [PaneDecorator] that allows for changing which [Destination] shows in which [Pane].
*/
internal fun interface PaneMappingTransform<Pane, Destination : Node>
: PaneTransform<Node, Destination, Pane> {
internal fun interface PaneMappingDecorator<Pane, Destination : Node>
: PaneDecorator<Node, Destination, Pane> {

/**
* Given the current [Destination], provide what [Destination] to show in a [Pane].
* Each [Destination] in the returned mapping must already exist in the
* back stack of the [MultiPaneDisplayState.navigationState].
*
* @param destination the current [Destination] to display.
* @param previousTransform a [PaneTransform] that when invoked, returns the pane to destination
* @param previousDecorator a [PaneDecorator] that when invoked, returns the pane to destination
* mapping for the current [Destination] pre-transform that can then be composed with new logic.
*/
@Composable
fun toPanesAndDestinations(
destination: Destination,
previousTransform: @Composable (Destination) -> Map<Pane, Destination?>,
previousDecorator: @Composable (Destination) -> Map<Pane, Destination?>,
): Map<Pane, Destination?>
}

/**
* A [PaneTransform] that allows for the rendering semantics of a [Destination] in a given
* A [PaneDecorator] that allows for the rendering semantics of a [Destination] in a given
* [PaneScope].
*/
internal fun interface PaneRenderTransform<Pane, Destination : Node>
: PaneTransform<Node, Destination, Pane> {
internal fun interface PaneRenderDecorator<Pane, Destination : Node>
: PaneDecorator<Node, Destination, Pane> {

/**
* Given the current [Destination], and its [PaneScope], compose additional presentation
* logic.
*
* @param destination the current [Destination] to display in the provided [PaneScope].
* @param previousTransform a [PaneTransform] that when invoked, renders the [Destination]
* for the [PaneScope ]pre-transform that can then be composed with new logic.
* @param previousDecorator a [PaneDecorator] that when invoked, renders the [Destination]
* for the [PaneScope ]pre-decoration that can then be composed with new logic.
*/
@Composable
fun PaneScope<Pane, Destination>.Render(
destination: Destination,
previousTransform: @Composable PaneScope<Pane, Destination>.(Destination) -> Unit,
previousDecorator: @Composable PaneScope<Pane, Destination>.(Destination) -> Unit,
)
}

Expand All @@ -67,30 +67,30 @@ internal fun interface PaneRenderTransform<Pane, Destination : Node>
* - destinationPaneMapper: A lambda that when invoked, returns the pane to destination
* mapping for the current [Destination] pre-transform that can then be composed with new logic.
*/
fun <NavigationState : Node, Destination : Node, Pane> paneMappingTransform(
fun <NavigationState : Node, Destination : Node, Pane> paneMappingDecorator(
mappingTransform: @Composable (
destination: Destination,
destinationPaneMapper: @Composable (Destination) -> Map<Pane, Destination?>
destinationPaneDecorator: @Composable (Destination) -> Map<Pane, Destination?>
) -> Map<Pane, Destination?>
): PaneTransform<NavigationState, Destination, Pane> =
PaneMappingTransform { destination, previousTransform ->
): PaneDecorator<NavigationState, Destination, Pane> =
PaneMappingDecorator { destination, previousTransform ->
mappingTransform(destination, previousTransform)
}

/**
* A [PaneTransform] that allows for adjusting the rendering semantics of a [Destination] in a
* A [PaneDecorator] that allows for adjusting the rendering semantics of a [Destination] in a
* for a given [Pane] in the [PaneScope].
*
* @param renderTransform a lambda providing the Composable to render. It has two arguments:
* - destination: The [Destination] being rendered in the provided [PaneScope].
* - destinationContent: A lambda that when invoked, renders the [Destination] pre-transform
*/
fun <Pane, Destination : Node, NavigationState : Node> paneRenderTransform(
fun <Pane, Destination : Node, NavigationState : Node> paneRenderDecorator(
renderTransform: @Composable PaneScope<Pane, Destination>.(
destination: Destination,
destinationContent: @Composable PaneScope<Pane, Destination>.(Destination) -> Unit
) -> Unit
): PaneTransform<NavigationState, Destination, Pane> =
PaneRenderTransform { destination, previousTransform ->
): PaneDecorator<NavigationState, Destination, Pane> =
PaneRenderDecorator { destination, previousTransform ->
renderTransform(destination, previousTransform)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.tunjid.treenav.compose.transforms
package com.tunjid.treenav.compose.panedecorators

import androidx.compose.foundation.layout.Box
import androidx.compose.ui.Modifier
Expand All @@ -28,10 +28,10 @@ import com.tunjid.treenav.compose.PaneScope
*
* @param paneModifier a lambda for specifying the [Modifier] for each [Pane] in a [PaneScope].
*/
fun <NavigationState : Node, Destination : Node, Pane> paneModifierTransform(
fun <NavigationState : Node, Destination : Node, Pane> paneModifierDecorator(
paneModifier: PaneScope<Pane, Destination>.() -> Modifier = { Modifier },
): PaneTransform<NavigationState, Destination, Pane> =
paneRenderTransform { destination, destinationContent ->
): PaneDecorator<NavigationState, Destination, Pane> =
paneRenderDecorator { destination, destinationContent ->
Box(
modifier = paneModifier()
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ import com.tunjid.treenav.compose.moveablesharedelement.MovableSharedElementHost
import com.tunjid.treenav.compose.multiPaneDisplayBackstack
import com.tunjid.treenav.compose.navigation3.ui.NavigationEventHandler
import com.tunjid.treenav.compose.threepane.ThreePane
import com.tunjid.treenav.compose.threepane.transforms.threePanedAdaptiveTransform
import com.tunjid.treenav.compose.threepane.transforms.threePanedMovableSharedElementTransform
import com.tunjid.treenav.compose.transforms.PaneTransform
import com.tunjid.treenav.compose.transforms.paneModifierTransform
import com.tunjid.treenav.compose.threepane.transforms.threePaneAdaptiveDecorator
import com.tunjid.treenav.compose.threepane.transforms.threePanedMovableSharedElementDecorator
import com.tunjid.treenav.compose.panedecorators.PaneDecorator
import com.tunjid.treenav.compose.panedecorators.paneModifierDecorator
import com.tunjid.treenav.pop
import com.tunjid.treenav.popToRoot
import com.tunjid.treenav.requireCurrent
Expand Down Expand Up @@ -117,7 +117,7 @@ fun App(
state = appState.rememberMultiPaneDisplayState(
remember {
listOf(
threePanedAdaptiveTransform(
threePaneAdaptiveDecorator(
secondaryPaneBreakPoint = mutableStateOf(
SecondaryPaneMinWidthBreakpointDp
),
Expand All @@ -128,10 +128,10 @@ fun App(
appState.splitLayoutState.size
}
),
threePanedMovableSharedElementTransform(
threePanedMovableSharedElementDecorator(
movableSharedElementHostState = movableSharedElementHostState
),
paneModifierTransform {
paneModifierDecorator {
if (paneState.pane == ThreePane.Primary
&& inPredictiveBack
&& isActive
Expand Down Expand Up @@ -322,7 +322,7 @@ class AppState(
companion object {
@Composable
fun AppState.rememberMultiPaneDisplayState(
transforms: List<PaneTransform<MultiStackNav, SampleDestination, ThreePane>>,
paneDecorators: List<PaneDecorator<MultiStackNav, SampleDestination, ThreePane>>,
): MultiPaneDisplayState<MultiStackNav, SampleDestination, ThreePane> {
val displayState = remember {
MultiPaneDisplayState(
Expand All @@ -345,7 +345,7 @@ class AppState(
is SampleDestination.Avatar -> avatarPaneEntry()
}
},
transforms = transforms,
paneDecorators = paneDecorators,
)
}
DisposableEffect(Unit) {
Expand Down