diff --git a/SHADE_Engine/src/Editor/Command/SHCommand.hpp b/SHADE_Engine/src/Editor/Command/SHCommand.hpp index 7a526506..149c8986 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommand.hpp +++ b/SHADE_Engine/src/Editor/Command/SHCommand.hpp @@ -24,6 +24,7 @@ namespace SHADE class SHCommand : SHBaseCommand { public: + using SHCommandPtr = std::unique_ptr; typedef std::function SetterFunction; SHCommand(T const& oldVal, T const& value, SetterFunction setFnc) diff --git a/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp b/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp index ee2d316d..7a965644 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp +++ b/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp @@ -13,7 +13,7 @@ namespace SHADE SHCommandManager::CommandStack SHCommandManager::undoStack{}; SHCommandManager::CommandStack SHCommandManager::redoStack{}; - void SHCommandManager::PerformCommand(CommandPtr commandPtr, bool const& overrideValue) + void SHCommandManager::PerformCommand(BaseCommandPtr commandPtr, bool const& overrideValue) { redoStack = CommandStack(); commandPtr->Execute(); @@ -27,9 +27,9 @@ namespace SHADE } } - void SHCommandManager::RegisterCommand(CommandPtr commandPtr) + void SHCommandManager::RegisterCommand(BaseCommandPtr commandPtr) { - undoStack.push(commandPtr); + undoStack.push(commandPtr); } void SHCommandManager::UndoCommand() diff --git a/SHADE_Engine/src/Editor/Command/SHCommandManager.h b/SHADE_Engine/src/Editor/Command/SHCommandManager.h index 9152c3cb..c1ceb4b0 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommandManager.h +++ b/SHADE_Engine/src/Editor/Command/SHCommandManager.h @@ -19,11 +19,13 @@ namespace SHADE //#==============================================================# //|| Type Aliases || //#==============================================================# - using CommandPtr = std::shared_ptr; - using CommandStack = std::stack; + using BaseCommandPtr = std::shared_ptr; + template + using SHCommandPtr = std::shared_ptr>; + using CommandStack = std::stack; - static void PerformCommand(CommandPtr commandPtr, bool const& overrideValue = false); - static void RegisterCommand(CommandPtr commandPtr); + static void PerformCommand(BaseCommandPtr commandPtr, bool const& overrideValue = false); + static void RegisterCommand(BaseCommandPtr commandPtr); static void UndoCommand(); static void RedoCommand(); static std::size_t GetUndoStackSize(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 13f3b679..c2316006 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -50,6 +50,7 @@ namespace SHADE if(const auto root = sceneGraph.GetRoot()) { auto const& children = root->GetChildren(); + for (const auto child : children) { RecursivelyDrawEntityNode(child); @@ -102,7 +103,7 @@ namespace SHADE } if (ImGui::SmallButton(ICON_MD_ADD_CIRCLE)) { - SHEntityManager::CreateEntity(); + SHCommandManager::PerformCommand(std::make_shared()); } if (ImGui::IsItemHovered()) { @@ -142,13 +143,24 @@ namespace SHADE auto* entity = SHEntityManager::GetEntityByID(currentNode->GetEntityID()); //Draw Node - bool isNodeOpen = ImGui::TreeNodeEx(reinterpret_cast(entity), nodeFlags, "%u: %s", EntityHandleGenerator::GetIndex(eid), entity->name.c_str()); + bool isNodeOpen = ImGui::TreeNodeEx(reinterpret_cast(entity), nodeFlags, "%u: %s", SHEntityManager::GetEntityIndex(eid), entity->name.c_str()); const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); //Check For Begin Drag if (SHDragDrop::BeginSource()) { - ImGui::Text("Moving EID: %zu", eid); + std::string moveLabel = "Moving EID: "; + if(!isSelected) + editor->selectedEntities.push_back(eid); + for(int i = 0; i < static_cast(editor->selectedEntities.size()); ++i) + { + moveLabel.append(std::to_string(editor->selectedEntities[i])); + if(i + 1 < static_cast(editor->selectedEntities.size())) + { + moveLabel.append(", "); + } + } + ImGui::Text(moveLabel.c_str()); SHDragDrop::SetPayload>(DRAG_EID, &editor->selectedEntities); SHDragDrop::EndSource(); } @@ -156,14 +168,7 @@ namespace SHADE { if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(DRAG_EID)) //If payload is valid { - std::vector const droppedEIDs = *eidPayload; - for(auto const& dropEID : droppedEIDs) - { - if(!sceneGraph.GetChild(dropEID, eid)) - sceneGraph.SetParent(dropEID, eid); - } - //if(!sceneGraph.GetChild(dropEID, eid)) - // sceneGraph.SetParent(dropEID, eid); //Set dropEID parent to eid (belonging to current Node) + ParentSelectedEntities(eid); SHDragDrop::EndTarget(); } } @@ -183,7 +188,7 @@ namespace SHADE if((currentNode->GetParent() != sceneGraph.GetRoot()) && ImGui::Selectable(std::format("{} Unparent Selected", ICON_MD_NORTH_WEST).data())) { - sceneGraph.SetParent(currentNode->GetEntityID(), nullptr); + ParentSelectedEntities(MAX_EID); } ImGui::EndPopup(); } @@ -195,7 +200,15 @@ namespace SHADE { if (!isSelected) { - if (!ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + if(ImGui::IsKeyDown(ImGuiKey_LeftShift)) + { + if(editor->selectedEntities.size() >= 1) + { + SelectRangeOfEntities(editor->selectedEntities[0], eid); + } + else editor->selectedEntities.clear(); + } + else if (!ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) editor->selectedEntities.clear(); editor->selectedEntities.push_back(eid); }//if not selected @@ -242,4 +255,90 @@ namespace SHADE { SHEntityManager::CreateEntity(MAX_EID, "DefaultChild", parentEID); } + + void SHHierarchyPanel::ParentSelectedEntities(EntityID parentEID) const noexcept + { + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + auto const editor = SHSystemManager::GetSystem(); + SHEntityParentCommand::EntityParentData entityParentData; + std::vector parentedEIDS; + for(auto const& eid : editor->selectedEntities) + { + if(sceneGraph.GetChild(eid, parentEID) == nullptr) + { + parentedEIDS.push_back(eid); + if(auto parent = sceneGraph.GetParent(eid)) + entityParentData[eid].oldParentEID = parent->GetEntityID(); + entityParentData[eid].newParentEID = parentEID; + } + } + SHCommandManager::PerformCommand(std::make_shared(parentedEIDS, entityParentData)); + } + + void SHHierarchyPanel::SelectRangeOfEntities(EntityID beginEID, EntityID endEID) + { + bool startSelecting = false; bool endSelecting = false; + auto const editor = SHSystemManager::GetSystem(); + editor->selectedEntities.clear(); + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + sceneGraph.Traverse([&](SHSceneNode* nodePtr) + { + auto eid = nodePtr->GetEntityID(); + if(!startSelecting) + { + if(eid == beginEID || eid == endEID) + { + startSelecting = true; + editor->selectedEntities.push_back(eid); + } + } + else + { + if(!endSelecting) + { + editor->selectedEntities.push_back(eid); + if(eid == endEID || eid == beginEID) + { + endSelecting = true; + } + } + } + }); + } + + void SHCreateEntityCommand::Execute() + { + EntityID newEID = SHEntityManager::CreateEntity(eid); + if(eid == MAX_EID) + eid = newEID; + } + + void SHCreateEntityCommand::Undo() + { + SHEntityManager::DestroyEntity(eid); + } + + void SHEntityParentCommand::Execute() + { + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + for(auto const& eid : entities) + { + if(entityParentData[eid].newParentEID == MAX_EID) + sceneGraph.SetParent(eid, nullptr); + else + sceneGraph.SetParent(eid, entityParentData[eid].newParentEID); + } + } + + void SHEntityParentCommand::Undo() + { + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + for(auto const& eid : entities) + { + if(entityParentData[eid].oldParentEID == MAX_EID) + sceneGraph.SetParent(eid, nullptr); + else + sceneGraph.SetParent(eid, entityParentData[eid].oldParentEID); + } + } }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index 88983ca9..0cfe6474 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -10,7 +10,7 @@ #include "imgui_internal.h" #include "ECS_Base/SHECSMacros.h" #include "Editor/EditorWindow/SHEditorWindow.h" - +#include "Editor/Command/SHCommand.hpp" namespace SHADE { class SHSceneNode; @@ -28,8 +28,40 @@ namespace SHADE void DrawMenuBar() const noexcept; ImRect RecursivelyDrawEntityNode(SHSceneNode*); void CreateChildEntity(EntityID parentEID) const noexcept; + void ParentSelectedEntities(EntityID parentEID) const noexcept; + void SelectRangeOfEntities(EntityID beginEID, EntityID EndEID); std::string filter; bool isAnyNodeSelected = false; EntityID scrollTo = MAX_EID; };//class SHHierarchyPanel + + //Might move to a different file + class SHCreateEntityCommand final : public SHBaseCommand + { + public: + void Execute() override; + void Undo() override; + private: + EntityID eid = MAX_EID; + }; + + class SHEntityParentCommand final : public SHBaseCommand + { + public: + struct Data + { + EntityID oldParentEID = MAX_EID; + EntityID newParentEID = MAX_EID; + }; + using EntityParentData = std::unordered_map; + + SHEntityParentCommand(std::vector entityIDs, EntityParentData inEntityParentData):entities(entityIDs),entityParentData(inEntityParentData){} + + void Execute() override; + void Undo() override; + private: + std::vector entities; + std::unordered_map entityParentData; + }; + }//namespace SHADE