Auto-commit after successful build: update app gameplay/UI, improve build setup
Changed files:\napp/build.gradle.kts app/src/main/java/com/kawomi/githugandroid/GitHugApp.kt
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
||||
applicationId = "com.kawomi.githugandroid"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 26
|
||||
versionName = "0.1.25"
|
||||
versionCode = 27
|
||||
versionName = "0.1.26"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -205,6 +206,7 @@ fun GitHugApp() {
|
||||
val scope = rememberCoroutineScope()
|
||||
val screenScrollState = rememberScrollState()
|
||||
val workspaceHeight = (configuration.screenHeightDp.dp * 1.05f).coerceAtLeast(560.dp)
|
||||
val screenHeightDp = configuration.screenHeightDp
|
||||
|
||||
var paneLayout by remember { mutableStateOf(defaultPaneLayout()) }
|
||||
var currentLevelIndex by remember { mutableStateOf(0) }
|
||||
@@ -214,6 +216,7 @@ fun GitHugApp() {
|
||||
var suppressedImeEcho by remember { mutableStateOf<String?>(null) }
|
||||
var output by remember { mutableStateOf(listOf(runtime.startupBanner())) }
|
||||
var hintIndex by remember { mutableStateOf(0) }
|
||||
var suggestionsExpanded by remember { mutableStateOf(false) }
|
||||
var completedLevels by remember { mutableStateOf(setOf<String>()) }
|
||||
var commandHistory by remember { mutableStateOf(listOf<String>()) }
|
||||
var historyIndex by remember { mutableStateOf(-1) }
|
||||
@@ -239,14 +242,32 @@ fun GitHugApp() {
|
||||
}
|
||||
}
|
||||
|
||||
fun applyRecommendedPaneWeights(persist: Boolean = true) {
|
||||
updatePaneLayout(
|
||||
transform = { layout ->
|
||||
layout.copy(
|
||||
weights = layout.weights + recommendedPaneWeights(
|
||||
level = currentLevel,
|
||||
levelCount = levels.size,
|
||||
outputLineCount = output.size,
|
||||
screenHeightDp = screenHeightDp,
|
||||
)
|
||||
)
|
||||
},
|
||||
persist = persist,
|
||||
)
|
||||
}
|
||||
|
||||
fun resetCurrentLevel(message: String = "Level reset.") {
|
||||
repo = runtime.prepareLevel(currentLevel)
|
||||
clearCommandInput()
|
||||
suppressedImeEcho = null
|
||||
output = listOf(message)
|
||||
hintIndex = 0
|
||||
suggestionsExpanded = false
|
||||
historyIndex = -1
|
||||
historyDraft = ""
|
||||
applyRecommendedPaneWeights(persist = false)
|
||||
}
|
||||
|
||||
fun loadLevel(index: Int) {
|
||||
@@ -256,8 +277,17 @@ fun GitHugApp() {
|
||||
clearCommandInput()
|
||||
suppressedImeEcho = null
|
||||
hintIndex = 0
|
||||
suggestionsExpanded = false
|
||||
historyIndex = -1
|
||||
historyDraft = ""
|
||||
paneLayout = paneLayout.copy(
|
||||
weights = paneLayout.weights + recommendedPaneWeights(
|
||||
level = levels[index],
|
||||
levelCount = levels.size,
|
||||
outputLineCount = 1,
|
||||
screenHeightDp = screenHeightDp,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun setCommandText(text: String) {
|
||||
@@ -351,6 +381,7 @@ fun GitHugApp() {
|
||||
} else {
|
||||
repo = newRepo
|
||||
output = newOutput
|
||||
applyRecommendedPaneWeights(persist = false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +434,8 @@ fun GitHugApp() {
|
||||
exerciseContent = {
|
||||
ExercisePane(
|
||||
level = currentLevel,
|
||||
suggestionsExpanded = suggestionsExpanded,
|
||||
onToggleSuggestions = { suggestionsExpanded = !suggestionsExpanded },
|
||||
onHint = {
|
||||
val hint = currentLevel.hints.getOrNull(hintIndex) ?: "No more hints for this level."
|
||||
output = output + "hint> $hint"
|
||||
@@ -766,9 +799,7 @@ private fun LevelsPane(
|
||||
onSelect: (Int) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
levels.forEachIndexed { index, level ->
|
||||
@@ -859,13 +890,13 @@ private fun VisualPane(repo: RepoState) {
|
||||
@Composable
|
||||
private fun ExercisePane(
|
||||
level: Level,
|
||||
suggestionsExpanded: Boolean,
|
||||
onToggleSuggestions: () -> Unit,
|
||||
onHint: () -> Unit,
|
||||
onReset: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Text(level.title, style = MaterialTheme.typography.titleLarge, color = TextPrimary)
|
||||
@@ -880,6 +911,12 @@ private fun ExercisePane(
|
||||
) {
|
||||
Text("Hint")
|
||||
}
|
||||
TextButton(
|
||||
onClick = onToggleSuggestions,
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = Accent),
|
||||
) {
|
||||
Text("Suggestions")
|
||||
}
|
||||
TextButton(
|
||||
onClick = onReset,
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = Accent),
|
||||
@@ -887,7 +924,7 @@ private fun ExercisePane(
|
||||
Text("Reset level")
|
||||
}
|
||||
}
|
||||
if (level.commandSuggestions.isNotEmpty()) {
|
||||
if (suggestionsExpanded && level.commandSuggestions.isNotEmpty()) {
|
||||
Text("Suggestions", color = TextPrimary, fontWeight = FontWeight.Bold)
|
||||
level.commandSuggestions.forEach { suggestion ->
|
||||
Text(suggestion, color = TextSecondary, fontFamily = FontFamily.Monospace)
|
||||
@@ -938,6 +975,7 @@ private fun TerminalPane(
|
||||
onHistoryUp: () -> Unit,
|
||||
onHistoryDown: () -> Unit,
|
||||
) {
|
||||
val configuration = LocalConfiguration.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
@@ -945,16 +983,26 @@ private fun TerminalPane(
|
||||
val horizontalScroll = rememberScrollState()
|
||||
val outputVerticalScroll = rememberScrollState()
|
||||
val terminalMinWidth = 80.dp * 7.2f
|
||||
val maxOutputHeight = configuration.screenHeightDp.dp * 0.7f
|
||||
val outputLineHeight = 20.dp
|
||||
val desiredOutputHeight = (output.size.coerceAtLeast(6) * outputLineHeight.value).dp.coerceAtMost(maxOutputHeight)
|
||||
|
||||
LaunchedEffect(inputFieldVersion) {
|
||||
focusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
horizontalScroll.scrollTo(0)
|
||||
}
|
||||
|
||||
LaunchedEffect(output.size) {
|
||||
outputVerticalScroll.animateScrollTo(outputVerticalScroll.maxValue)
|
||||
}
|
||||
|
||||
LaunchedEffect(output.size, commandInput.text) {
|
||||
if (output.isNotEmpty() && commandInput.text.isEmpty()) {
|
||||
horizontalScroll.scrollTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
fun submitCommand() {
|
||||
focusManager.clearFocus(force = true)
|
||||
keyboardController?.hide()
|
||||
@@ -976,7 +1024,8 @@ private fun TerminalPane(
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 120.dp, max = maxOutputHeight)
|
||||
.height(desiredOutputHeight)
|
||||
.fillMaxWidth()
|
||||
.background(PanelPrimary, RoundedCornerShape(10.dp))
|
||||
.padding(8.dp)
|
||||
@@ -1114,6 +1163,30 @@ private fun resizePanes(
|
||||
)
|
||||
}
|
||||
|
||||
private fun recommendedPaneWeights(
|
||||
level: Level,
|
||||
levelCount: Int,
|
||||
outputLineCount: Int,
|
||||
screenHeightDp: Int,
|
||||
): Map<PaneId, Float> {
|
||||
val screenHeight = screenHeightDp.coerceAtLeast(560)
|
||||
val levelsHeight = (56 + levelCount * 44).coerceAtMost((screenHeight * 0.35f).toInt())
|
||||
val descriptionLines = (level.description.length / 42).coerceAtLeast(2) + 2
|
||||
val suggestionsLines = level.commandSuggestions.size
|
||||
val exerciseHeight = (120 + descriptionLines * 24 + suggestionsLines * 22).coerceAtLeast(180)
|
||||
val terminalHeight = (120 + outputLineCount.coerceAtLeast(1) * 20).coerceAtMost((screenHeight * 0.7f).toInt())
|
||||
val visualHeight = (screenHeight * 0.24f).toInt().coerceAtLeast(170)
|
||||
|
||||
fun weightFor(height: Int): Float = (height.toFloat() / screenHeight.toFloat()).coerceAtLeast(0.6f)
|
||||
|
||||
return mapOf(
|
||||
PaneId.EXERCISE to weightFor(exerciseHeight),
|
||||
PaneId.TERMINAL to weightFor(terminalHeight),
|
||||
PaneId.VISUAL to weightFor(visualHeight),
|
||||
PaneId.LEVELS to weightFor(levelsHeight),
|
||||
)
|
||||
}
|
||||
|
||||
private fun commonPrefix(values: List<String>): String {
|
||||
if (values.isEmpty()) return ""
|
||||
var prefix = values.first()
|
||||
|
||||
Reference in New Issue
Block a user