package com.example.smarthome.ui import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Slider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.scale import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import com.example.smarthome.R 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.sp import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.border import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import android.view.SoundEffectConstants import kotlinx.coroutines.launch import kotlinx.coroutines.delay import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.geometry.Offset import androidx.compose.foundation.layout.offset import androidx.compose.ui.draw.rotate import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.tween import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode @Composable fun MainScaffold( selectedRoom: Int, onRoomSelect: (Int) -> Unit, selectedNavItem: Int = 0, onNavItemSelect: (Int) -> Unit = {}, rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoom: (String) -> Unit = {}, onDeleteRoom: (Int) -> Unit = {} ) { Row(modifier = Modifier.fillMaxSize().background(Color(0xFF121212))) { SideNavRail( selectedNavItem = selectedNavItem, onNavItemSelect = onNavItemSelect ) // 根据选中的导航项显示不同的内容 when (selectedNavItem) { 0 -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom) 5 -> SettingsContent() else -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom) } } } @Composable fun DashboardContent( selectedRoom: Int, onRoomSelect: (Int) -> Unit, rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoom: (String) -> Unit = {}, onDeleteRoom: (Int) -> Unit = {} ) { var showAddRoomDialog by remember { mutableStateOf(false) } var editMode by remember { mutableStateOf(false) } Box( modifier = Modifier .fillMaxSize() .pointerInput(editMode) { if (editMode) { detectTapGestures( onTap = { editMode = false } ) } } ) { Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { TopBar() RoomTabs( selectedRoom = selectedRoom, onRoomSelect = onRoomSelect, rooms = rooms, onAddRoomClick = { showAddRoomDialog = true }, onDeleteRoom = onDeleteRoom, editMode = editMode, onEditModeChange = { editMode = it } ) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(16.dp)) { AirConditionerCard(modifier = Modifier.weight(1f)) UsageStatusChart(modifier = Modifier.weight(1f)) } LightRow(modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp)) ModeButtonsRow(onModeSelected = { }) MyDevicesGrid(selectedRoom = selectedRoom, modifier = Modifier.fillMaxSize()) } if (showAddRoomDialog) { AddRoomDialog( onDismiss = { showAddRoomDialog = false }, onConfirm = { roomName -> onAddRoom(roomName) showAddRoomDialog = false } ) } } } @Composable fun HeaderHero() { TopBar() } @Composable fun StatusChips() { SideNavRail() } @Composable fun Chip(text: String) { Box(modifier = Modifier.clip(RoundedCornerShape(20.dp)).background(Color(0x331A1A1A)).padding(horizontal = 12.dp, vertical = 8.dp)) { Text(text = text, color = Color.White) } } @Composable fun QuickActions() { Row(modifier = Modifier.fillMaxWidth().padding(24.dp), horizontalArrangement = Arrangement.spacedBy(12.dp)) { QuickActionCard( title = "回家模式", colors = Brush.linearGradient(listOf(Color(0xFFFFD54F), Color(0xFFFF8F00))), modifier = Modifier.weight(1f), onClick = { /* TODO: 触发回家场景 */ } ) QuickActionCard( title = "出门模式", colors = Brush.linearGradient(listOf(Color(0xFFFF8A65), Color(0xFFFF7043))), modifier = Modifier.weight(1f), onClick = { /* TODO: 触发出门场景 */ } ) QuickActionCard( title = "玩乐模式", colors = Brush.linearGradient(listOf(Color(0xFF6A3DFF), Color(0xFF3A0CA3))), modifier = Modifier.weight(1f), onClick = { /* TODO: 触发玩乐场景 */ } ) } } @Composable fun QuickActionCard(title: String, colors: Brush, modifier: Modifier = Modifier, onClick: () -> Unit) { val shape = RoundedCornerShape(24.dp) var pressed by remember { mutableStateOf(false) } val scale by androidx.compose.animation.core.animateFloatAsState( targetValue = if (pressed) 0.97f else 1f, animationSpec = androidx.compose.animation.core.spring(), label = "quick_action_scale" ) Box( modifier = modifier .height(88.dp) .shadow(12.dp, shape, false) .clip(shape) .background(colors) .scale(scale) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { pressed = !pressed onClick() } .padding(16.dp), contentAlignment = Alignment.BottomStart ) { Text(text = title, fontWeight = FontWeight.Medium, color = Color.White) } } @Composable fun RoomTabs( selectedRoom: Int, onRoomSelect: (Int) -> Unit, rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoomClick: () -> Unit = {}, onDeleteRoom: (Int) -> Unit = {}, editMode: Boolean = false, onEditModeChange: (Boolean) -> Unit = {} ) { Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 16.dp), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { // 可滚动的房间标签区域 Row( modifier = Modifier .weight(1f) .horizontalScroll(rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { rooms.forEachIndexed { index, name -> RoomTab( name = name, selected = index == selectedRoom, editMode = editMode, canDelete = rooms.size > 1, onClick = { if (!editMode) { onRoomSelect(index) } }, onLongClick = { onEditModeChange(true) }, onDelete = { onDeleteRoom(index) onEditModeChange(false) } ) } } // 固定的添加按钮 GradientButton("+ 添加", onClick = onAddRoomClick) } } @Composable fun RoomTab( name: String, selected: Boolean, editMode: Boolean, canDelete: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, onDelete: () -> Unit ) { val scope = rememberCoroutineScope() // 改进的晃动动画 - 使用无限循环的旋转动画 val infiniteTransition = rememberInfiniteTransition(label = "shake") val rotation by infiniteTransition.animateFloat( initialValue = -3f, targetValue = 3f, animationSpec = infiniteRepeatable( animation = tween(150, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "rotation" ) // 缩放动画 val scale by androidx.compose.animation.core.animateFloatAsState( targetValue = if (editMode && canDelete) 0.95f else 1f, animationSpec = androidx.compose.animation.core.spring( dampingRatio = androidx.compose.animation.core.Spring.DampingRatioMediumBouncy, stiffness = androidx.compose.animation.core.Spring.StiffnessLow ), label = "scale" ) Box( modifier = Modifier .scale(scale) .then( if (editMode && canDelete) { Modifier.rotate(rotation) } else { Modifier } ) .pointerInput(Unit) { detectTapGestures( onTap = { onClick() }, onLongPress = { if (canDelete) { onLongClick() } } ) } ) { val base = Modifier.clip(RoundedCornerShape(20.dp)) val mod = if (selected) { base.background(brush = Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)))) } else { base.background(color = Color(0x22121212)) } Box( modifier = mod.padding(horizontal = 14.dp, vertical = 10.dp) ) { Text( text = name, color = if (selected) Color.Black else Color(0xFFB0B0B0), maxLines = 1 ) } // 删除按钮 if (editMode && canDelete) { Box( modifier = Modifier .size(22.dp) .offset(x = (-4).dp, y = (-4).dp) .align(Alignment.TopEnd) .shadow(4.dp, RoundedCornerShape(11.dp)) .clip(RoundedCornerShape(11.dp)) .background(Color(0xFFFF5252)) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { onDelete() }, contentAlignment = Alignment.Center ) { Text( text = "×", color = Color.White, fontSize = 18.sp, fontWeight = FontWeight.Bold ) } } } } @Composable fun DeviceGrid() { val devices = listOf( Device("空调", "室内 25℃", R.drawable.ic_ac, true), Device("智能开关", "待机中", R.drawable.ic_light, false), Device("吊灯", "6500K", R.drawable.ic_light, true), Device("扫地机", "清扫中", R.drawable.ic_media, true), Device("空气净化", "运行中", R.drawable.ic_security, true), Device("饮水机", "待机中", R.drawable.ic_media, false) ) LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 140.dp), modifier = Modifier.fillMaxSize().padding(24.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { items(devices) { d -> DeviceCard(d) } } } data class Device(val name: String, val sub: String, val icon: Int, val on: Boolean) @Composable fun DeviceCard(d: Device) { var active by remember { mutableStateOf(d.on) } Box(modifier = Modifier.height(120.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x26121212)).shadow(12.dp, RoundedCornerShape(24.dp), true).padding(16.dp)) { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) { Image(painter = painterResource(id = d.icon), contentDescription = null, modifier = Modifier.size(40.dp)) Column { Text(text = d.name, fontWeight = FontWeight.SemiBold) Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) { Box(modifier = Modifier.size(8.dp).clip(RoundedCornerShape(4.dp)).background(if (active) Color(0xFF00E676) else Color(0xFF9AA0A6))) Text(text = d.sub, color = Color(0xFF9AA0A6)) } } } Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Text(text = if (active) "运行中" else "待机中", color = if (active) Color(0xFF00E676) else Color(0xFF9AA0A6)) Toggle(active) { active = it } } } } } @Composable fun Toggle(on: Boolean, change: (Boolean) -> Unit) { Box(modifier = Modifier.size(48.dp, 28.dp).clip(RoundedCornerShape(18.dp)).background(if (on) Color(0xFF3A0CA3) else Color(0x33222222)).clickable { change(!on) }.padding(4.dp)) { Box(modifier = Modifier.size(20.dp).clip(RoundedCornerShape(10.dp)).background(Color.White).align(if (on) Alignment.CenterEnd else Alignment.CenterStart)) } } @Composable fun BottomNav() { Row(modifier = Modifier.fillMaxWidth().padding(16.dp).clip(RoundedCornerShape(28.dp)).background(Color(0x33121212)).padding(8.dp), horizontalArrangement = Arrangement.SpaceEvenly) { NavItem("首页", true) NavItem("设备", false) NavItem("我的", false) } } @Composable fun NavItem(title: String, selected: Boolean) { Box(modifier = Modifier.clip(RoundedCornerShape(20.dp)).background(if (selected) Color(0x55121212) else Color.Transparent).padding(horizontal = 18.dp, vertical = 10.dp)) { Text(text = title, color = if (selected) Color.White else Color(0xFFB0B0B0)) } } @Composable fun SideNavRail(selectedNavItem: Int = 0, onNavItemSelect: (Int) -> Unit = {}) { Column(modifier = Modifier.width(120.dp).fillMaxHeight().padding(12.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { Box(modifier = Modifier.size(48.dp).clip(RoundedCornerShape(16.dp)).background(Color(0x22121212))) NavRailItem("控制台", R.drawable.ic_dashboard, selectedNavItem == 0, onClick = { onNavItemSelect(0) }) NavRailItem("最近", R.drawable.ic_recent, selectedNavItem == 1, onClick = { onNavItemSelect(1) }) NavRailItem("收藏", R.drawable.ic_bookmark, selectedNavItem == 2, onClick = { onNavItemSelect(2) }) NavRailItem("下载", R.drawable.ic_download, selectedNavItem == 3, onClick = { onNavItemSelect(3) }) NavRailItem("支持", R.drawable.ic_support, selectedNavItem == 4, onClick = { onNavItemSelect(4) }) NavRailItem("设置", R.drawable.ic_settings, selectedNavItem == 5, onClick = { onNavItemSelect(5) }) } } @Composable fun NavRailItem(text: String, iconRes: Int, selected: Boolean, onClick: () -> Unit = {}) { val scale by androidx.compose.animation.core.animateFloatAsState( targetValue = if (selected) 1.05f else 1f, animationSpec = androidx.compose.animation.core.spring(), label = "nav_scale" ) Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier .scale(scale) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { onClick() } .padding(vertical = 4.dp) ) { Box( modifier = Modifier .size(48.dp) .shadow(if (selected) 8.dp else 0.dp, RoundedCornerShape(14.dp)) .clip(RoundedCornerShape(14.dp)) .background( if (selected) { Brush.linearGradient( listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)) ) } else { Brush.linearGradient( listOf(Color(0x22FFFFFF), Color(0x22FFFFFF)) ) } ), contentAlignment = Alignment.Center ) { Image( painter = painterResource(id = iconRes), contentDescription = text, modifier = Modifier.size(24.dp), colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( if (selected) Color.Black else Color(0xFFB0B0B0) ) ) } Text( text = text, color = if (selected) Color.White else Color(0xFFB0B0B0), fontSize = 13.sp, fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Normal, maxLines = 1 ) } } @Composable fun TopBar() { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { Text(text = "控制台", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold) Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) { Box(modifier = Modifier.width(280.dp).height(40.dp).clip(RoundedCornerShape(20.dp)).background(Color(0x22121212)).padding(horizontal = 16.dp), contentAlignment = Alignment.CenterStart) { Text(text = "搜索关键词", color = Color(0xFF9AA0A6)) } Box(modifier = Modifier.size(40.dp).clip(RoundedCornerShape(12.dp)).background(Color(0x22121212))) Box(modifier = Modifier.size(40.dp).clip(RoundedCornerShape(12.dp)).background(Color(0x22121212))) Box(modifier = Modifier.size(40.dp).clip(RoundedCornerShape(12.dp)).background(Color(0x22121212))) GradientButton("+ 添加设备") } } } @Composable fun GradientButton(text: String, onClick: () -> Unit = {}) { Box(modifier = Modifier.height(36.dp).clip(RoundedCornerShape(20.dp)).background(Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)))).clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { onClick() }.padding(horizontal = 16.dp), contentAlignment = Alignment.Center) { Text(text = text, color = Color.Black, fontWeight = FontWeight.Medium) } } @Composable fun MyDevicesGrid(selectedRoom: Int, modifier: Modifier = Modifier) { val devices = when (selectedRoom) { 1 -> listOf( Device("智能电视", "已运行 3 小时", R.drawable.ic_media, true), Device("音响", "已运行 3 小时", R.drawable.ic_media, false), Device("路由器", "已运行 3 小时", R.drawable.ic_security, true), Device("无线网络", "已运行 3 小时", R.drawable.ic_security, true), Device("暖气", "已运行 3 小时", R.drawable.ic_media, false), Device("插座", "已运行 3 小时", R.drawable.ic_media, true) ) else -> emptyList() } LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 140.dp), modifier = modifier.fillMaxSize(), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { items(devices) { d -> DeviceCard(d) } } } @Composable fun UsageStatusChart(modifier: Modifier = Modifier) { val values = listOf(12f, 18f, 16f, 30f, 20f, 22f, 17f, 19f, 25f, 32f) Box(modifier = modifier.height(220.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x26121212)).padding(16.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text(text = "使用状态", fontWeight = FontWeight.SemiBold) Row(horizontalArrangement = Arrangement.spacedBy(24.dp)) { Column { Text(text = "35.02kWh"); Text(text = "总消耗", color = Color(0xFF9AA0A6)) } Column { Text(text = "32h"); Text(text = "总时长", color = Color(0xFF9AA0A6)) } } Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.Bottom) { values.forEach { v -> Box(modifier = Modifier.weight(1f).height(v.dp).clip(RoundedCornerShape(6.dp)).background(Color(0xFF2A2A2A))) } } } } } @Composable fun AirConditionerCard(modifier: Modifier = Modifier) { var temp by remember { mutableStateOf(24f) } Box(modifier = modifier.height(220.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x26121212)).padding(16.dp)) { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { Text(text = "空调", fontWeight = FontWeight.SemiBold) Switch(checked = true, onCheckedChange = {}) } Text(text = "24°C", style = MaterialTheme.typography.headlineMedium) Slider(value = temp, onValueChange = { temp = it }, valueRange = 16f..32f) Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x22121212))) Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x22121212))) Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x22121212))) Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x22121212))) } } } } @Composable fun LightList(modifier: Modifier = Modifier) { val lights = listOf( "灯光 1" to 0.6f, "灯光 2" to 0.8f, "灯光 3" to 0.45f, "灯光 4" to 0.6f, "灯光 5" to 0.6f ) Column(modifier = modifier.fillMaxHeight(), verticalArrangement = Arrangement.spacedBy(12.dp)) { Text(text = "灯光", fontWeight = FontWeight.SemiBold) lights.forEach { (name, percent) -> Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clip(RoundedCornerShape(16.dp)).background(Color(0x26121212)).padding(12.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) { Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x22121212))) Column { Text(text = name); Text(text = "${(percent * 100).toInt()}%", color = Color(0xFF9AA0A6)) } } DotsProgress(percent) } } } } @Composable fun DotsProgress(percent: Float) { val total = 12 Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically) { repeat(total) { i -> val filled = i < (percent * total).toInt() Box(modifier = Modifier.size(6.dp).clip(RoundedCornerShape(3.dp)).background(if (filled) Color(0xFFA9F0FF) else Color(0xFF3A3A3A))) } } } enum class Mode { HOME, AWAY, FUN } @Composable fun ModeButtonsRow(onModeSelected: (Mode) -> Unit) { BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { val density = LocalDensity.current val buttonHeight = when { maxWidth >= 1024.dp -> with(density) { 500f.toDp() } maxWidth >= 600.dp -> with(density) { 460f.toDp() } else -> with(density) { 160f.toDp() } } var selected by remember { mutableStateOf(null) } val isVertical = maxWidth < 480.dp val containerModifier = Modifier.fillMaxWidth() val arrangement = Arrangement.spacedBy(16.dp) val items = listOf( Triple(Mode.HOME, Brush.linearGradient(listOf(Color(0xFF00D1FF), Color(0xFF00C9A7))), "回家"), Triple(Mode.AWAY, Brush.linearGradient(listOf(Color(0xFFFF8A65), Color(0xFFFF7043))), "出门"), Triple(Mode.FUN, Brush.linearGradient(listOf(Color(0xFF6A3DFF), Color(0xFF3A0CA3))), "玩乐"), ) if (isVertical) { Column(modifier = containerModifier, verticalArrangement = arrangement) { items.forEach { (mode, brush, label) -> ModeButton( text = label, modifier = Modifier.fillMaxWidth(), height = buttonHeight, brush = brush, selected = selected == mode, onClick = { selected = mode onModeSelected(mode) } ) } } } else { Row(modifier = containerModifier, horizontalArrangement = arrangement) { items.forEach { (mode, brush, label) -> ModeButton( text = label, modifier = Modifier.weight(1f), height = buttonHeight, brush = brush, selected = selected == mode, onClick = { selected = mode onModeSelected(mode) } ) } } } } } @Composable fun ModeButton( text: String, modifier: Modifier, height: androidx.compose.ui.unit.Dp, brush: Brush, selected: Boolean, onClick: () -> Unit ) { val shape = RoundedCornerShape(24.dp) val interaction = remember { MutableInteractionSource() } var pressed by remember { mutableStateOf(false) } val scale by androidx.compose.animation.core.animateFloatAsState( targetValue = if (pressed) 0.96f else 1f, animationSpec = androidx.compose.animation.core.spring( dampingRatio = androidx.compose.animation.core.Spring.DampingRatioMediumBouncy, stiffness = androidx.compose.animation.core.Spring.StiffnessLow ), label = "scale" ) val elevation by androidx.compose.animation.core.animateDpAsState( targetValue = if (selected) 16.dp else if (pressed) 4.dp else 8.dp, animationSpec = androidx.compose.animation.core.tween(300), label = "elevation" ) val view = LocalView.current LaunchedEffect(interaction) { interaction.interactions.collect { i -> when (i) { is PressInteraction.Press -> pressed = true is PressInteraction.Release, is PressInteraction.Cancel -> pressed = false } } } Box( modifier = modifier .height(height) .scale(scale) .shadow(elevation, shape) .background( if (selected) { brush } else { Brush.linearGradient( listOf( Color(0x40FFFFFF), Color(0x20FFFFFF) ) ) }, shape = shape ) .border( width = if (selected) 2.dp else 1.dp, brush = if (selected) { Brush.linearGradient( listOf( Color(0x99FFFFFF), Color(0x66FFFFFF) ) ) } else { Brush.linearGradient( listOf( Color(0x33FFFFFF), Color(0x11FFFFFF) ) ) }, shape = shape ) .clickable(indication = null, interactionSource = interaction) { view.playSoundEffect(SoundEffectConstants.CLICK) onClick() } .padding(24.dp), contentAlignment = Alignment.Center ) { Text( text = text, color = if (selected) Color.White else Color(0xFFB0B0B0), fontWeight = if (selected) FontWeight.Bold else FontWeight.Medium, fontSize = if (selected) 22.sp else 20.sp, style = MaterialTheme.typography.titleLarge ) } } @Composable fun LightRow(modifier: Modifier = Modifier) { val lights = listOf( "灯光 1" to 0.6f, "灯光 2" to 0.8f, "灯光 3" to 0.45f, "灯光 4" to 0.6f, "灯光 5" to 0.6f ).take(2) Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(16.dp)) { lights.forEach { (name, percent) -> LightCard(name = name, percent = percent, modifier = Modifier.weight(1f)) } } } @Composable fun LightCard(name: String, percent: Float, modifier: Modifier = Modifier) { Box(modifier = modifier.height(100.dp).clip(RoundedCornerShape(16.dp)).background(Color(0x26121212)).padding(12.dp)) { Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) { Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x22121212))) Column { Text(text = name); Text(text = "${(percent * 100).toInt()}%", color = Color(0xFF9AA0A6)) } } DotsProgress(percent) } } } @Composable fun AddRoomDialog( onDismiss: () -> Unit, onConfirm: (String) -> Unit ) { var roomName by remember { mutableStateOf("") } Box( modifier = Modifier .fillMaxSize() .background(Color(0x80000000)) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { onDismiss() }, contentAlignment = Alignment.Center ) { Box( modifier = Modifier .width(400.dp) .clip(RoundedCornerShape(24.dp)) .background(Color(0xFF1E1E1E)) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { /* 阻止点击穿透 */ } .padding(24.dp) ) { Column( verticalArrangement = Arrangement.spacedBy(20.dp) ) { Text( text = "添加房间", fontSize = 20.sp, fontWeight = FontWeight.Bold, color = Color.White ) androidx.compose.material3.TextField( value = roomName, onValueChange = { roomName = it }, placeholder = { Text("请输入房间名称", color = Color(0xFF9AA0A6)) }, modifier = Modifier.fillMaxWidth(), colors = androidx.compose.material3.TextFieldDefaults.colors( focusedContainerColor = Color(0x22FFFFFF), unfocusedContainerColor = Color(0x22FFFFFF), focusedTextColor = Color.White, unfocusedTextColor = Color.White, cursorColor = Color(0xFFA9F0FF), focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent ), shape = RoundedCornerShape(12.dp), singleLine = true ) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { Box( modifier = Modifier .weight(1f) .height(48.dp) .clip(RoundedCornerShape(12.dp)) .background(Color(0x22FFFFFF)) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { onDismiss() }, contentAlignment = Alignment.Center ) { Text( text = "取消", color = Color(0xFFB0B0B0), fontWeight = FontWeight.Medium ) } Box( modifier = Modifier .weight(1f) .height(48.dp) .clip(RoundedCornerShape(12.dp)) .background( Brush.linearGradient( listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)) ) ) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() }, enabled = roomName.isNotBlank() ) { if (roomName.isNotBlank()) { onConfirm(roomName.trim()) } } .alpha(if (roomName.isNotBlank()) 1f else 0.5f), contentAlignment = Alignment.Center ) { Text( text = "确定", color = Color.Black, fontWeight = FontWeight.Bold ) } } } } } }