feat(UI): W6 第一波——设计令牌层、页面注册表生成器与 UI 门禁补强
启动 docs/任务规划/README.md 的 W6 UI 扩展簇(用户裁定,覆盖 2026-10-11
的"本轮不排期")。四个工作包,全部验证通过。
## U1 设计令牌层(零行为变化)
新建 Drivers/BSP/UI/ui_tokens.h(20 个宏),框架 + 7 个页面 + App/task/
ui_task.c 全部接入。屏幕几何**复用 ui_port.h 既有 UI_SCREEN_W/H**,不重复
定义;令牌头自身只 include ui_port.h,不把 LVGL 拖进全部消费者。
补收了报告没列但代码里真重复的量:UI_TEXT_BAND_H 15(7 次)、列表左右边界
与每页行数(各 4 个页面逐字复制)、推流协议几何与显示几何同源(此前硬编码
3200 且注释写着"须与 UI_FB_SIZE 一致"——典型漂移隐患)、UI_TASK_TICK_MS
(ui_port_step 与 osDelay 必须同值,原先靠注释维系)。
故意不收页面私有几何与"同形不同义"的值(device_info 的 pitch=15 与列表
UI_ROW_PITCH=14 合并即改像素)。
## U3 页面注册表与生成器(删手工分发)
ui_page_registry.{c,h} 由生成器产出,**手工 menu_content_page_for() 已删
除**(仓库内仅存 2 处历史说明注释)。密码页按代码实况**单独出口**
(ui_page_registry_password())——它非节点绑定,任何 #P 叶子进同一页。
顺带落地此前判定"U1 阶段做不了"的共享 flat 样式 ui_style.{c,h}。
生成器除产注册表外**自动把新 .c 写进 uvprojx 与 CMakeLists.txt**,且
**双向对账**:加页打印 registered、删页打印 pruned,不留死条目;并双向
校验(#page 找不到实例报错、实例未被任何 #page 引用也报错)。
## U4 门禁补强(35 个钩子)
新增 font-charset 钩子(scripts/check_font_charset.py):复用
tools/font_gen 的同一套提取与 cmap 解析规则(importlib 直载,零第二套
规则),A 段查源码在用 vs 字符集、B 段查字符集 vs 字体 cmap。
registration-check 补第 ④ 类:实测原规则对"进了两端构建但没接描述符表"
的页面**全绿**——desc 被链接器剥离、编译链接全过、菜单永远打不开的死代码。
仍照不出的两项已如实写进文档(枚举漏加归 @build、菜单树漏挂归 golden)。
## 字体子集瘦身(D 案,4 留 3 删)
ui_cn_chars.txt 122→119 字(恰删 上/电/重,零新增)。异/常/片/芯 进
KEEP_EXTRA 带理由白名单:它们曾在 9eb1bd63 被写进中文自检描述落盘
/log/selftest.log(定长 64B 记录跨固件持久),删字形会使历史故障记录
渲染空白且不可逆。保留字按**必需**处理,反向有牙——谁只跑 extract_cn
把它们静默洗掉,钩子直接 FAIL 并逐字打印理由。
--strict 口径为"未知冗余 = 0"而非"冗余 = 0"。
## 验证
@build 0 错 0 警;ER_IROM1 232,052 B(较 HEAD −228 B)、RW_IRAM1
75,232 B **零增量**、RW_CCM 87.9% 未变。golden:菜单 8 状态全像素一致 +
内容页 11 页逐字节一致,**参考图 sha256 差异 0 条**(未用 --update 刷
基线);池水位菜单基线 5368 B、开页-关页往返 5448 B 与 U1 时相同。
38 个在途修改文件逐个核对 BOM/CRLF/U+FFFD 全部与 HEAD 一致。
L0 33 钩子 + L0.5 16 runner / 1104 断言全绿;check_doc_links --all
142 篇、无法判定 0;mem-budget / mem-numbers 已回填。
## 单位成本实测(U5 排期依据)
临时页 zzprobe 绑到"遥信量"叶子 → 只跑生成器 → 带页 @build 0/0 →
注册表出现 {5u, &ui_page_zzprobe_desc} → 回滚后 6 个产出文件 zzprobe
残留计数均 0、重编回基线。
**结论:新增一页 = 3 处人工(新 .c + 新 .h + 菜单树那一行加
#page=<名>)+ 1 条生成器命令,手工编辑注册文件 0 处。**
据此修正报告原口径"8 处降到 1 处"——那个 1 只在"1 处对既有文件的修改"
意义下成立,页面自身 2 个文件本就要写,真实处数是 3。
## 文档同步与两处自我修正
§0.1 UI 行 B → B+;§6.1「内容页」「设计令牌」两行按实况回填;§6.5 U1/U3
状态更新;§6 顶部加"建议栏不可照抄"总警示(本轮四次证伪:ui_port.h 已有
同名宏、LV_COLOR_BLACK/WHITE 不存在且极性反了、27 页实为目标页数现存 7
个、生成器草图漏密码页非节点绑定)。
两处改口:① §6.5 U4 从我上一版**误写的 3/3 改回 2/3**——"golden 脚本早已
存在"不等于"已进自动门禁",两个 golden 脚本至今未挂 pre-commit,该缺口
单列为待办;② 原计划"分三笔提交"**作废**,U1 与 U3 改同一批文件、文件内
交叠,硬切会产生不可编译的中间态,故整波一笔。
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
* 自绘全部内容(浅 V 装饰 + 居中标题 + 行数据 + 页脚页码),无子对象;
|
||||
* 只持机制:屏/容器按需建毁、焦点、按键分发、描述符驱动通用绘制;
|
||||
* 不持任何页面数据——各页数据/几何/钩子由 ui_page_xxx 模块经
|
||||
* content_page_def_t 接入描述符表 s_defs[](架构见 内容页架构.md);
|
||||
* content_page_def_t 提供,框架按**描述符指针**开页;"哪个叶子进哪一页"
|
||||
* 的绑定在生成物 ui_page_registry.c(唯一真源 = ui_menu_tree.txt 的
|
||||
* #page= 注记;架构见 内容页架构.md);
|
||||
* 进入时切屏并获焦、ESC 返回菜单屏并还原焦点;
|
||||
* 不持有导航状态——由逻辑层 ui_menu.c 在 ENTER 叶子节点时驱动
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||||
@@ -26,6 +28,12 @@
|
||||
* 描述符 init_cb(开页后调一次,页面模块重置运行态)
|
||||
* 2026-09-13 王建锋 cp_draw_cb 按区块拆为四个绘制子函数(cp_draw_deco/header/rows/footer),
|
||||
* 主函数仅编排(行数取一次供行窗口/页脚共用),行为零变化
|
||||
* 2026-10-15 王建锋 U1 设计令牌层:屏幕/骨架/行距/缓冲/1bpp 语义色字面量收进
|
||||
* ui_tokens.h(纯替换,像素逐位一致)
|
||||
* 2026-10-15 王建锋 U3 页面原型化:删 content_page_id_t 枚举与 s_defs[],open() 改收
|
||||
* 描述符指针、s_open 变指针(NULL = 未打开);7 个页面头包含移交
|
||||
* ui_page_registry.c;日志页身份改打描述符 id(ASCII 页名,比原
|
||||
* id=%u 更可定位);绘制/按键/几何一律未动
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
@@ -33,20 +41,6 @@
|
||||
|
||||
#include "ui_content_page.h"
|
||||
|
||||
#include "ui_page_device_info.h"
|
||||
|
||||
#include "ui_page_ac.h"
|
||||
|
||||
#include "ui_page_dc.h"
|
||||
|
||||
#include "ui_page_password.h"
|
||||
|
||||
#include "ui_page_net_cfg.h"
|
||||
|
||||
#include "ui_page_runlog.h"
|
||||
|
||||
#include "ui_page_selftest.h"
|
||||
|
||||
#include "ui_standby.h"
|
||||
|
||||
#include "ui_pwd_def.h"
|
||||
@@ -55,6 +49,10 @@
|
||||
|
||||
#include "ui_deco.h"
|
||||
|
||||
#include "ui_style.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#define DBG_TAG "[UI_CONTENT_PAGE]"
|
||||
@@ -63,57 +61,32 @@
|
||||
/* 私有宏定义区 */
|
||||
|
||||
/*
|
||||
* 布局常量(设备像素;参考图 10/20/21 实测,3x 放大图缩回 160x160 测量):
|
||||
* 标题 y=2(CJK 字形顶距 +1 → 墨迹自 y=3 起,与参考图一致)、页脚 y=143;
|
||||
* 字形顶距(CJK +1 / ASCII +2)已计入放置 y,墨迹与参考图对齐
|
||||
* 布局取值全部来自 ui_tokens.h(L0 设计令牌层,本文件不再自带同值字面量):
|
||||
* 屏幕 UI_SCREEN_W/H、标题 UI_TITLE_Y、页脚 UI_PAGE_LINE_Y、文本条带
|
||||
* UI_TEXT_BAND_H、行缓冲 UI_BUF_STRLEN、选中带内缩 UI_SEL_BAND_INSET_L/R、
|
||||
* 页脚左起 UI_FOOTER_X、语义色 UI_COL_FG/BG/SEL_FG/SEL_BG。
|
||||
* 几何实测来源仍是参考图 10/20/21(3x 放大图缩回 160x160 测量):标题 y=2
|
||||
* (CJK 字形顶距 +1 → 墨迹自 y=3 起,与参考图一致)、页脚 y=143;字形顶距
|
||||
* (CJK +1 / ASCII +2)已计入放置 y,墨迹与参考图对齐
|
||||
*/
|
||||
#define CP_SCREEN_W 160 /* 屏幕宽(px,与 ui_port.h / 模拟器一致) */
|
||||
#define CP_SCREEN_H 160 /* 屏幕高(px) */
|
||||
|
||||
#define CP_TITLE_Y 2 /* 标题放置 y(居中;墨迹自 y=3 起) */
|
||||
#define CP_PAGE_LINE_Y 143 /* 页脚"第 X 页,共 Y 页"放置 y(x=6 左对齐) */
|
||||
|
||||
#define CP_ROW_BUF_LEN 32 /* 动态行栈缓冲长度(同帧同步光栅化;超出行带即被裁切) */
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/*
|
||||
* 描述符表:页 ID → 页面模块描述符(每页一行;新增页 = 枚举值 + 此处一行,
|
||||
* 框架机制不变,见 内容页架构.md §七)
|
||||
* 当前打开的页:直接持页面描述符指针(NULL = 未打开)。
|
||||
* 页身份由调用方给出(逻辑层经 ui_page_registry_for / _password 取描述符),
|
||||
* 框架不再维护"页 ID 枚举 ↔ s_defs 下标 ↔ 菜单节点"三处必须手工对齐的映射
|
||||
* ——那正是旧 menu_content_page_for() 手写 case 的错源(表序一变即错位)。
|
||||
*/
|
||||
static const content_page_def_t *const s_defs[CONTENT_PAGE_COUNT] = {
|
||||
[CONTENT_PAGE_DEVICE_INFO] = &ui_page_device_info_desc,
|
||||
[CONTENT_PAGE_AC] = &ui_page_ac_desc,
|
||||
[CONTENT_PAGE_DC] = &ui_page_dc_desc,
|
||||
[CONTENT_PAGE_PASSWORD] = &ui_page_password_desc,
|
||||
[CONTENT_PAGE_NET_CFG] = &ui_page_net_cfg_desc,
|
||||
[CONTENT_PAGE_RUN_LOG] = &ui_page_runlog_desc,
|
||||
[CONTENT_PAGE_SELFTEST_LOG] = &ui_page_selftest_desc,
|
||||
};
|
||||
|
||||
static content_page_id_t s_open = CONTENT_PAGE_COUNT; /* 当前打开的页(COUNT = 未打开) */
|
||||
static const content_page_def_t *s_open = NULL;
|
||||
static uint16_t s_sel; /* 列表页选中行(全局下标,跨页连续) */
|
||||
static uint8_t s_perm; /* 本次授予权限(ui_perm_t;开页时装载,关页重置) */
|
||||
static lv_obj_t *s_scr; /* 页面屏幕(进入时建、返回时毁) */
|
||||
static lv_obj_t *s_cont; /* 页面全屏容器(焦点/键入口 + 自绘载体) */
|
||||
|
||||
/* flat:透明、无边框/圆角/内边距——剥离 mono 主题给普通 lv_obj 附加的 card 样式(同 ui_menu_view.c) */
|
||||
/* 用常量样式(LV_STYLE_CONST_INIT):属性存静态数组,零 LV_MEM 池分配;
|
||||
* 动态样式首次 set_prop 会从池分配 values_and_props 且永不释放(x64 实测 88B/个) */
|
||||
static const lv_style_const_prop_t cp_flat_props[] = {
|
||||
{ .prop = LV_STYLE_BG_OPA, .value = { .num = LV_OPA_TRANSP } },
|
||||
{ .prop = LV_STYLE_BORDER_WIDTH, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_RADIUS, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_LEFT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_RIGHT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_TOP, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_BOTTOM, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_ROW, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_COLUMN, .value = { .num = 0 } },
|
||||
LV_STYLE_CONST_PROPS_END
|
||||
};
|
||||
|
||||
LV_STYLE_CONST_INIT(cp_style_flat, cp_flat_props);
|
||||
/* 容器样式:flat(透明 + 无边框/圆角/内边距)取自共享样式表 ui_style.c——
|
||||
* 与 ui_menu_view / ui_standby 共用同一 const 对象(原先三处各定义一份逐字相同
|
||||
* 的 9 项属性数组);常量样式零 LV_MEM 池分配的纪律见 ui_style.h */
|
||||
|
||||
/* 函数定义区 */
|
||||
|
||||
@@ -175,18 +148,18 @@ static void cp_draw_centered_text(lv_layer_t *layer, const char *text, int32_t y
|
||||
lv_draw_label_dsc_t ld;
|
||||
|
||||
area.x1 = 0;
|
||||
area.x2 = CP_SCREEN_W - 1;
|
||||
area.x2 = UI_SCREEN_W - 1;
|
||||
area.y1 = y;
|
||||
area.y2 = y + 15; /* 覆盖字体行高(13)+ 余量 */
|
||||
area.y2 = y + UI_TEXT_BAND_H; /* 覆盖字体行高(13)+ 余量 */
|
||||
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.text = text;
|
||||
ld.text_static = 1; /* Flash 字符串:不拷内存 */
|
||||
ld.color = lv_color_white();
|
||||
ld.color = UI_COL_FG;
|
||||
/* 本 LVGL 版 lv_text_get_width:length=0 表示"不测量"(返回 0);
|
||||
* length≠0 时循环无 NUL 检查,传 -1 会越串尾死循环——必须传 strlen */
|
||||
ld.ofs_x = (CP_SCREEN_W - lv_text_get_width(text, (uint32_t)strlen(text), LV_FONT_DEFAULT, 0)) / 2;
|
||||
ld.ofs_x = (UI_SCREEN_W - lv_text_get_width(text, (uint32_t)strlen(text), LV_FONT_DEFAULT, 0)) / 2;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &area);
|
||||
}
|
||||
@@ -211,7 +184,7 @@ static void cp_draw_deco(lv_layer_t *layer, const content_page_def_t *d) {
|
||||
* d - 页面描述符 const content_page_def_t *
|
||||
* 返回值:无
|
||||
* 限定条件:页面已打开
|
||||
* 函数说明:标题放置 y 每页可配(title_y,0 = 框架默认 CP_TITLE_Y);
|
||||
* 函数说明:标题放置 y 每页可配(title_y,0 = 框架默认 UI_TITLE_Y);
|
||||
* 副标题仅信息页(subtitle 非空);重绘区不相交则跳过区块
|
||||
*/
|
||||
static void cp_draw_header(lv_layer_t *layer, const content_page_def_t *d) {
|
||||
@@ -219,12 +192,12 @@ static void cp_draw_header(lv_layer_t *layer, const content_page_def_t *d) {
|
||||
|
||||
/* 标题(居中;放置 y 每页可配,0 = 框架默认) */
|
||||
{
|
||||
int32_t ty = (d->title_y != 0) ? d->title_y : CP_TITLE_Y;
|
||||
int32_t ty = (d->title_y != 0) ? d->title_y : UI_TITLE_Y;
|
||||
|
||||
t.x1 = 0;
|
||||
t.x2 = CP_SCREEN_W - 1;
|
||||
t.x2 = UI_SCREEN_W - 1;
|
||||
t.y1 = ty;
|
||||
t.y2 = ty + 15;
|
||||
t.y2 = ty + UI_TEXT_BAND_H;
|
||||
if (cp_areas_intersect(&t, &layer->_clip_area)) {
|
||||
cp_draw_centered_text(layer, d->title, ty);
|
||||
}
|
||||
@@ -233,9 +206,9 @@ static void cp_draw_header(lv_layer_t *layer, const content_page_def_t *d) {
|
||||
/* 副标题(仅信息页) */
|
||||
if (d->subtitle != NULL) {
|
||||
t.x1 = 0;
|
||||
t.x2 = CP_SCREEN_W - 1;
|
||||
t.x2 = UI_SCREEN_W - 1;
|
||||
t.y1 = d->subtitle_y;
|
||||
t.y2 = d->subtitle_y + 15;
|
||||
t.y2 = d->subtitle_y + UI_TEXT_BAND_H;
|
||||
if (cp_areas_intersect(&t, &layer->_clip_area)) {
|
||||
cp_draw_centered_text(layer, d->subtitle, d->subtitle_y);
|
||||
}
|
||||
@@ -268,19 +241,19 @@ static void cp_draw_rows(lv_layer_t *layer, const content_page_def_t *d, int32_t
|
||||
}
|
||||
|
||||
bool dynamic = (d->refresh != NULL);
|
||||
char dyn_name[CP_ROW_BUF_LEN]; /* 动态行栈缓冲(同帧同步光栅化) */
|
||||
char dyn_value[CP_ROW_BUF_LEN];
|
||||
char dyn_name[UI_BUF_STRLEN]; /* 动态行栈缓冲(同帧同步光栅化) */
|
||||
char dyn_value[UI_BUF_STRLEN];
|
||||
|
||||
for (int32_t i = 0; i < cnt; i++) {
|
||||
int32_t row_idx = first + i; /* 数据下标(跨页连续) */
|
||||
int32_t slot_y = d->top_y + i * d->pitch; /* 行槽顶(窗口内位置固定) */
|
||||
bool is_sel = (d->selectable != 0u) && (row_idx == (int32_t)s_sel);
|
||||
lv_color_t color = is_sel ? lv_color_black() : lv_color_white();
|
||||
lv_color_t color = is_sel ? UI_COL_SEL_FG : UI_COL_FG;
|
||||
lv_area_t strip;
|
||||
|
||||
/* 行条带(整宽)不在重绘区:跳过(culling) */
|
||||
strip.x1 = 0;
|
||||
strip.x2 = CP_SCREEN_W - 1;
|
||||
strip.x2 = UI_SCREEN_W - 1;
|
||||
strip.y1 = slot_y;
|
||||
strip.y2 = slot_y + d->pitch - 1;
|
||||
if (!cp_areas_intersect(&strip, &layer->_clip_area)) {
|
||||
@@ -292,13 +265,13 @@ static void cp_draw_rows(lv_layer_t *layer, const content_page_def_t *d, int32_t
|
||||
lv_draw_rect_dsc_t fd;
|
||||
lv_area_t band;
|
||||
|
||||
band.x1 = 2;
|
||||
band.x2 = CP_SCREEN_W - 3; /* x=156(与参考图实测一致) */
|
||||
band.x1 = UI_SEL_BAND_INSET_L;
|
||||
band.x2 = UI_SCREEN_W - UI_SEL_BAND_INSET_R; /* x=156(与参考图实测一致) */
|
||||
band.y1 = slot_y;
|
||||
band.y2 = slot_y + d->band_h - 1;
|
||||
lv_draw_rect_dsc_init(&fd);
|
||||
fd.base.layer = layer;
|
||||
fd.bg_color = lv_color_white();
|
||||
fd.bg_color = UI_COL_SEL_BG;
|
||||
fd.bg_opa = LV_OPA_COVER;
|
||||
lv_draw_rect(layer, &fd, &band);
|
||||
}
|
||||
@@ -353,10 +326,10 @@ static void cp_draw_rows(lv_layer_t *layer, const content_page_def_t *d, int32_t
|
||||
* (hide_footer;密码页跟随实机)
|
||||
*/
|
||||
static void cp_draw_footer(lv_layer_t *layer, const content_page_def_t *d, int32_t nrows) {
|
||||
char buf[32]; /* 页行文本缓冲(栈):"第 %u 页,共 %u 页" 最长远不足 32B */
|
||||
char buf[UI_BUF_STRLEN]; /* 页行文本缓冲(栈):"第 %u 页,共 %u 页" 最长远不足 32B */
|
||||
uint8_t cur; /* 当前页码(1 基) */
|
||||
uint8_t pages; /* 总页数 */
|
||||
lv_area_t t; /* 页脚条带区域(整宽,放置于 CP_PAGE_LINE_Y) */
|
||||
lv_area_t t; /* 页脚条带区域(整宽,放置于 UI_PAGE_LINE_Y) */
|
||||
|
||||
/* 页码计算,两种来源:
|
||||
* - 列表页(selectable):随选中行现算——当前页 = 选中行/每页行数 + 1
|
||||
@@ -373,12 +346,12 @@ static void cp_draw_footer(lv_layer_t *layer, const content_page_def_t *d, int32
|
||||
pages = d->fixed_total_pages;
|
||||
}
|
||||
|
||||
/* 页脚条带:整宽,放置于 CP_PAGE_LINE_Y(143,字形顶距已计入放置 y,
|
||||
/* 页脚条带:整宽,放置于 UI_PAGE_LINE_Y(143,字形顶距已计入放置 y,
|
||||
* 墨迹与参考图对齐);高 15 = 字体行高 13 + 余量,整行文字一次覆盖 */
|
||||
t.x1 = 0;
|
||||
t.x2 = CP_SCREEN_W - 1;
|
||||
t.y1 = CP_PAGE_LINE_Y;
|
||||
t.y2 = CP_PAGE_LINE_Y + 15;
|
||||
t.x2 = UI_SCREEN_W - 1;
|
||||
t.y1 = UI_PAGE_LINE_Y;
|
||||
t.y2 = UI_PAGE_LINE_Y + UI_TEXT_BAND_H;
|
||||
|
||||
/* 双重门控:① 本页开启页脚(hide_footer;密码页跟随实机:无页码)
|
||||
* ② 页脚条带与重绘区相交(culling)——两者皆成立才格式化并绘制 */
|
||||
@@ -388,13 +361,13 @@ static void cp_draw_footer(lv_layer_t *layer, const content_page_def_t *d, int32
|
||||
|
||||
lv_draw_label_dsc_t ld;
|
||||
|
||||
/* ofs 相对 coords 原点(t.x1=0,t.y1=CP_PAGE_LINE_Y):
|
||||
/* ofs 相对 coords 原点(t.x1=0,t.y1=UI_PAGE_LINE_Y):
|
||||
* 水平 6 = 左对齐、左留白 6px(与实机一致);垂直 0(顶距已计入放置 y) */
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.text = buf; /* 栈缓冲:本页码每次现算,同帧内有效(同步光栅化) */
|
||||
ld.color = lv_color_white(); /* 页码恒白字(不像选中行那样反色) */
|
||||
ld.ofs_x = 6;
|
||||
ld.color = UI_COL_FG; /* 页码恒白字(不像选中行那样反色) */
|
||||
ld.ofs_x = UI_FOOTER_X;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &t);
|
||||
}
|
||||
@@ -410,7 +383,7 @@ static void cp_draw_footer(lv_layer_t *layer, const content_page_def_t *d, int32
|
||||
* 跳过不在重绘区的部分;末尾调 draw_cb(页自定义内容,在内置区块之后)
|
||||
*/
|
||||
static void cp_draw_cb(lv_event_t *e) {
|
||||
const content_page_def_t *d = s_defs[s_open];
|
||||
const content_page_def_t *d = s_open;
|
||||
lv_layer_t *layer = lv_event_get_layer(e);
|
||||
int32_t nrows = (int32_t)cp_row_total(d); /* 行数取一次:供行窗口与页脚页码共用 */
|
||||
|
||||
@@ -438,7 +411,7 @@ static void cp_draw_cb(lv_event_t *e) {
|
||||
* 信息页不响应;动态行数可能缩小,选中行越界时重置回首行
|
||||
*/
|
||||
static void cp_key_cb(lv_event_t *e) {
|
||||
const content_page_def_t *d = s_defs[s_open];
|
||||
const content_page_def_t *d = s_open;
|
||||
lv_key_t key = (lv_key_t)lv_event_get_key(e);
|
||||
|
||||
ui_standby_notify_activity(); /* 用户活动:重置待机空闲计时 */
|
||||
@@ -496,24 +469,23 @@ static void cp_key_cb(lv_event_t *e) {
|
||||
|
||||
/*
|
||||
* 函数功能:打开指定内容页(建屏 + 切屏 + 焦点置页面容器)
|
||||
* 入口参数:id - 内容页 ID content_page_id_t
|
||||
* 入口参数:desc - 页面描述符(取自 ui_page_registry_for / ui_page_registry_password)
|
||||
* const content_page_def_t *
|
||||
* perm - 本次进入授予的权限 ui_perm_t(UI_PERM_VIEW/MODIFY)
|
||||
* 返回值:true - 成功;false - 失败(ID 无效/已有页面打开/无描述符/对象创建失败)
|
||||
* 返回值:true - 成功;false - 失败(desc 为 NULL / 已有页面打开 / 对象创建失败)
|
||||
* 限定条件:ui_menu_init() 已完成(默认输入组与菜单屏存在)
|
||||
* 函数说明:1. lv_obj_create(NULL) 新建独立屏幕 + 全屏容器(自绘载体,无子对象)
|
||||
* 2. 容器入默认输入组并获焦后再切屏——按键立即由本模块接管
|
||||
* 3. 导航状态不压层:菜单 s_depth/s_sel 原样保留,ESC 返回即还原
|
||||
* 4. 记录授予权限(页面经 ui_content_page_is_editable 查询)
|
||||
*/
|
||||
bool ui_content_page_open(content_page_id_t id, uint8_t perm) {
|
||||
if (id >= CONTENT_PAGE_COUNT || s_open != CONTENT_PAGE_COUNT) {
|
||||
return false; /* ID 无效或已有页面打开(防御) */
|
||||
bool ui_content_page_open(const content_page_def_t *desc, uint8_t perm) {
|
||||
if (s_open != NULL) {
|
||||
return false; /* 已有页面打开(防御:页面按需建毁,同一时刻至多一页) */
|
||||
}
|
||||
|
||||
const content_page_def_t *d = s_defs[id];
|
||||
if (d == NULL) {
|
||||
DBG_ERROR("page open: id=%u no descriptor (enum/table drift)", (unsigned)id);
|
||||
return false; /* 枚举/描述符表漂移:拒开并留痕,不崩溃 */
|
||||
if (desc == NULL) {
|
||||
DBG_ERROR("page open: NULL descriptor (node has no bound page)");
|
||||
return false; /* 调用方把未绑定内容页的叶子当页面开了:拒开并留痕,不崩溃 */
|
||||
}
|
||||
|
||||
s_perm = perm; /* 本次授予权限(页面可编辑性 = edit_capable 且修改权限) */
|
||||
@@ -530,17 +502,17 @@ bool ui_content_page_open(content_page_id_t id, uint8_t perm) {
|
||||
return false;
|
||||
}
|
||||
lv_obj_add_flag(s_cont, LV_OBJ_FLAG_IGNORE_LAYOUT); /* 绝对定位,不参与父级布局 */
|
||||
lv_obj_set_size(s_cont, CP_SCREEN_W, CP_SCREEN_H); /* 全屏覆盖 */
|
||||
lv_obj_add_style(s_cont, &cp_style_flat, 0); /* 透明、无边框:剥离主题 card */
|
||||
lv_obj_set_size(s_cont, UI_SCREEN_W, UI_SCREEN_H); /* 全屏覆盖 */
|
||||
lv_obj_add_style(s_cont, &ui_style_flat, 0); /* 透明、无边框:剥离主题 card */
|
||||
lv_obj_add_flag(s_cont, LV_OBJ_FLAG_CLICK_FOCUSABLE); /* 可获焦:键事件入口 */
|
||||
lv_obj_add_event_cb(s_cont, cp_key_cb, LV_EVENT_KEY, NULL);
|
||||
lv_obj_add_event_cb(s_cont, cp_draw_cb, LV_EVENT_DRAW_MAIN, NULL);
|
||||
|
||||
s_open = id;
|
||||
s_open = desc;
|
||||
s_sel = 0u; /* 每次进入从首行开始(页面按需建毁,不跨次保留) */
|
||||
|
||||
if (d->init_cb != NULL) {
|
||||
d->init_cb(); /* 页面模块重置运行态(编辑模式/光标/保存标志等) */
|
||||
if (desc->init_cb != NULL) {
|
||||
desc->init_cb(); /* 页面模块重置运行态(编辑模式/光标/保存标志等) */
|
||||
}
|
||||
|
||||
/* 焦点/键入口:入默认输入组并获焦(先于切屏,保证按键立即由本模块接管) */
|
||||
@@ -551,7 +523,7 @@ bool ui_content_page_open(content_page_id_t id, uint8_t perm) {
|
||||
}
|
||||
|
||||
lv_screen_load(s_scr); /* 切到内容页(菜单屏对象保留、不销毁) */
|
||||
DBG_INFO("page open: id=%u rows=%u", (unsigned)id, (unsigned)cp_row_total(d));
|
||||
DBG_INFO("page open: id=%s rows=%u", desc->id, (unsigned)cp_row_total(desc));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -564,7 +536,7 @@ bool ui_content_page_open(content_page_id_t id, uint8_t perm) {
|
||||
* 2. 切回菜单屏后销毁页面屏幕,LVGL 池内存随即回落
|
||||
*/
|
||||
void ui_content_page_close(void) {
|
||||
if (s_open == CONTENT_PAGE_COUNT || s_cont == NULL) {
|
||||
if (s_open == NULL || s_cont == NULL) {
|
||||
return; /* 未打开(防御) */
|
||||
}
|
||||
|
||||
@@ -574,14 +546,14 @@ void ui_content_page_close(void) {
|
||||
lv_screen_load(lv_obj_get_screen(menu_cont)); /* 切回菜单屏(对象未动,原样还原) */
|
||||
}
|
||||
|
||||
DBG_INFO("page close: id=%u", (unsigned)s_open);
|
||||
DBG_INFO("page close: id=%s", s_open->id);
|
||||
|
||||
lv_obj_delete(s_cont); /* 先删容器 */
|
||||
lv_obj_delete(s_scr); /* 再删屏 */
|
||||
|
||||
s_scr = NULL;
|
||||
s_cont = NULL;
|
||||
s_open = CONTENT_PAGE_COUNT;
|
||||
s_open = NULL;
|
||||
s_sel = 0u;
|
||||
s_perm = (uint8_t)UI_PERM_VIEW; /* 权限重置(卫生:不留上次进入的授权) */
|
||||
}
|
||||
@@ -594,7 +566,7 @@ void ui_content_page_close(void) {
|
||||
* 函数说明:供逻辑层/调试判断当前处于菜单屏还是内容页
|
||||
*/
|
||||
bool ui_content_page_is_open(void) {
|
||||
return (s_open != CONTENT_PAGE_COUNT);
|
||||
return (s_open != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -605,10 +577,10 @@ bool ui_content_page_is_open(void) {
|
||||
* 函数说明:供页面钩子(key_cb/draw_cb)决定编辑行为;权限由 open 的 perm 实参授予
|
||||
*/
|
||||
bool ui_content_page_is_editable(void) {
|
||||
if (s_open == CONTENT_PAGE_COUNT || s_defs[s_open] == NULL) {
|
||||
if (s_open == NULL) {
|
||||
return false; /* 无页打开或无描述符(防御) */
|
||||
}
|
||||
return (s_defs[s_open]->edit_capable != 0u && s_perm == (uint8_t)UI_PERM_MODIFY);
|
||||
return (s_open->edit_capable != 0u && s_perm == (uint8_t)UI_PERM_MODIFY);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -619,10 +591,10 @@ bool ui_content_page_is_editable(void) {
|
||||
* 函数说明:供页面钩子(key_cb/draw_cb)感知当前选中位置
|
||||
*/
|
||||
uint16_t ui_content_page_selected_row(void) {
|
||||
if (s_open == CONTENT_PAGE_COUNT || s_defs[s_open] == NULL) {
|
||||
if (s_open == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
if (s_defs[s_open]->selectable == 0u) {
|
||||
if (s_open->selectable == 0u) {
|
||||
return 0u; /* 信息页/表单页无行选中概念 */
|
||||
}
|
||||
return s_sel;
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
* 屏/容器按需建毁生命周期 + 焦点 + 切屏、按键分发(内置 ↑↓/ESC + key_cb 钩子)、
|
||||
* 描述符驱动通用绘制(浅 V + 标题/副标题 + 行 + 选中带 + 页脚 + draw_cb);
|
||||
* 不持任何页面数据:各页数据/几何/钩子由 ui_page_xxx 模块经 content_page_def_t
|
||||
* 接入框架描述符表(架构见 内容页架构.md);
|
||||
* 提供,框架按**描述符指针**开页;"哪个叶子进哪一页"的绑定在生成物
|
||||
* ui_page_registry.c(真源 = 菜单树 ui_menu_tree.txt 的 #page= 注记),
|
||||
* 框架既无页 ID 枚举也无节点→页手写 case(架构见 内容页架构.md);
|
||||
* 进入时切屏并获焦、ESC 返回菜单屏并还原焦点;
|
||||
* 不持有导航状态——由逻辑层 ui_menu.c 在 ENTER 叶子节点时驱动
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||||
@@ -23,6 +25,11 @@
|
||||
* 是否过密码页/所输密码决定);描述符新增 edit_capable(页面
|
||||
* 是否支持编辑模式)+ 新 API ui_content_page_is_editable
|
||||
* (页面钩子据以决定编辑行为:edit_capable 且修改权限)
|
||||
* 2026-10-15 王建锋 U3 页面原型化:删 content_page_id_t 枚举与框架侧 s_defs[],
|
||||
* open() 改收描述符指针;节点→页面绑定移交给生成物
|
||||
* ui_page_registry.c(菜单树 #page= 注记为唯一真源),
|
||||
* 逻辑层手写 menu_content_page_for() 随之删除;
|
||||
* 描述符新增 id 字段(ASCII 页名,供日志/诊断定位是哪一页)
|
||||
*/
|
||||
#ifndef UI_CONTENT_PAGE_H
|
||||
#define UI_CONTENT_PAGE_H
|
||||
@@ -32,21 +39,6 @@
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
/*
|
||||
* 内容页 ID:与菜单叶子节点一一对应(映射见 ui_menu.c menu_content_page_for);
|
||||
* CONTENT_PAGE_COUNT 兼作"无内容页/未打开"哨兵值
|
||||
*/
|
||||
typedef enum {
|
||||
CONTENT_PAGE_DEVICE_INFO = 0u, /* 装置信息(node 1):静态键值行 + 副标题,单页 */
|
||||
CONTENT_PAGE_AC, /* 交流量(node 3):数据列表,24 项 / 8 项每页 */
|
||||
CONTENT_PAGE_DC, /* 直流量(node 4):数据列表,3 项单页 */
|
||||
CONTENT_PAGE_PASSWORD, /* 用户密钥(密码页):3 位密码,表单/操作页首个实例 */
|
||||
CONTENT_PAGE_NET_CFG, /* 网口设置(node 30):地址列表 + 保存行,可编辑(修改权限) */
|
||||
CONTENT_PAGE_RUN_LOG, /* 运行记录(node 40):动态列表,每记录 2 行(时间戳+描述) */
|
||||
CONTENT_PAGE_SELFTEST_LOG, /* 自检记录(node 39):动态列表,每记录 2 行(时间戳+故障) */
|
||||
CONTENT_PAGE_COUNT /* 哨兵:无效 ID / 未打开 */
|
||||
} content_page_id_t;
|
||||
|
||||
/* 行数据:左列名 + 右列值(Flash 字面量,不 strdup) */
|
||||
typedef struct {
|
||||
const char *name;
|
||||
@@ -54,14 +46,18 @@ typedef struct {
|
||||
} cp_row_t;
|
||||
|
||||
/*
|
||||
* 页面描述符:一页 = 一个 Flash const 结构(几何 + 数据源 + 可选钩子)
|
||||
* 由各 ui_page_xxx 页面模块提供,框架描述符表 s_defs[] 按页 ID 引用
|
||||
* 页面描述符:一页 = 一个 Flash const 结构(身份 + 几何 + 数据源 + 可选钩子)
|
||||
* 由各 ui_page_xxx 页面模块提供;"哪个叶子进哪一页"的绑定不在本头,也不在框架 .c,
|
||||
* 而在生成物 ui_page_registry.c(唯一真源 = 菜单树 ui_menu_tree.txt 的 #page= 注记)。
|
||||
* 框架按描述符**指针**开页,不再有页 ID 枚举与 s_defs[] 下标表——
|
||||
* 少一处"枚举/表/节点下标三处必须对齐"的手工同步点。
|
||||
*/
|
||||
typedef struct {
|
||||
const char *id; /* 页面模块名(ASCII,日志/诊断用;= ui_page_<id> 的 <id>) */
|
||||
const char *title; /* 标题(浅 V 区居中) */
|
||||
const char *subtitle; /* 副标题;NULL = 无(仅信息页有) */
|
||||
int32_t subtitle_y; /* 副标题放置 y(仅 subtitle 非空时有效) */
|
||||
int32_t title_y; /* 标题放置 y;0 = 框架默认(CP_TITLE_Y) */
|
||||
int32_t title_y; /* 标题放置 y;0 = 框架默认(UI_TITLE_Y) */
|
||||
uint8_t hide_deco; /* 1 = 不绘"浅 V"装饰(密码页跟随实机) */
|
||||
uint8_t hide_footer; /* 1 = 不绘页脚页码(密码页跟随实机:无页脚) */
|
||||
uint8_t selectable; /* 1 = 列表页(↑↓移选+分页);0 = 信息页(静态行) */
|
||||
@@ -88,17 +84,18 @@ typedef struct {
|
||||
|
||||
/*
|
||||
* 函数功能:打开指定内容页(建屏 + 切屏 + 焦点置页面容器)
|
||||
* 入口参数:id - 内容页 ID content_page_id_t
|
||||
* 入口参数:desc - 页面描述符(取自 ui_page_registry_for / ui_page_registry_password)
|
||||
* const content_page_def_t *
|
||||
* perm - 本次进入授予的权限 ui_perm_t(UI_PERM_VIEW/MODIFY;
|
||||
* 由逻辑层按是否过密码页/所输密码决定,页面经
|
||||
* ui_content_page_is_editable 查询)
|
||||
* 返回值:true - 成功;false - 失败(ID 无效/已有页面打开/无描述符/对象创建失败)
|
||||
* 返回值:true - 成功;false - 失败(desc 为 NULL / 已有页面打开 / 对象创建失败)
|
||||
* 限定条件:ui_menu_init() 已完成(默认输入组与菜单屏存在)
|
||||
* 函数说明:1. 新建独立屏幕,全屏容器自绘全部内容(无子对象,池开销极小)
|
||||
* 2. 容器入默认输入组并获焦——按键由本模块处理(↑↓移选/ESC返回/页面钩子)
|
||||
* 3. 导航状态不压层:菜单 s_depth/s_sel 原样保留,ESC 返回即还原
|
||||
*/
|
||||
bool ui_content_page_open(content_page_id_t id, uint8_t perm);
|
||||
bool ui_content_page_open(const content_page_def_t *desc, uint8_t perm);
|
||||
|
||||
/*
|
||||
* 函数功能:关闭当前内容页(焦点回菜单容器 + 切回菜单屏 + 销毁页面)
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
|
||||
#include "ui_deco.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
/* 私有宏定义区 */
|
||||
|
||||
#define DECO_SCREEN_W 160 /* 屏幕宽(px,与 ui_port.h / 模拟器一致) */
|
||||
#define DECO_LEG_W 3 /* "浅 V"两腿线宽(1bpp 下斜线边缘易被阈值吃掉,需加粗) */
|
||||
#define DECO_HORIZ_W 2 /* "浅 V"水平线宽 */
|
||||
|
||||
@@ -31,7 +32,7 @@ static const lv_point_precise_t s_leg_r[2] = { {158, 7}, {145, 17} }; /* 右腿
|
||||
static const lv_point_precise_t *const s_segs[3] = {s_leg_l, s_leg_r, s_hoz};
|
||||
static const int32_t s_seg_w[3] = {DECO_LEG_W, DECO_LEG_W, DECO_HORIZ_W};
|
||||
|
||||
static const lv_area_t s_band = {0, 0, DECO_SCREEN_W - 1, 19}; /* "浅 V" culling 区(与原线段对象范围一致) */
|
||||
static const lv_area_t s_band = {0, 0, UI_SCREEN_W - 1, 19}; /* "浅 V" culling 区(与原线段对象范围一致) */
|
||||
|
||||
/* 函数定义区 */
|
||||
|
||||
@@ -72,7 +73,7 @@ void ui_deco_draw(lv_layer_t *layer, const lv_area_t *clip) {
|
||||
|
||||
lv_draw_line_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.color = lv_color_white();
|
||||
ld.color = UI_COL_FG;
|
||||
ld.width = s_seg_w[i];
|
||||
ld.p1 = s_segs[i][0];
|
||||
ld.p2 = s_segs[i][1];
|
||||
|
||||
@@ -311,11 +311,6 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||
0x7f, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xff, 0x80,
|
||||
|
||||
/* U+4E0A "上" */
|
||||
0x8, 0x1, 0x0, 0x20, 0x4, 0x0, 0xfc, 0x10,
|
||||
0x2, 0x0, 0x40, 0x8, 0x1, 0x0, 0x20, 0x7f,
|
||||
0xf0,
|
||||
|
||||
/* U+4E3B "主" */
|
||||
0x8, 0x0, 0x80, 0x0, 0x3f, 0xe0, 0x40, 0x8,
|
||||
0x1, 0x3, 0xfe, 0x4, 0x0, 0x80, 0x10, 0x7f,
|
||||
@@ -671,11 +666,6 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||
0x7f, 0xd1, 0x14, 0x45, 0xff, 0x44, 0x51, 0x17,
|
||||
0xfd, 0x11, 0x44, 0x51, 0x18, 0x1c,
|
||||
|
||||
/* U+7535 "电" */
|
||||
0x8, 0x1, 0x3, 0xfe, 0x44, 0x48, 0x89, 0xff,
|
||||
0x22, 0x24, 0x44, 0xff, 0x91, 0x4, 0x20, 0x83,
|
||||
0xf0,
|
||||
|
||||
/* U+754C "界" */
|
||||
0x7f, 0xc8, 0x89, 0xff, 0x22, 0x27, 0xfc, 0x8,
|
||||
0x6, 0xc7, 0x7, 0x11, 0x2, 0x20, 0x84, 0x20,
|
||||
@@ -828,11 +818,6 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||
0x17, 0xfa, 0x8, 0x49, 0x29, 0xfd, 0x0, 0x5f,
|
||||
0xf0,
|
||||
|
||||
/* U+91CD "重" */
|
||||
0x3, 0xcf, 0x80, 0x10, 0x7f, 0xf4, 0x44, 0xff,
|
||||
0x91, 0x13, 0xfe, 0x4, 0xf, 0xf8, 0x10, 0x7f,
|
||||
0xf0,
|
||||
|
||||
/* U+91CF "量" */
|
||||
0x7f, 0xc8, 0x89, 0x9, 0x7f, 0xf4, 0x44, 0xff,
|
||||
0x91, 0x13, 0xfe, 0x4, 0xf, 0xf8, 0x10, 0x7f,
|
||||
@@ -1018,20 +1003,20 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||
{.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 = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 757, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 774, .adv_w = 192, .box_w = 10, .box_h = 11, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 788, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 804, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 821, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 837, .adv_w = 192, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = -1},
|
||||
{.bitmap_index = 757, .adv_w = 192, .box_w = 10, .box_h = 11, .ofs_x = 1, .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 = 9, .box_h = 10, .ofs_x = 1, .ofs_y = -1},
|
||||
{.bitmap_index = 832, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 849, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 866, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 883, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 900, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 917, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 934, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 951, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 968, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 951, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 967, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 984, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1001, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1018, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
@@ -1042,8 +1027,8 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||
{.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 = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1154, .adv_w = 192, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1168, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1185, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1202, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1219, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
@@ -1057,9 +1042,9 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||
{.bitmap_index = 1355, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1372, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1389, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1406, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1423, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1439, .adv_w = 192, .box_w = 9, .box_h = 11, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1406, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1422, .adv_w = 192, .box_w = 9, .box_h = 11, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1435, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1452, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1469, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1486, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
@@ -1067,22 +1052,22 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||
{.bitmap_index = 1520, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1537, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1554, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .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 = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1571, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1587, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1604, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1621, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1638, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1655, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1672, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1689, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1706, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1723, .adv_w = 192, .box_w = 10, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1737, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1754, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1771, .adv_w = 192, .box_w = 10, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1786, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1803, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1820, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1706, .adv_w = 192, .box_w = 10, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1720, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1737, .adv_w = 192, .box_w = 10, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1752, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1769, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1786, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1802, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1819, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1836, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1853, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1870, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
@@ -1091,9 +1076,9 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||
{.bitmap_index = 1921, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1938, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1955, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1972, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 1989, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2006, .adv_w = 192, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1972, .adv_w = 192, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
|
||||
{.bitmap_index = 1986, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2003, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2020, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2037, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2054, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
@@ -1113,18 +1098,15 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||
{.bitmap_index = 2292, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2309, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2326, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2343, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2360, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2377, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2394, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2343, .adv_w = 192, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2359, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2376, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2393, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2410, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2427, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2444, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2461, .adv_w = 192, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
|
||||
{.bitmap_index = 2478, .adv_w = 192, .box_w = 2, .box_h = 4, .ofs_x = 2, .ofs_y = 0},
|
||||
{.bitmap_index = 2479, .adv_w = 192, .box_w = 2, .box_h = 6, .ofs_x = 2, .ofs_y = 0},
|
||||
{.bitmap_index = 2481, .adv_w = 165, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1} /* U+25B6 */,
|
||||
{.bitmap_index = 2491, .adv_w = 192, .box_w = 2, .box_h = 4, .ofs_x = 2, .ofs_y = 0} /* U+FF0C */
|
||||
{.bitmap_index = 2427, .adv_w = 192, .box_w = 2, .box_h = 4, .ofs_x = 2, .ofs_y = 0},
|
||||
{.bitmap_index = 2428, .adv_w = 192, .box_w = 2, .box_h = 6, .ofs_x = 2, .ofs_y = 0},
|
||||
{.bitmap_index = 2430, .adv_w = 165, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1} /* U+25B6 */,
|
||||
{.bitmap_index = 2440, .adv_w = 192, .box_w = 2, .box_h = 4, .ofs_x = 2, .ofs_y = 0} /* U+FF0C */
|
||||
};
|
||||
|
||||
/*---------------------
|
||||
@@ -1132,22 +1114,21 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||
*--------------------*/
|
||||
|
||||
static const uint16_t unicode_list_1[] = {
|
||||
0x0, 0x1, 0x32, 0x82, 0x9b, 0xed, 0x153, 0x1d4,
|
||||
0x1d8, 0x233, 0x35c, 0x368, 0x36a, 0x42d, 0x444, 0x49f,
|
||||
0x50d, 0x531, 0x582, 0x5b9, 0x5c3, 0x5da, 0x5ee, 0x626,
|
||||
0x641, 0x927, 0x937, 0x982, 0xb04, 0xd47, 0xd4e, 0xd4f,
|
||||
0xd91, 0xd95, 0xdbd, 0x102f, 0x1086, 0x10f9, 0x1106, 0x1131,
|
||||
0x1149, 0x114a, 0x114c, 0x1266, 0x142e, 0x1442, 0x148d, 0x149b,
|
||||
0x149c, 0x1565, 0x159e, 0x15a0, 0x16c4, 0x173c, 0x1767, 0x176b,
|
||||
0x17d7, 0x17dc, 0x17ed, 0x1916, 0x1923, 0x1976, 0x19dc, 0x1a18,
|
||||
0x1ab7, 0x1c18, 0x1d72, 0x1ed9, 0x1f38, 0x1f42, 0x1ffc, 0x22b0,
|
||||
0x243e, 0x243f, 0x271f, 0x272c, 0x2743, 0x28eb, 0x2902, 0x29f8,
|
||||
0x2a63, 0x2c02, 0x2ce6, 0x2d23, 0x2e72, 0x2ef2, 0x30b6, 0x30bf,
|
||||
0x30eb, 0x3148, 0x3165, 0x33e1, 0x34a6, 0x3a43, 0x3abc, 0x3d5d,
|
||||
0x3d9b, 0x3da6, 0x3da7, 0x3db5, 0x3dd9, 0x3de6, 0x4166, 0x418a,
|
||||
0x41c7, 0x4211, 0x425c, 0x43c4, 0x43c6, 0x4696, 0x469c, 0x4710,
|
||||
0x4829, 0x485b, 0x4959, 0x4a6c, 0x4b7f, 0x4c83, 0x50cf, 0xb103,
|
||||
0xb111
|
||||
0x0, 0x32, 0x82, 0x9b, 0xed, 0x153, 0x1d4, 0x1d8,
|
||||
0x233, 0x35c, 0x368, 0x36a, 0x42d, 0x444, 0x49f, 0x50d,
|
||||
0x531, 0x582, 0x5b9, 0x5c3, 0x5da, 0x5ee, 0x626, 0x641,
|
||||
0x927, 0x937, 0x982, 0xb04, 0xd47, 0xd4e, 0xd4f, 0xd91,
|
||||
0xd95, 0xdbd, 0x102f, 0x1086, 0x10f9, 0x1106, 0x1131, 0x1149,
|
||||
0x114a, 0x114c, 0x1266, 0x142e, 0x1442, 0x148d, 0x149b, 0x149c,
|
||||
0x1565, 0x159e, 0x15a0, 0x16c4, 0x173c, 0x1767, 0x176b, 0x17d7,
|
||||
0x17dc, 0x17ed, 0x1916, 0x1923, 0x1976, 0x19dc, 0x1a18, 0x1ab7,
|
||||
0x1c18, 0x1d72, 0x1ed9, 0x1f38, 0x1f42, 0x1ffc, 0x22b0, 0x243e,
|
||||
0x243f, 0x271f, 0x2743, 0x28eb, 0x2902, 0x29f8, 0x2a63, 0x2c02,
|
||||
0x2ce6, 0x2d23, 0x2e72, 0x2ef2, 0x30b6, 0x30bf, 0x30eb, 0x3148,
|
||||
0x3165, 0x33e1, 0x34a6, 0x3a43, 0x3abc, 0x3d5d, 0x3d9b, 0x3da6,
|
||||
0x3da7, 0x3db5, 0x3dd9, 0x3de6, 0x4166, 0x418a, 0x41c7, 0x4211,
|
||||
0x425c, 0x43c6, 0x4696, 0x469c, 0x4710, 0x4829, 0x485b, 0x4959,
|
||||
0x4a6c, 0x4b7f, 0x4c83, 0x50cf, 0xb103, 0xb111
|
||||
};
|
||||
|
||||
/*Collect the unicode lists and glyph_id offsets*/
|
||||
@@ -1159,14 +1140,14 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
|
||||
},
|
||||
{
|
||||
.range_start = 19977, .range_length = 45330, .glyph_id_start = 96,
|
||||
.unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 121, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
|
||||
.unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 118, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
|
||||
},
|
||||
{
|
||||
.range_start = 9654, .range_length = 1, .glyph_id_start = 217,
|
||||
.range_start = 9654, .range_length = 1, .glyph_id_start = 214,
|
||||
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
|
||||
},
|
||||
{
|
||||
.range_start = 65292, .range_length = 1, .glyph_id_start = 218,
|
||||
.range_start = 65292, .range_length = 1, .glyph_id_start = 215,
|
||||
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
|
||||
}
|
||||
};
|
||||
|
||||
+11
-30
@@ -32,6 +32,8 @@
|
||||
|
||||
#include "ui_content_page.h"
|
||||
|
||||
#include "ui_page_registry.h"
|
||||
|
||||
#include "ui_page_password.h"
|
||||
|
||||
#include "ui_standby.h"
|
||||
@@ -105,28 +107,6 @@ static void menu_back(void) {
|
||||
ui_menu_view_focus();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:查叶子节点对应的内容页(无内容页返回哨兵值)
|
||||
* 入口参数:node - 菜单节点下标 uint8_t
|
||||
* 返回值:内容页 ID content_page_id_t;无内容页 = CONTENT_PAGE_COUNT
|
||||
* 限定条件:无
|
||||
* 函数说明:仅部分叶子有内容页(参考图 10/20/21 + 网口设置 + 记录查询);下标对应
|
||||
* ui_menu_tab.c
|
||||
* node 1=装置信息、3=交流量、4=直流量、30=网口设置、39=自检记录、40=运行记录
|
||||
* (表序变更时须同步本映射)
|
||||
*/
|
||||
static content_page_id_t menu_content_page_for(uint8_t node) {
|
||||
switch (node) {
|
||||
case 1u: return CONTENT_PAGE_DEVICE_INFO; /* 装置信息(L1 叶子) */
|
||||
case 3u: return CONTENT_PAGE_AC; /* 交流量(实时数据子项) */
|
||||
case 4u: return CONTENT_PAGE_DC; /* 直流量(实时数据子项) */
|
||||
case 30u: return CONTENT_PAGE_NET_CFG; /* 网口设置(通讯参数子项,需密码) */
|
||||
case 39u: return CONTENT_PAGE_SELFTEST_LOG; /* 自检记录(记录查询子项) */
|
||||
case 40u: return CONTENT_PAGE_RUN_LOG; /* 运行记录(记录查询子项) */
|
||||
default: return (content_page_id_t)CONTENT_PAGE_COUNT; /* 无内容页 */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:密码页确认一致回调(预装进密码页模块;uiTask 上下文调用)
|
||||
* 入口参数:perm - 密码页按所输密码授予的权限 ui_perm_t(UI_PERM_MODIFY/UI_PERM_VIEW)
|
||||
@@ -152,10 +132,10 @@ static void menu_password_ok(uint8_t perm) {
|
||||
|
||||
{
|
||||
uint8_t child = kids[s_sel[depth]];
|
||||
content_page_id_t pid = menu_content_page_for(child);
|
||||
const content_page_def_t *page = ui_page_registry_for(child);
|
||||
|
||||
if (pid < CONTENT_PAGE_COUNT) {
|
||||
ui_content_page_open(pid, perm); /* 页已关,守卫可过:开目标内容页(携权限) */
|
||||
if (page != NULL) {
|
||||
ui_content_page_open(page, perm); /* 页已关,守卫可过:开目标内容页(携权限) */
|
||||
}
|
||||
else if (depth < MENU_PAGE_MAX - 1u) {
|
||||
menu_open(child); /* 压层:建新框 + 焦点移到新框(叶子 → 空白终点框) */
|
||||
@@ -202,17 +182,18 @@ static void menu_key_handle(uint8_t depth, lv_key_t key) {
|
||||
/* 进子框:空框无条目可进;最深层(depth=MAX-1)无子层可再开 */
|
||||
if (cnt > 0u) {
|
||||
uint8_t child = kids[s_sel[depth]];
|
||||
content_page_id_t pid = menu_content_page_for(child);
|
||||
const content_page_def_t *page = ui_page_registry_for(child);
|
||||
|
||||
if (ui_menu_tab_has_pwd(child)) {
|
||||
/* 需密码叶子(含带内容页者):先经密码页;授权按所输密码
|
||||
(查看→只读 / 修改→可修改),回调携权续做"进入"动作 */
|
||||
(查看→只读 / 修改→可修改),回调携权续做"进入"动作;
|
||||
密码页非节点绑定,经注册表专用出口取 */
|
||||
ui_page_password_arm(menu_password_ok);
|
||||
ui_content_page_open(CONTENT_PAGE_PASSWORD, (uint8_t)UI_PERM_VIEW);
|
||||
ui_content_page_open(ui_page_registry_password(), (uint8_t)UI_PERM_VIEW);
|
||||
}
|
||||
else if (pid < CONTENT_PAGE_COUNT) {
|
||||
else if (page != NULL) {
|
||||
/* 叶子有内容页:无密码直接进(默认只读权限) */
|
||||
ui_content_page_open(pid, (uint8_t)UI_PERM_VIEW);
|
||||
ui_content_page_open(page, (uint8_t)UI_PERM_VIEW);
|
||||
}
|
||||
else if (depth < MENU_PAGE_MAX - 1u) {
|
||||
menu_open(child); /* 压层:建新框 + 焦点移到新框(叶子 → 空白终点框) */
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
|
||||
#include "ui_deco.h"
|
||||
|
||||
#include "ui_style.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#define DBG_TAG "[UI_MENU_VIEW]"
|
||||
@@ -79,22 +83,8 @@ static uint8_t s_row_node[MENU_PAGE_MAX][MENU_ITEM_MAX]; /* 行数据:节
|
||||
static menu_frame_t s_frame[MENU_PAGE_MAX]; /* 每层框几何缓存:位置/尺寸/选中行(现算不落状态,供子框对齐;见上方类型注释) */
|
||||
|
||||
/* 样式(静态,一次性初始化,常驻) */
|
||||
/* flat:通用——透明、无边框/圆角/内边距(剥离主题 card)。用常量样式(LV_STYLE_CONST_INIT):
|
||||
* 属性存静态数组、零 LV_MEM 池分配;动态样式首次 set_prop 会从池分配 values_and_props 且永不释放 */
|
||||
static const lv_style_const_prop_t s_flat_props[] = {
|
||||
{ .prop = LV_STYLE_BG_OPA, .value = { .num = LV_OPA_TRANSP } },
|
||||
{ .prop = LV_STYLE_BORDER_WIDTH, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_RADIUS, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_LEFT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_RIGHT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_TOP, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_BOTTOM, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_ROW, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_COLUMN, .value = { .num = 0 } },
|
||||
LV_STYLE_CONST_PROPS_END
|
||||
};
|
||||
|
||||
LV_STYLE_CONST_INIT(s_style_flat, s_flat_props);
|
||||
/* flat(透明、无边框/圆角/内边距,剥离主题 card)取自共享样式表 ui_style.c:
|
||||
* 与 ui_content_page / ui_standby 共用同一 const 对象,不再本地重复定义 */
|
||||
static lv_style_t s_style_frame; /* 框体:透明底 + 1px 白边框 */
|
||||
static lv_style_t s_style_scrollbar; /* 滚动条:白色、4px 宽 */
|
||||
|
||||
@@ -122,7 +112,7 @@ static void view_styles_init(void) {
|
||||
lv_style_init(&s_style_frame);
|
||||
lv_style_set_bg_opa(&s_style_frame, LV_OPA_TRANSP);
|
||||
lv_style_set_border_width(&s_style_frame, MENU_FRAME_BORDER);
|
||||
lv_style_set_border_color(&s_style_frame, lv_color_white());
|
||||
lv_style_set_border_color(&s_style_frame, UI_COL_FG);
|
||||
lv_style_set_radius(&s_style_frame, 0);
|
||||
lv_style_set_pad_all(&s_style_frame, 0);
|
||||
lv_style_set_pad_gap(&s_style_frame, 0);
|
||||
@@ -130,8 +120,8 @@ static void view_styles_init(void) {
|
||||
/* scrollbar:白色 4px 宽(行超框高时自动出现) */
|
||||
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_set_bg_color(&s_style_scrollbar, UI_COL_FG);
|
||||
lv_style_set_width(&s_style_scrollbar, UI_SCROLLBAR_W);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -237,7 +227,7 @@ static void view_frame_draw_cb(lv_event_t *e) {
|
||||
lv_area_t row_area;
|
||||
const char *name = ui_menu_tab_name(s_row_node[depth][i]);
|
||||
bool is_sel = (i == (int32_t)f->sel);
|
||||
lv_color_t color = is_sel ? lv_color_black() : lv_color_white();
|
||||
lv_color_t color = is_sel ? UI_COL_SEL_FG : UI_COL_FG;
|
||||
|
||||
row_area.x1 = area.x1;
|
||||
row_area.x2 = area.x2;
|
||||
@@ -254,7 +244,7 @@ static void view_frame_draw_cb(lv_event_t *e) {
|
||||
lv_draw_rect_dsc_t fd;
|
||||
lv_draw_rect_dsc_init(&fd);
|
||||
fd.base.layer = layer;
|
||||
fd.bg_color = lv_color_white();
|
||||
fd.bg_color = UI_COL_SEL_BG;
|
||||
fd.bg_opa = LV_OPA_COVER;
|
||||
lv_draw_rect(layer, &fd, &row_area);
|
||||
}
|
||||
@@ -407,7 +397,7 @@ void ui_menu_view_frame_build(uint8_t depth, uint8_t node, uint8_t sel, uint16_t
|
||||
if (box == NULL) {
|
||||
return; /* LVGL 池耗尽:框未建,状态回滚 */
|
||||
}
|
||||
lv_obj_add_style(box, &s_style_flat, 0);
|
||||
lv_obj_add_style(box, &ui_style_flat, 0);
|
||||
lv_obj_add_style(box, &s_style_frame, 0);
|
||||
lv_obj_add_flag(box, LV_OBJ_FLAG_IGNORE_LAYOUT);
|
||||
lv_obj_set_pos(box, f->x, f->y);
|
||||
@@ -571,7 +561,7 @@ static void view_deco_draw_cb(lv_event_t *e) {
|
||||
sd.base.layer = layer;
|
||||
sd.text = "当前模式 : 无模式";
|
||||
sd.text_static = 1; /* Flash 字符串:不拷内存 */
|
||||
sd.color = lv_color_white();
|
||||
sd.color = UI_COL_FG;
|
||||
sd.ofs_x = 0; /* 区域原点即状态行左上 (17,20) */
|
||||
sd.ofs_y = 0;
|
||||
lv_draw_label(layer, &sd, &s_status_area);
|
||||
@@ -608,7 +598,7 @@ int ui_menu_view_init(void) {
|
||||
if (menu == NULL) {
|
||||
return -1;
|
||||
}
|
||||
lv_obj_add_style(menu, &s_style_flat, 0); /* 透明、无边框:纯定位层,不可见 */
|
||||
lv_obj_add_style(menu, &ui_style_flat, 0); /* 透明、无边框:纯定位层,不可见 */
|
||||
lv_obj_set_size(menu, sw, sh);
|
||||
|
||||
/* 持久容器:全屏覆盖、永不删(焦点/键唯一入口;各层框均建其内,防悬空) */
|
||||
@@ -616,7 +606,7 @@ int ui_menu_view_init(void) {
|
||||
if (s_cont == NULL) {
|
||||
return -1;
|
||||
}
|
||||
lv_obj_add_style(s_cont, &s_style_flat, 0); /* 透明、无边框 */
|
||||
lv_obj_add_style(s_cont, &ui_style_flat, 0); /* 透明、无边框 */
|
||||
lv_obj_add_flag(s_cont, LV_OBJ_FLAG_IGNORE_LAYOUT); /* 绝对定位,不参与父级 flex 布局 */
|
||||
lv_obj_set_pos(s_cont, 0, 0);
|
||||
lv_obj_set_size(s_cont, sw, sh); /* 全屏覆盖 */
|
||||
@@ -634,7 +624,7 @@ int ui_menu_view_init(void) {
|
||||
if (s_frame_box[0] == NULL) {
|
||||
return -1;
|
||||
}
|
||||
lv_obj_add_style(s_frame_box[0], &s_style_flat, 0); /* 透明底 */
|
||||
lv_obj_add_style(s_frame_box[0], &ui_style_flat, 0); /* 透明底 */
|
||||
lv_obj_add_style(s_frame_box[0], &s_style_frame, 0); /* 1px 白边框 */
|
||||
lv_obj_add_flag(s_frame_box[0], LV_OBJ_FLAG_IGNORE_LAYOUT);
|
||||
lv_obj_set_pos(s_frame_box[0], MENU_ROOT_BOX_X, MENU_ROOT_BOX_Y); /* 左上 (0,44):装饰/状态行之下 */
|
||||
|
||||
@@ -21,11 +21,15 @@
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
#define MENU_PAGE_MAX 4 /* 页面深度上限(根框 + 3 级子框) */
|
||||
#define MENU_ITEM_MAX 16 /* 单框最大条目数 */
|
||||
|
||||
#define MENU_SCREEN_W 160 /* 屏幕宽(px,与 ui_port.h / 模拟器一致) */
|
||||
#define MENU_SCREEN_H 160 /* 屏幕高(px) */
|
||||
/* 屏幕尺寸单一来源 = ui_tokens.h(权威定义在 ui_port.h,两端一致);
|
||||
* 保留 MENU_SCREEN_* 旧名作为别名,兼容既有引用与本模块文档 */
|
||||
#define MENU_SCREEN_W UI_SCREEN_W /* 屏幕宽(px) */
|
||||
#define MENU_SCREEN_H UI_SCREEN_H /* 屏幕高(px) */
|
||||
|
||||
/*
|
||||
* 函数功能:初始化显示层(样式 + 4 层持久容器 + 根框装饰 + 根框框体)
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "net_socket.h"
|
||||
#include "net_types.h"
|
||||
#include "ui_net_sink_backend.h"
|
||||
#include "ui_tokens.h" /* 屏幕几何令牌(UI_SCREEN_W/H、UI_FB_SIZE 权威在 ui_port.h) */
|
||||
#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */
|
||||
#include "net_latency_test.h" /* M3 探针:prod_cycle_mcu(LCDMEM 处理净成本) */
|
||||
|
||||
@@ -53,16 +54,14 @@
|
||||
#define CMD_KEY UI_REMO_CMD_KEY
|
||||
#define CMD_LCDMEM UI_REMO_CMD_LCDMEM
|
||||
|
||||
/* 屏幕尺寸与帧大小(160x160 1bpp = 3200 字节) */
|
||||
#define UI_W 160
|
||||
#define UI_H 160
|
||||
#define UI_MEMSIZE (UI_W * UI_H / 8)
|
||||
/* 屏幕尺寸与帧大小取 ui_tokens.h / ui_port.h 的屏幕令牌(160x160 1bpp = 3200 字节),
|
||||
* 本模块不再自带同值字面量——显示几何与协议几何必须是同一个事实 */
|
||||
|
||||
#define RM_MAX_RX_PAYLOAD 256 /* 客户端→服务端 payload 上限,防长度字段损坏引发失步 */
|
||||
#define RM_RX_BUF 64 /* 接收暂存区:整块 recv 后逐字节喂入解析状态机 */
|
||||
|
||||
static uint8_t g_tx[4 + 4 + UI_MEMSIZE + 1]; /* 发送组帧缓冲:[TAG][CMD][LEN][DATA][CRC] */
|
||||
static uint8_t g_lcdmem_data[4 + UI_MEMSIZE]; /* LCDMEM 应答缓冲:4B 偏移 + 1bpp 位图 */
|
||||
static uint8_t g_tx[4 + 4 + UI_FB_SIZE + 1]; /* 发送组帧缓冲:[TAG][CMD][LEN][DATA][CRC] */
|
||||
static uint8_t g_lcdmem_data[4 + UI_FB_SIZE]; /* LCDMEM 应答缓冲:4B 偏移 + 1bpp 位图 */
|
||||
static uint8_t g_rx[RM_RX_BUF]; /* 接收暂存区:整块 recv 后逐字节喂入状态机 */
|
||||
|
||||
static int g_listen = -1; /* 监听 Socket fd(未启动或已停止时为 -1) */
|
||||
@@ -194,9 +193,9 @@ static int remo_send(uint8_t cmd, const uint8_t *data, uint16_t len) {
|
||||
static void remo_send_init(void) {
|
||||
uint8_t init[8];
|
||||
|
||||
put_u16(init, UI_W);
|
||||
put_u16(init + 2, UI_H);
|
||||
put_u32(init + 4, UI_MEMSIZE);
|
||||
put_u16(init, UI_SCREEN_W);
|
||||
put_u16(init + 2, UI_SCREEN_H);
|
||||
put_u32(init + 4, UI_FB_SIZE);
|
||||
(void)remo_send(CMD_INIT, init, 8);
|
||||
}
|
||||
|
||||
@@ -295,10 +294,10 @@ static void remo_handle(const uint8_t *f, int n) {
|
||||
#endif
|
||||
/* 整帧请求:经平台后端取 1bpp 位图(MCU: ui_port_get_framebuffer /
|
||||
* 模拟器: sim_port_get_screen_mono)写入应答缓冲尾部,失败则静默丢弃 */
|
||||
if (ui_net_sink_get_lcdmem(g_lcdmem_data + 4, UI_MEMSIZE) == 0) {
|
||||
if (ui_net_sink_get_lcdmem(g_lcdmem_data + 4, UI_FB_SIZE) == 0) {
|
||||
/* 应答数据段 = [偏移u32(回填请求原值)] + [1bpp 位图 3200B] */
|
||||
memcpy(g_lcdmem_data, data, 4);
|
||||
(void)remo_send(CMD_LCDMEM, g_lcdmem_data, (uint16_t)(4 + UI_MEMSIZE));
|
||||
(void)remo_send(CMD_LCDMEM, g_lcdmem_data, (uint16_t)(4 + UI_FB_SIZE));
|
||||
}
|
||||
#if defined(TEST_LATENCY_PROBE)
|
||||
lat_probe_add_seg("prod_cycle_mcu", lat_mark_us(&tm));
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ui_port.h" /* UI_FB_SIZE:显示几何与协议几何的单一来源 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 1bpp 帧缓冲尺寸(160x160 / 8 = 3200 字节,须与 ui_net_sink.c 的 UI_MEMSIZE 一致) */
|
||||
#define UI_NET_SINK_MONO_SIZE 3200
|
||||
/* 1bpp 帧缓冲尺寸 = 屏幕令牌的帧字节数(UI_SCREEN_W*UI_SCREEN_H/8 = 3200),
|
||||
* 与 ui_net_sink.c 的收发缓冲同源,不再各自写死字面量 */
|
||||
#define UI_NET_SINK_MONO_SIZE UI_FB_SIZE
|
||||
|
||||
/*
|
||||
* 函数功能:读取当前 1bpp 屏幕位图(LCDMEM 应答的数据源)
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
/* 头文件包含区 */
|
||||
#include "ui_page_ac.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/* 交流量行(参考图 20 = 第 1 页原样;第 2/3 页为占位数据,可替换) */
|
||||
@@ -43,17 +45,18 @@ static const cp_row_t s_rows[] = {
|
||||
{"Smax","2.45"},
|
||||
};
|
||||
|
||||
/* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用;列表页,钩子全 NULL) */
|
||||
/* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=ac 绑定引用;列表页,钩子全 NULL) */
|
||||
const content_page_def_t ui_page_ac_desc = {
|
||||
.id = "ac",
|
||||
.title = "交流量",
|
||||
.selectable = 1u,
|
||||
.top_y = 24,
|
||||
.pitch = 14,
|
||||
.band_h = 13,
|
||||
.top_y = UI_LIST_TOP,
|
||||
.pitch = UI_ROW_PITCH,
|
||||
.band_h = UI_ROW_BAND_H,
|
||||
.text_dy = 0,
|
||||
.left_x = 7,
|
||||
.right_x = 101,
|
||||
.rows_per_page = 8u,
|
||||
.left_x = UI_LIST_LEFT_X,
|
||||
.right_x = UI_LIST_RIGHT_X,
|
||||
.rows_per_page = UI_LIST_ROWS,
|
||||
.row_count = 24,
|
||||
.rows = s_rows,
|
||||
};
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#include "ui_content_page.h"
|
||||
|
||||
/*
|
||||
* 页面描述符(Flash const):交流量页 = CONTENT_PAGE_AC;
|
||||
* 接入框架描述符表 s_defs[](定义于 ui_page_ac.c)
|
||||
* 页面描述符(Flash const):交流量页(描述符 .id = "ac");
|
||||
* 由生成物 ui_page_registry.c 按菜单树 #page=ac 注记绑定引用(定义于 ui_page_ac.c)
|
||||
*/
|
||||
extern const content_page_def_t ui_page_ac_desc;
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/* 头文件包含区 */
|
||||
#include "ui_page_dc.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/* 直流量行(参考图 21) */
|
||||
@@ -21,17 +23,18 @@ static const cp_row_t s_rows[] = {
|
||||
{"Pa1", "3.288"},
|
||||
};
|
||||
|
||||
/* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用;列表页单页,钩子全 NULL) */
|
||||
/* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=dc 绑定引用;列表页单页,钩子全 NULL) */
|
||||
const content_page_def_t ui_page_dc_desc = {
|
||||
.id = "dc",
|
||||
.title = "直流量",
|
||||
.selectable = 1u,
|
||||
.top_y = 24,
|
||||
.pitch = 14,
|
||||
.band_h = 13,
|
||||
.top_y = UI_LIST_TOP,
|
||||
.pitch = UI_ROW_PITCH,
|
||||
.band_h = UI_ROW_BAND_H,
|
||||
.text_dy = 0,
|
||||
.left_x = 7,
|
||||
.right_x = 101,
|
||||
.rows_per_page = 8u,
|
||||
.left_x = UI_LIST_LEFT_X,
|
||||
.right_x = UI_LIST_RIGHT_X,
|
||||
.rows_per_page = UI_LIST_ROWS,
|
||||
.row_count = 3,
|
||||
.rows = s_rows,
|
||||
};
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#include "ui_content_page.h"
|
||||
|
||||
/*
|
||||
* 页面描述符(Flash const):直流量页 = CONTENT_PAGE_DC;
|
||||
* 接入框架描述符表 s_defs[](定义于 ui_page_dc.c)
|
||||
* 页面描述符(Flash const):直流量页(描述符 .id = "dc");
|
||||
* 由生成物 ui_page_registry.c 按菜单树 #page=dc 注记绑定引用(定义于 ui_page_dc.c)
|
||||
*/
|
||||
extern const content_page_def_t ui_page_dc_desc;
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@ static const cp_row_t s_rows[] = {
|
||||
{"程序日期:", "2024.08.27"},
|
||||
};
|
||||
|
||||
/* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用;静态信息页,钩子全 NULL) */
|
||||
/* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=device_info 绑定引用;静态信息页,钩子全 NULL) */
|
||||
const content_page_def_t ui_page_device_info_desc = {
|
||||
.id = "device_info",
|
||||
.title = "装置信息",
|
||||
.subtitle = "馈线自动化终端",
|
||||
.subtitle_y = 25,
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "ui_content_page.h"
|
||||
|
||||
/*
|
||||
* 页面描述符(Flash const):装置信息页 = CONTENT_PAGE_DEVICE_INFO;
|
||||
* 接入框架描述符表 s_defs[](定义于 ui_page_device_info.c)
|
||||
* 页面描述符(Flash const):装置信息页(描述符 .id = "device_info");
|
||||
* 由生成物 ui_page_registry.c 按菜单树 #page=device_info 注记绑定引用(定义于 ui_page_device_info.c)
|
||||
*/
|
||||
extern const content_page_def_t ui_page_device_info_desc;
|
||||
|
||||
|
||||
@@ -30,10 +30,13 @@
|
||||
|
||||
#include "net_cfg.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
/* 私有宏定义区 */
|
||||
|
||||
/*
|
||||
* 布局常量(设备像素;1bpp 屏 160x160):
|
||||
* 布局取值:行骨架用 ui_tokens.h 的列表页令牌(UI_LIST_TOP / UI_ROW_PITCH /
|
||||
* UI_ROW_BAND_H / UI_BUF_STRLEN),本文件只留本页私有的两列 x 与行数。
|
||||
* 5 行共 1 页(行槽顶 y = 24+14i,末行止于 y=93,与页脚 y=143 无冲突);
|
||||
* 名 x=4 / 值 x=54(值列须 ≥54:4 汉字名占 48px + 间隙;值列宽——
|
||||
* IP/掩码/网关 11 ASCII 字符 = 66px 止于 119,MAC 17 字符(冒号分隔)= 102px 止于 155,
|
||||
@@ -42,12 +45,8 @@
|
||||
#define NC_ROW_COUNT 5u /* 4 地址行 + 1 保存行 */
|
||||
#define NC_ADDR_ROW_MAX 4u /* 地址行范围 0..3(IP/掩码/网关/MAC) */
|
||||
#define NC_SAVE_ROW 4u /* 保存行下标 */
|
||||
#define NC_TOP_Y 24 /* 首行槽顶 y */
|
||||
#define NC_PITCH 14 /* 行距 */
|
||||
#define NC_BAND_H 13 /* 选中白带高 */
|
||||
#define NC_LEFT_X 4 /* 左列 x(名) */
|
||||
#define NC_RIGHT_X 54 /* 右列 x(值) */
|
||||
#define NC_ROW_BUF_LEN 32u /* 行缓冲长度(与框架 CP_ROW_BUF_LEN 一致) */
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
@@ -86,7 +85,7 @@ static uint8_t nc_octet_count(uint16_t row) {
|
||||
/*
|
||||
* 函数功能:生成指定地址行的值串(与 refresh 值列同源,编辑光标定位与行文本一致)
|
||||
* 入口参数:row - 地址行下标(0..3) uint16_t
|
||||
* p_buf - 输出缓冲(≥ NC_ROW_BUF_LEN) char *
|
||||
* p_buf - 输出缓冲(≥ UI_BUF_STRLEN) char *
|
||||
* 返回值:无
|
||||
* 限定条件:p_buf 非空
|
||||
* 函数说明:IP/掩码/网关 = "192.168.001.100"(每段 3 位补零定长,字节 i 恒在
|
||||
@@ -99,7 +98,7 @@ static void nc_fmt_value(uint16_t row, char *p_buf) {
|
||||
uint8_t mac[6];
|
||||
|
||||
net_cfg_get_mac(mac);
|
||||
(void)lv_snprintf(p_buf, NC_ROW_BUF_LEN, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
(void)lv_snprintf(p_buf, UI_BUF_STRLEN, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
(unsigned)mac[0], (unsigned)mac[1], (unsigned)mac[2],
|
||||
(unsigned)mac[3], (unsigned)mac[4], (unsigned)mac[5]);
|
||||
return;
|
||||
@@ -109,15 +108,15 @@ static void nc_fmt_value(uint16_t row, char *p_buf) {
|
||||
const uint8_t *field = (row == 0u) ? cfg->ip
|
||||
: ((row == 1u) ? cfg->mask : cfg->gw);
|
||||
|
||||
net_cfg_fmt_ipv4(field, p_buf, NC_ROW_BUF_LEN);
|
||||
net_cfg_fmt_ipv4(field, p_buf, UI_BUF_STRLEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:动态行刷新(逐行取名/值)
|
||||
* 入口参数:row - 行下标(0..4) uint16_t
|
||||
* name - 名缓冲(≥ NC_ROW_BUF_LEN,框架栈缓冲) char *
|
||||
* value - 值缓冲(≥ NC_ROW_BUF_LEN,框架栈缓冲) char *
|
||||
* name - 名缓冲(≥ UI_BUF_STRLEN,框架栈缓冲) char *
|
||||
* value - 值缓冲(≥ UI_BUF_STRLEN,框架栈缓冲) char *
|
||||
* 返回值:无
|
||||
* 限定条件:页面打开(框架绘制链内逐行调用)
|
||||
* 函数说明:地址行值取 net_cfg RAM 副本(MAC 全零时显示芯片实际 MAC 影子);
|
||||
@@ -133,15 +132,15 @@ static void nc_refresh(uint16_t row, char *name, char *value) {
|
||||
switch (row) {
|
||||
case 0u:
|
||||
(void)memcpy(name, "IP地址", sizeof("IP地址"));
|
||||
net_cfg_fmt_ipv4(cfg->ip, value, NC_ROW_BUF_LEN);
|
||||
net_cfg_fmt_ipv4(cfg->ip, value, UI_BUF_STRLEN);
|
||||
break;
|
||||
case 1u:
|
||||
(void)memcpy(name, "子网掩码", sizeof("子网掩码"));
|
||||
net_cfg_fmt_ipv4(cfg->mask, value, NC_ROW_BUF_LEN);
|
||||
net_cfg_fmt_ipv4(cfg->mask, value, UI_BUF_STRLEN);
|
||||
break;
|
||||
case 2u:
|
||||
(void)memcpy(name, "默认网关", sizeof("默认网关"));
|
||||
net_cfg_fmt_ipv4(cfg->gw, value, NC_ROW_BUF_LEN);
|
||||
net_cfg_fmt_ipv4(cfg->gw, value, UI_BUF_STRLEN);
|
||||
break;
|
||||
case 3u:
|
||||
(void)memcpy(name, "MAC地址", sizeof("MAC地址"));
|
||||
@@ -197,7 +196,7 @@ static void nc_adjust_octet(uint16_t row, int8_t delta) {
|
||||
* 步进宽实测定位(ASCII 步进 6px/字符,与行文本布局一致)
|
||||
*/
|
||||
static void nc_draw_cb(lv_layer_t *layer, const lv_area_t *clip) {
|
||||
char value[NC_ROW_BUF_LEN];
|
||||
char value[UI_BUF_STRLEN];
|
||||
lv_area_t box;
|
||||
uint32_t start;
|
||||
uint32_t seg_len;
|
||||
@@ -219,14 +218,14 @@ static void nc_draw_cb(lv_layer_t *layer, const lv_area_t *clip) {
|
||||
start = (uint32_t)s_cursor * ((sel == 3u) ? 3u : 4u); /* 字节 i 字符偏移:IP/掩码/网关 4i,MAC 3i(冒号分隔) */
|
||||
seg_len = (sel == 3u) ? 2u : 3u;
|
||||
|
||||
slot_y = NC_TOP_Y + (int32_t)sel * NC_PITCH;
|
||||
slot_y = UI_LIST_TOP + (int32_t)sel * UI_ROW_PITCH;
|
||||
x0 = NC_RIGHT_X + (int32_t)lv_text_get_width(value, start, LV_FONT_DEFAULT, 0);
|
||||
w = (int32_t)lv_text_get_width(value + start, seg_len, LV_FONT_DEFAULT, 0);
|
||||
|
||||
box.x1 = x0 - 1;
|
||||
box.x2 = x0 + w;
|
||||
box.y1 = slot_y;
|
||||
box.y2 = slot_y + NC_BAND_H - 1;
|
||||
box.y2 = slot_y + UI_ROW_BAND_H - 1;
|
||||
if (!nc_areas_intersect(&box, clip)) {
|
||||
return; /* 编辑光标不在重绘区:跳过(culling) */
|
||||
}
|
||||
@@ -237,7 +236,7 @@ static void nc_draw_cb(lv_layer_t *layer, const lv_area_t *clip) {
|
||||
|
||||
lv_draw_rect_dsc_init(&fd);
|
||||
fd.base.layer = layer;
|
||||
fd.bg_color = lv_color_black();
|
||||
fd.bg_color = UI_COL_BG;
|
||||
fd.bg_opa = LV_OPA_COVER;
|
||||
lv_draw_rect(layer, &fd, &box);
|
||||
|
||||
@@ -255,7 +254,7 @@ static void nc_draw_cb(lv_layer_t *layer, const lv_area_t *clip) {
|
||||
ld.base.layer = layer;
|
||||
ld.text = seg;
|
||||
ld.text_static = 0u; /* 栈缓冲(同帧有效) */
|
||||
ld.color = lv_color_white();
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = 1; /* box.x1 = x0-1,+1 = x0(与框架行文本原点一致) */
|
||||
ld.ofs_y = 0; /* 与行文本垂直对齐(text_dy = 0) */
|
||||
lv_draw_label(layer, &ld, &box);
|
||||
@@ -346,18 +345,19 @@ static void nc_init_cb(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用):
|
||||
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=net_cfg 绑定引用):
|
||||
* 列表页(5 行 1 页,动态值)+ 可编辑(edit_capable,还须修改权限)
|
||||
* + ← 拦截(编辑模式移字节光标;ESC 恒不可拦截)
|
||||
*/
|
||||
const content_page_def_t ui_page_net_cfg_desc = {
|
||||
.id = "net_cfg",
|
||||
.title = "网口设置",
|
||||
.selectable = 1u,
|
||||
.key_intercept = 1u,
|
||||
.edit_capable = 1u,
|
||||
.top_y = NC_TOP_Y,
|
||||
.pitch = NC_PITCH,
|
||||
.band_h = NC_BAND_H,
|
||||
.top_y = UI_LIST_TOP,
|
||||
.pitch = UI_ROW_PITCH,
|
||||
.band_h = UI_ROW_BAND_H,
|
||||
.text_dy = 0,
|
||||
.left_x = NC_LEFT_X,
|
||||
.right_x = NC_RIGHT_X,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 页面描述符(框架描述符表 s_defs[] 引用) */
|
||||
/* 页面描述符(生成物 ui_page_registry.c 按菜单树 #page=net_cfg 注记绑定引用;.id = "net_cfg") */
|
||||
extern const content_page_def_t ui_page_net_cfg_desc;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "ui_pwd_def.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
/* 私有宏定义区 */
|
||||
|
||||
/*
|
||||
@@ -45,7 +47,6 @@
|
||||
#define PW_TITLE_Y 44 /* 标题放置 y(中上;0 = 框架默认,实机明显低于其余各页的 y=2) */
|
||||
|
||||
#define PW_ERR_Y 104 /* "密码输入错误"提示行顶 y(数字框行之下) */
|
||||
#define PW_SCREEN_W 160 /* 屏幕宽(px,与 ui_port.h / 模拟器一致;提示文本居中用) */
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
@@ -130,16 +131,16 @@ static void pw_draw_cb(lv_layer_t *layer, const lv_area_t *clip) {
|
||||
fd.bg_opa = LV_OPA_COVER;
|
||||
if (i == s_cur) {
|
||||
/* 光标框:反显(1bpp:white=暗底,black=亮字) */
|
||||
fd.bg_color = lv_color_white();
|
||||
fd.bg_color = UI_COL_SEL_BG;
|
||||
}
|
||||
else {
|
||||
/* 非光标框:亮底暗字 + 1px 暗边(1bpp:black=亮底,white=暗) */
|
||||
fd.bg_color = lv_color_black();
|
||||
fd.border_color = lv_color_white();
|
||||
fd.bg_color = UI_COL_BG;
|
||||
fd.border_color = UI_COL_FG;
|
||||
fd.border_width = 1;
|
||||
}
|
||||
lv_draw_rect(layer, &fd, &box);
|
||||
pw_draw_digit(layer, &box, s_digits[i], (i == s_cur) ? lv_color_black() : lv_color_white());
|
||||
pw_draw_digit(layer, &box, s_digits[i], (i == s_cur) ? UI_COL_SEL_FG : UI_COL_FG);
|
||||
}
|
||||
|
||||
if (s_err != 0u) {
|
||||
@@ -152,16 +153,16 @@ static void pw_draw_cb(lv_layer_t *layer, const lv_area_t *clip) {
|
||||
if (w > 0) {
|
||||
lv_area_t err_box;
|
||||
|
||||
err_box.x1 = (PW_SCREEN_W - w) / 2;
|
||||
err_box.x1 = (UI_SCREEN_W - w) / 2;
|
||||
err_box.x2 = (int32_t)(err_box.x1 + w - 1u);
|
||||
err_box.y1 = PW_ERR_Y;
|
||||
err_box.y2 = (int32_t)(err_box.y1 + 15u); /* 覆盖字体行高(13)+ 余量(同框架约定) */
|
||||
err_box.y2 = (int32_t)(err_box.y1 + UI_TEXT_BAND_H); /* 覆盖字体行高(13)+ 余量(同框架约定) */
|
||||
if (pw_areas_intersect(&err_box, clip)) {
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.text = "密码输入错误";
|
||||
ld.text_static = 1;
|
||||
ld.color = lv_color_white();
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = 0;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &err_box);
|
||||
@@ -256,12 +257,13 @@ void ui_page_password_arm(void (*on_ok)(uint8_t perm)) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用):
|
||||
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 `# @password=password` 指令出口引用):
|
||||
* 表单/操作页——selectable=0、无行数据,自绘内容 + 自有键;
|
||||
* 标题/装饰/页脚跟随实机(无"浅 V"、标题中上、无页脚);
|
||||
* key_intercept=1:← 键先由 key_cb 处理(移光标),ESC 恒不可拦截
|
||||
*/
|
||||
const content_page_def_t ui_page_password_desc = {
|
||||
.id = "password",
|
||||
.title = "用户密钥",
|
||||
.title_y = PW_TITLE_Y,
|
||||
.hide_deco = 1u,
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
#include "ui_content_page.h"
|
||||
|
||||
/*
|
||||
* 页面描述符(Flash const):用户密钥页 = CONTENT_PAGE_PASSWORD;
|
||||
* 接入框架描述符表 s_defs[](定义于 ui_page_password.c)
|
||||
* 页面描述符(Flash const):用户密钥页(描述符 .id = "password");
|
||||
* 由生成物 ui_page_registry.c 按菜单树 `# @password=password` 指令**单独出口**
|
||||
* (密码页非节点绑定:任何 #P 叶子都进同一页,故不在 node→desc 映射里)
|
||||
*/
|
||||
extern const content_page_def_t ui_page_password_desc;
|
||||
|
||||
@@ -30,7 +31,7 @@ extern const content_page_def_t ui_page_password_desc;
|
||||
* 由逻辑层实现"关页 + 按权限续进入";
|
||||
* NULL = 一致时仅关页)
|
||||
* 返回值:无
|
||||
* 限定条件:由逻辑层在 uiTask 上下文、ui_content_page_open(CONTENT_PAGE_PASSWORD)
|
||||
* 限定条件:由逻辑层在 uiTask 上下文、ui_content_page_open(ui_page_registry_password())
|
||||
* 之前调用
|
||||
* 函数说明:页面比对 ui_pwd_def.h 的两个编译期密码(先查修改:两值同设时授高权);
|
||||
* 每次进入重置为初始态(全 0、光标首框,与实机一致);页面模块不感知菜单
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 模块名称:UI 页面注册表(自动生成)
|
||||
* 模块功能:叶子节点 → 内容页描述符映射表 + 2 个查询 API(导航逻辑层消费)
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 自动生成:本文件由 tools/menu_gen/gen_menu.py 从 ui_menu_tree.txt 的 #page= 注记生成,请勿手改。
|
||||
* 新增一页:菜单树给叶子加 " #page=<模块名>" + 写 ui_page_<模块名>.c,重跑生成器
|
||||
*/
|
||||
#include "ui_page_registry.h"
|
||||
|
||||
#include "ui_page_ac.h"
|
||||
#include "ui_page_dc.h"
|
||||
#include "ui_page_device_info.h"
|
||||
#include "ui_page_net_cfg.h"
|
||||
#include "ui_page_password.h"
|
||||
#include "ui_page_runlog.h"
|
||||
#include "ui_page_selftest.h"
|
||||
|
||||
const ui_page_entry_t g_ui_page_map[UI_PAGE_MAP_COUNT] = {
|
||||
{ 1u, &ui_page_device_info_desc }, /* node 1 装置信息 */
|
||||
{ 3u, &ui_page_ac_desc }, /* node 3 交流量 */
|
||||
{ 4u, &ui_page_dc_desc }, /* node 4 直流量 */
|
||||
{ 30u, &ui_page_net_cfg_desc }, /* node 30 网口设置 */
|
||||
{ 39u, &ui_page_selftest_desc }, /* node 39 自检记录 */
|
||||
{ 40u, &ui_page_runlog_desc }, /* node 40 运行记录 */
|
||||
};
|
||||
|
||||
/*
|
||||
* 函数功能:查叶子节点绑定的内容页描述符
|
||||
* 入口参数:node - 菜单叶子节点下标 uint8_t
|
||||
* 返回值:页面描述符;未绑定返回 NULL const content_page_def_t *
|
||||
* 限定条件:无(越界 node 不会命中任何条目,直接 NULL)
|
||||
* 函数说明:线性扫描绑定表(条目 = 有内容页的叶子,个位数量级)
|
||||
*/
|
||||
const content_page_def_t *ui_page_registry_for(uint8_t node) {
|
||||
for (uint8_t i = 0u; i < UI_PAGE_MAP_COUNT; i++) {
|
||||
if (g_ui_page_map[i].node == node) {
|
||||
return g_ui_page_map[i].desc;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:取密码页描述符(需密码叶子统一进这一页,非节点绑定)
|
||||
* 入口参数:无
|
||||
* 返回值:密码页描述符 const content_page_def_t *
|
||||
* 限定条件:无
|
||||
* 函数说明:取值来自 ui_menu_tree.txt 的 @password 指令(生成期解析,运行期零开销)
|
||||
*/
|
||||
const content_page_def_t *ui_page_registry_password(void) {
|
||||
return &ui_page_password_desc;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 模块名称:UI 页面注册表头文件(自动生成)
|
||||
* 模块功能:叶子节点 → 内容页描述符的映射表与查询 API;密码页非节点绑定,单独出口
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 自动生成:本文件由 tools/menu_gen/gen_menu.py 从 ui_menu_tree.txt 的 #page= 注记生成,请勿手改。
|
||||
* 新增一页:菜单树给叶子加 " #page=<模块名>" + 写 ui_page_<模块名>.c,重跑生成器
|
||||
* (双端注册 uvprojx/CMakeLists 亦由生成器自动写入)
|
||||
*/
|
||||
#ifndef UI_PAGE_REGISTRY_H
|
||||
#define UI_PAGE_REGISTRY_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ui_content_page.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 注册表条目:一个绑定了内容页的菜单叶子(表序 = 节点下标升序) */
|
||||
typedef struct {
|
||||
uint8_t node; /* 菜单叶子节点下标(g_menu_tab 下标) */
|
||||
const content_page_def_t *desc; /* 页面描述符(Flash const) */
|
||||
} ui_page_entry_t;
|
||||
|
||||
#define UI_PAGE_MAP_COUNT 6u /* 绑定了内容页的叶子数 */
|
||||
|
||||
extern const ui_page_entry_t g_ui_page_map[UI_PAGE_MAP_COUNT];
|
||||
|
||||
/*
|
||||
* 函数功能:查叶子节点绑定的内容页描述符
|
||||
* 入口参数:node - 菜单叶子节点下标 uint8_t
|
||||
* 返回值:页面描述符;该叶子未绑定内容页返回 NULL const content_page_def_t *
|
||||
* 限定条件:node < UI_MENU_TAB_COUNT
|
||||
* 函数说明:取代旧 ui_menu.c 手写 case 分发——映射的唯一真源是 ui_menu_tree.txt,
|
||||
* 表序变化不会造成"下标硬编码错位"(线性扫描:绑定页是个位数量级)
|
||||
*/
|
||||
const content_page_def_t *ui_page_registry_for(uint8_t node);
|
||||
|
||||
/*
|
||||
* 函数功能:取密码页描述符(需密码叶子统一进这一页)
|
||||
* 入口参数:无
|
||||
* 返回值:密码页描述符;菜单树未声明 @password 指令时返回 NULL const content_page_def_t *
|
||||
* 限定条件:无
|
||||
* 函数说明:密码页**不是**节点绑定的(任何 #P 叶子都进同一页),故不进 node→desc 映射,
|
||||
* 由本接口单独出口;取值来自 ui_menu_tree.txt 的 `# @password=<模块名>` 指令
|
||||
*/
|
||||
const content_page_def_t *ui_page_registry_password(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* UI_PAGE_REGISTRY_H */
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "ui_page_runlog.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
#include "sys_log.h"
|
||||
|
||||
/* 静态变量定义区 */
|
||||
@@ -45,7 +47,7 @@ static uint16_t rl_count_cb(void) {
|
||||
/*
|
||||
* 函数功能:逐行取数(偶数行 = 时间戳、奇数行 = 描述;值列恒空)
|
||||
* 入口参数:row - 全局行下标 uint16_t
|
||||
* name - 名输出缓冲(框架栈缓冲 CP_ROW_BUF_LEN=32,含 NUL) char *
|
||||
* name - 名输出缓冲(框架栈缓冲 UI_BUF_STRLEN=32,含 NUL) char *
|
||||
* value - 值输出缓冲(同上;本页恒写空串) char *
|
||||
* 限定条件:页面打开中(框架绘制/按键上下文,uiTask)
|
||||
*/
|
||||
@@ -79,21 +81,22 @@ static void rl_refresh(uint16_t row, char *name, char *value) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用):
|
||||
* 动态列表页——每记录 2 行、8 行/页 = 4 条/页;band_h=13(同 AC/DC 约定:
|
||||
* selectable 页选中行黑字必须配白带,band_h=0 会致选中行不可见);
|
||||
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=runlog 绑定引用):
|
||||
* 动态列表页——每记录 2 行、UI_LIST_ROWS 行/页 = 4 条/页;band_h=UI_ROW_BAND_H(同 AC/DC
|
||||
* 约定:selectable 页选中行黑字必须配白带,band_h=0 会致选中行不可见);
|
||||
* 时间戳 19 ASCII ≈114px < 屏宽可容
|
||||
*/
|
||||
const content_page_def_t ui_page_runlog_desc = {
|
||||
.id = "runlog",
|
||||
.title = "运行记录",
|
||||
.selectable = 1u,
|
||||
.top_y = 24,
|
||||
.pitch = 14,
|
||||
.band_h = 13,
|
||||
.top_y = UI_LIST_TOP,
|
||||
.pitch = UI_ROW_PITCH,
|
||||
.band_h = UI_ROW_BAND_H,
|
||||
.text_dy = 0,
|
||||
.left_x = 7,
|
||||
.right_x = 101,
|
||||
.rows_per_page = 8u,
|
||||
.left_x = UI_LIST_LEFT_X,
|
||||
.right_x = UI_LIST_RIGHT_X,
|
||||
.rows_per_page = UI_LIST_ROWS,
|
||||
.count_cb = rl_count_cb,
|
||||
.refresh = rl_refresh,
|
||||
};
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include "ui_content_page.h"
|
||||
|
||||
/*
|
||||
* 页面描述符(Flash const):运行记录页 = CONTENT_PAGE_RUN_LOG;
|
||||
* 接入框架描述符表 s_defs[](定义于 ui_page_runlog.c)
|
||||
* 页面描述符(Flash const):运行记录页(描述符 .id = "runlog");
|
||||
* 由生成物 ui_page_registry.c 按菜单树 #page=runlog 注记绑定引用(定义于 ui_page_runlog.c)
|
||||
*/
|
||||
extern const content_page_def_t ui_page_runlog_desc;
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "ui_page_selftest.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
#include "sys_log.h"
|
||||
|
||||
/* 静态变量定义区 */
|
||||
@@ -45,7 +47,7 @@ static uint16_t st_count_cb(void) {
|
||||
/*
|
||||
* 函数功能:逐行取数(偶数行 = 时间戳、奇数行 = 故障描述;值列恒空)
|
||||
* 入口参数:row - 全局行下标 uint16_t
|
||||
* name - 名输出缓冲(框架栈缓冲 CP_ROW_BUF_LEN=32,含 NUL) char *
|
||||
* name - 名输出缓冲(框架栈缓冲 UI_BUF_STRLEN=32,含 NUL) char *
|
||||
* value - 值输出缓冲(同上;本页恒写空串) char *
|
||||
* 限定条件:页面打开中(框架绘制/按键上下文,uiTask)
|
||||
*/
|
||||
@@ -79,21 +81,22 @@ static void st_refresh(uint16_t row, char *name, char *value) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用):
|
||||
* 动态列表页——每记录 2 行、8 行/页 = 4 条/页;band_h=13(同 AC/DC 约定:
|
||||
* selectable 页选中行黑字必须配白带,band_h=0 会致选中行不可见);
|
||||
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=selftest 绑定引用):
|
||||
* 动态列表页——每记录 2 行、UI_LIST_ROWS 行/页 = 4 条/页;band_h=UI_ROW_BAND_H(同 AC/DC
|
||||
* 约定:selectable 页选中行黑字必须配白带,band_h=0 会致选中行不可见);
|
||||
* 时间戳 19 ASCII ≈114px < 屏宽可容
|
||||
*/
|
||||
const content_page_def_t ui_page_selftest_desc = {
|
||||
.id = "selftest",
|
||||
.title = "自检记录",
|
||||
.selectable = 1u,
|
||||
.top_y = 24,
|
||||
.pitch = 14,
|
||||
.band_h = 13,
|
||||
.top_y = UI_LIST_TOP,
|
||||
.pitch = UI_ROW_PITCH,
|
||||
.band_h = UI_ROW_BAND_H,
|
||||
.text_dy = 0,
|
||||
.left_x = 7,
|
||||
.right_x = 101,
|
||||
.rows_per_page = 8u,
|
||||
.left_x = UI_LIST_LEFT_X,
|
||||
.right_x = UI_LIST_RIGHT_X,
|
||||
.rows_per_page = UI_LIST_ROWS,
|
||||
.count_cb = st_count_cb,
|
||||
.refresh = st_refresh,
|
||||
};
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include "ui_content_page.h"
|
||||
|
||||
/*
|
||||
* 页面描述符(Flash const):自检记录页 = CONTENT_PAGE_SELFTEST_LOG;
|
||||
* 接入框架描述符表 s_defs[](定义于 ui_page_selftest.c)
|
||||
* 页面描述符(Flash const):自检记录页(描述符 .id = "selftest");
|
||||
* 由生成物 ui_page_registry.c 按菜单树 #page=selftest 注记绑定引用(定义于 ui_page_selftest.c)
|
||||
*/
|
||||
extern const content_page_def_t ui_page_selftest_desc;
|
||||
|
||||
|
||||
+20
-33
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "ui_menu_view.h"
|
||||
#include "ui_style.h"
|
||||
#include "ui_tokens.h"
|
||||
|
||||
#define DBG_TAG "[UI_STANDBY]"
|
||||
#include "dbg_log.h"
|
||||
@@ -25,11 +27,9 @@
|
||||
|
||||
/*
|
||||
* 布局常量(设备像素;参考 mockups/待机显示页面.jpg):
|
||||
* 6 路电流值行自 y=28 起、行距 18(末行 y=118..130);分隔线 y=132;底部时钟 y=140 居中
|
||||
* 屏幕宽高取令牌 UI_SCREEN_W/H;6 路电流值行自 y=28 起、行距 18
|
||||
* (末行 y=118..130);分隔线 y=132;底部时钟 y=140 居中
|
||||
*/
|
||||
#define SB_SCREEN_W 160 /* 屏幕宽(px,与 ui_port.h / 模拟器一致) */
|
||||
#define SB_SCREEN_H 160 /* 屏幕高(px) */
|
||||
|
||||
#define SB_CH_COUNT 6u /* 电流通道数 */
|
||||
#define SB_ROW_Y0 28 /* 首数据行顶 y */
|
||||
#define SB_PITCH 18 /* 行距(相邻行槽顶间距) */
|
||||
@@ -55,21 +55,8 @@ static lv_obj_t *s_prev_scr; /* 进入待机前的活动屏(ENTER 还原
|
||||
static lv_obj_t *s_prev_focus; /* 进入待机前组内焦点对象 */
|
||||
static ui_standby_time_cb s_time_cb;
|
||||
|
||||
/* flat:透明、无边框/圆角/内边距——剥离 mono 主题给普通 lv_obj 附加的 card 样式(同 ui_content_page.c) */
|
||||
static const lv_style_const_prop_t sb_flat_props[] = {
|
||||
{ .prop = LV_STYLE_BG_OPA, .value = { .num = LV_OPA_TRANSP } },
|
||||
{ .prop = LV_STYLE_BORDER_WIDTH, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_RADIUS, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_LEFT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_RIGHT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_TOP, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_BOTTOM, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_ROW, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_COLUMN, .value = { .num = 0 } },
|
||||
LV_STYLE_CONST_PROPS_END
|
||||
};
|
||||
|
||||
LV_STYLE_CONST_INIT(sb_style_flat, sb_flat_props);
|
||||
/* flat 样式(透明、无边框/圆角/内边距,剥离 mono 主题 card)取自共享样式表
|
||||
* ui_style.c:与 ui_content_page / ui_menu_view 共用同一 const 对象,不再本地重复定义 */
|
||||
|
||||
/* 函数定义区 */
|
||||
|
||||
@@ -99,15 +86,15 @@ static void sb_draw_centered_text(lv_layer_t *layer, const char *text, int32_t y
|
||||
lv_draw_label_dsc_t ld;
|
||||
|
||||
area.x1 = 0;
|
||||
area.x2 = SB_SCREEN_W - 1;
|
||||
area.x2 = UI_SCREEN_W - 1;
|
||||
area.y1 = y;
|
||||
area.y2 = y + 15; /* 覆盖字体行高(13)+ 余量 */
|
||||
area.y2 = y + UI_TEXT_BAND_H; /* 覆盖字体行高(13)+ 余量 */
|
||||
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.text = text;
|
||||
ld.color = lv_color_white();
|
||||
ld.ofs_x = (SB_SCREEN_W - lv_text_get_width(text, (uint32_t)strlen(text), LV_FONT_DEFAULT, 0)) / 2;
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = (UI_SCREEN_W - lv_text_get_width(text, (uint32_t)strlen(text), LV_FONT_DEFAULT, 0)) / 2;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &area);
|
||||
}
|
||||
@@ -128,7 +115,7 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
lv_draw_label_dsc_t ld;
|
||||
|
||||
strip.x1 = 0;
|
||||
strip.x2 = SB_SCREEN_W - 1;
|
||||
strip.x2 = UI_SCREEN_W - 1;
|
||||
strip.y1 = row_y;
|
||||
strip.y2 = (int32_t)(row_y + SB_PITCH - 1u);
|
||||
if (!sb_areas_intersect(&strip, &layer->_clip_area)) {
|
||||
@@ -139,7 +126,7 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
ld.base.layer = layer;
|
||||
ld.text = s_ch_name[i];
|
||||
ld.text_static = 1;
|
||||
ld.color = lv_color_white();
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = SB_LABEL_X;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &strip);
|
||||
@@ -148,7 +135,7 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
ld.base.layer = layer;
|
||||
ld.text = "=";
|
||||
ld.text_static = 1;
|
||||
ld.color = lv_color_white();
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = SB_EQ_X;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &strip);
|
||||
@@ -157,7 +144,7 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
ld.base.layer = layer;
|
||||
ld.text = s_ch_value[i];
|
||||
ld.text_static = 1;
|
||||
ld.color = lv_color_white();
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = SB_VALUE_X;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &strip);
|
||||
@@ -169,13 +156,13 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
lv_draw_rect_dsc_t fd;
|
||||
|
||||
line.x1 = 8;
|
||||
line.x2 = SB_SCREEN_W - 9;
|
||||
line.x2 = UI_SCREEN_W - 9;
|
||||
line.y1 = SB_LINE_Y;
|
||||
line.y2 = SB_LINE_Y;
|
||||
if (sb_areas_intersect(&line, &layer->_clip_area)) {
|
||||
lv_draw_rect_dsc_init(&fd);
|
||||
fd.base.layer = layer;
|
||||
fd.bg_color = lv_color_white();
|
||||
fd.bg_color = UI_COL_FG;
|
||||
fd.bg_opa = LV_OPA_COVER;
|
||||
lv_draw_rect(layer, &fd, &line);
|
||||
}
|
||||
@@ -186,9 +173,9 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
lv_area_t t;
|
||||
|
||||
t.x1 = 0;
|
||||
t.x2 = SB_SCREEN_W - 1;
|
||||
t.x2 = UI_SCREEN_W - 1;
|
||||
t.y1 = SB_TIME_Y;
|
||||
t.y2 = (int32_t)(SB_TIME_Y + 15);
|
||||
t.y2 = (int32_t)(SB_TIME_Y + UI_TEXT_BAND_H);
|
||||
if (sb_areas_intersect(&t, &layer->_clip_area)) {
|
||||
char ts[24];
|
||||
|
||||
@@ -307,8 +294,8 @@ int ui_standby_init(uint32_t idle_timeout_ms, ui_standby_time_cb time_cb) {
|
||||
return -1;
|
||||
}
|
||||
lv_obj_add_flag(s_cont, LV_OBJ_FLAG_IGNORE_LAYOUT);
|
||||
lv_obj_set_size(s_cont, SB_SCREEN_W, SB_SCREEN_H);
|
||||
lv_obj_add_style(s_cont, &sb_style_flat, 0);
|
||||
lv_obj_set_size(s_cont, UI_SCREEN_W, UI_SCREEN_H);
|
||||
lv_obj_add_style(s_cont, &ui_style_flat, 0);
|
||||
lv_obj_add_flag(s_cont, LV_OBJ_FLAG_CLICK_FOCUSABLE);
|
||||
lv_obj_add_event_cb(s_cont, sb_key_cb, LV_EVENT_KEY, NULL);
|
||||
lv_obj_add_event_cb(s_cont, sb_draw_cb, LV_EVENT_DRAW_MAIN, NULL);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 模块名称:UI Shared Style (UI 共享样式表)
|
||||
* 模块功能:flat 常量样式的属性数组与样式对象定义(声明见 ui_style.h);
|
||||
* 消费者 = ui_menu_view.c / ui_content_page.c / ui_standby.c
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(U3:原三处逐字相同的定义合并到此单一来源)
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
#include "ui_style.h"
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/*
|
||||
* flat 属性数组:透明、无边框/圆角/内边距——剥离 mono 主题给普通 lv_obj 附加的
|
||||
* card 样式。用常量样式(LV_STYLE_CONST_INIT):属性存静态数组、零 LV_MEM 池分配;
|
||||
* 动态样式首次 set_prop 会从池分配 values_and_props 且永不释放(x64 实测 88 B/个)。
|
||||
* 属性项与顺序 = 合并前三处定义的公共内容(三者原本逐字相同)。
|
||||
*/
|
||||
static const lv_style_const_prop_t s_flat_props[] = {
|
||||
{ .prop = LV_STYLE_BG_OPA, .value = { .num = LV_OPA_TRANSP } },
|
||||
{ .prop = LV_STYLE_BORDER_WIDTH, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_RADIUS, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_LEFT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_RIGHT, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_TOP, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_BOTTOM, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_ROW, .value = { .num = 0 } },
|
||||
{ .prop = LV_STYLE_PAD_COLUMN, .value = { .num = 0 } },
|
||||
LV_STYLE_CONST_PROPS_END
|
||||
};
|
||||
|
||||
LV_STYLE_CONST_INIT(ui_style_flat, s_flat_props);
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 模块名称:UI Shared Style (UI 共享样式表)
|
||||
* 模块功能:跨模块复用的 LVGL 常量样式单一定义处——目前只有 flat(背景透明 +
|
||||
* 边框/圆角/内边距全 0,用于剥离 mono 主题给普通 lv_obj 附加的 card 样式);
|
||||
* 菜单显示层 ui_menu_view、内容页框架 ui_content_page、待机页 ui_standby
|
||||
* 三处共用同一 const 对象(此前各自定义一份逐字相同的 9 项属性数组,
|
||||
* = .rodata 里三份重复副本);
|
||||
* 常量样式属性存静态数组、零 LV_MEM 池分配——动态样式首次 set_prop 会从
|
||||
* 池分配 values_and_props 且永不释放(x64 实测 88 B/个),故全仓禁用
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(U3:合并三处逐字重复的 flat 常量样式定义)
|
||||
*/
|
||||
#ifndef UI_STYLE_H
|
||||
#define UI_STYLE_H
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* flat 样式:背景透明 + 边框/圆角/内边距全 0。
|
||||
* 用途:mono 主题会给普通 lv_obj 附加 card 样式(可见边框与内边距),自绘容器
|
||||
* 必须先剥掉它,才能拿到干净的整屏绘制面。
|
||||
* 定义在 ui_style.c;样式在 LVGL 内按指针被对象引用,三处共享同一 const 对象
|
||||
* 不改变任何绘制结果(属性值与原三份逐字节相同)。
|
||||
*/
|
||||
extern const lv_style_t ui_style_flat;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* UI_STYLE_H */
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 模块名称:UI Design Tokens (UI 设计令牌层 L0)
|
||||
* 模块功能:UI 视觉骨架的唯一取值来源——屏幕几何、内容页骨架(标题/页脚/文本
|
||||
* 条带/选中带内缩)、列表页原型(首行顶/行距/带高/两列 x/每页行数)、
|
||||
* 滚动条宽、文本缓冲长度、1bpp 语义色、UI 任务节拍与待机阈值;
|
||||
* 各页面/框架文件只引用本头令牌,不再自带同值字面量
|
||||
* (治理背景见 docs/问题记录/项目审查与优化建议报告.md §6.3 L0);
|
||||
* 纯取值定义,不含任何逻辑;改一处即全局生效,改前必须跑 ui-verify 像素回归
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(U1:收编散落各 UI 文件的布局/颜色/缓冲魔数,
|
||||
* 纯替换、零行为变化;像素与替换前逐位一致)
|
||||
*/
|
||||
#ifndef UI_TOKENS_H
|
||||
#define UI_TOKENS_H
|
||||
|
||||
/*
|
||||
* 依赖说明:
|
||||
* 1. 屏幕几何(UI_SCREEN_W / UI_SCREEN_H / UI_FB_SIZE)的**权威定义在 ui_port.h**
|
||||
* (显示端口,MCU 与模拟器两端共用),本头复用而不重复定义——避免同名宏二次定义。
|
||||
* 2. 语义色宏展开为 LVGL 取色函数调用,故引用方须已包含 "lvgl.h"
|
||||
* (UI 各 .c 均经 ui_content_page.h / ui_menu_view.h 等间接包含)。
|
||||
*/
|
||||
#include "ui_port.h"
|
||||
|
||||
/* ── 内容页骨架(框架 ui_content_page.c 与各自绘页共用)────────────────────── */
|
||||
|
||||
#define UI_TITLE_Y 2 /* 标题放置 y(居中;CJK 字形顶距 +1 → 墨迹自 y=3 起) */
|
||||
#define UI_PAGE_LINE_Y 143 /* 页脚"第 X 页,共 Y 页"放置 y */
|
||||
#define UI_TEXT_BAND_H 15 /* 单行文本条带下延量(字体行高 13 + 余量):y2 = y + 本值 */
|
||||
#define UI_BUF_STRLEN 32 /* 动态行 / 页脚文本缓冲长度(含 NUL;超出行带即被裁切) */
|
||||
#define UI_SEL_BAND_INSET_L 2 /* 选中白带左内缩(x1 = 本值) */
|
||||
#define UI_SEL_BAND_INSET_R 3 /* 选中白带右内缩(x2 = 屏宽 - 本值,即止于 x=156) */
|
||||
#define UI_FOOTER_X 6 /* 页脚文本左起 x(左对齐留白,与实机一致) */
|
||||
|
||||
/* ── 列表页原型(交流量/直流量/运行记录/自检记录/网口设置共用一套骨架)──────── */
|
||||
|
||||
#define UI_LIST_TOP 24 /* 首行槽顶 y */
|
||||
#define UI_ROW_PITCH 14 /* 行距(相邻行槽顶间距) */
|
||||
#define UI_ROW_BAND_H 13 /* 选中白带高(= 字体行高;selectable 页须 >0 否则选中行不可见) */
|
||||
#define UI_LIST_LEFT_X 7 /* 左列 x(名) */
|
||||
#define UI_LIST_RIGHT_X 101 /* 右列 x(值) */
|
||||
#define UI_LIST_ROWS 8u /* 每页可见行数 */
|
||||
|
||||
/* ── 菜单显示层 ───────────────────────────────────────────────────────────── */
|
||||
|
||||
#define UI_SCROLLBAR_W 4 /* 自动滚动条宽(px;行超框高时出现) */
|
||||
|
||||
/* ── 1bpp 语义色(LV_COLOR_FORMAT_I1)─────────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* 本屏只有黑白两值,但保留语义名:换色深时只改本段即可平移,不必回改各绘制点。
|
||||
* 极性(实测,勿凭直觉改):文本/分隔线/边框一律取 white(1bpp 下即"暗墨"),
|
||||
* black 是底;选中行反显 = 白底黑字,故 SEL 前景取 black、SEL 背景取 white。
|
||||
*/
|
||||
#define UI_COL_FG lv_color_white() /* 墨:文本 / 分隔线 / 边框 / 滚动条 */
|
||||
#define UI_COL_BG lv_color_black() /* 底:黑 */
|
||||
#define UI_COL_SEL_FG lv_color_black() /* 选中行字色(反显后为黑字) */
|
||||
#define UI_COL_SEL_BG lv_color_white() /* 选中行带底色(反显后为白带) */
|
||||
|
||||
/* ── UI 任务与待机策略 ────────────────────────────────────────────────────── */
|
||||
|
||||
#define UI_TASK_TICK_MS 10 /* uiTask 主循环周期:LVGL 推进步长 = osDelay 时长 */
|
||||
#define UI_STANDBY_IDLE_MS 60000u /* 无操作进入待机屏的阈值(ms) */
|
||||
|
||||
#endif /* UI_TOKENS_H */
|
||||
Reference in New Issue
Block a user