98ecce847507

scale the whole UI up on wide screens instead of capping or splitting it (June's pick after the 480dp cap and the two-pane both got rejected): a WideScreenScale wrapper at the app root overrides LocalDensity by width/411dp, clamped to 1.35x, so every dp and sp grows together and the same single-column layout reads as a bigger screen rather than a stretched phone. One wrapper, no per-screen breakpoints to keep in sync, and an exact no-op at or below 411dp so phones are pixel-identical — verified by shooting the vault list at both 686dp and phone width. Ceiling: a uniform scale is not real adaptive layout; a screen that ever wants a genuinely different wide arrangement will need its own breakpoint.

time
2026-07-24 20:09 UTC · 3 months ago
machine
koh2
agent
claude-opus-5
session
foldable width
parent
c8422d123645
1 file app/src/main/java/com/peachpasswords/MainActivity.kt+43 −1
app/src/main/java/com/peachpasswords/MainActivity.kt+43 −1
@@ -10,6 +10,14 @@1010 import androidx.activity.enableEdgeToEdge1111 import androidx.compose.foundation.isSystemInDarkTheme1212 import androidx.compose.runtime.SideEffect13+import androidx.compose.ui.unit.dp14+import androidx.compose.ui.unit.Density15+import androidx.compose.ui.platform.LocalDensity16+import androidx.compose.ui.Modifier17+import androidx.compose.runtime.CompositionLocalProvider18+import androidx.compose.runtime.Composable19+import androidx.compose.foundation.layout.fillMaxSize20+import androidx.compose.foundation.layout.BoxWithConstraints1321 import androidx.compose.runtime.collectAsState1422 import androidx.compose.runtime.getValue1523 import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen@@ -116,8 +124,42 @@116124                 accent = accent,117125                 reduceMotion = settings.reduceMotion,118126             ) {119-                PeachAppNavHost(startDestination = start, isUnlocked = vault.isUnlocked)127+                WideScreenScale {128+                    PeachAppNavHost(startDestination = start, isUnlocked = vault.isUnlocked)129+                }120130             }131+        }132+    }133+}134+135+/** Layout width a phone gives us; anything past this is "extra" to spend. */136+private val BASE_WIDTH = 411.dp137+138+/** Cap on the up-scale — past this, big text stops reading as deliberate. */139+private const val MAX_SCALE = 1.35f140+141+/**142+ * On a foldable's inner screen the app was a stretched phone: phone-sized type143+ * and rows with the surplus width absorbed as empty space. Scaling the density144+ * grows every dp and sp together, so the same single-column layout simply reads145+ * as a bigger screen — no per-screen breakpoints to keep in sync.146+ *147+ * Ceiling: a uniform scale, not real adaptive layout. If a screen ever wants a148+ * genuinely different wide-screen arrangement it needs its own breakpoint.149+ * No-op at or below [BASE_WIDTH], so phones are untouched.150+ */151+@Composable152+private fun WideScreenScale(content: @Composable () -> Unit) {153+    BoxWithConstraints(Modifier.fillMaxSize()) {154+        val base = LocalDensity.current155+        val scale = (maxWidth / BASE_WIDTH).coerceIn(1f, MAX_SCALE)156+        if (scale == 1f) {157+            content()158+        } else {159+            CompositionLocalProvider(160+                LocalDensity provides Density(base.density * scale, base.fontScale),161+                content = content,162+            )121163         }122164     }123165 }

view file