From 347f1bc75f2f7bde2df0ae2f42ff3fbb1105d3ee Mon Sep 17 00:00:00 2001 From: J-D-K Date: Fri, 20 Mar 2026 12:41:04 -0400 Subject: [PATCH] Initial commit. Needs debugging. --- Libraries/SDLLib | 2 +- Makefile | 2 +- include/JKSV.hpp | 26 ++++- include/StateManager.hpp | 6 +- include/appstates/AppletModeState.hpp | 4 +- include/appstates/BackupMenuState.hpp | 15 +-- include/appstates/BaseState.hpp | 4 +- include/appstates/BaseTask.hpp | 16 ++- include/appstates/BlacklistEditState.hpp | 5 +- include/appstates/ConfirmState.hpp | 101 +++++++---------- include/appstates/DataLoadingState.hpp | 21 ++-- include/appstates/ExtrasMenuState.hpp | 14 ++- include/appstates/FadeState.hpp | 17 ++- include/appstates/FileModeState.hpp | 12 +- include/appstates/FileOptionState.hpp | 10 +- include/appstates/MainMenuState.hpp | 23 ++-- include/appstates/MessageState.hpp | 22 ++-- include/appstates/ProgressState.hpp | 13 ++- include/appstates/SaveCreateState.hpp | 8 +- include/appstates/SaveImportState.hpp | 8 +- include/appstates/SettingsState.hpp | 6 +- include/appstates/TaskState.hpp | 14 ++- include/appstates/TextTitleSelectState.hpp | 10 +- include/appstates/TitleInfoState.hpp | 16 ++- include/appstates/TitleOptionState.hpp | 8 +- include/appstates/TitleSelectCommon.hpp | 4 +- include/appstates/TitleSelectState.hpp | 10 +- include/appstates/UserOptionState.hpp | 8 +- include/data/DataCommon.hpp | 8 +- include/data/DataContext.hpp | 3 +- include/data/TitleInfo.hpp | 8 +- include/data/User.hpp | 2 +- include/data/data.hpp | 3 +- include/graphics/ScopedRender.hpp | 43 +++++++ include/graphics/colors.hpp | 40 +++---- include/graphics/fonts.hpp | 28 +++++ include/graphics/gfxutil.hpp | 6 +- include/graphics/targets.hpp | 17 +++ include/input.hpp | 23 ---- include/mathutil.hpp | 6 +- include/ui/BoundingBox.hpp | 10 +- include/ui/ColorMod.hpp | 2 +- include/ui/ControlGuide.hpp | 13 ++- include/ui/DialogBox.hpp | 12 +- include/ui/Element.hpp | 4 +- include/ui/Frame.hpp | 10 +- include/ui/IconMenu.hpp | 12 +- include/ui/Menu.hpp | 17 +-- include/ui/PopMessage.hpp | 11 +- include/ui/PopMessageManager.hpp | 4 +- include/ui/SlideOutPanel.hpp | 16 ++- include/ui/TextScroll.hpp | 54 +++++---- include/ui/TitleTile.hpp | 17 +-- include/ui/TitleView.hpp | 12 +- romfs/Text/DE.json.z | Bin 0 -> 4132 bytes romfs/Text/ENGB.json.z | Bin 0 -> 3701 bytes romfs/Text/ENUS.json.z | Bin 0 -> 3706 bytes romfs/Text/ES.json.z | Bin 0 -> 4057 bytes romfs/Text/ES419.json.z | Bin 0 -> 4068 bytes romfs/Text/FR.json.z | Bin 0 -> 4190 bytes romfs/Text/FRCA.json.z | Bin 0 -> 4190 bytes romfs/Text/IT.json.z | Bin 0 -> 3994 bytes romfs/Text/JA.json.z | Bin 0 -> 4332 bytes romfs/Text/KO.json.z | Bin 0 -> 3956 bytes romfs/Text/NL.json.z | Bin 0 -> 3896 bytes romfs/Text/PT.json.z | Bin 0 -> 3930 bytes romfs/Text/PTBR.json.z | Bin 0 -> 3879 bytes romfs/Text/RU.json.z | Bin 0 -> 4580 bytes romfs/Text/ZHCN.json.z | Bin 0 -> 3727 bytes romfs/Text/ZHTW.json.z | Bin 0 -> 3760 bytes source/JKSV.cpp | 126 ++++++++++++--------- source/StateManager.cpp | 8 +- source/appstates/BackupMenuState.cpp | 91 ++++++++------- source/appstates/BaseTask.cpp | 32 +++--- source/appstates/BlacklistEditState.cpp | 15 ++- source/appstates/DataLoadingState.cpp | 50 ++++---- source/appstates/ExtrasMenuState.cpp | 46 ++++---- source/appstates/FadeState.cpp | 13 ++- source/appstates/FileModeState.cpp | 74 ++++++------ source/appstates/FileOptionState.cpp | 37 +++--- source/appstates/MainMenuState.cpp | 79 +++++++++---- source/appstates/MessageState.cpp | 53 +++++---- source/appstates/ProgressState.cpp | 53 ++++----- source/appstates/SaveCreateState.cpp | 14 +-- source/appstates/SaveImportState.cpp | 14 +-- source/appstates/SettingsState.cpp | 53 ++++++--- source/appstates/TaskState.cpp | 30 +++-- source/appstates/TextTitleSelectState.cpp | 56 +++++---- source/appstates/TitleInfoState.cpp | 54 ++++----- source/appstates/TitleOptionState.cpp | 16 +-- source/appstates/TitleSelectState.cpp | 41 ++++--- source/appstates/UserOptionState.cpp | 16 ++- source/data/DataContext.cpp | 10 +- source/data/TitleInfo.cpp | 22 ++-- source/data/User.cpp | 21 +++- source/data/data.cpp | 12 +- source/gfxutil.cpp | 49 +++++--- source/input.cpp | 29 ----- source/remote/remote.cpp | 5 - source/ui/BoundingBox.cpp | 45 +++++--- source/ui/ColorMod.cpp | 10 +- source/ui/ControlGuide.cpp | 50 ++++---- source/ui/DialogBox.cpp | 43 ++++--- source/ui/Frame.cpp | 29 +++-- source/ui/IconMenu.cpp | 34 +++--- source/ui/Menu.cpp | 63 ++++++----- source/ui/PopMessage.cpp | 30 ++--- source/ui/PopMessageManager.cpp | 9 +- source/ui/SlideOutPanel.cpp | 39 ++++--- source/ui/TextScroll.cpp | 113 +++++++++--------- source/ui/TitleTile.cpp | 27 +++-- source/ui/TitleView.cpp | 43 ++++--- 112 files changed, 1331 insertions(+), 1109 deletions(-) create mode 100644 include/graphics/ScopedRender.hpp create mode 100644 include/graphics/fonts.hpp create mode 100644 include/graphics/targets.hpp delete mode 100644 include/input.hpp create mode 100644 romfs/Text/DE.json.z create mode 100644 romfs/Text/ENGB.json.z create mode 100644 romfs/Text/ENUS.json.z create mode 100644 romfs/Text/ES.json.z create mode 100644 romfs/Text/ES419.json.z create mode 100644 romfs/Text/FR.json.z create mode 100644 romfs/Text/FRCA.json.z create mode 100644 romfs/Text/IT.json.z create mode 100644 romfs/Text/JA.json.z create mode 100644 romfs/Text/KO.json.z create mode 100644 romfs/Text/NL.json.z create mode 100644 romfs/Text/PT.json.z create mode 100644 romfs/Text/PTBR.json.z create mode 100644 romfs/Text/RU.json.z create mode 100644 romfs/Text/ZHCN.json.z create mode 100644 romfs/Text/ZHTW.json.z delete mode 100644 source/input.cpp diff --git a/Libraries/SDLLib b/Libraries/SDLLib index 9fa1be9..e890bf3 160000 --- a/Libraries/SDLLib +++ b/Libraries/SDLLib @@ -1 +1 @@ -Subproject commit 9fa1be94b7c0393f479f47dc9a74908a38112fe8 +Subproject commit e890bf32fe42695fc6448921d1c503c3d1eaf8d6 diff --git a/Makefile b/Makefile index c6e2e07..1c14ed2 100644 --- a/Makefile +++ b/Makefile @@ -183,7 +183,7 @@ clean: #--------------------------------------------------------------------------------- send: $(BUILD) - @nxlink $(TARGET).nro + @nxlink -a 192.168.0.171 $(TARGET).nro #--------------------------------------------------------------------------------- diff --git a/include/JKSV.hpp b/include/JKSV.hpp index a63a5b7..163788b 100644 --- a/include/JKSV.hpp +++ b/include/JKSV.hpp @@ -30,6 +30,21 @@ class JKSV static void request_quit() noexcept; private: + /// @brief SDL2 instance. + sdl2::SDL2 m_sdl2{}; + + /// @brief SDL2 Window. + sdl2::Window m_window{}; + + /// @brief SDL2 Renderer. + sdl2::Renderer m_renderer{}; + + /// @brief SDL2 Audio. + sdl2::Audio m_audio{}; + + /// @brief Not really a part of SDL2, but... + sdl2::Input m_input{}; + /// @brief Whether or not initialization was successful and JKSV is still running. static inline std::atomic_bool sm_isRunning{}; @@ -37,7 +52,13 @@ class JKSV bool m_showTranslationInfo{}; /// @brief JKSV icon in upper left corner. - sdl::SharedTexture m_headerIcon{}; + sdl2::SharedTexture m_headerIcon{}; + + /// @brief This is used for rendering JKSV. + sdl2::SharedFont m_titleFont{}; + + /// @brief This is used to render the build date. + sdl2::SharedFont m_buildFont{}; /// @brief Stores the translation string. std::string m_translationInfo{}; @@ -60,9 +81,6 @@ class JKSV // Creates the needed directories on SD. bool create_directories(); - /// @brief Adds the text color changing characters. - void add_color_chars(); - /// @brief Retrieves the strings from the map and sets them up for printing. void setup_translation_info_strings(); diff --git a/include/StateManager.hpp b/include/StateManager.hpp index 539022a..82d4006 100644 --- a/include/StateManager.hpp +++ b/include/StateManager.hpp @@ -1,6 +1,8 @@ #pragma once #include "appstates/BaseState.hpp" +#include "sdl.hpp" +#include #include #include @@ -14,10 +16,10 @@ class StateManager StateManager &operator=(StateManager &&) = delete; /// @brief Runs the state update routine. - static void update(); + static void update(const sdl2::Input &input); /// @brief Runs the state rendering routine(s); - static void render() noexcept; + static void render(sdl2::Renderer &renderer) noexcept; /// @brief Returns whether the back of the vector is a closable state. static bool back_is_closable() noexcept; diff --git a/include/appstates/AppletModeState.hpp b/include/appstates/AppletModeState.hpp index 6b67fea..a40c1d4 100644 --- a/include/appstates/AppletModeState.hpp +++ b/include/appstates/AppletModeState.hpp @@ -15,10 +15,10 @@ class AppletModeState final : public BaseState static inline std::shared_ptr create() { return std::make_shared(); } /// @brief Runs the update routine. Basically does nothing. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Renders the message to the screen. - void render() override; + void render(sdl2::Renderer &renderer) override; private: /// @brief Pointer to the string rendered to the screen. diff --git a/include/appstates/BackupMenuState.hpp b/include/appstates/BackupMenuState.hpp index 3f7595f..5829d15 100644 --- a/include/appstates/BackupMenuState.hpp +++ b/include/appstates/BackupMenuState.hpp @@ -26,9 +26,7 @@ class BackupMenuState final : public BaseState static inline std::shared_ptr create(data::User *user, data::TitleInfo *titleInfo, const FsSaveDataInfo *saveInfo) - { - return std::make_shared(user, titleInfo, saveInfo); - } + { return std::make_shared(user, titleInfo, saveInfo); } /// @brief Creates and pushes a new BackupMenuState to the vector. static inline std::shared_ptr create_and_push(data::User *user, @@ -41,10 +39,10 @@ class BackupMenuState final : public BaseState } /// @brief Required. Inherited virtual function from AppState. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Required. Inherited virtual function from AppState. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief Refreshes the directory listing and menu. void refresh(); @@ -153,7 +151,10 @@ class BackupMenuState final : public BaseState static inline std::shared_ptr sm_slidePanel{}; /// @brief Inner render target so the menu only renders to a certain area. - static inline sdl::SharedTexture sm_menuRenderTarget{}; + static inline sdl2::SharedTexture sm_menuRenderTarget{}; + + /// @brief Font used for rendering text. + static inline sdl2::SharedFont sm_font{}; /// @brief Initializes the static members all instances share if they haven't been already. void initialize_static_members(); @@ -174,7 +175,7 @@ class BackupMenuState final : public BaseState void initialize_remote_storage(); /// @brief This is the function called when New Backup is selected. - void name_and_create_backup(); + void name_and_create_backup(const sdl2::Input &input); /// @brief This is the function called when a backup is selected to be overwritten. void confirm_overwrite(); diff --git a/include/appstates/BaseState.hpp b/include/appstates/BaseState.hpp index d00b3a2..eb195e9 100644 --- a/include/appstates/BaseState.hpp +++ b/include/appstates/BaseState.hpp @@ -15,13 +15,13 @@ class BaseState virtual ~BaseState(); /// @brief Every derived class is required to have this function. - virtual void update() = 0; + virtual void update(const sdl2::Input &input) = 0; /// @brief Sub update routine. Meant to handle minor background tasks. Not meant for full update routines. virtual void sub_update() {}; /// @brief Every derived class is required to have this function. - virtual void render() = 0; + virtual void render(sdl2::Renderer &renderer) = 0; /// @brief Deactivates state and allows JKSV to purge it from the vector. void deactivate(); diff --git a/include/appstates/BaseTask.hpp b/include/appstates/BaseTask.hpp index 5813b60..9829ac4 100644 --- a/include/appstates/BaseTask.hpp +++ b/include/appstates/BaseTask.hpp @@ -1,5 +1,6 @@ #pragma once #include "appstates/BaseState.hpp" +#include "sdl.hpp" #include "sys/sys.hpp" #include "ui/ColorMod.hpp" @@ -15,10 +16,10 @@ class BaseTask : public BaseState /// @brief Runs the update routine for rendering the loading glyph animation. /// @param - virtual void update() = 0; + virtual void update(const sdl2::Input &input) = 0; /// @brief Virtual render function. - virtual void render() = 0; + virtual void render(sdl2::Renderer &renderer) = 0; protected: /// @brief Underlying system task. This needs to be allocated by the derived classes. @@ -28,15 +29,12 @@ class BaseTask : public BaseState void update_loading_glyph(); /// @brief Displays the "can't quit JKSV" when plus is pressed. - void pop_on_plus(); + void pop_on_plus(const sdl2::Input &input); /// @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(); - /// @brief This is the font size used for displaying text during tasks. - static inline constexpr int FONT_SIZE = 20; - private: /// @brief This is the current frame of the loading glyph animation. int m_currentFrame{}; @@ -53,4 +51,10 @@ class BaseTask : public BaseState /// @brief This array holds the glyphs of the loading sequence. I think it's from the Wii? static inline constexpr std::array sm_glyphArray = {"\ue020", "\ue021", "\ue022", "\ue023", "\ue024", "\ue025", "\ue026", "\ue027"}; + + /// @brief Font for rendering the glyph. + static inline sdl2::SharedFont sm_font{}; + + /// @brief Ensures the font is loaded. + void initialize_static_members(); }; diff --git a/include/appstates/BlacklistEditState.hpp b/include/appstates/BlacklistEditState.hpp index 0c472be..2c4038f 100644 --- a/include/appstates/BlacklistEditState.hpp +++ b/include/appstates/BlacklistEditState.hpp @@ -1,6 +1,7 @@ #pragma once #include "StateManager.hpp" #include "appstates/BaseState.hpp" +#include "sdl.hpp" #include "ui/ui.hpp" #include @@ -24,10 +25,10 @@ class BlacklistEditState final : public BaseState } /// @brief Update override. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Render override. - void render() override; + void render(sdl2::Renderer &renderer) override; private: /// @brief Local copy of the blacklist. diff --git a/include/appstates/ConfirmState.hpp b/include/appstates/ConfirmState.hpp index a2a9e84..0c802f6 100644 --- a/include/appstates/ConfirmState.hpp +++ b/include/appstates/ConfirmState.hpp @@ -5,8 +5,8 @@ #include "appstates/ProgressState.hpp" #include "appstates/TaskState.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" -#include "input.hpp" #include "logging/logger.hpp" #include "sdl.hpp" #include "strings/strings.hpp" @@ -108,9 +108,7 @@ class ConfirmState final : public BaseState sys::threadpool::JobFunction onConfirm, sys::threadpool::JobFunction onCancel, sys::Task::TaskData taskData) - { - return std::make_shared(query, holdRequired, onConfirm, onCancel, taskData); - } + { return std::make_shared(query, holdRequired, onConfirm, onCancel, taskData); } /// @brief Returns a new ConfirmState. See constructor. static inline std::shared_ptr create(std::string &query, @@ -118,9 +116,7 @@ class ConfirmState final : public BaseState sys::threadpool::JobFunction onConfirm, sys::threadpool::JobFunction onCancel, sys::Task::TaskData taskData) - { - return std::make_shared(query, holdRequired, onConfirm, onCancel, taskData); - } + { return std::make_shared(query, holdRequired, onConfirm, onCancel, taskData); } /// @brief Creates and returns a new ConfirmState and pushes it. static inline std::shared_ptr create_and_push(std::string_view query, @@ -173,23 +169,22 @@ class ConfirmState final : public BaseState } /// @brief Just updates the ConfirmState. - void update() override + void update(const sdl2::Input &input) override { switch (m_state) { case State::Opening: ConfirmState::update_dimensions(); break; - case State::Displaying: ConfirmState::update_handle_input(); break; + case State::Displaying: ConfirmState::update_handle_input(input); break; case State::Closing: ConfirmState::update_dimensions(); break; } } /// @brief Renders the state to screen. - void render() override + void render(sdl2::Renderer &renderer) override { // These are the rendering coordinates that aren't affected by the transition. static constexpr int TEXT_X = 312; static constexpr int TEXT_Y_OFFSET = 24; - static constexpr int TEXT_FONT_SIZE = 20; static constexpr int TEXT_WRAP_WIDTH = 656; // Divider line A. @@ -204,59 +199,28 @@ class ConfirmState final : public BaseState static constexpr int LINE_Y_OFFSET = 192; // Yes/NO - static constexpr int OPTION_Y_OFFSET = 214; - static constexpr int OPTION_FONT_SIZE = 22; + static constexpr int OPTION_Y_OFFSET = 214; const bool hasFocus = BaseState::has_focus(); const int y = m_transition.get_y(); // This is the dimming rectangle. - sdl::render_rect_fill(sdl::Texture::Null, - 0, - 0, - graphics::SCREEN_WIDTH, - graphics::SCREEN_HEIGHT, - colors::DIM_BACKGROUND); + renderer.render_rectangle(0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); // Render the dialog. Only render the rest if we're in the display state. - sm_dialog->render(sdl::Texture::Null, hasFocus); + sm_dialog->render(renderer, hasFocus); if (!m_transition.in_place() || m_state != State::Displaying) { return; } // Main string. - sdl::text::render(sdl::Texture::Null, - TEXT_X, - y + TEXT_Y_OFFSET, - TEXT_FONT_SIZE, - TEXT_WRAP_WIDTH, - colors::WHITE, - m_query); + sm_textFont->render_text_wrapped(TEXT_X, y + TEXT_Y_OFFSET, colors::WHITE, TEXT_WRAP_WIDTH, m_query); // Divider lines. - sdl::render_line(sdl::Texture::Null, - LINE_A_X_A, - y + LINE_Y_OFFSET, - LINE_A_X_B, - y + LINE_Y_OFFSET, - colors::DIV_COLOR); - sdl::render_line(sdl::Texture::Null, LINE_B_X, y + LINE_Y_OFFSET, LINE_B_X, y + LINE_B_Y_B, colors::DIV_COLOR); + renderer.render_line(LINE_A_X_A, y + LINE_Y_OFFSET, LINE_A_X_B, y + LINE_Y_OFFSET, colors::DIV_COLOR); + renderer.render_line(LINE_B_X, y + LINE_Y_OFFSET, LINE_B_X, y + LINE_B_Y_B, colors::DIV_COLOR); // Yes - sdl::text::render(sdl::Texture::Null, - m_yesX, - y + OPTION_Y_OFFSET, - OPTION_FONT_SIZE, - sdl::text::NO_WRAP, - colors::WHITE, - sm_yes); - - // No - sdl::text::render(sdl::Texture::Null, - m_noX, - y + OPTION_Y_OFFSET, - OPTION_FONT_SIZE, - sdl::text::NO_WRAP, - colors::WHITE, - sm_no); + sm_optionFont->render_text(m_yesX, y + OPTION_Y_OFFSET, colors::WHITE, sm_yes); + sm_optionFont->render_text(m_noX, y + OPTION_Y_OFFSET, colors::WHITE, sm_no); } private: @@ -313,20 +277,26 @@ class ConfirmState final : public BaseState /// @brief This dialog is shared between all instances. static inline std::shared_ptr sm_dialog{}; + /// @brief This is the font used to render the actual text. + static inline sdl2::SharedFont sm_textFont{}; + + /// @brief This is the font used to render the Yes or No options. + static inline sdl2::SharedFont sm_optionFont{}; + /// @brief This sound is shared. - static inline sdl::SharedSound sm_dialogPop{}; + static inline sdl2::SharedSound sm_dialogPop{}; void initialize_static_members() { // Name and path to the sound used. - static constexpr std::string_view POP_SOUND = "ConfirmPop"; - static constexpr const char *POP_PATH = "romfs:/Sound/ConfirmPop.wav"; + static constexpr std::string_view POP_PATH = "romfs:/Sound/ConfirmPop.wav"; // To do: Not sure about checking the holding text. - if (sm_dialog && sm_dialogPop && sm_yes && sm_no) { return; } + if (sm_dialog && sm_dialogPop && sm_yes && sm_no && sm_textFont && sm_optionFont) { return; } + // Init dialog and get it started. sm_dialog = ui::DialogBox::create(0, 0, 0, 0); - sm_dialogPop = sdl::SoundManager::load(POP_SOUND, POP_PATH); + sm_dialogPop = sdl2::SoundManager::create_load_resource(POP_PATH, POP_PATH); sm_dialog->set_from_transition(m_transition, true); // Load yes and no. @@ -336,6 +306,13 @@ class ConfirmState final : public BaseState // Loop load the holding strings. const char *hold{}; for (int i = 0; (hold = strings::get_by_name(strings::names::HOLDING_STRINGS, i)); i++) { sm_hold[i] = hold; } + + // Fonts for rendering text. + sm_textFont = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_FOUR_PIXEL, + graphics::fonts::sizes::TWENTY_PIXEL); + sm_optionFont = + sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_FOUR_PIXEL, + graphics::fonts::sizes::TWENTY_FOUR_PIXEL); } /// @brief Updates the dimensions of the dialog. @@ -353,13 +330,13 @@ class ConfirmState final : public BaseState } /// @brief Handles the input and updating. - void update_handle_input() noexcept + void update_handle_input(const sdl2::Input &input) noexcept { // Grab our input bools. - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); - const bool aHeld = input::button_held(HidNpadButton_A); - const bool aReleased = input::button_released(HidNpadButton_A); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); + const bool aHeld = input.button_held(HidNpadButton_A); + const bool aReleased = input.button_released(HidNpadButton_A); // This is to prevent A from auto triggering the dialog. m_triggerGuard = m_triggerGuard || (aPressed && !m_triggerGuard); @@ -379,13 +356,13 @@ class ConfirmState final : public BaseState // This just centers the Yes or holding text. void center_yes() { - const int yesWidth = sdl::text::get_width(22, sm_yes); + const int yesWidth = sm_optionFont->get_text_width(sm_yes); m_yesX = COORD_YES_X - (yesWidth / 2); } void center_no() { - const int noWidth = sdl::text::get_width(22, sm_no); + const int noWidth = sm_optionFont->get_text_width(sm_no); m_noX = COORD_NO_X - (noWidth / 2); } diff --git a/include/appstates/DataLoadingState.hpp b/include/appstates/DataLoadingState.hpp index 50aa706..712571b 100644 --- a/include/appstates/DataLoadingState.hpp +++ b/include/appstates/DataLoadingState.hpp @@ -15,41 +15,45 @@ class DataLoadingState final : public BaseTask using DestructFunction = std::function; DataLoadingState(data::DataContext &context, + sdl2::Renderer &renderer, DestructFunction destructFunction, sys::threadpool::JobFunction function, sys::Task::TaskData taskData); static inline std::shared_ptr create(data::DataContext &context, + sdl2::Renderer &renderer, DestructFunction destructFunction, sys::threadpool::JobFunction function, sys::Task::TaskData taskData) - { - return std::make_shared(context, destructFunction, function, taskData); - } + { return std::make_shared(context, renderer, destructFunction, function, taskData); } static inline std::shared_ptr create_and_push(data::DataContext &context, + sdl2::Renderer &renderer, DestructFunction destructFunction, sys::threadpool::JobFunction function, sys::Task::TaskData taskData) { - auto newState = DataLoadingState::create(context, destructFunction, function, taskData); + auto newState = DataLoadingState::create(context, renderer, destructFunction, function, taskData); StateManager::push_state(newState); return newState; } /// @brief Update override. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Updates the loading glyph. void sub_update() override; /// @brief Render override. - void render() override; + void render(sdl2::Renderer &renderer) override; private: /// @brief Reference to the data context to run post-init operations. data::DataContext &m_context; + /// @brief Reference to SDL2 renderer for loading icons. + sdl2::Renderer &m_renderer; + /// @brief X coord of the status text. int m_statusX{}; @@ -57,7 +61,10 @@ class DataLoadingState final : public BaseTask DestructFunction m_destructFunction{}; /// @brief Icon displayed in the center of the screen. - static inline sdl::SharedTexture sm_jksvIcon{}; + static inline sdl2::SharedTexture sm_jksvIcon{}; + + /// @brief Font used for rendering the status. + static inline sdl2::SharedFont sm_font{}; /// @brief Loads the icon if it hasn't been already. void initialize_static_members(); diff --git a/include/appstates/ExtrasMenuState.hpp b/include/appstates/ExtrasMenuState.hpp index 3314c23..37836f2 100644 --- a/include/appstates/ExtrasMenuState.hpp +++ b/include/appstates/ExtrasMenuState.hpp @@ -9,26 +9,30 @@ class ExtrasMenuState final : public BaseState { public: /// @brief Constructor. - ExtrasMenuState(); + ExtrasMenuState(sdl2::Renderer &renderer); /// @brief Returns a new ExtrasMenuState - static inline std::shared_ptr create() { return std::make_shared(); } + static inline std::shared_ptr create(sdl2::Renderer &renderer) + { return std::make_shared(renderer); } /// @brief Updates the menu. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Sub-update routine. void sub_update() override; /// @brief Renders the menu to screen. - void render() override; + void render(sdl2::Renderer &renderer) override; private: + /// @brief Reference to renderer for launching refresh. + sdl2::Renderer &m_renderer; + /// @brief Menu std::shared_ptr m_extrasMenu{}; /// @brief Render target for menu. - sdl::SharedTexture m_renderTarget{}; + sdl2::SharedTexture m_renderTarget{}; /// @brief Control guider for bottom right corner. std::shared_ptr m_controlGuide{}; diff --git a/include/appstates/FadeState.hpp b/include/appstates/FadeState.hpp index a8c03af..3d29202 100644 --- a/include/appstates/FadeState.hpp +++ b/include/appstates/FadeState.hpp @@ -18,19 +18,17 @@ class FadeState final : public BaseState /// @brief Creates a new fade in state. /// @param nextState The next state to push after the the fade is finished. - FadeState(sdl::Color baseColor, uint8_t startAlpha, uint8_t endAlpha, std::shared_ptr nextState); + FadeState(SDL_Color baseColor, uint8_t startAlpha, uint8_t endAlpha, std::shared_ptr nextState); /// @brief Returns a new fade in state. See constructor. - static inline std::shared_ptr create(sdl::Color baseColor, + static inline std::shared_ptr create(SDL_Color baseColor, uint8_t startAlpha, uint8_t endAlpha, std::shared_ptr nextState) - { - return std::make_shared(baseColor, startAlpha, endAlpha, nextState); - } + { return std::make_shared(baseColor, startAlpha, endAlpha, nextState); } /// @brief Creates, returns and pushes a new FadeInState to the statemanager. - static std::shared_ptr create_and_push(sdl::Color baseColor, + static std::shared_ptr create_and_push(SDL_Color baseColor, uint8_t startAlpha, uint8_t endAlpha, std::shared_ptr nextState) @@ -41,13 +39,14 @@ class FadeState final : public BaseState } /// @brief Update override. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Render override. - void render() override; + void render(sdl2::Renderer &renderer) override; private: - sdl::Color m_baseColor{}; + /// @brief Base color of the fade. + SDL_Color m_baseColor{}; /// @brief Alpha value. uint8_t m_alpha{}; diff --git a/include/appstates/FileModeState.hpp b/include/appstates/FileModeState.hpp index 7deb9f9..7633d95 100644 --- a/include/appstates/FileModeState.hpp +++ b/include/appstates/FileModeState.hpp @@ -17,9 +17,7 @@ class FileModeState final : public BaseState std::string_view mountB, int64_t journalSize = 0, bool isSystem = false) - { - return std::make_shared(mountA, mountB, journalSize, isSystem); - } + { return std::make_shared(mountA, mountB, journalSize, isSystem); } static inline std::shared_ptr create_and_push(std::string_view mountA, std::string_view mountB, @@ -32,10 +30,10 @@ class FileModeState final : public BaseState } /// @brief Update override. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Render override. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief This thing is a headache without this. friend class FileOptionState; @@ -95,7 +93,7 @@ class FileModeState final : public BaseState static inline std::shared_ptr sm_frame{}; /// @brief This is the render target the browsers are rendered to. - static inline sdl::SharedTexture sm_renderTarget{}; + static inline sdl2::SharedTexture sm_renderTarget{}; /// @brief Control guide shared by all instances. static inline std::shared_ptr sm_controlGuide{}; @@ -116,7 +114,7 @@ class FileModeState final : public BaseState void update_y() noexcept; /// @brief Handles input. - void update_handle_input() noexcept; + void update_handle_input(const sdl2::Input &input) noexcept; /// @brief Handles changing the current directory or opening the options. void enter_selected(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu); diff --git a/include/appstates/FileOptionState.hpp b/include/appstates/FileOptionState.hpp index 52925cc..1c9232a 100644 --- a/include/appstates/FileOptionState.hpp +++ b/include/appstates/FileOptionState.hpp @@ -17,9 +17,7 @@ class FileOptionState final : public BaseState /// @brief Inline creation function. static inline std::shared_ptr create(FileModeState *spawningState) - { - return std::make_shared(spawningState); - } + { return std::make_shared(spawningState); } /// @brief Same as above. Pushes state before returning it. static inline std::shared_ptr create_and_push(FileModeState *spawningState) @@ -30,10 +28,10 @@ class FileOptionState final : public BaseState } /// @brief Update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief Signals to this state to update the source/target menu on the next update() call. void update_source(); @@ -95,7 +93,7 @@ class FileOptionState final : public BaseState void update_dimensions() noexcept; /// @brief Updates and handles input. - void update_handle_input() noexcept; + void update_handle_input(const sdl2::Input &input) noexcept; /// @brief Updates the FileModeState's source data. void update_filemode_source(); diff --git a/include/appstates/MainMenuState.hpp b/include/appstates/MainMenuState.hpp index a5c5084..801d30b 100644 --- a/include/appstates/MainMenuState.hpp +++ b/include/appstates/MainMenuState.hpp @@ -13,27 +13,28 @@ class MainMenuState final : public BaseState { public: /// @brief Creates and initializes the main menu. - MainMenuState(); + MainMenuState(sdl2::Renderer &renderer); /// @brief Returns a new MainMenuState - static inline std::shared_ptr create() { return std::make_shared(); } + static inline std::shared_ptr create(sdl2::Renderer &renderer) + { return std::make_shared(renderer); } /// @brief Creates and returns a new MainMenuState. Pushes it automatically. - static inline std::shared_ptr create_and_push() + static inline std::shared_ptr create_and_push(sdl2::Renderer &renderer) { - auto newState = MainMenuState::create(); + auto newState = MainMenuState::create(renderer); StateManager::push_state(newState); return newState; } /// @brief Runs update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Runs the sub-update routine. void sub_update() override; /// @brief Renders menu to screen. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief Allows the update task to signal it found an update. void signal_update_found(); @@ -54,16 +55,16 @@ class MainMenuState final : public BaseState private: /// @brief Render target this state renders to. - sdl::SharedTexture m_renderTarget{}; + sdl2::SharedTexture m_renderTarget{}; /// @brief The background gradient. - sdl::SharedTexture m_background{}; + sdl2::SharedTexture m_background{}; /// @brief Icon for the settings option, - sdl::SharedTexture m_settingsIcon{}; + sdl2::SharedTexture m_settingsIcon{}; /// @brief Icon for the extras option. - sdl::SharedTexture m_extrasIcon{}; + sdl2::SharedTexture m_extrasIcon{}; /// @brief Special menu type that uses icons. std::shared_ptr m_mainMenu{}; @@ -93,7 +94,7 @@ class MainMenuState final : public BaseState static inline std::vector> sm_states{}; /// @brief Creates the settings and extras. - void initialize_settings_extras(); + void initialize_settings_extras(sdl2::Renderer &renderer); /// @brief Pushes the icons to the main menu. void initialize_menu(); diff --git a/include/appstates/MessageState.hpp b/include/appstates/MessageState.hpp index 7958fc7..e8db799 100644 --- a/include/appstates/MessageState.hpp +++ b/include/appstates/MessageState.hpp @@ -21,15 +21,11 @@ class MessageState final : public BaseState /// @brief Creates and returns a new MessageState. See constructor. static inline std::shared_ptr create(std::string_view message) - { - return std::make_shared(message); - } + { return std::make_shared(message); } /// @brief Creates and returns a new MessageState. See constructor. static inline std::shared_ptr create(std::string &message) - { - return std::make_shared(message); - } + { return std::make_shared(message); } /// @brief Same as above, only pushed to the StateManager before return. static inline std::shared_ptr create_and_push(std::string_view message) @@ -66,10 +62,10 @@ class MessageState final : public BaseState } /// @brief Update override. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Render override - void render() override; + void render(sdl2::Renderer &renderer) override; private: /// @brief States this state can be in. @@ -102,7 +98,13 @@ class MessageState final : public BaseState static inline std::shared_ptr sm_dialog{}; /// @brief This is the same sound that the confirmation uses. - static inline sdl::SharedSound sm_dialogPop{}; + static inline sdl2::SharedSound sm_dialogPop{}; + + /// @brief Font used to render the message. + static inline sdl2::SharedFont sm_textFont{}; + + /// @brief Font used to render the OK. + static inline sdl2::SharedFont sm_optionFont{}; /// @brief Allocates and ensures ^ void initialize_static_members(); @@ -111,7 +113,7 @@ class MessageState final : public BaseState void update_dimensions() noexcept; /// @brief Updates and handles the input. - void update_handle_input() noexcept; + void update_handle_input(const sdl2::Input &input) noexcept; /// @brief Closes and "hides" the dialog. void close_dialog(); diff --git a/include/appstates/ProgressState.hpp b/include/appstates/ProgressState.hpp index b726c4e..8cf28eb 100644 --- a/include/appstates/ProgressState.hpp +++ b/include/appstates/ProgressState.hpp @@ -20,9 +20,7 @@ class ProgressState final : public BaseTask /// @brief Creates and returns a new ProgressState static inline std::shared_ptr create(sys::threadpool::JobFunction function, sys::Task::TaskData taskData) - { - return std::make_shared(function, taskData); - } + { return std::make_shared(function, taskData); } /// @brief Creates, pushes, then returns a new ProgressState. static inline std::shared_ptr create_and_push(sys::threadpool::JobFunction function, @@ -42,10 +40,10 @@ class ProgressState final : public BaseTask } /// @brief Checks if the thread is finished and deactivates this state. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Renders the current progress to screen. - void render() override; + void render(sdl2::Renderer &renderer) override; private: /// @brief States this state can be in. @@ -84,7 +82,10 @@ class ProgressState final : public BaseTask static inline std::shared_ptr sm_dialog{}; /// @brief This is rendered over the edges of the bar to give it a slightly rounded look. - static inline sdl::SharedTexture sm_barEdges{}; + static inline sdl2::SharedTexture sm_barEdges{}; + + /// @brief Font used for rendering text. + static inline sdl2::SharedFont sm_font{}; /// @brief Initializes the shared dialog box. void initialize_static_members(); diff --git a/include/appstates/SaveCreateState.hpp b/include/appstates/SaveCreateState.hpp index 0bac497..996082f 100644 --- a/include/appstates/SaveCreateState.hpp +++ b/include/appstates/SaveCreateState.hpp @@ -19,9 +19,7 @@ class SaveCreateState final : public BaseState /// @brief Returns a new SaveCreate state. See constructor for arguments. static inline std::shared_ptr create(data::User *user, TitleSelectCommon *titleSelect) - { - return std::make_shared(user, titleSelect); - } + { return std::make_shared(user, titleSelect); } /// @brief Creates, pushes, returns and new SaveCreateState. static inline std::shared_ptr create_and_push(data::User *user, TitleSelectCommon *titleSelect) @@ -32,10 +30,10 @@ class SaveCreateState final : public BaseState } /// @brief Runs the update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Runs the render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief This signals so data and the view can be refreshed on the next update() to avoid threading shenanigans. void refresh_required(); diff --git a/include/appstates/SaveImportState.hpp b/include/appstates/SaveImportState.hpp index 3b30a8c..eebaec7 100644 --- a/include/appstates/SaveImportState.hpp +++ b/include/appstates/SaveImportState.hpp @@ -15,9 +15,7 @@ class SaveImportState final : public BaseState SaveImportState(data::User *user); static inline std::shared_ptr create(data::User *user) - { - return std::make_shared(user); - } + { return std::make_shared(user); } static inline std::shared_ptr create_and_push(data::User *user) { @@ -26,9 +24,9 @@ class SaveImportState final : public BaseState return newState; } - void update() override; + void update(const sdl2::Input &input) override; - void render() override; + void render(sdl2::Renderer &renderer) override; // clang-format off // Struct used to pass data to the task. diff --git a/include/appstates/SettingsState.hpp b/include/appstates/SettingsState.hpp index b0b133b..a168246 100644 --- a/include/appstates/SettingsState.hpp +++ b/include/appstates/SettingsState.hpp @@ -15,13 +15,13 @@ class SettingsState final : public BaseState static inline std::shared_ptr create() { return std::make_shared(); } /// @brief Runs the update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Sub update routine. void sub_update() override; /// @brief Runs the render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; private: /// @brief Menu for selecting and toggling settings. @@ -35,7 +35,7 @@ class SettingsState final : public BaseState std::shared_ptr m_controlGuide{}; /// @brief Render target to render to. - sdl::SharedTexture m_renderTarget{}; + sdl2::SharedTexture m_renderTarget{}; /// @brief Loads the settings menu strings. void load_settings_menu(); diff --git a/include/appstates/TaskState.hpp b/include/appstates/TaskState.hpp index b77f765..342eb59 100644 --- a/include/appstates/TaskState.hpp +++ b/include/appstates/TaskState.hpp @@ -16,9 +16,7 @@ class TaskState final : public BaseTask /// @brief Constructs and returns a TaskState. static inline std::shared_ptr create(sys::threadpool::JobFunction function, sys::Task::TaskData taskData) - { - return std::make_shared(function, taskData); - } + { return std::make_shared(function, taskData); } /// @brief Constructs, pushes, then returns a new TaskState. static inline std::shared_ptr create_and_push(sys::threadpool::JobFunction function, @@ -38,13 +36,19 @@ class TaskState final : public BaseTask } /// @brief Runs update routine. Waits for thread function to signal finish and deactivates. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Run render routine. Prints m_task's status string to screen, basically. /// @param - void render() override; + void render(sdl2::Renderer &renderer) override; private: + /// @brief Font used for rendering text. + static inline sdl2::SharedFont sm_font{}; + + /// @brief Ensures static members are initialized. + void initialize_static_members(); + /// @brief Performs some operations and marks the state for deletion. void deactivate_state(); }; diff --git a/include/appstates/TextTitleSelectState.hpp b/include/appstates/TextTitleSelectState.hpp index e32de3f..e5862e7 100644 --- a/include/appstates/TextTitleSelectState.hpp +++ b/include/appstates/TextTitleSelectState.hpp @@ -15,9 +15,7 @@ class TextTitleSelectState final : public TitleSelectCommon /// @brief Creates and returns a new TextTitleSelect. See constructor. static inline std::shared_ptr create(data::User *user) - { - return std::make_shared(user); - } + { return std::make_shared(user); } /// @brief Creates, pushes, and returns a new TextTitleSelect. static std::shared_ptr create_and_push(data::User *user) @@ -28,10 +26,10 @@ class TextTitleSelectState final : public TitleSelectCommon } /// @brief Runs update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Runs render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief Refreshes view for changes. void refresh() override; @@ -44,7 +42,7 @@ class TextTitleSelectState final : public TitleSelectCommon std::shared_ptr m_titleSelectMenu{}; /// @brief Target to render to. - sdl::SharedTexture m_renderTarget{}; + sdl2::SharedTexture m_renderTarget{}; /// @brief Creates a new backup menu instance. void create_backup_menu(); diff --git a/include/appstates/TitleInfoState.hpp b/include/appstates/TitleInfoState.hpp index c7f25c3..ec09bcc 100644 --- a/include/appstates/TitleInfoState.hpp +++ b/include/appstates/TitleInfoState.hpp @@ -21,9 +21,7 @@ class TitleInfoState final : public BaseState static inline std::shared_ptr create(data::User *user, data::TitleInfo *titleInfo, const FsSaveDataInfo *saveInfo) - { - return std::make_shared(user, titleInfo, saveInfo); - } + { return std::make_shared(user, titleInfo, saveInfo); } /// @brief Creates, pushes, and returns a new TitleInfoState. static inline std::shared_ptr create_and_push(data::User *user, @@ -36,10 +34,10 @@ class TitleInfoState final : public BaseState } /// @brief Runs update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Runs render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; private: /// @brief States this state can be in. @@ -60,7 +58,7 @@ class TitleInfoState final : public BaseState const FsSaveDataInfo *m_saveInfo{}; /// @brief This is a pointer to the title's icon. - sdl::SharedTexture m_icon{}; + sdl2::SharedTexture m_icon{}; /// @brief Transition for the open/close effect. ui::Transition m_transition{}; @@ -84,7 +82,7 @@ class TitleInfoState final : public BaseState static inline std::shared_ptr sm_frame{}; /// @brief This is the little chime that plays when this is opened. - static inline sdl::SharedSound sm_openChime{}; + static inline sdl2::SharedSound sm_openChime{}; /// @brief Initializes the static members if they haven't been already. void initialize_static_members(); @@ -126,10 +124,10 @@ class TitleInfoState final : public BaseState void update_dimensions() noexcept; /// @brief Handles input and updating. - void update_handle_input() noexcept; + void update_handle_input(const sdl2::Input &input) noexcept; /// @brief Returns the color to clear the field with. - inline sdl::Color get_field_color() noexcept; + inline SDL_Color get_field_color() noexcept; /// @brief Signals to close the state. void close() noexcept; diff --git a/include/appstates/TitleOptionState.hpp b/include/appstates/TitleOptionState.hpp index 9a874c2..7489806 100644 --- a/include/appstates/TitleOptionState.hpp +++ b/include/appstates/TitleOptionState.hpp @@ -23,9 +23,7 @@ class TitleOptionState final : public BaseState data::TitleInfo *titleInfo, const FsSaveDataInfo *saveInfo, TitleSelectCommon *titleSelect) - { - return std::make_shared(user, titleInfo, saveInfo, titleSelect); - } + { return std::make_shared(user, titleInfo, saveInfo, titleSelect); } /// @brief Creates, pushes, and returns a new TitleOptionState static std::shared_ptr create_and_push(data::User *user, @@ -39,13 +37,13 @@ class TitleOptionState final : public BaseState } /// @brief Runs update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Handles hiding the panel. void sub_update() override; /// @brief Runs the render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief This function allows tasks to signal to the spawning state to close itself on the next update() call. void close_on_update(); diff --git a/include/appstates/TitleSelectCommon.hpp b/include/appstates/TitleSelectCommon.hpp index a152665..3f96104 100644 --- a/include/appstates/TitleSelectCommon.hpp +++ b/include/appstates/TitleSelectCommon.hpp @@ -14,13 +14,13 @@ class TitleSelectCommon : public BaseState virtual ~TitleSelectCommon() {}; /// @brief Required, inherited. - virtual void update() = 0; + virtual void update(const sdl2::Input &input) = 0; /// @brief Sub-update routine. Normally in a file, but I didn't feel like it really needed one. void sub_update() override; /// @brief Required, inherited. - virtual void render() = 0; + virtual void render(sdl2::Renderer &renderer) = 0; /// @brief Both derived classes need this function. virtual void refresh() = 0; diff --git a/include/appstates/TitleSelectState.hpp b/include/appstates/TitleSelectState.hpp index 35b1e12..134b08e 100644 --- a/include/appstates/TitleSelectState.hpp +++ b/include/appstates/TitleSelectState.hpp @@ -15,9 +15,7 @@ class TitleSelectState final : public TitleSelectCommon /// @brief Returns a new TitleSelect state. static inline std::shared_ptr create(data::User *user) - { - return std::make_shared(user); - } + { return std::make_shared(user); } /// @brief Creates, pushes, and returns a new TitleSelectState. static inline std::shared_ptr create_and_push(data::User *user) @@ -28,10 +26,10 @@ class TitleSelectState final : public TitleSelectCommon } /// @brief Runs the update routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Runs the render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief Refreshes the view. void refresh() override; @@ -41,7 +39,7 @@ class TitleSelectState final : public TitleSelectCommon data::User *m_user{}; /// @brief Target to render to. - sdl::SharedTexture m_renderTarget{}; + sdl2::SharedTexture m_renderTarget{}; /// @brief Tiled title selection view. std::shared_ptr m_titleView{}; diff --git a/include/appstates/UserOptionState.hpp b/include/appstates/UserOptionState.hpp index 22b004a..b1d15cf 100644 --- a/include/appstates/UserOptionState.hpp +++ b/include/appstates/UserOptionState.hpp @@ -18,9 +18,7 @@ class UserOptionState final : public BaseState /// @brief Returns a new UserOptionState. See constructor. static inline std::shared_ptr create(data::User *user, TitleSelectCommon *titleSelect) - { - return std::make_shared(user, titleSelect); - } + { return std::make_shared(user, titleSelect); } /// @brief Creates, pushes, and returns a new UserOptionState. static inline std::shared_ptr create_and_push(data::User *user, TitleSelectCommon *titleSelect) @@ -31,13 +29,13 @@ class UserOptionState final : public BaseState } /// @brief Runs the render routine. - void update() override; + void update(const sdl2::Input &input) override; /// @brief Handles hiding the panel. void sub_update() override; /// @brief Runs the render routine. - void render() override; + void render(sdl2::Renderer &renderer) override; /// @brief Signals to the main update() function that a refresh is needed. /// @note Like this to prevent threading headaches. diff --git a/include/data/DataCommon.hpp b/include/data/DataCommon.hpp index 8b7c242..c994e19 100644 --- a/include/data/DataCommon.hpp +++ b/include/data/DataCommon.hpp @@ -10,16 +10,16 @@ namespace data DataCommon() = default; /// @brief Function to load the icon to a texture. - virtual void load_icon() = 0; + virtual void load_icon(sdl2::Renderer &renderer) = 0; /// @brief Returns the icon. - sdl::SharedTexture get_icon() { return m_icon; }; + sdl2::SharedTexture &get_icon() { return m_icon; }; /// @brief Sets the icon. - void set_icon(sdl::SharedTexture &icon) { m_icon = icon; }; + void set_icon(sdl2::SharedTexture &icon) { m_icon = icon; }; protected: /// @brief Shared texture of the icon. - sdl::SharedTexture m_icon{}; + sdl2::SharedTexture m_icon{}; }; } diff --git a/include/data/DataContext.hpp b/include/data/DataContext.hpp index 2f2d2d1..4c21138 100644 --- a/include/data/DataContext.hpp +++ b/include/data/DataContext.hpp @@ -2,6 +2,7 @@ #include "data/DataCommon.hpp" #include "data/TitleInfo.hpp" #include "data/User.hpp" +#include "sdl.hpp" #include "sys/Task.hpp" #include @@ -56,7 +57,7 @@ namespace data bool write_cache(sys::Task *task); /// @brief Processes the icon queue. - void process_icon_queue(); + void process_icon_queue(sdl2::Renderer &renderer); private: /// @brief User vector. diff --git a/include/data/TitleInfo.hpp b/include/data/TitleInfo.hpp index 353e2b5..9edaa08 100644 --- a/include/data/TitleInfo.hpp +++ b/include/data/TitleInfo.hpp @@ -83,16 +83,12 @@ namespace data /// @return True on success. False on failure. bool has_save_data_type(uint8_t saveType) const noexcept; - /// @brief Returns a pointer to the icon texture. - /// @return Icon - sdl::SharedTexture get_icon() const noexcept; - /// @brief Allows the path safe title to be set to a new path. /// @param newPathSafe Buffer containing the new safe path to use. void set_path_safe_title(const char *newPathSafe) noexcept; /// @brief Loads the icon from the nacp. - void load_icon() override; + void load_icon(sdl2::Renderer &renderer) override; private: /// @brief This defines how long the buffer is for the path safe version of the title. @@ -114,7 +110,7 @@ namespace data char m_pathSafeTitle[TitleInfo::SIZE_PATH_SAFE]{}; /// @brief Shared icon texture. - sdl::SharedTexture m_icon{}; + sdl2::SharedTexture m_icon{}; /// @brief Private function to get/create the path safe title. void get_create_path_safe_title() noexcept; diff --git a/include/data/User.hpp b/include/data/User.hpp index bf925e3..7e7ae0c 100644 --- a/include/data/User.hpp +++ b/include/data/User.hpp @@ -109,7 +109,7 @@ namespace data void load_user_data(); /// @brief Loads the icon from the system and converts it to a texture. - void load_icon() override; + void load_icon(sdl2::Renderer &renderer) override; private: /// @brief Account's ID diff --git a/include/data/data.hpp b/include/data/data.hpp index 8a307cf..c56a0e4 100644 --- a/include/data/data.hpp +++ b/include/data/data.hpp @@ -2,6 +2,7 @@ #include "data/TitleInfo.hpp" #include "data/User.hpp" #include "data/accountUID.hpp" +#include "sdl.hpp" #include "sys/sys.hpp" #include @@ -13,7 +14,7 @@ namespace data /// @brief Launches the data loading/initialization state. /// @param clear Whether or not the cache should be cleared. /// @param onDestruction Function that is executed upon destruction of the data loading screen. - void launch_initialization(bool clear, std::function onDestruction); + void launch_initialization(bool clear, sdl2::Renderer &renderer, std::function onDestruction); /// @brief Writes pointers to users to vectorOut /// @param userList List to push the pointers to. diff --git a/include/graphics/ScopedRender.hpp b/include/graphics/ScopedRender.hpp new file mode 100644 index 0000000..5975b66 --- /dev/null +++ b/include/graphics/ScopedRender.hpp @@ -0,0 +1,43 @@ +#pragma once +#include "sdl.hpp" + +#include + +namespace graphics +{ + /// @brief This is a workaround class because of SDLLib changes and I'm not willing to sink a ton of time into JKSV anymore. + class ScopedRender final + { + public: + /// @brief Constructs a new scoped rendering sequence. + /// @param renderer Reference to the renderer. + /// @param target Render target. + ScopedRender(sdl2::Renderer &renderer, sdl2::SharedTexture target) + : m_renderer{renderer} + { + // Push the target. + sm_renderTargets.push(target); + + // Set the target. + renderer.set_render_target(sm_renderTargets.top()); + } + + /// @brief Ends the ScopedRender'ing. + ~ScopedRender() + { + // Pop. + sm_renderTargets.pop(); + + // Set. + m_renderer.set_render_target(sm_renderTargets.top()); + } + + private: + /// @brief Reference to the renderer. + sdl2::Renderer &m_renderer; + + /// @brief Stack of render targets. Init'd with null as the base. + static inline std::stack sm_renderTargets = + std::stack({sdl2::Texture::null}); + }; +} \ No newline at end of file diff --git a/include/graphics/colors.hpp b/include/graphics/colors.hpp index 485f87c..77b8f27 100644 --- a/include/graphics/colors.hpp +++ b/include/graphics/colors.hpp @@ -3,26 +3,26 @@ namespace colors { - inline constexpr sdl::Color WHITE = {0xFFFFFFFF}; - inline constexpr sdl::Color BLACK = {0x000000FF}; - inline constexpr sdl::Color RED = {0xFF0000FF}; - inline constexpr sdl::Color DARK_RED = {0xDD0000FF}; - inline constexpr sdl::Color GREEN = {0x00FF00FF}; - inline constexpr sdl::Color BLUE = {0x0099EEFF}; - inline constexpr sdl::Color YELLOW = {0xF8FC00FF}; - inline constexpr sdl::Color PINK = {0xFF4444FF}; - inline constexpr sdl::Color BLUE_GREEN = {0x00FFC5FF}; - inline constexpr sdl::Color CLEAR_COLOR = {0x2D2D2DFF}; - inline constexpr sdl::Color CLEAR_PANEL = {0x0D0D0DFF}; - inline constexpr sdl::Color DIALOG_DARK = {0x505050FF}; - inline constexpr sdl::Color DIALOG_LIGHT = {0xDCDCDCFF}; - inline constexpr sdl::Color DIM_BACKGROUND = {0x00061180}; - inline constexpr sdl::Color TRANSPARENT = {0x00000000}; - inline constexpr sdl::Color SLIDE_PANEL_CLEAR = {0x000000CC}; - inline constexpr sdl::Color DIV_COLOR = {0x707070FF}; - inline constexpr sdl::Color GUIDE_COLOR = {0x0000007F}; - inline constexpr sdl::Color BAR_GREEN = {0x11CC33FF}; - inline constexpr sdl::Color GOLD = {0xFFD700FF}; + inline constexpr SDL_Color WHITE = {0xFF, 0xFF, 0xFF, 0xFF}; + inline constexpr SDL_Color BLACK = {0x00, 0x00, 0x00, 0xFF}; + inline constexpr SDL_Color RED = {0xFF, 0x00, 0x00, 0xFF}; + inline constexpr SDL_Color DARK_RED = {0xDD, 0x00, 0x00, 0xFF}; + inline constexpr SDL_Color GREEN = {0x00, 0xFF, 0x00, 0xFF}; + inline constexpr SDL_Color BLUE = {0x00, 0x99, 0xEE, 0xFF}; + inline constexpr SDL_Color YELLOW = {0xF8, 0xFC, 0x00, 0xFF}; + inline constexpr SDL_Color PINK = {0xFF, 0x44, 0x44, 0xFF}; + inline constexpr SDL_Color BLUE_GREEN = {0x00, 0xFF, 0xC5, 0xFF}; + inline constexpr SDL_Color CLEAR_COLOR = {0x2D, 0x2D, 0x2D, 0xFF}; + inline constexpr SDL_Color CLEAR_PANEL = {0x0D, 0x0D, 0x0D, 0xFF}; + inline constexpr SDL_Color DIALOG_DARK = {0x50, 0x50, 0x50, 0xFF}; + inline constexpr SDL_Color DIALOG_LIGHT = {0xDC, 0xDC, 0xDC, 0xFF}; + inline constexpr SDL_Color DIM_BACKGROUND = {0x00, 0x06, 0x11, 0x80}; + inline constexpr SDL_Color TRANSPARENT = {0x00, 0x00, 0x00, 0x00}; + inline constexpr SDL_Color SLIDE_PANEL_CLEAR = {0x00, 0x00, 0x00, 0xCC}; + inline constexpr SDL_Color DIV_COLOR = {0x70, 0x70, 0x70, 0xFF}; + inline constexpr SDL_Color GUIDE_COLOR = {0x00, 0x00, 0x00, 0x7F}; + inline constexpr SDL_Color BAR_GREEN = {0x11, 0xCC, 0x33, 0xFF}; + inline constexpr SDL_Color GOLD = {0xFF, 0xD7, 0x00, 0xFF}; inline constexpr uint8_t ALPHA_FADE_BEGIN = 0x00; inline constexpr uint8_t ALPHA_FADE_END = 0x80; diff --git a/include/graphics/fonts.hpp b/include/graphics/fonts.hpp new file mode 100644 index 0000000..29f63d8 --- /dev/null +++ b/include/graphics/fonts.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +namespace graphics::fonts +{ + namespace names + { + // These are all the internal names of the fonts so the mananger pulls and reuses them. + inline constexpr std::string_view FOURTEEN_PIXEL = "FourteenPixel"; + inline constexpr std::string_view TWENTY_PIXEL = "TwentyPixel"; + inline constexpr std::string_view TWENTY_TWO_PIXEL = "TwentyTwoPixel"; + inline constexpr std::string_view TWENTY_FOUR_PIXEL = "TwentyFourPixel"; + inline constexpr std::string_view THIRTY_TWO_PIXEL = "ThirtyTwoPixel"; + inline constexpr std::string_view THIRTY_FOUR_PIXEL = "ThirtyFourPixel"; + } + + namespace sizes + { + // These are the pixel sizes. + inline constexpr int FOURTEEN_PIXEL = 14; + inline constexpr int TWENTY_PIXEL = 20; + inline constexpr int TWENTY_TWO_PIXEL = 22; + inline constexpr int TWENTY_FOUR_PIXEL = 24; + inline constexpr int THIRTY_TWO_PIXEL = 32; + inline constexpr int THIRTY_FOUR_PIXEL = 34; + } + +} \ No newline at end of file diff --git a/include/graphics/gfxutil.hpp b/include/graphics/gfxutil.hpp index a6f2407..7d5cc57 100644 --- a/include/graphics/gfxutil.hpp +++ b/include/graphics/gfxutil.hpp @@ -12,5 +12,9 @@ namespace gfxutil /// @param background Background color to use. /// @param foreground Color to use to render the text. /// @return sdl::SharedTexture of the icon. - sdl::SharedTexture create_generic_icon(std::string_view text, int fontSize, sdl::Color background, sdl::Color foreground); + sdl2::SharedTexture create_generic_icon(sdl2::Renderer &renderer, + std::string_view text, + int fontSize, + SDL_Color background, + SDL_Color textColor); } // namespace gfxutil diff --git a/include/graphics/targets.hpp b/include/graphics/targets.hpp new file mode 100644 index 0000000..31969d3 --- /dev/null +++ b/include/graphics/targets.hpp @@ -0,0 +1,17 @@ +#pragma once +#include + +namespace graphics::targets +{ + namespace names + { + inline constexpr std::string_view SECONDARY = "SecondaryTarget"; + } + + namespace dims + { + // Dimensions of the secondary render target. + inline constexpr int SECONDARY_WIDTH = 1080; + inline constexpr int SECONDARY_HEIGHT = 555; + } +} \ No newline at end of file diff --git a/include/input.hpp b/include/input.hpp deleted file mode 100644 index 00fef10..0000000 --- a/include/input.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include - -namespace input -{ - /// @brief Initializes PadState and input. - void initialize(); - - /// @brief Updates the PadState. - void update() noexcept; - - /// @brief Returns if a button was pressed the current frame, but not the previous. - /// @param button Button to check. - bool button_pressed(HidNpadButton button) noexcept; - - /// @brief Returns if the button was pressed or held the previous and current frame. - /// @param button Button to check. - bool button_held(HidNpadButton button) noexcept; - - /// @brief Returns if the button was pressed or held the previous frame, but not the current. - /// @param button Button to check. - bool button_released(HidNpadButton button) noexcept; -} // namespace input diff --git a/include/mathutil.hpp b/include/mathutil.hpp index 8d1689c..9ffafc8 100644 --- a/include/mathutil.hpp +++ b/include/mathutil.hpp @@ -7,7 +7,11 @@ namespace math class Util { public: - static inline Type absolute_distance(Type a, Type b) noexcept { return a > b ? a - b : b - a; } + /// @brief Calculates the distance between two Types and returns the result as a positive Type. + static inline constexpr Type absolute_distance(Type a, Type b) noexcept { return a > b ? a - b : b - a; } + + /// @brief Returns size centered within area. + static inline constexpr Type center_within(Type area, Type size) noexcept { return (area / 2) - (size / 2); } }; } diff --git a/include/ui/BoundingBox.hpp b/include/ui/BoundingBox.hpp index ec45566..be275d5 100644 --- a/include/ui/BoundingBox.hpp +++ b/include/ui/BoundingBox.hpp @@ -19,15 +19,13 @@ namespace ui /// @brief Creates a returns a new BoundingBox. See constructor. static inline std::shared_ptr create(int x, int y, int width, int height) - { - return std::make_shared(x, y, width, height); - } + { return std::make_shared(x, y, width, height); } /// @brief Update override. - void update(bool hasFocus) override; + void update(const sdl2::Input &input, bool hasFocus) override; /// @brief Render override. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Sets the X coord. void set_x(int x) noexcept; @@ -58,7 +56,7 @@ namespace ui ui::ColorMod m_colorMod{}; /// @brief This is shared by all instances. - static inline sdl::SharedTexture sm_corners{}; + static inline sdl2::SharedTexture sm_corners{}; /// @brief Loads ^ void initialize_static_members(); diff --git a/include/ui/ColorMod.hpp b/include/ui/ColorMod.hpp index e76f92a..0237a91 100644 --- a/include/ui/ColorMod.hpp +++ b/include/ui/ColorMod.hpp @@ -17,7 +17,7 @@ namespace ui /// @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() const noexcept; + operator SDL_Color() const noexcept; private: /// @brief Whether we're adding or subtracting from the color value. diff --git a/include/ui/ControlGuide.hpp b/include/ui/ControlGuide.hpp index 7af21e1..6425c9d 100644 --- a/include/ui/ControlGuide.hpp +++ b/include/ui/ControlGuide.hpp @@ -14,18 +14,16 @@ namespace ui /// @brief Factory function to return a control guide. static inline std::shared_ptr create(const char *guide) - { - return std::make_shared(guide); - } + { return std::make_shared(guide); } /// @brief Update routine. Opens the guide. - void update(bool hasFocus) override; + void update(const sdl2::Input &input, bool hasFocus) override; /// @brief Sub update routine. Handles hiding the control guide. void sub_update(); /// @brief Renders the control guide. Both arguments are ignored in this case. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief This is a workaround for states where the guide can't really be hidden correctly. Ex: FileMode. void reset() noexcept; @@ -59,7 +57,10 @@ namespace ui ControlGuide::State m_state{}; /// @brief This is shared by all instances. - static inline sdl::SharedTexture sm_controlCap{}; + static inline sdl2::SharedTexture sm_controlCap{}; + + /// @brief Font used to render text. + static inline sdl2::SharedFont sm_font{}; /// @brief Ensures the control guide cap is loaded for all instances. void initialize_static_members(); diff --git a/include/ui/DialogBox.hpp b/include/ui/DialogBox.hpp index e0dfbb8..1715f3b 100644 --- a/include/ui/DialogBox.hpp +++ b/include/ui/DialogBox.hpp @@ -29,17 +29,15 @@ namespace ui int width, int height, DialogBox::Type type = DialogBox::Type::Dark) - { - return std::make_shared(x, y, width, height, type); - } + { return std::make_shared(x, y, width, height, type); } /// @brief Update override. This does NOTHING! - void update(bool hasFocus) override {}; + void update(const sdl2::Input &input, bool hasFocus) override {}; /// @brief Renders the dialog box to screen. /// @param target Render target to render to. /// @param hasFocus This is ignored. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Sets the X render coord. void set_x(int x) noexcept; @@ -75,10 +73,10 @@ namespace ui DialogBox::Type m_type{}; /// @brief All instances shared this and the other. - static inline sdl::SharedTexture sm_darkCorners{}; + static inline sdl2::SharedTexture sm_darkCorners{}; /// @brief This is the light cer - static inline sdl::SharedTexture sm_lightCorners{}; + static inline sdl2::SharedTexture sm_lightCorners{}; /// @brief Initializes and loads the corners texture. void initialize_static_members(); diff --git a/include/ui/Element.hpp b/include/ui/Element.hpp index 4535f31..4d2489f 100644 --- a/include/ui/Element.hpp +++ b/include/ui/Element.hpp @@ -15,11 +15,11 @@ namespace ui /// @brief Virtual update method. All derived classes must have this. /// @param HasFocus Whether or not the state containing the element currently has focus. - virtual void update(bool HasFocus) = 0; + virtual void update(const sdl2::Input &input, bool HasFocus) = 0; /// @brief Virtual render method. All derived classes must have this. /// @param target Target to render to. /// @param hasFocus Whether or not the containing state has focus. - virtual void render(sdl::SharedTexture &target, bool hasFocus) = 0; + virtual void render(sdl2::Renderer &renderer, bool hasFocus) = 0; }; } // namespace ui diff --git a/include/ui/Frame.hpp b/include/ui/Frame.hpp index 376e7b7..8695036 100644 --- a/include/ui/Frame.hpp +++ b/include/ui/Frame.hpp @@ -15,15 +15,13 @@ namespace ui /// @brief Inline function to make constructing nicer. static inline std::shared_ptr create(int x, int y, int width, int height) - { - return std::make_shared(x, y, width, height); - } + { return std::make_shared(x, y, width, height); } /// @brief Doesn't need to do anything for this. - void update(bool hasFocus) override {}; + void update(const sdl2::Input &input, bool hasFocus) override {}; /// @brief Renders the frame to the target passed. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Sets the X coord. void set_x(int x) noexcept; @@ -54,7 +52,7 @@ namespace ui int m_height{}; /// @brief This texture is shared by all instances. - static inline sdl::SharedTexture sm_frameCorners{}; + static inline sdl2::SharedTexture sm_frameCorners{}; /// @brief Ensures the texture is loading if it hasn't been. void initialize_static_members(); diff --git a/include/ui/IconMenu.hpp b/include/ui/IconMenu.hpp index 7821b4b..81a33a5 100644 --- a/include/ui/IconMenu.hpp +++ b/include/ui/IconMenu.hpp @@ -20,9 +20,7 @@ namespace ui /// @brief Creates and returns a new IconMenu instance. static inline std::shared_ptr create(int x, int y, int renderTargetHeight) - { - return std::make_shared(x, y, renderTargetHeight); - } + { return std::make_shared(x, y, renderTargetHeight); } /// @brief Initializes the menu. /// @param x X coordinate to render the menu to. @@ -33,19 +31,19 @@ namespace ui /// @brief Runs the update routine. /// @param hasFocus Whether or not the containing state has focus. - void update(bool hasFocus) override; + void update(const sdl2::Input &input, bool hasFocus) override; /// @brief Runs the render routine. /// @param target Target to render to. /// @param hasFocus Whether or not the containing state has focus. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Adds a new icon to the menu. /// @param newOption Icon to add. - void add_option(sdl::SharedTexture newOption); + void add_option(sdl2::SharedTexture newOption); private: /// @brief Vector of shared texture pointers to textures used. - std::vector m_options; + std::vector m_options; }; } // namespace ui diff --git a/include/ui/Menu.hpp b/include/ui/Menu.hpp index 7c0983b..bec3680 100644 --- a/include/ui/Menu.hpp +++ b/include/ui/Menu.hpp @@ -26,18 +26,16 @@ namespace ui /// @brief Creates and returns a new ui::Menu instance. static inline std::shared_ptr create(int x, int y, int width, int fontSize, int renderTargetHeight) - { - return std::make_shared(x, y, width, fontSize, renderTargetHeight); - } + { return std::make_shared(x, y, width, fontSize, renderTargetHeight); } /// @brief Runs the update routine. /// @param hasFocus Whether or not the calling state has focus. - void update(bool HasFocus) override; + void update(const sdl2::Input &input, bool HasFocus) override; /// @brief Renders the menu. /// @param target Target to render to. /// @param hasFocus Whether or not the calling state has focus. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Adds an option to the menu. /// @param newOption Option to add to menu. @@ -94,7 +92,7 @@ namespace ui int m_optionHeight{}; /// @brief Target options are rendered to. - sdl::SharedTexture m_optionTarget{}; + sdl2::SharedTexture m_optionTarget{}; /// @brief Bounding box for the selected option. std::shared_ptr m_boundingBox{}; @@ -127,8 +125,11 @@ namespace ui /// @brief Text scroll for when the current option is too long to on screen. std::shared_ptr m_optionScroll{}; + /// @brief Font used to render the text. + sdl2::SharedFont m_font{}; + /// @brief The sound played when the selected/cursor moves. - static inline sdl::SharedSound sm_cursor{}; + static inline sdl2::SharedSound sm_cursor{}; /// @brief Calculates the alignment variables. void calculate_alignments() noexcept; @@ -152,6 +153,6 @@ namespace ui void update_scrolling(); /// @brief Handles the menu's input routine. - void handle_input(); + void handle_input(const sdl2::Input &input); }; } // namespace ui diff --git a/include/ui/PopMessage.hpp b/include/ui/PopMessage.hpp index 9122f85..ddf38a8 100644 --- a/include/ui/PopMessage.hpp +++ b/include/ui/PopMessage.hpp @@ -7,7 +7,7 @@ namespace ui { - class PopMessage + class PopMessage final { public: /// @brief PopMessage constructor. @@ -20,7 +20,7 @@ namespace ui void update(double targetY); /// @brief Renders the dialog and text. - void render(); + void render(sdl2::Renderer &renderer); /// @brief Returns whether or not the message can be purged. bool finished() const noexcept; @@ -65,7 +65,10 @@ namespace ui sys::Timer m_typeTimer{}; /// @brief This is the texture used for the ends of the messages. - static inline sdl::SharedTexture sm_endCaps{}; + static inline sdl2::SharedTexture sm_endCaps{}; + + /// @brief Font used to render text. + static inline sdl2::SharedFont sm_font{}; /// @brief Ensures ^ is loaded and ready to go. void initialize_static_members(); @@ -83,6 +86,6 @@ namespace ui void update_display_timer() noexcept; /// @brief Renders the container around the message. - void render_container() noexcept; + void render_container(sdl2::Renderer &renderer) noexcept; }; } diff --git a/include/ui/PopMessageManager.hpp b/include/ui/PopMessageManager.hpp index 3e93bc2..c44b4c5 100644 --- a/include/ui/PopMessageManager.hpp +++ b/include/ui/PopMessageManager.hpp @@ -23,7 +23,7 @@ namespace ui static void update(); /// @brief Renders messages to screen. - static void render(); + static void render(sdl2::Renderer &renderer); /// @brief Pushes a new message to the queue for processing. static void push_message(int displayTicks, std::string_view message); @@ -57,7 +57,7 @@ namespace ui std::mutex m_queueMutex{}; /// @brief The little chirp that's played when messages pop. - sdl::SharedSound m_popSound{}; + sdl2::SharedSound m_popSound{}; /// @brief Loads the pop message sound into memory. void initialize_pop_sound(); diff --git a/include/ui/SlideOutPanel.hpp b/include/ui/SlideOutPanel.hpp index 8b866f2..546a7e3 100644 --- a/include/ui/SlideOutPanel.hpp +++ b/include/ui/SlideOutPanel.hpp @@ -29,13 +29,11 @@ namespace ui /// @brief Creates and returns and new ui::SlideOutPanel instance. static inline std::shared_ptr create(int width, SlideOutPanel::Side side) - { - return std::make_shared(width, side); - } + { return std::make_shared(width, side); } /// @brief Runs the update routine. /// @param hasFocus Whether or not the calling state has focus. - void update(bool hasFocus) override; + void update(const sdl2::Input &input, bool hasFocus) override; /// @brief Sub update routine. Allows the panel to hide and unhide itself even when not in focus. void sub_update(); @@ -43,10 +41,10 @@ namespace ui /// @brief Runs the render routine. /// @param target Target to render to. /// @param hasFocus Whether or the the calling state has focus. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Clears the target to a semi-transparent black. To do: Maybe not hard coded color. - void clear_target(); + void clear_target(sdl2::Renderer &renderer); /// @brief Resets the panel back to its default state. void reset() noexcept; @@ -83,7 +81,7 @@ namespace ui /// @brief Returns a pointer to the render target of the panel. /// @return Raw SDL_Texture pointer to target. - sdl::SharedTexture &get_target() noexcept; + sdl2::SharedTexture &get_target() noexcept; private: /// @brief States the panel can be in. @@ -113,7 +111,7 @@ namespace ui SlideOutPanel::State m_state{}; /// @brief Render target if panel. - sdl::SharedTexture m_renderTarget{}; + sdl2::SharedTexture m_renderTarget{}; /// @brief Vector of elements. std::vector> m_elements{}; @@ -131,6 +129,6 @@ namespace ui void update_position_state() noexcept; /// @brief Updates sub-elements. - void update_sub_elements(bool hasFocus) noexcept; + void update_sub_elements(const sdl2::Input &input, bool hasFocus) noexcept; }; } // namespace ui diff --git a/include/ui/TextScroll.hpp b/include/ui/TextScroll.hpp index 1a7fbb5..506e011 100644 --- a/include/ui/TextScroll.hpp +++ b/include/ui/TextScroll.hpp @@ -28,8 +28,8 @@ namespace ui int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center = true); TextScroll(std::string &text, @@ -38,8 +38,8 @@ namespace ui int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center = true); /// @brief Creates and returns a new TextScroll. See constructor. @@ -49,12 +49,10 @@ namespace ui int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center = true) - { - return std::make_shared(text, x, y, width, height, fontSize, textColor, clearColor, center); - } + { return std::make_shared(text, x, y, width, height, fontSize, textColor, clearColor, center); } static inline std::shared_ptr create(std::string &text, int x, @@ -62,12 +60,10 @@ namespace ui int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center = true) - { - return std::make_shared(text, x, y, width, height, fontSize, textColor, clearColor, center); - } + { return std::make_shared(text, x, y, width, height, fontSize, textColor, clearColor, center); } /// @brief Creates/sets the text and parameters for TextScroll. /// @param text Text to display/scroll. @@ -82,8 +78,8 @@ namespace ui int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center = true); void initialize(std::string &text, @@ -92,18 +88,18 @@ namespace ui int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center = true); /// @brief Runs the update routine. /// @param hasFocus Whether or not the calling state has focus. - void update(bool hasFocus) override; + void update(const sdl2::Input &input, bool hasFocus) override; /// @brief Runs the render routine. /// @param target Target to render to. /// @param hasFocus Whether or not the calling state has focus. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Returns the current text being used for scrolling. std::string_view get_text() const noexcept; @@ -133,11 +129,14 @@ namespace ui /// @brief Font size used to calculate and render text. int m_fontSize{}; + /// @brief Font used to render text. + sdl2::SharedFont m_font{}; + /// @brief Color used to render the text. - sdl::Color m_textColor{}; + SDL_Color m_textColor{}; /// @brief Color used to clear the render target. - sdl::Color m_clearColor{}; + SDL_Color m_clearColor{}; /// @brief Width of text in pixels. int m_textWidth{}; @@ -155,9 +154,16 @@ namespace ui bool m_textScrollTriggered{}; /// @brief Render target for the text so it can't be rendered outside of it. - sdl::SharedTexture m_renderTarget{}; + sdl2::SharedTexture m_renderTarget{}; /// @brief Timer for scrolling text. sys::Timer m_scrollTimer; + + /// @brief Generates and returns the name of the render target for the text scroll. + std::string generate_target_name(); + + /// @brief Generates a font name using the font size passed. + /// @param fontSize Size of the font. + std::string generate_font_name(int fontSize); }; -} // namespace ui +} // namespace ui \ No newline at end of file diff --git a/include/ui/TitleTile.hpp b/include/ui/TitleTile.hpp index 5f30656..e6be6cd 100644 --- a/include/ui/TitleTile.hpp +++ b/include/ui/TitleTile.hpp @@ -11,17 +11,17 @@ namespace ui /// @brief Constructor. /// @param isFavorite Whether the title is a favorite and should have the little heart rendered. /// @param icon Shared texture pointer to the icon. - TitleTile(bool isFavorite, int index, sdl::SharedTexture icon); + TitleTile(bool isFavorite, int index, sdl2::SharedTexture &icon); /// @brief Runs the update routine. /// @param isSelected Whether or not the tile is selected and needs to expand. - void update(int selected); + void update(bool isSelected); /// @brief Runs the render routine. /// @param target Target to render to. /// @param x X coordinate to render to. /// @param y Y coordinate to render to. - void render(sdl::SharedTexture &target, int x, int y); + void render(int x, int y); /// @brief Resets the width and height of the tile. void reset() noexcept; @@ -41,10 +41,13 @@ namespace ui /// @brief Whether or not the title is a favorite. bool m_isFavorite{}; - - int m_index{}; - /// @brief Title's icon texture. - sdl::SharedTexture m_icon{}; + sdl2::SharedTexture m_icon{}; + + /// @brief Font used for rendering the heart over the icon. + static inline sdl2::SharedFont sm_heartFont{}; + + /// @brief Ensures the font is loaded., + void initialize_static_members(); }; } // namespace ui diff --git a/include/ui/TitleView.hpp b/include/ui/TitleView.hpp index af2164c..b578fc9 100644 --- a/include/ui/TitleView.hpp +++ b/include/ui/TitleView.hpp @@ -20,18 +20,16 @@ namespace ui TitleView(data::User *user); static inline std::shared_ptr create(data::User *user) - { - return std::make_shared(user); - } + { return std::make_shared(user); } /// @brief Runs the update routine. /// @param hasFocus Whether the calling state has focus. - void update(bool hasFocus) override; + void update(const sdl2::Input &input, bool hasFocus) override; /// @brief Runs the render routine. /// @param target Target to render to. /// @param hasFocus Whether or not the calling state has focus. - void render(sdl::SharedTexture &target, bool hasFocus) override; + void render(sdl2::Renderer &renderer, bool hasFocus) override; /// @brief Returns index of the currently selected tile. /// @return Index of currently selected tile. @@ -75,13 +73,13 @@ namespace ui std::shared_ptr m_bounding{}; /// @brief Sound that is played when the selected title changes. This is shared with the menu code. - static inline sdl::SharedSound sm_cursor{}; + static inline sdl2::SharedSound sm_cursor{}; /// @brief Ensures static members are initialized properly. void initialize_static_members(); /// @brief Performs the input routine. - void handle_input(); + void handle_input(const sdl2::Input &input); /// @brief Performs the "scrolling" routine. void handle_scrolling(); diff --git a/romfs/Text/DE.json.z b/romfs/Text/DE.json.z new file mode 100644 index 0000000000000000000000000000000000000000..84dbc7be23b8d3cd0275742eab4be7b5c55df5b5 GIT binary patch literal 4132 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 literal 0 HcmV?d00001 diff --git a/romfs/Text/ENGB.json.z b/romfs/Text/ENGB.json.z new file mode 100644 index 0000000000000000000000000000000000000000..3b4cc39800d4fd55d784e60793f61c7654e94257 GIT binary patch literal 3701 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 TcnuqiH=QRg-~8|YrvwQ7{ERh2 literal 0 HcmV?d00001 diff --git a/romfs/Text/ENUS.json.z b/romfs/Text/ENUS.json.z new file mode 100644 index 0000000000000000000000000000000000000000..e845992707628059c8bf24d03b6ba4c67d42c913 GIT binary patch literal 3706 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 Li+}weqj%WaV|3V< literal 0 HcmV?d00001 diff --git a/romfs/Text/ES419.json.z b/romfs/Text/ES419.json.z new file mode 100644 index 0000000000000000000000000000000000000000..ec9973de2ac95c2cb869c3db0b701caeee1bf1ff GIT binary patch literal 4068 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 literal 0 HcmV?d00001 diff --git a/romfs/Text/FRCA.json.z b/romfs/Text/FRCA.json.z new file mode 100644 index 0000000000000000000000000000000000000000..59e05fb5e7234c9f105da130c37c3088d1ea28ea GIT binary patch literal 4190 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 oGVy~d4~j<*gIylgX`~)K=dBsQUOjU)r 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 A1ONa4 literal 0 HcmV?d00001 diff --git a/romfs/Text/JA.json.z b/romfs/Text/JA.json.z new file mode 100644 index 0000000000000000000000000000000000000000..e2c7aa420eb7d3c07a078e14bbb5d0a4c65e4cbe GIT binary patch literal 4332 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 literal 0 HcmV?d00001 diff --git a/romfs/Text/PT.json.z b/romfs/Text/PT.json.z new file mode 100644 index 0000000000000000000000000000000000000000..10a750695daef1e64ec5c577cfb5efad71664fce GIT binary patch literal 3930 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 osvxX>BlMvC9w>oT{`iLZ>R9 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 literal 0 HcmV?d00001 diff --git a/romfs/Text/RU.json.z b/romfs/Text/RU.json.z new file mode 100644 index 0000000000000000000000000000000000000000..ec6f7027f39bc7a749706e33ef13144b1bd7379f GIT binary patch literal 4580 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 O#x=e-{`G(L{i!Op4GEe6 literal 0 HcmV?d00001 diff --git a/romfs/Text/ZHCN.json.z b/romfs/Text/ZHCN.json.z new file mode 100644 index 0000000000000000000000000000000000000000..bfb2ab81667c5a9646fde247c11431a55a4272d5 GIT binary patch literal 3727 zcmV;A4sh{LE&u?B4gdgn+MQbKQyWJT|DL~M;}lhQRL}1Zn0I?Rip8YTJ2IP5v+ZkR*4F>6%)7N|K9udQE$jBpVqczo3@NZ>v*h7{iedaj%Qs zyZ^dBdBrzzU)W2_6v@V{TB*6Mxa6O>Hx~HO8yfRZ1jXeKxAlC16rSq2HT~%$V{^*b zoY7Y^$_E|64-}HhzOAmE`LMa9Ted`-dQLXtZcBP(OGtFRv9i&!#6|uu{ga~DWM^5+ zztEP7TD$-X(_b;2waSTVk-i0y8nbb|R&essvWH~0+(@r*#JwPY>!e#B{}*{-<;CZ+Z@@)AY^u3 z+h0Oa|2;No)K|fqP*jfi6WAKLvE!4-w5b7hIyXtq4JvAB-l)~ulXZ7dx;eGFehM8k z(IY)I!%N||pvTgfzFN@MXW^IBmRzZOEe{~|NF!g-mUlrZ+IkhDPm9HA=rJMrWnevR z;US#TbPYH5)O?ZsMt0{Ex`C!6#>qm2q+Zj5GR?FJ^Wl&;bYBv!J1LjnKzXZOcOeImbG7e@2vbfZI;+K>1JU1rQ!;7Pi>5S<%Cb?|P}~IR!{v~| zc&d+JUyFx%7Le!i$VW^l+z-7~!EHs^`kkk2HJ-;C@?Q3PN5>|9aB&E=k}$Kw+&cIy zTFE%rgkS0kTtdX)Vf6Z5g*=;=QqnME+?0;4?t$^~o80?ewyiSkvB-wM9#Wd_$7Mp4 ztNH}IiF?~`?{lXkct-(TQ0u=?r^BpX&ude=+>!x11PTjUH1)O$`Rz#Gj+5{TNBOZ$ zS)9S$G2;z;gb?}rgHXU5l!7aO1jX0rFJx<;EM!nJPOHNnfcIgK_IuLUijtLWPL*E< z{G&q?_xzu7DkOy%iEMM)LfddkLZkASP>E_g&mqS{RUtc$6g5$PfDrCnB6cDrM@BaG zjDwd#*7|g8+;?q~mKRbTLcEgbq2P)%c11`gi#3vp^QvNgiylTcj(HE;d!~iAc*nvp zbCHvlL~TkTTAmbV%-V!W4;Frxg6JLU8mF>?LGKTeq)gWi-k!J}x*Z}rDMYzJrDJ@2 z3?G`Xx>Rg=7_N|wEvPc#VUUy0$Ib9@d7&>2PhxZ2xOR(}y|+5Q;;Yfff_1 zy>}_&n$=!(hAdbmL*pU*nY&vue zo!sYMqS+oQ;;>1QnAfYXOua>m&|HW(jFf=3@YdLR0ZU{odu^6YpBItX~$Q?_7n-JbZ*W~b&1buIqAO#y2PM~sbDXd$# zeBt}~d%rg@>guP=6G1M>GiE1{itAfN=)F0EASIRw`oVKjUE*kZEE*a!G;L*y^zYfS zakNWL9&yG%QWho@q-n#;aC*qYvF)?B#+YXX_mGFPib=w%NT@L3A1HzJGy{%v+lVQp z{Me%G2s%&He$gj3``9b8w}f!=wi>;1@ohE6(Vnwt0V@<<_iu~er*G3XO~%aTayr_9Z@-|}{k zgxd*Bu1#FKcTWVb$Uf(O=b>Rg-@5d=??->gCy$csI-Ca%Obh1nV`D0<*A|WXGc6$| z$6@bJzDoh0H{_G67IZ@a3muv0-_y#ef7ds z5ADEY1y551IX;yvU}c;k-|{Mz^)T-;w&}6QFmA+xoV4G%@$;l4tBZv>O&hFx;`Y3F z>B0>@AFxEm1-@E30ActNfmuRY5HP;268l0Sn)p1v;N8I3q

yd&8JZp?QnOAUS%e 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 literal 0 HcmV?d00001 diff --git a/source/JKSV.cpp b/source/JKSV.cpp index f025bbf..0081b23 100644 --- a/source/JKSV.cpp +++ b/source/JKSV.cpp @@ -11,8 +11,8 @@ #include "error.hpp" #include "fslib.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" -#include "input.hpp" #include "logging/logger.hpp" #include "remote/remote.hpp" #include "sdl.hpp" @@ -46,9 +46,9 @@ namespace // This function allows any service init to be logged with its name without repeating the code. template -static bool initialize_service(Result (*function)(Args...), const char *serviceName, Args... args) +static bool initialize_service(Result (*function)(Args...), const char *serviceName, Args &&...args) { - Result error = (*function)(args...); + Result error = (*function)(std::forward(args)...); if (R_FAILED(error)) { logger::log("Error initializing %s: 0x%X.", serviceName, error); @@ -60,7 +60,15 @@ static bool initialize_service(Result (*function)(Args...), const char *serviceN // ---- Construction ---- JKSV::JKSV() + : m_sdl2() + , m_window(graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT) + , m_renderer(m_window) + , m_audio() + , m_input() { + // Ensure SDL initialized correctly. If not, return. + if (!m_sdl2.is_initialized() || !m_window.is_initialized() || !m_renderer.is_initialized()) { return; } + // Set boost mode first. JKSV::set_boost_mode(); @@ -78,7 +86,6 @@ JKSV::JKSV() ABORT_ON_FAILURE(curl::initialize()); // Config and input. - input::initialize(); config::initialize(); // These are the strings used in the UI. @@ -94,8 +101,8 @@ JKSV::JKSV() sys::threadpool::push_job(remote::initialize, nullptr); // Launch the loading init. Finish init is called afterwards. - auto init_finish = []() { MainMenuState::create_and_push(); }; // Lambda that's exec'd after state is finished. - data::launch_initialization(false, init_finish); + auto init_finish = [&]() { MainMenuState::create_and_push(m_renderer); }; // Lambda that's exec'd after state is finished. + data::launch_initialization(false, m_renderer, init_finish); // This isn't required, but why not? FadeState::create_and_push(colors::BLACK, 0xFF, 0x00, nullptr); @@ -115,8 +122,6 @@ JKSV::~JKSV() config::save(); curl::exit(); JKSV::exit_services(); - sdl::text::SystemFont::exit(); - sdl::exit(); appletSetCpuBoostMode(ApmCpuBoostMode_Normal); appletUnlockExit(); @@ -128,25 +133,35 @@ bool JKSV::is_running() const noexcept { return sm_isRunning && appletMainLoop() void JKSV::update() { - input::update(); + // Update the input instance. + m_input.update(); - const bool plusPressed = input::button_pressed(HidNpadButton_Plus); + // Check if we should close. + const bool plusPressed = m_input.button_pressed(HidNpadButton_Plus); const bool isClosable = StateManager::back_is_closable(); if (plusPressed && isClosable) { sm_isRunning = false; } - StateManager::update(); + // Update states and pop-ups. + StateManager::update(m_input); ui::PopMessageManager::update(); } void JKSV::render() { - sdl::frame_begin(colors::CLEAR_COLOR); + // Set target to frame buffer and clear. + m_renderer.frame_begin(colors::CLEAR_COLOR); + // Render the base. JKSV::render_base(); - StateManager::render(); - ui::PopMessageManager::render(); - sdl::frame_end(); + // Render the states. + StateManager::render(m_renderer); + + // Render pop-ups. + ui::PopMessageManager::render(m_renderer); + + // Present to screen. + m_renderer.frame_end(); } void JKSV::request_quit() noexcept { sm_isRunning = false; } @@ -188,18 +203,39 @@ bool JKSV::initialize_services() bool JKSV::initialize_sdl() { - // Initialize SDL, freetype and the system font. - bool sdlInit = sdl::initialize("JKSV", graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT); - sdlInit = sdlInit && sdl::text::SystemFont::initialize(); - if (!sdlInit) { return false; } + // These are for loading the header icon. + static constexpr std::string_view HEADER_PATH = "romfs:/Textures/HeaderIcon.png"; + + // These are our breakpoints for wrapping lines. + static constexpr std::array BREAKPOINTS = {L' ', L' ', L'/', L'_', L'-', L'。', L'、'}; + + // These are our color changing points. + static constexpr std::array, 7> COLOR_POINTS = {{{L'#', colors::BLUE}, + {L'*', colors::DARK_RED}, + {L'<', colors::YELLOW}, + {L'>', colors::GREEN}, + {L'`', colors::BLUE_GREEN}, + {L'^', colors::PINK}, + {L'$', colors::GOLD}}}; + + // Ensure SDL2 textures and sounds are ready to use. + sdl2::Texture::initialize(m_renderer); + sdl2::Sound::initialize(m_audio); + + // Register break and color points. + sdl2::Font::add_break_points(BREAKPOINTS); + sdl2::Font::add_color_points(COLOR_POINTS); + + // Load our fonts. + m_titleFont = sdl2::FontManager::create_load_resource(graphics::fonts::names::THIRTY_FOUR_PIXEL, + graphics::fonts::sizes::THIRTY_FOUR_PIXEL); + m_buildFont = sdl2::FontManager::create_load_resource(graphics::fonts::names::FOURTEEN_PIXEL, + graphics::fonts::sizes::FOURTEEN_PIXEL); // Load the icon in the top left. - m_headerIcon = sdl::TextureManager::load("headerIcon", "romfs:/Textures/HeaderIcon.png"); + m_headerIcon = sdl2::TextureManager::create_load_resource(HEADER_PATH, HEADER_PATH); if (!m_headerIcon) { return false; } - // Push the color changing characters. - JKSV::add_color_chars(); - return true; } @@ -220,17 +256,6 @@ bool JKSV::create_directories() return true; } -void JKSV::add_color_chars() -{ - sdl::text::add_color_character(L'#', colors::BLUE); - sdl::text::add_color_character(L'*', colors::DARK_RED); - sdl::text::add_color_character(L'<', colors::YELLOW); - sdl::text::add_color_character(L'>', colors::GREEN); - sdl::text::add_color_character(L'`', colors::BLUE_GREEN); - sdl::text::add_color_character(L'^', colors::PINK); - sdl::text::add_color_character(L'$', colors::GOLD); -} - void JKSV::setup_translation_info_strings() { const char *translationFormat = strings::get_by_name(strings::names::TRANSLATION, 0); @@ -269,43 +294,32 @@ void JKSV::render_base() static constexpr int HEADER_Y = 27; // Coordinates for the title text. - static constexpr int TITLE_X = 130; - static constexpr int TITLE_Y = 32; - static constexpr int TITLE_SIZE = 34; + static constexpr int TITLE_X = 130; + static constexpr int TITLE_Y = 32; // Coordinates for the translation info and build date. - static constexpr int BUILD_X = 8; - static constexpr int BUILD_Y = 700; - static constexpr int TRANS_Y = 680; - static constexpr int BUILD_SIZE = 14; + static constexpr int BUILD_X = 8; + static constexpr int BUILD_Y = 700; + static constexpr int TRANS_Y = 680; // This is just the JKSV string. static constexpr std::string_view TITLE_TEXT = "JKSV"; // Top and bottom framing lines. - sdl::render_line(sdl::Texture::Null, LINE_X_BEGIN, LINE_A_Y, LINE_X_END, LINE_A_Y, colors::WHITE); - sdl::render_line(sdl::Texture::Null, LINE_X_BEGIN, LINE_B_Y, LINE_X_END, LINE_B_Y, colors::WHITE); + m_renderer.render_line(LINE_X_BEGIN, LINE_A_Y, LINE_X_END, LINE_A_Y, colors::WHITE); + m_renderer.render_line(LINE_X_BEGIN, LINE_B_Y, LINE_X_END, LINE_B_Y, colors::WHITE); // Icon - m_headerIcon->render(sdl::Texture::Null, HEADER_X, HEADER_Y); + m_headerIcon->render(HEADER_X, HEADER_Y); // "JKSV" - sdl::text::render(sdl::Texture::Null, TITLE_X, TITLE_Y, TITLE_SIZE, sdl::text::NO_WRAP, colors::WHITE, TITLE_TEXT); + m_titleFont->render_text(TITLE_X, TITLE_Y, colors::WHITE, TITLE_TEXT); // Translation info in bottom left. - if (m_showTranslationInfo) - { - sdl::text::render(sdl::Texture::Null, - BUILD_X, - TRANS_Y, - BUILD_SIZE, - sdl::text::NO_WRAP, - colors::WHITE, - m_translationInfo); - } + if (m_showTranslationInfo) { m_buildFont->render_text(BUILD_X, TRANS_Y, colors::WHITE, m_translationInfo); } // Build date - sdl::text::render(sdl::Texture::Null, BUILD_X, BUILD_Y, BUILD_SIZE, sdl::text::NO_WRAP, colors::WHITE, m_buildString); + m_buildFont->render_text(BUILD_X, BUILD_Y, colors::WHITE, m_buildString); } void JKSV::exit_services() diff --git a/source/StateManager.cpp b/source/StateManager.cpp index 7060db9..9771ee6 100644 --- a/source/StateManager.cpp +++ b/source/StateManager.cpp @@ -1,6 +1,6 @@ #include "StateManager.hpp" -void StateManager::update() +void StateManager::update(const sdl2::Input &input) { // Grab the instance. StateManager &instance = StateManager::get_instance(); @@ -10,7 +10,7 @@ void StateManager::update() { auto &back = stateVector.back(); - if (back->is_active() && back->has_focus()) { back->update(); } + if (back->is_active() && back->has_focus()) { back->update(input); } } // Purge uneeded states. @@ -39,12 +39,12 @@ void StateManager::update() } } -void StateManager::render() noexcept +void StateManager::render(sdl2::Renderer &renderer) noexcept { StateManager &instance = StateManager::get_instance(); auto &stateVector = instance.m_stateVector; - for (std::shared_ptr &state : stateVector) { state->render(); } + for (std::shared_ptr &state : stateVector) { state->render(renderer); } } bool StateManager::back_is_closable() noexcept diff --git a/source/appstates/BackupMenuState.cpp b/source/appstates/BackupMenuState.cpp index c9b0cee..0998110 100644 --- a/source/appstates/BackupMenuState.cpp +++ b/source/appstates/BackupMenuState.cpp @@ -8,8 +8,9 @@ #include "error.hpp" #include "fs/fs.hpp" #include "fslib.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" -#include "input.hpp" +#include "graphics/fonts.hpp" #include "keyboard/keyboard.hpp" #include "sdl.hpp" #include "strings/strings.hpp" @@ -43,13 +44,13 @@ BackupMenuState::BackupMenuState(data::User *user, data::TitleInfo *titleInfo, c // ---- Public functions ---- -void BackupMenuState::update() +void BackupMenuState::update(const sdl2::Input &input) { // Grab focus once and only once. const bool hasFocus = BaseState::has_focus(); // Update the panel first. - sm_slidePanel->update(hasFocus); + sm_slidePanel->update(input, hasFocus); // Grab these. const bool isOpen = sm_slidePanel->is_open(); @@ -67,11 +68,11 @@ void BackupMenuState::update() } // Input bools. - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); - const bool xPressed = input::button_pressed(HidNpadButton_X); - const bool yPressed = input::button_pressed(HidNpadButton_Y); - const bool zrPressed = input::button_pressed(HidNpadButton_ZR); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); + const bool xPressed = input.button_pressed(HidNpadButton_B); + const bool yPressed = input.button_pressed(HidNpadButton_Y); + const bool zrPressed = input.button_pressed(HidNpadButton_ZR); // Conditions. const bool newSelected = selected == 0; @@ -82,7 +83,7 @@ void BackupMenuState::update() const bool uploadBackup = zrPressed && !newSelected; const bool popEmpty = aPressed && !m_saveHasData; - if (newBackup) { BackupMenuState::name_and_create_backup(); } + if (newBackup) { BackupMenuState::name_and_create_backup(input); } else if (overwriteBackup) { BackupMenuState::confirm_overwrite(); } else if (restoreBackup) { BackupMenuState::confirm_restore(); } else if (deleteBackup) { BackupMenuState::confirm_delete(); } @@ -93,10 +94,10 @@ void BackupMenuState::update() // Lock and update the menu. std::lock_guard menuGuard{sm_menuMutex}; - sm_backupMenu->update(hasFocus); + sm_backupMenu->update(input, hasFocus); } -void BackupMenuState::render() +void BackupMenuState::render(sdl2::Renderer &renderer) { // Line render coords. static constexpr int LINE_X = 10; @@ -106,32 +107,37 @@ void BackupMenuState::render() static constexpr int CONTROL_X = 32; static constexpr int CONTROL_Y = 673; - // Clear the render target. - sm_slidePanel->clear_target(); - // Grab whether or not the state has focus and the render target for the panel. - const bool hasFocus = BaseState::has_focus(); - sdl::SharedTexture &target = sm_slidePanel->get_target(); - - // Render the top, bottom lines. Control guide string. - sdl::render_line(target, LINE_X, LINE_A_Y, sm_panelWidth - LINE_X, LINE_A_Y, colors::WHITE); - sdl::render_line(target, LINE_X, LINE_B_Y, sm_panelWidth - LINE_X, LINE_B_Y, colors::WHITE); - sdl::text::render(target, CONTROL_X, CONTROL_Y, 22, sdl::text::NO_WRAP, colors::WHITE, m_controlGuide); + const bool hasFocus = BaseState::has_focus(); // This is the target for the menu so it can't render outside of the lines above. - sm_menuRenderTarget->clear(colors::TRANSPARENT); - - // Lock and render the menu. { - std::lock_guard menuGuard{sm_menuMutex}; - sm_backupMenu->render(sm_menuRenderTarget, hasFocus); + graphics::ScopedRender menuRender{renderer, sm_menuRenderTarget}; + renderer.frame_begin(colors::TRANSPARENT); + + // Lock and render the menu. + { + std::lock_guard menuGuard{sm_menuMutex}; + sm_backupMenu->render(renderer, hasFocus); + } + + // Get target and change target. + { + graphics::ScopedRender slideRender{renderer, sm_slidePanel->get_target()}; + renderer.frame_begin(colors::SLIDE_PANEL_CLEAR); + + // Render the top, bottom lines. Control guide string. + renderer.render_line(LINE_X, LINE_A_Y, sm_panelWidth - LINE_X, LINE_A_Y, colors::WHITE); + renderer.render_line(LINE_X, LINE_B_Y, sm_panelWidth - LINE_X, LINE_B_Y, colors::WHITE); + sm_font->render_text(CONTROL_X, CONTROL_Y, colors::WHITE, m_controlGuide); + + // Render the menu target to the slide panel target. + sm_menuRenderTarget->render(0, 43); + } } - // Render the menu target to the slide panel target. - sm_menuRenderTarget->render(target, 0, 43); - // Finally, render the target to the screen. - sm_slidePanel->render(sdl::Texture::Null, hasFocus); + sm_slidePanel->render(renderer, hasFocus); } void BackupMenuState::refresh() @@ -196,12 +202,18 @@ void BackupMenuState::save_data_written() void BackupMenuState::initialize_static_members() { - if (sm_backupMenu && sm_slidePanel && sm_menuRenderTarget && sm_panelWidth) { return; } + // Name of the render target for the backup menu. + static constexpr std::string_view BACKUP_TARGET = "BackupMenuTarget"; - sm_panelWidth = sdl::text::get_width(22, m_controlGuide) + 64; - sm_backupMenu = ui::Menu::create(8, 8, sm_panelWidth - 16, 22, 600); - sm_slidePanel = ui::SlideOutPanel::create(sm_panelWidth, ui::SlideOutPanel::Side::Right); - sm_menuRenderTarget = sdl::TextureManager::load("backupMenuTarget", sm_panelWidth, 600, SDL_TEXTUREACCESS_TARGET); + if (sm_backupMenu && sm_slidePanel && sm_menuRenderTarget && sm_panelWidth && sm_font) { return; } + + sm_font = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_TWO_PIXEL, + graphics::fonts::sizes::TWENTY_TWO_PIXEL); + sm_panelWidth = sm_font->get_text_width(m_controlGuide) + 64; + sm_backupMenu = ui::Menu::create(8, 8, sm_panelWidth - 16, 22, 600); + sm_slidePanel = ui::SlideOutPanel::create(sm_panelWidth, ui::SlideOutPanel::Side::Right); + sm_menuRenderTarget = + sdl2::TextureManager::create_load_resource(BACKUP_TARGET, sm_panelWidth, 600, SDL_TEXTUREACCESS_TARGET); } void BackupMenuState::ensure_target_directory() @@ -262,7 +274,7 @@ void BackupMenuState::initialize_remote_storage() remote->change_directory(remoteDir); } -void BackupMenuState::name_and_create_backup() +void BackupMenuState::name_and_create_backup(const sdl2::Input &input) { // Size of the buffer for naming backups. static constexpr size_t SIZE_NAME_LENGTH = 0x80; @@ -279,7 +291,7 @@ void BackupMenuState::name_and_create_backup() const bool exportZip = autoUpload || config::get_by_key(config::keys::EXPORT_TO_ZIP); // Input. - const bool zrHeld = input::button_held(HidNpadButton_ZR); + const bool zrHeld = input.button_held(HidNpadButton_ZR); // Whether or not we should skip the keyboard. const bool autoNamed = (autoName || zrHeld); // This can be eval'd here. @@ -525,7 +537,10 @@ void BackupMenuState::upload_backup() // Push the confirmation. ConfirmProgress::create_push_fade(query, holdRequired, tasks::backup::patch_backup, nullptr, m_dataStruct); } - else { ProgressState::create_push_fade(tasks::backup::upload_backup, m_dataStruct); } + else + { + ProgressState::create_push_fade(tasks::backup::upload_backup, m_dataStruct); + } } void BackupMenuState::pop_save_empty() diff --git a/source/appstates/BaseTask.cpp b/source/appstates/BaseTask.cpp index 470ea39..671170c 100644 --- a/source/appstates/BaseTask.cpp +++ b/source/appstates/BaseTask.cpp @@ -1,7 +1,7 @@ #include "appstates/BaseTask.hpp" #include "graphics/colors.hpp" -#include "input.hpp" +#include "graphics/fonts.hpp" #include "strings/strings.hpp" #include "ui/PopMessageManager.hpp" @@ -15,7 +15,8 @@ namespace BaseTask::BaseTask() : BaseState(false) , m_frameTimer(TICKS_GLYPH_TRIGGER) - , m_popUnableExit(strings::get_by_name(strings::names::GENERAL_POPS, 0)) {}; + , m_popUnableExit(strings::get_by_name(strings::names::GENERAL_POPS, 0)) +{ BaseTask::initialize_static_members(); } // ---- Public functions ---- @@ -27,9 +28,9 @@ void BaseTask::update_loading_glyph() else if (++m_currentFrame % 8 == 0) { m_currentFrame = 0; } } -void BaseTask::pop_on_plus() +void BaseTask::pop_on_plus(const sdl2::Input &input) { - const bool plusPressed = input::button_pressed(HidNpadButton_Plus); + const bool plusPressed = input.button_pressed(HidNpadButton_Plus); if (plusPressed) { ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_TICKS, m_popUnableExit); } } @@ -37,15 +38,18 @@ void BaseTask::pop_on_plus() void BaseTask::render_loading_glyph() { // Render coords. - static constexpr int RENDER_X = 56; - static constexpr int RENDER_Y = 673; - static constexpr int RENDER_SIZE = 32; + static constexpr int RENDER_X = 56; + static constexpr int RENDER_Y = 673; - sdl::text::render(sdl::Texture::Null, - RENDER_X, - RENDER_Y, - RENDER_SIZE, - sdl::text::NO_WRAP, - m_colorMod, - sm_glyphArray[m_currentFrame]); + sm_font->render_text(RENDER_X, RENDER_Y, colors::WHITE, sm_glyphArray[m_currentFrame]); } + +// ---- Private Functions ---- + +void BaseTask::initialize_static_members() +{ + if (sm_font) { return; } + + sm_font = sdl2::FontManager::create_load_resource(graphics::fonts::names::THIRTY_TWO_PIXEL, + graphics::fonts::sizes::THIRTY_TWO_PIXEL); +} \ No newline at end of file diff --git a/source/appstates/BlacklistEditState.cpp b/source/appstates/BlacklistEditState.cpp index db1296a..dfa6066 100644 --- a/source/appstates/BlacklistEditState.cpp +++ b/source/appstates/BlacklistEditState.cpp @@ -6,7 +6,6 @@ #include "data/data.hpp" #include "error.hpp" #include "graphics/screen.hpp" -#include "input.hpp" // ---- Construction ---- @@ -21,24 +20,24 @@ BlacklistEditState::BlacklistEditState() // ---- Public functions ---- -void BlacklistEditState::update() +void BlacklistEditState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); - sm_slidePanel->update(hasFocus); + sm_slidePanel->update(input, hasFocus); if (aPressed) { BlacklistEditState::remove_from_blacklist(); } else if (sm_slidePanel->is_closed()) { BlacklistEditState::deactivate_state(); } else if (bPressed || m_blacklist.empty()) { sm_slidePanel->close(); } } -void BlacklistEditState::render() +void BlacklistEditState::render(sdl2::Renderer &renderer) { const bool hasFocus = BaseState::has_focus(); - sm_slidePanel->clear_target(); - sm_slidePanel->render(sdl::Texture::Null, hasFocus); + + sm_slidePanel->render(renderer, hasFocus); } // ---- Private functions ---- diff --git a/source/appstates/DataLoadingState.cpp b/source/appstates/DataLoadingState.cpp index d174531..9d7888a 100644 --- a/source/appstates/DataLoadingState.cpp +++ b/source/appstates/DataLoadingState.cpp @@ -1,22 +1,21 @@ #include "appstates/DataLoadingState.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" #include "logging/logger.hpp" - -namespace -{ - constexpr int SCREEN_CENTER = 640; -} +#include "mathutil.hpp" // ---- Construction ---- DataLoadingState::DataLoadingState(data::DataContext &context, + sdl2::Renderer &renderer, DestructFunction destructFunction, sys::threadpool::JobFunction function, sys::Task::TaskData taskData) : BaseTask() , m_context(context) + , m_renderer(renderer) , m_destructFunction(destructFunction) { DataLoadingState::initialize_static_members(); @@ -25,27 +24,34 @@ DataLoadingState::DataLoadingState(data::DataContext &context, // ---- Public functions ---- -void DataLoadingState::update() +void DataLoadingState::update(const sdl2::Input &input) { BaseTask::update_loading_glyph(); if (!m_task->is_running()) { DataLoadingState::deactivate_state(); } - m_context.process_icon_queue(); + m_context.process_icon_queue(m_renderer); } void DataLoadingState::sub_update() { BaseTask::update_loading_glyph(); } -void DataLoadingState::render() +void DataLoadingState::render(sdl2::Renderer &renderer) { - static constexpr int ICON_X_COORD = SCREEN_CENTER - 128; - static constexpr int ICON_Y_COORD = 226; - const std::string status = m_task->get_status(); + // These are the dimensions of the icon loaded. + static constexpr int ICON_WIDTH = 256; + static constexpr int ICON_HEIGHT = 259; + static constexpr int ICON_X = math::Util::center_within(graphics::SCREEN_WIDTH, ICON_WIDTH); + static constexpr int ICON_Y = math::Util::center_within(graphics::SCREEN_HEIGHT, ICON_HEIGHT); - const int statusWidth = sdl::text::get_width(BaseTask::FONT_SIZE, status); - m_statusX = SCREEN_CENTER - (statusWidth / 2); + // Grab the status from the task and center it. + const std::string status = m_task->get_status(); + const int statusWidth = sm_font->get_text_width(status); + m_statusX = math::Util::center_within(graphics::SCREEN_WIDTH, statusWidth); - sdl::render_rect_fill(sdl::Texture::Null, 0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::CLEAR_COLOR); - sm_jksvIcon->render(sdl::Texture::Null, ICON_X_COORD, ICON_Y_COORD); - sdl::text::render(sdl::Texture::Null, m_statusX, 673, BaseTask::FONT_SIZE, sdl::text::NO_WRAP, colors::WHITE, status); + // This is to cover up the base rendering. + renderer.render_rectangle(0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::CLEAR_COLOR); + + // Render the icon, status and loading glyph. + sm_jksvIcon->render(ICON_X, ICON_Y); + sm_font->render_text(m_statusX, 673, colors::WHITE, status); BaseTask::render_loading_glyph(); } @@ -53,15 +59,21 @@ void DataLoadingState::render() void DataLoadingState::initialize_static_members() { - if (sm_jksvIcon) { return; } + // Path for the loading icon. + static constexpr std::string_view ICON_PATH = "romfs:/Textures/LoadingIcon.png"; - sm_jksvIcon = sdl::TextureManager::load("LoadingIcon", "romfs:/Textures/LoadingIcon.png"); + if (sm_jksvIcon && sm_font) { return; } + + // Load icon and font. + sm_jksvIcon = sdl2::TextureManager::create_load_resource(ICON_PATH, ICON_PATH); + sm_font = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_PIXEL, + graphics::fonts::sizes::TWENTY_PIXEL); } void DataLoadingState::deactivate_state() { // This is to catch any stragglers. - m_context.process_icon_queue(); + m_context.process_icon_queue(m_renderer); if (m_destructFunction) { m_destructFunction(); } BaseState::deactivate(); } diff --git a/source/appstates/ExtrasMenuState.cpp b/source/appstates/ExtrasMenuState.cpp index 9333a74..571e50b 100644 --- a/source/appstates/ExtrasMenuState.cpp +++ b/source/appstates/ExtrasMenuState.cpp @@ -4,8 +4,9 @@ #include "appstates/MainMenuState.hpp" #include "data/data.hpp" #include "error.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" -#include "input.hpp" +#include "graphics/targets.hpp" #include "keyboard/keyboard.hpp" #include "strings/strings.hpp" #include "ui/PopMessageManager.hpp" @@ -14,9 +15,6 @@ namespace { - // This target is shared be a lot of states. - constexpr std::string_view SECONDARY_TARGET = "SecondaryTarget"; - // Enum for switch case readability. enum { @@ -35,23 +33,25 @@ static void finish_reinitialization(); // ---- Construction ---- -ExtrasMenuState::ExtrasMenuState() - : m_renderTarget(sdl::TextureManager::load(SECONDARY_TARGET, 1080, 555, SDL_TEXTUREACCESS_TARGET)) +ExtrasMenuState::ExtrasMenuState(sdl2::Renderer &renderer) + : m_renderer(renderer) + , m_renderTarget(sdl2::TextureManager::create_load_resource(graphics::targets::names::SECONDARY, + graphics::targets::dims::SECONDARY_WIDTH, + graphics::targets::dims::SECONDARY_HEIGHT, + SDL_TEXTUREACCESS_TARGET)) , m_controlGuide(ui::ControlGuide::create(strings::get_by_name(strings::names::CONTROL_GUIDES, 5))) -{ - ExtrasMenuState::initialize_menu(); -} +{ ExtrasMenuState::initialize_menu(); } // ---- Public functions ---- -void ExtrasMenuState::update() +void ExtrasMenuState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); - m_extrasMenu->update(hasFocus); - m_controlGuide->update(hasFocus); + m_extrasMenu->update(input, hasFocus); + m_controlGuide->update(input, hasFocus); if (aPressed) { @@ -71,14 +71,20 @@ void ExtrasMenuState::update() void ExtrasMenuState::sub_update() { m_controlGuide->sub_update(); } -void ExtrasMenuState::render() +void ExtrasMenuState::render(sdl2::Renderer &renderer) { const bool hasFocus = BaseState::has_focus(); - m_renderTarget->clear(colors::TRANSPARENT); - m_extrasMenu->render(m_renderTarget, hasFocus); - m_renderTarget->render(sdl::Texture::Null, 201, 91); - m_controlGuide->render(sdl::Texture::Null, hasFocus); + { // Set the render target. + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + renderer.frame_begin(colors::TRANSPARENT); + + // Render the menu to it. + m_extrasMenu->render(renderer, hasFocus); + } + + m_renderTarget->render(201, 91); + m_controlGuide->render(renderer, hasFocus); } // ---- Private functions ---- @@ -93,7 +99,7 @@ void ExtrasMenuState::initialize_menu() } } -void ExtrasMenuState::reinitialize_data() { data::launch_initialization(true, finish_reinitialization); } +void ExtrasMenuState::reinitialize_data() { data::launch_initialization(true, m_renderer, finish_reinitialization); } void ExtrasMenuState::sd_to_sd_browser() { FileModeState::create_and_push("sdmc", "sdmc", false); } diff --git a/source/appstates/FadeState.cpp b/source/appstates/FadeState.cpp index 9982c94..6f69bed 100644 --- a/source/appstates/FadeState.cpp +++ b/source/appstates/FadeState.cpp @@ -12,7 +12,7 @@ namespace } // ---- Construction ---- -FadeState::FadeState(sdl::Color baseColor, uint8_t startAlpha, uint8_t endAlpha, std::shared_ptr nextState) +FadeState::FadeState(SDL_Color baseColor, uint8_t startAlpha, uint8_t endAlpha, std::shared_ptr nextState) : m_baseColor(baseColor) , m_alpha(startAlpha) , m_endAlpha(endAlpha) @@ -23,7 +23,7 @@ FadeState::FadeState(sdl::Color baseColor, uint8_t startAlpha, uint8_t endAlpha, m_fadeTimer.start(TICKS_TIMER_TRIGGER); } -void FadeState::update() +void FadeState::update(const sdl2::Input &input) { if (m_alpha == m_endAlpha) { @@ -38,12 +38,13 @@ void FadeState::update() } } -void FadeState::render() +void FadeState::render(sdl2::Renderer &renderer) { - const uint32_t rawColor = (m_baseColor.raw & 0xFFFFFF00) | m_alpha; - const sdl::Color fadeColor{rawColor}; + // Update alpha. + m_baseColor.a = m_alpha; - sdl::render_rect_fill(sdl::Texture::Null, 0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, fadeColor); + // Render overtop of everything. + renderer.render_rectangle(0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, m_baseColor); } // ---- Private functions ---- diff --git a/source/appstates/FileModeState.cpp b/source/appstates/FileModeState.cpp index 15d877c..63c7636 100644 --- a/source/appstates/FileModeState.cpp +++ b/source/appstates/FileModeState.cpp @@ -2,9 +2,9 @@ #include "appstates/FileOptionState.hpp" #include "config/config.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" #include "graphics/screen.hpp" -#include "input.hpp" #include "logging/logger.hpp" #include "mathutil.hpp" #include "strings/strings.hpp" @@ -39,17 +39,17 @@ FileModeState::FileModeState(std::string_view mountA, std::string_view mountB, i // ---- Public functions ---- -void FileModeState::update() +void FileModeState::update(const sdl2::Input &input) { switch (m_state) { case State::Rising: case State::Dropping: FileModeState::update_y(); break; - case State::Open: FileModeState::update_handle_input(); break; + case State::Open: FileModeState::update_handle_input(input); break; } } -void FileModeState::render() +void FileModeState::render(sdl2::Renderer &renderer) { // Coords for divider lines. static constexpr int LINE_A_X = 617; @@ -59,27 +59,25 @@ void FileModeState::render() static constexpr int LINE_Y_A = 0; static constexpr int LINE_Y_B = 538; + // Grab focus. const bool hasFocus = BaseState::has_focus(); - sm_renderTarget->clear(colors::TRANSPARENT); + // Switch targets, clear. + { + graphics::ScopedRender scopedRender{renderer, sm_renderTarget}; + renderer.frame_begin(colors::TRANSPARENT); - // This is here so it's rendered underneath the pop-up frame. - sm_controlGuide->render(sdl::Texture::Null, hasFocus); + // Divider lines & menus. + renderer.render_line(LINE_A_X, LINE_Y_A, LINE_A_X, LINE_Y_B, colors::WHITE); + renderer.render_line(LINE_B_X, LINE_Y_A, LINE_B_X, LINE_Y_B, colors::DIALOG_DARK); + m_dirMenuA->render(renderer, hasFocus && m_target == Target::MountA); + m_dirMenuB->render(renderer, hasFocus && m_target == Target::MountB); + } - // Center divider lines. - sdl::render_line(sm_renderTarget, LINE_A_X, LINE_Y_A, LINE_A_X, LINE_Y_B, colors::WHITE); - sdl::render_line(sm_renderTarget, LINE_B_X, LINE_Y_A, LINE_B_X, LINE_Y_B, colors::DIALOG_DARK); - - // Menus - m_dirMenuA->render(sm_renderTarget, hasFocus && m_target == Target::MountA); - m_dirMenuB->render(sm_renderTarget, hasFocus && m_target == Target::MountB); - - // Frame. - sm_frame->render(sdl::Texture::Null, true); - - // Main target. - const int y = m_transition.get_y(); - sm_renderTarget->render(sdl::Texture::Null, 23, y + 12); + // This needs to be in this specific order to look right. + sm_controlGuide->render(renderer, hasFocus); + sm_frame->render(renderer, hasFocus); + sm_renderTarget->render(23, m_transition.get_y() + 12); } // ---- Private functions ---- @@ -99,8 +97,9 @@ void FileModeState::initialize_static_members() if (sm_frame && sm_renderTarget && sm_controlGuide) { return; } - sm_frame = ui::Frame::create(PERMA_X, graphics::SCREEN_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT); - sm_renderTarget = sdl::TextureManager::load(RENDER_TARGET_NAME, INNER_WIDTH, INNER_HEIGHT, SDL_TEXTUREACCESS_TARGET); + sm_frame = ui::Frame::create(PERMA_X, graphics::SCREEN_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT); + sm_renderTarget = + sdl2::TextureManager::create_load_resource(RENDER_TARGET_NAME, INNER_WIDTH, INNER_HEIGHT, SDL_TEXTUREACCESS_TARGET); sm_controlGuide = ui::ControlGuide::create(strings::get_by_name(strings::names::CONTROL_GUIDES, 4)); } @@ -147,7 +146,10 @@ void FileModeState::initialize_directory_menu(const fslib::Path &path, fslib::Di { std::string option{}; if (entry.is_directory()) { option = DIR_PREFIX; } - else { option = FILE_PREFIX; } + else + { + option = FILE_PREFIX; + } option += entry.get_filename(); menu.add_option(option); @@ -170,7 +172,7 @@ void FileModeState::update_y() noexcept else if (finishedDropping) { FileModeState::deactivate_state(); } } -void FileModeState::update_handle_input() noexcept +void FileModeState::update_handle_input(const sdl2::Input &input) noexcept { // Get whether or not the state has focus. const bool hasFocus = BaseState::has_focus(); @@ -181,11 +183,11 @@ void FileModeState::update_handle_input() noexcept fslib::Directory &directory = FileModeState::get_source_directory(); // Input bools. - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); - const bool xPressed = input::button_pressed(HidNpadButton_X); - const bool zlZRPressed = input::button_pressed(HidNpadButton_ZL) || input::button_pressed(HidNpadButton_ZR); - const bool minusPressed = input::button_pressed(HidNpadButton_Minus); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); + const bool xPressed = input.button_pressed(HidNpadButton_X); + const bool zlZRPressed = input.button_pressed(HidNpadButton_ZL) || input.button_pressed(HidNpadButton_ZR); + const bool minusPressed = input.button_pressed(HidNpadButton_Minus); // Conditions if (aPressed) { FileModeState::enter_selected(path, directory, menu); } @@ -199,8 +201,8 @@ void FileModeState::update_handle_input() noexcept } // Update the menu and control guide. - menu.update(hasFocus); - sm_controlGuide->update(hasFocus); + menu.update(input, hasFocus); + sm_controlGuide->update(input, hasFocus); } void FileModeState::enter_selected(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu) @@ -256,14 +258,10 @@ void FileModeState::enter_directory(fslib::Path &path, } ui::Menu &FileModeState::get_source_menu() noexcept -{ - return m_target == Target::MountA ? *m_dirMenuA.get() : *m_dirMenuB.get(); -} +{ return m_target == Target::MountA ? *m_dirMenuA.get() : *m_dirMenuB.get(); } ui::Menu &FileModeState::get_destination_menu() noexcept -{ - return m_target == Target::MountA ? *m_dirMenuB.get() : *m_dirMenuA.get(); -} +{ return m_target == Target::MountA ? *m_dirMenuB.get() : *m_dirMenuA.get(); } fslib::Path &FileModeState::get_source_path() noexcept { return m_target == Target::MountA ? m_pathA : m_pathB; } diff --git a/source/appstates/FileOptionState.cpp b/source/appstates/FileOptionState.cpp index de6ebd0..878cc26 100644 --- a/source/appstates/FileOptionState.cpp +++ b/source/appstates/FileOptionState.cpp @@ -8,7 +8,6 @@ #include "error.hpp" #include "fs/fs.hpp" #include "fslib.hpp" -#include "input.hpp" #include "keyboard/keyboard.hpp" #include "logging/logger.hpp" #include "mathutil.hpp" @@ -70,23 +69,26 @@ FileOptionState::FileOptionState(FileModeState *spawningState) // ---- Public functions ---- -void FileOptionState::update() +void FileOptionState::update(const sdl2::Input &input) { switch (m_state) { case State::Opening: case State::Closing: FileOptionState::update_dimensions(); break; - case State::Opened: FileOptionState::update_handle_input(); break; + case State::Opened: FileOptionState::update_handle_input(input); break; } } -void FileOptionState::render() +void FileOptionState::render(sdl2::Renderer &renderer) { + // Grab focus. const bool hasFocus = BaseState::has_focus(); - sm_dialog->render(sdl::Texture::Null, hasFocus); + + // Render the dialog and bail if we're not read for the menu. + sm_dialog->render(renderer, hasFocus); if (!m_transition.in_place()) { return; } - sm_copyMenu->render(sdl::Texture::Null, hasFocus); + sm_copyMenu->render(renderer, hasFocus); } void FileOptionState::update_source() { m_updateSource = true; } @@ -156,7 +158,7 @@ void FileOptionState::update_dimensions() noexcept else if (isClosed) { FileOptionState::deactivate_state(); } } -void FileOptionState::update_handle_input() noexcept +void FileOptionState::update_handle_input(const sdl2::Input &input) noexcept { // Grab whether or not the state has focus since this is actually used a lot. const bool hasFocus = BaseState::has_focus(); @@ -166,12 +168,12 @@ void FileOptionState::update_handle_input() noexcept else if (m_updateDest) { FileOptionState::update_filemode_dest(); } // Update the menu input. - sm_copyMenu->update(hasFocus); + sm_copyMenu->update(input, hasFocus); // Local input handling. const int selected = sm_copyMenu->get_selected(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); if (aPressed) { @@ -398,7 +400,10 @@ void FileOptionState::get_show_target_properties() const bool isDir = fslib::directory_exists(targetPath); if (isDir) { FileOptionState::get_show_directory_properties(targetPath); } - else { FileOptionState::get_show_file_properties(targetPath); } + else + { + FileOptionState::get_show_file_properties(targetPath); + } } void FileOptionState::close_dialog() noexcept @@ -460,11 +465,11 @@ void FileOptionState::get_show_file_properties(const fslib::Path &path) const std::string pathString = path.string(); const std::string sizeString = get_size_string(fileSize); const std::string message = stringutil::get_formatted_string(messageFormat, - pathString.c_str(), - sizeString.c_str(), - createdBuffer, - lastModified, - lastAccessed); + pathString.c_str(), + sizeString.c_str(), + createdBuffer, + lastModified, + lastAccessed); MessageState::create_and_push(message); } diff --git a/source/appstates/MainMenuState.cpp b/source/appstates/MainMenuState.cpp index 6338d07..84a31b9 100644 --- a/source/appstates/MainMenuState.cpp +++ b/source/appstates/MainMenuState.cpp @@ -9,8 +9,8 @@ #include "appstates/TitleSelectState.hpp" #include "appstates/UserOptionState.hpp" #include "config/config.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" -#include "input.hpp" #include "logging/logger.hpp" #include "remote/remote.hpp" #include "sdl.hpp" @@ -21,18 +21,32 @@ #include "tasks/update.hpp" #include "ui/PopMessageManager.hpp" +namespace +{ + // Main menu render target. + constexpr std::string_view TARGET = "MainMenuTarget"; + constexpr int TARGET_WIDTH = 200; + constexpr int TARGET_HEIGHT = 555; + + // Background. + constexpr std::string_view MAIN_BG = "romfs:/Textures/MenuBackground.png"; + constexpr std::string_view SETT_ICON = "romfs:/Textures/SettingsIcon.png"; + constexpr std::string_view EXTRA_ICON = "romfs:/Textures/ExtrasIcon.png"; + +} + // ---- Construction ---- -MainMenuState::MainMenuState() - : m_renderTarget(sdl::TextureManager::load("mainMenuTarget", 200, 555, SDL_TEXTUREACCESS_TARGET)) - , m_background(sdl::TextureManager::load("mainBackground", "romfs:/Textures/MenuBackground.png")) - , m_settingsIcon(sdl::TextureManager::load("settingsIcon", "romfs:/Textures/SettingsIcon.png")) - , m_extrasIcon(sdl::TextureManager::load("extrasIcon", "romfs:/Textures/ExtrasIcon.png")) +MainMenuState::MainMenuState(sdl2::Renderer &renderer) + : m_renderTarget(sdl2::TextureManager::create_load_resource(TARGET, TARGET_WIDTH, TARGET_HEIGHT, SDL_TEXTUREACCESS_TARGET)) + , m_background(sdl2::TextureManager::create_load_resource(MAIN_BG, MAIN_BG)) + , m_settingsIcon(sdl2::TextureManager::create_load_resource(SETT_ICON, SETT_ICON)) + , m_extrasIcon(sdl2::TextureManager::create_load_resource(EXTRA_ICON, EXTRA_ICON)) , m_mainMenu(ui::IconMenu::create(50, 15, 555)) , m_controlGuide(ui::ControlGuide::create(strings::get_by_name(strings::names::CONTROL_GUIDES, 0))) , m_dataStruct(std::make_shared()) { - MainMenuState::initialize_settings_extras(); + MainMenuState::initialize_settings_extras(renderer); MainMenuState::initialize_menu(); MainMenuState::initialize_view_states(); MainMenuState::initialize_data_struct(); @@ -41,14 +55,17 @@ MainMenuState::MainMenuState() // ---- Public functions ---- -void MainMenuState::update() +void MainMenuState::update(const sdl2::Input &input) { const int selected = m_mainMenu->get_selected(); const bool hasFocus = BaseState::has_focus(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool xPressed = input::button_pressed(HidNpadButton_X); - const bool yPressed = input::button_pressed(HidNpadButton_Y); + // Input + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool xPressed = input.button_pressed(HidNpadButton_X); + const bool yPressed = input.button_pressed(HidNpadButton_Y); + + // Whether or not to open the options targeting users. const bool toUserOptions = xPressed && selected < sm_userCount; if (aPressed) { MainMenuState::push_target_state(); } @@ -60,28 +77,38 @@ void MainMenuState::update() MainMenuState::confirm_update(); } - m_mainMenu->update(hasFocus); - m_controlGuide->update(hasFocus); + m_mainMenu->update(input, hasFocus); + m_controlGuide->update(input, hasFocus); for (auto &state : sm_states) { state->sub_update(); } } void MainMenuState::sub_update() { m_controlGuide->sub_update(); } -void MainMenuState::render() +void MainMenuState::render(sdl2::Renderer &renderer) { const bool hasFocus = BaseState::has_focus(); const int selected = m_mainMenu->get_selected(); - m_background->render(m_renderTarget, 0, 0); - m_mainMenu->render(m_renderTarget, hasFocus); - m_renderTarget->render(sdl::Texture::Null, 0, 91); - m_controlGuide->render(sdl::Texture::Null, hasFocus); + { + // Switch target. + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + renderer.frame_begin(colors::CLEAR_COLOR); + // Render background and menu. + m_background->render(0, 0); + m_mainMenu->render(renderer, hasFocus); + } + + // Main FB stuff. + m_renderTarget->render(0, 91); + m_controlGuide->render(renderer, hasFocus); + + // If we have focus, render the state we're hovering. if (hasFocus) { BaseState *target = sm_states[selected].get(); - target->render(); + target->render(renderer); } } @@ -97,7 +124,10 @@ void MainMenuState::initialize_view_states() std::shared_ptr state{}; if (jksmMode) { state = TextTitleSelectState::create(user); } - else { state = TitleSelectState::create(user); } + else + { + state = TitleSelectState::create(user); + } sm_states.push_back(state); } @@ -116,12 +146,12 @@ void MainMenuState::refresh_view_states() // ---- Private functions ---- -void MainMenuState::initialize_settings_extras() +void MainMenuState::initialize_settings_extras(sdl2::Renderer &renderer) { if (!sm_settingsState || !sm_extrasState) { sm_settingsState = SettingsState::create(); - sm_extrasState = ExtrasMenuState::create(); + sm_extrasState = ExtrasMenuState::create(renderer); } } @@ -186,7 +216,10 @@ void MainMenuState::backup_all_for_all() { ConfirmProgress::create_push_fade(query, true, tasks::mainmenu::backup_all_for_all_remote, nullptr, m_dataStruct); } - else { ConfirmProgress::create_push_fade(query, true, tasks::mainmenu::backup_all_for_all_local, nullptr, m_dataStruct); } + else + { + ConfirmProgress::create_push_fade(query, true, tasks::mainmenu::backup_all_for_all_local, nullptr, m_dataStruct); + } } void MainMenuState::confirm_update() diff --git a/source/appstates/MessageState.cpp b/source/appstates/MessageState.cpp index 96b3aee..85aa22d 100644 --- a/source/appstates/MessageState.cpp +++ b/source/appstates/MessageState.cpp @@ -3,8 +3,9 @@ #include "StateManager.hpp" #include "appstates/FadeState.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" -#include "input.hpp" +#include "mathutil.hpp" #include "strings/strings.hpp" namespace @@ -55,45 +56,57 @@ MessageState::MessageState(std::string &message) // ---- Public functions ---- -void MessageState::update() +void MessageState::update(const sdl2::Input &input) { switch (m_state) { case State::Opening: case State::Closing: MessageState::update_dimensions(); break; - case State::Displaying: MessageState::update_handle_input(); break; + case State::Displaying: MessageState::update_handle_input(input); break; } } -void MessageState::render() +void MessageState::render(sdl2::Renderer &renderer) { + // This is were to render static constexpr int y = 229; - const bool hasFocus = BaseState::has_focus(); - sdl::render_rect_fill(sdl::Texture::Null, 0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); - sm_dialog->render(sdl::Texture::Null, hasFocus); + const bool hasFocus = BaseState::has_focus(); + + // Dim the background and render the dialog. Only continue if we're fully open. + renderer.render_rectangle(0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); + sm_dialog->render(renderer, hasFocus); if (!m_transition.in_place()) { return; } - sdl::text::render(sdl::Texture::Null, 312, y + 24, 20, 656, colors::WHITE, m_message); - sdl::render_line(sdl::Texture::Null, 280, y + 192, 999, y + 192, colors::DIV_COLOR); - sdl::text::render(sdl::Texture::Null, sm_okX, y + 214, 22, sdl::text::NO_WRAP, colors::WHITE, sm_okText); + sm_textFont->render_text_wrapped(312, y + 24, colors::WHITE, 656, m_message); + renderer.render_line(280, y + 192, 999, y + 192, colors::DIV_COLOR); + sm_optionFont->render_text(sm_okX, y + 214, colors::WHITE, sm_okText); } // ---- Private functions ---- void MessageState::initialize_static_members() { - static constexpr int HALF_WIDTH = 640; - static constexpr std::string_view POP_SOUND = "ConfirmPop"; - static constexpr const char *POP_PATH = "romfs:/Sound/ConfirmPop.wav"; + static constexpr std::string_view CONFIRM_POP = "romfs:/Sound/ConfirmPop.wav"; - if (sm_okText && sm_dialog && sm_dialogPop) { return; } + if (sm_okText && sm_dialog && sm_dialogPop && sm_textFont && sm_optionFont) { return; } - sm_okText = strings::get_by_name(strings::names::YES_NO_OK, 2); - sm_okX = HALF_WIDTH - (sdl::text::get_width(22, sm_okText) / 2); - sm_dialog = ui::DialogBox::create(0, 0, 0, 0); - sm_dialogPop = sdl::SoundManager::load(POP_SOUND, POP_PATH); + // This is needed later, so it comes first. + sm_textFont = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_PIXEL, + graphics::fonts::sizes::TWENTY_PIXEL); + sm_optionFont = sdl2::FontManager::create_load_resource(graphics::fonts::names::THIRTY_TWO_PIXEL, + graphics::fonts::sizes::TWENTY_TWO_PIXEL); + + // OK text and X coord. + sm_okText = strings::get_by_name(strings::names::YES_NO_OK, 2); + sm_okX = math::Util::center_within(graphics::SCREEN_WIDTH, sm_textFont->get_text_width(sm_okText)); + + // Dialog. + sm_dialog = ui::DialogBox::create(0, 0, 0, 0); sm_dialog->set_from_transition(m_transition, true); + + // Sound to play. + sm_dialogPop = sdl2::SoundManager::create_load_resource(CONFIRM_POP, CONFIRM_POP); } void MessageState::update_dimensions() noexcept @@ -109,10 +122,10 @@ void MessageState::update_dimensions() noexcept else if (closed) { MessageState::deactivate_state(); } } -void MessageState::update_handle_input() noexcept +void MessageState::update_handle_input(const sdl2::Input &input) noexcept { // Input bools. - const bool aPressed = input::button_pressed(HidNpadButton_A); + const bool aPressed = input.button_pressed(HidNpadButton_A); // Handle the triggerguard. m_triggerGuard = m_triggerGuard || (aPressed && !m_triggerGuard); diff --git a/source/appstates/ProgressState.cpp b/source/appstates/ProgressState.cpp index 7586ea7..8f99422 100644 --- a/source/appstates/ProgressState.cpp +++ b/source/appstates/ProgressState.cpp @@ -2,8 +2,9 @@ #include "appstates/FadeState.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" -#include "input.hpp" +#include "mathutil.hpp" #include "sdl.hpp" #include "strings/strings.hpp" #include "stringutil.hpp" @@ -43,17 +44,15 @@ ProgressState::ProgressState(sys::threadpool::JobFunction function, sys::Task::T TARGET_WIDTH, TARGET_HEIGHT, ui::Transition::DEFAULT_THRESHOLD) -{ - initialize_static_members(); -} +{ initialize_static_members(); } // ---- Public functions ---- -void ProgressState::update() +void ProgressState::update(const sdl2::Input &input) { // These are always updated and aren't conditional. BaseTask::update_loading_glyph(); - BaseTask::pop_on_plus(); + BaseTask::pop_on_plus(input); switch (m_state) { @@ -63,7 +62,7 @@ void ProgressState::update() } } -void ProgressState::render() +void ProgressState::render(sdl2::Renderer &renderer) { static constexpr int RIGHT_EDGE_X = (COORD_BAR_X + SIZE_BAR_WIDTH) - 16; @@ -71,51 +70,44 @@ void ProgressState::render() const bool hasFocus = BaseState::has_focus(); // This will dim the background. - sdl::render_rect_fill(sdl::Texture::Null, 0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); + renderer.render_rectangle(0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); // Render the glyph and dialog. Don't render anything else unless the task is running. BaseTask::render_loading_glyph(); - sm_dialog->render(sdl::Texture::Null, hasFocus); + sm_dialog->render(renderer, hasFocus); if (m_state != State::Running) { return; } - // This just makes this easier to work with. - const int barWidth = static_cast(SIZE_BAR_WIDTH); - // Grab and render the status. const std::string status = m_task->get_status(); - sdl::text::render(sdl::Texture::Null, 312, 255, BaseTask::FONT_SIZE, 656, colors::WHITE, status); + sm_font->render_text(312, 255, colors::WHITE, status); // This is the divider line. - sdl::render_line(sdl::Texture::Null, 280, 421, 999, 421, colors::DIV_COLOR); + renderer.render_line(280, 421, 999, 421, colors::DIV_COLOR); // Progress showing bar. - sdl::render_rect_fill(sdl::Texture::Null, COORD_BAR_X, COORD_BAR_Y, barWidth, 32, colors::BLACK); - sdl::render_rect_fill(sdl::Texture::Null, COORD_BAR_X, COORD_BAR_Y, m_progressBarWidth, 32, colors::BAR_GREEN); + renderer.render_rectangle(COORD_BAR_X, COORD_BAR_Y, static_cast(SIZE_BAR_WIDTH), 32, colors::BLACK); + renderer.render_rectangle(COORD_BAR_X, COORD_BAR_Y, m_progressBarWidth, 32, colors::BAR_GREEN); // These are the "caps" to round the edges of the bar. - sm_barEdges->render_part(sdl::Texture::Null, COORD_BAR_X, COORD_BAR_Y, 0, 0, 16, 32); - sm_barEdges->render_part(sdl::Texture::Null, RIGHT_EDGE_X, COORD_BAR_Y, 16, 0, 16, 32); + sm_barEdges->render_part(COORD_BAR_X, COORD_BAR_Y, 0, 0, 16, 32); + sm_barEdges->render_part(RIGHT_EDGE_X, COORD_BAR_Y, 16, 0, 16, 32); // Progress string. - sdl::text::render(sdl::Texture::Null, - m_percentageX, - COORD_TEXT_Y, - BaseTask::FONT_SIZE, - sdl::text::NO_WRAP, - colors::WHITE, - m_percentageString); + sm_font->render_text(m_percentageX, COORD_TEXT_Y, colors::WHITE, m_percentageString); } // ---- Private functions ---- void ProgressState::initialize_static_members() { - static constexpr std::string_view BAR_EDGE_NAME = "BarEdges"; + static constexpr std::string_view BAR_EDGES = "romfs:/Textures/BarEdges/png"; - if (sm_dialog && sm_barEdges) { return; } + if (sm_dialog && sm_barEdges && sm_font) { return; } sm_dialog = ui::DialogBox::create(0, 0, 0, 0); - sm_barEdges = sdl::TextureManager::load(BAR_EDGE_NAME, "romfs:/Textures/BarEdges.png"); + sm_barEdges = sdl2::TextureManager::create_load_resource(BAR_EDGES, BAR_EDGES); + sm_font = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_PIXEL, + graphics::fonts::sizes::TWENTY_PIXEL); sm_dialog->set_from_transition(m_transition, true); } @@ -152,9 +144,8 @@ void ProgressState::update_progress() noexcept // This is the actual string that's displayed. m_percentageString = stringutil::get_formatted_string("%u%%", m_progress); - const int stringWidth = sdl::text::get_width(BaseTask::FONT_SIZE, m_percentageString); - // Center the string above. - m_percentageX = COORD_DISPLAY_CENTER - (stringWidth / 2); + const int stringWidth = sm_font->get_text_width(m_percentageString); + m_percentageX = math::Util::center_within(graphics::SCREEN_WIDTH, stringWidth); // Handle closing and updating. const bool taskRunning = m_task->is_running(); diff --git a/source/appstates/SaveCreateState.cpp b/source/appstates/SaveCreateState.cpp index c2fc051..3e32093 100644 --- a/source/appstates/SaveCreateState.cpp +++ b/source/appstates/SaveCreateState.cpp @@ -5,7 +5,6 @@ #include "data/data.hpp" #include "error.hpp" #include "fs/fs.hpp" -#include "input.hpp" #include "keyboard/keyboard.hpp" #include "logging/logger.hpp" #include "strings/strings.hpp" @@ -38,14 +37,14 @@ SaveCreateState::SaveCreateState(data::User *user, TitleSelectCommon *titleSelec // ---- Public functions ---- -void SaveCreateState::update() +void SaveCreateState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); - sm_slidePanel->update(hasFocus); + sm_slidePanel->update(input, hasFocus); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); const bool panelClosed = sm_slidePanel->is_closed(); if (m_refreshRequired.load()) @@ -60,13 +59,12 @@ void SaveCreateState::update() else if (panelClosed) { SaveCreateState::deactivate_state(); } } -void SaveCreateState::render() +void SaveCreateState::render(sdl2::Renderer &renderer) { const bool hasFocus = BaseState::has_focus(); // Clear slide target, render menu, render slide to frame buffer. - sm_slidePanel->clear_target(); - sm_slidePanel->render(sdl::Texture::Null, hasFocus); + sm_slidePanel->render(renderer, hasFocus); } void SaveCreateState::refresh_required() { m_refreshRequired.store(true); } diff --git a/source/appstates/SaveImportState.cpp b/source/appstates/SaveImportState.cpp index 2842d4d..89d3ec6 100644 --- a/source/appstates/SaveImportState.cpp +++ b/source/appstates/SaveImportState.cpp @@ -3,7 +3,6 @@ #include "appstates/BackupMenuState.hpp" #include "appstates/ConfirmState.hpp" #include "config/config.hpp" -#include "input.hpp" #include "strings/strings.hpp" #include "stringutil.hpp" #include "tasks/saveimport.hpp" @@ -20,27 +19,26 @@ SaveImportState::SaveImportState(data::User *user) // ---- Public functions ---- -void SaveImportState::update() +void SaveImportState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); - sm_slidePanel->update(hasFocus); + sm_slidePanel->update(input, hasFocus); if (!sm_slidePanel->is_open()) { return; } - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); if (aPressed) { SaveImportState::import_backup(); } else if (bPressed) { sm_slidePanel->close(); } else if (sm_slidePanel->is_closed()) { SaveImportState::deactivate_state(); } } -void SaveImportState::render() +void SaveImportState::render(sdl2::Renderer &renderer) { const bool hasFocus = BaseState::has_focus(); - sm_slidePanel->clear_target(); - sm_slidePanel->render(sdl::Texture::Null, hasFocus); + sm_slidePanel->render(renderer, hasFocus); } // ---- Private functions ---- diff --git a/source/appstates/SettingsState.cpp b/source/appstates/SettingsState.cpp index bd690ff..a96a9ea 100644 --- a/source/appstates/SettingsState.cpp +++ b/source/appstates/SettingsState.cpp @@ -8,8 +8,9 @@ #include "error.hpp" #include "fs/fs.hpp" #include "fslib.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" -#include "input.hpp" +#include "graphics/targets.hpp" #include "keyboard/keyboard.hpp" #include "logging/logger.hpp" #include "strings/strings.hpp" @@ -72,7 +73,10 @@ namespace SettingsState::SettingsState() : m_settingsMenu(ui::Menu::create(32, 10, 1000, 23, 555)) , m_controlGuide(ui::ControlGuide::create(strings::get_by_name(strings::names::CONTROL_GUIDES, 3))) - , m_renderTarget(sdl::TextureManager::load(SECONDARY_TARGET, 1080, 555, SDL_TEXTUREACCESS_TARGET)) + , m_renderTarget(sdl2::TextureManager::create_load_resource(graphics::targets::names::SECONDARY, + graphics::targets::dims::SECONDARY_WIDTH, + graphics::targets::dims::SECONDARY_HEIGHT, + SDL_TEXTUREACCESS_TARGET)) { SettingsState::load_settings_menu(); SettingsState::load_extra_strings(); @@ -81,33 +85,44 @@ SettingsState::SettingsState() // ---- Public functions ---- -void SettingsState::update() +void SettingsState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); - const bool xPressed = input::button_pressed(HidNpadButton_X); - const bool minusPressed = input::button_pressed(HidNpadButton_Minus); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); + const bool xPressed = input.button_pressed(HidNpadButton_X); + const bool minusPressed = input.button_pressed(HidNpadButton_Minus); - m_settingsMenu->update(hasFocus); + m_settingsMenu->update(input, hasFocus); if (aPressed) { SettingsState::toggle_options(); } else if (xPressed) { SettingsState::reset_settings(); } else if (minusPressed) { SettingsState::create_push_description_message(); } else if (bPressed) { BaseState::deactivate(); } - m_controlGuide->update(hasFocus); + m_controlGuide->update(input, hasFocus); } void SettingsState::sub_update() { m_controlGuide->sub_update(); } -void SettingsState::render() +void SettingsState::render(sdl2::Renderer &renderer) { + // Grab focus state. const bool hasFocus = BaseState::has_focus(); - m_renderTarget->clear(colors::TRANSPARENT); - m_settingsMenu->render(m_renderTarget, hasFocus); - m_renderTarget->render(sdl::Texture::Null, 201, 91); - m_controlGuide->render(sdl::Texture::Null, hasFocus); + { + // Set target and clear. + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + renderer.frame_begin(colors::TRANSPARENT); + + // Render what we need to. + m_settingsMenu->render(renderer, hasFocus); + } + + // Render the target to the screen. + m_renderTarget->render(201, 91); + + // Control guide. + m_controlGuide->render(renderer, hasFocus); } // ---- Private functions ---- @@ -194,7 +209,10 @@ void SettingsState::change_working_directory() moved = fs::move_directory_recursively(oldPath, newPath); error::fslib(fslib::delete_directory_recursively(oldPath)); } - else { moved = fslib::rename_directory(oldPath, newPath); } + else + { + moved = fslib::rename_directory(oldPath, newPath); + } if (!moved) { @@ -292,7 +310,10 @@ void SettingsState::toggle_trash_folder() config::toggle_by_key(config::keys::ENABLE_TRASH_BIN); if (trashEnabled) { error::fslib(fslib::delete_directory_recursively(trashPath)); } - else { error::fslib(fslib::create_directory(trashPath)); } + else + { + error::fslib(fslib::create_directory(trashPath)); + } } void SettingsState::cycle_anim_scaling() diff --git a/source/appstates/TaskState.cpp b/source/appstates/TaskState.cpp index 09bbc6c..17ce4c8 100644 --- a/source/appstates/TaskState.cpp +++ b/source/appstates/TaskState.cpp @@ -2,8 +2,9 @@ #include "appstates/FadeState.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" -#include "input.hpp" +#include "mathutil.hpp" #include "sdl.hpp" #include "strings/strings.hpp" #include "ui/PopMessageManager.hpp" @@ -11,26 +12,25 @@ // ---- Construction ---- TaskState::TaskState(sys::threadpool::JobFunction function, sys::Task::TaskData taskData) -{ - m_task = std::make_unique(function, taskData); -} +{ m_task = std::make_unique(function, taskData); } // ---- Public functions ---- -void TaskState::update() +void TaskState::update(const sdl2::Input &input) { - BaseTask::pop_on_plus(); + BaseTask::pop_on_plus(input); BaseTask::update_loading_glyph(); if (!m_task->is_running()) { TaskState::deactivate_state(); } } -void TaskState::render() +void TaskState::render(sdl2::Renderer &renderer) { const std::string status = m_task->get_status(); - const int statusX = 640 - (sdl::text::get_width(BaseTask::FONT_SIZE, status.c_str()) / 2); + const int statusWidth = sm_font->get_text_width(status); + const int statusX = math::Util::center_within(graphics::SCREEN_WIDTH, statusWidth); - sdl::render_rect_fill(sdl::Texture::Null, 0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); - sdl::text::render(sdl::Texture::Null, statusX, 351, BaseTask::FONT_SIZE, sdl::text::NO_WRAP, colors::WHITE, status); + renderer.render_rectangle(0, 0, graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT, colors::DIM_BACKGROUND); + sm_font->render_text(statusX, 351, colors::WHITE, status); BaseTask::render_loading_glyph(); } @@ -40,3 +40,13 @@ void TaskState::deactivate_state() FadeState::create_and_push(colors::DIM_BACKGROUND, colors::ALPHA_FADE_END, colors::ALPHA_FADE_BEGIN, nullptr); BaseState::deactivate(); } + +// ---- Private Functions ---- + +void TaskState::initialize_static_members() +{ + if (sm_font) { return; } + + sm_font = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_PIXEL, + graphics::fonts::sizes::TWENTY_PIXEL); +} \ No newline at end of file diff --git a/source/appstates/TextTitleSelectState.cpp b/source/appstates/TextTitleSelectState.cpp index 0516854..9e7b97a 100644 --- a/source/appstates/TextTitleSelectState.cpp +++ b/source/appstates/TextTitleSelectState.cpp @@ -7,58 +7,63 @@ #include "config/config.hpp" #include "fs/save_mount.hpp" #include "fslib.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" -#include "input.hpp" +#include "graphics/targets.hpp" #include "logging/logger.hpp" #include "sdl.hpp" #include -namespace -{ - // All of these states share this same target. - constexpr std::string_view SECONDARY_TARGET = "SecondaryTarget"; -} // namespace - // ---- Construction ---- TextTitleSelectState::TextTitleSelectState(data::User *user) : TitleSelectCommon() , m_user(user) , m_titleSelectMenu(ui::Menu::create(32, 10, 1000, 23, 555)) - , m_renderTarget(sdl::TextureManager::load(SECONDARY_TARGET, 1080, 555, SDL_TEXTUREACCESS_TARGET)) -{ - TextTitleSelectState::refresh(); -} + , m_renderTarget(sdl2::TextureManager::create_load_resource(graphics::targets::names::SECONDARY, + graphics::targets::dims::SECONDARY_WIDTH, + graphics::targets::dims::SECONDARY_HEIGHT, + SDL_TEXTUREACCESS_TARGET)) +{ TextTitleSelectState::refresh(); } // ---- Public functions ---- -void TextTitleSelectState::update() +void TextTitleSelectState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); - const bool xPressed = input::button_pressed(HidNpadButton_X); - const bool yPressed = input::button_pressed(HidNpadButton_Y); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); + const bool xPressed = input.button_pressed(HidNpadButton_X); + const bool yPressed = input.button_pressed(HidNpadButton_Y); - m_titleSelectMenu->update(hasFocus); + m_titleSelectMenu->update(input, hasFocus); if (aPressed) { TextTitleSelectState::create_backup_menu(); } else if (xPressed) { TextTitleSelectState::create_title_option_menu(); } else if (yPressed) { TextTitleSelectState::add_remove_favorite(); } else if (bPressed) { BaseState::deactivate(); } - sm_controlGuide->update(hasFocus); + sm_controlGuide->update(input, hasFocus); } -void TextTitleSelectState::render() +void TextTitleSelectState::render(sdl2::Renderer &renderer) { + // Grab focus. const bool hasFocus = BaseState::has_focus(); - m_renderTarget->clear(colors::TRANSPARENT); - m_titleSelectMenu->render(m_renderTarget, hasFocus); - sm_controlGuide->render(sdl::Texture::Null, hasFocus); - m_renderTarget->render(sdl::Texture::Null, 201, 91); + { + // Switch targets, clear. + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + renderer.frame_begin(colors::TRANSPARENT); + + // Render menu to target. + m_titleSelectMenu->render(renderer, hasFocus); + } + + // Switch back, render target and guide. + m_renderTarget->render(201, 91); + sm_controlGuide->render(renderer, hasFocus); } void TextTitleSelectState::refresh() @@ -77,7 +82,10 @@ void TextTitleSelectState::refresh() std::string option{}; if (favorite) { option = std::string{STRING_HEART} + title; } - else { option = title; } + else + { + option = title; + } m_titleSelectMenu->add_option(option); } diff --git a/source/appstates/TitleInfoState.cpp b/source/appstates/TitleInfoState.cpp index 25fd9fc..90a6254 100644 --- a/source/appstates/TitleInfoState.cpp +++ b/source/appstates/TitleInfoState.cpp @@ -4,7 +4,6 @@ #include "error.hpp" #include "fs/save_data_functions.hpp" #include "graphics/colors.hpp" -#include "input.hpp" #include "sdl.hpp" #include "strings/strings.hpp" #include "stringutil.hpp" @@ -36,8 +35,8 @@ namespace } // namespace // Defined at bottom -static inline std::shared_ptr create_new_field(std::string_view text, int x, int y, sdl::Color clear); -static inline std::shared_ptr create_new_field(std::string &text, int x, int y, sdl::Color clear); +static inline std::shared_ptr create_new_field(std::string_view text, int x, int y, SDL_Color clear); +static inline std::shared_ptr create_new_field(std::string &text, int x, int y, SDL_Color clear); static bool is_a_best_game(uint64_t applicationID) noexcept; // ---- Construction ---- @@ -65,35 +64,34 @@ TitleInfoState::TitleInfoState(data::User *user, data::TitleInfo *titleInfo, con // ---- Public functions ---- -void TitleInfoState::update() +void TitleInfoState::update(const sdl2::Input &input) { switch (m_state) { case State::Opening: case State::Closing: TitleInfoState::update_dimensions(); break; - case State::Displaying: TitleInfoState::update_handle_input(); break; + case State::Displaying: TitleInfoState::update_handle_input(input); break; } } -void TitleInfoState::render() +void TitleInfoState::render(sdl2::Renderer &renderer) { // Grab cause needed everywhere. const bool hasFocus = BaseState::has_focus(); // Render the frame. Only continue further if we're currently displaying. - sm_frame->render(sdl::Texture::Null, hasFocus); + sm_frame->render(renderer, hasFocus); if (m_state != State::Displaying) { return; } // We only want to render what's been triggered so far for the tiling in effect. - for (int i = 0; i < m_fieldDisplayCount; i++) { m_infoFields[i]->render(sdl::Texture::Null, hasFocus); } + for (int i = 0; i < m_fieldDisplayCount; i++) { m_infoFields[i]->render(renderer, hasFocus); } } // ---- Private functions ---- void TitleInfoState::initialize_static_members() { - static constexpr std::string_view CHIME_NAME = "TitleInfoChime"; - static constexpr const char *CHIME_PATH = "romfs:/Sound/TitleInfo.wav"; + static constexpr std::string_view CHIME_PATH = "romfs:/Sound/TitleInfo.wav"; if (sm_frame && sm_openChime) { @@ -106,7 +104,7 @@ void TitleInfoState::initialize_static_members() const int width = m_transition.get_width(); const int height = m_transition.get_height(); sm_frame = ui::Frame::create(x, y, width, height); - sm_openChime = sdl::SoundManager::load(CHIME_NAME, CHIME_PATH); + sm_openChime = sdl2::SoundManager::create_load_resource(CHIME_PATH, CHIME_PATH); } void TitleInfoState::initialize_info_fields() @@ -166,13 +164,13 @@ void TitleInfoState::create_title(int y) { std::string bestTitle = BESTEST_STAR + " " + title + " " + BESTEST_STAR; field = ui::TextScroll::create(bestTitle, - X, - y, - WIDTH, - TITLE_FIELD_HEIGHT, - TITLE_FONT_SIZE, - colors::WHITE, - colors::TRANSPARENT); + X, + y, + WIDTH, + TITLE_FIELD_HEIGHT, + TITLE_FONT_SIZE, + colors::WHITE, + colors::TRANSPARENT); } else { @@ -377,7 +375,7 @@ void TitleInfoState::update_dimensions() noexcept else if (closed) { TitleInfoState::deactivate_state(); } } -void TitleInfoState::update_handle_input() noexcept +void TitleInfoState::update_handle_input(const sdl2::Input &input) noexcept { // Grab a cache this since it's needed a lot. const bool hasFocus = BaseState::has_focus(); @@ -387,14 +385,14 @@ void TitleInfoState::update_handle_input() noexcept if (m_fieldDisplayCount < currentCount && m_timer.is_triggered()) { ++m_fieldDisplayCount; } // Update the actually displayed fields so the text scrolls if need be. - for (int i = 0; i < m_fieldDisplayCount; i++) { m_infoFields[i]->update(hasFocus); } + for (int i = 0; i < m_fieldDisplayCount; i++) { m_infoFields[i]->update(input, hasFocus); } // Input bools. - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool bPressed = input.button_pressed(HidNpadButton_B); if (bPressed) { TitleInfoState::close(); } } -inline sdl::Color TitleInfoState::get_field_color() noexcept +inline SDL_Color TitleInfoState::get_field_color() noexcept { m_fieldClearSwitch = m_fieldClearSwitch ? false : true; return m_fieldClearSwitch ? colors::DIALOG_DARK : colors::CLEAR_COLOR; @@ -411,15 +409,11 @@ void TitleInfoState::deactivate_state() { BaseState::deactivate(); } // ---- Static functions ---- -static inline std::shared_ptr create_new_field(std::string_view text, int x, int y, sdl::Color clear) -{ - return ui::TextScroll::create(text, x, y, SIZE_FIELD_WIDTH, SIZE_FIELD_HEIGHT, SIZE_FONT, colors::WHITE, clear, false); -} +static inline std::shared_ptr create_new_field(std::string_view text, int x, int y, SDL_Color clear) +{ return ui::TextScroll::create(text, x, y, SIZE_FIELD_WIDTH, SIZE_FIELD_HEIGHT, SIZE_FONT, colors::WHITE, clear, false); } -static inline std::shared_ptr create_new_field(std::string &text, int x, int y, sdl::Color clear) -{ - return ui::TextScroll::create(text, x, y, SIZE_FIELD_WIDTH, SIZE_FIELD_HEIGHT, SIZE_FONT, colors::WHITE, clear, false); -} +static inline std::shared_ptr create_new_field(std::string &text, int x, int y, SDL_Color clear) +{ return ui::TextScroll::create(text, x, y, SIZE_FIELD_WIDTH, SIZE_FIELD_HEIGHT, SIZE_FONT, colors::WHITE, clear, false); } static bool is_a_best_game(uint64_t applicationID) noexcept { diff --git a/source/appstates/TitleOptionState.cpp b/source/appstates/TitleOptionState.cpp index dff39ef..683d88c 100644 --- a/source/appstates/TitleOptionState.cpp +++ b/source/appstates/TitleOptionState.cpp @@ -10,7 +10,6 @@ #include "fs/fs.hpp" #include "fslib.hpp" #include "graphics/colors.hpp" -#include "input.hpp" #include "keyboard/keyboard.hpp" #include "logging/logger.hpp" #include "remote/remote.hpp" @@ -58,16 +57,16 @@ TitleOptionState::TitleOptionState(data::User *user, // ---- Public functions ---- -void TitleOptionState::update() +void TitleOptionState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); sm_slidePanel->unhide_on_focus(hasFocus); - sm_slidePanel->update(hasFocus); + sm_slidePanel->update(input, hasFocus); const bool isOpen = sm_slidePanel->is_open(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); const int selected = sm_titleOptionMenu->get_selected(); if (m_refreshRequired) @@ -103,12 +102,13 @@ void TitleOptionState::update() void TitleOptionState::sub_update() { sm_slidePanel->sub_update(); } -void TitleOptionState::render() +void TitleOptionState::render(sdl2::Renderer &renderer) { + // Grab focus. const bool hasFocus = BaseState::has_focus(); - sm_slidePanel->clear_target(); - sm_slidePanel->render(sdl::Texture::Null, hasFocus); + // Render panel. + sm_slidePanel->render(renderer, hasFocus); } void TitleOptionState::close_on_update() { m_exitRequired = true; } diff --git a/source/appstates/TitleSelectState.cpp b/source/appstates/TitleSelectState.cpp index be7c8f5..65b36e0 100644 --- a/source/appstates/TitleSelectState.cpp +++ b/source/appstates/TitleSelectState.cpp @@ -5,8 +5,9 @@ #include "appstates/MainMenuState.hpp" #include "appstates/TitleOptionState.hpp" #include "config/config.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" -#include "input.hpp" +#include "graphics/targets.hpp" #include "logging/logger.hpp" #include "sdl.hpp" #include "strings/strings.hpp" @@ -24,21 +25,23 @@ namespace TitleSelectState::TitleSelectState(data::User *user) : TitleSelectCommon() , m_user(user) - , m_renderTarget(sdl::TextureManager::load(SECONDARY_TARGET, 1080, 555, SDL_TEXTUREACCESS_TARGET)) + , m_renderTarget(sdl2::TextureManager::create_load_resource(graphics::targets::names::SECONDARY, + graphics::targets::dims::SECONDARY_WIDTH, + graphics::targets::dims::SECONDARY_HEIGHT, + SDL_TEXTUREACCESS_TARGET)) , m_titleView(ui::TitleView::create(m_user)) {}; - // ---- Public functions ---- -void TitleSelectState::update() +void TitleSelectState::update(const sdl2::Input &input) { if (!TitleSelectState::title_count_check()) { return; } const bool hasFocus = BaseState::has_focus(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); - const bool xPressed = input::button_pressed(HidNpadButton_X); - const bool yPressed = input::button_pressed(HidNpadButton_Y); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); + const bool xPressed = input.button_pressed(HidNpadButton_X); + const bool yPressed = input.button_pressed(HidNpadButton_Y); if (aPressed) { TitleSelectState::create_backup_menu(); } else if (xPressed) { TitleSelectState::create_title_option_menu(); } @@ -49,18 +52,26 @@ void TitleSelectState::update() return; } - m_titleView->update(hasFocus); - sm_controlGuide->update(hasFocus); + m_titleView->update(input, hasFocus); + sm_controlGuide->update(input, hasFocus); } -void TitleSelectState::render() +void TitleSelectState::render(sdl2::Renderer &renderer) { + // Grab focus status. const bool hasFocus = BaseState::has_focus(); - m_renderTarget->clear(colors::TRANSPARENT); - m_titleView->render(m_renderTarget, hasFocus); - sm_controlGuide->render(sdl::Texture::Null, hasFocus); - m_renderTarget->render(sdl::Texture::Null, 201, 91); + { + // Set target, clear. + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + renderer.frame_begin(colors::TRANSPARENT); + + // Render view. + m_titleView->render(renderer, hasFocus); + } + + m_renderTarget->render(201, 91); + sm_controlGuide->render(renderer, hasFocus); } void TitleSelectState::refresh() { m_titleView->refresh(); } diff --git a/source/appstates/UserOptionState.cpp b/source/appstates/UserOptionState.cpp index 36530e4..858eca9 100644 --- a/source/appstates/UserOptionState.cpp +++ b/source/appstates/UserOptionState.cpp @@ -12,7 +12,6 @@ #include "error.hpp" #include "fs/fs.hpp" #include "fslib.hpp" -#include "input.hpp" #include "logging/logger.hpp" #include "remote/remote.hpp" #include "strings/strings.hpp" @@ -49,15 +48,15 @@ UserOptionState::UserOptionState(data::User *user, TitleSelectCommon *titleSelec // ---- Public functions ---- -void UserOptionState::update() +void UserOptionState::update(const sdl2::Input &input) { const bool hasFocus = BaseState::has_focus(); - const bool aPressed = input::button_pressed(HidNpadButton_A); - const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool aPressed = input.button_pressed(HidNpadButton_A); + const bool bPressed = input.button_pressed(HidNpadButton_B); // Start by running the unhide check routine and then update the panel. sm_menuPanel->unhide_on_focus(hasFocus); - sm_menuPanel->update(hasFocus); + sm_menuPanel->update(input, hasFocus); // Refresh here if needed to avoid threading issues. if (m_refreshRequired) @@ -86,14 +85,13 @@ void UserOptionState::update() void UserOptionState::sub_update() { sm_menuPanel->sub_update(); } -void UserOptionState::render() +void UserOptionState::render(sdl2::Renderer &renderer) { // Render target user's title selection screen. - m_titleSelect->render(); + m_titleSelect->render(renderer); // Render panel. - sm_menuPanel->clear_target(); - sm_menuPanel->render(sdl::Texture::Null, BaseState::has_focus()); + sm_menuPanel->render(renderer, BaseState::has_focus()); } void UserOptionState::refresh_required() { m_refreshRequired = true; } diff --git a/source/data/DataContext.cpp b/source/data/DataContext.cpp index c35dfe5..3c67b77 100644 --- a/source/data/DataContext.cpp +++ b/source/data/DataContext.cpp @@ -104,7 +104,8 @@ void data::DataContext::load_application_records(sys::Task *task) NsApplicationRecord record{}; bool listError{}; - do { + do + { listError = error::libnx(nsListApplicationRecord(&record, 1, offset++, &count)) || count <= 0; if (listError) { break; } if (DataContext::title_is_loaded(record.application_id)) { continue; } @@ -217,7 +218,8 @@ bool data::DataContext::read_cache(sys::Task *task) task->set_status(statusLoadingCache); NsApplicationControlData controlData{}; - do { + do + { const bool dataRead = cacheZip.read(&controlData, SIZE_CTRL_DATA) == SIZE_CTRL_DATA; if (!dataRead) { continue; } @@ -270,9 +272,9 @@ bool data::DataContext::write_cache(sys::Task *task) return true; } -void data::DataContext::process_icon_queue() +void data::DataContext::process_icon_queue(sdl2::Renderer &renderer) { std::lock_guard multiGuard{m_iconQueueMutex}; - for (data::DataCommon *common : m_iconQueue) { common->load_icon(); } + for (data::DataCommon *common : m_iconQueue) { common->load_icon(renderer); } m_iconQueue.clear(); } diff --git a/source/data/TitleInfo.cpp b/source/data/TitleInfo.cpp index 17b55b1..d679e1b 100644 --- a/source/data/TitleInfo.cpp +++ b/source/data/TitleInfo.cpp @@ -21,10 +21,10 @@ data::TitleInfo::TitleInfo(uint64_t applicationID) noexcept // This will filter from even trying to fetch control data for system titles. const bool isSystem = applicationID & 0x8000000000000000; const bool getError = !isSystem && error::libnx(nsGetApplicationControlData(NsApplicationControlSource_Storage, - m_applicationID, - &m_data, - SIZE_CTRL_DATA, - &controlSize)); + m_applicationID, + &m_data, + SIZE_CTRL_DATA, + &controlSize)); const bool entryError = !getError && error::libnx(nacpGetLanguageEntry(&m_data.nacp, &m_entry)); if (isSystem || getError) { @@ -149,8 +149,6 @@ bool data::TitleInfo::has_save_data_type(uint8_t saveType) const noexcept return false; } -sdl::SharedTexture data::TitleInfo::get_icon() const noexcept { return m_icon; } - void data::TitleInfo::set_path_safe_title(const char *newPathSafe) noexcept { const size_t length = std::char_traits::length(newPathSafe); @@ -160,20 +158,16 @@ void data::TitleInfo::set_path_safe_title(const char *newPathSafe) noexcept std::memcpy(m_pathSafeTitle, newPathSafe, length); } -void data::TitleInfo::load_icon() +void data::TitleInfo::load_icon(sdl2::Renderer &renderer) { // This is taken from the NacpStruct. static constexpr size_t SIZE_ICON = 0x20000; + const std::string textureName = stringutil::get_formatted_string("%04X", m_applicationID & 0xFFFF); - if (m_hasData) - { - const std::string textureName = stringutil::get_formatted_string("%016llX", m_applicationID); - m_icon = sdl::TextureManager::load(textureName, m_data.icon, SIZE_ICON); - } + if (m_hasData) { m_icon = sdl2::TextureManager::create_load_resource(textureName, m_data.icon, SIZE_ICON); } else { - const std::string text = stringutil::get_formatted_string("%04X", m_applicationID & 0xFFFF); - m_icon = gfxutil::create_generic_icon(text, 48, colors::DIALOG_DARK, colors::WHITE); + m_icon = gfxutil::create_generic_icon(renderer, textureName, 48, colors::DIALOG_DARK, colors::WHITE); } } diff --git a/source/data/User.cpp b/source/data/User.cpp index 9839fa7..a90bfad 100644 --- a/source/data/User.cpp +++ b/source/data/User.cpp @@ -45,7 +45,10 @@ data::User::User(AccountUid accountID, FsSaveDataType saveType) noexcept const bool profileError = error::libnx(accountGetProfile(&profile, m_accountID)); const bool baseError = !profileError && error::libnx(accountProfileGet(&profile, nullptr, &profileBase)); if (profileError || baseError) { User::create_account(); } - else { User::load_account(profile, profileBase); } + else + { + User::load_account(profile, profileBase); + } accountProfileClose(&profile); } @@ -174,7 +177,10 @@ void data::User::load_user_data() { infoReader.open(SAVE_DATA_SPACE_ORDER[i], m_accountID, SIZE_SAVE_INFO_BUFFER); } - else { infoReader.open(SAVE_DATA_SPACE_ORDER[i], m_saveType, SIZE_SAVE_INFO_BUFFER); } + else + { + infoReader.open(SAVE_DATA_SPACE_ORDER[i], m_saveType, SIZE_SAVE_INFO_BUFFER); + } if (!infoReader.is_open()) { continue; } while (infoReader.read()) @@ -218,7 +224,7 @@ void data::User::load_user_data() User::sort_data(); } -void data::User::load_icon() +void data::User::load_icon(sdl2::Renderer &renderer) { const std::string iconName = stringutil::get_formatted_string("%016llX%016llX", m_nickname, m_accountID.uid[0], m_accountID.uid[1]); @@ -230,7 +236,7 @@ void data::User::load_icon() const bool sizeError = !profileError && error::libnx(accountProfileGetImageSize(&profile, &iconSize)); if (profileError || sizeError) { - m_icon = gfxutil::create_generic_icon(m_nickname, SIZE_ICON_FONT, colors::DIALOG_DARK, colors::WHITE); + m_icon = gfxutil::create_generic_icon(renderer, m_nickname, SIZE_ICON_FONT, colors::DIALOG_DARK, colors::WHITE); return; } @@ -239,9 +245,12 @@ void data::User::load_icon() if (loadError) { return; } accountProfileClose(&profile); - m_icon = sdl::TextureManager::load(iconName, iconBuffer.get(), iconSize); + m_icon = sdl2::TextureManager::create_load_resource(iconName, iconBuffer.get(), iconSize); + } + else + { + m_icon = gfxutil::create_generic_icon(renderer, m_nickname, SIZE_ICON_FONT, colors::DIALOG_DARK, colors::WHITE); } - else { m_icon = gfxutil::create_generic_icon(m_nickname, SIZE_ICON_FONT, colors::DIALOG_DARK, colors::WHITE); } } void data::User::load_account(AccountProfile &profile, AccountProfileBase &profileBase) diff --git a/source/data/data.cpp b/source/data/data.cpp index e236707..2eb075f 100644 --- a/source/data/data.cpp +++ b/source/data/data.cpp @@ -25,21 +25,19 @@ namespace /// @brief The main routine for the task to load data. static void data_initialize_task(sys::threadpool::JobData taskData); -void data::launch_initialization(bool clearCache, std::function onDestruction) +void data::launch_initialization(bool clearCache, sdl2::Renderer &renderer, std::function onDestruction) { auto taskData = std::make_shared(); taskData->clearCache = clearCache; - auto loadingState = DataLoadingState::create(s_context, onDestruction, data_initialize_task, taskData); + auto loadingState = DataLoadingState::create(s_context, renderer, onDestruction, data_initialize_task, taskData); StateManager::push_state(loadingState); } void data::get_users(data::UserList &userList) { s_context.get_users(userList); } data::TitleInfo *data::get_title_info_by_id(uint64_t applicationID) noexcept -{ - return s_context.get_title_by_id(applicationID); -} +{ return s_context.get_title_by_id(applicationID); } void data::load_title_to_map(uint64_t applicationID) { s_context.load_title(applicationID); } @@ -48,9 +46,7 @@ bool data::title_exists_in_map(uint64_t applicationID) noexcept { return s_conte void data::get_title_info_list(data::TitleInfoList &listOut) { s_context.get_title_info_list(listOut); } void data::get_title_info_by_type(FsSaveDataType saveType, data::TitleInfoList &listOut) -{ - s_context.get_title_info_list_by_type(saveType, listOut); -} +{ s_context.get_title_info_list_by_type(saveType, listOut); } static void data_initialize_task(sys::threadpool::JobData taskData) { diff --git a/source/gfxutil.cpp b/source/gfxutil.cpp index 30da592..e30731e 100644 --- a/source/gfxutil.cpp +++ b/source/gfxutil.cpp @@ -1,28 +1,41 @@ #include "graphics/gfxutil.hpp" -namespace +#include "graphics/ScopedRender.hpp" +#include "mathutil.hpp" +#include "stringutil.hpp" + +sdl2::SharedTexture gfxutil::create_generic_icon(sdl2::Renderer &renderer, + std::string_view text, + int fontSize, + SDL_Color background, + SDL_Color textColor) { - /// @brief Width of generic icons in pixels. - constexpr int SIZE_ICON_WIDTH = 256; + // Icon dimensions. + static constexpr int ICON_WIDTH = 256; + static constexpr int ICON_HEIGHT = 256; - /// @brief Height of generic icons in pixels. - constexpr int SIZE_ICON_HEIGHT = 256; -} // namespace + // Create a new font to render with. + const std::string fontName = stringutil::get_formatted_string("IconFont%i", fontSize); + sdl2::SharedFont font = sdl2::FontManager::create_load_resource(fontName, fontSize); -sdl::SharedTexture gfxutil::create_generic_icon(std::string_view text, - int fontSize, - sdl::Color background, - sdl::Color foreground) -{ - // Create base icon texture. - sdl::SharedTexture icon = sdl::TextureManager::load(text, SIZE_ICON_WIDTH, SIZE_ICON_HEIGHT, SDL_TEXTUREACCESS_TARGET); + // Center our text. + const int textWidth = font->get_text_width(text); + const int textX = math::Util::center_within(ICON_WIDTH, textWidth); + const int textY = math::Util::center_within(ICON_HEIGHT, fontSize); - // Get the centered X and Y coordinates. - const int textX = (SIZE_ICON_WIDTH / 2) - (sdl::text::get_width(fontSize, text) / 2); - const int textY = (SIZE_ICON_HEIGHT / 2) - (fontSize / 2); + // Create the icon. + sdl2::SharedTexture icon = + sdl2::TextureManager::create_load_resource(text, ICON_WIDTH, ICON_HEIGHT, SDL_TEXTUREACCESS_TARGET); - icon->clear(background); - sdl::text::render(icon, textX, textY, fontSize, sdl::text::NO_WRAP, foreground, text); + { + // Target the icon we just created. + graphics::ScopedRender scopedRender(renderer, icon); + // Render. + renderer.frame_begin(background); + font->render_text(textX, textY, textColor, text); + } + + // Return. return icon; } diff --git a/source/input.cpp b/source/input.cpp deleted file mode 100644 index 8b6e6df..0000000 --- a/source/input.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "input.hpp" - -namespace -{ - PadState s_gamepad; -} - -void input::initialize() -{ - padConfigureInput(1, HidNpadStyleSet_NpadStandard); - padInitializeDefault(&s_gamepad); -} - -void input::update() noexcept { padUpdate(&s_gamepad); } - -bool input::button_pressed(HidNpadButton button) noexcept -{ - return (s_gamepad.buttons_cur & button) && !(s_gamepad.buttons_old & button); -} - -bool input::button_held(HidNpadButton button) noexcept -{ - return (s_gamepad.buttons_cur & button) && (s_gamepad.buttons_old & button); -} - -bool input::button_released(HidNpadButton button) noexcept -{ - return (s_gamepad.buttons_old & button) && !(s_gamepad.buttons_cur & button); -} diff --git a/source/remote/remote.cpp b/source/remote/remote.cpp index 708573c..8eee7b0 100644 --- a/source/remote/remote.cpp +++ b/source/remote/remote.cpp @@ -3,7 +3,6 @@ #include "StateManager.hpp" #include "appstates/TaskState.hpp" #include "error.hpp" -#include "input.hpp" #include "logging/logger.hpp" #include "remote/GoogleDrive.hpp" #include "remote/WebDav.hpp" @@ -136,10 +135,6 @@ static void drive_sign_in(sys::threadpool::JobData taskData) while (std::time(NULL) < expiration && !drive->poll_sign_in(deviceCode)) { - const bool bPressed = input::button_pressed(HidNpadButton_B); - const bool bHeld = input::button_held(HidNpadButton_B); - if (bPressed || bHeld) { break; } - std::this_thread::sleep_for(std::chrono::seconds(pollingInterval)); } diff --git a/source/ui/BoundingBox.cpp b/source/ui/BoundingBox.cpp index ed4ec7b..e6b0319 100644 --- a/source/ui/BoundingBox.cpp +++ b/source/ui/BoundingBox.cpp @@ -16,21 +16,26 @@ ui::BoundingBox::BoundingBox(int x, int y, int width, int height) , m_y(y) , m_width(width) , m_height(height) -{ - BoundingBox::initialize_static_members(); -} +{ BoundingBox::initialize_static_members(); } // ---- Public functions ---- -void ui::BoundingBox::update(bool hasFocus) { m_colorMod.update(); } +void ui::BoundingBox::update(const sdl2::Input &input, bool hasFocus) { m_colorMod.update(); } -void ui::BoundingBox::render(sdl::SharedTexture &target, bool hasFocus) +void ui::BoundingBox::render(sdl2::Renderer &renderer, bool hasFocus) { + // Sizes of the pieces. + static constexpr int CORNER_WIDTH = 8; + static constexpr int CORNER_HEIGHT = 8; + static constexpr int RECT_WIDTH = 4; + static constexpr int RECT_HEIGHT = 4; + + // Set texture color modifier. sm_corners->set_color_mod(m_colorMod); - const int rightX = (m_x + m_width) - CORNER_WIDTH; - const int rightRectX = (m_x + m_width) - RECT_WIDTH; - + // Calculating this all here maker the rest easier. + const int rightX = (m_x + m_width) - CORNER_WIDTH; + const int rightRectX = (m_x + m_width) - RECT_WIDTH; const int midX = m_x + CORNER_WIDTH; const int midY = m_y + CORNER_HEIGHT; const int midWidth = m_width - (CORNER_WIDTH * 2); @@ -38,16 +43,19 @@ void ui::BoundingBox::render(sdl::SharedTexture &target, bool hasFocus) const int bottomY = (m_y + m_height) - CORNER_HEIGHT; const int bottomRectY = (m_y + m_height) - RECT_HEIGHT; - sm_corners->render_part(target, m_x, m_y, 0, 0, CORNER_WIDTH, CORNER_HEIGHT); - sdl::render_rect_fill(target, midX, m_y, midWidth, RECT_HEIGHT, m_colorMod); - sm_corners->render_part(target, rightX, m_y, CORNER_HEIGHT, 0, CORNER_WIDTH, CORNER_HEIGHT); + // Top + sm_corners->render_part(m_x, m_y, 0, 0, CORNER_WIDTH, CORNER_HEIGHT); + renderer.render_rectangle(midX, m_y, midWidth, RECT_HEIGHT, m_colorMod); + sm_corners->render_part(rightX, m_y, CORNER_HEIGHT, 0, CORNER_WIDTH, CORNER_HEIGHT); + // Middle - sdl::render_rect_fill(target, m_x, midY, RECT_WIDTH, midHeight, m_colorMod); - sdl::render_rect_fill(target, rightRectX, midY, RECT_WIDTH, midHeight, m_colorMod); + renderer.render_rectangle(m_x, midY, RECT_WIDTH, midHeight, m_colorMod); + renderer.render_rectangle(rightRectX, midY, RECT_WIDTH, midHeight, m_colorMod); + // Bottom - sm_corners->render_part(target, m_x, bottomY, 0, CORNER_HEIGHT, CORNER_WIDTH, CORNER_HEIGHT); - sdl::render_rect_fill(target, midX, bottomRectY, midWidth, RECT_HEIGHT, m_colorMod); - sm_corners->render_part(target, rightX, bottomY, CORNER_WIDTH, CORNER_HEIGHT, CORNER_WIDTH, CORNER_HEIGHT); + sm_corners->render_part(m_x, bottomY, 0, CORNER_HEIGHT, CORNER_WIDTH, CORNER_HEIGHT); + renderer.render_rectangle(midX, bottomRectY, midWidth, RECT_HEIGHT, m_colorMod); + sm_corners->render_part(rightX, bottomY, CORNER_WIDTH, CORNER_HEIGHT, CORNER_WIDTH, CORNER_HEIGHT); } void ui::BoundingBox::set_x(int x) noexcept { m_x = x; } @@ -62,6 +70,9 @@ void ui::BoundingBox::set_height(int height) noexcept { m_height = height; } void ui::BoundingBox::initialize_static_members() { + // Path to load the corner texture from? + static constexpr std::string_view CORNER_PATH = "romfs:/Textures/MenuBounding.png"; + if (sm_corners) { return; } - sm_corners = sdl::TextureManager::load("menuCorners", "romfs:/Textures/MenuBounding.png"); + sm_corners = sdl2::TextureManager::create_load_resource(CORNER_PATH, CORNER_PATH); } diff --git a/source/ui/ColorMod.cpp b/source/ui/ColorMod.cpp index 1da05b0..d364586 100644 --- a/source/ui/ColorMod.cpp +++ b/source/ui/ColorMod.cpp @@ -10,11 +10,5 @@ void ui::ColorMod::update() noexcept else if (changeUp) { m_direction = true; } } -ui::ColorMod::operator sdl::Color() const noexcept -{ - uint32_t color{}; - color |= static_cast((0x88 + m_colorMod) << 16); - color |= static_cast((0xC5 + (m_colorMod / 2)) << 8); - color |= 0xFF; - return sdl::Color{color}; -} +ui::ColorMod::operator SDL_Color() const noexcept +{ return SDL_Color{0x00, static_cast(0x88 + m_colorMod), static_cast(0xC5 + m_colorMod * 0.5), 0xFF}; } diff --git a/source/ui/ControlGuide.cpp b/source/ui/ControlGuide.cpp index 7f6fdb5..6259cba 100644 --- a/source/ui/ControlGuide.cpp +++ b/source/ui/ControlGuide.cpp @@ -1,6 +1,7 @@ #include "ui/ControlGuide.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" #include "logging/logger.hpp" @@ -23,18 +24,21 @@ namespace ui::ControlGuide::ControlGuide(const char *guide) : m_guide(guide) - , m_textWidth(sdl::text::get_width(GUIDE_TEXT_SIZE, m_guide)) , m_targetX(GUIDE_X_OFFSET - (m_textWidth + CONTAINER_PADDING)) , m_guideWidth(graphics::SCREEN_WIDTH - m_targetX) , m_transition(graphics::SCREEN_WIDTH, TRANS_Y, 0, 0, m_targetX, TRANS_Y, 0, 0, ui::Transition::DEFAULT_THRESHOLD) , m_state(State::Hidden) { + // Init static. ui::ControlGuide::initialize_static_members(); + + // Grab the width of the text. + m_textWidth = sm_font->get_text_width(m_guide); } // ---- Public functions ---- -void ui::ControlGuide::update(bool hasFocus) +void ui::ControlGuide::update(const sdl2::Input &input, bool hasFocus) { switch (m_state) { @@ -46,42 +50,35 @@ void ui::ControlGuide::update(bool hasFocus) void ui::ControlGuide::sub_update() { - // To do: Maybe this differently? - ControlGuide::update(false); + // I don't like repeating, but it's the easiest way to adapt this for my SDL2 changes. + switch (m_state) + { + case State::Opening: + case State::Hiding: ControlGuide::update_position_state(); break; + default: ControlGuide::update_state(false); break; + } } -void ui::ControlGuide::render(sdl::SharedTexture &target, bool hasFocus) +void ui::ControlGuide::render(sdl2::Renderer &renderer, bool hasFocus) { // These are for the rectangle that makes up the rest of the container. static constexpr int RECT_OFFSET_X = 16; static constexpr int RECT_HEIGHT = 48; // This is where the text is rendered and its size. - static constexpr int TEXT_OFFSET_X = 24; - static constexpr int TEXT_OFFSET_Y = 10; - static constexpr int TEXT_FONT_SIZE = 24; + static constexpr int TEXT_OFFSET_X = 24; + static constexpr int TEXT_OFFSET_Y = 10; // Grab the X and Y. const int guideX = m_transition.get_x(); const int guideY = m_transition.get_y(); // Render the cap and the rectangle. - sm_controlCap->render(sdl::Texture::Null, guideX, guideY); - sdl::render_rect_fill(sdl::Texture::Null, - guideX + RECT_OFFSET_X, - guideY, - m_guideWidth - RECT_OFFSET_X, - RECT_HEIGHT, - colors::GUIDE_COLOR); + sm_controlCap->render(guideX, guideY); + renderer.render_rectangle(guideX + RECT_OFFSET_X, guideY, m_guideWidth - RECT_OFFSET_X, RECT_HEIGHT, colors::GUIDE_COLOR); // Guide text. - sdl::text::render(sdl::Texture::Null, - guideX + TEXT_OFFSET_X, - guideY + TEXT_OFFSET_Y, - TEXT_FONT_SIZE, - sdl::text::NO_WRAP, - colors::WHITE, - m_guide); + sm_font->render_text(guideX + TEXT_OFFSET_X, guideY + TEXT_OFFSET_Y, colors::WHITE, m_guide); } // ---- Private functions ---- @@ -89,11 +86,14 @@ void ui::ControlGuide::render(sdl::SharedTexture &target, bool hasFocus) void ui::ControlGuide::initialize_static_members() { // Name for the texture manager. - static constexpr std::string_view NAME_CAP = "ControlGuideCap"; + static constexpr std::string_view CAP_PATH = "romfs:/Textures/GuideCap.png"; // If it's already loaded, return. - if (sm_controlCap) { return; } - sm_controlCap = sdl::TextureManager::load(NAME_CAP, "romfs:/Textures/GuideCap.png"); + if (sm_controlCap && sm_font) { return; } + + sm_controlCap = sdl2::TextureManager::create_load_resource(CAP_PATH, CAP_PATH); + sm_font = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_FOUR_PIXEL, + graphics::fonts::sizes::TWENTY_FOUR_PIXEL); } void ui::ControlGuide::reset() noexcept diff --git a/source/ui/DialogBox.cpp b/source/ui/DialogBox.cpp index 7396623..f019de0 100644 --- a/source/ui/DialogBox.cpp +++ b/source/ui/DialogBox.cpp @@ -16,36 +16,32 @@ ui::DialogBox::DialogBox(int x, int y, int width, int height, ui::DialogBox::Typ , m_width(width) , m_height(height) , m_type(type) -{ - ui::DialogBox::initialize_static_members(); -} +{ ui::DialogBox::initialize_static_members(); } // ---- Public functions ---- -void ui::DialogBox::render(sdl::SharedTexture &target, bool hasFocus) +void ui::DialogBox::render(sdl2::Renderer &renderer, bool hasFocus) { - const bool darkDialog = m_type == DialogBox::Type::Dark; - sdl::SharedTexture &corners = darkDialog ? sm_darkCorners : sm_lightCorners; - const sdl::Color rectColor = darkDialog ? colors::DIALOG_DARK : colors::DIALOG_LIGHT; + const bool darkDialog = m_type == DialogBox::Type::Dark; + sdl2::SharedTexture &corners = darkDialog ? sm_darkCorners : sm_lightCorners; + const SDL_Color rectColor = darkDialog ? colors::DIALOG_DARK : colors::DIALOG_LIGHT; // Top - corners->render_part(target, m_x, m_y, 0, 0, CORNER_WIDTH, CORNER_HEIGHT); - sdl::render_rect_fill(target, m_x + CORNER_WIDTH, m_y, m_width - (CORNER_WIDTH * 2), CORNER_HEIGHT, rectColor); - corners->render_part(target, (m_x + m_width) - CORNER_WIDTH, m_y, CORNER_WIDTH, 0, CORNER_WIDTH, CORNER_HEIGHT); + corners->render_part(m_x, m_y, 0, 0, CORNER_WIDTH, CORNER_HEIGHT); + renderer.render_rectangle(m_x + CORNER_WIDTH, m_y, m_width - (CORNER_WIDTH * 2), CORNER_HEIGHT, rectColor); + corners->render_part((m_x + m_width) - CORNER_WIDTH, m_y, CORNER_WIDTH, 0, CORNER_WIDTH, CORNER_HEIGHT); // Middle - sdl::render_rect_fill(target, m_x, m_y + CORNER_HEIGHT, m_width, m_height - (CORNER_HEIGHT * 2), rectColor); + renderer.render_rectangle(m_x, m_y + CORNER_HEIGHT, m_width, m_height - (CORNER_HEIGHT * 2), rectColor); // Bottom - corners->render_part(target, m_x, (m_y + m_height) - CORNER_HEIGHT, 0, CORNER_HEIGHT, CORNER_WIDTH, CORNER_HEIGHT); - sdl::render_rect_fill(target, - m_x + CORNER_WIDTH, - (m_y + m_height) - CORNER_HEIGHT, - m_width - (CORNER_WIDTH * 2), - CORNER_HEIGHT, - rectColor); - corners->render_part(target, - (m_x + m_width) - CORNER_WIDTH, + corners->render_part(m_x, (m_y + m_height) - CORNER_HEIGHT, 0, CORNER_HEIGHT, CORNER_WIDTH, CORNER_HEIGHT); + renderer.render_rectangle(m_x + CORNER_WIDTH, + (m_y + m_height) - CORNER_HEIGHT, + m_width - (CORNER_WIDTH * 2), + CORNER_HEIGHT, + rectColor); + corners->render_part((m_x + m_width) - CORNER_WIDTH, (m_y + m_height) - CORNER_HEIGHT, CORNER_WIDTH, CORNER_HEIGHT, @@ -78,8 +74,11 @@ void ui::DialogBox::set_from_transition(ui::Transition &transition, bool centere void ui::DialogBox::initialize_static_members() { + static constexpr std::string_view DARK_PATH = "romfs:/Textures/DialogCornersDark.png"; + static constexpr std::string_view LIGHT_PATH = "romfs:/Textures/DialogCornersLight.png"; + if (sm_darkCorners && sm_lightCorners) { return; } - sm_darkCorners = sdl::TextureManager::load("darkCorners", "romfs:/Textures/DialogCornersDark.png"); - sm_lightCorners = sdl::TextureManager::load("lightCorners", "romfs:/Textures/DialogCornersLight.png"); + sm_darkCorners = sdl2::TextureManager::create_load_resource(DARK_PATH, DARK_PATH); + sm_lightCorners = sdl2::TextureManager::create_load_resource(LIGHT_PATH, LIGHT_PATH); } diff --git a/source/ui/Frame.cpp b/source/ui/Frame.cpp index 8a30954..8d3e7c4 100644 --- a/source/ui/Frame.cpp +++ b/source/ui/Frame.cpp @@ -9,13 +9,11 @@ ui::Frame::Frame(int x, int y, int width, int height) , m_y(y) , m_width(width) , m_height(height) -{ - Frame::initialize_static_members(); -} +{ Frame::initialize_static_members(); } // ---- Public functions ---- -void ui::Frame::render(sdl::SharedTexture &target, bool hasFocus) +void ui::Frame::render(sdl2::Renderer &renderer, bool hasFocus) { // This is the size of one of the "tiles" of the frame. static constexpr int TILE = 16; @@ -27,19 +25,19 @@ void ui::Frame::render(sdl::SharedTexture &target, bool hasFocus) const int textureCorner = TILE * 2; // Top. - sm_frameCorners->render_part(target, m_x, m_y, 0, 0, TILE, TILE); - sm_frameCorners->render_part_stretched(target, TILE, 0, TILE, TILE, m_x + TILE, m_y, midWidth, TILE); - sm_frameCorners->render_part(target, rightEdge, m_y, 32, 0, TILE, TILE); + sm_frameCorners->render_part(m_x, m_y, 0, 0, TILE, TILE); + sm_frameCorners->render_part_stretched(TILE, 0, TILE, TILE, m_x + TILE, m_y, midWidth, TILE); + sm_frameCorners->render_part(rightEdge, m_y, 32, 0, TILE, TILE); // Middle - sm_frameCorners->render_part_stretched(target, 0, TILE, TILE, TILE, m_x, m_y + TILE, TILE, midHeight); - sdl::render_rect_fill(target, m_x + TILE, m_y + TILE, midWidth, midHeight, colors::SLIDE_PANEL_CLEAR); - sm_frameCorners->render_part_stretched(target, textureCorner, TILE, TILE, TILE, rightEdge, m_y + TILE, TILE, midHeight); + sm_frameCorners->render_part_stretched(0, TILE, TILE, TILE, m_x, m_y + TILE, TILE, midHeight); + renderer.render_rectangle(m_x + TILE, m_y + TILE, midWidth, midHeight, colors::SLIDE_PANEL_CLEAR); + sm_frameCorners->render_part_stretched(textureCorner, TILE, TILE, TILE, rightEdge, m_y + TILE, TILE, midHeight); // Bottom - sm_frameCorners->render_part(target, m_x, bottomEdge, 0, textureCorner, TILE, TILE); - sm_frameCorners->render_part_stretched(target, TILE, textureCorner, TILE, TILE, m_x + TILE, bottomEdge, midWidth, TILE); - sm_frameCorners->render_part(target, rightEdge, bottomEdge, textureCorner, textureCorner, TILE, TILE); + sm_frameCorners->render_part(m_x, bottomEdge, 0, textureCorner, TILE, TILE); + sm_frameCorners->render_part_stretched(TILE, textureCorner, TILE, TILE, m_x + TILE, bottomEdge, midWidth, TILE); + sm_frameCorners->render_part(rightEdge, bottomEdge, textureCorner, textureCorner, TILE, TILE); } void ui::Frame::set_x(int x) noexcept { m_x = x; } @@ -67,9 +65,8 @@ void ui::Frame::set_from_transition(const ui::Transition &transition, bool cente void ui::Frame::initialize_static_members() { - static constexpr std::string_view FRAME_NAME = "FrameCorners"; - static constexpr const char *FRAME_PATH = "romfs:/Textures/Frame.png"; + static constexpr std::string_view FRAME = "romfs:/Textures/Frame.png"; if (sm_frameCorners) { return; } - sm_frameCorners = sdl::TextureManager::load(FRAME_NAME, FRAME_PATH); + sm_frameCorners = sdl2::TextureManager::create_load_resource(FRAME, FRAME); } diff --git a/source/ui/IconMenu.cpp b/source/ui/IconMenu.cpp index 4975c3d..ccb01c7 100644 --- a/source/ui/IconMenu.cpp +++ b/source/ui/IconMenu.cpp @@ -1,5 +1,6 @@ #include "ui/IconMenu.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" namespace @@ -24,33 +25,38 @@ ui::IconMenu::IconMenu(int x, int y, int renderTargetHeight) // ---- Public functions ---- -void ui::IconMenu::update(bool hasFocus) { Menu::update(hasFocus); } +void ui::IconMenu::update(const sdl2::Input &input, bool hasFocus) { Menu::update(input, hasFocus); } -void ui::IconMenu::render(sdl::SharedTexture &target, bool hasFocus) +void ui::IconMenu::render(sdl2::Renderer &renderer, bool hasFocus) { const int optionCount = m_options.size(); const int y = m_transition.get_y(); for (int i = 0, tempY = y; i < optionCount; i++, tempY += m_optionHeight) { - // Clear target. - m_optionTarget->clear(colors::TRANSPARENT); - if (i == m_selected) + // Set target, clear. { - if (hasFocus) + graphics::ScopedRender optionScope{renderer, m_optionTarget}; + renderer.frame_begin(colors::TRANSPARENT); + if (i == m_selected) { - m_boundingBox->set_x(m_x - 8); - m_boundingBox->set_y(tempY - 8); - m_boundingBox->render(target, hasFocus); + if (hasFocus) + { + m_boundingBox->set_x(m_x - 8); + m_boundingBox->set_y(tempY - 8); + m_boundingBox->render(renderer, hasFocus); + } + // This is always rendered. + renderer.render_rectangle(0, 0, 4, 130, colors::BLUE_GREEN); } - // This is always rendered. - sdl::render_rect_fill(m_optionTarget, 0, 0, 4, 130, colors::BLUE_GREEN); + + m_options[i]->render_stretched(8, 1, ICON_RENDER_WIDTH, ICON_RENDER_HEIGHT); } - m_options[i]->render_stretched(m_optionTarget, 8, 1, ICON_RENDER_WIDTH, ICON_RENDER_HEIGHT); - m_optionTarget->render(target, m_x, tempY); + + m_optionTarget->render(m_x, tempY); } } -void ui::IconMenu::add_option(sdl::SharedTexture newOption) +void ui::IconMenu::add_option(sdl2::SharedTexture newOption) { Menu::add_option("ICON"); // Parent class needs text for this to work correctly. m_options.push_back(newOption); diff --git a/source/ui/Menu.cpp b/source/ui/Menu.cpp index a642531..b98df65 100644 --- a/source/ui/Menu.cpp +++ b/source/ui/Menu.cpp @@ -1,8 +1,8 @@ #include "ui/Menu.hpp" #include "config/config.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" -#include "input.hpp" #include "mathutil.hpp" #include "ui/BoundingBox.hpp" @@ -27,20 +27,20 @@ ui::Menu::Menu(int x, int y, int width, int fontSize, int renderTargetHeight) // ---- Public functions ---- -void ui::Menu::update(bool hasFocus) +void ui::Menu::update(const sdl2::Input &input, bool hasFocus) { if (m_options.empty()) { return; } - m_boundingBox->update(hasFocus); - m_optionScroll->update(hasFocus); + m_boundingBox->update(input, hasFocus); + m_optionScroll->update(input, hasFocus); - Menu::handle_input(); + Menu::handle_input(input); Menu::update_scrolling(); Menu::update_scroll_text(); m_transition.update(); } -void ui::Menu::render(sdl::SharedTexture &target, bool hasFocus) +void ui::Menu::render(sdl2::Renderer &renderer, bool hasFocus) { if (m_options.empty()) { return; } @@ -52,22 +52,29 @@ void ui::Menu::render(sdl::SharedTexture &target, bool hasFocus) if (tempY < -m_fontSize) { continue; } else if (tempY > m_renderTargetHeight) { break; } - m_optionTarget->clear(colors::TRANSPARENT); - - if (i == m_selected) { - if (hasFocus) + graphics::ScopedRender optionScoped{renderer, m_optionTarget}; + renderer.frame_begin(colors::TRANSPARENT); + + if (i == m_selected) { - m_boundingBox->set_x(m_x - 4); - m_boundingBox->set_y(tempY - 4); - m_boundingBox->render(target, hasFocus); + if (hasFocus) + { + m_boundingBox->set_x(m_x - 4); + m_boundingBox->set_y(tempY - 4); + m_boundingBox->render(renderer, hasFocus); + } + renderer.render_rectangle(8, 8, 4, m_optionHeight - 12, colors::BLUE_GREEN); + m_optionScroll->render(renderer, hasFocus); + } + else + { + m_font->render_text(24, m_textY, colors::WHITE, m_options[i]); } - sdl::render_rect_fill(m_optionTarget, 8, 8, 4, m_optionHeight - 12, colors::BLUE_GREEN); - m_optionScroll->render(m_optionTarget, hasFocus); } - else { sdl::text::render(m_optionTarget, 24, m_textY, m_fontSize, sdl::text::NO_WRAP, colors::WHITE, m_options[i]); } + // render target to target - m_optionTarget->render(target, m_x, tempY); + m_optionTarget->render(m_x, tempY); } } @@ -144,7 +151,8 @@ void ui::Menu::initialize_option_target() static int MENU_ID{}; const std::string optionTargetName = "MENU_TARGET_" + std::to_string(MENU_ID++); - m_optionTarget = sdl::TextureManager::load(optionTargetName, m_width, m_optionHeight, SDL_TEXTUREACCESS_TARGET); + m_optionTarget = + sdl2::TextureManager::create_load_resource(optionTargetName, m_width, m_optionHeight, SDL_TEXTUREACCESS_TARGET); } void ui::Menu::initialize_ui_elements() @@ -166,11 +174,10 @@ void ui::Menu::initialize_ui_elements() void ui::Menu::initialize_sounds() { - static constexpr std::string_view CURSOR_NAME = "MenuCursor"; - static constexpr const char *CURSOR_PATH = "romfs:/Sound/MenuCursor.wav"; + static constexpr std::string_view CURSOR_PATH = "romfs:/Sound/MenuCursor.wav"; if (sm_cursor) { return; } - sm_cursor = sdl::SoundManager::load(CURSOR_NAME, CURSOR_PATH); + sm_cursor = sdl2::SoundManager::create_load_resource(CURSOR_PATH, CURSOR_PATH); } void ui::Menu::update_scroll_text() @@ -180,18 +187,18 @@ void ui::Menu::update_scroll_text() if (text != option) { m_optionScroll->set_text(std::string_view{option}, false); } } -void ui::Menu::handle_input() +void ui::Menu::handle_input(const sdl2::Input &input) { // Get the length of the menu. const int optionsSize = m_options.size(); // Control bools. - const bool upPressed = input::button_pressed(HidNpadButton_AnyUp); - const bool downPressed = input::button_pressed(HidNpadButton_AnyDown); - const bool leftPressed = input::button_pressed(HidNpadButton_AnyLeft); - const bool rightPressed = input::button_pressed(HidNpadButton_AnyRight); - const bool lShoulderPressed = input::button_pressed(HidNpadButton_L); - const bool rShoulderPressed = input::button_pressed(HidNpadButton_R); + const bool upPressed = input.button_pressed(HidNpadButton_AnyUp); + const bool downPressed = input.button_pressed(HidNpadButton_AnyDown); + const bool leftPressed = input.button_pressed(HidNpadButton_AnyLeft); + const bool rightPressed = input.button_pressed(HidNpadButton_AnyRight); + const bool lShoulderPressed = input.button_pressed(HidNpadButton_L); + const bool rShoulderPressed = input.button_pressed(HidNpadButton_R); // Wrapping conditions. const bool wrapEnd = upPressed && m_selected - 1 < 0; diff --git a/source/ui/PopMessage.cpp b/source/ui/PopMessage.cpp index a6eb72c..27b4dee 100644 --- a/source/ui/PopMessage.cpp +++ b/source/ui/PopMessage.cpp @@ -2,6 +2,7 @@ #include "config/config.hpp" #include "graphics/colors.hpp" +#include "graphics/fonts.hpp" #include "graphics/screen.hpp" #include "mathutil.hpp" #include "sdl.hpp" @@ -32,9 +33,7 @@ ui::PopMessage::PopMessage(int ticks, std::string &message) , m_ticks(ticks) , m_message(std::move(message)) , m_state(State::Rising) -{ - PopMessage::initialize_static_members(); -} +{ PopMessage::initialize_static_members(); } // ---- Public functions ---- @@ -62,9 +61,9 @@ void ui::PopMessage::update(double targetY) } } -void ui::PopMessage::render() +void ui::PopMessage::render(sdl2::Renderer &renderer) { - PopMessage::render_container(); + PopMessage::render_container(renderer); // Don't continue unless the message is in place or it's not in its closing state. if (m_state != State::Opening && m_state != State::Displaying) { return; } @@ -72,7 +71,7 @@ void ui::PopMessage::render() // This avoids allocating and returning another std::string. const int y = m_transition.get_y(); const std::string_view message{m_message.c_str(), static_cast(m_substrOffset)}; - sdl::text::render(sdl::Texture::Null, m_textX, y + 11, FONT_SIZE, sdl::text::NO_WRAP, colors::BLACK, message); + sm_font->render_text(m_textX, y + 11, colors::BLACK, message); } bool ui::PopMessage::finished() const noexcept { return m_state == State::Finished; } @@ -83,12 +82,13 @@ std::string_view ui::PopMessage::get_message() const noexcept { return m_message void ui::PopMessage::initialize_static_members() { - static constexpr std::string_view TEX_CAP_NAME = "PopCaps"; - static constexpr const char *TEX_CAP_PATH = "romfs:/Textures/PopMessage.png"; + static constexpr std::string_view CAP_PATH = "romfs:/Textures/PopMessage.png"; - if (sm_endCaps) { return; } + if (sm_endCaps && sm_font) { return; } - sm_endCaps = sdl::TextureManager::load(TEX_CAP_NAME, TEX_CAP_PATH); + sm_endCaps = sdl2::TextureManager::create_load_resource(CAP_PATH, CAP_PATH); + sm_font = sdl2::FontManager::create_load_resource(graphics::fonts::names::TWENTY_TWO_PIXEL, + graphics::fonts::sizes::TWENTY_TWO_PIXEL); } void ui::PopMessage::update_y() noexcept @@ -132,7 +132,7 @@ void ui::PopMessage::update_text_offset() // Get the substring and calculate the updated width and X of the message. const std::string_view subString{m_message.c_str(), static_cast(m_substrOffset)}; - const int subWidth = sdl::text::get_width(FONT_SIZE, subString); + const int subWidth = sm_font->get_text_width(subString); const int containerWidth = subWidth + CONTAINER_PADDING; m_textX = SCREEN_CENTER - (subWidth / 2); @@ -171,7 +171,7 @@ void ui::PopMessage::update_display_timer() noexcept } } -void ui::PopMessage::render_container() noexcept +void ui::PopMessage::render_container(sdl2::Renderer &renderer) noexcept { // The width of the CAP graphics. constexpr int CAP_WIDTH = 48; @@ -183,7 +183,7 @@ void ui::PopMessage::render_container() noexcept const int width = m_transition.get_width() + CAP_HALF; // Render the container. - sm_endCaps->render_part(sdl::Texture::Null, x, y, 0, 0, CAP_HALF, CAP_WIDTH); - sdl::render_rect_fill(sdl::Texture::Null, x + CAP_HALF, y, width - CAP_WIDTH, 48, colors::DIALOG_LIGHT); - sm_endCaps->render_part(sdl::Texture::Null, x + (width - CAP_HALF), y, CAP_HALF, 0, CAP_HALF, 48); + sm_endCaps->render_part(x, y, 0, 0, CAP_HALF, CAP_WIDTH); + renderer.render_rectangle(x + CAP_HALF, y, width - CAP_WIDTH, 48, colors::DIALOG_LIGHT); + sm_endCaps->render_part(x + (width - CAP_HALF), y, CAP_HALF, 0, CAP_HALF, 48); } diff --git a/source/ui/PopMessageManager.cpp b/source/ui/PopMessageManager.cpp index b9b2a69..5c6ace9 100644 --- a/source/ui/PopMessageManager.cpp +++ b/source/ui/PopMessageManager.cpp @@ -54,14 +54,14 @@ void ui::PopMessageManager::update() } } -void ui::PopMessageManager::render() +void ui::PopMessageManager::render(sdl2::Renderer &renderer) { PopMessageManager &manager = PopMessageManager::get_instance(); auto &messages = manager.m_messages; std::mutex &messageMutex = manager.m_messageMutex; std::lock_guard messageGuard{messageMutex}; - for (auto &message : messages) { message.render(); } + for (auto &message : messages) { message.render(renderer); } } void ui::PopMessageManager::push_message(int displayTicks, std::string_view message) @@ -118,10 +118,9 @@ void ui::PopMessageManager::push_message(int displayTicks, std::string &message) void ui::PopMessageManager::initialize_pop_sound() { - static constexpr std::string_view POP_NAME = "PopSound"; - static constexpr const char *POP_PATH = "romfs:/Sound/PopMessage.wav"; + static constexpr std::string_view POP_PATH = "romfs:/Sound/PopMessage.wav"; if (m_popSound) { return; } - m_popSound = sdl::SoundManager::load(POP_NAME, POP_PATH); + m_popSound = sdl2::SoundManager::create_load_resource(POP_PATH, POP_PATH); } \ No newline at end of file diff --git a/source/ui/SlideOutPanel.cpp b/source/ui/SlideOutPanel.cpp index 61a42cc..d4e7229 100644 --- a/source/ui/SlideOutPanel.cpp +++ b/source/ui/SlideOutPanel.cpp @@ -1,10 +1,12 @@ #include "ui/SlideOutPanel.hpp" #include "config/config.hpp" +#include "graphics/ScopedRender.hpp" #include "graphics/colors.hpp" #include "graphics/screen.hpp" #include "logging/logger.hpp" #include "mathutil.hpp" +#include "stringutil.hpp" #include #include @@ -25,38 +27,47 @@ ui::SlideOutPanel::SlideOutPanel(int width, Side side) 0, ui::Transition::DEFAULT_THRESHOLD) , m_state(State::Opening) - , m_renderTarget(sdl::TextureManager::load("PANEL_" + std::to_string(sm_targetID++), - m_width, - graphics::SCREEN_HEIGHT, - SDL_TEXTUREACCESS_TARGET)) {}; + , m_renderTarget(sdl2::TextureManager::create_load_resource(stringutil::get_formatted_string("PANEL_%i", sm_targetID++), + m_width, + graphics::SCREEN_HEIGHT, + SDL_TEXTUREACCESS_TARGET)) {}; // ---- Public functions ---- -void ui::SlideOutPanel::update(bool hasFocus) +void ui::SlideOutPanel::update(const sdl2::Input &input, bool hasFocus) { switch (m_state) { case State::Opening: case State::Closing: case State::Hiding: SlideOutPanel::update_position_state(); break; - case State::Opened: SlideOutPanel::update_sub_elements(hasFocus); break; + case State::Opened: SlideOutPanel::update_sub_elements(input, hasFocus); break; default: return; // Nothing should take place in any other state. } } void ui::SlideOutPanel::sub_update() { m_transition.update(); } -void ui::SlideOutPanel::render(sdl::SharedTexture &target, bool hasFocus) +void ui::SlideOutPanel::render(sdl2::Renderer &renderer, bool hasFocus) { - // Loop and render all sub-elements to the target. - for (auto ¤tElement : m_elements) { currentElement->render(m_renderTarget, hasFocus); } + { + // Set target. + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + + // Loop and render all sub-elements to the target. + for (auto ¤tElement : m_elements) { currentElement->render(renderer, hasFocus); } + } // Render the main target to the target passed (most likely the screen); const int x = m_transition.get_x(); - m_renderTarget->render(target, x, 0); + m_renderTarget->render(x, 0); } -void ui::SlideOutPanel::clear_target() { m_renderTarget->clear(colors::SLIDE_PANEL_CLEAR); } +void ui::SlideOutPanel::clear_target(sdl2::Renderer &renderer) +{ + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + renderer.frame_begin(colors::SLIDE_PANEL_CLEAR); +} void ui::SlideOutPanel::reset() noexcept { @@ -132,7 +143,7 @@ void ui::SlideOutPanel::push_new_element(std::shared_ptr newElement void ui::SlideOutPanel::clear_elements() { m_elements.clear(); } -sdl::SharedTexture &ui::SlideOutPanel::get_target() noexcept { return m_renderTarget; } +sdl2::SharedTexture &ui::SlideOutPanel::get_target() noexcept { return m_renderTarget; } // ---- Private functions ---- @@ -150,7 +161,7 @@ void ui::SlideOutPanel::update_position_state() noexcept else if (hidden) { m_state = State::Hidden; } } -void ui::SlideOutPanel::update_sub_elements(bool hasFocus) noexcept +void ui::SlideOutPanel::update_sub_elements(const sdl2::Input &input, bool hasFocus) noexcept { - for (auto &element : m_elements) { element->update(hasFocus); } + for (auto &element : m_elements) { element->update(input, hasFocus); } } \ No newline at end of file diff --git a/source/ui/TextScroll.cpp b/source/ui/TextScroll.cpp index 65dca74..5ae5941 100644 --- a/source/ui/TextScroll.cpp +++ b/source/ui/TextScroll.cpp @@ -1,6 +1,8 @@ #include "ui/TextScroll.hpp" +#include "graphics/ScopedRender.hpp" #include "sdl.hpp" +#include "stringutil.hpp" namespace { @@ -9,8 +11,6 @@ namespace /// @brief This is the number of pixels between the two renderings of the text. constexpr int SIZE_TEXT_GAP = 0; - - int TARGET_ID{}; } // namespace // ---- Construction ---- @@ -21,12 +21,10 @@ ui::TextScroll::TextScroll(std::string_view text, int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center) -{ - TextScroll::initialize(text, x, y, width, height, fontSize, textColor, clearColor, center); -} +{ TextScroll::initialize(text, x, y, width, height, fontSize, textColor, clearColor, center); } ui::TextScroll::TextScroll(std::string &text, int x, @@ -34,12 +32,10 @@ ui::TextScroll::TextScroll(std::string &text, int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center) -{ - TextScroll::initialize(text, x, y, width, height, fontSize, textColor, clearColor, center); -} +{ TextScroll::initialize(text, x, y, width, height, fontSize, textColor, clearColor, center); } // ---- Public functions ---- @@ -49,24 +45,24 @@ void ui::TextScroll::initialize(std::string_view text, int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center) { - m_renderX = x; - m_renderY = y; - m_fontSize = fontSize; - m_textColor = textColor; + m_renderX = x; + m_renderY = y; + m_fontSize = fontSize; + m_font = sdl2::FontManager::create_load_resource(TextScroll::generate_font_name(fontSize), fontSize); + m_textColor = textColor; m_clearColor = clearColor; m_targetWidth = width; m_targetHeight = height; m_textY = (m_targetHeight / 2) - (m_fontSize / 2); m_scrollTimer.start(TICKS_SCROLL_TRIGGER); - - { - const std::string targetName = "textScroll_" + std::to_string(TARGET_ID++); - m_renderTarget = sdl::TextureManager::load(targetName, m_targetWidth, m_targetHeight, SDL_TEXTUREACCESS_TARGET); - } + m_renderTarget = sdl2::TextureManager::create_load_resource(TextScroll::generate_target_name(), + m_targetWidth, + m_targetHeight, + SDL_TEXTUREACCESS_TARGET); TextScroll::set_text(text, center); } @@ -77,24 +73,24 @@ void ui::TextScroll::initialize(std::string &text, int width, int height, int fontSize, - sdl::Color textColor, - sdl::Color clearColor, + SDL_Color textColor, + SDL_Color clearColor, bool center) { - m_renderX = x; - m_renderY = y; - m_fontSize = fontSize; - m_textColor = textColor; + m_renderX = x; + m_renderY = y; + m_fontSize = fontSize; + m_font = sdl2::FontManager::create_load_resource(TextScroll::generate_font_name(fontSize), fontSize); + m_textColor = textColor; m_clearColor = clearColor; m_targetWidth = width; m_targetHeight = height; m_textY = (m_targetHeight / 2) - (m_fontSize / 2); m_scrollTimer.start(TICKS_SCROLL_TRIGGER); - - { - const std::string targetName = "textScroll_" + std::to_string(TARGET_ID++); - m_renderTarget = sdl::TextureManager::load(targetName, m_targetWidth, m_targetHeight, SDL_TEXTUREACCESS_TARGET); - } + m_renderTarget = sdl2::TextureManager::create_load_resource(TextScroll::generate_target_name(), + m_targetWidth, + m_targetHeight, + SDL_TEXTUREACCESS_TARGET); TextScroll::set_text(text, center); } @@ -104,7 +100,7 @@ std::string_view ui::TextScroll::get_text() const noexcept { return m_text; } void ui::TextScroll::set_text(std::string_view text, bool center) { m_text = text; - m_textWidth = sdl::text::get_width(m_fontSize, m_text.c_str()); + m_textWidth = m_font->get_text_width(m_text); m_textScrollTriggered = false; if (m_textWidth > m_targetWidth - 16) @@ -128,7 +124,7 @@ void ui::TextScroll::set_text(std::string_view text, bool center) void ui::TextScroll::set_text(std::string &text, bool center) { m_text = std::move(text); - m_textWidth = sdl::text::get_width(m_fontSize, m_text.c_str()); + m_textWidth = m_font->get_text_width(m_text); m_textScrollTriggered = false; if (m_textWidth > m_targetWidth - 16) @@ -149,7 +145,7 @@ void ui::TextScroll::set_text(std::string &text, bool center) } } -void ui::TextScroll::update(bool hasFocus) +void ui::TextScroll::update(const sdl2::Input &input, bool hasFocus) { // I don't think this needs to care about having focus. const int invertedWidth = -(m_textWidth + SIZE_TEXT_GAP); @@ -172,25 +168,34 @@ void ui::TextScroll::update(bool hasFocus) } } -void ui::TextScroll::render(sdl::SharedTexture &target, bool hasFocus) +void ui::TextScroll::render(sdl2::Renderer &renderer, bool hasFocus) { - m_renderTarget->clear(m_clearColor); + // Gap between scrolling text rendering. + static constexpr int TEXT_GAP = 8; - if (!m_textScrolling) { - sdl::text::render(m_renderTarget, m_textX, m_textY, m_fontSize, sdl::text::NO_WRAP, m_textColor, m_text); + // Scoped render to the target. + graphics::ScopedRender scopedRender{renderer, m_renderTarget}; + renderer.frame_begin(m_clearColor); + + if (!m_textScrolling) { m_font->render_text(m_textX, m_textY, m_textColor, m_text); } + else + { + // We're going to render text twice so it looks like it's scrolling and doesn't end. Ever. + m_font->render_text(m_textX, m_textY, m_textColor, m_text); + m_font->render_text(m_textX + m_textWidth + TEXT_GAP, m_textY, m_textColor, m_text); + } } - else - { - // We're going to render text twice so it looks like it's scrolling and doesn't end. Ever. - sdl::text::render(m_renderTarget, m_textX, m_textY, m_fontSize, sdl::text::NO_WRAP, m_textColor, m_text); - sdl::text::render(m_renderTarget, - m_textX + m_textWidth + 8, - m_textY, - m_fontSize, - sdl::text::NO_WRAP, - m_textColor, - m_text); - } - m_renderTarget->render(target, m_renderX, m_renderY); + m_renderTarget->render(m_renderX, m_renderY); } + +// ---- Private Functions ---- + +std::string ui::TextScroll::generate_target_name() +{ + static int targetID{}; + return stringutil::get_formatted_string("TextScroll_%i", targetID++); +} + +std::string ui::TextScroll::generate_font_name(int fontSize) +{ return stringutil::get_formatted_string("TextScrollFont_%i", fontSize); } \ No newline at end of file diff --git a/source/ui/TitleTile.cpp b/source/ui/TitleTile.cpp index 6f10a05..977a5ec 100644 --- a/source/ui/TitleTile.cpp +++ b/source/ui/TitleTile.cpp @@ -11,7 +11,7 @@ namespace // ---- Construction ---- -ui::TitleTile::TitleTile(bool isFavorite, int index, sdl::SharedTexture icon) +ui::TitleTile::TitleTile(bool isFavorite, int index, sdl2::SharedTexture &icon) : m_transition(0, 0, UNSELECTED_WIDTH_HEIGHT, @@ -22,15 +22,12 @@ ui::TitleTile::TitleTile(bool isFavorite, int index, sdl::SharedTexture icon) UNSELECTED_WIDTH_HEIGHT, m_transition.DEFAULT_THRESHOLD) , m_isFavorite(isFavorite) - , m_index(index) , m_icon(icon) {}; // ---- Public functions ---- -void ui::TitleTile::update(int selected) +void ui::TitleTile::update(bool isSelected) { - const bool isSelected = selected == m_index; - if (isSelected) { m_transition.set_target_width(SELECTED_WIDTH_HEIGHT); @@ -45,7 +42,7 @@ void ui::TitleTile::update(int selected) m_transition.update_width_height(); } -void ui::TitleTile::render(sdl::SharedTexture &target, int x, int y) +void ui::TitleTile::render(int x, int y) { static constexpr std::string_view HEART_CHAR = "\uE017"; @@ -54,8 +51,9 @@ void ui::TitleTile::render(sdl::SharedTexture &target, int x, int y) const int renderX = x - ((width - 128) / 2); const int renderY = y - ((width - 128) / 2); - m_icon->render_stretched(target, renderX, renderY, width, height); - if (m_isFavorite) { sdl::text::render(target, renderX + 2, renderY + 2, 28, sdl::text::NO_WRAP, colors::PINK, HEART_CHAR); } + m_icon->render_stretched(renderX, renderY, width, height); + + if (m_isFavorite) { sm_heartFont->render_text(renderX + 2, renderY + 2, colors::PINK, HEART_CHAR); } } void ui::TitleTile::reset() noexcept @@ -69,3 +67,16 @@ void ui::TitleTile::reset() noexcept int ui::TitleTile::get_width() const noexcept { return m_transition.get_width(); } int ui::TitleTile::get_height() const noexcept { return m_transition.get_height(); } + +// ---- Private Functions ---- + +void ui::TitleTile::initialize_static_members() +{ + // Font name for the manager. + static constexpr std::string_view FONT_NAME = "HeartFont"; + static constexpr int FONT_SIZE = 28; + + if (sm_heartFont) { return; } + + sm_heartFont = sdl2::FontManager::create_load_resource(FONT_NAME, FONT_SIZE); +} \ No newline at end of file diff --git a/source/ui/TitleView.cpp b/source/ui/TitleView.cpp index f453868..de12110 100644 --- a/source/ui/TitleView.cpp +++ b/source/ui/TitleView.cpp @@ -3,7 +3,6 @@ #include "config/config.hpp" #include "error.hpp" #include "graphics/colors.hpp" -#include "input.hpp" #include "logging/logger.hpp" #include @@ -26,19 +25,19 @@ ui::TitleView::TitleView(data::User *user) TitleView::refresh(); } -void ui::TitleView::update(bool hasFocus) +void ui::TitleView::update(const sdl2::Input &input, bool hasFocus) { if (m_titleTiles.empty()) { return; } - m_bounding->update(hasFocus); - TitleView::handle_input(); + m_bounding->update(input, hasFocus); + TitleView::handle_input(input); TitleView::handle_scrolling(); TitleView::update_tiles(); m_transition.update(); } -void ui::TitleView::render(sdl::SharedTexture &target, bool hasFocus) +void ui::TitleView::render(sdl2::Renderer &renderer, bool hasFocus) { static constexpr int TILE_SPACE_VERT = 144; static constexpr int TILE_SPACE_HOR = 144; @@ -59,8 +58,7 @@ void ui::TitleView::render(sdl::SharedTexture &target, bool hasFocus) continue; } - ui::TitleTile &tile = m_titleTiles[j]; - tile.render(target, tempX, tempY); + m_titleTiles[i].render(tempX, tempY); } } @@ -69,12 +67,11 @@ void ui::TitleView::render(sdl::SharedTexture &target, bool hasFocus) m_bounding->set_x(m_selectedX - 30); m_bounding->set_y(m_selectedY - 30); - sdl::render_rect_fill(target, m_selectedX - 28, m_selectedY - 28, 184, 184, colors::CLEAR_COLOR); - m_bounding->render(target, hasFocus); + renderer.render_rectangle(m_selectedX - 28, m_selectedY - 28, 184, 184, colors::CLEAR_COLOR); + m_bounding->render(renderer, hasFocus); } - ui::TitleTile &selectedTile = m_titleTiles[m_selected]; - selectedTile.render(target, m_selectedX, m_selectedY); + m_titleTiles[m_selected].render(m_selectedX, m_selectedY); } int ui::TitleView::get_selected() const noexcept { return m_selected; } @@ -83,6 +80,7 @@ void ui::TitleView::set_selected(int selected) noexcept { const int tilesCount = m_titleTiles.size(); if (selected < 0 || selected >= tilesCount) { return; } + m_selected = selected; } @@ -98,8 +96,8 @@ void ui::TitleView::refresh() data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); if (error::is_null(titleInfo)) { continue; } - const bool isFavorite = config::is_favorite(applicationID); - sdl::SharedTexture icon = titleInfo->get_icon(); // I don't like this but w/e. + const bool isFavorite = config::is_favorite(applicationID); + sdl2::SharedTexture &icon = titleInfo->get_icon(); m_titleTiles.emplace_back(isFavorite, i, icon); } @@ -120,23 +118,22 @@ void ui::TitleView::play_sound() noexcept { sm_cursor->play(); } void ui::TitleView::initialize_static_members() { - static constexpr std::string_view CURSOR_NAME = "MenuCursor"; - static constexpr const char *CURSOR_PATH = "romfs:/Sound/MenuCursor.wav"; + static constexpr std::string_view CURSOR_PATH = "romfs:/Sound/MenuCursor.wav"; if (sm_cursor) { return; } - sm_cursor = sdl::SoundManager::load(CURSOR_NAME, CURSOR_PATH); + sm_cursor = sdl2::SoundManager::create_load_resource(CURSOR_PATH, CURSOR_PATH); } -void ui::TitleView::handle_input() +void ui::TitleView::handle_input(const sdl2::Input &input) { const int totalTiles = m_titleTiles.size() - 1; - const bool upPressed = input::button_pressed(HidNpadButton_AnyUp); - const bool downPressed = input::button_pressed(HidNpadButton_AnyDown); - const bool leftPressed = input::button_pressed(HidNpadButton_AnyLeft); - const bool rightPressed = input::button_pressed(HidNpadButton_AnyRight); - const bool lShoulderPressed = input::button_pressed(HidNpadButton_L); - const bool rShoulderPressed = input::button_pressed(HidNpadButton_R); + const bool upPressed = input.button_pressed(HidNpadButton_AnyUp); + const bool downPressed = input.button_pressed(HidNpadButton_AnyDown); + const bool leftPressed = input.button_pressed(HidNpadButton_AnyLeft); + const bool rightPressed = input.button_pressed(HidNpadButton_AnyRight); + const bool lShoulderPressed = input.button_pressed(HidNpadButton_L); + const bool rShoulderPressed = input.button_pressed(HidNpadButton_R); const int previousSelected = m_selected;