mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
fix project saving, resource management and media node properties change
This commit is contained in:
parent
308586d4b8
commit
83d8542002
9 changed files with 253 additions and 200 deletions
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SDL_Texture*>(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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<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);
|
||||
SetImage(j["image"].get<std::string>());
|
||||
SetSound(j["sound"].get<std::string>());
|
||||
}
|
||||
|
||||
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>();
|
||||
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();
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
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> selected;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<std::shared_ptr<Resource>> m_items;
|
||||
std::pair<FilterIterator, FilterIterator> m_images;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue