Implemented vertical scrolling for descriptions (#271)

* options_menu: implemented vertical scrolling for descriptions

* use correct size calculation

* remove empty lines from descriptions without value desc, move fix

* remove calculating the space for the next annotation after the last line

---------

Co-authored-by: DeaTh-G <hatvongeorge@gmail.com>
Co-authored-by: DeaTh-G <55578911+DeaTh-G@users.noreply.github.com>
This commit is contained in:
Hyper 2025-02-04 17:30:42 +00:00 committed by GitHub
parent c40ffbc70d
commit 66648d550a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 143 additions and 50 deletions

View file

@ -138,14 +138,14 @@ float4 main(in Interpolators interpolators) : SV_Target
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE)
{
float minAlpha = saturate((interpolators.Position.x - g_PushConstants.BoundsMin.x) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.y);
color.a *= minAlpha;
color.a *= maxAlpha;
}
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE)
{
float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.y);
float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y);
color.a *= minAlpha;

View file

@ -80,7 +80,7 @@ void ResetTextSkew()
SetScale({ 1.0f, 1.0f });
}
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleLeft, float fadeScaleRight)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade);
callbackData->setMarqueeFade.boundsMin[0] = min.x;
@ -89,10 +89,15 @@ void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
callbackData->setMarqueeFade.boundsMax[1] = max.y;
SetShaderModifier(IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE);
SetScale({ fadeScale, 1.0f });
SetScale({ fadeScaleLeft, fadeScaleRight });
}
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
{
SetHorizontalMarqueeFade(min, max, fadeScale, fadeScale);
}
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleTop, float fadeScaleBottom)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade);
callbackData->setMarqueeFade.boundsMin[0] = min.x;
@ -101,7 +106,12 @@ void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
callbackData->setMarqueeFade.boundsMax[1] = max.y;
SetShaderModifier(IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE);
SetScale({ 1.0f, fadeScale });
SetScale({ fadeScaleTop, fadeScaleBottom });
}
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
{
SetVerticalMarqueeFade(min, max, fadeScale, fadeScale);
}
void ResetMarqueeFade()
@ -353,7 +363,8 @@ std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsi
return input;
}
std::pair<std::string, std::map<std::string, std::string>> RemoveRubyAnnotations(const char* input) {
std::pair<std::string, std::map<std::string, std::string>> RemoveRubyAnnotations(const char* input)
{
std::string output;
std::map<std::string, std::string> rubyMap;
std::string currentMain, currentRuby;
@ -400,7 +411,8 @@ std::pair<std::string, std::map<std::string, std::string>> RemoveRubyAnnotations
return { output, rubyMap };
}
std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std::map<std::string, std::string>& rubyMap) {
std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std::map<std::string, std::string>& rubyMap)
{
std::string annotatedText;
size_t idx = 0;
size_t length = wrappedText.length();
@ -431,7 +443,7 @@ std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std:
return annotatedText;
}
std::vector<std::string> Split(const char* strStart, const ImFont *font, float fontSize, float maxWidth)
std::vector<std::string> Split(const char* strStart, const ImFont* font, float fontSize, float maxWidth)
{
if (!strStart)
return {};
@ -445,6 +457,7 @@ std::vector<std::string> Split(const char* strStart, const ImFont *font, float f
const char *lineStart = strStart;
const bool wordWrapEnabled = (maxWidth > 0.0f);
const char *wordWrapEOL = nullptr;
while (*str != 0)
{
if (wordWrapEnabled)
@ -572,10 +585,9 @@ std::vector<std::string> RemoveAnnotationFromParagraph(const std::vector<std::st
for (auto& annotatedLine : paragraph.lines)
{
std::string annotationRemovedLine = "";
for (const auto& segment : annotatedLine)
{
annotationRemovedLine += segment.text;
}
result.push_back(annotationRemovedLine);
}
@ -588,9 +600,7 @@ std::string RemoveAnnotationFromParagraphLine(const std::vector<TextSegment>& an
std::string result = "";
for (auto& segment : annotatedLine)
{
result += segment.text;
}
return result;
}
@ -603,25 +613,19 @@ ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMar
const auto paragraph = CalculateAnnotatedParagraph(lines);
std::vector<std::string> annotationRemovedLines;
for (const auto& line : paragraph.lines)
{
annotationRemovedLines.emplace_back(RemoveAnnotationFromParagraphLine(line));
}
for (size_t i = 0; i < annotationRemovedLines.size(); i++)
{
auto& line = annotationRemovedLines[i];
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, line.c_str());
auto annotationSize = font->CalcTextSizeA(fontSize * ANNOTATION_FONT_SIZE_MODIFIER, FLT_MAX, 0, "");
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, annotationRemovedLines[i].c_str());
x = std::max(x, textSize.x);
y += textSize.y + Scale(lineMargin);
if (paragraph.annotated)
{
y += annotationSize.y;
}
if (paragraph.annotated && i != (annotationRemovedLines.size() - 1))
y += fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
}
return { x, y };
@ -629,23 +633,27 @@ ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMar
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text)
{
return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, font, fontSize, maxWidth));
const auto input = RemoveRubyAnnotations(text);
auto lines = Split(input.first.c_str(), font, fontSize, maxWidth);
for (auto& line : lines)
line = ReAddRubyAnnotations(line, input.second);
return MeasureCentredParagraph(font, fontSize, lineMargin, lines);
}
void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred)
{
float annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
const auto input = RemoveRubyAnnotations(text);
auto lines = Split(input.first.c_str(), font, fontSize, maxWidth);
for (auto& line : lines)
{
line = ReAddRubyAnnotations(line, input.second);
}
auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines);
float offsetY = 0.0f;
auto offsetY = 0.0f;
const auto paragraph = CalculateAnnotatedParagraph(lines);
@ -655,9 +663,8 @@ void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, c
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, annotationRemovedLine.c_str());
auto annotationSize = font->CalcTextSizeA(annotationFontSize, FLT_MAX, 0, "");
float textX = pos.x;
float textY = pos.y + offsetY;
auto textX = pos.x;
auto textY = pos.y + offsetY;
if (isCentred)
{
@ -681,14 +688,14 @@ void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, c
}
drawMethod(segment.text.c_str(), { textX, textY });
textX += textSize.x;
}
offsetY += textSize.y + Scale(lineMargin);
if (paragraph.annotated)
{
offsetY += annotationSize.y;
}
}
}

View file

@ -42,7 +42,9 @@ void SetOrigin(ImVec2 origin);
void SetScale(ImVec2 scale);
void SetTextSkew(float yCenter, float skewScale);
void ResetTextSkew();
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleLeft, float fadeScaleRight);
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale);
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleTop, float fadeScaleBottom);
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale);
void ResetMarqueeFade();
void SetOutline(float outline);

View file

@ -292,9 +292,9 @@ void MessageWindow::Draw()
if (Config::Language == ELanguage::Japanese)
{
textMarginX -= Scale(2.5f);
textMarginY -= Scale(7.5f);
textMarginY -= Scale(2.0f);
textY += Scale(lines.size() % 2 == 0 ? 8.5f : 15.5f);
textY += Scale(lines.size() % 2 == 0 ? 1.5f : 8.0f);
}
bool isController = hid::IsInputDeviceController();

View file

@ -55,6 +55,8 @@ static constexpr float SETTINGS_NARROW_GRID_COUNT = 70.0f;
static constexpr float INFO_NARROW_GRID_COUNT = 34.0f;
static constexpr float PADDING_NARROW_GRID_COUNT = 1.0f;
static constexpr float INFO_TEXT_MARQUEE_DELAY = 1.2f;
static constexpr int32_t g_categoryCount = 4;
static int32_t g_categoryIndex;
static ImVec2 g_categoryAnimMin;
@ -1404,7 +1406,7 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
}
else
{
// Specialised description for resolution scale
// Specialised description for resolution scale.
if (g_selectedItem == &Config::ResolutionScale)
{
char buf[100];
@ -1417,54 +1419,136 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
desc = buf;
}
desc += "\n\n" + g_selectedItem->GetValueDescription(Config::Language);
const auto& valueDescription = g_selectedItem->GetValueDescription(Config::Language);
if (!valueDescription.empty())
{
desc += "\n\n" + valueDescription;
}
}
clipRectMin = { clipRectMin.x, thumbnailMax.y };
auto fontSize = Scale(28.0f);
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
// Extra padding between the start of the description text and the bottom of the thumbnail
float offsetY = Scale(24.0f);
float textX = clipRectMin.x - Scale(0.5f);
float textY = thumbnailMax.y + offsetY;
/* Extra padding between the start
of the description text and the
bottom of the thumbnail. */
auto offsetY = Scale(24.0f);
auto textX = clipRectMin.x - Scale(0.5f);
auto textY = thumbnailMax.y + offsetY;
if (Config::Language == ELanguage::Japanese)
{
// Removing some padding of the applied due to the inclusion of annotation for Japanese
/* Removing some padding of the applied due
to the inclusion of annotation for Japanese. */
textY -= Scale(8.0f);
// The annotation (and thus the Japanese) can be drawn above the edges of the info panel thus the clip needs to be extended a bit
/* The annotation (and thus the Japanese) can be
drawn above the edges of the info panel thus the
clip needs to be extended a bit. */
clipRectMin.x -= annotationFontSize;
clipRectMin.y -= annotationFontSize;
clipRectMax.x += annotationFontSize;
clipRectMax.y += annotationFontSize;
textY += annotationFontSize;
}
auto textSize = MeasureCentredParagraph(g_seuratFont, fontSize, clipRectMax.x - clipRectMin.x, 5.0f, desc.c_str());
drawList->PushClipRect(clipRectMin, clipRectMax, false);
static auto isScrolling = false;
static auto isManualScrolling = false;
static auto scrollOffset = 0.0f;
static auto scrollTimer = 0.0f;
static auto scrollDirection = 1.0f;
auto scrollMax = textSize.y - (clipRectMax.y - textY);
auto scrollSpeed = Scale(50);
if (scrollMax > 0.0f)
{
if (auto pInputState = SWA::CInputState::GetInstance())
{
auto& rPadState = pInputState->GetPadState();
auto& vert = rPadState.RightStickVertical;
if (fabs(vert) > 0.25f)
{
isManualScrolling = true;
scrollOffset += vert * scrollSpeed * App::s_deltaTime;
}
else if (isManualScrolling && fabs(vert) <= 0.25f)
{
isManualScrolling = false;
}
}
if (!isManualScrolling)
{
if (!isScrolling)
{
scrollTimer += App::s_deltaTime;
if (scrollTimer >= INFO_TEXT_MARQUEE_DELAY)
isScrolling = true;
}
if (isScrolling)
{
scrollOffset += scrollSpeed * scrollDirection * App::s_deltaTime;
if (scrollOffset >= scrollMax)
{
isScrolling = false;
scrollOffset = scrollMax;
scrollTimer = 0.0f;
scrollDirection = -1.0f;
}
else if (scrollOffset <= 0.0f)
{
isScrolling = false;
scrollOffset = 0;
scrollTimer = 0.0f;
scrollDirection = 1.0f;
}
}
}
scrollOffset = std::clamp(scrollOffset, 0.0f, scrollMax);
}
SetVerticalMarqueeFade(clipRectMin, clipRectMax, Scale(24), Lerp(Scale(24), 0.0f, scrollOffset / scrollMax));
DrawRubyAnnotatedText
(
g_seuratFont,
fontSize,
clipRectMax.x - clipRectMin.x,
{ textX, textY },
{ textX, textY - scrollOffset },
5.0f,
desc.c_str(),
[=](const char* str, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, fontSize, pos, IM_COL32(255, 255, 255, 255), str);
DrawTextBasic(g_seuratFont, fontSize, pos, IM_COL32_WHITE, str);
},
[=](const char* str, float size, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, size, pos, IM_COL32(255, 255, 255, 255), str);
DrawTextBasic(g_seuratFont, size, pos, IM_COL32_WHITE, str);
}
);
ResetMarqueeFade();
drawList->PopClipRect();
// Reset parameters on new selected row.
if (ImGui::GetTime() - g_rowSelectionTime <= 0.0f)
{
isScrolling = false;
scrollOffset = 0.0f;
scrollTimer = 0.0f;
}
}
ResetProceduralOrigin();