From 8aa18fa5af42b636edd9dee56ac7cda85c61aded Mon Sep 17 00:00:00 2001 From: Anthony Rabine Date: Tue, 21 Oct 2025 15:55:40 +0200 Subject: [PATCH] Add module properties popup. --- .../interfaces/i_story_manager.h | 2 + story-editor/src/app/app_controller.cpp | 7 +- story-editor/src/app/app_controller.h | 6 +- .../src/dialogs/module_properties_dialog.h | 116 ++++++++++++++++++ .../src/dialogs/project_properties_dialog.h | 2 +- story-editor/src/main_window.cpp | 48 +++++--- story-editor/src/main_window.h | 6 +- 7 files changed, 165 insertions(+), 22 deletions(-) create mode 100644 story-editor/src/dialogs/module_properties_dialog.h diff --git a/core/story-manager/interfaces/i_story_manager.h b/core/story-manager/interfaces/i_story_manager.h index 076849d..ed6247e 100644 --- a/core/story-manager/interfaces/i_story_manager.h +++ b/core/story-manager/interfaces/i_story_manager.h @@ -34,6 +34,7 @@ public: virtual void OpenProject(const std::string &uuid) = 0; virtual void SaveProject() = 0; + virtual void SaveModule() = 0; virtual void ImportProject(const std::string &fileName, int format) = 0; virtual void Log(const std::string &txt, bool critical = false) = 0; virtual void PlaySoundFile(const std::string &fileName) = 0; @@ -41,6 +42,7 @@ public: virtual void OpenFunction(const std::string &uuid, const std::string &name) = 0; virtual std::shared_ptr GetCurrentProject() = 0; + virtual std::shared_ptr GetCurrentModule() = 0; // Node interaction virtual void BuildCode(bool compileonly) = 0; diff --git a/story-editor/src/app/app_controller.cpp b/story-editor/src/app/app_controller.cpp index 45bfa5c..30e6ef1 100644 --- a/story-editor/src/app/app_controller.cpp +++ b/story-editor/src/app/app_controller.cpp @@ -854,5 +854,10 @@ void AppController::ImportProject(const std::string &filePathName, int format) std::shared_ptr AppController::GetCurrentProject() { - return m_story; // Retourne le projet actuel + return m_story; +} + +std::shared_ptr AppController::GetCurrentModule() +{ + return m_module; } diff --git a/story-editor/src/app/app_controller.h b/story-editor/src/app/app_controller.h index 1344de8..93ad3f6 100644 --- a/story-editor/src/app/app_controller.h +++ b/story-editor/src/app/app_controller.h @@ -60,20 +60,19 @@ public: void CloseProject(); void SaveProject(); std::shared_ptr NewModule(); - void SaveModule(); + void CloseModule(); 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); - std::shared_ptr GetCurrentStory() const { return m_story; } - std::shared_ptr GetCurrentModule() const { return m_module; } void CompileNodes(IStoryProject::Type type); void Build(bool compileonly); void BuildModule(bool compileonly); void BuildCode(std::shared_ptr story, bool compileonly, bool force = false); // --- Fonctions de IStoryManager --- + void SaveModule() override; virtual void SetExternalSourceFile(const std::string &filename) override; virtual void LoadBinaryStory(const std::string &filename) override; virtual void ToggleBreakpoint(int line) override; @@ -90,6 +89,7 @@ public: virtual std::string VmState() const override; virtual void BuildCode(bool compileonly); virtual std::shared_ptr GetCurrentProject() override; + virtual std::shared_ptr GetCurrentModule() override; // --- Fonctions de IAudioEvent --- virtual void EndOfAudio() override; diff --git a/story-editor/src/dialogs/module_properties_dialog.h b/story-editor/src/dialogs/module_properties_dialog.h new file mode 100644 index 0000000..9820405 --- /dev/null +++ b/story-editor/src/dialogs/module_properties_dialog.h @@ -0,0 +1,116 @@ +// module_properties_dialog.h +#ifndef MODULE_PROPERTIES_DIALOG_H +#define MODULE_PROPERTIES_DIALOG_H + +#include "dialog_base.h" +#include +#include "i_story_manager.h" +#include "IconsMaterialDesignIcons.h" + +class ModulePropertiesDialog : public DialogBase +{ +public: + ModulePropertiesDialog(IStoryManager &storyManager) + : m_storyManager(storyManager) + { + m_module_name[0] = '\0'; + } + + // Affiche le popup "Module Properties". + // Doit être appelée à chaque frame dans la boucle de rendu ImGui. + void Draw() override + { + auto module = m_storyManager.GetCurrentModule(); + if (module) + { + if (m_firstOpen) + { + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + m_firstOpen = false; + // Copy current module name to the edit buffer + std::size_t length = module->GetName().copy(m_module_name, sizeof(m_module_name) - 1); + m_module_name[length] = '\0'; + } + DrawContent(module); + } + else + { + Reset(); + return; + } + } + +private: + IStoryManager &m_storyManager; + char m_module_name[256] = ""; + + // Implémentation de la méthode virtuelle pure GetTitle() + const char* GetTitle() const override + { + return "ModulePropertiesPopup"; + } + + void DrawContent(std::shared_ptr module) + { + if (ImGui::BeginPopupModal(GetTitle(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text(ICON_MDI_PUZZLE_OUTLINE " Module Properties"); + ImGui::Separator(); + ImGui::Spacing(); + + // Module Name + ImGui::Text("Module name:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(300.0f); + ImGui::InputTextWithHint("##module_name", "Enter module name", m_module_name, IM_ARRAYSIZE(m_module_name)); + + ImGui::Spacing(); + ImGui::Separator(); + ImGui::Spacing(); + + // Buttons + if (ImGui::Button("OK", ImVec2(120, 0))) + { + // Validate and save + if (strlen(m_module_name) > 0) + { + module->SetName(m_module_name); + m_storyManager.SaveModule(); + ImGui::CloseCurrentPopup(); + Reset(); + } + else + { + // Show error: name cannot be empty + ImGui::OpenPopup("EmptyNameError"); + } + } + + ImGui::SetItemDefaultFocus(); + ImGui::SameLine(); + + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + Reset(); + } + + // Error popup for empty name + if (ImGui::BeginPopupModal("EmptyNameError", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text(ICON_MDI_ALERT " Module name cannot be empty!"); + ImGui::Spacing(); + if (ImGui::Button("OK", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::EndPopup(); + } + } +}; + +#endif // MODULE_PROPERTIES_DIALOG_H \ No newline at end of file diff --git a/story-editor/src/dialogs/project_properties_dialog.h b/story-editor/src/dialogs/project_properties_dialog.h index 814804e..51bd6d0 100644 --- a/story-editor/src/dialogs/project_properties_dialog.h +++ b/story-editor/src/dialogs/project_properties_dialog.h @@ -58,7 +58,7 @@ private: void DrawContent(std::shared_ptr story) { - if (ImGui::BeginPopupModal(GetTitle(), NULL, ImGuiWindowFlags_AlwaysAutoResize)) + if (ImGui::BeginPopupModal(GetTitle(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("Project name: "); ImGui::SameLine(); diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index 975bbed..837cdfe 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -1,5 +1,7 @@ -#include "main_window.h" #include +#include + +#include "main_window.h" #include #include "platform_folders.h" @@ -24,6 +26,7 @@ #include "app_controller.h" #include "all_events.h" +#include "story_project.h" #include "nodes_factory.h" #include "media_node_widget.h" @@ -55,6 +58,7 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo , m_moduleEditorWindow(appController, appController.GetNodesFactory(), m_widgetFactory, IStoryProject::PROJECT_TYPE_MODULE) , m_libraryWindow(appController, appController.GetLibraryManager(), appController.GetNodesFactory(), eventBus) , m_projectPropertiesDialog(appController, appController.GetResourceManager()) + , m_modulePropertiesDialog(appController) { CloseProject(); CloseModule(); @@ -174,12 +178,12 @@ float MainWindow::DrawMainMenuBar() { CloseProject(); - m_story = m_appController.NewProject(); + auto newProject = m_appController.NewProject(); - if (m_story) + if (newProject) { m_appController.SaveProject(); - OpenProject(m_story->GetUuid()); + OpenProject(newProject->GetUuid()); } } @@ -201,7 +205,7 @@ float MainWindow::DrawMainMenuBar() } */ - bool init = m_story ? true : false; // local copy because CloseProject() changes the status between BeginDisabled/EndDisabled + bool init = m_nodeEditorWindow.GetCurrentStory() ? true : false; // local copy because CloseProject() changes the status between BeginDisabled/EndDisabled if (!init) ImGui::BeginDisabled(); @@ -244,6 +248,11 @@ float MainWindow::DrawMainMenuBar() CloseModule(); } + if (ImGui::MenuItem("Module settings")) + { + m_modulePropertiesDialog.Show(); + } + ImGui::EndMenu(); } @@ -273,10 +282,14 @@ float MainWindow::DrawMainMenuBar() } m_aboutDialog.Open(); - if (m_story) + if (m_nodeEditorWindow.GetCurrentStory()) { m_projectPropertiesDialog.Open(); } + if (m_moduleEditorWindow.GetCurrentStory()) + { + m_modulePropertiesDialog.Open(); + } return height; } @@ -339,8 +352,9 @@ bool MainWindow::ShowQuitConfirm() void MainWindow::OpenProject(const std::string &uuid) { - m_nodeEditorWindow.Load(m_story); - auto proj = m_story->GetProjectFilePath(); + auto p = dynamic_pointer_cast(m_appController.GetCurrentProject()); + m_nodeEditorWindow.Load(p); + auto proj = p->GetProjectFilePath(); // Add to recent if not exists if (std::find(m_recentProjects.begin(), m_recentProjects.end(), proj) == m_recentProjects.end()) { @@ -452,9 +466,14 @@ void MainWindow::RefreshProjectInformation() { std::string fullText = "Story Editor " + LibraryManager::GetVersion(); - if (m_story) + auto p = m_nodeEditorWindow.GetCurrentStory(); + if (p) { - fullText += " - " + m_story->GetProjectFilePath(); + auto proj = dynamic_pointer_cast(p); + if (proj) + { + fullText += " - " + proj->GetProjectFilePath(); + } } m_gui.SetWindowTitle(fullText); } @@ -607,14 +626,14 @@ bool MainWindow::Loop() ImGuiIO& io = ImGui::GetIO(); if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_S, false)) { - if (moduleEditorFocused && m_module) + if (moduleEditorFocused && m_moduleEditorWindow.GetCurrentStory()) { // Si l'éditeur de module a le focus, sauvegarder le module m_appController.SaveModule(); m_toastNotifier.success("Module sauvegardé"); m_logger.Log("Module sauvegardé via Ctrl+S"); } - else if (m_story) + else if (m_nodeEditorWindow.GetCurrentStory()) { // Sinon, sauvegarder l'histoire principale m_appController.SaveProject(); @@ -633,12 +652,12 @@ bool MainWindow::Loop() { bool moduleEditorFocused = m_moduleEditorWindow.IsFocused(); - if (moduleEditorFocused && m_module) + if (moduleEditorFocused && m_moduleEditorWindow.GetCurrentStory()) { m_logger.Log("Building module..."); m_appController.CompileNodes(IStoryProject::PROJECT_TYPE_MODULE); } - else if (m_story) + else if (m_nodeEditorWindow.GetCurrentStory()) { m_logger.Log("Building story..."); m_appController.CompileNodes(IStoryProject::PROJECT_TYPE_STORY); @@ -648,6 +667,7 @@ bool MainWindow::Loop() m_aboutDialog.Draw(); m_projectPropertiesDialog.Draw(); + m_modulePropertiesDialog.Draw(); m_toastNotifier.render(); diff --git a/story-editor/src/main_window.h b/story-editor/src/main_window.h index 4da4d36..22f83d4 100644 --- a/story-editor/src/main_window.h +++ b/story-editor/src/main_window.h @@ -21,6 +21,7 @@ // Dialogs #include "about_dialog.h" #include "project_properties_dialog.h" +#include "module_properties_dialog.h" #include "event_bus.h" #include "app_controller.h" @@ -31,6 +32,7 @@ #include "LanguageSelector.h" #include "chip32_machine.h" + class MainWindow : public std::enable_shared_from_this, public ILogSubject { public: @@ -48,9 +50,6 @@ private: AppController &m_appController; // Controller for application logic NodeWidgetFactory m_widgetFactory; - std::shared_ptr m_story; // Current story - std::shared_ptr m_module; // Current module - std::vector m_recentProjects; Gui m_gui; @@ -69,6 +68,7 @@ private: // Dialogs AboutDialog m_aboutDialog; ProjectPropertiesDialog m_projectPropertiesDialog; + ModulePropertiesDialog m_modulePropertiesDialog; ImGuiToastNotifier m_toastNotifier; LanguageSelector m_languageSelector;