/** * 日期: 2025-04-02 * 作者: coffee * 描述: ui页面管理, 独立模块, 用于管理页面切换和跳转 */ #ifndef __H_UI_PAGE_MANAGE_H__ #define __H_UI_PAGE_MANAGE_H__ #include // 外部需要定义该宏, 用于说明页面数量, 如果不定义, 默认搜索 kUIPageMax 枚举 #ifndef HUI_PAGE_MAX_NUM #define HUI_PAGE_MAX_NUM kUIPageMax #endif #ifndef __COUNT_ARGS_IMPL #define __COUNT_ARGS_IMPL(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N #endif #ifndef __COUNT_ARGS #define __COUNT_ARGS(...) __COUNT_ARGS_IMPL(dummy, ##__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #endif #define USE_UI_PAGE /** ================================================================= */ // 注册页面函数辅助宏 #define UIPAGE_FUNC(func) uint8_t func##PageCall(uint16_t cmd, void *data, uint16_t len) #define UIPAGE_DATA data #define UIPAGE_LEN len #define UIPAGE_CMD cmd #if 1 #define UIPAGE_CASE(page, func, ...) case page: { LogD("current page[%d], event[%s]", page, #page); func(__VA_ARGS__); LogD("event End"); } break #define UIPAGE_CASE_RET(page, func, ...) case page: { LogD("current page[%d], event[%s]", page, #page); return func(__VA_ARGS__); LogD("event End"); } break #else #define UIPAGE_CASE(page, func, ...) case page: { func(__VA_ARGS__); } break #define UIPAGE_CASE_RET(page, func, ...) case page: { return func(__VA_ARGS__); } break #endif #define UIPAGE_INIT_FUNC(func, ...) UIPAGE_CASE(kCallInitPage, func, __VA_ARGS__) #define UIPAGE_FREE_FUNC(func, ...) UIPAGE_CASE(kCallFreePage, func, __VA_ARGS__) #define UIPAGE_SHOW_FUNC(func, ...) UIPAGE_CASE(kCallShowPage, func, __VA_ARGS__) #define UIPAGE_HIDE_FUNC(func, ...) UIPAGE_CASE(kCallHidePage, func, __VA_ARGS__) #define UIPAGE_SWITCH_FUNC(func, ...) UIPAGE_CASE_RET(kCallSwitchPage, func, __VA_ARGS__) #define UIPAGE_INDEX_FUNC(func, ...) UIPAGE_CASE(kCallIndexSave, func, __VA_ARGS__) #define UIPAGE_EVENT_FUNC(func, ...) UIPAGE_CASE(kCallNotifyEvent, func, __VA_ARGS__) #define UIPAGE_INIT_SHOW_FUNC(init, show) UIPAGE_INIT_FUNC(init); UIPAGE_SHOW_FUNC(show) #define UIPAGE_DEINIT_HIDE_FUNC(deinit, hide) UIPAGE_FREE_FUNC(deinit); UIPAGE_HIDE_FUNC(hide) #define UIPAGE_INIT_DEINIT_FUNC(init, deinit) UIPAGE_INIT_FUNC(init); UIPAGE_FREE_FUNC(deinit) #define UIPAGE_SHOW_HIDE_FUNC(show, hide) UIPAGE_SHOW_FUNC(show); UIPAGE_HIDE_FUNC(hide) // 对应事件的data转换具体类型宏 #define UI_CALL_INDEX_TYPE(value) ((HUiPageIndex_t *)value) #define UI_CALL_EVENT_TYPE(value) ((HUiPageEvent_t *)value) // HD集成任务处理 #define HD_UIPAGE_EVENT_FUNC(func) UIPAGE_CASE(kCallNotifyEvent, func, *(UI_CALL_EVENT_TYPE(UIPAGE_DATA))) /** ================================================================= */ // hide -> free -> update Page -> init -> show // 或者非返回时如下, 此时的hide和index事件都可存储该页面索引: // hide -> index -> free -> update Page -> init -> show enum eCallCmd { kCallInitPage, ///< 构建页面, 对应页面自己内部创建数据(非释放页面不调用, 但如未构建则会触发) kCallFreePage, ///< 释放页面, 对应页面自己内部释放数据(非释放页面不调用) kCallShowPage, ///< 显示页面 kCallHidePage, ///< 退出页面, 此事件如果是返回触发的可以存储索引数据 kCallSwitchPage, ///< 切换页面, 对应页面返回false时则不切换, 所有切换都会触发这个事件 kCallIndexSave, ///< 存储当前索引事件, 通知此事件说明当前不是返回事件(cmd, HUiPageIndex_t *nextPage, 1); kCallNotifyEvent, ///< 通知事件, 传递一个参数 }; typedef uint8_t HUiPageIndex_t; typedef uint32_t HUiPageEvent_t; ///< cmd命令, data数据, len数据长度 typedef uint8_t (*pageCallType_t)(uint16_t cmd, void *data, uint16_t len); typedef void (*initPageType_t)(pageCallType_t *call); // 初始化页面管理, 当为NULL时内部初始化, 否则回调初始化, 请需要先设置好主页, 初始化会默认显示主页 void HUIPageInit(initPageType_t initPage); // 设置主页, 切换到主页清空返回栈, 如果返回栈为空时, 当前页面不是主页, 则切换到主页 void HUIPageSetHome(HUiPageIndex_t page); // 切换页面, 当前页面入栈, 相同页面不操作, 需要检查栈, 栈存在就回栈清空之后的, 不存在就压栈 // 因为有些页面可能不用HUIPageBack来返回, 所以需要兼容这种情况 void HUIPageSwitch(HUiPageIndex_t page); // 通知当前页面事件任务 void HUIPageNotifyEvent(HUiPageEvent_t event); // 切换到主页 void HUIPageSwitchHome(); // 返回上一个页面 void HUIPageBack(); // 清空返回栈, 不允许切换时, 将显示该页面, 而无法继续回栈, 成功返回1, 否则返回0 uint8_t HUIPageClear(); /** * @brief * 添加关联页面到返回栈, 用于一些按键跳转到指定页面, 或者一些不想逐个进入, 而是直接跳转, 然后返回时逐个返回的功能使用 * 如正常是 主页->菜单->该页面, 但快捷跳转是主页->该页面, 需要将菜单页面添加到返回栈 * 则需要调用 清空返回栈, 然后添加 主页->菜单, 最后再切换到该页面(内部会判断首个页面是否存在返回栈, 自动清空首个页面前的返回栈) * HUIPageClear(); HUIPageAddStack(主页, 菜单, 该页面); // 栈2个, 显示该页面 * 注: 调用这个时, 当前页面将不会入栈, 最大参数长度9个 * 如: 当前页面是菜单, 设置 主页->菜单, 则菜单页面不会入栈 * 因此, 此方法用于绝对顺序跳转 */ void _HUIPageAddStack(int len, ...); #define HUIPageAddStack(...) _HUIPageAddStack(__COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__) // 当前自身页面不释放内存, 用于某些暂不想释放页面, 等返回后再考虑释放 void HUIPageSetCurrNotFree(); // 恢复当前页面默认释放内存 void HUIPageResetCurrFree(); // 获取当前页面是否不释放 uint8_t HUIPageGetCurrNotFree(); // 设置指定页面不释放内存 void HUIPageSetNotFree(HUiPageIndex_t page); // 恢复指定页面默认释放内存 void HUIPageResetFree(HUiPageIndex_t page); // 获取指定页面是否不释放, 是返回1, 否则返回0 uint8_t HUIPageGetNotFree(HUiPageIndex_t page); // 设置用户数据, 在 kCallShowPage 和 kCallHidePage 都可调用, 但每次切换这些事件后都会重置数据为0 void HUIPageSetUserData(long userData); // 获取用户数据 long HUIPageGetUserData(); // 保存当前页面索引, 仅在 kCallHidePage 和 kCallIndexSave 有效, 成功返回1, 否则返回0 uint8_t HUIPageSaveIndex(uint8_t value); // 获取当前页面索引, 仅在 HUIPageIsBack() 返回1时有效, 否则返回0 uint8_t HUIPageGetIndex(); // 获取当前页面 HUiPageIndex_t HUIPageGetCurrPage(); // 获取上一个页面 HUiPageIndex_t HUIPageGetPrevPage(); // 获取返回栈使用长度 uint8_t HUIPageGetStackLen(); // 获取索引栈使用长度 uint8_t HUIPageGetIndexStackLen(); // 检查当前是否是返回 uint8_t HUIPageIsBack(); // 检查该页面是否在返回栈, 是返回1, 否则返回0 uint8_t HUIPageFindStack(HUiPageIndex_t page); #endif //__H_UI_PAGE_MANAGE_H__