MainActivity
package com.example.ventas2
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.graphics.Color
import android.graphics.Typeface
import android.view.GestureDetector
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.ListView
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AlertDialog
import kotlin.math.abs
class MainActivity : AppCompatActivity() {
data class NodoMenu(
var nombre: String,
val hijos: MutableList = mutableListOf(),
var unidad: String? = null,
var abreviatura: String? = null
)
private val ruta = mutableListOf()
private val pila = mutableListOf>()
private lateinit var menuPrincipal: MutableList
private lateinit var nivelActual: MutableList
private fun obtenerNodoVentas(): NodoMenu? {
return menuPrincipal.find { it.nombre == "Ventas" }
}
private fun agregarCategoria(nombre: String) {
val ventas = obtenerNodoVentas() ?: return
val nuevaCategoria = NodoMenu(nombre)
ventas.hijos.add(nuevaCategoria)
preguntarSubnivel(nuevaCategoria)
//actualizarLista()
}
private fun agregarSubcategoria(
categoria: NodoMenu,
nombre: String,
abreviatura: String
) {
val nuevaSub = NodoMenu(
nombre = nombre,
abreviatura = abreviatura
)
categoria.hijos.add(nuevaSub)
//actualizarLista()
}
//private val ruta = mutableListOf()
//private val ruta = mutableListOf()
private lateinit var gestureDetectorMain: GestureDetector
private lateinit var fragmentContainer: View
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, FragmentTicketVenta())
.commit()
val sidePanel = findViewById(R.id.sidePanel)
//val rootLayout = findViewById(R.id.main)
//val rootLayout = findViewById(R.id.rootLayout)
//val rootLayout = findViewById(R.id.fragment_container)
fragmentContainer = findViewById(R.id.fragment_container)
sidePanel.post {
sidePanel.translationX = -sidePanel.width.toFloat()
}
val contentFrame = findViewById(R.id.contentFrame)
val tvRuta = findViewById(R.id.tvRuta)
val listView = ListView(this)
contentFrame.addView(listView)
/////////////////////////////////////////////////////////
/*
data class NodoMenu(
val nombre: String,
val hijos: List = emptyList(),
//val hijos: MutableList = mutableListOf(),
val unidad: String? = null,
val abreviatura: String? = null // 👈 nuevo
)
*/
//val ruta = mutableListOf()
menuPrincipal = mutableListOf(
NodoMenu("Inicio"),
NodoMenu(
"Ventas",
mutableListOf(
NodoMenu(
"Cierres",
mutableListOf(
NodoMenu(
"Común",
mutableListOf(
NodoMenu("Rojo", unidad = "u", abreviatura = "ro"),
NodoMenu("Azul", unidad = "u", abreviatura = "az")
),abreviatura = "c"
),
NodoMenu("Diente de Perro", unidad = "u", abreviatura = "dp"),
NodoMenu("Metal", unidad = "u", abreviatura = "mt")
)
),
NodoMenu(
"Botones",
mutableListOf(
NodoMenu("Redondos", unidad = "u", abreviatura = "re"),
NodoMenu("Cuadrados", unidad = "u", abreviatura = "cu")
)
),
NodoMenu(
"Cintas",
mutableListOf(
NodoMenu("Negra", unidad = "m", abreviatura = "ne"),
NodoMenu("Roja", unidad = "m", abreviatura = "ro")
)
)
)
),
NodoMenu(
"Configuracion",
mutableListOf(
NodoMenu("Agregar"),
NodoMenu("Editar"),
NodoMenu("Eliminar")
)
),
NodoMenu("Salir")
)
val prueba = NodoMenu("Prueba", abreviatura = "pr")
menuPrincipal.add(prueba)
nivelActual = menuPrincipal
//val pila = mutableListOf>()
fun actualizarLista() {
val nombres = nivelActual.map { it.nombre }
listView.adapter = ArrayAdapter(
this,
android.R.layout.simple_list_item_1,
nombres
)
}
actualizarLista()
fun abrirMenu() {
sidePanel.animate()
.translationX(0f)
.setDuration(200)
.start()
}
fun cerrarMenu() {
sidePanel.animate()
.translationX(-sidePanel.width.toFloat())
.setDuration(200)
.start()
}
var startX = 0f
/*
rootLayout.setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.rawX
true
}
MotionEvent.ACTION_UP -> {
val delta = event.rawX - startX
if (delta > 120) {
abrirMenu()
} else if (delta < -120) {
cerrarMenu()
}
true
}
else -> false
}
}
*/
/* fun actualizarRuta() {
tvRuta.text = if (ruta.isEmpty()) {
"Ventas"
} else {
"Ventas > " + ruta.joinToString(" > ")
}
}
*/
fun actualizarRuta() {
//tvRuta.text = ruta.joinToString(" > ")
tvRuta.text = ruta.joinToString(" > ") { it.nombre }
}
fun animarLista(direccion: Int) {
val fromX = if (direccion > 0) 1f else -1f
listView.translationX = fromX * listView.width
val width = if (listView.width == 0) 300 else listView.width
listView.translationX = fromX * width
listView.animate()
.translationX(0f)
.setDuration(180)
.start()
}
fun volverNivel() {
if (pila.isNotEmpty()) {
nivelActual = pila[pila.size - 1]
pila.removeAt(pila.size - 1)
if (ruta.isNotEmpty()) {
ruta.removeAt(ruta.size - 1) // o ruta.removeAt(ruta.lastIndex)
}
actualizarRuta()
actualizarLista()
animarLista(-1)
}
}
val gestureDetector = GestureDetector(this,
object : GestureDetector.SimpleOnGestureListener() {
private val SWIPE_THRESHOLD = 100
private val SWIPE_VELOCITY_THRESHOLD = 100
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffX = e2.x - (e1?.x ?: 0f)
val diffY = e2.y - (e1?.y ?: 0f)
if (abs(diffX) > abs(diffY) &&
abs(diffX) > SWIPE_THRESHOLD &&
abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
)
// if (kotlin.math.abs(diffX) > SWIPE_THRESHOLD &&
// kotlin.math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
// )
{
if (diffX > 0) {
volverNivel()
}
return true
}
return false
}
})
listView.setOnTouchListener { _, event ->
gestureDetector.onTouchEvent(event)
false
}
// SELECTOR DE ITEM
listView.setOnItemClickListener { _, _, position, _ ->
val seleccionado = nivelActual[position]
// 🔥 PRIMERO detectamos casos especiales
if (seleccionado.nombre == "Inicio") {
// futura lógica
return@setOnItemClickListener
}
if (seleccionado.nombre == "Salir") {
// futura lógica
return@setOnItemClickListener
}
if (seleccionado.nombre == "Agregar") {
mostrarDialogAgregarCategoria()
actualizarLista()
return@setOnItemClickListener
}
if (seleccionado.nombre == "Editar") {
// futura lógica
return@setOnItemClickListener
}
if (seleccionado.nombre == "Eliminar") {
// futura lógica
return@setOnItemClickListener
}
if (seleccionado.hijos.isNotEmpty()) {
pila.add(nivelActual)
nivelActual = seleccionado.hijos
//ruta.add(seleccionado.nombre)
ruta.add(seleccionado)
actualizarRuta()
actualizarLista()
animarLista(1)
} else {
cerrarMenu()
/*
Toast.makeText(
this,
"Producto: ${seleccionado.nombre}",
Toast.LENGTH_SHORT
).show()
*/
// Acá después conectamos con tu diálogo real
val fragment = supportFragmentManager
.findFragmentById(R.id.fragment_container)
if (fragment is FragmentTicketVenta) {
val producto = seleccionado.nombre
val unidad = seleccionado.unidad ?: "u"
fragment.procesarSeleccionProducto(producto, unidad)
}
}
}
// GESTURE DE APERTURA Y CIERRA
gestureDetectorMain = GestureDetector(this,
object : GestureDetector.SimpleOnGestureListener() {
private val SWIPE_THRESHOLD = 80 // 70
private val SWIPE_VELOCITY_THRESHOLD = 50 // 40
// Usar onScroll() o usar onFling().
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffX = e2.x - (e1?.x ?: 0f)
val diffY = e2.y - (e1?.y ?: 0f)
if (kotlin.math.abs(diffX) > kotlin.math.abs(diffY) &&
kotlin.math.abs(diffX) > SWIPE_THRESHOLD &&
kotlin.math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
) {
if (diffX > 0) {
abrirMenu()
} else {
cerrarMenu()
}
return true
}
return false
}
})
fragmentContainer.setOnTouchListener { _: View, event: MotionEvent ->
gestureDetectorMain.onTouchEvent(event)
true
}
}
private fun mostrarDialogAgregarCategoria() {
val input = EditText(this)
AlertDialog.Builder(this)
.setTitle("Nueva Categoría")
.setView(input)
.setPositiveButton("Guardar") { _, _ ->
val nombre = input.text.toString()
if (nombre.isNotBlank()) {
agregarCategoria(nombre)
//actualizarLista()
}
}
.setNegativeButton("Cancelar", null)
.show()
}
private fun preguntarSubnivel(padre: NodoMenu) {
val layout = LinearLayout(this)
layout.orientation = LinearLayout.VERTICAL
val inputNombre = EditText(this)
inputNombre.hint = "Nombre Subcategoría"
val inputAbreviatura = EditText(this)
inputAbreviatura.hint = "Abreviatura"
layout.addView(inputNombre)
layout.addView(inputAbreviatura)
AlertDialog.Builder(this)
.setTitle("Agregar Subnivel a ${padre.nombre}")
.setView(layout)
.setPositiveButton("Agregar") { _, _ ->
val nombre = inputNombre.text.toString()
val abrev = inputAbreviatura.text.toString()
if (nombre.isNotBlank() && abrev.isNotBlank()) {
val nuevo = NodoMenu(
nombre = nombre,
abreviatura = abrev
)
padre.hijos.add(nuevo)
// 🔥 Preguntamos si quiere otro subnivel dentro del nuevo
preguntarSubnivel(nuevo)
} else {
// si no completa, simplemente termina
volverAVentas()
//actualizarRuta()
//actualizarLista()
}
}
.setNegativeButton("Terminar") { _, _ ->
volverAVentas()
}
.show()
}
private fun volverAVentas() {
val ventas = obtenerNodoVentas() ?: return
nivelActual = ventas.hijos
ruta.clear()
ruta.add(ventas)
//actualizarRuta()
//actualizarLista()
}
}