8.0 KiB
ProjectCommands
命名空间: XCEngine::Editor::Commands
类型: header-helper
源文件: editor/src/Commands/ProjectCommands.h
描述: Project 工作流命令集合,封装项目切换与保存、资源浏览与文件操作,以及脚本程序集重建等高层动作。
概述
ProjectCommands 当前已经不只是“打开资源 + 建目录 + 删除/移动”的薄命令头。
按 ProjectCommands.h 的真实实现,它覆盖四组链路:
- 资源浏览与编辑命令
- 项目文档保存命令
- 脚本程序集重建
- 项目切换与项目目录初始化
和 ProjectPanel 的关系也很清楚:
- Panel 负责 UI 与交互采样
- Commands 负责 guard、规则和 manager / application 调用
资源命令
打开资源
CanOpenAsset(item) 当前只在两种情况下返回 true:
item->isFolderitem->type == "Scene"
OpenAsset(context, item) 则进一步区分:
- 文件夹:
context.GetProjectManager().NavigateToFolder(item) - 场景资源: 转发到
LoadScene
因此当前“打开资源”仍然是收敛语义,不会在这里直接承载脚本、材质或图片编辑器分发。
创建文件夹
CreateFolder(projectManager, name) 当前只做最基本校验:
- 名称不能为空
真正的唯一命名、落盘和刷新由 IProjectManager::CreateFolder 负责。
创建材质
CreateMaterial(projectManager, name) 当前会:
- 读取当前目录与根目录。
- 验证当前目录位于项目根目录内。
- 自动补
.mat扩展名。 - 为重名文件追加数字后缀。
- 直接写出默认材质 JSON。
RefreshCurrentFolder()。- 定位新建条目并设为当前选中项。
因此 ProjectPanel 右键菜单里的 Create -> Material 已经是落地命令,不再只是 UI 预留位。
删除 / 移动 / 重命名
这三类命令当前都只是把高层工作流规则收口后,再委托给 IProjectManager:
DeleteAsset(...)->projectManager.DeleteItem(...)MoveAssetToFolder(...)->projectManager.MoveItem(...)RenameAsset(...)->projectManager.RenameItem(...)
其中 CanMoveAssetToFolder(...) 额外承担了较完整的安全校验,包括:
- 源与目标都必须位于项目根目录内
- 不能移动项目根目录本身
- 不能把目录移动到自己的子目录
- 不能覆盖同名目标
项目保存命令
EnsureProjectStructure(projectPath)
当前会确保以下目录存在:
Assets/Scenes.xceditor
BuildProjectDescriptor(context)
当前会结合:
context.GetProjectPath()context.GetSceneManager().GetCurrentScenePath()
构建项目描述;当没有有效当前场景时,回退到:
Assets/Scenes/Main.xc
SaveProject(context)
这是当前项目保存链路的总入口,主要流程是:
- 检查当前 runtime mode 是否允许文档编辑。
EnsureProjectStructure(context.GetProjectPath())。- 通过
SaveDirtySceneWithFallback(...)保存当前场景,必要时回退到Assets/Scenes/Main.xc。 context.GetProjectManager().RefreshCurrentFolder()。Application::Get().SaveProjectState()。SaveProjectDescriptor(context)。
这说明 ProjectCommands 当前已经超出“Project 面板本地命令”的范畴,开始承担项目文档生命周期本身。
还要注意这个顺序的真实含义:
RefreshCurrentFolder()只会同步Assets浏览树SaveProjectState()和项目描述文件会落在Assets之外的项目级位置
因此“保存项目”成功并不等于 ProjectPanel 一定出现明显可见变化;面板刷新更多是在保证项目资源视图和磁盘状态保持一致。
脚本程序集重建
CanRebuildScriptAssemblies(context)
当前 guard 很明确:
- 文档编辑必须允许
- 项目路径不能为空
RebuildScriptAssemblies(context)
这是 Scripts -> Rebuild Script Assemblies 菜单项的命令层入口。
当前流程是:
- 先走
CanRebuildScriptAssemblies(context)。 - 调用
Application::RebuildScriptingAssemblies。 - 若重建成功,执行
context.GetProjectManager().RefreshCurrentFolder()。 - 把布尔结果直接返回给调用方。
因此它的职责边界是:
ProjectCommands负责 guard 和后置刷新。Application负责真正的程序集构建。
这里有两个很实际的调用语义:
- 返回
false既可能是 guard 不通过,也可能是下游脚本构建失败。 - 即使返回
true,RefreshCurrentFolder()也不代表ProjectPanel一定会出现新条目,因为脚本程序集产物主要位于<Project>/Library/ScriptAssemblies,不在Assets投影视图内。
项目切换与目录选择
SwitchProject(context, projectPath)
当前项目切换主流程是:
- 要求允许文档编辑。
- 要求目标路径非空。
- 通过
SceneEditorUtils::ConfirmSceneSwitch(context)处理场景切换确认。 EnsureProjectStructure(projectPath)。Application::Get().SwitchProject(projectPath)。- 写回
context.SetProjectPath(projectPath)。 context.GetProjectManager().Initialize(projectPath)。context.GetSceneManager().LoadStartupScene(projectPath)。- 清空选择与 undo 历史。
RefreshCurrentFolder()。- 若成功加载启动场景,则
SaveProjectDescriptor(context)。
这说明 ProjectCommands 现在也是 Project 生命周期切换的统一入口,而不是只服务 ProjectPanel。
这里的重置动作也很关键:
ClearSelection()保证旧项目里的资源 / 实体选择不会泄漏到新项目上下文ClearHistory()保证旧项目的撤销栈不会跨项目复用
NewProjectWithDialog(context) / OpenProjectWithDialog(context)
这两个 helper 都通过 FileDialogUtils::PickFolderDialog(...) 选择目录,然后回到 SwitchProject(...)。
也就是说:
- 对话框归它们负责
- 真正的切换规则仍收口在
SwitchProject(...)
设计说明
当前 ProjectCommands 的设计方向很清楚:
- 把资源管理规则集中化
- 把项目文档保存链路集中化
- 把项目维护类动作从菜单 / 面板层抽离出来
- 让
ProjectPanel只消费命令,而不自己拼文件系统规则
当前最值得明确的一点是:旧文档里提到的项目级批量场景迁移入口已经不在这个头文件里,当前保留下来的项目维护动作只有:
- 保存项目
- 重建脚本程序集
- 切换项目 / 新建项目 / 打开项目
这才是当前源码里的真实分层。
测试锚点
tests/Editor/test_action_routing.cpp 当前直接覆盖或锚定了这组命令的关键行为:
ProjectCommandsReportWhenScriptAssembliesCanBeRebuiltProjectCommandsCreateFolderMoveAssetAndOpenFolderHelperProjectCommandsCreateFolderUsesUniqueDefaultNameProjectCommandsRenameAssetUpdatesSelectionAndPreservesFileExtensionProjectCommandsRejectInvalidMoveTargetsProjectCommandsRejectMovingFolderIntoItsDescendant
当前限制
- 资源打开仍然只覆盖文件夹和场景。
- 创建材质当前写的是内置默认 JSON 骨架,没有更复杂模板选择。
- 删除、移动、重命名仍然没有事务预览或冲突解决 UI。
- 资源复制 / 粘贴 / 重新导入仍未形成命令层协议。
- 脚本重建当前只返回布尔值,不负责弹窗反馈或进度 UI;主菜单层也不会消费这个返回值。