> **本文回答**：`mapcadLayer.inputManager` 全部方法 —— `getPoint / getEntity / getSelections / getReal / getInteger / getKeyword / getString / cancelCurrentInput`。

## 一、InputResult 结构（真实字段）

所有 `get*` 方法返回 `Promise<InputResult>`（或对应的 `XxxInputResult` 子类）：

```ts
class InputResult {
  status: InputStatusEnum;   // 'ok' | 'cancel' | 'enter' | 'keyword' | 'isNumber' | 'none'
  value: Point2D;            // getPoint 的点结果；数值型子类里 value 是 number/string
  stringResult: string;      // 原始输入串（含用户键入的关键字）
  numberValue?: number;      // status==='isNumber' 时用户直接输入的数值
  entity?: EntityBase;       // getEntity 结果
  entities?: EntityBase[];   // getSelections 结果
  pickPoint?: Point2D;       // 拾取点
  snapEntityId?: string;
  snapType?: string;
}
```

**注意字段名**：点结果在 **`r.value`**（Point2D），不是 `r.point`；关键字看 `status==='keyword'` + `r.stringResult`，没有 `r.keyword` 字段。

**`getPoint` 从不返回 `'enter'`**（1.0.1 中只有单选 `getEntity` 在回车/空格时返回它）：等待点时按回车/空格、右键、Esc **一律返回 `'cancel'`**——"回车结束循环取点"就按 `status !== 'ok'` 退出（内置 PLINE/SPLINE 即如此）。`getPoint` 实际只会给 `ok` / `cancel` / `keyword` / `isNumber`。另：`getKeyword` 选中关键字返回的是 `status: 'ok'` + `r.value`（不是 `'keyword'`）。

```js
const r = await im.getPoint({ message: '指定起点' });
if (r.status === vjmapext.InputStatusEnum.OK) {
  use(r.value);                       // Point2D
} else if (r.status === vjmapext.InputStatusEnum.Cancel) {
  return;
}
```

## 二、getPoint

```ts
getPoint(options?: PointInputOptions): Promise<InputResult>

class PointInputOptions {
  message: string;            // 提示语（字段名是 message，不是 prompt）
  useBasePoint: boolean;      // 是否启用橡皮筋起点
  basePoint: Point2D;         // 橡皮筋起点（须先设 useBasePoint）
  keywords: string[];         // 可选关键字列表
  keywordTips?: Record<string, string>;  // 关键字按钮悬浮提示
  callback?: (cursorCadPt: Point2D) => void;  // 光标移动回调
  allowNumberResult: boolean; // 允许直接键入数字（status='isNumber'，结果在 numberValue）
  useForceAngle: boolean;     // 强制角度：只对键入距离（resolvePointByDistance）生效，不约束鼠标拾取
  forceAngle: number;         // 弧度
  ignoreOrthoMode: boolean;   // 1.0.1 无正交模式，此字段未被读取
}
```

```js
const r = await im.getPoint({
  message: '指定终点 [闭合(C)/撤销(U)]',
  useBasePoint: true,
  basePoint: start,
  keywords: ['C', 'U']
});

if (r.status === 'keyword') {
  if (r.stringResult === 'C') closePolyline();
  else if (r.stringResult === 'U') undoLast();
}
```

`getPoint` 自动启用对象捕捉（见 [snap](snap.md)）。

## 三、getEntity / getSelections

```ts
getEntity(options?: EntityInputOptions): Promise<InputResult>
class EntityInputOptions {
  message: string;
  filter?: (entity) => boolean;   // 类型过滤
  keywords: string[];
  keywordTips?: Record<string, string>;
  multiple: boolean;              // 允许多选
  useExistingSelection: boolean;  // 复用已有选集
}

getSelections(options?: SelectionInputOptions): Promise<InputResult>
class SelectionInputOptions { message: string }   // 只有 message
```

```js
// 单选/过滤 → getEntity
const r = await im.getEntity({
  message: '选择一条线',
  filter: e => e.type === 'LINE'
});
if (r.status === 'ok') use(r.entity);

// 框选多个 → getSelections
const rs = await im.getSelections({ message: '选择要删除的实体' });
if (rs.status === 'ok') rs.entities.forEach(e => mapcadLayer.deleteEntity(e.id));
```

`getSelections` 等待中：单击实体 = toggle 选中；**单击空白处 = 立即结束选择**（返回 `ok` + 当前选集，与 AutoCAD"点空白开窗口"不同）；从空白处**拖动**才是框选（左→右 窗口 / 右→左 交叉，追加到选集）；回车/空格结束；右键无反应。

## 四、getReal / getInteger

```ts
getReal(options?: RealInputOptions): Promise<RealInputResult>  // { status, value: number }
class RealInputOptions {
  message: string;
  keywords: string[];
  keywordTips?: Record<string, string>;
  defaultValue: number;
  lowerLimit?: number;   // 字段名是 lowerLimit/upperLimit，不是 min/max
  upperLimit?: number;
}
```

```js
const r = await im.getReal({
  message: '输入半径',
  defaultValue: 10,
  lowerLimit: 0.1,
  upperLimit: 1000
});
if (r.status === 'ok') use(r.value);   // number
```

`getInteger` 同接口（`IntegerInputOptions`），`lowerLimit`/`upperLimit` **有默认值 `0`/`1024`**（非必填），只接受整数。

## 五、getKeyword

```ts
getKeyword(options?: KeywordInputOptions): Promise<KeywordInputResult> // { status, value: string }
class KeywordInputOptions {
  message: string;
  keywords: string[];
  keywordTips?: Record<string, string>;
  defaultKeyword: string;
}
```

```js
const r = await im.getKeyword({
  message: '选操作',
  keywords: ['移动', '复制', '旋转'],
  defaultKeyword: '移动'
});
// r.value: '移动' | '复制' | '旋转'
```

UI 层在命令栏显示按钮；也接受键盘输入。

## 六、getString

```ts
getString(options?: StringInputOptions): Promise<StringInputResult>  // { status, value: string }
class StringInputOptions { message; keywords; keywordTips?; allowEmpty: boolean; defaultValue: string }
```

```js
const r = await im.getString({ message: '输入文字', defaultValue: '标注' });
if (r.status === 'ok') use(r.value);   // string
```

> 自由文本落在 **`r.value`**，`r.stringResult` 此时为 `""`——`stringResult` 只在关键字路径填充（见 `getKeyword`/`getPoint` 的 `status==='keyword'` 分支）。

## 七、getDistanceOrTwoPoints（独立函数，不在 im 上）

```js
const r = await vjmapext.getDistanceOrTwoPoints(im, '输入距离或指定第一点', '指定第二点');
// 用户可直接键入数字，或点两点取间距；结果在 r.numberValue
// 注意：直接键入数字的 'isNumber' 已被函数内部归一为 'ok'——返回值只有 'ok'/'cancel'/'keyword'
if (r.status === 'ok') use(r.numberValue);
```

CIRCLE TTR 半径、OFFSET 距离用这个。**注意它是导出的工具函数**，不是 `im.getDistance()`。

## 八、cancelCurrentInput

```js
mapcadLayer.inputManager.cancelCurrentInput();
```

让当前 `get*` Promise 以 `status: 'cancel'` resolve，命令流程顺势 return。

### 程序化落地：resolve*Value 族（自动化/测试通道）

挂起的 `get*` Promise 可以用对应类型的 `resolve*Value` 直接落地，不依赖真实 UI 输入：

```js
im.resolvePointByValue(x, y);            // 落地 getPoint（绝对坐标）
im.resolvePointByDistance(dist);         // getPoint：直接键入距离
im.resolvePointByRelative(dx, dy);       // getPoint：相对 basePoint 直角偏移
im.resolvePointByPolar(dist, angleDeg);  // getPoint：相对 basePoint 极坐标（角度为度）
im.resolveStringValue('文字');           // 落地 getString
im.resolveRealValue(3.14);               // 落地 getReal
im.resolveIntegerValue(7);               // 落地 getInteger
im.resolveKeywordValue('移动');          // 落地 getKeyword / getPoint 的关键字分支
```

**类型必须对得上**：`getInteger` 挂起时调 `resolveStringValue("7")` 不会 resolve（Promise 一直挂起直到超时/cancel）——整数输入只能走 `resolveIntegerValue`。实体选择（`getEntity`/`getSelections`）没有对应的 resolve 方法，只能走真实拾取或 `cancelCurrentInput`。

## 九、微调

| 属性 | 说明 |
|---|---|
| `im.nudgePixels` | 方向键微移像素数（默认 `1`） |
| `im.nudgeRotateDeg` | 微旋转角度，度（默认 `1`） |
| `im.nudgeScaleFactor` | 微缩放因子（默认 `1.02`） |

## 十、典型场景：画直线

```js
class MyLineCmd {
  async main(ctx) {   // 实例方法（registry 内部 new 后调用），不能写 static
    const { inputManager: im, store, drawingDefaults: defaults } = ctx;

    const p1 = await im.getPoint({ message: '指定起点' });
    if (p1.status !== 'ok') return;

    const p2 = await im.getPoint({
      message: '指定终点 [撤销(U)]',
      useBasePoint: true,
      basePoint: p1.value,
      keywords: ['U']
    });
    if (p2.status !== 'ok') return;

    const line = new vjmapext.LineEnt(p1.value, p2.value);
    line.color = defaults.color;
    store.add(line);
  }
}
mapcadLayer.commandRegistry.register('MYLINE', MyLineCmd, '画直线');
```

## 十一、常见坑

1. **不判断 status 直接用 `r.value`**：取消时值不可靠 → 先判 `'ok'`。
2. **把点结果当 `r.point`**：字段是 `r.value`（Point2D）。
3. **options 用 `prompt`**：字段名是 `message`。
4. **`getSelection`（单数）不存在**：多选是 `getSelections`；单选/过滤用 `getEntity`（EntityInputOptions 才有 `filter`/`multiple`/`useExistingSelection`）。
5. **橡皮筋不显示**：须 `useBasePoint: true` + `basePoint` 一起给，没有单独的 `rubberBand` 开关。
6. **keywords 不写在 message 里**：用户不知道可用关键字；提示语括号带上。
7. **getSelections 返回空数组**：用户直接 Enter/空格，或第一下就单击了空白处；status 仍 `'ok'` 但 `entities=[]`。

## 相关

- 捕捉 → [snap](snap.md)
- 预览 → [preview](preview.md)
- 自定义命令 → [custom-command](custom-command.md)
