Skip to content
CrossFly is 100% open sourceFollow us on GitHub
CrossFlyCrossFly
Get started

This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.

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)

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

Input limits

telefone.<TextFieldStyle>getStyle()
   .maxLength(11)                    // at most 11 characters
   .capitalise(Capitalise.UPPER);   // forces uppercase (Capitalise: NORMAL/UPPER/LOWER)
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).
  • 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

Was this page helpful?