Desarrollar interfaces gráficas en Fyne puede ser desafiante, especialmente al manejar cálculos dinámicos de posición y la representación visual de elementos interactivos. En mi proyecto de simulador de estacionamiento, enfrenté un problema al intentar calcular la posición de cada slot y actualizar su color conforme llegaban y salían vehículos. Este artículo detalla el problema y cómo lo solucioné para ayudar a otros desarrolladores enfrentando retos similares.
El Problema
En mi simulador, los espacios de estacionamiento se distribuyen en dos filas (superior e inferior). Cada slot debía representarse con un rectángulo, cuya posición se calcula en tiempo de ejecución. Además, cada espacio debía cambiar de color dinámicamente: rojo al ser ocupado y negro al estar disponible.
La dificultad surge al:
Calcular posiciones dinámicas: Las posiciones de los slots dependían de un cálculo basado en el tamaño de la ventana y el espaciado entre los slots.
Actualizar colores correctamente: Los cambios de estado no se reflejaban de manera adecuada, lo que causaba inconsistencias visuales y errores.
La Solución
1 . Cálculo de posiciones dinámicas: Utilicé coordenadas relativas y un espaciado uniforme para posicionar los slots. Para esto, define un punto de inicio (xStart) y espaciar uniformemente los slots con un valor (spotSpacing). Las posiciones se almacenaron en una lista para acceder fácilmente.
xStart, yTop, yBottom := float32(185), float32(120), float32(200)
spotSpacing := float32(55)
// Fila superior
for i := 0; i < 10; i++ {
parkingSpots = append(parkingSpots, fyne.Position{X: xStart + float32(i)*spotSpacing, Y: yTop})
}
// Fila inferior
for i := 0; i < 10; i++ {
parkingSpots = append(parkingSpots, fyne.Position{X: xStart + float32(i)*spotSpacing, Y: yBottom})
}
2 . Actualización dinámica del color: Implementé funciones específicas para pintar un slot de rojo al ser ocupado y restaurar su color al ser liberado. Para gestionar eficientemente los slots, utilicé un mapa para vincular cada slot con su rectángulo en pantalla.
func (v *ParkingView) DrawRedRectangle(x, y float32, slotID int) {
if _, exists := v.slotRects[slotID]; exists {
return
}
rect := canvas.NewRectangle(color.RGBA{R: 255, G: 0, B: 0, A: 255})
rect.Resize(fyne.NewSize(30, 30))
rect.Move(fyne.NewPos(x, y))
v.Overlay.Add(rect)
v.slotRects[slotID] = rect
v.Overlay.Refresh()
}
func (v *ParkingView) RemoveRedRectangle(slotID int) {
if rect, exists := v.slotRects[slotID]; exists {
v.Overlay.Remove(rect)
delete(v.slotRects, slotID)
v.Overlay.Refresh()
}
}
3 . Sincronización visual: Cada vez que un vehículo entra o sale, actualizo la etiqueta y el estado visual de los slots:
func (v *ParkingView) UpdateState(espaciosDisponibles, capacidad int, id, cajon int, accion string) {
v.Label.SetText(fmt.Sprintf("Espacios disponibles: %d/%d", espaciosDisponibles, capacidad))
if accion == "entra" {
v.DrawRedRectangle(v.parkingSpots[cajon-1].X, v.parkingSpots[cajon-1].Y, cajon)
} else if accion == "sale" {
v.RemoveRedRectangle(cajon)
}
}
Lecciones Aprendidas
Divide y vencerás: Desglosar el problema en componentes manejables (calcular posiciones, gestionar colores) facilita la solución.
Prueba iterativa: Probar con un pequeño conjunto de elementos antes de ampliar el alcance ahorra tiempo y reduce errores.
Aprovecha los contenedores de Fyne: Utilizar contenedores sin diseño (container.NewWithoutLayout()) permite un control absoluto sobre las posiciones.
Conclusión
Este enfoque no solo resolvió el problema, sino que también mejoró mi comprensión de la gestión de UI en Fyne. Espero que esta experiencia sea útil para otros desarrolladores enfrentando retos similares. Si estás trabajando en algo parecido, ¡me encantaría saber cómo lo resolviste o en qué puedo ayudarte!
SOCIAL SHARE CARD GENERATOR