|
|
|
@ -18,6 +18,10 @@ package com.google.samples.apps.nowinandroid.ui.interests2pane
|
|
|
|
|
|
|
|
|
|
import androidx.activity.compose.BackHandler
|
|
|
|
|
import androidx.annotation.Keep
|
|
|
|
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
|
|
|
import androidx.compose.foundation.layout.Box
|
|
|
|
|
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
|
|
|
|
|
import androidx.compose.material3.VerticalDragHandle
|
|
|
|
|
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
|
|
|
|
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
|
|
|
|
|
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
|
|
|
@ -25,8 +29,10 @@ import androidx.compose.material3.adaptive.layout.AnimatedPane
|
|
|
|
|
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
|
|
|
|
|
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
|
|
|
|
|
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
|
|
|
|
|
import androidx.compose.material3.adaptive.layout.PaneExpansionAnchor
|
|
|
|
|
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem
|
|
|
|
|
import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
|
|
|
|
|
import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
|
|
|
|
|
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
|
|
|
|
|
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
@ -34,9 +40,14 @@ import androidx.compose.runtime.getValue
|
|
|
|
|
import androidx.compose.runtime.key
|
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
|
|
|
import androidx.compose.runtime.saveable.Saver
|
|
|
|
|
import androidx.compose.runtime.saveable.rememberSaveable
|
|
|
|
|
import androidx.compose.runtime.setValue
|
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
import androidx.compose.ui.draw.clipToBounds
|
|
|
|
|
import androidx.compose.ui.layout.layout
|
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
|
import androidx.hilt.navigation.compose.hiltViewModel
|
|
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
|
|
|
import androidx.navigation.NavGraphBuilder
|
|
|
|
@ -49,8 +60,10 @@ import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder
|
|
|
|
|
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicRoute
|
|
|
|
|
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
|
|
|
|
|
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
|
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
|
import kotlinx.serialization.Serializable
|
|
|
|
|
import java.util.UUID
|
|
|
|
|
import kotlin.math.max
|
|
|
|
|
|
|
|
|
|
@Serializable internal object TopicPlaceholderRoute
|
|
|
|
|
|
|
|
|
@ -93,8 +106,20 @@ internal fun InterestsListDetailScreen(
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
val coroutineScope = rememberCoroutineScope()
|
|
|
|
|
|
|
|
|
|
val paneExpansionState = rememberPaneExpansionState(
|
|
|
|
|
anchors = listOf(
|
|
|
|
|
PaneExpansionAnchor.Proportion(0f),
|
|
|
|
|
PaneExpansionAnchor.Proportion(0.5f),
|
|
|
|
|
PaneExpansionAnchor.Proportion(1f),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
BackHandler(listDetailNavigator.canNavigateBack()) {
|
|
|
|
|
listDetailNavigator.navigateBack()
|
|
|
|
|
coroutineScope.launch {
|
|
|
|
|
listDetailNavigator.navigateBack()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var nestedNavHostStartRoute by remember {
|
|
|
|
@ -123,40 +148,99 @@ internal fun InterestsListDetailScreen(
|
|
|
|
|
nestedNavHostStartRoute = TopicRoute(id = topicId)
|
|
|
|
|
nestedNavKey = UUID.randomUUID()
|
|
|
|
|
}
|
|
|
|
|
listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
|
|
|
|
|
coroutineScope.launch {
|
|
|
|
|
listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val mutableInteractionSource = remember { MutableInteractionSource() }
|
|
|
|
|
val minPaneWidth = 300.dp
|
|
|
|
|
|
|
|
|
|
ListDetailPaneScaffold(
|
|
|
|
|
value = listDetailNavigator.scaffoldValue,
|
|
|
|
|
directive = listDetailNavigator.scaffoldDirective,
|
|
|
|
|
listPane = {
|
|
|
|
|
AnimatedPane {
|
|
|
|
|
InterestsRoute(
|
|
|
|
|
onTopicClick = ::onTopicClickShowDetailPane,
|
|
|
|
|
highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(),
|
|
|
|
|
)
|
|
|
|
|
Box(
|
|
|
|
|
modifier = Modifier.clipToBounds()
|
|
|
|
|
.layout { measurable, constraints ->
|
|
|
|
|
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
|
|
|
|
|
val placeable = measurable.measure(
|
|
|
|
|
constraints.copy(
|
|
|
|
|
minWidth = minPaneWidth.roundToPx(),
|
|
|
|
|
maxWidth = width,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
layout(constraints.maxWidth, placeable.height) {
|
|
|
|
|
placeable.placeRelative(
|
|
|
|
|
x = 0,
|
|
|
|
|
y = 0,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
) {
|
|
|
|
|
InterestsRoute(
|
|
|
|
|
onTopicClick = ::onTopicClickShowDetailPane,
|
|
|
|
|
highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
detailPane = {
|
|
|
|
|
AnimatedPane {
|
|
|
|
|
key(nestedNavKey) {
|
|
|
|
|
NavHost(
|
|
|
|
|
navController = nestedNavController,
|
|
|
|
|
startDestination = nestedNavHostStartRoute,
|
|
|
|
|
route = DetailPaneNavHostRoute::class,
|
|
|
|
|
) {
|
|
|
|
|
topicScreen(
|
|
|
|
|
showBackButton = !listDetailNavigator.isListPaneVisible(),
|
|
|
|
|
onBackClick = listDetailNavigator::navigateBack,
|
|
|
|
|
onTopicClick = ::onTopicClickShowDetailPane,
|
|
|
|
|
)
|
|
|
|
|
composable<TopicPlaceholderRoute> {
|
|
|
|
|
TopicDetailPlaceholder()
|
|
|
|
|
Box(
|
|
|
|
|
modifier = Modifier.clipToBounds()
|
|
|
|
|
.layout { measurable, constraints ->
|
|
|
|
|
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
|
|
|
|
|
val placeable = measurable.measure(
|
|
|
|
|
constraints.copy(
|
|
|
|
|
minWidth = minPaneWidth.roundToPx(),
|
|
|
|
|
maxWidth = width,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
layout(constraints.maxWidth, placeable.height) {
|
|
|
|
|
placeable.placeRelative(
|
|
|
|
|
x = constraints.maxWidth -
|
|
|
|
|
max(constraints.maxWidth, placeable.width),
|
|
|
|
|
y = 0,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
) {
|
|
|
|
|
key(nestedNavKey) {
|
|
|
|
|
NavHost(
|
|
|
|
|
navController = nestedNavController,
|
|
|
|
|
startDestination = nestedNavHostStartRoute,
|
|
|
|
|
route = DetailPaneNavHostRoute::class,
|
|
|
|
|
) {
|
|
|
|
|
topicScreen(
|
|
|
|
|
showBackButton = !listDetailNavigator.isListPaneVisible(),
|
|
|
|
|
onBackClick = {
|
|
|
|
|
coroutineScope.launch {
|
|
|
|
|
listDetailNavigator.navigateBack()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onTopicClick = ::onTopicClickShowDetailPane,
|
|
|
|
|
)
|
|
|
|
|
composable<TopicPlaceholderRoute> {
|
|
|
|
|
TopicDetailPlaceholder()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
paneExpansionState = paneExpansionState,
|
|
|
|
|
paneExpansionDragHandle = {
|
|
|
|
|
VerticalDragHandle(
|
|
|
|
|
modifier = Modifier.paneExpansionDraggable(
|
|
|
|
|
state = paneExpansionState,
|
|
|
|
|
minTouchTargetSize = LocalMinimumInteractiveComponentSize.current,
|
|
|
|
|
interactionSource = mutableInteractionSource,
|
|
|
|
|
),
|
|
|
|
|
interactionSource = mutableInteractionSource,
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|