Adjust insets handling in tablet UI (#8711)

* Adds startBar slot in Scaffold to handle nav rail
* Consumes unneeded insets in settings
This commit is contained in:
Ivan Iskandar 2022-12-10 22:02:13 +07:00 committed by GitHub
parent 820ed6a468
commit ca500da4d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 53 deletions

View File

@ -23,7 +23,7 @@ import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.exclude
import androidx.compose.foundation.layout.withConsumedWindowInsets
import androidx.compose.foundation.layout.onConsumedWindowInsetsChanged
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ScaffoldDefaults
@ -72,9 +72,11 @@ import kotlin.math.max
* * Also take account of fab height when providing inner padding
* * Fixes for fab and snackbar horizontal placements when [contentWindowInsets] is used
* * Handle consumed window insets
* * Add startBar slot for Navigation Rail
*
* @param modifier the [Modifier] to be applied to this scaffold
* @param topBar top app bar of the screen, typically a [SmallTopAppBar]
* @param startBar side bar on the start of the screen, typically a [NavigationRail]
* @param bottomBar bottom bar of the screen, typically a [NavigationBar]
* @param snackbarHost component to host [Snackbar]s that are pushed to be shown via
* [SnackbarHostState.showSnackbar], typically a [SnackbarHost]
@ -100,6 +102,7 @@ fun Scaffold(
topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
topBar: @Composable (TopAppBarScrollBehavior) -> Unit = {},
bottomBar: @Composable () -> Unit = {},
startBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
@ -113,7 +116,7 @@ fun Scaffold(
androidx.compose.material3.Surface(
modifier = Modifier
.nestedScroll(topBarScrollBehavior.nestedScrollConnection)
.withConsumedWindowInsets { remainingWindowInsets.insets = contentWindowInsets.exclude(it) }
.onConsumedWindowInsetsChanged { remainingWindowInsets.insets = contentWindowInsets.exclude(it) }
.then(modifier),
color = containerColor,
contentColor = contentColor,
@ -121,6 +124,7 @@ fun Scaffold(
ScaffoldLayout(
fabPosition = floatingActionButtonPosition,
topBar = { topBar(topBarScrollBehavior) },
startBar = startBar,
bottomBar = bottomBar,
content = content,
snackbar = snackbarHost,
@ -147,6 +151,7 @@ fun Scaffold(
private fun ScaffoldLayout(
fabPosition: FabPosition,
topBar: @Composable () -> Unit,
startBar: @Composable () -> Unit,
content: @Composable (PaddingValues) -> Unit,
snackbar: @Composable () -> Unit,
fab: @Composable () -> Unit,
@ -168,8 +173,15 @@ private fun ScaffoldLayout(
val leftInset = contentWindowInsets.getLeft(this@SubcomposeLayout, layoutDirection)
val rightInset = contentWindowInsets.getRight(this@SubcomposeLayout, layoutDirection)
val bottomInset = contentWindowInsets.getBottom(this@SubcomposeLayout)
// Tachiyomi: Add startBar slot for Navigation Rail
val startBarPlaceables = subcompose(ScaffoldLayoutContent.StartBar, startBar).fastMap {
it.measure(looseConstraints)
}
val startBarWidth = startBarPlaceables.fastMaxBy { it.width }?.width ?: 0
// Tachiyomi: layoutWidth after horizontal insets
val insetLayoutWidth = layoutWidth - leftInset - rightInset
val insetLayoutWidth = layoutWidth - leftInset - rightInset - startBarWidth
val topBarPlaceables = subcompose(ScaffoldLayoutContent.TopBar, topBar).fastMap {
it.measure(topBarConstraints)
@ -256,7 +268,7 @@ private fun ScaffoldLayout(
} else {
max(bottomBarHeightPx.toDp(), fabOffsetDp)
},
start = insets.calculateStartPadding((this@SubcomposeLayout).layoutDirection),
start = max(insets.calculateStartPadding((this@SubcomposeLayout).layoutDirection), startBarWidth.toDp()),
end = insets.calculateEndPadding((this@SubcomposeLayout).layoutDirection),
)
content(innerPadding)
@ -267,6 +279,9 @@ private fun ScaffoldLayout(
bodyContentPlaceables.fastForEach {
it.place(0, 0)
}
startBarPlaceables.fastForEach {
it.placeRelative(0, 0)
}
topBarPlaceables.fastForEach {
it.place(0, 0)
}
@ -339,4 +354,4 @@ internal val LocalFabPlacement = staticCompositionLocalOf<FabPlacement?> { null
// FAB spacing above the bottom bar / bottom of the Scaffold
private val FabSpacing = 16.dp
private enum class ScaffoldLayoutContent { TopBar, MainContent, Snackbar, Fab, BottomBar }
private enum class ScaffoldLayoutContent { TopBar, MainContent, Snackbar, Fab, BottomBar, StartBar }

View File

@ -3,32 +3,43 @@ package eu.kanade.presentation.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
@Composable
fun TwoPanelBox(
modifier: Modifier = Modifier,
contentWindowInsets: WindowInsets = WindowInsets(0),
startContent: @Composable BoxScope.() -> Unit,
endContent: @Composable BoxScope.() -> Unit,
) {
val direction = LocalLayoutDirection.current
val padding = contentWindowInsets.asPaddingValues()
val startPadding = padding.calculateStartPadding(direction)
val endPadding = padding.calculateEndPadding(direction)
BoxWithConstraints(modifier = modifier.fillMaxSize()) {
val firstWidth = (maxWidth / 2).coerceAtMost(450.dp)
val secondWidth = maxWidth - firstWidth
val width = maxWidth - startPadding - endPadding
val firstWidth = (width / 2).coerceAtMost(450.dp)
val secondWidth = width - firstWidth
Box(
modifier = Modifier
.align(Alignment.TopStart)
.width(firstWidth),
.width(firstWidth + startPadding),
content = startContent,
)
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.width(secondWidth),
.width(secondWidth + endPadding),
content = endContent,
)
}

View File

@ -7,10 +7,9 @@ import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.with
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumedWindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
@ -25,7 +24,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.semantics.contentDescription
@ -85,53 +83,53 @@ object HomeScreen : Screen {
) { tabNavigator ->
// Provide usable navigator to content screen
CompositionLocalProvider(LocalNavigator provides navigator) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (isTabletUi()) {
NavigationRail {
tabs.fastForEach {
NavigationRailItem(it)
Scaffold(
startBar = {
if (isTabletUi()) {
NavigationRail {
tabs.fastForEach {
NavigationRailItem(it)
}
}
}
}
Scaffold(
bottomBar = {
if (!isTabletUi()) {
val bottomNavVisible by produceState(initialValue = true) {
showBottomNavEvent.receiveAsFlow().collectLatest { value = it }
}
AnimatedVisibility(
visible = bottomNavVisible,
enter = expandVertically(),
exit = shrinkVertically(),
) {
NavigationBar {
tabs.fastForEach {
NavigationBarItem(it)
}
},
bottomBar = {
if (!isTabletUi()) {
val bottomNavVisible by produceState(initialValue = true) {
showBottomNavEvent.receiveAsFlow().collectLatest { value = it }
}
AnimatedVisibility(
visible = bottomNavVisible,
enter = expandVertically(),
exit = shrinkVertically(),
) {
NavigationBar {
tabs.fastForEach {
NavigationBarItem(it)
}
}
}
},
contentWindowInsets = WindowInsets(0),
) { contentPadding ->
Box(
modifier = Modifier
.padding(contentPadding)
.consumedWindowInsets(contentPadding),
) {
AnimatedContent(
targetState = tabNavigator.current,
transitionSpec = {
materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) with
materialFadeThroughOut(durationMillis = TabFadeDuration)
},
content = {
tabNavigator.saveableState(key = "currentTab", it) {
it.Content()
}
},
)
}
},
contentWindowInsets = WindowInsets(0),
) { contentPadding ->
Box(
modifier = Modifier
.padding(contentPadding)
.consumeWindowInsets(contentPadding),
) {
AnimatedContent(
targetState = tabNavigator.current,
transitionSpec = {
materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) with
materialFadeThroughOut(durationMillis = TabFadeDuration)
},
content = {
tabNavigator.saveableState(key = "currentTab", it) {
it.Content()
}
},
)
}
}
}

View File

@ -1,7 +1,14 @@
package eu.kanade.tachiyomi.ui.setting
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator
@ -55,7 +62,11 @@ class SettingsScreen private constructor(
SettingsGeneralScreen
},
) {
val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)
TwoPanelBox(
modifier = Modifier
.windowInsetsPadding(insets)
.consumeWindowInsets(insets),
startContent = {
CompositionLocalProvider(LocalBackPress provides parentNavigator::pop) {
SettingsMainScreen.Content(twoPane = true)