fix project saving, resource management and media node properties change

This commit is contained in:
Anthony Rabine 2023-12-19 01:12:05 +01:00
parent 308586d4b8
commit 83d8542002
9 changed files with 253 additions and 200 deletions

View file

@ -191,6 +191,7 @@ public:
if (i.ID == pinId) if (i.ID == pinId)
{ {
found = true; found = true;
break;
} }
atIndex++; atIndex++;
} }
@ -206,6 +207,7 @@ public:
if (i.ID == pinId) if (i.ID == pinId)
{ {
found = true; found = true;
break;
} }
atIndex++; atIndex++;
} }

View file

@ -483,9 +483,28 @@ void LoadingIndicatorCircle(const char* label, const float indicator_radius,
} }
} // namespace ImGui } // namespace ImGui
void Gui::Image::Clear()
{
if (texture != nullptr)
{
SDL_DestroyTexture(static_cast<SDL_Texture*>(texture));
}
}
void Gui::Image::Load(const std::string &filename)
{
Clear();
Gui::LoadRawImage(filename, *this);
}
Gui::Image::Image() Gui::Image::Image()
{ {
texture = nullptr; texture = nullptr;
w = 0; w = 0;
h = 0; h = 0;
} }
Gui::Image::~Image()
{
Clear();
}

View file

@ -14,17 +14,22 @@ public:
struct Texture; struct Texture;
struct Image { struct Image {
void *texture; // Platform specific void *texture{nullptr}; // Platform specific
int w; int w;
int h; int h;
std::string name; std::string name;
bool valid() const { bool Valid() const {
return (w && h); return (w && h);
} }
void Clear();
void Load(const std::string &filename);
Image(); Image();
~Image();
}; };

View file

@ -43,7 +43,7 @@ void MediaNode::Draw()
ImGui::PopStyleVar(); ImGui::PopStyleVar();
if (m_image.valid()) if (m_image.Valid())
{ {
ImGui::Image(m_image.texture, ImVec2(320, 240)); ImGui::Image(m_image.texture, ImVec2(320, 240));
} }
@ -103,12 +103,8 @@ void MediaNode::Draw()
*/ */
void MediaNode::FromJson(const nlohmann::json &j) void MediaNode::FromJson(const nlohmann::json &j)
{ {
m_image.name = j["image"].get<std::string>(); SetImage(j["image"].get<std::string>());
SetSound(j["sound"].get<std::string>());
Gui::LoadRawImage(m_project.BuildFullAssetsPath(m_image.name), m_image);
m_soundName = j["sound"].get<std::string>();
m_soundPath = m_project.BuildFullAssetsPath(m_soundName);
} }
void MediaNode::ToJson(nlohmann::json &j) void MediaNode::ToJson(nlohmann::json &j)
@ -127,10 +123,10 @@ void MediaNode::DrawProperties()
ImGui::SameLine(); ImGui::SameLine();
std::string type = "sound"; static bool isImage = true;
if (ImGui::Button("Select...##image")) { if (ImGui::Button("Select...##image")) {
type = "image";
ImGui::OpenPopup("popup_button"); ImGui::OpenPopup("popup_button");
isImage = true;
} }
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
@ -146,27 +142,187 @@ void MediaNode::DrawProperties()
m_project.PlaySoundFile(m_soundPath); m_project.PlaySoundFile(m_soundPath);
} }
ImGui::SameLine();
if (ImGui::Button("Select...##sound")) { if (ImGui::Button("Select...##sound")) {
ImGui::OpenPopup("popup_button"); ImGui::OpenPopup("popup_button");
isImage = false;
} }
// This is the actual popup Gui drawing section. // This is the actual popup Gui drawing section.
if (ImGui::BeginPopup("popup_button")) { 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] = isImage ? m_project.Images() : m_project.Sounds();
auto [filtreDebut, filtreFin] = m_project.Sounds();
int n = 0; int n = 0;
for (auto it = filtreDebut; it != filtreFin; ++it, n++) for (auto it = filtreDebut; it != filtreFin; ++it, n++)
{ {
if (ImGui::Selectable((*it)->file.c_str()), n == item_current_idx) if (ImGui::Selectable((*it)->file.c_str()))
item_current_idx = n; {
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. 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>();
std::string sound = m_mediaData["sound"].get<std::string>();
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<ConnectionId> 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>());
std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get<std::string>());
// 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<ConnectionId> 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();
}
*/

View file

@ -32,4 +32,6 @@ private:
std::string m_id; std::string m_id;
std::string m_buttonUniqueName; std::string m_buttonUniqueName;
void SetImage(const std::string &f);
void SetSound(const std::string &f);
}; };

View file

@ -170,6 +170,7 @@ void NodeEditorWindow::Save(nlohmann::json &model)
node["position"] = position; node["position"] = position;
node["internal-data"] = internalData; node["internal-data"] = internalData;
nodes.push_back(node);
} }
model["nodes"] = nodes; 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>();
std::string sound = m_mediaData["sound"].get<std::string>();
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<ConnectionId> 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>());
std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get<std::string>());
// 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<ConnectionId> 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<BaseNode> NodeEditorWindow::GetSelectedNode() std::shared_ptr<BaseNode> NodeEditorWindow::GetSelectedNode()
{ {
std::shared_ptr<BaseNode> selected; std::shared_ptr<BaseNode> selected;

View file

@ -58,10 +58,7 @@ private:
// Fonction pour trouver le prochain élément qui correspond au filtre // Fonction pour trouver le prochain élément qui correspond au filtre
void searchNext() { void searchNext() {
if (filterType == "") { if (filterType != "")
++current;
}
else
{ {
while (current != end && (*current)->type != filterType) { while (current != end && (*current)->type != filterType) {
++current; ++current;

View file

@ -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: private:
std::vector<std::shared_ptr<Resource>> m_items; std::vector<std::shared_ptr<Resource>> m_items;
std::pair<FilterIterator, FilterIterator> m_images; std::pair<FilterIterator, FilterIterator> m_images;

View file

@ -85,25 +85,31 @@ void ResourcesWindow::Draw()
ChooseFile(); ChooseFile();
ImGuiTableFlags tableFlags = ImGuiTableFlags_Borders | static char description[260];
ImGuiTableFlags_RowBg |
ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY |
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti;
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("File", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Format", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Format", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
auto [b, e] = m_project.Resources(); auto [b, e] = m_project.Resources();
int id = 1000;
for (auto it = b; it != e; ++it) for (auto it = b; it != e; ++it)
{ {
ImGui::PushID(id);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s", (*it)->file.c_str()); ImGui::Text("%s", (*it)->file.c_str());
@ -111,10 +117,45 @@ void ResourcesWindow::Draw()
ImGui::Text("%s", (*it)->format.c_str()); ImGui::Text("%s", (*it)->format.c_str());
ImGui::TableNextColumn(); 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::Text("%s", (*it)->description.c_str());
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s", (*it)->type.c_str()); ImGui::Text("%s", (*it)->type.c_str());
ImGui::TableNextColumn();
if (ImGui::SmallButton("Delete"))
{
}
ImGui::PopID();
id++;
} }
ImGui::EndTable(); ImGui::EndTable();