Fix small-screen header clipping (#519) (#532)

* Fix small-screen header clipping: reserve status-bar space and center-constrain titles (fixes #519)

* Remove redundant Gradle configuration lines
This commit is contained in:
kargathara Aakash
2026-01-04 13:57:55 +07:00
committed by GitHub
parent 54c96bd737
commit d73976537d
3 changed files with 60 additions and 47 deletions
@@ -26,6 +26,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.style.TextAlign
import com.bitchat.android.core.ui.utils.singleOrTripleClickable import com.bitchat.android.core.ui.utils.singleOrTripleClickable
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
@@ -406,7 +408,9 @@ private fun PrivateChatHeader(
Text( Text(
text = titleText, text = titleText,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
color = Color(0xFFFF9500) // Orange color = Color(0xFFFF9500), // Orange
maxLines = 1,
overflow = TextOverflow.Ellipsis
) )
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
@@ -493,8 +497,12 @@ private fun ChannelHeader(
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
color = Color(0xFFFF9500), // Orange to match input field color = Color(0xFFFF9500), // Orange to match input field
modifier = Modifier modifier = Modifier
.align(Alignment.Center) .fillMaxWidth()
.clickable { onSidebarClick() } .padding(horizontal = 56.dp)
.clickable { onSidebarClick() },
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis
) )
// Leave button - positioned on the right // Leave button - positioned on the right
@@ -552,7 +560,9 @@ private fun MainHeader(
modifier = Modifier.singleOrTripleClickable( modifier = Modifier.singleOrTripleClickable(
onSingleClick = onTitleClick, onSingleClick = onTitleClick,
onTripleClick = onTripleTitleClick onTripleClick = onTripleTitleClick
) ),
maxLines = 1,
overflow = TextOverflow.Ellipsis
) )
Spacer(modifier = Modifier.width(2.dp)) Spacer(modifier = Modifier.width(2.dp))
@@ -23,6 +23,7 @@ import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitchat.android.model.BitchatMessage import com.bitchat.android.model.BitchatMessage
@@ -113,7 +114,10 @@ fun ChatScreen(viewModel: ChatViewModel) {
.background(colorScheme.background) // Extend background to fill entire screen including status bar .background(colorScheme.background) // Extend background to fill entire screen including status bar
) { ) {
val headerHeight = 42.dp val headerHeight = 42.dp
// Reserve exact height: header + status bar inset so content below won't be overlapped
val statusBarTop = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
val reservedHeaderHeight = headerHeight + statusBarTop
// Main content area that responds to keyboard/window insets // Main content area that responds to keyboard/window insets
Column( Column(
modifier = Modifier modifier = Modifier
@@ -121,11 +125,10 @@ fun ChatScreen(viewModel: ChatViewModel) {
.windowInsetsPadding(WindowInsets.ime) // This handles keyboard insets .windowInsetsPadding(WindowInsets.ime) // This handles keyboard insets
.windowInsetsPadding(WindowInsets.navigationBars) // Add bottom padding when keyboard is not expanded .windowInsetsPadding(WindowInsets.navigationBars) // Add bottom padding when keyboard is not expanded
) { ) {
// Header spacer - creates exact space for the floating header (status bar + compact header) // Header spacer - reserve space equal to header + status bar inset
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.windowInsetsPadding(WindowInsets.statusBars) .height(reservedHeaderHeight)
.height(headerHeight)
) )
// Messages area - takes up available space, will compress when keyboard appears // Messages area - takes up available space, will compress when keyboard appears
@@ -254,15 +257,7 @@ fun ChatScreen(viewModel: ChatViewModel) {
onLocationNotesClick = { showLocationNotesSheet = true } onLocationNotesClick = { showLocationNotesSheet = true }
) )
// Divider under header - positioned after status bar + header height
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.windowInsetsPadding(WindowInsets.statusBars)
.offset(y = headerHeight)
.zIndex(1f),
color = colorScheme.outline.copy(alpha = 0.3f)
)
val alpha by animateFloatAsState( val alpha by animateFloatAsState(
targetValue = if (showSidebar) 0.5f else 0f, targetValue = if (showSidebar) 0.5f else 0f,
@@ -459,38 +454,46 @@ private fun ChatFloatingHeader(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.zIndex(1f) .zIndex(1f)
.windowInsetsPadding(WindowInsets.statusBars), // Extend into status bar area .statusBarsPadding(), // Respect status bar insets
color = colorScheme.background // Solid background color extending into status bar color = colorScheme.background // Solid background color extending into status bar
) { ) {
TopAppBar( Column {
title = { TopAppBar(
ChatHeaderContent( title = {
selectedPrivatePeer = selectedPrivatePeer, ChatHeaderContent(
currentChannel = currentChannel, selectedPrivatePeer = selectedPrivatePeer,
nickname = nickname, currentChannel = currentChannel,
viewModel = viewModel, nickname = nickname,
onBackClick = { viewModel = viewModel,
when { onBackClick = {
selectedPrivatePeer != null -> viewModel.endPrivateChat() when {
currentChannel != null -> viewModel.switchToChannel(null) selectedPrivatePeer != null -> viewModel.endPrivateChat()
currentChannel != null -> viewModel.switchToChannel(null)
}
},
onSidebarClick = onSidebarToggle,
onTripleClick = onPanicClear,
onShowAppInfo = onShowAppInfo,
onLocationChannelsClick = onLocationChannelsClick,
onLocationNotesClick = {
// Ensure location is loaded before showing sheet
locationManager.refreshChannels()
onLocationNotesClick()
} }
}, )
onSidebarClick = onSidebarToggle, },
onTripleClick = onPanicClear, colors = TopAppBarDefaults.topAppBarColors(
onShowAppInfo = onShowAppInfo, containerColor = Color.Transparent
onLocationChannelsClick = onLocationChannelsClick, ),
onLocationNotesClick = { modifier = Modifier.heightIn(min = headerHeight) // Allow header to expand for accessibility font scales
// Ensure location is loaded before showing sheet )
locationManager.refreshChannels()
onLocationNotesClick() // Divider under header - always aligned with header bottom
} Divider(
) modifier = Modifier.fillMaxWidth(),
}, color = colorScheme.outline.copy(alpha = 0.3f)
colors = TopAppBarDefaults.topAppBarColors( )
containerColor = Color.Transparent }
),
modifier = Modifier.height(headerHeight) // Ensure compact header height
)
} }
} }
+1 -1
View File
@@ -26,4 +26,4 @@ android.nonTransitiveRClass=false
kotlin.code.style=official kotlin.code.style=official
# JVM heap size configuration to prevent OutOfMemoryError # JVM heap size configuration to prevent OutOfMemoryError
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError