Chapter 147 · XUI 容器与导航

TreeView 树形

TreeView 树形控件,支持展开/折叠、勾选框、图标装饰、Adapter 虚拟化数据源。

147

概念

TreeView 以父子层级展示数据:
• 节点模型:每个节点有 Id、ParentId、Text、展开/勾选状态
• 虚拟化:仅渲染可见节点(展开状态下的扁平列表)
• 装饰:展开箭头、图标、勾选框
• Adapter:按需回调获取子节点数量与数据,适合超大树

API 速查

函数说明
int xuiTreeViewCreate(xui_context pCtx, xui_widget* ppW, const xui_tree_view_desc_t* pDesc)创建树
int xuiTreeViewAddNode(xui_widget pW, int iId, int iParentId, const char* sText)添加节点
int xuiTreeViewSetNodes(xui_widget pW, const xui_tree_view_node_t* pNodes, int iCount)批量设置
int xuiTreeViewSetNodeExpanded(xui_widget pW, int iNodeId, int bExpanded)展开/折叠
int xuiTreeViewToggleNode(xui_widget pW, int iNodeId)切换展开
int xuiTreeViewSetNodeChecked(xui_widget pW, int iNodeId, int bChecked)勾选
int xuiTreeViewSetSelected(xui_widget pW, int iNodeId)选中节点
int xuiTreeViewEnsureVisible(xui_widget pW, int iNodeId)滚动到可见
int xuiTreeViewSetAdapter(xui_widget pW, onCount, onNode, void* pUser)虚拟化适配器
int xuiTreeViewSetMetrics(xui_widget pW, float fH, float fIndent, float fPad, float fBorder)度量

编译本章示例

gcc -O2 -I. -o ch147.exe ch147_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch147.exe

示例:目录树

xui_tree_view_desc_t desc = { 0 };

xui_widget pTree;
xuiTreeViewCreate(pCtx, &pTree, &desc);
xuiWidgetSetRect(pTree, (xui_rect_t){0, 0, 260.0f, 500.0f});

// 构建树结构(Id, ParentId, Text)
xuiTreeViewAddNode(pTree, 1, 0, "src");
xuiTreeViewAddNode(pTree, 2, 1, "main.c");
xuiTreeViewAddNode(pTree, 3, 1, "render.c");
xuiTreeViewAddNode(pTree, 4, 0, "include");
xuiTreeViewAddNode(pTree, 5, 4, "xge.h");

// 默认展开根节点
xuiTreeViewSetNodeExpanded(pTree, 1, 1);
xuiTreeViewSetNodeExpanded(pTree, 4, 1);

xuiTreeViewSetSelect(pTree, OnTreeSelect, NULL);
xuiWidgetAddChild(pParent, pTree);

完整代码

将以下代码保存为 ch147_main.c,编译后运行即可看到树视图效果:

#include "xge.h"
#include "xui.h"
#include <stdio.h>

static xui_context g_pCtx;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);
    float mx, my;
    xgeMouseGet(&mx, &my);
    xuiInputPointerMove(g_pCtx, mx, my);
    if (xgeMouseDown(XGE_MOUSE_LEFT))
        xuiInputPointerDown(g_pCtx, 0);
    else
        xuiInputPointerUp(g_pCtx, 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 = 800; d.iHeight = 600;
    d.sTitle = "ch147 - TreeView";
    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, 800, 600);

    /* 根容器 */
    xui_widget pRoot;
    xuiWidgetCreate(g_pCtx, &pRoot);
xuiWidgetSetLayoutType(pRoot, XUI_LAYOUT_COLUMN);
    xuiWidgetSetRect(pRoot, (xui_rect_t){0, 0, 800, 600});
    xuiWidgetSetPadding(pRoot, 40, 40, 40, 40);
    xuiSetRootWidget(g_pCtx, pRoot);

    /* 创建树视图 */
    xui_tree_view_desc_t tvd = { 0 };
    xui_widget pTree;
    xuiTreeViewCreate(g_pCtx, &pTree, &tvd);
    xuiWidgetSetRect(pTree, (xui_rect_t){0, 0, 300, 500});
    xuiWidgetAddChild(pRoot, pTree);

    /* 构建树结构 (Id, ParentId, Text) */
    xuiTreeViewAddNode(pTree, 1, 0, "src");
    xuiTreeViewAddNode(pTree, 2, 1, "main.c");
    xuiTreeViewAddNode(pTree, 3, 1, "render.c");
    xuiTreeViewAddNode(pTree, 4, 1, "ui.c");
    xuiTreeViewAddNode(pTree, 5, 0, "include");
    xuiTreeViewAddNode(pTree, 6, 5, "xge.h");
    xuiTreeViewAddNode(pTree, 7, 5, "xui.h");
    xuiTreeViewAddNode(pTree, 8, 0, "build");

    /* 默认展开根节点 */
    xuiTreeViewSetNodeExpanded(pTree, 1, 1);
    xuiTreeViewSetNodeExpanded(pTree, 5, 1);

    xgeRun(onFrame, NULL);
    xuiDestroy(g_pCtx);
    xgeUnit();
    return 0;
}

代码讲解

  • AddNode(Id, ParentId, Text):ParentId=0 表示根级节点,其他指定父节点 ID。
  • SetNodeExpanded:默认展开 src 和 include 目录,显示子文件。
  • 树形结构:通过 Id/ParentId 关系构建任意深度层次。
  • 内建滚动:节点多时自动显示滚动条。

要点与陷阱

  • ParentId=0:表示根级节点(无父节点)。
  • Adapter 模式:超大树(10 万+节点)使用 Adapter 按需加载子节点。
  • Decoration:SetNodeDecorations 控制每节点是否显示图标/勾选框。
  • VisibleCount:当前展开状态下的可见节点总数。
  • EnsureVisible:自动展开父链并滚动到目标节点。

进阶练习

  1. 文件浏览器:结合文件系统 API 动态加载子目录。
  2. 三态勾选:父节点根据子节点勾选状态自动切换半选。
TreeView 树形效果
图:TreeView 树形渲染效果