Fix Wear content clipping on watch shapes

This commit is contained in:
callebtc 2026-08-24 19:38:26 +02:00
parent eab894ce78
commit 5852fca9b5
8 changed files with 124 additions and 15 deletions

View File

@ -19,8 +19,8 @@ android {
targetSdk = libs.versions.targetSdk.get().toInt()
// Wear releases use a separate high range because Play requires every artifact in
// one application ID to have a unique version code across all form factors.
versionCode = 1_000_000_002
versionName = "0.1.1"
versionCode = 1_000_000_003
versionName = "0.1.2"
vectorDrawables {
useSupportLibrary = true

View File

@ -8,7 +8,6 @@ import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -28,6 +27,7 @@ import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.changedToUp
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
@ -256,7 +256,8 @@ private fun ChatBody(
}
}
) {
ScreenScaffold(scrollState = columnState) {
ScreenScaffold(scrollState = columnState) { scaffoldPadding ->
val layoutDirection = LocalLayoutDirection.current
TransformingLazyColumn(
state = columnState,
modifier = Modifier.fillMaxSize(),
@ -265,7 +266,11 @@ private fun ChatBody(
// The padding reserves permanent room for the floating header and action
// bar; being constant, it never disturbs an in-flight scroll gesture.
verticalArrangement = Arrangement.Bottom,
contentPadding = PaddingValues(top = 30.dp, bottom = 64.dp)
contentPadding = scaffoldPadding.withMinimumVerticalPadding(
layoutDirection = layoutDirection,
top = 30.dp,
bottom = 64.dp
)
) {
if (messages.isEmpty()) {
item {

View File

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

View File

@ -67,10 +67,11 @@ fun PeopleScreen(onOpenDm: (String) -> Unit, onEditNickname: () -> Unit) {
)
}
ScreenScaffold(scrollState = listState) {
ScreenScaffold(scrollState = listState) { contentPadding ->
ScalingLazyColumn(
state = listState,
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize(),
contentPadding = contentPadding
) {
item {
ListHeader {

View File

@ -1,7 +1,6 @@
package com.bitchat.watch.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -15,6 +14,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
@ -48,11 +48,16 @@ fun UserDetailScreen(
val listState = rememberScalingLazyListState()
val palette = LocalBitchatPalette.current
ScreenScaffold(scrollState = listState) {
ScreenScaffold(scrollState = listState) { scaffoldPadding ->
val layoutDirection = LocalLayoutDirection.current
ScalingLazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 8.dp)
contentPadding = scaffoldPadding.withAdditionalPadding(
layoutDirection = layoutDirection,
horizontal = 10.dp,
vertical = 8.dp
)
) {
item {
ListHeader {

View File

@ -2,7 +2,6 @@ package com.bitchat.watch.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -14,6 +13,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@ -43,11 +43,16 @@ fun VerificationCodeScreen(peerID: String) {
val listState = rememberScalingLazyListState()
val palette = LocalBitchatPalette.current
ScreenScaffold(scrollState = listState) {
ScreenScaffold(scrollState = listState) { scaffoldPadding ->
val layoutDirection = LocalLayoutDirection.current
ScalingLazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 8.dp)
contentPadding = scaffoldPadding.withAdditionalPadding(
layoutDirection = layoutDirection,
horizontal = 10.dp,
vertical = 8.dp
)
) {
item {
ListHeader {

View File

@ -0,0 +1,52 @@
package com.bitchat.watch.ui
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
/**
* Preserve the responsive, shape-aware padding supplied by Wear Material while allowing a
* screen to reserve additional room for its own content or floating controls.
*/
internal fun PaddingValues.withAdditionalPadding(
layoutDirection: LayoutDirection,
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,
top = calculateTopPadding() + vertical,
end = end + horizontal,
bottom = calculateBottomPadding() + vertical
)
}
internal fun PaddingValues.withMinimumVerticalPadding(
layoutDirection: LayoutDirection,
top: Dp,
bottom: 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,
top = maxOf(calculateTopPadding(), top),
end = end,
bottom = maxOf(calculateBottomPadding(), bottom)
)
}

View File

@ -0,0 +1,40 @@
package com.bitchat.watch.ui
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import org.junit.Assert.assertEquals
import org.junit.Test
class WearContentPaddingTest {
@Test
fun `additional padding preserves scaffold insets`() {
val resolved = PaddingValues(start = 10.dp, top = 18.dp, end = 12.dp, bottom = 20.dp)
.withAdditionalPadding(
layoutDirection = LayoutDirection.Ltr,
horizontal = 10.dp,
vertical = 8.dp
)
assertEquals(20.dp, resolved.calculateLeftPadding(LayoutDirection.Ltr))
assertEquals(22.dp, resolved.calculateRightPadding(LayoutDirection.Ltr))
assertEquals(26.dp, resolved.calculateTopPadding())
assertEquals(28.dp, resolved.calculateBottomPadding())
}
@Test
fun `chat padding keeps responsive sides and overlay clearances`() {
val resolved = PaddingValues(start = 10.dp, top = 18.dp, end = 12.dp, bottom = 20.dp)
.withMinimumVerticalPadding(
layoutDirection = LayoutDirection.Ltr,
top = 30.dp,
bottom = 64.dp
)
assertEquals(10.dp, resolved.calculateLeftPadding(LayoutDirection.Ltr))
assertEquals(12.dp, resolved.calculateRightPadding(LayoutDirection.Ltr))
assertEquals(30.dp, resolved.calculateTopPadding())
assertEquals(64.dp, resolved.calculateBottomPadding())
}
}