diff --git a/include/appstates/ConfirmState.hpp b/include/appstates/ConfirmState.hpp index e73eec5..a2a9e84 100644 --- a/include/appstates/ConfirmState.hpp +++ b/include/appstates/ConfirmState.hpp @@ -432,6 +432,9 @@ class ConfirmState final : public BaseState FadeState::create_and_push(colors::DIM_BACKGROUND, colors::ALPHA_FADE_END, colors::ALPHA_FADE_BEGIN, nullptr); } + // Return the yes to its original state. + sm_yes = strings::get_by_name(strings::names::YES_NO_OK, 0); + // Deactivate this state. BaseState::deactivate(); } diff --git a/include/appstates/MessageState.hpp b/include/appstates/MessageState.hpp index a051277..7958fc7 100644 --- a/include/appstates/MessageState.hpp +++ b/include/appstates/MessageState.hpp @@ -73,7 +73,7 @@ class MessageState final : public BaseState private: /// @brief States this state can be in. - enum class State + enum class State : uint8_t { Opening, Displaying, diff --git a/include/appstates/ProgressState.hpp b/include/appstates/ProgressState.hpp index e2300db..b726c4e 100644 --- a/include/appstates/ProgressState.hpp +++ b/include/appstates/ProgressState.hpp @@ -48,6 +48,20 @@ class ProgressState final : public BaseTask void render() override; private: + /// @brief States this state can be in. + enum class State : uint8_t + { + Opening, + Running, + Closing + }; + + /// @brief Stores the function for task. + sys::threadpool::JobFunction m_job{}; + + /// @brief Stores the data for the task. + sys::Task::TaskData m_taskData{}; + /// @brief Progress which is saved as a rounded whole number. size_t m_progress{}; @@ -63,8 +77,8 @@ class ProgressState final : public BaseTask /// @brief Transition. ui::Transition m_transition{}; - /// @brief Controls the closing transition. - bool m_close{}; + /// @brief Current state of the current state. + ProgressState::State m_state{}; /// @brief This is the dialog box everything is rendered to. static inline std::shared_ptr sm_dialog{}; @@ -75,6 +89,12 @@ class ProgressState final : public BaseTask /// @brief Initializes the shared dialog box. void initialize_static_members(); + /// @brief Updates the dimensions of the dialog. + void update_dimensions() noexcept; + + /// @brief This updates the current progress displayed. + void update_progress() noexcept; + /// @brief Closes the dialog. void close_dialog(); diff --git a/source/appstates/ProgressState.cpp b/source/appstates/ProgressState.cpp index 1e9c384..1a46f71 100644 --- a/source/appstates/ProgressState.cpp +++ b/source/appstates/ProgressState.cpp @@ -13,6 +13,13 @@ namespace { + // Initial coords. + constexpr int INITIAL_WIDTH_HEIGHT = 32; + + // Target coords. + constexpr int TARGET_WIDTH = 720; + constexpr int TARGET_HEIGHT = 256; + constexpr int COORD_BAR_X = 296; constexpr int COORD_BAR_Y = 437; @@ -25,57 +32,71 @@ namespace // ---- Construction ---- ProgressState::ProgressState(sys::threadpool::JobFunction function, sys::Task::TaskData taskData) - : m_transition(0, 0, 32, 32, 0, 0, graphics::SCREEN_HEIGHT, 256, ui::Transition::DEFAULT_THRESHOLD) + : m_job(function) + , m_taskData(taskData) + , m_transition(0, + 0, + INITIAL_WIDTH_HEIGHT, + INITIAL_WIDTH_HEIGHT, + 0, + 0, + TARGET_WIDTH, + TARGET_HEIGHT, + ui::Transition::DEFAULT_THRESHOLD) { initialize_static_members(); - m_task = std::make_unique(function, taskData); } // ---- Public functions ---- void ProgressState::update() { + // These are always updated and aren't conditional. BaseTask::update_loading_glyph(); BaseTask::pop_on_plus(); - m_transition.update(); - sm_dialog->set_from_transition(m_transition, true); - if (!m_transition.in_place()) { return; } - sys::ProgressTask *task = static_cast(m_task.get()); - const double current = task->get_progress(); - - const bool isRunning = m_task->is_running(); - if (!isRunning && !m_close) { ProgressState::close_dialog(); } - else if (m_close && m_transition.in_place()) { ProgressState::deactivate_state(); } - - m_progressBarWidth = std::round(SIZE_BAR_WIDTH * current); - m_progress = std::round(current * 100); - m_percentageString = stringutil::get_formatted_string("%u%%", m_progress); - const int percentageWidth = sdl::text::get_width(BaseTask::FONT_SIZE, m_percentageString); - m_percentageX = COORD_DISPLAY_CENTER - (percentageWidth / 2); + switch (m_state) + { + case State::Opening: ProgressState::update_dimensions(); break; + case State::Running: ProgressState::update_progress(); break; + case State::Closing: ProgressState::update_dimensions(); break; + } } void ProgressState::render() { static constexpr int RIGHT_EDGE_X = (COORD_BAR_X + SIZE_BAR_WIDTH) - 16; - const bool hasFocus = BaseState::has_focus(); + // Grab the focus since everything wants it. + const bool hasFocus = BaseState::has_focus(); + + // This will dim the background. sdl::render_rect_fill(sdl::Texture::Null, 0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); - sm_dialog->render(sdl::Texture::Null, hasFocus); - BaseTask::render_loading_glyph(); - if (!m_transition.in_place()) { return; } - const int barWidth = static_cast(SIZE_BAR_WIDTH); + // Render the glyph and dialog. Don't render anything else unless the task is running. + BaseTask::render_loading_glyph(); + sm_dialog->render(sdl::Texture::Null, hasFocus); + if (m_state != State::Running) { return; } + + // This just makes this easier to work with. + const int barWidth = static_cast(SIZE_BAR_WIDTH); + + // Grab and render the status. const std::string status = m_task->get_status(); sdl::text::render(sdl::Texture::Null, 312, 255, BaseTask::FONT_SIZE, 656, colors::WHITE, status); + // This is the divider line. sdl::render_line(sdl::Texture::Null, 280, 421, 999, 421, colors::DIV_COLOR); + + // Progress showing bar. sdl::render_rect_fill(sdl::Texture::Null, COORD_BAR_X, COORD_BAR_Y, barWidth, 32, colors::BLACK); sdl::render_rect_fill(sdl::Texture::Null, COORD_BAR_X, COORD_BAR_Y, m_progressBarWidth, 32, colors::BAR_GREEN); + // These are the "caps" to round the edges of the bar. sm_barEdges->render_part(sdl::Texture::Null, COORD_BAR_X, COORD_BAR_Y, 0, 0, 16, 32); sm_barEdges->render_part(sdl::Texture::Null, RIGHT_EDGE_X, COORD_BAR_Y, 16, 0, 16, 32); + // Progress string. sdl::text::render(sdl::Texture::Null, m_percentageX, COORD_TEXT_Y, @@ -95,15 +116,56 @@ void ProgressState::initialize_static_members() sm_dialog = ui::DialogBox::create(0, 0, 0, 0); sm_barEdges = sdl::TextureManager::load(BAR_EDGE_NAME, "romfs:/Textures/BarEdges.png"); - sm_dialog->set_from_transition(m_transition, true); } +void ProgressState::update_dimensions() noexcept +{ + // Update the transition and dialog. + m_transition.update(); + sm_dialog->set_from_transition(m_transition, true); + + // State shifting. + const bool opened = m_state == State::Opening && m_transition.in_place(); + const bool closed = m_state == State::Closing && m_transition.in_place(); + if (opened) + { + // Gonna start the task here. Sometimes JKSV is too quick and it looks like nothing happened otherwise. + m_task = std::make_unique(m_job, m_taskData); + m_state = State::Running; + } + else if (closed) { ProgressState::deactivate_state(); } +} + +void ProgressState::update_progress() noexcept +{ + // Cast pointer to the task. To do: Consider dynamic for this... + auto *task = static_cast(m_task.get()); + + // Current progress. + const double current = task->get_progress(); + + // Update the width and actual progress. + m_progressBarWidth = std::round(SIZE_BAR_WIDTH * current); + m_progress = std::round(current * 100); + + // This is the actual string that's displayed. + m_percentageString = stringutil::get_formatted_string("%u%%", m_progress); + + // Center the string above. + const int stringWidth = sdl::text::get_width(BaseTask::FONT_SIZE, m_percentageString); + m_percentageX = COORD_DISPLAY_CENTER - (stringWidth / 2); + + // Handle closing and updating. + const bool taskRunning = m_task->is_running(); + if (!taskRunning && m_state != State::Closing) { ProgressState::close_dialog(); } +} + void ProgressState::close_dialog() { + m_state = State::Closing; m_transition.set_target_width(32); m_transition.set_target_height(32); - m_close = true; } void ProgressState::deactivate_state()