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) 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 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 *= minAlpha;
color.a *= maxAlpha; color.a *= maxAlpha;
} }
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE) 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); float maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y);
color.a *= minAlpha; color.a *= minAlpha;

View file

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

View file

@ -42,7 +42,9 @@ void SetOrigin(ImVec2 origin);
void SetScale(ImVec2 scale); void SetScale(ImVec2 scale);
void SetTextSkew(float yCenter, float skewScale); void SetTextSkew(float yCenter, float skewScale);
void ResetTextSkew(); void ResetTextSkew();
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleLeft, float fadeScaleRight);
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale); 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 SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale);
void ResetMarqueeFade(); void ResetMarqueeFade();
void SetOutline(float outline); void SetOutline(float outline);

View file

@ -292,9 +292,9 @@ void MessageWindow::Draw()
if (Config::Language == ELanguage::Japanese) if (Config::Language == ELanguage::Japanese)
{ {
textMarginX -= Scale(2.5f); 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(); 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 INFO_NARROW_GRID_COUNT = 34.0f;
static constexpr float PADDING_NARROW_GRID_COUNT = 1.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 constexpr int32_t g_categoryCount = 4;
static int32_t g_categoryIndex; static int32_t g_categoryIndex;
static ImVec2 g_categoryAnimMin; static ImVec2 g_categoryAnimMin;
@ -1404,7 +1406,7 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
} }
else else
{ {
// Specialised description for resolution scale // Specialised description for resolution scale.
if (g_selectedItem == &Config::ResolutionScale) if (g_selectedItem == &Config::ResolutionScale)
{ {
char buf[100]; char buf[100];
@ -1417,54 +1419,136 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
desc = buf; 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 fontSize = Scale(28.0f);
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER; auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
// Extra padding between the start of the description text and the bottom of the thumbnail /* Extra padding between the start
float offsetY = Scale(24.0f); of the description text and the
bottom of the thumbnail. */
float textX = clipRectMin.x - Scale(0.5f); auto offsetY = Scale(24.0f);
float textY = thumbnailMax.y + offsetY;
auto textX = clipRectMin.x - Scale(0.5f);
auto textY = thumbnailMax.y + offsetY;
if (Config::Language == ELanguage::Japanese) 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); 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.x -= annotationFontSize;
clipRectMin.y -= annotationFontSize;
clipRectMax.x += annotationFontSize; clipRectMax.x += annotationFontSize;
clipRectMax.y += annotationFontSize;
textY += annotationFontSize; textY += annotationFontSize;
} }
auto textSize = MeasureCentredParagraph(g_seuratFont, fontSize, clipRectMax.x - clipRectMin.x, 5.0f, desc.c_str());
drawList->PushClipRect(clipRectMin, clipRectMax, false); 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 DrawRubyAnnotatedText
( (
g_seuratFont, g_seuratFont,
fontSize, fontSize,
clipRectMax.x - clipRectMin.x, clipRectMax.x - clipRectMin.x,
{ textX, textY }, { textX, textY - scrollOffset },
5.0f, 5.0f,
desc.c_str(), desc.c_str(),
[=](const char* str, ImVec2 pos) [=](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) [=](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(); drawList->PopClipRect();
// Reset parameters on new selected row.
if (ImGui::GetTime() - g_rowSelectionTime <= 0.0f)
{
isScrolling = false;
scrollOffset = 0.0f;
scrollTimer = 0.0f;
}
} }
ResetProceduralOrigin(); ResetProceduralOrigin();