Add loops nodes !
Some checks are pending
Build-StoryEditor / build_win32 (push) Waiting to run
Build-StoryEditor / build_linux (push) Waiting to run

This commit is contained in:
Anthony Rabine 2025-10-20 23:34:17 +02:00
parent c29d099ec9
commit dc11cb33dd
23 changed files with 1238 additions and 39 deletions

View file

@ -324,6 +324,10 @@ private:
#include "wait_delay_node.h" #include "wait_delay_node.h"
#include "play_media_node.h" #include "play_media_node.h"
#include "send_signal_node.h" #include "send_signal_node.h"
#include "for_loop_node.h"
#include "while_loop_node.h"
#include "break_node.h"
#include "continue_node.h"
// =================================================================== // ===================================================================
// SECTION 2: CLASSE TACGenerator - MODIFICATIONS // SECTION 2: CLASSE TACGenerator - MODIFICATIONS
@ -376,6 +380,16 @@ private:
TACProgram m_program; TACProgram m_program;
int m_tempCounter; int m_tempCounter;
int m_labelCounter; int m_labelCounter;
// Structure pour le contexte de boucle
struct LoopContext {
std::string startLabel; // Label du début de la boucle
std::string endLabel; // Label de fin de la boucle
std::string continueLabel; // Label pour continue (peut différer du start)
};
// Pile de contextes de boucles pour gérer Break/Continue et boucles imbriquées
std::vector<LoopContext> m_loopStack;
// Map pour garder en mémoire où sont stockés les résultats des nœuds // Map pour garder en mémoire où sont stockés les résultats des nœuds
std::map<std::string, std::shared_ptr<TACOperand>> m_nodeResults; std::map<std::string, std::shared_ptr<TACOperand>> m_nodeResults;
@ -383,13 +397,38 @@ private:
// Set pour éviter de visiter deux fois le même nœud // Set pour éviter de visiter deux fois le même nœud
std::set<std::string> m_visitedNodes; std::set<std::string> m_visitedNodes;
// NOUVEAU: Variables disponibles pour résolution // Variables disponibles pour résolution
std::vector<std::shared_ptr<Variable>> m_variables; std::vector<std::shared_ptr<Variable>> m_variables;
// =================================================================== // ===================================================================
// HELPERS // HELPERS
// =================================================================== // ===================================================================
void GenerateExecutionChain(std::shared_ptr<ASTNode> startNode) {
if (!startNode) return;
// Générer le nœud actuel
GenerateNode(startNode);
// Suivre la chaîne d'exécution (enfants d'exécution)
for (const auto& child : startNode->children) {
GenerateExecutionChain(child);
}
}
// Vérifie si on est dans une boucle
bool IsInLoop() const {
return !m_loopStack.empty();
}
// Obtient le contexte de la boucle courante
const LoopContext& GetCurrentLoop() const {
if (m_loopStack.empty()) {
throw std::runtime_error("Not in a loop context");
}
return m_loopStack.back();
}
// Génère un nouveau temporaire // Génère un nouveau temporaire
std::shared_ptr<TACOperand> NewTemp() { std::shared_ptr<TACOperand> NewTemp() {
return std::make_shared<TACOperand>( return std::make_shared<TACOperand>(
@ -407,7 +446,7 @@ private:
} }
// =================================================================== // ===================================================================
// NOUVEAU: HELPERS POUR RÉSOLUTION DE VARIABLES // HELPERS POUR RÉSOLUTION DE VARIABLES
// =================================================================== // ===================================================================
std::shared_ptr<Variable> ResolveVariableByUuid(const std::string& uuid) const { std::shared_ptr<Variable> ResolveVariableByUuid(const std::string& uuid) const {
@ -491,6 +530,15 @@ private:
std::cout << " -> Type: CallFunctionNode\n"; std::cout << " -> Type: CallFunctionNode\n";
GenerateCallFunctionNode(node); GenerateCallFunctionNode(node);
} }
else if (node->IsType<FunctionEntryNode>()) {
std::cout << " -> Type: FunctionEntryNode (generating children)\n";
// Entry point, générer les enfants
for (size_t i = 0; i < node->children.size(); i++) {
std::cout << " Processing child [" << i << "]\n";
GenerateNode(node->children[i]);
}
}
// =================================================================== // ===================================================================
// NŒUDS SYSCALL // NŒUDS SYSCALL
// =================================================================== // ===================================================================
@ -510,14 +558,31 @@ private:
std::cout << " -> Type: SendSignalNode\n"; std::cout << " -> Type: SendSignalNode\n";
GenerateSendSignalNode(node); GenerateSendSignalNode(node);
} }
else if (node->IsType<FunctionEntryNode>()) {
std::cout << " -> Type: FunctionEntryNode (generating children)\n"; // ===================================================================
// Entry point, générer les enfants // OUR LES BOUCLES
for (size_t i = 0; i < node->children.size(); i++) { // ===================================================================
std::cout << " Processing child [" << i << "]\n"; else if (node->IsType<ForLoopNode>()) {
GenerateNode(node->children[i]); std::cout << " -> Type: ForLoopNode\n";
} GenerateForLoopNode(node);
return nullptr;
} }
else if (node->IsType<WhileLoopNode>()) {
std::cout << " -> Type: WhileLoopNode\n";
GenerateWhileLoopNode(node);
return nullptr;
}
else if (node->IsType<BreakNode>()) {
std::cout << " -> Type: BreakNode\n";
GenerateBreakNode(node);
return nullptr;
}
else if (node->IsType<ContinueNode>()) {
std::cout << " -> Type: ContinueNode\n";
GenerateContinueNode(node);
return nullptr;
}
// Mémoriser le résultat // Mémoriser le résultat
if (result) { if (result) {
@ -532,6 +597,291 @@ private:
// GÉNÉRATION PAR TYPE DE NŒUD - EXISTANTS (pas de changement) // GÉNÉRATION PAR TYPE DE NŒUD - EXISTANTS (pas de changement)
// =================================================================== // ===================================================================
// ===================================================================
// GÉNÉRATION DU FOR LOOP
// ===================================================================
void GenerateForLoopNode(std::shared_ptr<ASTNode> node) {
auto* forLoopNode = node->GetAs<ForLoopNode>();
if (!forLoopNode) {
throw std::runtime_error("ForLoopNode cast failed");
}
std::cout << " Generating ForLoopNode\n";
// Créer les labels pour la boucle
auto loopStart = NewLabel("for_start");
auto loopBody = NewLabel("for_body");
auto loopContinue = NewLabel("for_continue");
auto loopEnd = NewLabel("for_end");
// ⭐ IMPORTANT : Empiler le contexte AVANT de générer le corps
m_loopStack.push_back({
loopStart->GetValue(),
loopEnd->GetValue(),
loopContinue->GetValue()
});
// === 1. INITIALISATION : index = start ===
std::shared_ptr<TACOperand> startOperand;
auto startInput = node->GetDataInput(1);
if (startInput) {
startOperand = GenerateNode(startInput);
} else {
startOperand = std::make_shared<TACOperand>(
TACOperand::Type::CONSTANT,
std::to_string(forLoopNode->GetStartIndex())
);
}
// Créer la variable temporaire pour l'index
auto indexTemp = NewTemp();
auto copyInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::COPY,
indexTemp,
startOperand
);
m_program.AddInstruction(copyInstr);
// === 2. LABEL loop_start ===
auto labelStart = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
loopStart
);
m_program.AddInstruction(labelStart);
// === 3. CONDITION : index < end ===
std::shared_ptr<TACOperand> endOperand;
auto endInput = node->GetDataInput(2);
if (endInput) {
endOperand = GenerateNode(endInput);
} else {
endOperand = std::make_shared<TACOperand>(
TACOperand::Type::CONSTANT,
std::to_string(forLoopNode->GetEndIndex())
);
}
// Test: index < end
auto condTemp = NewTemp();
auto ltInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LT,
condTemp,
indexTemp,
endOperand
);
m_program.AddInstruction(ltInstr);
// if (!condition) goto loop_end
auto ifFalse = std::make_shared<TACInstruction>(
TACInstruction::OpCode::IF_FALSE,
loopEnd,
condTemp
);
m_program.AddInstruction(ifFalse);
// === 4. LABEL loop_body ===
auto labelBody = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
loopBody
);
m_program.AddInstruction(labelBody);
// === 5. CORPS DE LA BOUCLE - CORRECTION ===
// Générer toute la chaîne d'exécution (pas juste le premier enfant)
if (node->GetChild(0)) {
std::cout << " Generating loop body execution chain...\n";
GenerateExecutionChain(node->GetChild(0));
}
// === 6. LABEL loop_continue (pour Continue) ===
auto labelContinue = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
loopContinue
);
m_program.AddInstruction(labelContinue);
// === 7. INCRÉMENTATION : index = index + step ===
std::shared_ptr<TACOperand> stepOperand;
auto stepInput = node->GetDataInput(3);
if (stepInput) {
stepOperand = GenerateNode(stepInput);
} else {
stepOperand = std::make_shared<TACOperand>(
TACOperand::Type::CONSTANT,
std::to_string(forLoopNode->GetStep())
);
}
auto addInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::ADD,
indexTemp,
indexTemp,
stepOperand
);
m_program.AddInstruction(addInstr);
// === 8. RETOUR AU DÉBUT ===
auto gotoStart = std::make_shared<TACInstruction>(
TACInstruction::OpCode::GOTO,
loopStart
);
m_program.AddInstruction(gotoStart);
// === 9. LABEL loop_end ===
auto labelEnd = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
loopEnd
);
m_program.AddInstruction(labelEnd);
// ⭐ IMPORTANT : Dépiler APRÈS avoir généré tout le corps
m_loopStack.pop_back();
// === 10. COMPLETED (port de sortie 1) ===
if (node->GetChild(1)) {
GenerateNode(node->GetChild(1));
}
}
// ===================================================================
// GÉNÉRATION DU WHILE LOOP
// ===================================================================
void GenerateWhileLoopNode(std::shared_ptr<ASTNode> node) {
auto* whileLoopNode = node->GetAs<WhileLoopNode>();
if (!whileLoopNode) {
throw std::runtime_error("WhileLoopNode cast failed");
}
std::cout << " Generating WhileLoopNode\n";
// Créer les labels
auto loopStart = NewLabel("while_start");
auto loopBody = NewLabel("while_body");
auto loopEnd = NewLabel("while_end");
// ⭐ Empiler le contexte AVANT le corps
m_loopStack.push_back({
loopStart->GetValue(),
loopEnd->GetValue(),
loopStart->GetValue()
});
// === 1. LABEL loop_start ===
auto labelStart = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
loopStart
);
m_program.AddInstruction(labelStart);
// === 2. ÉVALUER LA CONDITION ===
auto conditionInput = node->GetDataInput(1);
if (!conditionInput) {
throw std::runtime_error("WhileLoopNode missing condition input on port 1");
}
auto conditionOperand = GenerateNode(conditionInput);
// === 3. TEST : if (!condition) goto loop_end ===
auto ifFalse = std::make_shared<TACInstruction>(
TACInstruction::OpCode::IF_FALSE,
loopEnd,
conditionOperand
);
m_program.AddInstruction(ifFalse);
// === 4. LABEL loop_body ===
auto labelBody = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
loopBody
);
m_program.AddInstruction(labelBody);
// === 5. CORPS DE LA BOUCLE - CORRECTION ===
if (node->GetChild(0)) {
GenerateExecutionChain(node->GetChild(0));
}
// === 6. RETOUR AU DÉBUT ===
auto gotoStart = std::make_shared<TACInstruction>(
TACInstruction::OpCode::GOTO,
loopStart
);
m_program.AddInstruction(gotoStart);
// === 7. LABEL loop_end ===
auto labelEnd = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
loopEnd
);
m_program.AddInstruction(labelEnd);
// ⭐ Dépiler APRÈS le corps
m_loopStack.pop_back();
// === 8. COMPLETED (port de sortie 1) ===
if (node->GetChild(1)) {
GenerateNode(node->GetChild(1));
}
}
// ===================================================================
// GÉNÉRATION DU BREAK
// ===================================================================
void GenerateBreakNode(std::shared_ptr<ASTNode> node) {
auto* breakNode = node->GetAs<BreakNode>();
if (!breakNode) {
throw std::runtime_error("BreakNode cast failed");
}
if (!IsInLoop()) {
throw std::runtime_error("Break statement outside of loop");
}
std::cout << " Generating BreakNode (jump to loop end)\n";
// Sauter vers la fin de la boucle courante
const auto& loopContext = GetCurrentLoop();
auto endLabel = std::make_shared<TACOperand>(
TACOperand::Type::LABEL,
loopContext.endLabel
);
auto gotoEnd = std::make_shared<TACInstruction>(
TACInstruction::OpCode::GOTO,
endLabel
);
m_program.AddInstruction(gotoEnd);
}
// ===================================================================
// GÉNÉRATION DU CONTINUE
// ===================================================================
void GenerateContinueNode(std::shared_ptr<ASTNode> node) {
auto* continueNode = node->GetAs<ContinueNode>();
if (!continueNode) {
throw std::runtime_error("ContinueNode cast failed");
}
if (!IsInLoop()) {
throw std::runtime_error("Continue statement outside of loop");
}
std::cout << " Generating ContinueNode (jump to loop continue point)\n";
// Sauter vers le point de continue de la boucle courante
const auto& loopContext = GetCurrentLoop();
auto continueLabel = std::make_shared<TACOperand>(
TACOperand::Type::LABEL,
loopContext.continueLabel
);
auto gotoContinue = std::make_shared<TACInstruction>(
TACInstruction::OpCode::GOTO,
continueLabel
);
m_program.AddInstruction(gotoContinue);
}
std::shared_ptr<TACOperand> GenerateVariableNode(std::shared_ptr<ASTNode> node) { std::shared_ptr<TACOperand> GenerateVariableNode(std::shared_ptr<ASTNode> node) {
auto* varNode = node->GetAs<VariableNode>(); auto* varNode = node->GetAs<VariableNode>();
if (!varNode) { if (!varNode) {

View file

@ -0,0 +1,24 @@
#include "break_node.h"
BreakNode::BreakNode(const std::string &type)
: BaseNode(type, "Break")
{
SetBehavior(Behavior::BEHAVIOR_EXECUTION);
// Port d'entrée 0: Exécution (EXECUTION_PORT)
AddInputPort(Port::Type::EXECUTION_PORT, ">", true);
// Pas de port de sortie : le flux est redirigé vers la fin de la boucle
// par le générateur TAC
// Initialiser les données internes
nlohmann::json j;
j["node_type"] = "break";
SetInternalData(j);
}
void BreakNode::Initialize()
{
// Charger les données sauvegardées si nécessaire
nlohmann::json j = GetInternalData();
}

View file

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "base_node.h"
class BreakNode : public BaseNode
{
public:
BreakNode(const std::string &type = "break-node");
void Initialize() override;
};

View file

@ -0,0 +1,24 @@
#include "continue_node.h"
ContinueNode::ContinueNode(const std::string &type)
: BaseNode(type, "Continue")
{
SetBehavior(Behavior::BEHAVIOR_EXECUTION);
// Port d'entrée 0: Exécution (EXECUTION_PORT)
AddInputPort(Port::Type::EXECUTION_PORT, ">", true);
// Pas de port de sortie : le flux est redirigé vers le début de la boucle
// par le générateur TAC
// Initialiser les données internes
nlohmann::json j;
j["node_type"] = "continue";
SetInternalData(j);
}
void ContinueNode::Initialize()
{
// Charger les données sauvegardées si nécessaire
nlohmann::json j = GetInternalData();
}

View file

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "base_node.h"
class ContinueNode : public BaseNode
{
public:
ContinueNode(const std::string &type = "continue-node");
void Initialize() override;
};

View file

@ -0,0 +1,46 @@
#include "for_loop_node.h"
ForLoopNode::ForLoopNode(const std::string &type)
: BaseNode(type, "For Loop")
{
SetBehavior(Behavior::BEHAVIOR_EXECUTION);
// Port d'entrée 0: Exécution (EXECUTION_PORT)
AddInputPort(Port::Type::EXECUTION_PORT, ">", true);
// Ports d'entrée pour les données
AddInputPort(Port::Type::DATA_PORT, "start"); // Port 1
AddInputPort(Port::Type::DATA_PORT, "end"); // Port 2
AddInputPort(Port::Type::DATA_PORT, "step"); // Port 3
// Ports de sortie pour l'exécution
AddOutputPort(Port::Type::EXECUTION_PORT, "body", true); // Port 0
AddOutputPort(Port::Type::EXECUTION_PORT, "done", true); // Port 1
// Port de sortie pour l'index courant
AddOutputPort(Port::Type::DATA_PORT, "index"); // Port 2
// Initialiser les données internes
nlohmann::json j;
j["start_index"] = m_startIndex;
j["end_index"] = m_endIndex;
j["step"] = m_step;
j["node_type"] = "for_loop";
SetInternalData(j);
}
void ForLoopNode::Initialize()
{
// Charger les données sauvegardées
nlohmann::json j = GetInternalData();
if (j.contains("start_index")) {
m_startIndex = j["start_index"].get<int>();
}
if (j.contains("end_index")) {
m_endIndex = j["end_index"].get<int>();
}
if (j.contains("step")) {
m_step = j["step"].get<int>();
}
}

View file

@ -0,0 +1,42 @@
#pragma once
#include <string>
#include "base_node.h"
class ForLoopNode : public BaseNode
{
public:
ForLoopNode(const std::string &type = "for-loop-node");
void Initialize() override;
// Propriétés configurables (valeurs par défaut si non connectées)
int GetStartIndex() const { return m_startIndex; }
void SetStartIndex(int value) {
m_startIndex = value;
nlohmann::json j = GetInternalData();
j["start_index"] = m_startIndex;
SetInternalData(j);
}
int GetEndIndex() const { return m_endIndex; }
void SetEndIndex(int value) {
m_endIndex = value;
nlohmann::json j = GetInternalData();
j["end_index"] = m_endIndex;
SetInternalData(j);
}
int GetStep() const { return m_step; }
void SetStep(int value) {
m_step = value;
nlohmann::json j = GetInternalData();
j["step"] = m_step;
SetInternalData(j);
}
private:
int m_startIndex{0};
int m_endIndex{10};
int m_step{1};
};

View file

@ -0,0 +1,28 @@
#include "while_loop_node.h"
WhileLoopNode::WhileLoopNode(const std::string &type)
: BaseNode(type, "While Loop")
{
SetBehavior(Behavior::BEHAVIOR_EXECUTION);
// Port d'entrée 0: Exécution (EXECUTION_PORT)
AddInputPort(Port::Type::EXECUTION_PORT, ">", true);
// Port d'entrée 1: Condition (DATA_PORT)
AddInputPort(Port::Type::DATA_PORT, "condition");
// Ports de sortie pour l'exécution
AddOutputPort(Port::Type::EXECUTION_PORT, "body", true); // Port 0
AddOutputPort(Port::Type::EXECUTION_PORT, "done", true); // Port 1
// Initialiser les données internes
nlohmann::json j;
j["node_type"] = "while_loop";
SetInternalData(j);
}
void WhileLoopNode::Initialize()
{
// Charger les données sauvegardées si nécessaire
nlohmann::json j = GetInternalData();
}

View file

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "base_node.h"
class WhileLoopNode : public BaseNode
{
public:
WhileLoopNode(const std::string &type = "while-loop-node");
void Initialize() override;
};

View file

@ -20,6 +20,10 @@
#include "wait_event_node.h" #include "wait_event_node.h"
#include "play_media_node.h" #include "play_media_node.h"
#include "send_signal_node.h" #include "send_signal_node.h"
#include "for_loop_node.h"
#include "while_loop_node.h"
#include "break_node.h"
#include "continue_node.h"
static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec"; static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec";
static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb"; static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb";
@ -31,6 +35,10 @@ static const std::string WaitEventNodeUuid = "02225cff-4975-400e-8130-41524d8af7
static const std::string WaitDelayNodeUuid = "02455ef0-4975-4546-94de-720cae6baae3"; static const std::string WaitDelayNodeUuid = "02455ef0-4975-4546-94de-720cae6baae3";
static const std::string PlayMediaNodeUuid = "0285e90a-2eb7-4605-baa9-b3712a14dff8"; static const std::string PlayMediaNodeUuid = "0285e90a-2eb7-4605-baa9-b3712a14dff8";
static const std::string SendSignalNodeUuid = "02c2ce4b-8783-47cb-a55f-90056bebd64b"; 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";
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type); typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type);
@ -54,6 +62,10 @@ public:
registerNode<WaitDelayNode>(WaitDelayNodeUuid, std::make_shared<StoryPrimitive>("Wait Delay")); registerNode<WaitDelayNode>(WaitDelayNodeUuid, std::make_shared<StoryPrimitive>("Wait Delay"));
registerNode<PlayMediaNode>(PlayMediaNodeUuid, std::make_shared<StoryPrimitive>("Play Media")); registerNode<PlayMediaNode>(PlayMediaNodeUuid, std::make_shared<StoryPrimitive>("Play Media"));
registerNode<SendSignalNode>(SendSignalNodeUuid, std::make_shared<StoryPrimitive>("Send Signal")); registerNode<SendSignalNode>(SendSignalNodeUuid, std::make_shared<StoryPrimitive>("Send Signal"));
registerNode<ForLoopNode>(ForLoopNodeUuid, std::make_shared<StoryPrimitive>("For Loop"));
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"));
} }
~NodesFactory() = default; ~NodesFactory() = default;

View file

@ -31,6 +31,7 @@ add_executable(${PROJECT_NAME}
test_ast.cpp test_ast.cpp
test_print_node.cpp test_print_node.cpp
test_branch.cpp test_branch.cpp
test_loops.cpp
../story-manager/src/nodes/base_node.cpp ../story-manager/src/nodes/base_node.cpp
../story-manager/src/nodes/branch_node.cpp ../story-manager/src/nodes/branch_node.cpp
@ -41,6 +42,10 @@ add_executable(${PROJECT_NAME}
../story-manager/src/nodes/wait_delay_node.cpp ../story-manager/src/nodes/wait_delay_node.cpp
../story-manager/src/nodes/play_media_node.cpp ../story-manager/src/nodes/play_media_node.cpp
../story-manager/src/nodes/send_signal_node.cpp ../story-manager/src/nodes/send_signal_node.cpp
../story-manager/src/nodes/for_loop_node.cpp
../story-manager/src/nodes/while_loop_node.cpp
../story-manager/src/nodes/break_node.cpp
../story-manager/src/nodes/continue_node.cpp
../chip32/chip32_assembler.cpp ../chip32/chip32_assembler.cpp

417
core/tests/test_loops.cpp Normal file
View file

@ -0,0 +1,417 @@
// ===================================================================
// core/tests/test_loops.cpp
// Tests unitaires pour ForLoop, WhileLoop, Break et Continue
// ===================================================================
#include <catch2/catch_test_macros.hpp>
#include "for_loop_node.h"
#include "while_loop_node.h"
#include "break_node.h"
#include "continue_node.h"
#include "variable_node.h"
#include "function_entry_node.h"
#include "operator_node.h"
#include "print_node.h"
#include "connection.h"
#include "ast_builder.h"
#include "assembly_generator_chip32_tac.h"
#include "chip32_machine.h"
#include "variable.h"
// ===================================================================
// TEST 1 : ForLoopNode simple (0 à 5)
// ===================================================================
TEST_CASE("Simple ForLoop counting 0 to 5", "[loop][for]") {
std::cout << "\n=== Test: Simple ForLoop 0 to 5 ===\n";
std::vector<std::shared_ptr<Variable>> variables;
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto forLoop = std::make_shared<ForLoopNode>("for-loop");
forLoop->SetStartIndex(0);
forLoop->SetEndIndex(5);
forLoop->SetStep(1);
forLoop->Initialize();
auto printNode = std::make_shared<PrintNode>("print-loop");
printNode->SetText("Iteration");
printNode->Initialize();
auto printEnd = std::make_shared<PrintNode>("print-end");
printEnd->SetText("Loop completed!");
printEnd->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry,
forLoop,
printNode,
printEnd
};
// Connections
std::vector<std::shared_ptr<Connection>> connections;
// Execution: Entry → ForLoop
auto execConn1 = std::make_shared<Connection>();
execConn1->outNodeId = functionEntry->GetId();
execConn1->outPortIndex = 0;
execConn1->inNodeId = forLoop->GetId();
execConn1->inPortIndex = 0;
execConn1->type = Connection::EXECUTION_LINK;
connections.push_back(execConn1);
// Execution: ForLoop.body → Print
auto execConn2 = std::make_shared<Connection>();
execConn2->outNodeId = forLoop->GetId();
execConn2->outPortIndex = 0; // Loop Body
execConn2->inNodeId = printNode->GetId();
execConn2->inPortIndex = 0;
execConn2->type = Connection::EXECUTION_LINK;
connections.push_back(execConn2);
// Execution: ForLoop.completed → PrintEnd
auto execConn3 = std::make_shared<Connection>();
execConn3->outNodeId = forLoop->GetId();
execConn3->outPortIndex = 1; // Completed
execConn3->inNodeId = printEnd->GetId();
execConn3->inPortIndex = 0;
execConn3->type = Connection::EXECUTION_LINK;
connections.push_back(execConn3);
// Build AST
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
// Generate Assembly
AssemblyGenerator::GeneratorContext context(
variables,
"2025-01-20 10:00:00",
"test-for-loop",
true,
true,
1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
std::cout << "\n--- Generated Assembly ---\n";
std::cout << assembly << std::endl;
// Verify
REQUIRE(assembly.find("for_start") != std::string::npos);
REQUIRE(assembly.find("for_end") != std::string::npos);
// Execute
std::cout << "\n--- Execution Output ---\n";
std::cout << "Expected: 5 iterations + completion message\n";
Chip32::Machine machine;
machine.QuickExecute(assembly);
std::cout << "\n✓ Test passed\n";
}
// ===================================================================
// TEST 2 : WhileLoop avec condition variable
// ===================================================================
TEST_CASE("WhileLoop with variable condition", "[loop][while]") {
std::cout << "\n=== Test: WhileLoop with condition ===\n";
std::vector<std::shared_ptr<Variable>> variables;
auto varCounter = std::make_shared<Variable>("counter");
varCounter->SetIntegerValue(0);
variables.push_back(varCounter);
auto varLimit = std::make_shared<Variable>("limit");
varLimit->SetIntegerValue(3);
variables.push_back(varLimit);
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto varNodeCounter = std::make_shared<VariableNode>("var-counter");
varNodeCounter->SetVariable(varCounter);
auto varNodeLimit = std::make_shared<VariableNode>("var-limit");
varNodeLimit->SetVariable(varLimit);
// Condition: counter < limit
auto compareNode = std::make_shared<OperatorNode>("compare-lt");
compareNode->SetOperationType(OperatorNode::OperationType::LESS_THAN);
compareNode->Initialize();
auto whileLoop = std::make_shared<WhileLoopNode>("while-loop");
whileLoop->Initialize();
auto printNode = std::make_shared<PrintNode>("print-loop");
printNode->SetText("Counter value");
printNode->Initialize();
auto printEnd = std::make_shared<PrintNode>("print-end");
printEnd->SetText("While loop completed!");
printEnd->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry,
varNodeCounter,
varNodeLimit,
compareNode,
whileLoop,
printNode,
printEnd
};
// Connections
std::vector<std::shared_ptr<Connection>> connections;
// Execution: Entry → While
auto execConn1 = std::make_shared<Connection>();
execConn1->outNodeId = functionEntry->GetId();
execConn1->outPortIndex = 0;
execConn1->inNodeId = whileLoop->GetId();
execConn1->inPortIndex = 0;
execConn1->type = Connection::EXECUTION_LINK;
connections.push_back(execConn1);
// Data: counter → compare.in1
auto dataConn1 = std::make_shared<Connection>();
dataConn1->outNodeId = varNodeCounter->GetId();
dataConn1->outPortIndex = 0;
dataConn1->inNodeId = compareNode->GetId();
dataConn1->inPortIndex = 0;
dataConn1->type = Connection::DATA_LINK;
connections.push_back(dataConn1);
// Data: limit → compare.in2
auto dataConn2 = std::make_shared<Connection>();
dataConn2->outNodeId = varNodeLimit->GetId();
dataConn2->outPortIndex = 0;
dataConn2->inNodeId = compareNode->GetId();
dataConn2->inPortIndex = 1;
dataConn2->type = Connection::DATA_LINK;
connections.push_back(dataConn2);
// Data: compare.out → while.condition
auto dataConn3 = std::make_shared<Connection>();
dataConn3->outNodeId = compareNode->GetId();
dataConn3->outPortIndex = 0;
dataConn3->inNodeId = whileLoop->GetId();
dataConn3->inPortIndex = 1;
dataConn3->type = Connection::DATA_LINK;
connections.push_back(dataConn3);
// Execution: While.body → Print
auto execConn2 = std::make_shared<Connection>();
execConn2->outNodeId = whileLoop->GetId();
execConn2->outPortIndex = 0;
execConn2->inNodeId = printNode->GetId();
execConn2->inPortIndex = 0;
execConn2->type = Connection::EXECUTION_LINK;
connections.push_back(execConn2);
// Execution: While.completed → PrintEnd
auto execConn3 = std::make_shared<Connection>();
execConn3->outNodeId = whileLoop->GetId();
execConn3->outPortIndex = 1;
execConn3->inNodeId = printEnd->GetId();
execConn3->inPortIndex = 0;
execConn3->type = Connection::EXECUTION_LINK;
connections.push_back(execConn3);
// Build and test
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-20", "test-while-loop", true, true, 1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
std::cout << "\n--- Generated Assembly ---\n";
std::cout << assembly << std::endl;
REQUIRE(assembly.find("while_start") != std::string::npos);
REQUIRE(assembly.find("while_end") != std::string::npos);
std::cout << "\n✓ Test passed\n";
}
// ===================================================================
// TEST 3 : ForLoop avec Break
// ===================================================================
TEST_CASE("ForLoop with Break at first iteration", "[loop][for][break]") {
std::cout << "\n=== Test: ForLoop with Break ===\n";
std::vector<std::shared_ptr<Variable>> variables;
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto forLoop = std::make_shared<ForLoopNode>("for-loop");
forLoop->SetStartIndex(0);
forLoop->SetEndIndex(10);
forLoop->SetStep(1);
forLoop->Initialize();
auto printNode = std::make_shared<PrintNode>("print-iter");
printNode->SetText("Iteration");
printNode->Initialize();
auto breakNode = std::make_shared<BreakNode>("break-node");
breakNode->Initialize();
auto printEnd = std::make_shared<PrintNode>("print-end");
printEnd->SetText("Broke out of loop");
printEnd->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry,
forLoop,
printNode,
breakNode,
printEnd
};
// Connections
std::vector<std::shared_ptr<Connection>> connections;
// Entry → ForLoop
auto execConn1 = std::make_shared<Connection>();
execConn1->outNodeId = functionEntry->GetId();
execConn1->outPortIndex = 0;
execConn1->inNodeId = forLoop->GetId();
execConn1->inPortIndex = 0;
execConn1->type = Connection::EXECUTION_LINK;
connections.push_back(execConn1);
// ForLoop.body → Print
auto execConn2 = std::make_shared<Connection>();
execConn2->outNodeId = forLoop->GetId();
execConn2->outPortIndex = 0;
execConn2->inNodeId = printNode->GetId();
execConn2->inPortIndex = 0;
execConn2->type = Connection::EXECUTION_LINK;
connections.push_back(execConn2);
// Print → Break
auto execConn3 = std::make_shared<Connection>();
execConn3->outNodeId = printNode->GetId();
execConn3->outPortIndex = 0;
execConn3->inNodeId = breakNode->GetId();
execConn3->inPortIndex = 0;
execConn3->type = Connection::EXECUTION_LINK;
connections.push_back(execConn3);
// ForLoop.completed → PrintEnd
auto execConn4 = std::make_shared<Connection>();
execConn4->outNodeId = forLoop->GetId();
execConn4->outPortIndex = 1;
execConn4->inNodeId = printEnd->GetId();
execConn4->inPortIndex = 0;
execConn4->type = Connection::EXECUTION_LINK;
connections.push_back(execConn4);
// Build and generate
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-20", "test-break", true, true, 1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
std::cout << "\n--- Generated Assembly ---\n";
std::cout << assembly << std::endl;
REQUIRE(assembly.find("for_end") != std::string::npos);
REQUIRE(assembly.find("jump") != std::string::npos);
std::cout << "\n✓ Test passed\n";
}
// ===================================================================
// TEST 4 : ForLoop avec Continue
// ===================================================================
TEST_CASE("ForLoop with Continue", "[loop][for][continue]") {
std::cout << "\n=== Test: ForLoop with Continue ===\n";
std::vector<std::shared_ptr<Variable>> variables;
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto forLoop = std::make_shared<ForLoopNode>("for-loop");
forLoop->SetStartIndex(0);
forLoop->SetEndIndex(5);
forLoop->Initialize();
auto continueNode = std::make_shared<ContinueNode>("continue-node");
continueNode->Initialize();
auto printNode = std::make_shared<PrintNode>("print-odd");
printNode->SetText("Should not print");
printNode->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry,
forLoop,
continueNode,
printNode
};
std::vector<std::shared_ptr<Connection>> connections;
// Entry → ForLoop
auto execConn1 = std::make_shared<Connection>();
execConn1->outNodeId = functionEntry->GetId();
execConn1->outPortIndex = 0;
execConn1->inNodeId = forLoop->GetId();
execConn1->inPortIndex = 0;
execConn1->type = Connection::EXECUTION_LINK;
connections.push_back(execConn1);
// ForLoop.body → Continue
auto execConn2 = std::make_shared<Connection>();
execConn2->outNodeId = forLoop->GetId();
execConn2->outPortIndex = 0;
execConn2->inNodeId = continueNode->GetId();
execConn2->inPortIndex = 0;
execConn2->type = Connection::EXECUTION_LINK;
connections.push_back(execConn2);
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-20", "test-continue", true, true, 1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
std::cout << "\n--- Generated Assembly ---\n";
std::cout << assembly << std::endl;
REQUIRE(assembly.find("for_continue") != std::string::npos);
std::cout << "\n✓ Test passed\n";
}

View file

@ -152,6 +152,10 @@ set(SRCS
src/node_editor/wait_delay_node_widget.cpp src/node_editor/wait_delay_node_widget.cpp
src/node_editor/play_media_node_widget.cpp src/node_editor/play_media_node_widget.cpp
src/node_editor/send_signal_node_widget.cpp src/node_editor/send_signal_node_widget.cpp
src/node_editor/for_loop_node_widget.cpp
src/node_editor/while_loop_node_widget.cpp
src/node_editor/break_node_widget.cpp
src/node_editor/continue_node_widget.cpp
src/gui.cpp src/gui.cpp
src/media_converter.cpp src/media_converter.cpp
@ -193,6 +197,10 @@ set(SRCS
../core/story-manager/src/nodes/wait_delay_node.cpp ../core/story-manager/src/nodes/wait_delay_node.cpp
../core/story-manager/src/nodes/play_media_node.cpp ../core/story-manager/src/nodes/play_media_node.cpp
../core/story-manager/src/nodes/send_signal_node.cpp ../core/story-manager/src/nodes/send_signal_node.cpp
../core/story-manager/src/nodes/for_loop_node.cpp
../core/story-manager/src/nodes/while_loop_node.cpp
../core/story-manager/src/nodes/break_node.cpp
../core/story-manager/src/nodes/continue_node.cpp
../core/story-manager/src/nodes/connection.cpp ../core/story-manager/src/nodes/connection.cpp
../core/story-manager/src/story_db.cpp ../core/story-manager/src/story_db.cpp

View file

@ -1,6 +1,6 @@
[Window][WindowOverViewport_11111111] [Window][WindowOverViewport_11111111]
Pos=60,26 Pos=60,26
Size=1220,694 Size=1860,982
Collapsed=0 Collapsed=0
[Window][Debug##Default] [Window][Debug##Default]
@ -9,32 +9,32 @@ Size=400,400
Collapsed=0 Collapsed=0
[Window][Library Manager] [Window][Library Manager]
Pos=710,26 Pos=1060,26
Size=570,469 Size=860,653
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x00000002,0
[Window][Console] [Window][Console]
Pos=60,497 Pos=60,681
Size=527,223 Size=805,327
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
[Window][Emulator] [Window][Emulator]
Pos=710,26 Pos=1060,26
Size=570,469 Size=860,653
Collapsed=0 Collapsed=0
DockId=0x00000002,5 DockId=0x00000002,5
[Window][Code viewer] [Window][Code viewer]
Pos=710,26 Pos=1060,26
Size=570,469 Size=860,653
Collapsed=0 Collapsed=0
DockId=0x00000002,4 DockId=0x00000002,4
[Window][Resources] [Window][Resources]
Pos=710,26 Pos=1060,26
Size=570,469 Size=860,653
Collapsed=0 Collapsed=0
DockId=0x00000002,1 DockId=0x00000002,1
@ -50,36 +50,36 @@ Size=150,42
Collapsed=0 Collapsed=0
[Window][Variables] [Window][Variables]
Pos=589,497 Pos=867,681
Size=691,223 Size=1053,327
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
[Window][CPU] [Window][CPU]
Pos=710,26 Pos=1060,26
Size=570,469 Size=860,653
Collapsed=0 Collapsed=0
DockId=0x00000002,2 DockId=0x00000002,2
[Window][RAM view] [Window][RAM view]
Pos=710,26 Pos=1060,26
Size=570,469 Size=860,653
Collapsed=0 Collapsed=0
DockId=0x00000002,3 DockId=0x00000002,3
[Window][Properties] [Window][Properties]
Pos=589,497 Pos=867,681
Size=691,223 Size=1053,327
Collapsed=0 Collapsed=0
DockId=0x00000005,1 DockId=0x00000005,1
[Window][ToolBar] [Window][ToolBar]
Pos=0,26 Pos=0,26
Size=60,694 Size=60,982
Collapsed=0 Collapsed=0
[Window][QuitConfirm] [Window][QuitConfirm]
Pos=508,312 Pos=828,456
Size=264,96 Size=264,96
Collapsed=0 Collapsed=0
@ -90,13 +90,13 @@ Collapsed=0
[Window][Module editor] [Window][Module editor]
Pos=60,26 Pos=60,26
Size=648,469 Size=998,653
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000001,0
[Window][Story editor] [Window][Story editor]
Pos=60,26 Pos=60,26
Size=648,469 Size=998,653
Collapsed=0 Collapsed=0
DockId=0x00000001,1 DockId=0x00000001,1
@ -106,8 +106,8 @@ Size=687,422
Collapsed=0 Collapsed=0
[Window][Error List] [Window][Error List]
Pos=60,497 Pos=60,681
Size=527,223 Size=805,327
Collapsed=0 Collapsed=0
DockId=0x00000004,1 DockId=0x00000004,1
@ -155,11 +155,11 @@ Column 2 Weight=1.0000
Column 3 Width=60 Column 3 Width=60
[Docking][Data] [Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1860,982 Split=Y
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,469 Split=X DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,365 Split=X
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=648,694 CentralNode=1 Selected=0x93ADCAAB DockNode ID=0x00000001 Parent=0x00000007 SizeRef=998,694 CentralNode=1 Selected=0x93ADCAAB
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=570,694 Selected=0x63869CAF DockNode ID=0x00000002 Parent=0x00000007 SizeRef=860,694 Selected=0x4B07C626
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,223 Split=X Selected=0xEA83D666 DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,327 Split=X Selected=0xEA83D666
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=621,192 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=0x8C72BEA8

View file

@ -38,6 +38,10 @@
#include "send_signal_node_widget.h" #include "send_signal_node_widget.h"
#include "wait_delay_node_widget.h" #include "wait_delay_node_widget.h"
#include "wait_event_node_widget.h" #include "wait_event_node_widget.h"
#include "for_loop_node_widget.h"
#include "while_loop_node_widget.h"
#include "break_node_widget.h"
#include "continue_node_widget.h"
MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController) MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController)
: m_logger(logger) : m_logger(logger)
@ -72,6 +76,10 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
m_widgetFactory.registerNode<WaitDelayNodeWidget>(WaitDelayNodeUuid); m_widgetFactory.registerNode<WaitDelayNodeWidget>(WaitDelayNodeUuid);
m_widgetFactory.registerNode<PlayMediaNodeWidget>(PlayMediaNodeUuid); m_widgetFactory.registerNode<PlayMediaNodeWidget>(PlayMediaNodeUuid);
m_widgetFactory.registerNode<SendSignalNodeWidget>(SendSignalNodeUuid); m_widgetFactory.registerNode<SendSignalNodeWidget>(SendSignalNodeUuid);
m_widgetFactory.registerNode<ForLoopNodeWidget>(ForLoopNodeUuid);
m_widgetFactory.registerNode<WhileLoopNodeWidget>(WhileLoopNodeUuid);
m_widgetFactory.registerNode<BreakNodeWidget>(BreakNodeUuid);
m_widgetFactory.registerNode<ContinueNodeWidget>(ContinueNodeUuid);
m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) { m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) {
OpenProject(event.GetUuid()); OpenProject(event.GetUuid());

View file

@ -0,0 +1,24 @@
#include "break_node_widget.h"
#include "imgui.h"
#include "IconsMaterialDesignIcons.h"
BreakNodeWidget::BreakNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base)
: BaseNodeWidget(manager, base)
{
m_breakNode = std::dynamic_pointer_cast<BreakNode>(base);
}
void BreakNodeWidget::Draw()
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f));
ImGui::Text(ICON_MDI_STOP " Break");
ImGui::PopStyleColor();
}
void BreakNodeWidget::DrawProperties(std::shared_ptr<IStoryProject> story)
{
ImGui::Text("Break Statement");
ImGui::Separator();
ImGui::TextWrapped("Exits the current loop immediately.");
ImGui::TextWrapped("Must be placed inside a For or While loop.");
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "base_node_widget.h"
#include "break_node.h"
class BreakNodeWidget : public BaseNodeWidget
{
public:
BreakNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base);
virtual ~BreakNodeWidget() = default;
void Draw() override;
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
private:
std::shared_ptr<BreakNode> m_breakNode;
};

View file

@ -0,0 +1,24 @@
#include "continue_node_widget.h"
#include "imgui.h"
#include "IconsMaterialDesignIcons.h"
ContinueNodeWidget::ContinueNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base)
: BaseNodeWidget(manager, base)
{
m_continueNode = std::dynamic_pointer_cast<ContinueNode>(base);
}
void ContinueNodeWidget::Draw()
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 1.0f, 0.4f, 1.0f));
ImGui::Text(ICON_MDI_SKIP_NEXT " Continue");
ImGui::PopStyleColor();
}
void ContinueNodeWidget::DrawProperties(std::shared_ptr<IStoryProject> story)
{
ImGui::Text("Continue Statement");
ImGui::Separator();
ImGui::TextWrapped("Skips to the next iteration of the current loop.");
ImGui::TextWrapped("Must be placed inside a For or While loop.");
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "base_node_widget.h"
#include "continue_node.h"
class ContinueNodeWidget : public BaseNodeWidget
{
public:
ContinueNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base);
virtual ~ContinueNodeWidget() = default;
void Draw() override;
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
private:
std::shared_ptr<ContinueNode> m_continueNode;
};

View file

@ -0,0 +1,51 @@
// story-editor/src/node_editor/for_loop_node_widget.cpp
#include "for_loop_node_widget.h"
#include "imgui.h"
#include "IconsMaterialDesignIcons.h"
ForLoopNodeWidget::ForLoopNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base)
: BaseNodeWidget(manager, base)
{
m_forLoopNode = std::dynamic_pointer_cast<ForLoopNode>(base);
}
void ForLoopNodeWidget::Draw()
{
// Affichage simple du nœud dans l'éditeur
ImGui::Text(ICON_MDI_SYNC " For Loop");
// Afficher les valeurs configurées (si non connectées)
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
ImGui::TextWrapped("Start: %d, End: %d, Step: %d",
m_forLoopNode->GetStartIndex(),
m_forLoopNode->GetEndIndex(),
m_forLoopNode->GetStep());
ImGui::PopStyleColor();
}
void ForLoopNodeWidget::DrawProperties(std::shared_ptr<IStoryProject> story)
{
ImGui::Text("For Loop Properties");
ImGui::Separator();
// Configuration des valeurs par défaut
int start = m_forLoopNode->GetStartIndex();
if (ImGui::InputInt("Start Index", &start)) {
m_forLoopNode->SetStartIndex(start);
}
int end = m_forLoopNode->GetEndIndex();
if (ImGui::InputInt("End Index", &end)) {
m_forLoopNode->SetEndIndex(end);
}
int step = m_forLoopNode->GetStep();
if (ImGui::InputInt("Step", &step)) {
if (step != 0) { // Éviter division par zéro
m_forLoopNode->SetStep(step);
}
}
ImGui::Separator();
ImGui::TextWrapped("Note: These values are used only if the corresponding input ports are not connected.");
}

View file

@ -0,0 +1,18 @@
// story-editor/src/node_editor/for_loop_node_widget.h
#pragma once
#include "base_node_widget.h"
#include "for_loop_node.h"
class ForLoopNodeWidget : public BaseNodeWidget
{
public:
ForLoopNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base);
virtual ~ForLoopNodeWidget() = default;
void Draw() override;
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
private:
std::shared_ptr<ForLoopNode> m_forLoopNode;
};

View file

@ -0,0 +1,30 @@
// story-editor/src/node_editor/while_loop_node_widget.cpp
#include "while_loop_node_widget.h"
#include "imgui.h"
#include "IconsMaterialDesignIcons.h"
WhileLoopNodeWidget::WhileLoopNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base)
: BaseNodeWidget(manager, base)
{
m_whileLoopNode = std::dynamic_pointer_cast<WhileLoopNode>(base);
}
void WhileLoopNodeWidget::Draw()
{
// Affichage simple
ImGui::Text(ICON_MDI_REPEAT " While Loop");
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
ImGui::TextWrapped("Loops while condition is true");
ImGui::PopStyleColor();
}
void WhileLoopNodeWidget::DrawProperties(std::shared_ptr<IStoryProject> story)
{
ImGui::Text("While Loop Properties");
ImGui::Separator();
ImGui::TextWrapped("The loop executes as long as the condition input evaluates to true (non-zero).");
ImGui::Spacing();
ImGui::TextWrapped("Warning: Ensure the condition can become false to avoid infinite loops.");
}

View file

@ -0,0 +1,18 @@
// story-editor/src/node_editor/while_loop_node_widget.h
#pragma once
#include "base_node_widget.h"
#include "while_loop_node.h"
class WhileLoopNodeWidget : public BaseNodeWidget
{
public:
WhileLoopNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base);
virtual ~WhileLoopNodeWidget() = default;
void Draw() override;
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
private:
std::shared_ptr<WhileLoopNode> m_whileLoopNode;
};