Chapter 151 · XUI 容器与导航

Tabs 标签页

Tabs 多页签容器,支持四方向放置、可关闭标签、滚动溢出、图标、脏标记指示。

151

概念

Tabs 由 TabBar + Client 组成:
• TabBar:标签按钮条(可滚动、可溢出菜单)
• Client:当前选中页的内容区
• Placement:标签条位于上/下/左/右
• CloseButton:每个标签可带关闭按钮
• Dirty:未保存修改的标签显示圆点标记

API 速查

函数说明
int xuiTabsCreate(xui_context pCtx, xui_widget* ppW, const xui_tabs_desc_t* pDesc)创建标签页
int xuiTabsAddPage(xui_widget pW, const char* sTitle, xui_widget* ppPage)添加页
int xuiTabsSetSelected(xui_widget pW, int iIndex)切换选中
int xuiTabsGetSelected(xui_widget pW)获取选中索引
int xuiTabsSetPlacement(xui_widget pW, int iPlacement)标签位置
int xuiTabsSetClose(xui_widget pW, onClose, int bButtons, void* pUser)关闭回调
int xuiTabsSetScrollable(xui_widget pW, int bScrollable)可滚动
int xuiTabsSetDirtyItems(xui_widget pW, const int* arr, int iCount)脏标记
int xuiTabsSetIcons(xui_widget pW, xui_surface* arr, const xui_rect_t* arrSrc, int iCount)标签图标
int xuiTabsSetTabSize(xui_widget pW, float fWidth, float fHeight)标签尺寸

编译本章示例

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

示例:编辑器多文件标签

xui_tabs_desc_t desc = { 0 };

xui_widget pTabs;
xuiTabsCreate(pCtx, &pTabs, &desc);
xuiWidgetSetRect(pTabs, (xui_rect_t){0, 0, 800.0f, 600.0f});

// 添加页面
xui_widget pPage1, pPage2;
xuiTabsAddPage(pTabs, "main.c", &pPage1);
xuiTabsAddPage(pTabs, "render.c", &pPage2);

// 可关闭 + 可滚动
xuiTabsSetClose(pTabs, OnTabClose, 1, NULL);
xuiTabsSetScrollable(pTabs, 1);
xuiTabsSetPlacement(pTabs, 0); // 顶部

// 标记 main.c 为已修改
int dirty[] = { 1, 0 };
xuiTabsSetDirtyItems(pTabs, dirty, 2);

xuiTabsSetSelect(pTabs, OnTabSelect, NULL);
xuiWidgetAddChild(pParent, pTabs);

完整代码

将以下代码保存为 ch151_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 = "ch151 - Tabs";
    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});
    xuiSetRootWidget(g_pCtx, pRoot);

    /* 创建标签页 */
    xui_tabs_desc_t td = { 0 };
    xui_widget pTabs;
    xuiTabsCreate(g_pCtx, &pTabs, &td);
    xuiWidgetSetRect(pTabs, (xui_rect_t){0, 0, 800, 600});
    xuiTabsSetPlacement(pTabs, 0); // 顶部
    xuiTabsSetScrollable(pTabs, 1);
    xuiWidgetAddChild(pRoot, pTabs);

    /* 添加页面 */
    xui_widget pPage1, pPage2, pPage3;
    xuiTabsAddPage(pTabs, "main.c", &pPage1);
    xuiTabsAddPage(pTabs, "render.c", &pPage2);
    xuiTabsAddPage(pTabs, "ui.c", &pPage3);

    /* 向页面添加内容 */
    xui_label_desc_t lb = { 0 };
    lb.sText = "这是 main.c 的内容";
    xui_widget pLb;
    xuiLabelCreate(g_pCtx, &pLb, &lb);
    xuiWidgetAddChild(pPage1, pLb);

    /* 标记脏页 */
    int dirty[] = { 1, 0, 0 };
    xuiTabsSetDirtyItems(pTabs, dirty, 3);

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

代码讲解

  • AddPage:返回页容器指针,向其中添加子控件构成页面内容。
  • Placement=0:标签条位于顶部,可设为 1/2/3 对应下/左/右。
  • Scrollable:标签过多时显示滚动箭头。
  • DirtyItems:标记未保存修改的标签显示圆点标记。

要点与陷阱

  • AddPage:返回页容器指针,向其中添加子控件构成页面内容。
  • Close 回调:返回非零可阻止关闭(如提示保存)。
  • Overflow:标签过多时出现溢出按钮,弹出菜单列出所有标签。
  • EnsureVisible:程序切换标签时自动滚动到可见位置。
  • EnabledItems:可逐标签禁用(灰色不可点击)。

进阶练习

  1. 可拖拽排序标签:结合 Drag 系统实现标签重排。
  2. 右侧垂直标签:Placement 设为右侧,适合属性面板。
Tabs 标签页效果
图:Tabs 标签页渲染效果