#include "Actions/ActionRouting.h" #include "Actions/ProjectActionRouter.h" #include "Commands/ProjectCommands.h" #include "ProjectPanel.h" #include "Core/IEditorContext.h" #include "Core/IProjectManager.h" #include "Core/AssetItem.h" #include "UI/UI.h" #include namespace XCEngine { namespace Editor { ProjectPanel::ProjectPanel() : Panel("Project") { } void ProjectPanel::Initialize(const std::string& projectPath) { m_context->GetProjectManager().Initialize(projectPath); } void ProjectPanel::Render() { UI::PanelWindowScope panel(m_name.c_str()); if (!panel.IsOpen()) { return; } Actions::ObserveFocusedActionRoute(*m_context, EditorActionRoute::Project); auto& manager = m_context->GetProjectManager(); UI::PanelToolbarScope toolbar("ProjectToolbar", UI::ProjectPanelToolbarHeight()); if (toolbar.IsOpen()) { bool canGoBack = manager.CanNavigateBack(); if (Actions::DrawToolbarAction(Actions::MakeNavigateBackAction(canGoBack), UI::ProjectBackButtonSize())) { manager.NavigateBack(); } ImGui::SameLine(); size_t pathDepth = manager.GetPathDepth(); UI::DrawToolbarBreadcrumbs( "Assets", pathDepth, [&](size_t index) { return manager.GetPathName(index); }, [&](size_t index) { manager.NavigateToIndex(index); }); UI::DrawToolbarRowGap(); UI::ToolbarSearchField("##Search", "Search assets", m_searchBuffer, sizeof(m_searchBuffer)); } UI::PanelContentScope content( "ProjectContent", UI::AssetPanelContentPadding(), ImGuiWindowFlags_None, true, UI::AssetGridSpacing()); if (!content.IsOpen()) { return; } const float tileWidth = UI::AssetTileSize().x; const float spacing = UI::AssetGridSpacing().x; const float panelWidth = ImGui::GetContentRegionAvail().x; int columns = static_cast((panelWidth + spacing) / (tileWidth + spacing)); if (columns < 1) columns = 1; auto& items = manager.GetCurrentItems(); std::string searchStr = m_searchBuffer; int displayedCount = 0; for (int i = 0; i < (int)items.size(); i++) { if (!searchStr.empty()) { if (items[i]->name.find(searchStr) == std::string::npos) { continue; } } if (displayedCount > 0 && displayedCount % columns != 0) { ImGui::SameLine(); } RenderAssetItem(items[i], i); displayedCount++; } if (displayedCount == 0) { UI::DrawEmptyState( searchStr.empty() ? "No Assets" : "No Search Results", searchStr.empty() ? "Current folder is empty" : "No assets match the current search"); } if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered()) { manager.SetSelectedIndex(-1); } m_itemContextMenu.ConsumeOpenRequest("ItemContextMenu"); if (UI::BeginPopup("ItemContextMenu")) { if (m_itemContextMenu.HasTarget()) { Actions::DrawProjectAssetContextActions(*m_context, m_itemContextMenu.TargetValue()); } UI::EndPopup(); } if (!ImGui::IsPopupOpen("ItemContextMenu") && !m_itemContextMenu.HasPendingOpenRequest()) { m_itemContextMenu.Clear(); } if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(1) && !ImGui::IsAnyItemHovered()) { ImGui::OpenPopup("EmptyContextMenu"); } if (UI::BeginPopup("EmptyContextMenu")) { Actions::DrawProjectEmptyContextActions(m_createFolderDialog); UI::EndPopup(); } Actions::DrawProjectCreateFolderDialog(*m_context, m_createFolderDialog); } void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) { auto& manager = m_context->GetProjectManager(); bool isSelected = (manager.GetSelectedIndex() == index); ImGui::PushID(index); const bool isDraggingThisItem = Actions::IsProjectAssetBeingDragged(item); const UI::AssetIconKind iconKind = item->isFolder ? UI::AssetIconKind::Folder : UI::AssetIconKind::File; const UI::AssetTileResult tile = UI::DrawAssetTile( item->name.c_str(), isSelected, isDraggingThisItem, [&](ImDrawList* drawList, const ImVec2& iconMin, const ImVec2& iconMax) { UI::DrawAssetIcon(drawList, iconMin, iconMax, iconKind); }); if (tile.clicked) { manager.SetSelectedIndex(index); } if (tile.contextRequested) { manager.SetSelectedIndex(index); m_itemContextMenu.RequestOpen(item); } Actions::AcceptProjectAssetDrop(manager, item); Actions::BeginProjectAssetDrag(item, iconKind); if (tile.openRequested) { Commands::OpenAsset(*m_context, item); } ImGui::PopID(); } } }