From 06e1e45c1220b5d50d3f91191cef9eea6fae3b09 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Thu, 6 Aug 2026 18:07:16 +0200 Subject: [PATCH] Keep the bars docked to the window out of the navigation bar Scaffold places its bottom slot against the bottom of the window and reserves nothing for it -- the documentation is explicit that topBar and bottomBar are expected to handle insets themselves, which is why NavigationBar and BottomAppBar carry windowInsets of their own. Three of this app's four docked bars are plain Surfaces and reserved nothing, so their contents were drawn under the navigation bar. It bites on every detail screen whatever the navigation style, because the navigation container is hidden away from a panel root and NavigationSuiteScaffold consumes NoWindowInsets while hidden: nothing above the screen has taken the system bars. With three-button navigation that is 48dp, and on the scope editor it left a few pixels of Apply and Discard to aim at, which is #884. Gesture navigation is 24dp and was covered too, the handle drawn across the supporting line. The padding goes inside each bar's Surface rather than on it, so the fill still reaches the bottom edge and the bar reads as one surface. Insets already consumed count for nothing, so the same call adds nothing in the arrangements where a container below has taken them. Home is the same fault from the other direction. It sets contentWindowInsets to zero so the header can run under the status bar, which gave away the bottom as well; with the panels floating there is no container to have taken it, and both the last row of the feed and the scroll controls ended up behind the navigation bar. Take the bottom edge alone, from the Scaffold's own default so a bottom display cutout counts. Verified on a Pixel 6 (Android 17) under both navigation styles. With three buttons the bar occupies 2274 to 2400: Apply now ends at 2212 and the scroll controls at 2217. The eleven ModalBottomSheets need nothing -- none overrides contentWindowInsets and the default already covers the bottom edge. --- .../vector/manager/ui/screens/home/HomeScreen.kt | 12 +++++++++--- .../manager/ui/screens/modules/ScopeScreen.kt | 15 ++++++++++++++- .../ui/screens/report/TroubleshootScreen.kt | 5 ++++- .../ui/screens/update/FrameworkUpdateScreen.kt | 5 +++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/home/HomeScreen.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/home/HomeScreen.kt index 26ab63e24..3be33eb41 100644 --- a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/home/HomeScreen.kt +++ b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/home/HomeScreen.kt @@ -14,7 +14,8 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -40,6 +41,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.pulltorefresh.PullToRefreshBox @@ -210,8 +212,12 @@ fun HomeScreen( Scaffold( // The header draws its own status-bar inset so it can run under the bar; letting the - // Scaffold consume it here would leave a band of plain background above the pane. - contentWindowInsets = WindowInsets(0), + // Scaffold consume it here would leave a band of plain background above the pane. The + // bottom is the Scaffold's to reserve, though: with the panels floating there is no + // navigation container underneath to have taken it, and the last row of the feed would end + // up behind three-button navigation. Already-consumed insets are excluded from this, so it + // still adds nothing in the arrangements where a container is there. + contentWindowInsets = ScaffoldDefaults.contentWindowInsets.only(WindowInsetsSides.Bottom), snackbarHost = { VectorSnackbarHost(snackbars) }, ) { padding -> val listState = rememberLazyListState() diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt index 10dfe7dfb..4b1fe622f 100644 --- a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt +++ b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -1037,7 +1038,19 @@ private fun ApplyBar( ) { Surface(tonalElevation = 3.dp, color = MaterialTheme.colorScheme.surfaceContainerHigh) { Row( - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 10.dp), + // The bar is the last child of the window, and this screen is a detail screen: the + // navigation container is hidden here, so nothing above has reserved the system bars + // and Scaffold hands its bottom slot the whole window. Without this the buttons sit + // under three-button navigation, where what is left of them is a few pixels tall. + // + // Inside the Surface rather than on it, so the tonal fill still runs to the bottom + // edge and the bar reads as one surface rather than as a strip floating above the + // system's own. Insets already consumed count for nothing here, so the same call is + // correct in the arrangements where a container below has taken them. + modifier = + Modifier.fillMaxWidth() + .navigationBarsPadding() + .padding(horizontal = 16.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, ) { Column(Modifier.weight(1f)) { diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/report/TroubleshootScreen.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/report/TroubleshootScreen.kt index 62fb8d8d1..70f8483e0 100644 --- a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/report/TroubleshootScreen.kt +++ b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/report/TroubleshootScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -120,7 +121,9 @@ fun TroubleshootScreen( bottomBar = { Surface(tonalElevation = 3.dp) { Row( - modifier = Modifier.fillMaxWidth().padding(20.dp), + // Scaffold places its bottom slot against the bottom of the window and leaves + // the system bars to it, so the button says where it stands itself. + modifier = Modifier.fillMaxWidth().navigationBarsPadding().padding(20.dp), horizontalArrangement = Arrangement.Center, ) { Button(onClick = { onOpenUrl(GitHubRepository.ISSUES_URL) }) { diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/update/FrameworkUpdateScreen.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/update/FrameworkUpdateScreen.kt index 2b4b9e619..257a1c817 100644 --- a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/update/FrameworkUpdateScreen.kt +++ b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/update/FrameworkUpdateScreen.kt @@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -312,6 +313,10 @@ private fun UpdateBar( modifier = Modifier.fillMaxWidth() .background(colors.surfaceContainer) + // After the fill, before the padding: the container colour runs to the bottom edge + // of the window while the flash button stays clear of three-button navigation, + // which Scaffold leaves to its bottom slot rather than reserving itself. + .navigationBarsPadding() .padding(horizontal = 16.dp, vertical = 12.dp) ) { when (flash) {