From fd2d8f9b347377e21f5b425d77165c334ae6d19a Mon Sep 17 00:00:00 2001 From: atdunbg Date: Sun, 4 Oct 2026 00:55:19 +0800 Subject: [PATCH] fix(editor): asset browser directory navigation The file list was hardwired to the root directory, so it was always empty (all assets live in subdirectories). Directories are now selectable from both the tree (click row) and the list (click [D] row), with a breadcrumb + Root button. Prefix check done manually (path::starts_with is missing in this libstdc++). --- App/editor/AssetBrowserPanel.cpp | 63 +++++++++++++++++++++++++++++--- App/editor/AssetBrowserPanel.h | 2 + 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/App/editor/AssetBrowserPanel.cpp b/App/editor/AssetBrowserPanel.cpp index d3052cb..0749ae2 100644 --- a/App/editor/AssetBrowserPanel.cpp +++ b/App/editor/AssetBrowserPanel.cpp @@ -111,15 +111,50 @@ void AssetBrowserPanel::Draw(const EditorContext& ctx) ImGui::Separator(); const std::filesystem::path root(kRootPaths[m_Root]); + // 根切换 / 首帧:当前目录回根。前缀比较手写(path::starts_with + // 是 libstdc++ 11 才补齐的成员,本机 GCC 没有);补 '/' 分隔校验 + // 防 "assets_x" 之类误匹配 + const std::string rootStr = root.generic_string(); + const std::string curStr = m_CurrentDir.generic_string(); + const bool under = curStr == rootStr || + (curStr.size() > rootStr.size() && + curStr.compare(0, rootStr.size(), rootStr) == 0 && + curStr[rootStr.size()] == '/'); + if (m_CurrentDir.empty() || !under) + { + m_CurrentDir = root; + } - // ---- 左:目录树 | 右:文件列表 ---- + // ---- 顶栏下:面包屑(当前目录相对根路径 + 回根按钮)---- + if (ImGui::Button("Root")) + { + m_CurrentDir = root; + } + ImGui::SameLine(); + std::string rel = std::filesystem::relative(m_CurrentDir, root) + .generic_string(); + ImGui::TextDisabled("%s/%s", kRootNames[m_Root], + rel.empty() ? "" : rel.c_str()); + ImGui::Separator(); + + // ---- 左:目录树 | 右:当前目录文件列表 ---- const float treeWidth = 180.0f; ImGui::BeginChild("##tree", ImVec2(treeWidth, 0.0f), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders); if (std::filesystem::exists(root)) { + // 根节点也画进树(可点击选中) ImGui::SetNextItemOpen(true, ImGuiCond_Once); - DrawDirTree(ctx, root); + if (ImGui::TreeNodeEx(kRootNames[m_Root], + ImGuiTreeNodeFlags_OpenOnArrow)) + { + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) + { + m_CurrentDir = root; + } + DrawDirTree(ctx, root); + ImGui::TreePop(); + } } else { @@ -130,9 +165,9 @@ void AssetBrowserPanel::Draw(const EditorContext& ctx) ImGui::SameLine(); ImGui::BeginChild("##files", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders); - if (std::filesystem::exists(root)) + if (std::filesystem::exists(m_CurrentDir)) { - DrawFileList(ctx, root); + DrawFileList(ctx, m_CurrentDir); } ImGui::EndChild(); @@ -149,7 +184,15 @@ void AssetBrowserPanel::DrawDirTree(const EditorContext& ctx, continue; } const std::string name = entry.path().filename().string(); - if (ImGui::TreeNode(name.c_str())) + // OpenOnArrow:单击行 = 选中(列表切换),箭头 = 折叠 + const bool open = ImGui::TreeNodeEx( + name.c_str(), ImGuiTreeNodeFlags_OpenOnArrow | + ImGuiTreeNodeFlags_SpanFullWidth); + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) + { + m_CurrentDir = entry.path(); + } + if (open) { DrawDirTree(ctx, entry.path()); ImGui::TreePop(); @@ -172,6 +215,16 @@ void AssetBrowserPanel::DrawFileList(const EditorContext& ctx, { if (entry.is_directory()) { + // 子目录也进列表:单行点击进入(树/列表双向导航) + const std::string dname = entry.path().filename().string(); + ImGui::PushID(dname.c_str()); + ImGui::TextUnformatted("[D]"); + ImGui::SameLine(); + if (ImGui::Selectable(dname.c_str())) + { + m_CurrentDir = entry.path(); + } + ImGui::PopID(); continue; } const Kind kind = KindOf(entry.path()); diff --git a/App/editor/AssetBrowserPanel.h b/App/editor/AssetBrowserPanel.h index e759281..c10c447 100644 --- a/App/editor/AssetBrowserPanel.h +++ b/App/editor/AssetBrowserPanel.h @@ -31,6 +31,8 @@ private: // 固定根(下标即身份):项目资产 / 场景文件 int m_Root = 0; + // 当前浏览目录(点目录树切换;列表显示这里的文件) + std::filesystem::path m_CurrentDir; // 类型过滤(0 = All;其余见 kTypeNames) int m_TypeFilter = 0; // 名字子串过滤(空 = 不过滤)