From c762dafc670c3c49b0d98ac0b3eb64486bf8cde2 Mon Sep 17 00:00:00 2001 From: ITotalJustice <47043333+ITotalJustice@users.noreply.github.com> Date: Sun, 12 Jan 2025 23:16:12 +0000 Subject: [PATCH] add text scrolling to sidebar array see #87 --- sphaira/include/app.hpp | 4 ++ sphaira/include/ui/sidebar.hpp | 4 ++ sphaira/source/app.cpp | 8 ++++ sphaira/source/ui/menus/main_menu.cpp | 9 ++++ sphaira/source/ui/sidebar.cpp | 64 +++++++++++++++++++++++++-- 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/sphaira/include/app.hpp b/sphaira/include/app.hpp index b006cef..12c0924 100644 --- a/sphaira/include/app.hpp +++ b/sphaira/include/app.hpp @@ -80,6 +80,7 @@ class App { static auto GetThemeShuffleEnable() -> bool; static auto GetThemeMusicEnable() -> bool; static auto GetLanguage() -> long; + static auto GetTextScrollSpeed() -> long; static void SetMtpEnable(bool enable); static void SetFtpEnable(bool enable); @@ -92,6 +93,7 @@ class App { static void SetThemeShuffleEnable(bool enable); static void SetThemeMusicEnable(bool enable); static void SetLanguage(long index); + static void SetTextScrollSpeed(long index); static auto Install(OwoConfig& config) -> Result; static auto Install(ui::ProgressBox* pbox, OwoConfig& config) -> Result; @@ -161,6 +163,8 @@ class App { option::OptionBool m_theme_shuffle{INI_SECTION, "theme_shuffle", false}; option::OptionBool m_theme_music{INI_SECTION, "theme_music", true}; option::OptionLong m_language{INI_SECTION, "language", 0}; // auto + // todo: move this into it's own menu + option::OptionLong m_text_scroll_speed{"accessibility", "text_scroll_speed", 1}; // normal PLSR_BFSAR m_qlaunch_bfsar{}; PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{}; diff --git a/sphaira/include/ui/sidebar.hpp b/sphaira/include/ui/sidebar.hpp index 3489c7d..ef98928 100644 --- a/sphaira/include/ui/sidebar.hpp +++ b/sphaira/include/ui/sidebar.hpp @@ -57,12 +57,16 @@ class SidebarEntryArray final : public SidebarEntryBase { SidebarEntryArray(std::string title, Items items, std::string& index); auto Draw(NVGcontext* vg, Theme* theme) -> void override; + auto OnFocusGained() noexcept -> void override; + auto OnFocusLost() noexcept -> void override; private: Items m_items; ListCallback m_list_callback; Callback m_callback; s64 m_index; + s64 m_tick{}; + float m_text_yoff{}; }; template diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index df7a402..9ff3bf2 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -585,6 +585,10 @@ auto App::GetLanguage() -> long { return g_app->m_language.Get(); } +auto App::GetTextScrollSpeed() -> long { + return g_app->m_text_scroll_speed.Get(); +} + void App::SetNxlinkEnable(bool enable) { if (App::GetNxlinkEnable() != enable) { g_app->m_nxlink_enabled.Set(enable); @@ -767,6 +771,10 @@ void App::SetLanguage(long index) { } } +void App::SetTextScrollSpeed(long index) { + g_app->m_text_scroll_speed.Set(index); +} + auto App::Install(OwoConfig& config) -> Result { R_TRY(romfsInit()); ON_SCOPE_EXIT(romfsExit()); diff --git a/sphaira/source/ui/menus/main_menu.cpp b/sphaira/source/ui/menus/main_menu.cpp index bbd3ede..76c9084 100644 --- a/sphaira/source/ui/menus/main_menu.cpp +++ b/sphaira/source/ui/menus/main_menu.cpp @@ -329,6 +329,11 @@ MainMenu::MainMenu() { install_items.push_back("System memory"_i18n); install_items.push_back("microSD card"_i18n); + SidebarEntryArray::Items text_scroll_speed_items; + text_scroll_speed_items.push_back("Slow"_i18n); + text_scroll_speed_items.push_back("Normal"_i18n); + text_scroll_speed_items.push_back("Fast"_i18n); + options->Add(std::make_shared("Logging"_i18n, App::GetLogEnable(), [this](bool& enable){ App::SetLogEnable(enable); }, "Enabled"_i18n, "Disabled"_i18n)); @@ -348,6 +353,10 @@ MainMenu::MainMenu() { options->Add(std::make_shared("Show install warning"_i18n, App::GetInstallPrompt(), [this](bool& enable){ App::SetInstallPrompt(enable); }, "Enabled"_i18n, "Disabled"_i18n)); + + options->Add(std::make_shared("Text scroll speed"_i18n, text_scroll_speed_items, [this](s64& index_out){ + App::SetTextScrollSpeed(index_out); + }, (s64)App::GetTextScrollSpeed())); })); }}) ); diff --git a/sphaira/source/ui/sidebar.cpp b/sphaira/source/ui/sidebar.cpp index 74f8dc0..d824400 100644 --- a/sphaira/source/ui/sidebar.cpp +++ b/sphaira/source/ui/sidebar.cpp @@ -7,6 +7,14 @@ namespace sphaira::ui { namespace { +auto GetTextScrollSpeed() -> float { + switch (App::GetTextScrollSpeed()) { + case 0: return 0.5; + default: case 1: return 1.0; + case 2: return 1.5; + } +} + auto DistanceBetweenY(Vec4 va, Vec4 vb) -> Vec4 { return Vec4{ va.x, va.y, @@ -147,11 +155,59 @@ auto SidebarEntryArray::Draw(NVGcontext* vg, Theme* theme) -> void { SidebarEntryBase::Draw(vg, theme); const auto& text_entry = m_items[m_index]; - // const auto& colour = HasFocus() ? theme->GetColour(ThemeEntryID_TEXT_SELECTED) : theme->GetColour(ThemeEntryID_TEXT); - const auto& colour = theme->GetColour(ThemeEntryID_TEXT); - gfx::drawText(vg, Vec2{m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f)}, 20.f, colour, m_title.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); - gfx::drawText(vg, Vec2{m_pos.x + m_pos.w - 15.f, m_pos.y + (m_pos.h / 2.f)}, 20.f, theme->GetColour(ThemeEntryID_TEXT_SELECTED), text_entry.c_str(), NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE); + // scrolling text + // todo: move below in a flexible class and use it for all text drawing. + float bounds[4]; + nvgFontSize(vg, 20); + nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); + nvgTextBounds(vg, 0, 0, m_title.c_str(), nullptr, bounds); + const float start_x = bounds[2] + 50; + const float max_off = m_pos.w - start_x - 15.f; + + auto value_str = m_items[m_index]; + nvgTextBounds(vg, 0, 0, value_str.c_str(), nullptr, bounds); + + if (HasFocus()) { + const auto scroll_amount = GetTextScrollSpeed(); + if (bounds[2] > max_off) { + value_str += " "; + nvgTextBounds(vg, 0, 0, value_str.c_str(), nullptr, bounds); + + if (!m_text_yoff) { + m_tick++; + if (m_tick >= 90) { + m_tick = 0; + m_text_yoff += scroll_amount; + } + } else if (bounds[2] > m_text_yoff) { + m_text_yoff += std::min(scroll_amount, bounds[2] - m_text_yoff); + } else { + m_text_yoff = 0; + } + + value_str += text_entry; + } + } + + const Vec2 key_text_pos{m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f)}; + gfx::drawText(vg, key_text_pos, 20.f, theme->GetColour(ThemeEntryID_TEXT), m_title.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); + + nvgSave(vg); + const float xpos = m_pos.x + m_pos.w - 15.f - std::min(max_off, bounds[2]); + nvgIntersectScissor(vg, xpos, GetY(), max_off, GetH()); + const Vec2 value_text_pos{xpos - m_text_yoff, m_pos.y + (m_pos.h / 2.f)}; + gfx::drawText(vg, value_text_pos, 20.f, theme->GetColour(ThemeEntryID_TEXT_SELECTED), value_str.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); + nvgRestore(vg); +} + +auto SidebarEntryArray::OnFocusGained() noexcept -> void { + Widget::OnFocusGained(); +} + +auto SidebarEntryArray::OnFocusLost() noexcept -> void { + Widget::OnFocusLost(); + m_text_yoff = 0; } Sidebar::Sidebar(std::string title, Side side, Items&& items)