# TextField

> Campo de texto de uma linha com máscara, EditMode, modo senha, ícones clicáveis e caption flutuante.

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

# TextField

`TextField` é a entrada de texto **de uma linha**. Sobre a base [`TextInput`](/docs/ui/widgets/textinput), adiciona: motor de **máscara** (data, moeda, customizada), **modo senha** (com toggle mostrar/ocultar), ícones esquerdo/direito clicáveis, rolagem horizontal e um **caption flutuante** opcional. O comportamento de entrada é definido por [`EditMode`](/docs/reference) (NORMAL/DATE/CURRENCY/NUMERIC/TIME/PASSWORD), que também escolhe o teclado padrão.

> 🟡 · **Pacote:** `dev.crossfly.ui` · **Estende:** `TextInput` · **Estilo:** `TextFieldStyle` (default e focused distintos)

## Quando usar
- Qualquer entrada de **uma linha**: nome, e-mail, senha, valores numéricos/monetários, data, hora.
- Campos com máscara (CEP, telefone, data) ou com ícone clicável (limpar, buscar, olho de senha).

## Quando NÃO usar
- Texto de **múltiplas linhas** (observações, comentários) → use [`TextArea`](/docs/ui/widgets/textarea).
- Escolha entre opções fixas → use `Dropdown` / `Option` (ver [`ui/`](/docs/ui)).

## Aparência
Caixa com fundo/borda do `TextFieldStyle`, texto numa linha, ícones opcionais nas pontas e, opcionalmente, um **caption flutuante** (`floatingCaption`) que sobe acima do campo quando há foco ou conteúdo.

``

## Exemplo completo (copiar, colar e rodar)
```java
package com.exemplo;

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

/** Tela principal executável. Aponte o launcher (classname) e rode: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // Campo de uma linha com caption flutuante
      TextField nome = new TextField().caption("Nome");
      add(LAY_AUTO, nome);

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

      // Não há onChange por tecla: leia getText() no envio
      Button enviar = new Button("Enviar")
         .onAction(c -> saida.setText("Olá, " + nome.getText() + "!"));
      add(LAY_AUTO, enviar);
   }
}
```

> Os trechos abaixo mostram **só o miolo** (o que vai dentro de `onShow()`). Para rodar, coloque-os no esqueleto `MainWindow` acima.

## Uso básico
```java
TextField nome = new TextField().caption("Nome");
container.add(LAY_AUTO, nome);
// ...
String valor = nome.getText();
```

## Construtores
| Construtor | Descrição |
|---|---|
| `TextField()` | vazio |
| `TextField(String text)` | com texto inicial (`null` = vazio) |

## `EditMode` (modo de edição)
| `EditMode` | Teclado padrão | Observações |
|---|---|---|
| `NORMAL` | `DEFAULT` | texto comum; pode ter máscara customizada |
| `DATE` | `DATE` | abre `DatePicker`; máscara de data |
| `CURRENCY` | `CALCULATOR` | moeda/decimal, máscara da direita p/ esquerda |
| `NUMERIC` | `NUMERIC` | numérico (no desktop cai para `KEYPAD`) |
| `TIME` | `TIME` | abre `TimePicker` |
| `PASSWORD` | `DEFAULT` | exibe `passwordChar` no lugar dos caracteres |

`setMode(mode)` ou `setMode(mode, maskedEdit)` definem o modo; `maskedEdit=true` ativa a auto-formatação por máscara.

## Propriedades e personalização
| Propriedade / método | Tipo | Padrão | O que faz |
|---|---|---|---|
| `text(String)` / `setText(String)` | `TextField` / `void` | — | define o texto (normaliza máscara/moeda; cursor ao fim) |
| `getText()` | `String` | — | texto exibido (**com** máscara, se ativa) |
| `getTextWithoutMask()` | `String` | — | valor "limpo" (só dígitos / decimal formatado) |
| `caption(String)` | `TextField` (fluente) | `null` | caption flutuante / placeholder |
| `setMode(EditMode[, boolean])` | `void` | `NORMAL` | modo de edição (+ máscara) |
| `keyboard(Keyboard)` / `setKeyboard(Keyboard)` | `TextField` / `void` | por `EditMode` | tipo de teclado |
| `leftIcon(Icon[, Action])` / `rightIcon(Icon[, Action])` | `TextField` (fluente) | `null` | ícones (com ação opcional) |
| `leftIconAction(Action)` / `rightIconAction(Action)` | `TextField` (fluente) | — | ação de ícone (separada) |
| `isPassword()` | `boolean` | — | está em modo senha? |
| `clearText()` | `void` | — | limpa texto, máscara e cache |
| 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) | limite de caracteres |
| estilo `.passwordToggle(true)` | `boolean` | `false` | cria o ícone "olho" mostrar/ocultar |
| estilo `.floatingCaption(true)` | `boolean` | — | caption sobe ao focar/preencher |
| `getDefaultCurrencyMask(int)` *(static)* | `char[]` | — | máscara de moeda (separadores de `LocaleFormats`) |

## Callbacks e eventos
| Callback | Quando dispara |
|---|---|
| `onAction(Action)` (herdado) | em `onTouchUp`, após abrir o teclado |
| `onFocus()` / `onBlur()` (herdados) | ganho / perda de foco |
| `leftIconAction` / `rightIconAction` | ao tocar (e soltar sobre) o ícone correspondente |

Não há `onChange` dedicado — reaja via ação ou consulte `getText()`/`getTextWithoutMask()`.

## Estados
- **focused:** estilo `textFieldFocused(...)`, cursor piscando, teclado/picker abre no toque; caption sobe se `floatingCaption`.
- **password:** com `passwordToggle`, o ícone direito alterna `PASSWORD`↔`NORMAL` (mostrar/ocultar), atualizando os estilos default e focused.
- **disabled / read-only:** `setEnabled(false)` ou `editable=false` impedem edição.

### Campo de senha com 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));   // cria o ícone "olho" automaticamente
container.add(LAY_AUTO, senha);
```

### Data com 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 o DatePicker; ao selecionar, o campo é preenchido.
container.add(LAY_AUTO, data);
```

### CEP com validação e ícone de limpar
```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();   // só os 8 dígitos
```

## Boas práticas
- Para máscara, ative `maskedEdit` (`setMode(mode, true)`) e defina a `mask` no estilo — só definir a máscara sem `maskedEdit` não auto-formata.
- Use `getTextWithoutMask()` ao persistir/validar (dígitos limpos) e `getText()` ao exibir.
- Prefira `EditMode`/`Keyboard` corretos para abrir o teclado ideal em cada campo.

## Erros comuns
- **Definir `mask` mas esquecer `maskedEdit`:** o texto não é formatado. Use `setMode(mode, true)`.
- **Ler `getText()` esperando dígitos limpos:** ele retorna **com** separadores; use `getTextWithoutMask()`.
- **`passwordToggle` sem modo senha:** o toggle já implica senha por padrão, mas confirme `EditMode.PASSWORD` / `FieldRole.PASSWORD` para o visual correto.

## Veja também
- Guia da área: [`ui/`](/docs/ui) · Receitas: [`cookbook/`](/docs/cookbook)
- Conceito (máscara/validação/foco — aprofundamento): [`concepts/`](/docs/concepts)
- Componentes relacionados: [`TextInput`](/docs/ui/widgets/textinput) · [`TextArea`](/docs/ui/widgets/textarea) · [`Keypad`](/docs/ui/widgets/keypad)
