/* * 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 #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; }