Reimplement Wii loading glyph for tasks.

This commit is contained in:
J-D-K
2025-06-13 11:23:59 -04:00
parent 5c0333c704
commit 5cab0d505b
10 changed files with 111 additions and 20 deletions

View File

@@ -0,0 +1,42 @@
#pragma once
#include "appstates/AppState.hpp"
#include "system/Timer.hpp"
#include "ui/ColorMod.hpp"
#include <array>
#include <string_view>
/// @brief Normally, I wouldn't do this, but this holds a single function both TaskState and ProgressState share...
class BaseTask : public AppState
{
public:
/// @brief Constructor. Starts the glyph timer and sets AppState to not allow closing.
BaseTask(void);
/// @brief Virtual destructor.
virtual ~BaseTask() {};
/// @brief Runs the update routine for rendering the loading glyph animation.
/// @param
void update(void) override;
/// @brief Virtual render function.
virtual void render(void) = 0;
/// @brief This function renders the loading glyph in the bottom left corner.
/// @note This is mostly just so users don't think JKSV has frozen when operations take a long time.
void render_loading_glyph(void);
private:
/// @brief This is the current frame of the loading glyph animation.
int m_currentFrame = 0;
/// @brief This is the timer used for changing the current glyph/frame of the animation.
sys::Timer m_frameTimer;
/// @brief This is used to give the animation its pulsing color.
ui::ColorMod m_colorMod;
/// @brief This array holds the glyphs of the loading sequence. I think it's from the Wii?
static inline std::array<std::string_view, 8> sm_glyphArray =
{"\ue020", "\ue021", "\ue022", "\ue023", "\ue024", "\ue025", "\ue026", "\ue027"};
};

View File

@@ -1,11 +1,11 @@
#pragma once
#include "appstates/AppState.hpp"
#include "appstates/BaseTask.hpp"
#include "system/ProgressTask.hpp"
#include <string>
#include <switch.h>
/// @brief State that shows progress of a task.
class ProgressState : public AppState
class ProgressState : public BaseTask
{
public:
/// @brief Constructs a new ProgressState.
@@ -14,7 +14,7 @@ class ProgressState : public AppState
/// @note All functions passed to this must follow this signature: void function(sys::ProgressTask *, <arguments>)
template <typename... Args>
ProgressState(void (*function)(sys::ProgressTask *, Args...), Args... args)
: AppState(false), m_task(function, std::forward<Args>(args)...){};
: BaseTask(), m_task(function, std::forward<Args>(args)...){};
/// @brief Required destructor.
~ProgressState() {};

View File

@@ -1,10 +1,10 @@
#pragma once
#include "appstates/AppState.hpp"
#include "appstates/BaseTask.hpp"
#include "system/Task.hpp"
#include <switch.h>
/// @brief State that spawns a task and allows updates to be printed to screen.
class TaskState : public AppState
class TaskState : public BaseTask
{
public:
/// @brief Constructs and spawns a new TaskState.
@@ -13,7 +13,7 @@ class TaskState : public AppState
/// @note All functions passed must follow this signature: void function(sys::Task *, <arguments>)
template <typename... Args>
TaskState(void (*function)(sys::Task *, Args...), Args... args)
: AppState(false), m_task(function, std::forward<Args>(args)...){};
: BaseTask(), m_task(function, std::forward<Args>(args)...){};
/// @brief Required destructor.
~TaskState() {};

View File

@@ -1,4 +1,5 @@
#pragma once
#include "sdl.hpp"
#include <cstdint>
namespace ui
@@ -13,12 +14,14 @@ namespace ui
/// @brief Updates the color modification variable.
void update(void);
/// @brief Allows me to use this like it's a uint8_t directly.
operator uint8_t(void) const;
/// @brief Operator that allows using this as an sdl::Color directly.
/// @note Since all of these pulse the same color, no sense in not doing this.
operator sdl::Color(void) const;
private:
/// @brief Whether we're adding or subtracting from the color value.
bool m_direction = true;
/// @brief Color value.
uint8_t m_colorMod = 0;
};

View File

@@ -1,5 +1,6 @@
#pragma once
#include "sdl.hpp"
#include "ui/ColorMod.hpp"
// These are just functions to render generic parts of the UI.
namespace ui
@@ -19,5 +20,5 @@ namespace ui
/// @param width Width of dialog box in pixels.
/// @param height Height of dialog box in pixels.
/// @param colorMod Color to multiply in rendering.
void render_bounding_box(SDL_Texture *target, int x, int y, int width, int height, uint8_t colorMod);
void render_bounding_box(SDL_Texture *target, int x, int y, int width, int height, const ui::ColorMod &colorMod);
} // namespace ui

View File

@@ -0,0 +1,37 @@
#include "appstates/BaseTask.hpp"
#include "colors.hpp"
namespace
{
/// @brief This is the time in milliseconds between changing glyphs.
constexpr uint64_t TICKS_GLYPH_TRIGGER = 50;
} // namespace
BaseTask::BaseTask(void) : AppState(false)
{
m_frameTimer.start(TICKS_GLYPH_TRIGGER);
}
void BaseTask::update(void)
{
// Just bail if the timer wasn't triggered yet.
if (!m_frameTimer.is_triggered())
{
return;
}
// Reset to 0 here.
if (++m_currentFrame >= 8)
{
m_currentFrame = 0;
}
// Update the color pulse.
m_colorMod.update();
}
void BaseTask::render_loading_glyph(void)
{
// This assumes it's being called after the background was dimmed.
sdl::text::render(NULL, 56, 673, 32, sdl::text::NO_TEXT_WRAP, m_colorMod, sm_glyphArray.at(m_currentFrame).data());
}

View File

@@ -10,6 +10,9 @@
void ProgressState::update(void)
{
// Base routine.
BaseTask::update();
if (m_task.is_running() && input::button_pressed(HidNpadButton_Plus))
{
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
@@ -44,4 +47,7 @@ void ProgressState::render(void)
colors::WHITE,
"%s%%",
m_percentageString.c_str());
// Glyph in the corner.
BaseTask::render_loading_glyph();
}

View File

@@ -7,6 +7,9 @@
void TaskState::update(void)
{
// Run the base update routine.
BaseTask::update();
if (m_task.is_running() && input::button_pressed(HidNpadButton_Plus))
{
// Throw the message.
@@ -29,4 +32,6 @@ void TaskState::render(void)
sdl::render_rect_fill(NULL, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
// Render the status.
sdl::text::render(NULL, statusX, 351, 24, sdl::text::NO_TEXT_WRAP, colors::WHITE, status.c_str());
// Render the loading glyph
BaseTask::render_loading_glyph();
}

View File

@@ -12,7 +12,7 @@ void ui::ColorMod::update(void)
}
}
ui::ColorMod::operator uint8_t(void) const
ui::ColorMod::operator sdl::Color(void) const
{
return m_colorMod;
return {static_cast<uint32_t>((0x88 + m_colorMod) << 16 | (0xC5 + (m_colorMod / 2)) << 8 | 0xFF)};
}

View File

@@ -27,7 +27,7 @@ void ui::render_dialog_box(SDL_Texture *target, int x, int y, int width, int hei
s_dialogCorners->render_part(NULL, (x + width) - 16, (y + height) - 16, 16, 16, 16, 16);
}
void ui::render_bounding_box(SDL_Texture *target, int x, int y, int width, int height, uint8_t colorMod)
void ui::render_bounding_box(SDL_Texture *target, int x, int y, int width, int height, const ui::ColorMod &colorMod)
{
if (!s_menuBoundingCorners)
{
@@ -35,21 +35,18 @@ void ui::render_bounding_box(SDL_Texture *target, int x, int y, int width, int h
sdl::TextureManager::create_load_texture("MenuBoundingCorners", "romfs:/Textures/MenuBounding.png");
}
// Setup color.
sdl::Color renderMod = {static_cast<uint32_t>((0x88 + colorMod) << 16 | (0xC5 + (colorMod / 2)) << 8 | 0xFF)};
// This shouldn't fail, but I don't really care if it does.
s_menuBoundingCorners->set_color_mod(renderMod);
s_menuBoundingCorners->set_color_mod(colorMod);
// Top
s_menuBoundingCorners->render_part(target, x, y, 0, 0, 8, 8);
sdl::render_rect_fill(target, x + 8, y, width - 16, 4, renderMod);
sdl::render_rect_fill(target, x + 8, y, width - 16, 4, colorMod);
s_menuBoundingCorners->render_part(target, (x + width) - 8, y, 8, 0, 8, 8);
// Middle
sdl::render_rect_fill(target, x, y + 8, 4, height - 16, renderMod);
sdl::render_rect_fill(target, (x + width) - 4, y + 8, 4, height - 16, renderMod);
sdl::render_rect_fill(target, x, y + 8, 4, height - 16, colorMod);
sdl::render_rect_fill(target, (x + width) - 4, y + 8, 4, height - 16, colorMod);
// Bottom
s_menuBoundingCorners->render_part(target, x, (y + height) - 8, 0, 8, 8, 8);
sdl::render_rect_fill(target, x + 8, (y + height) - 4, width - 16, 4, renderMod);
sdl::render_rect_fill(target, x + 8, (y + height) - 4, width - 16, 4, colorMod);
s_menuBoundingCorners->render_part(target, (x + width) - 8, (y + height) - 8, 8, 8, 8, 8);
}