# TextField

> Single-line text field with mask, EditMode, password mode, clickable icons and a floating caption.

Source: https://crossfly.dev/docs/ui/widgets/textfield — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# TextField

`TextField` is the **single-line** text input. On top of the [`TextInput`](/docs/ui/widgets/textinput) base, it adds: a **mask** engine (date, currency, custom), **password mode** (with a show/hide toggle), clickable left/right icons, horizontal scrolling and an optional **floating caption**. The input behavior is defined by [`EditMode`](/docs/reference) (NORMAL/DATE/CURRENCY/NUMERIC/TIME/PASSWORD), which also chooses the default keyboard.

> 🟡 · **Package:** `dev.crossfly.ui` · **Extends:** `TextInput` · **Style:** `TextFieldStyle` (distinct default and focused)

## When to use
- Any **single-line** input: name, email, password, numeric/monetary values, date, time.
- Fields with a mask (ZIP code, phone, date) or with a clickable icon (clear, search, password eye).

## When NOT to use
- **Multi-line** text (notes, comments) → use [`TextArea`](/docs/ui/widgets/textarea).
- Choice among fixed options → use `Dropdown` / `Option` (see [`ui/`](/docs/ui)).

## Appearance
Box with background/border from the `TextFieldStyle`, text on a single line, optional icons at the ends and, optionally, a **floating caption** (`floatingCaption`) that rises above the field when it has focus or content.

``

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

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

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

      // Single-line field with floating caption
      TextField nome = new TextField().caption("Nome");
      add(LAY_AUTO, nome);

      // Output label
      Label saida = new Label("Digite seu nome e toque em Enviar");
      add(LAY_AUTO, saida);

      // There is no onChange per key: read getText() on submit
      Button enviar = new Button("Enviar")
         .onAction(c -> saida.setText("Olá, " + nome.getText() + "!"));
      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.

## Basic usage
```java
TextField nome = new TextField().caption("Nome");
container.add(LAY_AUTO, nome);
// ...
String valor = nome.getText();
```

## Constructors
| Constructor | Description |
|---|---|
| `TextField()` | empty |
| `TextField(String text)` | with initial text (`null` = empty) |

## `EditMode` (edit mode)
| `EditMode` | Default keyboard | Notes |
|---|---|---|
| `NORMAL` | `DEFAULT` | plain text; may have a custom mask |
| `DATE` | `DATE` | opens `DatePicker`; date mask |
| `CURRENCY` | `CALCULATOR` | currency/decimal, right-to-left mask |
| `NUMERIC` | `NUMERIC` | numeric (on desktop falls back to `KEYPAD`) |
| `TIME` | `TIME` | opens `TimePicker` |
| `PASSWORD` | `DEFAULT` | displays `passwordChar` in place of the characters |

`setMode(mode)` or `setMode(mode, maskedEdit)` set the mode; `maskedEdit=true` enables mask auto-formatting.

## Properties and customization
| Property / method | Type | Default | What it does |
|---|---|---|---|
| `text(String)` / `setText(String)` | `TextField` / `void` | — | sets the text (normalizes mask/currency; cursor at end) |
| `getText()` | `String` | — | displayed text (**with** mask, if active) |
| `getTextWithoutMask()` | `String` | — | "clean" value (digits only / formatted decimal) |
| `caption(String)` | `TextField` (fluent) | `null` | floating caption / placeholder |
| `setMode(EditMode[, boolean])` | `void` | `NORMAL` | edit mode (+ mask) |
| `keyboard(Keyboard)` / `setKeyboard(Keyboard)` | `TextField` / `void` | by `EditMode` | keyboard type |
| `leftIcon(Icon[, Action])` / `rightIcon(Icon[, Action])` | `TextField` (fluent) | `null` | icons (with optional action) |
| `leftIconAction(Action)` / `rightIconAction(Action)` | `TextField` (fluent) | — | icon action (separate) |
| `isPassword()` | `boolean` | — | is it in password mode? |
| `clearText()` | `void` | — | clears text, mask and cache |
| style `.mask(String)` | `char[]` | — | mask (`9`=digit, rest literal): `"99/99/9999"` |
| style `.validChars(String)` | `String` | `null` | allowed characters |
| style `.maxLength(int)` | `int` | `0` (unlimited) | character limit |
| style `.passwordToggle(true)` | `boolean` | `false` | creates the show/hide "eye" icon |
| style `.floatingCaption(true)` | `boolean` | — | caption rises on focus/fill |
| `getDefaultCurrencyMask(int)` *(static)* | `char[]` | — | currency mask (separators from `LocaleFormats`) |

## Callbacks and events
| Callback | When it fires |
|---|---|
| `onAction(Action)` (inherited) | on `onTouchUp`, after opening the keyboard |
| `onFocus()` / `onBlur()` (inherited) | focus gained / lost |
| `leftIconAction` / `rightIconAction` | on tapping (and releasing over) the corresponding icon |

There is no dedicated `onChange` — react via the action or query `getText()`/`getTextWithoutMask()`.

## States
- **focused:** `textFieldFocused(...)` style, blinking cursor, keyboard/picker opens on tap; caption rises if `floatingCaption`.
- **password:** with `passwordToggle`, the right icon toggles `PASSWORD`↔`NORMAL` (show/hide), updating the default and focused styles.
- **disabled / read-only:** `setEnabled(false)` or `editable=false` prevent editing.

### Password field with a show/hide toggle
```java
TextField senha = new TextField().caption("Senha");
senha.setMode(EditMode.PASSWORD);
senha.style(Styles.theme()
   .field(FieldRole.PASSWORD, State.NORMAL)
   .passwordToggle(true));   // creates the "eye" icon automatically
container.add(LAY_AUTO, senha);
```

### Date with mask + DatePicker
```java
TextField data = new TextField().caption("Data");
data.setMode(EditMode.DATE, true);          // maskedEdit = true
data.style(Styles.theme()
   .field(FieldRole.NORMAL, State.NORMAL)
   .mask("99/99/9999"));
// Tapping opens the DatePicker; on selection, the field is filled.
container.add(LAY_AUTO, data);
```

### ZIP code with validation and a clear icon
```java
TextField cep = new TextField().caption("CEP");
cep.style(Styles.theme()
   .field(FieldRole.NORMAL, State.NORMAL)
   .mask("99999-999")
   .validChars("0123456789"));
cep.rightIcon(new MaterialIcon(""), c -> cep.clearText());  // "close"
container.add(LAY_AUTO, cep);

String limpo = cep.getTextWithoutMask();   // only the 8 digits
```

## Best practices
- For a mask, enable `maskedEdit` (`setMode(mode, true)`) and set the `mask` in the style — setting the mask alone without `maskedEdit` does not auto-format.
- Use `getTextWithoutMask()` when persisting/validating (clean digits) and `getText()` when displaying.
- Prefer the correct `EditMode`/`Keyboard` to open the ideal keyboard for each field.

## Common mistakes
- **Setting a `mask` but forgetting `maskedEdit`:** the text is not formatted. Use `setMode(mode, true)`.
- **Reading `getText()` expecting clean digits:** it returns **with** separators; use `getTextWithoutMask()`.
- **`passwordToggle` without password mode:** the toggle already implies password by default, but confirm `EditMode.PASSWORD` / `FieldRole.PASSWORD` for the correct look.

## See also
- Area guide: [`ui/`](/docs/ui) · Recipes: [`cookbook/`](/docs/cookbook)
- Concept (mask/validation/focus — in depth): [`concepts/`](/docs/concepts)
- Related components: [`TextInput`](/docs/ui/widgets/textinput) · [`TextArea`](/docs/ui/widgets/textarea) · [`Keypad`](/docs/ui/widgets/keypad)
