7.7 KiB
ProjectPanel
命名空间: XCEngine::Editor
类型: class
源文件: editor/src/panels/ProjectPanel.h
描述: 项目资源浏览面板,负责把 IProjectManager 当前目录模型渲染成“目录树 + breadcrumb + 搜索 + 资源网格 + 上下文菜单 + 拖放”的 Project Browser。
概述
ProjectPanel 当前是 Editor 里项目资源浏览的前端外壳,但它不是资产数据库本身。
它的职责边界比较明确:
- 项目目录模型与选择状态来自 IProjectManager
- 资源打开、创建、删除、重命名、移动等语义落到 ProjectCommands
- 少量通用交互协议由 ProjectActionRouter 提供
- 面板自身主要负责布局、即时模式 UI 状态和延迟动作收口
生命周期与公开入口
- Constructor
创建标题固定为
"Project"的面板。 - Initialize
把项目路径初始化工作委托给当前
IProjectManager。 - Render 执行完整的项目浏览器绘制与交互驱动。
面板内部状态
ProjectPanel 当前长期持有的主要是 UI 状态,而不是项目数据本体:
| 状态 | 作用 |
|---|---|
m_searchBuffer |
当前目录搜索关键字 |
m_navigationWidth |
左侧目录树宽度 |
m_folderTreeState |
目录树展开状态 |
m_renameState |
行内重命名状态 |
m_assetDragDropState |
本帧拖放源 / 目标状态 |
m_deferredContextAction |
延迟到本帧末尾执行的上下文菜单动作 |
初始化与每帧主流程
Initialize(projectPath)
当前实现只有一行:
m_context->GetProjectManager().Initialize(projectPath);
这说明 ProjectPanel 不自己扫描目录,也不自己构建项目树。
Render()
当前每帧主要顺序是:
- 打开
PanelWindowScope - 调用
ObserveFocusedActionRoute(*m_context, EditorActionRoute::Project) BeginAssetDragDropFrame()- 渲染顶部 toolbar
- 渲染左侧目录树与右侧浏览区
FinalizeAssetDragDrop(manager)- 在帧末执行
m_deferredContextAction
这里最关键的两个点是:
- Project 面板会显式声明当前焦点 route
- 拖放提交和上下文菜单命令都在本帧尾部统一收口,避免打断当前 UI 遍历
顶部工具栏
RenderToolbar() 当前负责两件事:
- 左侧显示当前
ResourceManager的 project asset import 状态 - 右侧提供当前目录搜索框
导入状态文本和 tooltip 来自:
ResourceManager::GetProjectAssetImportStatus()AssetImportService::ImportStatusSnapshot
搜索行为则比较克制:
- 只过滤
manager.GetCurrentItems()返回的当前目录条目 - 匹配逻辑使用
UI::SearchQuery::Matches(item->name) - 不做全项目搜索、类型过滤或标签过滤
左侧目录树
数据来源
目录树主要消费:
GetRootFolder()GetCurrentFolder()
节点绘制与交互
每个目录节点当前通过 UI::DrawTreeNode(...) 绘制,并带有:
- 当前目录高亮
- 基于
IsCurrentTreeBranch(...)的默认展开 - 自定义 folder icon prefix
当前支持的主要交互包括:
- 左键点击目录 ->
NavigateToFolder(folder) - 目录作为资源拖放目标
- 空白区右键弹出项目上下文菜单
若根目录不可用,则显示 No Assets Folder 空状态。
右侧浏览区
右侧浏览区分成两部分:
RenderBrowserHeader(manager)RenderBrowserPane(manager)的资源网格主体
Breadcrumb
Header 当前通过 UI::DrawToolbarBreadcrumbs(...) 渲染路径,并消费:
manager.GetPathDepth()manager.GetPathName(index)manager.NavigateToIndex(index)
因此目录导航当前主要依赖目录树和 breadcrumb,而不是单独的“后退按钮”。
资源网格
主体区域当前会:
- 先基于搜索关键字计算
visibleItems - 动态估算卡片高度与列数
- 逐项调用
RenderAssetItem(...) - 在帧末统一处理选中、打开与空白区清选
RenderAssetItem(...) 当前支持:
- 单击选中
- 双击打开
- 右键选中并弹出上下文菜单
- 纹理预览与图标 fallback
- 行内重命名
- 拖放源 / 目录拖放目标
若搜索结果为空,会显示:
No Search ResultsNo assets match the current search
上下文菜单与重命名
行内重命名
当前重命名由 m_renameState 驱动:
BeginRename(item)激活编辑状态DrawInlineRenameFieldAt(...)覆盖卡片 label 区- 提交时调用
CommitRename(...) - 真正的重命名落到
Commands::RenameAsset(...)
若用户没有实际修改显示名称,CommitRename(...) 会直接结束重命名,而不会重复提交文件操作。
右键菜单
DrawProjectContextMenu(...) 当前支持:
Create -> FolderCreate -> MaterialShow in ExploreOpenDeleteRenameCopy Path
其中创建资源采用 deferred action:
- 必要时先导航到目标目录
- 调用
Commands::CreateFolder(...)或Commands::CreateMaterial(...) - 新建成功后自动进入重命名状态
拖放与移动资源
当前拖放逻辑被收口成一套帧内状态机,而不是散落在每个 tile 里。
帧开始
BeginAssetDragDropFrame() 会:
- 清空
m_assetDragDropState - 通过
Actions::GetDraggedProjectAssetPath()识别当前拖拽源
目录作为 drop target
RegisterFolderDropTarget(...) 会:
- 只接受目录目标
- 校验 payload 类型为
ProjectAssetPayloadType() - 用
Commands::CanMoveAssetToFolder(...)判断是否合法 - 在真正投递时记录源路径与目标目录
帧结束统一提交
FinalizeAssetDragDrop(...) 会:
- 根据当前悬停是否合法切换鼠标样式
- 若本帧成功投递,则统一调用
Commands::MoveAssetToFolder(...)
这样可以避免在 UI 遍历过程中直接修改底层目录树。
与项目工作流命令的关系
ProjectPanel 当前直接消费的主要是资源级命令:
CreateFolderCreateMaterialDeleteAssetRenameAssetMoveAssetToFolderOpenAsset
它不负责发起以下项目级入口:
Save ProjectOpen Project...New Project...Rebuild Script Assemblies
这些工作流分别由主菜单和命令层收口。
当外部命令触发 RefreshCurrentFolder() 时,面板会在下一帧反映新的目录树状态。
测试锚点
tests/Editor/test_action_routing.cpp 当前与这条资源链路直接相关的测试包括:
ProjectCommandsCreateFolderMoveAssetAndOpenFolderHelperProjectCommandsCreateFolderUsesUniqueDefaultNameProjectCommandsRenameAssetUpdatesSelectionAndPreservesFileExtensionProjectCommandsRejectInvalidMoveTargetsProjectSelectionSurvivesRefreshWhenItemOrderChangesProjectCommandsRejectMovingFolderIntoItsDescendant
当前限制
- 搜索当前只覆盖当前目录的名称匹配。
- 目录树展开状态当前只保存在内存里。
- 拖拽预览 tooltip 被关闭,拖放反馈比较轻量。
- 资源上下文菜单仍偏基础,没有批量操作或复杂预览。
- 面板本身不是项目保存或脚本重建入口。