548 lines
14 KiB
C++
548 lines
14 KiB
C++
|
/*
|
||
|
* MIT License
|
||
|
* Copyright (c) 2021 _VIFEXTech
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all
|
||
|
* copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
*/
|
||
|
#include "PageManager.h"
|
||
|
#include "PM_Log.h"
|
||
|
|
||
|
/**
|
||
|
* @brief Enter a new page, replace the old page
|
||
|
* @param name: The name of the page to enter
|
||
|
* @param stash: Parameters passed to the new page
|
||
|
* @retval Return true if successful
|
||
|
*/
|
||
|
bool PageManager::Replace(const char* name, const PageBase::Stash_t* stash)
|
||
|
{
|
||
|
/* Check whether the animation of switching pages is being executed */
|
||
|
if (!SwitchAnimStateCheck())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Check whether the stack is repeatedly pushed */
|
||
|
if (FindPageInStack(name) != nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was multi push", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Check if the page is registered in the page pool */
|
||
|
PageBase* base = FindPageInPool(name);
|
||
|
|
||
|
if (base == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was not install", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Get the top page of the stack */
|
||
|
PageBase* top = GetStackTop();
|
||
|
|
||
|
if (top == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Stack top is NULL");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Force disable cache */
|
||
|
top->priv.IsCached = false;
|
||
|
|
||
|
/* Synchronous automatic cache configuration */
|
||
|
base->priv.IsDisableAutoCache = base->priv.ReqDisableAutoCache;
|
||
|
|
||
|
/* Remove current page */
|
||
|
_PageStack.pop();
|
||
|
|
||
|
/* Push into the stack */
|
||
|
_PageStack.push(base);
|
||
|
|
||
|
PM_LOG_INFO("Page(%s) replace Page(%s) (stash = 0x%p)", name, top->_Name, stash);
|
||
|
|
||
|
/* Page switching execution */
|
||
|
return SwitchTo(base, true, stash);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Enter a new page, the old page is pushed onto the stack
|
||
|
* @param name: The name of the page to enter
|
||
|
* @param stash: Parameters passed to the new page
|
||
|
* @retval Return true if successful
|
||
|
*/
|
||
|
bool PageManager::Push(const char* name, const PageBase::Stash_t* stash)
|
||
|
{
|
||
|
/* Check whether the animation of switching pages is being executed */
|
||
|
if (!SwitchAnimStateCheck())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Check whether the stack is repeatedly pushed */
|
||
|
if (FindPageInStack(name) != nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was multi push", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Check if the page is registered in the page pool */
|
||
|
PageBase* base = FindPageInPool(name);
|
||
|
|
||
|
if (base == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was not install", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Synchronous automatic cache configuration */
|
||
|
base->priv.IsDisableAutoCache = base->priv.ReqDisableAutoCache;
|
||
|
|
||
|
/* Push into the stack */
|
||
|
_PageStack.push(base);
|
||
|
|
||
|
PM_LOG_INFO("Page(%s) push >> [Screen] (stash = 0x%p)", name, stash);
|
||
|
|
||
|
/* Page switching execution */
|
||
|
return SwitchTo(base, true, stash);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Pop the current page
|
||
|
* @param None
|
||
|
* @retval Return true if successful
|
||
|
*/
|
||
|
bool PageManager::Pop()
|
||
|
{
|
||
|
/* Check whether the animation of switching pages is being executed */
|
||
|
if (!SwitchAnimStateCheck())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Get the top page of the stack */
|
||
|
PageBase* top = GetStackTop();
|
||
|
|
||
|
if (top == nullptr)
|
||
|
{
|
||
|
PM_LOG_WARN("Page stack is empty, cat't pop");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Whether to turn off automatic cache */
|
||
|
if (!top->priv.IsDisableAutoCache)
|
||
|
{
|
||
|
PM_LOG_INFO("Page(%s) has auto cache, cache disabled", top->_Name);
|
||
|
top->priv.IsCached = false;
|
||
|
}
|
||
|
|
||
|
PM_LOG_INFO("Page(%s) pop << [Screen]", top->_Name);
|
||
|
|
||
|
/* Page popup */
|
||
|
_PageStack.pop();
|
||
|
|
||
|
/* Get the next page */
|
||
|
top = GetStackTop();
|
||
|
|
||
|
/* Page switching execution */
|
||
|
return SwitchTo(top, false, nullptr);;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Page switching
|
||
|
* @param newNode: Pointer to new page
|
||
|
* @param isEnterAct: Whether it is a ENTER action
|
||
|
* @param stash: Parameters passed to the new page
|
||
|
* @retval Return true if successful
|
||
|
*/
|
||
|
bool PageManager::SwitchTo(PageBase* newNode, bool isEnterAct, const PageBase::Stash_t* stash)
|
||
|
{
|
||
|
if (newNode == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("newNode is nullptr");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Whether page switching has been requested */
|
||
|
if (_AnimState.IsSwitchReq)
|
||
|
{
|
||
|
PM_LOG_WARN("Page switch busy, reqire(%s) is ignore", newNode->_Name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
_AnimState.IsSwitchReq = true;
|
||
|
|
||
|
/* Is there a parameter to pass */
|
||
|
if (stash != nullptr)
|
||
|
{
|
||
|
PM_LOG_INFO("stash is detect, %s >> stash(0x%p) >> %s", GetPagePrevName(), stash, newNode->_Name);
|
||
|
|
||
|
void* buffer = nullptr;
|
||
|
|
||
|
if (newNode->priv.Stash.ptr == nullptr)
|
||
|
{
|
||
|
buffer = lv_mem_alloc(stash->size);
|
||
|
if (buffer == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("stash malloc failed");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PM_LOG_INFO("stash(0x%p) malloc[%d]", buffer, stash->size);
|
||
|
}
|
||
|
}
|
||
|
else if(newNode->priv.Stash.size == stash->size)
|
||
|
{
|
||
|
buffer = newNode->priv.Stash.ptr;
|
||
|
PM_LOG_INFO("stash(0x%p) is exist", buffer);
|
||
|
}
|
||
|
|
||
|
if (buffer != nullptr)
|
||
|
{
|
||
|
memcpy(buffer, stash->ptr, stash->size);
|
||
|
PM_LOG_INFO("stash memcpy[%d] 0x%p >> 0x%p", stash->size, stash->ptr, buffer);
|
||
|
newNode->priv.Stash.ptr = buffer;
|
||
|
newNode->priv.Stash.size = stash->size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Record current page */
|
||
|
_PageCurrent = newNode;
|
||
|
|
||
|
/* If the current page has a cache */
|
||
|
if (_PageCurrent->priv.IsCached)
|
||
|
{
|
||
|
/* Direct display, no need to load */
|
||
|
PM_LOG_INFO("Page(%s) has cached, appear driectly", _PageCurrent->_Name);
|
||
|
_PageCurrent->priv.State = PageBase::PAGE_STATE_WILL_APPEAR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Load page */
|
||
|
_PageCurrent->priv.State = PageBase::PAGE_STATE_LOAD;
|
||
|
}
|
||
|
|
||
|
if (_PagePrev != nullptr)
|
||
|
{
|
||
|
_PagePrev->priv.Anim.IsEnter = false;
|
||
|
}
|
||
|
|
||
|
_PageCurrent->priv.Anim.IsEnter = true;
|
||
|
|
||
|
_AnimState.IsEntering = isEnterAct;
|
||
|
|
||
|
if (_AnimState.IsEntering)
|
||
|
{
|
||
|
/* Update the animation configuration according to the current page */
|
||
|
SwitchAnimTypeUpdate(_PageCurrent);
|
||
|
}
|
||
|
|
||
|
/* Update the state machine of the previous page */
|
||
|
StateUpdate(_PagePrev);
|
||
|
|
||
|
/* Update the state machine of the current page */
|
||
|
StateUpdate(_PageCurrent);
|
||
|
|
||
|
/* Move the layer, move the new page to the front */
|
||
|
if (_AnimState.IsEntering)
|
||
|
{
|
||
|
PM_LOG_INFO("Page ENTER is detect, move Page(%s) to foreground", _PageCurrent->_Name);
|
||
|
if (_PagePrev)lv_obj_move_foreground(_PagePrev->_root);
|
||
|
lv_obj_move_foreground(_PageCurrent->_root);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PM_LOG_INFO("Page EXIT is detect, move Page(%s) to foreground", GetPagePrevName());
|
||
|
lv_obj_move_foreground(_PageCurrent->_root);
|
||
|
if (_PagePrev)lv_obj_move_foreground(_PagePrev->_root);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Force the end of the life cycle of the page without animation
|
||
|
* @param base: Pointer to the page being executed
|
||
|
* @retval Return true if successful
|
||
|
*/
|
||
|
bool PageManager::FourceUnload(PageBase* base)
|
||
|
{
|
||
|
if (base == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page is nullptr, Unload failed");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
PM_LOG_INFO("Page(%s) Fource unloading...", base->_Name);
|
||
|
|
||
|
if (base->priv.State == PageBase::PAGE_STATE_ACTIVITY)
|
||
|
{
|
||
|
PM_LOG_INFO("Page state is ACTIVITY, Disappearing...");
|
||
|
base->onViewWillDisappear();
|
||
|
base->onViewDidDisappear();
|
||
|
}
|
||
|
|
||
|
base->priv.State = StateUnloadExecute(base);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Back to the main page (the page at the bottom of the stack)
|
||
|
* @param None
|
||
|
* @retval Return true if successful
|
||
|
*/
|
||
|
bool PageManager::BackHome()
|
||
|
{
|
||
|
/* Check whether the animation of switching pages is being executed */
|
||
|
if (!SwitchAnimStateCheck())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
SetStackClear(true);
|
||
|
|
||
|
_PagePrev = nullptr;
|
||
|
|
||
|
PageBase* home = GetStackTop();
|
||
|
|
||
|
SwitchTo(home, false);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Check if the page switching animation is being executed
|
||
|
* @param None
|
||
|
* @retval Return true if it is executing
|
||
|
*/
|
||
|
bool PageManager::SwitchAnimStateCheck()
|
||
|
{
|
||
|
if (_AnimState.IsSwitchReq || _AnimState.IsBusy)
|
||
|
{
|
||
|
PM_LOG_WARN(
|
||
|
"Page switch busy[AnimState.IsSwitchReq = %d,"
|
||
|
"AnimState.IsBusy = %d],"
|
||
|
"request ignored",
|
||
|
_AnimState.IsSwitchReq,
|
||
|
_AnimState.IsBusy
|
||
|
);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Page switching request check
|
||
|
* @param None
|
||
|
* @retval Return true if all pages are executed
|
||
|
*/
|
||
|
bool PageManager::SwitchReqCheck()
|
||
|
{
|
||
|
bool ret = false;
|
||
|
bool lastNodeBusy = _PagePrev && _PagePrev->priv.Anim.IsBusy;
|
||
|
|
||
|
if (!_PageCurrent->priv.Anim.IsBusy && !lastNodeBusy)
|
||
|
{
|
||
|
PM_LOG_INFO("----Page switch was all finished----");
|
||
|
_AnimState.IsSwitchReq = false;
|
||
|
ret = true;
|
||
|
_PagePrev = _PageCurrent;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (_PageCurrent->priv.Anim.IsBusy)
|
||
|
{
|
||
|
PM_LOG_WARN("Page PageCurrent(%s) is busy", _PageCurrent->_Name);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PM_LOG_WARN("Page PagePrev(%s) is busy", GetPagePrevName());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief PPage switching animation execution end callback
|
||
|
* @param a: Pointer to animation
|
||
|
* @retval None
|
||
|
*/
|
||
|
void PageManager::onSwitchAnimFinish(lv_anim_t* a)
|
||
|
{
|
||
|
PageBase* base = (PageBase*)lv_anim_get_user_data(a);
|
||
|
PageManager* manager = base->_Manager;
|
||
|
|
||
|
PM_LOG_INFO("Page(%s) Anim finish", base->_Name);
|
||
|
|
||
|
manager->StateUpdate(base);
|
||
|
base->priv.Anim.IsBusy = false;
|
||
|
bool isFinished = manager->SwitchReqCheck();
|
||
|
|
||
|
if (!manager->_AnimState.IsEntering && isFinished)
|
||
|
{
|
||
|
manager->SwitchAnimTypeUpdate(manager->_PageCurrent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Create page switching animation
|
||
|
* @param a: Point to the animated page
|
||
|
* @retval None
|
||
|
*/
|
||
|
void PageManager::SwitchAnimCreate(PageBase* base)
|
||
|
{
|
||
|
LoadAnimAttr_t animAttr;
|
||
|
if (!GetCurrentLoadAnimAttr(&animAttr))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
lv_anim_t a;
|
||
|
AnimDefaultInit(&a);
|
||
|
lv_anim_set_user_data(&a, base);
|
||
|
lv_anim_set_var(&a, base->_root);
|
||
|
lv_anim_set_ready_cb(&a, onSwitchAnimFinish);
|
||
|
lv_anim_set_exec_cb(&a, animAttr.setter);
|
||
|
|
||
|
int32_t start = 0;
|
||
|
|
||
|
if (animAttr.getter)
|
||
|
{
|
||
|
start = animAttr.getter(base->_root);
|
||
|
}
|
||
|
|
||
|
if (_AnimState.IsEntering)
|
||
|
{
|
||
|
if (base->priv.Anim.IsEnter)
|
||
|
{
|
||
|
lv_anim_set_values(
|
||
|
&a,
|
||
|
animAttr.push.enter.start,
|
||
|
animAttr.push.enter.end
|
||
|
);
|
||
|
}
|
||
|
else /* Exit */
|
||
|
{
|
||
|
lv_anim_set_values(
|
||
|
&a,
|
||
|
start,
|
||
|
animAttr.push.exit.end
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
else /* Pop */
|
||
|
{
|
||
|
if (base->priv.Anim.IsEnter)
|
||
|
{
|
||
|
lv_anim_set_values(
|
||
|
&a,
|
||
|
animAttr.pop.enter.start,
|
||
|
animAttr.pop.enter.end
|
||
|
);
|
||
|
}
|
||
|
else /* Exit */
|
||
|
{
|
||
|
lv_anim_set_values(
|
||
|
&a,
|
||
|
start,
|
||
|
animAttr.pop.exit.end
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lv_anim_start(&a);
|
||
|
base->priv.Anim.IsBusy = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Set global animation properties
|
||
|
* @param anim: Animation type
|
||
|
* @param time: Animation duration
|
||
|
* @param path: Animation curve
|
||
|
* @retval None
|
||
|
*/
|
||
|
void PageManager::SetGlobalLoadAnimType(LoadAnim_t anim, uint16_t time, lv_anim_path_cb_t path)
|
||
|
{
|
||
|
if (anim > _LOAD_ANIM_LAST)
|
||
|
{
|
||
|
anim = LOAD_ANIM_NONE;
|
||
|
}
|
||
|
|
||
|
_AnimState.Global.Type = anim;
|
||
|
_AnimState.Global.Time = time;
|
||
|
_AnimState.Global.Path = path;
|
||
|
|
||
|
PM_LOG_INFO("Set global load anim type = %d", anim);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Update current animation properties, apply page custom animation
|
||
|
* @param base: Pointer to page
|
||
|
* @retval None
|
||
|
*/
|
||
|
void PageManager::SwitchAnimTypeUpdate(PageBase* base)
|
||
|
{
|
||
|
if (base->priv.Anim.Attr.Type == LOAD_ANIM_GLOBAL)
|
||
|
{
|
||
|
PM_LOG_INFO(
|
||
|
"Page(%s) Anim.Type was not set, use AnimState.Global.Type = %d",
|
||
|
base->_Name,
|
||
|
_AnimState.Global.Type
|
||
|
);
|
||
|
_AnimState.Current = _AnimState.Global;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (base->priv.Anim.Attr.Type > _LOAD_ANIM_LAST)
|
||
|
{
|
||
|
PM_LOG_ERROR(
|
||
|
"Page(%s) ERROR custom Anim.Type = %d, use AnimState.Global.Type = %d",
|
||
|
base->_Name,
|
||
|
base->priv.Anim.Attr.Type,
|
||
|
_AnimState.Global.Type
|
||
|
);
|
||
|
base->priv.Anim.Attr = _AnimState.Global;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PM_LOG_INFO(
|
||
|
"Page(%s) custom Anim.Type set = %d",
|
||
|
base->_Name,
|
||
|
base->priv.Anim.Attr.Type
|
||
|
);
|
||
|
}
|
||
|
_AnimState.Current = base->priv.Anim.Attr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Set animation default parameters
|
||
|
* @param a: Pointer to animation
|
||
|
* @retval None
|
||
|
*/
|
||
|
void PageManager::AnimDefaultInit(lv_anim_t* a)
|
||
|
{
|
||
|
lv_anim_init(a);
|
||
|
|
||
|
uint32_t time = (GetCurrentLoadAnimType() == LOAD_ANIM_NONE) ? 0 : _AnimState.Current.Time;
|
||
|
lv_anim_set_time(a, time);
|
||
|
lv_anim_set_path_cb(a, _AnimState.Current.Path);
|
||
|
}
|