mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
load/save module success, better toaster
This commit is contained in:
parent
883257fd78
commit
479497f1df
12 changed files with 370 additions and 123 deletions
|
|
@ -42,9 +42,6 @@ public:
|
|||
|
||||
virtual std::shared_ptr<IStoryProject> GetCurrentProject() = 0;
|
||||
|
||||
// Modules
|
||||
virtual std::shared_ptr<IStoryProject> OpenModule(const std::string &uuid) = 0;
|
||||
|
||||
// Node interaction
|
||||
virtual void CompileNodes(bool compileonly) = 0;
|
||||
virtual void BuildCode(bool compileonly) = 0;
|
||||
|
|
|
|||
|
|
@ -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<StoryProject>(entry.second.first);
|
||||
if (module)
|
||||
auto p = dynamic_cast<StoryProject*>(n.second.first.get());
|
||||
if (p)
|
||||
{
|
||||
module->Save(manager);
|
||||
p->Save(manager);
|
||||
}
|
||||
// Only modules
|
||||
// auto module = std::dynamic_pointer_cast<StoryProject>(entry.second.first);
|
||||
// if (module)
|
||||
// {
|
||||
// module->Save(manager);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -880,23 +880,21 @@ void AppController::SaveModule()
|
|||
m_logger.Log("Modules saved.");
|
||||
}
|
||||
|
||||
std::shared_ptr<IStoryProject> AppController::OpenModule(const std::string &uuid)
|
||||
std::shared_ptr<StoryProject> AppController::OpenModule(const std::string &uuid)
|
||||
{
|
||||
m_module = m_nodesFactory.GetModule(uuid);
|
||||
if (!m_module)
|
||||
{
|
||||
m_eventBus.Emit(std::make_shared<GenericResultEvent>(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>(ModuleEvent::Type::Opened, uuid));
|
||||
m_logger.Log("Open module success: " + uuid);
|
||||
m_eventBus.Emit(std::make_shared<GenericResultEvent>(true, "Open module success: " + uuid));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventBus.Emit(std::make_shared<GenericResultEvent>(false, "Failed to open module: " + uuid));
|
||||
m_logger.Log("Open module error: " + uuid, true);
|
||||
}
|
||||
return m_module;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public:
|
|||
std::shared_ptr<StoryProject> NewModule();
|
||||
void SaveModule();
|
||||
void CloseModule();
|
||||
std::shared_ptr<IStoryProject> OpenModule(const std::string &uuid);
|
||||
std::shared_ptr<StoryProject> OpenModule(const std::string &uuid);
|
||||
void OpenStory(const std::string &path = "");
|
||||
void SaveStory(const std::string &path = "");
|
||||
void ExportStory(const std::string &filename);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class ModuleEvent : public Event
|
|||
public:
|
||||
enum class Type
|
||||
{
|
||||
Opened,
|
||||
Open,
|
||||
Closed,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ struct Toast {
|
|||
class ImGuiToastNotifier {
|
||||
private:
|
||||
std::vector<Toast> 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);
|
||||
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, ImVec2(1.0f, 0.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;
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(0.75f);
|
||||
if (ImGui::Begin("##ToastWindow", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing)) {
|
||||
// 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::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<float>(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();
|
||||
|
||||
// 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
|
||||
}
|
||||
};
|
||||
|
|
@ -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<ModuleEvent>([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);
|
||||
|
|
|
|||
|
|
@ -103,52 +103,139 @@ void NodeEditorWindow::Load(std::shared_ptr<StoryProject> 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 << "Cannot load null story" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
try
|
||||
{
|
||||
// CreateLink(*it,
|
||||
// GetInputPin((*it)->inNodeId, (*it)->inPortIndex),
|
||||
// GetOutputPin((*it)->outNodeId, (*it)->outPortIndex));
|
||||
// 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<NodeEditorPage>(m_story->MainUuid(), "Main");
|
||||
m_pages.push_back(page);
|
||||
m_currentPage = page;
|
||||
|
||||
// Map to store node UUID -> ImNodeFlow UID mapping
|
||||
std::map<std::string, ImFlow::NodeUID> 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<NodeDelegate>(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<int>(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<int>(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) " << e.what() << std::endl;
|
||||
std::cout << "(NodeEditorWindow::Load) Exception: " << e.what() << std::endl;
|
||||
m_loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// std::cout << "Loaded " << m_currentPage->m_nodes.size() << " nodes, " << m_currentPage->m_links.size() << " links" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
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<NodeDelegate*>(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<NodeDelegate*>(leftNode);
|
||||
auto* rightDelegate = dynamic_cast<NodeDelegate*>(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<int>(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<int>(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>();
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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>(ModuleEvent::Type::Open, module.uuid));
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "thread_safe_queue.h"
|
||||
#include "downloader.h"
|
||||
#include "nodes_factory.h"
|
||||
#include "event_bus.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue