ca9a1c6b0197
fix: settings deck fills down to just above the build label (v2.9.27)
- time
- 2026-08-01 22:11 UTC · 2 months ago
- machine
- koh2
- parent
- 469044a2b623
↓ peach-android-ca9a1c6b.tar.gzatlas at this save
files changed3 files
VERSION+1 −1app/src/main/java/com/peachpasswords/ui/settings/SettingsScreen.kt+27 −13app/src/test/java/com/peachpasswords/ui/settings/SettingsStackTest.kt+11 −10app/src/main/java/com/peachpasswords/ui/settings/SettingsScreen.kt+27 −13
@@ -22,6 +22,9 @@2222 import androidx.compose.foundation.layout.Column2323 import androidx.compose.foundation.layout.Row2424 import androidx.compose.foundation.layout.Spacer25+import androidx.compose.foundation.layout.WindowInsets26+import androidx.compose.foundation.layout.asPaddingValues27+import androidx.compose.foundation.layout.navigationBars2528 import androidx.compose.foundation.layout.fillMaxSize2629 import androidx.compose.foundation.layout.fillMaxWidth2730 import androidx.compose.foundation.layout.height@@ -92,8 +95,17 @@9295 private val TAB_H = 46.dp9396 private val TAB_OVERLAP = 14.dp9497 private val TAB_CARD_H = TAB_H + 4.dp95-private val DECK_BOTTOM_GUTTER = 36.dp9698 99+/**100+ * Vertical room the build label needs at the bottom corner, excluding its101+ * navigation-bar inset (measured live, since it varies with gesture vs102+ * three-button nav): its bottom padding plus roughly one line of 9sp mono.103+ */104+private val VERSION_LABEL_FOOTPRINT = PeachSpacing.xs + 12.dp105+106+/** Clean gap left between the lowest fanned card and the build label. */107+private val DECK_TO_VERSION_GAP = 6.dp108+97109 /** Drag distance past which a stack card flips between focused and deck states. */98110 private val DRAG_THRESHOLD = 60.dp99111 @@ -129,18 +141,19 @@129141 expandedH - TAB_OVERLAP + COLLAPSED_TAB_STRIDE * distance130142 131143 /**132- * The deck fills the available screen height minus a bottom gutter on every133- * screen size; the gutter reserves room for the build label pinned to the134- * bottom corner. An earlier version capped this at a multiple of a constant135- * ported from the browser's small popup (`StackDeck.tsx`), but on tall panels136- * like the Galaxy Z Fold 7 the available height exceeded that cap — stranding137- * a band of dead space below the stack and clipping the Pro card's copy/reveal138- * buttons. Filling to the gutter is how phones already behave, so this just139- * makes tall screens consistent with them.144+ * The deck fills the available height down to just above the build label pinned145+ * to the bottom corner. [navBarBottom] is the label's navigation-bar inset,146+ * measured live by the caller (it varies with gesture vs three-button nav); here147+ * we additionally reserve the label's own padding/line height148+ * ([VERSION_LABEL_FOOTPRINT]) and a small visual gap ([DECK_TO_VERSION_GAP]) so149+ * the lowest fanned card sits cleanly above the label on every screen — phone or150+ * Fold. A previous fixed gutter ignored the nav inset, so it stranded a band of151+ * dead space below the stack (and clipped the Pro card's copy/reveal buttons)152+ * wherever the two didn't happen to line up.140153 */141-internal fun settingsDeckHeight(availableHeight: Dp): Dp {142- val constrained = availableHeight - DECK_BOTTOM_GUTTER143- return if (constrained < 0.dp) 0.dp else constrained154+internal fun settingsDeckHeight(availableHeight: Dp, navBarBottom: Dp): Dp {155+ val reserved = navBarBottom + VERSION_LABEL_FOOTPRINT + DECK_TO_VERSION_GAP156+ return (availableHeight - reserved).coerceAtLeast(0.dp)144157 }145158 146159 @Composable@@ -206,7 +219,8 @@206219 Spacer(Modifier.height(PeachSpacing.sm))207220 208221 BoxWithConstraints(Modifier.fillMaxSize()) {209- val deckHeight = settingsDeckHeight(maxHeight)222+ val navBarBottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()223+ val deckHeight = settingsDeckHeight(maxHeight, navBarBottom)210224 val focusHeight = deckHeight - 8.dp211225 // The deckOpen "pick a card" fan used a fixed EXPANDED_H (a literal212226 // browser-popup constant) for the active card's preview height,app/src/test/java/com/peachpasswords/ui/settings/SettingsStackTest.kt+11 −10
@@ -25,16 +25,17 @@2525 assertEquals(collapsedTabTop(5) - collapsedTabTop(4), stride)2626 }2727 28- @Test fun settings_deck_fills_available_height_instead_of_the_browsers_small_popup_cap() {29- // The deck used to hard-cap (a literal port of the browser's small popup,30- // later a MAX_DECK_H*2 bound), which stranded dead space below the stack and31- // clipped the Pro card's buttons on tall panels like the Galaxy Z Fold 7. It32- // now fills the available height minus the bottom gutter on every screen size.33- assertEquals(864.dp, settingsDeckHeight(900.dp))34- assertEquals(444.dp, settingsDeckHeight(480.dp))35- assertEquals(0.dp, settingsDeckHeight(24.dp))36- // Taller than the old 924dp cap allowed: still fills to the gutter, no cap.37- assertEquals(1164.dp, settingsDeckHeight(1200.dp))28+ @Test fun settings_deck_fills_down_to_just_above_the_build_label() {29+ // Reserves the label's nav-bar inset (passed in) + its padding/line height30+ // (xs 4dp + 12dp = 16dp) + a 6dp gap = navBarBottom + 22dp. No hard cap, so31+ // tall panels (Galaxy Z Fold 7) fill to the label instead of stranding dead32+ // space below the stack and clipping the Pro card's copy/reveal buttons.33+ assertEquals(878.dp, settingsDeckHeight(900.dp, 0.dp))34+ assertEquals(458.dp, settingsDeckHeight(480.dp, 0.dp))35+ assertEquals(2.dp, settingsDeckHeight(24.dp, 0.dp))36+ assertEquals(1178.dp, settingsDeckHeight(1200.dp, 0.dp))37+ // A larger nav-bar inset (three-button nav) simply reserves more at the bottom.38+ assertEquals(830.dp, settingsDeckHeight(900.dp, 48.dp))3839 }3940 4041 // ── Drag gesture resolution ───────────────────────────────────────────────