Replace button chip grid with controller diagram in test screen

Render pressed buttons as a visual gamepad layout instead of a flat
grid of chips. Fix stale overlay visibility preference in GameScreen.
This commit is contained in:
2026-04-12 16:26:22 -04:00
parent f19aeffdce
commit 4bb80d6501
2 changed files with 108 additions and 56 deletions

View File

@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -11,13 +12,16 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
@@ -41,6 +45,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.lazy.emulate.input.ControllerManager
@@ -154,7 +161,7 @@ fun ControllerTestScreen(
// Live button indicators
Text("Pressed Buttons", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(6.dp))
ButtonGrid(pressed = pressed)
ControllerDiagram(pressed = pressed)
Spacer(Modifier.height(16.dp))
@@ -221,54 +228,120 @@ fun ControllerTestScreen(
}
@Composable
private fun ButtonGrid(pressed: Set<GamepadButton>) {
val rows = listOf(
listOf(GamepadButton.DPAD_UP, GamepadButton.DPAD_DOWN, GamepadButton.DPAD_LEFT, GamepadButton.DPAD_RIGHT),
listOf(GamepadButton.FACE_TOP, GamepadButton.FACE_BOTTOM, GamepadButton.FACE_LEFT, GamepadButton.FACE_RIGHT),
listOf(GamepadButton.C_BUTTON, GamepadButton.Z_BUTTON, GamepadButton.MODE),
listOf(GamepadButton.L1, GamepadButton.R1, GamepadButton.L2, GamepadButton.R2),
listOf(GamepadButton.L3, GamepadButton.R3, GamepadButton.START, GamepadButton.SELECT)
)
private fun ControllerDiagram(pressed: Set<GamepadButton>) {
val btnSize = 34.dp
val smallBtn = 28.dp
val pillW = 38.dp
val pillH = 22.dp
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(8.dp)) {
rows.forEach { row ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(6.dp)
BoxWithConstraints(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.padding(8.dp)
) {
row.forEach { btn ->
ButtonChip(
label = shortLabel(btn),
active = btn in pressed,
modifier = Modifier.weight(1f)
)
}
}
Spacer(Modifier.height(6.dp))
val w = maxWidth
val h = maxHeight
// --- Shoulder buttons ---
// L2 / R2 (top row)
ControllerButton("L2", GamepadButton.L2 in pressed, pillW, pillH, RoundedCornerShape(4.dp),
x = w * 0.08f, y = h * 0.02f)
ControllerButton("R2", GamepadButton.R2 in pressed, pillW, pillH, RoundedCornerShape(4.dp),
x = w - w * 0.08f - pillW, y = h * 0.02f)
// L1 / R1
ControllerButton("L1", GamepadButton.L1 in pressed, pillW, pillH, RoundedCornerShape(4.dp),
x = w * 0.08f, y = h * 0.15f)
ControllerButton("R1", GamepadButton.R1 in pressed, pillW, pillH, RoundedCornerShape(4.dp),
x = w - w * 0.08f - pillW, y = h * 0.15f)
// --- D-Pad (left side) ---
val dpadCx = w * 0.18f
val dpadCy = h * 0.50f
val dpadOff = btnSize * 0.85f
ControllerButton("", GamepadButton.DPAD_UP in pressed, smallBtn, smallBtn, CircleShape,
x = dpadCx - smallBtn / 2, y = dpadCy - dpadOff - smallBtn / 2)
ControllerButton("", GamepadButton.DPAD_DOWN in pressed, smallBtn, smallBtn, CircleShape,
x = dpadCx - smallBtn / 2, y = dpadCy + dpadOff - smallBtn / 2)
ControllerButton("", GamepadButton.DPAD_LEFT in pressed, smallBtn, smallBtn, CircleShape,
x = dpadCx - dpadOff - smallBtn / 2, y = dpadCy - smallBtn / 2)
ControllerButton("", GamepadButton.DPAD_RIGHT in pressed, smallBtn, smallBtn, CircleShape,
x = dpadCx + dpadOff - smallBtn / 2, y = dpadCy - smallBtn / 2)
// --- Face buttons (right side, diamond) ---
val faceCx = w * 0.82f
val faceCy = h * 0.50f
val faceOff = btnSize * 0.85f
ControllerButton("", GamepadButton.FACE_TOP in pressed, btnSize, btnSize, CircleShape,
x = faceCx - btnSize / 2, y = faceCy - faceOff - btnSize / 2)
ControllerButton("", GamepadButton.FACE_BOTTOM in pressed, btnSize, btnSize, CircleShape,
x = faceCx - btnSize / 2, y = faceCy + faceOff - btnSize / 2)
ControllerButton("", GamepadButton.FACE_LEFT in pressed, btnSize, btnSize, CircleShape,
x = faceCx - faceOff - btnSize / 2, y = faceCy - btnSize / 2)
ControllerButton("", GamepadButton.FACE_RIGHT in pressed, btnSize, btnSize, CircleShape,
x = faceCx + faceOff - btnSize / 2, y = faceCy - btnSize / 2)
// --- Center buttons (Select / Start) ---
val centerY = h * 0.38f
ControllerButton("SL", GamepadButton.SELECT in pressed, pillW, pillH, RoundedCornerShape(4.dp),
x = w * 0.5f - pillW - 4.dp, y = centerY)
ControllerButton("ST", GamepadButton.START in pressed, pillW, pillH, RoundedCornerShape(4.dp),
x = w * 0.5f + 4.dp, y = centerY)
// --- Stick clicks (L3 / R3) ---
val stickY = h * 0.78f
ControllerButton("L3", GamepadButton.L3 in pressed, btnSize, btnSize, CircleShape,
x = w * 0.33f - btnSize / 2, y = stickY - btnSize / 2)
ControllerButton("R3", GamepadButton.R3 in pressed, btnSize, btnSize, CircleShape,
x = w * 0.67f - btnSize / 2, y = stickY - btnSize / 2)
// --- Genesis extras (bottom center, only visible when pressed) ---
val extraY = h * 0.62f
if (GamepadButton.MODE in pressed || GamepadButton.Z_BUTTON in pressed || GamepadButton.C_BUTTON in pressed) {
ControllerButton("MD", GamepadButton.MODE in pressed, smallBtn, smallBtn, CircleShape,
x = w * 0.42f - smallBtn / 2, y = extraY)
ControllerButton("Z", GamepadButton.Z_BUTTON in pressed, smallBtn, smallBtn, CircleShape,
x = w * 0.50f - smallBtn / 2, y = extraY)
ControllerButton("C", GamepadButton.C_BUTTON in pressed, smallBtn, smallBtn, CircleShape,
x = w * 0.58f - smallBtn / 2, y = extraY)
}
}
}
}
@Composable
private fun ButtonChip(label: String, active: Boolean, modifier: Modifier = Modifier) {
private fun ControllerButton(
label: String,
active: Boolean,
width: Dp,
height: Dp,
shape: androidx.compose.ui.graphics.Shape,
x: Dp,
y: Dp
) {
val density = LocalDensity.current
val bg = if (active) MaterialTheme.colorScheme.primary else Color.Transparent
val fg = if (active) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant
val borderColor = if (active) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outlineVariant
Box(
modifier = modifier
.height(36.dp)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.outline,
shape = RoundedCornerShape(6.dp)
modifier = Modifier
.offset {
IntOffset(
with(density) { x.roundToPx() },
with(density) { y.roundToPx() }
)
.background(bg, RoundedCornerShape(6.dp)),
}
.size(width, height)
.border(1.5.dp, borderColor, shape)
.background(bg, shape),
contentAlignment = Alignment.Center
) {
Text(
text = label,
color = fg,
fontSize = 12.sp,
fontSize = 10.sp,
fontWeight = if (active) FontWeight.Bold else FontWeight.Normal
)
}
@@ -341,24 +414,3 @@ private fun AxisRow(label: String, x: Float, y: Float) {
}
}
private fun shortLabel(button: GamepadButton): String = when (button) {
GamepadButton.DPAD_UP -> ""
GamepadButton.DPAD_DOWN -> ""
GamepadButton.DPAD_LEFT -> ""
GamepadButton.DPAD_RIGHT -> ""
GamepadButton.FACE_TOP -> "△/Y/X"
GamepadButton.FACE_BOTTOM -> "✕/B/A"
GamepadButton.FACE_LEFT -> "□/X/Y"
GamepadButton.FACE_RIGHT -> "○/A/B"
GamepadButton.L1 -> "L1"
GamepadButton.R1 -> "R1"
GamepadButton.L2 -> "L2"
GamepadButton.R2 -> "R2"
GamepadButton.L3 -> "L3"
GamepadButton.R3 -> "R3"
GamepadButton.START -> "START"
GamepadButton.SELECT -> "SELECT"
GamepadButton.MODE -> "MODE"
GamepadButton.Z_BUTTON -> "Z"
GamepadButton.C_BUTTON -> "C"
}

View File

@@ -100,7 +100,7 @@ fun GameScreen(
var saveSlots by remember { mutableStateOf<List<SaveSlotInfo>>(emptyList()) }
val activeController by controllerManager.activeController.collectAsState()
val overlayVisibility = remember { preferencesManager.overlayVisibility }
val overlayVisibility = preferencesManager.overlayVisibility
val showTouchControls = when (overlayVisibility) {
OverlayVisibility.ALWAYS_SHOW -> true
OverlayVisibility.ALWAYS_HIDE -> false