# Keypad

> 数字键盘与计算器窗口，会锚定到当前获得焦点的 TextField 上方。

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

# Keypad

`Keypad` 是一个带数字键盘的**窗口**（`Window`），它锚定在获得焦点的文本字段的上方或下方。当处于计算器模式（`isCalculator`）时，它会增加第 4 列 `+ − × ÷ =`，解算输入的算式，并将结果写回字段。

其用法几乎总是**间接的**：你在 [`TextField`](/docs/ui/widgets/textfield) 上配置 `Keyboard.KEYPAD`/`CALCULATOR`（或 `EditMode.CURRENCY`），字段本身会在获得焦点时实例化并显示 `Keypad`。手动实例化它的情况很少见。

> 🔴 · **包：** `dev.crossfly.ui` · **继承：** `Window` · **样式：** `KeypadStyle`

## 何时使用
- 内嵌的数字输入（不依赖原生键盘），包括在桌面端。
- 计算器模式：允许用户在货币/数字字段内进行计算。

## 何时不要使用
- 自由文本输入 → 使用原生键盘（`Keyboard.DEFAULT`）。
- 你很少直接实例化 `Keypad` —— 请在 [`TextField`](/docs/ui/widgets/textfield) 上配置键盘。

## 外观
按钮网格（`GridLayout`）：3 列（0–9、退格、回车）或计算器模式下 4 列（带运算符）。按钮使用 `keyWidth`/`keyHeight`（× 字体高度）和四角圆角。窗口在能容纳时定位于字段下方，否则位于上方（会考虑浮动的 caption）。

``

## 完整示例（复制、粘贴并运行）
```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());

      // 接收 Keypad 按键的数字字段
      TextField valor = new TextField().caption("Quantidade");
      add(LAY_AUTO, valor);

      // 输出 Label
      Label saida = new Label("Digite no keypad e toque em Confirmar");
      add(LAY_AUTO, saida);

      // 打开锚定到该字段的 Keypad 的按钮（直接使用）
      Button abrir = new Button("Abrir keypad").onAction(c -> {
         Keypad kp = new Keypad(valor);   // target 是上面的 TextField
         kp.show();
      });
      add(LAY_AUTO, abrir);

      // Keypad 写入该字段；提交时读取 getText()
      Button confirmar = new Button("Confirmar")
         .onAction(c -> saida.setText("Valor: " + valor.getText()));
      add(LAY_AUTO, confirmar);
   }
}
```

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

## 基本用法（间接，通过 TextField）
```java
// TextField 在聚焦时自动显示 Keypad：
TextField valor = new TextField().caption("Quantidade");
valor.setKeyboard(Keyboard.KEYPAD);      // 内置数字键盘
container.add(LAY_AUTO, valor);

// 计算器模式 — CURRENCY 默认已使用 CALCULATOR：
TextField preco = new TextField().caption("Preço");
preco.setMode(EditMode.CURRENCY, true);
container.add(LAY_AUTO, preco);
```

## 直接使用（高级）
```java
// 手动将 Keypad 锚定到某个字段：
Keypad kp = new Keypad(campo);   // 字段是一个 TextInput (TextField/TextArea)
kp.show();

// 在计算器模式下：
Keypad calc = new Keypad(campo);
calc.<KeypadStyle>getStyle().isCalculator(true);
calc.show();
```

## 构造函数
| 构造函数 | 描述 |
|---|---|
| `Keypad(TextInput target)` | 创建锚定到字段 `target` 的键盘，`target` 将接收按键 |

## 属性与自定义（通过 `KeypadStyle`）
| 属性 | 类型 | 作用 |
|---|---|---|
| `.isCalculator(boolean)` | `boolean` | 带 `+ − × ÷` 和 `=` 按钮的第 4 列 |
| `.is1to9(boolean)` | `boolean` | 按键顺序（顶部为 1-2-3 还是 7-8-9） |
| `.keyWidth` / `.keyHeight` | `float` | 按键尺寸（× 字体高度） |
| `.digitColor` / `.specialColor` | ARGB | 数字 / 特殊键的颜色 |
| `.keypadBackColor` | ARGB | 按键背景 |
| `.enterIcon` / `.backspaceIcon` | `Icon` | 回车和退格的图标 |
| `.radiusTL/TR/BL/BR` | `float` | 圆角半径 |

## 回调与事件
`Keypad` 不暴露自己的 `onAction`。它将按键发送给 `target`：
- 数字 → `target.onKeyTyped(c)`。
- 退格 / 回车 → `target.onKeyDown(Input.Keys.BACKSPACE / ENTER)`。
- 在计算器模式下，`=` 会计算并通过 `target.setText(...)` 写入结果。
- 关闭时（`onRemove`），焦点通过 `restoreFocusAtEnd()` 返回字段。

要作出响应，请观察**字段**（`getText()`），而不是 `Keypad`。

## 状态
- **Enter ↔ Equals：** 在计算器模式下，底部按钮会根据是否有待处理的运算，在 `Enter`（图标）和 `=`（文本）之间切换。
- 点击外部或确认（`Enter`）时窗口关闭。

### 带内嵌计算器的货币字段
```java
TextField total = new TextField().caption("Total");
total.setMode(EditMode.CURRENCY, true);   // 以计算器模式打开 Keypad
container.add(LAY_AUTO, total);
// 用户输入 "10 + 5 ="，字段随后显示 15,00。
```

### 在桌面端强制使用 Keypad
```java
// NUMERIC 在桌面端调用 showInput() 时会自动降级为 KEYPAD；
// 要在任意平台上确保使用 Keypad，请显式使用 KEYPAD：
TextField codigo = new TextField().caption("Código");
codigo.setKeyboard(Keyboard.KEYPAD);
container.add(LAY_AUTO, codigo);
```

## 最佳实践
- 优先在 `TextField` 上**配置键盘**（`setKeyboard`/`setMode`），而不是直接实例化 `Keypad`。
- 对于货币输入，`EditMode.CURRENCY` 已自带计算器 —— 无需额外配置任何东西。

## 常见错误
- **在 `Keypad` 上注册 `onAction`：** 它不会触发自己的动作；请观察目标字段。
- **在没有可聚焦 `target` 的情况下实例化 `Keypad`：** 它需要一个 `TextInput` 来发送按键。

## 另请参阅
- 领域指南：[`ui/`](/docs/ui) · 食谱：[`cookbook/`](/docs/cookbook)
- 概念（虚拟键盘、焦点）：[`concepts/`](/docs/concepts)
- 相关组件：[`TextField`](/docs/ui/widgets/textfield) · [`TextInput`](/docs/ui/widgets/textinput) · [`TextArea`](/docs/ui/widgets/textarea)
