完成一个主界面开发流程

This commit is contained in:
2026-09-03 23:08:16 +08:00
parent 52d7dc7d4d
commit 89462d8aa1
21 changed files with 2177 additions and 198 deletions
+144 -123
View File
@@ -1,7 +1,7 @@
/*******************************************************************************
* Size: 12 px
* Bpp: 1
* Opts: --font C:\Users\16241\AppData\Local\Temp\simsun_regular.ttf --size 12 --bpp 1 -r 0x20-0x7E --symbols 主事交件位作信值元入全出制功动占厂压参双变号告复字定实家开强归录恢息手护据操故数时显未本查检波流测版现电界直看示系络维网置能自节装警认讯记设询调通遥部配量间零面默() --no-compress --format lvgl --lv-font-name ui_font_cn_12 -o D:\Code\DTU 程序\STM32F4-Base\Drivers\BSP\UI\ui_font_cn_12.c
* Opts: --font C:\Users\Administrator\AppData\Local\Temp\simsun_regular.ttf --size 12 --bpp 1 -r 0x20-0x7E --symbols ▶三主事交件位作信值元入全出制前动厂压参双变号告回复字定实家开式强归当录恢息手护据操故数无时显本查检模波流测版电界直看示系络维网置自节装警认讯记设询调返通遥部配量间零面默: --no-compress --format lvgl --lv-font-name ui_font_cn_12 -o D:\Code\DTU Code\STM32F4-Base\Drivers\BSP\UI\ui_font_cn_12.c
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -308,6 +308,10 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+007E "~" */
0x40, 0x81, 0x80,
/* U+4E09 "三" */
0x7f, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f,
0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xff, 0x80,
/* U+4E3B "主" */
0x8, 0x0, 0x80, 0x0, 0x3f, 0xe0, 0x40, 0x8,
0x1, 0x3, 0xfe, 0x4, 0x0, 0x80, 0x10, 0x7f,
@@ -372,9 +376,9 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x5f, 0x4a, 0xa9, 0x55, 0x2a, 0x85, 0x70, 0x88,
0x70,
/* U+529F "功" */
0x2, 0x0, 0x43, 0xe8, 0x13, 0xf2, 0x22, 0x44,
0x48, 0x89, 0x11, 0x3c, 0x38, 0x84, 0x20, 0x88,
/* U+524D "前" */
0x20, 0x82, 0x23, 0xff, 0x80, 0x7, 0xc4, 0x8a,
0x9f, 0x52, 0x2a, 0x7d, 0x48, 0xa9, 0x11, 0x26,
0x60,
/* U+52A8 "动" */
@@ -382,10 +386,6 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x50, 0x4a, 0x89, 0x89, 0x3f, 0x44, 0x8, 0x82,
0x60,
/* U+5360 "占" */
0x8, 0x2, 0x0, 0x80, 0x3f, 0x8, 0x2, 0xf,
0xfa, 0x2, 0x80, 0xa0, 0x2f, 0xfa, 0x2,
/* U+5382 "厂" */
0x7f, 0xe8, 0x1, 0x0, 0x20, 0x4, 0x0, 0x80,
0x10, 0x2, 0x0, 0x40, 0x8, 0x2, 0x0, 0x0,
@@ -418,6 +418,10 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xc0, 0x3, 0xfe, 0x40, 0x48, 0x9, 0xff, 0x20,
0x20,
/* U+56DE "回" */
0xff, 0xe0, 0x18, 0x6, 0x79, 0x92, 0x64, 0x99,
0xe6, 0x1, 0x80, 0x7f, 0xf8, 0x4,
/* U+590D "复" */
0x3f, 0xe8, 0x2, 0xff, 0x10, 0x23, 0xfc, 0x40,
0x8f, 0xf0, 0x80, 0x3f, 0xca, 0x10, 0x3c, 0x38,
@@ -447,6 +451,11 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x7f, 0xc2, 0x20, 0x44, 0x8, 0x81, 0x11, 0xff,
0xc4, 0x40, 0x88, 0x21, 0x4, 0x21, 0x4, 0x0,
/* U+5F0F "式" */
0x1, 0x40, 0x24, 0x4, 0x7f, 0xf0, 0x10, 0x2,
0x1f, 0x40, 0x88, 0x10, 0x82, 0x14, 0x79, 0xf0,
0x10,
/* U+5F3A "强" */
0x7, 0xdc, 0x88, 0x91, 0x13, 0xee, 0x11, 0x1f,
0xe2, 0x4f, 0x49, 0x2f, 0xe4, 0x20, 0x85, 0x67,
@@ -457,6 +466,10 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xe4, 0xc, 0x81, 0x90, 0x24, 0x4, 0x9f, 0xa0,
0x10,
/* U+5F53 "当" */
0x8, 0x44, 0x52, 0x41, 0xf, 0xf8, 0x4, 0x2,
0xff, 0x0, 0x80, 0x7f, 0xe0, 0x10,
/* U+5F55 "录" */
0x7f, 0x80, 0x10, 0xfe, 0x0, 0x4f, 0xfe, 0x8,
0x11, 0x11, 0x74, 0x15, 0x4, 0x93, 0x11, 0x86,
@@ -502,6 +515,10 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xa5, 0x51, 0xa, 0xf9, 0x49, 0x10, 0xc5, 0x65,
0x10,
/* U+65E0 "无" */
0x7f, 0xc0, 0x80, 0x10, 0x2, 0xf, 0xfe, 0x14,
0x2, 0x80, 0x90, 0x22, 0x28, 0x46, 0x7, 0x80,
/* U+65F6 "时" */
0x0, 0x5e, 0xa, 0x41, 0x4b, 0xf9, 0x5, 0xe0,
0xa4, 0x94, 0x8a, 0x91, 0x5e, 0xa, 0x41, 0x0,
@@ -511,11 +528,6 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x7f, 0xc8, 0x9, 0xff, 0x20, 0x27, 0xfc, 0x22,
0x24, 0x4a, 0x8a, 0x31, 0x82, 0x23, 0xff, 0x80,
/* U+672A "未" */
0x4, 0x0, 0x81, 0xff, 0x2, 0x0, 0x41, 0xff,
0xc3, 0x80, 0xa8, 0x24, 0x98, 0x8c, 0x10, 0x2,
0x0,
/* U+672C "本" */
0x4, 0x0, 0x83, 0xff, 0x82, 0x0, 0xe0, 0x2a,
0x5, 0x41, 0x24, 0x44, 0x57, 0xf4, 0x10, 0x2,
@@ -531,6 +543,11 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x9c, 0x5, 0x12, 0x29, 0x44, 0xa8, 0x92, 0x17,
0xf0,
/* U+6A21 "模" */
0x22, 0x85, 0xfc, 0x8a, 0x7b, 0xe2, 0x44, 0xcf,
0x9d, 0x15, 0x3e, 0x21, 0x5, 0xfc, 0x8a, 0x16,
0x30,
/* U+6CE2 "波" */
0x1, 0x10, 0x21, 0x7f, 0x88, 0x91, 0x11, 0x3f,
0x95, 0x10, 0xa2, 0x12, 0x8a, 0x22, 0x4a, 0x16,
@@ -551,10 +568,6 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x51, 0x4b, 0xaa, 0x55, 0x4a, 0x91, 0x65, 0x49,
0x10,
/* U+73B0 "现" */
0xf7, 0xc4, 0x88, 0x95, 0x12, 0xaf, 0x54, 0x4a,
0x89, 0x51, 0x14, 0x32, 0x98, 0x94, 0x21, 0x80,
/* U+7535 "电" */
0x8, 0x1, 0x3, 0xfe, 0x44, 0x48, 0x89, 0xff,
0x22, 0x24, 0x44, 0xff, 0x91, 0x4, 0x20, 0x83,
@@ -603,11 +616,6 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xf, 0xe1, 0x4, 0x20, 0x84, 0x90, 0x92, 0x7f,
0xf0,
/* U+80FD "能" */
0x22, 0x49, 0x53, 0xec, 0x1, 0x17, 0x9e, 0x90,
0x1e, 0x82, 0x52, 0x7a, 0x89, 0x65, 0x28, 0xac,
0xf0,
/* U+81EA "自" */
0x8, 0x8, 0x3f, 0xf0, 0x18, 0xf, 0xfe, 0x3,
0x1, 0xff, 0xc0, 0x60, 0x3f, 0xf0,
@@ -657,6 +665,11 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xd2, 0xa, 0x5d, 0x4a, 0xad, 0x75, 0x20, 0x88,
0x70,
/* U+8FD4 "返" */
0x40, 0xe5, 0xe0, 0x20, 0x7, 0xfe, 0x82, 0x54,
0x4a, 0x51, 0x44, 0x29, 0x46, 0x45, 0x40, 0x47,
0xf0,
/* U+901A "通" */
0x9f, 0x88, 0x90, 0xc, 0xf, 0xed, 0x24, 0xbf,
0x94, 0x92, 0xfe, 0x52, 0x4a, 0x59, 0x0, 0x5f,
@@ -699,11 +712,12 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x3f, 0x20, 0x84, 0x1d, 0x5c, 0x29, 0x55, 0x55,
0x10,
/* U+FF08 "(" */
0x29, 0x49, 0x22, 0x44,
/* U+FF1A ":" */
0xf0, 0xf0,
/* U+FF09 ")" */
0x89, 0x12, 0x4a, 0x50
/* U+25B6 "▶"(来源:Segoe UI Symbol;宋体无此字形) */
0x80, 0xe0, 0xf0, 0xfc, 0xff, 0xfe, 0xf8, 0xf0,
0xc0, 0x0
};
@@ -808,89 +822,92 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 476, .adv_w = 96, .box_w = 1, .box_h = 13, .ofs_x = 3, .ofs_y = -2},
{.bitmap_index = 478, .adv_w = 96, .box_w = 3, .box_h = 12, .ofs_x = 1, .ofs_y = -1},
{.bitmap_index = 483, .adv_w = 96, .box_w = 6, .box_h = 3, .ofs_x = 0, .ofs_y = 8},
{.bitmap_index = 486, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 503, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 520, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 537, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 554, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 571, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 588, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 605, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 622, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 638, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 655, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 672, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 689, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 706, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 723, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 740, .adv_w = 192, .box_w = 10, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 755, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 771, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 787, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 804, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 820, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 837, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 854, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 871, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 888, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 905, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 922, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 939, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 956, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 972, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 989, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1006, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1023, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1040, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1057, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1074, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1091, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1108, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1125, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1142, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1159, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1176, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1192, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1209, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1226, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1243, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1260, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1277, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1294, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1311, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1328, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1344, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1361, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1378, .adv_w = 192, .box_w = 10, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1393, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1410, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1426, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1443, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1460, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1477, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1494, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1511, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1528, .adv_w = 192, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1542, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1559, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1576, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1593, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1610, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1627, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1644, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1661, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1678, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1695, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1712, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1729, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1746, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1762, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1779, .adv_w = 192, .box_w = 10, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1794, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1811, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1827, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1844, .adv_w = 192, .box_w = 3, .box_h = 10, .ofs_x = 6, .ofs_y = -1},
{.bitmap_index = 1848, .adv_w = 192, .box_w = 3, .box_h = 10, .ofs_x = 2, .ofs_y = -1}
{.bitmap_index = 486, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 502, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 519, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 536, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 553, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 570, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 587, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 604, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 621, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 638, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 654, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 671, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 688, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 705, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 722, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 739, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 756, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 772, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 788, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 805, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 821, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 838, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 855, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 872, .adv_w = 192, .box_w = 10, .box_h = 11, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 886, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 903, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 920, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 937, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 954, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 971, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 987, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1004, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1021, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1038, .adv_w = 192, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1052, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1069, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1086, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1103, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1120, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1137, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1154, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1171, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1188, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1205, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1221, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1238, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1254, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1271, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1288, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1305, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1322, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1339, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1356, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1373, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1390, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1407, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1424, .adv_w = 192, .box_w = 10, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1439, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1456, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1472, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1489, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1506, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1523, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1540, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1557, .adv_w = 192, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1571, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1588, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1605, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1622, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1639, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1656, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1673, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1690, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1707, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1724, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1741, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1758, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1775, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1792, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1808, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1825, .adv_w = 192, .box_w = 10, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1840, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1857, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1873, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1890, .adv_w = 192, .box_w = 2, .box_h = 6, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1892, .adv_w = 165, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1} /* U+25B6 ▶ */
};
/*---------------------
@@ -898,17 +915,17 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
*--------------------*/
static const uint16_t unicode_list_1[] = {
0x0, 0x50, 0x69, 0xbb, 0x112, 0x121, 0x1a6, 0x201,
0x308, 0x32a, 0x32d, 0x3bf, 0x3fb, 0x464, 0x46d, 0x525,
0x547, 0x550, 0x587, 0x591, 0x59d, 0x5bc, 0x60f, 0xad2,
0xd1c, 0xd5f, 0xd63, 0xd7b, 0x10c5, 0x10ff, 0x1117, 0x111a,
0x1227, 0x1234, 0x1410, 0x1469, 0x1533, 0x1692, 0x170a, 0x1735,
0x17bb, 0x1803, 0x18ef, 0x18f1, 0x19aa, 0x1a85, 0x1ea7, 0x1f06,
0x1f10, 0x240d, 0x2575, 0x26fa, 0x2711, 0x28b9, 0x28d0, 0x2aff,
0x2ec0, 0x30a1, 0x30b9, 0x3116, 0x3133, 0x32c2, 0x33af, 0x3447,
0x3a8a, 0x3d2b, 0x3d69, 0x3d74, 0x3d75, 0x3d83, 0x3da7, 0x3dc8,
0x41df, 0x422a, 0x42ad, 0x4312, 0x4394, 0x47b9, 0x48bb, 0x4927,
0x509d, 0xb0cd, 0xb0ce
0x0, 0x32, 0x82, 0x9b, 0xed, 0x144, 0x153, 0x1d8,
0x233, 0x33a, 0x35c, 0x35f, 0x3f1, 0x42d, 0x444, 0x49f,
0x579, 0x582, 0x5b9, 0x5c3, 0x5cf, 0x5ee, 0x641, 0x8d5,
0xb04, 0xd4e, 0xd91, 0xd95, 0xdad, 0x10f7, 0x1106, 0x1131,
0x1149, 0x114a, 0x114c, 0x1259, 0x1266, 0x1442, 0x149b, 0x1565,
0x16c4, 0x173c, 0x1767, 0x17d7, 0x17ed, 0x1835, 0x1923, 0x19dc,
0x1ab7, 0x1c18, 0x1ed9, 0x1f38, 0x1f42, 0x243f, 0x272c, 0x2743,
0x28eb, 0x2902, 0x2b31, 0x2ef2, 0x30d3, 0x30eb, 0x3148, 0x3165,
0x33e1, 0x3479, 0x3abc, 0x3d5d, 0x3d9b, 0x3da6, 0x3da7, 0x3db5,
0x3dd9, 0x3dfa, 0x41cb, 0x4211, 0x425c, 0x42df, 0x4344, 0x43c6,
0x47eb, 0x48ed, 0x4959, 0x50cf, 0xb111
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -919,8 +936,12 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
},
{
.range_start = 20027, .range_length = 45263, .glyph_id_start = 96,
.unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 83, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.range_start = 19977, .range_length = 45330, .glyph_id_start = 96,
.unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 85, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
},
{
.range_start = 9654, .range_length = 1, .glyph_id_start = 181,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
}
};
@@ -945,7 +966,7 @@ static lv_font_fmt_txt_dsc_t font_dsc = {
.cmaps = cmaps,
.kern_dsc = NULL,
.kern_scale = 0,
.cmap_num = 2,
.cmap_num = 3,
.bpp = 1,
.kern_classes = 0,
.bitmap_format = 0,
+533 -38
View File
@@ -1,11 +1,15 @@
/*
* 模块名称:UI Menu (菜单页面构建)
* 模块功能:基于 lv_menu 复现 DTU-HMI 树形菜单,支持方向键导航与返回
* 模块功能:基于 LVGL 基元控件(页面导航)复现 DTU-HMI 树形菜单,支持方向键导航与返回
* 适用平台:STM32F407ZGTx
* 作者:王建锋
* 创建日期:2026-08-29
* 修改记录:
* 2026-08-29 王建锋 创建初始版本
* 2026-09-02 王建锋 弃用已废弃的 lv_menu 控件,改为基元控件页面导航
* 2026-09-02 王建锋 修复选中行文字不可见(白底白字):行改用 label 承载,选中态样式直接覆盖文字色
* 2026-09-03 王建锋 主界面按设计稿重构:顶部开口浅 V 装饰 + 状态行 + 左对齐带框 7 行列表(选中行整行反色、无箭头,其余行带 ▶);
* 根页条目收敛为一级 7 项(排除"厂家设置"子树);重命名 定值参数→参数定值、遥测量→三遥设置、通讯设置→通讯参数
*/
#include "ui_menu.h"
@@ -27,7 +31,7 @@ static const ui_menu_node_t g_menu_tab[] = {
{2, "交流量"},
{2, "直流量"},
{2, "开入量"},
{1, "定值参数"},
{1, "参数定值"},
{2, "定值"},
{3, "定值"},
{3, "遥调"},
@@ -38,7 +42,7 @@ static const ui_menu_node_t g_menu_tab[] = {
{3, "遥调"},
{3, "零压"},
{3, "电压"},
{1, "遥测量"},
{1, "三遥设置"},
{2, "测量值"},
{2, "遥调系数"},
{2, "遥测系数"},
@@ -49,7 +53,7 @@ static const ui_menu_node_t g_menu_tab[] = {
{2, "强制复归"},
{2, "手动录波"},
{2, "自动录波"},
{1, "通讯设置"},
{1, "通讯参数"},
{2, "通讯参数"},
{2, "网络配置"},
{2, "SNTP设置"},
@@ -85,31 +89,418 @@ static const ui_menu_node_t g_menu_tab[] = {
{1, "版本信息"},
};
#define MENU_NODE_COUNT (sizeof(g_menu_tab) / sizeof(g_menu_tab[0]))
#define MENU_NODE_ROOT 0xFFFF /* 虚拟根节点(根页面) */
#define MENU_PAGE_MAX 4 /* 页面深度上限(根页 + 3 级子页) */
#define MENU_ITEM_MAX 16 /* 单页最大条目数 */
#define MENU_HEADER_H 18 /* 子页页头高度(px) */
/* 主界面(根页)布局(设备像素,依据设计稿 mockup 实测) */
#define MENU_ROW_H 15 /* 行高 */
#define MENU_ROW_TXT_TOP 1 /* 行内文字垂直居中偏移((15-13)/2,字体行高 13) */
#define MENU_ROW_PAD_L 8 /* 行内文字左留白 */
#define MENU_ROW_ARROW_X 58 /* 行内箭头 x(紧跟 4 字文本之后) */
#define MENU_ROOT_BOX_X 0 /* 列表框左(左对齐,贴左缘) */
#define MENU_ROOT_BOX_Y 44 /* 列表框顶 */
#define MENU_ROOT_BOX_W 99 /* 列表框宽 */
#define MENU_ROOT_BOX_H 107 /* 列表框高(7 行 × 15 + 上下边框 2) */
#define MENU_ROOT_STATUS_X 17 /* 状态行左 */
#define MENU_ROOT_STATUS_Y 20 /* 状态行顶 */
#define MENU_DECO_LEG_W 3 /* 顶部"浅 V"两腿线宽(1bpp 下斜线边缘易被阈值吃掉,需比水平线粗才接近原图) */
#define MENU_DECO_HORIZ_W 2 /* 顶部"浅 V"水平线宽(≈原图 ~2px) */
/*
* 函数功能:构建菜单(lv_menu 页面树),复现参考项目菜单结构
* 页面:持久容器 + 按导航动态建毁的条目
* 容器常驻对象树(焦点对象不可删除,避免输入设备悬空指针),
* 条目按钮进入页面时创建、返回时销毁,内存占用 = 当前路径
*
* 每条目 = 行容器(整行背景)+ 文本 label + 可选箭头 label:
* - 行容器承载整行反色高亮(选中态白底)
* - 文本/箭头为子 label(绝对定位);选中态文字色须显式置黑
* (主题对 label 直接给白字,父级继承不可靠,见 2026-09-02 记录)
*/
typedef struct {
lv_obj_t *cont; /* 页面容器(持久) */
lv_obj_t *row[MENU_ITEM_MAX]; /* 行容器(动态) */
lv_obj_t *row_text[MENU_ITEM_MAX]; /* 行文本 label */
lv_obj_t *row_arrow[MENU_ITEM_MAX]; /* 行箭头 label(NULL=无) */
uint16_t item_node[MENU_ITEM_MAX]; /* 各条目对应 g_menu_tab 下标 */
uint16_t node; /* 本页展示节点(MENU_NODE_ROOT=虚拟根) */
uint8_t item_cnt; /* 条目数 */
uint8_t sel; /* 当前选中条目下标 */
uint8_t depth; /* 页面深度(0=根页) */
} ui_menu_page_t;
static ui_menu_page_t s_pages[MENU_PAGE_MAX];
static ui_menu_page_t *s_cur = NULL; /* 当前显示页面 */
static lv_obj_t *s_header = NULL; /* 子页页头容器(返回按钮 + 标题);根页隐藏 */
static lv_obj_t *s_back_btn = NULL; /* 页头返回按钮 */
static lv_obj_t *s_title = NULL; /* 页头标题 */
static lv_obj_t *s_root_deco = NULL; /* 主界面顶部装饰(开口浅 V) */
static lv_obj_t *s_root_status = NULL; /* 主界面状态行 */
static lv_obj_t *s_root_box = NULL; /* 主界面列表框(1px 边框) */
/* 样式(静态,一次性初始化) */
static lv_style_t s_style_flat; /* 通用:透明、无边框/圆角/内边距 */
static lv_style_t s_style_selected; /* 选中态:整行反色(白底) */
static lv_style_t s_style_back; /* 返回按钮:细边框、小内边距 */
static lv_style_t s_style_scrollbar; /* 滚动条:白色、4px 宽 */
static lv_style_t s_style_root_box; /* 主界面列表框:透明底 + 1px 白边框 */
/*
* 函数功能:初始化菜单样式(静态样式,仅执行一次)
* 入口参数:无
* 返回值:无
* 限定条件:无
* 函数说明: 1. flat 用于剥离主题给对象附加的卡片样式(边框/圆角/内边距)
* 2. selected 为选中条目整行反色高亮(1bpp:白底;文字色在刷新时显式置黑)
* 3. 样式后加优先,可覆盖主题在创建时附加的样式
*/
static void menu_styles_init(void)
{
static bool inited = false;
if (inited) {
return;
}
inited = true;
lv_style_init(&s_style_flat);
lv_style_set_bg_opa(&s_style_flat, LV_OPA_TRANSP);
lv_style_set_border_width(&s_style_flat, 0);
lv_style_set_radius(&s_style_flat, 0);
lv_style_set_pad_all(&s_style_flat, 0);
lv_style_set_pad_gap(&s_style_flat, 0);
lv_style_init(&s_style_selected);
lv_style_set_bg_opa(&s_style_selected, LV_OPA_COVER);
lv_style_set_bg_color(&s_style_selected, lv_color_white());
lv_style_set_text_color(&s_style_selected, lv_color_black());
lv_style_init(&s_style_back);
lv_style_set_bg_opa(&s_style_back, LV_OPA_TRANSP);
lv_style_set_border_width(&s_style_back, 1);
lv_style_set_border_color(&s_style_back, lv_color_white());
lv_style_set_radius(&s_style_back, 0);
lv_style_set_pad_all(&s_style_back, 1);
lv_style_init(&s_style_scrollbar);
lv_style_set_bg_opa(&s_style_scrollbar, LV_OPA_COVER);
lv_style_set_bg_color(&s_style_scrollbar, lv_color_white());
lv_style_set_width(&s_style_scrollbar, 4);
lv_style_init(&s_style_root_box);
lv_style_set_bg_opa(&s_style_root_box, LV_OPA_TRANSP);
lv_style_set_border_width(&s_style_root_box, 1);
lv_style_set_border_color(&s_style_root_box, lv_color_white());
lv_style_set_radius(&s_style_root_box, 0);
lv_style_set_pad_all(&s_style_root_box, 0);
/* mono 主题 card 样式自带 pad_gap=PAD_DEF(4),须显式清零,否则行间距被撑大导致溢出 */
lv_style_set_pad_gap(&s_style_root_box, 0);
}
/*
* 函数功能:向页面添加一个条目(行容器 + 文本 + 可选箭头)
* 入口参数:page - 页面结构指针 ui_menu_page_t *
* parent - 条目父容器 lv_obj_t * 根页=s_root_box,子页=page->cont
* node - g_menu_tab 节点下标 uint16_t
* has_arrow - 是否创建箭头 label bool
* 返回值:无
* 限定条件:page->item_cnt < MENU_ITEM_MAX
* 函数说明:1. 行容器整行宽、高 MENU_ROW_H,承载选中反色高亮
* 2. 文本 label 左留白 MENU_ROW_PAD_L;箭头 label 位于 MENU_ROW_ARROW_X
* 3. 文本/箭头子 label 加 IGNORE_LAYOUT 做行内绝对定位
*/
static void menu_page_add_item(ui_menu_page_t *page, lv_obj_t *parent,
uint16_t node, bool has_arrow)
{
if (page->item_cnt >= MENU_ITEM_MAX) {
return;
}
uint8_t idx = page->item_cnt;
lv_obj_t *row = lv_obj_create(parent);
if (row == NULL) {
return;
}
lv_obj_add_style(row, &s_style_flat, 0);
lv_obj_add_style(row, &s_style_selected, LV_STATE_CHECKED);
lv_obj_set_width(row, lv_pct(100));
lv_obj_set_height(row, MENU_ROW_H);
lv_obj_t *lbl = lv_label_create(row);
if (lbl == NULL) {
lv_obj_delete(row);
return;
}
lv_obj_add_style(lbl, &s_style_flat, 0);
lv_obj_add_flag(lbl, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_pos(lbl, MENU_ROW_PAD_L, MENU_ROW_TXT_TOP);
lv_label_set_text_static(lbl, g_menu_tab[node].name);
lv_obj_t *ar = NULL;
if (has_arrow) {
ar = lv_label_create(row);
if (ar != NULL) {
lv_obj_add_style(ar, &s_style_flat, 0);
lv_obj_add_flag(ar, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_pos(ar, MENU_ROW_ARROW_X, MENU_ROW_TXT_TOP);
lv_label_set_text_static(ar, "▶");
}
}
page->row[idx] = row;
page->row_text[idx] = lbl;
page->row_arrow[idx] = ar;
page->item_node[idx] = node;
page->item_cnt++;
}
/* 前向声明(menu_page_build 先于其定义调用) */
static void menu_page_refresh_sel(ui_menu_page_t *page);
/*
* 函数功能:构建页面条目列表
* 入口参数:page - 页面结构指针 ui_menu_page_t *
* node - 展示节点 uint16_t MENU_NODE_ROOT 表示虚拟根
* 返回值:无
* 限定条件:根页时 s_root_box 已创建
* 函数说明:1. 根页:一级节点 7 项(至第二个根"厂家设置"前截断),带箭头,父容器=s_root_box
* 2. 子页:node+1 起 level 为 (level+1) 的连续段,无箭头,父容器=page->cont
* 3. 先清空父容器旧条目再重建,默认选中首项
*/
static void menu_page_build(ui_menu_page_t *page, uint16_t node)
{
bool is_root = (node == MENU_NODE_ROOT);
lv_obj_t *parent = is_root ? s_root_box : page->cont;
bool has_arrow = is_root;
lv_obj_clean(parent);
page->node = node;
page->item_cnt = 0;
page->sel = 0;
if (is_root) {
for (uint32_t i = 1; i < MENU_NODE_COUNT; i++) {
if (g_menu_tab[i].level == 0) {
break; /* 抵达第二个根(厂家设置),主界面仅取其之前的一级项 */
}
if (g_menu_tab[i].level == 1) {
menu_page_add_item(page, parent, (uint16_t)i, has_arrow);
}
}
}
else {
uint8_t lvl = (uint8_t)(g_menu_tab[node].level + 1);
for (uint32_t i = node + 1; i < MENU_NODE_COUNT; i++) {
if (g_menu_tab[i].level != lvl) {
break;
}
menu_page_add_item(page, parent, (uint16_t)i, has_arrow);
}
}
if (page->item_cnt > 0) {
menu_page_refresh_sel(page);
}
}
/*
* 函数功能:更新选中高亮、文字色与箭头显隐
* 入口参数:page - 页面结构指针 ui_menu_page_t *
* 返回值:无
* 限定条件:page->item_cnt > 0
* 函数说明:1. 选中行整行反色(白底),文字/箭头显式置黑并隐藏箭头
* 2. 非选中行透明底,文字/箭头置白并显示箭头
* 3. 滚动使选中条目进入可视区
*/
static void menu_page_refresh_sel(ui_menu_page_t *page)
{
if (page->item_cnt == 0) {
return;
}
for (uint8_t i = 0; i < page->item_cnt; i++) {
bool sel = (i == page->sel);
if (sel) {
lv_obj_add_state(page->row[i], LV_STATE_CHECKED);
}
else {
lv_obj_clear_state(page->row[i], LV_STATE_CHECKED);
}
lv_obj_set_style_text_color(page->row_text[i],
sel ? lv_color_black() : lv_color_white(), 0);
if (page->row_arrow[i] != NULL) {
if (sel) {
lv_obj_add_flag(page->row_arrow[i], LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_remove_flag(page->row_arrow[i], LV_OBJ_FLAG_HIDDEN);
}
lv_obj_set_style_text_color(page->row_arrow[i],
sel ? lv_color_black() : lv_color_white(), 0);
}
}
lv_obj_scroll_to_view(page->row[page->sel], LV_ANIM_OFF);
}
/*
* 函数功能:切换显示页面
* 入口参数:page - 目标页面 ui_menu_page_t *
* 返回值:无
* 限定条件:ui_menu_init() 已完成
* 函数说明:1. 隐藏旧页、显示新页(仅切换 HIDDEN 标志)
* 2. 页头仅子页显示;根页隐藏页头(让位顶部装饰 + 状态行,内容区撑满 160)
* 3. 焦点移至新页容器(按键输入入口)
*/
static void menu_show_page(ui_menu_page_t *page)
{
if (s_cur != NULL && s_cur != page) {
lv_obj_add_flag(s_cur->cont, LV_OBJ_FLAG_HIDDEN);
}
s_cur = page;
lv_obj_clear_flag(page->cont, LV_OBJ_FLAG_HIDDEN);
if (page->depth == 0) {
lv_obj_add_flag(s_header, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_remove_flag(s_header, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text_static(s_title, g_menu_tab[page->node].name);
}
lv_group_focus_obj(page->cont);
}
/*
* 函数功能:打开选中条目的子页面
* 入口参数:page - 当前页面 ui_menu_page_t *
* 返回值:无
* 限定条件:page->item_cnt > 0
* 函数说明:1. 在下一深度页面容器上重建条目并显示
* 2. 叶子节点(无子节点)打开空白占位页
* 3. 深度保护:超过 MENU_PAGE_MAX 不再下钻
*/
static void menu_open_selected(ui_menu_page_t *page)
{
if (page->item_cnt == 0) {
return;
}
if (page->depth + 1 >= MENU_PAGE_MAX) {
return;
}
ui_menu_page_t *child = &s_pages[page->depth + 1];
menu_page_build(child, page->item_node[page->sel]);
menu_show_page(child);
}
/*
* 函数功能:返回上级页面
* 入口参数:page - 当前页面 ui_menu_page_t *
* 返回值:无
* 限定条件:page->depth > 0
* 函数说明:1. 销毁本页条目释放 LVGL 池内存(容器保留)
* 2. 显示父页并恢复焦点(父页选中状态保持不变)
*/
static void menu_go_back(ui_menu_page_t *page)
{
if (page->depth == 0) {
return;
}
lv_obj_clean(page->cont);
menu_show_page(&s_pages[page->depth - 1]);
}
/*
* 函数功能:页面容器按键事件处理
* 入口参数:e - 事件指针 lv_event_t *
* 返回值:无
* 限定条件:容器已加入默认输入组并获焦
* 函数说明:1. UP/DOWN:移动选中(环绕)
* 2. ENTER/RIGHT:打开选中条目子页
* 3. ESC/LEFT:返回上级页面
*/
static void menu_page_key_cb(lv_event_t *e)
{
ui_menu_page_t *page = (ui_menu_page_t *)lv_event_get_user_data(e);
lv_key_t key = (lv_key_t)lv_event_get_key(e);
switch (key) {
case LV_KEY_UP:
if (page->item_cnt > 0) {
page->sel = (uint8_t)((page->sel + page->item_cnt - 1) % page->item_cnt);
menu_page_refresh_sel(page);
}
break;
case LV_KEY_DOWN:
if (page->item_cnt > 0) {
page->sel = (uint8_t)((page->sel + 1) % page->item_cnt);
menu_page_refresh_sel(page);
}
break;
case LV_KEY_ENTER:
case LV_KEY_RIGHT:
menu_open_selected(page);
break;
case LV_KEY_ESC:
case LV_KEY_LEFT:
menu_go_back(page);
break;
default:
break;
}
}
/*
* 函数功能:返回按钮点击回调(预留触摸输入)
* 入口参数:e - 事件指针 lv_event_t *
* 返回值:无
* 限定条件:ui_menu_init() 已完成
* 函数说明:返回上级页面;按键输入由页面容器 ESC/LEFT 处理
*/
static void menu_back_btn_cb(lv_event_t *e)
{
(void)e;
if (s_cur != NULL) {
menu_go_back(s_cur);
}
}
/*
* 函数功能:应用主界面顶部"浅 V"装饰线段的公共样式
* 入口参数:line - lv_line 对象;width - 线宽(px)
* 返回值:无
* 限定条件:line 已创建且已设点集
* 函数说明:绝对定位于 (0,0)、忽略布局、白色实线、指定线宽
*/
static void deco_line_apply(lv_obj_t * line, int32_t width)
{
lv_obj_add_flag(line, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_pos(line, 0, 0);
lv_obj_set_size(line, 160, 20);
lv_obj_set_style_line_width(line, width, 0);
lv_obj_set_style_line_color(line, lv_color_white(), 0);
lv_obj_set_style_line_opa(line, LV_OPA_COVER, 0);
}
/*
* 函数功能:构建菜单(基元控件页面导航)
* 入口参数:无
* 返回值:0 - 成功,其他 - 失败(对象创建失败)
* 限定条件:ui_port_init() 已成功调用,活动屏幕存在
* 函数说明:1. 仅构建主页面(根页 + 一级菜单项),子树暂不构建以控制内存占用
* 2. 文本全部使用静态字符串(不 strdup),减少 LVGL 堆分配
* 3. 构建中监控 LVGL 池空闲内存,不足时中止而非崩溃
* 限定条件:ui_port_init()/sim_port_init() 已成功调用,活动屏幕存在
* 函数说明: 1. 子页页头(返回按钮 + 标题)+ 内容区,4 个页面容器(每深度一个,持久)
* 2. 主界面(根页):顶部开口浅 V 装饰 + 状态行 + 左对齐带框 7 行列表
* 3. 页面按需构建:ENTER 时建、返回时毁,内存 = 当前路径
* 4. 页面容器加入默认输入组,按键由容器统一处理
* 5. 构建前后监控 LVGL 池内存并打印
*/
int ui_menu_init(void)
{
lv_obj_t *menu = lv_menu_create(lv_screen_active());
if (menu == NULL) {
lv_obj_t *scr = lv_screen_active();
if (scr == NULL) {
return -1;
}
lv_obj_set_size(menu, 160, 160);
lv_menu_set_mode_root_back_button(menu, LV_MENU_ROOT_BACK_BUTTON_ENABLED);
lv_group_add_obj(lv_group_get_default(), menu);
/* 仅构建主页面:根页 + 一级菜单项(静态文本,不建子树,省内存) */
lv_obj_t *root_page = lv_menu_page_create(menu, NULL);
if (root_page == NULL) {
return -1;
}
lv_menu_set_page_title_static(root_page, "主界面");
menu_styles_init();
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
@@ -117,28 +508,132 @@ int ui_menu_init(void)
(unsigned)mon.total_size,
(unsigned)mon.free_size, (unsigned)mon.free_biggest_size);
uint32_t n = sizeof(g_menu_tab) / sizeof(g_menu_tab[0]);
for (uint32_t i = 1; i < n; i++) {
if (g_menu_tab[i].level != 1) {
continue; /* 只显示一级菜单项,子树暂不构建 */
}
/* 根容器:铺满屏幕,纵向布局 */
lv_display_t *disp = lv_display_get_default();
lv_obj_t *menu = lv_obj_create(scr);
if (menu == NULL) {
return -1;
}
lv_obj_add_style(menu, &s_style_flat, 0);
lv_obj_set_size(menu, lv_display_get_horizontal_resolution(disp),
lv_display_get_vertical_resolution(disp));
lv_obj_set_flex_flow(menu, LV_FLEX_FLOW_COLUMN);
/* 内存警戒:空闲池不足则中止,避免 LVGL 内部 NULL 解引用崩溃 */
lv_mem_monitor(&mon);
if (mon.free_biggest_size < 1024) {
DBG_ERROR("menu mem low: abort at item %u (free=%u big=%u)",
(unsigned)i, (unsigned)mon.free_size,
(unsigned)mon.free_biggest_size);
break;
}
/* 子页页头:返回按钮 + 标题(根页隐藏) */
s_header = lv_obj_create(menu);
if (s_header == NULL) {
return -1;
}
lv_obj_add_style(s_header, &s_style_flat, 0);
lv_obj_set_width(s_header, lv_pct(100));
lv_obj_set_height(s_header, MENU_HEADER_H);
lv_obj_set_flex_flow(s_header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(s_header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_gap(s_header, 4, 0);
const char *name = g_menu_tab[i].name;
lv_obj_t *item = lv_menu_cont_create(root_page);
lv_obj_t *lbl = lv_label_create(item);
lv_label_set_text_static(lbl, name);
s_back_btn = lv_button_create(s_header);
if (s_back_btn == NULL) {
return -1;
}
lv_obj_add_style(s_back_btn, &s_style_back, 0);
lv_obj_set_size(s_back_btn, 28, 16);
lv_obj_set_flex_flow(s_back_btn, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(s_back_btn, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_t *back_lbl = lv_label_create(s_back_btn);
if (back_lbl == NULL) {
return -1;
}
lv_obj_add_style(back_lbl, &s_style_flat, 0);
lv_label_set_text_static(back_lbl, "返回");
lv_obj_add_event_cb(s_back_btn, menu_back_btn_cb, LV_EVENT_CLICKED, NULL);
s_title = lv_label_create(s_header);
if (s_title == NULL) {
return -1;
}
lv_obj_add_style(s_title, &s_style_flat, 0);
lv_label_set_text_static(s_title, "主界面");
lv_obj_add_flag(s_header, LV_OBJ_FLAG_HIDDEN); /* 默认根页:隐藏页头 */
/* 内容区:占满剩余高度(根页隐藏页头时撑满 160) */
lv_obj_t *content = lv_obj_create(menu);
if (content == NULL) {
return -1;
}
lv_obj_add_style(content, &s_style_flat, 0);
lv_obj_set_width(content, lv_pct(100));
lv_obj_set_flex_grow(content, 1);
lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN);
/* 页面容器:每深度一个,持久存在(条目按导航建毁) */
for (uint8_t i = 0; i < MENU_PAGE_MAX; i++) {
s_pages[i].cont = lv_obj_create(content);
if (s_pages[i].cont == NULL) {
return -1;
}
lv_obj_add_style(s_pages[i].cont, &s_style_flat, 0);
lv_obj_set_size(s_pages[i].cont, lv_pct(100), lv_pct(100));
lv_obj_add_flag(s_pages[i].cont, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_group_add_obj(lv_group_get_default(), s_pages[i].cont);
lv_obj_add_event_cb(s_pages[i].cont, menu_page_key_cb, LV_EVENT_KEY, &s_pages[i]);
s_pages[i].depth = i;
s_pages[i].node = MENU_NODE_ROOT;
if (i > 0) {
/* 子页容器:纵向堆叠条目 + 自动滚动条 */
lv_obj_set_flex_flow(s_pages[i].cont, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(s_pages[i].cont, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_START);
lv_obj_set_scrollbar_mode(s_pages[i].cont, LV_SCROLLBAR_MODE_AUTO);
lv_obj_add_style(s_pages[i].cont, &s_style_scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_flag(s_pages[i].cont, LV_OBJ_FLAG_HIDDEN);
}
}
lv_menu_set_page(menu, root_page);
/* 主界面(根页):顶部"开口浅 V"装饰 + 状态行 + 列表框(绝对定位于 s_pages[0].cont)
* 拆 3 段:两腿加粗(1bpp 下斜线边缘易被阈值吃掉,需比水平线粗才接近原图 ~4px),水平线保持细线 */
{
static const lv_point_precise_t g_deco_leg_l[] = { {7, 7}, {16, 17} };
static const lv_point_precise_t g_deco_hoz[] = { {16, 17}, {145, 17} };
static const lv_point_precise_t g_deco_leg_r[] = { {158, 7}, {145, 17} };
lv_obj_t * leg_l = lv_line_create(s_pages[0].cont);
lv_obj_t * leg_r = lv_line_create(s_pages[0].cont);
s_root_deco = lv_line_create(s_pages[0].cont);
if (leg_l == NULL || leg_r == NULL || s_root_deco == NULL) {
return -1;
}
lv_line_set_points(leg_l, g_deco_leg_l, 2);
lv_line_set_points(leg_r, g_deco_leg_r, 2);
lv_line_set_points(s_root_deco, g_deco_hoz, 2);
deco_line_apply(leg_l, MENU_DECO_LEG_W);
deco_line_apply(leg_r, MENU_DECO_LEG_W);
deco_line_apply(s_root_deco, MENU_DECO_HORIZ_W);
}
s_root_status = lv_label_create(s_pages[0].cont);
if (s_root_status == NULL) {
return -1;
}
lv_obj_add_style(s_root_status, &s_style_flat, 0);
lv_obj_add_flag(s_root_status, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_pos(s_root_status, MENU_ROOT_STATUS_X, MENU_ROOT_STATUS_Y);
lv_label_set_text_static(s_root_status, "当前模式 : 无模式");
s_root_box = lv_obj_create(s_pages[0].cont);
if (s_root_box == NULL) {
return -1;
}
lv_obj_add_style(s_root_box, &s_style_root_box, 0);
lv_obj_add_flag(s_root_box, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_pos(s_root_box, MENU_ROOT_BOX_X, MENU_ROOT_BOX_Y);
lv_obj_set_size(s_root_box, MENU_ROOT_BOX_W, MENU_ROOT_BOX_H);
lv_obj_set_flex_flow(s_root_box, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(s_root_box, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_START);
/* 构建根页并显示 */
menu_page_build(&s_pages[0], MENU_NODE_ROOT);
menu_show_page(&s_pages[0]);
lv_mem_monitor(&mon);
DBG_INFO("menu build done: total=%u free=%u big=%u used=%u",
+6 -5
View File
@@ -1,11 +1,12 @@
/*
* 模块名称:UI Menu (菜单页面构建)
* 模块功能:基于 lv_menu 复现 DTU-HMI 树形菜单,支持方向键导航与返回
* 模块功能:基于 LVGL 基元控件(页面导航)复现 DTU-HMI 树形菜单,支持方向键导航与返回
* 适用平台:STM32F407ZGTx
* 作者:王建锋
* 创建日期:2026-08-29
* 修改记录:
* 2026-08-29 王建锋 创建初始版本
* 2026-09-02 王建锋 弃用已废弃的 lv_menu 控件,改为基元控件页面导航
*/
#ifndef UI_MENU_H
#define UI_MENU_H
@@ -15,13 +16,13 @@ extern "C" {
#endif
/*
* 函数功能:构建菜单(lv_menu 页面树),复现参考项目菜单结构
* 函数功能:构建菜单(基元控件页面导航:页头返回按钮 + 页面切换)
* 入口参数:无
* 返回值:0 - 成功,其他 - 失败(对象创建失败)
* 限定条件:ui_port_init() 已成功调用,活动屏幕存在
* 函数说明:1. 依据紧凑节点表按层级生成 lv_menu 页面与返回栈
* 2. 将菜单对象加入默认输入组,支持远端按键导航
* 3. 叶子节点挂占位页,等待后续逐项填充具体功能
* 函数说明:1. 依据紧凑节点表按层级组织页面,ENTER 进入子页、ESC/LEFT 返回
* 2. 页面容器加入默认输入组,按键由当前页容器统一处理
* 3. 页面按需构建(进入时建、返回时毁),叶子节点为空白占位页
*/
int ui_menu_init(void);