> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/all-out-docs/docs-es/ui/ui-reference.md).

# Referencia de UI

Notas de referencia y patrones para el dibujo de la UI de CSL.

Esta página recopila reglas, patrones y ejemplos de UI que son útiles una vez que conoces lo básico.

Lee [Fundamentos de la UI](/all-out-docs/docs-es/ui/fundamentals.md) primero.

### Principios básicos

1. **Y crece hacia arriba.** Las coordenadas de pantalla usan (0, 0) en la esquina inferior izquierda.
2. **Empieza por los rects de pantalla.** Usa `UI.get_safe_screen_rect()` o `UI.get_screen_rect()`, y luego deriva todo a partir de esos rects.
3. **Patrón push/pop.** Usa `defer` para cada push para evitar filtrar el estado de la UI.
4. **Primero móvil.** Evita las interacciones que dependen solo del hover.
5. **El texto usa UTF-8, pero la cobertura de caracteres es incompleta.** Un carácter aún necesita un glifo en la fuente seleccionada o en sus fuentes de reserva. Algunos caracteres, incluidos los emoji, aún no son compatibles.
6. **Usa la pila de actualización tardía del jugador.** Dibuja toda la UI del jugador desde el `ao_late_update` dentro de `is_local_or_server()`.

### Inicio rápido: un botón simple del HUD

```go
Player :: class : Player_Base {
    ao_late_update :: method(dt: float) {
        if this.is_local_or_server() {
            draw_my_hud(this);
        }
    }
}

draw_my_hud :: proc(player: Player) {
    rect := UI.get_safe_screen_rect()
        .bottom_right_rect()
        .grow(40, 150, 40, 150)
        .offset(-50, 50);

    bs := UI.default_button_settings();
    ts := UI.default_text_settings();

    if UI.button(rect, bs, ts, "Acción").clicked {
        log_info("acción de %", {player.get_username()});
    }
}
```

### Dibujo básico

#### Quads e imágenes

```go
draw_ui :: proc() {
    rect := UI.get_screen_rect().center_rect().grow(100); // 200x200
    texture := get_asset(Texture_Asset, "my_icon.png");
    UI.quad(rect, texture);

    // Con tinte de color (RGBA 0-1)
    UI.quad(rect, texture, {1, 0, 0, 0.5});

    // Color sólido usando el sprite blanco
    UI.quad(rect, core_globals.white_sprite, {0, 0, 0, 0.75});
}
```

#### Quads rellenos

Pasa `Quad_Params.fill` desde `UI.quad_fill` para mostrar solo una parte de un quad. Los valores disponibles de `Fill_Direction` son `.NONE`, `.RIGHT`, `.LEFT`, `.UP`, `.DOWN`, y `.RADIAL`.

Los rellenos lineales usan valores desde `0` hasta `1`. Los rellenos radiales comienzan a las 12 en punto y usan un valor con signo: `-1..0` rellena en sentido antihorario y `0..1` rellena en sentido horario. Los valores fuera del rango aplicable se limitan. Un valor radial de cero no dibuja nada; `-1` y `1` ambos dibujan el quad completo.

```csl
draw_cooldown :: proc(rect: Rect, icon: Texture_Asset, amount: float) {
    params: Quad_Params;
    params.fill = UI.quad_fill(clamp(amount, -1, 1), .RADIAL);
    UI.quad(rect, icon, params=params);
}
```

Para un relleno cambiante, mantén un `Float_Interpolation_Helper` persistente y pasa su resultado a `UI.quad_fill`. Limita antes de llamar a `update`; conservar el signo es necesario para los rellenos radiales antihorarios. Usa `UI.push_fill_amount` y `UI.pop_fill_amount` en su lugar cuando el mismo relleno deba aplicarse a varias llamadas de dibujo. Empareja siempre el push con un pop diferido.

#### Texto

```go
draw_text :: proc() {
    rect := UI.get_screen_rect().center_rect().grow(200, 300, 50, 300);

    ts := UI.default_text_settings();
    ts.size = 48;
    ts.color = {1, 1, 1, 1};
    ts.halign = .CENTER;
    ts.valign = .CENTER;

    UI.text(rect, ts, "¡Hola, mundo!");

    score := 1500;
    UI.text(rect, ts, "Puntuación: %", {score});

    pct := 67;
    UI.text(rect, ts, "% %%", {pct}); // "67 %"

    // Devuelve el rect renderizado real
    actual_rect := UI.text_sync(rect, ts, "Texto dinámico");
}
```

### Diseño: empieza por los rects

```go
layout_example :: proc() {
    screen := UI.get_screen_rect();
    safe := UI.get_safe_screen_rect();

    center := screen.center_rect();
    top_left := screen.top_left_rect();
    bottom_right := screen.bottom_right_rect();

    button_rect := center.grow(50, 150, 50, 150);
    icon_rect := center.grow(64);
}
```

### Usa cut para el diseño

Las funciones cut **deben** usarse para el diseño al colocar varios elementos de la UI:

```go
draw_panel :: proc() {
    rect := UI.get_safe_screen_rect().inset(20);

    header := rect.cut_top(80);
    footer := rect.cut_bottom(60);
    body := rect;

    UI.quad(header, core_globals.white_sprite, {0.1, 0.1, 0.1, 0.8});
    UI.quad(footer, core_globals.white_sprite, {0.1, 0.1, 0.1, 0.8});
    UI.quad(body, core_globals.white_sprite, {0.05, 0.05, 0.05, 0.8});
}
```

### Autoescalado y rects sin escala

Las funciones rect normales toman **puntos** y los escalan por `UI.get_current_scale_factor()` (basado en un lienzo de referencia de 1080 puntos de alto).

Usa las variantes `_unscaled` solo cuando un valor ya esté expresado en píxeles reales de pantalla, como una dimensión medida a partir de un rect existente:

```go
layout_with_unscaled :: proc(window_rect: Rect, items: []string) {
    ts := UI.default_text_settings();
    list_item := window_rect.top_rect().grow_bottom(20);

    for item: items {
        UI.text(list_item, ts, item);

        // height() ya está en píxeles de pantalla, así que no lo escales otra vez
        list_item = list_item.offset_unscaled(0, -list_item.height());
    }
}
```

### Botones

```go
draw_buttons :: proc() {
    rect := UI.get_safe_screen_rect()
        .bottom_center_rect()
        .grow(40, 150, 40, 150)
        .offset(0, 100);

    bs := UI.default_button_settings();
    ts := UI.default_text_settings();

    bs.sprite = get_asset(Texture_Asset, "$AO/new/modal/buttons_2/button_2.png");
    bs.press_scaling = 0.35;

    result := UI.button(rect, bs, ts, "¡Haz clic en mí!");
    if result.clicked {
        log_info("¡Se hizo clic en el botón!");
    }
}
```

#### Reglas de sprites de botones

`UI.default_button_settings()` ya proporciona un estilo completo de botón. Para un botón personalizado, establece `sprite` y opcionalmente `sprite_hovered` y `sprite_pressed`. Cuando los sprites opcionales son nulos, se reutiliza el sprite base.

### Modales

Usa `UI.begin_modal` / `UI.end_modal` para un fondo atenuado que se cierra cuando el jugador toca fuera del modal o pulsa Escape.

```go
Player :: class : Player_Base {
    settings_open: bool;
}

draw_settings_modal :: proc(player: Player) {
    if !player.settings_open return;

    UI.begin_modal(UI.get_screen_rect(), "settings_modal", ref player.settings_open);
    defer UI.end_modal();

    window := UI.get_safe_screen_rect().center_rect().grow(240, 360, 240, 360);
    UI.quad(window, core_globals.white_sprite, {0, 0, 0, 0.9});

    ts := UI.default_text_settings();
    ts.halign = .CENTER;
    ts.valign = .CENTER;
    UI.text(window, ts, "Ajustes");
}
```

### Gestión del estado de la UI

Usa `defer` para cada par push/pop:

```go
draw_layered_ui :: proc() {
    UI.push_screen_draw_context();
    defer UI.pop_draw_context();

    UI.push_layer(10); // Mantén la UI del juego por debajo del chat nativo (capa 75).
    defer UI.pop_layer();

    UI.push_color_multiplier({1, 1, 1, 0.5});
    defer UI.pop_color_multiplier();

    // Dibuja la UI aquí
}
```

### IDs para elementos repetidos

Al dibujar listas o elementos repetidos, empuja IDs únicos:

```go
draw_list :: proc(items: []string) {
    rect := UI.get_safe_screen_rect().inset(20);
    bs := UI.default_button_settings();
    ts := UI.default_text_settings();

    for i: 0..<items.count {
        UI.push_id("item_%", {i});
        defer UI.pop_id();

        item_rect := rect.cut_top(60);
        if UI.button(item_rect, bs, ts, items[i]).clicked {
            log_info("elemento seleccionado %", {i});
        }
    }
}
```

### Tamaños comunes

Algunos tamaños que se sabe que se ven bien en distintos dispositivos:

* Puntos de texto en pantalla: Título 52, Cuerpo 36
* Puntos de rect en pantalla: diálogo simple 600x400, botón estándar 210x74, botón de salida 65x65
* Tamaño del texto en espacio mundial: 0,30 metros

### Buenas prácticas

* Usa `defer` para cada par push/pop.
* Empuja IDs para elementos repetidos.
* Usa funciones sin escala con dimensiones calculadas.
* Ajusta las relaciones de aspecto de los iconos con `rect.fit_aspect(texture.get_aspect())`.
* Comprueba siempre `is_local_or_server()` antes de dibujar UI interactiva.
