# Focus and validation

> Focus control (focusOnTouch/focusOnKey), editable, limits and validation on submit.

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

# Focus and validation

Since there is **no per-key event**, validation in CrossFly happens **on submit** (or when focus is
lost): you read `getText()`, decide, and flag the error.

## Complete example (copy, paste and run)
```java
package com.exemplo;

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

/** Runnable main screen. Point the launcher (classname) and run: 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);

      // Read-only field: shows value, does not open keyboard or focus on tap
      TextField codigo = new TextField("ABC-123").caption("Código");
      codigo.<TextFieldStyle>getStyle().editable(false);
      add(LAY_AUTO, codigo);

      // Validation HAPPENS ON SUBMIT (there is no per-key event):
      // empty field → Hint.show(...) + returns focus to the field with the 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);
   }
}
```

> The snippets below show **only the core** (what goes inside `onShow()`). To run them, place them in the `MainWindow` skeleton above.

## Focus
| Property (style) / method | Effect |
|---|---|
| `focusOnTouch` (style) | touching the field gives it focus (default in field styles) |
| `focusOnKey` (style) | typing with the parent focused moves focus to the field |
| `onFocus()` / `onBlur()` | hooks called on gaining / losing focus (override in a subclass) |
| `hasFocus()` | queries whether the field is focused |
| `restoreFocusAtEnd()` | repositions the cursor at the end (after filling via a picker) |

```java
// read-only field (shows value, does not open keyboard or focus on tap):
saldo.<TextFieldStyle>getStyle().editable(false);
```

## Input limits
```java
telefone.<TextFieldStyle>getStyle()
   .maxLength(11)                    // at most 11 characters
   .capitalise(Capitalise.UPPER);   // forces uppercase (Capitalise: NORMAL/UPPER/LOWER)
```

## Validate on submit (recommended pattern)
```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(...)` for non-blocking notices; `Alert` for confirmations (see
  [Dialogs and modals](/docs/ui/navigation/dialogs-and-modals)).
- Return focus to the field with the error so the user can correct it.

## Best practices
- Validate **at submit time** reading `getText()`/`getTextWithoutMask()` — don't try to intercept every key.
- Use `editable(false)` for calculated/derived fields instead of "disabling and forcing read-only".
- Restrict input at the source (`mask`, `maxLength`, `EditMode.NUMERIC`) to reduce validation.

## Common mistakes
- Looking for a per-key change listener — it doesn't exist; validate on submit/on leaving the field.
- Forgetting to return focus to the invalid field, leaving the user lost.
- Validating `getText()` (with mask) when the rule is about the digits → use `getTextWithoutMask()`.

## See also
- [Text fields](/docs/ui/forms-and-input/text-fields) · [Masks and keyboards](/docs/ui/forms-and-input/masks-and-keyboards) · [Dialogs and modals](/docs/ui/navigation/dialogs-and-modals)
