From 479497f1df89b8b054a381d26784df2feeaadc33 Mon Sep 17 00:00:00 2001 From: "anthony@rabine.fr" Date: Wed, 1 Oct 2025 17:25:56 +0200 Subject: [PATCH] load/save module success, better toaster --- .../interfaces/i_story_manager.h | 3 - core/story-manager/src/nodes_factory.h | 15 +- core/story-manager/src/story_project.cpp | 4 - story-editor/imgui.ini | 58 ++-- story-editor/src/app/app_controller.cpp | 8 +- story-editor/src/app/app_controller.h | 2 +- story-editor/src/events/all_events.h | 2 +- story-editor/src/imgui_toast_notifier.h | 73 ++++- story-editor/src/main_window.cpp | 7 +- .../src/node_editor/node_editor_window.cpp | 309 ++++++++++++++---- story-editor/src/windows/library_window.cpp | 8 +- story-editor/src/windows/library_window.h | 4 +- 12 files changed, 370 insertions(+), 123 deletions(-) diff --git a/core/story-manager/interfaces/i_story_manager.h b/core/story-manager/interfaces/i_story_manager.h index 4419de8..9b975da 100644 --- a/core/story-manager/interfaces/i_story_manager.h +++ b/core/story-manager/interfaces/i_story_manager.h @@ -42,9 +42,6 @@ public: virtual std::shared_ptr GetCurrentProject() = 0; - // Modules - virtual std::shared_ptr OpenModule(const std::string &uuid) = 0; - // Node interaction virtual void CompileNodes(bool compileonly) = 0; virtual void BuildCode(bool compileonly) = 0; diff --git a/core/story-manager/src/nodes_factory.h b/core/story-manager/src/nodes_factory.h index 56514a2..cececdc 100644 --- a/core/story-manager/src/nodes_factory.h +++ b/core/story-manager/src/nodes_factory.h @@ -114,14 +114,19 @@ public: void SaveAllModules(ResourceManager &manager) { - for (const auto &entry : m_registry) + for (const auto &n : m_registry) { - // Only modules - auto module = std::dynamic_pointer_cast(entry.second.first); - if (module) + auto p = dynamic_cast(n.second.first.get()); + if (p) { - module->Save(manager); + p->Save(manager); } + // Only modules + // auto module = std::dynamic_pointer_cast(entry.second.first); + // if (module) + // { + // module->Save(manager); + // } } } diff --git a/core/story-manager/src/story_project.cpp b/core/story-manager/src/story_project.cpp index 5ba3aa9..f96b4f7 100644 --- a/core/story-manager/src/story_project.cpp +++ b/core/story-manager/src/story_project.cpp @@ -637,10 +637,6 @@ void StoryProject::Save(ResourceManager &manager) void StoryProject::Clear() { - m_uuid = ""; - m_working_dir = ""; - m_project_file_path = ""; - m_initialized = false; m_variables.clear(); m_pages.clear(); } diff --git a/story-editor/imgui.ini b/story-editor/imgui.ini index a68d4ea..ef62650 100644 --- a/story-editor/imgui.ini +++ b/story-editor/imgui.ini @@ -1,6 +1,6 @@ [Window][WindowOverViewport_11111111] Pos=60,26 -Size=1220,694 +Size=1343,777 Collapsed=0 [Window][Debug##Default] @@ -9,32 +9,32 @@ Size=400,400 Collapsed=0 [Window][Library Manager] -Pos=591,26 -Size=689,224 +Pos=714,26 +Size=689,465 Collapsed=0 DockId=0x00000003,0 [Window][Console] Pos=60,533 -Size=610,187 +Size=672,270 Collapsed=0 DockId=0x00000004,0 [Window][Emulator] -Pos=591,26 -Size=689,224 +Pos=714,26 +Size=689,465 Collapsed=0 DockId=0x00000003,5 [Window][Code viewer] -Pos=591,26 -Size=689,224 +Pos=714,26 +Size=689,465 Collapsed=0 DockId=0x00000003,4 [Window][Resources] -Pos=591,26 -Size=689,224 +Pos=714,26 +Size=689,465 Collapsed=0 DockId=0x00000003,1 @@ -50,36 +50,36 @@ Size=150,42 Collapsed=0 [Window][Variables] -Pos=672,533 -Size=608,187 +Pos=734,533 +Size=669,270 Collapsed=0 DockId=0x00000005,0 [Window][CPU] -Pos=591,26 -Size=689,224 +Pos=714,26 +Size=689,465 Collapsed=0 DockId=0x00000003,2 [Window][RAM view] -Pos=591,26 -Size=689,224 +Pos=714,26 +Size=689,465 Collapsed=0 DockId=0x00000003,3 [Window][Properties] -Pos=591,252 -Size=689,279 +Pos=714,493 +Size=689,38 Collapsed=0 DockId=0x00000006,0 [Window][ToolBar] Pos=0,26 -Size=60,694 +Size=60,777 Collapsed=0 [Window][QuitConfirm] -Pos=508,312 +Pos=569,353 Size=264,96 Collapsed=0 @@ -90,15 +90,15 @@ Collapsed=0 [Window][Module editor] Pos=60,26 -Size=529,505 +Size=652,505 Collapsed=0 -DockId=0x00000001,1 +DockId=0x00000001,0 [Window][Story editor] Pos=60,26 -Size=529,505 +Size=652,505 Collapsed=0 -DockId=0x00000001,0 +DockId=0x00000001,1 [Window][Choose a library directory##ChooseLibraryDirDialog] Pos=490,260 @@ -142,13 +142,13 @@ RefScale=20 Column 0 Sort=0v [Docking][Data] -DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y - DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,505 Split=X +DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1343,777 Split=Y + DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,422 Split=X DockNode ID=0x00000001 Parent=0x00000007 SizeRef=721,694 CentralNode=1 Selected=0x93ADCAAB DockNode ID=0x00000002 Parent=0x00000007 SizeRef=689,694 Split=Y Selected=0x52EB28B5 - DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,224 Selected=0x63869CAF - DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,279 Selected=0x8C72BEA8 - DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,187 Split=X Selected=0xEA83D666 + DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,388 Selected=0x63869CAF + DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,32 Selected=0x8C72BEA8 + DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,270 Split=X Selected=0xEA83D666 DockNode ID=0x00000004 Parent=0x00000008 SizeRef=610,192 Selected=0xEA83D666 DockNode ID=0x00000005 Parent=0x00000008 SizeRef=608,192 Selected=0x6DE9B20C diff --git a/story-editor/src/app/app_controller.cpp b/story-editor/src/app/app_controller.cpp index 9a9bd3a..f43871c 100644 --- a/story-editor/src/app/app_controller.cpp +++ b/story-editor/src/app/app_controller.cpp @@ -880,23 +880,21 @@ void AppController::SaveModule() m_logger.Log("Modules saved."); } -std::shared_ptr AppController::OpenModule(const std::string &uuid) +std::shared_ptr AppController::OpenModule(const std::string &uuid) { m_module = m_nodesFactory.GetModule(uuid); if (!m_module) { m_eventBus.Emit(std::make_shared(false, "Cannot find module: " + uuid)); - m_logger.Log("Cannot find module: " + uuid, true); + } else if (m_module->Load(m_resources, m_nodesFactory)) { - m_eventBus.Emit(std::make_shared(ModuleEvent::Type::Opened, uuid)); - m_logger.Log("Open module success: " + uuid); + m_eventBus.Emit(std::make_shared(true, "Open module success: " + uuid)); } else { m_eventBus.Emit(std::make_shared(false, "Failed to open module: " + uuid)); - m_logger.Log("Open module error: " + uuid, true); } return m_module; } diff --git a/story-editor/src/app/app_controller.h b/story-editor/src/app/app_controller.h index 5971d05..c27f25b 100644 --- a/story-editor/src/app/app_controller.h +++ b/story-editor/src/app/app_controller.h @@ -61,7 +61,7 @@ public: std::shared_ptr NewModule(); void SaveModule(); void CloseModule(); - std::shared_ptr OpenModule(const std::string &uuid); + std::shared_ptr OpenModule(const std::string &uuid); void OpenStory(const std::string &path = ""); void SaveStory(const std::string &path = ""); void ExportStory(const std::string &filename); diff --git a/story-editor/src/events/all_events.h b/story-editor/src/events/all_events.h index 946641b..bbca984 100644 --- a/story-editor/src/events/all_events.h +++ b/story-editor/src/events/all_events.h @@ -60,7 +60,7 @@ class ModuleEvent : public Event public: enum class Type { - Opened, + Open, Closed, }; diff --git a/story-editor/src/imgui_toast_notifier.h b/story-editor/src/imgui_toast_notifier.h index eeeefb5..b7ac0a8 100644 --- a/story-editor/src/imgui_toast_notifier.h +++ b/story-editor/src/imgui_toast_notifier.h @@ -26,6 +26,8 @@ struct Toast { class ImGuiToastNotifier { private: std::vector toasts; + const float TOAST_WIDTH = 300.0f; + const float TOAST_PADDING = 10.0f; public: void addToast(const std::string& title, const std::string& text, ToastType type, float duration = 3.0f) { @@ -37,56 +39,101 @@ public: return; } - ImGuiIO& io = ImGui::GetIO(); - ImVec2 viewport_pos = ImGui::GetMainViewport()->Pos; - ImVec2 viewport_size = ImGui::GetMainViewport()->Size; - ImVec2 window_pos = ImVec2(viewport_pos.x + viewport_size.x - 10.0f, viewport_pos.y + 10.0f); + // Get the main viewport work area (excludes menu bar, status bar, etc.) + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImVec2 work_pos = viewport->WorkPos; + ImVec2 work_size = viewport->WorkSize; + + // Position in top-right corner of the work area + ImVec2 window_pos = ImVec2( + work_pos.x + work_size.x - TOAST_PADDING, + work_pos.y + TOAST_PADDING + ); + ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, ImVec2(1.0f, 0.0f)); - - ImGui::SetNextWindowBgAlpha(0.75f); - if (ImGui::Begin("##ToastWindow", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing)) { + ImGui::SetNextWindowSize(ImVec2(TOAST_WIDTH, 0), ImGuiCond_Always); + ImGui::SetNextWindowBgAlpha(0.90f); + + // Add NoNav to prevent interfering with other windows + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_AlwaysAutoResize; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12, 12)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 8)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0f); + + if (ImGui::Begin("##ToastWindow", nullptr, window_flags)) { auto now = std::chrono::steady_clock::now(); auto it = toasts.begin(); + while (it != toasts.end()) { auto elapsed = std::chrono::duration(now - it->startTime).count(); + if (elapsed > it->duration) { it = toasts.erase(it); continue; } + // Calculate fade out effect in the last 0.5 seconds + float alpha = 1.0f; + if (elapsed > it->duration - 0.5f) { + alpha = (it->duration - elapsed) / 0.5f; + } + ImGui::PushID(it->title.c_str()); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha); ImVec4 color; const char* icon; switch (it->type) { case Success: color = ImVec4(0.18f, 0.80f, 0.44f, 1.0f); - icon = ICON_FA_CHECK_CIRCLE; // Font Awesome 5 success icon (check-circle) + icon = ICON_FA_CHECK_CIRCLE; break; case Warning: color = ImVec4(1.0f, 0.84f, 0.0f, 1.0f); - icon = ICON_FA_EXCLAMATION_TRIANGLE; // Font Awesome 5 warning icon (exclamation-triangle) + icon = ICON_FA_EXCLAMATION_TRIANGLE; break; case Error: color = ImVec4(0.94f, 0.31f, 0.31f, 1.0f); - icon = ICON_FA_TIMES_CIRCLE; // Font Awesome 5 error icon (times-circle) + icon = ICON_FA_TIMES_CIRCLE; break; } + // Draw icon and title on the same line ImGui::PushStyleColor(ImGuiCol_Text, color); ImGui::Text("%s", icon); ImGui::PopStyleColor(); - + ImGui::SameLine(); + ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); // Use default font for title ImGui::TextUnformatted(it->title.c_str()); - ImGui::TextWrapped("%s", it->text.c_str()); + ImGui::PopFont(); - ImGui::Separator(); + // Draw message text with wrapping + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + TOAST_WIDTH - 24.0f); + ImGui::TextUnformatted(it->text.c_str()); + ImGui::PopTextWrapPos(); + + ImGui::PopStyleVar(); // Alpha + + // Add separator between toasts if not the last one + if (std::next(it) != toasts.end()) { + ImGui::Separator(); + } + ImGui::PopID(); ++it; } ImGui::End(); } + + ImGui::PopStyleVar(3); // WindowPadding, ItemSpacing, WindowRounding } }; \ No newline at end of file diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index 85dcb15..8f22941 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -46,7 +46,7 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo , m_resourcesDock(appController.GetResourceManager(), appController) , m_nodeEditorWindow(appController, appController.GetNodesFactory(), m_widgetFactory, IStoryProject::PROJECT_TYPE_STORY) , m_moduleEditorWindow(appController, appController.GetNodesFactory(), m_widgetFactory, IStoryProject::PROJECT_TYPE_MODULE) - , m_libraryWindow(appController, appController.GetLibraryManager(), appController.GetNodesFactory()) + , m_libraryWindow(appController, appController.GetLibraryManager(), appController.GetNodesFactory(), eventBus) , m_projectPropertiesDialog(appController, appController.GetResourceManager()) { CloseProject(); @@ -96,7 +96,7 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo }); m_eventBus.Subscribe([this](const ModuleEvent &event) { - if (event.GetType() == ModuleEvent::Type::Opened) { + if (event.GetType() == ModuleEvent::Type::Open) { OpenModule(event.GetUuid()); } else if (event.GetType() == ModuleEvent::Type::Closed) { CloseModule(); @@ -327,6 +327,7 @@ void MainWindow::NewModule() void MainWindow::SaveModule() { + m_moduleEditorWindow.SaveNodesToProject(); m_appController.SaveModule(); m_logger.Log("Modules saved"); m_toastNotifier.addToast("Module", "Module saved", ToastType::Success); @@ -334,7 +335,7 @@ void MainWindow::SaveModule() void MainWindow::OpenModule(const std::string &uuid) { - auto module = m_appController.GetCurrentModule(); + auto module = m_appController.OpenModule(uuid); if (module) { m_moduleEditorWindow.Load(module); diff --git a/story-editor/src/node_editor/node_editor_window.cpp b/story-editor/src/node_editor/node_editor_window.cpp index 70e6bfe..684ff5f 100644 --- a/story-editor/src/node_editor/node_editor_window.cpp +++ b/story-editor/src/node_editor/node_editor_window.cpp @@ -102,53 +102,140 @@ void NodeEditorWindow::LoadPage(const std::string &uuid, const std::string &name void NodeEditorWindow::Load(std::shared_ptr story) { m_story = story; - - if (m_story) + + if (!m_story) { - try { - - BaseNodeWidget::InitId(); - InitializeProject(); - - auto [node_begin, node_end] = m_story->Nodes(m_currentPage->Uuid()); - - int i = 0; - - for (auto it = node_begin; it != node_end; ++it) - { - auto n = m_widgetFactory.CreateNodeWidget((*it)->GetType(), m_manager, (*it)); - if (n) - { - n->Initialize(); - // n->SetOutputs(m_story->OutputsCount((*it)->GetId())); // il faut que les noeuds aient une bonne taille de outputs avant de créer les liens - // m_currentPage->AddNode(n); - } - else - { - throw std::logic_error(std::string("No registered model with name ") + (*it)->GetType()); - } - - std::cout << "Created " << ++i << " node" << std::endl; - } - auto [link_begin, link_end] = m_story->Links(m_currentPage->Uuid()); - - for (auto it = link_begin; it != link_end; ++it) - { - // CreateLink(*it, - // GetInputPin((*it)->inNodeId, (*it)->inPortIndex), - // GetOutputPin((*it)->outNodeId, (*it)->outPortIndex)); - } - - m_loaded = true; - } - catch(std::exception &e) - { - std::cout << "(NodeEditorWindow::Load) " << e.what() << std::endl; - } + std::cout << "Cannot load null story" << std::endl; + return; } - // std::cout << "Loaded " << m_currentPage->m_nodes.size() << " nodes, " << m_currentPage->m_links.size() << " links" << std::endl; - + try + { + // Clear existing pages + m_pages.clear(); + m_currentPage.reset(); + + // Load all pages from the project + auto [nodesBegin, nodesEnd] = m_story->Nodes(m_story->MainUuid()); + auto [linksBegin, linksEnd] = m_story->Links(m_story->MainUuid()); + + // Create the main page + auto page = std::make_shared(m_story->MainUuid(), "Main"); + m_pages.push_back(page); + m_currentPage = page; + + // Map to store node UUID -> ImNodeFlow UID mapping + std::map nodeUidMap; + + // 1. Load all nodes from the project into ImNodeFlow + for (auto it = nodesBegin; it != nodesEnd; ++it) + { + auto baseNode = *it; + if (!baseNode) + continue; + + // Create the widget for this node + auto widget = m_widgetFactory.CreateNodeWidget( + baseNode->GetType(), + m_manager, + baseNode + ); + + if (!widget) + { + std::cout << "Failed to create widget for node type: " + << baseNode->GetType() << std::endl; + continue; + } + + // Initialize the widget + widget->Initialize(); + + // Create a NodeDelegate in ImNodeFlow + ImVec2 nodePos(baseNode->GetX(), baseNode->GetY()); + auto delegate = page->mINF.addNode(nodePos); + + // Link the delegate with the widget + delegate->SetWidget(widget); + + // Store the mapping between project node UUID and ImNodeFlow UID + nodeUidMap[baseNode->GetId()] = delegate->getUID(); + + std::cout << "Loaded node: " << baseNode->GetType() + << " at (" << nodePos.x << ", " << nodePos.y << ")" << std::endl; + } + + // 2. Load all connections/links + for (auto it = linksBegin; it != linksEnd; ++it) + { + auto connection = *it; + if (!connection) + continue; + + // Find the source and target nodes in ImNodeFlow + auto sourceUidIt = nodeUidMap.find(connection->outNodeId); + auto targetUidIt = nodeUidMap.find(connection->inNodeId); + + if (sourceUidIt == nodeUidMap.end() || targetUidIt == nodeUidMap.end()) + { + std::cout << "Warning: Cannot create link - node not found" << std::endl; + continue; + } + + // Get the ImNodeFlow nodes + auto& nodes = page->mINF.getNodes(); + auto sourceNodeIt = nodes.find(sourceUidIt->second); + auto targetNodeIt = nodes.find(targetUidIt->second); + + if (sourceNodeIt == nodes.end() || targetNodeIt == nodes.end()) + { + std::cout << "Warning: Node UID not found in ImNodeFlow" << std::endl; + continue; + } + + auto sourceNode = sourceNodeIt->second; + auto targetNode = targetNodeIt->second; + + // Get the pins from the nodes + // Output pin from source node + auto& sourcePins = sourceNode->getOuts(); + if (connection->outPortIndex >= static_cast(sourcePins.size())) + { + std::cout << "Warning: Invalid output port index: " + << connection->outPortIndex << std::endl; + continue; + } + auto* sourcePin = sourcePins[connection->outPortIndex].get(); + + // Input pin from target node + auto& targetPins = targetNode->getIns(); + if (connection->inPortIndex >= static_cast(targetPins.size())) + { + std::cout << "Warning: Invalid input port index: " + << connection->inPortIndex << std::endl; + continue; + } + auto* targetPin = targetPins[connection->inPortIndex].get(); + + // Create the link in ImNodeFlow + if (sourcePin && targetPin) + { + targetPin->createLink(sourcePin); + std::cout << "Created link: " << connection->outNodeId + << "[" << connection->outPortIndex << "] -> " + << connection->inNodeId + << "[" << connection->inPortIndex << "]" << std::endl; + } + } + + m_loaded = true; + std::cout << "Loaded " << nodeUidMap.size() << " nodes successfully" << std::endl; + } + catch(std::exception &e) + { + std::cout << "(NodeEditorWindow::Load) Exception: " << e.what() << std::endl; + m_loaded = false; + } } void NodeEditorWindow::SaveNodePositions() @@ -158,28 +245,140 @@ void NodeEditorWindow::SaveNodePositions() void NodeEditorWindow::SaveNodesToProject() { + if (!m_story) + { + std::cout << "Cannot save: no story project loaded" << std::endl; + return; + } + + // Clear current project structure + m_story->Clear(); + // Pour toutes les pages for (const auto& page : m_pages) { - // Clear current project nodes and links - m_story->Clear(); - + // Create the page in the project auto currentPage = m_story->CreatePage(page->Uuid()); - // On récupère les noeuds de la page - for (const auto& node : page->GetNodes()) + // 1. Save all nodes with their updated positions + for (auto &nodeEntry : page->mINF.getNodes()) { - // On les ajoute au projet - // currentPage->AddNode(node); + auto delegate = dynamic_cast(nodeEntry.second.get()); + if (!delegate) + continue; + + auto widget = delegate->GetWidget(); + if (!widget) + continue; + + auto baseNode = widget->Base(); + if (!baseNode) + continue; + + // Update node position from ImNodeFlow + ImVec2 nodePos = nodeEntry.second->getPos(); + baseNode->SetPosition(nodePos.x, nodePos.y); + + // Add node to project + m_story->AddNode(currentPage->Uuid(), baseNode); + + std::cout << "Saved node: " << baseNode->GetId() + << " at (" << nodePos.x << ", " << nodePos.y << ")" << std::endl; } - // On récupère tous les liens de la page - for (const auto& link : page->GetLinks()) + // 2. Save all links/connections + const auto& links = page->mINF.getLinks(); + + for (const auto& weakLink : links) { - // On les ajoute au projet - // currentPage->AddLink(link); + auto link = weakLink.lock(); + if (!link) + continue; + + // Get left (source) and right (target) pins + auto* leftPin = link->left(); + auto* rightPin = link->right(); + + if (!leftPin || !rightPin) + continue; + + // Get the nodes that own these pins + auto* leftNode = leftPin->getParent(); + auto* rightNode = rightPin->getParent(); + + if (!leftNode || !rightNode) + continue; + + // Cast to NodeDelegate to get the widget + auto* leftDelegate = dynamic_cast(leftNode); + auto* rightDelegate = dynamic_cast(rightNode); + + if (!leftDelegate || !rightDelegate) + continue; + + auto leftWidget = leftDelegate->GetWidget(); + auto rightWidget = rightDelegate->GetWidget(); + + if (!leftWidget || !rightWidget) + continue; + + // Get the base nodes (model) + auto leftBaseNode = leftWidget->Base(); + auto rightBaseNode = rightWidget->Base(); + + if (!leftBaseNode || !rightBaseNode) + continue; + + // Find the pin indices + int leftPinIndex = -1; + int rightPinIndex = -1; + + // Find output pin index on left node + const auto& leftOuts = leftNode->getOuts(); + for (size_t i = 0; i < leftOuts.size(); ++i) + { + if (leftOuts[i].get() == leftPin) + { + leftPinIndex = static_cast(i); + break; + } + } + + // Find input pin index on right node + const auto& rightIns = rightNode->getIns(); + for (size_t i = 0; i < rightIns.size(); ++i) + { + if (rightIns[i].get() == rightPin) + { + rightPinIndex = static_cast(i); + break; + } + } + + if (leftPinIndex < 0 || rightPinIndex < 0) + { + std::cout << "Warning: Could not find pin indices for connection" << std::endl; + continue; + } + + // Create the connection object + auto connection = std::make_shared(); + connection->outNodeId = leftBaseNode->GetId(); + connection->outPortIndex = leftPinIndex; + connection->inNodeId = rightBaseNode->GetId(); + connection->inPortIndex = rightPinIndex; + + // Add connection to project + m_story->AddConnection(currentPage->Uuid(), connection); + + std::cout << "Saved connection: " << connection->outNodeId + << "[" << connection->outPortIndex << "] -> " + << connection->inNodeId + << "[" << connection->inPortIndex << "]" << std::endl; } } + + std::cout << "SaveNodesToProject completed successfully" << std::endl; } void NodeEditorWindow::OpenFunction(const std::string &uuid, const std::string &name) diff --git a/story-editor/src/windows/library_window.cpp b/story-editor/src/windows/library_window.cpp index c786539..4948093 100644 --- a/story-editor/src/windows/library_window.cpp +++ b/story-editor/src/windows/library_window.cpp @@ -9,6 +9,8 @@ #include "gui.h" #include "ImGuiFileDialog.h" +#include "all_events.h" + typedef int (*xfer_callback_t)(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); @@ -50,11 +52,12 @@ void download_file(CURL *curl, } -LibraryWindow::LibraryWindow(IStoryManager &project, LibraryManager &library, NodesFactory &nodesFactory) +LibraryWindow::LibraryWindow(IStoryManager &project, LibraryManager &library, NodesFactory &nodesFactory, EventBus& eventBus) : WindowBase("Library Manager") , m_storyManager(project) , m_libraryManager(library) , m_nodesFactory(nodesFactory) + , m_eventBus(eventBus) { m_downloadThread = std::thread( std::bind(&LibraryWindow::DownloadThread, this) ); @@ -678,8 +681,7 @@ void LibraryWindow::Draw() ImGui::PushID(module.uuid.c_str()); if (ImGui::SmallButton("Open")) { - // Open the module in the editor - m_storyManager.OpenModule(module.uuid); + m_eventBus.Emit(std::make_shared(ModuleEvent::Type::Open, module.uuid)); } ImGui::PopID(); } diff --git a/story-editor/src/windows/library_window.h b/story-editor/src/windows/library_window.h index 4d57a59..001a1eb 100644 --- a/story-editor/src/windows/library_window.h +++ b/story-editor/src/windows/library_window.h @@ -7,6 +7,7 @@ #include "thread_safe_queue.h" #include "downloader.h" #include "nodes_factory.h" +#include "event_bus.h" #include @@ -65,7 +66,7 @@ struct TransferProgress { class LibraryWindow : public WindowBase { public: - LibraryWindow(IStoryManager &project, LibraryManager &library, NodesFactory &nodesFactory); + LibraryWindow(IStoryManager &project, LibraryManager &library, NodesFactory &nodesFactory, EventBus& eventBus); void Initialize(); virtual void Draw() override; @@ -75,6 +76,7 @@ private: IStoryManager &m_storyManager; LibraryManager &m_libraryManager; NodesFactory &m_nodesFactory; + EventBus &m_eventBus; Downloader m_downloader; CURL *m_curl;