diff options
Diffstat (limited to 'composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui')
5 files changed, 404 insertions, 0 deletions
diff --git a/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/ArcadeButton.kt b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/ArcadeButton.kt new file mode 100644 index 0000000..9d50192 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/ArcadeButton.kt @@ -0,0 +1,52 @@ +package coffee.liz.abstractionengine.ui + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +enum class ArcadeButtonColor { + RED, YELLOW +} + +@Composable +fun ArcadeButton( + label: String, + color: ArcadeButtonColor = ArcadeButtonColor.RED, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + val buttonColor = when (color) { + ArcadeButtonColor.RED -> GameBoyColors.ButtonRed + ArcadeButtonColor.YELLOW -> GameBoyColors.ButtonYellow + } + + Button( + onClick = onClick, + modifier = modifier + .size(65.dp) + .border(1.dp, MaterialTheme.colorScheme.outline, CircleShape), + shape = CircleShape, + colors = ButtonDefaults.buttonColors( + containerColor = buttonColor, + contentColor = MaterialTheme.colorScheme.onPrimary + ), + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = label, + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onPrimary + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/ArcadeControls.kt b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/ArcadeControls.kt new file mode 100644 index 0000000..f5b4839 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/ArcadeControls.kt @@ -0,0 +1,130 @@ +package coffee.liz.abstractionengine.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +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.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun ArcadeControls( + onDirectionPressed: (Direction) -> Unit, + onActionA: () -> Unit, + onActionB: () -> Unit, + modifier: Modifier = Modifier, + gameContent: @Composable BoxScope.() -> Unit = { + // Default placeholder content + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = "GAME AREA", + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onPrimary + ) + } + } +) { + var lastDirection by remember { mutableStateOf<Direction?>(null) } + var lastAction by remember { mutableStateOf<String?>(null) } + + Box(modifier = modifier.fillMaxSize()) { + // PCB background layer + PCBBackground() + + // Transparent casing overlay + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0x33000000)) + ) + + // Content + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Game area (square) + Box( + modifier = Modifier + .fillMaxWidth(0.95f) + .aspectRatio(1f) + .background( + color = GameBoyColors.ScreenGreen.copy(alpha = 0.85f), + shape = RoundedCornerShape(8.dp) + ) + .border(1.dp, MaterialTheme.colorScheme.outline, RoundedCornerShape(8.dp)) + .padding(8.dp), + contentAlignment = Alignment.Center, + content = gameContent + ) + + // Control panel + Box( + modifier = Modifier + .fillMaxWidth(0.95f) + .background( + color = MaterialTheme.colorScheme.surface.copy(alpha = 0.7f), + shape = RoundedCornerShape(16.dp) + ) + .border(1.dp, MaterialTheme.colorScheme.outline, RoundedCornerShape(16.dp)) + .padding(horizontal = 16.dp, vertical = 16.dp), + contentAlignment = Alignment.Center + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + // D-Pad on the left + DPad( + onDirectionPressed = { direction -> + lastDirection = direction + lastAction = null + onDirectionPressed(direction) + } + ) + + // Action buttons on the right + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + ArcadeButton( + label = "B", + color = ArcadeButtonColor.YELLOW, + onClick = { + lastAction = "B" + lastDirection = null + onActionB() + } + ) + ArcadeButton( + label = "A", + color = ArcadeButtonColor.RED, + onClick = { + lastAction = "A" + lastDirection = null + onActionA() + } + ) + } + } + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/DPad.kt b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/DPad.kt new file mode 100644 index 0000000..52f5866 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/DPad.kt @@ -0,0 +1,105 @@ +package coffee.liz.abstractionengine.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +enum class Direction { + UP, DOWN, LEFT, RIGHT +} + +@Composable +fun DPad( + onDirectionPressed: (Direction) -> Unit, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .size(140.dp) + .background( + color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f), + shape = CircleShape + ) + .border(1.dp, MaterialTheme.colorScheme.outline, CircleShape) + ) { + // Up button + DirectionButton( + text = "▲", + onClick = { onDirectionPressed(Direction.UP) }, + modifier = Modifier + .align(Alignment.TopCenter) + .offset(y = 20.dp) + ) + + // Down button + DirectionButton( + text = "▼", + onClick = { onDirectionPressed(Direction.DOWN) }, + modifier = Modifier + .align(Alignment.BottomCenter) + .offset(y = (-20).dp) + ) + + // Left button + DirectionButton( + text = "◀", + onClick = { onDirectionPressed(Direction.LEFT) }, + modifier = Modifier + .align(Alignment.CenterStart) + .offset(x = 20.dp) + ) + + // Right button + DirectionButton( + text = "▶", + onClick = { onDirectionPressed(Direction.RIGHT) }, + modifier = Modifier + .align(Alignment.CenterEnd) + .offset(x = (-20).dp) + ) + + // Center circle + Box( + modifier = Modifier + .align(Alignment.Center) + .size(38.dp) + .background( + color = MaterialTheme.colorScheme.surface.copy(alpha = 0.5f), + shape = CircleShape + ) + .border(1.dp, MaterialTheme.colorScheme.outline, CircleShape) + ) + } +} + +@Composable +private fun DirectionButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Button( + onClick = onClick, + modifier = modifier.size(38.dp), + shape = RoundedCornerShape(4.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f), + contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f) + ), + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = text, + fontSize = 14.sp, + style = MaterialTheme.typography.headlineMedium + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/PCBBackground.kt b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/PCBBackground.kt new file mode 100644 index 0000000..be35a1a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/PCBBackground.kt @@ -0,0 +1,85 @@ +package coffee.liz.abstractionengine.ui + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathEffect + +@Composable +fun PCBBackground(modifier: Modifier = Modifier) { + val pcbGreen = Color(0xFF0D5F0D) + val traceColor = Color(0xFF1A7F1A) + val padColor = Color(0xFFD4AF37) + + Canvas(modifier = modifier.fillMaxSize().background(pcbGreen)) { + val width = size.width + val height = size.height + + // Draw circuit traces (horizontal and vertical lines) + val pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 5f), 0f) + + // Horizontal traces + for (i in 0..15) { + val y = (i * height / 15) + drawLine( + color = traceColor, + start = Offset(0f, y), + end = Offset(width, y), + strokeWidth = 2f, + pathEffect = pathEffect + ) + } + + // Vertical traces + for (i in 0..10) { + val x = (i * width / 10) + drawLine( + color = traceColor, + start = Offset(x, 0f), + end = Offset(x, height), + strokeWidth = 2f, + pathEffect = pathEffect + ) + } + + // Draw pads/components (small circles at intersections) + for (i in 0..10 step 2) { + for (j in 0..15 step 3) { + val x = i * width / 10 + val y = j * height / 15 + drawCircle( + color = padColor, + radius = 4f, + center = Offset(x, y) + ) + } + } + + // Draw some resistor-like rectangles + for (i in 1..9 step 4) { + val x = i * width / 10 + val y = height / 2 + drawRect( + color = Color(0xFF2A4A2A), + topLeft = Offset(x - 15f, y - 8f), + size = androidx.compose.ui.geometry.Size(30f, 16f) + ) + // Resistor contacts + drawCircle( + color = padColor, + radius = 3f, + center = Offset(x - 15f, y) + ) + drawCircle( + color = padColor, + radius = 3f, + center = Offset(x + 15f, y) + ) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/Theme.kt b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/Theme.kt new file mode 100644 index 0000000..661cb09 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/coffee/liz/abstractionengine/ui/Theme.kt @@ -0,0 +1,32 @@ +package coffee.liz.abstractionengine.ui + +import androidx.compose.material3.darkColorScheme +import androidx.compose.ui.graphics.Color + +// GameBoy-inspired color palette +object GameBoyColors { + val DarkestGreen = Color(0xFF0F380F) + val DarkGreen = Color(0xFF306230) + val MediumGreen = Color(0xFF8BAC0F) + val LightGreen = Color(0xFF9BBC0F) + val ScreenGreen = Color(0xFF8BAC0F) + + // Accent colors for buttons (still retro but with more variety) + val ButtonRed = Color(0xFFE76F51) + val ButtonYellow = Color(0xFFF4A261) + val DPadGray = Color(0xFF4A5759) + val DPadLight = Color(0xFF6B7F82) +} + +val GameBoyTheme = darkColorScheme( + primary = GameBoyColors.MediumGreen, + onPrimary = GameBoyColors.DarkestGreen, + secondary = GameBoyColors.LightGreen, + onSecondary = GameBoyColors.DarkestGreen, + background = GameBoyColors.DarkestGreen, + onBackground = GameBoyColors.LightGreen, + surface = GameBoyColors.DarkGreen, + onSurface = GameBoyColors.LightGreen, + surfaceVariant = GameBoyColors.DPadGray, + outline = GameBoyColors.DarkestGreen, +) |
