diff --git a/Include/AppStates/MainMenuState.hpp b/Include/AppStates/MainMenuState.hpp index 4b78069..c16b8d6 100644 --- a/Include/AppStates/MainMenuState.hpp +++ b/Include/AppStates/MainMenuState.hpp @@ -25,6 +25,8 @@ class MainMenuState : public AppState UI::IconMenu m_MainMenu; // Vector of pointers to users. std::vector m_Users; + // Vector of view states for each user, settings, and extras + std::vector> m_States; // Pointer to control guide string so I don't need to call and fetch it every loop. const char *m_ControlGuide = nullptr; // X coordinate to render controls at. diff --git a/Include/AppStates/SettingsState.hpp b/Include/AppStates/SettingsState.hpp new file mode 100644 index 0000000..8acc0c8 --- /dev/null +++ b/Include/AppStates/SettingsState.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "AppStates/AppState.hpp" +#include "SDL.hpp" +#include "UI/Menu.hpp" + +class SettingsState : public AppState +{ + public: + SettingsState(void); + ~SettingsState() {}; + + void Update(void); + void Render(void); + + private: + // Menu containing options. + UI::Menu m_SettingsMenu; + // Render target. + SDL::SharedTexture m_RenderTarget = nullptr; +}; diff --git a/Include/AppStates/TitleSelectState.hpp b/Include/AppStates/TitleSelectState.hpp new file mode 100644 index 0000000..e2648de --- /dev/null +++ b/Include/AppStates/TitleSelectState.hpp @@ -0,0 +1,25 @@ +#pragma once +#include "AppStates/AppState.hpp" +#include "Data/Data.hpp" +#include "SDL.hpp" +#include "UI/TitleView.hpp" + +class TitleSelectState : public AppState +{ + public: + TitleSelectState(Data::User *User); + ~TitleSelectState() {}; + + void Update(void); + void Render(void); + + private: + // Save pointer to user + Data::User *m_User = nullptr; + // Render target. + SDL::SharedTexture m_RenderTarget = nullptr; + // Title select view + UI::TitleView m_TitleView; + // X coordinate for control guide. Shared between all instances. Only should be calculated once. + static inline int m_TitleControlsX = 0; +}; diff --git a/Include/Colors.hpp b/Include/Colors.hpp index 9227edd..82949f2 100644 --- a/Include/Colors.hpp +++ b/Include/Colors.hpp @@ -10,6 +10,7 @@ namespace Colors static constexpr SDL::Color Blue = {0x0099EEFF}; static constexpr SDL::Color Yellow = {0xF8FC00FF}; static constexpr SDL::Color Pink = {0xFF4444FF}; + static constexpr SDL::Color BlueGreen = {0x00FFC5FF}; static constexpr SDL::Color ClearColor = {0x2D2D2DFF}; static constexpr SDL::Color DialogBox = {0x505050FF}; static constexpr SDL::Color BackgroundDim = {0x00000088}; diff --git a/Include/Strings.hpp b/Include/Strings.hpp index 68997e5..ffdd91b 100644 --- a/Include/Strings.hpp +++ b/Include/Strings.hpp @@ -14,5 +14,7 @@ namespace Strings static constexpr std::string_view ControlGuides = "ControlGuides"; static constexpr std::string_view SaveDataTypes = "SaveDataTypes"; static constexpr std::string_view MainMenuNames = "MainMenuNames"; + static constexpr std::string_view SettingsMenu = "SettingsMenu"; + static constexpr std::string_view OnOff = "OnOff"; } // namespace Names } // namespace Strings diff --git a/Include/UI/Element.hpp b/Include/UI/Element.hpp index 48b1b7b..0bdc53e 100644 --- a/Include/UI/Element.hpp +++ b/Include/UI/Element.hpp @@ -9,7 +9,7 @@ namespace UI Element(void) = default; virtual ~Element() {}; - virtual void Update(void) = 0; + virtual void Update(bool HasFocus) = 0; virtual void Render(SDL_Texture *Target, bool HasFocus) = 0; }; } // namespace UI diff --git a/Include/UI/IconMenu.hpp b/Include/UI/IconMenu.hpp index 46717f9..a0ba86c 100644 --- a/Include/UI/IconMenu.hpp +++ b/Include/UI/IconMenu.hpp @@ -10,7 +10,7 @@ namespace UI IconMenu(int X, int Y, int ScrollLength, int RenderTargetHeight); ~IconMenu() {}; - void Update(void); + void Update(bool HasFocus); void Render(SDL_Texture *Target, bool HasFocus); // Adds icon to menu. diff --git a/Include/UI/Menu.hpp b/Include/UI/Menu.hpp index 906211d..b20256f 100644 --- a/Include/UI/Menu.hpp +++ b/Include/UI/Menu.hpp @@ -15,7 +15,7 @@ namespace UI ~Menu() {}; // Required functions from Element. - void Update(void); + void Update(bool HasFocus); void Render(SDL_Texture *Target, bool HasFocus); // Adds an option to the menu. diff --git a/Include/UI/TitleTile.hpp b/Include/UI/TitleTile.hpp index 4d14ef4..5804124 100644 --- a/Include/UI/TitleTile.hpp +++ b/Include/UI/TitleTile.hpp @@ -11,6 +11,12 @@ namespace UI void Update(bool IsSelected); void Render(SDL_Texture *Target, int X, int Y); + // Resets width and height. + void Reset(void); + // Returns RenderWidth and RenderHeight + int GetWidth(void) const; + int GetHeight(void) const; + private: // Width and height so icon can "expand" when highlighted. int m_RenderWidth = 128, m_RenderHeight = 128; diff --git a/Include/UI/TitleView.hpp b/Include/UI/TitleView.hpp index 4771c0a..9d6314b 100644 --- a/Include/UI/TitleView.hpp +++ b/Include/UI/TitleView.hpp @@ -12,24 +12,22 @@ namespace UI { public: TitleView(Data::User *User); - + void Update(bool HasFocus); + void Render(SDL_Texture *Target, bool HasFocus); // Returns index of selected title. int GetSelected(void) const; - - void Update(void); - void Render(SDL_Texture *Target); + // Resets all tiles to 128x128 + void Reset(void); private: // Saves pointer to the user passed. Data::User *m_User = nullptr; // Y coordinate. double m_Y = 38; - // Target Y for scrolling effect. - double m_TargetY = 38; // Currently highlighted/selected title. int m_Selected = 0; // This is to save the X and Y coordinates so the selected icon can be drawn last. - int m_SelectedX, m_SelectedY; + double m_SelectedX, m_SelectedY = 38.0f; // Color mod for pulse. UI::ColorMod m_ColorMod; // Vector of tiles. diff --git a/Makefile b/Makefile index b1a6c5f..728d358 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ override CFLAGS += $(INCLUDE) -D__SWITCH__ override CFLAGS += `sdl2-config --cflags` `freetype-config --cflags` `curl-config --cflags` override CFLAGS += -g -Wall -O2 -ffunction-sections -ffast-math $(ARCH) $(DEFINES) -CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++23 +CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=c++23 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) diff --git a/RomFS/Text/ENUS.json b/RomFS/Text/ENUS.json index baf978b..3799800 100644 --- a/RomFS/Text/ENUS.json +++ b/RomFS/Text/ENUS.json @@ -5,7 +5,7 @@ ], "ControlGuides" : [ "[A] Select [Y] Dump All Saves [X] User Options", - "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back", + "[A] Select [L] [R] Jump [Y] Favorite [X] Title Options [B] Back", "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close", "[A] Toggle [X] Defaults [B] Back" ], @@ -17,5 +17,26 @@ "Temporary", "Cache", "System BCAT" + ], + "SettingsMenu" : [ + "Set JKSV output folder.", + "Edit Blacklist", + "Include Device Saves with users: %s", + "Auto-backup on restore: %s", + "Auto-name backups: %s", + "Hold to delete backups: %s", + "Hold to restore backups: %s", + "Hold to overwrite backups: %s", + "Only list mountable titles: %s", + "Show account system saves: %s", + "Enable writing to system saves and NAND: %s", + "Export saves to ZIP: %s", + "Force English: %s", + "Enable trash bin: %s", + "Animation scaling: %s" + ], + "OnOff" : [ + "Off", + ">On>" ] } diff --git a/RomFS/Textures/BCAT.png b/RomFS/Textures/BCAT.png index da6c45c..0444086 100644 Binary files a/RomFS/Textures/BCAT.png and b/RomFS/Textures/BCAT.png differ diff --git a/RomFS/Textures/Cache.png b/RomFS/Textures/Cache.png index 5cacbe4..f7075a3 100644 Binary files a/RomFS/Textures/Cache.png and b/RomFS/Textures/Cache.png differ diff --git a/RomFS/Textures/Icon.alpha b/RomFS/Textures/Icon.alpha new file mode 100644 index 0000000..9735322 Binary files /dev/null and b/RomFS/Textures/Icon.alpha differ diff --git a/RomFS/Textures/SettingsIcon.png b/RomFS/Textures/SettingsIcon.png index 0d00ba1..e151364 100644 Binary files a/RomFS/Textures/SettingsIcon.png and b/RomFS/Textures/SettingsIcon.png differ diff --git a/RomFS/Textures/SystemSaves.png b/RomFS/Textures/SystemSaves.png index 35edc5f..53daa8d 100644 Binary files a/RomFS/Textures/SystemSaves.png and b/RomFS/Textures/SystemSaves.png differ diff --git a/Source/AppStates/MainMenuState.cpp b/Source/AppStates/MainMenuState.cpp index cbb263a..fec53ef 100644 --- a/Source/AppStates/MainMenuState.cpp +++ b/Source/AppStates/MainMenuState.cpp @@ -1,6 +1,10 @@ #include "AppStates/MainMenuState.hpp" +#include "AppStates/SettingsState.hpp" +#include "AppStates/TitleSelectState.hpp" #include "Colors.hpp" #include "Config.hpp" +#include "Input.hpp" +#include "JKSV.hpp" #include "Logger.hpp" #include "SDL.hpp" #include "Strings.hpp" @@ -15,7 +19,7 @@ MainMenuState::MainMenuState(void) : m_RenderTarget(SDL::TextureManager::CreateLoadTexture("MainMenuTarget", 200, 555, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)), m_Background(SDL::TextureManager::CreateLoadTexture("MainMenuBackground", "romfs:/Textures/MenuBackground.png")), m_MainMenu(50, 15, 1, 555), m_ControlGuide(Strings::GetByName(Strings::Names::ControlGuides, 0)), - m_ControlGuideX(1220 - SDL::Text::GetWidth(20, m_ControlGuide)) + m_ControlGuideX(1220 - SDL::Text::GetWidth(22, m_ControlGuide)) { // Fetch user list. Data::GetUsers(m_Users); @@ -24,10 +28,13 @@ MainMenuState::MainMenuState(void) for (size_t i = 0; i < m_Users.size(); i++) { m_MainMenu.AddOption(m_Users.at(i)->GetSharedIcon()); + m_States.push_back(std::make_shared(m_Users.at(i))); } + m_States.push_back(std::make_shared()); // Create icons for the other two. m_SettingsIcon = SDL::TextureManager::CreateLoadTexture("SettingsIcon", "romfs:/Textures/SettingsIcon.png"); + m_ExtrasIcon = SDL::TextureManager::CreateLoadTexture("ExtrasIcon", 256, 256, 0); // Finally add them to the end. m_MainMenu.AddOption(m_SettingsIcon); @@ -36,7 +43,19 @@ MainMenuState::MainMenuState(void) void MainMenuState::Update(void) { - m_MainMenu.Update(); + m_MainMenu.Update(AppState::HasFocus()); + + if (Input::ButtonPressed(HidNpadButton_A) && m_MainMenu.GetSelected() < m_Users.size() && + m_Users.at(m_MainMenu.GetSelected())->GetTotalDataEntries() > 0) + { + m_States.at(m_MainMenu.GetSelected())->Reactivate(); + JKSV::PushState(m_States.at(m_MainMenu.GetSelected())); + } + else if (Input::ButtonPressed(HidNpadButton_A) && m_MainMenu.GetSelected() >= m_Users.size()) + { + m_States.at(m_MainMenu.GetSelected())->Reactivate(); + JKSV::PushState(m_States.at(m_MainMenu.GetSelected())); + } } void MainMenuState::Render(void) @@ -48,9 +67,10 @@ void MainMenuState::Render(void) // Render target to screen. m_RenderTarget->Render(NULL, 0, 91); - // Control Guide. + // Render next state for current user and control guide if this state has focus. if (AppState::HasFocus()) { - SDL::Text::Render(NULL, m_ControlGuideX, 673, 20, SDL::Text::NO_TEXT_WRAP, Colors::White, m_ControlGuide); + m_States.at(m_MainMenu.GetSelected())->Render(); + SDL::Text::Render(NULL, m_ControlGuideX, 673, 22, SDL::Text::NO_TEXT_WRAP, Colors::White, m_ControlGuide); } } diff --git a/Source/AppStates/SettingsState.cpp b/Source/AppStates/SettingsState.cpp new file mode 100644 index 0000000..7dc87f0 --- /dev/null +++ b/Source/AppStates/SettingsState.cpp @@ -0,0 +1,40 @@ +#include "AppStates/SettingsState.hpp" +#include "Colors.hpp" +#include "Input.hpp" +#include "Strings.hpp" + +namespace +{ + // All of these states share the same render target. + constexpr std::string_view SECONDARY_TARGET = "SecondaryTarget"; +} // namespace + +SettingsState::SettingsState(void) + : m_SettingsMenu(32, 18, 1002, 32, 4, 555), + m_RenderTarget(SDL::TextureManager::CreateLoadTexture(SECONDARY_TARGET, 1080, 555, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)) +{ + // Loop through strings and add them until nullptr. + int CurrentString = 0; + const char *SettingsString = nullptr; + while ((SettingsString = Strings::GetByName(Strings::Names::SettingsMenu, CurrentString++)) != nullptr) + { + m_SettingsMenu.AddOption(SettingsString); + } +} + +void SettingsState::Update(void) +{ + m_SettingsMenu.Update(AppState::HasFocus()); + + if (Input::ButtonPressed(HidNpadButton_B)) + { + AppState::Deactivate(); + } +} + +void SettingsState::Render(void) +{ + m_RenderTarget->Clear(Colors::Transparent); + m_SettingsMenu.Render(m_RenderTarget->Get(), AppState::HasFocus()); + m_RenderTarget->Render(NULL, 201, 91); +} diff --git a/Source/AppStates/TitleSelectState.cpp b/Source/AppStates/TitleSelectState.cpp new file mode 100644 index 0000000..5b880e1 --- /dev/null +++ b/Source/AppStates/TitleSelectState.cpp @@ -0,0 +1,59 @@ +#include "AppStates/TitleSelectState.hpp" +#include "Colors.hpp" +#include "Config.hpp" +#include "Input.hpp" +#include "SDL.hpp" +#include "Strings.hpp" +#include + +namespace +{ + // All of these states share the same render target. + constexpr std::string_view SECONDARY_TARGET = "SecondaryTarget"; +} // namespace + +TitleSelectState::TitleSelectState(Data::User *User) + : m_User(User), + m_RenderTarget(SDL::TextureManager::CreateLoadTexture(SECONDARY_TARGET, 1080, 555, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)), + m_TitleView(m_User) +{ + if (m_TitleControlsX == 0) + { + m_TitleControlsX = 1220 - SDL::Text::GetWidth(22, Strings::GetByName(Strings::Names::ControlGuides, 1)); + } +} + +void TitleSelectState::Update(void) +{ + m_TitleView.Update(AppState::HasFocus()); + + if (Input::ButtonPressed(HidNpadButton_B)) + { + // This will reset all the tiles so they're 128x128. + m_TitleView.Reset(); + AppState::Deactivate(); + } + else if (Input::ButtonPressed(HidNpadButton_Y)) + { + uint64_t ApplicationID = m_User->GetApplicationIDAt(m_TitleView.GetSelected()); + Config::AddRemoveFavorite(ApplicationID); + } +} + +void TitleSelectState::Render(void) +{ + m_RenderTarget->Clear(Colors::Transparent); + m_TitleView.Render(m_RenderTarget->Get(), AppState::HasFocus()); + + if (AppState::HasFocus()) + { + SDL::Text::Render(NULL, + m_TitleControlsX, + 673, + 22, + SDL::Text::NO_TEXT_WRAP, + Colors::White, + Strings::GetByName(Strings::Names::ControlGuides, 1)); + } + m_RenderTarget->Render(NULL, 201, 91); +} diff --git a/Source/Config.cpp b/Source/Config.cpp index b73bb55..baea6de 100644 --- a/Source/Config.cpp +++ b/Source/Config.cpp @@ -1,6 +1,7 @@ #include "Config.hpp" #include "JSON.hpp" #include "Logger.hpp" +#include "StringUtil.hpp" #include #include #include @@ -38,7 +39,7 @@ static void ReadArrayToVector(std::vector &Vector, json_object *Array) { continue; } - Vector.push_back(json_object_get_uint64(ArrayEntry)); + Vector.push_back(std::strtoull(json_object_get_string(ArrayEntry), NULL, 16)); } } @@ -134,7 +135,8 @@ void Config::Save(void) json_object *FavoritesArray = json_object_new_array(); for (uint64_t &TitleID : s_Favorites) { - json_object *NewFavorite = json_object_new_uint64(TitleID); + // Need to do it like this or json-c does decimal instead of hex. + json_object *NewFavorite = json_object_new_string(StringUtil::GetFormattedString("%016llX", TitleID).c_str()); json_object_array_add(FavoritesArray, NewFavorite); } json_object_object_add(ConfigJSON.get(), Config::Keys::Favorites.data(), FavoritesArray); @@ -143,7 +145,7 @@ void Config::Save(void) json_object *BlacklistArray = json_object_new_array(); for (uint64_t &TitleID : s_Blacklist) { - json_object *NewBlacklist = json_object_new_uint64(TitleID); + json_object *NewBlacklist = json_object_new_string(StringUtil::GetFormattedString("%016llX", TitleID).c_str()); json_object_array_add(BlacklistArray, NewBlacklist); } json_object_object_add(ConfigJSON.get(), Config::Keys::BlackList.data(), BlacklistArray); diff --git a/Source/Data/TitleInfo.cpp b/Source/Data/TitleInfo.cpp index 539ff43..d65275b 100644 --- a/Source/Data/TitleInfo.cpp +++ b/Source/Data/TitleInfo.cpp @@ -30,9 +30,10 @@ Data::TitleInfo::TitleInfo(uint64_t ApplicationID) sprintf(m_PathSafeTitle, "%016llX", ApplicationID); // Create a place holder icon. - int TextX = 128 - (SDL::Text::GetWidth(32, ApplicationIDHex.c_str()) / 2); + int TextX = 128 - (SDL::Text::GetWidth(48, ApplicationIDHex.c_str()) / 2); m_Icon = SDL::TextureManager::CreateLoadTexture(ApplicationIDHex, 256, 256, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET); - SDL::Text::Render(m_Icon->Get(), TextX, 112, 32, SDL::Text::NO_TEXT_WRAP, Colors::White, ApplicationIDHex.c_str()); + m_Icon->Clear(Colors::DialogBox); + SDL::Text::Render(m_Icon->Get(), TextX, 104, 48, SDL::Text::NO_TEXT_WRAP, Colors::White, ApplicationIDHex.c_str()); } else if (R_SUCCEEDED(NsError) && R_SUCCEEDED(nacpGetLanguageEntry(&NsControlData.nacp, &LanguageEntry))) { diff --git a/Source/JKSV.cpp b/Source/JKSV.cpp index 51f9f41..863acb3 100644 --- a/Source/JKSV.cpp +++ b/Source/JKSV.cpp @@ -126,6 +126,7 @@ void JKSV::Update(void) { while (!m_StateVector.back()->IsActive()) { + m_StateVector.back()->TakeFocus(); m_StateVector.pop_back(); m_StateVector.back()->GiveFocus(); } diff --git a/Source/Main.cpp b/Source/Main.cpp index 895a046..3e9e57f 100644 --- a/Source/Main.cpp +++ b/Source/Main.cpp @@ -1,12 +1,15 @@ +#include "Config.hpp" #include "JKSV.hpp" +#include int main(void) { JKSV Jksv; - while (Jksv.IsRunning()) + while (appletMainLoop() && Jksv.IsRunning()) { Jksv.Update(); Jksv.Render(); } + Config::Save(); return 0; } diff --git a/Source/UI/IconMenu.cpp b/Source/UI/IconMenu.cpp index 999efe9..5a8a9f3 100644 --- a/Source/UI/IconMenu.cpp +++ b/Source/UI/IconMenu.cpp @@ -2,25 +2,31 @@ #include "Colors.hpp" #include "UI/RenderFunctions.hpp" -UI::IconMenu::IconMenu(int X, int Y, int ScrollLength, int RenderTargetHeight) : Menu(X, Y, 152, 132, ScrollLength, RenderTargetHeight) {}; +UI::IconMenu::IconMenu(int X, int Y, int ScrollLength, int RenderTargetHeight) : Menu(X, Y, 152, 88, ScrollLength, RenderTargetHeight) {}; -void UI::IconMenu::Update(void) +void UI::IconMenu::Update(bool HasFocus) { - Menu::Update(); + Menu::Update(HasFocus); } void UI::IconMenu::Render(SDL_Texture *Target, bool HasFocus) { - m_ColorMod.Update(); + if (HasFocus) + { + m_ColorMod.Update(); + } - for (int i = 0, TempY = m_Y; i < m_OptionsLength; i++, TempY += m_OptionHeight) + for (int i = 0, TempY = m_Y; i <= m_OptionsLength; i++, TempY += m_OptionHeight) { // Clear target. m_OptionTarget->Clear(Colors::Transparent); if (i == m_Selected) { - UI::RenderBoundingBox(Target, m_X - 8, TempY - 8, 152, 146, m_ColorMod); - SDL::RenderRectFill(m_OptionTarget->Get(), 0, 0, 4, 130, {0x00FFC5FF}); + if (HasFocus) + { + UI::RenderBoundingBox(Target, m_X - 8, TempY - 8, 152, 146, m_ColorMod); + } + SDL::RenderRectFill(m_OptionTarget->Get(), 0, 0, 6, 130, {0x00FFC5FF}); } //m_Options.at(i)->Render(m_OptionTarget->Get(), 0, 0); m_Options.at(i)->RenderStretched(m_OptionTarget->Get(), 8, 1, 128, 128); diff --git a/Source/UI/Menu.cpp b/Source/UI/Menu.cpp index 888c174..727a37b 100644 --- a/Source/UI/Menu.cpp +++ b/Source/UI/Menu.cpp @@ -3,10 +3,11 @@ #include "Config.hpp" #include "Input.hpp" #include "UI/RenderFunctions.hpp" +#include UI::Menu::Menu(int X, int Y, int Width, int FontSize, int ScrollLength, int RenderTargetHeight) : m_X(X), m_Y(Y), m_OriginalY(Y), m_FontSize(FontSize), m_ScrollLength(ScrollLength), m_MenuRenderLength(ScrollLength * 2), m_Width(Width), - m_OptionHeight(FontSize + 16), m_TargetY(Y), m_RenderTargetHeight(RenderTargetHeight) + m_OptionHeight(std::ceil(static_cast(FontSize) * 1.8f)), m_TargetY(Y), m_RenderTargetHeight(RenderTargetHeight) { static int MenuID = 0; // Create target for menu option @@ -15,7 +16,7 @@ UI::Menu::Menu(int X, int Y, int Width, int FontSize, int ScrollLength, int Rend SDL::TextureManager::CreateLoadTexture(MenuTargetName, m_Width, m_OptionHeight, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET); } -void UI::Menu::Update(void) +void UI::Menu::Update(bool HasFocus) { // Bail if there's nothing to update. if (m_Options.empty()) @@ -46,7 +47,8 @@ void UI::Menu::Update(void) { m_TargetY = m_OriginalY; } - else if (PreviousSelected != m_Selected && m_Selected >= m_ScrollLength && (m_Selected > PreviousSelected || m_Selected < PreviousSelected)) + else if (PreviousSelected != m_Selected && m_Selected >= m_ScrollLength && + (m_Selected > PreviousSelected || m_Selected < PreviousSelected) && m_Selected < (m_OptionsLength - m_ScrollLength)) { m_TargetY = m_OriginalY - ((m_Selected - m_ScrollLength) * m_OptionHeight); } @@ -81,18 +83,21 @@ void UI::Menu::Render(SDL_Texture *Target, bool HasFocus) if (i == m_Selected) { - // Render the bounding box - UI::RenderBoundingBox(Target, m_X - 4, m_Y - 4, m_Width + 8, m_OptionHeight + 8, m_ColorMod); + if (HasFocus) + { + // Render the bounding box + UI::RenderBoundingBox(Target, m_X - 4, TempY - 4, m_Width + 8, m_OptionHeight + 8, m_ColorMod); + } // Render the little rectangle. - SDL::RenderRectFill(m_OptionTarget->Get(), 8, 2, 4, m_OptionHeight - 4, {0x00FFC5FF}); + SDL::RenderRectFill(m_OptionTarget->Get(), 8, 8, 6, m_OptionHeight - 16, Colors::BlueGreen); } // Render text to target. SDL::Text::Render(m_OptionTarget->Get(), - 14, + 20, (m_OptionHeight / 2) - (m_FontSize / 2), m_FontSize, SDL::Text::NO_TEXT_WRAP, - i == m_Selected ? Colors::Blue : Colors::White, + i == m_Selected ? Colors::BlueGreen : Colors::White, m_Options.at(i).c_str()); // Render target to target m_OptionTarget->Render(Target, m_X, TempY); diff --git a/Source/UI/TitleTile.cpp b/Source/UI/TitleTile.cpp index a845f01..b298b3b 100644 --- a/Source/UI/TitleTile.cpp +++ b/Source/UI/TitleTile.cpp @@ -26,6 +26,22 @@ void UI::TitleTile::Render(SDL_Texture *Target, int X, int Y) m_Icon->RenderStretched(Target, RenderX, RenderY, m_RenderWidth, m_RenderHeight); if (m_IsFavorite) { - SDL::Text::Render(Target, RenderX + 8, RenderY + 8, 20, SDL::Text::NO_TEXT_WRAP, Colors::Pink, "\uE017"); + SDL::Text::Render(Target, RenderX + 4, RenderY + 2, 28, SDL::Text::NO_TEXT_WRAP, Colors::Pink, "\uE017"); } } + +void UI::TitleTile::Reset(void) +{ + m_RenderWidth = 128; + m_RenderHeight = 128; +} + +int UI::TitleTile::GetWidth(void) const +{ + return m_RenderWidth; +} + +int UI::TitleTile::GetHeight(void) const +{ + return m_RenderHeight; +} diff --git a/Source/UI/TitleView.cpp b/Source/UI/TitleView.cpp index c00f1f8..8173c59 100644 --- a/Source/UI/TitleView.cpp +++ b/Source/UI/TitleView.cpp @@ -1,5 +1,15 @@ #include "UI/TitleView.hpp" +#include "Colors.hpp" #include "Config.hpp" +#include "Input.hpp" +#include "Logger.hpp" +#include "UI/RenderFunctions.hpp" +#include + +namespace +{ + constexpr int ICON_ROW_SIZE = 7; +} UI::TitleView::TitleView(Data::User *User) : m_User(User) { @@ -11,3 +21,101 @@ UI::TitleView::TitleView(Data::User *User) : m_User(User) m_TitleTiles.emplace_back(Config::IsFavorite(m_User->GetApplicationIDAt(i)), CurrentTitleInfo->GetIcon()); } } + +void UI::TitleView::Update(bool HasFocus) +{ + if (m_TitleTiles.empty()) + { + return; + } + + // Update pulse + if (HasFocus) + { + m_ColorMod.Update(); + } + + // Input. + int TotalTiles = m_TitleTiles.size() - 1; + if (Input::ButtonPressed(HidNpadButton_AnyUp) && (m_Selected -= ICON_ROW_SIZE) < 0) + { + m_Selected = 0; + } + else if (Input::ButtonPressed(HidNpadButton_AnyDown) && (m_Selected += ICON_ROW_SIZE) > TotalTiles) + { + m_Selected = TotalTiles; + } + else if (Input::ButtonPressed(HidNpadButton_AnyLeft) && m_Selected > 0) + { + --m_Selected; + } + else if (Input::ButtonPressed(HidNpadButton_AnyRight) && m_Selected < TotalTiles) + { + ++m_Selected; + } + + double Scaling = Config::GetAnimationScaling(); + if (m_SelectedY > 280.0f) + { + m_Y += std::ceil((280.0f - m_SelectedY) / Scaling); + } + else if (m_SelectedY < 38.0f) + { + m_Y += std::ceil((38.0f - m_SelectedY) / Scaling); + } + + for (size_t i = 0; i < m_TitleTiles.size(); i++) + { + m_TitleTiles.at(i).Update(m_Selected == i ? true : false); + } +} + +void UI::TitleView::Render(SDL_Texture *Target, bool HasFocus) +{ + if (m_TitleTiles.empty()) + { + return; + } + + for (int i = 0, TempY = m_Y; i < static_cast(m_TitleTiles.size()); TempY += 144) + { + int EndRow = i + 7; + for (int j = i, TempX = 32; j < EndRow; j++, i++, TempX += 144) + { + if (i >= static_cast(m_TitleTiles.size())) + { + break; + } + + // Save the X and Y to render the selected tile over the rest. + if (i == m_Selected) + { + m_SelectedX = TempX; + m_SelectedY = TempY; + continue; + } + // Just render + m_TitleTiles.at(i).Render(Target, TempX, TempY); + } + } + // Now render the selected title. + if (HasFocus) + { + SDL::RenderRectFill(Target, m_SelectedX - 23, m_SelectedY - 23, 174, 174, Colors::ClearColor); + UI::RenderBoundingBox(Target, m_SelectedX - 24, m_SelectedY - 24, 176, 176, m_ColorMod); + } + m_TitleTiles.at(m_Selected).Render(Target, m_SelectedX, m_SelectedY); +} + +int UI::TitleView::GetSelected(void) const +{ + return m_Selected; +} + +void UI::TitleView::Reset(void) +{ + for (UI::TitleTile &CurrentTile : m_TitleTiles) + { + CurrentTile.Reset(); + } +}