diff --git a/include/JKSV.hpp b/include/JKSV.hpp index 9ed6f40..91c3ef0 100644 --- a/include/JKSV.hpp +++ b/include/JKSV.hpp @@ -24,10 +24,6 @@ class JKSV /// @brief Runs JKSV's render routine. void render(void); - /// @brief Pushes a new state to JKSV's state vector. - /// @param newState State to push to vector. - static void push_state(std::shared_ptr newState); - private: /// @brief Whether or not initialization was successful and JKSV is still running. bool m_isRunning = false; @@ -37,10 +33,4 @@ class JKSV /// @brief JKSV icon in upper left corner. sdl::SharedTexture m_headerIcon = nullptr; - - /// @brief Vector of states to update and render. - static inline std::vector> sm_stateVector; - - /// @brief Purges and updates states in sm_stateVector. - static void update_state_vector(void); }; diff --git a/include/StateManager.hpp b/include/StateManager.hpp new file mode 100644 index 0000000..f736b2c --- /dev/null +++ b/include/StateManager.hpp @@ -0,0 +1,38 @@ +#pragma once +#include "appstates/AppState.hpp" +#include +#include + +class StateManager +{ + public: + // Singleton. No copying or constructing. + StateManager(const StateManager &) = delete; + StateManager(StateManager &&) = delete; + StateManager &operator=(const StateManager &) = delete; + StateManager &operator=(StateManager &&) = delete; + + /// @brief Runs the state update routine. + static void update(void); + + /// @brief Runs the state rendering routine(s); + static void render(void); + + /// @brief Returns whether the back of the vector is a closable state. + static bool back_is_closable(void); + + /// @brief Pushes a new state to the state vector. + /// @param newState Shared_ptr to state to push. + static void push_state(std::shared_ptr newState); + + private: + /// @brief Private constructor so no constructing. + StateManager(void) = default; + + /// @brief Returns a reference to the instance of StateManger. + /// @return Reference to state manager. + static StateManager &get_instance(void); + + /// @brief This is the vector that holds the pointers to the states. + static inline std::vector> sm_stateVector; +}; diff --git a/include/appstates/ConfirmState.hpp b/include/appstates/ConfirmState.hpp index fe8f1d4..74bbd88 100644 --- a/include/appstates/ConfirmState.hpp +++ b/include/appstates/ConfirmState.hpp @@ -1,5 +1,5 @@ #pragma once -#include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/AppState.hpp" #include "appstates/ProgressState.hpp" #include "appstates/TaskState.hpp" @@ -63,7 +63,10 @@ class ConfirmState : public AppState if (m_triggerGuard && input::button_pressed(HidNpadButton_A) && !m_hold) { AppState::deactivate(); - JKSV::push_state(std::make_shared(m_function, m_dataStruct)); + + auto newState = std::make_shared(m_function, m_dataStruct); + + StateManager::push_state(newState); } else if (m_triggerGuard && input::button_pressed(HidNpadButton_A) && m_hold) { @@ -79,7 +82,8 @@ class ConfirmState : public AppState if (TickCount >= 3000) { AppState::deactivate(); - JKSV::push_state(std::make_shared(m_function, m_dataStruct)); + + auto newState = std::make_shared(m_function, m_dataStruct); } else if (TickCount >= 2000) { @@ -135,10 +139,10 @@ class ConfirmState : public AppState std::string m_yesString; /// @brief X coordinate to render the Yes [A] - int m_yesX = 0; + int m_yesX{}; /// @brief Position of No. - int m_noX = 0; + int m_noX{}; /// @brief This is to prevent the dialog from triggering immediately. bool m_triggerGuard = false; @@ -147,7 +151,7 @@ class ConfirmState : public AppState bool m_hold; /// @brief Keep track of the ticks/time needed to confirm. - uint64_t m_startingTickCount = 0; + uint64_t m_startingTickCount{}; /// @brief Function to execute if action is confirmed. TaskFunction m_function; diff --git a/source/JKSV.cpp b/source/JKSV.cpp index c943c0f..8169818 100644 --- a/source/JKSV.cpp +++ b/source/JKSV.cpp @@ -1,4 +1,5 @@ #include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/MainMenuState.hpp" #include "colors.hpp" #include "config.hpp" @@ -120,7 +121,7 @@ JKSV::JKSV(void) m_headerIcon = sdl::TextureManager::create_load_texture("HeaderIcon", "romfs:/Textures/HeaderIcon.png"); // Push initial main menu state. - JKSV::push_state(std::make_shared()); + StateManager::push_state(std::make_shared()); m_isRunning = true; } @@ -154,12 +155,13 @@ void JKSV::update(void) { input::update(); - if (input::button_pressed(HidNpadButton_Plus) && !sm_stateVector.empty() && sm_stateVector.back()->is_closable()) + if (input::button_pressed(HidNpadButton_Plus) && StateManager::back_is_closable()) { m_isRunning = false; } - JKSV::update_state_vector(); + // State update. + StateManager::update(); // Update pop messages. ui::PopMessageManager::update(); @@ -204,55 +206,11 @@ void JKSV::render(void) BUILD_DAY, BUILD_YEAR); - // State render loop. - if (!sm_stateVector.empty()) - { - for (auto &CurrentState : sm_stateVector) - { - CurrentState->render(); - } - } + // State render. + StateManager::render(); // Render messages. ui::PopMessageManager::render(); sdl::frame_end(); } - -void JKSV::push_state(std::shared_ptr newState) -{ - if (!sm_stateVector.empty()) - { - sm_stateVector.back()->take_focus(); - } - newState->give_focus(); - sm_stateVector.push_back(newState); -} - -void JKSV::update_state_vector(void) -{ - if (sm_stateVector.empty()) - { - return; - } - - // Check for and purge deactivated states. - for (size_t i = 0; i < sm_stateVector.size(); i++) - { - if (!sm_stateVector.at(i)->is_active()) - { - // This is a just in case thing. Some states are never actually purged. - sm_stateVector.at(i)->take_focus(); - sm_stateVector.erase(sm_stateVector.begin() + i); - } - } - - // Make sure the back has focus. - if (!sm_stateVector.back()->has_focus()) - { - sm_stateVector.back()->give_focus(); - } - - // Only update the back most state. - sm_stateVector.back()->update(); -} diff --git a/source/StateManager.cpp b/source/StateManager.cpp new file mode 100644 index 0000000..f5027c3 --- /dev/null +++ b/source/StateManager.cpp @@ -0,0 +1,85 @@ +#include "StateManager.hpp" + +void StateManager::update(void) +{ + // Grab the instance. + StateManager &instance = StateManager::get_instance(); + + if (instance.sm_stateVector.empty()) + { + // Just return. + return; + } + + // Purge uneeded states. + for (size_t i = instance.sm_stateVector.size() - 1; i > 0; i--) + { + // Grab a raw pointer to avoid reference count increase. + AppState *appState = instance.sm_stateVector.at(i).get(); + + if (!appState->is_active()) + { + // Take focus first. + appState->take_focus(); + instance.sm_stateVector.erase(instance.sm_stateVector.begin() + i); + } + } + + // Check if the back has focus. It should always have it. + if (!instance.sm_stateVector.back()->has_focus()) + { + instance.sm_stateVector.back()->give_focus(); + } + + // Only call update on the back. + instance.sm_stateVector.back()->update(); +} + +void StateManager::render(void) +{ + // Instance. + StateManager &instance = StateManager::get_instance(); + + // Loop and render all states. + for (std::shared_ptr &appState : instance.sm_stateVector) + { + appState->render(); + } +} + +bool StateManager::back_is_closable(void) +{ + // Instance. + StateManager &instance = StateManager::get_instance(); + + // Not too sure how to handle this yet. + if (instance.sm_stateVector.empty()) + { + return false; + } + + // Just return this. + return instance.sm_stateVector.back()->is_closable(); +} + +void StateManager::push_state(std::shared_ptr newState) +{ + // Instance. + StateManager &instance = StateManager::get_instance(); + + // Take focus from the current back() + if (!instance.sm_stateVector.empty()) + { + instance.sm_stateVector.back()->take_focus(); + } + + // Give the incoming state focus and then push it. + newState->give_focus(); + instance.sm_stateVector.push_back(newState); +} + +StateManager &StateManager::get_instance(void) +{ + static StateManager instance; + return instance; +} diff --git a/source/appstates/BackupMenuState.cpp b/source/appstates/BackupMenuState.cpp index 7c5b95b..1282e1e 100644 --- a/source/appstates/BackupMenuState.cpp +++ b/source/appstates/BackupMenuState.cpp @@ -1,5 +1,5 @@ #include "appstates/BackupMenuState.hpp" -#include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/ConfirmState.hpp" #include "appstates/ProgressState.hpp" #include "colors.hpp" @@ -123,12 +123,15 @@ void BackupMenuState::update(void) return; } + // This is the path to write the backup to. + fslib::Path targetPath = m_directoryPath / backupName; + + // Create the state. + auto createNewBackup = + std::make_shared(create_new_backup, m_user, m_titleInfo, targetPath, this); + // Push the task. - JKSV::push_state(std::make_shared(create_new_backup, - m_user, - m_titleInfo, - m_directoryPath / backupName, - this)); + StateManager::push_state(createNewBackup); } else if (input::button_pressed(HidNpadButton_A) && sm_backupMenu->get_selected() == 0 && !m_saveHasData) { @@ -153,7 +156,7 @@ void BackupMenuState::update(void) overwrite_backup, m_dataStruct); - JKSV::push_state(confirm); + StateManager::push_state(confirm); } else if (input::button_pressed(HidNpadButton_A) && !m_saveHasData && sm_backupMenu->get_selected() > 0) { @@ -197,7 +200,7 @@ void BackupMenuState::update(void) restore_backup, m_dataStruct); - JKSV::push_state(confirm); + StateManager::push_state(confirm); } else if (input::button_pressed(HidNpadButton_X) && sm_backupMenu->get_selected() > 0) { @@ -219,7 +222,7 @@ void BackupMenuState::update(void) m_dataStruct); // Create/push new state. - JKSV::push_state(confirm); + StateManager::push_state(confirm); } else if (input::button_pressed(HidNpadButton_B)) { diff --git a/source/appstates/MainMenuState.cpp b/source/appstates/MainMenuState.cpp index 4fff916..6317d50 100644 --- a/source/appstates/MainMenuState.cpp +++ b/source/appstates/MainMenuState.cpp @@ -1,5 +1,5 @@ #include "appstates/MainMenuState.hpp" -#include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/ExtrasMenuState.hpp" #include "appstates/SettingsState.hpp" #include "appstates/TextTitleSelectState.hpp" @@ -53,16 +53,17 @@ void MainMenuState::update(void) int selected = m_mainMenu.get_selected(); + // To do: Simplify this logic. if (input::button_pressed(HidNpadButton_A) && selected < static_cast(sm_users.size()) && sm_users.at(selected)->get_total_data_entries() > 0) { sm_states.at(selected)->reactivate(); - JKSV::push_state(sm_states.at(selected)); + StateManager::push_state(sm_states.at(selected)); } else if (input::button_pressed(HidNpadButton_A) && selected >= static_cast(sm_users.size())) { sm_states.at(selected)->reactivate(); - JKSV::push_state(sm_states.at(selected)); + StateManager::push_state(sm_states.at(selected)); } else if (input::button_pressed(HidNpadButton_X) && selected < static_cast(sm_users.size())) { @@ -70,7 +71,7 @@ void MainMenuState::update(void) data::User *targetUser = sm_users.at(selected); TitleSelectCommon *targetTitleSelect = reinterpret_cast(sm_states.at(selected).get()); - JKSV::push_state(std::make_shared(targetUser, targetTitleSelect)); + StateManager::push_state(std::make_shared(targetUser, targetTitleSelect)); } } diff --git a/source/appstates/SaveCreateState.cpp b/source/appstates/SaveCreateState.cpp index 458852c..92982ed 100644 --- a/source/appstates/SaveCreateState.cpp +++ b/source/appstates/SaveCreateState.cpp @@ -1,5 +1,5 @@ #include "appstates/SaveCreateState.hpp" -#include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/TaskState.hpp" #include "data/data.hpp" #include "fs/fs.hpp" @@ -62,7 +62,7 @@ void SaveCreateState::update(void) if (input::button_pressed(HidNpadButton_A)) { data::TitleInfo *targetTitle = m_titleInfoVector.at(m_saveMenu.get_selected()); - JKSV::push_state(std::make_shared(create_save_data, m_user, targetTitle, this)); + StateManager::push_state(std::make_shared(create_save_data, m_user, targetTitle, this)); } else if (input::button_pressed(HidNpadButton_B)) { diff --git a/source/appstates/TextTitleSelectState.cpp b/source/appstates/TextTitleSelectState.cpp index b6b8d1e..5df479d 100644 --- a/source/appstates/TextTitleSelectState.cpp +++ b/source/appstates/TextTitleSelectState.cpp @@ -1,5 +1,5 @@ #include "appstates/TextTitleSelectState.hpp" -#include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/BackupMenuState.hpp" #include "appstates/MainMenuState.hpp" #include "appstates/TitleOptionState.hpp" @@ -49,9 +49,13 @@ void TextTitleSelectState::update(void) if ((fslib::directory_exists(targetPath) || fslib::create_directory(targetPath)) && fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *saveInfo)) { - JKSV::push_state(std::make_shared(m_user, - titleInfo, - static_cast(saveInfo->save_data_type))); + // State + auto backupMenuState = + std::make_shared(m_user, + titleInfo, + static_cast(saveInfo->save_data_type)); + + StateManager::push_state(backupMenuState); } else { @@ -65,7 +69,9 @@ void TextTitleSelectState::update(void) uint64_t applicationID = m_user->get_application_id_at(selected); data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); - JKSV::push_state(std::make_shared(m_user, titleInfo, this)); + auto titleOptionState = std::make_shared(m_user, titleInfo, this); + + StateManager::push_state(titleOptionState); } else if (input::button_pressed(HidNpadButton_Y)) { diff --git a/source/appstates/TitleOptionState.cpp b/source/appstates/TitleOptionState.cpp index e8e9f50..af890a9 100644 --- a/source/appstates/TitleOptionState.cpp +++ b/source/appstates/TitleOptionState.cpp @@ -1,4 +1,5 @@ #include "appstates/TitleOptionState.hpp" +#include "StateManager.hpp" #include "appstates/ConfirmState.hpp" #include "appstates/MainMenuState.hpp" #include "appstates/TitleInfoState.hpp" @@ -100,8 +101,10 @@ void TitleOptionState::update(void) { case INFORMATION: { + auto titleInfoState = std::make_shared(m_user, m_titleInfo); + // Just push the state. - JKSV::push_state(std::make_shared(m_user, m_titleInfo)); + StateManager::push_state(titleInfoState); } break; @@ -120,7 +123,7 @@ void TitleOptionState::update(void) m_dataStruct); // Push - JKSV::push_state(confirm); + StateManager::push_state(confirm); } break; @@ -149,7 +152,7 @@ void TitleOptionState::update(void) delete_all_backups_for_title, m_dataStruct); - JKSV::push_state(confirm); + StateManager::push_state(confirm); } break; @@ -175,7 +178,7 @@ void TitleOptionState::update(void) reset_save_data, m_dataStruct); - JKSV::push_state(confirm); + StateManager::push_state(confirm); } break; @@ -202,7 +205,7 @@ void TitleOptionState::update(void) delete_save_data_from_system, m_dataStruct); - JKSV::push_state(confirm); + StateManager::push_state(confirm); } break; @@ -217,7 +220,7 @@ void TitleOptionState::update(void) } // State. - JKSV::push_state(std::make_shared(extend_save_data, m_dataStruct)); + StateManager::push_state(std::make_shared(extend_save_data, m_dataStruct)); } break; diff --git a/source/appstates/TitleSelectState.cpp b/source/appstates/TitleSelectState.cpp index 0372d8b..a3bb066 100644 --- a/source/appstates/TitleSelectState.cpp +++ b/source/appstates/TitleSelectState.cpp @@ -1,5 +1,5 @@ #include "appstates/TitleSelectState.hpp" -#include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/BackupMenuState.hpp" #include "appstates/MainMenuState.hpp" #include "appstates/TitleOptionState.hpp" @@ -50,9 +50,12 @@ void TitleSelectState::update(void) if ((fslib::directory_exists(targetPath) || fslib::create_directory(targetPath)) && fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *saveInfo)) { - JKSV::push_state(std::make_shared(m_user, - titleInfo, - static_cast(saveInfo->save_data_type))); + auto backupMenuState = + std::make_shared(m_user, + titleInfo, + static_cast(saveInfo->save_data_type)); + + StateManager::push_state(backupMenuState); } else { @@ -64,7 +67,9 @@ void TitleSelectState::update(void) uint64_t applicationID = m_user->get_application_id_at(m_titleView.get_selected()); data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); - JKSV::push_state(std::make_shared(m_user, titleInfo, this)); + auto titleOptionState = std::make_shared(m_user, titleInfo, this); + + StateManager::push_state(std::make_shared(m_user, titleInfo, this)); } else if (input::button_pressed(HidNpadButton_B)) { diff --git a/source/appstates/UserOptionState.cpp b/source/appstates/UserOptionState.cpp index 20500a5..efe5757 100644 --- a/source/appstates/UserOptionState.cpp +++ b/source/appstates/UserOptionState.cpp @@ -1,5 +1,5 @@ #include "appstates/UserOptionState.hpp" -#include "JKSV.hpp" +#include "StateManager.hpp" #include "appstates/ConfirmState.hpp" #include "appstates/MainMenuState.hpp" #include "appstates/ProgressState.hpp" @@ -90,14 +90,16 @@ void UserOptionState::update(void) backup_all_for_user, m_dataStruct); - JKSV::push_state(confirmBackupAll); + StateManager::push_state(confirmBackupAll); } break; case CREATE_SAVE: { + auto saveCreateState = std::make_shared(m_user, m_titleSelect); + // This just pushes the state with the menu to select. - JKSV::push_state(std::make_shared(m_user, m_titleSelect)); + StateManager::push_state(std::make_shared(m_user, m_titleSelect)); } break; @@ -115,7 +117,7 @@ void UserOptionState::update(void) m_dataStruct); // Done? - JKSV::push_state(confirmCreateAll); + StateManager::push_state(confirmCreateAll); } break; @@ -132,7 +134,7 @@ void UserOptionState::update(void) delete_all_save_data_for_user, m_dataStruct); - JKSV::push_state(confirmDeleteAll); + StateManager::push_state(confirmDeleteAll); } break; }