mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 08:59:35 +01:00
new function entry/exit nodes with parameters
This commit is contained in:
parent
8aa18fa5af
commit
c594e01912
22 changed files with 1366 additions and 140 deletions
|
|
@ -27,6 +27,8 @@ struct Callback<Ret(Params...)> {
|
|||
template <typename Ret, typename... Params>
|
||||
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;
|
||||
|
||||
class NodesFactory;
|
||||
|
||||
class IStoryManager
|
||||
{
|
||||
public:
|
||||
|
|
@ -50,6 +52,7 @@ public:
|
|||
virtual void LoadBinaryStory(const std::string &filename) = 0;
|
||||
virtual void ToggleBreakpoint(int line) = 0;
|
||||
virtual uint32_t GetRegister(int reg) = 0;
|
||||
virtual NodesFactory& GetNodesFactory() = 0;
|
||||
|
||||
virtual void Play() = 0;
|
||||
virtual void Step() = 0;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,38 @@ public:
|
|||
m_outputPorts.clear();
|
||||
}
|
||||
|
||||
// Clear only data input ports, keep execution ports
|
||||
void ClearDataInputPorts() {
|
||||
auto it = m_inputPorts.begin();
|
||||
while (it != m_inputPorts.end()) {
|
||||
if (it->type == Port::Type::DATA_PORT) {
|
||||
it = m_inputPorts.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear only data output ports, keep execution ports
|
||||
void ClearDataOutputPorts() {
|
||||
auto it = m_outputPorts.begin();
|
||||
while (it != m_outputPorts.end()) {
|
||||
if (it->type == Port::Type::DATA_PORT) {
|
||||
it = m_outputPorts.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearAllInputPorts() {
|
||||
m_inputPorts.clear();
|
||||
}
|
||||
|
||||
void ClearAllOutputPorts() {
|
||||
m_outputPorts.clear();
|
||||
}
|
||||
|
||||
// Port management
|
||||
void AddInputPort(Port::Type type, const std::string& label, bool customRendering = false) {
|
||||
m_inputPorts.push_back({type, label, customRendering});
|
||||
|
|
|
|||
|
|
@ -2,28 +2,92 @@
|
|||
#pragma once
|
||||
|
||||
#include "base_node.h"
|
||||
#include <map>
|
||||
|
||||
class CallFunctionNode : public BaseNode
|
||||
{
|
||||
public:
|
||||
enum InputBindingMode {
|
||||
MODE_CONNECTED, // Value comes from a connected pin
|
||||
MODE_CONSTANT // Value is a constant set in the UI
|
||||
};
|
||||
|
||||
struct InputBinding {
|
||||
std::string paramName;
|
||||
InputBindingMode mode;
|
||||
std::string constantValue;
|
||||
|
||||
nlohmann::json ToJson() const {
|
||||
return {
|
||||
{"paramName", paramName},
|
||||
{"mode", mode == MODE_CONNECTED ? "connected" : "constant"},
|
||||
{"constantValue", constantValue}
|
||||
};
|
||||
}
|
||||
|
||||
static InputBinding FromJson(const nlohmann::json& j) {
|
||||
InputBinding ib;
|
||||
ib.paramName = j.value("paramName", "");
|
||||
std::string modeStr = j.value("mode", "connected");
|
||||
ib.mode = (modeStr == "constant") ? MODE_CONSTANT : MODE_CONNECTED;
|
||||
ib.constantValue = j.value("constantValue", "");
|
||||
return ib;
|
||||
}
|
||||
};
|
||||
|
||||
struct OutputMapping {
|
||||
std::string returnValueName;
|
||||
std::string targetVariable; // Optional: map to a global variable
|
||||
|
||||
nlohmann::json ToJson() const {
|
||||
return {
|
||||
{"returnValueName", returnValueName},
|
||||
{"targetVariable", targetVariable}
|
||||
};
|
||||
}
|
||||
|
||||
static OutputMapping FromJson(const nlohmann::json& j) {
|
||||
OutputMapping om;
|
||||
om.returnValueName = j.value("returnValueName", "");
|
||||
om.targetVariable = j.value("targetVariable", "");
|
||||
return om;
|
||||
}
|
||||
};
|
||||
|
||||
CallFunctionNode(const std::string &type)
|
||||
: BaseNode(type, "Call Function Node")
|
||||
, m_functionName("")
|
||||
, m_functionUuid("")
|
||||
{
|
||||
SetBehavior(BaseNode::BEHAVIOR_EXECUTION);
|
||||
SetupExecutionPorts(true, 1, true); // 1 entrée, 1 sortie
|
||||
SetupExecutionPorts(true, 1, true); // 1 entrée, sorties dynamiques
|
||||
}
|
||||
|
||||
void Initialize() override {
|
||||
// Charger le nom et l'UUID de la fonction depuis les données internes
|
||||
nlohmann::json j = GetInternalData();
|
||||
|
||||
if (j.contains("functionName")) {
|
||||
m_functionName = j["functionName"].get<std::string>();
|
||||
}
|
||||
if (j.contains("functionUuid")) {
|
||||
m_functionUuid = j["functionUuid"].get<std::string>();
|
||||
}
|
||||
|
||||
// Load input bindings
|
||||
m_inputBindings.clear();
|
||||
if (j.contains("inputBindings") && j["inputBindings"].is_array()) {
|
||||
for (const auto& ibJson : j["inputBindings"]) {
|
||||
m_inputBindings.push_back(InputBinding::FromJson(ibJson));
|
||||
}
|
||||
}
|
||||
|
||||
// Load output mappings
|
||||
m_outputMappings.clear();
|
||||
if (j.contains("outputMappings") && j["outputMappings"].is_array()) {
|
||||
for (const auto& omJson : j["outputMappings"]) {
|
||||
m_outputMappings.push_back(OutputMapping::FromJson(omJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetFunctionName() const {
|
||||
|
|
@ -37,15 +101,113 @@ public:
|
|||
void SetFunction(const std::string& uuid, const std::string& name) {
|
||||
m_functionUuid = uuid;
|
||||
m_functionName = name;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
// Input bindings management
|
||||
const std::vector<InputBinding>& GetInputBindings() const {
|
||||
return m_inputBindings;
|
||||
}
|
||||
|
||||
void SetInputBindingMode(const std::string& paramName, InputBindingMode mode, const std::string& constantValue = "") {
|
||||
// Find or create binding
|
||||
auto it = std::find_if(m_inputBindings.begin(), m_inputBindings.end(),
|
||||
[¶mName](const InputBinding& ib) { return ib.paramName == paramName; });
|
||||
|
||||
// Sauvegarder dans les données internes pour la persistance
|
||||
nlohmann::json j;
|
||||
j["functionName"] = m_functionName;
|
||||
j["functionUuid"] = m_functionUuid;
|
||||
SetInternalData(j);
|
||||
if (it != m_inputBindings.end()) {
|
||||
it->mode = mode;
|
||||
it->constantValue = constantValue;
|
||||
} else {
|
||||
InputBinding ib;
|
||||
ib.paramName = paramName;
|
||||
ib.mode = mode;
|
||||
ib.constantValue = constantValue;
|
||||
m_inputBindings.push_back(ib);
|
||||
}
|
||||
|
||||
SaveData();
|
||||
}
|
||||
|
||||
InputBinding* GetInputBinding(const std::string& paramName) {
|
||||
auto it = std::find_if(m_inputBindings.begin(), m_inputBindings.end(),
|
||||
[¶mName](const InputBinding& ib) { return ib.paramName == paramName; });
|
||||
|
||||
return (it != m_inputBindings.end()) ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
// Output mappings management
|
||||
const std::vector<OutputMapping>& GetOutputMappings() const {
|
||||
return m_outputMappings;
|
||||
}
|
||||
|
||||
void SetOutputMapping(const std::string& returnValueName, const std::string& targetVariable) {
|
||||
auto it = std::find_if(m_outputMappings.begin(), m_outputMappings.end(),
|
||||
[&returnValueName](const OutputMapping& om) { return om.returnValueName == returnValueName; });
|
||||
|
||||
if (it != m_outputMappings.end()) {
|
||||
it->targetVariable = targetVariable;
|
||||
} else {
|
||||
OutputMapping om;
|
||||
om.returnValueName = returnValueName;
|
||||
om.targetVariable = targetVariable;
|
||||
m_outputMappings.push_back(om);
|
||||
}
|
||||
|
||||
SaveData();
|
||||
}
|
||||
|
||||
// Rebuild ports based on the module's interface
|
||||
void RebuildPortsFromModule(const std::vector<std::pair<std::string, std::string>>& parameters,
|
||||
const std::vector<std::pair<std::string, std::string>>& exitLabels,
|
||||
const std::map<std::string, std::vector<std::pair<std::string, std::string>>>& returnValuesByExit) {
|
||||
// Clear all ports except the main execution input
|
||||
ClearDataInputPorts();
|
||||
ClearAllOutputPorts();
|
||||
|
||||
// Add data input ports for each parameter
|
||||
for (const auto& param : parameters) {
|
||||
AddInputPort(Port::Type::DATA_PORT, param.first, false);
|
||||
}
|
||||
|
||||
// Add execution output ports for each exit
|
||||
for (const auto& exitLabel : exitLabels) {
|
||||
AddOutputPort(Port::Type::EXECUTION_PORT, exitLabel.first, true);
|
||||
}
|
||||
|
||||
// Add data output ports for all unique return values across all exits
|
||||
std::set<std::string> allReturnValues;
|
||||
for (const auto& exitPair : returnValuesByExit) {
|
||||
for (const auto& rv : exitPair.second) {
|
||||
allReturnValues.insert(rv.first);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& rvName : allReturnValues) {
|
||||
AddOutputPort(Port::Type::DATA_PORT, rvName, false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_functionName;
|
||||
std::string m_functionUuid;
|
||||
std::vector<InputBinding> m_inputBindings;
|
||||
std::vector<OutputMapping> m_outputMappings;
|
||||
|
||||
void SaveData() {
|
||||
nlohmann::json j;
|
||||
j["functionName"] = m_functionName;
|
||||
j["functionUuid"] = m_functionUuid;
|
||||
|
||||
j["inputBindings"] = nlohmann::json::array();
|
||||
for (const auto& ib : m_inputBindings) {
|
||||
j["inputBindings"].push_back(ib.ToJson());
|
||||
}
|
||||
|
||||
j["outputMappings"] = nlohmann::json::array();
|
||||
for (const auto& om : m_outputMappings) {
|
||||
j["outputMappings"].push_back(om.ToJson());
|
||||
}
|
||||
|
||||
SetInternalData(j);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,11 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "base_node.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class FunctionEntryNode : public BaseNode
|
||||
{
|
||||
public:
|
||||
struct Parameter {
|
||||
std::string name;
|
||||
std::string type; // "int", "string", "bool", "float"
|
||||
std::string defaultValue;
|
||||
|
||||
nlohmann::json ToJson() const {
|
||||
return {
|
||||
{"name", name},
|
||||
{"type", type},
|
||||
{"defaultValue", defaultValue}
|
||||
};
|
||||
}
|
||||
|
||||
static Parameter FromJson(const nlohmann::json& j) {
|
||||
Parameter p;
|
||||
p.name = j.value("name", "");
|
||||
p.type = j.value("type", "int");
|
||||
p.defaultValue = j.value("defaultValue", "");
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
FunctionEntryNode(const std::string &type)
|
||||
: BaseNode(type, "Function Entry Node")
|
||||
{
|
||||
|
|
@ -15,12 +37,78 @@ public:
|
|||
}
|
||||
|
||||
void Initialize() override {
|
||||
// Initialisation spécifique pour FunctionEntryNode
|
||||
// Par exemple, préparer les entrées nécessaires pour la fonction
|
||||
// Load parameters from internal data
|
||||
nlohmann::json j = GetInternalData();
|
||||
m_parameters.clear();
|
||||
|
||||
if (j.contains("parameters") && j["parameters"].is_array()) {
|
||||
for (const auto& paramJson : j["parameters"]) {
|
||||
m_parameters.push_back(Parameter::FromJson(paramJson));
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild output ports for parameters
|
||||
RebuildParameterPorts();
|
||||
}
|
||||
|
||||
// Ajoutez des méthodes spécifiques pour gérer l'entrée de la fonction
|
||||
void PrepareFunctionEntry() {
|
||||
// Logique pour préparer l'entrée de la fonction
|
||||
void AddParameter(const std::string& name, const std::string& type, const std::string& defaultValue = "") {
|
||||
Parameter param;
|
||||
param.name = name;
|
||||
param.type = type;
|
||||
param.defaultValue = defaultValue;
|
||||
m_parameters.push_back(param);
|
||||
|
||||
SaveParameters();
|
||||
RebuildParameterPorts();
|
||||
}
|
||||
};
|
||||
|
||||
void RemoveParameter(size_t index) {
|
||||
if (index < m_parameters.size()) {
|
||||
m_parameters.erase(m_parameters.begin() + index);
|
||||
SaveParameters();
|
||||
RebuildParameterPorts();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateParameter(size_t index, const std::string& name, const std::string& type, const std::string& defaultValue) {
|
||||
if (index < m_parameters.size()) {
|
||||
m_parameters[index].name = name;
|
||||
m_parameters[index].type = type;
|
||||
m_parameters[index].defaultValue = defaultValue;
|
||||
SaveParameters();
|
||||
RebuildParameterPorts();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Parameter>& GetParameters() const {
|
||||
return m_parameters;
|
||||
}
|
||||
|
||||
size_t GetParameterCount() const {
|
||||
return m_parameters.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Parameter> m_parameters;
|
||||
|
||||
void SaveParameters() {
|
||||
nlohmann::json j;
|
||||
j["parameters"] = nlohmann::json::array();
|
||||
|
||||
for (const auto& param : m_parameters) {
|
||||
j["parameters"].push_back(param.ToJson());
|
||||
}
|
||||
|
||||
SetInternalData(j);
|
||||
}
|
||||
|
||||
void RebuildParameterPorts() {
|
||||
// Clear all data output ports (keep execution port)
|
||||
ClearDataOutputPorts();
|
||||
|
||||
// Add a data output port for each parameter
|
||||
for (const auto& param : m_parameters) {
|
||||
AddOutputPort(Port::Type::DATA_PORT, param.name, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
123
core/story-manager/src/nodes/function_exit_node.h
Normal file
123
core/story-manager/src/nodes/function_exit_node.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#pragma once
|
||||
|
||||
#include "base_node.h"
|
||||
#include <vector>
|
||||
|
||||
class FunctionExitNode : public BaseNode
|
||||
{
|
||||
public:
|
||||
struct ReturnValue {
|
||||
std::string name;
|
||||
std::string type; // "int", "string", "bool", "float"
|
||||
|
||||
nlohmann::json ToJson() const {
|
||||
return {
|
||||
{"name", name},
|
||||
{"type", type}
|
||||
};
|
||||
}
|
||||
|
||||
static ReturnValue FromJson(const nlohmann::json& j) {
|
||||
ReturnValue rv;
|
||||
rv.name = j.value("name", "");
|
||||
rv.type = j.value("type", "int");
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
FunctionExitNode(const std::string &type)
|
||||
: BaseNode(type, "Function Exit Node")
|
||||
, m_exitLabel("Return")
|
||||
{
|
||||
SetWeight(900); // High weight, near the end
|
||||
SetBehavior(BaseNode::BEHAVIOR_EXECUTION);
|
||||
SetupExecutionPorts(true, 0, true); // Has input, no output (it's the end)
|
||||
}
|
||||
|
||||
void Initialize() override {
|
||||
// Load return values and exit label from internal data
|
||||
nlohmann::json j = GetInternalData();
|
||||
m_returnValues.clear();
|
||||
|
||||
m_exitLabel = j.value("exitLabel", "Return");
|
||||
|
||||
if (j.contains("returnValues") && j["returnValues"].is_array()) {
|
||||
for (const auto& rvJson : j["returnValues"]) {
|
||||
m_returnValues.push_back(ReturnValue::FromJson(rvJson));
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild input ports for return values
|
||||
RebuildReturnValuePorts();
|
||||
}
|
||||
|
||||
void SetExitLabel(const std::string& label) {
|
||||
m_exitLabel = label;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
std::string GetExitLabel() const {
|
||||
return m_exitLabel;
|
||||
}
|
||||
|
||||
void AddReturnValue(const std::string& name, const std::string& type) {
|
||||
ReturnValue rv;
|
||||
rv.name = name;
|
||||
rv.type = type;
|
||||
m_returnValues.push_back(rv);
|
||||
|
||||
SaveData();
|
||||
RebuildReturnValuePorts();
|
||||
}
|
||||
|
||||
void RemoveReturnValue(size_t index) {
|
||||
if (index < m_returnValues.size()) {
|
||||
m_returnValues.erase(m_returnValues.begin() + index);
|
||||
SaveData();
|
||||
RebuildReturnValuePorts();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateReturnValue(size_t index, const std::string& name, const std::string& type) {
|
||||
if (index < m_returnValues.size()) {
|
||||
m_returnValues[index].name = name;
|
||||
m_returnValues[index].type = type;
|
||||
SaveData();
|
||||
RebuildReturnValuePorts();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ReturnValue>& GetReturnValues() const {
|
||||
return m_returnValues;
|
||||
}
|
||||
|
||||
size_t GetReturnValueCount() const {
|
||||
return m_returnValues.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_exitLabel;
|
||||
std::vector<ReturnValue> m_returnValues;
|
||||
|
||||
void SaveData() {
|
||||
nlohmann::json j;
|
||||
j["exitLabel"] = m_exitLabel;
|
||||
j["returnValues"] = nlohmann::json::array();
|
||||
|
||||
for (const auto& rv : m_returnValues) {
|
||||
j["returnValues"].push_back(rv.ToJson());
|
||||
}
|
||||
|
||||
SetInternalData(j);
|
||||
}
|
||||
|
||||
void RebuildReturnValuePorts() {
|
||||
// Clear all data input ports (keep execution port)
|
||||
ClearDataInputPorts();
|
||||
|
||||
// Add a data input port for each return value
|
||||
for (const auto& rv : m_returnValues) {
|
||||
AddInputPort(Port::Type::DATA_PORT, rv.name, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -24,21 +24,24 @@
|
|||
#include "while_loop_node.h"
|
||||
#include "break_node.h"
|
||||
#include "continue_node.h"
|
||||
#include "function_exit_node.h"
|
||||
|
||||
static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec";
|
||||
static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb";
|
||||
static const std::string VariableNodeUuid = "020cca4e-9cdc-47e7-a6a5-53e4c9152ed0";
|
||||
static const std::string PrintNodeUuid = "02ee27bc-ff1d-4f94-b700-eab55052ad1c";
|
||||
static const std::string FunctionEntryNodeUuid = "02fd145a-b3a6-43c2-83ce-6a187e6d4b5b";
|
||||
static const std::string BranchNodeUuid = "027b723d-2327-4646-a17a-79ddc2e016e4";
|
||||
static const std::string WaitEventNodeUuid = "02225cff-4975-400e-8130-41524d8af773";
|
||||
static const std::string WaitDelayNodeUuid = "02455ef0-4975-4546-94de-720cae6baae3";
|
||||
static const std::string PlayMediaNodeUuid = "0285e90a-2eb7-4605-baa9-b3712a14dff8";
|
||||
static const std::string SendSignalNodeUuid = "02c2ce4b-8783-47cb-a55f-90056bebd64b";
|
||||
static const std::string ForLoopNodeUuid = "02a1b2c3-4d5e-6f7a-8b9c-0d1e2f3a4b5c";
|
||||
static const std::string WhileLoopNodeUuid = "02b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d";
|
||||
static const std::string BreakNodeUuid = "02c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e";
|
||||
static const std::string ContinueNodeUuid = "02d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f";
|
||||
|
||||
static constexpr const char* OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec";
|
||||
static constexpr const char* CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb";
|
||||
static constexpr const char* VariableNodeUuid = "020cca4e-9cdc-47e7-a6a5-53e4c9152ed0";
|
||||
static constexpr const char* PrintNodeUuid = "02ee27bc-ff1d-4f94-b700-eab55052ad1c";
|
||||
static constexpr const char* FunctionEntryNodeUuid = "02fd145a-b3a6-43c2-83ce-6a187e6d4b5b";
|
||||
static constexpr const char* BranchNodeUuid = "027b723d-2327-4646-a17a-79ddc2e016e4";
|
||||
static constexpr const char* WaitEventNodeUuid = "02225cff-4975-400e-8130-41524d8af773";
|
||||
static constexpr const char* WaitDelayNodeUuid = "02455ef0-4975-4546-94de-720cae6baae3";
|
||||
static constexpr const char* PlayMediaNodeUuid = "0285e90a-2eb7-4605-baa9-b3712a14dff8";
|
||||
static constexpr const char* SendSignalNodeUuid = "02c2ce4b-8783-47cb-a55f-90056bebd64b";
|
||||
static constexpr const char* ForLoopNodeUuid = "02a1b2c3-4d5e-6f7a-8b9c-0d1e2f3a4b5c";
|
||||
static constexpr const char* WhileLoopNodeUuid = "02b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d";
|
||||
static constexpr const char* BreakNodeUuid = "02c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e";
|
||||
static constexpr const char* ContinueNodeUuid = "02d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f";
|
||||
static constexpr const char* FunctionExitNodeUuid = "02d78b65-9246-4108-91fc-03dfc142d9ee";
|
||||
|
||||
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type);
|
||||
|
||||
|
|
@ -66,6 +69,8 @@ public:
|
|||
registerNode<WhileLoopNode>(WhileLoopNodeUuid, std::make_shared<StoryPrimitive>("While Loop"));
|
||||
registerNode<BreakNode>(BreakNodeUuid, std::make_shared<StoryPrimitive>("Break"));
|
||||
registerNode<ContinueNode>(ContinueNodeUuid, std::make_shared<StoryPrimitive>("Continue"));
|
||||
registerNode<FunctionExitNode>(FunctionExitNodeUuid, std::make_shared<StoryPrimitive>("Function exit"));
|
||||
|
||||
}
|
||||
|
||||
~NodesFactory() = default;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "story_project.h"
|
||||
#include "json.hpp"
|
||||
// #include "media_node.h"
|
||||
#include "variable_node.h"
|
||||
#include "operator_node.h"
|
||||
#include "print_node.h"
|
||||
|
|
@ -234,6 +233,18 @@ void StoryProject::ScanVariable(const std::function<bool(std::shared_ptr<Variabl
|
|||
}
|
||||
}
|
||||
|
||||
void StoryProject::ScanNodes(const std::function<bool(std::shared_ptr<BaseNode>)>& callback) {
|
||||
for (auto& page : m_pages) {
|
||||
auto [nodesBegin, nodesEnd] = page->Nodes();
|
||||
std::vector<std::shared_ptr<BaseNode>> pageNodes(nodesBegin, nodesEnd);
|
||||
for (auto& node : pageNodes) {
|
||||
if (!callback(node)) {
|
||||
return; // Stop scanning if callback returns false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StoryProject::AddVariable()
|
||||
{
|
||||
auto v = std::make_shared<Variable>("var_" + std::to_string(m_variables.size()));
|
||||
|
|
@ -271,6 +282,8 @@ bool StoryProject::ModelFromJson(const nlohmann::json &model, NodesFactory &fact
|
|||
|
||||
std::string type = element["type"].get<std::string>();
|
||||
|
||||
std::cout << "!!!!!!!!!!!!!!!!!" << type << std::endl;
|
||||
|
||||
auto n = factory.CreateNode(type);
|
||||
if (n)
|
||||
{
|
||||
|
|
@ -301,16 +314,13 @@ bool StoryProject::ModelFromJson(const nlohmann::json &model, NodesFactory &fact
|
|||
p->AddLink(std::make_shared<Connection>(connection.get<Connection>()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
success = true;
|
||||
}
|
||||
catch(nlohmann::json::exception &e)
|
||||
{
|
||||
std::cout << "(NodeEditorWindow::Load) " << e.what() << std::endl;
|
||||
std::cout << "(StoryProject::ModelFromJson) " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
@ -533,8 +543,14 @@ bool StoryProject::Load(ResourceManager &manager, NodesFactory &factory)
|
|||
|
||||
if (j.contains("pages"))
|
||||
{
|
||||
ModelFromJson(j, factory);
|
||||
m_initialized = true;
|
||||
if (ModelFromJson(j, factory))
|
||||
{
|
||||
m_initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::logic_error("Model read error");
|
||||
}
|
||||
}
|
||||
|
||||
if (j.contains("variables"))
|
||||
|
|
@ -575,7 +591,11 @@ bool StoryProject::Load(ResourceManager &manager, NodesFactory &factory)
|
|||
}
|
||||
catch(nlohmann::json::exception &e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
m_log.Log(e.what(), true);
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
m_log.Log(e.what(), true);
|
||||
}
|
||||
|
||||
if (m_pages.size() == 0)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
#include "json.hpp"
|
||||
|
||||
|
||||
#include "json.hpp"
|
||||
#include "resource_manager.h"
|
||||
|
|
@ -103,7 +103,7 @@ public:
|
|||
std::pair<std::list<std::shared_ptr<BaseNode>>::iterator, std::list<std::shared_ptr<BaseNode>>::iterator> Nodes(const std::string_view &page_uuid);
|
||||
std::pair<std::list<std::shared_ptr<Connection>>::iterator, std::list<std::shared_ptr<Connection>>::iterator> Links(const std::string_view &page_uuid);
|
||||
|
||||
|
||||
void ScanNodes(const std::function<bool(std::shared_ptr<BaseNode>)>& callback);
|
||||
void ScanVariable(const std::function<bool(std::shared_ptr<Variable> element)>& operation) override;
|
||||
void AddVariable() override;
|
||||
void DeleteVariable(int i) override;
|
||||
|
|
|
|||
186
core/story-manager/src/utils/json_wrapper.cpp
Normal file
186
core/story-manager/src/utils/json_wrapper.cpp
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
#include "json_wrapper.h"
|
||||
#include "json.hpp"
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class JsonWrapper::Impl {
|
||||
public:
|
||||
json j;
|
||||
};
|
||||
|
||||
JsonWrapper::JsonWrapper() : impl(std::make_unique<Impl>()) {
|
||||
impl->j = json::object();
|
||||
}
|
||||
|
||||
JsonWrapper::JsonWrapper(JsonWrapper&& other) noexcept : impl(std::move(other.impl)) {}
|
||||
|
||||
JsonWrapper& JsonWrapper::operator=(JsonWrapper&& other) noexcept {
|
||||
if (this != &other) {
|
||||
impl = std::move(other.impl);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonWrapper::JsonWrapper(const std::string& jsonString) : impl(std::make_unique<Impl>()) {
|
||||
try {
|
||||
impl->j = json::parse(jsonString);
|
||||
} catch (...) {
|
||||
impl->j = json::object();
|
||||
}
|
||||
}
|
||||
|
||||
JsonWrapper::~JsonWrapper() = default;
|
||||
|
||||
bool JsonWrapper::hasKey(const std::string& key) const {
|
||||
return impl->j.contains(key);
|
||||
}
|
||||
|
||||
std::optional<std::string> JsonWrapper::getString(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<std::string>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> JsonWrapper::getInt(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<int>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<double> JsonWrapper::getDouble(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<double>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<bool> JsonWrapper::getBool(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<bool>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonWrapper::setString(const std::string& key, const std::string& value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setInt(const std::string& key, int value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setDouble(const std::string& key, double value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setBool(const std::string& key, bool value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setArrayImpl(const std::string& key, const void* values, size_t typeHash) {
|
||||
// Ici on doit caster et stocker dans json, mais sans RTTI avancé on limite
|
||||
// Par exemple on peut specialiser cette méthode pour les types courants
|
||||
if (typeHash == typeid(int).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<int>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else if (typeHash == typeid(double).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<double>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else if (typeHash == typeid(std::string).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<std::string>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else if (typeHash == typeid(bool).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<bool>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else {
|
||||
// Pour les types non pris en charge, on peut lever une exception ou ignorer
|
||||
throw std::runtime_error("Type non supporté pour setArray");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::vector<T>> JsonWrapper::getArrayImpl(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<std::vector<T>>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T JsonWrapper::getImpl(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<T>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T JsonWrapper::asImpl(std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.get<T>();
|
||||
} catch (...) {
|
||||
errorKey = "[root]";
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
JsonWrapper JsonWrapper::fromImpl(const void* value, size_t typeHash) {
|
||||
JsonWrapper wrapper;
|
||||
if (typeHash == typeid(int).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const int*>(value);
|
||||
} else if (typeHash == typeid(double).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const double*>(value);
|
||||
} else if (typeHash == typeid(std::string).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const std::string*>(value);
|
||||
} else if (typeHash == typeid(bool).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const bool*>(value);
|
||||
} else {
|
||||
// Si c’est un type complexe, il faut une surcharge ou un specialization dans le .cpp
|
||||
// Exemple: si T a to_json/from_json, on peut faire une conversion nlohmann::json value = T;
|
||||
// Ici on fait un cast "générique" (moins safe)
|
||||
// On peut faire un throw ici pour forcer l’utilisateur à spécialiser.
|
||||
throw std::runtime_error("Type non supporté pour from()");
|
||||
}
|
||||
return std::move(wrapper);
|
||||
}
|
||||
|
||||
// Explicit instantiations for getArrayImpl, getImpl, asImpl for common types
|
||||
|
||||
template std::optional<std::vector<int>> JsonWrapper::getArrayImpl<int>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<double>> JsonWrapper::getArrayImpl<double>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<std::string>> JsonWrapper::getArrayImpl<std::string>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<bool>> JsonWrapper::getArrayImpl<bool>(const std::string&, std::string&) const;
|
||||
|
||||
template int JsonWrapper::getImpl<int>(const std::string&, std::string&) const;
|
||||
template double JsonWrapper::getImpl<double>(const std::string&, std::string&) const;
|
||||
template std::string JsonWrapper::getImpl<std::string>(const std::string&, std::string&) const;
|
||||
template bool JsonWrapper::getImpl<bool>(const std::string&, std::string&) const;
|
||||
|
||||
template int JsonWrapper::asImpl<int>(std::string&) const;
|
||||
template double JsonWrapper::asImpl<double>(std::string&) const;
|
||||
template std::string JsonWrapper::asImpl<std::string>(std::string&) const;
|
||||
template bool JsonWrapper::asImpl<bool>(std::string&) const;
|
||||
|
||||
std::string JsonWrapper::dump(int indent) const {
|
||||
if (indent < 0) return impl->j.dump();
|
||||
return impl->j.dump(indent);
|
||||
}
|
||||
82
core/story-manager/src/utils/json_wrapper.h
Normal file
82
core/story-manager/src/utils/json_wrapper.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
class JsonWrapper {
|
||||
public:
|
||||
JsonWrapper();
|
||||
explicit JsonWrapper(const std::string& jsonString);
|
||||
~JsonWrapper();
|
||||
|
||||
// Ajout move constructor et move assign
|
||||
JsonWrapper(JsonWrapper&&) noexcept;
|
||||
JsonWrapper& operator=(JsonWrapper&&) noexcept;
|
||||
|
||||
// Supprimer copie pour éviter erreur
|
||||
JsonWrapper(const JsonWrapper&) = delete;
|
||||
JsonWrapper& operator=(const JsonWrapper&) = delete;
|
||||
|
||||
bool hasKey(const std::string& key) const;
|
||||
|
||||
// Valeurs simples
|
||||
std::optional<std::string> getString(const std::string& key, std::string& errorKey) const;
|
||||
std::optional<int> getInt(const std::string& key, std::string& errorKey) const;
|
||||
std::optional<double> getDouble(const std::string& key, std::string& errorKey) const;
|
||||
std::optional<bool> getBool(const std::string& key, std::string& errorKey) const;
|
||||
|
||||
void setString(const std::string& key, const std::string& value);
|
||||
void setInt(const std::string& key, int value);
|
||||
void setDouble(const std::string& key, double value);
|
||||
void setBool(const std::string& key, bool value);
|
||||
|
||||
// Array génériques
|
||||
template <typename T>
|
||||
void setArray(const std::string& key, const std::vector<T>& values) {
|
||||
setArrayImpl(key, reinterpret_cast<const void*>(&values), typeid(T).hash_code());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::vector<T>> getArray(const std::string& key, std::string& errorKey) const {
|
||||
return getArrayImpl<T>(key, errorKey);
|
||||
}
|
||||
|
||||
// Sérialisation/Désérialisation générique
|
||||
template <typename T>
|
||||
T get(const std::string& key, std::string& errorKey) const {
|
||||
return getImpl<T>(key, errorKey);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T as(std::string& errorKey) const {
|
||||
return asImpl<T>(errorKey);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static JsonWrapper from(const T& value) {
|
||||
return fromImpl(reinterpret_cast<const void*>(&value), typeid(T).hash_code());
|
||||
}
|
||||
|
||||
// Dump JSON
|
||||
std::string dump(int indent = -1) const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
// Implémentations privées non template (définies dans .cpp)
|
||||
void setArrayImpl(const std::string& key, const void* values, size_t typeHash);
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::vector<T>> getArrayImpl(const std::string& key, std::string& errorKey) const;
|
||||
|
||||
template <typename T>
|
||||
T getImpl(const std::string& key, std::string& errorKey) const;
|
||||
|
||||
template <typename T>
|
||||
T asImpl(std::string& errorKey) const;
|
||||
|
||||
static JsonWrapper fromImpl(const void* value, size_t typeHash);
|
||||
};
|
||||
|
|
@ -32,6 +32,7 @@ add_executable(${PROJECT_NAME}
|
|||
test_print_node.cpp
|
||||
test_branch.cpp
|
||||
test_loops.cpp
|
||||
test_json.cpp
|
||||
|
||||
../story-manager/src/nodes/base_node.cpp
|
||||
../story-manager/src/nodes/branch_node.cpp
|
||||
|
|
@ -47,6 +48,7 @@ add_executable(${PROJECT_NAME}
|
|||
../story-manager/src/nodes/break_node.cpp
|
||||
../story-manager/src/nodes/continue_node.cpp
|
||||
|
||||
../story-manager/src/utils/json_wrapper.cpp
|
||||
|
||||
../chip32/chip32_assembler.cpp
|
||||
../chip32/chip32_vm.c
|
||||
|
|
@ -58,6 +60,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
|||
../story-manager/src
|
||||
../story-manager/src/nodes
|
||||
../story-manager/src/compiler
|
||||
../story-manager/src/utils
|
||||
../story-manager/interfaces
|
||||
../../shared
|
||||
)
|
||||
|
|
|
|||
101
core/tests/test_json.cpp
Normal file
101
core/tests/test_json.cpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include "json_wrapper.h"
|
||||
#include "json.hpp"
|
||||
using Catch::Matchers::WithinRel;
|
||||
using namespace nlohmann;
|
||||
|
||||
TEST_CASE("JsonWrapper basic types", "[json]") {
|
||||
JsonWrapper json;
|
||||
json.setString("name", "Alice");
|
||||
json.setInt("age", 30);
|
||||
json.setDouble("pi", 3.14159);
|
||||
json.setBool("is_valid", true);
|
||||
|
||||
std::string err;
|
||||
|
||||
SECTION("Get string") {
|
||||
auto name = json.getString("name", err);
|
||||
REQUIRE(name.has_value());
|
||||
REQUIRE(name.value() == "Alice");
|
||||
}
|
||||
|
||||
SECTION("Get int") {
|
||||
auto age = json.getInt("age", err);
|
||||
REQUIRE(age.has_value());
|
||||
REQUIRE(age.value() == 30);
|
||||
}
|
||||
|
||||
SECTION("Get double") {
|
||||
auto pi = json.getDouble("pi", err);
|
||||
REQUIRE(pi.has_value());
|
||||
REQUIRE_THAT(pi.value(), WithinRel(3.14159, 1e-6));
|
||||
}
|
||||
|
||||
SECTION("Get bool") {
|
||||
auto valid = json.getBool("is_valid", err);
|
||||
REQUIRE(valid.has_value());
|
||||
REQUIRE(valid.value() == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonWrapper arrays", "[json]") {
|
||||
JsonWrapper json;
|
||||
|
||||
json.setArray<int>("scores", {10, 20, 30});
|
||||
json.setArray<std::string>("tags", {"news", "tech"});
|
||||
json.setArray<double>("measurements", {1.1, 2.2, 3.3});
|
||||
json.setArray<bool>("flags", {true, false, true});
|
||||
|
||||
std::string err;
|
||||
|
||||
auto scores = json.getArray<int>("scores", err);
|
||||
REQUIRE(scores.has_value());
|
||||
REQUIRE(scores->size() == 3);
|
||||
REQUIRE(scores->at(1) == 20);
|
||||
|
||||
auto tags = json.getArray<std::string>("tags", err);
|
||||
REQUIRE(tags.has_value());
|
||||
REQUIRE(tags->at(0) == "news");
|
||||
|
||||
auto measurements = json.getArray<double>("measurements", err);
|
||||
REQUIRE(measurements.has_value());
|
||||
REQUIRE_THAT(measurements->at(2), WithinRel(3.3, 1e-6));
|
||||
|
||||
auto flags = json.getArray<bool>("flags", err);
|
||||
REQUIRE(flags.has_value());
|
||||
REQUIRE(flags->at(0) == true);
|
||||
}
|
||||
|
||||
struct Connection {
|
||||
std::string host;
|
||||
int port;ccM9XAGZ$mz^b*52T5p&sMA@ujPbCUNW
|
||||
};
|
||||
|
||||
// from_json / to_json must be defined for Connection
|
||||
inline void to_json(nlohmann::json& j, const Connection& c) {
|
||||
j = nlohmann::json{{"host", c.host}, {"port", c.port}};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, Connection& c) {
|
||||
j.at("host").get_to(c.host);
|
||||
j.at("port").get_to(c.port);
|
||||
}
|
||||
|
||||
template Connection JsonWrapper::asImpl<Connection>(std::string&) const;
|
||||
template Connection JsonWrapper::getImpl<Connection>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<Connection>> JsonWrapper::getArrayImpl<Connection>(const std::string&, std::string&) const;
|
||||
|
||||
|
||||
TEST_CASE("JsonWrapper generic serialization/deserialization", "[json][struct]") {
|
||||
|
||||
|
||||
Connection original{"127.0.0.1", 5000};
|
||||
auto wrapper = JsonWrapper::from(original);
|
||||
|
||||
std::string err;
|
||||
auto restored = wrapper.as<Connection>(err);
|
||||
|
||||
REQUIRE(restored.host == "127.0.0.1");
|
||||
REQUIRE(restored.port == 5000);
|
||||
}
|
||||
|
|
@ -292,6 +292,7 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
|
|||
../core/story-manager/src
|
||||
../core/story-manager/src/nodes
|
||||
../core/story-manager/src/compiler
|
||||
../core/story-manager/src/utils
|
||||
../core/story-manager/interfaces
|
||||
)
|
||||
|
||||
|
|
@ -300,7 +301,7 @@ add_definitions(
|
|||
-DVERSION_MAJOR=${PROJECT_VERSION_MAJOR}
|
||||
-DVERSION_MINOR=${PROJECT_VERSION_MINOR}
|
||||
-DVERSION_PATCH=${PROJECT_VERSION_PATCH}
|
||||
|
||||
-DJSON_DIAGNOSTICS=1
|
||||
-DRAUDIO_STANDALONE
|
||||
-DSUPPORT_MODULE_RAUDIO
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[Window][WindowOverViewport_11111111]
|
||||
Pos=60,26
|
||||
Size=1860,982
|
||||
Size=1220,694
|
||||
Collapsed=0
|
||||
|
||||
[Window][Debug##Default]
|
||||
|
|
@ -9,34 +9,34 @@ Size=400,400
|
|||
Collapsed=0
|
||||
|
||||
[Window][Library Manager]
|
||||
Pos=1060,26
|
||||
Size=860,653
|
||||
Pos=730,26
|
||||
Size=550,441
|
||||
Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][Console]
|
||||
Pos=60,681
|
||||
Size=805,327
|
||||
Pos=60,469
|
||||
Size=527,251
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][Emulator]
|
||||
Pos=1060,26
|
||||
Size=860,653
|
||||
Collapsed=0
|
||||
DockId=0x00000002,5
|
||||
|
||||
[Window][Code viewer]
|
||||
Pos=1060,26
|
||||
Size=860,653
|
||||
Pos=730,26
|
||||
Size=550,441
|
||||
Collapsed=0
|
||||
DockId=0x00000002,4
|
||||
|
||||
[Window][Resources]
|
||||
Pos=1060,26
|
||||
Size=860,653
|
||||
[Window][Code viewer]
|
||||
Pos=730,26
|
||||
Size=550,441
|
||||
Collapsed=0
|
||||
DockId=0x00000002,1
|
||||
DockId=0x00000002,3
|
||||
|
||||
[Window][Resources]
|
||||
Pos=589,469
|
||||
Size=691,251
|
||||
Collapsed=0
|
||||
DockId=0x00000005,1
|
||||
|
||||
[Window][Node editor]
|
||||
Pos=60,26
|
||||
|
|
@ -50,36 +50,36 @@ Size=150,42
|
|||
Collapsed=0
|
||||
|
||||
[Window][Variables]
|
||||
Pos=867,681
|
||||
Size=1053,327
|
||||
Pos=589,469
|
||||
Size=691,251
|
||||
Collapsed=0
|
||||
DockId=0x00000005,0
|
||||
|
||||
[Window][CPU]
|
||||
Pos=1060,26
|
||||
Size=860,653
|
||||
Pos=730,26
|
||||
Size=550,441
|
||||
Collapsed=0
|
||||
DockId=0x00000002,1
|
||||
|
||||
[Window][RAM view]
|
||||
Pos=730,26
|
||||
Size=550,441
|
||||
Collapsed=0
|
||||
DockId=0x00000002,2
|
||||
|
||||
[Window][RAM view]
|
||||
Pos=1060,26
|
||||
Size=860,653
|
||||
Collapsed=0
|
||||
DockId=0x00000002,3
|
||||
|
||||
[Window][Properties]
|
||||
Pos=867,681
|
||||
Size=1053,327
|
||||
Pos=730,26
|
||||
Size=550,441
|
||||
Collapsed=0
|
||||
DockId=0x00000005,1
|
||||
DockId=0x00000002,5
|
||||
|
||||
[Window][ToolBar]
|
||||
Pos=0,26
|
||||
Size=60,982
|
||||
Size=60,694
|
||||
Collapsed=0
|
||||
|
||||
[Window][QuitConfirm]
|
||||
Pos=828,456
|
||||
Pos=508,312
|
||||
Size=264,96
|
||||
Collapsed=0
|
||||
|
||||
|
|
@ -90,13 +90,13 @@ Collapsed=0
|
|||
|
||||
[Window][Module editor]
|
||||
Pos=60,26
|
||||
Size=998,653
|
||||
Size=668,441
|
||||
Collapsed=0
|
||||
DockId=0x00000001,0
|
||||
|
||||
[Window][Story editor]
|
||||
Pos=60,26
|
||||
Size=998,653
|
||||
Size=668,441
|
||||
Collapsed=0
|
||||
DockId=0x00000001,1
|
||||
|
||||
|
|
@ -106,11 +106,21 @@ Size=687,422
|
|||
Collapsed=0
|
||||
|
||||
[Window][Error List]
|
||||
Pos=60,681
|
||||
Size=805,327
|
||||
Pos=60,469
|
||||
Size=527,251
|
||||
Collapsed=0
|
||||
DockId=0x00000004,1
|
||||
|
||||
[Window][AboutPopup]
|
||||
Pos=436,235
|
||||
Size=408,249
|
||||
Collapsed=0
|
||||
|
||||
[Window][ModulePropertiesPopup]
|
||||
Pos=424,289
|
||||
Size=432,142
|
||||
Collapsed=0
|
||||
|
||||
[Table][0x7728942D,5]
|
||||
RefScale=20
|
||||
Column 0 Width=44 Sort=0v
|
||||
|
|
@ -155,11 +165,11 @@ Column 2 Weight=1.0000
|
|||
Column 3 Width=60
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1860,982 Split=Y
|
||||
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,365 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=998,694 CentralNode=1 Selected=0x93ADCAAB
|
||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=860,694 Selected=0x4B07C626
|
||||
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,327 Split=X Selected=0xEA83D666
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
|
||||
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,441 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=668,694 CentralNode=1 Selected=0x93ADCAAB
|
||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=550,694 Selected=0x63869CAF
|
||||
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,251 Split=X Selected=0xEA83D666
|
||||
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=621,192 Selected=0xEA83D666
|
||||
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=813,192 Selected=0x8C72BEA8
|
||||
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=813,192 Selected=0x30401527
|
||||
|
||||
|
|
|
|||
|
|
@ -852,6 +852,11 @@ void AppController::ImportProject(const std::string &filePathName, int format)
|
|||
|
||||
}
|
||||
|
||||
NodesFactory& AppController::GetNodesFactory()
|
||||
{
|
||||
return m_nodesFactory;
|
||||
}
|
||||
|
||||
std::shared_ptr<IStoryProject> AppController::GetCurrentProject()
|
||||
{
|
||||
return m_story;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ public:
|
|||
virtual void LoadBinaryStory(const std::string &filename) override;
|
||||
virtual void ToggleBreakpoint(int line) override;
|
||||
virtual uint32_t GetRegister(int reg) override;
|
||||
virtual NodesFactory& GetNodesFactory() override;
|
||||
virtual void Play() override;
|
||||
virtual void Step() override;
|
||||
virtual void Run() override;
|
||||
|
|
@ -113,7 +114,6 @@ public:
|
|||
// Getters pour les managers gérés par AppController
|
||||
ResourceManager& GetResourceManager() { return m_resources; }
|
||||
LibraryManager& GetLibraryManager() { return m_libraryManager; }
|
||||
NodesFactory& GetNodesFactory() { return m_nodesFactory; }
|
||||
AudioPlayer& GetAudioPlayer() { return m_player; }
|
||||
WebServer& GetWebServer() { return m_webServer; }
|
||||
const std::vector<std::string>& GetRecentProjects() const { return m_recentProjects; }
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
#include "variable_node_widget.h"
|
||||
#include "operator_node_widget.h"
|
||||
#include "print_node_widget.h"
|
||||
#include "function_entry_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"
|
||||
|
|
@ -45,6 +45,8 @@
|
|||
#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)
|
||||
|
|
@ -74,7 +76,7 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
|
|||
// m_widgetFactory.registerNode<ModuleNodeWidget>("module-node");
|
||||
m_widgetFactory.registerNode<VariableNodeWidget>(VariableNodeUuid);
|
||||
m_widgetFactory.registerNode<PrintNodeWidget>(PrintNodeUuid);
|
||||
m_widgetFactory.registerNode<FunctionEntryWidget>(FunctionEntryNodeUuid);
|
||||
m_widgetFactory.registerNode<FunctionEntryNodeWidget>(FunctionEntryNodeUuid);
|
||||
m_widgetFactory.registerNode<BranchNodeWidget>(BranchNodeUuid);
|
||||
m_widgetFactory.registerNode<WaitEventNodeWidget>(WaitEventNodeUuid);
|
||||
m_widgetFactory.registerNode<WaitDelayNodeWidget>(WaitDelayNodeUuid);
|
||||
|
|
@ -84,6 +86,8 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
|
|||
m_widgetFactory.registerNode<WhileLoopNodeWidget>(WhileLoopNodeUuid);
|
||||
m_widgetFactory.registerNode<BreakNodeWidget>(BreakNodeUuid);
|
||||
m_widgetFactory.registerNode<ContinueNodeWidget>(ContinueNodeUuid);
|
||||
m_widgetFactory.registerNode<FunctionExitNodeWidget>(FunctionExitNodeUuid);
|
||||
|
||||
|
||||
m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) {
|
||||
OpenProject(event.GetUuid());
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@
|
|||
#include "i_story_manager.h"
|
||||
#include "i_story_project.h"
|
||||
#include "call_function_node.h"
|
||||
#include "function_entry_node.h"
|
||||
#include "function_exit_node.h"
|
||||
#include "gui.h"
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
|
||||
class CallFunctionNodeWidget : public BaseNodeWidget
|
||||
{
|
||||
|
|
@ -26,16 +29,25 @@ public:
|
|||
BaseNodeWidget::Initialize();
|
||||
m_functionName = m_callFunctionNode->GetFunctionName();
|
||||
m_functionUuid = m_callFunctionNode->GetFunctionUuid();
|
||||
|
||||
// If a function is selected, rebuild ports
|
||||
if (!m_functionUuid.empty()) {
|
||||
RebuildPortsFromModule();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawProperties(std::shared_ptr<IStoryProject> story) override {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
|
||||
// Liste déroulante des fonctions disponibles
|
||||
if (ImGui::BeginCombo("Function", m_functionName.empty() ? "<Select function>" : m_functionName.c_str())) {
|
||||
ImGui::TextColored(ImVec4(0.2f, 0.6f, 1.0f, 1.0f), ICON_MDI_FUNCTION " Call Function");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Récupérer la liste des fonctions du projet
|
||||
auto functions = story->GetFunctionsList(); // À implémenter dans IStoryProject
|
||||
// Function selection
|
||||
ImGui::Text("Select function/module:");
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::BeginCombo("##function", m_functionName.empty() ? "<Select function>" : m_functionName.c_str())) {
|
||||
// Get list of available functions/modules
|
||||
auto functions = story->GetFunctionsList();
|
||||
|
||||
for (size_t i = 0; i < functions.size(); ++i) {
|
||||
const bool is_selected = (m_functionUuid == functions[i].uuid);
|
||||
|
|
@ -44,6 +56,9 @@ public:
|
|||
m_functionUuid = functions[i].uuid;
|
||||
m_functionName = functions[i].name;
|
||||
m_callFunctionNode->SetFunction(m_functionUuid, m_functionName);
|
||||
|
||||
// Rebuild ports based on the selected module
|
||||
RebuildPortsFromModule();
|
||||
}
|
||||
|
||||
if (is_selected)
|
||||
|
|
@ -52,25 +67,38 @@ public:
|
|||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
// Bouton pour ouvrir la fonction dans l'éditeur
|
||||
ImGui::Spacing();
|
||||
|
||||
// Désactiver le bouton si aucune fonction n'est sélectionnée
|
||||
|
||||
// Open function button
|
||||
if (m_functionUuid.empty()) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if (ImGui::Button("> Open function")) {
|
||||
if (ImGui::Button(ICON_MDI_OPEN_IN_NEW " Open function")) {
|
||||
m_manager.OpenFunction(m_functionUuid, m_functionName);
|
||||
}
|
||||
|
||||
if (m_functionUuid.empty()) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// If a function is selected, show input/output configuration
|
||||
if (!m_functionUuid.empty() && !m_moduleParameters.empty()) {
|
||||
DrawInputBindingsUI();
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
DrawOutputMappingsUI(story);
|
||||
}
|
||||
}
|
||||
|
||||
void Draw() override {
|
||||
// Afficher le nom de la fonction dans le noeud
|
||||
ImGui::TextUnformatted(m_functionName.empty()
|
||||
? "<No function>"
|
||||
: m_functionName.c_str());
|
||||
|
|
@ -81,4 +109,143 @@ private:
|
|||
std::shared_ptr<CallFunctionNode> m_callFunctionNode;
|
||||
std::string m_functionName;
|
||||
std::string m_functionUuid;
|
||||
|
||||
// Cache of module interface
|
||||
std::vector<std::pair<std::string, std::string>> m_moduleParameters; // name, type
|
||||
std::vector<std::string> m_exitLabels;
|
||||
std::map<std::string, std::vector<std::pair<std::string, std::string>>> m_returnValuesByExit;
|
||||
|
||||
void RebuildPortsFromModule() {
|
||||
// Get the module from NodesFactory
|
||||
auto module = m_manager.GetNodesFactory().GetModule(m_functionUuid);
|
||||
if (!module) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find FunctionEntryNode in the module
|
||||
m_moduleParameters.clear();
|
||||
std::shared_ptr<FunctionEntryNode> entryNode = nullptr;
|
||||
|
||||
module->ScanNodes([&](std::shared_ptr<BaseNode> node) {
|
||||
if (node->GetType() == "function-entry-node") {
|
||||
entryNode = std::dynamic_pointer_cast<FunctionEntryNode>(node);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (entryNode) {
|
||||
for (const auto& param : entryNode->GetParameters()) {
|
||||
m_moduleParameters.push_back({param.name, param.type});
|
||||
}
|
||||
}
|
||||
|
||||
// Find all FunctionExitNodes in the module
|
||||
m_exitLabels.clear();
|
||||
m_returnValuesByExit.clear();
|
||||
std::vector<std::shared_ptr<FunctionExitNode>> exitNodes;
|
||||
|
||||
module->ScanNodes([&](std::shared_ptr<BaseNode> node) {
|
||||
if (node->GetType() == "function-exit-node") {
|
||||
auto exitNode = std::dynamic_pointer_cast<FunctionExitNode>(node);
|
||||
if (exitNode) {
|
||||
exitNodes.push_back(exitNode);
|
||||
m_exitLabels.push_back(exitNode->GetExitLabel());
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> returnValues;
|
||||
for (const auto& rv : exitNode->GetReturnValues()) {
|
||||
returnValues.push_back({rv.name, rv.type});
|
||||
}
|
||||
m_returnValuesByExit[exitNode->GetExitLabel()] = returnValues;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Rebuild the node's ports
|
||||
std::vector<std::pair<std::string, std::string>> exitLabelsWithType;
|
||||
for (const auto& label : m_exitLabels) {
|
||||
exitLabelsWithType.push_back({label, "execution"});
|
||||
}
|
||||
|
||||
m_callFunctionNode->RebuildPortsFromModule(m_moduleParameters, exitLabelsWithType, m_returnValuesByExit);
|
||||
}
|
||||
|
||||
void DrawInputBindingsUI() {
|
||||
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), ICON_MDI_ARROW_RIGHT " Input Parameters");
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Configure how each parameter receives its value:");
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::BeginTable("input_bindings_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Parameter", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
||||
ImGui::TableSetupColumn("Mode", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
||||
ImGui::TableSetupColumn("Constant Value", ImGuiTableColumnFlags_WidthFixed, 150.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto& param : m_moduleParameters) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(param.first.c_str());
|
||||
|
||||
// Parameter name
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s (%s)", param.first.c_str(), param.second.c_str());
|
||||
|
||||
// Mode selection
|
||||
ImGui::TableNextColumn();
|
||||
auto binding = m_callFunctionNode->GetInputBinding(param.first);
|
||||
int currentMode = (binding && binding->mode == CallFunctionNode::MODE_CONSTANT) ? 1 : 0;
|
||||
|
||||
const char* modes[] = {"Connected", "Constant"};
|
||||
if (ImGui::Combo("##mode", ¤tMode, modes, 2)) {
|
||||
auto mode = (currentMode == 1) ? CallFunctionNode::MODE_CONSTANT : CallFunctionNode::MODE_CONNECTED;
|
||||
std::string value = binding ? binding->constantValue : "";
|
||||
m_callFunctionNode->SetInputBindingMode(param.first, mode, value);
|
||||
}
|
||||
|
||||
// Constant value (only if mode is constant)
|
||||
ImGui::TableNextColumn();
|
||||
if (currentMode == 1) {
|
||||
char valueBuf[256] = "";
|
||||
if (binding) {
|
||||
strncpy(valueBuf, binding->constantValue.c_str(), sizeof(valueBuf) - 1);
|
||||
}
|
||||
|
||||
if (ImGui::InputText("##value", valueBuf, sizeof(valueBuf))) {
|
||||
m_callFunctionNode->SetInputBindingMode(param.first, CallFunctionNode::MODE_CONSTANT, valueBuf);
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled("(from pin)");
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), ICON_MDI_INFORMATION_OUTLINE " Use 'Connected' to get values from connected pins, 'Constant' to set a fixed value.");
|
||||
}
|
||||
|
||||
void DrawOutputMappingsUI(std::shared_ptr<IStoryProject> story) {
|
||||
ImGui::TextColored(ImVec4(0.8f, 0.4f, 0.2f, 1.0f), ICON_MDI_ARROW_LEFT " Return Values");
|
||||
ImGui::Spacing();
|
||||
|
||||
// Show all return values grouped by exit
|
||||
for (const auto& exitPair : m_returnValuesByExit) {
|
||||
ImGui::Text("Exit: %s", exitPair.first.c_str());
|
||||
ImGui::Indent();
|
||||
|
||||
for (const auto& rv : exitPair.second) {
|
||||
ImGui::BulletText("%s (%s)", rv.first.c_str(), rv.second.c_str());
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), ICON_MDI_INFORMATION_OUTLINE " Return values are available on output pins.");
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Connect them to use the returned data.");
|
||||
}
|
||||
};
|
||||
136
story-editor/src/node_editor/function_entry_node_widget.h
Normal file
136
story-editor/src/node_editor/function_entry_node_widget.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "base_node_widget.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "i_story_project.h"
|
||||
#include "function_entry_node.h"
|
||||
#include "gui.h"
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
|
||||
class FunctionEntryNodeWidget : public BaseNodeWidget
|
||||
{
|
||||
public:
|
||||
FunctionEntryNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> node)
|
||||
: BaseNodeWidget(manager, node)
|
||||
, m_manager(manager)
|
||||
{
|
||||
m_functionEntryNode = std::dynamic_pointer_cast<FunctionEntryNode>(node);
|
||||
SetTitle("Function Entry");
|
||||
}
|
||||
|
||||
void Draw() override {
|
||||
ImGui::TextUnformatted("Entry Point");
|
||||
if (m_functionEntryNode->GetParameterCount() > 0) {
|
||||
ImGui::Text(ICON_MDI_ARROW_RIGHT " %zu parameter(s)", m_functionEntryNode->GetParameterCount());
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool HasSync() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void DrawProperties(std::shared_ptr<IStoryProject> story) override {
|
||||
ImGui::TextColored(ImVec4(0.2f, 0.8f, 1.0f, 1.0f), ICON_MDI_IMPORT " Function Entry");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text("This node defines the entry point of the function/module.");
|
||||
ImGui::Text("Add parameters that will be passed when this function is called.");
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Parameters list
|
||||
ImGui::Text(ICON_MDI_FORMAT_LIST_BULLETED " Parameters:");
|
||||
ImGui::Spacing();
|
||||
|
||||
const auto& parameters = m_functionEntryNode->GetParameters();
|
||||
|
||||
// Table for parameters
|
||||
if (ImGui::BeginTable("parameters_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
||||
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 80.0f);
|
||||
ImGui::TableSetupColumn("Default", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
||||
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 80.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Display existing parameters
|
||||
for (size_t i = 0; i < parameters.size(); ++i) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
// Name
|
||||
ImGui::TableNextColumn();
|
||||
char nameBuf[128];
|
||||
strncpy(nameBuf, parameters[i].name.c_str(), sizeof(nameBuf) - 1);
|
||||
nameBuf[sizeof(nameBuf) - 1] = '\0';
|
||||
|
||||
if (ImGui::InputText("##name", nameBuf, sizeof(nameBuf))) {
|
||||
m_functionEntryNode->UpdateParameter(i, nameBuf, parameters[i].type, parameters[i].defaultValue);
|
||||
}
|
||||
|
||||
// Type
|
||||
ImGui::TableNextColumn();
|
||||
const char* types[] = {"int", "float", "string", "bool"};
|
||||
int currentType = 0;
|
||||
for (int t = 0; t < 4; ++t) {
|
||||
if (parameters[i].type == types[t]) {
|
||||
currentType = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Combo("##type", ¤tType, types, 4)) {
|
||||
m_functionEntryNode->UpdateParameter(i, parameters[i].name, types[currentType], parameters[i].defaultValue);
|
||||
}
|
||||
|
||||
// Default value
|
||||
ImGui::TableNextColumn();
|
||||
char defaultBuf[128];
|
||||
strncpy(defaultBuf, parameters[i].defaultValue.c_str(), sizeof(defaultBuf) - 1);
|
||||
defaultBuf[sizeof(defaultBuf) - 1] = '\0';
|
||||
|
||||
if (ImGui::InputText("##default", defaultBuf, sizeof(defaultBuf))) {
|
||||
m_functionEntryNode->UpdateParameter(i, parameters[i].name, parameters[i].type, defaultBuf);
|
||||
}
|
||||
|
||||
// Actions
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MDI_DELETE "##delete")) {
|
||||
m_functionEntryNode->RemoveParameter(i);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Add new parameter
|
||||
if (ImGui::Button(ICON_MDI_PLUS " Add Parameter")) {
|
||||
m_functionEntryNode->AddParameter("newParam", "int", "0");
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Info
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), ICON_MDI_INFORMATION_OUTLINE " Each parameter creates an output port on this node.");
|
||||
}
|
||||
|
||||
virtual void Initialize() override {
|
||||
BaseNodeWidget::Initialize();
|
||||
}
|
||||
|
||||
private:
|
||||
IStoryManager &m_manager;
|
||||
std::shared_ptr<FunctionEntryNode> m_functionEntryNode;
|
||||
};
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "base_node_widget.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "i_story_project.h"
|
||||
#include "function_entry_node.h"
|
||||
#include "gui.h"
|
||||
|
||||
class FunctionEntryWidget : public BaseNodeWidget
|
||||
{
|
||||
public:
|
||||
FunctionEntryWidget(IStoryManager &manager, std::shared_ptr<BaseNode> node)
|
||||
: BaseNodeWidget(manager, node)
|
||||
, m_manager(manager)
|
||||
{
|
||||
m_functionEntryNode = std::dynamic_pointer_cast<FunctionEntryNode>(node);
|
||||
SetTitle("Function Entry");
|
||||
}
|
||||
|
||||
void Draw() override {
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
}
|
||||
|
||||
virtual bool HasSync() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void DrawProperties(std::shared_ptr<IStoryProject> story) override {
|
||||
|
||||
}
|
||||
virtual void Initialize() override {
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
IStoryManager &m_manager;
|
||||
|
||||
std::shared_ptr<FunctionEntryNode> m_functionEntryNode;
|
||||
|
||||
};
|
||||
143
story-editor/src/node_editor/function_exit_node_widget.h
Normal file
143
story-editor/src/node_editor/function_exit_node_widget.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "base_node_widget.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "i_story_project.h"
|
||||
#include "function_exit_node.h"
|
||||
#include "gui.h"
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
|
||||
class FunctionExitNodeWidget : public BaseNodeWidget
|
||||
{
|
||||
public:
|
||||
FunctionExitNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> node)
|
||||
: BaseNodeWidget(manager, node)
|
||||
, m_manager(manager)
|
||||
{
|
||||
m_functionExitNode = std::dynamic_pointer_cast<FunctionExitNode>(node);
|
||||
SetTitle("Function Exit");
|
||||
}
|
||||
|
||||
void Draw() override {
|
||||
ImGui::TextUnformatted(m_functionExitNode->GetExitLabel().c_str());
|
||||
if (m_functionExitNode->GetReturnValueCount() > 0) {
|
||||
ImGui::Text(ICON_MDI_ARROW_LEFT " %zu return value(s)", m_functionExitNode->GetReturnValueCount());
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool HasSync() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void DrawProperties(std::shared_ptr<IStoryProject> story) override {
|
||||
ImGui::TextColored(ImVec4(0.8f, 0.2f, 0.2f, 1.0f), ICON_MDI_EXPORT " Function Exit");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text("This node defines an exit point for the function/module.");
|
||||
ImGui::Text("It returns values and terminates the function execution.");
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Exit label
|
||||
ImGui::Text("Exit Label:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
|
||||
char labelBuf[128];
|
||||
strncpy(labelBuf, m_functionExitNode->GetExitLabel().c_str(), sizeof(labelBuf) - 1);
|
||||
labelBuf[sizeof(labelBuf) - 1] = '\0';
|
||||
|
||||
if (ImGui::InputTextWithHint("##exit_label", "Success / Error / Cancel...", labelBuf, sizeof(labelBuf))) {
|
||||
m_functionExitNode->SetExitLabel(labelBuf);
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Return values list
|
||||
ImGui::Text(ICON_MDI_ARROW_LEFT_BOLD " Return Values:");
|
||||
ImGui::Spacing();
|
||||
|
||||
const auto& returnValues = m_functionExitNode->GetReturnValues();
|
||||
|
||||
// Table for return values
|
||||
if (ImGui::BeginTable("return_values_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 150.0f);
|
||||
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
||||
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 80.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Display existing return values
|
||||
for (size_t i = 0; i < returnValues.size(); ++i) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
// Name
|
||||
ImGui::TableNextColumn();
|
||||
char nameBuf[128];
|
||||
strncpy(nameBuf, returnValues[i].name.c_str(), sizeof(nameBuf) - 1);
|
||||
nameBuf[sizeof(nameBuf) - 1] = '\0';
|
||||
|
||||
if (ImGui::InputText("##name", nameBuf, sizeof(nameBuf))) {
|
||||
m_functionExitNode->UpdateReturnValue(i, nameBuf, returnValues[i].type);
|
||||
}
|
||||
|
||||
// Type
|
||||
ImGui::TableNextColumn();
|
||||
const char* types[] = {"int", "float", "string", "bool"};
|
||||
int currentType = 0;
|
||||
for (int t = 0; t < 4; ++t) {
|
||||
if (returnValues[i].type == types[t]) {
|
||||
currentType = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Combo("##type", ¤tType, types, 4)) {
|
||||
m_functionExitNode->UpdateReturnValue(i, returnValues[i].name, types[currentType]);
|
||||
}
|
||||
|
||||
// Actions
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MDI_DELETE "##delete")) {
|
||||
m_functionExitNode->RemoveReturnValue(i);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Add new return value
|
||||
if (ImGui::Button(ICON_MDI_PLUS " Add Return Value")) {
|
||||
m_functionExitNode->AddReturnValue("result", "int");
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Info
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), ICON_MDI_INFORMATION_OUTLINE " Each return value creates an input port on this node.");
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "The exit label will be the name of the execution output port on CallFunctionNode.");
|
||||
}
|
||||
|
||||
virtual void Initialize() override {
|
||||
BaseNodeWidget::Initialize();
|
||||
}
|
||||
|
||||
private:
|
||||
IStoryManager &m_manager;
|
||||
std::shared_ptr<FunctionExitNode> m_functionExitNode;
|
||||
};
|
||||
Loading…
Reference in a new issue