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

Use Compose state for back previews #20

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
Nov 24, 2024
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
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ kotlinxCoroutines = "1.9.0"
kotlinxDatetime = "0.6.1"
lifecycle-runtime = "2.8.6"
tunjidStateHolder = "1.1.0"
tunjidComposables = "0.0.9"
tunjidComposables = "0.0.11"
junit = "4.13.2"
runner = "1.0.2"
espressoCore = "3.0.2"
Expand Down
6 changes: 3 additions & 3 deletions libraryVersion.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
# limitations under the License.
#
groupId=com.tunjid.treenav
treenav_version=0.0.11
strings_version=0.0.11
compose_version=0.0.11
treenav_version=0.0.12
strings_version=0.0.12
compose_version=0.0.12
2 changes: 2 additions & 0 deletions sample/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ dependencies {
implementation(libs.jetbrains.compose.animation)

implementation(libs.google.material)

implementation(libs.tunjid.composables)
}
38 changes: 26 additions & 12 deletions sample/android/src/main/java/com/tunjid/tyler/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.remember
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.round
import com.tunjid.demo.common.ui.App
import com.tunjid.demo.common.ui.AppState
import com.tunjid.demo.common.ui.AppTheme
import com.tunjid.demo.common.ui.SampleApp
import com.tunjid.demo.common.ui.SampleAppState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectIndexed
import kotlin.coroutines.cancellation.CancellationException

class MainActivity : AppCompatActivity() {
Expand All @@ -37,20 +38,33 @@ class MainActivity : AppCompatActivity() {
enableEdgeToEdge()
setContent {
AppTheme {
val appState = remember { SampleAppState() }
SampleApp(appState)
val appState = remember { AppState() }
App(appState)

PredictiveBackHandler { progress: Flow<BackEventCompat> ->
PredictiveBackHandler { backEvents: Flow<BackEventCompat> ->
try {
progress.collectIndexed { index, backEvent ->
// if (index == 0) onStarted()
val touchOffset = Offset(backEvent.touchX, backEvent.touchY)
val progressFraction = backEvent.progress
appState.updatePredictiveBack(touchOffset, progressFraction)
backEvents.collect { backEvent ->
appState.backPreviewState.apply {
atStart = backEvent.swipeEdge == BackEventCompat.EDGE_LEFT
progress = backEvent.progress
pointerOffset = Offset(
x = backEvent.touchX,
y = backEvent.touchY
).round()
}
}
// Dismiss back preview
appState.backPreviewState.apply {
progress = Float.NaN
pointerOffset = IntOffset.Zero
}
// Pop navigation
appState.goBack()
} catch (e: CancellationException) {
appState.cancelPredictiveBack()
appState.backPreviewState.apply {
progress = Float.NaN
pointerOffset = IntOffset.Zero
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,15 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import com.tunjid.composables.backpreview.BackPreviewState
import com.tunjid.composables.backpreview.backPreview
import com.tunjid.composables.splitlayout.SplitLayout
import com.tunjid.composables.splitlayout.SplitLayoutState
import com.tunjid.demo.common.ui.SampleAppState.Companion.rememberPanedNavHostState
import com.tunjid.demo.common.ui.AppState.Companion.rememberPanedNavHostState
import com.tunjid.demo.common.ui.chat.chatPaneStrategy
import com.tunjid.demo.common.ui.chatrooms.chatRoomPaneStrategy
import com.tunjid.demo.common.ui.data.NavigationRepository
Expand All @@ -78,6 +77,7 @@ import com.tunjid.demo.common.ui.profile.profilePaneStrategy
import com.tunjid.treenav.MultiStackNav
import com.tunjid.treenav.compose.PanedNavHost
import com.tunjid.treenav.compose.PanedNavHostConfiguration
import com.tunjid.treenav.compose.PanedNavHostScope
import com.tunjid.treenav.compose.SavedStatePanedNavHostState
import com.tunjid.treenav.compose.configurations.animatePaneBoundsConfiguration
import com.tunjid.treenav.compose.configurations.paneModifierConfiguration
Expand All @@ -96,8 +96,8 @@ import kotlinx.coroutines.launch

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun SampleApp(
appState: SampleAppState = remember { SampleAppState() },
fun App(
appState: AppState = remember { AppState() },
) {
NavigationSuiteScaffold(
navigationSuiteItems = {
Expand All @@ -112,25 +112,8 @@ fun SampleApp(
}
) {
SharedTransitionScope { sharedTransitionModifier ->
val order = remember {
listOf(
ThreePane.Tertiary,
ThreePane.Secondary,
ThreePane.Primary,
)
}
val splitLayoutState = remember {
SplitLayoutState(
orientation = Orientation.Horizontal,
maxCount = order.size,
keyAtIndex = { index ->
val indexDiff = order.size - visibleCount
order[index + indexDiff]
}
)
}
val surfaceColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
animateDpAsState(if (appState.predictiveBackStatus.value) 16.dp else 0.dp).value
val backPreviewSurfaceColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
animateDpAsState(if (appState.isPreviewingBack) 16.dp else 0.dp).value
)
val density = LocalDensity.current
val movableSharedElementHostState = remember {
Expand All @@ -153,21 +136,20 @@ fun SampleApp(
.paneModifierConfiguration {
if (paneState.pane == ThreePane.TransientPrimary) Modifier
.fillMaxSize()
.predictiveBackModifier(
touchOffsetState = appState.backTouchOffsetState,
progressState = appState.backProgressFractionState
)
.background(surfaceColor, RoundedCornerShape(16.dp))
.backPreview(appState.backPreviewState)
.background(backPreviewSurfaceColor, RoundedCornerShape(16.dp))
else Modifier
.fillMaxSize()
}
.threePanedNavHostConfiguration(
windowWidthState = derivedStateOf {
splitLayoutState.size
appState.splitLayoutState.size
}
)
.predictiveBackConfiguration(
isPreviewingBack = appState.predictiveBackStatus,
isPreviewingBack = derivedStateOf {
appState.isPreviewingBack
},
backPreviewTransform = MultiStackNav::pop,
)
.threePanedMovableSharedElementConfiguration(
Expand All @@ -189,26 +171,26 @@ fun SampleApp(
)
},
) {
val filteredOrder by remember {
derivedStateOf { order.filter { nodeFor(it) != null } }
val filteredPaneOrder by remember {
derivedStateOf { appState.filteredPaneOrder(this) }
}
splitLayoutState.visibleCount = filteredOrder.size
appState.splitLayoutState.visibleCount = filteredPaneOrder.size
SplitLayout(
state = splitLayoutState,
state = appState.splitLayoutState,
modifier = Modifier
.fillMaxSize()
then sharedTransitionModifier,
itemSeparators = { paneIndex, offset ->
PaneSeparator(
splitLayoutState = splitLayoutState,
splitLayoutState = appState.splitLayoutState,
interactionSource = appState.paneInteractionSourceAt(paneIndex),
index = paneIndex,
density = density,
xOffset = offset,
)
},
itemContent = { index ->
val pane = filteredOrder[index]
val pane = filteredPaneOrder[index]
Destination(pane)
if (pane == ThreePane.Primary) Destination(ThreePane.TransientPrimary)
}
Expand Down Expand Up @@ -281,7 +263,7 @@ fun InteractionSource.isActive(): Boolean {
}

@Stable
class SampleAppState(
class AppState(
private val navigationRepository: NavigationRepository = NavigationRepository
) {

Expand All @@ -292,12 +274,32 @@ class SampleAppState(
navigationState
)
private val paneInteractionSourceList = mutableStateListOf<MutableInteractionSource>()

val backTouchOffsetState = mutableStateOf(IntOffset.Zero)
val backProgressFractionState = mutableFloatStateOf(Float.NaN)
private val paneRenderOrder = listOf(
ThreePane.Secondary,
ThreePane.Primary,
)

val currentNavigation by navigationState
val predictiveBackStatus = derivedStateOf { !backProgressFractionState.value.isNaN() }
val backPreviewState = BackPreviewState()
val splitLayoutState = SplitLayoutState(
orientation = Orientation.Horizontal,
maxCount = paneRenderOrder.size,
minSize = 10.dp,
keyAtIndex = { index ->
val indexDiff = paneRenderOrder.size - visibleCount
paneRenderOrder[index + indexDiff]
}
)

internal val isPreviewingBack
get() = !backPreviewState.progress.isNaN()

fun filteredPaneOrder(
panedNavHostScope: PanedNavHostScope<ThreePane, SampleDestination>
): List<ThreePane> {
val order = paneRenderOrder.filter { panedNavHostScope.nodeFor(it) != null }
return order
}

fun setTab(destination: SampleDestination.NavTabs) {
navigationRepository.navigate {
Expand All @@ -317,27 +319,13 @@ class SampleAppState(
fun isInteractingWithPanes(): Boolean =
paneInteractionSourceList.any { it.isActive() }

fun updatePredictiveBack(
touchOffset: Offset,
fraction: Float,
) {
backTouchOffsetState.value = touchOffset.round()
backProgressFractionState.value = fraction
}

fun cancelPredictiveBack() {
backTouchOffsetState.value = IntOffset.Zero
backProgressFractionState.value = Float.NaN
}

fun goBack() {
cancelPredictiveBack()
navigationRepository.navigate(MultiStackNav::pop)
}

companion object {
@Composable
fun SampleAppState.rememberPanedNavHostState(
fun AppState.rememberPanedNavHostState(
configurationBlock: PanedNavHostConfiguration<
ThreePane,
MultiStackNav,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tunjid.composables.ui.interpolate
import com.tunjid.composables.ui.animate
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
import treenavigation.sample.common.generated.resources.Res
Expand All @@ -39,7 +39,7 @@ fun ProfilePhoto(
Image(
modifier = modifier
.clip(RoundedCornerShape(animateDpAsState(args.cornerRadius).value)),
contentScale = args.contentScale.interpolate(),
contentScale = args.contentScale.animate(),
contentDescription = args.contentDescription,
painter = painterResource(args.profilePhotoResource())
)
Expand Down
Loading
Loading