# TextField

> Campo de texto de una línea con máscara, EditMode, modo contraseña, íconos clicables y caption flotante.

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

# TextField

`TextField` es la entrada de texto **de una línea**. Sobre la base [`TextInput`](/docs/ui/widgets/textinput), añade: motor de **máscara** (fecha, moneda, personalizada), **modo contraseña** (con toggle mostrar/ocultar), íconos izquierdo/derecho clicables, desplazamiento horizontal y un **caption flotante** opcional. El comportamiento de entrada se define por [`EditMode`](/docs/reference) (NORMAL/DATE/CURRENCY/NUMERIC/TIME/PASSWORD), que también elige el teclado por defecto.

> 🟡 · **Paquete:** `dev.crossfly.ui` · **Extiende:** `TextInput` · **Estilo:** `TextFieldStyle` (default y focused distintos)

## Cuándo usar
- Cualquier entrada de **una línea**: nombre, e-mail, contraseña, valores numéricos/monetarios, fecha, hora.
- Campos con máscara (código postal, teléfono, fecha) o con ícono clicable (limpiar, buscar, ojo de contraseña).

## Cuándo NO usar
- Texto de **múltiples líneas** (observaciones, comentarios) → usa [`TextArea`](/docs/ui/widgets/textarea).
- Elección entre opciones fijas → usa `Dropdown` / `Option` (ver [`ui/`](/docs/ui)).

## Apariencia
Caja con fondo/borde del `TextFieldStyle`, texto en una línea, íconos opcionales en los extremos y, opcionalmente, un **caption flotante** (`floatingCaption`) que sube por encima del campo cuando hay foco o contenido.

``

## 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 de una línea con caption flotante
      TextField nome = new TextField().caption("Nome");
      add(LAY_AUTO, nome);

      // Label de salida
      Label saida = new Label("Digite seu nome e toque em Enviar");
      add(LAY_AUTO, saida);

      // No hay onChange por tecla: lee getText() al enviar
      Button enviar = new Button("Enviar")
         .onAction(c -> saida.setText("Olá, " + nome.getText() + "!"));
      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
TextField nome = new TextField().caption("Nome");
container.add(LAY_AUTO, nome);
// ...
String valor = nome.getText();
```

## Constructores
| Constructor | Descripción |
|---|---|
| `TextField()` | vacío |
| `TextField(String text)` | con texto inicial (`null` = vacío) |

## `EditMode` (modo de edición)
| `EditMode` | Teclado por defecto | Observaciones |
|---|---|---|
| `NORMAL` | `DEFAULT` | texto común; puede tener máscara personalizada |
| `DATE` | `DATE` | abre `DatePicker`; máscara de fecha |
| `CURRENCY` | `CALCULATOR` | moneda/decimal, máscara de derecha a izquierda |
| `NUMERIC` | `NUMERIC` | numérico (en desktop cae a `KEYPAD`) |
| `TIME` | `TIME` | abre `TimePicker` |
| `PASSWORD` | `DEFAULT` | muestra `passwordChar` en lugar de los caracteres |

`setMode(mode)` o `setMode(mode, maskedEdit)` definen el modo; `maskedEdit=true` activa el auto-formateo por máscara.

## Propiedades y personalización
| Propiedad / método | Tipo | Por defecto | Qué hace |
|---|---|---|---|
| `text(String)` / `setText(String)` | `TextField` / `void` | — | define el texto (normaliza máscara/moneda; cursor al final) |
| `getText()` | `String` | — | texto mostrado (**con** máscara, si está activa) |
| `getTextWithoutMask()` | `String` | — | valor "limpio" (solo dígitos / decimal formateado) |
| `caption(String)` | `TextField` (fluido) | `null` | caption flotante / placeholder |
| `setMode(EditMode[, boolean])` | `void` | `NORMAL` | modo de edición (+ máscara) |
| `keyboard(Keyboard)` / `setKeyboard(Keyboard)` | `TextField` / `void` | por `EditMode` | tipo de teclado |
| `leftIcon(Icon[, Action])` / `rightIcon(Icon[, Action])` | `TextField` (fluido) | `null` | íconos (con acción opcional) |
| `leftIconAction(Action)` / `rightIconAction(Action)` | `TextField` (fluido) | — | acción de ícono (separada) |
| `isPassword()` | `boolean` | — | ¿está en modo contraseña? |
| `clearText()` | `void` | — | limpia texto, máscara y caché |
| estilo `.mask(String)` | `char[]` | — | máscara (`9`=dígito, resto literal): `"99/99/9999"` |
| estilo `.validChars(String)` | `String` | `null` | caracteres permitidos |
| estilo `.maxLength(int)` | `int` | `0` (ilimitado) | límite de caracteres |
| estilo `.passwordToggle(true)` | `boolean` | `false` | crea el ícono "ojo" mostrar/ocultar |
| estilo `.floatingCaption(true)` | `boolean` | — | el caption sube al enfocar/rellenar |
| `getDefaultCurrencyMask(int)` *(static)* | `char[]` | — | máscara de moneda (separadores de `LocaleFormats`) |

## 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 |
| `leftIconAction` / `rightIconAction` | al tocar (y soltar sobre) el ícono correspondiente |

No hay un `onChange` dedicado — reacciona vía acción o consulta `getText()`/`getTextWithoutMask()`.

## Estados
- **focused:** estilo `textFieldFocused(...)`, cursor parpadeando, el teclado/picker abre al toque; el caption sube si `floatingCaption`.
- **password:** con `passwordToggle`, el ícono derecho alterna `PASSWORD`↔`NORMAL` (mostrar/ocultar), actualizando los estilos default y focused.
- **disabled / read-only:** `setEnabled(false)` o `editable=false` impiden la edición.

### Campo de contraseña con toggle mostrar/ocultar
```java
TextField senha = new TextField().caption("Senha");
senha.setMode(EditMode.PASSWORD);
senha.style(Styles.theme()
   .field(FieldRole.PASSWORD, State.NORMAL)
   .passwordToggle(true));   // crea el ícono "ojo" automáticamente
container.add(LAY_AUTO, senha);
```

### Fecha con máscara + DatePicker
```java
TextField data = new TextField().caption("Data");
data.setMode(EditMode.DATE, true);          // maskedEdit = true
data.style(Styles.theme()
   .field(FieldRole.NORMAL, State.NORMAL)
   .mask("99/99/9999"));
// Tocar abre el DatePicker; al seleccionar, el campo se rellena.
container.add(LAY_AUTO, data);
```

### Código postal con validación e ícono de limpiar
```java
TextField cep = new TextField().caption("CEP");
cep.style(Styles.theme()
   .field(FieldRole.NORMAL, State.NORMAL)
   .mask("99999-999")
   .validChars("0123456789"));
cep.rightIcon(new MaterialIcon(""), c -> cep.clearText());  // "close"
container.add(LAY_AUTO, cep);

String limpo = cep.getTextWithoutMask();   // solo los 8 dígitos
```

## Buenas prácticas
- Para máscara, activa `maskedEdit` (`setMode(mode, true)`) y define la `mask` en el estilo — solo definir la máscara sin `maskedEdit` no auto-formatea.
- Usa `getTextWithoutMask()` al persistir/validar (dígitos limpios) y `getText()` al mostrar.
- Prefiere el `EditMode`/`Keyboard` correcto para abrir el teclado ideal en cada campo.

## Errores comunes
- **Definir `mask` pero olvidar `maskedEdit`:** el texto no se formatea. Usa `setMode(mode, true)`.
- **Leer `getText()` esperando dígitos limpios:** retorna **con** separadores; usa `getTextWithoutMask()`.
- **`passwordToggle` sin modo contraseña:** el toggle ya implica contraseña por defecto, pero confirma `EditMode.PASSWORD` / `FieldRole.PASSWORD` para el visual correcto.

## Véase también
- Guía del área: [`ui/`](/docs/ui) · Recetas: [`cookbook/`](/docs/cookbook)
- Concepto (máscara/validación/foco — profundización): [`concepts/`](/docs/concepts)
- Componentes relacionados: [`TextInput`](/docs/ui/widgets/textinput) · [`TextArea`](/docs/ui/widgets/textarea) · [`Keypad`](/docs/ui/widgets/keypad)
