feat: 7种牌桌背景+7种卡面风格下拉选择,页面切换动画丝滑过渡
This commit is contained in:
parent
167ebdff27
commit
8f843a4e6f
@ -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
|
||||||
|
|||||||
@ -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,17 +138,75 @@ 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,6 +214,64 @@ private fun MidnightCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 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
|
||||||
fun CardInfoPopup(card: Card, onDismiss: () -> Unit) {
|
fun CardInfoPopup(card: Card, onDismiss: () -> Unit) {
|
||||||
val effectText = when (card.type) {
|
val effectText = when (card.type) {
|
||||||
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(
|
||||||
value = currentTheme.displayName,
|
expanded = themeExpanded,
|
||||||
onClick = onToggleTheme
|
onExpandedChange = { themeExpanded = it }
|
||||||
)
|
) {
|
||||||
SettingsRow(
|
OutlinedTextField(
|
||||||
icon = Icons.Default.Wallpaper,
|
value = currentTheme.displayName,
|
||||||
label = "牌桌背景",
|
onValueChange = {},
|
||||||
value = currentBg.displayName,
|
readOnly = true,
|
||||||
onClick = onToggleBg
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ExposedDropdownMenu(
|
||||||
|
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,
|
||||||
|
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 = "屏幕方向",
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user