# 主题与状态栏

> 文档：`vjcad/doc/ui/index.md`（theme + statusbar 段）
> 示例：
> - `vjcad/example/ui/07theme.js` — 主题切换
> - `vjcad/example/ui/08statusbar.js` — 状态栏功能按钮

## 1. 主题切换

vjcad 的"主题"其实是两套互不相干的东西：

1. **`Engine.THEME_MODE`** —— **画布**主题（0 深 / 1 浅）。setter 只做三件事：写 localStorage、同步状态栏主题按钮、自动 `setBgc(0|255)`。**不改菜单 / 对话框 / 侧边栏等 UI 配色**
2. **UI 外壳配色** —— `MainView` 配置 `themeColors: {...}`，或运行时 `cadView.setThemeColors(partial)` / `Engine.setThemeColors(partial)`（`resetThemeColors()` 复原、`getThemeColors()` 读取），写的是 `--theme-color-*` CSS 变量

`Engine.setBgc(gray)` 是画布背景的底层开关，`THEME_MODE` 已经替你调用了。

```js
// 画布切浅色（自动 setBgc(255) 并重绘）
Engine.THEME_MODE = 1;

// 画布切回深色（自动 setBgc(0) → #212830）
Engine.THEME_MODE = 0;

// UI 外壳换色（与 THEME_MODE 无关）
Engine.setThemeColors({ shellBg: '#f3f4f6', font: '#111827', border: '#d1d5db', accent: '#2563eb' });
```

| 值 | 含义 |
|---|---|
| `THEME_MODE = 0` | 画布深色（默认） |
| `THEME_MODE = 1` | 画布浅色 |
| `setBgc(0)` | 默认深色底 `Engine.DARK_BG_COLOR`（`#212830`，不是纯黑） |
| `setBgc(255)` | 白色 `Engine.LIGHT_BG_COLOR`（`#FFFFFF`） |
| `setBgc(1~254)` | 按灰度 `rgb(g,g,g)`（如 33 → `#212121`，与默认 `#212830` 不同） |

## 2. 主题相关辅助函数

```js
Engine.isDarkTheme();                   // boolean
Engine.getThemeBackground();            // CSS 颜色字符串
// ⚠️ 无 Engine.themeColor()——实体的"主题自动反色"用 ACI 颜色 7（见下节）；
// 主题色板读写用 Engine.getThemeColors() / Engine.setThemeColors(partial)
```

## 3. 颜色 7 的特殊性

ACI 颜色 7 在 vjcad 里是"主题色"——

- 深色主题：白色 (`0xffffff`)
- 浅色主题：黑色 (`0x000000`)

写实体时直接 `entity.color = 7` 即可，**不用** 自己根据主题改颜色。

```js
const text = new TextEnt([0, 0], '随主题变化', 10);   // (insertionPoint, textString, height)
text.setDefaults();
text.color = 7;
Engine.addEntities(text);
```

## 4. 状态栏（FuncButtons）

底部状态栏由两块组成：

- **左侧：CoordsBar** —— 实时坐标
- **右侧：FuncButtons** —— 模式开关按钮

```js
const coordsBar  = Engine.view.coordsBar;
const funcButtons = coordsBar.funcButtons;

funcButtons.osnapButton.isOn;       // 对象捕捉是否开
funcButtons.gridmodeButton.isOn;    // 栅格
funcButtons.orthomodeButton.isOn;   // 正交
funcButtons.polarmodeButton.isOn;   // 极轴追踪（字段名是 polarmodeButton，不是 polarButton）
```

按钮状态由对应 Engine 全局变量驱动：

| 变量 | 含义 | 快捷键 |
|---|---|---|
| `Engine.OSMODE` | 对象捕捉位掩码（1~512 各模式位；**16384 位 = 捕捉总开关关闭**，F3 就是切这一位；默认 255） | F3 |
| `Engine.GRIDMODE` | 栅格 0/1 | F7 |
| `Engine.ORTHOMODE` | 正交 0/1 | F8 |
| `Engine.POLARMODE` | 极轴追踪 0/1 | F10 |
| ~~`Engine.SNAPMODE`~~ | ❌ 不存在——栅格捕捉开关在源码里没有独立 sysvar，只有 GRIDMODE/ORTHOMODE 等 | F9 |

```js
// 程序化打开 / 关闭
Engine.ORTHOMODE = 1;
Engine.GRIDMODE  = 1;

// 设置对象捕捉位
const OSNAP = {
    END: 1, MID: 2, CEN: 4, NODE: 8, QUA: 16, INT: 32,
    INS: 64, PER: 128, TAN: 256, NEA: 512
};
Engine.OSMODE = OSNAP.END | OSNAP.MID | OSNAP.CEN;
```

变量改了之后状态栏按钮 **会自动更新**（双向绑定）。

## 5. 主题变更监听

**没有 `CadEvents.ThemeChanged` 事件**。`Engine.THEME_MODE` setter 内部已经做了三件事：写 localStorage（`THEME_CACHE_KEY`）、更新状态栏主题按钮、`pcanvas.setBgc(0|255)`。要感知主题变化，只能包一层 setter 或轮询：

```js
let last = Engine.THEME_MODE;
setInterval(() => {
    if (Engine.THEME_MODE !== last) {
        last = Engine.THEME_MODE;
        console.log('当前主题:', last === 0 ? '深色' : '浅色');
    }
}, 500);
```

## 6. 状态栏自定义按钮（高级）

**没有往状态栏加按钮的 API**。`coordsBar.funcButtons`（`<func-buttons>`）是 Lit 组件，按钮都渲染在它自己的 shadow DOM 里且**没有 `<slot>`**——`funcBar.appendChild(btn)` 只会挂到 light DOM，**不会显示**：

```js
const funcBar = Engine.view.coordsBar.funcButtons;
funcBar.appendChild(myBtn);   // ❌ 无效：无 slot，不渲染
```

要放自定义开关，改用浮动工具栏 [`createFloatingToolbar`](floating-toolbar.md)、Ribbon 按钮（[plugin/menu-ribbon](../plugin/menu-ribbon.md)）或自己定位的 DOM。自绘按钮想跟随 UI 配色，用真实存在的变量 `--theme-color-button-bg` / `--theme-color-button-hover-bg` / `--theme-color-font` / `--theme-color-border`（由 `setThemeColors` 写到 `document.documentElement`）。

## 7. 完全隐藏状态栏

```js
new MainView({
    showBottomBar:   false,         // 不显示底栏（没有 showFuncButtons 键）
    showCoordsBar:   false,         // 不显示坐标
});
// 或：
new MainView({ viewMode: 'viewer' });   // 强制隐藏整个状态栏（viewer 下 showCoordsBar:true 也无效）
```

## 8. 实战：主题持久化

```js
// THEME_MODE setter 自带 localStorage 持久化 + setBgc + 状态栏按钮同步，
// 启动时无需手动读存——赋一次值即可（它内部已写 THEME_CACHE_KEY）
Engine.THEME_MODE = 1;          // 浅色
Engine.isDarkTheme();           // 读当前
Engine.getThemeBackground();    // 当前背景色 hex
```

## 红旗

- `THEME_MODE` 只管画布（并已自动 `setBgc`），**不改 UI 外壳颜色**；想 UI 也变浅要另调 `setThemeColors(...)`（或 MainView `themeColors` 配置）
- `setBgc` 内部已自动重绘（深浅类型变化时 `regen`，否则 `redraw`），无需再调 `Engine.redraw()`；默认深色底是 `setBgc(0)`（`#212830`），不是 33
- 状态栏按钮的 `isOn` 是只读的；要切换状态请改对应 Engine 变量（如 `Engine.ORTHOMODE = 1`），按钮会自动同步
- `Engine.OSMODE` 是 **位掩码**；`= 0` 关闭所有捕捉，全开用 `1023`（1~512 十个模式位）；临时关闭保留各模式用 `Engine.OSMODE |= 16384`，恢复 `&= ~16384`。⚠️ 别写 `0xFFFF`——它含 16384 位，等于**关闭**捕捉
- 颜色 7 在某些导出 / 转 PDF 场景下不会跟随主题（取决于导出器实现），强制可见的题图请显式设颜色
- `viewMode: 'viewer'` 会一并隐藏状态栏，且 viewer 分支只认 `sidebarStyle / enableContextMenu:true / showGrips:true`，`showCoordsBar: true` **无效**；要坐标条就用 `viewMode:'full'` + 细粒度开关（无 showFuncButtons 键）

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/ui/index.md`（theme + statusbar 段）
- 示例：
- `vjcad/example/ui/07theme.js` — 主题切换
- `vjcad/example/ui/08statusbar.js` — 状态栏功能按钮
