# Foco y validación

> Control de foco (focusOnTouch/focusOnKey), editable, límites y validación al enviar.

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

# Foco y validación

Como **no hay evento por tecla**, la validación en CrossFly ocurre **al enviar** (o al perder el
foco): lees `getText()`, decides y señalas el error.

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

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // TextFieldStyle (config de comportamiento)

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

      TextField nome  = new TextField().caption("Nome");
      add(LAY_AUTO, nome);

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

      // Campo de solo lectura: muestra valor, no abre teclado ni enfoca al tocar
      TextField codigo = new TextField("ABC-123").caption("Código");
      codigo.<TextFieldStyle>getStyle().editable(false);
      add(LAY_AUTO, codigo);

      // La validación OCURRE AL ENVIAR (no hay evento por tecla):
      // campo vacío → Hint.show(...) + devuelve el foco al campo con error.
      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);
   }
}
```

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

## Foco
| Propiedad (estilo) / método | Efecto |
|---|---|
| `focusOnTouch` (estilo) | tocar el campo da foco (predeterminado en los estilos de campo) |
| `focusOnKey` (estilo) | pulsar una tecla con el padre enfocado mueve el foco al campo |
| `onFocus()` / `onBlur()` | ganchos llamados al ganar / perder el foco (sobrescribe en una subclase) |
| `hasFocus()` | consulta si el campo está enfocado |
| `restoreFocusAtEnd()` | reposiciona el cursor al final (tras llenar vía picker) |

```java
// campo de solo lectura (muestra valor, no abre teclado ni enfoca al tocar):
saldo.<TextFieldStyle>getStyle().editable(false);
```

## Límites de entrada
```java
telefone.<TextFieldStyle>getStyle()
   .maxLength(11)                    // como máximo 11 caracteres
   .capitalise(Capitalise.UPPER);   // fuerza mayúsculas (Capitalise: NORMAL/UPPER/LOWER)
```

## Validar al enviar (patrón 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 no bloqueantes; `Alert` para confirmaciones (ver
  [Diálogos y modales](/docs/ui/navigation/dialogs-and-modals)).
- Devuelve el foco al campo con error para que el usuario corrija.

## Buenas prácticas
- Valida **en el momento del envío** leyendo `getText()`/`getTextWithoutMask()` — no intentes interceptar cada tecla.
- Usa `editable(false)` para campos calculados/derivados en lugar de "deshabilitar y forzar la lectura".
- Restringe la entrada en el origen (`mask`, `maxLength`, `EditMode.NUMERIC`) para reducir la validación.

## Errores comunes
- Buscar un listener de cambio por tecla — no existe; valida al enviar/al salir del campo.
- Olvidar dar el foco de vuelta al campo inválido, dejando al usuario perdido.
- Validar `getText()` (con máscara) cuando la regla es sobre los dígitos → usa `getTextWithoutMask()`.

## Ver también
- [Campos de texto](/docs/ui/forms-and-input/text-fields) · [Máscaras y teclados](/docs/ui/forms-and-input/masks-and-keyboards) · [Diálogos y modales](/docs/ui/navigation/dialogs-and-modals)
