package com.rizoma.orbita.ui import androidx.activity.compose.BackHandler import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource 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.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.Surface 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.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel import androidx.compose.runtime.collectAsState import com.rizoma.orbita.R import com.rizoma.orbita.model.AppNode import com.rizoma.orbita.ui.theme.NeonCian import com.rizoma.orbita.ui.theme.NeonRosa import com.rizoma.orbita.ui.theme.NeonVerde import com.rizoma.orbita.ui.theme.Superficie import com.rizoma.orbita.ui.theme.Tenue import com.rizoma.orbita.ui.theme.Vacio @Composable fun OrbitApp(vm: OrbitaViewModel = viewModel()) { val nodes by vm.nodes.collectAsState() var searchOpen by remember { mutableStateOf(false) } var menuNode by remember { mutableStateOf(null) } // Como launcher: "atrás" cierra la búsqueda o no hace nada (nunca sale al sistema). BackHandler(enabled = true) { if (searchOpen) searchOpen = false } Box(Modifier.fillMaxSize().background(Vacio)) { OrbitLayout( nodes = nodes, onLaunch = vm::launch, onLongPress = { menuNode = it }, onCenterTap = { searchOpen = true }, ) if (searchOpen) { SearchOverlay( nodes = nodes, onLaunch = { vm.launch(it) searchOpen = false }, onClose = { searchOpen = false }, ) } menuNode?.let { node -> NodeMenu( node = node, onPin = { vm.togglePin(node); menuNode = null }, onInfo = { vm.openInfo(node); menuNode = null }, onUninstall = { vm.uninstall(node); menuNode = null }, onDismiss = { menuNode = null }, ) } } } @Composable private fun SearchOverlay( nodes: List, onLaunch: (AppNode) -> Unit, onClose: () -> Unit, ) { var query by remember { mutableStateOf("") } val filtered = remember(query, nodes) { if (query.isBlank()) nodes else nodes.filter { it.label.contains(query.trim(), ignoreCase = true) } } val focus = remember { FocusRequester() } LaunchedEffect(Unit) { focus.requestFocus() } Box( Modifier .fillMaxSize() .background(Vacio.copy(alpha = 0.97f)) // Tocar el fondo cierra; el contenido absorbe sus propios toques. .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = onClose, ) ) { Column( Modifier .fillMaxSize() .padding(horizontal = 28.dp) .padding(top = 96.dp) ) { BasicTextField( value = query, onValueChange = { query = it }, singleLine = true, textStyle = TextStyle(color = NeonVerde, fontSize = 26.sp, fontWeight = FontWeight.Light), cursorBrush = SolidColor(NeonRosa), keyboardOptions = androidx.compose.foundation.text.KeyboardOptions(imeAction = ImeAction.Search), modifier = Modifier.fillMaxWidth().focusRequester(focus), decorationBox = { inner -> if (query.isEmpty()) { Text( text = stringResource(R.string.search_hint), color = Tenue, fontSize = 26.sp, fontWeight = FontWeight.Light, ) } inner() }, ) Spacer(Modifier.height(4.dp)) Box(Modifier.fillMaxWidth().height(1.dp).background(NeonVerde.copy(alpha = 0.4f))) Spacer(Modifier.height(16.dp)) LazyColumn(Modifier.fillMaxSize()) { items(filtered, key = { it.key }) { node -> Row( Modifier .fillMaxWidth() .clickable { onLaunch(node) } .padding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { Image( bitmap = node.icon, contentDescription = node.label, modifier = Modifier.size(40.dp).clip(CircleShape), ) Spacer(Modifier.width(16.dp)) Text( text = node.label, color = androidx.compose.ui.graphics.Color.White, fontSize = 16.sp, maxLines = 1, overflow = TextOverflow.Ellipsis, ) } } } } } } @Composable private fun NodeMenu( node: AppNode, onPin: () -> Unit, onInfo: () -> Unit, onUninstall: () -> Unit, onDismiss: () -> Unit, ) { Dialog(onDismissRequest = onDismiss) { Surface( color = Superficie, shape = RoundedCornerShape(20.dp), ) { Column(Modifier.width(240.dp).padding(vertical = 10.dp)) { Text( text = node.label, color = androidx.compose.ui.graphics.Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.padding(horizontal = 20.dp, vertical = 10.dp), ) MenuRow( label = stringResource(if (node.pinned) R.string.menu_unpin else R.string.menu_pin), accent = NeonRosa, onClick = onPin, ) MenuRow(label = stringResource(R.string.menu_info), accent = NeonCian, onClick = onInfo) MenuRow(label = stringResource(R.string.menu_uninstall), accent = NeonRosa, onClick = onUninstall) } } } } @Composable private fun MenuRow(label: String, accent: androidx.compose.ui.graphics.Color, onClick: () -> Unit) { Row( Modifier .fillMaxWidth() .clickable(onClick = onClick) .padding(horizontal = 20.dp, vertical = 14.dp), verticalAlignment = Alignment.CenterVertically, ) { Box(Modifier.size(6.dp).clip(CircleShape).background(accent)) Spacer(Modifier.width(14.dp)) Text(text = label, color = androidx.compose.ui.graphics.Color.White, fontSize = 15.sp) } }