Fix soft-keyboard level advance behavior

- Keep terminal focus without explicitly reopening the software keyboard after command field recreation.
- Preserve submitted-command IME echo suppression when advancing to the next level.
- Recreate the terminal input field when loading a level so stale editor state is discarded.
This commit is contained in:
Joe Tretter
2026-05-13 15:05:02 -05:00
parent e8bcf660d9
commit 61951408de
3 changed files with 13 additions and 13 deletions

View File

@@ -19,8 +19,8 @@ android {
applicationId = "solutions.tretter.githugandroid" applicationId = "solutions.tretter.githugandroid"
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 138 versionCode = 139
versionName = "0.1.137" versionName = "0.1.138"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true

View File

@@ -211,13 +211,19 @@ fun GitHugApp() {
applyRecommendedPaneWeights(persist = false) applyRecommendedPaneWeights(persist = false)
} }
fun loadLevel(index: Int, message: List<String> = listOf("Loaded level: ${levels[index].title}")) { fun loadLevel(
index: Int,
message: List<String> = listOf("Loaded level: ${levels[index].title}"),
keepSuppressedImeEcho: Boolean = false,
) {
AppLog.d("GitHugApp", "Loading level index=$index id=${levels[index].id} title=${levels[index].title}") AppLog.d("GitHugApp", "Loading level index=$index id=${levels[index].id} title=${levels[index].title}")
currentLevelIndex = index currentLevelIndex = index
repo = runtime.prepareLevel(levels[index]) repo = runtime.prepareLevel(levels[index])
output = message output = message
clearCommandInput() clearCommandInput(recreateField = true)
suppressedImeEcho = null if (!keepSuppressedImeEcho) {
suppressedImeEcho = null
}
hintIndex = 0 hintIndex = 0
activeExerciseDetail = null activeExerciseDetail = null
visibleHint = null visibleHint = null
@@ -327,7 +333,7 @@ fun GitHugApp() {
"Level solved ${levelForResult.id}; advancing to next incomplete index=$nextLevelIndex id=$nextLevelId", "Level solved ${levelForResult.id}; advancing to next incomplete index=$nextLevelIndex id=$nextLevelId",
) )
scope.launch { persistProgress(updatedCompletedLevels, nextLevelId) } scope.launch { persistProgress(updatedCompletedLevels, nextLevelId) }
loadLevel(nextLevelIndex, message = emptyList()) loadLevel(nextLevelIndex, message = emptyList(), keepSuppressedImeEcho = true)
} else { } else {
AppLog.d("GitHugApp", "All levels completed") AppLog.d("GitHugApp", "All levels completed")
scope.launch { persistProgress(updatedCompletedLevels, levels.lastOrNull()?.id) } scope.launch { persistProgress(updatedCompletedLevels, levels.lastOrNull()?.id) }

View File

@@ -40,13 +40,12 @@ import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.boundsInRoot import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type import androidx.compose.ui.input.key.type
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
@@ -77,7 +76,6 @@ fun TerminalPane(
) { ) {
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
val outputHorizontalScroll = rememberScrollState() val outputHorizontalScroll = rememberScrollState()
val outputVerticalScroll = rememberScrollState() val outputVerticalScroll = rememberScrollState()
val terminalMinWidth = 80.dp * 7.2f val terminalMinWidth = 80.dp * 7.2f
@@ -90,7 +88,6 @@ fun TerminalPane(
LaunchedEffect(inputFieldVersion) { LaunchedEffect(inputFieldVersion) {
outputHorizontalScroll.scrollTo(0) outputHorizontalScroll.scrollTo(0)
focusRequester.requestFocus() focusRequester.requestFocus()
keyboardController?.show()
} }
LaunchedEffect(output.size) { LaunchedEffect(output.size) {
@@ -184,7 +181,6 @@ fun TerminalPane(
commandInput = commandInput, commandInput = commandInput,
onValueChange = onValueChange, onValueChange = onValueChange,
focusRequester = focusRequester, focusRequester = focusRequester,
keyboardController = keyboardController,
onFocused = onInputHintDismiss, onFocused = onInputHintDismiss,
onSubmit = { submitCommand() }, onSubmit = { submitCommand() },
onTab = onTab, onTab = onTab,
@@ -205,7 +201,6 @@ private fun RowScope.TerminalInputField(
commandInput: TextFieldValue, commandInput: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit, onValueChange: (TextFieldValue) -> Unit,
focusRequester: FocusRequester, focusRequester: FocusRequester,
keyboardController: androidx.compose.ui.platform.SoftwareKeyboardController?,
onFocused: () -> Unit, onFocused: () -> Unit,
onSubmit: () -> Unit, onSubmit: () -> Unit,
onTab: () -> Unit, onTab: () -> Unit,
@@ -224,7 +219,6 @@ private fun RowScope.TerminalInputField(
.onFocusChanged { state -> .onFocusChanged { state ->
if (state.isFocused) { if (state.isFocused) {
onFocused() onFocused()
keyboardController?.show()
} }
} }
.onPreviewKeyEvent { event -> .onPreviewKeyEvent { event ->