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