HCoreBase/src/HUIPageManage.c
2025-10-23 21:11:15 +08:00

484 lines
14 KiB
C

#include <HUIPageManage.h>
#include <HByteStack.h>
#include <HBit.h>
#include <HDPageInit.h>
#include <stdarg.h>
#include <string.h>
// 页面栈最大深度
#ifndef HUIPAGE_STACK_SIZE
#define HUIPAGE_STACK_SIZE 10
#endif
#ifndef LogD
#if 0
#include <stdio.h>
#define LogD(format, ...) printf("[%s:%s:%d]"format "\r\n", __FILE_NAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define LogD(...)
#endif
#endif
typedef struct HUIPageManage
{
pageCallType_t pageCall[HUI_PAGE_MAX_NUM]; ///< 页面回调
long userData; ///< 用户数据
uint8_t homePage; ///< 首页, 当切换到首页时清空返回栈
uint8_t currPage; ///< 当前页面
uint8_t prevPage; ///< 上一个页面
uint8_t modifyUserData : 1; ///< 标记是否修改用户数据
uint8_t isInit : 1; ///< 初始化显示页面
uint8_t saveIndex : 1; ///< 允许保存索引
uint8_t isBack : 1; ///< 标记是否是返回
uint8_t tmpIndex : 4; ///< 临时页面索引
} HUIPageManage;
// 页面管理
static struct HUIPageManage pageManage;
// 返回栈
static HBYTE_STACK_DEFINE(pageStack, HUIPAGE_STACK_SIZE);
// 存储索引栈, 切换新页面时, hide和saveIndex事件后增加1, 回退时减少1, 仅切换事件上有效
static HBYTE_STACK_DEFINE(indexStack, HUIPAGE_STACK_SIZE);
// 不释放页面
static HBIT_DEFINE(notFreeBit, HUI_PAGE_MAX_NUM);
// 已初始化页面
static HBIT_DEFINE(initPageBit, HUI_PAGE_MAX_NUM);
/** =================================================== */
#define INIT_PAGE(page) pageManage.pageCall[kUIPage##page] = page##PageCall
static void InitPage() {
// 页面统一初始化
}
#undef INIT_PAGE
/** =================================================== */
static void UpdateIndexEvent(uint8_t currPage, uint8_t isHideEvent, HUiPageIndex_t nextPage) {
// 非回退情况下, 保存索引
pageManage.saveIndex = pageManage.isBack == 0;
if (isHideEvent) {
pageManage.pageCall[currPage](kCallHidePage, NULL, 0);
} else {
pageManage.pageCall[currPage](kCallIndexSave, &nextPage, 1);
// 当存储事件未存储数据时, 则自动补充对齐
if (HByteStackGetUseLen(pageStack) > HByteStackGetUseLen(indexStack)) {
HByteStackPush(indexStack, 0);
}
}
pageManage.saveIndex = 0;
}
static uint8_t CheckPageIsNull(HUiPageIndex_t page) {
if (pageManage.pageCall[page] == NULL) {
LogD("Page[%d] is nullptr", page);
return 0;
}
return 1;
}
static uint8_t CheckSwitchPage() {
if (CheckPageIsNull(pageManage.currPage) == 0) {
return 0;
}
// 检查是否可以允许切换页面
if (pageManage.pageCall[pageManage.currPage](kCallSwitchPage, NULL, 0) == 0) {
LogD("page[%d] not allow switch", pageManage.currPage);
return 0;
}
return 1;
}
static void HideFreePage(HUiPageIndex_t currPage, HUiPageIndex_t nextPage) {
if (pageManage.isInit == 1) {
// hide -> free -> update Page -> init -> show
UpdateIndexEvent(currPage, 1, nextPage);
if (pageManage.isBack == 0) {
// 非回退时触发存储索引事件
// hide -> index -> free -> update Page -> init -> show
UpdateIndexEvent(currPage, 0, nextPage);
}
if (HBitGet(notFreeBit, currPage) == 0) {
pageManage.pageCall[pageManage.currPage](kCallFreePage, NULL, 0);
HBitSet(initPageBit, currPage, 0);
}
} else {
pageManage.isInit = 1;
}
}
static void RecoveryUserDataFlag() {
// 上一个页面退出时, 不修改用户数据就重置为0
if (pageManage.modifyUserData == 0) {
pageManage.userData = 0;
}
pageManage.modifyUserData = 0;
}
static void SwitchPage(HUiPageIndex_t page) {
if (CheckPageIsNull(page) == 0) {
LogD("Switch Page[%d] is nullptr", page);
return ;
}
HideFreePage(pageManage.currPage, page);
RecoveryUserDataFlag();
pageManage.prevPage = pageManage.currPage;
pageManage.currPage = page;
// 检查要显示的页面是不是已经初始化
if (HBitGet(initPageBit, pageManage.currPage) == 0) {
pageManage.pageCall[pageManage.currPage](kCallInitPage, NULL, 0);
} else {
// 检查要显示的页面是不是不需要释放
if (HBitGet(notFreeBit, pageManage.currPage) == 0) {
pageManage.pageCall[pageManage.currPage](kCallInitPage, NULL, 0);
}
}
HBitSet(initPageBit, pageManage.currPage, 1);
pageManage.pageCall[pageManage.currPage](kCallShowPage, NULL, 0);
RecoveryUserDataFlag();
pageManage.isBack = 0;
}
void HUIPageInit(initPageType_t initPage) {
HByteStackSetUseLen(pageStack, 0);
HBitGet(notFreeBit, 0);
HBitGet(initPageBit, 0);
// 注册所有页面
if (initPage != NULL) {
initPage(pageManage.pageCall);
} else {
// 不是外部注册就需要内部注册了
InitPage();
}
//SwitchPage(pageManage.homePage);
}
void HUIPageSetHome(HUiPageIndex_t page) {
pageManage.homePage = page;
}
HUiPageIndex_t HUIPageGetHome() {
return pageManage.homePage;
}
// 切换页面, 当前页面入栈, 相同页面不操作, 需要检查栈, 栈存在就回栈清空之后的, 不存在就压栈
// 因为有些页面可能不用HUIPageBack来返回, 所以需要兼容这种情况
void HUIPageSwitch(HUiPageIndex_t page) {
if (page >= HUI_PAGE_MAX_NUM) {
LogD("page[%d] out of range", page);
return ;
}
// 如果当前页面和上一个页面相同, 则不切换
if (pageManage.currPage == page) {
return ;
}
if (CheckPageIsNull(page) == 0) {
return ;
}
// 检查当前页面是否允许切换
if (CheckSwitchPage() == 0) {
return ;
}
// 切换到首页时清空返回栈
pageManage.isBack = 0;
if (page == pageManage.homePage) {
if (HUIPageClear() == 0) {
return ;
}
} else {
// 如果查找页面在栈中, 说明这个是返回, 当前页面不能入栈
HByteLenType pos = HByteStackFind(pageStack, page);
if (pos != HBYTE_STACK_ERROR) {
// 回栈
HByteStackSetUseLen(pageStack, pos);
HByteStackSetUseLen(indexStack, pos);
pageManage.isBack = 1;
} else {
// 当前页面压栈
HByteStackPush(pageStack, pageManage.currPage);
}
}
SwitchPage(page);
}
void HUIPageNotifyEvent(HUiPageEvent_t event)
{
pageManage.pageCall[pageManage.currPage](kCallNotifyEvent, &event, 1);
}
void HUIPageSwitchHome() {
if (HUIPageClear() == 0) {
return ;
}
SwitchPage(pageManage.homePage);
}
// 返回上一个页面
void HUIPageBack() {
// 检查当前页面是否允许切换
if (CheckSwitchPage() == 0) {
return ;
}
pageManage.isBack = 1;
if (HByteStackEmpty(pageStack)) {
if (pageManage.currPage != pageManage.homePage) {
LogD("currPage[%d], backHome", pageManage.currPage);
SwitchPage(pageManage.homePage);
}
return;
}
SwitchPage(HByteStackPop(pageStack));
HByteStackPop(indexStack);
}
// 清空返回栈, 不允许切换时, 将显示该页面, 而无法继续回栈
uint8_t HUIPageClear() {
uint8_t isBack = pageManage.isBack;
uint8_t needShow = 0;
uint8_t result = 1;
pageManage.isBack = 1;
// 逐个回栈清理
while (HByteStackEmpty(pageStack) == 0) {
if (CheckSwitchPage() == 0) {
if (needShow) {
SwitchPage(pageManage.currPage);
}
result = 0;
break;
}
HideFreePage(pageManage.currPage, HByteStackTop(pageStack));
pageManage.prevPage = pageManage.currPage;
pageManage.currPage = HByteStackTop(pageStack);
RecoveryUserDataFlag();
HByteStackPop(pageStack);
HByteStackPop(indexStack);
needShow = 1;
}
pageManage.isBack = isBack;
return result;
}
/**
* @brief
* 添加关联页面到返回栈, 用于一些按键跳转到指定页面, 或者一些不想逐个进入, 而是直接跳转, 然后返回时逐个返回的功能使用
* 如正常是 主页->菜单->该页面, 但快捷跳转是主页->该页面, 需要将菜单页面添加到返回栈
* 则需要调用 清空返回栈, 然后添加 主页->菜单, 最后再切换到该页面(内部会判断首个页面是否存在返回栈, 自动清空首个页面前的返回栈)
* HUIPageClear(); HUIPageAddStack(主页, 菜单, 该页面);
* 注: 调用这个时, 当前页面将不会入栈
* 如: 当前页面是菜单, 设置 主页->菜单, 则菜单页面不会入栈
* 因此, 此方法用于绝对顺序跳转
*/
void _HUIPageAddStack(int len, ...) {
if (len <= 0) {
LogD("len error[%d]", len);
return ;
}
// 检查页面是否允许切换
if (CheckSwitchPage() == 0) {
return ;
}
va_list args;
va_start(args, len);
HUiPageIndex_t curr = (HUiPageIndex_t)va_arg(args, int);
HByteLenType findPos = HByteStackFind(pageStack, curr);
if (findPos != HBYTE_STACK_ERROR) {
HByteStackSetUseLen(pageStack, findPos);
HByteStackSetUseLen(indexStack, findPos);
pageManage.isBack = 1;
} else if (curr == pageManage.homePage) {
// 如果查找不到, 传入的首个页面又是首页, 则清空返回栈
if (HUIPageClear() == 0) {
va_end(args);
return ;
}
}
findPos = HByteStackGetUseLen(pageStack);
HByteStackPush(pageStack, curr);
for (int i = 1; i < len; ++i) {
curr = (HUiPageIndex_t)va_arg(args, int);
if (curr >= HUI_PAGE_MAX_NUM) {
LogD("page[%d] out of range", curr);
continue;
}
// 检查新加入的页面是否已经在栈中
HByteLenType checkPos = HByteStackFindEx(pageStack, curr, findPos);
if (checkPos != HBYTE_STACK_ERROR) {
LogD("page[%d] already in stack, startIndex[%d] FindIndex[%d], stackSize[%d]", curr, findPos, checkPos, HByteStackGetUseLen(pageStack));
continue;
}
// 开始不是首页, 然后中间页面传入首页, 这是个异常情况
if (curr == pageManage.homePage) {
LogD("page[%d] error, it is home page", curr);
continue;
}
// 检查新加入的页面是否为空, 避免异常崩溃
if (CheckPageIsNull(curr) == 0) {
LogD("page[%d] is null", curr);
continue;
}
// 处理对应页面的存储事件
if (i < len) {
UpdateIndexEvent(HByteStackTop(pageStack), 0, curr);
}
HByteStackPush(pageStack, curr);
}
va_end(args);
SwitchPage(HByteStackPop(pageStack));
}
// 当前自身页面不释放内存, 用于某些暂不想释放页面, 等返回后再考虑释放
void HUIPageSetCurrNotFree() {
HUIPageSetNotFree(pageManage.currPage);
}
// 恢复当前页面默认释放内存
void HUIPageResetCurrFree() {
HUIPageResetFree(pageManage.currPage);
}
// 获取当前页面是否不释放
uint8_t HUIPageGetCurrNotFree() {
return HUIPageGetNotFree(pageManage.currPage);
}
// 设置指定页面不释放内存
void HUIPageSetNotFree(HUiPageIndex_t page) {
HBitSet(notFreeBit, page, 1);
}
// 恢复指定页面默认释放内存
void HUIPageResetFree(HUiPageIndex_t page) {
HBitSet(notFreeBit, page, 0);
}
// 获取指定页面是否不释放
uint8_t HUIPageGetNotFree(HUiPageIndex_t page) {
return HBitGet(notFreeBit, page);
}
// 设置用户数据, 在 kCallShowPage 和 kCallHidePage 都可调用, 但每次切换这些事件后都会重置数据为0
void HUIPageSetUserData(long userData) {
pageManage.modifyUserData = 1;
pageManage.userData = userData;
}
// 获取用户数据
long HUIPageGetUserData() {
return pageManage.userData;
}
// 保存当前页面索引, 仅在 kCallHidePage 和 kCallIndexSave 有效, 成功返回1, 否则返回0
uint8_t HUIPageSaveIndex(uint8_t value) {
if (pageManage.saveIndex == 0) {
LogD("save Index error, not event");
return 0;
}
uint16_t len = HByteStackGetUseLen(indexStack);
uint16_t pageLen = HByteStackGetUseLen(pageStack);
if (len >= pageLen) {
HByteStackPop(indexStack);
}
HByteStackPush(indexStack, value);
return 1;
}
// 获取当前页面索引, 仅在 HUIPageIsBack() 返回1时有效, 否则返回0
uint8_t HUIPageGetIndex() {
if (pageManage.isBack == 0) {
LogD("not back Event, value[%d] size[%d]", HByteStackTop(indexStack), HByteStackGetUseLen(indexStack));
return 0;
}
if (HByteStackEmpty(indexStack)) {
return 0;
}
return HByteStackTop(indexStack);
}
// 临时保存当前页面索引
void HUIPageSaveTempIndex(uint8_t value) {
pageManage.tmpIndex = value;
}
// 获取临时保存的当前页面索引
uint8_t HUIPageGetTempIndex() {
return pageManage.tmpIndex;
}
// 获取当前页面
HUiPageIndex_t HUIPageGetCurrPage() {
return pageManage.currPage;
}
// 获取上一个页面
HUiPageIndex_t HUIPageGetPrevPage() {
return pageManage.prevPage;
}
// 获取返回栈使用长度
uint8_t HUIPageGetStackLen() {
return HByteStackGetUseLen(pageStack);
}
// 获取索引栈使用长度
uint8_t HUIPageGetIndexStackLen() {
return HByteStackGetUseLen(indexStack);
}
// 检查当前是否是返回
uint8_t HUIPageIsBack() {
return pageManage.isBack;
}
// 检查该页面是否在返回栈, 是返回1, 否则返回0
uint8_t HUIPageFindStack(HUiPageIndex_t page) {
return HByteStackFind(pageStack, page) != HBYTE_STACK_ERROR;
}