321 lines
7.6 KiB
C++
321 lines
7.6 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"
|
||
|
#include <algorithm>
|
||
|
|
||
|
#define PM_EMPTY_PAGE_NAME "EMPTY_PAGE"
|
||
|
|
||
|
/**
|
||
|
* @brief Page manager constructor
|
||
|
* @param factory: Pointer to the page factory
|
||
|
* @retval None
|
||
|
*/
|
||
|
PageManager::PageManager(PageFactory* factory)
|
||
|
: _Factory(factory)
|
||
|
, _PagePrev(nullptr)
|
||
|
, _PageCurrent(nullptr)
|
||
|
, _RootDefaultStyle(nullptr)
|
||
|
{
|
||
|
memset(&_AnimState, 0, sizeof(_AnimState));
|
||
|
|
||
|
SetGlobalLoadAnimType();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Page manager destructor
|
||
|
* @param None
|
||
|
* @retval None
|
||
|
*/
|
||
|
PageManager::~PageManager()
|
||
|
{
|
||
|
SetStackClear();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Search pages in the page pool
|
||
|
* @param name: Page name
|
||
|
* @retval A pointer to the base class of the page, or nullptr if not found
|
||
|
*/
|
||
|
PageBase* PageManager::FindPageInPool(const char* name)
|
||
|
{
|
||
|
for (auto iter : _PagePool)
|
||
|
{
|
||
|
if (strcmp(name, iter->_Name) == 0)
|
||
|
{
|
||
|
return iter;
|
||
|
}
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Search pages in the page stack
|
||
|
* @param name: Page name
|
||
|
* @retval A pointer to the base class of the page, or nullptr if not found
|
||
|
*/
|
||
|
PageBase* PageManager::FindPageInStack(const char* name)
|
||
|
{
|
||
|
decltype(_PageStack) stk = _PageStack;
|
||
|
while (!stk.empty())
|
||
|
{
|
||
|
PageBase* base = stk.top();
|
||
|
|
||
|
if (strcmp(name, base->_Name) == 0)
|
||
|
{
|
||
|
return base;
|
||
|
}
|
||
|
|
||
|
stk.pop();
|
||
|
}
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Install the page, and register the page to the page pool
|
||
|
* @param className: The class name of the page
|
||
|
* @param appName: Page application name, no duplicates allowed
|
||
|
* @retval Return true if successful
|
||
|
*/
|
||
|
bool PageManager::Install(const char* className, const char* appName)
|
||
|
{
|
||
|
if (_Factory == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Factory was not registered, can't install page");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (appName == nullptr)
|
||
|
{
|
||
|
PM_LOG_WARN("appName has not set");
|
||
|
appName = className;
|
||
|
}
|
||
|
|
||
|
if (FindPageInPool(appName) != nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was registered", appName);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
PageBase* base = _Factory->CreatePage(className);
|
||
|
if (base == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Factory has not %s", className);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
base->_root = nullptr;
|
||
|
base->_ID = 0;
|
||
|
base->_Manager = nullptr;
|
||
|
base->_UserData = nullptr;
|
||
|
memset(&base->priv, 0, sizeof(base->priv));
|
||
|
|
||
|
PM_LOG_INFO("Install Page[class = %s, name = %s]", className, appName);
|
||
|
bool retval = Register(base, appName);
|
||
|
|
||
|
base->onCustomAttrConfig();
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Uninstall page
|
||
|
* @param appName: Page application name, no duplicates allowed
|
||
|
* @retval Return true if the uninstallation is successful
|
||
|
*/
|
||
|
bool PageManager::Uninstall(const char* appName)
|
||
|
{
|
||
|
PM_LOG_INFO("Page(%s) uninstall...", appName);
|
||
|
|
||
|
PageBase* base = FindPageInPool(appName);
|
||
|
if (base == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was not found", appName);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!Unregister(appName))
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) unregister failed", appName);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (base->priv.IsCached)
|
||
|
{
|
||
|
PM_LOG_WARN("Page(%s) has cached, unloading...", appName);
|
||
|
base->priv.State = PageBase::PAGE_STATE_UNLOAD;
|
||
|
StateUpdate(base);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PM_LOG_INFO("Page(%s) has not cache", appName);
|
||
|
}
|
||
|
|
||
|
delete base;
|
||
|
PM_LOG_INFO("Uninstall OK");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Register the page to the page pool
|
||
|
* @param name: Page application name, duplicate registration is not allowed
|
||
|
* @retval Return true if the registration is successful
|
||
|
*/
|
||
|
bool PageManager::Register(PageBase* base, const char* name)
|
||
|
{
|
||
|
if (FindPageInPool(name) != nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was multi registered", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
base->_Manager = this;
|
||
|
base->_Name = name;
|
||
|
|
||
|
_PagePool.push_back(base);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Log out the page from the page pool
|
||
|
* @param name: Page application name
|
||
|
* @retval Return true if the logout is successful
|
||
|
*/
|
||
|
bool PageManager::Unregister(const char* name)
|
||
|
{
|
||
|
PM_LOG_INFO("Page(%s) unregister...", name);
|
||
|
|
||
|
PageBase* base = FindPageInStack(name);
|
||
|
|
||
|
if (base != nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was in stack", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
base = FindPageInPool(name);
|
||
|
if (base == nullptr)
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was not found", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
auto iter = std::find(_PagePool.begin(), _PagePool.end(), base);
|
||
|
|
||
|
if (iter == _PagePool.end())
|
||
|
{
|
||
|
PM_LOG_ERROR("Page(%s) was not found in PagePool", name);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
_PagePool.erase(iter);
|
||
|
|
||
|
PM_LOG_INFO("Unregister OK");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Get the top page of the page stack
|
||
|
* @param None
|
||
|
* @retval A pointer to the base class of the page
|
||
|
*/
|
||
|
PageBase* PageManager::GetStackTop()
|
||
|
{
|
||
|
return _PageStack.empty() ? nullptr : _PageStack.top();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Get the page below the top of the page stack
|
||
|
* @param None
|
||
|
* @retval A pointer to the base class of the page
|
||
|
*/
|
||
|
PageBase* PageManager::GetStackTopAfter()
|
||
|
{
|
||
|
PageBase* top = GetStackTop();
|
||
|
|
||
|
if (top == nullptr)
|
||
|
{
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
_PageStack.pop();
|
||
|
|
||
|
PageBase* topAfter = GetStackTop();
|
||
|
|
||
|
_PageStack.push(top);
|
||
|
|
||
|
return topAfter;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Clear the page stack and end the life cycle of all pages in the page stack
|
||
|
* @param keepBottom: Whether to keep the bottom page of the stack
|
||
|
* @retval None
|
||
|
*/
|
||
|
void PageManager::SetStackClear(bool keepBottom)
|
||
|
{
|
||
|
while (1)
|
||
|
{
|
||
|
PageBase* top = GetStackTop();
|
||
|
|
||
|
if (top == nullptr)
|
||
|
{
|
||
|
PM_LOG_INFO("Page stack is empty, breaking...");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
PageBase* topAfter = GetStackTopAfter();
|
||
|
|
||
|
if (topAfter == nullptr)
|
||
|
{
|
||
|
if (keepBottom)
|
||
|
{
|
||
|
_PagePrev = top;
|
||
|
PM_LOG_INFO("Keep page stack bottom(%s), breaking...", top->_Name);
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_PagePrev = nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FourceUnload(top);
|
||
|
|
||
|
_PageStack.pop();
|
||
|
}
|
||
|
PM_LOG_INFO("Stack clear done");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Get the name of the previous page
|
||
|
* @param None
|
||
|
* @retval The name of the previous page, if it does not exist, return PM_EMPTY_PAGE_NAME
|
||
|
*/
|
||
|
const char* PageManager::GetPagePrevName()
|
||
|
{
|
||
|
return _PagePrev ? _PagePrev->_Name : PM_EMPTY_PAGE_NAME;
|
||
|
}
|