唯杰地图 logo唯杰地图
VJMAP
EN

图标注册(IconRegistry)

vjcad/references/ui/icons.md 7.3 KB162 行
原文

文档:vjcad/doc/ui/icons.md 示例:vjcad/example/ui/01ribbon.js、命令 / 插件示例中频繁使用

1. 模型

vjcad 把所有图标做成"按 类别 + 名字 索引"的单例字典:

class IconRegistry {                        // 全部静态方法,无 getInstance
    static getIcon(key: string, category?: IconCategory): string | undefined;
    static registerIcon(key: string, svg: string, options?: IconRegistryOptions): boolean;
    static registerIcons(icons: Record<string, string>, category: IconCategory): void;
    static registerCommandIcon(commandName: string, svg: string, override?: boolean): boolean;
    static getCommandIcon(commandName: string): string | undefined;
    static hasIcon(key: string, category?: IconCategory): boolean;
    static hasCommandIcon(commandName: string): boolean;
    static getIconsByCategory(category: IconCategory): Record<string, string>;
    static getIconInfo(key: string): IconInfo | undefined;
    static removeIcon(key: string, category: IconCategory): boolean;
    static createIconElement(key: string, category?: IconCategory, className?: string): HTMLElement | null;
}

interface IconRegistryOptions {
    category?: IconCategory;   // 默认 Custom
    override?: boolean;        // 是否覆盖已存在图标
}

enum IconCategory {
    Commands = 'commands', Actbar = 'actbar', FuncButtons = 'func-buttons',
    LayerDialog = 'layer-dialog', Menubar = 'menubar', Osnap = 'osnap',
    Palette = 'palette', PaletteLayer = 'palette-layer', PalettePlot = 'palette-plot',
    Misc = 'misc', Custom = 'custom',   // 无 Toolbar/Sidebar/General;默认类别是 Custom
}

注册后并不是所有 UI 都能找到——各 UI 查找的分类不同:

UI 查找方式
Ribbon 按钮 icon、右键菜单 icon、菜单默认的命令图标、命令面板 只查 Commands 分类
菜单项显式 icon 字段 全分类搜索
侧栏 ActivityBar 图标、<tool-bar-button src> 解析 images/<分类>/<名>.svg 路径,到对应分类查
浮动工具栏 icon 不查注册表,只认内联 SVG 串或图片 URL

所以给命令 / Ribbon / 右键用的图标要注册到 IconCategory.Commands(registerCommandIcon 或插件 context.registerIcon)。所有 key 统一小写化存储与查询——getIcon('MYCMD') 和 getIcon('mycmd') 等价。移除用 removeIcon(key, category);重复注册默认 override: true(即静默覆盖)。

⚠️ 不对称:getIcon/hasIcon 的 category 省略时全分类搜索,但 removeIcon 的 category 是必填——removeIcon('my-icon') 不带分类返回 false 删不掉,必须 removeIcon('my-icon', IconCategory.Custom)(注册时默认入 Custom 类)。

2. 注册示例

const ICON_LINE = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
     stroke-linecap="round" stroke-linejoin="round">
    <line x1="4" y1="20" x2="20" y2="4"/>
</svg>`;

IconRegistry.registerIcon('my-line', ICON_LINE, { category: IconCategory.Commands });

// 之后菜单 / Ribbon 用名字引用
{ command: 'MYLINE', label: '我的线', icon: 'my-line' }

不显式指定时 options.category 默认 Custom,不是 General——而 Ribbon / 右键菜单只查 Commands,注册到 Custom 的图标在那里显示不出来。

3. 命令图标的命名约定

图标 key 在注册和查询时都会被 .toLowerCase() 归一化——只有一个 key 空间,大小写不敏感:

context.registerCommand('DRAWCROSS', '绘制十字', DrawCrossCommand, opt);
context.registerIcon('DRAWCROSS', SVG);
// PluginContext.registerIcon(name, svg) → IconRegistry.registerCommandIcon(name.toUpperCase(), svg)
// 最终存进 commands 类别的 key 是 'drawcross'(自动小写化)

一次注册即可——菜单、Ribbon、命令面板查 commands 类别时同样走小写化查询,'DRAWCROSS' 和 'drawcross' 命中同一个图标。

4. 常用获取 / 检查

const svg = IconRegistry.getIcon('line', IconCategory.Commands);
if (!svg) console.warn('未注册');

if (!IconRegistry.hasIcon('my-line', IconCategory.Commands)) {
    IconRegistry.registerIcon('my-line', SVG, { category: IconCategory.Commands });
}

// 覆盖已注册图标(override 默认就是 true,可省):
IconRegistry.registerIcon('my-line', SVG2, { category: IconCategory.Commands });

// 真要移除:
IconRegistry.removeIcon('my-line', IconCategory.Commands);

5. SVG 设计规范

<svg viewBox="0 0 24 24"
     fill="none"
     stroke="currentColor"
     stroke-width="2"
     stroke-linecap="round"
     stroke-linejoin="round">
    <!-- 路径 -->
</svg>

要点:

  • viewBox="0 0 24 24" 与系统其他图标一致;不一致会显示比例错乱
  • stroke="currentColor" 让按钮主题色(深 / 浅)自动套用;写死 #fff 在浅色主题下会"消失"
  • 单色图标用线性描边,stroke-width="2" 视觉密度统一
  • 主体填色推荐 fill="none" 或 fill="currentColor",避免硬编码
  • 多色 / 渐变可以使用,但要在两种主题下都好看

6. 在 MenuBar / Ribbon / Toolbar 中使用

// 菜单项
{ type: 'command', command: 'MYCMD' }   // 自动用 IconRegistry.getIcon('MYCMD', Commands)

// Ribbon 按钮(icon 是 Commands 分类里的图标名;提示字段叫 prompt,没有 tooltip)
{ cmd: 'MYCMD', icon: 'mycmd', label: '我的命令', prompt: '...' }

// 浮动工具栏
{ id: 'mycmd', icon: IconRegistry.getIcon('mycmd', IconCategory.Commands),
  tooltip: '我的命令', command: 'MYCMD' }

7. 内置图标速查(节选)

name(小写) 含义
line pline circle arc ellipse polygon rectang 绘制
move copy rotate scale mirror offset trim extend fillet explode erase 编辑(没有 chamfer)
undo redo regen zoom 系统
hatch text mtext dimlinear dimdiameter dimradius dimaligned angledim 标注 / 填充(角度标注是 angledim,没有 dimangular)

完整名以注册表为准:Object.keys(IconRegistry.getAllCommandIcons())(或 IconRegistry.getIconsByCategory(IconCategory.Commands))列出全部内置命令图标名。

8. 跨插件共享 / 优先级

  • 第二次注册同名图标 会覆盖 之前的(无警告)
  • 这意味着插件可以"皮肤化"内置图标;也意味着插件互相有可能踩
  • 建议:插件自己的图标加前缀 myplugin-foo,避免冲突

红旗

  • 没有正确写 currentColor 时,主题切换图标会"消失"
  • viewBox 不为 24×24 → 在 Ribbon 里被裁切或留白
  • 注册重复名导致 谁后注册谁生效(override 默认 true,无警告);多插件场景务必用前缀
  • 图标 key 大小写不敏感(统一小写化)——注册 'DRAWCROSS' 还是 'drawcross' 结果一样,一次即可
  • SVG 字符串末尾 / 开头不要带 <?xml ... ?> 声明 —— vjcad 内部直接 innerHTML 拼接

参考(官方文档 MCP)

  • 文档:vjcad/doc/ui/icons.md
  • 示例:vjcad/example/ui/01ribbon.js、命令 / 插件示例中频繁使用