# Como validar um campo de texto

> Restringir caracteres, limitar tamanho e ler o valor limpo antes de submeter.

Source: https://crossfly.dev/pt/docs/cookbook/forms/validar-campo — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Como validar um campo de texto

**Propósito.** Garantir que o usuário só consiga digitar caracteres válidos, limitar o número de caracteres e obter o valor sem máscara na hora do envio. No CrossFly a validação de entrada é declarativa (feita no estilo do campo), e a validação de negócio é feita ao submeter.

## APIs usadas
- `campo.<TextFieldStyle>getStyle().validChars("0123456789")` — filtra os caracteres aceitos. `null` (padrão) aceita tudo. A comparação é feita em MAIÚSCULO: internamente `isCharValid(c)` faz `validChars.indexOf(Character.toUpperCase(c))`, então informe as letras em maiúsculo mesmo que o campo aceite minúsculas.
- `campo.<TextFieldStyle>getStyle().maxLength(11)` — limita o total de caracteres (`0` = ilimitado). Vale para digitação e para colar (`clipboardPaste`).
- `campo.<TextFieldStyle>getStyle().capitalise(Capitalise.UPPER)` — força MAIÚSCULO/minúsculo (`UPPER`, `LOWER`, `NORMAL`).
- `TextField.getText()` — texto como exibido (com máscara, se houver).
- `TextField.getTextWithoutMask()` — só os dígitos/conteúdo bruto (ex.: `"12345678900"` em vez de `"123.456.789-00"`). Use este no envio.
- `TextInput.mapKeys(from, to)` — remapeia teclas (ex.: substituir vírgula por ponto). `from` e `to` devem ter o mesmo tamanho.

> Não há callback por tecla no CrossFly. Não existe `onTextChanged` — você lê `getText()`/`getTextWithoutMask()` no momento do envio (ou dentro do `onAction` do botão).

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

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // TextFieldStyle (config no estilo)

/** 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());

      TextField cpf = new TextField("");
      cpf.caption("CPF");
      cpf.<TextFieldStyle>getStyle()
         .validChars("0123456789")   // só dígitos
         .maxLength(11);             // 11 dígitos

      TextField apelido = new TextField("");
      apelido.caption("Apelido");
      apelido.<TextFieldStyle>getStyle()
         .capitalise(Capitalise.LOWER)
         .maxLength(20);

      Label resultado = new Label("");

      Button enviar = new Button("Enviar");
      enviar.onAction(origin ->
      {
         String cpfLimpo = cpf.getTextWithoutMask();   // "12345678900"
         if (cpfLimpo.length() != 11)
         {
            Hint.show("CPF deve ter 11 dígitos", 2000);
            return;
         }
         resultado.setText("CPF: " + cpfLimpo + " · apelido: " + apelido.getText());
      });

      add(LAY_FILL, cpf);
      add(LAY_FILL, apelido);
      add(LAY_AUTO, enviar);
      add(LAY_FILL, resultado);
   }
}
```

> O trecho abaixo mostra **só o miolo**. Para rodar, coloque-o no esqueleto `MainWindow` acima.

## Trecho mínimo
```java
import dev.crossfly.ui.*;
import dev.crossfly.ui.style.*;
import static dev.crossfly.ui.UIConstants.Capitalise;

TextField cpf = new TextField("");
cpf.caption("CPF");
cpf.<TextFieldStyle>getStyle()
   .validChars("0123456789")   // só dígitos
   .maxLength(11);             // 11 dígitos

TextField apelido = new TextField("");
apelido.caption("Apelido");
apelido.<TextFieldStyle>getStyle()
   .capitalise(Capitalise.LOWER)
   .maxLength(20);

Button enviar = new Button("Enviar");
enviar.onAction(origin ->
{
   String cpfLimpo = cpf.getTextWithoutMask();   // "12345678900"
   if (cpfLimpo.length() != 11)
   {
      new Alert("Erro", "CPF deve ter 11 dígitos").show();
      return;
   }
   // ... prossegue com o envio
});
```

## Aprofunde-se
- [Máscaras e teclados](/docs/ui/forms-and-input/masks-and-keyboards) · [Foco e validação](/docs/ui/forms-and-input/focus-and-validation)
- Receitas irmãs: [Máscara de CPF/CEP/data](/docs/cookbook/forms/mascara-cpf) · [Campo de senha](/docs/cookbook/forms/campo-de-senha)
- Fonte canônica: Capítulo 14 — Widgets básicos · [Enums (UIConstants)](/docs/reference/enums-uiconstants)
