唯杰地图 logo唯杰地图
VJMAP
EN

input-manager

vjmapext/references/drawing/input-manager.md 9.4 KB249 行
原文

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

一、InputResult 结构(真实字段)

所有 get* 方法返回 Promise<InputResult>(或对应的 XxxInputResult 子类):

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')。

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

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 无正交模式,此字段未被读取
}
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)。

三、getEntity / getSelections

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
// 单选/过滤 → 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

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;
}
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

getKeyword(options?: KeywordInputOptions): Promise<KeywordInputResult> // { status, value: string }
class KeywordInputOptions {
  message: string;
  keywords: string[];
  keywordTips?: Record<string, string>;
  defaultKeyword: string;
}
const r = await im.getKeyword({
  message: '选操作',
  keywords: ['移动', '复制', '旋转'],
  defaultKeyword: '移动'
});
// r.value: '移动' | '复制' | '旋转'

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

六、getString

getString(options?: StringInputOptions): Promise<StringInputResult>  // { status, value: string }
class StringInputOptions { message; keywords; keywordTips?; allowEmpty: boolean; defaultValue: string }
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 上)

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

mapcadLayer.inputManager.cancelCurrentInput();

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

程序化落地:resolve*Value 族(自动化/测试通道)

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

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)

十、典型场景:画直线

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=[]。

相关