# TextArea

> Campo de texto multilínea con word-wrap, desplazamiento vertical, íconos por línea y modo read-only desplazable.

Source: https://crossfly.dev/es/docs/ui/widgets/textarea — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# TextArea

`TextArea` es la entrada de texto **de múltiples líneas**. Sobre la base [`TextInput`](/docs/ui/widgets/textinput), añade: word-wrap, estructura de líneas, desplazamiento vertical, mapeo de coordenadas 2D y layout multilínea. Soporta íconos a la derecha (uno por línea visible) y un modo **read-only desplazable** (para mostrar texto largo que el usuario solo recorre).

A diferencia de [`TextField`](/docs/ui/widgets/textfield), el `TextArea` **no tiene máscara ni modo contraseña**; `ENTER` inserta salto de línea y `getText()` preserva los `\n`.

> 🟡 · **Paquete:** `dev.crossfly.ui` · **Extiende:** `TextInput` · **Estilo:** `TextAreaStyle`

## Cuándo usar
- Observaciones, comentarios, descripciones, direcciones — cualquier texto de **varias líneas**.
- Mostrar un bloque largo desplazable (con `editable=false`).

## Cuándo NO usar
- Entrada de **una línea** (nombre, e-mail, valor) → usa [`TextField`](/docs/ui/widgets/textfield).
- Máscara / modo contraseña → usa [`TextField`](/docs/ui/widgets/textfield) (el `TextArea` no los soporta).

## Apariencia
Rectángulo con fondo/borde del `TextAreaStyle`, texto dividido en líneas (word-wrap en el ancho interno), con desplazamiento vertical cuando el contenido excede la altura. La altura preferida viene de `rowCount` (número de líneas visibles) × altura de línea + `ingap`. Los íconos a la derecha, cuando se definen, ocupan una columna propia, uno por línea visible.

``

## Ejemplo completo (copiar, pegar y ejecutar)
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // Campo multilínea; LAY_FILL da altura al área desplazable
      TextArea obs = new TextArea().caption("Observações");
      add(LAY_FILL, obs);

      // Label de salida
      Label saida = new Label("Escreva algo e toque em Enviar");
      add(LAY_AUTO, saida);

      // No hay onChange por tecla: lee getText() al enviar (preserva los \n)
      Button enviar = new Button("Enviar")
         .onAction(c -> saida.setText("Você escreveu " + obs.getText().length() + " caracteres"));
      add(LAY_AUTO, enviar);
   }
}
```

> Los fragmentos de abajo muestran **solo el núcleo** (lo que va dentro de `onShow()`). Para ejecutarlos, colócalos en el esqueleto `MainWindow` de arriba.

## Uso básico
```java
TextArea obs = new TextArea().caption("Observações");
container.add(0.4f, obs);
// ...
String texto = obs.getText();   // preserva los \n
```

## Constructores
| Constructor | Descripción |
|---|---|
| `TextArea()` | vacío |
| `TextArea(String text)` | con texto inicial (`\r\n`/`\r` normalizados a `\n`) |

## Propiedades y personalización
| Propiedad / método | Tipo | Por defecto | Qué hace |
|---|---|---|---|
| `text(String)` / `setText(String)` | `TextArea` / `void` | — | define el texto (normaliza saltos; cursor al inicio) |
| `getText()` | `String` | — | texto en bruto, preservando `\n` |
| `caption(String)` | `TextArea` (fluido) | `null` | placeholder |
| `rightIcons(Icon...)` | `TextArea` (fluido) | — | íconos a la derecha, uno por línea visible |
| `rightIconAction(int row, Action)` | `TextArea` (fluido) | — | acción del ícono de la línea `row` (0-based) |
| `clearText()` | `void` | — | limpia todo el texto |
| `getNumberOfTextLines()` | `int` | — | total de líneas lógicas (word-wrapped) |
| `scrollToTop()` / `scrollToBottom()` / `scrollToLine(int)` | `void` | — | desplazamiento programático |
| estilo `.rowCount(int)` | `int` | — | nº de líneas visibles (define la altura preferida) |
| estilo `.maxLength(int)` | `int` | `0` (ilimitado) | límite de caracteres |
| estilo `.validChars(String)` | `String` | `null` | caracteres permitidos (`\n` siempre aceptado) |
| estilo `.editable(false)` | `boolean` | `true` | read-only desplazable |
| estilo `.spaceBetweenLines(int)` | `int` | — | espacio extra entre líneas |

## Callbacks y eventos
| Callback | Cuándo se dispara |
|---|---|
| `onAction(Action)` (heredado) | en `onTouchUp`, tras abrir el teclado |
| `onFocus()` / `onBlur()` (heredados) | ganancia / pérdida de foco |
| `rightIconAction(row, Action)` | al tocar (y soltar sobre) el ícono de esa línea |

## Estados
- **focused:** cursor parpadeando, el teclado abre al toque; las flechas ↑/↓ navegan entre líneas.
- **selection:** selección 2D por arrastre; long-touch selecciona todo y abre el `ActionSheet`.
- **read-only (`editable=false`):** sin cursor/edición; el arrastre **desplaza** el contenido; la rueda del ratón también desplaza.

### Campo de observaciones con límite
```java
TextArea obs = new TextArea().caption("Observações");
obs.style(Styles.theme()
   .textArea(State.NORMAL)
   .rowCount(4)
   .maxLength(500));
container.add(LAY_AUTO, obs);
```

### Bloque de texto solo-lectura, desplazable
```java
TextArea termos = new TextArea(textoDosTermos);
termos.style(Styles.theme()
   .textArea(State.NORMAL)
   .editable(false)      // solo desplazar, sin editar
   .rowCount(8));
container.add(LAY_FILL, termos);
termos.scrollToTop();
```

### Líneas con ícono de acción a la derecha
```java
TextArea lista = new TextArea("Item A\nItem B\nItem C");
lista.rightIcons(
   new MaterialIcon(""),   // "delete" en la línea 0
   new MaterialIcon(""),
   new MaterialIcon(""));
lista.rightIconAction(0, c -> removerLinha(0));
container.add(0.3f, lista);
```

## Buenas prácticas
- Define `rowCount` en el estilo para controlar la altura visible; combínalo con `LAY_AUTO` en el layout.
- Para mostrar texto largo sin edición, usa `editable(false)` — el arrastre pasa a desplazar en vez de seleccionar.
- Usa `getText()` para persistir (preserva `\n`); la normalización de `\r\n` ya se hace en la entrada.

## Errores comunes
- **Esperar máscara o modo contraseña:** el `TextArea` no los soporta — usa [`TextField`](/docs/ui/widgets/textfield).
- **Definir demasiados `rightIcons`:** solo se dibujan hasta `visibleRows` (nº de líneas visibles); los íconos más allá de eso se ignoran.

## Véase también
- Guía del área: [`ui/`](/docs/ui) · Recetas: [`cookbook/`](/docs/cookbook)
- Concepto (foco, selección, scroll): [`concepts/`](/docs/concepts)
- Componentes relacionados: [`TextInput`](/docs/ui/widgets/textinput) · [`TextField`](/docs/ui/widgets/textfield) · [`Keypad`](/docs/ui/widgets/keypad)
