1. 增加ui管理页面

This commit is contained in:
coffee 2025-04-03 10:48:36 +08:00
parent f9444f3dbf
commit c014737966
2 changed files with 538 additions and 0 deletions

133
include/HUIPageManage.h Normal file
View File

@ -0,0 +1,133 @@
/**
* : 2025-04-02
* : coffee
* : ui页面管理, ,
*/
#ifndef __H_UI_PAGE_MANAGE_H__
#define __H_UI_PAGE_MANAGE_H__
#include <stdint.h>
#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 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
// 对应事件的data转换具体类型宏
#define UI_CALL_INDEX_TYPE(value) ((enum eUIPage *)value)
// 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, enum eUIPage *nextPage, 1);
};
// 每个页面占用一个枚举, 根据枚举切换页面, kUIPageMax最大页面值, 要求格式统一 kUIPage + 页面名
enum eUIPage
{
kUIPageMax, ///< ui最大个数, 不超过254个页面, 如超过则需要修改字节栈类型
};
///< 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(enum eUIPage page);
// 切换页面, 当前页面入栈, 相同页面不操作, 需要检查栈, 栈存在就回栈清空之后的, 不存在就压栈
// 因为有些页面可能不用HUIPageBack来返回, 所以需要兼容这种情况
void HUIPageSwitch(enum eUIPage page);
// 返回上一个页面
void HUIPageBack();
// 清空返回栈
void 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(enum eUIPage page);
// 恢复指定页面默认释放内存
void HUIPageResetFree(enum eUIPage page);
// 获取指定页面是否不释放, 是返回1, 否则返回0
uint8_t HUIPageGetNotFree(enum eUIPage 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();
// 获取当前页面
enum eUIPage HUIPageGetCurrPage();
// 获取上一个页面
enum eUIPage HUIPageGetPrevPage();
// 获取返回栈使用长度
uint8_t HUIPageGetStackLen();
// 获取索引栈使用长度
uint8_t HUIPageGetIndexStackLen();
// 检查当前是否是返回
uint8_t HUIPageIsBack();
// 检查该页面是否在返回栈, 是返回1, 否则返回0
uint8_t HUIPageFindStack(enum eUIPage page);
#endif //__H_UI_PAGE_MANAGE_H__

405
src/HUIPageManage.c Normal file
View File

@ -0,0 +1,405 @@
#include <HUIPageManage.h>
#include <HByteStack.h>
#include <HBit.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[kUIPageMax]; ///< 页面回调
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; ///< 标记是否是返回
} 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, kUIPageMax);
// 已初始化页面
static HBIT_DEFINE(initPageBit, kUIPageMax);
/** =================================================== */
#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, enum eUIPage nextPage) {
pageManage.saveIndex = 1;
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(enum eUIPage 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 SwitchPage(enum eUIPage page) {
if (pageManage.isInit == 1) {
// hide -> free -> update Page -> init -> show
UpdateIndexEvent(pageManage.currPage, 1, page);
if (pageManage.isBack == 0) {
// 非回退时触发存储索引事件
// hide -> index -> free -> update Page -> init -> show
UpdateIndexEvent(pageManage.currPage, 0, page);
}
if (HBitGet(notFreeBit, pageManage.currPage) == 0) {
pageManage.pageCall[pageManage.currPage](kCallFreePage, NULL, 0);
HBitSet(initPageBit, pageManage.currPage, 0);
}
} else {
pageManage.isInit = 1;
}
// 上一个页面退出时, 不修改用户数据就重置为0
if (pageManage.modifyUserData == 0) {
pageManage.userData = 0;
}
pageManage.modifyUserData = 0;
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);
if (pageManage.modifyUserData == 0) {
pageManage.userData = 0;
}
pageManage.modifyUserData = 0;
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(enum eUIPage page) {
pageManage.homePage = page;
}
// 切换页面, 当前页面入栈, 相同页面不操作, 需要检查栈, 栈存在就回栈清空之后的, 不存在就压栈
// 因为有些页面可能不用HUIPageBack来返回, 所以需要兼容这种情况
void HUIPageSwitch(enum eUIPage page) {
if (page < 0 || page >= kUIPageMax) {
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) {
HUIPageClear();
} 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 HUIPageBack() {
pageManage.isBack = 1;
if (HByteStackEmpty(pageStack)) {
SwitchPage(pageManage.homePage);
return;
}
SwitchPage(HByteStackPop(pageStack));
HByteStackPop(indexStack);
}
// 清空返回栈
void HUIPageClear() {
HByteStackClear(pageStack);
HByteStackClear(indexStack);
}
/**
* @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);
enum eUIPage curr = va_arg(args, enum eUIPage);
HByteLenType findPos = HByteStackFind(pageStack, curr);
if (findPos != HBYTE_STACK_ERROR) {
HByteStackSetUseLen(pageStack, findPos);
HByteStackSetUseLen(indexStack, findPos);
pageManage.isBack = 1;
} else if (curr == pageManage.homePage) {
// 如果查找不到, 传入的首个页面又是首页, 则清空返回栈
HUIPageClear();
}
findPos = HByteStackGetUseLen(pageStack);
HByteStackPush(pageStack, curr);
for (int i = 1; i < len; ++i) {
curr = va_arg(args, enum eUIPage);
if (curr < 0 || curr >= kUIPageMax) {
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 + 1 < 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(enum eUIPage page) {
HBitSet(notFreeBit, page, 1);
}
// 恢复指定页面默认释放内存
void HUIPageResetFree(enum eUIPage page) {
HBitSet(notFreeBit, page, 0);
}
// 获取指定页面是否不释放
uint8_t HUIPageGetNotFree(enum eUIPage 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;
}
// 获取当前页面索引
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);
}
// 获取当前页面
enum eUIPage HUIPageGetCurrPage() {
return pageManage.currPage;
}
// 获取上一个页面
enum eUIPage 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(enum eUIPage page) {
return HByteStackFind(pageStack, page) != HBYTE_STACK_ERROR;
}