#include #include #include "main_window.h" #include #include "platform_folders.h" #include "media_converter.h" #include "pack_archive.h" #include "uuid.h" #include "sys_lib.h" #ifdef USE_WINDOWS_OS #include #include #include #include #pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "ws2_32.lib") #endif #include "IconsMaterialDesignIcons.h" #include "ImGuiFileDialog.h" #include "imgui_memory_editor.h" #include "app_controller.h" #include "all_events.h" #include "story_project.h" #include "nodes_factory.h" #include "media_node_widget.h" #include "call_function_node_widget.h" #include "module_node_widget.h" #include "variable_node_widget.h" #include "operator_node_widget.h" #include "print_node_widget.h" #include "function_entry_node_widget.h" #include "branch_node_widget.h" #include "play_media_node_widget.h" #include "send_signal_node_widget.h" #include "wait_delay_node_widget.h" #include "wait_event_node_widget.h" #include "for_loop_node_widget.h" #include "while_loop_node_widget.h" #include "break_node_widget.h" #include "continue_node_widget.h" #include "function_exit_node_widget.h" MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController) : m_logger(logger) , m_eventBus(eventBus) , m_appController(appController) , m_emulatorDock(appController) , m_debuggerWindow(appController) , m_cpuWindow(appController) , 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(), eventBus) , m_projectPropertiesDialog(appController, appController.GetResourceManager()) , m_modulePropertiesDialog(appController) { CloseProject(); CloseModule(); // define style for all directories ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir, "", ImVec4(0.5f, 1.0f, 0.9f, 0.9f), ICON_MDI_FOLDER); // define style for all files ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile, "", ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ICON_MDI_FILE); // registerNode("media-node"); m_widgetFactory.registerNode(OperatorNodeUuid); m_widgetFactory.registerNode(CallFunctionNodeUuid); // m_widgetFactory.registerNode("module-node"); m_widgetFactory.registerNode(VariableNodeUuid); m_widgetFactory.registerNode(PrintNodeUuid); m_widgetFactory.registerNode(FunctionEntryNodeUuid); m_widgetFactory.registerNode(BranchNodeUuid); m_widgetFactory.registerNode(WaitEventNodeUuid); m_widgetFactory.registerNode(WaitDelayNodeUuid); m_widgetFactory.registerNode(PlayMediaNodeUuid); m_widgetFactory.registerNode(SendSignalNodeUuid); m_widgetFactory.registerNode(ForLoopNodeUuid); m_widgetFactory.registerNode(WhileLoopNodeUuid); m_widgetFactory.registerNode(BreakNodeUuid); m_widgetFactory.registerNode(ContinueNodeUuid); m_widgetFactory.registerNode(FunctionExitNodeUuid); m_eventBus.Subscribe([this](const OpenProjectEvent &event) { OpenProject(event.GetUuid()); }); m_eventBus.Subscribe([this](const OpenFunctionEvent &event) { OpenFunction(event.GetUuid(), event.GetName()); }); m_eventBus.Subscribe([this](const VmStateEvent &event) { if (event.type == VmStateEvent::Type::PrintEvent) { m_logger.Log("Print from VM: " + event.printOutput); m_emulatorDock.AddVmOutput(event.printOutput); } }); m_eventBus.Subscribe([this](const GenericResultEvent &event) { if (event.IsSuccess()) { m_logger.Log("Operation successful: " + event.GetMessage()); m_toastNotifier.addToast("Success", event.GetMessage(), ToastType::Success); } else { m_logger.Log("Operation failed: " + event.GetMessage(), true); m_toastNotifier.addToast("Error", event.GetMessage(), ToastType::Error); } }); m_eventBus.Subscribe([this](const ModuleEvent &event) { if (event.GetType() == ModuleEvent::Type::Open) { OpenModule(event.GetUuid()); } else if (event.GetType() == ModuleEvent::Type::Closed) { CloseModule(); } else if (event.GetType() == ModuleEvent::Type::BuildSuccess) { m_toastNotifier.addToast("Module", "Module built successfully! Binary ready for testing.", ToastType::Success); m_debuggerWindow.SetScript(m_appController.GetModuleAssembly()); m_emulatorDock.ClearVmOutput(); // Show success message if no errors if (!m_errorListDock.HasErrors()) { // You can also open a popup if desired m_logger.Log("Module compilation successful - Binary saved and loaded into VM"); } } else if (event.GetType() == ModuleEvent::Type::BuildFailure) { m_logger.Log("Module compilation failed: " + event.GetMessage(), true); m_toastNotifier.addToast("Module", "Module build failed - Check Error List", ToastType::Error); // Make sure error list is visible m_errorListDock.Enable(); m_errorListDock.Show(); static CompilationError err; err.line = event.GetLine(); err.message = event.GetMessage(); // Add error to dock m_errorListDock.AddError(err); } }); m_languageSelector.SetOnLanguageChanged([this](const std::string& langCode) { // Sauvegarder la préférence m_appController.SaveParams(); m_logger.Log("Language changed to: " + langCode); }); } MainWindow::~MainWindow() { m_gui.Destroy(); } float MainWindow::DrawMainMenuBar() { bool showNewProject = false; bool showOpenProject = false; float height = 60; if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu(TR("menu.file"))) { if (ImGui::MenuItem("New story project")) { CloseProject(); auto newProject = m_appController.NewProject(); if (newProject) { m_appController.SaveProject(); OpenProject(newProject->GetUuid()); } } /* if (ImGui::BeginMenu("Open Recent")) { for (auto &e : m_recentProjects) { if (e.size() > 0) { if (ImGui::MenuItem(e.c_str())) { OpenProject(e); } } } ImGui::EndMenu(); } */ bool init = m_nodeEditorWindow.GetCurrentStory() ? true : false; // local copy because CloseProject() changes the status between BeginDisabled/EndDisabled if (!init) ImGui::BeginDisabled(); if (ImGui::MenuItem("Save project")) { m_appController.SaveProject(); } if (ImGui::MenuItem("Close project")) { CloseProject(); } if (ImGui::MenuItem("Project settings")) { m_projectPropertiesDialog.Show(); } if (!init) ImGui::EndDisabled(); ImGui::Separator(); if (ImGui::MenuItem("New module")) { // Current module project CloseModule(); showNewProject = true; NewModule(); } if (ImGui::MenuItem("Save module (ctrl+s)")) { SaveModule(); } if (ImGui::MenuItem("Close module")) { CloseModule(); } if (ImGui::MenuItem("Module settings")) { m_modulePropertiesDialog.Show(); } ImGui::EndMenu(); } // if (ImGui::BeginMenu(TR("menu.view"))) { // ImGui::MenuItem(TR("window.console"), nullptr, &m_showConsole); // ImGui::MenuItem(TR("window.resources"), nullptr, &m_showResources); // ImGui::MenuItem(TR("window.properties"), nullptr, &m_showProperties); // ImGui::EndMenu(); // } m_languageSelector.DrawMenu(); if (ImGui::BeginMenu("Help")) { if (ImGui::MenuItem("About")) { m_aboutDialog.Show(); } ImGui::EndMenu(); } height = ImGui::GetFrameHeight(); ImGui::EndMainMenuBar(); } m_aboutDialog.Open(); if (m_nodeEditorWindow.GetCurrentStory()) { m_projectPropertiesDialog.Open(); } if (m_moduleEditorWindow.GetCurrentStory()) { m_modulePropertiesDialog.Open(); } return height; } bool MainWindow::Initialize() { bool success = false; m_logger.RegisterSubject(shared_from_this()); // GUI Init if (m_gui.Initialize()) { // gui.ApplyTheme(); m_debuggerWindow.Initialize(); m_emulatorDock.Initialize(); m_nodeEditorWindow.Initialize(); m_moduleEditorWindow.Initialize(); m_propertiesWindow.Initialize(); m_libraryWindow.Initialize(); success = true; } return success; } bool MainWindow::ShowQuitConfirm() { bool quitRequest = false; // Always center this window when appearing ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); // ImGui::SetNextWindowSize(ImVec2(200, 150)); if (ImGui::BeginPopupModal("QuitConfirm", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("Really quit without saving?"); ImGui::Separator(); if (ImGui::Button("OK", ImVec2(120, 0))) { quitRequest = true; ImGui::CloseCurrentPopup(); } ImGui::SetItemDefaultFocus(); ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } return quitRequest; } void MainWindow::OpenProject(const std::string &uuid) { 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()) { m_recentProjects.push_back(proj); // Limit to 10 recent projects if (m_recentProjects.size() > 10) { m_recentProjects.pop_back(); } // Save recent projects on disk m_appController.SaveParams(); } m_nodeEditorWindow.Enable(); m_emulatorDock.Enable(); m_consoleWindow.Enable(); m_debuggerWindow.Enable(); m_resourcesDock.Enable(); m_propertiesWindow.Enable(); m_variablesWindow.Enable(); m_cpuWindow.Enable(); RefreshProjectInformation(); } void MainWindow::NewModule() { auto module = m_appController.NewModule(); if (module) { m_moduleEditorWindow.Load(module); m_moduleEditorWindow.Enable(); m_logger.Log("New module created."); } else { m_logger.Log("Failed to create new module."); } } void MainWindow::SaveModule() { m_moduleEditorWindow.SaveNodesToProject(); m_appController.SaveModule(); m_logger.Log("Modules saved"); m_toastNotifier.addToast("Module", "Module saved", ToastType::Success); } void MainWindow::OpenModule(const std::string &uuid) { auto module = m_appController.OpenModule(uuid); if (module) { m_moduleEditorWindow.Load(module); m_moduleEditorWindow.Enable(); m_emulatorDock.Enable(); m_consoleWindow.Enable(); m_debuggerWindow.Enable(); m_resourcesDock.Enable(); m_propertiesWindow.Enable(); m_variablesWindow.Enable(); m_cpuWindow.Enable(); } } 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_appController.CloseProject(); m_nodeEditorWindow.Clear(); m_nodeEditorWindow.Disable(); m_emulatorDock.ClearImage(); m_consoleWindow.ClearLog(); m_debuggerWindow.ClearErrors(); m_debuggerWindow.SetScript(""); m_emulatorDock.Disable(); m_debuggerWindow.Disable(); m_resourcesDock.Disable(); m_propertiesWindow.Disable(); m_variablesWindow.Disable(); m_cpuWindow.Disable(); RefreshProjectInformation(); } void MainWindow::CloseModule() { m_moduleEditorWindow.Clear(); m_moduleEditorWindow.Disable(); } void MainWindow::RefreshProjectInformation() { std::string fullText = "Story Editor " + LibraryManager::GetVersion(); auto p = m_nodeEditorWindow.GetCurrentStory(); if (p) { auto proj = dynamic_pointer_cast(p); if (proj) { fullText += " - " + proj->GetProjectFilePath(); } } m_gui.SetWindowTitle(fullText); } void MainWindow::DrawToolBar(float topPadding) { ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking; // Définit la taille et la position de la barre d'outils ImVec2 size = ImVec2(60, ImGui::GetIO().DisplaySize.y - topPadding); ImGui::SetNextWindowSize(size); ImGui::SetNextWindowPos(ImVec2(0, topPadding)); // Style pour les coins arrondis des boutons ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 4)); // Création de la fenêtre pour la barre d'outils if (ImGui::Begin("ToolBar", nullptr, window_flags)) { // Taille réduite des boutons float buttonSize = 36.0f; float windowWidth = ImGui::GetContentRegionAvail().x; float offsetX = (windowWidth - buttonSize) * 0.5f; // Style pour les couleurs des boutons ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.2f, 0.2f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.3f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); // Taille de fonte réduite pour les icônes float old_size = ImGui::GetFont()->Scale; ImGui::GetFont()->Scale *= 1.2f; ImGui::PushFont(ImGui::GetFont()); // Bouton Stop Sound ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.9f, 0.3f, 0.3f, 1.0f)); if (ImGui::Button(ICON_MDI_SPEAKER_STOP "##stop_sound", ImVec2(buttonSize, buttonSize))) { m_appController.StopAudio(); } ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Stop Sound"); } // Espacement vertical ImGui::Dummy(ImVec2(0, 8)); // Bouton Build ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.3f, 0.8f, 0.9f, 1.0f)); if (ImGui::Button(ICON_MDI_HAMMER "##build_project", ImVec2(buttonSize, buttonSize))) { // Compile story if window focused, otherwise module if (m_nodeEditorWindow.IsFocused()) { m_appController.CompileNodes(IStoryProject::PROJECT_TYPE_STORY); } else { m_appController.CompileNodes(IStoryProject::PROJECT_TYPE_MODULE); } } ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Build Project"); } // Restaurer le scale de la fonte ImGui::GetFont()->Scale = old_size; ImGui::PopFont(); // Pop les couleurs des boutons ImGui::PopStyleColor(3); // Button, ButtonHovered, ButtonActive // Fermeture de la fenêtre ImGui ImGui::End(); } // Pop les styles de frame ImGui::PopStyleVar(2); // FrameRounding, FramePadding } #include "imgui_internal.h" bool MainWindow::Loop() { // Main loop static bool done = false; Uint64 frameStart = SDL_GetTicks(); bool aboutToClose = m_gui.PollEvent(); m_gui.StartFrame(); auto vp = ImGui::GetMainViewport(); auto pos = vp->WorkPos; auto size = vp->WorkSize; pos.x += 60; size.x -= 60; vp->WorkPos = pos; vp->WorkSize = size; ImGui::DockSpaceOverViewport(0, vp); float height = DrawMainMenuBar(); // ------------ Draw all windows m_libraryWindow.Draw(); m_errorListDock.Draw(); if (m_appController.IsLibraryManagerInitialized()) { bool nodeEditorFocused = m_nodeEditorWindow.IsFocused(); bool moduleEditorFocused = m_moduleEditorWindow.IsFocused(); m_consoleWindow.Draw(); m_emulatorDock.Draw(); m_debuggerWindow.Draw(); m_resourcesDock.Draw(); m_nodeEditorWindow.Draw(); m_moduleEditorWindow.Draw(); auto currentStory = nodeEditorFocused ? m_nodeEditorWindow.GetCurrentStory() : m_moduleEditorWindow.GetCurrentStory(); m_variablesWindow.Draw(currentStory); m_cpuWindow.Draw(); static MemoryEditor mem_edit_1; static uint32_t ram_size = 0; static uint8_t *ram = m_appController.GetRam(ram_size); mem_edit_1.DrawWindow("RAM view", ram, ram_size); auto selectedNode = nodeEditorFocused ? m_nodeEditorWindow.GetSelectedNode() : m_moduleEditorWindow.GetSelectedNode(); m_propertiesWindow.SetSelectedNode(selectedNode); m_propertiesWindow.Draw(currentStory); // static ImGuiAxis toolbar2_axis = ImGuiAxis_Y; // DockingToolbar("Toolbar2", &toolbar2_axis); DrawToolBar(height); ImGuiIO& io = ImGui::GetIO(); if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_S, false)) { 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_nodeEditorWindow.GetCurrentStory()) { // Sinon, sauvegarder l'histoire principale m_appController.SaveProject(); m_toastNotifier.success("Projet sauvegardé"); m_logger.Log("Projet sauvegardé via Ctrl+S"); } else { // Aucun projet ouvert m_toastNotifier.warning("Aucun projet ou module à sauvegarder"); m_logger.Log("Tentative de sauvegarde sans projet ouvert", true); } } if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_B, false)) { bool moduleEditorFocused = m_moduleEditorWindow.IsFocused(); if (moduleEditorFocused && m_moduleEditorWindow.GetCurrentStory()) { m_logger.Log("Building module..."); m_appController.CompileNodes(IStoryProject::PROJECT_TYPE_MODULE); } else if (m_nodeEditorWindow.GetCurrentStory()) { m_logger.Log("Building story..."); m_appController.CompileNodes(IStoryProject::PROJECT_TYPE_STORY); } } } m_aboutDialog.Draw(); m_projectPropertiesDialog.Draw(); m_modulePropertiesDialog.Draw(); m_toastNotifier.render(); if (aboutToClose) { ImGui::OpenPopup("QuitConfirm"); } if (ShowQuitConfirm()) { done = true; } m_gui.EndFrame(); // Rendering and event handling Uint64 frameTime = SDL_GetTicks() - frameStart; // Temps écoulé pour la frame if (frameTime < 32) { // 16 ms = 60 FPS SDL_Delay(32 - frameTime); // Attendez pour compléter la frame } return done; } void MainWindow::LogEvent(const std::string &txt, bool critical) { m_consoleWindow.AddLog(txt, critical ? 1 : 0); } void MainWindow::OpenFunction(const std::string &uuid, const std::string &name) { m_nodeEditorWindow.OpenFunction(uuid, name); }