Function entry/exit nodes + custom socket (wip)
Some checks failed
Build-StoryEditor / build_linux (push) Has been cancelled
Build-StoryEditor / build_win32 (push) Has been cancelled
Deploy-Documentation / deploy (push) Has been cancelled

This commit is contained in:
anthony@rabine.fr 2025-09-07 23:14:55 +02:00
parent fc37a9ffa1
commit d06f05d207
12 changed files with 260 additions and 18 deletions

View file

@ -36,6 +36,7 @@ public:
Port::Type type{EXECUTION_PORT}; Port::Type type{EXECUTION_PORT};
std::string label; std::string label;
bool customSocketIcon{false};
}; };
struct NodePosition struct NodePosition
@ -124,10 +125,37 @@ public:
m_inputPorts.push_back({type, label}); m_inputPorts.push_back({type, label});
} }
void AddOutputPort(Port::Type type, const std::string& label) { void AddOutputPort(Port::Type type, const std::string& label, bool customRendering = false) {
m_outputPorts.push_back({type, label}); m_outputPorts.push_back({type, label, customRendering});
} }
bool HasOutputCustomRendering(uint32_t index) const {
if (index < m_outputPorts.size()) {
return m_outputPorts[index].customSocketIcon;
}
return false;
}
bool HasInputCustomRendering(uint32_t index) const {
if (index < m_inputPorts.size()) {
return m_inputPorts[index].customSocketIcon;
}
return false;
}
Port GetInputPort(uint32_t index) const {
if (index < m_inputPorts.size()) {
return m_inputPorts[index];
}
return {Port::Type::EXECUTION_PORT, "?", false};
}
Port GetOutputPort(uint32_t index) const {
if (index < m_outputPorts.size()) {
return m_outputPorts[index];
}
return {Port::Type::EXECUTION_PORT, "?", false};
}
uint32_t OutputsCount() const { uint32_t OutputsCount() const {
return m_outputPorts.size(); return m_outputPorts.size();
@ -144,6 +172,10 @@ public:
return m_behavior; return m_behavior;
} }
bool IsExecutable() const {
return m_behavior == BEHAVIOR_EXECUTION;
}
void Accept(IVariableVisitor &visitor); void Accept(IVariableVisitor &visitor);

View file

@ -10,6 +10,8 @@ public:
: BaseNode(type, "Function Entry Node") : BaseNode(type, "Function Entry Node")
{ {
SetWeight(100); SetWeight(100);
SetBehavior(BaseNode::BEHAVIOR_EXECUTION);
AddOutputPort(BaseNode::Port::Type::EXECUTION_PORT, ">", true);
} }
void Initialize() override { void Initialize() override {

View file

@ -1,22 +1,23 @@
#pragma once #pragma once
#include "execution_node.h" #include "base_node.h"
class FunctionExitNode : public ExecutionNode class FunctionExitNode : public BaseNode
{ {
public: public:
FunctionExitNode(const std::string &type, const std::string &typeName) FunctionExitNode(const std::string &type)
: ExecutionNode(type, typeName) {} : BaseNode(type, "Function Exit Node")
{
SetBehavior(BaseNode::BEHAVIOR_EXECUTION);
}
void Initialize() override { void Initialize() override {
// Initialisation spécifique pour FunctionExitNode // Initialisation spécifique pour FunctionExitNode
// Par exemple, préparer les sorties nécessaires pour la fonction // Par exemple, préparer les sorties nécessaires pour la fonction
} }
std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override {
// Logique de construction pour FunctionExitNode
return "FunctionExitNode Build";
}
// Ajoutez des méthodes spécifiques pour gérer la sortie de la fonction // Ajoutez des méthodes spécifiques pour gérer la sortie de la fonction
void FinalizeFunctionExit() { void FinalizeFunctionExit() {

View file

@ -13,6 +13,8 @@ PrintNode::PrintNode(const std::string &type)
m_label = v->GetLabel(); m_label = v->GetLabel();
m_variables[m_label] = v; m_variables[m_label] = v;
SetBehavior(BaseNode::BEHAVIOR_EXECUTION);
SetText(""); SetText("");
} }

View file

@ -9,6 +9,7 @@ VariableNode::VariableNode(const std::string &type)
{ {
nlohmann::json j{ {"uuid", ""} }; nlohmann::json j{ {"uuid", ""} };
SetInternalData(j); SetInternalData(j);
SetBehavior(BaseNode::BEHAVIOR_DATA);
} }
void VariableNode::Initialize() void VariableNode::Initialize()

View file

@ -15,14 +15,16 @@
#include "syscall_node.h" #include "syscall_node.h"
#include "story_project.h" #include "story_project.h"
#include "story_primitive.h" #include "story_primitive.h"
#include "function_entry_node.h"
#include "function_exit_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";
static const std::string VariableNodeUuid = "020cca4e-9cdc-47e7-a6a5-53e4c9152ed0"; static const std::string VariableNodeUuid = "020cca4e-9cdc-47e7-a6a5-53e4c9152ed0";
static const std::string PrintNodeUuid = "02ee27bc-ff1d-4f94-b700-eab55052ad1c"; static const std::string PrintNodeUuid = "02ee27bc-ff1d-4f94-b700-eab55052ad1c";
static const std::string SyscallNodeUuid = "02225cff-4975-400e-8130-41524d8af773"; static const std::string SyscallNodeUuid = "02225cff-4975-400e-8130-41524d8af773";
static const std::string FunctionEntryNodeUuid = "02fd145a-b3a6-43c2-83ce-6a187e6d4b5b";
static const std::string FunctionExitNodeUuid = "027b723d-2327-4646-a17a-79ddc2e016e4";
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type); typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type);
@ -41,6 +43,8 @@ public:
registerNode<VariableNode>(VariableNodeUuid, std::make_shared<StoryPrimitive>("Variable")); registerNode<VariableNode>(VariableNodeUuid, std::make_shared<StoryPrimitive>("Variable"));
registerNode<PrintNode>(PrintNodeUuid, std::make_shared<StoryPrimitive>("Print")); registerNode<PrintNode>(PrintNodeUuid, std::make_shared<StoryPrimitive>("Print"));
registerNode<SyscallNode>(SyscallNodeUuid, std::make_shared<StoryPrimitive>("System call")); registerNode<SyscallNode>(SyscallNodeUuid, std::make_shared<StoryPrimitive>("System call"));
registerNode<FunctionEntryNode>(FunctionEntryNodeUuid, std::make_shared<StoryPrimitive>("Function entry"));
registerNode<FunctionExitNode>(FunctionExitNodeUuid, std::make_shared<StoryPrimitive>("Function exit"));
} }
~NodesFactory() = default; ~NodesFactory() = default;

View file

@ -33,6 +33,8 @@
#include "operator_node_widget.h" #include "operator_node_widget.h"
#include "print_node_widget.h" #include "print_node_widget.h"
#include "syscall_node_widget.h" #include "syscall_node_widget.h"
#include "function_entry_widget.h"
#include "function_exit_widget.h"
MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController) MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController)
: m_logger(logger) : m_logger(logger)
@ -71,7 +73,8 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
m_widgetFactory.registerNode<VariableNodeWidget>(VariableNodeUuid); m_widgetFactory.registerNode<VariableNodeWidget>(VariableNodeUuid);
m_widgetFactory.registerNode<PrintNodeWidget>(PrintNodeUuid); m_widgetFactory.registerNode<PrintNodeWidget>(PrintNodeUuid);
m_widgetFactory.registerNode<SyscallNodeWidget>(SyscallNodeUuid); m_widgetFactory.registerNode<SyscallNodeWidget>(SyscallNodeUuid);
m_widgetFactory.registerNode<FunctionEntryWidget>(FunctionEntryNodeUuid);
m_widgetFactory.registerNode<FunctionExitWidget>(FunctionExitNodeUuid);
m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) { m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) {
OpenProject(event.GetUuid()); OpenProject(event.GetUuid());

View file

@ -8,7 +8,7 @@
#include "json.hpp" #include "json.hpp"
#include "i_story_manager.h" #include "i_story_manager.h"
#include "base_node.h" #include "base_node.h"
#include "gui.h"
enum class PinType enum class PinType
{ {
@ -37,6 +37,22 @@ enum class NodeType
Houdini Houdini
}; };
struct PinStyle
{
/// @brief Socket and link color
ImU32 color;
/// @brief Socket shape ID
int socket_shape;
/// @brief Socket radius
float socket_radius;
/// @brief Socket radius when hovered
float socket_hovered_radius;
/// @brief Socket radius when connected
float socket_connected_radius;
/// @brief Socket outline thickness when empty
float socket_thickness;
};
/** /**
* @brief Basically a wrapper class around ImGuiNodeEditor Node structure * @brief Basically a wrapper class around ImGuiNodeEditor Node structure
* *
@ -53,6 +69,22 @@ public:
virtual void DrawProperties(std::shared_ptr<IStoryProject> story) = 0; virtual void DrawProperties(std::shared_ptr<IStoryProject> story) = 0;
virtual void DrawSocket(uint32_t index, bool isInput, ImVec2 pin_pos, bool isConnected) {}
virtual bool HasSync() const {
bool hasSync = false;
if (m_base)
{
// Si c'est un noeud executable, il y a une entrée de syncho (lein entre les fonctions, appels)
if (m_base->IsExecutable())
{
hasSync = true;
}
}
return hasSync;
}
uint32_t Inputs() const { return m_base->InputsCount(); } uint32_t Inputs() const { return m_base->InputsCount(); }
uint32_t Outputs() const { return m_base->OutputsCount(); } uint32_t Outputs() const { return m_base->OutputsCount(); }
@ -68,9 +100,13 @@ public:
std::shared_ptr<BaseNode> Base() { return m_base; } std::shared_ptr<BaseNode> Base() { return m_base; }
void SetTitle(const std::string& title) { m_title = title; }
std::string GetTitle() const { return m_title; }
private: private:
IStoryManager &m_manager; IStoryManager &m_manager;
std::shared_ptr<BaseNode> m_base; std::shared_ptr<BaseNode> m_base;
std::string m_title;
bool m_firstFrame{true}; bool m_firstFrame{true};

View file

@ -0,0 +1,83 @@
#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);
}
void DrawSocket(uint32_t index, bool isInput, ImVec2 pin_pos, bool isConnected) override
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
float socket_size = 4;
// Définir les points du polygone pour le symbole de synchronisation
// C'est un polygone fermé à 5 points
ImVec2 p1(pin_pos.x - socket_size * 0.5f, pin_pos.y - socket_size);
ImVec2 p2(pin_pos.x + socket_size * 0.5f, pin_pos.y - socket_size);
ImVec2 p3(pin_pos.x + socket_size * 0.5f, pin_pos.y + socket_size);
ImVec2 p4(pin_pos.x - socket_size * 0.5f, pin_pos.y + socket_size);
ImVec2 p5(pin_pos.x + socket_size * 1.5f, pin_pos.y);
ImVec2 vertices[] = {p1, p2, p5, p3, p4}; // Ordre des sommets
// Pour la détection de survol (hover) on peut toujours utiliser le rectangle englobant
ImVec2 tl = pin_pos - ImVec2(socket_size * 1.5f, socket_size);
ImVec2 br = pin_pos + ImVec2(socket_size * 1.5f, socket_size);
if (isConnected)
{
draw_list->AddConvexPolyFilled(vertices, IM_ARRAYSIZE(vertices), IM_COL32(255,255,255,255));
}
else
{
if (ImGui::IsItemHovered() || ImGui::IsMouseHoveringRect(tl, br))
{
draw_list->AddPolyline(vertices, IM_ARRAYSIZE(vertices),IM_COL32(255,255,255,255), ImDrawFlags_Closed, 4.67f);
}
else
{
draw_list->AddPolyline(vertices, IM_ARRAYSIZE(vertices), IM_COL32(255,255,255,255), ImDrawFlags_Closed, 1.3f);
}
}
}
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;
};

View file

@ -0,0 +1,42 @@
#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"
class FunctionExitWidget : public BaseNodeWidget
{
public:
FunctionExitWidget(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::SetNextItemWidth(100.f);
}
virtual void DrawProperties(std::shared_ptr<IStoryProject> story) override {
}
virtual void Initialize() override {
}
private:
IStoryManager &m_manager;
std::shared_ptr<FunctionExitNode> m_functionExitNode;
};

View file

@ -42,19 +42,54 @@ public:
m_widget = widget; m_widget = widget;
// Initialize delegate // Initialize delegate
setTitle("Node Delegate"); setTitle(m_widget->GetTitle());
setStyle(ImFlow::NodeStyle::green()); setStyle(ImFlow::NodeStyle::green());
// Add Sync input if it is an executable node
if (m_widget->HasSync())
{
ImFlow::BaseNode::addIN<int>(">", 0, ImFlow::ConnectionFilter::SameType());
}
// Add inputs // Add inputs
for (int i = 0; i < m_widget->Inputs(); ++i) { for (int i = 0; i < m_widget->Inputs(); ++i) {
auto port = m_widget->Base()->GetInputPort(i);
if (port.customSocketIcon)
{
ImFlow::BaseNode::addIN<int>("In" + std::to_string(i), 0, ImFlow::ConnectionFilter::SameType())->renderer([this, i](ImFlow::Pin* p) {
ImGui::Text("C");
p->drawDecoration();
//p->drawSocket();
m_widget->DrawSocket(i, true, p->getPos(), p->isConnected());
});
}
else
{
ImFlow::BaseNode::addIN<int>("In" + std::to_string(i), 0, ImFlow::ConnectionFilter::SameType()); ImFlow::BaseNode::addIN<int>("In" + std::to_string(i), 0, ImFlow::ConnectionFilter::SameType());
} }
}
// Add outputs // Add outputs
for (int i = 0; i < m_widget->Outputs(); ++i) { for (int i = 0; i < m_widget->Outputs(); ++i)
{
auto port = m_widget->Base()->GetOutputPort(i);
if (port.customSocketIcon)
{
ImFlow::BaseNode::addOUT<int>("Out" + std::to_string(i), nullptr)->renderer([this, i](ImFlow::Pin* p) {
ImGui::Text("C");
p->drawDecoration();
// p->drawSocket();
m_widget->DrawSocket(i, false, p->getPos(), p->isConnected());
});
}
else
{
ImFlow::BaseNode::addOUT<int>("Out" + std::to_string(i), nullptr)->behaviour([this, i]() { return getInVal<int>("In" + std::to_string(i)) + m_valB; }); ImFlow::BaseNode::addOUT<int>("Out" + std::to_string(i), nullptr)->behaviour([this, i]() { return getInVal<int>("In" + std::to_string(i)) + m_valB; });
} }
} }
}
std::shared_ptr<BaseNodeWidget> GetWidget() { std::shared_ptr<BaseNodeWidget> GetWidget() {
return m_widget; return m_widget;

View file

@ -11,6 +11,7 @@ VariableNodeWidget::VariableNodeWidget(IStoryManager &manager, std::shared_ptr<B
, m_manager(manager) , m_manager(manager)
{ {
m_variableNode = std::dynamic_pointer_cast<VariableNode>(node); m_variableNode = std::dynamic_pointer_cast<VariableNode>(node);
SetTitle("Variable");
} }
void VariableNodeWidget::Initialize() void VariableNodeWidget::Initialize()