diff --git a/app/src/main/kotlin/com/wire/android/ui/common/topappbar/WireCenterAlignedTopAppBar.kt b/app/src/main/kotlin/com/wire/android/ui/common/topappbar/WireCenterAlignedTopAppBar.kt index b0a7d9734b9..36cb81db391 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/topappbar/WireCenterAlignedTopAppBar.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/topappbar/WireCenterAlignedTopAppBar.kt @@ -18,10 +18,13 @@ package com.wire.android.ui.common.topappbar +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -30,16 +33,25 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import com.wire.android.ui.common.dimensions +import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography +import com.wire.android.util.ui.PreviewMultipleThemes +import kotlin.math.ceil @Composable fun WireCenterAlignedTopAppBar( title: String, + modifier: Modifier = Modifier, titleStyle: TextStyle = MaterialTheme.wireTypography.title01, maxLines: Int = 2, subtitleContent: @Composable ColumnScope.() -> Unit = {}, @@ -47,7 +59,6 @@ fun WireCenterAlignedTopAppBar( navigationIconType: NavigationIconType? = NavigationIconType.Back, elevation: Dp = MaterialTheme.wireDimensions.topBarShadowElevation, actions: @Composable RowScope.() -> Unit = {}, - modifier: Modifier = Modifier, bottomContent: @Composable ColumnScope.() -> Unit = {} ) { WireCenterAlignedTopAppBar( @@ -72,12 +83,12 @@ fun WireCenterAlignedTopAppBar( @Composable fun WireCenterAlignedTopAppBar( titleContent: @Composable ColumnScope.() -> Unit, + modifier: Modifier = Modifier, subtitleContent: @Composable ColumnScope.() -> Unit = {}, onNavigationPressed: () -> Unit = {}, navigationIconType: NavigationIconType? = NavigationIconType.Back, elevation: Dp = MaterialTheme.wireDimensions.topBarShadowElevation, actions: @Composable RowScope.() -> Unit = {}, - modifier: Modifier = Modifier, bottomContent: @Composable ColumnScope.() -> Unit = {} ) { Surface( @@ -106,16 +117,76 @@ fun WireCenterAlignedTopAppBar( fun WireTopAppBarTitle( title: String, style: TextStyle, + modifier: Modifier = Modifier, maxLines: Int = 2 ) { - Text( - modifier = Modifier.padding( - start = dimensions().spacing6x, - end = dimensions().spacing6x - ), - text = title, - style = style, - maxLines = maxLines, - overflow = TextOverflow.Ellipsis - ) + // There's an ongoing issue about multiline text taking all width available instead of wrapping visible text. + // https://issuetracker.google.com/issues/206039942 + // It's very noticeable on TopAppBar because due to that issue, the title is not centered, even if there are large enough empty spaces + // on both sides and all lines of text are actually shorter and could fit at the center. + // This workaround is based on this: https://stackoverflow.com/a/69947555, but instead of using SubcomposeLayout, we just measure text. + BoxWithConstraints( + modifier = modifier + ) { + val textMeasurer = rememberTextMeasurer() + val textLayoutResult: TextLayoutResult = textMeasurer.measure( + text = title, + style = style, + maxLines = maxLines, + overflow = TextOverflow.Ellipsis, + constraints = Constraints( + minWidth = 0, + minHeight = 0, + maxWidth = constraints.maxWidth, + maxHeight = constraints.maxHeight + ) + ) + val width = with(LocalDensity.current) { + (0 until textLayoutResult.lineCount).maxOf { line -> + ceil(textLayoutResult.getLineRight(line) - textLayoutResult.getLineLeft(line)).toInt() + }.toDp() + } + Text( + modifier = Modifier + .padding(horizontal = dimensions().spacing6x) + .width(width), + text = title, + style = style, + maxLines = maxLines, + overflow = TextOverflow.Ellipsis, + ) + } +} + +@PreviewMultipleThemes +@Composable +fun PreviewWireCenterAlignedTopAppBarWithDefaultTitle() = WireTheme { + Box(modifier = Modifier.width(400.dp)) { + WireCenterAlignedTopAppBar( + title = "This is title", + titleStyle = MaterialTheme.wireTypography.title01 + ) + } +} + +@PreviewMultipleThemes +@Composable +fun PreviewWireCenterAlignedTopAppBarWithDefaultTwoLinesTitle() = WireTheme { + Box(modifier = Modifier.width(400.dp)) { + WireCenterAlignedTopAppBar( + title = "This is title is very long this_is_a_very_long_word", + titleStyle = MaterialTheme.wireTypography.title01 + ) + } +} + +@PreviewMultipleThemes +@Composable +fun PreviewWireCenterAlignedTopAppBarWithDefaultTwoLinesTooLongTitle() = WireTheme { + Box(modifier = Modifier.width(400.dp)) { + WireCenterAlignedTopAppBar( + title = "This is title is even longer than previous one this_is_a_very_long_word", + titleStyle = MaterialTheme.wireTypography.title01 + ) + } }