diff --git a/core/story-manager/src/nodes/base_node.h b/core/story-manager/src/nodes/base_node.h index 793165c..0f18ea8 100644 --- a/core/story-manager/src/nodes/base_node.h +++ b/core/story-manager/src/nodes/base_node.h @@ -36,6 +36,7 @@ public: Port::Type type{EXECUTION_PORT}; std::string label; + bool customSocketIcon{false}; }; struct NodePosition @@ -124,10 +125,37 @@ public: m_inputPorts.push_back({type, label}); } - void AddOutputPort(Port::Type type, const std::string& label) { - m_outputPorts.push_back({type, label}); + void AddOutputPort(Port::Type type, const std::string& label, bool customRendering = false) { + 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 { return m_outputPorts.size(); @@ -144,6 +172,10 @@ public: return m_behavior; } + bool IsExecutable() const { + return m_behavior == BEHAVIOR_EXECUTION; + } + void Accept(IVariableVisitor &visitor); diff --git a/core/story-manager/src/nodes/function_entry_node.h b/core/story-manager/src/nodes/function_entry_node.h index a71b01f..89fa575 100644 --- a/core/story-manager/src/nodes/function_entry_node.h +++ b/core/story-manager/src/nodes/function_entry_node.h @@ -10,6 +10,8 @@ public: : BaseNode(type, "Function Entry Node") { SetWeight(100); + SetBehavior(BaseNode::BEHAVIOR_EXECUTION); + AddOutputPort(BaseNode::Port::Type::EXECUTION_PORT, ">", true); } void Initialize() override { diff --git a/core/story-manager/src/nodes/function_exit_node.h b/core/story-manager/src/nodes/function_exit_node.h index 4d50831..8cf2dcb 100644 --- a/core/story-manager/src/nodes/function_exit_node.h +++ b/core/story-manager/src/nodes/function_exit_node.h @@ -1,22 +1,23 @@ #pragma once -#include "execution_node.h" +#include "base_node.h" -class FunctionExitNode : public ExecutionNode +class FunctionExitNode : public BaseNode { public: - FunctionExitNode(const std::string &type, const std::string &typeName) - : ExecutionNode(type, typeName) {} + FunctionExitNode(const std::string &type) + : BaseNode(type, "Function Exit Node") + { + + SetBehavior(BaseNode::BEHAVIOR_EXECUTION); + } void Initialize() override { // Initialisation spécifique pour FunctionExitNode // 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 void FinalizeFunctionExit() { diff --git a/core/story-manager/src/nodes/print_node.cpp b/core/story-manager/src/nodes/print_node.cpp index ba1778b..dc8620f 100644 --- a/core/story-manager/src/nodes/print_node.cpp +++ b/core/story-manager/src/nodes/print_node.cpp @@ -13,6 +13,8 @@ PrintNode::PrintNode(const std::string &type) m_label = v->GetLabel(); m_variables[m_label] = v; + SetBehavior(BaseNode::BEHAVIOR_EXECUTION); + SetText(""); } diff --git a/core/story-manager/src/nodes/variable_node.cpp b/core/story-manager/src/nodes/variable_node.cpp index e047cd8..5830942 100644 --- a/core/story-manager/src/nodes/variable_node.cpp +++ b/core/story-manager/src/nodes/variable_node.cpp @@ -9,6 +9,7 @@ VariableNode::VariableNode(const std::string &type) { nlohmann::json j{ {"uuid", ""} }; SetInternalData(j); + SetBehavior(BaseNode::BEHAVIOR_DATA); } void VariableNode::Initialize() diff --git a/core/story-manager/src/nodes_factory.h b/core/story-manager/src/nodes_factory.h index 78554c2..56514a2 100644 --- a/core/story-manager/src/nodes_factory.h +++ b/core/story-manager/src/nodes_factory.h @@ -15,14 +15,16 @@ #include "syscall_node.h" #include "story_project.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 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 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 (*GenericCreator)(const std::string &type); @@ -41,6 +43,8 @@ public: registerNode(VariableNodeUuid, std::make_shared("Variable")); registerNode(PrintNodeUuid, std::make_shared("Print")); registerNode(SyscallNodeUuid, std::make_shared("System call")); + registerNode(FunctionEntryNodeUuid, std::make_shared("Function entry")); + registerNode(FunctionExitNodeUuid, std::make_shared("Function exit")); } ~NodesFactory() = default; diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index 50f04bf..85dcb15 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -33,6 +33,8 @@ #include "operator_node_widget.h" #include "print_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) : m_logger(logger) @@ -71,7 +73,8 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo m_widgetFactory.registerNode(VariableNodeUuid); m_widgetFactory.registerNode(PrintNodeUuid); m_widgetFactory.registerNode(SyscallNodeUuid); - + m_widgetFactory.registerNode(FunctionEntryNodeUuid); + m_widgetFactory.registerNode(FunctionExitNodeUuid); m_eventBus.Subscribe([this](const OpenProjectEvent &event) { OpenProject(event.GetUuid()); diff --git a/story-editor/src/node_editor/base_node_widget.h b/story-editor/src/node_editor/base_node_widget.h index 2c95746..481a0fd 100644 --- a/story-editor/src/node_editor/base_node_widget.h +++ b/story-editor/src/node_editor/base_node_widget.h @@ -8,7 +8,7 @@ #include "json.hpp" #include "i_story_manager.h" #include "base_node.h" - +#include "gui.h" enum class PinType { @@ -37,6 +37,22 @@ enum class NodeType 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 * @@ -53,6 +69,22 @@ public: virtual void DrawProperties(std::shared_ptr 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 Outputs() const { return m_base->OutputsCount(); } @@ -68,9 +100,13 @@ public: std::shared_ptr Base() { return m_base; } + void SetTitle(const std::string& title) { m_title = title; } + std::string GetTitle() const { return m_title; } + private: IStoryManager &m_manager; std::shared_ptr m_base; + std::string m_title; bool m_firstFrame{true}; diff --git a/story-editor/src/node_editor/function_entry_widget.h b/story-editor/src/node_editor/function_entry_widget.h new file mode 100644 index 0000000..515aa57 --- /dev/null +++ b/story-editor/src/node_editor/function_entry_widget.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include + +#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 node) + : BaseNodeWidget(manager, node) + , m_manager(manager) + { + m_functionEntryNode = std::dynamic_pointer_cast(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 story) override { + + } + virtual void Initialize() override { + + } + +private: + IStoryManager &m_manager; + + std::shared_ptr m_functionEntryNode; + +}; diff --git a/story-editor/src/node_editor/function_exit_widget.h b/story-editor/src/node_editor/function_exit_widget.h new file mode 100644 index 0000000..92ccae4 --- /dev/null +++ b/story-editor/src/node_editor/function_exit_widget.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include + +#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 node) + : BaseNodeWidget(manager, node) + , m_manager(manager) + { + m_functionExitNode = std::dynamic_pointer_cast(node); + SetTitle("Function Exit"); + } + + void Draw() override { + ImGui::SetNextItemWidth(100.f); + } + + virtual void DrawProperties(std::shared_ptr story) override { + + } + virtual void Initialize() override { + + } + +private: + IStoryManager &m_manager; + + std::shared_ptr m_functionExitNode; + +}; diff --git a/story-editor/src/node_editor/node_editor_page.h b/story-editor/src/node_editor/node_editor_page.h index 6d72edd..954abf2 100644 --- a/story-editor/src/node_editor/node_editor_page.h +++ b/story-editor/src/node_editor/node_editor_page.h @@ -42,17 +42,52 @@ public: m_widget = widget; // Initialize delegate - setTitle("Node Delegate"); + setTitle(m_widget->GetTitle()); setStyle(ImFlow::NodeStyle::green()); + // Add Sync input if it is an executable node + if (m_widget->HasSync()) + { + ImFlow::BaseNode::addIN(">", 0, ImFlow::ConnectionFilter::SameType()); + } + // Add inputs for (int i = 0; i < m_widget->Inputs(); ++i) { - ImFlow::BaseNode::addIN("In" + std::to_string(i), 0, ImFlow::ConnectionFilter::SameType()); + + auto port = m_widget->Base()->GetInputPort(i); + + if (port.customSocketIcon) + { + ImFlow::BaseNode::addIN("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("In" + std::to_string(i), 0, ImFlow::ConnectionFilter::SameType()); + } } // Add outputs - for (int i = 0; i < m_widget->Outputs(); ++i) { - ImFlow::BaseNode::addOUT("Out" + std::to_string(i), nullptr)->behaviour([this, i]() { return getInVal("In" + std::to_string(i)) + m_valB; }); + for (int i = 0; i < m_widget->Outputs(); ++i) + { + auto port = m_widget->Base()->GetOutputPort(i); + if (port.customSocketIcon) + { + ImFlow::BaseNode::addOUT("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("Out" + std::to_string(i), nullptr)->behaviour([this, i]() { return getInVal("In" + std::to_string(i)) + m_valB; }); + } } } diff --git a/story-editor/src/node_editor/variable_node_widget.cpp b/story-editor/src/node_editor/variable_node_widget.cpp index 149674f..a7c580b 100644 --- a/story-editor/src/node_editor/variable_node_widget.cpp +++ b/story-editor/src/node_editor/variable_node_widget.cpp @@ -11,6 +11,7 @@ VariableNodeWidget::VariableNodeWidget(IStoryManager &manager, std::shared_ptr(node); + SetTitle("Variable"); } void VariableNodeWidget::Initialize()