#include "Drv/pages/YC/view.h" #include static const PageRenderPort *s_port = NULL; /* ------------------------------------------------------------------------- * 宏定义:遥测页面布局常量 * ------------------------------------------------------------------------- */ #define YC_TOP_BAND_MAX_Y (24u) /* 顶栏最大 Y 坐标 */ #define YC_CONTENT_BASE_Y (24u) /* 内容区起始 Y 坐标 */ #define YC_NAME_X (6u) /* 名称显示 X 坐标 */ #define YC_VALUE_X (100u) /* 数值显示 X 坐标 */ #define YC_ROW_PAD_X0 (2u) /* 行内容左侧留白 */ #define YC_ROW_PAD_X1 (2u) /* 行内容右侧留白 */ #define YC_FOOTER_Y_FROM_BOTTOM (16u) /* 底栏距底部距离(与 PageRenderer_LcdShowInfoPage 一致)*/ #define YC_FOOTER_GAP_PX (2u) /* 内容区与底栏之间的留白间隙 */ /* ------------------------------------------------------------------------- * 函数名:YCView_ShowInfoPage * 功能: * 显示当前页码和总页数信息到底栏。 * * 参数: * wPageNum - 当前页码(从 1 开始) * wPageMax - 总页数 * * 边界处理: * - 本函数不做参数合法性校验,直接传递给渲染端口显示。 * * 说明: * - 该函数通过 s_port->show_info_page() 调用底层渲染接口显示页码信息。 * - 通常在内容区绘制完成后调用,用于告知用户当前浏览位置。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_ShowInfoPage(uint16_t wPageNum, uint16_t wPageMax) { s_port->show_info_page(wPageNum, wPageMax); } /* ------------------------------------------------------------------------- * 函数名:YCView_ShowTopName * 功能: * 在页面顶栏居中显示顶部名称,并绘制装饰性箭头。 * * 参数: * name - 要显示的顶部名称字符串(UTF-8 编码) * * 边界处理: * - 本函数不对 name 做空指针校验,调用方需保证 name 有效。 * - 若名称过长导致居中计算偏移为负数,显示位置可能异常,由底层渲染处理。 * * 说明: * - 首先填充整个屏幕为背景色,实现清屏效果。 * - 通过 s_port->get_utf8_len() 和 s_port->get_ascii_width() 计算字符串显示宽度。 * - 使用居中公式 (screen_width - text_width) / 2 确定 X 坐标。 * - 在 Y=3 位置绘制名称,并在固定位置 (18, 2) 绘制箭头装饰(draw_meitou)。 * - 顶栏高度为 0-23 像素,名称显示于第 3 行以确保视觉美观。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_ShowTopName(const uint8_t *name) { uint16_t wLen = s_port->get_utf8_len(name) * s_port->get_ascii_width(); s_port->fill_rect(0, 0, s_port->get_size_x() - 1, s_port->get_size_y() - 1, s_port->get_color_back()); /* 显示顶部名称,居中显示 */ s_port->show_str((s_port->get_size_x() - wLen) / 2, 3, name); s_port->draw_meitou(18, 2); } /* ------------------------------------------------------------------------- * 函数名:YCView_RowY * 功能: * 根据行号计算该行在屏幕上的实际 Y 坐标。 * * 参数: * row - 行号(从 0 开始) * * 边界处理: * - 本函数不做行号范围校验,调用方应保证 row 有效。 * * 说明: * - 计算公式:row_y = YC_CONTENT_BASE_Y + row × (字符高度 + 行间距) * - YC_CONTENT_BASE_Y 为内容区起始 Y 坐标(24 像素)。 * - 每行高度由 s_port->get_ascii_height() 和 s_port->get_row_space() 决定。 * - 该函数确保所有遥测数据显示在内容区内,从第 24 像素开始向下排列。 * * 返回值: * - 该行在屏幕上的 Y 坐标 * ------------------------------------------------------------------------- */ static uint16_t YCView_RowY(uint8_t row) { uint16_t lineH = s_port->get_ascii_height() + s_port->get_row_space(); return YC_CONTENT_BASE_Y + (uint16_t)row * lineH; } /* ------------------------------------------------------------------------- * 函数名:YCView_DrawRowNameAndValue * 功能: * 在指定行绘制遥测项的名称和数值。 * * 参数: * name - 名称字符串指针 * value - 数值字符串指针 * row - 目标行号(从 0 开始) * drawName - 是否显示名称的标志,非 0 表示显示,0 表示不显示 * * 边界处理: * - 本函数不对 name/value 做空指针校验,调用方需保证字符串有效。 * - 若 drawName 为 0,则跳过名称绘制,仅显示数值。 * * 说明: * - 根据 drawName 标志决定是否在左侧区域(YC_NAME_X)显示名称。 * - 数值始终在右侧固定位置(YC_VALUE_X)显示。 * - Y 坐标通过 YCView_RowY(row) 计算,确保内容对齐到指定行。 * - 该函数是遥测数据显示的核心绘制单元,被 YCView_PaintContent() 调用。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_DrawRowNameAndValue(const uint8_t *name, const uint8_t *value, uint8_t row, uint8_t drawName) { if (drawName != 0u) { s_port->show_str(YC_NAME_X, YCView_RowY(row), name); } s_port->show_str(YC_VALUE_X, YCView_RowY(row), value); } /* ------------------------------------------------------------------------- * 函数名:YCView_ClearContentArea * 功能: * 清空内容区域(不含顶栏和底栏),填充背景色并准备重绘。 * * 参数: * 无 * * 边界处理: * - 若内容区上边界 y0 大于等于下边界 y1,或屏幕宽度为 0,不执行填充操作。 * - 自动将下边界限制在屏幕有效范围内(sy-1)。 * * 说明: * - 内容区范围:从 YC_CONTENT_BASE_Y-2 到 YC_FOOTER_Y_FROM_BOTTOM * - 上边界预留 2 像素用于视觉缓冲,避免与顶栏过于接近。 * - 下边界通过 YC_FOOTER_Y_FROM_BOTTOM 计算,确保不覆盖底栏页码信息。 * - 清空后内容区处于待绘制状态,由调用方继续执行数据项绘制。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_ClearContentArea(void) { uint16_t sx = s_port->get_size_x(); uint16_t sy = s_port->get_size_y(); uint16_t y0 = YC_CONTENT_BASE_Y - 2u; uint16_t y1 = sy - YC_FOOTER_Y_FROM_BOTTOM; if ((y1 > y0) && (sx > 0u)) { s_port->fill_rect(0, y0, sx - 1u, y1, s_port->get_color_back()); } } /* ------------------------------------------------------------------------- * 函数名:YCView_ApplyCursorInvert * 功能: * 对当前光标所在行进行反显高亮处理。 * * 参数: * input - 视图输入结构体,包含页码、光标位置等信息 * * 边界处理: * - 若数据为空(input->empty != 0)或没有可显示的行数,直接返回。 * - 若计算的反显区域越界,自动调整至有效范围内。 * - 若反显上边界大于等于下边界,不执行反显操作。 * * 说明: * - 光标行 Y 坐标通过 YCView_RowY(input->cursor_row) 计算。 * - 反显区域高度为单行高度(lineH-1),确保覆盖整个字符区域。 * - X 方向留白:左侧 YC_ROW_PAD_X0,右侧 sx-1-YC_ROW_PAD_X1,避免触及屏幕边缘。 * - 下边界受 YC_FOOTER_Y_FROM_BOTTOM 限制,防止覆盖底栏页码。 * - 该函数在绘制页码前调用,避免因页码绘制导致光标高亮消失。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_ApplyCursorInvert(const yc_view_input_t *input) { uint16_t sx = s_port->get_size_x(); uint16_t rows = input->rows_on_page; uint16_t lineH = s_port->get_ascii_height() + s_port->get_row_space(); uint16_t y0; uint16_t y1; uint16_t yMax; if ((input->empty != 0u) || (rows == 0u)) { return; } y0 = YCView_RowY((uint8_t)input->cursor_row); y1 = y0 + lineH - 1u; yMax = s_port->get_size_y() - YC_FOOTER_Y_FROM_BOTTOM; if (y1 > yMax) { y1 = yMax; } if (y1 < y0) { return; } s_port->invert(YC_ROW_PAD_X0, y0, sx - 1u - YC_ROW_PAD_X1, y1); } /* ------------------------------------------------------------------------- * 函数名:YCView_PaintContent * 功能: * 绘制内容区,包括清空背景、显示遥测数据项、光标高亮和页码信息。 * * 参数: * input - 视图输入结构体,包含页面索引、行数据等信息 * * 边界处理: * - 若数据为空(input->empty != 0),显示"无遥测数据"提示并返回。 * - 跳过无效行(input->row_valid[row] == 0)。 * - 循环次数受 input->rows_on_page 和 YC_VIEW_ROWS_MAX 双重限制。 * * 说明: * - 绘制流程: * 1) 调用 YCView_ClearContentArea() 清空内容区背景 * 2) 若数据为空,显示提示信息并返回 * 3) 遍历有效行,逐行调用 YCView_DrawRowNameAndValue() 绘制名称和数值 * 4) 先反显光标行(YCView_ApplyCursorInvert),再绘制页码 * - 顺序重要:避免页码绘制覆盖光标高亮效果 * 5) 最后显示页码信息(YCView_ShowInfoPage) * - 该函数是遥测页面内容绘制的核心入口,被 YCView_DrawFull() 和 YCView_DrawValues() 调用。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_PaintContent(const yc_view_input_t *input) { uint8_t row; uint16_t rows; YCView_ClearContentArea(); if (input->empty != 0u) { s_port->show_str(6u, YC_CONTENT_BASE_Y + 20u, (const uint8_t *)"无遥测数据"); return; } rows = input->rows_on_page; for (row = 0; row < (uint8_t)rows && row < YC_VIEW_ROWS_MAX; row++) { if (input->row_valid[row] == 0u) { continue; } YCView_DrawRowNameAndValue(input->row_name[row], input->row_value[row], row, 1u); } /* 先反显光标,再绘页码,避免 invert 覆盖底栏「第 x 页」造成花屏 */ YCView_ApplyCursorInvert(input); YCView_ShowInfoPage(input->page, input->page_max); } /* ------------------------------------------------------------------------- * 函数名:YCView_DrawFull * 功能: * 完整绘制遥测页面,包括顶栏和内容区。 * * 参数: * view - 视图对象指针(当前未使用) * input - 视图输入结构体,包含顶部名称和行数据等信息 * * 边界处理: * - 若 input 或 s_port 为 NULL,直接返回。 * - 通过 (void)view 消除未使用参数警告。 * * 说明: * - 完整绘制流程:先显示顶栏名称(YCView_ShowTopName),再绘制内容区(YCView_PaintContent)。 * - 整屏擦除操作在 MenuPage_OnExit() 统一完成,此处无需重复清屏。 * - 该函数用于首次加载页面或需要完整重绘的场景。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_DrawFull(view_t *view, const yc_view_input_t *input) { (void)view; if ((input == NULL) || (s_port == NULL)) { return; } /* 整屏擦除在 MenuPage_OnExit 统一完成,此处直接绘制顶栏与内容 */ YCView_ShowTopName(input->top_name); YCView_PaintContent(input); } /* ------------------------------------------------------------------------- * 函数名:YCView_DrawValues * 功能: * 仅重绘内容区数值(不重绘顶栏),用于主循环中的数值刷新。 * * 参数: * view - 视图对象指针(当前未使用) * input - 视图输入结构体,包含页面和行数据信息 * * 边界处理: * - 若 input 或 s_port 为 NULL,直接返回。 * - 通过 (void)view 消除未使用参数警告。 * * 说明: * - 该函数是主循环节拍的核心调用点,用于周期性刷新遥测数值显示。 * - 仅重绘内容区(YCView_PaintContent),顶栏保持不变以减少绘制开销。 * - 在桩模式下,此函数会触发 YCView_FormatStubValue() 生成模拟数据并更新显示。 * - 相比 YCView_DrawFull(),此函数更高效,适合高频刷新场景。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ static void YCView_DrawValues(view_t *view, const yc_view_input_t *input) { (void)view; if ((input == NULL) || (s_port == NULL)) { return; } /* 主循环节拍:重绘内容区以更新桩数值(不重画面顶栏) */ YCView_PaintContent(input); } /* ------------------------------------------------------------------------- * 函数名:View_Init * 功能: * 初始化遥测页面视图,绑定渲染端口和回调函数。 * * 参数: * view - 视图对象指针,待初始化的视图结构体 * * 边界处理: * - 本函数不对 view 做空指针校验,调用方需保证 view 有效。 * * 说明: * - 初始化流程: * 1) 获取 LCD 渲染端口(s_port = PageRenderer_Lcd()) * 2) 设置 show_top_name 为 NULL,表示不使用自定义顶栏显示函数 * 3) 绑定 show_info_page 回调到 YCView_ShowInfoPage * 4) 绑定 draw_full 回调到 YCView_DrawFull(完整绘制入口) * 5) 绑定 draw_values 回调到 YCView_DrawValues(数值刷新入口) * - 初始化后,视图对象即可被页面管理器使用。 * * 返回值: * - 无 * ------------------------------------------------------------------------- */ void View_Init(view_t *view) { s_port = PageRenderer_Lcd(); view->show_top_name = NULL; view->show_info_page = YCView_ShowInfoPage; view->draw_full = YCView_DrawFull; view->draw_values = YCView_DrawValues; }