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:
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user