Skip to content

Commit

Permalink
Merge pull request android#548 from android/jv/js_rtl
Browse files Browse the repository at this point in the history
[Jetsnack] Improve RTL layouts
  • Loading branch information
JolandaVerhoef authored May 27, 2021
2 parents 84e0235 + 6a81198 commit 73d7f25
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fun VerticalGrid(
val columnY = Array(columns) { 0 }
placeables.forEachIndexed { index, placeable ->
val column = index % columns
placeable.place(
placeable.placeRelative(
x = column * itemWidth,
y = columnY[column]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package com.example.jetsnack.ui.components

import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material.ContentAlpha
Expand All @@ -28,14 +30,15 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Remove
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ChainStyle
import androidx.constraintlayout.compose.ConstraintLayout
import com.example.jetsnack.R
import com.example.jetsnack.ui.theme.JetsnackTheme

Expand All @@ -46,35 +49,27 @@ fun QuantitySelector(
increaseItemCount: () -> Unit,
modifier: Modifier = Modifier
) {
ConstraintLayout(modifier = modifier) {
val (qty, minus, quantity, plus) = createRefs()
createHorizontalChain(qty, minus, quantity, plus, chainStyle = ChainStyle.Packed)
Row(modifier = modifier) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
text = stringResource(R.string.quantity),
style = MaterialTheme.typography.subtitle1,
color = JetsnackTheme.colors.textSecondary,
modifier = Modifier
.padding(end = 18.dp)
.constrainAs(qty) {
start.linkTo(parent.start)
linkTo(top = parent.top, bottom = parent.bottom)
}
.align(Alignment.CenterVertically)
)
}
JetsnackGradientTintedIconButton(
imageVector = Icons.Default.Remove,
onClick = decreaseItemCount,
contentDescription = stringResource(R.string.label_decrease),
modifier = Modifier.constrainAs(minus) {
centerVerticallyTo(quantity)
linkTo(top = parent.top, bottom = parent.bottom)
}
modifier = Modifier.align(Alignment.CenterVertically)
)
Crossfade(
targetState = count,
modifier = Modifier
.constrainAs(quantity) { baseline.linkTo(qty.baseline) }
.align(Alignment.CenterVertically)
) {
Text(
text = "$it",
Expand All @@ -89,16 +84,15 @@ fun QuantitySelector(
imageVector = Icons.Default.Add,
onClick = increaseItemCount,
contentDescription = stringResource(R.string.label_increase),
modifier = Modifier.constrainAs(plus) {
end.linkTo(parent.end)
centerVerticallyTo(quantity)
linkTo(top = parent.top, bottom = parent.bottom)
}
modifier = Modifier.align(Alignment.CenterVertically)
)
}
}

@Preview
@Preview("Default")
@Preview("Large font", fontScale = 2f)
@Preview("Small font", fontScale = 0.5f)
@Preview("Dark theme", uiMode = UI_MODE_NIGHT_YES)
@Composable
fun QuantitySelectorPreview() {
JetsnackTheme {
Expand All @@ -107,3 +101,15 @@ fun QuantitySelectorPreview() {
}
}
}

@Preview("RTL")
@Composable
fun QuantitySelectorPreviewRtl() {
JetsnackTheme {
JetsnackSurface {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
QuantitySelector(1, {}, {})
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.ArrowForward
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
Expand All @@ -58,6 +59,7 @@ import com.example.jetsnack.model.Snack
import com.example.jetsnack.model.SnackCollection
import com.example.jetsnack.model.snacks
import com.example.jetsnack.ui.theme.JetsnackTheme
import com.example.jetsnack.ui.utils.mirroringIcon
import com.google.accompanist.coil.rememberCoilPainter

private val HighlightCardWidth = 170.dp
Expand Down Expand Up @@ -100,7 +102,10 @@ fun SnackCollection(
modifier = Modifier.align(Alignment.CenterVertically)
) {
Icon(
imageVector = Icons.Outlined.ArrowForward,
imageVector = mirroringIcon(
ltrIcon = Icons.Outlined.ArrowForward,
rtlIcon = Icons.Outlined.ArrowBack
),
tint = JetsnackTheme.colors.brand,
contentDescription = null
)
Expand Down
19 changes: 12 additions & 7 deletions Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Home.kt
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,10 @@ private fun JetsnackBottomNavLayout(
height = itemPlaceables.maxByOrNull { it.height }?.height ?: 0
) {
val indicatorLeft = indicatorIndex.value * unselectedWidth
indicatorPlaceable.place(x = indicatorLeft.toInt(), y = 0)
indicatorPlaceable.placeRelative(x = indicatorLeft.toInt(), y = 0)
var x = 0
itemPlaceables.forEach { placeable ->
placeable.place(x = x, y = 0)
placeable.placeRelative(x = x, y = 0)
x += placeable.width
}
}
Expand Down Expand Up @@ -311,12 +311,17 @@ private fun JetsnackBottomNavItemLayout(
) {
Layout(
content = {
Box(Modifier.layoutId("icon"), content = icon)
Box(
modifier = Modifier
.layoutId("icon")
.padding(horizontal = TextIconSpacing),
content = icon
)
val scale = lerp(0.6f, 1f, animationProgress)
Box(
modifier = Modifier
.layoutId("text")
.padding(start = TextIconSpacing)
.padding(horizontal = TextIconSpacing)
.graphicsLayer {
alpha = animationProgress
scaleX = scale
Expand Down Expand Up @@ -355,9 +360,9 @@ private fun MeasureScope.placeTextAndIcon(
val textX = iconX + iconPlaceable.width

return layout(width, height) {
iconPlaceable.place(iconX.toInt(), iconY)
iconPlaceable.placeRelative(iconX.toInt(), iconY)
if (animationProgress != 0f) {
textPlaceable.place(textX.toInt(), textY)
textPlaceable.placeRelative(textX.toInt(), textY)
}
}
}
Expand All @@ -376,7 +381,7 @@ private fun JetsnackBottomNavIndicator(
)
}

private val TextIconSpacing = 4.dp
private val TextIconSpacing = 2.dp
private val BottomNavHeight = 56.dp
private val BottomNavLabelTransformOrigin = TransformOrigin(0f, 0.5f)
private val BottomNavIndicatorShape = RoundedCornerShape(percent = 50)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,7 @@ fun CartItem(
style = MaterialTheme.typography.subtitle1,
color = JetsnackTheme.colors.textSecondary,
modifier = Modifier.constrainAs(name) {
linkTo(
start = image.end,
startMargin = 16.dp,
end = remove.start,
endMargin = 16.dp,
bias = 0f
)
start.linkTo(image.end, margin = 16.dp)
}
)
IconButton(
Expand All @@ -238,34 +232,23 @@ fun CartItem(
style = MaterialTheme.typography.body1,
color = JetsnackTheme.colors.textHelp,
modifier = Modifier.constrainAs(tag) {
linkTo(
start = image.end,
startMargin = 16.dp,
end = parent.end,
endMargin = 16.dp,
bias = 0f
)
start.linkTo(image.end, margin = 16.dp)
}
)
Spacer(
Modifier
.height(8.dp)
.constrainAs(priceSpacer) {
linkTo(top = tag.bottom, bottom = price.top)
top.linkTo(tag.bottom)
bottom.linkTo(price.top)
}
)
Text(
text = formatPrice(snack.price),
style = MaterialTheme.typography.subtitle1,
color = JetsnackTheme.colors.textPrimary,
modifier = Modifier.constrainAs(price) {
linkTo(
start = image.end,
end = quantity.start,
startMargin = 16.dp,
endMargin = 16.dp,
bias = 0f
)
start.linkTo(image.end, margin = 16.dp)
}
)
QuantitySelector(
Expand All @@ -279,7 +262,8 @@ fun CartItem(
)
JetsnackDivider(
Modifier.constrainAs(divider) {
linkTo(start = parent.start, end = parent.end)
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(parent.bottom)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,11 @@ private fun SearchCategory(
width = constraints.maxWidth,
height = constraints.minHeight
) {
textPlaceable.place(
textPlaceable.placeRelative(
x = 0,
y = (constraints.maxHeight - textPlaceable.height) / 2 // centered
)
imagePlaceable.place(
imagePlaceable.placeRelative(
// image is placed to end of text i.e. will overflow to the end (but be clipped)
x = textWidth,
y = (constraints.maxHeight - imagePlaceable.height) / 2 // centered
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Search
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -62,6 +61,7 @@ import com.example.jetsnack.model.SnackRepo
import com.example.jetsnack.ui.components.JetsnackDivider
import com.example.jetsnack.ui.components.JetsnackSurface
import com.example.jetsnack.ui.theme.JetsnackTheme
import com.example.jetsnack.ui.utils.mirroringBackIcon
import com.google.accompanist.insets.statusBarsPadding

@Composable
Expand Down Expand Up @@ -190,7 +190,7 @@ private fun SearchBar(
if (searchFocused) {
IconButton(onClick = onClearQuery) {
Icon(
imageVector = Icons.Outlined.ArrowBack,
imageVector = mirroringBackIcon(),
tint = JetsnackTheme.colors.iconPrimary,
contentDescription = stringResource(R.string.label_back)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
Expand Down Expand Up @@ -74,6 +72,7 @@ import com.example.jetsnack.ui.components.SnackImage
import com.example.jetsnack.ui.theme.JetsnackTheme
import com.example.jetsnack.ui.theme.Neutral8
import com.example.jetsnack.ui.utils.formatPrice
import com.example.jetsnack.ui.utils.mirroringBackIcon
import com.google.accompanist.insets.navigationBarsPadding
import com.google.accompanist.insets.statusBarsPadding
import kotlin.math.max
Expand Down Expand Up @@ -133,7 +132,7 @@ private fun Up(upPress: () -> Unit) {
)
) {
Icon(
imageVector = Icons.Outlined.ArrowBack,
imageVector = mirroringBackIcon(),
tint = JetsnackTheme.colors.iconInteractive,
contentDescription = stringResource(R.string.label_back)
)
Expand Down Expand Up @@ -323,7 +322,7 @@ private fun CollapsingImageLayout(
width = constraints.maxWidth,
height = imageY + imageWidth
) {
imagePlaceable.place(imageX, imageY)
imagePlaceable.placeRelative(imageX, imageY)
}
}
}
Expand Down
40 changes: 40 additions & 0 deletions Jetsnack/app/src/main/java/com/example/jetsnack/ui/utils/Rtl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.jetsnack.ui.utils

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.ArrowForward
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection

/**
* Returns the correct icon based on the current layout direction.
*/
@Composable
fun mirroringIcon(ltrIcon: ImageVector, rtlIcon: ImageVector): ImageVector =
if (LocalLayoutDirection.current == LayoutDirection.Ltr) ltrIcon else rtlIcon

/**
* Returns the correct back navigation icon based on the current layout direction.
*/
@Composable
fun mirroringBackIcon() = mirroringIcon(
ltrIcon = Icons.Outlined.ArrowBack, rtlIcon = Icons.Outlined.ArrowForward
)

0 comments on commit 73d7f25

Please sign in to comment.