Spot - Toolkit de GUI de escritorio similar a React para el lenguaje Go
(github.com/roblillack)- Spot es un toolkit reactivo simple para crear GUI de escritorio multiplataforma en Go; usa widgets nativos cuando es posible y ofrece de forma consistente APIs específicas de cada plataforma
- Funciona recreando un árbol de componentes inmutable cuando cambia el estado de la aplicación, y comparándolo con el estado anterior para decidir qué controles de UI actualizar
- Los backends actuales son una implementación basada en Cocoa para macOS y otra basada en FLTK para las demás plataformas; en macOS también se puede usar FLTK opcionalmente
spotes el paquete core independiente del backend que proporciona el modelo reactivo y el renderizado, yspot/uies una colección de controles GUI multiplataforma ya preparados- Todavía no ofrece auto layout, múltiples ventanas, diálogos modales, ventanas redimensionables, barra de menús, widgets personalizados, acceso a widgets nativos, drag and drop ni internacionalización
Objetivo y modelo básico de Spot
- Spot es un toolkit de GUI reactivo para Go, diseñado para usar widgets nativos donde sea posible y ofrecer una API consistente en varias plataformas
- Al agregarlo como una dependencia simple al proyecto y escribir solo código Go, se pueden crear binarios GUI nativos autocontenidos sin herramientas adicionales ni generación de código
- El ejemplo crea una ventana y un botón con el flujo
ui.Init(),spot.MountFn(...),ui.Run(), y administra el estado del conteo de clics conspot.UseState[int](<https://github.com/roblillack/ctx, 0>) - El handler del clic del botón llama a
setCounter(counter + 1)y, cuando cambia el estado, el título del botón cambia con el formato"Clicked %d times!"
Método de actualización reactiva
- En Spot, reactive significa que la UI se actualiza automáticamente cuando cambia el estado de la aplicación
- Ante un cambio de estado, recrea el árbol de componentes inmutable y lo compara rápidamente con el estado anterior para decidir qué controles de UI actualizar
- En la web, esta idea suele llamarse virtual DOM, y Spot nació de un experimento para llevar este concepto al entorno de escritorio de Go e implementar una biblioteca GUI similar a React
- En lugar de actualizar la UI manualmente, el desarrollador administra la lógica y el estado de la aplicación mediante funciones de renderizado sin efectos secundarios y hooks como
UseState
Backends y organización de paquetes
- Spot selecciona automáticamente en tiempo de compilación el backend adecuado para la plataforma de ejecución
- Actualmente se ofrecen dos backends
- Implementación basada en FLTK: usa go-fltk
- Implementación basada en Cocoa: usa una versión modificada de gocoa
- En macOS usa el backend Cocoa, y en las demás plataformas usa el backend basado en FLTK
- También se puede usar FLTK opcionalmente en macOS, y la mejora del soporte para Windows queda como plan futuro
spotes el paquete core que proporciona el modelo reactivo y las funciones de renderizado, y puede usarse con cualquier conjunto de controles que implemente la interfazspot.Controlspot/uies un paquete de controles GUI multiplataforma ya preparados para usar con Spot
Componentes, controles y hooks
- Como en React, se pueden crear hooks personalizados
- Se crea una función que recibe
*spot.RenderContextcomo primer argumento y se llama aspot.UseState,spot.UseEffect, etc. para conectarse con el ciclo de vida de Spot - Por convención, el nombre de la función lleva el prefijo
Use…
- Se crea una función que recibe
- Los componentes personalizados pueden crearse como structs que implementan la interfaz
spot.Component- Esta interfaz tiene un único método:
Render(ctx *spot.RenderContext) spot.Component - Los componentes creados de esta forma pueden usarse igual que los componentes integrados
- Esta interfaz tiene un único método:
- En Spot, un component es una unidad lógica que contiene lógica de negocio y estado
- Los componentes se componen de otros componentes y finalmente se renderizan como uno o más controles
- Un control es un componente especial que se monta en el árbol de UI y representa un elemento visual en pantalla
- Por lo general se basa en una implementación nativa del backend GUI, como un botón, una etiqueta o una entrada de texto
- También puede usarse una biblioteca de widgets completamente distinta de la biblioteca de widgets incluida
- Basta con crear structs que implementen la interfaz
spot.Componenty administren widgets nativos
- Basta con crear structs que implementen la interfaz
- Actualmente no se admite usar
spot/uicon backends que no sean Cocoa o FLTK
Términos del ciclo de vida de renderizado
- Make: proceso de crear una instancia de un struct que implementa la interfaz
spot.Component, o de llamar aspot.Makecon una función de renderizado para crear una nueva instancia de componente - Render: proceso de aplicar el estado del componente a sus elementos y devolver otra instancia de componente
- Build: proceso de renderizar componentes recursivamente para crear un árbol de controles
- Se ejecuta pasando una instancia de componente a
spot.Build, o pasando una función de renderizado aspot.BuildFn
- Se ejecuta pasando una instancia de componente a
- Mount: proceso de crear controles de UI reales a partir del árbol de controles virtual
- Puede hacerse llamando a
Mounten el nodo del árbol, o usandospot.Mount,spot.MountFn
- Puede hacerse llamando a
- Update: proceso de actualizar el árbol de controles montado
- Se realiza llamando a
Updateen el nodo del árbol
- Se realiza llamando a
Funciones que no ofrece actualmente
- Actualmente Spot no ofrece las siguientes funciones
-
Auto layout
- Múltiples ventanas
- Diálogos modales
- Ventanas redimensionables
- Barra de menús
- Widgets personalizados
- Acceso a widgets nativos
- Drag and drop
- Internacionalización
-
Controles de UI soportados
- Spot incluye de forma predeterminada varios controles de UI, como botones, etiquetas, entradas de texto, sliders y dropdowns
- Los estados de soporte se indican como ❓ no implementado, 🚧 en progreso, ⚠️ implementación parcial y ✅ completado
- Los principales controles completados son los siguientes
- Button: botón para ejecutar una acción simple; usa
Fl_ButtonyNSButton - Checkbox: control para elegir entre dos opciones; usa
Fl_Check_ButtonyNSButton - Dropdown: dropdown para elegir una opción entre varios elementos; usa
Fl_ChoiceyNSComboBox - Image: control para mostrar imágenes bitmap; usa
Fl_Boxy unNSButtonpersonalizado - Label: etiqueta de texto no editable; usa
Fl_BoxyNSTextField - ListBox: control de lista con selección única o múltiple; usa
Fl_Select_Browser/Fl_Multi_BrowseryNSTableView - ProgressBar: muestra el progreso de tareas largas; usa
Fl_ProgressyNSProgressIndicator - Slider: entrada con slider horizontal; usa
Fl_SlideryNSSlider - Spinner: entrada numérica con botones arriba/abajo; usa
Fl_SpinneryNSTextField+NSStepper - TextField: entrada de texto de una sola línea; usa
Fl_InputyNSTextField - TextEditor: edición de texto multilínea; usa
Fl_Text_EditoryNSTextView - Window: control de ventana de nivel superior; usa
Fl_WindowyNSWindow
- Button: botón para ejecutar una acción simple; usa
- También hay controles parcialmente implementados o no implementados
- Dial: control circular de estado, con estado ⚠️ de implementación parcial
- ComboBox: menú dropdown combinado con entrada de texto, aún no iniciado
- Se propone
https://github.com/rodrigocfd/windigocomo posible candidato de backend futuro para una biblioteca de controles nativos de Windows
1 comentarios
Opiniones en Hacker News
Tengo que ver esto sí o sí. Estaba buscando una forma simple de crear herramientas internas de desarrollo en Go; básicamente son formularios con botones y campos de texto.
También probé Gio, pero me costó entenderlo, y ahora uso wails, que me gusta mucho más. Este proyecto también se ve interesante y parece que vale la pena revisarlo.
Recomiendo seriamente reducir el alcance de esa dirección de “multiplataforma: aprovechando FLTK[1] y Cocoa[2], Spot funciona en Mac, Linux y BSD, con planes de soporte nativo para Windows en el futuro”.
Conviene conservar lo aprendido para mantener flexibilidad más adelante, pero primero es mejor hacerlo bien con un solo toolkit. Los toolkits de GUI, los bindings de GUI y las GUI en sí ya tienden a ahogarse en detalles; si además uno se carga voluntariamente los detalles de varios toolkits base, es muy probable que termine sin hacer bien ninguno.
Existe el dicho de que “el primer 90% del trabajo toma el 90% del tiempo, y el 10% restante toma otro 90%”, pero en GUI incluso eso suena optimista. El primer 10% es el 90% del trabajo; el siguiente 10% cuesta 10 veces más, y el siguiente 10% otras 10 veces más. Intentar ser multiplataforma puede terminar asfixiándote.
No espero que estés de acuerdo con esta idea ahora mismo, pero cuando más adelante te encuentres con que tres toolkits te obligan a manejar algo como rich text de tres maneras contradictorias, espero que te des permiso de quedarte solo con el toolkit base mejor soportado o más popular y descartar los demás.
Llevo un tiempo buscando algo así en Go. Creo que Go tiene una gran oportunidad de ofrecer una excelente experiencia de desarrollo para UI multiplataforma porque su proceso de build es simple.
Según mi experiencia, la mitad del dolor del desarrollo multiplataforma está en gestionar la complejidad del build, y Go prácticamente elimina eso.
Eso sí, me da curiosidad cómo resolverán el layout multiplataforma, ya que el tamaño predeterminado de los controles nativos varía entre plataformas. No he visto que otros toolkits multiplataforma lo resuelvan particularmente bien. Aun así, les deseo suerte.
Hace unos años estaba buscando algo así. Pero también necesitaba soporte para Windows. Al final me pasé a C++ para usar wxWidgets, y pude obtener un binario pequeño y autocontenido.
Me impresionó ver que, siendo un toolkit nativo, FLTK soporta zoom de toda la app con Ctrl-+ y Ctrl+-, como un navegador. Y gracias a https://github.com/fltk-rs/fltk-theme?tab=readme-ov-file#wid..., mejoró mi impresión sobre qué tan “nativo” se puede hacer ver FLTK.
Relacionado con esto, recientemente también descubrí GoVCL https://z-kit.cc/en/ y quiero probarlo.
Un “Hello World” autocontenido de Spot pesa 2.3MiB en mi Mac. No es bonito, pero para mí funciona lo suficiente.
Me pregunto cuál es la ventaja del enfoque de árbol virtual de controles frente a actualizar directamente los controles que se muestran al usuario.
Terminas escribiendo callbacks por todos lados, y cada callback puede tener que revisar cuidadosamente el estado actual de todas las demás actividades antes de actualizar decenas de widgets.
Con un enfoque reactivo, escribes una sola función de render que describe la interfaz para un estado dado, y el framework se encarga de cuándo llamarla y qué entradas darle. Es mucho más fácil de entender, y después de usar React cuesta volver atrás, así que me puse a experimentar si algo parecido era posible en Go.
Se ve bien. Me pregunto si podrían agregar al README las plataformas soportadas.
Información como Windows, Linux, macOS, *BSD, Android, iOS, Web o Tizen sería bastante interesante.
Podrían presentarlo como en la documentación de Flutter: https://docs.flutter.dev/reference/supported-platforms
Aplaudo el esfuerzo, pero ¿multiplataforma sin soporte para Windows?
Ojalá hubiera sabido de esto hace 3 semanas, o que hubiera existido entonces según el historial de commits. Desde hace mucho digo que un React portado a Go o un framework similar a React para Go daría una experiencia de desarrollo increíble, y esto parece encajar perfecto.
Odiaba bastante React.js hasta que React.lua me hizo cambiar de opinión.
Al final encontré una forma de hacer algo parecido usando solo
html/templatede la biblioteca estándar de Go, y escribí los detalles de la implementación aquí: https://www.sheshbabu.com/posts/react-like-composition-using...El gran problema es que, cuando lanzas algo para escritorio, normalmente también quieres una versión web. Sobre todo si no es una app muy de nicho que interactúe mucho con el sistema operativo.
O necesitas algo que apunte a varias plataformas, incluyendo móvil.
Busqué durante bastante tiempo, y lo más cercano que encontré fue Qt y React Native; ambos eran opciones dolorosas por varias razones.
FLTK soporta Windows. Me pregunto si todavía no soporta Windows porque planean usar otra solución.
Sin embargo, mi objetivo —aunque de baja prioridad— es implementar un backend basado en Win32, y el primer paso ya está terminado: https://github.com/roblillack/spot/pull/4