Files
XCEngine/docs/api/XCEngine/Editor/Managers/ProjectManager/ProjectManager.md

209 lines
7.2 KiB
Markdown
Raw Normal View History

2026-03-27 14:40:29 +08:00
# ProjectManager
**命名空间**: `XCEngine::Editor`
**类型**: `class`
**源文件**: `editor/src/Managers/ProjectManager.h`
2026-04-04 01:02:57 +08:00
**描述**: `IProjectManager` 的默认实现,负责把项目 `Assets` 目录扫描成 `AssetItem` 树,并提供路径导航、选择同步与基础文件操作。
2026-03-27 14:40:29 +08:00
## 概述
2026-04-04 01:02:57 +08:00
`ProjectManager` 当前不是 `AssetDatabase`,也不是资源导入服务;但它也已经不只是“轻量文件浏览器”。
2026-03-27 14:40:29 +08:00
2026-04-04 01:02:57 +08:00
按当前实现,它承担三类稳定职责:
2026-03-27 14:40:29 +08:00
2026-04-04 01:02:57 +08:00
-`<Project>/Assets` 扫描为 `AssetItem` 树,供 [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md) 消费
- 维护浏览路径、当前选择与刷新后的状态恢复
- 处理创建、删除、移动、重命名等基础文件系统操作
2026-03-27 14:40:29 +08:00
2026-04-04 01:02:57 +08:00
因此它更准确的定位是:
2026-03-27 14:40:29 +08:00
2026-04-04 01:02:57 +08:00
- editor 侧项目文件系统投影层
- Project Browser 的默认执行层
2026-03-27 14:40:29 +08:00
2026-04-04 01:02:57 +08:00
而不是 engine 侧资产数据库本身。
2026-03-27 14:40:29 +08:00
2026-04-04 01:02:57 +08:00
## 内部状态模型
当前长期持有的核心状态只有四类:
| 状态 | 作用 |
|------|------|
| `m_rootFolder` | 当前 `Assets` 根节点。 |
| `m_path` | 当前浏览路径的目录节点栈,也是面包屑来源。 |
| `m_selectedItemPath` | 当前选中项的完整路径字符串。 |
| `m_projectPath` | 当前项目根目录。 |
这套设计的直接收益是:
- 刷新树时,不需要保留旧 `AssetItem` 指针身份
- 只要路径还能重新解析,当前目录和当前选中项就能在重建后恢复
## 扫描与类型推断
### 根目录初始化
[Initialize](Initialize.md) 当前固定把 `<project>/Assets` 当作根目录:
- 若目录不存在,会先创建 `Assets/``Assets/Scenes/`
- 根节点名称固定重写为 `Assets`
- 初始化后 `m_path` 只包含根节点
它不会额外创建旧文档里提到的演示子目录,也不会在这里预热资产数据库。
### 递归扫描
`ScanDirectory(...)` 当前会:
- 遍历目录项
- 对子目录递归构建子 `AssetItem`
- 跳过显示层面的 `.meta` 文件
- 把目录排在文件前面,再按名称排序
这意味着 `ProjectPanel` 看到的是“过滤掉 `.meta` 侧车文件后的浏览树”,而不是磁盘上的逐项镜像。
### 类型推断
文件项当前主要按扩展名启发式推断:
| 扩展名 | 当前类型 |
|------|------|
| 图片扩展名 | `Texture` |
| `.fbx/.obj/.gltf/.glb` | `Model` |
| `.cs/.cpp/.h` | `Script` |
| `.mat` | `Material` |
| `.xc/.unity/.scene` | `Scene` |
| `.prefab` | `Prefab` |
| 其他 | `File` |
同时还会记录:
- `extensionLower`
- `isImageAsset`
- `canUseImagePreview`
`ProjectPanel` 的图标与缩略图策略使用。
## 路径导航与选择恢复
### 路径导航
当前路径相关公开 API 基本都围绕 `m_path` 工作:
- `NavigateToFolder(...)`
- 重新解析从根到目标目录的完整路径
- 成功后清空当前选择
- `NavigateBack()`
- 只在 `m_path.size() > 1` 时后退
- 后退后清空当前选择
- `NavigateToIndex(index)`
- 直接截断面包屑到指定层级
- 同样会清空当前选择
`GetCurrentPath()` 始终返回从 `"Assets"` 开始的逻辑路径,而不是磁盘绝对路径。
### 选择恢复
`ProjectManager` 当前把选择保存成 `m_selectedItemPath`,而不是保留旧 `AssetItemPtr`
因此:
- `RefreshCurrentFolder()` 重建目录树后,只要同一路径仍存在,选择可以恢复
- 若原路径不存在,则 `SyncSelection()` 自动清除选择
这也是 `ProjectSelectionSurvivesRefreshWhenItemOrderChanges` 能成立的根本原因。
## 基础文件操作
当前真正值得文档化的,不是 getter / setter而是这几组带规则的写操作。
### 创建目录
[CreateFolder](CreateFolder.md) 当前会:
- trim 名称
- 拒绝空名、`.``..` 和 Windows 非法文件名字符
- 在当前目录下生成唯一目录名,如 `New Folder 1`
- 刷新树并自动选中新目录
### 删除资源
[DeleteItem](DeleteItem.md) 当前会:
- 拒绝空路径、根目录本身和项目根之外的路径
- 删除目标本体
- 额外删除同名 `.meta` sidecar
- 必要时清空选择
- 刷新目录树
这里删除 `.meta` 并不是“顺手清理垃圾文件”,而是为了避免资源本体被移除后仍残留旧 GUID / 导入元数据。
### 移动资源
[MoveItem](MoveItem.md) 当前会:
- 要求源和目标都位于当前 `Assets` 根目录之下
- 要求目标是已存在目录
- 禁止移动根目录本身
- 禁止把目录移动到自己的子目录
- 禁止覆盖已有同名目标
- 成功后同步移动 `.meta` sidecar
同步移动 sidecar 的意义同样是保持资源身份与导入配置跟随资源本体一起迁移,而不是把 `.meta` 留在旧路径。
### 重命名资源
[RenameItem](RenameItem.md) 当前会:
- trim 并校验新名字
- 对文件项在“不写扩展名”时自动保留原扩展名
- 支持 Windows 下 case-only rename
- 成功后同步重命名 `.meta` sidecar
- 若被重命名项正好是当前选中项,会同步更新 `m_selectedItemPath`
因此当前重命名语义已经不只是 UI 上改显示名而是显式维护资源路径、sidecar 和选择状态的一致性。
## 公开方法
| 方法 | 说明 |
|------|------|
| [Initialize](Initialize.md) | 初始化项目根、确保 `Assets/Scenes` 存在并建立根目录树。 |
| [RefreshCurrentFolder](RefreshCurrentFolder.md) | 重建目录树并尽量保留当前路径与当前选择。 |
| [CreateFolder](CreateFolder.md) | 在当前目录创建唯一命名的新文件夹。 |
| [DeleteItem](DeleteItem.md) | 删除项目内资源或目录,并移除同名 `.meta` sidecar。 |
| [MoveItem](MoveItem.md) | 在项目目录内移动资源或目录,并同步移动 `.meta` sidecar。 |
| [RenameItem](RenameItem.md) | 重命名资源或目录,支持保留扩展名与 case-only rename。 |
其余 getter / setter / 导航方法当前语义较直接,类型页中已概述,不再逐个拆页。
## 测试与行为锚点
2026-04-04 01:05:04 +08:00
和当前 `ProjectManager` 直接相关的测试锚点主要在 `tests/Editor/test_action_routing.cpp`
2026-04-04 01:02:57 +08:00
- `ProjectCommandsCreateFolderMoveAssetAndOpenFolderHelper`
- `ProjectCommandsCreateFolderUsesUniqueDefaultName`
- `ProjectCommandsRenameAssetUpdatesSelectionAndPreservesFileExtension`
- `ProjectCommandsRejectInvalidMoveTargets`
- `ProjectSelectionSurvivesRefreshWhenItemOrderChanges`
- `ProjectCommandsRejectMovingFolderIntoItsDescendant`
这些测试虽然多数通过 `ProjectCommands` 外层 helper 进入,但已经把 `ProjectManager` 的真实文件系统行为钉得比较牢。
## 当前限制
- 当前仍是主动扫描树,不是文件系统监听器模型。
- 类型识别仍然是扩展名启发式,不直接查询资产数据库。
- 目录扫描和大部分文件系统错误当前都以“吞异常并保持可用”为主,诊断能力较弱。
- `.meta` 文件只在变更操作时被显式跟随处理,不会单独显示在浏览树里。
2026-03-27 14:40:29 +08:00
## 相关文档
- [Managers](../Managers.md)
- [IProjectManager](../../Core/IProjectManager/IProjectManager.md)
2026-04-04 01:02:57 +08:00
- [ProjectCommands](../../Commands/ProjectCommands/ProjectCommands.md)
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
2026-03-27 14:40:29 +08:00
- [AssetItem](../../Core/AssetItem/AssetItem.md)
2026-04-04 01:02:57 +08:00
- [AssetDatabase](../../../Core/Asset/AssetDatabase/AssetDatabase.md)