# Foco e validação

> Controle de foco (focusOnTouch/focusOnKey), editável, limites e validação ao enviar.

Source: https://crossfly.dev/pt/docs/ui/forms-and-input/focus-and-validation — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Foco e validação

Como **não há evento por tecla**, a validação no CrossFly acontece **ao enviar** (ou ao perder o
foco): você lê `getText()`, decide, e sinaliza o erro.

## 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 comportamental)

/** 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 nome  = new TextField().caption("Nome");
      add(LAY_AUTO, nome);

      TextField email = new TextField().caption("E-mail");
      add(LAY_AUTO, email);

      // Campo só-leitura: mostra valor, não abre teclado nem foca ao toque
      TextField codigo = new TextField("ABC-123").caption("Código");
      codigo.<TextFieldStyle>getStyle().editable(false);
      add(LAY_AUTO, codigo);

      // Validação ACONTECE NO ENVIO (não há evento por tecla):
      // campo vazio → Hint.show(...) + devolve o foco ao campo com erro.
      Button enviar = new Button("Cadastrar");
      enviar.onAction(c -> {
         String n = nome.getText().trim();
         String e = email.getText().trim();
         if (n.isEmpty())         { Hint.show("Informe o nome", 2000);  nome.setFocused(true);  return; }
         if (e.indexOf('@') < 0)  { Hint.show("E-mail inválido", 2000); email.setFocused(true); return; }
         Hint.show("Cadastrado: " + n, 2000);
      });
      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.

## Foco
| Propriedade (estilo) / método | Efeito |
|---|---|
| `focusOnTouch` (estilo) | tocar no campo dá foco (padrão nos estilos de campo) |
| `focusOnKey` (estilo) | teclar com o pai focado move o foco para o campo |
| `onFocus()` / `onBlur()` | ganchos chamados ao ganhar / perder o foco (sobrescreva numa subclasse) |
| `hasFocus()` | consulta se o campo está focado |
| `restoreFocusAtEnd()` | reposiciona o cursor no fim (após preencher via picker) |

```java
// campo só-leitura (mostra valor, não abre teclado nem foca ao toque):
saldo.<TextFieldStyle>getStyle().editable(false);
```

## Limites de entrada
```java
telefone.<TextFieldStyle>getStyle()
   .maxLength(11)                    // no máximo 11 caracteres
   .capitalise(Capitalise.UPPER);   // força maiúsculas (Capitalise: NORMAL/UPPER/LOWER)
```

## Validar ao enviar (padrão recomendado)
```java
Button enviar = new Button("Cadastrar");
enviar.onAction(c -> {
   String nome  = nomeField.getText().trim();
   String email = emailField.getText().trim();
   if (nome.isEmpty()) { Hint.show("Informe o nome", 2000); nomeField.setFocused(true); return; }
   if (email.indexOf('@') < 0) { Hint.show("E-mail inválido", 2000); emailField.setFocused(true); return; }
   salvar(nome, email);
});
```
- `Hint.show(...)` para avisos não bloqueantes; `Alert` para confirmações (ver
  [Diálogos e modais](/docs/ui/navigation/dialogs-and-modals)).
- Devolva o foco ao campo com erro para o usuário corrigir.

## Boas práticas
- Valide **na hora do envio** lendo `getText()`/`getTextWithoutMask()` — não tente interceptar cada tecla.
- Use `editable(false)` para campos calculados/derivados em vez de "desabilitar e forçar leitura".
- Restrinja a entrada na origem (`mask`, `maxLength`, `EditMode.NUMERIC`) para reduzir validação.

## Erros comuns
- Procurar um listener de mudança por tecla — não existe; valide ao enviar/ao sair do campo.
- Esquecer de dar foco de volta ao campo inválido, deixando o usuário perdido.
- Validar `getText()` (com máscara) quando a regra é sobre os dígitos → use `getTextWithoutMask()`.

## Veja também
- [Campos de texto](/docs/ui/forms-and-input/text-fields) · [Máscaras e teclados](/docs/ui/forms-and-input/masks-and-keyboards) · [Diálogos e modais](/docs/ui/navigation/dialogs-and-modals)
