feat: 7种牌桌背景+7种卡面风格下拉选择,页面切换动画丝滑过渡

This commit is contained in:
flykhan 2026-04-26 19:00:30 +08:00
parent 167ebdff27
commit 8f843a4e6f
5 changed files with 314 additions and 57 deletions

View File

@ -4,12 +4,15 @@ import android.os.Bundle
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.compose.* import androidx.navigation.compose.*
import androidx.navigation.navArgument import androidx.navigation.navArgument
@ -191,10 +194,18 @@ fun UnoApp() {
LocalCardTheme provides cardTheme, LocalCardTheme provides cardTheme,
LocalTableBg provides tableBg LocalTableBg provides tableBg
) { ) {
val enterAnim: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = {
fadeIn(animationSpec = tween(300)) + slideInHorizontally(animationSpec = tween(350)) { it / 4 }
}
val exitAnim: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = {
fadeOut(animationSpec = tween(300)) + slideOutHorizontally(animationSpec = tween(350)) { -it / 4 }
}
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = Screen.MainMenu.route, startDestination = Screen.MainMenu.route,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize(),
enterTransition = enterAnim,
exitTransition = exitAnim
) { ) {
composable(Screen.MainMenu.route) { composable(Screen.MainMenu.route) {
MainMenuScreen( MainMenuScreen(
@ -416,15 +427,13 @@ fun UnoApp() {
savedName = name savedName = name
prefs.edit().putString("player_name", name).apply() prefs.edit().putString("player_name", name).apply()
}, },
onToggleTheme = { onSetTheme = { theme ->
val next = CardTheme.values()[(cardTheme.ordinal + 1) % CardTheme.values().size] cardTheme = theme
cardTheme = next CardTheme.save(context, theme)
CardTheme.save(context, next)
}, },
onToggleBg = { onSetBg = { bg ->
val next = TableBg.values()[(tableBg.ordinal + 1) % TableBg.values().size] tableBg = bg
tableBg = next TableBg.save(context, bg)
TableBg.save(context, next)
}, },
onToggleOrientation = { onToggleOrientation = {
isLandscape = !isLandscape isLandscape = !isLandscape

View File

@ -16,7 +16,6 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
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 androidx.compose.ui.window.Popup import androidx.compose.ui.window.Popup
@ -44,6 +43,10 @@ fun CardView(
CardTheme.CLASSIC -> ClassicCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true }) CardTheme.CLASSIC -> ClassicCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
CardTheme.ELEGANT -> ElegantCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true }) CardTheme.ELEGANT -> ElegantCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
CardTheme.MIDNIGHT -> MidnightCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true }) CardTheme.MIDNIGHT -> MidnightCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
CardTheme.NEON -> NeonCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
CardTheme.PASTEL -> PastelCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
CardTheme.FOREST -> ForestCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
CardTheme.OCEAN -> OceanCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
} }
if (showInfo) { if (showInfo) {
@ -94,7 +97,6 @@ private fun ElegantCard(
.then(Modifier.combinedClickable(onClick = onClick, onLongClick = onLongPress)), .then(Modifier.combinedClickable(onClick = onClick, onLongClick = onLongPress)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
// Inner colored oval
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth(0.82f).fillMaxHeight(0.78f) .fillMaxWidth(0.82f).fillMaxHeight(0.78f)
@ -105,14 +107,10 @@ private fun ElegantCard(
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text( Text(card.displayText, color = Color.White,
text = card.displayText,
color = Color.White,
fontSize = if (card.type == CardType.NUMBER && card.number >= 10) 16.sp else 22.sp, fontSize = if (card.type == CardType.NUMBER && card.number >= 10) 16.sp else 22.sp,
fontWeight = FontWeight.Black fontWeight = FontWeight.Black)
)
} }
// Corner numbers
Text(card.displayText, modifier = Modifier.align(Alignment.TopStart).padding(6.dp, 4.dp), Text(card.displayText, modifier = Modifier.align(Alignment.TopStart).padding(6.dp, 4.dp),
color = if (isWild) Color.White else cardBg, fontSize = 10.sp, fontWeight = FontWeight.Bold) color = if (isWild) Color.White else cardBg, fontSize = 10.sp, fontWeight = FontWeight.Bold)
Text(card.displayText, modifier = Modifier.align(Alignment.BottomEnd).padding(6.dp, 4.dp), Text(card.displayText, modifier = Modifier.align(Alignment.BottomEnd).padding(6.dp, 4.dp),
@ -140,22 +138,138 @@ private fun MidnightCard(
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
Text( Text(card.displayText, color = if (isWild) Color.Magenta else getCardColor(card.color.name),
text = card.displayText, fontSize = 24.sp, fontWeight = FontWeight.Black)
color = if (isWild) Color.Magenta else getCardColor(card.color.name),
fontSize = 24.sp,
fontWeight = FontWeight.Black
)
if (card.color != CardColor.WILD) { if (card.color != CardColor.WILD) {
// mini color dots
Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
repeat(3) { repeat(3) { Box(Modifier.size(3.dp).clip(CircleShape).background(getCardColor(card.color.name))) }
Box(Modifier.size(3.dp).clip(CircleShape).background(getCardColor(card.color.name)))
} }
} }
} }
} }
} }
// ── Neon ──
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun NeonCard(
card: Card, cardBg: Color, isWild: Boolean,
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
) {
val neonColor = if (isWild) Color.Magenta else cardBg
Box(
modifier = modifier
.width(60.dp).height(90.dp)
.then(if (selected) Modifier.offset(y = (-10).dp) else Modifier)
.shadow(if (selected) 10.dp else 4.dp, RoundedCornerShape(8.dp), ambientColor = neonColor, spotColor = neonColor)
.clip(RoundedCornerShape(8.dp))
.background(Color(0xFF0A0A0A))
.border(2.dp, neonColor, RoundedCornerShape(8.dp))
.then(Modifier.combinedClickable(onClick = onClick, onLongClick = onLongPress)),
contentAlignment = Alignment.Center
) {
Text(card.displayText, color = neonColor, fontSize = 26.sp, fontWeight = FontWeight.Black)
if (!isWild) {
Text(card.displayText, modifier = Modifier.align(Alignment.TopStart).padding(6.dp, 4.dp),
color = neonColor.copy(alpha = 0.6f), fontSize = 9.sp)
Text(card.displayText, modifier = Modifier.align(Alignment.BottomEnd).padding(6.dp, 4.dp),
color = neonColor.copy(alpha = 0.6f), fontSize = 9.sp)
}
}
}
// ── Pastel ──
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun PastelCard(
card: Card, cardBg: Color, isWild: Boolean,
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
) {
Box(
modifier = modifier
.width(60.dp).height(90.dp)
.then(if (selected) Modifier.offset(y = (-10).dp) else Modifier)
.shadow(if (selected) 6.dp else 2.dp, RoundedCornerShape(16.dp))
.clip(RoundedCornerShape(16.dp))
.background(
if (isWild) Brush.horizontalGradient(listOf(UnoRed.copy(alpha = 0.15f), UnoBlue.copy(alpha = 0.15f),
UnoGreen.copy(alpha = 0.15f), UnoYellow.copy(alpha = 0.15f)))
else Brush.verticalGradient(listOf(Color.White, cardBg.copy(alpha = 0.08f)))
)
.border(1.5.dp, cardBg.copy(alpha = 0.3f), RoundedCornerShape(16.dp))
.then(Modifier.combinedClickable(onClick = onClick, onLongClick = onLongPress)),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(card.displayText, color = cardBg, fontSize = 26.sp, fontWeight = FontWeight.Bold)
if (isWild) {
Spacer(modifier = Modifier.height(2.dp))
Row(Modifier.fillMaxWidth(0.6f), horizontalArrangement = Arrangement.SpaceEvenly) {
listOf(UnoRed, UnoBlue, UnoGreen, UnoYellow).forEach { c ->
Box(Modifier.size(5.dp).clip(CircleShape).background(c))
}
}
}
}
}
}
// ── Forest ──
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ForestCard(
card: Card, cardBg: Color, isWild: Boolean,
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
) {
Box(
modifier = modifier
.width(60.dp).height(90.dp)
.then(if (selected) Modifier.offset(y = (-10).dp) else Modifier)
.shadow(if (selected) 6.dp else 2.dp, RoundedCornerShape(6.dp))
.clip(RoundedCornerShape(6.dp))
.background(
if (isWild) Brush.verticalGradient(listOf(Color(0xFF2E7D32), Color(0xFF1B5E20)))
else Brush.verticalGradient(listOf(Color(0xFFE8F5E9), Color(0xFFC8E6C9)))
)
.border(1.5.dp, if (isWild) Color(0xFF81C784) else Color(0xFF388E3C).copy(alpha = 0.3f), RoundedCornerShape(6.dp))
.then(Modifier.combinedClickable(onClick = onClick, onLongClick = onLongPress)),
contentAlignment = Alignment.Center
) {
Text(card.displayText, color = if (isWild) Color.White else cardBg, fontSize = 24.sp, fontWeight = FontWeight.Black)
}
}
// ── Ocean ──
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun OceanCard(
card: Card, cardBg: Color, isWild: Boolean,
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
) {
Box(
modifier = modifier
.width(60.dp).height(90.dp)
.then(if (selected) Modifier.offset(y = (-10).dp) else Modifier)
.shadow(if (selected) 6.dp else 3.dp, RoundedCornerShape(10.dp))
.clip(RoundedCornerShape(10.dp))
.background(
if (isWild) Brush.verticalGradient(listOf(Color(0xFF0277BD), Color(0xFF00BCD4), Color(0xFF0288D1)))
else Brush.verticalGradient(listOf(cardBg.copy(alpha = 0.7f), cardBg))
)
.border(1.dp, Color.White.copy(alpha = 0.3f), RoundedCornerShape(10.dp))
.then(Modifier.combinedClickable(onClick = onClick, onLongClick = onLongPress)),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(card.displayText, color = Color.White, fontSize = 26.sp, fontWeight = FontWeight.Black)
if (!isWild) {
Spacer(modifier = Modifier.height(2.dp))
Row(horizontalArrangement = Arrangement.spacedBy(3.dp)) {
repeat(3) { Box(Modifier.size(4.dp).clip(CircleShape).background(Color.White.copy(alpha = 0.5f))) }
}
}
}
}
} }
@Composable @Composable
@ -209,15 +323,12 @@ fun CardInfoPopup(card: Card, onDismiss: () -> Unit) {
} }
} }
// ── Shared inner content for classic ──
@Composable @Composable
private fun CardContent(card: Card, isWild: Boolean) { private fun CardContent(card: Card, isWild: Boolean) {
if (isWild) { if (isWild) {
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
Text( Text(if (card.type == CardType.WILD_DRAW_FOUR) "+4" else "W", color = Color.White,
text = if (card.type == CardType.WILD_DRAW_FOUR) "+4" else "W", fontSize = 18.sp, fontWeight = FontWeight.Black)
color = Color.White, fontSize = 18.sp, fontWeight = FontWeight.Black
)
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Row(Modifier.fillMaxWidth(0.7f), horizontalArrangement = Arrangement.SpaceEvenly) { Row(Modifier.fillMaxWidth(0.7f), horizontalArrangement = Arrangement.SpaceEvenly) {
listOf(UnoRed, UnoBlue, UnoGreen, UnoYellow).forEach { c -> listOf(UnoRed, UnoBlue, UnoGreen, UnoYellow).forEach { c ->
@ -267,12 +378,8 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
.border(2.dp, GoldAccent, RoundedCornerShape(14.dp)), .border(2.dp, GoldAccent, RoundedCornerShape(14.dp)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Box( Box(Modifier.fillMaxWidth(0.75f).fillMaxHeight(0.7f).clip(RoundedCornerShape(40)).background(DarkCard),
Modifier.fillMaxWidth(0.75f).fillMaxHeight(0.7f) contentAlignment = Alignment.Center) {
.clip(RoundedCornerShape(40))
.background(DarkCard),
contentAlignment = Alignment.Center
) {
Text("UNO", color = GoldAccent, fontSize = 16.sp, fontWeight = FontWeight.Black) Text("UNO", color = GoldAccent, fontSize = 16.sp, fontWeight = FontWeight.Black)
} }
} }
@ -285,9 +392,47 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
.background(DarkCard) .background(DarkCard)
.border(1.5.dp, Color.Magenta.copy(alpha = 0.5f), RoundedCornerShape(12.dp)), .border(1.5.dp, Color.Magenta.copy(alpha = 0.5f), RoundedCornerShape(12.dp)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) { Text("U", color = Color.Magenta, fontSize = 32.sp, fontWeight = FontWeight.Black) }
Text("U", color = Color.Magenta, fontSize = 32.sp, fontWeight = FontWeight.Black) }
} CardTheme.NEON -> {
Box(
modifier = modifier.width(60.dp).height(90.dp)
.shadow(4.dp, RoundedCornerShape(8.dp), ambientColor = Color.Cyan)
.clip(RoundedCornerShape(8.dp))
.background(Color(0xFF0A0A0A))
.border(2.dp, Color.Cyan, RoundedCornerShape(8.dp)),
contentAlignment = Alignment.Center
) { Text("UNO", color = Color.Cyan, fontSize = 14.sp, fontWeight = FontWeight.Black) }
}
CardTheme.PASTEL -> {
Box(
modifier = modifier.width(60.dp).height(90.dp)
.shadow(2.dp, RoundedCornerShape(16.dp))
.clip(RoundedCornerShape(16.dp))
.background(Brush.verticalGradient(listOf(Color(0xFFFFF3E0), Color(0xFFFCE4EC))))
.border(1.5.dp, Color(0xFFE0C0C0), RoundedCornerShape(16.dp)),
contentAlignment = Alignment.Center
) { Text("UNO", color = Color(0xFFCC9999), fontSize = 14.sp, fontWeight = FontWeight.Black) }
}
CardTheme.FOREST -> {
Box(
modifier = modifier.width(60.dp).height(90.dp)
.shadow(2.dp, RoundedCornerShape(6.dp))
.clip(RoundedCornerShape(6.dp))
.background(Brush.verticalGradient(listOf(Color(0xFF2E7D32), Color(0xFF1B5E20))))
.border(1.5.dp, Color(0xFF81C784), RoundedCornerShape(6.dp)),
contentAlignment = Alignment.Center
) { Text("UNO", color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Black) }
}
CardTheme.OCEAN -> {
Box(
modifier = modifier.width(60.dp).height(90.dp)
.shadow(3.dp, RoundedCornerShape(10.dp))
.clip(RoundedCornerShape(10.dp))
.background(Brush.verticalGradient(listOf(Color(0xFF0277BD), Color(0xFF00BCD4))))
.border(1.dp, Color.White.copy(alpha = 0.3f), RoundedCornerShape(10.dp)),
contentAlignment = Alignment.Center
) { Text("UNO", color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Black) }
} }
} }
} }

View File

@ -1,6 +1,7 @@
package com.unogame.ui.screens package com.unogame.ui.screens
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@ -12,10 +13,15 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
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.unogame.model.CardColor
import com.unogame.model.CardType
import com.unogame.model.Card
import com.unogame.ui.components.CardView
import com.unogame.ui.theme.* import com.unogame.ui.theme.*
val HUMAN_NAME_PRESETS = listOf( val HUMAN_NAME_PRESETS = listOf(
@ -26,6 +32,8 @@ val HUMAN_NAME_PRESETS = listOf(
"低带宽生物", "情感过载体" "低带宽生物", "情感过载体"
) )
val SAMPLE_CARD = Card(CardColor.RED, CardType.NUMBER, 7)
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SettingsScreen( fun SettingsScreen(
@ -34,13 +42,15 @@ fun SettingsScreen(
currentBg: TableBg, currentBg: TableBg,
isLandscape: Boolean, isLandscape: Boolean,
onNameChanged: (String) -> Unit, onNameChanged: (String) -> Unit,
onToggleTheme: () -> Unit, onSetTheme: (CardTheme) -> Unit,
onToggleBg: () -> Unit, onSetBg: (TableBg) -> Unit,
onToggleOrientation: () -> Unit, onToggleOrientation: () -> Unit,
onBack: () -> Unit onBack: () -> Unit
) { ) {
var playerName by remember { mutableStateOf(initialName) } var playerName by remember { mutableStateOf(initialName) }
var showAbout by remember { mutableStateOf(false) } var showAbout by remember { mutableStateOf(false) }
var themeExpanded by remember { mutableStateOf(false) }
var bgExpanded by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxSize().background(LocalTableBg.current.color)) { Box(modifier = Modifier.fillMaxSize().background(LocalTableBg.current.color)) {
Column( Column(
@ -120,21 +130,106 @@ fun SettingsScreen(
Spacer(modifier = Modifier.height(28.dp)) Spacer(modifier = Modifier.height(28.dp))
// Card theme // Appearance
Text("外观设置", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp) Text("外观设置", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp)
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
SettingsRow(
icon = Icons.Default.Palette, // Card theme dropdown
label = "卡面风格", ExposedDropdownMenuBox(
expanded = themeExpanded,
onExpandedChange = { themeExpanded = it }
) {
OutlinedTextField(
value = currentTheme.displayName, value = currentTheme.displayName,
onClick = onToggleTheme onValueChange = {},
readOnly = true,
label = { Text("卡面风格") },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = themeExpanded) },
modifier = Modifier.fillMaxWidth().menuAnchor(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedLabelColor = GoldAccent,
unfocusedLabelColor = Color.White.copy(alpha = 0.6f),
focusedBorderColor = GoldAccent,
unfocusedBorderColor = Color.Gray,
cursorColor = GoldAccent
) )
SettingsRow( )
icon = Icons.Default.Wallpaper, ExposedDropdownMenu(
label = "牌桌背景", expanded = themeExpanded,
onDismissRequest = { themeExpanded = false }
) {
CardTheme.values().forEach { theme ->
DropdownMenuItem(
text = {
Row(verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(LocalCardTheme provides theme) {
CardView(card = SAMPLE_CARD, selected = false, playable = false, onClick = {},
modifier = Modifier.size(30.dp, 45.dp))
}
Spacer(modifier = Modifier.width(12.dp))
Text(theme.displayName, fontSize = 14.sp)
}
},
onClick = { onSetTheme(theme); themeExpanded = false }
)
}
}
}
Spacer(modifier = Modifier.height(12.dp))
// Table bg dropdown
ExposedDropdownMenuBox(
expanded = bgExpanded,
onExpandedChange = { bgExpanded = it }
) {
OutlinedTextField(
value = currentBg.displayName, value = currentBg.displayName,
onClick = onToggleBg onValueChange = {},
readOnly = true,
label = { Text("牌桌背景") },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = bgExpanded) },
modifier = Modifier.fillMaxWidth().menuAnchor(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedLabelColor = GoldAccent,
unfocusedLabelColor = Color.White.copy(alpha = 0.6f),
focusedBorderColor = GoldAccent,
unfocusedBorderColor = Color.Gray,
cursorColor = GoldAccent
) )
)
ExposedDropdownMenu(
expanded = bgExpanded,
onDismissRequest = { bgExpanded = false }
) {
TableBg.values().forEach { bg ->
DropdownMenuItem(
text = {
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.size(30.dp)
.clip(RoundedCornerShape(6.dp))
.border(1.dp, Color.White.copy(alpha = 0.2f), RoundedCornerShape(6.dp))
.background(bg.color)
)
Spacer(modifier = Modifier.width(12.dp))
Text(bg.displayName, fontSize = 14.sp)
}
},
onClick = { onSetBg(bg); bgExpanded = false }
)
}
}
}
Spacer(modifier = Modifier.height(12.dp))
// Orientation
SettingsRow( SettingsRow(
icon = if (isLandscape) Icons.Default.StayCurrentLandscape else Icons.Default.StayCurrentPortrait, icon = if (isLandscape) Icons.Default.StayCurrentLandscape else Icons.Default.StayCurrentPortrait,
label = "屏幕方向", label = "屏幕方向",

View File

@ -5,7 +5,11 @@ import android.content.Context
enum class CardTheme(val displayName: String) { enum class CardTheme(val displayName: String) {
CLASSIC("经典"), CLASSIC("经典"),
ELEGANT("优雅"), ELEGANT("优雅"),
MIDNIGHT("暗夜"); MIDNIGHT("暗夜"),
NEON("霓虹"),
PASTEL("马卡龙"),
FOREST("森林"),
OCEAN("海洋");
companion object { companion object {
private const val KEY = "card_theme" private const val KEY = "card_theme"

View File

@ -6,7 +6,11 @@ import androidx.compose.ui.graphics.Color
enum class TableBg(val displayName: String, val color: Color) { enum class TableBg(val displayName: String, val color: Color) {
DARK("暗黑", Color(0xFF121212)), DARK("暗黑", Color(0xFF121212)),
GREEN("墨绿", Color(0xFF1A3C2A)), GREEN("墨绿", Color(0xFF1A3C2A)),
BLUE("深蓝", Color(0xFF0D1B2A)); BLUE("深蓝", Color(0xFF0D1B2A)),
PURPLE("暗紫", Color(0xFF1A1035)),
RED("酒红", Color(0xFF2D1111)),
TEAL("深青", Color(0xFF0D2B2A)),
CHARCOAL("炭灰", Color(0xFF1E1E1E));
companion object { companion object {
private const val KEY = "table_bg" private const val KEY = "table_bg"