Popup 通用弹出容器,支持锚点定位、方向翻转、模态/非模态、关闭策略、焦点管理。
Popup 是所有弹出式 UI 的基础容器(Menu、ComboBox 下拉均基于它):
• Anchor:锚点矩形 + 锚点位置(弹出相对于锚点的对齐方式)
• Direction:弹出方向(上/下/左/右),空间不足时自动翻转
• ClosePolicy:点击外部/点击 Owner/按 Esc 时是否关闭
• Modal:模态模式显示半透明遮罩
• ContentSize / MaxSize:内容尺寸与最大限制
| 函数 | 说明 |
|---|---|
int xuiPopupCreate(xui_context pCtx, xui_widget* ppW, const xui_popup_desc_t* pDesc) | 创建弹出层 |
int xuiPopupSetOpen(xui_widget pW, int bOpen) | 打开/关闭 |
int xuiPopupSetAnchorRect(xui_widget pW, xui_rect_t tAnchor) | 锚点矩形 |
int xuiPopupSetDirection(xui_widget pW, int iDirection) | 弹出方向 |
int xuiPopupSetClosePolicy(xui_widget pW, int iOutside, int iOwner, int iEsc) | 关闭策略 |
int xuiPopupSetModal(xui_widget pW, int bModal) | 模态 |
int xuiPopupSetContentSize(xui_widget pW, float fW, float fH) | 内容尺寸 |
int xuiPopupSetMatchOwnerWidth(xui_widget pW, int bEnabled) | 匹配 Owner 宽度 |
xui_widget xuiPopupGetContentWidget(xui_widget pW) | 获取内容容器 |
int xuiPopupSetFocusPolicy(xui_widget pW, int iPolicy, xui_widget pCustom) | 焦点策略 |
gcc -O2 -I. -o ch154.exe ch154_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch154.exe
xui_popup_desc_t desc = { 0 };
xui_widget pPopup;
xuiPopupCreate(pCtx, &pPopup, &desc);
// 锚定到按钮下方
xuiPopupSetOwner(pPopup, pButton);
xuiPopupSetDirection(pPopup, 1); // 向下
xuiPopupSetMatchOwnerWidth(pPopup, 1);
xuiPopupSetGap(pPopup, 4.0f);
// 关闭策略:点击外部关闭,Esc 关闭
xuiPopupSetClosePolicy(pPopup, 1, 1, 1);
// 添加内容
xui_widget pContent = xuiPopupGetContentWidget(pPopup);
xuiWidgetAddChild(pContent, pListView);
// 打开
xuiPopupSetOpen(pPopup, 1);将以下代码保存为 ch154_main.c,编译后运行即可看到弹出层效果:
#include "xge.h"
#include "xui.h"
static xui_context g_pCtx;
static xui_widget g_pPopup;
static int g_bOpen = 0;
static void onBtnClick(xui_widget pW, void* p) {
(void)pW; (void)p;
g_bOpen = !g_bOpen;
xuiPopupSetOpen(g_pPopup, g_bOpen);
}
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 = "ch154 - Popup";
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_button_desc_t btn = { 0 };
btn.sText = "打开菜单";
xui_widget pBtn;
xuiButtonCreate(g_pCtx, &pBtn, &btn);
xuiWidgetSetRect(pBtn, (xui_rect_t){0, 0, 150, 36});
xuiButtonSetClick(pBtn, onBtnClick, NULL);
xuiWidgetAddChild(pRoot, pBtn);
/* 创建弹出层 */
xui_popup_desc_t pd = { 0 };
xuiPopupCreate(g_pCtx, &g_pPopup, &pd);
xuiPopupSetOwner(g_pPopup, pBtn);
xuiPopupSetDirection(g_pPopup, 1);
xuiPopupSetMatchOwnerWidth(g_pPopup, 1);
xuiPopupSetClosePolicy(g_pPopup, 1, 1, 1);
xuiPopupSetContentSize(g_pPopup, 150, 120);
/* 弹出层内容 */
xui_widget pContent = xuiPopupGetContentWidget(g_pPopup);
xui_label_desc_t lb = { 0 };
lb.sText = "菜单项 1\n菜单项 2\n菜单项 3";
xui_widget pLb;
xuiLabelCreate(g_pCtx, &pLb, &lb);
xuiWidgetAddChild(pContent, pLb);
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}