输入与事件系统
用户输入交互和事件监听。
输入函数
getPoint - 获取点
const { PointInputOptions, InputStatusEnum, getPoint, Point2D } = vjcad;
// 基本用法
const options = new PointInputOptions("指定点位置:");
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
const point = result.value; // Point2D {x, y}
console.log(`点: (${point.x}, ${point.y})`);
}
带橡皮筋线:
const opt = new PointInputOptions("指定终点:");
opt.useBasePoint = true; // 启用橡皮筋线
opt.basePoint = startPoint; // 设置基点
const result = await getPoint(opt);
隐藏默认橡皮筋线,使用自定义预览:
const opt = new PointInputOptions("指定终点:");
opt.useBasePoint = true;
opt.basePoint = startPoint;
opt.hideRubberBand = true; // 隐藏默认橡皮筋线
opt.callback = (canvasPt) => {
const worldPt = Engine.canvasToWcs(canvasPt);
// 创建自定义预览
const preview = new LineEnt([startPoint.x, startPoint.y], [worldPt.x, worldPt.y]);
preview.setDefaults();
preview.color = 1;
Engine.clearPreview();
Engine.drawPreviewEntities([preview]);
};
const result = await getPoint(opt);
Engine.clearPreview();
带关键字选项:
const opt = new PointInputOptions("指定点 [直线(L)/圆(C)/退出(E)]:");
opt.keywords = ["L", "C", "E"];
const result = await getPoint(opt);
switch (result.status) {
case InputStatusEnum.OK:
// 用户点击了坐标点
const point = result.value;
break;
case InputStatusEnum.Keyword:
// 用户输入了关键字
const keyword = result.stringResult.toUpperCase();
if (keyword === "L") { /* 处理直线 */ }
else if (keyword === "C") { /* 处理圆 */ }
else if (keyword === "E") { /* 退出 */ }
break;
case InputStatusEnum.Cancel:
// 用户取消(ESC)
break;
}
getSelections - 获取选择集
const { SelectionInputOptions, InputStatusEnum, getSelections } = vjcad;
const options = new SelectionInputOptions(); // 无参构造:提示语不在这里传
const result = await getSelections(options);
if (result.status === InputStatusEnum.OK) {
const entities = result.value; // 实体数组
console.log(`选中了 ${entities.length} 个实体`);
entities.forEach((ent, i) => {
console.log(`${i + 1}. ${ent.type} (ID: ${ent.id}, 颜色: ${ent.color})`);
});
}
特点:
- 支持框选和点选
- 用户按回车确认选择
- 返回实体数组
getReal - 获取实数
const { RealInputOptions, InputStatusEnum, getReal } = vjcad;
const options = new RealInputOptions("输入圆的半径:");
options.defaultValue = 30; // 默认值(构造时默认就是 0)
const result = await getReal(options);
if (result.status === InputStatusEnum.OK) {
// 直接回车/空格时 getReal 返回 OK + defaultValue(getPoint/getInteger/getString 则返回 None)
const radius = result.value;
} else if (result.status === InputStatusEnum.None) {
// 仅当 defaultValue 为 null/undefined 时,直接回车才返回 None
}
getInteger - 获取整数
const { IntegerInputOptions, InputStatusEnum, getInteger } = vjcad;
const options = new IntegerInputOptions("输入多边形边数:");
options.defaultValue = 6; // getInteger 不会自动套用 defaultValue
const result = await getInteger(options);
if (result.status === InputStatusEnum.OK) {
const sides = result.value;
} else if (result.status === InputStatusEnum.None) {
// 直接回车/空格(或输入非数字)返回 None,需自己回退到默认值
const sides = options.defaultValue;
}
getString - 获取字符串
const { StringInputOptions, InputStatusEnum, getString } = vjcad;
const options = new StringInputOptions("输入图层名称:");
// StringInputOptions 没有 defaultValue 字段(只有 keywords/allowEmpty/defaultKeyWord)
const result = await getString(options);
if (result.status === InputStatusEnum.OK) {
const layerName = result.value;
} else if (result.status === InputStatusEnum.None) {
// 直接回车/空格(空输入),默认值自己兜底
const layerName = "新图层";
}
注意:getString 中空格也会结束输入(与回车相同),因此输入值不能含空格。
InputStatusEnum 状态
| 状态 | 说明 | 常见场景 |
|---|---|---|
OK |
成功获取输入 | 用户点击/输入有效值;直接回车时 getReal 返回 OK + defaultValue、getKeyword(设了 defaultKeyword)返回 OK + 默认关键字 |
Cancel |
用户取消 | 按 ESC |
None |
直接回车/空格(空输入) | getPoint / getInteger / getString(getInteger 输入非数字也返回 None) |
EnterOrSpace |
回车/空格 | 仅 getEntity 返回 |
Keyword |
用户输入关键字 | 带 keywords 选项时(getPoint 设了 defaultKeyword 时空回车返回该关键字) |
事件系统
CadEventManager 基本用法
const { CadEventManager, CadEvents, Engine } = vjcad;
// 获取事件管理器
const events = Engine.eventManager;
// 或: const events = CadEventManager.getInstance();
// 注册监听器
events.on(CadEvents.EntityAdded, (args) => {
console.log('实体已添加:', args.entity.type);
});
// 链式调用
events
.on(CadEvents.EntityAdded, handler1)
.on(CadEvents.EntityModified, handler2);
// 一次性监听器(DocumentOpened 在 vjcad@1.0.16 从不派发,打开/切换文档用 DocumentSwitched)
events.once(CadEvents.DocumentSwitched, (args) => {
console.log('当前文档:', args.currentDocument.name); // 另有 args.previousDocument(可能为 null)
});
取消订阅
// 保存处理函数引用,用 off 精确移除
const handler = (args) => { console.log(args); };
events.on(CadEvents.EntityAdded, handler);
events.off(CadEvents.EntityAdded, handler);
⚠️ 不要用 offAll() / offAll(event) 做清理:Engine.eventManager 是全局单例,内核自己也在上面订阅(标注关联反应器桥 ReactorEventBridge 订阅 EntityAdded/EntityModified、RibbonBar 订阅 Plugin*/RibbonConfigChanged、MainView 订阅 DocumentCreated),且只订阅一次——offAll 会把它们一并移除,关联标注、Ribbon 刷新等从此失效。只用 off(event, handler)。
事件管理器方法
| 方法 | 说明 |
|---|---|
on(event, handler) |
注册监听器 |
once(event, handler) |
注册一次性监听器 |
off(event, handler) |
移除指定监听器(清理只用它) |
offAll(event?) |
移除所有监听器——含内核内部监听,勿用 |
hasListeners(event) |
检查是否有监听器 |
listenerCount(event) |
获取监听器数量 |
suspendEvents(queueEvents = false) |
暂停事件触发(默认丢弃期间的事件;传 true 则排队,resume 时补发) |
resumeEvents() |
恢复事件触发 |
常用事件类型
实体事件
// 实体添加
events.on(CadEvents.EntityAdded, (args) => {
console.log('实体:', args.entity);
console.log('文档:', args.document);
});
// 批量添加——只由 Engine.currentSpace.addEntities([...]) 派发(期间逐个 EntityAdded 被抑制);
// Engine.addEntities 走逐个 pcanvas.addEntity,只派发逐个 EntityAdding/EntityAdded
events.on(CadEvents.EntitiesAdded, (args) => {
console.log('添加数量:', args.entities.length);
});
// 实体修改
events.on(CadEvents.EntityModified, (args) => {
console.log('修改的实体:', args.entity);
});
// 实体删除
events.on(CadEvents.EntitiesErased, (args) => {
args.entities.forEach(ent => {
console.log('已删除:', ent.type);
});
});
选择事件
events.on(CadEvents.SelectionChanged, (args) => {
const selected = Engine.ssGetFirst();
console.log('当前选中数量:', selected.length);
});
命令事件
events.on(CadEvents.CommandStarted, (args) => {
console.log('命令开始:', args.commandName);
});
// Esc 取消的命令同样触发 CommandEnded(CommandCancelled 在 vjcad@1.0.16 从不派发);
// success 只表示 main() 没抛异常,duration 为耗时毫秒
events.on(CadEvents.CommandEnded, (args) => {
console.log('命令结束:', args.commandName, args.success, args.duration);
});
文档事件
// 打开/新建/切换文档后都会触发(DocumentOpened/DocumentSaved 等在 vjcad@1.0.16 从不派发)
events.on(CadEvents.DocumentSwitched, (args) => {
console.log('当前文档:', args.currentDocument.name, '之前:', args.previousDocument?.name);
});
// 只在文档从"未修改"变为"已修改"时触发一次;保存清除修改标记前不会再触发
events.on(CadEvents.DocumentModified, (args) => {
console.log('文档已修改:', args.document.name);
});
图层事件
events.on(CadEvents.LayerAdded, (args) => {
console.log('新图层:', args.layer.name);
});
events.on(CadEvents.CurrentLayerChanged, (args) => {
// 参数是图层**名**字符串,不是 Layer 对象:previousLayerName / currentLayerName
console.log('当前图层:', args.currentLayerName);
});
可取消事件
设 args.cancel = true 即取消(只对下方列表里真正派发且检查 cancel 的事件有效)。
// 阻止删除保护图层上的实体(EntityErasing 从不派发,用批量的 EntitiesErasing)
events.on(CadEvents.EntitiesErasing, (args) => {
const protectedEnts = args.entities.filter(ent => ent.layer === "保护图层");
if (protectedEnts.length === args.entities.length) {
args.cancel = true; // 整批取消
args.cancelReason = "保护图层上的实体不允许删除";
} else if (protectedEnts.length > 0) {
args.excludeEntities = protectedEnts; // 只把这些排除,其余照删
}
});
// 阻止命令执行
events.on(CadEvents.CommandStarting, (args) => {
if (args.commandName === 'ERASE' && !hasPermission()) {
args.cancel = true;
args.cancelReason = "没有删除权限";
}
});
可取消事件列表(vjcad@1.0.16 实际派发并检查 cancel 的):
DocumentCreating(取消后Engine.view.newDocument()/openDbDoc不判空会直接抛错,慎用)EntityAdding/EntitiesAdding/EntitiesErasing(后者另支持args.excludeEntities部分排除)CommandStartingLayerAdding/LayerDeletingContextMenuOpening
DocumentOpening / DocumentClosing / DocumentSaving / EntityErasing / BlockAdding / BlockDeleting 虽在 CadEvents 枚举里,但从不派发,监听它们取消不了任何操作。
事件类型完整列表
⚠️ 标 ✗ 的事件只存在于 CadEvents 枚举,vjcad@1.0.16 从不派发(监听永远不触发)。
文档事件
| 事件 | 说明 |
|---|---|
DocumentCreating / DocumentCreated |
新建文档 |
DocumentOpening / DocumentOpened |
✗ 打开文档——改用 DocumentSwitched |
DocumentClosing / DocumentClosed |
✗ 关闭文档 |
DocumentSaving / DocumentSaved |
✗ 保存文档 |
DocumentModified |
文档由未修改变为已修改(仅首次) |
DocumentSwitched |
打开/新建/切换文档(previousDocument / currentDocument) |
实体事件
| 事件 | 说明 |
|---|---|
EntityAdding / EntityAdded |
添加实体(Engine.addEntities 逐个派发) |
EntitiesAdding / EntitiesAdded |
批量添加(仅 currentSpace.addEntities) |
EntityModified |
实体修改 |
EntityErasing / EntityErased |
✗ 删除实体——删除只派发批量版 |
EntitiesErasing / EntitiesErased |
删除实体(单个删除也走这对) |
命令事件
| 事件 | 说明 |
|---|---|
CommandStarting / CommandStarted |
命令开始 |
CommandEnded |
命令结束(Esc 取消也触发) |
CommandCancelled |
✗ 命令取消——看 CommandEnded |
选择事件
| 事件 | 说明 |
|---|---|
SelectionChanged |
选择集变化 |
SelectionCleared |
选择集清空 |
图层事件
| 事件 | 说明 |
|---|---|
LayerAdding / LayerAdded |
添加图层 |
LayerDeleting / LayerDeleted |
删除图层 |
LayerModified |
✗ 图层修改 |
CurrentLayerChanged |
当前图层切换 |
视图事件
| 事件 | 说明 |
|---|---|
ViewZoomed |
视图缩放 |
ViewPanned |
✗ 视图平移 |
ViewRegenerated |
视图重绘 |
块事件
| 事件 | 说明 |
|---|---|
BlockAdding / BlockAdded |
✗ 添加块定义 |
BlockDeleting / BlockDeleted |
✗ 删除块定义 |
BlockModified |
✗ 块定义修改 |