# TextInput

> Abstract base for text input — cursor, selection, clipboard, focus and keyboard, common to TextField and TextArea.

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

# TextInput

`TextInput` is the **abstract class** that concentrates all the editing logic shared between [`TextField`](/docs/ui/widgets/textfield) (single-line) and [`TextArea`](/docs/ui/widgets/textarea) (multiline): cursor, selection, clipboard, focus/blink, keyboard shortcuts, arrow-key navigation, long-touch with `ActionSheet` and integration with the virtual keyboard. The subclasses implement the spatial mapping (1D vs 2D), scrolling, rendering and masks.

You **do not instantiate** `TextInput` directly — you instantiate `TextField` or `TextArea`. This page is the overview of what both inherit. It implements `UIConstants.TextInputComponent` (`setText`/`getText`/`isPassword`/`replaceText`/`keyboardHidden`/`getCursorPos`/`hasFocus`).

> 🟡 · **Package:** `dev.crossfly.ui` · **Extends:** `Component` · **Implements:** `UIConstants.TextInputComponent` · **Abstract**

## When to use
- As the **common type** when referencing any input field (`TextInput campo = new TextField(...)`).
- When writing code that operates on cursor/selection/clipboard without caring whether it is 1 or N lines.

## When NOT to use
- To instantiate a concrete field → use [`TextField`](/docs/ui/widgets/textfield) (1 line) or [`TextArea`](/docs/ui/widgets/textarea) (multiline).

## Hierarchy
`Component` → **`TextInput`** (abstract) → [`TextField`](/docs/ui/widgets/textfield) / [`TextArea`](/docs/ui/widgets/textarea). The constructor receives the initial buffer capacity: `TextInput(int initialCapacity)`. It is `isPressable() → true`.

## Internal state (key fields)
| Field | Type | Meaning |
|---|---|---|
| `chars` | `StringBuffer` | raw text buffer |
| `text` | `String` | displayed text (updated by the subclass; starts `""`) |
| `insertPos` | `int` | cursor (insertion) position |
| `startSelectPos` | `int` | selection start, or `-1` if there is no selection |
| `cursorShowing` | `boolean` | visible phase of the cursor blink |
| `keyboardCanOpen` | `boolean` | the keyboard may open on the next touch-up |
| `hasFocus` | `boolean` | has keyboard focus |
| `caption` | `String` | placeholder / floating caption |

## Inherited methods (cursor, selection, clipboard)
| Method | Return | What it does |
|---|---|---|
| `setText(String)` | `void`* | sets the text (implemented by the subclass) |
| `getText()` | `String` | current text (with mask in `TextField`) |
| `setCursorPos(int start, int end)` | `void` | sets selection; if `start==end`, only moves the cursor |
| `setCursorPos(int pos)` | `void` | cursor without selection |
| `getCursorPos()` | `int[]` | `[startSelectPos, insertPos]` |
| `getInsertPos()` | `int` | cursor position |
| `hasSelection()` | `boolean` | is there a selected range? |
| `getSelectionStart()` / `getSelectionEnd()` | `int` | min / max of the positions |
| `getTextBuffer()` / `getLength()` | `StringBuffer` / `int` | raw buffer / size |
| `clipboardCopy()` / `clipboardCut()` | `void` | copies / cuts the selection (via `Device.clipboard*`) |
| `clipboardPaste()` / `clearText()` | `void` | **abstract** — the subclass handles mask/`\n` |
| `mapKeys(String from, String to)` | `void` | character-by-character remapping (throws `IllegalArgumentException` if sizes differ; `null`/`null` clears) |
| `hasFocus()` | `boolean` | has keyboard focus |
| `restoreFocusAtEnd()` | `void` | reassigns focus and moves cursor to the end (after closing a picker/keypad) |

*`setText` returns `void` in the `TextComponent` contract; `TextField.text(...)`/`TextArea.text(...)` offer **fluent** variants that return the field itself.

## Callbacks and events
| Callback | When it fires |
|---|---|
| `onAction(Action)` (inherited) | on `onTouchUp` (after opening the keyboard) |
| `onFocus()` / `onBlur()` | focus gained / lost (turns the cursor and blink on/off) |

There is no dedicated `onChange`: to react to changes, observe via the action callback or query `getText()`.

## States
- **focused:** cursor on and blinking (`cursorShowing`), keyboard/`Keypad` opens on tap; `onFocus()`/`onBlur()` control the transition.
- **selection:** range `[startSelectPos, insertPos]`; long-touch selects all and opens the `ActionSheet` (Copy/Cut/Paste/Clear).
- **disabled / read-only:** `setEnabled(false)` or `editable=false` in the style prevent editing.

## Keys (inherited from `onKeyDown`)
- **Ctrl+C / V / X** → copy / paste / cut; **Ctrl+A** → select all.
- **Backspace / Delete** → deletes the selection or a single character.
- **Left / Right / Home / End** → move the cursor; with **Shift** extend the selection.

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

      // Polymorphic reference: TextInput is the abstract base of TextField/TextArea
      TextInput campo = new TextField("valor inicial");
      add(LAY_AUTO, (Component) campo);

      // Output label
      Label saida = new Label("Leia o campo com o botão");
      add(LAY_AUTO, saida);

      // There is no per-key event: read getText() on submit
      Button enviar = new Button("Enviar")
         .onAction(c -> saida.setText("Você digitou: " + campo.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.

## Example (using the base type)
```java
// Polymorphic reference to the concrete field
TextInput campo = new TextField("valor inicial");
container.add(LAY_AUTO, (Component) campo);

// Common operations via the base
if (campo.hasSelection())
   campo.clipboardCopy();
int[] sel = campo.getCursorPos();     // [start, insert]
campo.setCursorPos(campo.getLength()); // cursor to the end
```

## Best practices
- Program against `TextInput`/`UIConstants.TextInputComponent` when the code does not depend on the format (1 vs N lines).
- After closing a picker/`Keypad`, call `restoreFocusAtEnd()` to return focus correctly.

## Common mistakes
- **Trying to instantiate `TextInput`:** it is abstract — use `TextField`/`TextArea`.
- **Expecting `clipboardPaste()`/`clearText()` to work in the base:** they are abstract; the behavior comes from the subclass (mask in `TextField`, `\n` in `TextArea`).

## See also
- Area guide: [`ui/`](/docs/ui) · Recipes: [`cookbook/`](/docs/cookbook)
- Concept (focus, input events): [`concepts/`](/docs/concepts)
- Related components: [`TextField`](/docs/ui/widgets/textfield) · [`TextArea`](/docs/ui/widgets/textarea) · [`Keypad`](/docs/ui/widgets/keypad)
