fpx · ui_kit
UI mínima con el tema como datos: UiThemeDefinition (.tres) → UiThemeBuilder → Theme con variaciones y colores semánticos, presets dark/light, ContextView y pila de ventanas.
El ZIP incluye: addons/fpx/ui_kit + sus tests.
Qué es
La base de UI que inventory_ui necesita, y nada más: un tema = un .tres (UiThemeDefinition: tipografía, tamaños, texto, superficies, colores semánticos, rarezas, botones, campos, contextuales, scrollbars, skin opcional) → UiThemeBuilder.build() → un Theme de Godot con variaciones (HotbarSlot, ContextPanel, ContextBand, TooltipPanel, OverlayPanel, SmallLabel, MutedLabel, HeadingLabel…), iconos SVG generados desde los colores y el tipo Semantic con los colores semánticos (D-FW-7). ThemeColors es la vía única para leerlos desde código (ThemeColors.of(control, &"success")). ContextView: menús y fichas contextuales (acciones, submenús, bandas, info). UiWindowStack: pila LIFO de ventanas flotantes con capas, arrastre, chrome de título y sonido por hook.
Presets del kit: data/themes/dark.tres y data/themes/light.tres — sin skin de arte ni fuente propia (cada juego pone las suyas). Desde 2026-08-31 los defaults del esquema SON la paleta del mockup del diseñador (carbón #1A1E23, headers #15181C, dorado #D6C893, pizarra #3A4150): dark = defaults → el kit, sus demos y el tema por defecto del juego visten el mismo «nuevo modelo».
Ficha de ítem (mockup del diseñador 2026-08-31): el builder publica además las variaciones que inventory_ui/ItemTooltip usa para no hardcodear estilos — TooltipSectionBand + TooltipSectionHeader (headers de sección edge-to-edge), TooltipWarnBand (banner de aviso, fondo derivado de color_warning sobre el panel), TooltipStatValue/TooltipStatLabel (valor bold coloreado por signo + etiqueta blanca), KeyBadge (badges de tecla del pie) y TooltipFlavorLabel (itálica falsa por skew, funciona con cualquier fuente). Dial nuevo: color_set (línea de set del header; clave semántica set).
Instalación
- Descarga
fpx-ui_kit-v0.2.0.zip(botón de arriba) y extráelo en la raíz de tu proyecto Godot — deja la estructura tal cual:
tu-juego/
├── project.godot
├── addons/
│ └── fpx/
│ └── ui_kit/
└── tests/ ← opcional: bórralo si no quieres los tests- Abre el proyecto (o corre
--importheadless). Sin plugins ni autoloads que activar: las clases quedan disponibles en cualquier script. - Opcional — verifica con la suite (necesita GdUnit4, ver la guía):
-a tests/ui_kit.
Ejemplo de uso
Usa las fixtures incluidas en el componente para probar en segundos; en tu juego, sustitúyelas por tus datos.
# Un tema = un .tres. Los presets van SIN skin ni fuente propia, y los
# DEFAULTS del esquema son la paleta del «nuevo modelo» del diseñador
# (2026-08-31): dark() = ese look tal cual; tu juego aporta fuente y ajustes.
var def := UiKitFixtures.dark() # o light(), o tu UiThemeDefinition.tres
var theme := UiThemeBuilder.build(def) # Theme con variaciones e iconos
# Aplicarlo al juego y registrar el fallback para leer colores desde código:
get_tree().root.theme = theme
ThemeColors.fallback_theme = theme
# Variaciones listas: HeadingLabel, SmallLabel, MutedLabel, TooltipPanel,
# OverlayPanel, ContextPanel… y las de la ficha de ítem (TooltipSectionBand,
# TooltipWarnBand, KeyBadge, TooltipFlavorLabel). Por theme_type_variation.
var title := Label.new()
title.theme_type_variation = &"HeadingLabel"
# Colores semánticos (D-FW-7): la VÍA ÚNICA desde código.
var danger := ThemeColors.of(title, &"danger")
var hp := ThemeColors.of(title, &"hp")
# Menús y fichas contextuales, sin UI propia del juego:
var menu := ContextView.open_menu(self, "Acciones", get_global_mouse_position())
menu.add_action("Atacar", func() -> void: attack())
menu.add_check("Sigilo", false, func(on: bool) -> void: set_stealth(on))
menu.add_submenu("Más…", func(sub: ContextView) -> void:
sub.add_action("Opción anidada", func() -> void: pass)
)
var card := ContextView.open_card(self, "Enemigo", at)
card.add_bar(64, 100, &"HpBar", "64 / 100 PV")
card.add_info("envenenado", &"danger")
# Ventanas flotantes con chrome y arrastre (pila LIFO, ESC cierra la de arriba):
var window := CanvasLayer.new()
window.layer = UiWindowStack.next_layer()
add_child(window)
var panel := PanelContainer.new()
window.add_child(panel)
UiWindowStack.apply_chrome(window, panel, mi_contenido, "*", "Inventario",
func() -> void: window.queue_free())
UiWindowStack.opened(window)Demo en vivo
El componente REAL corriendo en tu navegador — Godot 4.7 exportado a Web sobre las fixtures del kit. La primera carga baja el motor (~10 MB comprimido) y se comparte entre todas las demos.
API pública
| Clase | Qué da |
|---|---|
UiThemeDefinition (Resource) | todos los diales exportados · semantic_color(key) |
UiThemeBuilder | build(def) -> Theme · SEMANTIC_KEYS · _panel_box(bg, border, w, radius, margin) |
ThemeColors | of(control, key) -> Color · constant_of(control, name, type, default) · fallback_theme (lo registra el juego al aplicar su tema) · SEMANTIC_TYPE |
ContextView | open_menu(host, title, at) · open_card(host, title, at) · open_card_above(...) · add_action(text, callable, hint, enabled) · add_submenu · add_separator · bandas e info · any_menu_open() |
UiWindowStack | opened/closed(window) · is_topmost · next_layer() · apply_chrome(window, panel, box, icon, title, on_close) · make_draggable · refit · sound_hook: Callable · clear() |
Qué configura el juego
Su UiThemeDefinition.tres (o un preset del kit), lo aplica a get_tree().root.theme y registra ThemeColors.fallback_theme = theme; UiWindowStack.sound_hook = tu_reproductor si quiere sonido de ventanas. Con skin: skin_dir con la lámina 9-slice (opcional).
Fixture mínima
testing/ui_kit_fixtures.gd — UiKitFixtures.dark()/light() (= los .tres del kit), apply(def) (construye y registra el fallback), reset().
ui_kit_fixtures.gdcódigo completo
# res://addons/fpx/ui_kit/testing/ui_kit_fixtures.gd
class_name UiKitFixtures
extends RefCounted
## Fixture mínima del componente `ui_kit`: los DOS presets minimalistas del kit
## — `dark()` y `light()` — SIN skin de arte ni fuente propia (decisión del
## diseñador 2026-08-29: el kit se ve desacoplado y quien lo usa le pone su
## propio tema). Son los mismos valores que `data/themes/{dark,light}.tres`
## (test anti-drift). `dark()` = los defaults del esquema (= el tema base de
## Stremus sin lámina); `light()` = la paleta invertida con los colores
## semánticos oscurecidos para legibilidad sobre claro.
static func dark() -> UiThemeDefinition:
var def := UiThemeDefinition.new()
def.id = &"fpx_dark"
def.display_name = "Funpix Kit · Dark"
def.skin_dir = ""
return def
static func light() -> UiThemeDefinition:
var def := UiThemeDefinition.new()
def.id = &"fpx_light"
def.display_name = "Funpix Kit · Light"
def.skin_dir = ""
def.text_primary = Color(0.12, 0.13, 0.16)
def.text_secondary = Color(0.35, 0.37, 0.42)
def.text_muted = Color(0.0, 0.0, 0.0, 0.50)
def.text_outline = Color(1.0, 1.0, 1.0, 0.85)
def.panel_bg = Color(0.97, 0.97, 0.98, 0.96)
def.panel_border = Color(0.75, 0.77, 0.82, 0.90)
def.overlay_bg = Color(0.93, 0.94, 0.96, 0.90)
def.overlay_border = Color(0.75, 0.77, 0.82, 0.45)
def.color_player = Color(0.05, 0.55, 0.48)
def.color_enemy = Color(0.80, 0.22, 0.20)
def.color_active = Color(0.75, 0.55, 0.00)
def.color_warning = Color(0.85, 0.42, 0.05)
def.color_success = Color(0.15, 0.60, 0.25)
def.color_danger = Color(0.80, 0.18, 0.15)
def.color_resource = Color(0.72, 0.50, 0.05)
def.color_ap = Color(0.80, 0.50, 0.05)
def.color_mp = Color(0.15, 0.55, 0.30)
def.color_hp = Color(0.78, 0.20, 0.25)
def.rarity_comun = Color(0.45, 0.48, 0.54)
def.rarity_trascendente = Color(0.55, 0.45, 0.15)
def.button_bg = Color(0.90, 0.91, 0.94, 0.95)
def.button_bg_hover = Color(0.84, 0.86, 0.90)
def.button_bg_pressed = Color(0.78, 0.80, 0.85)
def.button_bg_disabled = Color(0.90, 0.91, 0.94, 0.55)
def.button_border = Color(0.66, 0.69, 0.75)
def.button_border_hover = Color(0.75, 0.55, 0.00)
def.button_text = Color(0.12, 0.13, 0.16)
def.button_text_disabled = Color(0.0, 0.0, 0.0, 0.35)
def.input_bg = Color(1.0, 1.0, 1.0, 0.98)
def.input_border = Color(0.70, 0.72, 0.78)
def.tooltip_bg = Color(0.99, 0.99, 1.0, 0.98)
def.tooltip_border = Color(0.65, 0.68, 0.74)
def.context_bg = Color(0.98, 0.98, 0.99, 0.98)
def.context_border = Color(0.72, 0.75, 0.80, 0.70)
def.context_band_bg = Color(0.90, 0.91, 0.94, 1.0)
def.context_item_hover = Color(0.0, 0.0, 0.0, 0.06)
def.widget_header_bg = Color(0.90, 0.91, 0.94, 0.95)
def.scrollbar_track = Color(0.0, 0.0, 0.0, 0.06)
def.scrollbar_grabber = Color(0.0, 0.0, 0.0, 0.22)
def.scrollbar_grabber_hover = Color(0.0, 0.0, 0.0, 0.40)
return def
## Construye el Theme de un preset y lo deja como fallback de ThemeColors (lo
## que el juego haría al aplicar su tema): así los widgets del kit resuelven
## colores también fuera del árbol y en tests headless.
static func apply(def: UiThemeDefinition) -> Theme:
var theme := UiThemeBuilder.build(def)
ThemeColors.fallback_theme = theme
return theme
static func reset() -> void:
ThemeColors.fallback_theme = nullCómo se prueba
tests/ui_kit/ — ui_theme_test (ambos presets construyen sin skin, controles de formulario e iconos rasterizados, tipo Semantic completo, .tres == fixture), window_stack_test (LIFO, capas, poda), theme_colors_test (resolución por control/fallback/blanco).
Ejemplar oro (Stremus)
scripts/ui/{ui_theme_definition,ui_theme_builder,theme_colors,window_stack}.gd, scripts/ui/context/context_view.gd (main@670d5a0); sus temas data/themes/ (Sora, jerarquía de tamaños, skin de arte) son el ejemplar de contenido — el kit NO los trae.
Deuda declarada
- FLAG-UI-MINIMAL-FIRST: HUD-grid (
HudWidget/HudManager/HudLayout),Keymap,FloatText,InfoBubble,StatusIcons,SkillIcons,ThemeEditorCorellegan en FK-11. - FLAG-I18N: los textos de chrome («✕», títulos) los pasa el llamador; no hay tabla de etiquetas.
UiThemeDefinitionconserva diales del ejemplar que el kit no usa aún (hotbar_*,widget_header_bg).