# MicroGUI **Repository Path**: water_source/MicroGUI ## Basic Information - **Project Name**: MicroGUI - **Description**: MicroGUI界面设计 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-23 - **Last Updated**: 2026-08-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # µGUI 模块化工程(基于 µGUI V0.31) µGUI 原是 Achim Döbler 2015 年发布的嵌入式图形库(协议见 `LICENSE.md`,原版说明见 `docs/README_original.md`)。本工程在原版 V0.31 基础上做了以下改进: 1. **模块化拆分**:把单文件 `ugui.c`(8237 行)/`ugui.h`(1052 行)按功能拆分为多个模块 (核心 + 基础控件 + 本工程新增控件),行为与原版保持一致; 2. **显示屏移植层**(`ugui_port.h/.c`):一套接口支持单色屏 / RGB565 / RGB888,内置颜色 空间自动转换、单色屏帧缓冲、矩形填充加速; 3. **输入设备移植层**(`ugui_input.h/.c`):触摸屏(含 ADC 标定/方向变换)、鼠标、虚拟键盘 (物理按键导航)三类输入统一归一为 uGUI 触摸事件,控件代码零改动; 4. **主题系统**(`ugui_theme.h`):集中式调色板管理,支持 RGB888 / RGB565 / BW(黑白)三种 颜色模式,所有控件边框/填充色通过 `C_PAL_*` 宏统一控制,修改主题即可全局换色; 5. **进度条控件**(`ugui_progressbar.h/.c`):支持 3D / 2D / 网格三种样式, 智能增量重绘(进度增加时仅画新区域,进度减少时全量重绘); 6. **鼠标指针显示**(`ugui_cursor.h/.c`):可随时使能/禁止的屏幕鼠标指针,内置箭头/十字 两种形状,移动时自动恢复指针下方背景(窗口区自动重绘、桌面区回填),三种颜色模式 (RGB888/RGB565/BW)与单色屏均支持; 7. **图片按钮控件**(`ugui_imagebutton.h/.c`):在普通按钮基础上叠加 BMP 图标,支持 图左文右 / 图上文下两种排列,可配 3D / 2D / 无边框样式及备用色(TOGGLE_COLORS), 适用于带图标的功能按钮; 8. **开关按钮控件**(`ugui_toggle.h/.c`):iOS 风格圆角轨道 + 圆形滑块,点击即切换 ON/OFF, 支持可选文字标签(ON/OFF)、3D / 无边框样式、自定义轨道/滑块颜色; 9. **滑块控件**(`ugui_slider.h/.c`):水平 / 垂直方向可拖动滑块,拖动时持续触发 `VALUE_CHANGED` 事件,支持 min/max 范围设定、3D / 2D / 无边框样式; 10. **下拉列表控件**(`ugui_dropdown.h/.c`):点击展开/收起列表,支持最多 16 个选项, 选中项高亮显示,触发 `SELECTED` 事件, 支持 3D / 无边框样式。 11. **数字时钟控件**(`ugui_digitalclock.h/.c`):以文本显示 `hh:mm:ss`,支持 12/24 小时制、 字体、颜色与对齐;时间变化时触发 `VALUE_CHANGED` 事件。 12. **模拟时钟控件**(`ugui_analogclock.h/.c`):圆形表盘 + 时针/分针/秒针,支持自定义边框、 表盘、各指针颜色与 3D 样式;时间变化时触发 `VALUE_CHANGED` 事件。 13. **日期控件**(`ugui_date.h/.c`):以文本显示 `YYYY-MM-DD`(分隔符可配置),支持字体、 颜色、对齐;日期变化时触发 `VALUE_CHANGED` 事件。 14. **表格控件**(`ugui_table.h/.c`):固定行列网格,逐单元格文本,支持表头、网格线与选中行 高亮(最多 12×6),选中行触发 `SELECTED` 事件,支持 3D / 表头样式。 15. **图表控件**(`ugui_chart.h/.c`):支持折线图 / 柱状图(最多 16 个数据点),可设颜色、 量程与 3D 样式,适用于轻量数据可视化。 16. **日历控件**(`ugui_calendar.h/.c`):月视图日历,含标题栏(`年-月` 标题 + `<`/`>` 翻页 箭头)、周表头(周末红字)、6×7 日期网格;支持选中高亮(青绿圆角)、今天高亮 (淡紫填充 + 紫边)、跨月日期灰显、自定义标题格式与周名、一周首日(周一/周日); 翻页与选日均通过窗口回调上报事件。 --- ## 1. 目录结构 ``` E:\AIProject\UGUI\ ├── ugui.h 总头文件(应用只需 #include "ugui.h") ├── ugui_config.h 全局配置:颜色模式 / 字体开关 / 整数类型 ├── ugui_prv.h 库内部共享声明(仅 ugui_*.c 使用,应用不要包含) ├── ugui_types.h/.c 基础类型、结构体(UG_GUI/UG_OBJECT/UG_WINDOW/UG_FONT…) ├── ugui_color.h 颜色宏表 C_RED…(随 ugui_config.h 的颜色模式切换) ├── ugui_theme.h ★ 主题系统:灰度色阶 + 默认色 + 调色板宏 C_PAL_* ├── ugui_font.h/.c 字体点阵数据 + 字体 extern 声明 ├── ugui_core.h/.c UG_Init、全局 gui 指针、驱动注册 ├── ugui_draw.h/.c 绘图原语(FillScreen/DrawLine/DrawCircle/DrawBMP…) ├── ugui_text.h/.c 文字渲染、Console ├── ugui_object.h/.c 对象框架、触摸状态机、UG_Update ├── ugui_window.h/.c 窗口管理 ├── ugui_button.h/.c 按钮控件 ├── ugui_checkbox.h/.c 复选框控件 ├── ugui_textbox.h/.c 文本框控件 ├── ugui_image.h/.c 图片控件 ├── ugui_progressbar.h/.c ★ 进度条控件(3D/2D/网格样式,智能增量重绘) ├── ugui_imagebutton.h/.c ★ 图片按钮控件(BMP 图标 + 文字,3D/2D/无边框) ├── ugui_toggle.h/.c ★ 开关按钮控件(圆角轨道 + 圆形滑块,ON/OFF 切换) ├── ugui_slider.h/.c ★ 滑块控件(水平/垂直拖动,VALUE_CHANGED 事件) ├── ugui_dropdown.h/.c ★ 下拉列表控件(展开/收起,最多 16 选项,SELECTED 事件) ├── ugui_port.h/.c ★ 显示屏移植层(单色 / RGB565 / RGB888) ├── ugui_input.h/.c ★ 输入设备移植层(触摸 / 鼠标 / 虚拟键盘) ├── ugui_cursor.h/.c ★ 鼠标指针显示(使能/禁止、形状/颜色、自动恢复背景) ├── ugui_digitalclock.h/.c ★ 数字时钟控件(OBJ_TYPE 14) ├── ugui_analogclock.h/.c ★ 模拟时钟控件(OBJ_TYPE 15) ├── ugui_date.h/.c ★ 日期控件(OBJ_TYPE 16) ├── ugui_table.h/.c ★ 表格控件(OBJ_TYPE 17) ├── ugui_chart.h/.c ★ 图表控件(OBJ_TYPE 18) ├── ugui_calendar.h/.c ★ 日历控件(OBJ_TYPE 19) ├── pc_simulator/ ★ PC 模拟器(SDL2,见 6.1/6.2 节) │ ├── sim_main.c 主函数 / 模式调度 / 主循环 / 帧推进 │ ├── sim_display.c SDL 显示移植(UG_PORT_HW_* 钩子 + 显存 + 截图) │ ├── sim_input.c SDL 输入移植(UG_IN_HW_Update 钩子 + 合成事件) │ ├── sim_ui.c UGUI 界面设计(三页演示 + 自动化测试脚本) │ ├── CMakeLists.txt 独立 CMake 工程 │ ├── SDL2/ 内置 64 位 SDL2(仅 MinGW,编译链接用) │ └── mono_128x64/ rgb565_320x240/ rgb888_480x320/ 三屏型验证工程(见 6.4 节) ├── templates/ ★ 移植代码模板(显示/输入,见第 5 节,位于仓库根目录) │ ├── port_display_template.c 显示钩子(MONO/RGB565/RGB888) │ └── port_input_template.c 输入钩子(UG_IN_HW_Update) ├── outputs/ ★ 无头渲染工具(非 SDL,见 6.5 节) │ ├── capture.c 无头帧缓冲渲染,输出原始 RGB 字节流 │ └── make_png.py 用 Python 标准库 zlib 将 RGB 编码为 PNG ├── docs/ 文档(含原版 README) └── original_v0.31/ 原始 V0.31 文件备份(未改动,供对照) ``` 加入工程的方法:把根目录所有 `ugui_*.c/.h` 与 `ugui.h`、`ugui_config.h` 加入编译即可 (`pc_simulator/`、`templates/`、`original_v0.31/`、`docs/`、`outputs/` 不要加入)。旧工程只需 `#include "ugui.h"`,所有原版 API 不变。 --- ## 2. 移植总体流程 ``` 硬件层(你写的代码 / 模板) 移植层核心(本工程提供) uGUI 核心 ──────────────────── ───────────────────── ────────── UG_PORT_HW_Init ──► ugui_port.c ──► 绘图/文字 UG_PORT_HW_DrawPixel ──► 颜色转换+帧缓冲 ──► 窗口/控件 UG_PORT_HW_FillRect ──► DRIVER_FILL_FRAME UG_Update() UG_PORT_HW_Flush ──► (仅调用,不实现) UG_ScreenBufferFlush() UG_IN_HW_Update ──► ugui_input.c ──► UG_TouchUpdate() (轮询触摸/鼠标/键盘) 标定/方向/指针模拟 ``` > **重要**:`UG_PORT_HW_*` / `UG_IN_HW_Update` 这些硬件钩子**由移植模板 > `templates/port_display_template.c` / `port_input_template.c`(或你的工程)实现, > `ugui_port.c` / `ugui_input.c` 只保留与硬件无关的适配核心,**不再提供默认桩**。 > 若工程未实现这些钩子,链接器会报错提醒你补全移植。 应用主循环只有:`UG_Update()` 在UG_Update()内调用`UG_IN_Update()` 和 `UG_ScreenBufferFlush()`。 最小示例: ```c #include "ugui" static UG_GUI g_gui; int main( void ) { UG_Init( &g_gui, UG_PORT_pset, UG_LCD_WIDTH, UG_LCD_HEIGHT ); UG_PORT_Init( &g_gui ); /* 初始化显示 + uGUI */ UG_IN_Init( &g_gui ); /* 初始化输入 */ UG_FillScreen( C_BLACK ); UG_ConsoleSetForecolor( C_WHITE ); UG_ConsolePutString( "Hello uGUI\n" ); while ( 1 ) { UG_Update(); /* 窗口/控件刷新 + 触摸事件分发 */ } } ``` --- ## 3. 显示屏移植(ugui_port) ### 3.1 选择屏型(三选一,编译期宏) | 宏 | 屏型 | 典型控制器 | |----|------|-----------| | `UG_PORT_DISPLAY_MONO` | 1bpp 单色屏 | SSD1306 / SH1106 / ST7565 / E-Paper | | `UG_PORT_DISPLAY_RGB565` | 16 位彩屏(默认) | ILI9341 / ST7789 / ST7735 | | `UG_PORT_DISPLAY_RGB888` | 24 位彩屏 | SSD1963 / LTDC / RGB 并口 | 定义方式:编译选项 `-DUG_PORT_DISPLAY_XXX`,或直接取消注释 `ugui_port.h` 第 30~32 行。 ### 3.2 颜色空间自动转换 uGUI 内部颜色由 `ugui_config.h` 决定(`USE_COLOR_RGB888` 或 `USE_COLOR_RGB565`),**与屏型 无关**。移植层在像素输出时自动转换,四种组合都验证通过: | uGUI 内部 | 屏幕 | 转换 | |-----------|------|------| | RGB888 | RGB565 | 888→565 压缩(C_RED→0xF800,C_GREEN→0x07E0) | | RGB565 | RGB565 | 直通 | | RGB888 | RGB888 | 直通 | | 任意 | 单色 | BT.601 亮度公式 → 阈值二值化(阈值 `UG_PORT_MONO_THRESHOLD`,极性 `UG_PORT_MONO_INVERT`) | ### 3.3 硬件钩子(实现即生效,弱符号,无需改库代码) | 钩子 | 说明 | 是否必须 | |------|------|---------| | `void UG_PORT_HW_Init(void)` | 屏上电、复位、控制器 init 序列。`UG_PORT_Init` 中最先调用 | 必须(由模板/工程提供) | | `void UG_PORT_HW_DrawPixel(x,y,c)` | 画点,`c` 已是屏幕原生颜色 | 彩屏必须(由模板/工程提供) | | `UG_RESULT UG_PORT_HW_FillRect(x1,y1,x2,y2,c)` | 矩形填充加速;返回 `UG_RESULT_FAIL` 则自动回退软件逐点绘制 | 可选(强烈建议,由模板/工程提供) | | `void UG_PORT_HW_Flush(void)` | 单色屏推送帧缓冲到控制器 | 单色屏必须(由模板/工程提供) | > 上表 4 个钩子**由移植模板 `templates/port_display_template.c` 或你的工程实现**, > `ugui_port.c` 不再提供默认桩——未实现时链接器报错。最快做法:直接把模板复制进工程。 ### 3.4 移植步骤(三步) 1. 定义屏型宏(见 3.1); 2. 把 `templates/port_display_template.c` 复制到工程并按控制器填充 TODO,实现需要的 `UG_PORT_HW_*` 钩子(也可手写,不依赖模板——只要提供这些符号即可); 3. 用 `UG_PORT_Init(&gui)` 完成:硬件初始化 → 注册 `DRIVER_FILL_FRAME` 填充加速驱动。 ### 3.5 示例 A:ILI9341 类 RGB565 屏(SPI 并口通用写法) ```c /* ---- board_lcd.c:用户板级代码 ---- */ #include "ugui.h" void UG_PORT_HW_Init( void ) { LCD_Reset(); /* 你的复位/GPIO/背光代码 */ LCD_WriteCmdSequence( ili9341_init ); /* 控制器初始化序列 */ } void UG_PORT_HW_DrawPixel( UG_S16 x, UG_S16 y, UG_PORT_COLOR c ) /* c = UG_U16 */ { LCD_SetWindow( x, y, x, y ); /* 列行地址窗口 */ LCD_WriteData16( c ); } UG_RESULT UG_PORT_HW_FillRect( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_PORT_COLOR c ) { UG_U32 n = (UG_U32)( x2 - x1 + 1 ) * ( y2 - y1 + 1 ); LCD_SetWindow( x1, y1, x2, y2 ); while ( n-- ) LCD_WriteData16( c ); return UG_RESULT_OK; /* 告诉 uGUI:已绘制,别走软件循环 */ } ``` ### 3.6 示例 B:SSD1306 类单色屏(内置帧缓冲) 单色屏不需要实现 DrawPixel——像素写入移植层内置的 1bpp 帧缓冲(每行整字节、MSB 在前), `UG_PORT_MonoFlush()` 在主循环中把脏帧缓冲推送出去: ```c #include "ugui.h" void UG_PORT_HW_Init( void ) /* SSD1306 上电序列 */ { SSD1306_InitSeq(); } void UG_PORT_HW_Flush( void ) /* 取内置帧缓冲并整屏推送 */ { UG_U8* fb = UG_PORT_GetMonoFramebuffer(); SSD1306_WriteFullScreen( fb ); /* 页式布局(SSD1306)需在此按页重排 */ } int main( void ) { UG_PORT_Init( &gui); while ( 1 ) { /* ... 业务绘制 ... */ UG_PORT_MonoFlush(); /* 有脏标志才真正推送 */ } } ``` 单色屏可调参数:`UG_PORT_MONO_FB_SIZE`(帧缓冲字节数,需 ≥ 宽×高/8,默认 1024 = 128×64)、 `UG_PORT_MONO_THRESHOLD`(点亮阈值,默认 128)、`UG_PORT_MONO_INVERT`(反色极性 OLED)。 大于 128×64 的屏需在编译选项放大 `UG_PORT_MONO_FB_SIZE`。 --- ## 4. 输入设备移植(ugui_input) ### 4.1 架构:三类输入归一为触摸事件 ``` 触摸屏 UG_IN_TouchReport(rawx,rawy,pressed) ─┐ 标定换算 + 方向变换 鼠标 UG_IN_MouseMove(dx,dy) / MouseButton ─┼─► UG_TouchUpdate() ─► UG_Update() ─► 控件事件 虚拟键盘 UG_IN_KeyReport(key,pressed) ─┘ 指针位置维护 / 按键模拟 ``` 所有输入最终变成"指针坐标 + 按下/释放",与 uGUI 原生触摸状态机严格对齐:事件只在 按下沿 / 按住拖动 / 释放沿产生。按钮、复选框等控件代码零改动即可被鼠标或物理按键操作。 ### 4.2 移植步骤(三步) 1. `UG_PORT_Init(&gui)` 之后调用 `UG_IN_Init(&gui)`; 2. 二选一接入硬件数据: - 实现硬件钩子 `void UG_IN_HW_Init(void)` `void UG_IN_HW_Update(void)`(由模板 `templates/port_input_template.c` 或你的工程提供,库不再提供默认桩),在里面轮询硬件并调用三个 `Report` 函数 (推荐,主循环 `UG_IN_Update()` 会自动调用它); - 或者由驱动 / 中断服务程序直接调用 `Report` 函数(此时不用实现钩子,但库仍会 在 `UG_IN_Update()` 中尝试调用 `UG_IN_HW_Update`,需保证链接有定义——模板已提供空实现); 3. 主循环:`UG_IN_Update()` → `UG_Update()`。 ### 4.3 触摸屏 ```c /* 一次性标定:12 位 ADC,X 左端 300 / 右端 3800,Y 上端 200 / 下端 3900 */ UG_IN_SetTouchCalibration( 300, 3800, 200, 3900 ); /* 轮询钩子示例(XPT2046 类) */ void UG_IN_HW_Update( void ) { UG_S16 rx, ry; UG_U8 pressed = XPT2046_Read( &rx, &ry ); /* 你的采样代码 */ if ( pressed ) UG_IN_TouchReport( rx, ry, 1 ); else UG_IN_TouchReport( rx, ry, 0 ); /* 抬起沿 */ } ``` - 标定支持轴反向(`xmin > xmax` 即 X 反向); - 屏幕与触摸控制器安装方向不一致时,用编译宏修正: `-DUG_IN_SWAP_XY`(交换轴)、`-DUG_IN_FLIP_X` / `-DUG_IN_FLIP_Y`(轴反向); - 默认 1:1(原始值即像素),适合已经输出像素坐标的触摸 IC(如 GT911 配置后)。 ### 4.4 鼠标(USB 鼠标 / 旋转编码器 / 触控板) ```c void UG_IN_HW_Update( void ) { int dx, dy; UG_U8 btn; if ( MouseRead( &dx, &dy, &btn ) ) { UG_IN_MouseMove( (UG_S16)dx, (UG_S16)dy ); UG_IN_MouseButton( btn ); } } ``` 相对位移驱动指针,自动限制在屏幕边界内;按住左键移动即产生拖动事件。 `UG_IN_SetMouseSpeed(step)` 调节步进(默认 4 像素/事件)。 ### 4.5 虚拟键盘(GPIO 按键 / 按键矩阵 / 单摇杆) | 键值 | 行为 | |------|------| | `UG_IN_KEY_UP/DOWN/LEFT/RIGHT` | 按步进移动虚拟指针(默认 8 像素,`UG_IN_SetKeyCursorStep` 可调) | | `UG_IN_KEY_OK` | 在指针当前位置产生 按下/抬起 —— 纯按键即可操作按钮等控件 | | `UG_IN_KEY_BACK` 及 `UG_IN_KEY_USER`(0x40) 以上 | 仅透传给应用回调,不产生指针事件 | ```c static void my_key_cb( UG_U8 key, UG_U8 pressed ) { if ( key >= UG_IN_KEY_USER ) { /* 处理自定义键 */ } } UG_IN_SetKeyCallback( my_key_cb ); /* 所有键先经过回调 */ /* UG_IN_SetKeyEmulation(0); */ /* 可选:完全接管,关闭指针模拟 */ ``` ### 4.6 状态查询 `UG_IN_GetPointerX/Y()`(指针坐标)、`UG_IN_GetPointerPressed()`(按下状态)。 ### 4.7 鼠标指针显示(ugui_cursor) `ugui_cursor` 模块在屏幕最顶层绘制鼠标指针,**可随时使能/禁止**,指针位置随 `UG_IN_MouseMove` / `UG_IN_TouchReport` / 虚拟键盘方向键自动跟随: ```c UG_CursorEnable( 1 ); /* 使能:立即显示指针 */ UG_CursorSetPosition( 64, 32 ); /* 设置位置(自动钳制在屏幕内) */ UG_CursorSetShape( cursor_shape_cross, /* 切换形状:内置箭头 / 十字 */ CURSOR_CROSS_W, CURSOR_CROSS_H ); UG_CursorSetColors( C_BLACK, C_WHITE ); /* 前景色/背景色(BW 模式自动黑白化) */ UG_CursorEnable( 0 ); /* 禁止:指针消失,恢复底层画面 */ ``` | API | 说明 | |------|------| | `UG_CursorEnable(state)` / `UG_CursorIsEnabled()` | 使能/禁止指针显示(禁止后输入层不再更新它) | | `UG_CursorSetShape(shape, w, h)` | 设置形状:`cursor_shape_arrow`(默认箭头)或 `cursor_shape_cross`(十字),也可传入自定义 2bpp 位图;传 NULL 恢复默认箭头。**指针尺寸按屏幕分辨率自动选择**:屏宽 < 400 用 8×8,否则用 16×16(见 `ugui_config.h` 的 `UG_CURSOR_SMALL_THRESHOLD`) | | `UG_CursorSetColors(fc,bc)` | 指针前景/背景色;BW 模式下按亮度阈值自动转为黑/白 | | `UG_CursorSetPosition(x,y)` / `UG_CursorMove(dx,dy)` | 绝对/相对移动(自动钳制边界,保证整个形状留在屏内) | | `UG_CursorGetX()` / `UG_CursorGetY()` | 查询当前指针位置 | **恢复机制(无需显示内存读回)**:指针移动/隐藏时,窗口区域内标记当前活动窗口 `WND_STATE_UPDATE` 触发整窗重绘(含所有控件),窗口外的桌面区域直接以桌面色回填; 随后 `UG_Update()` 末尾把指针重画在新位置。增量开销仅为一整窗重绘 + 一次指针位图绘制。 自定义形状:形状为 2bpp 打包位图(0=透明、1=前景色、2=背景色、3=保留),MSB 前, 每行从字节边界开始,例如 8×12 形状每行 2 字节、共 24 字节;热点固定为位图左上角。 PC 模拟器交互模式默认使能指针,运行中按 **C 键** 可随时 开启/关闭; `pc_simulator/ugui_sim.exe --mouse` 为 SDL 合成鼠标事件自动化测试(见 6.1 节)。 --- ## 5. 配置(ugui_config.h) | 配置 | 说明 | |------|------| | `USE_COLOR_RGB888` / `USE_COLOR_RGB565` / `USE_COLOR_BW` | uGUI **内部**颜色空间(三选一,与屏型无关);BW 模式下 `UG_COLOR` 为 `UG_U8`,所有颜色名按亮度阈值映射为 `C_BLACK`(0x00)/`C_WHITE`(0xFF),8BPP 抗锯齿字体改用阈值抖动 | | `USE_FONT_4X6` … `USE_FONT_12X16` | 启用字体(同时影响 `ugui_font.c` 参与编译的数据) | | `USE_PRERENDER_EVENT` / `USE_POSTRENDER_EVENT` | 窗口渲染前后事件回调 | | `typedef UG_U8/UG_S8/...` | 平台整数类型(默认 stdint) | --- ## 6. 测试说明与效果 本工程有两套 PC 端验证手段: - **SDL2 模拟器**(`pc_simulator/`,见 6.1 / 6.2 节):用 SDL2 把 uGUI 当作一块 RGB565 屏驱动,既可肉眼查看绘制结果,又能用真实鼠标/键盘与控件交互;支持 `--shot` 自动截图 与 `--mouse` 自动化鼠标测试。 - **无头渲染工具**(`outputs/`,见 6.5 节):无需 SDL,直接将 demo 渲染为原始 RGB 字节流 并由 Python 编码为 PNG,适合在没有图形环境的机器上做一键视觉验证。 三屏型验证工程(`pc_simulator/mono_128x64`、`rgb565_320x240`、`rgb888_480x320`,见 6.4 节) 分别在新控件 + 基础控件场景下验证三种屏型。 ### 6.1 PC 端 SDL2 模拟器(可视化验证) 为方便在 PC 上肉眼验证 UI 效果,提供了 PC 模拟器(`pc_simulator/` 目录,4 个文件拆分, 见 6.2 节)—— 用 SDL2 把 uGUI 当作一块 RGB565 屏来驱动,窗口里既能看绘制结果, 又能用真实鼠标/键盘与控件交互。 模拟器源码按职责分为 4 个文件(移植到真实硬件时只需按 `templates/` 里的模板重写显示与输入钩子,见第 5 节): | 文件 | 职责 | |------|------| | `sim_main.c` | 主函数、`--shot`/`--mouse` 模式调度、主循环、帧推进 `sim_pump_frames()` | | `sim_display.c` | **SDL 显示移植**:`UG_PORT_HW_Init/DrawPixel/FillRect` + 显存 + present + BMP 截图 | | `sim_input.c` | **SDL 输入移植**:`UG_IN_HW_Update` 钩子(轮询 SDL 事件 → 触摸/键盘/指针)+ 合成事件注入 | | `sim_ui.c` | **UGUI 界面设计**:三页演示窗口、回调、绘图原语、`--shot`/`--mouse` 自动化脚本 | 构建(需 mingw64 gcc,与 `pc_simulator/SDL2/` 内置的 64 位 SDL2 库匹配): ```bash cd E:\AIProject\UGUI /d/tool/pc_toolchain/mingw64/bin/gcc -std=c99 -Wall -I. -Ipc_simulator -Ipc_simulator/SDL2/include \ -DUSE_FONT_8X8 -DUSE_FONT_12X16 -DUG_PORT_DISPLAY_RGB565 \ pc_simulator/sim_main.c pc_simulator/sim_display.c pc_simulator/sim_input.c pc_simulator/sim_ui.c \ ugui_window.c ugui_button.c ugui_checkbox.c ugui_textbox.c ugui_image.c \ ugui_progressbar.c ugui_font.c ugui_port.c ugui_input.c ugui_cursor.c \ -o pc_simulator/ugui_sim.exe -Lpc_simulator/SDL2/lib -lSDL2 -lm cp pc_simulator/SDL2/lib/SDL2.dll pc_simulator/ ``` 也可使用独立的 CMake 工程 `pc_simulator/`(推荐,见 6.2 节),无需手敲 gcc 命令。 启动(交互模式,320×240 逻辑分辨率,2x 缩放为 640×480 窗口): ```bash ./pc_simulator/ugui_sim.exe # 或 cmake --build pc_simulator/build ``` 交互: - 鼠标左键按下/移动/释放 → 直接以鼠标坐标作为触摸点送入 `UG_IN_TouchReport` - 方向键 → 移动虚拟指针(步进 4 像素) - Enter / Space → 在指针处产生按下/抬起 - 窗口/按钮/复选框按消息回调工作;按 OK 时窗口回调会收到 `OBJ_EVENT_PRESSED/CLICKED` (CLICKED 在按下后释放在同一对象上时触发,已修复原版被 RELEASED 覆盖的 bug) - ESC → 退出 无界面模式(无人值守截图): ```bash ./pc_simulator/ugui_sim.exe --shot # 在 pc_simulator/ 生成 sdl_shot.bmp / sdl_shot2.bmp / sdl_shot3.bmp ./pc_simulator/ugui_sim.exe --mouse # SDL 合成鼠标事件自动化测试(无人值守) ``` `--mouse` 模式用 `SDL_PushEvent` 合成真实的鼠标移动/按下/释放事件流,端到端验证 鼠标指针与控件交互(任何一步失败即打印 FAIL 并以非零码退出): 1. 使能指针并置于 (2,2),校验桌面区指针尖端像素已绘制(前景色白)→ 截 `mouse_shot1.bmp` 2. 移动到 OK 按钮中心 (63,79) 并按下,校验指针坐标随事件更新、按钮呈按压态且 指针仍叠绘其上 → 截 `mouse_shot2.bmp` 3. 释放 → 校验窗口回调收到 `OBJ_EVENT_CLICKED`(计数 =1);再移到 Sound 复选框 点击 → 校验 `UG_CheckboxGetChecked()==1` → 截 `mouse_shot3.bmp` 4. 模拟按 **C 键** 关闭指针 → 校验 `UG_CursorIsEnabled()==0` 且按钮区域像素恢复 (指针消失、底层画面完整回填);再按 C 重新使能 三张 BMP(640×480,2x 缩放)同时用于人工核对:指针形状、按压反色、背景恢复效果。 `--mouse` 模式最近一次实测输出(pc_simulator/mouse_shot1/2/3.bmp,640×480): shot1 指针悬停桌面左上角(尖端白像素清晰可见);shot2 指针位于按压态 OK 按钮上 (按钮深色按压样式 + 指针叠加);shot3 按钮恢复常态、Sound 复选框已勾选、指针移开 且原位置背景完整恢复。像素级校验:指针区域白色像素 208/244/(移开后降为 96), 与断言一致。 #### 6.1.1 多控件演示(pc_simulator/ 当前版本) 模拟器有三个页面,**覆盖 uGUI 全部 5 类控件 + 绘图原语 + 主题调色板**: - **Page 1 — Widgets demo** - 3 个 Button(OK / Draw / Clear,三种深色背板) - 2 个 Checkbox(Sound / Wi-Fi) - 1 个 Image(24×24 RGB565 程序生成图案:四象限配色 + 对角十字 + 中央十字) - 2 个 Textbox:TXB_ID_0 显示点击计数并动态更新(`OK clicked: N times`), TXB_ID_1 设置 `ALIGN_TOP_CENTER` 显示操作提示 - 下方 Console 区逐行输出窗口回调事件(PRESSED/CLICKED、Button/Checkbox 名称、状态变化) - **Page 2 — Primitives demo**(按 Tab 或点 Draw 按钮进入) - `UG_DrawMesh` + `UG_DrawLine` 网格与对角线 - `UG_DrawCircle` / `UG_FillCircle` 圆 - `UG_DrawArc` 弧(4 段全开) - `UG_DrawFrame` / `UG_DrawRoundFrame` / `UG_FillRoundFrame` 直角/圆角框 - `UG_PutString` 文字标签 - `< Back` 按钮切回 Page 1 - **Page 3 — Progress Bar demo**(按 Tab 从 Page 2 进入) - 3 个 Progress Bar:3D 样式(绿色填充+深灰底)、2D 样式(蓝色填充+白底)、 3D+网格样式(橙色填充+深灰网格底) - `UG_ProgressSetProgress` 自动每帧递增 2%,循环 0→100→0 - Textbox 显示当前进度百分比 - `Back` 按钮切回 Page 1,`Reset` 按钮重置进度 `--shot` 模式自动演示:点 OK 按钮一次(计数变 1)→ 勾选 Sound 复选框 → 截 `sdl_shot.bmp` (Page 1)→ 切到 Page 2 → 截 `sdl_shot2.bmp`(Page 2)→ 切到 Page 3 → 设进度为 65%/40%/85% → 截 `sdl_shot3.bmp`(Page 3)。三份 BMP 头部尺寸相同、内容不同。 实测页面截图: - Page 1 — 三个按钮(深红 OK / 深绿 Draw / 深蓝 Clear)、Sound/Wi-Fi 复选框(框内 X 标记 表示勾选状态)、右上角 24×24 Image 控件(红/绿/蓝/金四象限)、TXB_ID_0 黄字"OK clicked: 1 times"、 TXB_ID_1 居中两行提示、Console 底栏显示回调日志 - Page 2 — 深绿标题栏"Primitives demo"、客户区含 olive 网格 + 白色/红色对角线、 黄色空心圆 + 蓝色实心圆 + 青色弧、绿色直角框(FRAME 标签)、品红圆角框(ROUND FRAME 标签)、 深蓝实心圆角框 - Page 3 — 紫色标题栏"Progress Bar demo"、三个进度条从上到下排列: 3D 绿色进度条(65% 填充,深灰剩余区)、2D 蓝色进度条(40% 填充,白色剩余区)、 3D+网格橙色进度条(85% 填充,深灰网格剩余区);底部 Textbox 显示"Progress: 65%", Back/Reset 按钮可操作 > 注意:当前 32 位 gcc(`gcc` 命令)不能链接 64 位 `pc_simulator/SDL2/lib/libSDL2.dll.a`。 > PC 端 SDL 模拟必须用 `/d/tool/pc_toolchain/mingw64/bin/gcc`。32 位编译链可继续做 > 头文件/逻辑测试(不链接 SDL)。 ### 6.2 CMake 工程(pc_simulator/) 模拟器另提供独立 CMake 工程 `pc_simulator/`,自动完成源文件收集、SDL2 链接和 DLL 复制,支持 Ninja / MinGW Makefiles / MSVC(MSVC 需系统安装 SDL2): ```bash cd .\UGUI # 方式一:Ninja + mingw64 gcc(推荐,已实测) cmake -S pc_simulator -B pc_simulator/build -G Ninja \ -DCMAKE_C_COMPILER="D:/tool/pc_toolchain/mingw64/bin/gcc.exe" \ -DCMAKE_MAKE_PROGRAM="D:/CMake/bin/ninja.exe" cmake --build pc_simulator/build # 方式二:MinGW Makefiles(已实测) cmake -S pc_simulator -B pc_simulator/build -G "MinGW Makefiles" \ -DCMAKE_C_COMPILER="D:/tool/pc_toolchain/mingw64/bin/gcc.exe" \ -DCMAKE_MAKE_PROGRAM="D:/tool/pc_toolchain/mingw64/bin/mingw32-make.exe" cmake --build pc_simulator/build ``` 要点: - uGUI 库编译为静态库 `libugui.a`,源码直接引用仓库根目录(可用 `-DUGUI_DIR` 覆盖路径) - 编译配置:`UG_PORT_DISPLAY_RGB565` 显示 + `USE_FONT_8X8`/`USE_FONT_12X16`; 内部颜色默认 RGB888(由 `ugui_config.h` 决定) - **BW 模式**:`-DUGUI_COLOR_MODE=BW` 切换为黑白内部色(`UG_COLOR=UG_U8`), 所有命名颜色按亮度阈值映射为 `C_BLACK`/`C_WHITE`,8BPP 字体改用阈值抖动, `UG_PORT_ColorToNative` 自动将 0x00/0xFF 转换为屏幕原生黑/白 - 默认链接仓库内置 `pc_simulator/SDL2`(仅 MinGW);系统安装的 SDL2 用 `-DUGUI_SIM_USE_SYSTEM_SDL2=ON` 切换(支持 MSVC/vcpkg/MSYS2) - Windows 下构建后自动复制 `SDL2.dll` 到输出目录 - 便捷目标:`cmake --build pc_simulator/build --target shot` = 自动演示 + 在 build 目录生成 `sdl_shot.bmp`/`sdl_shot2.bmp`/`sdl_shot3.bmp` - **移植模板(唯一硬件钩子来源)**:`templates/port_display_template.c` (显示,覆盖 MONO/RGB565/RGB888 三套 `UG_PORT_HW_*` 钩子 + `UG_PORT_ColorToNative`) 与 `port_input_template.c`(输入,`UG_IN_HW_Update` 钩子示例:触摸/鼠标/键盘)。 **`ugui_port.c` / `ugui_input.c` 不再提供这些钩子的默认桩**,模板是移植到真实硬件的 起点,复制到工程并按控制器填充 TODO 即可。 ### 6.3 三屏型分工程测试程序(pc_simulator/mono_128x64、rgb565_320x240、rgb888_480x320) 三个独立工程分别验证三种屏型下全部新控件(图片按钮/开关/滑块/下拉列表/进度条/数字时钟/ 模拟时钟/日期/表格/图表)+ 基础控件的表现。每个工程文件结构相同,ugui 界面代码与显示/输入 移植分文件存放: ``` pc_simulator/<屏型>/ ├── main.c # 主循环:UG_PORT_Init -> ui_build -> 输入轮询 -> UG_Update -> present ├── sim_display.c/.h # SDL 显示移植(实现 UG_PORT_HW_Init/DrawPixel/FillRect/Flush) ├── sim_input.c/.h # SDL 输入移植(实现 UG_IN_HW_Update 钩子) ├── sim_ui.c/.h # uGUI 界面代码(窗口/控件构建 + 回调) └── CMakeLists.txt # 复用根目录 ugui_*.c + pc_simulator/SDL2 自带 SDL2 ``` | 工程 | 屏型 | 分辨率 | 缩放 | 内部色 | 指针 | |------|------|--------|------|--------|------| | `mono_128x64` | `UG_PORT_DISPLAY_MONO` | 128×64 | 4× | — (1bpp) | 8×8(<400) | | `rgb565_320x240` | `UG_PORT_DISPLAY_RGB565` | 320×240 | 2× | RGB565/888 | 8×8(<400) | | `rgb888_480x320` | `UG_PORT_DISPLAY_RGB888` | 480×320 | 1× | RGB888 | 16×16(≥400) | **通用构建**(以 `rgb565_320x240` 为例,其余两个同理替换目录名与屏型宏): ```bash cd E:\AIProject\UGUI\pc_simulator\rgb565_320x240 # 方式一:CMake(在 cmd/PowerShell 或 PATH 含 mingw64 的 Git Bash 中) cmake -B build -G "MinGW Makefiles" \ -DCMAKE_C_COMPILER="D:/tool/pc_toolchain/mingw64/bin/gcc.exe" \ -DCMAKE_MAKE_PROGRAM="D:/tool/pc_toolchain/mingw64/bin/mingw32-make.exe" cmake --build build cp ../../SDL2/lib/SDL2.dll build/ ./build/rgb565_320x240.exe # 方式二:直接 gcc(Git Bash,绕过 CMake 的 POSIX 路径问题) /d/tool/pc_toolchain/mingw64/bin/gcc -std=c99 -O2 -Wall \ -DUG_PORT_DISPLAY_RGB565 -DUSE_FONT_8X8 -DUSE_FONT_12X16 \ -I ../.. -I ../../pc_simulator -I ../../pc_simulator/SDL2/include \ main.c sim_display.c sim_input.c sim_ui.c ../../ugui_*.c \ -o rgb565_320x240.exe -L ../../SDL2/lib -lSDL2 cp ../../SDL2/lib/SDL2.dll . ./rgb565_320x240.exe ``` > 三个工程均通过 mingw64 编译 + 运行启动验证(打印 `simulator started`,无崩溃)。 > 每个工程均含第 16 个「Calendar」演示窗口(月视图日历,演示选中/今天高亮与 `<`/`>` 翻页)。 > 交互操作:鼠标左键 = 触摸;ESC = 退出。 ### 6.4 无头渲染工具(outputs/) 部分环境没有图形界面,无法运行 SDL2。为此提供 `outputs/` 下的无头渲染工具:直接用帧缓冲 渲染 uGUI demo,导出原始 RGB888 字节流,再用 Python 标准库 `zlib` 编码为 PNG,**完全避开手工 实现 PNG/zlib 的坑**(C 手写 deflate 极易出错)。 **文件**: - `outputs/capture.c`:定义静态 RGB565 帧缓冲并实现 `UG_PORT_HW_*`(无 SDL),`ui_build()` 后逐个像素把 RGB565 转 RGB888,写入 `outputs/ugui_demo.rgb`(每行前缀 0x00 作为 PNG filter)。 - `outputs/make_png.py`:读 `ugui_demo.rgb`,构造 `IHDR` + `IDAT`(`zlib.compress`)+ `IEND`, 用 `binascii.crc32` 校验各 chunk,输出 `outputs/ugui_demo.png`。 **构建与渲染**: ```bash cd E:\AIProject\UGUI # 1) 无头编译(无需 SDL),链接 RGB565 工程 UI /d/tool/pc_toolchain/mingw64/bin/gcc -std=c99 -O2 -Wall \ -DUSE_FONT_8X8 -DUSE_FONT_12X16 -DUG_PORT_DISPLAY_RGB565 \ -I. -Ipc_simulator -Ipc_simulator/rgb565_320x240 \ outputs/capture.c ugui_*.c \ -o outputs/capture.exe -lm outputs/capture.exe # 生成 outputs/ugui_demo.rgb # 2) 编码 PNG(需 Python 3) python3 outputs/make_png.py outputs/ugui_demo.rgb outputs/ugui_demo.png ``` > BMP/PNG 输出位于 `outputs/`,用于在无 SDL 环境下做视觉核对与持续集成。注意:手写 PNG > 编码容易在 zlib 块长度、CRC、filter 上出错,本方案把 deflate 与 CRC 交给 Python 标准库, > 从而保证生成文件可被标准解码器正确读取。 ## 7. 新控件 API(图片按钮 / 开关 / 滑块 / 下拉列表 / 数字时钟 / 模拟时钟 / 日期 / 表格 / 图表) 本节列出全部新增控件的类型常量、事件常量、样式宏与核心 API。所有控件遵循与原版按钮一致的 框架:`Create` 返回 `UG_RESULT`,`Delete/Show/Hide/Set*/Get*` 遵循同一模式,事件通过窗口回调 的 `UG_MESSAGE` 传递。消息布局:`msg.type = MSG_TYPE_OBJECT`,`msg.id = 控件类型`(OBJ_TYPE_*), `msg.sub_id = 控件 ID`(OBJ_ID_*),`msg.event = 事件码`(OBJ_EVENT_*)。 ### 7.1 通用类型与事件常量(ugui_types.h) | 常量 | 值 | 说明 | |------|----|------| | `OBJ_TYPE_IMAGEBUTTON` | 6 | 图片按钮控件类型 | | `OBJ_TYPE_TOGGLE` | 7 | 开关按钮控件类型 | | `OBJ_TYPE_SLIDER` | 8 | 滑块控件类型 | | `OBJ_TYPE_DROPDOWN` | 9 | 下拉列表控件类型 | | `OBJ_TYPE_DIGITALCLOCK` | 14 | 数字时钟控件类型 | | `OBJ_TYPE_ANALOGCLOCK` | 15 | 模拟时钟控件类型 | | `OBJ_TYPE_DATE` | 16 | 日期控件类型 | | `OBJ_TYPE_TABLE` | 17 | 表格控件类型 | | `OBJ_TYPE_CHART` | 18 | 图表控件类型 | | `OBJ_TYPE_CALENDAR` | 19 | 日历控件类型 | | `OBJ_EVENT_VALUE_CHANGED` | 6 | 值变化事件(滑块拖动 / 时钟走秒 / 日期变更时触发) | | `OBJ_EVENT_SELECTED` | 7 | 选中事件(下拉列表 / 表格选中行时触发) | ### 7.2 图片按钮(ugui_imagebutton) 在普通按钮基础上叠加 BMP 图标,支持图左文右 / 图上文下两种排列: ```c UG_IMAGEBUTTON ibtn; UG_ImageButtonCreate( &ibtn, IBTN_ID_0, 10, 10, 80, 40, "Save", &font_12x16 ); UG_ImageButtonSetImage( &ibtn, &g_save_bmp ); /* BMP_BPP_16 RGB565 */ UG_ImageButtonSetImageAlign( &ibtn, IBTN_IMG_LEFT ); /* 图左文右(默认) */ UG_ImageButtonSetStyle( &ibtn, IBTN_STYLE_3D ); /* 3D 边框(默认) */ UG_ImageButtonSetAlternateColors( &ibtn, C_WHITE, C_DARK_GRAY ); /* 备用色 */ UG_ImageButtonShow( &ibtn ); ``` | 样式宏 | 说明 | |--------|------| | `IBTN_STYLE_3D` | 3D 立体边框(默认) | | `IBTN_STYLE_TOGGLE_COLORS` | 按下时切换为备用色(afc/abc) | | `IBTN_STYLE_USE_ALTERNATE_COLORS` | 当前使用备用色 | | `IBTN_STYLE_NO_BORDERS` | 不画边框 | | `IBTN_STYLE_NO_FILL` | 不填充背景 | | 图文对齐宏 | 说明 | |------------|------| | `IBTN_IMG_LEFT`(默认) | 图标在左,文字在右 | | `IBTN_IMG_TOP` | 图标在上,文字在下 | 核心 API:`UG_ImageButtonCreate / Delete / Show / Hide`、 `SetForeColor / SetBackColor / SetAlternateForeColor / SetAlternateBackColor`、 `SetText / SetFont / SetStyle / SetImage / SetImageAlign`、 `SetHSpace / SetVSpace / SetAlignment`、`GetForeColor / GetBackColor / GetStyle` 等。 ### 7.3 开关按钮(ugui_toggle) iOS 风格圆角轨道 + 圆形滑块,点击即切换 ON/OFF: ```c UG_TOGGLE tg; UG_ToggleCreate( &tg, TG_ID_0, 10, 60, 60, 26, &font_8x8 ); UG_ToggleSetState( &tg, 1 ); /* 初始 ON */ UG_ToggleSetTrackColor( &tg, C_GREEN ); /* ON 时轨道色 */ UG_ToggleSetKnobColor( &tg, C_WHITE ); /* 滑块色 */ UG_ToggleSetLabels( &tg, "ON", "OFF" ); /* 可选文字标签 */ UG_ToggleShow( &tg ); ``` | 事件宏 | 说明 | |--------|------| | `TG_EVENT_CLICKED` | 点击(= `OBJ_EVENT_CLICKED`) | | `TG_EVENT_ON` | 切换为 ON(= `OBJ_EVENT_VALUE_CHANGED | (1<<4)`) | | `TG_EVENT_OFF` | 切换为 OFF(= `OBJ_EVENT_VALUE_CHANGED | (1<<5)`) | | 样式宏 | 说明 | |--------|------| | `TG_STYLE_3D` | 3D 立体边框 | | `TG_STYLE_NO_BORDERS` | 无边框 | | `TG_STYLE_SHOW_LABEL` | 显示 ON/OFF 文字标签 | 核心 API:`UG_ToggleCreate / Delete / Show / Hide`、 `SetState / GetState`、`SetForeColor / SetBackColor / SetTrackColor / SetKnobColor`、 `SetFont / SetStyle / SetLabels / SetAlignment`、`GetStyle / GetAlignment`。 ### 7.4 滑块(ugui_slider) 水平 / 垂直方向可拖动滑块,拖动时持续触发 `VALUE_CHANGED`: ```c UG_SLIDER sld; UG_SliderCreate( &sld, SLD_ID_0, 10, 100, 200, 20, &font_8x8 ); UG_SliderSetRange( &sld, 0, 100 ); /* 值域 0~100 */ UG_SliderSetAlign( &sld, SLD_ALIGN_HORIZONTAL ); /* 水平方向(默认) */ UG_SliderSetValue( &sld, 50 ); /* 初始值 50 */ UG_SliderShow( &sld ); /* 回调中读取当前值 */ UG_S16 val = UG_SliderGetValue( &sld ); ``` | 事件宏 | 说明 | |--------|------| | `SLD_EVENT_PRESSED` | 按下(= `OBJ_EVENT_PRESSED`) | | `SLD_EVENT_RELEASED` | 释放(= `OBJ_EVENT_RELEASED`) | | `SLD_EVENT_CLICKED` | 点击(= `OBJ_EVENT_CLICKED`) | | `SLD_EVENT_VALUE_CHANGED` | 值变化(拖动中持续触发) | | 样式宏 | 说明 | |--------|------| | `SLD_STYLE_3D` | 3D 立体边框 | | `SLD_STYLE_NO_BORDERS` | 无边框 | | `SLD_STYLE_NO_FILL` | 不填充背景 | 核心 API:`UG_SliderCreate / Delete / Show / Hide`、 `SetValue / GetValue / SetRange / SetAlign / SetStyle`、 `SetForeColor / SetBackColor`、`GetAlign / GetStyle`。 ### 7.5 下拉列表(ugui_dropdown) 点击展开/收起列表,支持最多 16 个选项: ```c UG_DROPDOWN dd; const char *fruits[] = { "Apple", "Banana", "Cherry", "Date" }; UG_DropdownCreate( &dd, DD_ID_0, 10, 130, 120, 24, &font_8x8 ); UG_DropdownSetItems( &dd, fruits, 4 ); /* 设置选项列表 */ UG_DropdownSetSelected( &dd, 0 ); /* 默认选第 0 项 */ UG_DropdownShow( &dd ); /* 回调中读取选中索引 */ UG_U8 sel = UG_DropdownGetSelected( &dd ); /* 返回 0~3 */ ``` | 事件宏 | 说明 | |--------|------| | `DD_EVENT_CLICKED` | 点击 header(= `OBJ_EVENT_CLICKED`) | | `DD_EVENT_EXPANDED` | 展开列表 | | `DD_EVENT_COLLAPSED` | 收起列表 | | `DD_EVENT_SELECTED` | 选中某项(= `OBJ_EVENT_SELECTED`) | | 样式宏 | 说明 | |--------|------| | `DD_STYLE_3D` | 3D 立体边框 | | `DD_STYLE_NO_BORDERS` | 无边框 | 核心 API:`UG_DropdownCreate / Delete / Show / Hide`、 `SetItems / SetSelected / GetSelected / GetItemCount`、 `SetForeColor / SetBackColor / SetAlternateForeColor / SetAlternateBackColor`、 `SetFont / SetStyle / SetAlignment`。 ### 7.6 数字时钟(ugui_digitalclock) 以文本形式显示 `hh:mm:ss`,支持 12/24 小时制、字体、颜色与对齐;时间变化时触发 `VALUE_CHANGED`(=`OBJ_EVENT_VALUE_CHANGED`)。 ```c UG_DIGITALCLOCK dc; UG_DigitalClockCreate( &dc, DC_ID_0, 10, 10, 120, 40, &wnd, &font_12x16 ); UG_DigitalClockSetTime( &dc, 13, 45, 30 ); /* 13:45:30 */ UG_DigitalClockSetFormat( &dc, 1 ); /* 1 = 12 小时制(0 = 24 小时制) */ UG_DigitalClockSetForeColor( &dc, C_WHITE ); UG_DigitalClockSetBackColor( &dc, C_BLACK ); UG_DigitalClockSetAlignment( &dc, ALIGN_CENTER ); UG_DigitalClockShow( &dc ); ``` | 样式宏 | 说明 | |--------|------| | `DC_STYLE_2D`(默认) | 平面 | | `DC_STYLE_3D` | 3D 边框 | | `DC_STYLE_NO_BGD` | 不画背景 | 核心 API:`UG_DigitalClockCreate / Delete / Show / Hide`、`SetTime(h,m,s)`、 `SetForeColor / SetBackColor / SetFont / SetAlignment / SetFormat`。 ### 7.7 模拟时钟(ugui_analogclock) 圆形表盘 + 时针/分针/秒针,支持自定义边框、表盘与各指针颜色,以及 3D 样式;时间变化时 触发 `VALUE_CHANGED`。 ```c UG_ANALOGCLOCK ac; UG_AnalogClockCreate( &ac, AC_ID_0, 10, 10, 110, 110, &wnd ); UG_AnalogClockSetTime( &ac, 10, 9, 0 ); /* 10:09:00 */ UG_AnalogClockSetColors( &ac, C_NAVY, C_WHITE, /* 边框/表盘 */ C_RED, C_GREEN, C_BLUE ); /* 时/分/秒针 */ UG_AnalogClockSetStyle( &ac, AC_STYLE_3D ); UG_AnalogClockShow( &ac ); ``` | 样式宏 | 说明 | |--------|------| | `AC_STYLE_3D` | 3D 立体边框(默认 3D) | 核心 API:`UG_AnalogClockCreate / Delete / Show / Hide`、`SetTime(h,m,s)`、 `SetColors(fc,bc,hc,mc,sc)`、`SetStyle`。 ### 7.8 日期(ugui_date) 以文本显示 `YYYY-MM-DD`,分隔符可配置(默认 `-`),支持字体、颜色与对齐;日期变化时触发 `VALUE_CHANGED`。 ```c UG_DATE dt; UG_DateCreate( &dt, DT_ID_0, 10, 150, 130, 180, &wnd, &font_12x16 ); UG_DateSetDate( &dt, 2026, 3, 20 ); /* 2026-03-20 */ UG_DateSetSeparator( &dt, '/' ); /* 改为 2026/03/20 */ UG_DateSetForeColor( &dt, C_WHITE ); UG_DateSetBackColor( &dt, C_BLACK ); UG_DateShow( &dt ); ``` | 样式宏 | 说明 | |--------|------| | `DT_STYLE_2D`(默认) | 平面 | | `DT_STYLE_3D` | 3D 边框 | | `DT_STYLE_NO_BGD` | 不画背景 | 核心 API:`UG_DateCreate / Delete / Show / Hide`、`SetDate(y,m,d)`、 `SetForeColor / SetBackColor / SetFont / SetAlignment / SetSeparator`。 ### 7.9 表格(ugui_table) 固定行列网格(最多 12 行 × 6 列 × 16 字符/格),逐单元格文本,支持表头、网格线与选中行高亮; 选中行变化触发 `SELECTED`(=`OBJ_EVENT_SELECTED`)。 ```c UG_TABLE tb; UG_TableCreate( &tb, TB_ID_0, 10, 10, 200, 120, &wnd ); UG_TableSetSize( &tb, 4, 3 ); /* 4 行 3 列 */ UG_TableSetCell( &tb, 0, 0, "Name" ); /* 填充单元格 */ UG_TableSetCell( &tb, 1, 0, "Alice" ); UG_TableSetSelected( &tb, 1 ); /* 高亮第 1 行 */ UG_TableSetHeaderColors( &tb, C_WHITE, C_NAVY ); /* 表头前景/背景 */ UG_TableSetSelectColors( &tb, C_WHITE, C_RED ); /* 选中行前景/背景 */ UG_TableShow( &tb ); ``` | 样式宏 | 说明 | |--------|------| | `TB_STYLE_2D`(默认) | 平面 | | `TB_STYLE_3D` | 3D 边框 | | `TB_STYLE_HEADER` | 显示表头行 | 核心 API:`UG_TableCreate / Delete / Show / Hide`、`SetSize(rows,cols)`、`SetCell(r,c,text)`、 `SetSelected(row)`、`SetForeColor / SetBackColor / SetHeaderColors / SetSelectColors`、`SetFont / SetStyle`。 ### 7.10 图表(ugui_chart) 支持折线图(`CH_MODE_LINE`)与柱状图(`CH_MODE_BAR`),最多 16 个数据点,可设颜色、量程与 3D 样式,适用于轻量数据可视化。 ```c UG_CHART ch; UG_U8 vals[4] = { 20, 45, 30, 60 }; UG_ChartCreate( &ch, CH_ID_0, 10, 10, 200, 120, &wnd ); UG_ChartSetMode( &ch, CH_MODE_BAR ); /* 柱状图 */ UG_ChartSetRange( &ch, 100 ); /* 满量程 100 */ UG_ChartSetData( &ch, vals, 4 ); /* 4 个数据点 */ UG_ChartSetColors( &ch, C_BLACK, C_WHITE, C_RED ); /* 边框/背景/数据系列 */ UG_ChartSetStyle( &ch, CH_STYLE_3D ); UG_ChartShow( &ch ); ``` | 样式宏 | 说明 | |--------|------| | `CH_STYLE_2D`(默认) | 平面 | | `CH_STYLE_3D` | 3D 立体 | | `CH_MODE_LINE` | 折线图 | | `CH_MODE_BAR` | 柱状图 | 核心 API:`UG_ChartCreate / Delete / Show / Hide`、`SetData(values,count)`、`SetMode(mode)`、 `SetRange(vmax)`、`SetColors(fc,bc,dc)`、`SetStyle`。 ### 7.11 日历控件(ugui_calendar) 月视图日历:标题栏显示 `年-月` 标题并带 `<` / `>` 翻页箭头,下方为周表头(周末红字)与 6×7 日期网格;支持选中高亮(青绿圆角矩形填充)、今天高亮(淡紫填充 + 紫色描边)、 跨月(前置/后置)日期灰显、自定义标题格式与周名(可注入中文/日文)、一周首日可设为 周一或周日。翻页与选日均通过窗口回调上报 `CAL_EVENT_*` 事件。 ```c UG_CALENDAR cal; UG_CalendarCreate( &wnd, &cal, CAL_ID_0, 0, 2, 318, 205 ); /* 相对窗口坐标 */ UG_CalendarSetFont( &wnd, CAL_ID_0, &FONT_12X16 ); UG_CalendarSetToday( &wnd, CAL_ID_0, 2026, 8, 27 ); /* "今天"高亮(淡紫) */ UG_CalendarSetDate( &wnd, CAL_ID_0, 2026, 8, 3 ); /* 初始选中 8/3(青绿) */ UG_CalendarSetWeekdayFirst( &wnd, CAL_ID_0, CAL_WEEKDAY_FIRST_MONDAY ); /* 周一为首(默认) */ UG_CalendarShow( &wnd, CAL_ID_0 ); /* 翻页(也可点击标题栏 < / > 箭头) */ UG_CalendarGotoPrevMonth( &wnd, CAL_ID_0 ); UG_CalendarGotoNextMonth( &wnd, CAL_ID_0 ); /* 回调中读取选中日期 / 当前显示月份 */ UG_U16 yr = UG_CalendarGetYear( &wnd, CAL_ID_0 ); UG_U8 mo = UG_CalendarGetMonth( &wnd, CAL_ID_0 ); UG_U8 day = UG_CalendarGetSelectedDay( &wnd, CAL_ID_0 ); ``` | 事件宏 | 说明 | |--------|------| | `CAL_EVENT_CLICKED` | 点击某一日期单元格(= `OBJ_EVENT_CLICKED`) | | `CAL_EVENT_SELECTED` | 选中日期变化(点击非当前选中日,= `OBJ_EVENT_SELECTED`) | | `CAL_EVENT_MONTH_CHANGED` | 翻到其它月份(= `OBJ_EVENT_VALUE_CHANGED \| (1<<4)`) | | `CAL_EVENT_YEAR_CHANGED` | 翻页跨年(= `OBJ_EVENT_VALUE_CHANGED \| (1<<5)`) | | 样式宏 | 说明 | |--------|------| | `CAL_STYLE_2D`(默认) | 平面 | | `CAL_STYLE_3D` | 3D 立体边框 | | 一周首日宏 | 说明 | |------------|------| | `CAL_WEEKDAY_FIRST_MONDAY`(默认,0) | 周一为首(中式/欧式) | | `CAL_WEEKDAY_FIRST_SUNDAY`(1) | 周日为首(美式) | 默认配色:选中态 `hl_bg = C_MEDIUM_SEA_GREEN`(青绿)、今天 `td_bg = C_LAVENDER`(淡紫) + `td_bd = td_fc = C_MEDIUM_PURPLE`(紫边/紫字)、周末 `wfc = C_RED`(红字)、 跨月 `dim_fc = C_LIGHT_GRAY`、标题栏 `tl_bg = C_GAINSBORO`。 核心 API:`UG_CalendarCreate / Delete / Show / Hide`、`SetDate(y,m,d)` / `SetMonth(y,m)` / `SetToday(y,m,d)`、`SetBackColor / SetForeColor / SetWeekendColor / SetDimColor`、 `SetTitleColors(bg,fg,arrow)` / `SetWeekdayColors(bg,fc,wfc)` / `SetHighlightColors(bg,fg)` / `SetTodayColors(bg,fg,border)`、`SetFont / SetTitleFormat / SetWeekdayNames / SetWeekdayFirst`、 `GotoPrevMonth / GotoNextMonth / GotoToday`、`GetYear / GetMonth / GetSelectedDay`。 ### 7.12 回调消息处理示例 ```c static void window_callback( UG_MESSAGE *msg ) { if ( msg->type == MSG_TYPE_OBJECT ) { switch ( msg->id ) /* msg.id = 控件类型 */ { case OBJ_TYPE_IMAGEBUTTON: if ( msg->event == OBJ_EVENT_CLICKED ) printf( "image button id=%d clicked\n", msg->sub_id ); break; case OBJ_TYPE_TOGGLE: if ( msg->event == TG_EVENT_ON ) printf( "toggle id=%d -> ON\n", msg->sub_id ); else if ( msg->event == TG_EVENT_OFF ) printf( "toggle id=%d -> OFF\n", msg->sub_id ); break; case OBJ_TYPE_SLIDER: if ( msg->event == SLD_EVENT_VALUE_CHANGED ) printf( "slider id=%d = %d\n", msg->sub_id, UG_SliderGetValue(...) ); break; case OBJ_TYPE_DROPDOWN: if ( msg->event == DD_EVENT_SELECTED ) printf( "dropdown id=%d selected index=%d\n", msg->sub_id, UG_DropdownGetSelected(...) ); break; case OBJ_TYPE_TABLE: if ( msg->event == TB_EVENT_SELECTED ) printf( "table id=%d selected row=%d\n", msg->sub_id, UG_TableGetSelected(...) ); break; case OBJ_TYPE_DIGITALCLOCK: case OBJ_TYPE_ANALOGCLOCK: case OBJ_TYPE_DATE: if ( msg->event == OBJ_EVENT_VALUE_CHANGED ) printf( "clock/date id=%d ticked\n", msg->sub_id ); break; case OBJ_TYPE_CALENDAR: if ( msg->event == CAL_EVENT_SELECTED ) printf( "calendar id=%d selected day=%d\n", msg->sub_id, UG_CalendarGetSelectedDay(...) ); else if ( msg->event == CAL_EVENT_MONTH_CHANGED ) printf( "calendar id=%d -> %04u-%02u\n", msg->sub_id, UG_CalendarGetYear(...), UG_CalendarGetMonth(...) ); break; } } } ``` --- ## 8. 与原版 V0.31 的差异 1. **文件拆分**:`ugui.c/h` → 19 个功能模块 + `ugui_prv.h` 内部共享头。所有 API、结构体、 行为不变;`gui` 全局指针由 `static` 改为库内共享(`ugui_prv.h` 中 `extern`,应用仍应通过 `UG_SelectGUI()` 切换)。 2. **新增显示移植层** `ugui_port.h/.c`:屏型宏 + 4 个硬件钩子 + 颜色转换 + 单色帧缓冲 + 填充加速自动注册。 3. **新增输入移植层** `ugui_input.h/.c`:触摸/鼠标/虚拟键盘归一为触摸事件。 4. **新增鼠标指针模块** `ugui_cursor.h/.c`:2bpp 形状位图(内置箭头/十字)+ 前景/背景色, `UG_CursorEnable/Disable` 随时开关;指针绘制在 `UG_Update()` 末尾叠加于所有窗口之上, 移动时窗口区触发重绘、桌面区回填桌面色实现背景恢复,不依赖显示读回;BW 模式下 指针颜色按亮度阈值自动黑/白化。 5. **新增主题系统** `ugui_theme.h`:灰度色阶(C_WHITE_39/41/63/89/94)、默认色 (C_DESKTOP_COLOR/C_FORE_COLOR/C_BACK_COLOR/C_TITLE_*_COLOR)、调色板宏 (C_PAL_WINDOW/C_PAL_BUTTON_PRESSED/C_PAL_BUTTON_RELEASED/C_PAL_PROGRESS)。 所有控件的 3D 边框色不再硬编码,而是通过主题宏统一管理,修改一处即可全局换色。 支持 BW(黑白)模式:`UG_COLOR` 退化为 `UG_U8`,`ugui_color.h` 将全部命名颜色 按亮度阈值(ITU-R BT.601)映射为 `C_BLACK`(0x00)/`C_WHITE`(0xFF),3D 边框用黑白 交替产生立体感。`ugui_text.c` 8BPP 抗锯齿字体在 BW 模式改用阈值抖动替代颜色混合。 `ugui_port.c` 的 `UG_PORT_ColorToNative` 在 BW 模式下将 0x00/0xFF 直接映射为 屏幕原生黑/白,不再经过无效的 888→565 转换。 6. **新增进度条控件** `ugui_progressbar.h/.c`:`UG_PROGRESS` 结构体(style/fc/bc/progress), 支持 `PGB_STYLE_2D`/`PGB_STYLE_3D`/`PGB_STYLE_NO_BORDERS`/`PGB_STYLE_FORE_COLOR_MESH`/ `PGB_STYLE_NO_FILL` 样式组合。智能重绘:进度增加时仅画新增区域(增量更新), 进度减少时全量重绘(含背景恢复)。API 与其他控件一致(Create/Delete/Show/Hide/Set*/Get*)。 7. **修复 CLICKED 事件 bug**:原版 `_UG_ButtonUpdate`/`_UG_CheckboxUpdate` 中, CLICKED 事件在同一更新周期内被 PRESSED 或 RELEASED 覆盖——按下后释放在同一对象上 只能收到 RELEASED,永远收不到 CLICKED。已重构 if-else 链使 CLICKED 优先, 并在检测到点击时清除 pressed 状态。复选框的 `checked` 切换也移至 CLICKED 分支 (原版在 RELEASED 分支,导致在对象外释放也会切换状态)。 8. **修复上游 msg.src bug**:原版 `_UG_HandleEvents` 中 `msg.src = &obj;` 把栈上局部变量地址 传给窗口回调(未定义行为),已改为 `msg.src = obj;`。回调中对象类型/ID 用 `msg.id` / `msg.sub_id`,`msg.src` 现可安全转换为 `UG_OBJECT*`(测试已回归覆盖)。 9. **新增图片按钮控件** `ugui_imagebutton.h/.c`:在普通按钮基础上叠加 BMP 图标 (`UG_DrawBMP`,仅 `BMP_BPP_16` RGB565),支持图左文右(`IBTN_IMG_LEFT`)/ 图上文下 (`IBTN_IMG_TOP`)两种排列、3D/2D/无边框/不填充样式、备用色切换(`TOGGLE_COLORS`)。 事件处理与按钮一致(CLICKED/PRESSED/RELEASED)。 10. **新增开关按钮控件** `ugui_toggle.h/.c`:iOS 风格圆角轨道 + 圆形滑块,点击即切换 ON/OFF,ON 时轨道为 `track_on` 色(默认绿),OFF 时为 `track_off` 色(默认灰)。 支持 `TG_EVENT_ON`/`TG_EVENT_OFF` 事件、可选 ON/OFF 文字标签、3D/无边框样式。 11. **新增滑块控件** `ugui_slider.h/.c`:水平/垂直方向可拖动滑块,拖动时读 `gui->touch.xp/yp` 换算 value 并持续触发 `SLD_EVENT_VALUE_CHANGED`。支持 min/max 范围设定、 3D/2D/无边框/不填充样式。 12. **新增下拉列表控件** `ugui_dropdown.h/.c`:点击 header 切换展开/收起,展开时读 `gui->touch` 命中列表项选中并触发 `DD_EVENT_SELECTED`。支持最多 16 个选项 (`UG_DROPDOWN.items[16]`)、3D/无边框样式、备用色(展开项高亮)。 13. **新增验证工程**:`pc_simulator/` 下三屏型分工程(`mono_128x64` / `rgb565_320x240` / `rgb888_480x320`,见 6.3 节)与 `outputs/` 下的无头渲染工具(见 6.4 节)。前者用 SDL2 验证三种屏型下全部新控件 + 基础控件;后者无需 SDL,直接渲染为 PNG,适合无图形环境。 14. **鼠标指针改为空心化**:`ugui_cursor.c` 内置箭头/十字位图由实心填充改为空心轮廓, 保持 2bpp 机制不变(前景像素形成轮廓而非实心)。指针尺寸按屏幕分辨率自适应:屏宽 < 400 用 8×8,否则用 16×16(见 `ugui_config.h` 的 `UG_CURSOR_SMALL_THRESHOLD`)。 15. **可配置宏集中到 `ugui_config.h`**:屏型宏(`UG_PORT_DISPLAY_MONO/RGB565/RGB888`)、 屏幕分辨率(`UG_LCD_WIDTH/HEIGHT`)、鼠标指针自适应阈值(`UG_CURSOR_SMALL_THRESHOLD`)、 单色屏参数(`UG_PORT_MONO_FB_SIZE/THRESHOLD/INVERT`)、输入方向变换 (`UG_IN_SWAP_XY/FLIP_X/FLIP_Y`)、鼠标速度/键盘步进等全部收拢到 `ugui_config.h`, 各源文件保留 `#ifndef` 默认值以向后兼容(编译器 `-D` 或 `ugui_config.h` 均可覆盖)。 16. **新增三屏型分工程 demo** `pc_simulator/mono_128x64/`、`pc_simulator/rgb565_320x240/`、 `pc_simulator/rgb888_480x320/`:分别验证三种屏型下全部新控件 + 基础控件。每个工程文件结构相同 (`main.c` + `sim_display` + `sim_input` + `sim_ui` + `CMakeLists.txt`),ugui 界面代码与 显示/输入移植分文件存放,可在 shell 终端直接用 CMake 编译(见 6.4 节)。 17. 原始文件完整保留在 `original_v0.31/`,可随时对照。 18. **移植层与硬件解耦**:`ugui_port.c` / `ugui_input.c` 移除全部 `UG_PORT_HW_*` / `UG_IN_HW_Update` 硬件钩子的默认桩实现,只保留与硬件无关的适配核心(颜色转换基类、 单色帧缓冲、pset、DRIVER_FILL_FRAME 适配器、UG_PORT_Init、输入标定/变换/聚合逻辑)。 所有硬件钩子改由移植模板 `templates/port_display_template.c` / `port_input_template.c` (或用户工程)提供;库不再提供默认桩,未实现时链接器报错提醒补全移植。 19. **新增数字时钟控件** `ugui_digitalclock.h/.c`(`OBJ_TYPE_DIGITALCLOCK=14`):以文本显示 `hh:mm:ss`,支持 12/24 小时制、字体、颜色与对齐;时间变化时触发 `VALUE_CHANGED`。 20. **新增模拟时钟控件** `ugui_analogclock.h/.c`(`OBJ_TYPE_ANALOGCLOCK=15`):圆形表盘 + 时针/分针/秒针(基于 `sin/cos` 计算指针),支持自定义边框/表盘/各指针颜色与 3D 样式; 时间变化时触发 `VALUE_CHANGED`。 21. **新增日期控件** `ugui_date.h/.c`(`OBJ_TYPE_DATE=16`):以文本显示 `YYYY-MM-DD`,分隔符 可配置(默认 `-`),支持字体、颜色与对齐;日期变化时触发 `VALUE_CHANGED`。 22. **新增表格控件** `ugui_table.h/.c`(`OBJ_TYPE_TABLE=17`):固定行列网格(最多 12×6), 逐单元格文本,支持表头、网格线与选中行高亮(样式 `TB_STYLE_HEADER`); 选中行变化触发 `SELECTED`。 23. **新增图表控件** `ugui_chart.h/.c`(`OBJ_TYPE_CHART=18`):支持折线图(`CH_MODE_LINE`)与 柱状图(`CH_MODE_BAR`),最多 16 个数据点,可设颜色、量程与 3D 样式,适用于轻量数据可视化。 24. **新增日历控件** `ugui_calendar.h/.c`(`OBJ_TYPE_CALENDAR=19`):月视图日历,标题栏显示 `年-月` 并带 `<`/`>` 翻页箭头,周表头(周末红色)、6×7 日期网格;支持选中高亮 (青绿圆角矩形)、今天高亮(淡紫填充 + 紫色描边)、跨月日期灰显、自定义标题格式 与周名(可注入中文/日文)、一周首日可设为周一或周日。`today_year` 字段为 `UG_U16` 以容纳 4 位年份(避免年份被 `UG_U8` 截断导致"今天"高亮失效)。翻页与选日均通过窗口 回调上报 `CAL_EVENT_*` 事件。 --- *µGUI V0.31 © 2015 Achim Döbler(原始版权与许可见 `LICENSE.md`)。模块化拆分与移植层为本工程新增。*