下拉框 Dropdown
下拉框(也叫下拉菜单、组合框)的作用是:在界面上只占一行的高度,点击后展开一个列表,用户从中选择一项。它特别适合选项较多、但又不希望一直占地方的场景,比如"选择分辨率"、"选择语言"、"选择角色职业"等。
CheckboxGroup:所有选项一直可见,适合选项少、需要快速扫视的场景。Dropdown:平时只显示当前选中项,点击才展开,适合选项多、或者界面空间紧张的场景。
函数原型
HXGInt Dropdown(DropdownProfile &Profile);
HXGInt 是 HiEasyX 定义的有符号整数类型,通常就是 int 或 long long 的别名。你可以放心地把它当普通整数用。
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
Profile | DropdownProfile & | 下拉框的配置结构体。必须是 static 或全局变量。 |
返回值
| 类型 | 说明 |
|---|---|
HXGInt | 当前被选中的项的索引(从 0 开始)。如果没有选中任何项(比如刚初始化且 SelectingItem = -1),返回 -1。 |
Dropdown() 的返回值每一帧都会返回当前选中项,而不仅仅是切换的那一次。如果你只想在"用户换了选项"时执行动作,需要自己记录上一帧的值做对比。
DropdownProfile 结构体
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Items | std::vector<HXString> | 空 | 下拉列表中的所有选项文字。 |
SelectingItem | HXGInt | -1 | 当前被选中的项的索引。-1 表示未选中任何项。可以被你读取或修改。 |
Width | int | 150 | 下拉框的宽度(像素)。 |
ItemHeight | int | 28 | 每个下拉项的高度(像素)。 |
MaxVisibleItems | int | 5 | 下拉列表最多同时显示多少项。如果选项更多,会出现滚动条。 |
FontSize | int | 16 | 文字字体大小。 |
RenderItemCallback | std::function<void(int, const HXString &, bool, bool)> | nullptr | 自定义项渲染回调。如果为空,使用默认样式。 |
自定义回调的原型大致如下:
[](int index, const HXString &text, bool isHovered, bool isSelected) {
// index: 项的索引
// text: 项的文字
// isHovered: 鼠标是否悬停在这项上
// isSelected: 这项是否是当前选中项
}
通过自定义回调,你可以实现带图标的下拉项、变色高亮、甚至自定义绘制。
基本用法
创建一个简单的下拉框
static HX::DropdownProfile ddProfile;
ddProfile.Items = {
HXStr("简体中文"),
HXStr("繁體中文"),
HXStr("English"),
HXStr("日本語")
};
ddProfile.SelectingItem = 0; // 默认选中第一项
HXGInt selected = HX::Dropdown(ddProfile);
// selected 就是当前选中的索引
根据选择执行逻辑
static HX::DropdownProfile dd;
dd.Items = { HXStr("800x600"), HXStr("1280x720"), HXStr("1920x1080") };
static HXGInt lastSelected = -1;
HXGInt sel = HX::Dropdown(dd);
if (sel != lastSelected) {
// 用户切换了分辨率
lastSelected = sel;
changeResolution(dd.Items[sel]);
}
DropdownProfile 必须是 static 或全局变量。如果写成局部变量,Items 列表和 SelectingItem 状态每一帧都会重置,下拉框永远无法正常展开和选择!
设置默认选中项
初始化时把 SelectingItem 设为想要的索引即可:
static HX::DropdownProfile dd;
dd.Items = { HXStr("角色A"), HXStr("角色B"), HXStr("角色C") };
dd.SelectingItem = 1; // 默认选中"角色B"
如果你想清空选择(显示空白或者占位文字),设为 -1:
dd.SelectingItem = -1; // 未选择任何项
HiEasyX 的 Dropdown 在 SelectingItem = -1 时通常会在框内显示空白或默认提示。如果你希望显示"请选择..."之类的占位文字,可以在选项列表的第一项放占位符,或者使用自定义渲染回调实现。
动态修改选项列表
Items 就是一个普通的 std::vector,你可以随时增删改:
static HX::DropdownProfile dd;
static HX::ButtonProfile btnAdd;
static HX::ButtonProfile btnRemove;
if (dd.Items.empty()) {
dd.Items = { HXStr("选项1"), HXStr("选项2") };
}
if (HX::Button(HXStr("添加选项"), btnAdd)) {
dd.Items.push_back(HXStr("新选项"));
}
if (HX::Button(HXStr("删除末项"), btnRemove) && !dd.Items.empty()) {
dd.Items.pop_back();
if (dd.SelectingItem >= static_cast<HXGInt>(dd.Items.size())) {
dd.SelectingItem = static_cast<HXGInt>(dd.Items.size()) - 1;
}
}
HX::Dropdown(dd);
自定义项渲染
如果你想让下拉项更炫酷——比如不同颜色表示不同稀有度、左边带个小图标、选中项加粗——可以用 RenderItemCallback:
static HX::DropdownProfile dd;
dd.Items = { HXStr("普通"), HXStr("稀有"), HXStr("传说") };
dd.RenderItemCallback = [](int index, const HXString &text, bool isHovered, bool isSelected) {
// 这里你可以自定义绘制每一项
// 注意:回调内部的绘制坐标由框架自动管理
// 简单示例:根据索引改变颜色
HXColor color = HXColor{200, 200, 200, 255}; // 默认灰
if (index == 2) color = HXColor{255, 200, 50, 255}; // 传说 = 金色
if (index == 1) color = HXColor{100, 150, 255, 255}; // 稀有 = 蓝色
if (isHovered) color = HXColor{255, 255, 255, 255}; // 悬停 = 白色
// 实际的绘制逻辑在回调内部完成
// (具体绘制 API 请参考框架内部实现)
};
自定义回调给了你完全的控制权,但也意味着你需要自己处理文字绘制、背景填充、悬停高亮等。如果你只是想改改颜色,建议先看看主题系统 HXTheme 能否满足需求,比写回调省心。
完整示例代码
下面的示例展示了一个语言选择下拉框、一个带滚动条的长列表、以及动态添加/删除选项的功能。
#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>
int main() {
initgraph(800, 600);
setbkcolor(WHITE);
cleardevice();
HX::HXInitForEasyX();
HX::SetBuffer(GetWorkingImage());
BeginBatchDraw();
// ===== 必须是 static 或全局变量 =====
static HX::WindowProfile wp;
wp.Size = {500, 450};
wp.Position = {150, 75};
// 语言选择
static HX::DropdownProfile langProfile;
langProfile.Items = {
HXStr("简体中文"),
HXStr("繁體中文"),
HXStr("English"),
HXStr("日本語"),
HXStr("한국어")
};
langProfile.SelectingItem = 0;
langProfile.Width = 180;
// 长列表(需要滚动)
static HX::DropdownProfile longProfile;
for (int i = 1; i <= 20; ++i) {
longProfile.Items.push_back(HXStr("选项 ") + std::to_wstring(i));
}
longProfile.SelectingItem = 0;
longProfile.MaxVisibleItems = 6; // 最多同时显示 6 项
// 动态列表
static HX::DropdownProfile dynamicProfile;
dynamicProfile.Items = { HXStr("苹果"), HXStr("香蕉") };
dynamicProfile.SelectingItem = 0;
static HX::ButtonProfile btnAdd;
static HX::ButtonProfile btnRemove;
while (true) {
HX::HXBegin();
ExMessage msg;
while (peekmessage(&msg)) {
HX::PushMessage(HX::GetHXMessage(&msg));
}
HX::Window(HXStr("Dropdown 演示"), wp);
// --- 语言选择 ---
HX::Text(HXStr("选择语言:"));
HXGInt lang = HX::Dropdown(langProfile);
HX::Text(HXStr("当前选中索引: ") + std::to_wstring(lang));
HX::Text(HXStr("")); // 空行
// --- 长列表 ---
HX::Text(HXStr("长列表(20项,显示6项):"));
HX::Dropdown(longProfile);
HX::Text(HXStr("")); // 空行
// --- 动态增删 ---
HX::Text(HXStr("动态列表:"));
HX::Dropdown(dynamicProfile);
if (HX::Button(HXStr("添加水果"), btnAdd)) {
static int fruitCount = 2;
dynamicProfile.Items.push_back(HXStr("水果") + std::to_wstring(++fruitCount));
}
if (HX::Button(HXStr("删除末项"), btnRemove)) {
if (!dynamicProfile.Items.empty()) {
dynamicProfile.Items.pop_back();
if (dynamicProfile.SelectingItem >= static_cast<HXGInt>(dynamicProfile.Items.size())) {
dynamicProfile.SelectingItem = static_cast<HXGInt>(dynamicProfile.Items.size()) - 1;
}
}
}
HX::End();
HX::Render();
FlushBatchDraw();
Sleep(16);
}
closegraph();
return 0;
}
Dropdown 有一个隐含的坑:如果你在展开下拉列表的时候,代码逻辑把 Items 给改了(比如删除了当前正在悬停的那一项),可能会导致索引越界或显示异常。建议在修改 Items 之前先关闭下拉框,或者确保 SelectingItem 在有效范围内。
Dropdown() 本身没有 SameLine 参数。如果你想让下拉框和旁边的按钮在同一行,可以把它们放在 Window() 内部的同一行布局上下文中,或者使用 HX::SameLine()(如果框架提供了这个辅助函数)。具体方式请参考 HiEasyX 的布局系统文档。
运行效果
截图占位符:请补充 $name 控件的运行效果截图。
截图占位符:请补充
Dropdown 运行效果的运行效果截图,保存为./assets/Dropdown_view.png。`n