Implement States for SlideOutPanel.

This commit is contained in:
J-D-K 2025-11-14 20:52:25 -05:00
parent e6978dfb51
commit 785b51c455
15 changed files with 171 additions and 62 deletions

View File

@ -53,7 +53,7 @@ class BackupMenuState final : public BaseState
void save_data_written();
// clang-format off
enum class MenuEntryType
enum class MenuEntryType : uint8_t
{
Null,
Local,

View File

@ -10,7 +10,7 @@ class FadeState final : public BaseState
{
public:
/// @brief Direction (In/Out) of the fade. This is auto-determined at construction.
enum class Direction
enum class Direction : uint8_t
{
In,
Out

View File

@ -4,7 +4,7 @@
namespace stringutil
{
/// @brief Enum for creating date strings.
enum class DateFormat
enum class DateFormat : uint8_t
{
YearMonthDay,
YearDayMonth

View File

@ -13,7 +13,7 @@ namespace ui
{
public:
/// @brief Enum for which side of the screen the panel slides from.
enum class Side
enum class Side : uint8_t
{
Left,
Right
@ -60,13 +60,16 @@ namespace ui
/// @brief Unhides the panel.
void unhide() noexcept;
/// @brief This function unhide the panel when the bool passed is true.
void unhide_on_focus(bool hasFocus) noexcept;
/// @brief Returns if the panel is fully open.
/// @return If the panel is fully open.
bool is_open() const noexcept;
/// @brief Returns if the panel is fully closed.
/// @return If the panel is fully closed.
bool is_closed() noexcept;
bool is_closed() const noexcept;
/// @brief Returns whether or not the panel is hidden.
bool is_hidden() const noexcept;
@ -83,14 +86,16 @@ namespace ui
sdl::SharedTexture &get_target() noexcept;
private:
/// @brief Bool for whether panel is fully open or not.
bool m_isOpen{};
/// @brief Whether or not to close panel.
bool m_closePanel{};
/// @brief Whether or not to hide the panel.
bool m_hidePanel{};
/// @brief States the panel can be in.
enum class State : uint8_t
{
Opening,
Opened,
Closing,
Closed,
Hiding,
Hidden
};
/// @brief Width of the panel in pixels.
int m_width{};
@ -104,6 +109,9 @@ namespace ui
/// @brief Transition for the slide out effect.
ui::Transition m_transition{};
/// @brief Current state of the panel.
SlideOutPanel::State m_state{};
/// @brief Render target if panel.
sdl::SharedTexture m_renderTarget{};
@ -118,5 +126,11 @@ namespace ui
/// @brief Returns the target X to close/hide the panel.
inline int get_target_close_x() { return m_side == Side::Left ? -m_width : graphics::SCREEN_WIDTH; }
/// @brief Updates the position of the panel.
void update_position_state() noexcept;
/// @brief Updates sub-elements.
void update_sub_elements(bool hasFocus) noexcept;
};
} // namespace ui

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 543 B

View File

@ -45,20 +45,35 @@ BackupMenuState::BackupMenuState(data::User *user, data::TitleInfo *titleInfo, c
void BackupMenuState::update()
{
// Grab focus once and only once.
const bool hasFocus = BaseState::has_focus();
const bool isOpen = sm_slidePanel->is_open();
// Update the panel first.
sm_slidePanel->update(hasFocus);
if (!isOpen) { return; }
std::lock_guard menuGuard{sm_menuMutex};
// Grab these.
const bool isOpen = sm_slidePanel->is_open();
const bool isClosed = sm_slidePanel->is_closed();
const int selected = sm_backupMenu->get_selected();
// If the panel is closed, deactivate. If it's not open, return.
if (isClosed) { BackupMenuState::deactivate_state(); }
else if (!isOpen) { return; }
// Grab the currently selected index.
int selected{};
{
std::lock_guard menuGuard{sm_menuMutex};
selected = sm_backupMenu->get_selected();
}
// 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);
// Conditions.
const bool newSelected = selected == 0;
const bool newBackup = aPressed && newSelected && m_saveHasData;
const bool overwriteBackup = aPressed && !newSelected && m_saveHasData;
@ -75,6 +90,9 @@ void BackupMenuState::update()
else if (popEmpty) { BackupMenuState::pop_save_empty(); }
else if (bPressed) { sm_slidePanel->close(); }
else if (sm_slidePanel->is_closed()) { BackupMenuState::deactivate_state(); }
// Lock and update the menu.
std::lock_guard menuGuard{sm_menuMutex};
sm_backupMenu->update(hasFocus);
}
@ -88,22 +106,31 @@ 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();
sm_slidePanel->clear_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);
// 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);
}
// 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);
}
@ -149,7 +176,14 @@ void BackupMenuState::refresh()
void BackupMenuState::save_data_written()
{
if (!m_saveHasData) { m_saveHasData = true; }
// Temporarily mount the save.
fs::ScopedSaveMount tempMount{fs::DEFAULT_SAVE_MOUNT, m_saveInfo};
// Check to see if the root is empty.
fslib::Directory saveRoot{fs::DEFAULT_SAVE_ROOT, false};
// Just check if the root is empty.
m_saveHasData = saveRoot.get_count() > 0;
}
// ---- Private functions ----
@ -408,6 +442,7 @@ void BackupMenuState::pop_save_empty()
void BackupMenuState::deactivate_state()
{
logger::log("BackupMenuState::deactivate_state()");
sm_slidePanel->clear_elements();
sm_slidePanel->reset();
sm_backupMenu->reset();

View File

@ -43,9 +43,9 @@ void FileModeState::update()
{
switch (m_state)
{
case State::Rising: FileModeState::update_y(); break;
case State::Open: FileModeState::update_handle_input(); break;
case State::Rising:
case State::Dropping: FileModeState::update_y(); break;
case State::Open: FileModeState::update_handle_input(); break;
}
}

View File

@ -74,9 +74,9 @@ void FileOptionState::update()
{
switch (m_state)
{
case State::Opening: FileOptionState::update_dimensions(); break;
case State::Opened: FileOptionState::update_handle_input(); break;
case State::Opening:
case State::Closing: FileOptionState::update_dimensions(); break;
case State::Opened: FileOptionState::update_handle_input(); break;
}
}

View File

@ -59,9 +59,9 @@ void MessageState::update()
{
switch (m_state)
{
case State::Opening: MessageState::update_dimensions(); break;
case State::Displaying: MessageState::update_handle_input(); break;
case State::Opening:
case State::Closing: MessageState::update_dimensions(); break;
case State::Displaying: MessageState::update_handle_input(); break;
}
}

View File

@ -58,8 +58,8 @@ void ProgressState::update()
switch (m_state)
{
case State::Opening: ProgressState::update_dimensions(); break;
case State::Running: ProgressState::update_progress(); break;
case State::Closing: ProgressState::update_dimensions(); break;
case State::Running: ProgressState::update_progress(); break;
}
}
@ -152,9 +152,9 @@ void ProgressState::update_progress() noexcept
// This is the actual string that's displayed.
m_percentageString = stringutil::get_formatted_string("%u%%", m_progress);
// Center the string above.
const int stringWidth = sdl::text::get_width(BaseTask::FONT_SIZE, m_percentageString);
m_percentageX = COORD_DISPLAY_CENTER - (stringWidth / 2);
// Center the string above.
m_percentageX = COORD_DISPLAY_CENTER - (stringWidth / 2);
// Handle closing and updating.
const bool taskRunning = m_task->is_running();

View File

@ -69,9 +69,9 @@ void TitleInfoState::update()
{
switch (m_state)
{
case State::Opening: TitleInfoState::update_dimensions(); break;
case State::Displaying: TitleInfoState::update_handle_input(); break;
case State::Opening:
case State::Closing: TitleInfoState::update_dimensions(); break;
case State::Displaying: TitleInfoState::update_handle_input(); break;
}
}
@ -85,11 +85,7 @@ void TitleInfoState::render()
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++)
{
auto &currentField = m_infoFields[i];
currentField->render(sdl::Texture::Null, hasFocus);
}
for (int i = 0; i < m_fieldDisplayCount; i++) { m_infoFields[i]->render(sdl::Texture::Null, hasFocus); }
}
// ---- Private functions ----

View File

@ -62,6 +62,7 @@ void TitleOptionState::update()
{
const bool hasFocus = BaseState::has_focus();
sm_slidePanel->unhide_on_focus(hasFocus);
sm_slidePanel->update(hasFocus);
const bool isOpen = sm_slidePanel->is_open();
@ -150,7 +151,7 @@ void TitleOptionState::add_to_blacklist()
{
const char *title = m_titleInfo->get_title();
const char *confirmFormat = strings::get_by_name(strings::names::TITLEOPTION_CONFS, 0);
const std::string query = stringutil::get_formatted_string(confirmFormat, title);
std::string query = stringutil::get_formatted_string(confirmFormat, title);
ConfirmTask::create_push_fade(query, false, tasks::titleoptions::blacklist_title, nullptr, m_dataStruct);
}
@ -225,7 +226,7 @@ void TitleOptionState::delete_all_local_backups()
{
const char *title = m_titleInfo->get_title();
const char *confirmFormat = strings::get_by_name(strings::names::TITLEOPTION_CONFS, 1);
const std::string query = stringutil::get_formatted_string(confirmFormat, title);
std::string query = stringutil::get_formatted_string(confirmFormat, title);
ConfirmTask::create_push_fade(query, true, tasks::titleoptions::delete_all_local_backups_for_title, nullptr, m_dataStruct);
}
@ -234,7 +235,7 @@ void TitleOptionState::delete_all_remote_backups()
{
const char *title = m_titleInfo->get_title();
const char *confirmFormat = strings::get_by_name(strings::names::TITLEOPTION_CONFS, 1);
const std::string query = stringutil::get_formatted_string(confirmFormat, title);
std::string query = stringutil::get_formatted_string(confirmFormat, title);
ConfirmTask::create_push_fade(query, true, tasks::titleoptions::delete_all_remote_backups_for_title, nullptr, m_dataStruct);
}
@ -252,7 +253,7 @@ void TitleOptionState::reset_save_data()
const char *title = m_titleInfo->get_title();
const char *confirmFormat = strings::get_by_name(strings::names::TITLEOPTION_CONFS, 2);
const std::string query = stringutil::get_formatted_string(confirmFormat, title);
std::string query = stringutil::get_formatted_string(confirmFormat, title);
ConfirmTask::create_push_fade(query, true, tasks::titleoptions::reset_save_data, nullptr, m_dataStruct);
}
@ -272,7 +273,7 @@ void TitleOptionState::delete_save_from_system()
const char *nickname = m_user->get_nickname();
const char *title = m_titleInfo->get_title();
const char *confirmFormat = strings::get_by_name(strings::names::TITLEOPTION_CONFS, 3);
const std::string query = stringutil::get_formatted_string(confirmFormat, nickname, title);
std::string query = stringutil::get_formatted_string(confirmFormat, nickname, title);
ConfirmTask::create_push_fade(query, true, tasks::titleoptions::delete_save_data_from_system, nullptr, m_dataStruct);
}
@ -326,8 +327,7 @@ void TitleOptionState::export_svi_file()
}
const NsApplicationControlData *controlData = m_titleInfo->get_control_data();
const bool magicWritten = sviFile.write(&fs::SAVE_META_MAGIC, sizeof(uint32_t)) == sizeof(uint32_t);
const bool magicWritten = sviFile.write(&fs::SAVE_META_MAGIC, sizeof(uint32_t)) == sizeof(uint32_t);
const bool appIdWritten = magicWritten && sviFile.write(&applicationID, sizeof(uint64_t)) == sizeof(uint64_t);
const bool controlWritten =
appIdWritten && sviFile.write(controlData, sizeof(NsApplicationControlData)) == sizeof(NsApplicationControlData);

View File

@ -55,8 +55,11 @@ void UserOptionState::update()
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);
// Refresh here if needed to avoid threading issues.
if (m_refreshRequired)
{
m_user->load_user_data();

View File

@ -132,7 +132,9 @@ bool remote::WebDav::upload_file(const fslib::Path &source, std::string_view rem
return false;
}
if (task) { task->reset(static_cast<double>(sourceFile.get_size())); }
// This is used multiple multiple places. No need to recall.
const int64_t fileSize = sourceFile.get_size();
if (task) { task->reset(static_cast<double>(fileSize)); }
remote::URL url{m_origin};
url.append_path(m_parent).append_path(escapedName);
@ -143,14 +145,14 @@ bool remote::WebDav::upload_file(const fslib::Path &source, std::string_view rem
curl::set_option(m_curl, CURLOPT_URL, url.get());
curl::set_option(m_curl, CURLOPT_UPLOAD, 1L);
curl::set_option(m_curl, CURLOPT_UPLOAD_BUFFERSIZE, Storage::SIZE_UPLOAD_BUFFER);
curl::set_option(m_curl, CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(sourceFile.get_size()));
curl::set_option(m_curl, CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(fileSize));
curl::set_option(m_curl, CURLOPT_READFUNCTION, curl::read_data_from_file);
curl::set_option(m_curl, CURLOPT_READDATA, &uploadData);
if (!curl::perform(m_curl)) { return false; }
const std::string id = m_parent + "/" + escapedName;
m_list.emplace_back(remoteName, id, m_parent, sourceFile.get_size(), false);
m_list.emplace_back(remoteName, id, m_parent, fileSize, false);
return true;
}

View File

@ -3,6 +3,7 @@
#include "config/config.hpp"
#include "graphics/colors.hpp"
#include "graphics/screen.hpp"
#include "logging/logger.hpp"
#include "mathutil.hpp"
#include <cmath>
@ -23,6 +24,7 @@ ui::SlideOutPanel::SlideOutPanel(int width, Side side)
0,
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,
@ -32,14 +34,13 @@ ui::SlideOutPanel::SlideOutPanel(int width, Side side)
void ui::SlideOutPanel::update(bool hasFocus)
{
const int targetX = m_transition.get_target_x();
if (hasFocus && targetX != m_targetX) { SlideOutPanel::unhide(); }
m_transition.update();
if (m_transition.in_place())
switch (m_state)
{
for (auto &currentElement : m_elements) { currentElement->update(hasFocus); }
case State::Opening:
case State::Closing:
case State::Hiding: SlideOutPanel::update_position_state(); break;
case State::Opened: SlideOutPanel::update_sub_elements(hasFocus); break;
default: return; // Nothing should take place in any other state.
}
}
@ -47,8 +48,10 @@ void ui::SlideOutPanel::sub_update() { m_transition.update(); }
void ui::SlideOutPanel::render(sdl::SharedTexture &target, bool hasFocus)
{
// Loop and render all sub-elements to the target.
for (auto &currentElement : m_elements) { currentElement->render(m_renderTarget, 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);
}
@ -57,41 +60,97 @@ void ui::SlideOutPanel::clear_target() { m_renderTarget->clear(colors::SLIDE_PAN
void ui::SlideOutPanel::reset() noexcept
{
const int x = SlideOutPanel::get_target_close_x();
// Grab the X to close and to open.
const int x = SlideOutPanel::get_target_close_x();
const int targetX = SlideOutPanel::get_target_open_x();
// Since we're resetting, we just set this immediately to be 100% sure. There is no transition.
m_transition.set_x(x);
m_isOpen = false;
m_closePanel = false;
// This is reset for the next opening.
m_transition.set_target_x(targetX);
// Reset state to opening.
m_state = State::Opening;
}
void ui::SlideOutPanel::close() noexcept
{
// Grab target X to close the set the target X to that.
const int targetX = SlideOutPanel::get_target_close_x();
// Set the target to the closed X.
m_transition.set_target_x(targetX);
m_closePanel = true;
// Set the state to closing.
m_state = State::Closing;
}
void ui::SlideOutPanel::hide() noexcept
{
// Get close X, but set to hiding instead of closing.
const int targetX = SlideOutPanel::get_target_close_x();
m_transition.set_target_x(targetX);
m_hidePanel = true;
m_state = State::Hiding;
}
void ui::SlideOutPanel::unhide() noexcept
{
// Get open X, set state to opening.
const int targetX = SlideOutPanel::get_target_open_x();
m_transition.set_target_x(targetX);
m_hidePanel = false;
m_state = State::Opening;
}
bool ui::SlideOutPanel::is_open() const noexcept { return m_transition.in_place(); }
void ui::SlideOutPanel::unhide_on_focus(bool hasFocus) noexcept
{
// Just bail if we don't even have focus.
if (!hasFocus) { return; }
bool ui::SlideOutPanel::is_closed() noexcept { return m_closePanel && m_transition.in_place(); }
// Need to know if the panel is hiding.
const bool isHiding = m_state == State::Hiding || m_state == State::Hidden;
if (!isHiding) { return; }
bool ui::SlideOutPanel::is_hidden() const noexcept { return m_hidePanel; }
// At this point, we need to unhide.
// Grab the open x target and set the target.
const int targetX = SlideOutPanel::get_target_open_x();
m_transition.set_target_x(targetX);
// Set the state to opening.
m_state = State::Opening;
}
bool ui::SlideOutPanel::is_open() const noexcept { return m_state == State::Opened; }
bool ui::SlideOutPanel::is_closed() const noexcept { return m_state == State::Closed; }
bool ui::SlideOutPanel::is_hidden() const noexcept { return m_state == State::Hidden; }
void ui::SlideOutPanel::push_new_element(std::shared_ptr<ui::Element> newElement) { m_elements.push_back(newElement); }
void ui::SlideOutPanel::clear_elements() { m_elements.clear(); }
sdl::SharedTexture &ui::SlideOutPanel::get_target() noexcept { return m_renderTarget; }
// ---- Private functions ----
void ui::SlideOutPanel::update_position_state() noexcept
{
// Update transition.
m_transition.update();
// State shifting conditions.
const bool opened = m_state == State::Opening && m_transition.in_place_xy();
const bool closed = !opened && m_state == State::Closing && m_transition.in_place_xy();
const bool hidden = !opened && !closed && m_state == State::Hiding && m_transition.in_place_xy();
if (opened) { m_state = State::Opened; }
else if (closed) { m_state = State::Closed; }
else if (hidden) { m_state = State::Hidden; }
}
void ui::SlideOutPanel::update_sub_elements(bool hasFocus) noexcept
{
for (auto &element : m_elements) { element->update(hasFocus); }
}