diff --git a/include/appstates/FileModeState.hpp b/include/appstates/FileModeState.hpp index 0b8d29b..7deb9f9 100644 --- a/include/appstates/FileModeState.hpp +++ b/include/appstates/FileModeState.hpp @@ -41,6 +41,22 @@ class FileModeState final : public BaseState friend class FileOptionState; private: + /// @brief States the browser can be in. + enum class State : uint8_t + { + Rising, + Open, + Dropping + }; + + /// @brief This is to make the target code easier to understand and read. + // I didn't know what else to name these either... + enum class Target : uint8_t + { + MountA, + MountB + }; + /// @brief These store the mount points to close the filesystems upon construction. std::string m_mountA{}; std::string m_mountB{}; @@ -57,24 +73,24 @@ class FileModeState final : public BaseState std::shared_ptr m_dirMenuA{}; std::shared_ptr m_dirMenuB{}; - /// @brief Controls which menu/filesystem is currently targetted. - bool m_target{}; - - /// @brief Stores the size for committing data (if needed) to mountA. - int64_t m_journalSize{}; - - /// @brief Stores whether or not the panel should be closed. - bool m_close{}; - - /// @brief Transition for the pop-up effect. - ui::Transition m_transition{}; - /// @brief Stores whether the instance is dealing with sensitive data. bool m_isSystem{}; /// @brief Stores the config setting from config to allow writing to the sensitive parts of the system. bool m_allowSystem{}; + /// @brief Stores the size for committing data (if needed) to mountA. + int64_t m_journalSize{}; + + /// @brief Controls which menu/filesystem is currently targetted. + FileModeState::Target m_target{}; + + /// @brief Transition for the pop-up effect. + ui::Transition m_transition{}; + + /// @brief Stores the current state. + FileModeState::State m_state{}; + /// @brief Frame shared by all instances. static inline std::shared_ptr sm_frame{}; @@ -96,11 +112,11 @@ class FileModeState final : public BaseState /// @brief Loads the current directory listings and menus. void initialize_directory_menu(const fslib::Path &path, fslib::Directory &directory, ui::Menu &menu); - /// @brief Starts the dialog hiding process. - void hide_dialog() noexcept; + /// @brief Updates the current Y coordinate of the dialog. + void update_y() noexcept; - /// @brief Returns whether or not the dialog is hidden. - bool is_hidden() noexcept; + /// @brief Handles input. + void update_handle_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 8871c2b..068b6b6 100644 --- a/include/appstates/FileOptionState.hpp +++ b/include/appstates/FileOptionState.hpp @@ -56,7 +56,7 @@ class FileOptionState final : public BaseState FileModeState *m_spawningState{}; /// @brief Stores the target for easier access. - bool m_target{}; + FileModeState::Target m_target{}; /// @brief Transition. ui::Transition m_transition{}; @@ -126,20 +126,20 @@ class FileOptionState final : public BaseState /// @brief Returns if the copy/from is allowed before continuing. inline bool system_write_check() { - const bool target = m_spawningState->m_target; - const bool isSystem = m_spawningState->m_isSystem; - const bool allowSystem = m_spawningState->m_allowSystem; + const FileModeState::Target target = m_spawningState->m_target; + const bool isSystem = m_spawningState->m_isSystem; + const bool allowSystem = m_spawningState->m_allowSystem; - return target && isSystem && !allowSystem; + return target == FileModeState::Target::MountB && isSystem && !allowSystem; } /// @brief Returns if the operation is allowed. inline bool system_operation_check() { - const bool target = m_spawningState->m_target; - const bool isSystem = m_spawningState->m_isSystem; - const bool allowSystem = m_spawningState->m_allowSystem; + const FileModeState::Target target = m_spawningState->m_target; + const bool isSystem = m_spawningState->m_isSystem; + const bool allowSystem = m_spawningState->m_allowSystem; - return !target && isSystem && !allowSystem; + return target == FileModeState::Target::MountA && isSystem && !allowSystem; } }; \ No newline at end of file diff --git a/include/ui/ControlGuide.hpp b/include/ui/ControlGuide.hpp index 54b4b0f..e9d3e41 100644 --- a/include/ui/ControlGuide.hpp +++ b/include/ui/ControlGuide.hpp @@ -12,6 +12,7 @@ namespace ui /// @param string Pointer to the control guide string to render. ControlGuide(const char *guide); + /// @brief Factory function to return a control guide. static inline std::shared_ptr create(const char *guide) { return std::make_shared(guide); diff --git a/source/appstates/FadeState.cpp b/source/appstates/FadeState.cpp index 51cca3f..9982c94 100644 --- a/source/appstates/FadeState.cpp +++ b/source/appstates/FadeState.cpp @@ -50,6 +50,7 @@ void FadeState::render() void FadeState::find_divisor() { + // Get the distance between where we start and need to end. m_divisor = math::Util::absolute_distance(m_alpha, m_endAlpha); // Going to loop and try to find the highest divisible number. To do: Maybe not brute force this? diff --git a/source/appstates/FileModeState.cpp b/source/appstates/FileModeState.cpp index a7e6dbc..9d3af45 100644 --- a/source/appstates/FileModeState.cpp +++ b/source/appstates/FileModeState.cpp @@ -12,15 +12,25 @@ #include +namespace +{ + /// @brief This is the X position of the file mode pop up. + constexpr int PERMA_X = 15; + + /// @brief This is the target Y of the pop-up. The starting Y is the height of the screen. + constexpr int TARGET_Y = 91; +} + // ---- Construction ---- FileModeState::FileModeState(std::string_view mountA, std::string_view mountB, int64_t journalSize, bool isSystem) : m_mountA(mountA) , m_mountB(mountB) - , m_journalSize(journalSize) - , m_transition(15, graphics::SCREEN_HEIGHT, 0, 0, 15, 90, 0, 0, ui::Transition::DEFAULT_THRESHOLD) , m_isSystem(isSystem) , m_allowSystem(config::get_by_key(config::keys::ALLOW_WRITING_TO_SYSTEM)) + , m_journalSize(journalSize) + , m_transition(PERMA_X, graphics::SCREEN_HEIGHT, 0, 0, PERMA_X, TARGET_Y, 0, 0, ui::Transition::DEFAULT_THRESHOLD) + , m_state(State::Rising) { FileModeState::initialize_static_members(); FileModeState::initialize_paths(); @@ -31,39 +41,24 @@ FileModeState::FileModeState(std::string_view mountA, std::string_view mountB, i void FileModeState::update() { - m_transition.update(); - if (!m_transition.in_place()) + switch (m_state) { - const int y = m_transition.get_y(); - sm_frame->set_y(y); - return; + case State::Rising: FileModeState::update_y(); break; + case State::Open: FileModeState::update_handle_input(); break; + case State::Dropping: FileModeState::update_y(); break; } - - const bool hasFocus = BaseState::has_focus(); - - ui::Menu &menu = FileModeState::get_source_menu(); - fslib::Path &path = FileModeState::get_source_path(); - fslib::Directory &directory = FileModeState::get_source_directory(); - - 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); - - if (aPressed) { FileModeState::enter_selected(path, directory, menu); } - else if (bPressed) { FileModeState::up_one_directory(path, directory, menu); } - else if (xPressed) { FileModeState::open_option_menu(directory, menu); } - else if (zlZRPressed) { FileModeState::change_target(); } - else if (minusPressed) { FileModeState::hide_dialog(); } - else if (FileModeState::is_hidden()) { FileModeState::deactivate_state(); } - - menu.update(hasFocus); - sm_controlGuide->update(hasFocus); } void FileModeState::render() { + // Coords for divider lines. + static constexpr int LINE_A_X = 617; + static constexpr int LINE_B_X = 618; + + // Shared. + static constexpr int LINE_Y_A = 0; + static constexpr int LINE_Y_B = 538; + const bool hasFocus = BaseState::has_focus(); sm_renderTarget->clear(colors::TRANSPARENT); @@ -71,14 +66,18 @@ void FileModeState::render() // This is here so it's rendered underneath the pop-up frame. sm_controlGuide->render(sdl::Texture::Null, hasFocus); - sdl::render_line(sm_renderTarget, 617, 0, 617, 538, colors::WHITE); - sdl::render_line(sm_renderTarget, 618, 0, 618, 538, colors::DIALOG_DARK); + // 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); - m_dirMenuA->render(sm_renderTarget, hasFocus && m_target == false); - m_dirMenuB->render(sm_renderTarget, hasFocus && m_target); + // 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); } @@ -87,12 +86,21 @@ void FileModeState::render() void FileModeState::initialize_static_members() { + // Frame coords and dimensions. + static constexpr int FRAME_WIDTH = 1250; + static constexpr int FRAME_HEIGHT = 555; + + // Inner target coords and dimensions. + static constexpr int INNER_WIDTH = 1234; + static constexpr int INNER_HEIGHT = 538; + + // This is the name of the render target for the main body. static constexpr std::string_view RENDER_TARGET_NAME = "FMRenderTarget"; if (sm_frame && sm_renderTarget && sm_controlGuide) { return; } - sm_frame = ui::Frame::create(15, graphics::SCREEN_HEIGHT, 1250, 555); - sm_renderTarget = sdl::TextureManager::load(RENDER_TARGET_NAME, 1234, 538, SDL_TEXTUREACCESS_TARGET); + 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_controlGuide = ui::ControlGuide::create(strings::get_by_name(strings::names::CONTROL_GUIDES, 4)); } @@ -104,8 +112,20 @@ void FileModeState::initialize_paths() void FileModeState::initialize_menus() { - m_dirMenuA = ui::Menu::create(8, 5, 594, 20, 538); - m_dirMenuB = ui::Menu::create(630, 5, 594, 20, 538); + // Menu A. + static constexpr int MENU_A_X = 8; + + // Menu B. + static constexpr int MENU_B_X = 630; + + // Shared. + static constexpr int MENU_Y = 5; + static constexpr int MENU_WIDTH = 594; + static constexpr int MENU_FONT_SIZE = 20; + static constexpr int MENU_TARGET_HEIGHT = 538; + + m_dirMenuA = ui::Menu::create(MENU_A_X, MENU_Y, MENU_WIDTH, MENU_FONT_SIZE, MENU_TARGET_HEIGHT); + m_dirMenuB = ui::Menu::create(MENU_B_X, MENU_Y, MENU_WIDTH, MENU_FONT_SIZE, MENU_TARGET_HEIGHT); FileModeState::initialize_directory_menu(m_pathA, m_dirA, *m_dirMenuA.get()); FileModeState::initialize_directory_menu(m_pathB, m_dirB, *m_dirMenuB.get()); @@ -134,15 +154,54 @@ void FileModeState::initialize_directory_menu(const fslib::Path &path, fslib::Di } } -void FileModeState::hide_dialog() noexcept +void FileModeState::update_y() noexcept { - if (!m_transition.in_place()) { return; } - sm_controlGuide->reset(); - m_transition.set_target_y(graphics::SCREEN_HEIGHT); - m_close = true; + // Update the transition. + m_transition.update(); + + // Grab the Y and update the frame. + const int y = m_transition.get_y(); + sm_frame->set_y(y); + + // Conditions for changing to next state. + const bool finishedRising = m_state == State::Rising && m_transition.in_place_xy(); + const bool finishedDropping = m_state == State::Dropping && m_transition.in_place_xy(); + if (finishedRising) { m_state = State::Open; } + else if (finishedDropping) { FileModeState::deactivate_state(); } } -bool FileModeState::is_hidden() noexcept { return m_close && m_transition.in_place(); } +void FileModeState::update_handle_input() noexcept +{ + // Get whether or not the state has focus. + const bool hasFocus = BaseState::has_focus(); + + // Grab references to the current target we're working with. + ui::Menu &menu = FileModeState::get_source_menu(); + fslib::Path &path = FileModeState::get_source_path(); + 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); + + // Conditions + if (aPressed) { FileModeState::enter_selected(path, directory, menu); } + else if (bPressed) { FileModeState::up_one_directory(path, directory, menu); } + else if (xPressed) { FileModeState::open_option_menu(directory, menu); } + else if (zlZRPressed) { FileModeState::change_target(); } + else if (minusPressed) + { + m_state = State::Dropping; + m_transition.set_target_y(graphics::SCREEN_HEIGHT); + } + + // Update the menu and control guide. + menu.update(hasFocus); + sm_controlGuide->update(hasFocus); +} void FileModeState::enter_selected(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu) { @@ -166,10 +225,11 @@ void FileModeState::open_option_menu(fslib::Directory &directory, ui::Menu &menu { const int selected = menu.get_selected(); + // Don't push the menu if the '..' is highlighted. if (selected == 0 || selected > 1) { FileOptionState::create_and_push(this); } } -void FileModeState::change_target() { m_target = m_target ? false : true; } +void FileModeState::change_target() { m_target = m_target == Target::MountA ? Target::MountB : Target::MountA; } void FileModeState::up_one_directory(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu) { @@ -195,22 +255,36 @@ void FileModeState::enter_directory(fslib::Path &path, FileModeState::initialize_directory_menu(path, directory, menu); } -ui::Menu &FileModeState::get_source_menu() noexcept { return m_target ? *m_dirMenuB.get() : *m_dirMenuA.get(); } +ui::Menu &FileModeState::get_source_menu() noexcept +{ + return m_target == Target::MountA ? *m_dirMenuA.get() : *m_dirMenuB.get(); +} -ui::Menu &FileModeState::get_destination_menu() noexcept { return m_target ? *m_dirMenuA.get() : *m_dirMenuB.get(); } +ui::Menu &FileModeState::get_destination_menu() noexcept +{ + return m_target == Target::MountA ? *m_dirMenuB.get() : *m_dirMenuA.get(); +} -fslib::Path &FileModeState::get_source_path() noexcept { return m_target ? m_pathB : m_pathA; } +fslib::Path &FileModeState::get_source_path() noexcept { return m_target == Target::MountA ? m_pathA : m_pathB; } -fslib::Path &FileModeState::get_destination_path() noexcept { return m_target ? m_pathA : m_pathB; } +fslib::Path &FileModeState::get_destination_path() noexcept { return m_target == Target::MountA ? m_pathB : m_pathA; } -fslib::Directory &FileModeState::get_source_directory() noexcept { return m_target ? m_dirB : m_dirA; } +fslib::Directory &FileModeState::get_source_directory() noexcept { return m_target == Target::MountA ? m_dirA : m_dirB; } -fslib::Directory &FileModeState::get_destination_directory() noexcept { return m_target ? m_dirA : m_dirB; } +fslib::Directory &FileModeState::get_destination_directory() noexcept { return m_target == Target::MountA ? m_dirB : m_dirA; } void FileModeState::deactivate_state() noexcept { + // This should be already set to this, but just to be sure. sm_frame->set_y(graphics::SCREEN_HEIGHT); + + // Close both mount points. fslib::close_file_system(m_mountA); fslib::close_file_system(m_mountB); + + // Reset the control guide. + sm_controlGuide->reset(); + + // Mark the state for deletion. BaseState::deactivate(); } diff --git a/source/appstates/FileOptionState.cpp b/source/appstates/FileOptionState.cpp index a4f16fe..761b2c9 100644 --- a/source/appstates/FileOptionState.cpp +++ b/source/appstates/FileOptionState.cpp @@ -44,11 +44,11 @@ static std::string get_size_string(int64_t totalSize); FileOptionState::FileOptionState(FileModeState *spawningState) : m_spawningState(spawningState) , m_target(spawningState->m_target) - , m_transition(m_target ? RIGHT_X : LEFT_X, + , m_transition(m_target == FileModeState::Target::MountA ? LEFT_X : RIGHT_X, 232, 32, 32, - m_target ? RIGHT_X : LEFT_X, + m_target == FileModeState::Target::MountA ? LEFT_X : RIGHT_X, 232, 256, 256, @@ -134,7 +134,7 @@ void FileOptionState::initialize_static_members() return; } - sm_copyMenu = ui::Menu::create(m_target ? RIGHT_X + 9 : LEFT_X + 9, + sm_copyMenu = ui::Menu::create(m_target == FileModeState::Target::MountA ? LEFT_X + 9 : RIGHT_X + 9, 253, 234, 20, @@ -289,7 +289,7 @@ void FileOptionState::rename_target() const std::string newString = newPath.string(); // If this is false and there's a journaling size set, we need to commit on renaming for it to stick. - const bool isSource = !m_target; + const bool isSource = m_target == FileModeState::Target::MountA; const int64_t journalSize = m_spawningState->m_journalSize; const bool commitNeeded = isSource && journalSize > 0;