use spinner instead of default icon for homebrew + games menus.

more menus will use it soon.
need to have a way to show the spinner when loading, then revert to the default icon if
failed to load, or an icon doesn't exist.

otherwise, the user may think that the icon is still loading and wait for it.
This commit is contained in:
ITotalJustice
2025-09-21 04:08:36 +01:00
parent 3c504cc85d
commit a772d660f3
4 changed files with 48 additions and 32 deletions

View File

@@ -42,6 +42,8 @@ void drawScrollbar2(NVGcontext*, const Theme*, s64 index_off, s64 count, s64 row
void drawAppLable(NVGcontext* vg, const Theme*, ScrollingText& st, float x, float y, float w, const char* name); void drawAppLable(NVGcontext* vg, const Theme*, ScrollingText& st, float x, float y, float w, const char* name);
void drawSpinner(NVGcontext* vg, const Theme*, float cx, float cy, float r, float t);
void updateHighlightAnimation(); void updateHighlightAnimation();
void getHighlightAnimation(float* gradientX, float* gradientY, float* color); void getHighlightAnimation(float* gradientX, float* gradientY, float* color);

View File

@@ -2,6 +2,8 @@
#include "ui/menus/grid_menu_base.hpp" #include "ui/menus/grid_menu_base.hpp"
#include "ui/nvg_util.hpp" #include "ui/nvg_util.hpp"
#include <cmath>
namespace sphaira::ui::menu::grid { namespace sphaira::ui::menu::grid {
void Menu::DrawEntry(NVGcontext* vg, Theme* theme, int layout, const Vec4& v, bool selected, int image, const char* name, const char* author, const char* version) { void Menu::DrawEntry(NVGcontext* vg, Theme* theme, int layout, const Vec4& v, bool selected, int image, const char* name, const char* author, const char* version) {
@@ -45,7 +47,19 @@ Vec4 Menu::DrawEntry(NVGcontext* vg, Theme* theme, bool draw_image, int layout,
} }
if (draw_image) { if (draw_image) {
gfx::drawImage(vg, image_v, image ?: App::GetDefaultImage(), 5); if (image > 0) {
gfx::drawImage(vg, image_v, image, 5);
} else {
// https://www.mathopenref.com/arcradius.html
auto spinner = image_v;
spinner.w /= 2;
spinner.h /= 2;
spinner.x += (image_v.w / 2);
spinner.y += (image_v.h / 2);
const auto rad = (spinner.h / 2) + (std::powf(spinner.w, 2) / (spinner.h * 8));
gfx::drawSpinner(vg, theme, spinner.x, spinner.y, rad, armTicksToNs(armGetSystemTick()) / 1e+9);
}
} }
return image_v; return image_v;

View File

@@ -387,6 +387,36 @@ void drawAppLable(NVGcontext* vg, const Theme* theme, ScrollingText& st, float x
st.Draw(vg, true, text_x, text_y, box_w - text_pad * 2, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT_SELECTED), name); st.Draw(vg, true, text_x, text_y, box_w - text_pad * 2, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT_SELECTED), name);
} }
// https://github.com/memononen/nanovg/blob/f93799c078fa11ed61c078c65a53914c8782c00b/example/demo.c#L500
void drawSpinner(NVGcontext* vg, const Theme* theme, float cx, float cy, float r, float t)
{
float a0 = 0.0f + t*6;
float a1 = NVG_PI + t*6;
float r0 = r;
float r1 = r * 0.75f;
float ax,ay, bx,by;
NVGpaint paint;
nvgSave(vg);
auto colourb = theme->GetColour(ThemeEntryID_PROGRESSBAR);
colourb.a = 0.5;
nvgBeginPath(vg);
nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW);
nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW);
nvgClosePath(vg);
ax = cx + cosf(a0) * (r0+r1)*0.5f;
ay = cy + sinf(a0) * (r0+r1)*0.5f;
bx = cx + cosf(a1) * (r0+r1)*0.5f;
by = cy + sinf(a1) * (r0+r1)*0.5f;
paint = nvgLinearGradient(vg, ax,ay, bx,by, nvgRGBA(0,0,0,0), colourb);
nvgFillPaint(vg, paint);
nvgFill(vg);
nvgRestore(vg);
}
#define HIGHLIGHT_SPEED 350.0 #define HIGHLIGHT_SPEED 350.0
static double highlightGradientX = 0; static double highlightGradientX = 0;

View File

@@ -21,36 +21,6 @@ void threadFunc(void* arg) {
d->pbox->RequestExit(); d->pbox->RequestExit();
} }
// https://github.com/memononen/nanovg/blob/f93799c078fa11ed61c078c65a53914c8782c00b/example/demo.c#L500
void drawSpinner(NVGcontext* vg, Theme* theme, float cx, float cy, float r, float t)
{
float a0 = 0.0f + t*6;
float a1 = NVG_PI + t*6;
float r0 = r;
float r1 = r * 0.75f;
float ax,ay, bx,by;
NVGpaint paint;
nvgSave(vg);
auto colourb = theme->GetColour(ThemeEntryID_PROGRESSBAR);
colourb.a = 0.5;
nvgBeginPath(vg);
nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW);
nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW);
nvgClosePath(vg);
ax = cx + cosf(a0) * (r0+r1)*0.5f;
ay = cy + sinf(a0) * (r0+r1)*0.5f;
bx = cx + cosf(a1) * (r0+r1)*0.5f;
by = cy + sinf(a1) * (r0+r1)*0.5f;
paint = nvgLinearGradient(vg, ax,ay, bx,by, nvgRGBA(0,0,0,0), colourb);
nvgFillPaint(vg, paint);
nvgFill(vg);
nvgRestore(vg);
}
} // namespace } // namespace
ProgressBox::ProgressBox(int image, const std::string& action, const std::string& title, const ProgressBoxCallback& callback, const ProgressBoxDoneCallback& done) ProgressBox::ProgressBox(int image, const std::string& action, const std::string& title, const ProgressBoxCallback& callback, const ProgressBoxDoneCallback& done)
@@ -180,7 +150,7 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
gfx::drawTextArgs(vg, prog_bar.x + prog_bar.w + pad, prog_bar.y + prog_bar.h / 2, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT), "%u%%", percentage); gfx::drawTextArgs(vg, prog_bar.x + prog_bar.w + pad, prog_bar.y + prog_bar.h / 2, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT), "%u%%", percentage);
const auto rad = 15; const auto rad = 15;
drawSpinner(vg, theme, prog_bar.x - pad - rad, prog_bar.y + prog_bar.h / 2, rad, armTicksToNs(armGetSystemTick()) / 1e+9); gfx::drawSpinner(vg, theme, prog_bar.x - pad - rad, prog_bar.y + prog_bar.h / 2, rad, armTicksToNs(armGetSystemTick()) / 1e+9);
const double speed_mb = (double)speed / (1024.0 * 1024.0); const double speed_mb = (double)speed / (1024.0 * 1024.0);
const double speed_kb = (double)speed / (1024.0); const double speed_kb = (double)speed / (1024.0);