# TextArea

> 多行文本字段，支持自动换行、垂直滚动、按行图标和可滚动的只读模式。

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

# TextArea

`TextArea` 是**多行**文本输入。在基类 [`TextInput`](/docs/ui/widgets/textinput) 之上，它增加了：自动换行、行结构、垂直滚动、二维坐标映射和多行布局。它支持右侧图标（每个可见行一个）和一个**可滚动的只读**模式（用于展示用户仅浏览的长文本）。

与 [`TextField`](/docs/ui/widgets/textfield) 不同，`TextArea` **没有掩码也没有密码模式**；`ENTER` 插入换行，`getText()` 保留 `\n`。

> 🟡 · **包：** `dev.crossfly.ui` · **继承：** `TextInput` · **样式：** `TextAreaStyle`

## 何时使用
- 备注、评论、描述、地址 —— 任何**多行**文本。
- 展示一大块可滚动的长文本（使用 `editable=false`）。

## 何时不使用
- **单行**输入（姓名、邮箱、金额）→ 使用 [`TextField`](/docs/ui/widgets/textfield)。
- 掩码 / 密码模式 → 使用 [`TextField`](/docs/ui/widgets/textfield)（`TextArea` 不支持它们）。

## 外观
带有 `TextAreaStyle` 的背景/边框的矩形，文本在内部宽度上自动换行分行，当内容超过高度时垂直滚动。首选高度来自 `rowCount`（可见行数）× 行高 + `ingap`。定义了右侧图标时，它们占据自己的一列，每个可见行一个。

``

## 完整示例（复制、粘贴并运行）
```java
package com.exemplo;

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

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // 多行字段；LAY_FILL 为可滚动区域提供高度
      TextArea obs = new TextArea().caption("Observações");
      add(LAY_FILL, obs);

      // 输出 Label
      Label saida = new Label("Escreva algo e toque em Enviar");
      add(LAY_AUTO, saida);

      // 没有按键 onChange 事件：在提交时读取 getText()（保留 \n）
      Button enviar = new Button("Enviar")
         .onAction(c -> saida.setText("Você escreveu " + obs.getText().length() + " caracteres"));
      add(LAY_AUTO, enviar);
   }
}
```

> 下面的片段**只展示核心部分**（放入 `onShow()` 内的内容）。要运行它们，请将它们放入上面的 `MainWindow` 骨架中。

## 基本用法
```java
TextArea obs = new TextArea().caption("Observações");
container.add(0.4f, obs);
// ...
String texto = obs.getText();   // 保留 \n
```

## 构造函数
| 构造函数 | 描述 |
|---|---|
| `TextArea()` | 空 |
| `TextArea(String text)` | 带初始文本（`\r\n`/`\r` 被规范化为 `\n`） |

## 属性与自定义
| 属性 / 方法 | 类型 | 默认值 | 作用 |
|---|---|---|---|
| `text(String)` / `setText(String)` | `TextArea` / `void` | — | 设置文本（规范化换行；光标置于开头） |
| `getText()` | `String` | — | 原始文本，保留 `\n` |
| `caption(String)` | `TextArea`（流式） | `null` | 占位符 |
| `rightIcons(Icon...)` | `TextArea`（流式） | — | 右侧图标，每个可见行一个 |
| `rightIconAction(int row, Action)` | `TextArea`（流式） | — | 第 `row` 行图标的动作（0 起始） |
| `clearText()` | `void` | — | 清除全部文本 |
| `getNumberOfTextLines()` | `int` | — | 逻辑行总数（自动换行后） |
| `scrollToTop()` / `scrollToBottom()` / `scrollToLine(int)` | `void` | — | 编程式滚动 |
| 样式 `.rowCount(int)` | `int` | — | 可见行数（定义首选高度） |
| 样式 `.maxLength(int)` | `int` | `0`（无限制） | 字符数限制 |
| 样式 `.validChars(String)` | `String` | `null` | 允许的字符（`\n` 始终接受） |
| 样式 `.editable(false)` | `boolean` | `true` | 可滚动的只读 |
| 样式 `.spaceBetweenLines(int)` | `int` | — | 行间额外间距 |

## 回调与事件
| 回调 | 何时触发 |
|---|---|
| `onAction(Action)`（继承） | 在 `onTouchUp`，打开键盘之后 |
| `onFocus()` / `onBlur()`（继承） | 获得 / 失去焦点 |
| `rightIconAction(row, Action)` | 触摸（并松开于）该行图标时 |

## 状态
- **focused：** 光标闪烁，触摸时打开键盘；↑/↓ 方向键在行间导航。
- **selection：** 通过拖动进行二维选择；长按选中全部并打开 `ActionSheet`。
- **read-only（`editable=false`）：** 无光标/无编辑；拖动会**滚动**内容；鼠标滚轮也会滚动。

### 带限制的备注字段
```java
TextArea obs = new TextArea().caption("Observações");
obs.style(Styles.theme()
   .textArea(State.NORMAL)
   .rowCount(4)
   .maxLength(500));
container.add(LAY_AUTO, obs);
```

### 只读、可滚动的文本块
```java
TextArea termos = new TextArea(textoDosTermos);
termos.style(Styles.theme()
   .textArea(State.NORMAL)
   .editable(false)      // 只滚动，不编辑
   .rowCount(8));
container.add(LAY_FILL, termos);
termos.scrollToTop();
```

### 右侧带动作图标的行
```java
TextArea lista = new TextArea("Item A\nItem B\nItem C");
lista.rightIcons(
   new MaterialIcon(""),   // 第 0 行的 "delete"
   new MaterialIcon(""),
   new MaterialIcon(""));
lista.rightIconAction(0, c -> removerLinha(0));
container.add(0.3f, lista);
```

## 最佳实践
- 在样式中定义 `rowCount` 以控制可见高度；在布局中与 `LAY_AUTO` 配合。
- 要展示不可编辑的长文本，使用 `editable(false)` —— 拖动将变为滚动而非选择。
- 使用 `getText()` 进行持久化（保留 `\n`）；`\r\n` 的规范化已在输入时完成。

## 常见错误
- **期待掩码或密码模式：** `TextArea` 不支持它们 —— 使用 [`TextField`](/docs/ui/widgets/textfield)。
- **定义了过多 `rightIcons`：** 只绘制到 `visibleRows`（可见行数）为止；超出的图标被忽略。

## 另见
- 领域指南：[`ui/`](/docs/ui) · 食谱：[`cookbook/`](/docs/cookbook)
- 概念（焦点、选择、滚动）：[`concepts/`](/docs/concepts)
- 相关组件：[`TextInput`](/docs/ui/widgets/textinput) · [`TextField`](/docs/ui/widgets/textfield) · [`Keypad`](/docs/ui/widgets/keypad)
