Chapter 182 · XUI 专业控件

FlowGraph 流程图

节点/端口/连线图编辑器:可视化编程、状态机、数据流图。

182

概念

FlowGraph 是节点图编辑器(独立句柄,非 Widget):
• Node:节点,包含标题/位置/端口
• Port:端口(输入/输出),可指定类型和颜色
• Edge:连线,连接两个端口
• 交互:拖拽节点/连线/框选/缩放
适用场景:可视化脚本、Shader 图、状态机、数据管线。

API 速查

函数说明
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;
}

代码讲解

  • FlowGraphCreate:创建独立句柄节点图,非 Widget 树节点。
  • AddNode:添加节点,指定 ID/标题/位置。
  • AddPort:为节点添加输入/输出端口,bOutput 决定方向。
  • AddEdge:通过端口 ID 连接两个节点,删除节点时自动删除关联连线。

要点与陷阱

  • 独立句柄:FlowGraph 使用 xui_flow_graph 句柄,非 Widget 树节点。
  • ID 唯一:Node/Port/Edge 的 sId 必须全局唯一。
  • RemoveNode:删除节点时自动删除关联的 Edge。
  • 渲染集成:需配合 Canvas 或自定义渲染将 Graph 绘制到 Widget。

进阶练习

  1. Shader 图:实现 Texture/Color/Multiply/Add 节点类型。
  2. 状态机编辑器:节点为状态,连线为转换条件。
FlowGraph 流程图效果
图:FlowGraph 流程图渲染效果