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.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
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.graphics.Color
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.lazy.emulate.input.ControllerManager
|
import com.lazy.emulate.input.ControllerManager
|
||||||
@@ -154,7 +161,7 @@ fun ControllerTestScreen(
|
|||||||
// Live button indicators
|
// Live button indicators
|
||||||
Text("Pressed Buttons", style = MaterialTheme.typography.titleMedium)
|
Text("Pressed Buttons", style = MaterialTheme.typography.titleMedium)
|
||||||
Spacer(Modifier.height(6.dp))
|
Spacer(Modifier.height(6.dp))
|
||||||
ButtonGrid(pressed = pressed)
|
ControllerDiagram(pressed = pressed)
|
||||||
|
|
||||||
Spacer(Modifier.height(16.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
|
|
||||||
@@ -221,54 +228,120 @@ fun ControllerTestScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ButtonGrid(pressed: Set<GamepadButton>) {
|
private fun ControllerDiagram(pressed: Set<GamepadButton>) {
|
||||||
val rows = listOf(
|
val btnSize = 34.dp
|
||||||
listOf(GamepadButton.DPAD_UP, GamepadButton.DPAD_DOWN, GamepadButton.DPAD_LEFT, GamepadButton.DPAD_RIGHT),
|
val smallBtn = 28.dp
|
||||||
listOf(GamepadButton.FACE_TOP, GamepadButton.FACE_BOTTOM, GamepadButton.FACE_LEFT, GamepadButton.FACE_RIGHT),
|
val pillW = 38.dp
|
||||||
listOf(GamepadButton.C_BUTTON, GamepadButton.Z_BUTTON, GamepadButton.MODE),
|
val pillH = 22.dp
|
||||||
listOf(GamepadButton.L1, GamepadButton.R1, GamepadButton.L2, GamepadButton.R2),
|
|
||||||
listOf(GamepadButton.L3, GamepadButton.R3, GamepadButton.START, GamepadButton.SELECT)
|
|
||||||
)
|
|
||||||
Card(modifier = Modifier.fillMaxWidth()) {
|
Card(modifier = Modifier.fillMaxWidth()) {
|
||||||
Column(modifier = Modifier.padding(8.dp)) {
|
BoxWithConstraints(
|
||||||
rows.forEach { row ->
|
modifier = Modifier
|
||||||
Row(
|
.fillMaxWidth()
|
||||||
modifier = Modifier.fillMaxWidth(),
|
.height(200.dp)
|
||||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
row.forEach { btn ->
|
val w = maxWidth
|
||||||
ButtonChip(
|
val h = maxHeight
|
||||||
label = shortLabel(btn),
|
|
||||||
active = btn in pressed,
|
// --- Shoulder buttons ---
|
||||||
modifier = Modifier.weight(1f)
|
// 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),
|
||||||
Spacer(Modifier.height(6.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
|
@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 bg = if (active) MaterialTheme.colorScheme.primary else Color.Transparent
|
||||||
val fg = if (active) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant
|
val fg = if (active) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
val borderColor = if (active) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outlineVariant
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = Modifier
|
||||||
.height(36.dp)
|
.offset {
|
||||||
.border(
|
IntOffset(
|
||||||
width = 1.dp,
|
with(density) { x.roundToPx() },
|
||||||
color = MaterialTheme.colorScheme.outline,
|
with(density) { y.roundToPx() }
|
||||||
shape = RoundedCornerShape(6.dp)
|
|
||||||
)
|
)
|
||||||
.background(bg, RoundedCornerShape(6.dp)),
|
}
|
||||||
|
.size(width, height)
|
||||||
|
.border(1.5.dp, borderColor, shape)
|
||||||
|
.background(bg, shape),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = label,
|
text = label,
|
||||||
color = fg,
|
color = fg,
|
||||||
fontSize = 12.sp,
|
fontSize = 10.sp,
|
||||||
fontWeight = if (active) FontWeight.Bold else FontWeight.Normal
|
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()) }
|
var saveSlots by remember { mutableStateOf<List<SaveSlotInfo>>(emptyList()) }
|
||||||
|
|
||||||
val activeController by controllerManager.activeController.collectAsState()
|
val activeController by controllerManager.activeController.collectAsState()
|
||||||
val overlayVisibility = remember { preferencesManager.overlayVisibility }
|
val overlayVisibility = preferencesManager.overlayVisibility
|
||||||
val showTouchControls = when (overlayVisibility) {
|
val showTouchControls = when (overlayVisibility) {
|
||||||
OverlayVisibility.ALWAYS_SHOW -> true
|
OverlayVisibility.ALWAYS_SHOW -> true
|
||||||
OverlayVisibility.ALWAYS_HIDE -> false
|
OverlayVisibility.ALWAYS_HIDE -> false
|
||||||
|
|||||||
Reference in New Issue
Block a user