From 83d85420024e51f439aff9b039cb3e7f0114d58a Mon Sep 17 00:00:00 2001 From: Anthony Rabine Date: Tue, 19 Dec 2023 01:12:05 +0100 Subject: [PATCH] fix project saving, resource management and media node properties change --- story-editor/src/base_node.h | 2 + story-editor/src/gui.cpp | 19 +++ story-editor/src/gui.h | 9 +- story-editor/src/media_node.cpp | 190 +++++++++++++++++++++--- story-editor/src/media_node.h | 2 + story-editor/src/node_editor_window.cpp | 139 +---------------- story-editor/src/resource.h | 5 +- story-editor/src/resource_manager.h | 32 ---- story-editor/src/resources_window.cpp | 55 ++++++- 9 files changed, 253 insertions(+), 200 deletions(-) diff --git a/story-editor/src/base_node.h b/story-editor/src/base_node.h index bc25b4d..b37a53f 100644 --- a/story-editor/src/base_node.h +++ b/story-editor/src/base_node.h @@ -191,6 +191,7 @@ public: if (i.ID == pinId) { found = true; + break; } atIndex++; } @@ -206,6 +207,7 @@ public: if (i.ID == pinId) { found = true; + break; } atIndex++; } diff --git a/story-editor/src/gui.cpp b/story-editor/src/gui.cpp index 7181ff0..46a5c53 100644 --- a/story-editor/src/gui.cpp +++ b/story-editor/src/gui.cpp @@ -483,9 +483,28 @@ void LoadingIndicatorCircle(const char* label, const float indicator_radius, } } // namespace ImGui +void Gui::Image::Clear() +{ + if (texture != nullptr) + { + SDL_DestroyTexture(static_cast(texture)); + } +} + +void Gui::Image::Load(const std::string &filename) +{ + Clear(); + Gui::LoadRawImage(filename, *this); +} + Gui::Image::Image() { texture = nullptr; w = 0; h = 0; } + +Gui::Image::~Image() +{ + Clear(); +} diff --git a/story-editor/src/gui.h b/story-editor/src/gui.h index 966ad0f..01fbb20 100644 --- a/story-editor/src/gui.h +++ b/story-editor/src/gui.h @@ -14,17 +14,22 @@ public: struct Texture; struct Image { - void *texture; // Platform specific + void *texture{nullptr}; // Platform specific int w; int h; std::string name; - bool valid() const { + bool Valid() const { return (w && h); } + void Clear(); + + void Load(const std::string &filename); + Image(); + ~Image(); }; diff --git a/story-editor/src/media_node.cpp b/story-editor/src/media_node.cpp index d1eab5b..c590683 100644 --- a/story-editor/src/media_node.cpp +++ b/story-editor/src/media_node.cpp @@ -43,7 +43,7 @@ void MediaNode::Draw() ImGui::PopStyleVar(); - if (m_image.valid()) + if (m_image.Valid()) { ImGui::Image(m_image.texture, ImVec2(320, 240)); } @@ -103,12 +103,8 @@ void MediaNode::Draw() */ void MediaNode::FromJson(const nlohmann::json &j) { - m_image.name = j["image"].get(); - - Gui::LoadRawImage(m_project.BuildFullAssetsPath(m_image.name), m_image); - - m_soundName = j["sound"].get(); - m_soundPath = m_project.BuildFullAssetsPath(m_soundName); + SetImage(j["image"].get()); + SetSound(j["sound"].get()); } void MediaNode::ToJson(nlohmann::json &j) @@ -127,10 +123,10 @@ void MediaNode::DrawProperties() ImGui::SameLine(); - std::string type = "sound"; + static bool isImage = true; if (ImGui::Button("Select...##image")) { - type = "image"; ImGui::OpenPopup("popup_button"); + isImage = true; } ImGui::AlignTextToFramePadding(); @@ -146,27 +142,187 @@ void MediaNode::DrawProperties() m_project.PlaySoundFile(m_soundPath); } + ImGui::SameLine(); + if (ImGui::Button("Select...##sound")) { ImGui::OpenPopup("popup_button"); + isImage = false; } - // This is the actual popup Gui drawing section. if (ImGui::BeginPopup("popup_button")) { - ImGui::SeparatorText("Sounds"); + ImGui::SeparatorText(isImage ? "Images" : "Sounds"); - static int item_current_idx = 0; // Here we store our selection data as an index. - - - auto [filtreDebut, filtreFin] = m_project.Sounds(); + auto [filtreDebut, filtreFin] = isImage ? m_project.Images() : m_project.Sounds(); int n = 0; for (auto it = filtreDebut; it != filtreFin; ++it, n++) { - if (ImGui::Selectable((*it)->file.c_str()), n == item_current_idx) - item_current_idx = n; + if (ImGui::Selectable((*it)->file.c_str())) + { + if (isImage) + { + SetImage((*it)->file); + } + else + { + SetSound((*it)->file); + } + } } ImGui::EndPopup(); // Note this does not do anything to the popup open/close state. It just terminates the content declaration. } } + +void MediaNode::SetImage(const std::string &f) +{ + m_image.name = f; + m_image.Load(m_project.BuildFullAssetsPath(f)); +} + +void MediaNode::SetSound(const std::string &f) +{ + m_soundName = f; + m_soundPath = m_project.BuildFullAssetsPath(m_soundName); +} + + + +/* +std::string NodeEditorWindow::ChoiceLabel() const +{ + std::stringstream ss; + ss << "mediaChoice" << std::setw(4) << std::setfill('0') << GetId(); + return ss.str(); +} + +std::string NodeEditorWindow::EntryLabel() const +{ + std::stringstream ss; + ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << getNodeId(); + return ss.str(); +} + + +std::string NodeEditorWindow::GenerateConstants() +{ + std::string s; + + std::string image = m_mediaData["image"].get(); + std::string sound = m_mediaData["sound"].get(); + if (image.size() > 0) + { + s = StoryProject::FileToConstant(image, ".qoi"); // FIXME: Generate the extension setup in user option of output format + } + if (sound.size() > 0) + { + s += StoryProject::FileToConstant(sound, ".wav"); // FIXME: Generate the extension setup in user option of output format + } + + int nb_out_conns = ComputeOutputConnections(); + if (nb_out_conns > 1) + { + // Generate choice table if needed (out ports > 1) + std::stringstream ss; + std::string label = ChoiceLabel(); + ss << "$" << label + << " DC32, " + << nb_out_conns << ", "; + + std::unordered_set conns = m_model.allConnectionIds(getNodeId()); + int i = 0; + for (auto & c : conns) + { + std::stringstream ssChoice; + + // On va chercher le label d'entrée du noeud connecté à l'autre bout + ss << m_model.GetNodeEntryLabel(c.inNodeId); + if (i < (nb_out_conns - 1)) + { + ss << ", "; + } + else + { + ss << "\n"; + } + i++; + } + + s += ss.str(); + } + + return s; +} + +std::string NodeEditorWindow::Build() +{ + std::stringstream ss; + int nb_out_conns = ComputeOutputConnections(); + + ss << R"(; ---------------------------- )" + << GetNodeTitle() + << " Type: " + << (nb_out_conns == 0 ? "End" : nb_out_conns == 1 ? "Transition" : "Choice") + << "\n"; + std::string image = StoryProject::RemoveFileExtension(m_mediaData["image"].get()); + std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get()); + + // Le label de ce noeud est généré de la façon suivante : + // "media" + Node ID + id du noeud parent. Si pas de noeud parent, alors rien + ss << EntryLabel() << ":\n"; + + if (image.size() > 0) + { + ss << "lcons r0, $" << image << "\n"; + } + else + { + ss << "lcons r0, 0\n"; + } + + if (sound.size() > 0) + { + ss << "lcons r1, $" << sound << "\n"; + } + else + { + ss << "lcons r1, 0\n"; + } + // Call the media executor (image, sound) + ss << "syscall 1\n"; + + // Check output connections number + // == 0: end node : generate halt + // == 1: transition node : image + sound on demand, jump directly to the other node when OK + // > 1 : choice node : call the node choice manager + + if (nb_out_conns == 0) // End node + { + ss << "halt\n"; + } + else if (nb_out_conns == 1) // Transition node + { + std::unordered_set conns = m_model.allConnectionIds(getNodeId()); + + + for (auto c : conns) + { + if (c.outNodeId == getNodeId()) + { + // On place dans R0 le prochain noeud à exécuter en cas de OK + ss << "lcons r0, " + << m_model.GetNodeEntryLabel(c.inNodeId) << "\n" + << "ret\n"; + } + } + + } + else // Choice node + { + ss << "lcons r0, $" << ChoiceLabel() << "\n" + << "jump .media ; no return possible, so a jump is enough"; + } + return ss.str(); +} +*/ diff --git a/story-editor/src/media_node.h b/story-editor/src/media_node.h index 6c2372f..6dea129 100644 --- a/story-editor/src/media_node.h +++ b/story-editor/src/media_node.h @@ -32,4 +32,6 @@ private: std::string m_id; std::string m_buttonUniqueName; + void SetImage(const std::string &f); + void SetSound(const std::string &f); }; diff --git a/story-editor/src/node_editor_window.cpp b/story-editor/src/node_editor_window.cpp index 5b295b1..b49e95d 100644 --- a/story-editor/src/node_editor_window.cpp +++ b/story-editor/src/node_editor_window.cpp @@ -170,6 +170,7 @@ void NodeEditorWindow::Save(nlohmann::json &model) node["position"] = position; node["internal-data"] = internalData; + nodes.push_back(node); } model["nodes"] = nodes; @@ -206,144 +207,6 @@ void NodeEditorWindow::Save(nlohmann::json &model) } -/* -std::string NodeEditorWindow::ChoiceLabel() const -{ - std::stringstream ss; - ss << "mediaChoice" << std::setw(4) << std::setfill('0') << GetId(); - return ss.str(); -} - -std::string NodeEditorWindow::EntryLabel() const -{ - std::stringstream ss; - ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << getNodeId(); - return ss.str(); -} - - -std::string NodeEditorWindow::GenerateConstants() -{ - std::string s; - - std::string image = m_mediaData["image"].get(); - std::string sound = m_mediaData["sound"].get(); - if (image.size() > 0) - { - s = StoryProject::FileToConstant(image, ".qoi"); // FIXME: Generate the extension setup in user option of output format - } - if (sound.size() > 0) - { - s += StoryProject::FileToConstant(sound, ".wav"); // FIXME: Generate the extension setup in user option of output format - } - - int nb_out_conns = ComputeOutputConnections(); - if (nb_out_conns > 1) - { - // Generate choice table if needed (out ports > 1) - std::stringstream ss; - std::string label = ChoiceLabel(); - ss << "$" << label - << " DC32, " - << nb_out_conns << ", "; - - std::unordered_set conns = m_model.allConnectionIds(getNodeId()); - int i = 0; - for (auto & c : conns) - { - std::stringstream ssChoice; - - // On va chercher le label d'entrée du noeud connecté à l'autre bout - ss << m_model.GetNodeEntryLabel(c.inNodeId); - if (i < (nb_out_conns - 1)) - { - ss << ", "; - } - else - { - ss << "\n"; - } - i++; - } - - s += ss.str(); - } - - return s; -} - -std::string NodeEditorWindow::Build() -{ - std::stringstream ss; - int nb_out_conns = ComputeOutputConnections(); - - ss << R"(; ---------------------------- )" - << GetNodeTitle() - << " Type: " - << (nb_out_conns == 0 ? "End" : nb_out_conns == 1 ? "Transition" : "Choice") - << "\n"; - std::string image = StoryProject::RemoveFileExtension(m_mediaData["image"].get()); - std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get()); - - // Le label de ce noeud est généré de la façon suivante : - // "media" + Node ID + id du noeud parent. Si pas de noeud parent, alors rien - ss << EntryLabel() << ":\n"; - - if (image.size() > 0) - { - ss << "lcons r0, $" << image << "\n"; - } - else - { - ss << "lcons r0, 0\n"; - } - - if (sound.size() > 0) - { - ss << "lcons r1, $" << sound << "\n"; - } - else - { - ss << "lcons r1, 0\n"; - } - // Call the media executor (image, sound) - ss << "syscall 1\n"; - - // Check output connections number - // == 0: end node : generate halt - // == 1: transition node : image + sound on demand, jump directly to the other node when OK - // > 1 : choice node : call the node choice manager - - if (nb_out_conns == 0) // End node - { - ss << "halt\n"; - } - else if (nb_out_conns == 1) // Transition node - { - std::unordered_set conns = m_model.allConnectionIds(getNodeId()); - - - for (auto c : conns) - { - if (c.outNodeId == getNodeId()) - { - // On place dans R0 le prochain noeud à exécuter en cas de OK - ss << "lcons r0, " - << m_model.GetNodeEntryLabel(c.inNodeId) << "\n" - << "ret\n"; - } - } - - } - else // Choice node - { - ss << "lcons r0, $" << ChoiceLabel() << "\n" - << "jump .media ; no return possible, so a jump is enough"; - } - return ss.str(); -} -*/ - std::shared_ptr NodeEditorWindow::GetSelectedNode() { std::shared_ptr selected; diff --git a/story-editor/src/resource.h b/story-editor/src/resource.h index 7d132fb..65f495b 100644 --- a/story-editor/src/resource.h +++ b/story-editor/src/resource.h @@ -58,10 +58,7 @@ private: // Fonction pour trouver le prochain élément qui correspond au filtre void searchNext() { - if (filterType == "") { - ++current; - } - else + if (filterType != "") { while (current != end && (*current)->type != filterType) { ++current; diff --git a/story-editor/src/resource_manager.h b/story-editor/src/resource_manager.h index 493ea17..fa1621d 100644 --- a/story-editor/src/resource_manager.h +++ b/story-editor/src/resource_manager.h @@ -60,38 +60,6 @@ public: } - /* - void StoryProject::AppendResource(const Resource &res) -{ - m_resources.push_back(res); -} - -bool StoryProject::GetResourceAt(int index, Resource &resOut) -{ - bool success = false; - if ((index >= 0) && (index < m_resources.size())) - { - resOut = m_resources[index]; - success = true; - } - return success; -} - -void StoryProject::ClearResources() -{ - m_resources.clear(); -} - -void StoryProject::DeleteResourceAt(int index) -{ - if ((index >= 0) && (index < m_resources.size())) - { - m_resources.erase(m_resources.begin() + index); - } -} -*/ - - private: std::vector> m_items; std::pair m_images; diff --git a/story-editor/src/resources_window.cpp b/story-editor/src/resources_window.cpp index e851a19..e5b3b71 100644 --- a/story-editor/src/resources_window.cpp +++ b/story-editor/src/resources_window.cpp @@ -85,25 +85,31 @@ void ResourcesWindow::Draw() ChooseFile(); - ImGuiTableFlags tableFlags = ImGuiTableFlags_Borders | - ImGuiTableFlags_RowBg | - ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | - ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | - ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti; + static char description[260]; - if (ImGui::BeginTable("table1", 4, tableFlags)) + static ImGuiTableFlags tableFlags = + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable + | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti + | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody + | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY + | ImGuiTableFlags_SizingFixedFit; + + if (ImGui::BeginTable("table1", 5, tableFlags)) { ImGui::TableSetupColumn("File", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Format", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed); ImGui::TableHeadersRow(); auto [b, e] = m_project.Resources(); + + int id = 1000; for (auto it = b; it != e; ++it) { + ImGui::PushID(id); ImGui::TableNextColumn(); ImGui::Text("%s", (*it)->file.c_str()); @@ -111,10 +117,45 @@ void ResourcesWindow::Draw() ImGui::Text("%s", (*it)->format.c_str()); ImGui::TableNextColumn(); + static bool init = false; + + if (ImGui::SmallButton("..")) + { + ImGui::OpenPopup("edit-comment-popup"); + init = true; + + } + if (ImGui::BeginPopup("edit-comment-popup")) + { + if (init) + { + strncpy(description, (*it)->description.c_str(), sizeof(description)); + init = false; + } +// ImGui::PushID(id); + ImGui::InputText("Description", description, sizeof(description)); + if (ImGui::Button("Close")) + { + (*it)->description.assign(description); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); +// ImGui::PopID(); + } + ImGui::SameLine(); ImGui::Text("%s", (*it)->description.c_str()); + + ImGui::TableNextColumn(); ImGui::Text("%s", (*it)->type.c_str()); + + ImGui::TableNextColumn(); + if (ImGui::SmallButton("Delete")) + { + } + ImGui::PopID(); + id++; } ImGui::EndTable();