# TextInput

> Base abstrata de entrada de texto — cursor, seleção, clipboard, foco e teclado, comuns a TextField e TextArea.

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

# TextInput

`TextInput` é a **classe abstrata** que concentra toda a lógica de edição compartilhada entre [`TextField`](/docs/ui/widgets/textfield) (uma linha) e [`TextArea`](/docs/ui/widgets/textarea) (multilinha): cursor, seleção, clipboard, foco/blink, atalhos de teclado, navegação por setas, long-touch com `ActionSheet` e integração com o teclado virtual. As subclasses implementam o mapeamento espacial (1D vs 2D), rolagem, renderização e máscaras.

Você **não instancia** `TextInput` diretamente — instancia `TextField` ou `TextArea`. Esta página é a visão geral do que ambos herdam. Ela implementa `UIConstants.TextInputComponent` (`setText`/`getText`/`isPassword`/`replaceText`/`keyboardHidden`/`getCursorPos`/`hasFocus`).

> 🟡 · **Pacote:** `dev.crossfly.ui` · **Estende:** `Component` · **Implementa:** `UIConstants.TextInputComponent` · **Abstrata**

## Quando usar
- Como **tipo comum** ao referenciar qualquer campo de entrada (`TextInput campo = new TextField(...)`).
- Ao escrever código que opera sobre cursor/seleção/clipboard sem se importar se é 1 ou N linhas.

## Quando NÃO usar
- Para instanciar um campo concreto → use [`TextField`](/docs/ui/widgets/textfield) (1 linha) ou [`TextArea`](/docs/ui/widgets/textarea) (multilinha).

## Hierarquia
`Component` → **`TextInput`** (abstrata) → [`TextField`](/docs/ui/widgets/textfield) / [`TextArea`](/docs/ui/widgets/textarea). O construtor recebe a capacidade inicial do buffer: `TextInput(int initialCapacity)`. É `isPressable() → true`.

## Estado interno (campos-chave)
| Campo | Tipo | Significado |
|---|---|---|
| `chars` | `StringBuffer` | buffer bruto do texto |
| `text` | `String` | texto exibido (atualizado pela subclasse; inicia `""`) |
| `insertPos` | `int` | posição do cursor (inserção) |
| `startSelectPos` | `int` | início da seleção, ou `-1` se não há seleção |
| `cursorShowing` | `boolean` | fase visível do blink do cursor |
| `keyboardCanOpen` | `boolean` | o teclado pode abrir no próximo touch-up |
| `hasFocus` | `boolean` | tem foco de teclado |
| `caption` | `String` | placeholder / caption flutuante |

## Métodos herdados (cursor, seleção, clipboard)
| Método | Retorno | O que faz |
|---|---|---|
| `setText(String)` | `void`* | define o texto (implementado pela subclasse) |
| `getText()` | `String` | texto atual (com máscara em `TextField`) |
| `setCursorPos(int start, int end)` | `void` | define seleção; se `start==end`, só move o cursor |
| `setCursorPos(int pos)` | `void` | cursor sem seleção |
| `getCursorPos()` | `int[]` | `[startSelectPos, insertPos]` |
| `getInsertPos()` | `int` | posição do cursor |
| `hasSelection()` | `boolean` | há um intervalo selecionado? |
| `getSelectionStart()` / `getSelectionEnd()` | `int` | min / max das posições |
| `getTextBuffer()` / `getLength()` | `StringBuffer` / `int` | buffer bruto / tamanho |
| `clipboardCopy()` / `clipboardCut()` | `void` | copia / recorta a seleção (via `Device.clipboard*`) |
| `clipboardPaste()` / `clearText()` | `void` | **abstratos** — a subclasse trata máscara/`\n` |
| `mapKeys(String from, String to)` | `void` | remapeamento caractere-a-caractere (lança `IllegalArgumentException` se tamanhos diferirem; `null`/`null` limpa) |
| `hasFocus()` | `boolean` | tem foco de teclado |
| `restoreFocusAtEnd()` | `void` | reatribui foco e move cursor ao fim (após fechar picker/keypad) |

*`setText` retorna `void` no contrato `TextComponent`; `TextField.text(...)`/`TextArea.text(...)` oferecem variantes **fluentes** que retornam o próprio campo.

## Callbacks e eventos
| Callback | Quando dispara |
|---|---|
| `onAction(Action)` (herdado) | em `onTouchUp` (após abrir o teclado) |
| `onFocus()` / `onBlur()` | ganho / perda de foco (liga/desliga cursor e blink) |

Não existe um `onChange` dedicado: para reagir a mudanças, observe via callback de ação ou consulte `getText()`.

## Estados
- **focused:** cursor ligado e piscando (`cursorShowing`), teclado/`Keypad` abre no toque; `onFocus()`/`onBlur()` controlam a transição.
- **selection:** intervalo `[startSelectPos, insertPos]`; long-touch seleciona tudo e abre o `ActionSheet` (Copy/Cut/Paste/Clear).
- **disabled / read-only:** `setEnabled(false)` ou `editable=false` no estilo impedem edição.

## Teclas (herdadas de `onKeyDown`)
- **Ctrl+C / V / X** → copiar / colar / recortar; **Ctrl+A** → selecionar tudo.
- **Backspace / Delete** → apaga seleção ou um caractere.
- **Left / Right / Home / End** → move o cursor; com **Shift** estende a seleção.

## Exemplo completo (copiar, colar e rodar)
```java
package com.exemplo;

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

/** Tela principal executável. Aponte o launcher (classname) e rode: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // Referência polimórfica: TextInput é a base abstrata de TextField/TextArea
      TextInput campo = new TextField("valor inicial");
      add(LAY_AUTO, (Component) campo);

      // Label de saída
      Label saida = new Label("Leia o campo com o botão");
      add(LAY_AUTO, saida);

      // Não há evento por tecla: leia getText() no envio
      Button enviar = new Button("Enviar")
         .onAction(c -> saida.setText("Você digitou: " + campo.getText()));
      add(LAY_AUTO, enviar);
   }
}
```

> Os trechos abaixo mostram **só o miolo** (o que vai dentro de `onShow()`). Para rodar, coloque-os no esqueleto `MainWindow` acima.

## Exemplo (usar o tipo base)
```java
// Referência polimórfica ao campo concreto
TextInput campo = new TextField("valor inicial");
container.add(LAY_AUTO, (Component) campo);

// Operações comuns via a base
if (campo.hasSelection())
   campo.clipboardCopy();
int[] sel = campo.getCursorPos();     // [start, insert]
campo.setCursorPos(campo.getLength()); // cursor ao fim
```

## Boas práticas
- Programe contra `TextInput`/`UIConstants.TextInputComponent` quando o código não depender do formato (1 vs N linhas).
- Após fechar um picker/`Keypad`, chame `restoreFocusAtEnd()` para devolver o foco corretamente.

## Erros comuns
- **Tentar instanciar `TextInput`:** é abstrata — use `TextField`/`TextArea`.
- **Esperar que `clipboardPaste()`/`clearText()` funcionem na base:** são abstratos; o comportamento vem da subclasse (máscara em `TextField`, `\n` em `TextArea`).

## Veja também
- Guia da área: [`ui/`](/docs/ui) · Receitas: [`cookbook/`](/docs/cookbook)
- Conceito (foco, eventos de entrada): [`concepts/`](/docs/concepts)
- Componentes relacionados: [`TextField`](/docs/ui/widgets/textfield) · [`TextArea`](/docs/ui/widgets/textarea) · [`Keypad`](/docs/ui/widgets/keypad)
