# UI 与插件系统

Ribbon 工具栏、内置对话框、上下文菜单、插件系统。

如需创建独立插件项目，请参阅 [plugin-development.md](plugin-development.md)。

如需创建自定义对话框，请参阅 [custom-dialog.md](custom-dialog.md)。

## Ribbon 工具栏

### 添加自定义标签页

```javascript
const { RibbonConfigManager, CommandRegistry, CommandDefinition, CommandOptions } = vjcad;

// 1. 先注册命令
class MyToolCommand {
    async main() {
        console.log('执行我的工具');
    }
}

const opt = new CommandOptions();
opt.useAutoComplete = true;
CommandRegistry.regist(new CommandDefinition('MYTOOL', '我的工具', MyToolCommand, opt));

// 2. 创建标签页配置
const newTab = {
    id: 'my-tab',
    label: '自定义',
    groups: [{
        id: 'my-tools',
        label: '我的工具',
        primaryButtons: [
            {
                cmd: 'MYTOOL',
                icon: 'circle',
                label: '创建圆',
                prompt: '创建一个圆'      // Ribbon 按钮的悬停提示字段是 prompt（没有 tooltip）
            }
        ],
        moreButtons: [
            { cmd: 'ZOOM', icon: 'zoom', label: '缩放' }
        ]
    }]
};

// 3. 添加到配置并刷新
const currentConfig = RibbonConfigManager.getConfig();
currentConfig.tabs.push(newTab);
RibbonConfigManager.refresh();
```

### 添加到现有标签页

```javascript
// 添加组到现有标签页
Engine.view.ribbonBar.addGroup('plugins', {
    id: 'my-plugin-group',
    label: '我的插件',
    pinnable: true,
    primaryButtons: [
        {
            icon: 'mygrid',
            cmd: 'MYGRID',
            prompt: '绘制网格',
            type: 'large'  // 'large' | 'small'
        }
    ]
});

// 移除组
Engine.view.ribbonBar.removeGroup('plugins', 'my-plugin-group');
```

---

## 侧边栏面板扩展（SidebarPanel）

> 用于在左 / 右侧侧边栏中加入自定义面板，例如工具面板、信息面板。

```javascript
const { BasePanelComponent, registerSidebarPanel, unregisterSidebarPanel, activateSidebarPanel } = vjcad;

// 1. 定义面板组件（继承 BasePanelComponent）
class MyToolsPanel extends BasePanelComponent {
    static styles = [BasePanelComponent.baseStyles];
    render() {
        return html`<div class="panel-title">我的工具</div>`;
    }
}

// 2. 注册到侧边栏
registerSidebarPanel({
    name: "my-tools",
    label: "我的工具",
    icon: "./images/actbar/actbar-draw-settings.svg",
    position: "left",        // "left" | "right"
    panelClass: MyToolsPanel,
    order: 10                // ⚠️ 1.0.16 不生效：实际按注册顺序排列
});

// 3. 激活 / 注销
activateSidebarPanel("my-tools");     // 切换到该面板
// unregisterSidebarPanel("my-tools");  // 注销面板
```

### 控制属性面板与内置面板位置

```javascript
const { MainView } = vjcad;

const cadView = new MainView({
    // ... 其他配置

    // 选择实体时是否自动切换到特性面板
    autoActivatePropertyPanel: false,

    // 配置 7 个内置面板的左右位置
    // 可用名称: "props" | "blocks" | "images" | "dsettings" | "commands" | "alias" | "history"
    leftPanels: ["props", "dsettings"],                          // 左侧
    rightPanels: ["blocks", "images", "commands", "alias", "history"], // 右侧
});
```

---

## 浮动工具栏（FloatingToolbar）

> 可拖动的图标工具栏，支持横式/竖式布局、标题栏、单例管理。

```javascript
const { createFloatingToolbar, IconRegistry, IconCategory } = vjcad;

// 获取内置图标（不存在时返回 undefined）
function cmdIcon(name) {
    return IconRegistry.getIcon(name, IconCategory.Commands);
}

// 创建横式工具栏（4列，带标题）
const toolbar = createFloatingToolbar('draw-tools', {
    title: '绘图工具',           // 设置后显示标题栏；空字符串或省略则无标题
    columns: 4,                  // 固定列数（横式）；用 rows 则为竖式
    iconSize: 30,                // 图标按钮尺寸(px)
    position: { top: '80px', right: '20px' },  // 初始位置
    items: [
        { id: 'line',   icon: cmdIcon('line'),   tooltip: '直线',   command: 'LINE' },
        { id: 'circle', icon: cmdIcon('circle'), tooltip: '圆',     command: 'CIRCLE' },
        { id: 'arc',    icon: cmdIcon('arc'),    tooltip: '圆弧',   command: 'ARC' },
        // 自定义 onClick 代替 command
        { id: 'zoomext', icon: '<svg>...</svg>', tooltip: '缩放全图',
          onClick: () => Engine.zoomExtents() },
    ],
});
toolbar.show();

// 动态增删
toolbar.addItem({ id: 'spline', icon: cmdIcon('spline'), tooltip: '样条曲线', command: 'SPLINE' });
toolbar.removeItem('spline');

// 同 id 重复调用返回已有实例（单例）
const same = createFloatingToolbar('draw-tools', {});  // === toolbar

// 管理方法
toolbar.hide();       // 隐藏
toolbar.show();       // 显示
toolbar.destroy();    // 销毁
```

---

## 消息输出

```javascript
const { writeMessage, message } = vjcad;

// 输出到命令行（支持 HTML）
writeMessage("普通消息");
writeMessage("<br/>换行消息");
writeMessage("<br/><span style='color:red'>红色警告</span>");
writeMessage("<br/><b>粗体消息</b>");

// 页面顶部居中的浮动提示（toast，默认 3 秒自动消失；不是控制台输出）
message.info("信息提示");
```

---

## 对话框

### 确认对话框

```javascript
const { showConfirm, showWarningConfirm, showInfo, showError } = vjcad;

// 简单确认
const ok = await showConfirm("确定要删除吗？", "确认删除");
// ok: boolean（true=确定）

// 警告确认（同样返回 boolean）
const ok2 = await showWarningConfirm("此操作不可恢复！", "警告");

// 信息提示
await showInfo("操作已完成", "提示");

// 错误提示
await showError("操作失败：文件不存在", "错误");
```

### 高级确认对话框

```javascript
const { YesNoDialog, YesNoDialogConfig } = vjcad;

const config = new YesNoDialogConfig({
    title: "保存更改",
    message: "文件已修改，是否保存更改？",
    type: "warning",           // 'info' | 'warning' | 'error' | 'confirm'
    yesTitle: "保存(S)",
    noTitle: "不保存(N)",
    showCancel: true,
    cancelTitle: "取消"
});

const dialog = new YesNoDialog();
const result = await dialog.showMessageBox(config);
// result: 'yes' | 'no' | 'cancel' | undefined（直接关闭时）
```

### 输入对话框

```javascript
const { showPrompt, showInputDialog, showSelectDialog } = vjcad;

// 简单输入
const name = await showPrompt("请输入名称:", "默认值", "输入");
// 返回字符串或 null

// 带验证的输入
const result = await showInputDialog({
    title: "设置圆半径",
    label: "请输入半径值 (1-100):",
    placeholder: "输入数字...",
    defaultValue: "25",
    required: true,
    type: "text",  // 'text' | 'password'
    validator: (value) => {
        const num = parseFloat(value);
        if (isNaN(num)) return "请输入有效数字";
        if (num < 1 || num > 100) return "半径必须在 1-100 之间";
        return null;  // null 表示验证通过
    },
    description: "输入的半径将用于创建新圆形"
});

if (result.confirmed) {
    const radius = parseFloat(result.value);
}

// 下拉选择
const sel = await showSelectDialog({
    title: "选择线型",
    label: "请选择线型:",
    options: [
        { value: "Continuous", label: "Continuous (实线)" },
        { value: "DASHED", label: "DASHED (虚线)" },
        { value: "CENTER", label: "CENTER (中心线)" }
    ],
    defaultValue: "Continuous",
    description: "选择的线型将应用于新创建的直线"
});
```

---

## 上下文菜单

### 基本用法

```javascript
const { CadEventManager, CadEvents, Engine } = vjcad;

const events = CadEventManager.getInstance();

events.on(CadEvents.ContextMenuOpening, (args) => {
    // 在默认菜单之前添加
    args.prependItems.push({
        label: "我的工具",
        icon: "tool",
        submenu: [
            {
                label: "画圆",
                icon: "circle",
                callback: () => Engine.editor.executerWithOp('CIRCLE')
            },
            {
                label: "画线",
                icon: "line",
                callback: () => Engine.editor.executerWithOp('LINE')
            }
        ]
    });
    
    // 分隔线
    args.prependItems.push({ isSeparator: true });
    
    // 在默认菜单之后添加
    args.appendItems.push({
        label: "缩放全图",
        shortcut: "Z+E",
        callback: () => Engine.zoomExtents()
    });
});
```

### 根据命令状态定制

```javascript
events.on(CadEvents.ContextMenuOpening, (args) => {
    if (args.isCommandActive) {
        // 有命令执行中
        if (args.activeCommandName === 'LINE') {
            args.appendItems.push({
                label: "闭合",
                callback: () => writeMessage("<br/>C")
            });
        }
    } else {
        // 空闲状态
        args.prependItems.push({
            label: "快捷命令",
            submenu: [
                { label: "画线", command: "LINE" },
                { label: "画圆", command: "CIRCLE" }
            ]
        });
    }
});
```

### 完全自定义菜单

```javascript
events.on(CadEvents.ContextMenuOpening, (args) => {
    args.useDefaultItems = false;  // 禁用默认项
    args.prependItems.push({
        label: "自定义菜单",
        callback: () => { /* ... */ }
    });
});
```

### 禁止右键菜单

```javascript
events.on(CadEvents.ContextMenuOpening, (args) => {
    args.cancel = true;
});
```

### 菜单项属性

| 属性 | 说明 |
|------|------|
| `label` | 菜单文本 |
| `icon` | 图标名称 |
| `shortcut` | 快捷键文本 |
| `callback` | 点击回调函数 |
| `command` | 命令名称 |
| `submenu` | 子菜单数组 |
| `isSeparator` | 是否为分隔线 |
| `disabled` | 是否禁用 |

---

## 插件系统

### 插件基本结构

```javascript
class MyPlugin {
    constructor() {
        this.name = "MyPlugin";
        this.version = "1.0.0";
        this.description = "示例插件";
        this.commands = [];
        this.eventHandlers = [];
    }
    
    install() {
        this.registerCommands();
        this.addEventListeners();
        this.initUI();
    }
    
    uninstall() {
        // 移除事件监听
        this.eventHandlers.forEach(handler => {
            Engine.eventManager.off(handler.event, handler.callback);
        });
        
        // 注销命令
        this.commands.forEach(cmdName => {
            CommandRegistry.unregist(cmdName);
        });
        
        // 移除 UI
        Engine.view.ribbonBar.removeGroup('plugins', 'my-plugin-group');
    }
    
    registerCommands() {
        class DrawGridCommand {
            async main() {
                // 绘制网格逻辑
            }
        }
        
        CommandRegistry.regist(
            new CommandDefinition('MYGRID', '绘制网格', DrawGridCommand)
        );
        this.commands.push('MYGRID');
    }
    
    addEventListeners() {
        const onEntityAdded = (args) => {
            console.log('实体已添加:', args.entity.type);
        };
        
        Engine.eventManager.on(CadEvents.EntityAdded, onEntityAdded);
        this.eventHandlers.push({
            event: CadEvents.EntityAdded,
            callback: onEntityAdded
        });
    }
    
    initUI() {
        // 注册图标
        IconRegistry.registerCommandIcon('MYGRID', '<svg>...</svg>');
        
        // 添加 Ribbon 组
        Engine.view.ribbonBar.addGroup('plugins', {
            id: 'my-plugin-group',
            label: '我的插件',
            primaryButtons: [
                { icon: 'mygrid', cmd: 'MYGRID', prompt: '绘制网格', type: 'large' }
            ]
        });
    }
}

// 安装插件
const plugin = new MyPlugin();
plugin.install();
```

### 标准插件结构（Plugin 接口）

```javascript
export default {
    manifest: {
        id: 'my-demo-plugin',
        name: '示例插件',
        version: '1.0.0',
        author: 'vjmap.com',
        description: '演示标准插件结构',
        keywords: ['demo', 'grid']
    },
    
    onLoad(context) {
        console.log('插件已加载');
    },
    
    onActivate(context) {
        // 注册图标
        context.registerIcon('MYGRID', '<svg>...</svg>');
        
        // 注册命令
        context.registerCommand('MYGRID', '绘制网格', DrawGridCommand);
        
        // 添加菜单项
        context.addMenuItem('tool', { command: 'MYGRID' });
        
        // 添加 Ribbon 组
        context.addRibbonGroup('plugins', {
            id: 'my-demo',
            label: '示例工具',
            primaryButtons: [
                { icon: 'mygrid', cmd: 'MYGRID', prompt: '绘制网格', type: 'large' }
            ]
        });
    },
    
    onDeactivate(context) {
        console.log('插件已停用');
    },
    
    onUnload(context) {
        console.log('插件已卸载');
    }
};
```

### PluginManager API

```javascript
const { PluginManager } = vjcad;

const pm = PluginManager.getInstance();

// 加载插件
await pm.loadFromContent(jsContent, cssContent);   // 从代码内容
await pm.loadFromUrl(jsUrl, cssUrl);               // 从 URL
await pm.loadFromPath(jsPath, cssPath);            // 从本地路径

// 插件管理
pm.getLoadedPlugins();      // 获取已加载插件列表
pm.getPluginInfo(id);       // 获取指定插件信息
pm.isLoaded(id);            // 检查是否已加载
pm.isActive(id);            // 检查是否已激活
pm.activate(id);            // 激活插件
pm.deactivate(id);          // 停用插件
pm.unload(id);              // 卸载插件
```

### 插件生命周期

1. `onLoad` - 插件加载时调用（只调用一次）
2. `onActivate` - 插件激活时调用（可多次调用）
3. `onDeactivate` - 插件停用时调用（可多次调用）
4. `onUnload` - 插件卸载时调用（只调用一次）

