节点/端口/连线图编辑器:可视化编程、状态机、数据流图。
FlowGraph 是节点图编辑器(独立句柄,非 Widget):
• Node:节点,包含标题/位置/端口
• Port:端口(输入/输出),可指定类型和颜色
• Edge:连线,连接两个端口
• 交互:拖拽节点/连线/框选/缩放
适用场景:可视化脚本、Shader 图、状态机、数据管线。
| 函数 | 说明 |
|---|---|
int xuiFlowGraphCreate(xui_flow_graph* ppGraph) | 创建图 |
int xuiFlowGraphAddNode(pGraph, pDesc, pIndex) | 添加节点 |
int xuiFlowGraphAddPort(pGraph, iNode, pDesc, pIndex) | 添加端口 |
int xuiFlowGraphAddEdge(pGraph, pDesc, pIndex) | 添加连线 |
int xuiFlowGraphRemoveNode(pGraph, sId) | 删除节点 |
int xuiFlowGraphRemoveEdge(pGraph, sId) | 删除连线 |
int xuiFlowGraphGetNodeCount(pGraph) | 节点数量 |
int xuiFlowGraphGetEdgeCount(pGraph) | 连线数量 |
gcc -O2 -I. -o ch182.exe ch182_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch182.exe
xui_flow_graph pGraph;
xuiFlowGraphCreate(&pGraph);
// 添加 "Texture" 节点
xui_flow_node_desc_t nodeDesc = { 0 };
nodeDesc.sId = "tex_node";
nodeDesc.sTitle = "Texture Sample";
nodeDesc.fX = 100.0f;
nodeDesc.fY = 200.0f;
int iTexNode;
xuiFlowGraphAddNode(pGraph, &nodeDesc, &iTexNode);
// 添加输出端口
xui_flow_port_desc_t portDesc = { 0 };
portDesc.sId = "tex_out";
portDesc.sName = "RGBA";
portDesc.bOutput = 1;
int iPort;
xuiFlowGraphAddPort(pGraph, iTexNode, &portDesc, &iPort);
// 连线
xui_flow_edge_desc_t edgeDesc = { 0 };
edgeDesc.sId = "edge_1";
edgeDesc.sFromPort = "tex_out";
edgeDesc.sToPort = "mat_in";
xuiFlowGraphAddEdge(pGraph, &edgeDesc, NULL);将以下代码保存为 ch182_main.c,编译后运行即可看到节点图编辑器:
#include "xge.h"
#include "xui.h"
static xui_context g_pCtx;
static int onFrame(void* pUser) {
(void)pUser;
xgeClear(0x1A1A2EFF);
xui_vec2_t pos;
xgeMouseGet(&pos.x, &pos.y);
xuiInputPointerMove(g_pCtx, pos.x, pos.y);
if (xgeMouseDown(XGE_MOUSE_LEFT))
xuiInputPointerDown(g_pCtx, pos.x, pos.y, 0);
else
xuiInputPointerUp(g_pCtx, pos.x, pos.y, 0);
xuiUpdate(g_pCtx, xgeGetDelta());
xuiLayout(g_pCtx);
xuiRender(g_pCtx, NULL, NULL, 0);
return XGE_OK;
}
int main(void) {
xge_desc_t d = { 0 };
d.iWidth = 900; d.iHeight = 640;
d.sTitle = "ch182 - FlowGraph";
d.iFlags = XGE_INIT_VSYNC;
xgeInit(&d);
xuiCreate(&g_pCtx);
extern const xui_proxy_t tXgeProxy;
xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
xuiSetViewportSize(g_pCtx, 900, 640);
// 创建节点图
xui_flow_graph pGraph;
xuiFlowGraphCreate(&pGraph);
// 添加 Texture 节点
xui_flow_node_desc_t n1 = { 0 };
n1.sId = "tex";
n1.sTitle = "Texture Sample";
n1.fX = 100.0f; n1.fY = 200.0f;
int iTex;
xuiFlowGraphAddNode(pGraph, &n1, &iTex);
// 添加 Material 节点
xui_flow_node_desc_t n2 = { 0 };
n2.sId = "mat";
n2.sTitle = "Material";
n2.fX = 400.0f; n2.fY = 200.0f;
int iMat;
xuiFlowGraphAddNode(pGraph, &n2, &iMat);
// 添加端口
xui_flow_port_desc_t po = { 0 };
po.sId = "tex_out"; po.sName = "RGBA"; po.bOutput = 1;
xuiFlowGraphAddPort(pGraph, iTex, &po, NULL);
xui_flow_port_desc_t pi = { 0 };
pi.sId = "mat_in"; pi.sName = "Albedo"; pi.bOutput = 0;
xuiFlowGraphAddPort(pGraph, iMat, &pi, NULL);
// 连线
xui_flow_edge_desc_t e = { 0 };
e.sId = "e1";
e.sFromPort = "tex_out";
e.sToPort = "mat_in";
xuiFlowGraphAddEdge(pGraph, &e, NULL);
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}