Runtime GUI (forja-gui-rt)
Crate: crates/forja-gui-rt/ — Basado en Xilem v0.4 (Masonry + Vello)
¿Qué es?
El runtime GUI de Forja permite crear aplicaciones de ventana con interfaz gráfica usando el mismo lenguaje Forja. Está implementado sobre xilem, un framework UI reactivo con renderizado GPU acelerado por Vello (WebGPU).
Cuando un programa Forja importa "gui", el transpilador genera código Rust que llama a forja_gui_rt::build_and_run(), el cual se encarga de:
- Inicializar el estado evaluando las variables y funciones del programa
- Extraer el layout de la función
main() - Crear una ventana nativa con los widgets Xilem correspondientes
- Manejar el event loop: renders, interacciones, callbacks
Arquitectura
Código Forja (.fa) ──→ AST (forja::ast::Programa)
│
┌───────────────┼───────────────┐
▼ ▼ ▼
inicializar_estado extraer_layout build_and_run
(evalúa vars y (convierte AST (crea ventana
main() nativo) → Layout enum) Xilem + Vello)
│
▼
layout_a_view
(Layout → AnyWidgetView) Estructura del crate
| Archivo | Descripción |
|---|---|
gui_nativa.rs | Motor principal: enum Layout (87+ variantes), expr_a_layout() (200+ funciones Forja), layout_a_view() (conversión a Xilem), AppStateNativo, build_and_run() |
evaluador.rs | Intérprete tree-walking: ValorGUI, ejecutar_funcion(), evaluar_expresion(), funciones nativas (SQLite, archivos, JSON) |
signals.rs | Sistema reactivo: Signal<T>, VariableStore, generación atómica, Stream<T> |
accessibility.rs | Accesibilidad: roles Material, descripciones en español, TalkBack/VoiceOver |
icons.rs | Catálogo de 70+ iconos Material Design con fallback a emoji |
svg_icon_widget.rs | Renderizador SVG de iconos via Vello |
shadow_widget.rs | Conversión de sombras a BoxShadow |
animated_widgets.rs | Widgets animados: AnimatedOpacity, AnimatedScale, AnimatedSlide, RippleWidget |
animation_root.rs | Widget raíz que tickea el motor de animación en cada frame |
gesture_widgets.rs | Gestos: Swipe, Pan, PullToRefresh, PinchZoom, Rotate |
pickers.rs | Selectores: DatePicker, TimePicker, ColorPicker |
qr_widget.rs | Widget QR Code con render Vello |
chart_widgets.rs | Gráficos: Bar, Line, Pie, Gauge, Sparkline con Vello |
canvas_widget.rs | Lienzo de dibujo libre con 20+ comandos vía JSON |
theme/ | Sistema de tema Material You: ColorScheme, TypeScale, ShapeSystem, ElevationSystem, MotionSystem, animaciones, HCT color space |
Quick Start
Creá un archivo app.fa:
importar "gui"
funcion al_click() {
escribir("¡Hiciste clic!")
}
funcion main() {
columna(
texto_grande("Mi App"),
boton("Saludar", &al_click)
)
} Ejecutar:
forja ejecutar --native app.fa Widgets de Layout
Columnas y Filas
columna(etiqueta("Uno"), etiqueta("Dos"))
fila(etiqueta("A"), etiqueta("B"))
columna_con_gap(12, [etiqueta("Uno"), etiqueta("Dos")])
columna_centrada(etiqueta("Centrado")) Apilamiento (ZStack)
pila(
etiqueta("Fondo"),
boton("Frente", &fn)
) Scroll y Contenedores
desplazable(columna(etiqueta("Largo contenido...")))
contenedor(columna(...), 600)
relleno(16, etiqueta("Con padding"))
expansor(etiqueta("Empuja al final"))
centrado(etiqueta("Centrado"))
espacio(24) Relación de Aspecto
caja_relativa(16.0/9.0, video) Flex Layout
flex_layout("horizontal", 8, verdadero, [etiqueta("A"), etiqueta("B")])
flujo(4, [etiqueta("Tag1"), etiqueta("Tag2")]) Layout Adaptable (Responsive)
Tres variantes según el ancho de ventana: Compact (<600px), Medium (600–840px), Expanded (>840px).
adaptable(
columna(etiqueta("Compact")),
fila(etiqueta("Medium")),
fila(etiqueta("Expanded"))
) Widgets de Texto
etiqueta("Hola Mundo")
etiqueta_dinamica("mi_variable")
titulo("Mi App")
etiqueta_color("Rojo", "rojo")
texto_enriquecido("<b>Negrita</b> y <i>itálica</i>")
visor_markdown("# Título\n\nPárrafo") Estilos Tipográficos (15 estilos MD3)
| Función | Estilo | Tamaño | Peso |
|---|---|---|---|
texto_grande | display_large | 57sp | Regular |
texto_mediano | display_medium | 45sp | Regular |
texto_pequeño | display_small | 36sp | Regular |
titular_grande | headline_large | 32sp | Regular |
titular_mediano | headline_medium | 28sp | Regular |
titular_pequeño | headline_small | 24sp | Regular |
encabezado_grande | title_large | 22sp | Regular |
encabezado_mediano | title_medium | 16sp | Medium |
encabezado_pequeño | title_small | 14sp | Medium |
cuerpo_grande | body_large | 16sp | Regular |
cuerpo_mediano | body_medium | 14sp | Regular |
cuerpo_pequeño | body_small | 12sp | Regular |
etiqueta_grande | label_large | 14sp | Medium |
etiqueta_mediana | label_medium | 12sp | Medium |
etiqueta_pequeña | label_small | 11sp | Medium |
Botones Material Design 3
5 Variantes
boton("Relleno", &fn) // Filled (default)
boton_relleno("Relleno", &fn) // Filled
boton_tonal("Tonal", &fn) // Tonal
boton_perfilado("Perfilado", &fn) // Outlined
boton_texto("Texto", &fn) // Text
boton_elevado("Elevado", &fn) // Elevated Con Icono
boton("Con icono", &fn, "favorite")
boton("Inactivo", &fn, "home", falso, verdadero) FAB
fab("+", &agregar) // Medium
fab_pequeño("+", &agregar) // Small
fab_grande("+", &agregar) // Large
fab_extendido("add", "Nuevo", &fn) // Medium + texto Icon Button
boton_icono("search", &buscar) // Standard
boton_icono_relleno("search", &buscar) // Filled
boton_icono_tonal("favorite", &like) // Tonal
boton_icono_perfilado("close", &cerrar) // Outlined Botón Segmentado
segmentado(["Día", "Semana", "Mes"], &cambiar, seleccion)
segmentado_multiple(["A", "B"], &fn, selecciones) Chips
subconjunto_asistente("Ayuda", &fn)
subconjunto_filtro("Filtro", &fn, activo)
subconjunto_entrada("Tag", &click, &eliminar)
subconjunto_sugerencia("Sugerencia", &sel) Campos de Entrada
// Texto
campo_texto("nombre", "Nombre", "Tu nombre")
campo_perfilado("email", "Email")
campo_email("email")
campo_telefono("tel")
campo_url("sitio")
campo_numero("edad", "Edad", 0, 150, 0)
campo_contraseña("pass", "Contraseña")
campo_busqueda("query", "Buscar...")
area_texto("desc", "Descripción") // multilínea
// Con error y contador
campo_texto("nombre", "Nombre", "Tu nombre", "filled", falso, "Error", verdadero)
// Dropdown / Select / Autocomplete
contraer_desplegable(["Op1", "Op2"], sel, "Elige...")
menu_seleccion(["Op1", "Op2"], sel, "Label")
autocompletar(["Manzana", "Banana"], "fruta")
// Switch / Checkbox / Sliders
interruptor("Wifi", "wifi_activado")
casilla("Acepto", "acepto")
deslizante("volumen", 0, 100)
deslizante_discreto("brillo", 0, 100, 10)
deslizante_rango("min", "max", 0, 100) Selectores y Grupos
grupo_radio("color", ["Rojo", "Verde"], sel, &fn, "horizontal")
grupo_subconjuntos(["Tag1", "Tag2"], selecciones, &fn, verdadero)
selector_fecha("fecha_nac")
selector_hora("hora")
selector_color("color_fondo") Tarjetas, Listas y Tablas
// Cards
tarjeta(columna(etiqueta("Título")))
tarjeta_elevada(columna(...))
tarjeta_perfilada(columna(...))
tarjeta_seleccionable(columna(...), &on_click, seleccionado)
// List Items
elemento_lista("Título", "Subtítulo", icono, &fn)
elemento_lista_doble("Título", "Subtítulo", icono, &fn)
lista([item1, item2])
lista_con_dividores([item1, item2])
lista_control([item1, item2], "checkbox", ["var1", "var2"])
lista_seleccion([item1, item2], selecciones, &fn, verdadero)
// Data Table
tabla_datos(
["Nombre", "Edad"],
[["Ana", "30"], ["Luis", "25"]]
)
tabla_ordenable(...)
tabla_seleccion(...)
// Surface / Scaffold
superficie(columna(...))
andamio(top, body, bottom, fab) Superposiciones y Feedback
// Diálogos
dialogo_alerta("Error", "Ocurrió un error", "OK", "", &ok, &ignorar)
dialogo_confirmacion("¿Salir?", "¿Seguro?", "Salir", "Cancelar", &fn, &fn)
dialogo_personalizado("Título", columna(...), &cerrar)
// Bottom Sheets
hoja_inferior(columna(...), "visible")
hoja_inferior_modal(columna(...), "visible", &fn)
hoja_inferior_grande(columna(...), "visible")
// Snackbar / Tooltip / Menú
notificación("Mensaje")
notificación_accion("Mensaje", "Deshacer", &fn, 4000, "snack_visible")
información(boton(...), "Tooltip")
menú_desplegable(["Op1", "Op2"], &fn, "menu_visible")
menú_contexto(["Copiar", "Pegar"], &fn, "ctx_visible") Navegación
Navigator (gestor de pantallas)
variable pantalla_actual = "inicio"
funcion cambiar(id) {
pantalla_actual = id
}
funcion main() {
navegador(
navegador_pantallas(
pantalla("Inicio", columna(etiqueta("Home"))),
pantalla("Perfil", columna(etiqueta("Profile")))
),
"pantalla_actual",
"barra",
&cambiar,
"fade" // animación opcional
)
} Tipos de navegación:
| Valor | Descripción |
|---|---|
"ninguno" | Solo contenido |
"barra" / "bottom" | Bottom navigation bar |
"riel" / "rail" | Navigation rail lateral |
"pestañas" / "tabs" | Tabs superiores |
"cajón" / "drawer" | Drawer lateral |
Animaciones (5to argumento): "fade", "slide", "ninguno" (default)
Componentes de Navegación
// Barras
barra_superior("Título", [icono_buscar()])
barra_superior_media("Título", [icono_buscar()])
barra_superior_grande("Título", [icono_buscar()])
barra_inferior([icono_casa(), icono_perfil()])
// Tabs / Navegación lateral
pestañas(["Tab1", "Tab2"], sel, &fn)
riel_navegacion([item_nav("Inicio", "home")], sel, &fn)
cajon_navegacion([item_nav(...)]) Indicadores y Avatares
// Progress
barra_progreso("var")
barra_progreso_indeterminada()
circulo_progreso("var", 48)
circulo_progreso_indeterminado(48)
// Badge / Skeleton
distintivo(boton(...), "5")
distintivo_punto(boton(...))
esqueleto(200, 20)
esqueleto_tarjeta(300, 100)
esqueleto_linea(200, 16)
// Estados
estado_vacio("search", "Sin resultados", "Recargar", &fn)
estado_error("Error", &reintentar)
// Avatares
avatar("JD")
avatar_icono("person")
grupo_avatar(["A", "B", "C"], 3) Gráficos (Charts)
// Line Chart
gráfico_linea([10, 25, 15, 30], "#6366f1", ["Ene", "Feb", "Mar", "Abr"])
// Bar Chart
gráfico_barras([30, 50, 20], ["#f59e0b", "#6366f1", "#10b981"], ["A", "B", "C"], falso)
// Pie / Donut
gráfico_pastel([30, 50, 20], ["A", "B", "C"])
gráfico_donut([30, 50, 20], ["A", "B", "C"])
// Gauge / Sparkline
gráfico_indicador(75, 0, 100, "#10b981")
minigráfico([5, 10, 8, 15, 12], "#6366f1") Widgets Avanzados
calificación(3, 5, &fn) // Star Rating
asistente_pasos(["Paso1", "Paso2"], actual, &fn)
migaja_de_pan(["Inicio", "Perfil"], "→")
calendario(7, 2026, "fecha", &fn)
visor_qr("https://forja-lang.github.io", 200)
selector_archivo(["txt", "json"], falso, &fn) Widgets Expressive
tarjeta_vidrio(columna(...), 10, 0.3)
gradiente_lineal(["#f59e0b", "#ef4444"], "vertical", columna(...))
boton_morphing("add", "Nuevo", &fn)
fondo_expresivo(["#1a1a2e", "#16213e"], verdadero)
efecto_brillo(columna(...), "#f59e0b", 2.0) Canvas de Dibujo
Canvas interactivo que renderiza comandos Vello desde una variable JSON.
variable comandos = '[]'
funcion dibujar() {
comandos = '[{"FillCircle": {"x":100,"y":100,"radius":50,"color":"#f59e0b"}}]'
}
funcion main() {
columna(
lienzo("comandos", 400, 300),
boton("Dibujar", &dibujar)
)
} Comandos de canvas: FillCircle, StrokeCircle, FillRect, StrokeRect, FillLine, StrokeLine, FillPath, StrokePath, FillText, ClearCanvas, FillPolygon, StrokePolygon, FillRoundedRect, StrokeRoundedRect, DrawEllipse, DrawArc, DrawBezier, DrawImage, DrawLinearGradient
Iconos Material Design
icono_material("home", 24, "#f59e0b")
icono_relleno("star", 24, "#f59e0b")
icono_perfilado("home", 24, "#6366f1")
icono_redondo("search", 24, "#10b981")
icono_agudo("settings", 24, "#ec4899")
icono_dos_tonos("email", 24, "#6366f1") 70+ iconos: home, search, settings, menu, arrow_back, arrow_forward, add, delete, edit, save, copy, done, close, refresh, filter, sort, email, phone, chat, notifications, person, group, file, folder, download, upload, image, code, wifi, location, place, info, warning, error, help, favorite, star, thumb_up, share, lock, calendar_today, shopping_cart y más.
Sistema de Temas Material You
Uso básico
// Color semilla (se genera toda la paleta automáticamente)
tema_material(columna(...), "#f59e0b")
// Tema claro u oscuro
tema_material(columna(...), "#6366f1", falso) // is_dark=false Colores del Scheme (29 roles)
Funciones para aplicar colores: color_primario(child), color_secundario(child), color_terciario(child), color_error(child), color_superficie(child), color_fondo(child), color_perfil(child), color_primario_contenedor(child), color_secundario_contenedor(child), etc.
Tipografía
15 estilos tipográficos MD3 (ver tabla en Widgets de Texto).
Formas (Shapes)
esquinas_pequeñas(child) // 8px
esquinas_medianas(child) // 12px
esquinas_grandes(child) // 16px
esquinas_completas(child) // 28px o 50% Elevaciones / Sombras
sombra(child, 1) // nivel 1 (1dp)
sombra(child, 3) // nivel 3 (6dp)
// Niveles: 0–5 (0dp, 1dp, 3dp, 6dp, 8dp, 12dp) Estado Reactivo
Las variables de Forja se sincronizan automáticamente con los widgets. Cuando una variable cambia (por input del usuario o callback), todos los widgets que la lean se re-renderizan automáticamente.
variable nombre = "Invitado"
funcion saludar() {
escribir("Hola, " + nombre)
}
funcion main() {
columna(
campo_texto("nombre", "Nombre"),
boton("Saludar", &saludar)
)
} Callbacks y Funciones Nativas
Callbacks
funcion mi_callback() {
// ...
}
// Conectar con &nombre_funcion:
boton("Click", &mi_callback)
// Callback que recibe argumentos del widget:
campo_texto("variable", "Label", "Placeholder", &on_change)
deslizante("volumen", 0, 100, &on_change) Funciones Built-in
| Función | Descripción |
|---|---|
timestamp() | Milisegundos desde Unix epoch |
fecha_desde_timestamp(ts) | Formatea timestamp |
dividir(texto, sep) | Divide string → JSON array |
a_numero(valor) | Convierte a entero |
longitud(valor) | Longitud de array/texto |
tipo(valor) | Nombre del tipo |
a_texto(valor) | Convierte a string |
empujar(array, elem) | Agrega a array JSON |
_json_stringificar(valor) | Serializa a JSON |
_archivo_leer(ruta) | Lee archivo → Texto |
_archivo_escribir(ruta, contenido) | Escribe archivo → Entero |
_sqlite_abrir(ruta) | Abre BD → índice |
_sqlite_cerrar(idx) | Cierra conexión |
_sqlite_ejecutar(idx, sql) | Ejecuta SQL |
_sqlite_consultar(idx, sql) | Consulta → JSON |
_sqlite_ejecutar_params(idx, sql, valores) | Con bind params |
_sqlite_consultar_params(idx, sql, valores) | Con bind params |
_sqlite_ultimo_id(idx) | Último rowid |
_sqlite_tablas(idx) | Lista de tablas |
_sqlite_columnas(idx, tabla) | Columnas de tabla |
Animación y Motion
FadeTransition
transición(columna(...), "visible", 300)
// (child, variable_bool, duración_ms) Easing Curves (API Rust)
EASE_STANDARD // (0.2, 0.0, 0.0, 1.0)
EASE_EMPHASIZED // (0.3, 0.0, 0.0, 1.0)
EASE_DECELERATE // (0.0, 0.0, 0.0, 1.0)
EASE_ACCELERATE // (0.3, 0.0, 1.0, 1.0)
EASE_EXPRESSIVE // (0.34, 1.56, 0.64, 1.0) AnimationPresets (API Rust)
AnimationPresets::button_ripple() // 150ms
AnimationPresets::card_elevate() // 200ms
AnimationPresets::page_transition() // 300ms
AnimationPresets::spinner_loop() // 800ms Gestos y Touch
pull_to_refresh(columna(...), &recargar, "refrescando")
swipe_to_dismiss(tarjeta(...), &descartar, "Descartar", "descartado")
zoom_pellizco(imagen, 0.5, 3.0)
rotar(imagen) Ejemplo Completo
importar "gui"
variable nombre = ""
variable email = ""
variable acepta = falso
funcion enviar() {
si (nombre != "" && email != "" && acepta) {
notificación("Formulario enviado")
} sino {
notificación("Completa todos los campos")
}
}
funcion main() {
tema_material(columna(
titulo("Registro"),
campo_texto("nombre", "Nombre", "Tu nombre"),
campo_email("email"),
casilla("Acepto términos", "acepta"),
boton("Enviar", &enviar, "send")
), "#6366f1")
}