Polish Wear shape-aware layout implementation

This commit is contained in:
callebtc 2026-08-26 16:46:13 +02:00
parent 3ed46222ba
commit a125da48aa
4 changed files with 59 additions and 27 deletions

View File

@ -20,7 +20,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.BlendMode
@ -298,8 +298,7 @@ private fun ChatBody(
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithContent {
drawContent()
.drawWithCache {
val edgeMask = if (isScreenRound) {
// Fade toward the actual circular contour so glyphs become
// transparent before the panel can crop their left or right edge.
@ -323,10 +322,13 @@ private fun ChatBody(
1f to Color.Transparent
)
}
drawRect(
brush = edgeMask,
blendMode = BlendMode.DstIn
)
onDrawWithContent {
drawContent()
drawRect(
brush = edgeMask,
blendMode = BlendMode.DstIn
)
}
},
// Arrangement.Bottom anchors short content to the bottom: the first message
// starts just above the action bar and new messages push history upward.
@ -335,9 +337,9 @@ private fun ChatBody(
// point. Rows may travel behind the overlays only after they have begun the Wear
// edge scale/fade treatment.
verticalArrangement = Arrangement.Bottom,
// Wear Material already supplies a responsive 5.2% horizontal inset. Keep it,
// while replacing its vertical inset with the overlay clearances used before the
// shape fix. This avoids both duplicated padding and a shortened list viewport.
// Keep Wear Material's responsive horizontal inset while replacing its vertical
// inset with the overlay clearances used before the shape fix. This avoids both
// duplicated padding and a shortened list viewport.
contentPadding = scaffoldPadding.withVerticalClearance(
layoutDirection = layoutDirection,
top = CHAT_HEADER_CONTENT_CLEARANCE,

View File

@ -46,11 +46,10 @@ fun PeerDebugScreen() {
val rssi = mesh?.getPeerRSSI() ?: emptyMap()
val identityRevision by WearPeerIdentityState.revision.collectAsState()
ScreenScaffold(scrollState = listState) { scaffoldPadding ->
ScreenScaffold(scrollState = listState) {
ScalingLazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
contentPadding = scaffoldPadding
modifier = Modifier.fillMaxSize()
) {
item {
ListHeader {

View File

@ -14,18 +14,10 @@ internal fun PaddingValues.withAdditionalPadding(
horizontal: Dp = 0.dp,
vertical: Dp = 0.dp
): PaddingValues {
val start = when (layoutDirection) {
LayoutDirection.Ltr -> calculateLeftPadding(layoutDirection)
LayoutDirection.Rtl -> calculateRightPadding(layoutDirection)
}
val end = when (layoutDirection) {
LayoutDirection.Ltr -> calculateRightPadding(layoutDirection)
LayoutDirection.Rtl -> calculateLeftPadding(layoutDirection)
}
return PaddingValues(
start = start + horizontal,
start = calculateStartPadding(layoutDirection) + horizontal,
top = calculateTopPadding() + vertical,
end = end + horizontal,
end = calculateEndPadding(layoutDirection) + horizontal,
bottom = calculateBottomPadding() + vertical
)
}
@ -40,13 +32,22 @@ internal fun PaddingValues.withVerticalClearance(
top: Dp,
bottom: Dp
): PaddingValues {
val start = when (layoutDirection) {
return PaddingValues(
start = calculateStartPadding(layoutDirection),
top = top,
end = calculateEndPadding(layoutDirection),
bottom = bottom
)
}
private fun PaddingValues.calculateStartPadding(layoutDirection: LayoutDirection): Dp =
when (layoutDirection) {
LayoutDirection.Ltr -> calculateLeftPadding(layoutDirection)
LayoutDirection.Rtl -> calculateRightPadding(layoutDirection)
}
val end = when (layoutDirection) {
private fun PaddingValues.calculateEndPadding(layoutDirection: LayoutDirection): Dp =
when (layoutDirection) {
LayoutDirection.Ltr -> calculateRightPadding(layoutDirection)
LayoutDirection.Rtl -> calculateLeftPadding(layoutDirection)
}
return PaddingValues(start = start, top = top, end = end, bottom = bottom)
}

View File

@ -37,4 +37,34 @@ class WearContentPaddingTest {
assertEquals(30.dp, resolved.calculateTopPadding())
assertEquals(64.dp, resolved.calculateBottomPadding())
}
@Test
fun `additional padding preserves logical start and end in rtl`() {
val resolved = PaddingValues(start = 10.dp, top = 18.dp, end = 12.dp, bottom = 20.dp)
.withAdditionalPadding(
layoutDirection = LayoutDirection.Rtl,
horizontal = 10.dp,
vertical = 8.dp
)
assertEquals(22.dp, resolved.calculateLeftPadding(LayoutDirection.Rtl))
assertEquals(20.dp, resolved.calculateRightPadding(LayoutDirection.Rtl))
assertEquals(26.dp, resolved.calculateTopPadding())
assertEquals(28.dp, resolved.calculateBottomPadding())
}
@Test
fun `vertical clearance preserves logical start and end in rtl`() {
val resolved = PaddingValues(start = 10.dp, top = 20.dp, end = 12.dp, bottom = 20.dp)
.withVerticalClearance(
layoutDirection = LayoutDirection.Rtl,
top = 30.dp,
bottom = 64.dp
)
assertEquals(12.dp, resolved.calculateLeftPadding(LayoutDirection.Rtl))
assertEquals(10.dp, resolved.calculateRightPadding(LayoutDirection.Rtl))
assertEquals(30.dp, resolved.calculateTopPadding())
assertEquals(64.dp, resolved.calculateBottomPadding())
}
}