From d75035bf7d918be8f52591ffd7d60489a64329a1 Mon Sep 17 00:00:00 2001 From: David Gamboa Date: Mon, 1 Nov 2021 12:43:48 +0100 Subject: [PATCH] Introduce TopAppBar component. --- .../java/kiwi/orbit/compose/catalog/Screen.kt | 17 +- .../compose/catalog/components/AppBar.kt | 187 ------------------ .../compose/catalog/screens/MainScreen.kt | 7 +- ui/build.gradle.kts | 5 + .../orbit/compose/ui/controls/TopAppBar.kt | 119 +++++++++++ .../res/drawable/ic_appbar_arrow_left.xml | 9 + .../res/drawable/ic_appbar_arrow_right.xml | 15 ++ ui/src/main/res/values/strings.xml | 1 + 8 files changed, 152 insertions(+), 208 deletions(-) delete mode 100644 catalog/src/main/java/kiwi/orbit/compose/catalog/components/AppBar.kt create mode 100644 ui/src/main/java/kiwi/orbit/compose/ui/controls/TopAppBar.kt create mode 100644 ui/src/main/res/drawable/ic_appbar_arrow_left.xml create mode 100644 ui/src/main/res/drawable/ic_appbar_arrow_right.xml diff --git a/catalog/src/main/java/kiwi/orbit/compose/catalog/Screen.kt b/catalog/src/main/java/kiwi/orbit/compose/catalog/Screen.kt index ab0be94d8..a6448466d 100644 --- a/catalog/src/main/java/kiwi/orbit/compose/catalog/Screen.kt +++ b/catalog/src/main/java/kiwi/orbit/compose/catalog/Screen.kt @@ -4,18 +4,14 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.google.accompanist.insets.LocalWindowInsets import com.google.accompanist.insets.rememberInsetsPaddingValues import kiwi.orbit.compose.catalog.components.Scaffold -import kiwi.orbit.compose.catalog.components.TopAppBar import kiwi.orbit.compose.ui.OrbitTheme -import kiwi.orbit.compose.ui.controls.Icon -import kiwi.orbit.compose.ui.controls.IconButton import kiwi.orbit.compose.ui.controls.Text +import kiwi.orbit.compose.ui.controls.TopAppBar @Composable fun Screen( @@ -28,16 +24,7 @@ fun Screen( topBar = { TopAppBar( title = { Text(text = title) }, - contentPadding = rememberInsetsPaddingValues( - insets = LocalWindowInsets.current.statusBars, - ), - navigationIcon = { - IconButton( - onClick = onUpClick, - ) { - Icon(imageVector = Icons.Rounded.ArrowBack, contentDescription = null) - } - }, + onBack = onUpClick, ) }, content = { diff --git a/catalog/src/main/java/kiwi/orbit/compose/catalog/components/AppBar.kt b/catalog/src/main/java/kiwi/orbit/compose/catalog/components/AppBar.kt deleted file mode 100644 index 171fcbbfa..000000000 --- a/catalog/src/main/java/kiwi/orbit/compose/catalog/components/AppBar.kt +++ /dev/null @@ -1,187 +0,0 @@ -package kiwi.orbit.compose.catalog.components - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import kiwi.orbit.compose.ui.OrbitTheme -import kiwi.orbit.compose.ui.controls.Surface -import kiwi.orbit.compose.ui.foundation.ContentEmphasis -import kiwi.orbit.compose.ui.foundation.LocalContentEmphasis -import kiwi.orbit.compose.ui.foundation.ProvideContentEmphasis -import kiwi.orbit.compose.ui.foundation.ProvideMergedTextStyle -import kiwi.orbit.compose.ui.foundation.contentColorFor - -@Composable -fun TopAppBar( - title: @Composable () -> Unit, - modifier: Modifier = Modifier, - navigationIcon: @Composable (() -> Unit)? = null, - actions: @Composable RowScope.() -> Unit = {}, - backgroundColor: Color = OrbitTheme.colors.surface.main, - contentColor: Color = contentColorFor(backgroundColor), - contentPadding: PaddingValues = AppBarDefaults.ContentPadding, - elevation: Dp = AppBarDefaults.TopAppBarElevation -) { - AppBar( - backgroundColor, - contentColor, - elevation, - contentPadding, - modifier - ) { - if (navigationIcon == null) { - Spacer(TitleInsetWithoutIcon) - } else { - Row(TitleIconModifier, verticalAlignment = Alignment.CenterVertically) { - CompositionLocalProvider( - LocalContentEmphasis provides ContentEmphasis.Normal, - content = navigationIcon - ) - } - } - - Row( - Modifier - .fillMaxHeight() - .weight(1f), - verticalAlignment = Alignment.CenterVertically - ) { - ProvideMergedTextStyle(value = OrbitTheme.typography.title3) { - CompositionLocalProvider( - LocalContentEmphasis provides ContentEmphasis.Normal, - content = title - ) - } - } - - ProvideContentEmphasis(ContentEmphasis.Minor) { - Row( - Modifier.fillMaxHeight(), - horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically, - content = actions - ) - } - } -} - -@Composable -fun TopAppBar( - modifier: Modifier = Modifier, - backgroundColor: Color = OrbitTheme.colors.surface.main, - contentColor: Color = contentColorFor(backgroundColor), - elevation: Dp = AppBarDefaults.TopAppBarElevation, - contentPadding: PaddingValues = AppBarDefaults.ContentPadding, - content: @Composable RowScope.() -> Unit -) { - AppBar( - backgroundColor, - contentColor, - elevation, - contentPadding, - modifier = modifier, - content = content - ) -} - -@Composable -fun BottomAppBar( - modifier: Modifier = Modifier, - backgroundColor: Color = OrbitTheme.colors.surface.main, - contentColor: Color = contentColorFor(backgroundColor), - elevation: Dp = AppBarDefaults.BottomAppBarElevation, - contentPadding: PaddingValues = AppBarDefaults.ContentPadding, - content: @Composable RowScope.() -> Unit -) { - AppBar( - backgroundColor, - contentColor, - elevation, - contentPadding, - modifier, - content - ) -} - -/** - * Contains default values used for [TopAppBar] and [BottomAppBar]. - */ -object AppBarDefaults { - // TODO: clarify elevation in surface mapping - spec says 0.dp but it appears to have an - // elevation overlay applied in dark theme examples. - /** - * Default elevation used for [TopAppBar]. - */ - val TopAppBarElevation = 4.dp - - /** - * Default elevation used for [BottomAppBar]. - */ - val BottomAppBarElevation = 8.dp - - /** - * Default padding used for [TopAppBar] and [BottomAppBar]. - */ - val ContentPadding = PaddingValues( - start = AppBarHorizontalPadding, - end = AppBarHorizontalPadding - ) -} - -@Composable -private fun AppBar( - backgroundColor: Color, - contentColor: Color, - elevation: Dp, - contentPadding: PaddingValues, - modifier: Modifier = Modifier, - content: @Composable RowScope.() -> Unit, -) { - Surface( - color = backgroundColor, - contentColor = contentColor, - elevation = elevation, - shape = RectangleShape, - modifier = modifier - ) { - ProvideContentEmphasis(ContentEmphasis.Minor) { - Row( - Modifier - .fillMaxWidth() - .padding(contentPadding) - .height(AppBarHeight), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, - content = content - ) - } - } -} - -private val AppBarHeight = 56.dp - -// TODO: this should probably be part of the touch target of the start and end icons, clarify this -private val AppBarHorizontalPadding = 4.dp - -// Start inset for the title when there is no navigation icon provided -private val TitleInsetWithoutIcon = Modifier.width(16.dp - AppBarHorizontalPadding) - -// Start inset for the title when there is a navigation icon provided -private val TitleIconModifier = Modifier - .fillMaxHeight() - .width(72.dp - AppBarHorizontalPadding) diff --git a/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/MainScreen.kt b/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/MainScreen.kt index 2397f4201..a1df85ae0 100644 --- a/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/MainScreen.kt +++ b/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/MainScreen.kt @@ -25,18 +25,16 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp -import com.google.accompanist.insets.LocalWindowInsets import com.google.accompanist.insets.navigationBarsPadding -import com.google.accompanist.insets.rememberInsetsPaddingValues import kiwi.orbit.compose.catalog.MainActions import kiwi.orbit.compose.catalog.components.Scaffold -import kiwi.orbit.compose.catalog.components.TopAppBar import kiwi.orbit.compose.icons.Icons import kiwi.orbit.compose.ui.OrbitTheme import kiwi.orbit.compose.ui.controls.Card import kiwi.orbit.compose.ui.controls.Icon import kiwi.orbit.compose.ui.controls.IconButton import kiwi.orbit.compose.ui.controls.Text +import kiwi.orbit.compose.ui.controls.TopAppBar import androidx.compose.material.icons.Icons.Rounded as MaterialIcons @Composable @@ -72,9 +70,6 @@ fun MainScreen( topBar = { TopAppBar( title = { Text("Orbit Compose Catalog") }, - contentPadding = rememberInsetsPaddingValues( - insets = LocalWindowInsets.current.statusBars, - ), actions = { IconButton(onClick = onToggleTheme) { Icon(MaterialIcons.BrightnessMedium, contentDescription = null) diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index e65feacb9..4e1093d51 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -50,7 +50,12 @@ dependencies { implementation(projects.icons) implementation(libs.kotlin.stdlib) + + implementation(libs.accompanist.insets) + implementation(libs.accompanist.insetsUi) + implementation(libs.androidx.core) + implementation(libs.compose.runtime) implementation(libs.compose.foundation) implementation(libs.compose.materialRipple) diff --git a/ui/src/main/java/kiwi/orbit/compose/ui/controls/TopAppBar.kt b/ui/src/main/java/kiwi/orbit/compose/ui/controls/TopAppBar.kt new file mode 100644 index 000000000..20152ffd1 --- /dev/null +++ b/ui/src/main/java/kiwi/orbit/compose/ui/controls/TopAppBar.kt @@ -0,0 +1,119 @@ +package kiwi.orbit.compose.ui.controls + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.RowScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTag +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.google.accompanist.insets.LocalWindowInsets +import com.google.accompanist.insets.rememberInsetsPaddingValues +import kiwi.orbit.compose.ui.OrbitTheme +import kiwi.orbit.compose.ui.R +import kiwi.orbit.compose.ui.foundation.ContentEmphasis +import kiwi.orbit.compose.ui.foundation.LocalContentEmphasis +import kiwi.orbit.compose.ui.foundation.ProvideMergedTextStyle +import kiwi.orbit.compose.ui.foundation.contentColorFor +import com.google.accompanist.insets.ui.TopAppBar as AccompanistTopAppBar + +@Composable +public fun TopAppBar( + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + navigationIcon: @Composable (() -> Unit)? = null, + actions: @Composable RowScope.() -> Unit = {}, + backgroundColor: Color = OrbitTheme.colors.surface.main, + contentColor: Color = contentColorFor(backgroundColor), + elevation: Dp = 2.dp, +) { + AccompanistTopAppBar( + title = { + ProvideMergedTextStyle( + value = OrbitTheme.typography.title2.copy( + fontSize = 20.sp, + letterSpacing = 0.15.sp, + fontWeight = FontWeight.Medium, + ) + ) { + CompositionLocalProvider( + LocalContentEmphasis provides ContentEmphasis.Normal + ) { + Box( + Modifier.semantics(mergeDescendants = true) { + testTag = TopAppBarSemantics.TitleTag + } + ) { + title() + } + } + } + }, + modifier = modifier.testTag(TopAppBarSemantics.Tag), + navigationIcon = if (navigationIcon != null) { + { + CompositionLocalProvider( + LocalContentEmphasis provides ContentEmphasis.Normal + ) { + navigationIcon.invoke() + } + } + } else null, + contentPadding = rememberInsetsPaddingValues( + insets = LocalWindowInsets.current.systemBars, + applyBottom = false + ), + actions = actions, + backgroundColor = backgroundColor, + contentColor = contentColor, + elevation = elevation, + ) +} + +@Composable +public fun TopAppBar( + title: @Composable () -> Unit, + onBack: () -> Unit, + modifier: Modifier = Modifier, + actions: @Composable RowScope.() -> Unit = {}, +) { + TopAppBar( + title = title, + modifier = modifier, + navigationIcon = { + IconButton( + onClick = { onBack() }, + ) { + Icon( + getBackArrowIcon(), + contentDescription = stringResource(R.string.orbit_cd_navigate_up), + ) + } + }, + actions = actions, + ) +} + +@Composable +private fun getBackArrowIcon(): Painter { + val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl + return painterResource( + if (isRtl) R.drawable.ic_appbar_arrow_right else R.drawable.ic_appbar_arrow_left + ) +} + +public object TopAppBarSemantics { + public const val Tag: String = "top_app_bar" + public const val TitleTag: String = "title" +} diff --git a/ui/src/main/res/drawable/ic_appbar_arrow_left.xml b/ui/src/main/res/drawable/ic_appbar_arrow_left.xml new file mode 100644 index 000000000..d948a3da5 --- /dev/null +++ b/ui/src/main/res/drawable/ic_appbar_arrow_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/ui/src/main/res/drawable/ic_appbar_arrow_right.xml b/ui/src/main/res/drawable/ic_appbar_arrow_right.xml new file mode 100644 index 000000000..f7b24e721 --- /dev/null +++ b/ui/src/main/res/drawable/ic_appbar_arrow_right.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index c4a9bfc4c..5f9f7cdcb 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ + Navigate up Add Remove Hide password