From a125da48aabbfedc922d298f323543ee79c54486 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:46:13 +0200 Subject: [PATCH] Polish Wear shape-aware layout implementation --- .../java/com/bitchat/watch/ui/ChatScaffold.kt | 22 +++++++------- .../com/bitchat/watch/ui/PeerDebugScreen.kt | 5 ++-- .../bitchat/watch/ui/WearContentPadding.kt | 29 +++++++++--------- .../watch/ui/WearContentPaddingTest.kt | 30 +++++++++++++++++++ 4 files changed, 59 insertions(+), 27 deletions(-) diff --git a/wear/src/main/java/com/bitchat/watch/ui/ChatScaffold.kt b/wear/src/main/java/com/bitchat/watch/ui/ChatScaffold.kt index bec5125d..15b18436 100644 --- a/wear/src/main/java/com/bitchat/watch/ui/ChatScaffold.kt +++ b/wear/src/main/java/com/bitchat/watch/ui/ChatScaffold.kt @@ -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, diff --git a/wear/src/main/java/com/bitchat/watch/ui/PeerDebugScreen.kt b/wear/src/main/java/com/bitchat/watch/ui/PeerDebugScreen.kt index fa83e59b..1994f976 100644 --- a/wear/src/main/java/com/bitchat/watch/ui/PeerDebugScreen.kt +++ b/wear/src/main/java/com/bitchat/watch/ui/PeerDebugScreen.kt @@ -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 { diff --git a/wear/src/main/java/com/bitchat/watch/ui/WearContentPadding.kt b/wear/src/main/java/com/bitchat/watch/ui/WearContentPadding.kt index e24671ff..738e56cf 100644 --- a/wear/src/main/java/com/bitchat/watch/ui/WearContentPadding.kt +++ b/wear/src/main/java/com/bitchat/watch/ui/WearContentPadding.kt @@ -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) -} diff --git a/wear/src/test/java/com/bitchat/watch/ui/WearContentPaddingTest.kt b/wear/src/test/java/com/bitchat/watch/ui/WearContentPaddingTest.kt index 477fad77..60191d67 100644 --- a/wear/src/test/java/com/bitchat/watch/ui/WearContentPaddingTest.kt +++ b/wear/src/test/java/com/bitchat/watch/ui/WearContentPaddingTest.kt @@ -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()) + } }