跳到主要内容

下拉框 Dropdown

下拉框(也叫下拉菜单、组合框)的作用是:在界面上只占一行的高度,点击后展开一个列表,用户从中选择一项。它特别适合选项较多、但又不希望一直占地方的场景,比如"选择分辨率"、"选择语言"、"选择角色职业"等。

Dropdown vs CheckboxGroup
  • CheckboxGroup:所有选项一直可见,适合选项少、需要快速扫视的场景。
  • Dropdown:平时只显示当前选中项,点击才展开,适合选项多、或者界面空间紧张的场景。

函数原型

HXGInt Dropdown(DropdownProfile &Profile);
HXGInt 是什么?

HXGInt 是 HiEasyX 定义的有符号整数类型,通常就是 intlong long 的别名。你可以放心地把它当普通整数用。


参数说明

参数类型说明
ProfileDropdownProfile &下拉框的配置结构体。必须是 static 或全局变量

返回值

类型说明
HXGInt当前被选中的项的索引(从 0 开始)。如果没有选中任何项(比如刚初始化且 SelectingItem = -1),返回 -1
返回值是"当前选中索引"

Dropdown() 的返回值每一帧都会返回当前选中项,而不仅仅是切换的那一次。如果你只想在"用户换了选项"时执行动作,需要自己记录上一帧的值做对比。


字段类型默认值说明
Itemsstd::vector<HXString>下拉列表中的所有选项文字。
SelectingItemHXGInt-1当前被选中的项的索引。-1 表示未选中任何项。可以被你读取或修改
Widthint150下拉框的宽度(像素)。
ItemHeightint28每个下拉项的高度(像素)。
MaxVisibleItemsint5下拉列表最多同时显示多少项。如果选项更多,会出现滚动条。
FontSizeint16文字字体大小。
RenderItemCallbackstd::function<void(int, const HXString &, bool, bool)>nullptr自定义项渲染回调。如果为空,使用默认样式。
RenderItemCallback 参数说明

自定义回调的原型大致如下:

[](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 的 DropdownSelectingItem = -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 在有效范围内。

不支持 SameLine

Dropdown() 本身没有 SameLine 参数。如果你想让下拉框和旁边的按钮在同一行,可以把它们放在 Window() 内部的同一行布局上下文中,或者使用 HX::SameLine()(如果框架提供了这个辅助函数)。具体方式请参考 HiEasyX 的布局系统文档。

运行效果

截图占位符:请补充 $name 控件的运行效果截图。

截图占位符:请补充 Dropdown 运行效果 的运行效果截图,保存为 ./assets/Dropdown_view.png。`n