mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
custom socket styling
This commit is contained in:
parent
d06f05d207
commit
883257fd78
4 changed files with 267 additions and 32 deletions
2
story-editor/externals/ImNodeFlow
vendored
2
story-editor/externals/ImNodeFlow
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 1d06616de63ab497f18e9403b128b6eccef3115d
|
||||
Subproject commit e4984a42d3939d361f977e8d0a56bbfd49d0a702
|
||||
|
|
@ -10,6 +10,9 @@
|
|||
#include "base_node.h"
|
||||
#include "gui.h"
|
||||
|
||||
namespace Nw
|
||||
{
|
||||
|
||||
enum class PinType
|
||||
{
|
||||
Flow,
|
||||
|
|
@ -40,19 +43,43 @@ enum class NodeType
|
|||
struct PinStyle
|
||||
{
|
||||
/// @brief Socket and link color
|
||||
ImU32 color;
|
||||
ImU32 color{IM_COL32(255,255,255,255)};
|
||||
/// @brief Socket shape ID
|
||||
int socket_shape;
|
||||
int socket_shape{5};
|
||||
/// @brief Socket radius
|
||||
float socket_radius;
|
||||
float socket_radius{4.f};
|
||||
/// @brief Socket radius when hovered
|
||||
float socket_hovered_radius;
|
||||
float socket_hovered_radius{4.4f};
|
||||
/// @brief Socket radius when connected
|
||||
float socket_connected_radius;
|
||||
float socket_connected_radius{4.2f};
|
||||
/// @brief Socket outline thickness when empty
|
||||
float socket_thickness;
|
||||
float socket_thickness{1.f};
|
||||
ImVec2 padding = ImVec2(3.f, 1.f);
|
||||
/// @brief Border and background corner rounding
|
||||
float bg_radius = 8.f;
|
||||
/// @brief Border thickness
|
||||
float border_thickness = 1.f;
|
||||
/// @brief Background color
|
||||
ImU32 bg_color = IM_COL32(23, 16, 16, 0);
|
||||
/// @brief Background color when hovered
|
||||
ImU32 bg_hover_color = IM_COL32(100, 100, 255, 70);
|
||||
/// @brief Border color
|
||||
ImU32 border_color = IM_COL32(255, 255, 255, 0);
|
||||
};
|
||||
|
||||
struct Pin
|
||||
{
|
||||
ImVec2 pos = ImVec2(0.f, 0.f);
|
||||
ImVec2 size;
|
||||
ImVec2 pinPoint = ImVec2(0.f, 0.f);
|
||||
bool isConnected{false};
|
||||
int index{0};
|
||||
PinKind pinKind{PinKind::Input};
|
||||
PinStyle style;
|
||||
};
|
||||
|
||||
} // namespace Nw
|
||||
|
||||
/**
|
||||
* @brief Basically a wrapper class around ImGuiNodeEditor Node structure
|
||||
*
|
||||
|
|
@ -69,7 +96,7 @@ public:
|
|||
virtual void DrawProperties(std::shared_ptr<IStoryProject> story) = 0;
|
||||
|
||||
|
||||
virtual void DrawSocket(uint32_t index, bool isInput, ImVec2 pin_pos, bool isConnected) {}
|
||||
virtual void DrawSocket(const Nw::Pin &pin) {}
|
||||
|
||||
|
||||
virtual bool HasSync() const {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,108 @@
|
|||
#include "gui.h"
|
||||
|
||||
|
||||
void DrawBlueprintSyncSocket(ImDrawList* draw_list, const ImVec2& center, float size, ImU32 color, bool filled = true) {
|
||||
const float half_size = size * 0.5f;
|
||||
const float triangle_size = size * 0.6f; // Triangle légèrement plus petit que le carré
|
||||
|
||||
// Coordonnées du carré (partie gauche)
|
||||
ImVec2 square_min = ImVec2(center.x - half_size, center.y - half_size);
|
||||
ImVec2 square_max = ImVec2(center.x, center.y + half_size);
|
||||
|
||||
// Coordonnées du triangle (partie droite, pointant vers la droite)
|
||||
ImVec2 triangle_p1 = ImVec2(center.x, center.y - triangle_size * 0.5f); // Point haut
|
||||
ImVec2 triangle_p2 = ImVec2(center.x, center.y + triangle_size * 0.5f); // Point bas
|
||||
ImVec2 triangle_p3 = ImVec2(center.x + triangle_size * 0.7f, center.y); // Point de la pointe
|
||||
|
||||
if (filled) {
|
||||
// Dessiner le carré rempli
|
||||
draw_list->AddRectFilled(square_min, square_max, color);
|
||||
|
||||
// Dessiner le triangle rempli
|
||||
draw_list->AddTriangleFilled(triangle_p1, triangle_p2, triangle_p3, color);
|
||||
} else {
|
||||
// Dessiner les contours
|
||||
const float thickness = 2.0f;
|
||||
|
||||
// Contour du carré
|
||||
draw_list->AddRect(square_min, square_max, color, 0.0f, 0, thickness);
|
||||
|
||||
// Contour du triangle
|
||||
draw_list->AddTriangle(triangle_p1, triangle_p2, triangle_p3, color, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
// Version avec dégradé pour un effet plus moderne
|
||||
void DrawBlueprintSyncSocketGradient(ImDrawList* draw_list, const ImVec2& center, float size, ImU32 color_start, ImU32 color_end) {
|
||||
const float half_size = size * 0.5f;
|
||||
const float triangle_size = size * 0.6f;
|
||||
|
||||
// Coordonnées du carré
|
||||
ImVec2 square_min = ImVec2(center.x - half_size, center.y - half_size);
|
||||
ImVec2 square_max = ImVec2(center.x, center.y + half_size);
|
||||
|
||||
// Coordonnées du triangle
|
||||
ImVec2 triangle_p1 = ImVec2(center.x, center.y - triangle_size * 0.5f);
|
||||
ImVec2 triangle_p2 = ImVec2(center.x, center.y + triangle_size * 0.5f);
|
||||
ImVec2 triangle_p3 = ImVec2(center.x + triangle_size * 0.7f, center.y);
|
||||
|
||||
// Carré avec dégradé horizontal
|
||||
draw_list->AddRectFilledMultiColor(
|
||||
square_min, square_max,
|
||||
color_start, color_end,
|
||||
color_end, color_start
|
||||
);
|
||||
|
||||
// Triangle uni (couleur de fin du dégradé)
|
||||
draw_list->AddTriangleFilled(triangle_p1, triangle_p2, triangle_p3, color_end);
|
||||
}
|
||||
|
||||
// Variante avec animation de pulsation pour indiquer l'activité
|
||||
void DrawBlueprintSyncSocketAnimated(ImDrawList* draw_list, const ImVec2& center, float size, ImU32 color, float time) {
|
||||
// Effet de pulsation basé sur le temps
|
||||
float pulse = 0.8f + 0.2f * sinf(time * 3.0f); // Oscille entre 0.8 et 1.0
|
||||
float animated_size = size * pulse;
|
||||
|
||||
// Socket principal
|
||||
DrawBlueprintSyncSocket(draw_list, center, animated_size, color, true);
|
||||
|
||||
// Halo subtil autour
|
||||
ImU32 halo_color = ImGui::ColorConvertFloat4ToU32(ImVec4(
|
||||
((color >> IM_COL32_R_SHIFT) & 0xFF) / 255.0f,
|
||||
((color >> IM_COL32_G_SHIFT) & 0xFF) / 255.0f,
|
||||
((color >> IM_COL32_B_SHIFT) & 0xFF) / 255.0f,
|
||||
0.3f * (pulse - 0.8f) * 5.0f // Alpha qui varie avec la pulsation
|
||||
));
|
||||
|
||||
DrawBlueprintSyncSocket(draw_list, center, size * 1.2f, halo_color, false);
|
||||
}
|
||||
|
||||
// Utilisation dans ImNodeFlow
|
||||
void DrawSyncSocketInNode(ImDrawList* draw_list, const ImVec2& socket_pos, bool is_connected, bool is_hovered) {
|
||||
const float socket_size = 16.0f;
|
||||
|
||||
// Couleurs selon l'état
|
||||
ImU32 base_color = IM_COL32(100, 150, 255, 255); // Bleu par défaut
|
||||
ImU32 connected_color = IM_COL32(50, 255, 100, 255); // Vert si connecté
|
||||
ImU32 hover_color = IM_COL32(255, 200, 50, 255); // Orange au survol
|
||||
|
||||
ImU32 final_color = base_color;
|
||||
if (is_connected) final_color = connected_color;
|
||||
if (is_hovered) final_color = hover_color;
|
||||
|
||||
// Dessiner le socket
|
||||
if (is_connected) {
|
||||
// Version animée si connecté
|
||||
float time = ImGui::GetTime();
|
||||
DrawBlueprintSyncSocketAnimated(draw_list, socket_pos, socket_size, final_color, time);
|
||||
} else {
|
||||
// Version statique
|
||||
DrawBlueprintSyncSocket(draw_list, socket_pos, socket_size, final_color, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class FunctionEntryWidget : public BaseNodeWidget
|
||||
{
|
||||
public:
|
||||
|
|
@ -27,27 +129,123 @@ public:
|
|||
ImGui::SetNextItemWidth(100.f);
|
||||
}
|
||||
|
||||
void DrawSocket(uint32_t index, bool isInput, ImVec2 pin_pos, bool isConnected) override
|
||||
|
||||
void DrawSocket(const Nw::Pin &pin) override
|
||||
{
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Taille du socket
|
||||
float socket_size = 4.0f;
|
||||
|
||||
// Définir les 5 points du polygone (flèche pointant vers la droite pour Output)
|
||||
// Pour Input, la flèche pointerait vers la gauche
|
||||
ImVec2 p1, p2, p3, p4, p5;
|
||||
|
||||
if (pin.pinKind == Nw::PinKind::Output) {
|
||||
// Flèche pointant vers la droite (→)
|
||||
p1 = ImVec2(pin.pinPoint.x - socket_size * 1.5f, pin.pinPoint.y - socket_size);
|
||||
p2 = ImVec2(pin.pinPoint.x - socket_size * 0.5f, pin.pinPoint.y - socket_size);
|
||||
p3 = ImVec2(pin.pinPoint.x + socket_size * 0.5f, pin.pinPoint.y); // Pointe
|
||||
p4 = ImVec2(pin.pinPoint.x - socket_size * 0.5f, pin.pinPoint.y + socket_size);
|
||||
p5 = ImVec2(pin.pinPoint.x - socket_size * 1.5f, pin.pinPoint.y + socket_size);
|
||||
} else {
|
||||
// Flèche pointant vers la gauche (←)
|
||||
p1 = ImVec2(pin.pinPoint.x + socket_size * 1.5f, pin.pinPoint.y - socket_size);
|
||||
p2 = ImVec2(pin.pinPoint.x + socket_size * 0.5f, pin.pinPoint.y - socket_size);
|
||||
p3 = ImVec2(pin.pinPoint.x - socket_size * 0.5f, pin.pinPoint.y); // Pointe
|
||||
p4 = ImVec2(pin.pinPoint.x + socket_size * 0.5f, pin.pinPoint.y + socket_size);
|
||||
p5 = ImVec2(pin.pinPoint.x + socket_size * 1.5f, pin.pinPoint.y + socket_size);
|
||||
}
|
||||
|
||||
ImVec2 vertices[] = {p1, p2, p3, p4, p5};
|
||||
|
||||
// Rectangle pour la détection de hover
|
||||
ImVec2 tl = pin.pinPoint - ImVec2(socket_size * 1.5f, socket_size);
|
||||
ImVec2 br = pin.pinPoint + ImVec2(socket_size * 1.5f, socket_size);
|
||||
|
||||
bool hovered = ImGui::IsItemHovered() || ImGui::IsMouseHoveringRect(tl, br);
|
||||
|
||||
// Dessin du socket
|
||||
if (pin.isConnected) {
|
||||
// Rempli quand connecté
|
||||
draw_list->AddConvexPolyFilled(vertices, IM_ARRAYSIZE(vertices),
|
||||
pin.style.color);
|
||||
} else {
|
||||
// Contour seulement quand non connecté
|
||||
if (hovered) {
|
||||
draw_list->AddPolyline(vertices, IM_ARRAYSIZE(vertices),
|
||||
pin.style.color,
|
||||
ImDrawFlags_Closed,
|
||||
pin.style.socket_hovered_radius); // Épaisseur au hover
|
||||
} else {
|
||||
draw_list->AddPolyline(vertices, IM_ARRAYSIZE(vertices),
|
||||
pin.style.color,
|
||||
ImDrawFlags_Closed,
|
||||
pin.style.socket_thickness); // Épaisseur normale
|
||||
}
|
||||
}
|
||||
|
||||
// Optionnel : dessiner la décoration (fond hover) si nécessaire
|
||||
if (hovered) {
|
||||
draw_list->AddRectFilled(pin.pos - pin.style.padding,
|
||||
pin.pos + pin.size + pin.style.padding,
|
||||
pin.style.bg_hover_color,
|
||||
pin.style.bg_radius);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// bonne position du socket
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// 1. Dessiner le socket à pin.pinPoint (pas à pin.pos !)
|
||||
if (pin.isConnected) {
|
||||
draw_list->AddCircleFilled(pin.pinPoint,
|
||||
pin.style.socket_connected_radius,
|
||||
pin.style.color);
|
||||
} else {
|
||||
// Gérer le hover vous-même si nécessaire
|
||||
ImVec2 tl = pin.pinPoint - ImVec2(pin.style.socket_radius, pin.style.socket_radius);
|
||||
ImVec2 br = pin.pinPoint + ImVec2(pin.style.socket_radius, pin.style.socket_radius);
|
||||
bool hovered = ImGui::IsMouseHoveringRect(tl, br);
|
||||
|
||||
draw_list->AddCircle(pin.pinPoint,
|
||||
hovered ? pin.style.socket_hovered_radius
|
||||
: pin.style.socket_radius,
|
||||
pin.style.color,
|
||||
pin.style.socket_shape,
|
||||
pin.style.socket_thickness);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
float socket_size = 4;
|
||||
|
||||
// pin.pos.x = pin.pos.x + 10;
|
||||
|
||||
ImVec2 w_pos = ImGui::GetCursorPos();
|
||||
std::cout << "x = " << w_pos.x << ", y = " << w_pos.y << std::endl;;
|
||||
|
||||
// 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 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);
|
||||
ImVec2 tl = pin.pos - ImVec2(socket_size * 1.5f, socket_size);
|
||||
ImVec2 br = pin.pos + ImVec2(socket_size * 1.5f, socket_size);
|
||||
|
||||
if (isConnected)
|
||||
if (pin.isConnected)
|
||||
{
|
||||
draw_list->AddConvexPolyFilled(vertices, IM_ARRAYSIZE(vertices), IM_COL32(255,255,255,255));
|
||||
}
|
||||
|
|
@ -55,13 +253,15 @@ public:
|
|||
{
|
||||
if (ImGui::IsItemHovered() || ImGui::IsMouseHoveringRect(tl, br))
|
||||
{
|
||||
draw_list->AddPolyline(vertices, IM_ARRAYSIZE(vertices),IM_COL32(255,255,255,255), ImDrawFlags_Closed, 4.67f);
|
||||
draw_list->AddPolyline(vertices, IM_ARRAYSIZE(vertices),IM_COL32(255,255,255,255), ImDrawFlags_Closed, 2.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
draw_list->AddPolyline(vertices, IM_ARRAYSIZE(vertices), IM_COL32(255,255,255,255), ImDrawFlags_Closed, 1.3f);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
virtual bool HasSync() const override {
|
||||
|
|
|
|||
|
|
@ -59,10 +59,15 @@ public:
|
|||
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());
|
||||
Nw::Pin pin;
|
||||
pin.index = i;
|
||||
pin.isConnected = p->isConnected();
|
||||
pin.pinKind = Nw::PinKind::Input;
|
||||
pin.pinPoint = p->pinPoint();
|
||||
pin.pos = p->getPos();
|
||||
pin.size = p->getSize();
|
||||
|
||||
m_widget->DrawSocket(pin);
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
@ -78,10 +83,16 @@ public:
|
|||
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());
|
||||
|
||||
Nw::Pin pin;
|
||||
pin.index = i;
|
||||
pin.isConnected = p->isConnected();
|
||||
pin.pinKind = Nw::PinKind::Output;
|
||||
pin.pinPoint = p->pinPoint();
|
||||
pin.pos = p->getPos();
|
||||
pin.size = p->getSize();
|
||||
|
||||
m_widget->DrawSocket(pin);
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
@ -122,9 +133,6 @@ struct NodeEditorPage : public ImFlow::BaseNode
|
|||
{
|
||||
|
||||
mINF.setSize({500, 500});
|
||||
// mINF.addNode<SimpleSum>({0, 0});
|
||||
// mINF.addNode<SimpleSum>({10, 10});
|
||||
|
||||
}
|
||||
|
||||
~NodeEditorPage() {
|
||||
|
|
@ -182,7 +190,7 @@ struct NodeEditorPage : public ImFlow::BaseNode
|
|||
mINF.rightClickPopUpContent([this, openPopupPosition, &nodesFactory, &widgetFactory, &storyManager](ImFlow::BaseNode* node){
|
||||
// std::cout << "Right-clicked on node: " << node->getName() << std::endl;
|
||||
|
||||
auto newNodePostion = openPopupPosition;
|
||||
auto newNodePosition = mINF.screen2grid(openPopupPosition);
|
||||
auto nodeTypes = nodesFactory.ListOfNodes();
|
||||
|
||||
for (auto &type : nodeTypes)
|
||||
|
|
@ -196,11 +204,11 @@ struct NodeEditorPage : public ImFlow::BaseNode
|
|||
if (n)
|
||||
{
|
||||
// Create delegate
|
||||
auto delegate = mINF.addNode<NodeDelegate>({newNodePostion.x, newNodePostion.y});
|
||||
auto delegate = mINF.placeNode<NodeDelegate>();
|
||||
// Link with the widget
|
||||
delegate->SetWidget(n);
|
||||
|
||||
n->Base()->SetPosition(newNodePostion.x, newNodePostion.y);
|
||||
n->Base()->SetPosition(newNodePosition.x, newNodePosition.y);
|
||||
n->Initialize();
|
||||
// AddNode(n);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue