状态:✅ 已完成
难度:⭐⭐
代码行数:~282 行
Checkbox 是一个交互式复选框组件,演示了双状态管理、点击交互和勾选标记(✓)的绘制技术。
- ✅ 双状态管理:选中(checked)/ 未选中(unchecked)
- ✅ 点击切换:鼠标点击复选框切换状态
- ✅ 勾选标记绘制:使用旋转矩形绘制 ✓ 符号
- ✅ 悬停效果:鼠标悬停时的高亮反馈
- ✅ 多复选框支持:同时管理多个独立复选框
- ✅ 键盘控制:空格键切换第一个选项
- ✅ 实时统计:显示选中数量
| 状态 | 描述 | 视觉效果 |
|---|---|---|
| 未选中 | 默认状态 | 空的方框 |
| 选中 | 激活状态 | 方框内显示绿色 ✓ |
| 悬停 | 鼠标悬停 | 高亮边框 + 半透明外框 |
┌──────────────────────────────────┐
│ Checkbox Component Demo │
│ [Space] Toggle first option │
│ [ESC] Quit │
│ │
│ ☑ Enable Notifications │
│ ☐ Auto-save │
│ ☐ Dark Mode │
│ ☑ Show Tips │
│ │
│ Checked: 2 / 4 │
│ ✓ ← 装饰性标记
└──────────────────────────────────┘
// 1. 获取鼠标位置
float mouseX, mouseY;
input->getMousePosition(mouseX, mouseY);
// 2. 检测点击
if (isMousePressed && !wasMousePressed) {
// 检查是否点击在复选框内
for (int i = 0; i < 4; i++) {
if (isPointInCheckbox(mouseX, mouseY, checkboxes[i])) {
// 切换状态
*(checkboxes[i].checked) = !*(checkboxes[i].checked);
break;
}
}
}| 按键 | 功能 |
|---|---|
| Space | 切换第一个复选框 |
| ESC | 退出程序 |
struct CheckboxData {
float x, y, size; // 位置和大小
bool* checked; // 状态指针
const char* label; // 标签文本
};
// 复选框数组
CheckboxData checkboxes[4];bool isPointInCheckbox(float x, float y, const CheckboxData& checkbox) const {
return x >= checkbox.x &&
x <= checkbox.x + checkbox.size &&
y >= checkbox.y &&
y <= checkbox.y + checkbox.size;
}勾选标记由两条旋转的矩形线段组成:
void drawCheckmark(float centerX, float centerY, float size, const float* color) {
// 短边(左下 → 中间)
float x1 = centerX - size * 0.3f;
float y1 = centerY;
float x2 = centerX - size * 0.05f;
float y2 = centerY + size * 0.25f;
// 计算旋转角度
float angle1 = std::atan2(y2 - y1, x2 - x1);
// 绘制旋转矩形
renderer->drawRectangle(...);
// 长边(中间 → 右上)
float x3 = centerX - size * 0.05f;
float y3 = centerY + size * 0.25f;
float x4 = centerX + size * 0.35f;
float y4 = centerY - size * 0.3f;
// 绘制第二段
renderer->drawRectangle(...);
}void drawCheckbox(const CheckboxData& checkbox, bool isHovered) {
// 1. 绘制背景
renderer->drawRectangle(checkbox.x, checkbox.y,
checkbox.size, checkbox.size,
colorBoxBg);
// 2. 绘制边框(上下左右四条)
float borderWidth = 2.0f;
// ... 绘制四条边框
// 3. 如果选中,绘制勾选标记
if (*(checkbox.checked)) {
drawCheckmark(...);
}
// 4. 绘制标签
renderer->drawText(checkbox.label, ...);
// 5. 如果悬停,绘制高亮
if (isHovered) {
float highlightColor[4] = {0.5f, 0.5f, 0.6f, 0.3f};
renderer->drawRectangle(..., highlightColor);
}
}由于渲染 API 只提供 drawRectangle,要绘制勾选标记需要:
- 计算线段的角度(
atan2) - 计算线段的长度(
sqrt) - 使用旋转后的矩形近似线段
bool option1 = true;
bool option2 = false;
bool option3 = false;
bool option4 = true;
// 使用指针数组管理
CheckboxData checkboxes[4] = {
{x, y, size, &option1, "Enable Notifications"},
{x, y, size, &option2, "Auto-save"},
{x, y, size, &option3, "Dark Mode"},
{x, y, size, &option4, "Show Tips"}
};// 每帧获取鼠标位置
float mouseX, mouseY;
input->getMousePosition(mouseX, mouseY);
// 渲染时检测悬停
for (int i = 0; i < 4; i++) {
bool isHovered = isPointInCheckbox(mouseX, mouseY, checkboxes[i]);
drawCheckbox(checkboxes[i], isHovered);
}每帧渲染调用(4 个复选框):
- 背景矩形:4 次
- 边框矩形:16 次(每个复选框 4 条边)
- 勾选标记:~6 次(每个选中的复选框 2-3 次调用)
- 文本渲染:5 次(4 个标签 + 1 个统计信息)
- 装饰性标记:3 次(可选)
总计:约 30-35 次渲染调用/帧
-
双状态管理
- 如何表示和切换 true/false 状态
- 使用指针管理多个状态
-
点击检测
- 点对矩形的碰撞检测
- 鼠标按下/释放状态判断
-
基础图形绘制
- 矩形边框的绘制技巧
- 复合图形的组合
-
向量图形绘制
- 使用基础图元组合复杂形状
- 旋转矩形实现任意角度线段
-
交互设计
- 视觉反馈的重要性
- 悬停状态的实现
-
数据结构设计
- 使用结构体组织复选框数据
- 指针在状态管理中的应用
- 三态复选框:添加"部分选中"状态
- 禁用状态:灰色显示且无法点击
- 动画效果:勾选标记的出现/消失动画
- 单选框(Radio):同一组内只能选中一个
- 复选框组:管理相关复选框的组
- 全选/反选:批量操作功能
- 自定义勾选标记:支持不同样式的勾选符号
- 键盘导航:Tab 键切换焦点,Enter 键确认
- 数据绑定:与数据模型双向绑定
# 1. 配置项目
cd examples/cpp/checkbox
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
# 2. 编译
cmake --build build --config Release
# 3. 复制 DLL 到运行时目录
copy build\Release\Release\checkbox.dll ..\..\..\..\native\build\Release\
# 4. 编译着色器
cd assets\shaders
compile_shaders.batcd ..\..\..\..\native
.\build\Release\bitui_native.exe .\build\Release\checkbox.dllcheckbox/
├── checkbox.cpp # 组件实现(~282 行)
├── CMakeLists.txt # 构建配置
├── README.md # 本文档
├── assets/
│ └── shaders/ # 着色器目录
│ ├── triangle.vert # 顶点着色器(GLSL)
│ ├── triangle.frag # 片段着色器(GLSL)
│ ├── compile_shaders.bat # 着色器编译脚本
│ └── spv/ # 编译后的 SPIR-V
│ ├── triangle_vert.spv
│ └── triangle_frag.spv
└── build/ # 构建输出
└── Release/
└── Release/
└── checkbox.dll # 组件库
| 组件 | 关系 | 说明 |
|---|---|---|
| Button | 兄弟组件 | 类似的点击交互模式 |
| Switch | 兄弟组件 | 同样是双状态组件 |
| Radio | 未实现 | 单选框(互斥选择) |
IInput::isMouseButtonPressed(int button)- 检测鼠标按键状态IInput::getMousePosition(float& x, float& y)- 获取鼠标位置IInput::isKeyJustPressed(int keyCode)- 检测键盘按键IRenderer::drawRectangle()- 绘制矩形IRenderer::drawText()- 绘制文本
std::atan2(dy, dx)- 计算向量角度std::sqrt(x)- 计算平方根std::sin(x)- 正弦函数(用于动画)
-
状态管理
- 使用指针统一管理多个复选框的状态
- 保持状态变量的独立性
-
交互反馈
- 提供明确的视觉反馈(悬停、选中)
- 支持多种输入方式(鼠标、键盘)
-
代码组织
- 使用结构体封装复选框数据
- 分离检测逻辑和绘制逻辑
-
图形绘制
- 分层绘制(背景 → 边框 → 标记 → 标签)
- 使用常量颜色便于修改
-
点击无响应
- 检查鼠标坐标系统
- 验证碰撞检测范围
- 确认
wasMousePressed状态正确更新
-
勾选标记显示异常
- 检查旋转角度计算
- 验证矩形的位置和大小
- 确认颜色 alpha 通道不为 0
-
悬停效果不准确
- 检查
isPointInCheckbox的边界条件 - 验证鼠标坐标是否正确获取
- 检查
- v1.0 (2025-10-12) - 初始版本
- 实现基础复选框功能
- 支持 4 个独立复选框
- 添加悬停和点击交互
- 实现勾选标记绘制
- 项目主页:
Bit HCI - 文档:
docs/guides/ - 示例:
examples/cpp/
Happy Coding! 🎉