GeekTrack/2.Firmware/components/GeekOS/System/PageManager/PM_Router.cpp

548 lines
14 KiB
C++
Raw Normal View History

2022-09-18 18:40:49 +08:00
/*
* 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);
}