From 65094d88a353683c94c5dc75398412497c6cbd57 Mon Sep 17 00:00:00 2001 From: "anthony@rabine.fr" Date: Sun, 27 Jul 2025 16:09:48 +0200 Subject: [PATCH] Module save/load --- .../interfaces/i_story_project.h | 6 - core/story-manager/lib/nodes_factory.h | 127 +++++--- core/story-manager/src/story_primitive.h | 25 ++ core/story-manager/src/story_project.cpp | 117 +++---- core/story-manager/src/story_project.h | 15 +- story-editor/imgui.ini | 44 +-- .../src/node_editor/node_editor_window.cpp | 289 ++++++++++-------- .../src/node_editor/node_editor_window.h | 3 + story-editor/src/windows/main_window.cpp | 46 ++- story-editor/src/windows/main_window.h | 4 +- 10 files changed, 426 insertions(+), 250 deletions(-) create mode 100644 core/story-manager/src/story_primitive.h diff --git a/core/story-manager/interfaces/i_story_project.h b/core/story-manager/interfaces/i_story_project.h index 2e584dd..6ff3a96 100644 --- a/core/story-manager/interfaces/i_story_project.h +++ b/core/story-manager/interfaces/i_story_project.h @@ -19,12 +19,6 @@ public: virtual std::string GetName() const = 0; - virtual std::list> GetNodeConnections(const std::string &nodeId) = 0; - virtual int OutputsCount(const std::string &nodeId) = 0; - virtual StoryOptions GetOptions() = 0; - - /* Retourne true si la resource existe déjà et que le code a déjà été généré */ - virtual bool UseResource(const std::string &label) = 0; }; diff --git a/core/story-manager/lib/nodes_factory.h b/core/story-manager/lib/nodes_factory.h index e55233e..8d057f6 100644 --- a/core/story-manager/lib/nodes_factory.h +++ b/core/story-manager/lib/nodes_factory.h @@ -14,6 +14,7 @@ #include "print_node.h" #include "syscall_node.h" #include "story_project.h" +#include "story_primitive.h" static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec"; static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb"; @@ -35,18 +36,24 @@ public: { // Register node types // registerNode("media-node"); - registerNode(OperatorNodeUuid, nullptr); - registerNode(CallFunctionNodeUuid, nullptr); - registerNode(VariableNodeUuid, nullptr); - registerNode(PrintNodeUuid, nullptr); - registerNode(SyscallNodeUuid, nullptr); + registerNode(OperatorNodeUuid, std::make_shared("Operator")); + registerNode(CallFunctionNodeUuid, std::make_shared("Call function")); + registerNode(VariableNodeUuid, std::make_shared("Variable")); + registerNode(PrintNodeUuid, std::make_shared("Print")); + registerNode(SyscallNodeUuid, std::make_shared("System call")); } ~NodesFactory() = default; - std::vector GetNodeTypes() const { - std::vector l; - for(auto const& imap: m_registry) l.push_back(imap.first); + struct NodeInfo { + std::string uuid; + std::string name; + }; + + // key: uuid, value: node name + std::vector LitOfNodes() const { + std::vector l; + for(auto const& imap: m_registry) l.push_back(NodeInfo{imap.first, imap.second.first->GetName() }); return l; } @@ -78,7 +85,12 @@ public: // We have a module here, get the name if (n.second.first->GetName() == name) { - module = n.second.first; + auto p = dynamic_cast(n.second.first.get()); + if (p == nullptr) + { + throw std::runtime_error("Node is not a StoryProject"); + } + module = p->shared_from_this(); } } } @@ -87,45 +99,88 @@ public: return module; } + void SaveAllModules(ResourceManager &manager) + { + for (const auto &entry : m_registry) + { + // Uniquement les modules custom (ici ModuleNodeUuid) + if (entry.first == ModuleNodeUuid) + { + // Get the module from the registry + auto module = std::dynamic_pointer_cast(entry.second.first); + if (module) + { + module->Save(manager); + } + } + } + } + + // Creates a new empty module (StoryProject) and registers it as a module node. + std::shared_ptr NewModule(const std::string& moduleName = "Untitled Module") { + // Create a new StoryProject with the given name + auto module = std::make_shared(m_log); + module->New(Uuid().String(), m_rootPath); + module->SetName(moduleName); + module->SetTitleImage(""); + module->SetTitleSound(""); + module->SetDisplayFormat(320, 240); + module->SetImageFormat(Resource::ImageFormat::IMG_SAME_FORMAT); + module->SetSoundFormat(Resource::SoundFormat::SND_SAME_FORMAT); + module->SetDescription(""); + module->SetProjectType(IStoryProject::PROJECT_TYPE_MODULE); + + // Register as module node if not already in registry + registerNode(ModuleNodeUuid, module); + + return module; + } + + + void SetModulesRootDirectory(const std::string &rootPath) { m_rootPath = rootPath; } void ScanModules() { - // This function should scan the rootPath for modules and register them - // For now, we just log the root path std::cout << "Scanning modules in: " << m_rootPath << std::endl; - // Here you would implement the logic to scan the directory and register modules - - // For example, you could read JSON files in the directory and create nodes based on their content - // This is a placeholder for actual scanning logic - // Example: registerNode("custom-module-node"); // Loop through files in m_rootPath // and register them as nodes if they match certain criteria for (const auto& entry : std::filesystem::directory_iterator(m_rootPath)) { - if (entry.is_regular_file() && entry.path().extension() == ".json") + // Enter directory and look for .json files + if (entry.is_directory()) { - // Load the JSON file and register a node based on its content - std::ifstream file(entry.path()); - nlohmann::json j; - file >> j; - - auto p = std::make_shared(m_log); - p->ParseStoryInformation(j); - - if (p->IsModule()) + std::cout << "Scanning directory: " << entry.path() << std::endl; + for (const auto& subEntry : std::filesystem::directory_iterator(entry.path())) { - registerNode(ModuleNodeUuid, p); - // For now, function node use only primitives nodes - // FIXME: in the future, allow function node to use other function nodes - // Need a list of required nodes to be registered + if (subEntry.is_regular_file() && subEntry.path().extension() == ".json") + { + std::cout << "Found module file: " << subEntry.path() << std::endl; + // Load the JSON file and register a node based on its content + std::ifstream file(subEntry.path()); + nlohmann::json j; + file >> j; + auto p = std::make_shared(m_log); + p->ParseStoryInformation(j); + if (p->IsModule()) + { + registerNode(ModuleNodeUuid, p); + // For now, function node use only primitives nodes + // FIXME: in the future, allow function node to use other function nodes + // Need a list of required nodes to be registered - // Maybe this algorithm: - // 1. load all function nodes - // 2. for each function node, check if it has a "requires" field - // 3. if it does, check if we have them + // Maybe this algorithm: + // 1. load all function nodes + // 2. for each function node, check if it has a "requires" field + // 3. if it does, check if we have them + } + else + { + std::cout << "Skipping non-module project: " << p->GetName() << std::endl; + } + } } } } @@ -143,11 +198,11 @@ private: }; // UUID is the key, and the value is a function that creates the node - typedef std::map, GenericCreator>> Registry; + typedef std::map, GenericCreator>> Registry; Registry m_registry; template - void registerNode(const std::string& uuid, std::shared_ptr moduleInfo) { + void registerNode(const std::string& uuid, std::shared_ptr moduleInfo) { m_registry.insert(std::make_pair(uuid, std::make_pair(moduleInfo, Factory::create_func))); } }; diff --git a/core/story-manager/src/story_primitive.h b/core/story-manager/src/story_primitive.h new file mode 100644 index 0000000..7a6604f --- /dev/null +++ b/core/story-manager/src/story_primitive.h @@ -0,0 +1,25 @@ +#pragma once + +#include "i_story_project.h" + + +class StoryPrimitive : public IStoryProject +{ + +public: + StoryPrimitive(const std::string &name) + : m_name(name) + { + + } + virtual ~StoryPrimitive() {}; + + virtual std::string GetName() const + { + return m_name; + } + +private: + std::string m_name; + +}; \ No newline at end of file diff --git a/core/story-manager/src/story_project.cpp b/core/story-manager/src/story_project.cpp index a15a446..8531700 100644 --- a/core/story-manager/src/story_project.cpp +++ b/core/story-manager/src/story_project.cpp @@ -558,65 +558,80 @@ bool StoryProject::Load(ResourceManager &manager, NodesFactory &factory) void StoryProject::Save(ResourceManager &manager) { - nlohmann::json j; - j["project"] = { {"name", m_name}, {"uuid", m_uuid}, { "title_image", m_titleImage }, { "title_sound", m_titleSound } }; - + try { - nlohmann::json resourcesData; + nlohmann::json j; + j["project"] = { + {"name", m_name}, + {"uuid", m_uuid}, + { "title_image", m_titleImage }, + { "title_sound", m_titleSound }, + {"description", m_description}, + {"type", (m_projectType == IStoryProject::PROJECT_TYPE_STORY) ? "story" : "module"}, + {"version", m_version} + }; - auto [b, e] = manager.Items(); - for (auto it = b; it != e; ++it) { - nlohmann::json obj = {{"type", (*it)->type}, - {"format", (*it)->format}, - {"description", (*it)->description}, - {"file", (*it)->file}}; + nlohmann::json resourcesData; - resourcesData.push_back(obj); + auto [b, e] = manager.Items(); + for (auto it = b; it != e; ++it) + { + nlohmann::json obj = {{"type", (*it)->type}, + {"format", (*it)->format}, + {"description", (*it)->description}, + {"file", (*it)->file}}; + + resourcesData.push_back(obj); + } + j["resources"] = resourcesData; } - j["resources"] = resourcesData; + + nlohmann::json model; + ModelToJson(model); + j["pages"] = model; + + nlohmann::json variablesData; + for (const auto &v : m_variables) + { + std::string value; + + if (v->IsFloat()) + { + value = std::to_string(v->GetFloatValue()); + } + else if (v->IsInteger()) + { + value = std::to_string(v->GetIntegerValue()); + } + else if (v->IsString()) + { + value = v->GetStringValue(); + } + else if (v->IsBool()) + { + value = v->GetBoolValue() ? "true" : "false"; + } + + nlohmann::json obj = {{"name", v->GetVariableName()}, + {"label", v->GetLabel()}, + {"uuid", v->GetUuid()}, + {"value", value}, + {"scale", v->GetScalePower()}, + {"constant", v->IsConstant()}, + {"type", Variable::ValueTypeToString(v->GetValueType())}}; + + variablesData.push_back(obj); + } + j["variables"] = variablesData; + + std::ofstream o(m_project_file_path); + o << std::setw(4) << j << std::endl; } - - nlohmann::json model; - ModelToJson(model); - j["pages"] = model; - - nlohmann::json variablesData; - for (const auto &v : m_variables) + catch(const std::exception& e) { - std::string value; - - if (v->IsFloat()) - { - value = std::to_string(v->GetFloatValue()); - } - else if (v->IsInteger()) - { - value = std::to_string(v->GetIntegerValue()); - } - else if (v->IsString()) - { - value = v->GetStringValue(); - } - else if (v->IsBool()) - { - value = v->GetBoolValue() ? "true" : "false"; - } - - nlohmann::json obj = {{"name", v->GetVariableName()}, - {"label", v->GetLabel()}, - {"uuid", v->GetUuid()}, - {"value", value}, - {"scale", v->GetScalePower()}, - {"constant", v->IsConstant()}, - {"type", Variable::ValueTypeToString(v->GetValueType())}}; - - variablesData.push_back(obj); + std::cerr << e.what() << '\n'; } - j["variables"] = variablesData; - - std::ofstream o(m_project_file_path); - o << std::setw(4) << j << std::endl; } diff --git a/core/story-manager/src/story_project.h b/core/story-manager/src/story_project.h index 7fed896..243db9a 100644 --- a/core/story-manager/src/story_project.h +++ b/core/story-manager/src/story_project.h @@ -19,7 +19,7 @@ class NodesFactory; -struct StoryProject : public IStoryProject +struct StoryProject : public std::enable_shared_from_this, public IStoryProject { public: @@ -30,6 +30,10 @@ public: return &m_selected; } + // std::shared_ptr shared_from_this() { + // return shared_from_this(); + // } + std::string MainUuid() const { return "490745ab-df4d-476d-ae27-027e94b8ee0a"; } @@ -86,14 +90,15 @@ public: const bool IsInitialized() const { return m_initialized; } const bool IsModule() const { return m_projectType == IStoryProject::PROJECT_TYPE_MODULE; } const bool IsStory() const { return m_projectType == IStoryProject::PROJECT_TYPE_STORY; } + void SetProjectType(IStoryProject::Type type) { m_projectType = type; } bool ParseStoryInformation(nlohmann::json &j); // From IStoryProject - virtual std::list> GetNodeConnections(const std::string &nodeId) override; - virtual int OutputsCount(const std::string &nodeId) override; - virtual StoryOptions GetOptions() override { return m_storyOptions; } - virtual bool UseResource(const std::string &label) override; + virtual std::list> GetNodeConnections(const std::string &nodeId); + virtual int OutputsCount(const std::string &nodeId); + StoryOptions GetOptions() { return m_storyOptions; } + virtual bool UseResource(const std::string &label); // Node interaction std::shared_ptr CreatePage(const std::string &uuid); diff --git a/story-editor/imgui.ini b/story-editor/imgui.ini index d11dc92..088f469 100644 --- a/story-editor/imgui.ini +++ b/story-editor/imgui.ini @@ -4,13 +4,13 @@ Size=1220,694 Collapsed=0 [Window][Debug##Default] -Pos=60,60 +Pos=106,62 Size=400,400 Collapsed=0 [Window][Library Manager] -Pos=796,26 -Size=484,268 +Pos=643,26 +Size=637,268 Collapsed=0 DockId=0x00000003,0 @@ -21,20 +21,20 @@ Collapsed=0 DockId=0x00000004,0 [Window][Emulator] -Pos=796,26 -Size=484,268 +Pos=643,26 +Size=637,268 Collapsed=0 DockId=0x00000003,5 [Window][Code viewer] -Pos=796,26 -Size=484,268 +Pos=643,26 +Size=637,268 Collapsed=0 DockId=0x00000003,4 [Window][Resources] -Pos=796,26 -Size=484,268 +Pos=643,26 +Size=637,268 Collapsed=0 DockId=0x00000003,1 @@ -56,20 +56,20 @@ Collapsed=0 DockId=0x00000005,0 [Window][CPU] -Pos=796,26 -Size=484,268 +Pos=643,26 +Size=637,268 Collapsed=0 DockId=0x00000003,2 [Window][RAM view] -Pos=796,26 -Size=484,268 +Pos=643,26 +Size=637,268 Collapsed=0 DockId=0x00000003,3 [Window][Properties] -Pos=796,296 -Size=484,169 +Pos=643,296 +Size=637,169 Collapsed=0 DockId=0x00000006,0 @@ -79,8 +79,8 @@ Size=60,694 Collapsed=0 [Window][QuitConfirm] -Pos=479,312 -Size=321,96 +Pos=508,312 +Size=264,96 Collapsed=0 [Window][ProjectPropertiesPopup] @@ -90,13 +90,13 @@ Collapsed=0 [Window][Module editor] Pos=60,26 -Size=734,439 +Size=581,439 Collapsed=0 DockId=0x00000001,1 [Window][Story editor] Pos=60,26 -Size=734,439 +Size=581,439 Collapsed=0 DockId=0x00000001,0 @@ -122,9 +122,9 @@ Column 2 Width=124 [Docking][Data] DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,439 Split=X - DockNode ID=0x00000001 Parent=0x00000007 SizeRef=734,694 CentralNode=1 Selected=0x93ADCAAB - DockNode ID=0x00000002 Parent=0x00000007 SizeRef=484,694 Split=Y Selected=0x52EB28B5 - DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,268 Selected=0x4B07C626 + DockNode ID=0x00000001 Parent=0x00000007 SizeRef=581,694 CentralNode=1 Selected=0x54E37131 + DockNode ID=0x00000002 Parent=0x00000007 SizeRef=637,694 Split=Y Selected=0x52EB28B5 + DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,268 Selected=0x63869CAF DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,169 Selected=0x8C72BEA8 DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,253 Split=X Selected=0xEA83D666 DockNode ID=0x00000004 Parent=0x00000008 SizeRef=610,192 Selected=0xEA83D666 diff --git a/story-editor/src/node_editor/node_editor_window.cpp b/story-editor/src/node_editor/node_editor_window.cpp index 33cc78c..8c7303a 100644 --- a/story-editor/src/node_editor/node_editor_window.cpp +++ b/story-editor/src/node_editor/node_editor_window.cpp @@ -34,28 +34,47 @@ NodeEditorWindow::NodeEditorWindow(IStoryManager &manager, NodesFactory &factory , m_editorType(type) { +// g OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec"; +// static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb"; +// static const std::string VariableNodeUuid = "020cca4e-9cdc-47e7-a6a5-53e4c9152ed0"; +// static const std::string PrintNodeUuid = "02ee27bc-ff1d-4f94-b700-eab55052ad1c"; +// static const std::string SyscallNodeUuid = "02225cff-4975-400e-8130-41524d8af773"; +// static const std::string ModuleNodeUuid = "02e4c728-ef72-4003-b7c8-2bee8834a47e"; + + // registerNode("media-node"); - registerNode("operator-node"); - registerNode("call-function-node"); - registerNode("module-node"); - registerNode("variable-node"); - registerNode("print-node"); - registerNode("syscall-node"); + registerNode(OperatorNodeUuid); + registerNode(CallFunctionNodeUuid); + // registerNode("module-node"); + registerNode(VariableNodeUuid); + registerNode(PrintNodeUuid); + registerNode(SyscallNodeUuid); } NodeEditorWindow::~NodeEditorWindow() { - m_pages.clear(); - m_story.reset(); + Clear(); } - +void NodeEditorWindow::Clear() +{ + m_pages.clear(); + m_story.reset(); + m_callStack.clear(); +} void NodeEditorWindow::Initialize() { - m_pages.clear(); - m_callStack.clear(); + Clear(); +} +void NodeEditorWindow::InitializeProject() +{ + // Always ensure a main page exists (this matches StoryProject::New) + if (!m_story->GetPage(m_story->MainUuid())) + { + m_story->CreatePage(m_story->MainUuid()); + } m_currentPage = std::make_shared(m_story->MainUuid(), "Main"); m_pages.push_back(m_currentPage); m_callStack.push_back(m_currentPage); @@ -108,7 +127,7 @@ void NodeEditorWindow::Load(std::shared_ptr story) try { BaseNodeWidget::InitId(); - Initialize(); + InitializeProject(); auto [node_begin, node_end] = m_story->Nodes(m_currentPage->Uuid()); @@ -204,6 +223,11 @@ bool NodeEditorWindow::FillConnection(std::shared_ptr c, ed::PinId p std::shared_ptr NodeEditorWindow::GetSelectedNode() { + if (!m_currentPage) + { + return nullptr; // No current page, nothing to select + } + std::shared_ptr selected; m_currentPage->Select(); @@ -226,155 +250,168 @@ void NodeEditorWindow::Draw() if (WindowBase::BeginDraw()) { - m_currentPage->Select(); - ToolbarUI(); - - ed::Begin(m_currentPage->Uuid().data(), ImVec2(0.0, 0.0f)); - - - // Draw our nodes - m_currentPage->Draw(); - - // Handle creation action, returns true if editor want to create new object (node or link) - if (ed::BeginCreate()) + if (m_currentPage) { - ed::PinId startId, endId; - if (ed::QueryNewLink(&startId, &endId)) + m_currentPage->Select(); + + ToolbarUI(); + + ed::Begin(m_currentPage->Uuid().data(), ImVec2(0.0, 0.0f)); + + // Draw our nodes + m_currentPage->Draw(); + + // Handle creation action, returns true if editor want to create new object (node or link) + if (ed::BeginCreate()) { - // QueryNewLink returns true if editor want to create new link between pins. - // - // Link can be created only for two valid pins, it is up to you to - // validate if connection make sense. Editor is happy to make any. - // - // Link always goes from input to output. User may choose to drag - // link from output pin or input pin. This determine which pin ids - // are valid and which are not: - // * input valid, output invalid - user started to drag new ling from input pin - // * input invalid, output valid - user started to drag new ling from output pin - // * input valid, output valid - user dragged link over other pin, can be validated + ed::PinId startId, endId; + if (ed::QueryNewLink(&startId, &endId)) + { + // QueryNewLink returns true if editor want to create new link between pins. + // + // Link can be created only for two valid pins, it is up to you to + // validate if connection make sense. Editor is happy to make any. + // + // Link always goes from input to output. User may choose to drag + // link from output pin or input pin. This determine which pin ids + // are valid and which are not: + // * input valid, output invalid - user started to drag new ling from input pin + // * input invalid, output valid - user started to drag new ling from output pin + // * input valid, output valid - user dragged link over other pin, can be validated - if (startId && endId) // both are valid, let's accept link - { - // ed::AcceptNewItem() return true when user release mouse button. - if (ed::AcceptNewItem()) - { - auto c = std::make_shared(); + if (startId && endId) // both are valid, let's accept link + { + // ed::AcceptNewItem() return true when user release mouse button. + if (ed::AcceptNewItem()) + { + auto c = std::make_shared(); - // On cherche à quel noeud appartien les pin (selon si le lien a été créé à partir d'une entrée ou d'une sortie) - if (FillConnection(c, startId)) - { - if (FillConnection(c, endId)) + // On cherche à quel noeud appartien les pin (selon si le lien a été créé à partir d'une entrée ou d'une sortie) + if (FillConnection(c, startId)) { - m_story->AddConnection(m_currentPage->Uuid(), c); + if (FillConnection(c, endId)) + { + m_story->AddConnection(m_currentPage->Uuid(), c); - CreateLink(c, startId, endId); + CreateLink(c, startId, endId); - // Draw new link. - ed::Link(m_currentPage->m_links.back()->ed_link->Id, startId, endId); + // Draw new link. + ed::Link(m_currentPage->m_links.back()->ed_link->Id, startId, endId); + } } - } - } + } - // You may choose to reject connection between these nodes - // by calling ed::RejectNewItem(). This will allow editor to give - // visual feedback by changing link thickness and color. - } + // You may choose to reject connection between these nodes + // by calling ed::RejectNewItem(). This will allow editor to give + // visual feedback by changing link thickness and color. + } + } + + ed::EndCreate(); // Wraps up object creation action handling. } - - ed::EndCreate(); // Wraps up object creation action handling. - } - + - // Handle deletion action - if (ed::BeginDelete()) - { - ed::NodeId nodeId = 0; - while (ed::QueryDeletedNode(&nodeId)) + // Handle deletion action + if (ed::BeginDelete()) { + ed::NodeId nodeId = 0; + while (ed::QueryDeletedNode(&nodeId)) + { + if (ed::AcceptDeletedItem()) + { + std::shared_ptr node; + if (m_currentPage->GetNode(nodeId, node)) + { + // First delete model, then current entry + m_story->DeleteNode(m_currentPage->Uuid(), node->Base()->GetId()); + m_currentPage->DeleteNode(nodeId); + } + } + } + + // There may be many links marked for deletion, let's loop over them. + ed::LinkId deletedLinkId; + while (ed::QueryDeletedLink(&deletedLinkId)) + { + // If you agree that link can be deleted, accept deletion. if (ed::AcceptDeletedItem()) { - std::shared_ptr node; - if (m_currentPage->GetNode(nodeId, node)) - { - // First delete model, then current entry - m_story->DeleteNode(m_currentPage->Uuid(), node->Base()->GetId()); - m_currentPage->DeleteNode(nodeId); - } + std::shared_ptr model; + if (m_currentPage->GetModel(deletedLinkId, model)) + { + m_story->DeleteLink(m_currentPage->Uuid(), model); + m_currentPage->EraseLink(deletedLinkId); + } + } + + // You may reject link deletion by calling: + // ed::RejectDeletedItem(); } } + ed::EndDelete(); // Wrap up deletion action - // There may be many links marked for deletion, let's loop over them. - ed::LinkId deletedLinkId; - while (ed::QueryDeletedLink(&deletedLinkId)) + + auto openPopupPosition = ImGui::GetMousePos(); + ed::Suspend(); + + if (ed::ShowBackgroundContextMenu()) { - // If you agree that link can be deleted, accept deletion. - if (ed::AcceptDeletedItem()) - { - std::shared_ptr model; - if (m_currentPage->GetModel(deletedLinkId, model)) - { - m_story->DeleteLink(m_currentPage->Uuid(), model); - m_currentPage->EraseLink(deletedLinkId); - } - } - - // You may reject link deletion by calling: - // ed::RejectDeletedItem(); + ImGui::OpenPopup("Create New Node"); } - } - ed::EndDelete(); // Wrap up deletion action - - auto openPopupPosition = ImGui::GetMousePos(); - ed::Suspend(); - - if (ed::ShowBackgroundContextMenu()) - { - ImGui::OpenPopup("Create New Node"); - } - - if (ImGui::BeginPopup("Create New Node")) - { - auto newNodePostion = openPopupPosition; - std::shared_ptr base; - auto nodeTypes = m_nodesFactory.GetNodeTypes(); - - for (auto &type : nodeTypes) + if (ImGui::BeginPopup("Create New Node")) { - if (ImGui::MenuItem(type.c_str())) + auto newNodePostion = openPopupPosition; + std::shared_ptr base; + auto nodeTypes = m_nodesFactory.LitOfNodes(); + + for (auto &type : nodeTypes) { - base = m_nodesFactory.CreateNode(type); - if (base) + if (ImGui::MenuItem(type.name.c_str())) { - m_story->AddNode(m_currentPage->Uuid(), base); - auto n = CreateNodeWidget(type, m_manager, base); - if (n) + base = m_nodesFactory.CreateNode(type.uuid); + if (base) { - n->Base()->SetPosition(newNodePostion.x, newNodePostion.y); - n->Initialize(); - m_currentPage->AddNode(n); + m_story->AddNode(m_currentPage->Uuid(), base); + auto n = CreateNodeWidget(type.uuid, m_manager, base); + if (n) + { + n->Base()->SetPosition(newNodePostion.x, newNodePostion.y); + n->Initialize(); + m_currentPage->AddNode(n); + } } } } + + ImGui::EndPopup(); } - ImGui::EndPopup(); - } + if (m_loaded) + { + ed::NavigateToContent(); + m_loaded = false; + } - if (m_loaded) + ed::Resume(); + + + ed::End(); + ed::SetCurrentEditor(nullptr); + } + else { - ed::NavigateToContent(); - m_loaded = false; + // Set background color to light gray + // ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.7f, 0.7f, 0.7f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); + + ImGui::Text("Please load or create a project."); + + ImGui::PopStyleColor(1); // Pop both colors } - ed::Resume(); - - - ed::End(); - ed::SetCurrentEditor(nullptr); - } WindowBase::EndDraw(); diff --git a/story-editor/src/node_editor/node_editor_window.h b/story-editor/src/node_editor/node_editor_window.h index beb9732..bc6bb24 100644 --- a/story-editor/src/node_editor/node_editor_window.h +++ b/story-editor/src/node_editor/node_editor_window.h @@ -33,6 +33,9 @@ public: virtual void Draw() override; void Initialize(); + void InitializeProject(); + void NewProject(); + void Clear(); void Load(std::shared_ptr story); void SaveNodePositions(); void OpenFunction(const std::string &uuid, const std::string &name); diff --git a/story-editor/src/windows/main_window.cpp b/story-editor/src/windows/main_window.cpp index 6d8d273..667a421 100644 --- a/story-editor/src/windows/main_window.cpp +++ b/story-editor/src/windows/main_window.cpp @@ -52,6 +52,7 @@ MainWindow::MainWindow() m_chip32_ctx.syscall = static_cast(Callback::callback); CloseProject(); + CloseModule(); // define style for all directories ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir, "", ImVec4(0.5f, 1.0f, 0.9f, 0.9f), ICON_MDI_FOLDER); @@ -459,9 +460,18 @@ float MainWindow::DrawMainMenuBar() OpenProject(m_story->GetUuid()); } } + if (ImGui::MenuItem("New module")) { + // Current module project + CloseModule(); showNewProject = true; + NewModule(); + } + + if (ImGui::MenuItem("Save module")) + { + SaveModule(); } /* @@ -492,6 +502,8 @@ float MainWindow::DrawMainMenuBar() SaveProject(); } + + if (ImGui::MenuItem("Close project")) { CloseProject(); @@ -502,6 +514,11 @@ float MainWindow::DrawMainMenuBar() showParameters = true; } + if (ImGui::MenuItem("Close module")) + { + CloseModule(); + } + if (!init) ImGui::EndDisabled(); @@ -575,6 +592,8 @@ bool MainWindow::Initialize() bool success = false; LoadParams(); + m_nodesFactory.ScanModules(); + // GUI Init if (m_gui.Initialize()) { @@ -896,6 +915,21 @@ void MainWindow::OpenProject(const std::string &uuid) RefreshProjectInformation(); } +void MainWindow::NewModule() +{ + auto module = m_nodesFactory.NewModule(); + m_moduleEditorWindow.Load(module); + + m_moduleEditorWindow.Enable(); +} + + +void MainWindow::SaveModule() +{ + m_nodesFactory.SaveAllModules(m_resources);; + Log("Modules saved"); +} + void MainWindow::OpenModule(const std::string &uuid) { m_module = m_nodesFactory.GetModule(uuid); @@ -930,7 +964,7 @@ void MainWindow::CloseProject() // } m_resources.Clear(); - m_nodeEditorWindow.Initialize(); + m_nodeEditorWindow.Clear(); m_nodeEditorWindow.Disable(); m_emulatorWindow.ClearImage(); @@ -951,7 +985,7 @@ void MainWindow::CloseProject() void MainWindow::CloseModule() { - m_moduleEditorWindow.Initialize(); + m_moduleEditorWindow.Clear(); m_moduleEditorWindow.Disable(); } @@ -1337,6 +1371,12 @@ void MainWindow::SaveParams() void MainWindow::LoadParams() { try { + + // Modules directory + std::filesystem::path dlDir = std::filesystem::path(pf::getConfigHome()) / "ost_modules"; + std::filesystem::create_directories(dlDir); + m_nodesFactory.SetModulesRootDirectory(dlDir.string()); + std::string loc = pf::getConfigHome() + "/ost_settings.json"; // read a JSON file std::ifstream i(loc); @@ -1365,6 +1405,6 @@ void MainWindow::LoadParams() } catch(std::exception &e) { - + Log(e.what(), true); } } diff --git a/story-editor/src/windows/main_window.h b/story-editor/src/windows/main_window.h index ad022b7..6e1ae00 100644 --- a/story-editor/src/windows/main_window.h +++ b/story-editor/src/windows/main_window.h @@ -141,7 +141,9 @@ private: void SaveProject(); void CloseProject(); - void OpenModule(const std::string &uuid); + void OpenModule(const std::string &uuid); + void NewModule(); + void SaveModule(); void CloseModule(); // From IStoryManager (proxy to StoryProject class)