Add hidden diagnostic skin toggle

- Open a hidden diagnostics dialog after tapping the header seven times within fifteen seconds.
- Let the diagnostics dialog toggle the pane color/stripe skin on demand.
- Restore the normal app background and pane colors by default.
- Stop Scaffold from preserving the bottom navigation-bar inset while the keyboard is visible.
- Apply bottom navigation padding only when the IME is not open.
This commit is contained in:
Joe Tretter
2026-05-13 18:35:36 -05:00
parent bd6568357f
commit 618e6e36bd
7 changed files with 142 additions and 21 deletions

View File

@@ -1,14 +1,18 @@
package solutions.tretter.githugandroid
import android.os.SystemClock
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
@@ -26,6 +30,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
@@ -37,6 +42,7 @@ fun GitHugApp() {
MaterialTheme(colorScheme = GitHugColorScheme) {
val context = LocalContext.current
val configuration = LocalConfiguration.current
val density = LocalDensity.current
val runtime = remember(context) { GitRepositoryRuntime(context.applicationContext) }
if (!runtime.isNativeGitAvailable()) {
MissingNativeGitScreen(message = runtime.unavailableMessage())
@@ -76,9 +82,18 @@ fun GitHugApp() {
var showHelpOverlay by rememberSaveable { mutableStateOf(false) }
var showHelpOnStart by rememberSaveable { mutableStateOf(true) }
var helpPreferenceInitialized by rememberSaveable { mutableStateOf(false) }
var showDiagnosticDialog by rememberSaveable { mutableStateOf(false) }
var diagnosticSkinEnabled by rememberSaveable { mutableStateOf(false) }
var headerTapTimes by remember { mutableStateOf(emptyList<Long>()) }
var exerciseBounds by remember { mutableStateOf<Rect?>(null) }
var promptBounds by remember { mutableStateOf<Rect?>(null) }
val currentLevel = levels[currentLevelIndex]
val appBackground = if (diagnosticSkinEnabled) DiagnosticAppBackground else AppBackground
val imeBottom = WindowInsets.ime.getBottom(density)
val navigationBottom = WindowInsets.navigationBars.getBottom(density)
val bottomSystemPadding = with(density) {
if (imeBottom > navigationBottom) 0.dp else navigationBottom.toDp()
}
LaunchedEffect(solvedCelebrationTitle) {
if (solvedCelebrationTitle != null) {
@@ -465,30 +480,50 @@ fun GitHugApp() {
output = output + runtime.commandReferenceLines()
}
Scaffold(containerColor = AppBackground) { padding ->
fun registerHeaderTap() {
val now = SystemClock.elapsedRealtime()
val recentTaps = (headerTapTimes + now).filter { tapTime -> now - tapTime <= 15_000L }
if (recentTaps.size >= 7) {
headerTapTimes = emptyList()
showDiagnosticDialog = true
} else {
headerTapTimes = recentTaps
}
}
Scaffold(
containerColor = appBackground,
contentWindowInsets = WindowInsets(0.dp),
) { padding ->
Surface(
modifier = Modifier
.fillMaxSize()
.padding(padding),
color = AppBackground,
color = appBackground,
) {
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.fillMaxSize()
.background(AppBackground)
.imePadding()
.background(appBackground)
.statusBarsPadding()
.verticalScroll(screenScrollState)
.padding(horizontal = 10.dp, vertical = 8.dp),
.padding(
start = 10.dp,
top = 8.dp,
end = 10.dp,
bottom = 8.dp + bottomSystemPadding,
),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
FixedHeader()
FixedHeader(onTap = { registerHeaderTap() })
Box(
modifier = Modifier.fillMaxWidth(),
) {
PaneWorkspace(
modifier = Modifier.fillMaxWidth(),
paneLayout = paneLayout,
diagnosticSkinEnabled = diagnosticSkinEnabled,
onMovePane = { paneId, delta ->
updatePaneLayout(transform = { layout -> movePane(layout, paneId, delta) })
},
@@ -623,6 +658,14 @@ fun GitHugApp() {
)
}
if (showDiagnosticDialog) {
DiagnosticSkinDialog(
diagnosticSkinEnabled = diagnosticSkinEnabled,
onDiagnosticSkinEnabledChange = { diagnosticSkinEnabled = it },
onDismiss = { showDiagnosticDialog = false },
)
}
solvedCelebrationTitle?.let { title ->
SolvedCelebrationOverlay(title = title)
}