Start of module editor with pre version of independant factory
Some checks failed
Build-StoryEditor / build_linux (push) Has been cancelled
Build-StoryEditor / build_win32 (push) Has been cancelled
Deploy-Documentation / deploy (push) Has been cancelled

This commit is contained in:
anthony@rabine.fr 2025-07-06 14:25:41 +02:00
parent b962d303c5
commit 81f7e9343b
10 changed files with 218 additions and 80 deletions

View file

@ -32,7 +32,7 @@ class IStoryManager
public: public:
virtual ~IStoryManager() {} virtual ~IStoryManager() {}
virtual void OpenProject(const std::string &uuid) = 0; virtual void OpenProject(const std::string &uuid, IStoryManager::ProjectType type) = 0;
virtual void ImportProject(const std::string &fileName, int format) = 0; virtual void ImportProject(const std::string &fileName, int format) = 0;
virtual void Log(const std::string &txt, bool critical = false) = 0; virtual void Log(const std::string &txt, bool critical = false) = 0;
virtual void PlaySoundFile(const std::string &fileName) = 0; virtual void PlaySoundFile(const std::string &fileName) = 0;

View file

@ -9,7 +9,7 @@ class IStoryProject
{ {
public: public:
enum ProjectType enum Type
{ {
PROJECT_TYPE_STORY = 0, PROJECT_TYPE_STORY = 0,
PROJECT_TYPE_MODULE = 1, PROJECT_TYPE_MODULE = 1,

View file

@ -1,5 +1,10 @@
#pragma once #pragma once
#include <fstream>
#include <iostream>
#include <map>
#include <filesystem>
#include "json.hpp" #include "json.hpp"
// #include "media_node.h" // #include "media_node.h"
#include "function_node.h" #include "function_node.h"
@ -7,20 +12,34 @@
#include "operator_node.h" #include "operator_node.h"
#include "print_node.h" #include "print_node.h"
#include "syscall_node.h" #include "syscall_node.h"
#include "story_project.h"
static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec";
static const std::string FunctionNodeUuid = "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";
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type);
class NodesFactory class NodesFactory
{ {
public: public:
NodesFactory() {
NodesFactory(ILogger &log)
: m_log(log)
, m_rootPath("")
{
// Register node types // Register node types
// registerNode<MediaNode>("media-node"); // registerNode<MediaNode>("media-node");
registerNode<OperatorNode>("operator-node"); registerNode<OperatorNode>(OperatorNodeUuid, nullptr);
registerNode<FunctionNode>("function-node"); registerNode<FunctionNode>(FunctionNodeUuid, nullptr);
registerNode<VariableNode>("variable-node"); registerNode<VariableNode>(VariableNodeUuid, nullptr);
registerNode<PrintNode>("print-node"); registerNode<PrintNode>(PrintNodeUuid, nullptr);
registerNode<SyscallNode>("syscall-node"); registerNode<SyscallNode>(SyscallNodeUuid, nullptr);
} }
~NodesFactory() = default; ~NodesFactory() = default;
std::vector<std::string> GetNodeTypes() const { std::vector<std::string> GetNodeTypes() const {
@ -38,12 +57,58 @@ public:
} }
else else
{ {
auto n = i->second(type); auto n = i->second.second(type);
return n; return n;
} }
} }
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<CustomModuleNode>("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")
{
// 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<StoryProject>(m_log);
p->ParseStoryInformation(j);
if (p->IsModule())
{
registerNode<FunctionNode>(FunctionNodeUuid, 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
}
}
}
}
private: private:
ILogger &m_log;
std::string m_rootPath;
template<class NodeType> template<class NodeType>
struct Factory { struct Factory {
@ -52,12 +117,13 @@ private:
} }
}; };
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type); // UUID is the key, and the value is a function that creates the node
typedef std::map<std::string, GenericCreator> Registry; typedef std::map<std::string, std::pair<std::shared_ptr<IStoryProject>, GenericCreator>> Registry;
Registry m_registry; Registry m_registry;
template<class Derived> template<class Derived>
void registerNode(const std::string& key) { void registerNode(const std::string& uuid, std::shared_ptr<IStoryProject> moduleInfo = nullptr) {
m_registry.insert(typename Registry::value_type(key, Factory<Derived>::create_func)); info.creator = ;
m_registry.insert(typename Registry::value_type(uuid, std::make_pair<moduleInfo, Factory<Derived>::create_func>));
} }
}; };

View file

@ -13,7 +13,11 @@ public:
virtual void Initialize() override; virtual void Initialize() override;
private: void SetContent(nlohmann::json &content) {
m_content = content;
}
private:
nlohmann::json m_content;
}; };

View file

@ -104,6 +104,23 @@ bool StoryProject::ParseStoryInformation(nlohmann::json &j)
m_name = projectData["name"].get<std::string>(); m_name = projectData["name"].get<std::string>();
m_uuid = projectData["uuid"].get<std::string>(); m_uuid = projectData["uuid"].get<std::string>();
m_uuid = projectData["description"].get<std::string>();
std::string typeString = projectData["type"].get<std::string>();
if (typeString == "story")
{
m_projectType = IStoryProject::PROJECT_TYPE_STORY;
}
else if (typeString == "module")
{
m_projectType = IStoryProject::PROJECT_TYPE_MODULE;
}
else
{
m_log.Log("Unknown project type: " + typeString, true);
return false;
}
m_titleImage = projectData.value("title_image", ""); m_titleImage = projectData.value("title_image", "");
m_titleSound = projectData.value("title_sound", ""); m_titleSound = projectData.value("title_sound", "");

View file

@ -83,6 +83,8 @@ public:
// Initialize with an existing project // Initialize with an existing project
const bool IsInitialized() const { return m_initialized; } 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; }
bool ParseStoryInformation(nlohmann::json &j); bool ParseStoryInformation(nlohmann::json &j);
@ -117,9 +119,11 @@ private:
std::string m_titleImage; std::string m_titleImage;
std::string m_titleSound; std::string m_titleSound;
std::string m_description; std::string m_description;
IStoryProject::Type m_projectType{IStoryProject::PROJECT_TYPE_STORY};
uint32_t m_version; uint32_t m_version;
bool m_selected{false}; bool m_selected{false};
IStoryProject::ProjectType m_type{IStoryProject::PROJECT_TYPE_STORY};
std::unordered_set<std::string> m_usedLabels; // permet de ne pas générer un label qui existe déjà std::unordered_set<std::string> m_usedLabels; // permet de ne pas générer un label qui existe déjà

View file

@ -26,10 +26,11 @@ if (!(x)) { \
#include "json.hpp" #include "json.hpp"
NodeEditorWindow::NodeEditorWindow(IStoryManager &manager, NodesFactory &factory) NodeEditorWindow::NodeEditorWindow(IStoryManager &manager, NodesFactory &factory, IStoryManager::ProjectType type)
: WindowBase("Node editor") : WindowBase(type == IStoryManager::ProjectType::PROJECT_TYPE_STORY ? "Story editor" : "Module editor")
, m_manager(manager) , m_manager(manager)
, m_nodesFactory(factory) , m_nodesFactory(factory)
, m_editorType(type)
{ {
// registerNode<MediaNodeWidget>("media-node"); // registerNode<MediaNodeWidget>("media-node");
@ -379,26 +380,20 @@ void NodeEditorWindow::Draw()
void NodeEditorWindow::ToolbarUI() void NodeEditorWindow::ToolbarUI()
{ {
auto& io = ImGui::GetIO(); // auto& io = ImGui::GetIO();
ImVec2 window_pos = ImGui::GetWindowPos(); // ImVec2 window_pos = ImGui::GetWindowPos();
ImVec2 window_size = ImGui::GetWindowSize(); // ImVec2 window_size = ImGui::GetWindowSize();
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove; // ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove;
// if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
// {
// const ImGuiViewport* viewport = ImGui::GetWindowViewport();
// window_flags |= ImGuiWindowFlags_NoDocking;
// io.ConfigViewportsNoDecoration = true;
// ImGui::SetNextWindowViewport(viewport->ID);
// }
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
// ImGui::PushStyleColor(ImGuiCol_Button, m_StyleColors[BluePrintStyleColor_ToolButton]);
// ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_StyleColors[BluePrintStyleColor_ToolButtonHovered]);
// ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_StyleColors[BluePrintStyleColor_ToolButtonActive]);
// ImGui::PushStyleColor(ImGuiCol_TexGlyphShadow, ImVec4(0.1, 0.1, 0.1, 0.8));
// ImGui::PushStyleVar(ImGuiStyleVar_TexGlyphShadowOffset, ImVec2(2.0, 2.0));
ImGui::Begin("TOOLBAR", NULL, window_flags); // ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
// ImGui::PopStyleVar();
// ImGui::Begin("TOOLBAR", NULL, window_flags);
ImGui::SetCursorPos(ImVec2(10, 40));
ImGui::BeginChild("ToolbarChild", ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 1.5f), false, ImGuiWindowFlags_NoScrollbar);
ImGui::PushID(m_editorType);
// Draw call stack, each function is a button // Draw call stack, each function is a button
for (auto page : m_callStack) for (auto page : m_callStack)
@ -422,11 +417,17 @@ void NodeEditorWindow::ToolbarUI()
ImGui::SameLine(); ImGui::SameLine();
} }
ImGui::End(); ImGui::PopID();
// ImGui::PopStyleVar();
// ImGui::PopStyleColor(4); // ImGui::End();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{ ImGui::EndChild(); // Fin de la ChildWindow de la barre d'outils
io.ConfigViewportsNoDecoration = false;
} ImGui::SetCursorPos(ImVec2(0, 0));
// if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
// {
// io.ConfigViewportsNoDecoration = false;
// }
} }

View file

@ -26,7 +26,8 @@ namespace ed = ax::NodeEditor;
class NodeEditorWindow : public WindowBase class NodeEditorWindow : public WindowBase
{ {
public: public:
NodeEditorWindow(IStoryManager &manager, NodesFactory &factory);
NodeEditorWindow(IStoryManager &manager, NodesFactory &factory, IStoryManager::ProjectType type = IStoryManager::ProjectType::PROJECT_TYPE_STORY);
~NodeEditorWindow(); ~NodeEditorWindow();
virtual void Draw() override; virtual void Draw() override;
@ -40,6 +41,7 @@ public:
private: private:
IStoryManager &m_manager; IStoryManager &m_manager;
NodesFactory &m_nodesFactory; NodesFactory &m_nodesFactory;
IStoryManager::ProjectType m_editorType{IStoryManager::ProjectType::PROJECT_TYPE_STORY};
bool m_loaded{false}; bool m_loaded{false};

View file

@ -30,6 +30,7 @@ MainWindow::MainWindow()
, m_cpuWindow(*this) , m_cpuWindow(*this)
, m_resourcesWindow(*this) , m_resourcesWindow(*this)
, m_nodeEditorWindow(*this, m_nodesFactory) , m_nodeEditorWindow(*this, m_nodesFactory)
, m_moduleEditorWindow(*this, m_nodesFactory, NodeEditorWindow::EditorType::NODE_EDITOR_MODULE)
, m_libraryWindow(*this, m_libraryManager) , m_libraryWindow(*this, m_libraryManager)
, m_variablesWindow(*this) , m_variablesWindow(*this)
, m_player(*this) , m_player(*this)
@ -582,6 +583,7 @@ bool MainWindow::Initialize()
m_debuggerWindow.Initialize(); m_debuggerWindow.Initialize();
m_emulatorWindow.Initialize(); m_emulatorWindow.Initialize();
m_nodeEditorWindow.Initialize(); m_nodeEditorWindow.Initialize();
m_moduleEditorWindow.Initialize();
m_PropertiesWindow.Initialize(); m_PropertiesWindow.Initialize();
m_libraryWindow.Initialize(); m_libraryWindow.Initialize();
@ -835,7 +837,7 @@ void MainWindow::ProjectPropertiesPopup()
} }
} }
void MainWindow::SaveProject() void MainWindow::SaveProject(IStoryManager::ProjectType type)
{ {
nlohmann::json model; nlohmann::json model;
m_story->Save(m_resources); m_story->Save(m_resources);
@ -844,7 +846,8 @@ void MainWindow::SaveProject()
void MainWindow::OpenProject(const std::string &uuid) void MainWindow::OpenProject(const std::string &uuid)
{ {
CloseProject(); CloseProject(IStoryManager::ProjectType::PROJECT_TYPE_STORY);
m_story = m_libraryManager.GetStory(uuid); m_story = m_libraryManager.GetStory(uuid);
// DEBUG CODE !!!!!!!!!!!!! Permet si décommenter de forcer l'import, permet de tester plus facilement l'algo en ouvrant le projet // DEBUG CODE !!!!!!!!!!!!! Permet si décommenter de forcer l'import, permet de tester plus facilement l'algo en ouvrant le projet
@ -875,6 +878,7 @@ void MainWindow::OpenProject(const std::string &uuid)
} }
m_nodeEditorWindow.Enable(); m_nodeEditorWindow.Enable();
m_emulatorWindow.Enable(); m_emulatorWindow.Enable();
m_consoleWindow.Enable(); m_consoleWindow.Enable();
m_debuggerWindow.Enable(); m_debuggerWindow.Enable();
@ -891,6 +895,70 @@ void MainWindow::OpenProject(const std::string &uuid)
RefreshProjectInformation(); RefreshProjectInformation();
} }
void MainWindow::OpenModule(const std::string &uuid)
{
m_story = m_libraryManager.GetModule(uuid);
if (!m_story)
{
Log("Cannot find module: " + uuid);
}
else if (m_story->Load(m_resources, m_nodesFactory))
{
Log("Open module success");
m_moduleEditorWindow.Load(m_story);
}
else
{
Log("Open module error");
}
m_moduleEditorWindow.Enable();
}
void MainWindow::CloseProject(IStoryManager::ProjectType type)
{
// FIXME: not sure but if present, we lost some information in the library manager
// if (m_story)
// {
// m_story->Clear();
// m_story.reset();
// }
m_resources.Clear();
if (type == IStoryManager::ProjectType::PROJECT_TYPE_STORY)
{
m_nodeEditorWindow.Initialize();
m_nodeEditorWindow.Disable();
}
else if (type == IStoryManager::ProjectType::PROJECT_TYPE_MODULE)
{
m_moduleEditorWindow.Initialize();
m_moduleEditorWindow.Disable();
}
// FIXME: si un des deux types de projets est encore ouvert, on ne va pas fermer
// le sautres fenêtres de preview
m_emulatorWindow.ClearImage();
m_consoleWindow.ClearLog();
m_debuggerWindow.ClearErrors();
m_debuggerWindow.SetScript("");
m_emulatorWindow.Disable();
m_debuggerWindow.Disable();
m_resourcesWindow.Disable();
m_PropertiesWindow.Disable();
m_variablesWindow.Disable();
m_cpuWindow.Disable();
RefreshProjectInformation();
}
void MainWindow::ImportProject(const std::string &filePathName, int format) void MainWindow::ImportProject(const std::string &filePathName, int format)
{ {
@ -927,36 +995,6 @@ void MainWindow::RefreshProjectInformation()
} }
void MainWindow::CloseProject()
{
// FIXME: not sure but if present, we lost some information in the library manager
// if (m_story)
// {
// m_story->Clear();
// m_story.reset();
// }
m_resources.Clear();
m_nodeEditorWindow.Initialize();
m_emulatorWindow.ClearImage();
m_consoleWindow.ClearLog();
m_debuggerWindow.ClearErrors();
m_debuggerWindow.SetScript("");
m_nodeEditorWindow.Disable();
m_emulatorWindow.Disable();
m_debuggerWindow.Disable();
m_resourcesWindow.Disable();
m_PropertiesWindow.Disable();
m_variablesWindow.Disable();
m_cpuWindow.Disable();
RefreshProjectInformation();
}
void MainWindow::DrawToolBar(float topPadding) void MainWindow::DrawToolBar(float topPadding)
{ {
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration |
@ -1031,6 +1069,7 @@ void MainWindow::Loop()
m_debuggerWindow.Draw(); m_debuggerWindow.Draw();
m_resourcesWindow.Draw(); m_resourcesWindow.Draw();
m_nodeEditorWindow.Draw(); m_nodeEditorWindow.Draw();
m_moduleEditorWindow.Draw();
m_variablesWindow.Draw(); m_variablesWindow.Draw();
m_cpuWindow.Draw(); m_cpuWindow.Draw();

View file

@ -116,6 +116,8 @@ private:
NodeEditorWindow m_nodeEditorWindow; NodeEditorWindow m_nodeEditorWindow;
NodeEditorWindow m_moduleEditorWindow;
PropertiesWindow m_PropertiesWindow; PropertiesWindow m_PropertiesWindow;
LibraryWindow m_libraryWindow; LibraryWindow m_libraryWindow;
@ -134,7 +136,12 @@ private:
WebServer m_webServer; WebServer m_webServer;
// From IStoryManager (proxy to StoryProject class) // From IStoryManager (proxy to StoryProject class)
virtual void OpenProject(const std::string &uuid) override; virtual void OpenProject(const std::string &uuid, IStoryManager::ProjectType type) override;
void SaveProject(IStoryManager::ProjectType type);
void CloseProject(IStoryManager::ProjectType type);
// From IStoryManager (proxy to StoryProject class)
virtual void ImportProject(const std::string &filePathName, int format); virtual void ImportProject(const std::string &filePathName, int format);
virtual void PlaySoundFile(const std::string &fileName) override;; virtual void PlaySoundFile(const std::string &fileName) override;;
virtual std::string BuildFullAssetsPath(const std::string_view fileName) const override; virtual std::string BuildFullAssetsPath(const std::string_view fileName) const override;
@ -185,8 +192,6 @@ private:
bool ShowQuitConfirm(); bool ShowQuitConfirm();
void DrawToolBar(float topPadding); void DrawToolBar(float topPadding);
void SaveProject();
void CloseProject();
void DrawStatusBar(); void DrawStatusBar();
void UpdateVmView(); void UpdateVmView();