mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-04-24 15:27:10 -05:00
Tweak SlidePanel behavior.
This commit is contained in:
parent
bc3f028082
commit
58e6217e73
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "sdl.hpp"
|
||||
#include "ui/Element.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ui
|
||||
|
|
@ -47,7 +48,7 @@ namespace ui
|
|||
|
||||
/// @brief Returns if the panel is fully closed.
|
||||
/// @return If the panel is fully closed.
|
||||
bool is_closed() const;
|
||||
bool is_closed();
|
||||
|
||||
/// @brief Pushes a new element to the element vector.
|
||||
/// @param newElement New element to push.
|
||||
|
|
@ -62,27 +63,27 @@ namespace ui
|
|||
|
||||
private:
|
||||
/// @brief Bool for whether panel is fully open or not.
|
||||
bool m_isOpen = false;
|
||||
bool m_isOpen{};
|
||||
|
||||
/// @brief Whether or not to close panel.
|
||||
bool m_closePanel = false;
|
||||
bool m_closePanel{};
|
||||
|
||||
/// @brief Current X coordinate to render to. Panels are always 720 pixels in height so no Y is required.
|
||||
double m_x;
|
||||
double m_x{};
|
||||
|
||||
/// @brief Width of the panel in pixels.
|
||||
int m_width;
|
||||
int m_width{};
|
||||
|
||||
/// @brief Target X position of panel.
|
||||
double m_targetX;
|
||||
double m_targetX{};
|
||||
|
||||
/// @brief Which side the panel is on.
|
||||
SlideOutPanel::Side m_side;
|
||||
SlideOutPanel::Side m_side{};
|
||||
|
||||
/// @brief Render target if panel.
|
||||
sdl::SharedTexture m_renderTarget;
|
||||
sdl::SharedTexture m_renderTarget{};
|
||||
|
||||
/// @brief Vector of elements.
|
||||
std::vector<std::shared_ptr<ui::Element>> m_elements;
|
||||
std::vector<std::shared_ptr<ui::Element>> m_elements{};
|
||||
};
|
||||
} // namespace ui
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@
|
|||
],
|
||||
"SaveCreatePops": [
|
||||
"0: Save data created for #%s#!",
|
||||
"1: Error creating save data for #%s#!",
|
||||
"1: Error creating save data!",
|
||||
"2: Error deleting save data!"
|
||||
],
|
||||
"SaveDataTypes": [
|
||||
|
|
|
|||
|
|
@ -122,17 +122,19 @@ static bool compare_info(data::TitleInfo *infoA, data::TitleInfo *infoB)
|
|||
const char *titleA = infoA->get_title();
|
||||
const char *titleB = infoB->get_title();
|
||||
|
||||
size_t titleALength = std::char_traits<char>::length(titleA);
|
||||
size_t titleBLength = std::char_traits<char>::length(titleB);
|
||||
size_t shortestTitle = titleALength < titleBLength ? titleALength : titleBLength;
|
||||
const size_t titleALength = std::char_traits<char>::length(titleA);
|
||||
const size_t titleBLength = std::char_traits<char>::length(titleB);
|
||||
const size_t shortestTitle = titleALength < titleBLength ? titleALength : titleBLength;
|
||||
// To do: This doesn't take into account which is the shortest title. This can still go out-of-bounds.
|
||||
for (size_t i = 0, j = 0; i < shortestTitle;)
|
||||
{
|
||||
uint32_t codepointA = 0;
|
||||
uint32_t codepointB = 0;
|
||||
|
||||
ssize_t unitCountA = decode_utf8(&codepointA, reinterpret_cast<const uint8_t *>(&titleA[i]));
|
||||
ssize_t unitCountB = decode_utf8(&codepointB, reinterpret_cast<const uint8_t *>(&titleB[j]));
|
||||
const uint8_t *pointA = reinterpret_cast<const uint8_t *>(&titleA[i]);
|
||||
const uint8_t *pointB = reinterpret_cast<const uint8_t *>(&titleB[j]);
|
||||
const ssize_t unitCountA = decode_utf8(&codepointA, pointA);
|
||||
const ssize_t unitCountB = decode_utf8(&codepointB, pointB);
|
||||
|
||||
if (unitCountA <= 0 || unitCountB <= 0) { return false; }
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ void TitleInfoState::initialize_static_members()
|
|||
{
|
||||
if (!sm_slidePanel)
|
||||
{
|
||||
sm_slidePanel = std::make_unique<ui::SlideOutPanel>(SIZE_PANEL_WIDTH, ui::SlideOutPanel::Side::Right);
|
||||
sm_slidePanel = std::make_unique<ui::SlideOutPanel>(SIZE_PANEL_WIDTH, ui::SlideOutPanel::Side::Left);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -282,15 +282,16 @@ static void change_output_path(data::TitleInfo *targetTitle)
|
|||
static constexpr size_t SIZE_PATH_BUFFER = 0x200;
|
||||
const char *headerTemplate = strings::get_by_name(strings::names::KEYBOARD, 7);
|
||||
const int popTicks = ui::PopMessageManager::DEFAULT_TICKS;
|
||||
const char *popSuccess = strings::get_by_name(strings::names::TITLEOPTION_POPS, 1);
|
||||
const char *popFailure = strings::get_by_name(strings::names::TITLEOPTION_POPS, 0);
|
||||
const char *popSuccess = strings::get_by_name(strings::names::TITLEOPTION_POPS, 8);
|
||||
const char *popFailure = strings::get_by_name(strings::names::TITLEOPTION_POPS, 9);
|
||||
const char *pathSafeTitle = targetTitle->get_path_safe_title();
|
||||
|
||||
const std::string headerString = stringutil::get_formatted_string(headerTemplate, targetTitle->get_title());
|
||||
char pathBuffer[SIZE_PATH_BUFFER] = {0};
|
||||
const bool inputIsValid = keyboard::get_input(SwkbdType_QWERTY, pathSafeTitle, headerString, pathBuffer, SIZE_PATH_BUFFER);
|
||||
const bool sanitized = inputIsValid && stringutil::sanitize_string_for_path(pathBuffer, pathBuffer, SIZE_PATH_BUFFER);
|
||||
if (!inputIsValid || !sanitized)
|
||||
const bool notEmpty = std::char_traits<char>::length(pathBuffer) > 0;
|
||||
if (!inputIsValid || !sanitized || !notEmpty)
|
||||
{
|
||||
ui::PopMessageManager::push_message(popTicks, popFailure);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "strings.hpp"
|
||||
|
||||
#include "JSON.hpp"
|
||||
#include "error.hpp"
|
||||
#include "fslib.hpp"
|
||||
#include "stringutil.hpp"
|
||||
|
||||
|
|
@ -33,41 +34,9 @@ namespace
|
|||
{SetLanguage_PTBR, "PTBR.json"}};
|
||||
} // namespace
|
||||
|
||||
// This returns the language file to use depending on the system's language.
|
||||
static fslib::Path get_file_path()
|
||||
{
|
||||
fslib::Path returnPath = "romfs:/Text";
|
||||
|
||||
uint64_t languageCode = 0;
|
||||
Result setError = setGetLanguageCode(&languageCode);
|
||||
if (R_FAILED(setError)) { return returnPath / s_fileMap.at(SetLanguage_ENUS); }
|
||||
|
||||
SetLanguage language;
|
||||
setError = setMakeLanguage(languageCode, &language);
|
||||
if (R_FAILED(setError)) { return returnPath / s_fileMap.at(SetLanguage_ENUS); }
|
||||
return returnPath / s_fileMap.at(language);
|
||||
}
|
||||
|
||||
static void replace_buttons_in_string(std::string &target)
|
||||
{
|
||||
stringutil::replace_in_string(target, "[A]", "\ue0e0");
|
||||
stringutil::replace_in_string(target, "[B]", "\ue0e1");
|
||||
stringutil::replace_in_string(target, "[X]", "\ue0e2");
|
||||
stringutil::replace_in_string(target, "[Y]", "\ue0e3");
|
||||
stringutil::replace_in_string(target, "[L]", "\ue0e4");
|
||||
stringutil::replace_in_string(target, "[R]", "\ue0e5");
|
||||
stringutil::replace_in_string(target, "[ZL]", "\ue0e6");
|
||||
stringutil::replace_in_string(target, "[ZR]", "\ue0e7");
|
||||
stringutil::replace_in_string(target, "[SL]", "\ue0e8");
|
||||
stringutil::replace_in_string(target, "[SR]", "\ue0e9");
|
||||
stringutil::replace_in_string(target, "[DPAD]", "\ue0ea");
|
||||
stringutil::replace_in_string(target, "[DUP]", "\ue0eb");
|
||||
stringutil::replace_in_string(target, "[DDOWN]", "\ue0ec");
|
||||
stringutil::replace_in_string(target, "[DLEFT]", "\ue0ed");
|
||||
stringutil::replace_in_string(target, "[DRIGHT]", "\ue0ee");
|
||||
stringutil::replace_in_string(target, "[+]", "\ue0ef");
|
||||
stringutil::replace_in_string(target, "[-]", "\ue0f0");
|
||||
}
|
||||
// Definitions at bottom.
|
||||
static fslib::Path get_file_path();
|
||||
static void replace_buttons_in_string(std::string &target);
|
||||
|
||||
bool strings::initialize()
|
||||
{
|
||||
|
|
@ -90,9 +59,9 @@ bool strings::initialize()
|
|||
{
|
||||
json_object *string = json_object_array_get_idx(array, i);
|
||||
std::string_view slicer = json_object_get_string(string);
|
||||
const int mapIndex = i;
|
||||
const auto mapPair = std::make_pair(name, mapIndex);
|
||||
const size_t begin = slicer.find(": ");
|
||||
const auto mapPair = std::make_pair(name, i);
|
||||
|
||||
const size_t begin = slicer.find(": ");
|
||||
if (begin != slicer.npos) { slicer = slicer.substr(begin + 2); }
|
||||
|
||||
s_stringMap[mapPair] = slicer;
|
||||
|
|
@ -114,3 +83,40 @@ const char *strings::get_by_name(std::string_view name, int index)
|
|||
if (findPair == s_stringMap.end()) { return nullptr; }
|
||||
return s_stringMap.at(mapPair).c_str();
|
||||
}
|
||||
|
||||
static fslib::Path get_file_path()
|
||||
{
|
||||
static constexpr std::string_view PATH_BASE = "romfs:/Text";
|
||||
|
||||
fslib::Path returnPath{PATH_BASE};
|
||||
uint64_t languageCode{};
|
||||
SetLanguage language{};
|
||||
|
||||
const bool codeError = error::libnx(setGetLanguageCode(&languageCode));
|
||||
const bool langError = !codeError && error::libnx(setMakeLanguage(languageCode, &language));
|
||||
if (codeError || langError) { returnPath /= s_fileMap[SetLanguage_ENUS]; }
|
||||
else { returnPath /= s_fileMap[language]; }
|
||||
|
||||
return returnPath;
|
||||
}
|
||||
|
||||
static void replace_buttons_in_string(std::string &target)
|
||||
{
|
||||
stringutil::replace_in_string(target, "[A]", "\ue0e0");
|
||||
stringutil::replace_in_string(target, "[B]", "\ue0e1");
|
||||
stringutil::replace_in_string(target, "[X]", "\ue0e2");
|
||||
stringutil::replace_in_string(target, "[Y]", "\ue0e3");
|
||||
stringutil::replace_in_string(target, "[L]", "\ue0e4");
|
||||
stringutil::replace_in_string(target, "[R]", "\ue0e5");
|
||||
stringutil::replace_in_string(target, "[ZL]", "\ue0e6");
|
||||
stringutil::replace_in_string(target, "[ZR]", "\ue0e7");
|
||||
stringutil::replace_in_string(target, "[SL]", "\ue0e8");
|
||||
stringutil::replace_in_string(target, "[SR]", "\ue0e9");
|
||||
stringutil::replace_in_string(target, "[DPAD]", "\ue0ea");
|
||||
stringutil::replace_in_string(target, "[DUP]", "\ue0eb");
|
||||
stringutil::replace_in_string(target, "[DDOWN]", "\ue0ec");
|
||||
stringutil::replace_in_string(target, "[DLEFT]", "\ue0ed");
|
||||
stringutil::replace_in_string(target, "[DRIGHT]", "\ue0ee");
|
||||
stringutil::replace_in_string(target, "[+]", "\ue0ef");
|
||||
stringutil::replace_in_string(target, "[-]", "\ue0f0");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <switch.h>
|
||||
|
||||
namespace
|
||||
|
|
@ -18,72 +19,63 @@ namespace
|
|||
|
||||
std::string stringutil::get_formatted_string(const char *format, ...)
|
||||
{
|
||||
char vaBuffer[VA_BUFFER_SIZE] = {0};
|
||||
std::array<char, VA_BUFFER_SIZE> vaBuffer = {0};
|
||||
|
||||
std::va_list vaList;
|
||||
va_start(vaList, format);
|
||||
vsnprintf(vaBuffer, VA_BUFFER_SIZE, format, vaList);
|
||||
vsnprintf(vaBuffer.data(), VA_BUFFER_SIZE, format, vaList);
|
||||
va_end(vaList);
|
||||
|
||||
return std::string(vaBuffer);
|
||||
return std::string(vaBuffer.data());
|
||||
}
|
||||
|
||||
void stringutil::replace_in_string(std::string &target, std::string_view find, std::string_view replace)
|
||||
{
|
||||
size_t stringPosition = 0;
|
||||
while ((stringPosition = target.find(find, stringPosition)) != target.npos)
|
||||
const size_t findLength = find.length();
|
||||
const size_t replaceLength = replace.length();
|
||||
|
||||
for (size_t i = target.find(find); i != target.npos; i = target.find(find, i + replaceLength))
|
||||
{
|
||||
target.replace(stringPosition, find.length(), replace);
|
||||
target.replace(i, findLength, replace);
|
||||
}
|
||||
}
|
||||
|
||||
void stringutil::strip_character(char c, std::string &target)
|
||||
{
|
||||
size_t charPosition = 0;
|
||||
while ((charPosition = target.find_first_of(c, charPosition)) != target.npos)
|
||||
for (size_t i = target.find_first_of(c); i != target.npos; i = target.find_first_of(c, i))
|
||||
{
|
||||
target.erase(target.begin() + charPosition);
|
||||
target.erase(target.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, size_t stringOutSize)
|
||||
{
|
||||
uint32_t codepoint = 0;
|
||||
size_t stringLength = std::strlen(stringIn);
|
||||
for (size_t i = 0, stringOutOffset = 0; i < stringLength;)
|
||||
uint32_t codepoint{};
|
||||
const size_t length = std::char_traits<char>::length(stringIn);
|
||||
for (size_t i = 0, offset = 0; i < length;)
|
||||
{
|
||||
ssize_t unitCount = decode_utf8(&codepoint, reinterpret_cast<const uint8_t *>(&stringIn[i]));
|
||||
if (unitCount <= 0 || i + unitCount >= stringOutSize) { break; }
|
||||
const uint8_t *point = reinterpret_cast<const uint8_t *>(&stringIn[i]);
|
||||
const ssize_t count = decode_utf8(&codepoint, point);
|
||||
const bool countCheck = count <= 0 || i + count >= stringOutSize;
|
||||
const bool codeCheck = codepoint < 0x20 || codepoint > 0x7E;
|
||||
if (countCheck) { break; }
|
||||
else if (codeCheck) { return false; }
|
||||
|
||||
if (codepoint < 0x20 || codepoint > 0x7E)
|
||||
{
|
||||
// Don't even bother. It's not possible.
|
||||
return false;
|
||||
}
|
||||
|
||||
// replace forbidden with spaces.
|
||||
if (std::find(FORBIDDEN_PATH_CHARACTERS.begin(), FORBIDDEN_PATH_CHARACTERS.end(), codepoint) !=
|
||||
FORBIDDEN_PATH_CHARACTERS.end())
|
||||
{
|
||||
stringOut[stringOutOffset++] = 0x20;
|
||||
}
|
||||
else if (codepoint == L'é') { stringOut[stringOutOffset++] = 'e'; }
|
||||
const bool forbidden = std::find(FORBIDDEN_PATH_CHARACTERS.begin(), FORBIDDEN_PATH_CHARACTERS.end(), codepoint) !=
|
||||
FORBIDDEN_PATH_CHARACTERS.end();
|
||||
if (forbidden) { stringOut[offset++] = 0x20; }
|
||||
else if (codepoint == L'é') { stringOut[offset++] = 'e'; }
|
||||
else
|
||||
{
|
||||
// Just memcpy it over. This is a safety thing to be honest. Since it's only Ascii allowed, unitcount should only
|
||||
// be 1.
|
||||
std::memcpy(&stringOut[stringOutOffset], &stringIn[i], static_cast<size_t>(unitCount));
|
||||
stringOutOffset += unitCount;
|
||||
std::memcpy(&stringOut[offset], &stringIn[i], static_cast<size_t>(count));
|
||||
offset += count;
|
||||
}
|
||||
i += unitCount;
|
||||
i += count;
|
||||
}
|
||||
|
||||
// Loop backwards and trim off spaces and periods.
|
||||
size_t stringOutLength = std::strlen(stringOut);
|
||||
while (stringOut[stringOutLength - 1] == ' ' || stringOut[stringOutLength - 1] == '.')
|
||||
{
|
||||
stringOut[--stringOutLength] = 0x00;
|
||||
}
|
||||
const int outLength = std::char_traits<char>::length(stringOut) - 1;
|
||||
for (int i = outLength; i > 0 && (stringOut[i] == ' ' || stringOut[i] == '.'); --i) { stringOut[i] = '\0'; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,15 @@
|
|||
|
||||
#include <cmath>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int SCREEN_WIDTH = 1280;
|
||||
}
|
||||
|
||||
ui::SlideOutPanel::SlideOutPanel(int width, Side side)
|
||||
: m_x(side == Side::Left ? -width : 1280)
|
||||
: m_x(side == Side::Left ? -width : SCREEN_WIDTH)
|
||||
, m_width(width)
|
||||
, m_targetX(side == Side::Left ? 0 : 1280 - m_width)
|
||||
, m_targetX(side == Side::Left ? 0 : SCREEN_WIDTH - m_width)
|
||||
, m_side(side)
|
||||
{
|
||||
static int slidePanelTargetID = 0;
|
||||
|
|
@ -21,24 +26,18 @@ ui::SlideOutPanel::SlideOutPanel(int width, Side side)
|
|||
|
||||
void ui::SlideOutPanel::update(bool hasFocus)
|
||||
{
|
||||
double scaling = config::get_animation_scaling();
|
||||
|
||||
// The first two conditions are just a workaround because my math keeps leaving two pixels.
|
||||
if (!m_isOpen && m_side == Side::Left && m_x >= -4)
|
||||
const double scaling = config::get_animation_scaling();
|
||||
const bool openingFromLeft = !m_isOpen && m_side == Side::Left && m_x < m_targetX;
|
||||
const bool openingFromRight = !m_isOpen && m_side == Side::Right && m_x > m_targetX;
|
||||
if (openingFromLeft) { m_x -= std::round(m_x / scaling); }
|
||||
else if (openingFromRight)
|
||||
{
|
||||
m_x = 0;
|
||||
m_isOpen = true;
|
||||
}
|
||||
else if (!m_isOpen && m_side == Side::Right && m_x - m_targetX <= 4)
|
||||
{
|
||||
m_x = 1280 - m_width;
|
||||
m_isOpen = true;
|
||||
}
|
||||
else if (!m_isOpen && m_side == Side::Left && m_x != m_targetX) { m_x -= std::ceil(m_x / scaling); }
|
||||
else if (!m_isOpen && m_side == Side::Right && m_x != m_targetX)
|
||||
{
|
||||
m_x += std::ceil((1280.0f - (static_cast<double>(m_width)) - m_x) / scaling);
|
||||
const double screenWidth = static_cast<double>(SCREEN_WIDTH);
|
||||
const double width = static_cast<double>(m_width);
|
||||
const double pixels = (screenWidth - width - m_x) / scaling;
|
||||
m_x += std::round(pixels);
|
||||
}
|
||||
else { m_isOpen = true; }
|
||||
|
||||
// I'm going to leave it to the individual elements whether they update if the state is active.
|
||||
if (m_isOpen)
|
||||
|
|
@ -58,7 +57,7 @@ void ui::SlideOutPanel::clear_target() { m_renderTarget->clear(colors::SLIDE_PAN
|
|||
|
||||
void ui::SlideOutPanel::reset()
|
||||
{
|
||||
m_x = m_side == Side::Left ? -(m_width) : 1280.0f;
|
||||
m_x = m_side == Side::Left ? -(m_width) : SCREEN_WIDTH;
|
||||
m_isOpen = false;
|
||||
m_closePanel = false;
|
||||
}
|
||||
|
|
@ -67,7 +66,18 @@ void ui::SlideOutPanel::close() { m_closePanel = true; }
|
|||
|
||||
bool ui::SlideOutPanel::is_open() const { return m_isOpen; }
|
||||
|
||||
bool ui::SlideOutPanel::is_closed() const { return m_closePanel && (m_side == Side::Left ? m_x > -(m_width) : m_x < 1280); }
|
||||
bool ui::SlideOutPanel::is_closed()
|
||||
{
|
||||
// I'm assuming this is going to be called, waiting for the panel to close so.
|
||||
const double scaling = config::get_animation_scaling();
|
||||
const bool closeToLeft = m_closePanel && m_side == Side::Left && m_x > -m_width;
|
||||
const bool closeToRight = m_closePanel && m_side == Side::Right && m_x < SCREEN_WIDTH;
|
||||
if (closeToLeft) { m_x += -(m_width - m_x) / scaling; }
|
||||
else if (closeToRight) { m_x += m_x / scaling; }
|
||||
|
||||
const bool closed = m_side == Side::Left ? m_x <= -m_width : m_x >= SCREEN_WIDTH;
|
||||
return m_closePanel && closed;
|
||||
}
|
||||
|
||||
void ui::SlideOutPanel::push_new_element(std::shared_ptr<ui::Element> newElement) { m_elements.push_back(newElement); }
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
sdl::SharedTexture s_dialogCorners = nullptr;
|
||||
sdl::SharedTexture s_menuBoundingCorners = nullptr;
|
||||
sdl::SharedTexture s_dialogCorners{};
|
||||
sdl::SharedTexture s_menuBoundingCorners{};
|
||||
} // namespace
|
||||
|
||||
void ui::render_dialog_box(SDL_Texture *target, int x, int y, int width, int height)
|
||||
|
|
@ -19,8 +19,10 @@ void ui::render_dialog_box(SDL_Texture *target, int x, int y, int width, int hei
|
|||
s_dialogCorners->render_part(target, x, y, 0, 0, 16, 16);
|
||||
sdl::render_rect_fill(target, x + 16, y, width - 32, 16, colors::DIALOG_BOX);
|
||||
s_dialogCorners->render_part(target, (x + width) - 16, y, 16, 0, 16, 16);
|
||||
|
||||
// Middle
|
||||
sdl::render_rect_fill(NULL, x, y + 16, width, height - 32, colors::DIALOG_BOX);
|
||||
|
||||
// Bottom
|
||||
s_dialogCorners->render_part(target, x, (y + height) - 16, 0, 16, 16, 16);
|
||||
sdl::render_rect_fill(NULL, x + 16, (y + height) - 16, width - 32, 16, colors::DIALOG_BOX);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user