diff --git a/sphaira/include/app.hpp b/sphaira/include/app.hpp index a648816..5ca9649 100644 --- a/sphaira/include/app.hpp +++ b/sphaira/include/app.hpp @@ -339,6 +339,10 @@ public: double m_delta_time{}; + static constexpr const char* INSTALL_DEPENDS_STR = + "Installing is disabled.\n\n" + "Enable in the options by selecting Menu (Y) -> Advanced -> Install options -> Enable."; + private: // from nanovg decko3d example by adubbz static constexpr unsigned NumFramebuffers = 2; static constexpr unsigned StaticCmdSize = 0x1000; diff --git a/sphaira/include/ui/sidebar.hpp b/sphaira/include/ui/sidebar.hpp index 7895573..656fe10 100644 --- a/sphaira/include/ui/sidebar.hpp +++ b/sphaira/include/ui/sidebar.hpp @@ -11,17 +11,46 @@ namespace sphaira::ui { class SidebarEntryBase : public Widget { +public: + using DependsCallback = std::function; + public: explicit SidebarEntryBase(const std::string& title, const std::string& info); using Widget::Draw; virtual void Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left); + void Depends(const DependsCallback& callback, const std::string& depends_info) { + m_depends_callback = callback; + m_depends_info = depends_info; + } + + void Depends(bool& value, const std::string& depends_info) { + m_depends_callback = [&value](){ return value; }; + m_depends_info = depends_info; + } + + void Depends(option::OptionBool& value, const std::string& depends_info) { + m_depends_callback = [&value](){ return value.Get(); }; + m_depends_info = depends_info; + } + +protected: + auto IsEnabled() -> bool { + if (m_depends_callback) { + return m_depends_callback(); + } + + return true; + } + protected: std::string m_title; private: - std::string m_info; + std::string m_info{}; + std::string m_depends_info{}; + DependsCallback m_depends_callback{}; ScrollingText m_scolling_title{}; }; @@ -101,11 +130,11 @@ public: auto OnFocusGained() noexcept -> void override; auto OnFocusLost() noexcept -> void override; - void Add(std::unique_ptr&& entry); + auto Add(std::unique_ptr&& entry) -> SidebarEntryBase*; template - void Add(Args&&... args) { - Add(std::make_unique(std::forward(args)...)); + auto Add(Args&&... args) -> SidebarEntryBase* { + return Add(std::make_unique(std::forward(args)...)); } private: diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index 3fa6b6a..e979d7b 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -1658,13 +1658,15 @@ void App::DisplayMiscOptions(bool left_side) { continue; } else if (e.name == g_app->m_right_menu.Get()) { continue; - } else if (e.IsInstall() && !App::GetInstallEnable()) { - continue; } - options->Add(i18n::get(e.title), [e](){ + auto entry = options->Add(i18n::get(e.title), [e](){ App::Push(e.func(ui::menu::MenuFlag_None)); }, i18n::get(e.info)); + + if (e.IsInstall()) { + entry->Depends(App::GetInstallEnable, i18n::get(App::INSTALL_DEPENDS_STR)); + } } if (App::IsApplication()) { @@ -1714,10 +1716,6 @@ void App::DisplayAdvancedOptions(bool left_side) { continue; } - if (e.IsInstall() && !App::GetInstallEnable()) { - continue; - } - menu_names.emplace_back(e.name); menu_items.push_back(i18n::get(e.name)); } diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index 1e17455..549a602 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -359,7 +359,7 @@ FsView::FsView(Menu* menu, const fs::FsPath& path, const FsEntry& entry, ViewSid nro_launch(GetNewPathCurrent()); } }); - } else if (App::GetInstallEnable() && IsExtension(entry.GetExtension(), INSTALL_EXTENSIONS)) { + } else if (IsExtension(entry.GetExtension(), INSTALL_EXTENSIONS)) { InstallFiles(); } else if (IsSd()) { const auto assoc_list = m_menu->FindFileAssocFor(); @@ -703,6 +703,16 @@ void FsView::InstallForwarder() { } void FsView::InstallFiles() { + if (!App::GetInstallEnable()) { + App::Push( + "Install disabled...\n" + "Please enable installing via the install options."_i18n, + "OK"_i18n + ); + + return; + } + const auto targets = GetSelectedEntries(); App::Push("Install Selected files?"_i18n, "No"_i18n, "Yes"_i18n, 0, [this, targets](auto op_index){ @@ -1674,17 +1684,18 @@ void FsView::DisplayOptions() { }; // if install is enabled, check if all currently selected files are installable. - if (m_entries_current.size() && App::GetInstallEnable()) { + if (m_entries_current.size()) { if (check_all_ext(INSTALL_EXTENSIONS)) { - options->Add("Install"_i18n, [this](){ + auto entry = options->Add("Install"_i18n, [this](){ InstallFiles(); }); + entry->Depends(App::GetInstallEnable, i18n::get(App::INSTALL_DEPENDS_STR)); } } if (IsSd() && m_entries_current.size() && !m_selected_count) { - if (App::GetInstallEnable() && GetEntry().IsFile() && (IsSamePath(GetEntry().GetExtension(), "nro") || !m_menu->FindFileAssocFor().empty())) { - options->Add("Install Forwarder"_i18n, [this](){; + if (GetEntry().IsFile() && (IsSamePath(GetEntry().GetExtension(), "nro") || !m_menu->FindFileAssocFor().empty())) { + auto entry = options->Add("Install Forwarder"_i18n, [this](){; if (App::GetInstallPrompt()) { App::Push( "WARNING: Installing forwarders will lead to a ban!"_i18n, @@ -1698,6 +1709,7 @@ void FsView::DisplayOptions() { InstallForwarder(); } }); + entry->Depends(App::GetInstallEnable, i18n::get(App::INSTALL_DEPENDS_STR)); } } diff --git a/sphaira/source/ui/menus/homebrew.cpp b/sphaira/source/ui/menus/homebrew.cpp index 3e6ee89..d5487ed 100644 --- a/sphaira/source/ui/menus/homebrew.cpp +++ b/sphaira/source/ui/menus/homebrew.cpp @@ -123,22 +123,22 @@ Menu::Menu() : grid::Menu{"Homebrew"_i18n, MenuFlag_Tab} { ); }, true); - if (App::GetInstallEnable()) { - options->Add("Install Forwarder"_i18n, [this](){ - if (App::GetInstallPrompt()) { - App::Push( - "WARNING: Installing forwarders will lead to a ban!"_i18n, - "Back"_i18n, "Install"_i18n, 0, [this](auto op_index){ - if (op_index && *op_index) { - InstallHomebrew(); - } - }, m_entries[m_index].image - ); - } else { - InstallHomebrew(); - } - }, true); - } + auto forwarder_entry = options->Add("Install Forwarder"_i18n, [this](){ + if (App::GetInstallPrompt()) { + App::Push( + "WARNING: Installing forwarders will lead to a ban!"_i18n, + "Back"_i18n, "Install"_i18n, 0, [this](auto op_index){ + if (op_index && *op_index) { + InstallHomebrew(); + } + }, m_entries[m_index].image + ); + } else { + InstallHomebrew(); + } + }, true); + + forwarder_entry->Depends(App::GetInstallEnable, i18n::get(App::INSTALL_DEPENDS_STR)); } }}) ); diff --git a/sphaira/source/ui/sidebar.cpp b/sphaira/source/ui/sidebar.cpp index 36ebc66..e70a0f1 100644 --- a/sphaira/source/ui/sidebar.cpp +++ b/sphaira/source/ui/sidebar.cpp @@ -35,7 +35,9 @@ void SidebarEntryBase::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, if (HasFocus()) { gfx::drawRectOutline(vg, theme, 4.f, m_pos); - if (!m_info.empty()) { + const auto& info = IsEnabled() ? m_info : m_depends_info; + + if (!info.empty()) { // reset clip here as the box will draw oob. nvgSave(vg); nvgScissor(vg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); @@ -62,7 +64,7 @@ void SidebarEntryBase::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, nvgFontSize(vg, info_font_size); nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); nvgTextLineHeight(vg, 1.7); - nvgTextBoxBounds(vg, 0, 0, end_w, m_info.c_str(), nullptr, bounds); + nvgTextBoxBounds(vg, 0, 0, end_w, info.c_str(), nullptr, bounds); info_box.h = pad_after_title + info_pad * 2 + bounds[3] - bounds[1]; gfx::drawRect(vg, info_box, theme->GetColour(ThemeEntryID_SIDEBAR), 5); @@ -71,7 +73,7 @@ void SidebarEntryBase::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, m_scolling_title.Draw(vg, true, x, y, end_w, title_font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), m_title.c_str()); y += pad_after_title; - gfx::drawTextBox(vg, x, y, info_font_size, end_w, theme->GetColour(ThemeEntryID_TEXT), m_info.c_str()); + gfx::drawTextBox(vg, x, y, info_font_size, end_w, theme->GetColour(ThemeEntryID_TEXT), info.c_str()); } } } @@ -91,9 +93,10 @@ SidebarEntryBool::SidebarEntryBool(const std::string& title, bool option, Callba } SetAction(Button::A, Action{"OK"_i18n, [this](){ + if (IsEnabled()) { m_option ^= 1; m_callback(m_option); - } + } } }); } @@ -126,7 +129,8 @@ void SidebarEntryBool::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, // } else { // } - gfx::drawText(vg, Vec2{m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f)}, 20.f, theme->GetColour(ThemeEntryID_TEXT), m_title.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); + const auto colour_id = IsEnabled() ? ThemeEntryID_TEXT : ThemeEntryID_TEXT_INFO; + gfx::drawText(vg, Vec2{m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f)}, 20.f, theme->GetColour(colour_id), m_title.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); if (m_option == true) { 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), m_true_str.c_str(), NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE); @@ -140,11 +144,12 @@ SidebarEntryCallback::SidebarEntryCallback(const std::string& title, Callback cb , m_callback{cb} , m_pop_on_click{pop_on_click} { SetAction(Button::A, Action{"OK"_i18n, [this](){ + if (IsEnabled()) { m_callback(); if (m_pop_on_click) { SetPop(); } - } + }} }); } @@ -156,10 +161,11 @@ SidebarEntryCallback::SidebarEntryCallback(const std::string& title, Callback cb void SidebarEntryCallback::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) { SidebarEntryBase::Draw(vg, theme, root_pos, left); + const auto colour_id = IsEnabled() ? ThemeEntryID_TEXT : ThemeEntryID_TEXT_INFO; // if (HasFocus()) { // gfx::drawText(vg, Vec2{m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f)}, 20.f, theme->GetColour(ThemeEntryID_TEXT_SELECTED), m_title.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); // } else { - gfx::drawText(vg, Vec2{m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f)}, 20.f, theme->GetColour(ThemeEntryID_TEXT), m_title.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); + gfx::drawText(vg, Vec2{m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f)}, 20.f, theme->GetColour(colour_id), m_title.c_str(), NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); // } } @@ -205,15 +211,17 @@ SidebarEntryArray::SidebarEntryArray(const std::string& title, const Items& item }; SetAction(Button::A, Action{"OK"_i18n, [this](){ + if (IsEnabled()) { // m_callback(m_index); m_list_callback(); - } + }} }); } void SidebarEntryArray::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) { SidebarEntryBase::Draw(vg, theme, root_pos, left); + const auto colour_id = IsEnabled() ? ThemeEntryID_TEXT : ThemeEntryID_TEXT_INFO; const auto& text_entry = m_items[m_index]; // scrolling text @@ -251,7 +259,7 @@ void SidebarEntryArray::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, } 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); + gfx::drawText(vg, key_text_pos, 20.f, theme->GetColour(colour_id), 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]); @@ -389,15 +397,17 @@ auto Sidebar::OnFocusLost() noexcept -> void { SetHidden(true); } -void Sidebar::Add(std::unique_ptr&& entry) { - m_items.emplace_back(std::forward(entry)); - m_items.back()->SetPos(m_base_pos); +auto Sidebar::Add(std::unique_ptr&& _entry) -> SidebarEntryBase* { + auto& entry = m_items.emplace_back(std::forward(_entry)); + entry->SetPos(m_base_pos); // give focus to first entry. if (m_items.size() == 1) { - m_items[m_index]->OnFocusGained(); + entry->OnFocusGained(); SetupButtons(); } + + return entry.get(); } void Sidebar::SetIndex(s64 index) {