菜单栏与 Ribbon 扩展
大约 7 分钟
菜单栏与 Ribbon 扩展
本章详细介绍如何通过插件扩展 WebCAD 的菜单栏和 Ribbon 工具栏。
菜单栏扩展
默认菜单项 ID
WebCAD 默认提供以下菜单项:
| ID | 名称 | 说明 |
|---|---|---|
file | 文件 | 新建、打开、保存、导出等 |
create | 创建 | 绘图命令(直线、圆、多段线等) |
edit | 编辑 | 编辑命令(移动、复制、旋转等) |
view | 显示 | 视图控制(缩放、平移等) |
draworder | 显示顺序 | 实体显示顺序控制 |
layer | 图层 | 图层管理 |
image | 图像 | 图像插入和管理 |
block | 块 | 块创建和管理 |
dim | 标注 | 尺寸标注 |
property | 属性 | 实体属性 |
tool | 工具 | 工具和实用程序 |
appearance | 外观 | 界面外观设置 |
help | 帮助 | 帮助和关于 |
添加菜单项到现有菜单
import type { Plugin, PluginContext } from 'vjcad';
const plugin: Plugin = {
manifest: { id: 'my-plugin', name: '我的插件', version: '1.0.0' },
async onActivate(context: PluginContext): Promise<void> {
// 注册命令
context.registerCommand('MYCMD', '我的命令', MyCommand);
context.registerIcon('MYCMD', '<svg>...</svg>');
// 添加到工具菜单
context.addMenuItem('tool', {
command: 'MYCMD',
shortcut: 'Ctrl+M' // 可选:快捷键提示
});
// 添加到编辑菜单,指定位置
context.addMenuItem('edit', {
command: 'MYCMD2',
after: 'COPY' // 在 COPY 命令之后插入
});
}
};创建新的主菜单
当需要添加一组相关命令时,可以创建新的主菜单:
async onActivate(context: PluginContext): Promise<void> {
// 注册命令和图标
context.registerCommand('TOOL1', '工具1', Tool1Command);
context.registerCommand('TOOL2', '工具2', Tool2Command);
context.registerIcon('TOOL1', '<svg>...</svg>');
context.registerIcon('TOOL2', '<svg>...</svg>');
// 创建新主菜单(如果不存在)
// 多个插件可以安全调用,只有第一个会创建
context.addMenu({
id: 'my-tools', // 唯一ID
label: '我的工具', // 显示名称
after: 'tool' // 在工具菜单后面(可选)
});
// 添加菜单项
context.addMenuItem('my-tools', { command: 'TOOL1' });
context.addMenuItem('my-tools', { command: 'TOOL2' });
}
async onDeactivate(context: PluginContext): Promise<void> {
// 移除菜单项
context.removeMenuItem('my-tools', 'TOOL1');
context.removeMenuItem('my-tools', 'TOOL2');
// 移除主菜单(如果是本插件创建的)
context.removeMenu('my-tools');
}菜单项配置
interface MenuItemConfig {
/** 命令名称(必填) */
command: string;
/** 快捷键提示(可选) */
shortcut?: string;
/** 在指定命令之前插入(可选) */
before?: string;
/** 在指定命令之后插入(可选) */
after?: string;
}
interface MenuConfig2 {
/** 菜单唯一ID(必填) */
id: string;
/** 显示名称(必填) */
label: string;
/** 在指定菜单之后插入(可选) */
after?: string;
}菜单图标
菜单项会自动使用与命令同名的图标。确保在添加菜单项前注册图标:
// 图标名称需与命令名一致
context.registerIcon('MYCMD', '<svg viewBox="0 0 24 24">...</svg>');
context.registerCommand('MYCMD', '我的命令', MyCommand);
context.addMenuItem('tool', { command: 'MYCMD' }); // 自动使用 MYCMD 图标Ribbon 工具栏扩展
默认 Ribbon 标签页
| ID | 名称 | 说明 |
|---|---|---|
default | 默认 | 常用绘图、修改、编辑等工具 |
tools | 工具 | 组、清理、特性、图像、测量等 |
plugins | 插件 | 插件管理 |
recent | 最近命令 | 动态显示最近执行的命令 |
默认 Ribbon 组(按标签页)
default 标签页:
| 组 ID | 名称 | 主要命令 |
|---|---|---|
draw | 绘图 | LINE, PLINE, ARC, CIRCLE, RECTANG, ELLIPSE, HATCH, SPLINE... |
modify | 修改 | MOVE, ROTATE, TRIM, COPY, MIRROR, FILLET, STRETCH, EXTEND, OFFSET... |
edit | 编辑 | UNDO, REDO, COPYCLIP, CUTCLIP, PASTECLIP... |
annotation | 注释 | TEXT, MTEXT, DIMLINEAR... |
layer | 图层特性 | LAYER, MAKELAYER, LAYALLON... |
navigation | 导航 | ZOOM, ZOOMEXTENTS, REGENALL... |
block | 块 | INSERT, BLOCK, QBLOCK... |
tools 标签页:
| 组 ID | 名称 | 主要命令 |
|---|---|---|
group | 组 | GROUP, UNGROUP |
purge | 清理 | PURGE, PURGEBLOCK, PURGEIMAGE... |
properties | 特性 | PROPERTIES, MATCHPROP... |
image | 图像 | IMAGE, IMAGEADJUST, IMAGECLIP... |
measure | 测量 | DIST, MEASUREANGLE, ID |
stats | 统计 | ENTITYSTATS, COUNTBLOCK... |
script | 脚本 | EXECSTR, EXECJS |
help | 帮助 | ABOUT, GRAPHICSINFO |
plugins 标签页:
| 组 ID | 名称 | 主要命令 |
|---|---|---|
plugin-manager | 插件管理 | PLUGINS |
添加按钮到现有组
import type { RibbonButtonConfig } from 'vjcad';
async onActivate(context: PluginContext): Promise<void> {
context.registerCommand('MYTOOL', '我的工具', MyToolCommand);
context.registerIcon('mytool', '<svg>...</svg>');
// 添加按钮到 default 标签页的 draw 组
const button: RibbonButtonConfig = {
icon: 'mytool', // 图标ID
cmd: 'MYTOOL', // 命令名
prompt: '我的绘图工具', // 工具提示
label: '工具', // 紧凑模式显示文本(可选)
type: 'small' // 按钮类型(可选)
};
context.addRibbonButton('default', 'draw', button, 'primary');
// 或添加到更多按钮区域
// context.addRibbonButton('default', 'draw', button, 'more');
}
async onDeactivate(context: PluginContext): Promise<void> {
context.removeRibbonButton('default', 'draw', 'MYTOOL');
}添加组到现有标签页
import type { RibbonGroupConfig } from 'vjcad';
async onActivate(context: PluginContext): Promise<void> {
// 注册命令和图标
context.registerCommand('CMD1', '命令1', Cmd1);
context.registerCommand('CMD2', '命令2', Cmd2);
context.registerIcon('cmd1', '<svg>...</svg>');
context.registerIcon('cmd2', '<svg>...</svg>');
// 添加组到 tools 标签页
const group: RibbonGroupConfig = {
id: 'my-group',
label: '我的工具',
pinnable: true, // 是否支持固定
displayMode: 'large', // 显示模式
primaryButtons: [
{ icon: 'cmd1', cmd: 'CMD1', prompt: '命令1', type: 'large' },
{ icon: 'cmd2', cmd: 'CMD2', prompt: '命令2', type: 'large' }
],
moreButtons: [
{ icon: 'cmd3', cmd: 'CMD3', prompt: '命令3' }
]
};
context.addRibbonGroup('tools', group);
}
async onDeactivate(context: PluginContext): Promise<void> {
context.removeRibbonGroup('tools', 'my-group');
}创建新的 Ribbon 标签页
import type { RibbonTabConfig } from 'vjcad';
async onActivate(context: PluginContext): Promise<void> {
// 注册命令和图标...
// 创建新标签页(如果不存在)
// 多个插件可以安全调用同一 ID,只有第一个会创建
context.addRibbonTab({
id: 'my-plugin-tab',
label: '我的插件',
groups: [] // 初始为空,稍后添加组
}, 'tools'); // 在 tools 标签页后插入(可选)
// 添加组到新标签页
context.addRibbonGroup('my-plugin-tab', {
id: 'main-group',
label: '主要功能',
pinnable: true,
primaryButtons: [
{ icon: 'cmd1', cmd: 'CMD1', prompt: '命令1', type: 'large' }
]
});
}
async onDeactivate(context: PluginContext): Promise<void> {
context.removeRibbonGroup('my-plugin-tab', 'main-group');
context.removeRibbonTab('my-plugin-tab');
}Ribbon 配置接口
/** 按钮类型 */
type RibbonButtonType = 'large' | 'small' | 'icon-only' | 'list';
/** 组显示模式 */
type RibbonGroupDisplayMode =
| 'large' // 大图标模式(图标+文字垂直排列)
| 'compact' // 紧凑模式(小图标+文字)
| 'small-icons' // 纯小图标模式(无文字,两行排列)
| 'layer' // 图层模式(特殊的图层列表)
| 'recent'; // 最近命令模式
/** 按钮配置 */
interface RibbonButtonConfig {
icon: string; // 图标ID
cmd: string; // 命令名
prompt?: string; // 工具提示
label?: string; // 紧凑模式显示文本
type?: RibbonButtonType; // 按钮类型
items?: RibbonButtonConfig[]; // 下拉列表子项(type='list'时)
}
/** 组配置 */
interface RibbonGroupConfig {
id: string; // 组唯一ID
label: string; // 组名称
pinnable?: boolean; // 是否支持固定
displayMode?: RibbonGroupDisplayMode; // 显示模式
primaryButtons: RibbonButtonConfig[]; // 主要按钮
moreButtons?: RibbonButtonConfig[]; // 更多按钮
}
/** 标签页配置 */
interface RibbonTabConfig {
id: string; // 标签页唯一ID
label: string; // 标签页名称
groups: RibbonGroupConfig[]; // 组列表
}完整示例:快速选择插件
以下是一个完整的插件示例,展示如何同时扩展菜单栏和 Ribbon:
import type { Plugin, PluginManifest, PluginContext } from 'vjcad';
// 命令类
class QuickSelectCommand {
async main(): Promise<void> {
// 打开快速选择面板...
}
}
// 图标 SVG
const ICON_QSELECT = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
<rect x="3" y="3" width="18" height="18" rx="2" stroke-width="2"/>
<path d="M9 12l2 2 4-4" stroke-width="2"/>
</svg>`;
// 插件清单
const manifest: PluginManifest = {
id: 'quick-select',
name: '快速选择',
version: '1.0.0',
author: 'WebCAD Team',
description: '快速选择和过滤图元'
};
// 插件定义
const plugin: Plugin = {
manifest,
async onActivate(context: PluginContext): Promise<void> {
// 1. 注册命令
context.registerCommand('QSELECT', '快速选择', QuickSelectCommand);
// 2. 注册图标(名称与命令一致,用于菜单显示)
context.registerIcon('QSELECT', ICON_QSELECT);
// 小写版本用于 Ribbon
context.registerIcon('qselect', ICON_QSELECT);
// 3. 添加到"插件"主菜单(如果不存在则创建)
context.addMenu({
id: 'plugins',
label: '插件',
after: 'tool'
});
context.addMenuItem('plugins', { command: 'QSELECT' });
// 4. 添加到"插件" Ribbon 标签页(如果不存在则创建)
context.addRibbonTab({
id: 'plugins',
label: '插件',
groups: []
});
// 5. 添加 Ribbon 组
context.addRibbonGroup('plugins', {
id: 'quick-select',
label: '快速选择',
pinnable: true,
primaryButtons: [
{
icon: 'qselect',
cmd: 'QSELECT',
prompt: '快速选择图元',
type: 'large'
}
]
});
console.log('[快速选择] 插件已激活');
},
async onDeactivate(context: PluginContext): Promise<void> {
// 清理资源(按添加的逆序)
context.removeRibbonGroup('plugins', 'quick-select');
context.removeMenuItem('plugins', 'QSELECT');
context.unregisterCommand('QSELECT');
console.log('[快速选择] 插件已停用');
}
};
export default plugin;API 参考
菜单相关
| 方法 | 说明 |
|---|---|
addMenu(config) | 添加主菜单(已存在则跳过) |
removeMenu(menuId) | 移除主菜单 |
addMenuItem(menuId, config) | 添加菜单项 |
removeMenuItem(menuId, command) | 移除菜单项 |
Ribbon 相关
| 方法 | 说明 |
|---|---|
addRibbonTab(tab, afterTabId?) | 添加标签页(已存在则跳过) |
removeRibbonTab(tabId) | 移除标签页 |
addRibbonGroup(tabId, group) | 添加组到标签页 |
removeRibbonGroup(tabId, groupId) | 移除组 |
addRibbonButton(tabId, groupId, btn, type) | 添加按钮到组 |
removeRibbonButton(tabId, groupId, cmd) | 移除按钮 |
图标相关
| 方法 | 说明 |
|---|---|
registerIcon(name, svg) | 注册 SVG 图标 |
最佳实践
命名规范:
- 命令名使用大写(如
MYCMD) - 图标名使用小写(如
mycmd) - 菜单/组/标签页 ID 使用 kebab-case(如
my-plugin)
- 命令名使用大写(如
资源清理:
- 在
onDeactivate中清理所有注册的资源 - 按照添加的逆序进行清理
- 在
共享资源:
- 使用统一的 ID(如
plugins)让多个插件共享同一菜单/标签页 addMenu和addRibbonTab会自动检测是否已存在
- 使用统一的 ID(如
图标一致性:
- 菜单项自动使用与命令同名的图标
- Ribbon 按钮需要明确指定图标 ID