# Masks and keyboards

> EditMode (date/time/currency), mask with '9', native keyboards and the built-in Keypad.

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

# Masks and keyboards

The field's `EditMode` controls **two things at once**: the **formatting** (mask) and the
**keyboard** that opens. It is the central piece of structured input.

## `EditMode` → keyboard
| `EditMode` | Formatting | Keyboard that opens |
|---|---|---|
| `NORMAL` | none | OS default keyboard |
| `NUMERIC` | digits | native numeric (`Keypad` on desktop) |
| `DATE` | date mask | **DatePicker** |
| `TIME` | time mask | **TimePicker** |
| `CURRENCY` | currency (separators) | **Keypad** calculator |
| `PASSWORD` | hides characters | default keyboard |

## 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());

      // Date field: enabled mask opens the DatePicker on tap
      TextField data = new TextField().caption("Data");
      data.setMode(EditMode.DATE, true);      // maskedEdit=true → formats and opens DatePicker
      add(LAY_AUTO, data);

      // Currency field: formats as currency while typing
      TextField valor = new TextField().caption("Valor");
      valor.setMode(EditMode.CURRENCY, true);
      valor.<TextFieldStyle>getStyle().decimalPlaces(2);
      add(LAY_AUTO, valor);

      // Label where the read values will be displayed
      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      // There is NO per-key callback: read getText() on submit.
      // getTextWithoutMask() returns only the digits (good for saving to the database).
      Button enviar = new Button("Enviar");
      enviar.onAction(c -> resultado.setText(
         "Data = " + data.getText() + "  |  Valor = " + valor.getTextWithoutMask()));
      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.

## Enable the automatic mask
`setMode(mode, true)` turns on real-time formatting (`maskedEdit`):
```java
TextField data = new TextField().caption("Data");
data.setMode(EditMode.DATE, true);         // while typing, becomes 15/03/2024 and opens the DatePicker

TextField valor = new TextField().caption("Valor");
valor.setMode(EditMode.CURRENCY, true);    // formats as currency while typing
valor.<TextFieldStyle>getStyle().decimalPlaces(2);
```

## Custom mask (the `'9'` is the digit)
In a mask, each `'9'` is a digit position; the other characters are fixed literals:
```java
TextField cpf = new TextField().caption("CPF");
cpf.<TextFieldStyle>getStyle()
   .mask("999.999.999-99".toCharArray())    // 11 digits + separators
   .mode(EditMode.NUMERIC, true);
// getText()          -> "123.456.789-00"  (with mask)
// getTextWithoutMask()-> "12345678900"    (digits only)
```
Ready-made currency: `TextField.getDefaultCurrencyMask(int casasDecimais)` generates the mask with the locale's
thousands/decimal separators.

## Choose the keyboard explicitly
```java
campo.keyboard(Keyboard.NUMERIC);   // forces numeric
campo.keyboard(Keyboard.NONE);      // never opens keyboard (visually read-only field)
```
`Keyboard` (in `UIConstants`): `NONE, DEFAULT, NUMERIC, KEYPAD, CALCULATOR, DATE, TIME`. `DEFAULT`
adopts the keyboard implied by the current `EditMode`.

## Built-in Keypad
`Keyboard.KEYPAD`/`CALCULATOR` use CrossFly's own **`Keypad`** component (not the OS keyboard) —
useful in kiosks/POS. It is opened automatically by the field; to use it standalone:
```java
new Keypad(meuCampo).show();        // numeric keyboard bound to the field
```

## Best practices
- Save the **`getTextWithoutMask()`** (raw digits) to the database; display it with the mask.
- Combine `mask(...)` with `mode(EditMode.NUMERIC, true)` for documents (CPF, ZIP code, phone).
- On desktop, `NUMERIC` falls back to the `Keypad` automatically — don't assume a native keyboard.

## Common mistakes
- Turning on `mode(EditMode.DATE)` **without** `maskedEdit=true` → no automatic formatting.
- Persisting `getText()` (with dots/dashes) when the back end expects only digits.
- A mask with no `'9'` → no digit is accepted.

## See also
- [Text fields](/docs/ui/forms-and-input/text-fields) · Catalog: [DatePicker](/docs/ui/widgets/datepicker) · [TimePicker](/docs/ui/widgets/timepicker) · [Keypad](/docs/ui/widgets/keypad)
