feat: 弃牌堆/摸牌堆1.8倍放大,手牌区高度可调0~240dp默认160dp
This commit is contained in:
parent
570d96c5c0
commit
807976ca51
@ -70,6 +70,8 @@ fun UnoApp() {
|
|||||||
var cardTheme by remember { mutableStateOf(CardTheme.load(context)) }
|
var cardTheme by remember { mutableStateOf(CardTheme.load(context)) }
|
||||||
var tableBg by remember { mutableStateOf(TableBg.load(context)) }
|
var tableBg by remember { mutableStateOf(TableBg.load(context)) }
|
||||||
var isLandscape by remember { mutableStateOf(prefs.getBoolean("landscape", false)) }
|
var isLandscape by remember { mutableStateOf(prefs.getBoolean("landscape", false)) }
|
||||||
|
var cardScale by remember { mutableStateOf(loadCardScale(context)) }
|
||||||
|
var handOffset by remember { mutableStateOf(loadHandOffset(context)) }
|
||||||
|
|
||||||
// Apply orientation on start
|
// Apply orientation on start
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
@ -192,7 +194,8 @@ fun UnoApp() {
|
|||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalCardTheme provides cardTheme,
|
LocalCardTheme provides cardTheme,
|
||||||
LocalTableBg provides tableBg
|
LocalTableBg provides tableBg,
|
||||||
|
LocalCardScale provides cardScale
|
||||||
) {
|
) {
|
||||||
val enterAnim: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = {
|
val enterAnim: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = {
|
||||||
fadeIn(animationSpec = tween(300)) + slideInHorizontally(animationSpec = tween(350)) { it / 4 }
|
fadeIn(animationSpec = tween(300)) + slideInHorizontally(animationSpec = tween(350)) { it / 4 }
|
||||||
@ -401,6 +404,7 @@ fun UnoApp() {
|
|||||||
totalPlayers = totalPlayers,
|
totalPlayers = totalPlayers,
|
||||||
humanPlayerName = humanPlayerName,
|
humanPlayerName = humanPlayerName,
|
||||||
botNames = botNames,
|
botNames = botNames,
|
||||||
|
handOffset = handOffset,
|
||||||
onBackToMenu = {
|
onBackToMenu = {
|
||||||
navController.navigate(Screen.MainMenu.route) {
|
navController.navigate(Screen.MainMenu.route) {
|
||||||
popUpTo(0) { inclusive = true }
|
popUpTo(0) { inclusive = true }
|
||||||
@ -423,6 +427,7 @@ fun UnoApp() {
|
|||||||
currentTheme = cardTheme,
|
currentTheme = cardTheme,
|
||||||
currentBg = tableBg,
|
currentBg = tableBg,
|
||||||
isLandscape = isLandscape,
|
isLandscape = isLandscape,
|
||||||
|
handOffset = handOffset,
|
||||||
onNameChanged = { name ->
|
onNameChanged = { name ->
|
||||||
savedName = name
|
savedName = name
|
||||||
prefs.edit().putString("player_name", name).apply()
|
prefs.edit().putString("player_name", name).apply()
|
||||||
@ -435,6 +440,10 @@ fun UnoApp() {
|
|||||||
tableBg = bg
|
tableBg = bg
|
||||||
TableBg.save(context, bg)
|
TableBg.save(context, bg)
|
||||||
},
|
},
|
||||||
|
onSetHandOffset = { offset ->
|
||||||
|
handOffset = offset
|
||||||
|
saveHandOffset(context, offset)
|
||||||
|
},
|
||||||
onToggleOrientation = {
|
onToggleOrientation = {
|
||||||
isLandscape = !isLandscape
|
isLandscape = !isLandscape
|
||||||
prefs.edit().putBoolean("landscape", isLandscape).apply()
|
prefs.edit().putBoolean("landscape", isLandscape).apply()
|
||||||
|
|||||||
@ -16,6 +16,7 @@ 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.unit.Dp
|
||||||
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
|
||||||
@ -32,21 +33,25 @@ fun CardView(
|
|||||||
selected: Boolean = false,
|
selected: Boolean = false,
|
||||||
playable: Boolean = true,
|
playable: Boolean = true,
|
||||||
theme: CardTheme = LocalCardTheme.current,
|
theme: CardTheme = LocalCardTheme.current,
|
||||||
|
scale: Float = LocalCardScale.current,
|
||||||
onClick: () -> Unit = {}
|
onClick: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val cardBg = getCardBgColor(card.color.name)
|
val cardBg = getCardBgColor(card.color.name)
|
||||||
val isWild = card.color == CardColor.WILD
|
val isWild = card.color == CardColor.WILD
|
||||||
var showInfo by remember { mutableStateOf(false) }
|
var showInfo by remember { mutableStateOf(false) }
|
||||||
|
val w = (60 * scale).dp
|
||||||
|
val h = (90 * scale).dp
|
||||||
|
val off = (-30 * scale).dp
|
||||||
|
|
||||||
Box {
|
Box {
|
||||||
when (theme) {
|
when (theme) {
|
||||||
CardTheme.CLASSIC -> ClassicCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
CardTheme.CLASSIC -> ClassicCard(card, cardBg, isWild, w, h, off, 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, w, h, off, 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, w, h, off, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
||||||
CardTheme.NEON -> NeonCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
CardTheme.NEON -> NeonCard(card, cardBg, isWild, w, h, off, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
||||||
CardTheme.PASTEL -> PastelCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
CardTheme.PASTEL -> PastelCard(card, cardBg, isWild, w, h, off, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
||||||
CardTheme.FOREST -> ForestCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
CardTheme.FOREST -> ForestCard(card, cardBg, isWild, w, h, off, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
||||||
CardTheme.OCEAN -> OceanCard(card, cardBg, isWild, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
CardTheme.OCEAN -> OceanCard(card, cardBg, isWild, w, h, off, modifier, selected, playable, onClick, onLongPress = { showInfo = true })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showInfo) {
|
if (showInfo) {
|
||||||
@ -59,13 +64,13 @@ fun CardView(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ClassicCard(
|
private fun ClassicCard(
|
||||||
card: Card, cardBg: Color, isWild: Boolean,
|
card: Card, cardBg: Color, isWild: Boolean, w: Dp, h: Dp, off: Dp,
|
||||||
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(60.dp).height(90.dp)
|
.width(w).height(h)
|
||||||
.then(if (selected) Modifier.offset(y = (-30).dp) else Modifier)
|
.then(if (selected) Modifier.offset(y = off) else Modifier)
|
||||||
.shadow(if (selected) 8.dp else 2.dp, RoundedCornerShape(10.dp))
|
.shadow(if (selected) 8.dp else 2.dp, RoundedCornerShape(10.dp))
|
||||||
.clip(RoundedCornerShape(10.dp))
|
.clip(RoundedCornerShape(10.dp))
|
||||||
.background(
|
.background(
|
||||||
@ -82,14 +87,14 @@ private fun ClassicCard(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ElegantCard(
|
private fun ElegantCard(
|
||||||
card: Card, cardBg: Color, isWild: Boolean,
|
card: Card, cardBg: Color, isWild: Boolean, w: Dp, h: Dp, off: Dp,
|
||||||
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
val borderColor = if (isWild) GoldAccent else cardBg.copy(alpha = 0.5f)
|
val borderColor = if (isWild) GoldAccent else cardBg.copy(alpha = 0.5f)
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(60.dp).height(90.dp)
|
.width(w).height(h)
|
||||||
.then(if (selected) Modifier.offset(y = (-30).dp) else Modifier)
|
.then(if (selected) Modifier.offset(y = off) else Modifier)
|
||||||
.shadow(if (selected) 8.dp else 3.dp, RoundedCornerShape(14.dp))
|
.shadow(if (selected) 8.dp else 3.dp, RoundedCornerShape(14.dp))
|
||||||
.clip(RoundedCornerShape(14.dp))
|
.clip(RoundedCornerShape(14.dp))
|
||||||
.background(Color.White)
|
.background(Color.White)
|
||||||
@ -122,14 +127,14 @@ private fun ElegantCard(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun MidnightCard(
|
private fun MidnightCard(
|
||||||
card: Card, cardBg: Color, isWild: Boolean,
|
card: Card, cardBg: Color, isWild: Boolean, w: Dp, h: Dp, off: Dp,
|
||||||
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
val glow = if (isWild) Color.Magenta.copy(alpha = 0.6f) else cardBg.copy(alpha = 0.6f)
|
val glow = if (isWild) Color.Magenta.copy(alpha = 0.6f) else cardBg.copy(alpha = 0.6f)
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(60.dp).height(90.dp)
|
.width(w).height(h)
|
||||||
.then(if (selected) Modifier.offset(y = (-30).dp) else Modifier)
|
.then(if (selected) Modifier.offset(y = off) else Modifier)
|
||||||
.shadow(if (selected) 12.dp else 4.dp, RoundedCornerShape(12.dp), ambientColor = glow, spotColor = glow)
|
.shadow(if (selected) 12.dp else 4.dp, RoundedCornerShape(12.dp), ambientColor = glow, spotColor = glow)
|
||||||
.clip(RoundedCornerShape(12.dp))
|
.clip(RoundedCornerShape(12.dp))
|
||||||
.background(DarkCard)
|
.background(DarkCard)
|
||||||
@ -153,14 +158,14 @@ private fun MidnightCard(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun NeonCard(
|
private fun NeonCard(
|
||||||
card: Card, cardBg: Color, isWild: Boolean,
|
card: Card, cardBg: Color, isWild: Boolean, w: Dp, h: Dp, off: Dp,
|
||||||
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
val neonColor = if (isWild) Color.Magenta else cardBg
|
val neonColor = if (isWild) Color.Magenta else cardBg
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(60.dp).height(90.dp)
|
.width(w).height(h)
|
||||||
.then(if (selected) Modifier.offset(y = (-30).dp) else Modifier)
|
.then(if (selected) Modifier.offset(y = off) else Modifier)
|
||||||
.shadow(if (selected) 10.dp else 4.dp, RoundedCornerShape(8.dp), ambientColor = neonColor, spotColor = neonColor)
|
.shadow(if (selected) 10.dp else 4.dp, RoundedCornerShape(8.dp), ambientColor = neonColor, spotColor = neonColor)
|
||||||
.clip(RoundedCornerShape(8.dp))
|
.clip(RoundedCornerShape(8.dp))
|
||||||
.background(Color(0xFF0A0A0A))
|
.background(Color(0xFF0A0A0A))
|
||||||
@ -182,13 +187,13 @@ private fun NeonCard(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun PastelCard(
|
private fun PastelCard(
|
||||||
card: Card, cardBg: Color, isWild: Boolean,
|
card: Card, cardBg: Color, isWild: Boolean, w: Dp, h: Dp, off: Dp,
|
||||||
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(60.dp).height(90.dp)
|
.width(w).height(h)
|
||||||
.then(if (selected) Modifier.offset(y = (-30).dp) else Modifier)
|
.then(if (selected) Modifier.offset(y = off) else Modifier)
|
||||||
.shadow(if (selected) 6.dp else 2.dp, RoundedCornerShape(16.dp))
|
.shadow(if (selected) 6.dp else 2.dp, RoundedCornerShape(16.dp))
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.background(
|
.background(
|
||||||
@ -218,13 +223,13 @@ private fun PastelCard(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ForestCard(
|
private fun ForestCard(
|
||||||
card: Card, cardBg: Color, isWild: Boolean,
|
card: Card, cardBg: Color, isWild: Boolean, w: Dp, h: Dp, off: Dp,
|
||||||
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(60.dp).height(90.dp)
|
.width(w).height(h)
|
||||||
.then(if (selected) Modifier.offset(y = (-30).dp) else Modifier)
|
.then(if (selected) Modifier.offset(y = off) else Modifier)
|
||||||
.shadow(if (selected) 6.dp else 2.dp, RoundedCornerShape(6.dp))
|
.shadow(if (selected) 6.dp else 2.dp, RoundedCornerShape(6.dp))
|
||||||
.clip(RoundedCornerShape(6.dp))
|
.clip(RoundedCornerShape(6.dp))
|
||||||
.background(
|
.background(
|
||||||
@ -243,13 +248,13 @@ private fun ForestCard(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun OceanCard(
|
private fun OceanCard(
|
||||||
card: Card, cardBg: Color, isWild: Boolean,
|
card: Card, cardBg: Color, isWild: Boolean, w: Dp, h: Dp, off: Dp,
|
||||||
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
modifier: Modifier, selected: Boolean, playable: Boolean, onClick: () -> Unit, onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(60.dp).height(90.dp)
|
.width(w).height(h)
|
||||||
.then(if (selected) Modifier.offset(y = (-30).dp) else Modifier)
|
.then(if (selected) Modifier.offset(y = off) else Modifier)
|
||||||
.shadow(if (selected) 6.dp else 3.dp, RoundedCornerShape(10.dp))
|
.shadow(if (selected) 6.dp else 3.dp, RoundedCornerShape(10.dp))
|
||||||
.clip(RoundedCornerShape(10.dp))
|
.clip(RoundedCornerShape(10.dp))
|
||||||
.background(
|
.background(
|
||||||
@ -351,11 +356,13 @@ private fun CardContent(card: Card, isWild: Boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.current) {
|
fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.current, scale: Float = LocalCardScale.current) {
|
||||||
|
val w = (60 * scale).dp
|
||||||
|
val h = (90 * scale).dp
|
||||||
when (theme) {
|
when (theme) {
|
||||||
CardTheme.CLASSIC -> {
|
CardTheme.CLASSIC -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.width(60.dp).height(90.dp)
|
modifier = modifier.width(w).height(h)
|
||||||
.shadow(2.dp, RoundedCornerShape(10.dp))
|
.shadow(2.dp, RoundedCornerShape(10.dp))
|
||||||
.clip(RoundedCornerShape(10.dp))
|
.clip(RoundedCornerShape(10.dp))
|
||||||
.background(DarkCard)
|
.background(DarkCard)
|
||||||
@ -371,7 +378,7 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
|
|||||||
}
|
}
|
||||||
CardTheme.ELEGANT -> {
|
CardTheme.ELEGANT -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.width(60.dp).height(90.dp)
|
modifier = modifier.width(w).height(h)
|
||||||
.shadow(3.dp, RoundedCornerShape(14.dp))
|
.shadow(3.dp, RoundedCornerShape(14.dp))
|
||||||
.clip(RoundedCornerShape(14.dp))
|
.clip(RoundedCornerShape(14.dp))
|
||||||
.background(Color.White)
|
.background(Color.White)
|
||||||
@ -386,7 +393,7 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
|
|||||||
}
|
}
|
||||||
CardTheme.MIDNIGHT -> {
|
CardTheme.MIDNIGHT -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.width(60.dp).height(90.dp)
|
modifier = modifier.width(w).height(h)
|
||||||
.shadow(4.dp, RoundedCornerShape(12.dp), ambientColor = Color.Magenta.copy(alpha = 0.4f))
|
.shadow(4.dp, RoundedCornerShape(12.dp), ambientColor = Color.Magenta.copy(alpha = 0.4f))
|
||||||
.clip(RoundedCornerShape(12.dp))
|
.clip(RoundedCornerShape(12.dp))
|
||||||
.background(DarkCard)
|
.background(DarkCard)
|
||||||
@ -396,7 +403,7 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
|
|||||||
}
|
}
|
||||||
CardTheme.NEON -> {
|
CardTheme.NEON -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.width(60.dp).height(90.dp)
|
modifier = modifier.width(w).height(h)
|
||||||
.shadow(4.dp, RoundedCornerShape(8.dp), ambientColor = Color.Cyan)
|
.shadow(4.dp, RoundedCornerShape(8.dp), ambientColor = Color.Cyan)
|
||||||
.clip(RoundedCornerShape(8.dp))
|
.clip(RoundedCornerShape(8.dp))
|
||||||
.background(Color(0xFF0A0A0A))
|
.background(Color(0xFF0A0A0A))
|
||||||
@ -406,7 +413,7 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
|
|||||||
}
|
}
|
||||||
CardTheme.PASTEL -> {
|
CardTheme.PASTEL -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.width(60.dp).height(90.dp)
|
modifier = modifier.width(w).height(h)
|
||||||
.shadow(2.dp, RoundedCornerShape(16.dp))
|
.shadow(2.dp, RoundedCornerShape(16.dp))
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.background(Brush.verticalGradient(listOf(Color(0xFFFFF3E0), Color(0xFFFCE4EC))))
|
.background(Brush.verticalGradient(listOf(Color(0xFFFFF3E0), Color(0xFFFCE4EC))))
|
||||||
@ -416,7 +423,7 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
|
|||||||
}
|
}
|
||||||
CardTheme.FOREST -> {
|
CardTheme.FOREST -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.width(60.dp).height(90.dp)
|
modifier = modifier.width(w).height(h)
|
||||||
.shadow(2.dp, RoundedCornerShape(6.dp))
|
.shadow(2.dp, RoundedCornerShape(6.dp))
|
||||||
.clip(RoundedCornerShape(6.dp))
|
.clip(RoundedCornerShape(6.dp))
|
||||||
.background(Brush.verticalGradient(listOf(Color(0xFF2E7D32), Color(0xFF1B5E20))))
|
.background(Brush.verticalGradient(listOf(Color(0xFF2E7D32), Color(0xFF1B5E20))))
|
||||||
@ -426,7 +433,7 @@ fun CardBack(modifier: Modifier = Modifier, theme: CardTheme = LocalCardTheme.cu
|
|||||||
}
|
}
|
||||||
CardTheme.OCEAN -> {
|
CardTheme.OCEAN -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.width(60.dp).height(90.dp)
|
modifier = modifier.width(w).height(h)
|
||||||
.shadow(3.dp, RoundedCornerShape(10.dp))
|
.shadow(3.dp, RoundedCornerShape(10.dp))
|
||||||
.clip(RoundedCornerShape(10.dp))
|
.clip(RoundedCornerShape(10.dp))
|
||||||
.background(Brush.verticalGradient(listOf(Color(0xFF0277BD), Color(0xFF00BCD4))))
|
.background(Brush.verticalGradient(listOf(Color(0xFF0277BD), Color(0xFF00BCD4))))
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.unogame.model.Card
|
import com.unogame.model.Card
|
||||||
import com.unogame.model.CardColor
|
import com.unogame.model.CardColor
|
||||||
|
import com.unogame.ui.theme.LocalCardScale
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerHand(
|
fun PlayerHand(
|
||||||
@ -25,6 +26,7 @@ fun PlayerHand(
|
|||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
val cardScale = LocalCardScale.current
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
Text(
|
Text(
|
||||||
@ -45,6 +47,7 @@ fun PlayerHand(
|
|||||||
card = card,
|
card = card,
|
||||||
selected = index == selectedIndex,
|
selected = index == selectedIndex,
|
||||||
playable = isPlayable,
|
playable = isPlayable,
|
||||||
|
scale = cardScale,
|
||||||
onClick = { onCardClick(index) },
|
onClick = { onCardClick(index) },
|
||||||
modifier = Modifier.padding(horizontal = 2.dp)
|
modifier = Modifier.padding(horizontal = 2.dp)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -37,6 +37,7 @@ fun GameScreen(
|
|||||||
onShowLog: () -> Unit = {},
|
onShowLog: () -> Unit = {},
|
||||||
onPlaySeven: (Int) -> Unit = {},
|
onPlaySeven: (Int) -> Unit = {},
|
||||||
isSevenZeroMode: Boolean = false,
|
isSevenZeroMode: Boolean = false,
|
||||||
|
handOffset: Float = 0f,
|
||||||
errorMessage: String
|
errorMessage: String
|
||||||
) {
|
) {
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
@ -44,6 +45,8 @@ fun GameScreen(
|
|||||||
var showColorPicker by remember { mutableStateOf(false) }
|
var showColorPicker by remember { mutableStateOf(false) }
|
||||||
var selectedAutoCard by remember { mutableIntStateOf(-1) }
|
var selectedAutoCard by remember { mutableIntStateOf(-1) }
|
||||||
val flipped = gameState.flipped
|
val flipped = gameState.flipped
|
||||||
|
val cardScale = LocalCardScale.current
|
||||||
|
val centerScale = 1.8f // 弃牌堆/摸牌堆较大的尺寸
|
||||||
|
|
||||||
val topCard = gameState.topCard
|
val topCard = gameState.topCard
|
||||||
val currentPlayer = gameState.currentPlayer
|
val currentPlayer = gameState.currentPlayer
|
||||||
@ -63,7 +66,7 @@ fun GameScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(scrollState)
|
.verticalScroll(scrollState)
|
||||||
.padding(top = 24.dp, bottom = 100.dp)
|
.padding(top = 16.dp, bottom = (100f - handOffset).coerceAtLeast(0f).dp)
|
||||||
) {
|
) {
|
||||||
// 出牌状态条,最多2行
|
// 出牌状态条,最多2行
|
||||||
Box(modifier = Modifier.fillMaxWidth().heightIn(min = 36.dp, max = 56.dp)) {
|
Box(modifier = Modifier.fillMaxWidth().heightIn(min = 36.dp, max = 56.dp)) {
|
||||||
@ -144,7 +147,7 @@ fun GameScreen(
|
|||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
|
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other players
|
// Other players
|
||||||
@ -176,7 +179,7 @@ fun GameScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
|
||||||
// Center area: discard pile + draw pile
|
// Center area: discard pile + draw pile
|
||||||
Row(
|
Row(
|
||||||
@ -189,6 +192,7 @@ fun GameScreen(
|
|||||||
// Draw pile
|
// Draw pile
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
CardBack(
|
CardBack(
|
||||||
|
scale = centerScale,
|
||||||
modifier = Modifier.clickable(enabled = isMyTurn) {
|
modifier = Modifier.clickable(enabled = isMyTurn) {
|
||||||
onDrawCard()
|
onDrawCard()
|
||||||
}
|
}
|
||||||
@ -216,6 +220,7 @@ fun GameScreen(
|
|||||||
color = if (gameState.currentWildColor != null && topCard.color == CardColor.WILD)
|
color = if (gameState.currentWildColor != null && topCard.color == CardColor.WILD)
|
||||||
gameState.currentWildColor!! else displayTop.color
|
gameState.currentWildColor!! else displayTop.color
|
||||||
),
|
),
|
||||||
|
scale = centerScale,
|
||||||
playable = false
|
playable = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -325,6 +330,7 @@ fun GameScreen(
|
|||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
|
.offset(y = (-handOffset).dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
.background(LocalTableBg.current.color.copy(alpha = 0.9f))
|
.background(LocalTableBg.current.color.copy(alpha = 0.9f))
|
||||||
|
|||||||
@ -121,6 +121,7 @@ fun LocalGameScreen(
|
|||||||
humanPlayerName: String,
|
humanPlayerName: String,
|
||||||
mode: GameMode,
|
mode: GameMode,
|
||||||
botNames: List<String> = emptyList(),
|
botNames: List<String> = emptyList(),
|
||||||
|
handOffset: Float = 0f,
|
||||||
onBackToMenu: () -> Unit
|
onBackToMenu: () -> Unit
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@ -600,6 +601,7 @@ fun LocalGameScreen(
|
|||||||
myPlayerId = myPlayerId,
|
myPlayerId = myPlayerId,
|
||||||
isMyTurn = isMyTurn,
|
isMyTurn = isMyTurn,
|
||||||
errorMessage = errorMessage,
|
errorMessage = errorMessage,
|
||||||
|
handOffset = handOffset,
|
||||||
isSevenZeroMode = mode == GameMode.SEVEN_ZERO,
|
isSevenZeroMode = mode == GameMode.SEVEN_ZERO,
|
||||||
onPlayCard = { index -> executePlay(index, selectedWildColor) },
|
onPlayCard = { index -> executePlay(index, selectedWildColor) },
|
||||||
onPlaySeven = { index -> executePlay(index, null) },
|
onPlaySeven = { index -> executePlay(index, null) },
|
||||||
|
|||||||
@ -41,9 +41,11 @@ fun SettingsScreen(
|
|||||||
currentTheme: CardTheme,
|
currentTheme: CardTheme,
|
||||||
currentBg: TableBg,
|
currentBg: TableBg,
|
||||||
isLandscape: Boolean,
|
isLandscape: Boolean,
|
||||||
|
handOffset: Float,
|
||||||
onNameChanged: (String) -> Unit,
|
onNameChanged: (String) -> Unit,
|
||||||
onSetTheme: (CardTheme) -> Unit,
|
onSetTheme: (CardTheme) -> Unit,
|
||||||
onSetBg: (TableBg) -> Unit,
|
onSetBg: (TableBg) -> Unit,
|
||||||
|
onSetHandOffset: (Float) -> Unit,
|
||||||
onToggleOrientation: () -> Unit,
|
onToggleOrientation: () -> Unit,
|
||||||
onBack: () -> Unit
|
onBack: () -> Unit
|
||||||
) {
|
) {
|
||||||
@ -237,6 +239,37 @@ fun SettingsScreen(
|
|||||||
onClick = onToggleOrientation
|
onClick = onToggleOrientation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
// 手牌区高度调整
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = DarkSurface)
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Icon(Icons.Default.UnfoldLess, null, tint = Color.White.copy(alpha = 0.6f), modifier = Modifier.size(20.dp))
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
Text("手牌区高度", color = Color.White, fontSize = 15.sp, modifier = Modifier.weight(1f))
|
||||||
|
Text("${handOffset.toInt()}dp", color = GoldAccent, fontSize = 14.sp)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Slider(
|
||||||
|
value = handOffset,
|
||||||
|
onValueChange = { onSetHandOffset(it) },
|
||||||
|
valueRange = 0f..240f,
|
||||||
|
steps = 23,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = SliderDefaults.colors(
|
||||||
|
thumbColor = GoldAccent,
|
||||||
|
activeTrackColor = GoldAccent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text("贴底 ← → 抬高", color = Color.White.copy(alpha = 0.35f), fontSize = 11.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(28.dp))
|
Spacer(modifier = Modifier.height(28.dp))
|
||||||
|
|
||||||
// About
|
// About
|
||||||
|
|||||||
@ -1,6 +1,30 @@
|
|||||||
package com.unogame.ui.theme
|
package com.unogame.ui.theme
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
|
||||||
val LocalCardTheme = compositionLocalOf { CardTheme.ELEGANT }
|
val LocalCardTheme = compositionLocalOf { CardTheme.ELEGANT }
|
||||||
val LocalTableBg = compositionLocalOf { TableBg.GREEN }
|
val LocalTableBg = compositionLocalOf { TableBg.GREEN }
|
||||||
|
val LocalCardScale = compositionLocalOf { 1.0f }
|
||||||
|
|
||||||
|
// 手牌缩放:0.5 ~ 1.3,默认 1.0(原始大小)
|
||||||
|
fun loadCardScale(context: Context): Float {
|
||||||
|
return context.getSharedPreferences("unogame_prefs", Context.MODE_PRIVATE)
|
||||||
|
.getFloat("card_scale", 1.0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveCardScale(context: Context, scale: Float) {
|
||||||
|
context.getSharedPreferences("unogame_prefs", Context.MODE_PRIVATE)
|
||||||
|
.edit().putFloat("card_scale", scale).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手牌区高度偏移(dp),越大手牌越靠上。默认 160,范围 0~240
|
||||||
|
fun loadHandOffset(context: Context): Float {
|
||||||
|
return context.getSharedPreferences("unogame_prefs", Context.MODE_PRIVATE)
|
||||||
|
.getFloat("hand_offset", 160f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveHandOffset(context: Context, offset: Float) {
|
||||||
|
context.getSharedPreferences("unogame_prefs", Context.MODE_PRIVATE)
|
||||||
|
.edit().putFloat("hand_offset", offset).apply()
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user