diff --git a/UnleashedRecomp/gpu/shader/imgui_ps.hlsl b/UnleashedRecomp/gpu/shader/imgui_ps.hlsl index 11069c4..a61c4d1 100644 --- a/UnleashedRecomp/gpu/shader/imgui_ps.hlsl +++ b/UnleashedRecomp/gpu/shader/imgui_ps.hlsl @@ -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; diff --git a/UnleashedRecomp/ui/imgui_utils.cpp b/UnleashedRecomp/ui/imgui_utils.cpp index 5e9ac0e..9f7e276 100644 --- a/UnleashedRecomp/ui/imgui_utils.cpp +++ b/UnleashedRecomp/ui/imgui_utils.cpp @@ -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> RemoveRubyAnnotations(const char* input) { +std::pair> RemoveRubyAnnotations(const char* input) +{ std::string output; std::map rubyMap; std::string currentMain, currentRuby; @@ -400,7 +411,8 @@ std::pair> RemoveRubyAnnotations return { output, rubyMap }; } -std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std::map& rubyMap) { +std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std::map& 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 Split(const char* strStart, const ImFont *font, float fontSize, float maxWidth) +std::vector Split(const char* strStart, const ImFont* font, float fontSize, float maxWidth) { if (!strStart) return {}; @@ -445,6 +457,7 @@ std::vector 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 RemoveAnnotationFromParagraph(const std::vector& 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 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 drawMethod, std::function 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; - } } } diff --git a/UnleashedRecomp/ui/imgui_utils.h b/UnleashedRecomp/ui/imgui_utils.h index 8d669f2..aa15825 100644 --- a/UnleashedRecomp/ui/imgui_utils.h +++ b/UnleashedRecomp/ui/imgui_utils.h @@ -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); diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp index 8e562c7..fe05b8b 100644 --- a/UnleashedRecomp/ui/message_window.cpp +++ b/UnleashedRecomp/ui/message_window.cpp @@ -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(); diff --git a/UnleashedRecomp/ui/options_menu.cpp b/UnleashedRecomp/ui/options_menu.cpp index dadf018..c4e6d5e 100644 --- a/UnleashedRecomp/ui/options_menu.cpp +++ b/UnleashedRecomp/ui/options_menu.cpp @@ -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();