From 34fdbafe853d52f3c5dbeea6e6ad6eda3e32abc3 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 1 Mar 2025 16:00:03 +1000 Subject: [PATCH] FullscreenUI: Add drop shadows to most text Improves readability with custom backgrounds. --- src/core/achievements.cpp | 155 +++++++--------- src/core/fullscreen_ui.cpp | 70 +++---- src/util/imgui_fullscreen.cpp | 341 ++++++++++++++++------------------ src/util/imgui_fullscreen.h | 17 ++ 4 files changed, 277 insertions(+), 306 deletions(-) diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index 99803c52e..561f05e58 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -2584,6 +2584,7 @@ bool Achievements::PrepareAchievementsWindow() void Achievements::DrawAchievementsWindow() { using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::RenderShadowedTextClipped; using ImGuiFullscreen::UIStyle; auto lock = Achievements::GetLock(); @@ -2602,6 +2603,7 @@ void Achievements::DrawAchievementsWindow() const ImVec4 background = ImGuiFullscreen::ModAlpha(UIStyle.BackgroundColor, alpha); const ImVec4 heading_background = ImGuiFullscreen::ModAlpha(UIStyle.BackgroundColor, heading_alpha); const ImVec2 display_size = ImGui::GetIO().DisplaySize; + const u32 text_color = ImGui::GetColorU32(ImGuiCol_Text); const float heading_height = LayoutScale(heading_height_unscaled); bool close_window = false; @@ -2651,10 +2653,8 @@ void Achievements::DrawAchievementsWindow() top += UIStyle.LargeFont->FontSize + spacing; - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), - &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, text.c_str(), text.end_ptr(), + nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb); const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize)); if (s_state.game_summary.num_core_achievements > 0) @@ -2679,10 +2679,8 @@ void Achievements::DrawAchievementsWindow() top += UIStyle.MediumFont->FontSize + spacing; - ImGui::PushFont(UIStyle.MediumFont); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, text.c_str(), text.end_ptr(), nullptr, - ImVec2(0.0f, 0.0f), &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, text_color, text.c_str(), + text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); if (s_state.game_summary.num_core_achievements > 0) { @@ -2778,6 +2776,7 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) { using ImGuiFullscreen::LayoutScale; using ImGuiFullscreen::LayoutUnscale; + using ImGuiFullscreen::RenderShadowedTextClipped; using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; @@ -2785,6 +2784,7 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) static constexpr float progress_spacing_unscaled = 5.0f; const float spacing = ImGuiFullscreen::LayoutScale(4.0f); + const u32 text_color = ImGui::GetColorU32(ImGuiCol_Text); const bool is_unlocked = (cheevo->state == RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED); const std::string_view measured_progress(cheevo->measured_progress); @@ -2876,26 +2876,24 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f); const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(points_start, midpoint)); - const ImRect summary_bb(ImVec2(text_start_x, midpoint), - ImVec2(points_start, midpoint + UIStyle.MediumFont->FontSize + extra_summary_height)); + const ImRect summary_bb(ImVec2(text_start_x, midpoint), ImVec2(points_start, midpoint + summary_text_size.y)); const ImRect points_bb(ImVec2(points_start, midpoint), bb.Max); const ImRect lock_bb(ImVec2(points_template_start + ((points_template_size.x - right_icon_size.x) * 0.5f), bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, cheevo->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::RenderTextClipped(lock_bb.Min, lock_bb.Max, right_icon_text, nullptr, &right_icon_size, ImVec2(0.0f, 0.0f), - &lock_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, cheevo->title, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &title_bb); + RenderShadowedTextClipped(UIStyle.LargeFont, lock_bb.Min, lock_bb.Max, text_color, right_icon_text, nullptr, + &right_icon_size, ImVec2(0.0f, 0.0f), 0.0f, &lock_bb); - ImGui::PushFont(UIStyle.MediumFont); if (cheevo->description && summary_length > 0) { - ImGui::RenderTextWrapped(summary_bb.Min, cheevo->description, cheevo->description + summary_length, - summary_wrap_width); + RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, text_color, cheevo->description, + cheevo->description + summary_length, &summary_text_size, ImVec2(0.0f, 0.0f), + summary_wrap_width, &summary_bb); } - ImGui::RenderTextClipped(points_bb.Min, points_bb.Max, text.c_str(), text.end_ptr(), &points_size, ImVec2(0.0f, 0.0f), - &points_bb); + RenderShadowedTextClipped(UIStyle.MediumFont, points_bb.Min, points_bb.Max, text_color, text.c_str(), text.end_ptr(), + &points_size, ImVec2(0.0f, 0.0f), 0.0f, &points_bb); if (is_unlocked) { @@ -2904,8 +2902,8 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) text.format(TRANSLATE_FS("Achievements", "Unlocked: {}"), date); const ImRect unlock_bb(summary_bb.Min.x, summary_bb.Max.y + spacing, summary_bb.Max.x, bb.Max.y); - ImGui::RenderTextClipped(unlock_bb.Min, unlock_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), - &unlock_bb); + RenderShadowedTextClipped(UIStyle.MediumFont, unlock_bb.Min, unlock_bb.Max, text_color, text.c_str(), + text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &unlock_bb); } else if (is_measured) { @@ -2934,8 +2932,6 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) INFO_LOG("Opening achievement details: {}", url); Host::OpenURL(url); } - - ImGui::PopFont(); } bool Achievements::PrepareLeaderboardsWindow() @@ -2960,6 +2956,7 @@ bool Achievements::PrepareLeaderboardsWindow() void Achievements::DrawLeaderboardsWindow() { using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::RenderShadowedTextClipped; using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; @@ -2982,6 +2979,7 @@ void Achievements::DrawLeaderboardsWindow() const ImVec4 background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIStyle.BackgroundColor, alpha); const ImVec4 heading_background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIStyle.BackgroundColor, heading_alpha); const ImVec2 display_size = ImGui::GetIO().DisplaySize; + const u32 text_color = ImGui::GetColorU32(ImGuiCol_Text); const float padding = LayoutScale(10.0f); const float spacing = LayoutScale(10.0f); const float spacing_small = spacing / 2.0f; @@ -3062,10 +3060,8 @@ void Achievements::DrawLeaderboardsWindow() top += UIStyle.LargeFont->FontSize + spacing; - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), - &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, text.c_str(), text.end_ptr(), + nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb); if (is_leaderboard_open) { @@ -3074,10 +3070,8 @@ void Achievements::DrawLeaderboardsWindow() top += UIStyle.LargeFont->FontSize + spacing_small; - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(subtitle_bb.Min, subtitle_bb.Max, text.c_str(), text.end_ptr(), nullptr, - ImVec2(0.0f, 0.0f), &subtitle_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, subtitle_bb.Min, subtitle_bb.Max, text_color, text.c_str(), + text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &subtitle_bb); text.assign(s_state.open_leaderboard->description); } @@ -3092,24 +3086,21 @@ void Achievements::DrawLeaderboardsWindow() const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize)); top += UIStyle.MediumFont->FontSize + spacing_small; - ImGui::PushFont(UIStyle.MediumFont); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, text.c_str(), text.end_ptr(), nullptr, - ImVec2(0.0f, 0.0f), &summary_bb); + RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, text_color, text.c_str(), + text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); if (!is_leaderboard_open && !Achievements::IsHardcoreModeActive()) { const ImRect hardcore_warning_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize)); top += UIStyle.MediumFont->FontSize + spacing_small; - ImGui::RenderTextClipped( - hardcore_warning_bb.Min, hardcore_warning_bb.Max, + RenderShadowedTextClipped( + UIStyle.MediumFont, hardcore_warning_bb.Min, hardcore_warning_bb.Max, text_color, TRANSLATE("Achievements", "Submitting scores is disabled because hardcore mode is off. Leaderboards are read-only."), - nullptr, nullptr, ImVec2(0.0f, 0.0f), &hardcore_warning_bb); + nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &hardcore_warning_bb); } - ImGui::PopFont(); - if (is_leaderboard_open) { const float tab_width = (ImGui::GetWindowWidth() / ImGuiFullscreen::UIStyle.LayoutScale) * 0.5f; @@ -3149,19 +3140,23 @@ void Achievements::DrawLeaderboardsWindow() &visible, &hovered, &bb.Min, &bb.Max, 0, alpha); UNREFERENCED_VARIABLE(pressed); + // add padding from the window below, don't want the menu items butted up against the edge + bb.Min.x += LayoutScale(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING); + bb.Max.x -= LayoutScale(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING); + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); float text_start_x = bb.Min.x + LayoutScale(15.0f) + padding; - ImGui::PushFont(UIStyle.LargeFont); - const ImRect rank_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::RenderTextClipped(rank_bb.Min, rank_bb.Max, TRANSLATE("Achievements", "Rank"), nullptr, nullptr, - ImVec2(0.0f, 0.0f), &rank_bb); + RenderShadowedTextClipped(UIStyle.LargeFont, rank_bb.Min, rank_bb.Max, text_color, + TRANSLATE("Achievements", "Rank"), nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &rank_bb); text_start_x += rank_column_width + column_spacing; const ImRect user_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::RenderTextClipped(user_bb.Min, user_bb.Max, TRANSLATE("Achievements", "Name"), nullptr, nullptr, - ImVec2(0.0f, 0.0f), &user_bb); + RenderShadowedTextClipped(UIStyle.LargeFont, user_bb.Min, user_bb.Max, text_color, + TRANSLATE("Achievements", "Name"), nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &user_bb); text_start_x += name_column_width + column_spacing; static const char* value_headings[NUM_RC_CLIENT_LEADERBOARD_FORMATS] = { @@ -3171,19 +3166,18 @@ void Achievements::DrawLeaderboardsWindow() }; const ImRect score_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::RenderTextClipped( - score_bb.Min, score_bb.Max, + RenderShadowedTextClipped( + UIStyle.LargeFont, score_bb.Min, score_bb.Max, text_color, Host::TranslateToCString( "Achievements", value_headings[std::min(s_state.open_leaderboard->format, NUM_RC_CLIENT_LEADERBOARD_FORMATS - 1)]), - nullptr, nullptr, ImVec2(0.0f, 0.0f), &score_bb); + nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &score_bb); text_start_x += time_column_width + column_spacing; const ImRect date_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::RenderTextClipped(date_bb.Min, date_bb.Max, TRANSLATE("Achievements", "Date Submitted"), nullptr, - nullptr, ImVec2(0.0f, 0.0f), &date_bb); - - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, date_bb.Min, date_bb.Max, text_color, + TRANSLATE("Achievements", "Date Submitted"), nullptr, nullptr, ImVec2(0.0f, 0.0f), + 0.0f, &date_bb); const float line_thickness = LayoutScale(1.0f); const float line_padding = LayoutScale(5.0f); @@ -3260,15 +3254,11 @@ void Achievements::DrawLeaderboardsWindow() } else { - ImGui::PushFont(UIStyle.LargeFont); - const ImVec2 pos_min(0.0f, heading_height); const ImVec2 pos_max(display_size.x, display_size.y); - ImGui::RenderTextClipped(pos_min, pos_max, - TRANSLATE("Achievements", "Downloading leaderboard data, please wait..."), nullptr, - nullptr, ImVec2(0.5f, 0.5f)); - - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, pos_min, pos_max, text_color, + TRANSLATE("Achievements", "Downloading leaderboard data, please wait..."), nullptr, + nullptr, ImVec2(0.5f, 0.5f), 0.0f); } } else @@ -3292,10 +3282,9 @@ void Achievements::DrawLeaderboardsWindow() const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, TRANSLATE("Achievements", "Loading..."), nullptr, - nullptr, ImVec2(0, 0), &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, + TRANSLATE("Achievements", "Loading..."), nullptr, nullptr, ImVec2(0, 0), 0.0f, + &title_bb); if (!s_state.leaderboard_fetch_handle) FetchNextLeaderboardEntries(); @@ -3329,6 +3318,7 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent float column_spacing) { using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::RenderShadowedTextClipped; using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; @@ -3347,14 +3337,11 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent text.format("{}", entry.rank); - ImGui::PushFont(UIStyle.LargeFont); - - if (is_self) - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 242, 0, 255)); + const u32 text_color = is_self ? IM_COL32(255, 242, 0, 255) : ImGui::GetColorU32(ImGuiCol_Text); const ImRect rank_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::RenderTextClipped(rank_bb.Min, rank_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), - &rank_bb); + RenderShadowedTextClipped(UIStyle.LargeFont, rank_bb.Min, rank_bb.Max, text_color, text.c_str(), text.end_ptr(), + nullptr, ImVec2(0.0f, 0.0f), 0.0f, &rank_bb); text_start_x += rank_column_width + column_spacing; const float icon_size = bb.Max.y - bb.Min.y; @@ -3383,23 +3370,20 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent } const ImRect user_bb(ImVec2(text_start_x + column_spacing + icon_size, bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::RenderTextClipped(user_bb.Min, user_bb.Max, entry.user, nullptr, nullptr, ImVec2(0.0f, 0.0f), &user_bb); + RenderShadowedTextClipped(UIStyle.LargeFont, user_bb.Min, user_bb.Max, text_color, entry.user, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &user_bb); text_start_x += name_column_width + column_spacing; const ImRect score_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::RenderTextClipped(score_bb.Min, score_bb.Max, entry.display, nullptr, nullptr, ImVec2(0.0f, 0.0f), &score_bb); + RenderShadowedTextClipped(UIStyle.LargeFont, score_bb.Min, score_bb.Max, text_color, entry.display, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &score_bb); text_start_x += time_column_width + column_spacing; const ImRect time_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); SmallString submit_time; FullscreenUI::TimeToPrintableString(&submit_time, entry.submitted); - ImGui::RenderTextClipped(time_bb.Min, time_bb.Max, submit_time.c_str(), submit_time.end_ptr(), nullptr, - ImVec2(0.0f, 0.0f), &time_bb); - - if (is_self) - ImGui::PopStyleColor(); - - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, time_bb.Min, time_bb.Max, text_color, submit_time.c_str(), + submit_time.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &time_bb); if (pressed) { @@ -3411,6 +3395,7 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent void Achievements::DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboard) { using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::RenderShadowedTextClipped; using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; @@ -3427,19 +3412,17 @@ void Achievements::DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboar const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); const float text_start_x = bb.Min.x + LayoutScale(15.0f); + const u32 text_color = ImGui::GetColorU32(ImGuiCol_Text); const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max); - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, lboard->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, lboard->title, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &title_bb); if (lboard->description && lboard->description[0] != '\0') { - ImGui::PushFont(UIStyle.MediumFont); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, lboard->description, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, text_color, lboard->description, + nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } if (pressed) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 4f06a8d78..96e7711b1 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -143,6 +143,7 @@ using ImGuiFullscreen::PopPrimaryColor; using ImGuiFullscreen::PushPrimaryColor; using ImGuiFullscreen::QueueResetFocus; using ImGuiFullscreen::RangeButton; +using ImGuiFullscreen::RenderShadowedTextClipped; using ImGuiFullscreen::ResetFocusHere; using ImGuiFullscreen::RightAlignNavButtons; using ImGuiFullscreen::SetFullscreenFooterText; @@ -1765,17 +1766,19 @@ void FullscreenUI::DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size) ImDrawList* const dl = ImGui::GetWindowDrawList(); SmallString heading_str; - ImGui::PushFont(heading_font); - ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); + const u32 text_color = ImGui::GetColorU32(UIStyle.PrimaryTextColor); // draw branding { const ImVec2 logo_pos = LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING); const ImVec2 logo_size = LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY); dl->AddImage(s_state.app_icon_texture.get(), logo_pos, logo_pos + logo_size); - dl->AddText(heading_font, heading_font->FontSize, - ImVec2(logo_pos.x + logo_size.x + LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING), logo_pos.y), - ImGui::GetColorU32(ImGuiCol_Text), "DuckStation"); + + const char* heading_text = "DuckStation"; + const ImVec2 text_size = heading_font->CalcTextSizeA(heading_font->FontSize, FLT_MAX, 0.0f, heading_text); + const ImVec2 text_pos = ImVec2(logo_pos.x + logo_size.x + LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING), logo_pos.y); + ImGuiFullscreen::RenderShadowedTextClipped(heading_font, text_pos, text_pos + text_size, text_color, heading_text, + nullptr, &text_size); } // draw time @@ -1786,7 +1789,8 @@ void FullscreenUI::DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size) const ImVec2 time_size = heading_font->CalcTextSizeA(heading_font->FontSize, FLT_MAX, 0.0f, "00:00"); time_pos = ImVec2(heading_size.x - LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING) - time_size.x, LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING)); - ImGui::RenderTextClipped(time_pos, time_pos + time_size, heading_str.c_str(), heading_str.end_ptr(), &time_size); + ImGuiFullscreen::RenderShadowedTextClipped(heading_font, time_pos, time_pos + time_size, text_color, + heading_str.c_str(), heading_str.end_ptr(), &time_size); } // draw achievements info @@ -1799,7 +1803,8 @@ void FullscreenUI::DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size) const ImVec2 name_size = heading_font->CalcTextSizeA(heading_font->FontSize, FLT_MAX, 0.0f, username); const ImVec2 name_pos = ImVec2(time_pos.x - name_size.x - LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING), time_pos.y); - ImGui::RenderTextClipped(name_pos, name_pos + name_size, username, nullptr, &name_size); + ImGuiFullscreen::RenderShadowedTextClipped(heading_font, name_pos, name_pos + name_size, text_color, username, + nullptr, &name_size); if (s_state.achievements_user_badge_path.empty()) [[unlikely]] s_state.achievements_user_badge_path = Achievements::GetLoggedInUserBadgePath(); @@ -1815,9 +1820,6 @@ void FullscreenUI::DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size) } } } - - ImGui::PopStyleColor(); - ImGui::PopFont(); } EndFullscreenWindow(); } @@ -7026,19 +7028,16 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) const ImVec2 title_pos(bb.Min.x, bb.Min.y + image_height + title_spacing); const ImRect title_bb(title_pos, ImVec2(bb.Max.x, title_pos.y + UIStyle.LargeFont->FontSize)); - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, entry.title.c_str(), nullptr, nullptr, ImVec2(0.0f, 0.0f), - &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, ImGui::GetColorU32(ImGuiCol_Text), + entry.title.c_str(), nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb); if (!entry.summary.empty()) { const ImVec2 summary_pos(bb.Min.x, title_pos.y + UIStyle.LargeFont->FontSize + summary_spacing); const ImRect summary_bb(summary_pos, ImVec2(bb.Max.x, summary_pos.y + UIStyle.MediumFont->FontSize)); - ImGui::PushFont(UIStyle.MediumFont); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, entry.summary.c_str(), nullptr, nullptr, - ImVec2(0.0f, 0.0f), &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, + ImGui::GetColorU32(ImGuiCol_Text), entry.summary.c_str(), nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } if (pressed) @@ -7489,6 +7488,8 @@ void FullscreenUI::DrawGameListWindow() void FullscreenUI::DrawGameList(const ImVec2& heading_size) { + static constexpr auto to_mb = [](s64 size) { return static_cast((size + 1048575) / 1048576); }; + if (!BeginFullscreenColumns(nullptr, heading_size.y, true, true)) { EndFullscreenColumns(); @@ -7515,6 +7516,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) BeginMenuButtons(); SmallString summary; + const u32 text_color = ImGui::GetColorU32(ImGuiCol_Text); for (const GameList::Entry* entry : s_state.game_list_sorted_entries) { @@ -7528,11 +7530,13 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) GPUTexture* cover_texture = GetGameListCover(entry, true); if (entry->serial.empty()) - summary.format("{} - ", Settings::GetDiscRegionDisplayName(entry->region)); + { + summary.format("{} | {} MB", Path::GetFileName(entry->path), to_mb(entry->file_size)); + } else - summary.format("{} - {} - ", entry->serial, Settings::GetDiscRegionDisplayName(entry->region)); - - summary.append(Path::GetFileName(entry->path)); + { + summary.format("{} | {} | {} MB", entry->serial, Path::GetFileName(entry->path), to_mb(entry->file_size)); + } const ImRect image_rect( CenterImage(ImRect(bb.Min, bb.Min + image_size), ImVec2(static_cast(cover_texture->GetWidth()), @@ -7546,17 +7550,14 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max); - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, entry->title.c_str(), - entry->title.c_str() + entry->title.size(), nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, entry->title.c_str(), + entry->title.c_str() + entry->title.size(), nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &title_bb); if (!summary.empty()) { - ImGui::PushFont(UIStyle.MediumFont); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary.c_str(), summary.end_ptr(), nullptr, - ImVec2(0.0f, 0.0f), &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, text_color, summary.c_str(), + summary.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } if (pressed) @@ -7604,6 +7605,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) static constexpr float field_margin_y = 4.0f; static constexpr float start_x = 50.0f; float text_y = info_top_margin + cover_size + info_top_margin; + float text_width; PushPrimaryColor(); @@ -7700,7 +7702,6 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) // size if (selected_entry->file_size >= 0) { - const auto to_mb = [](s64 size) { return static_cast((size + 1048575) / 1048576); }; ImGui::Text(FSUI_CSTR("File Size: %u MB (%u MB on disk)"), to_mb(selected_entry->file_size), to_mb(selected_entry->uncompressed_size)); } @@ -7766,6 +7767,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) const u32 grid_count_x = static_cast(std::floor(ImGui::GetWindowWidth() / item_width_with_spacing)); const float start_x = (static_cast(ImGui::GetWindowWidth()) - (item_width_with_spacing * static_cast(grid_count_x))) * 0.5f; + const u32 text_color = ImGui::GetColorU32(ImGuiCol_Text); SmallString draw_title; @@ -7822,10 +7824,8 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) const std::string_view title( std::string_view(entry->title).substr(0, (entry->title.length() > 31) ? 31 : std::string_view::npos)); draw_title.format("{}{}", title, (title.length() == entry->title.length()) ? "" : "..."); - ImGui::PushFont(UIStyle.MediumFont); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, draw_title.c_str(), draw_title.end_ptr(), nullptr, - ImVec2(0.5f, 0.0f), &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(UIStyle.MediumFont, title_bb.Min, title_bb.Max, text_color, draw_title.c_str(), + draw_title.end_ptr(), nullptr, ImVec2(0.5f, 0.0f), 0.0f, &title_bb); if (pressed) { diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp index 9071794e9..241a1a462 100644 --- a/src/util/imgui_fullscreen.cpp +++ b/src/util/imgui_fullscreen.cpp @@ -1004,8 +1004,12 @@ void ImGuiFullscreen::DrawFullscreenFooter() return; } + static constexpr float TRANSITION_TIME = 0.15f; + const float padding = LayoutScale(LAYOUT_FOOTER_PADDING); const float height = LayoutScale(LAYOUT_FOOTER_HEIGHT); + const ImVec2 shadow_offset = LayoutScale(LAYOUT_SHADOW_OFFSET, LAYOUT_SHADOW_OFFSET); + const u32 text_color = ImGui::GetColorU32(UIStyle.PrimaryTextColor); ImDrawList* dl = ImGui::GetForegroundDrawList(); dl->AddRectFilled(ImVec2(0.0f, io.DisplaySize.y - height), io.DisplaySize, @@ -1019,23 +1023,24 @@ void ImGuiFullscreen::DrawFullscreenFooter() s_state.fullscreen_footer_text != s_state.last_fullscreen_footer_text) { if (s_state.fullscreen_text_change_time == 0.0f) - s_state.fullscreen_text_change_time = 0.15f; + s_state.fullscreen_text_change_time = TRANSITION_TIME; else s_state.fullscreen_text_change_time = std::max(s_state.fullscreen_text_change_time - io.DeltaTime, 0.0f); if (s_state.fullscreen_text_change_time == 0.0f) s_state.last_fullscreen_footer_text = s_state.fullscreen_footer_text; - prev_opacity = s_state.fullscreen_text_change_time * (1.0f / 0.15f); + prev_opacity = s_state.fullscreen_text_change_time * (1.0f / TRANSITION_TIME); if (prev_opacity > 0.0f) { const ImVec2 text_size = font->CalcTextSizeA(font->FontSize, max_width, 0.0f, s_state.last_fullscreen_footer_text.c_str(), s_state.last_fullscreen_footer_text.end_ptr()); - dl->AddText(font, font->FontSize, - ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding), - ImGui::GetColorU32(ImVec4(UIStyle.PrimaryTextColor.x, UIStyle.PrimaryTextColor.y, - UIStyle.PrimaryTextColor.z, prev_opacity)), + const ImVec2 text_pos = + ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding); + dl->AddText(font, font->FontSize, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, prev_opacity), + s_state.last_fullscreen_footer_text.c_str(), s_state.last_fullscreen_footer_text.end_ptr()); + dl->AddText(font, font->FontSize, text_pos, ModAlpha(text_color, prev_opacity), s_state.last_fullscreen_footer_text.c_str(), s_state.last_fullscreen_footer_text.end_ptr()); } } @@ -1049,11 +1054,13 @@ void ImGuiFullscreen::DrawFullscreenFooter() const ImVec2 text_size = font->CalcTextSizeA(font->FontSize, max_width, 0.0f, s_state.fullscreen_footer_text.c_str(), s_state.fullscreen_footer_text.end_ptr()); - dl->AddText(font, font->FontSize, - ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding), - ImGui::GetColorU32(ImVec4(UIStyle.PrimaryTextColor.x, UIStyle.PrimaryTextColor.y, - UIStyle.PrimaryTextColor.z, 1.0f - prev_opacity)), + const ImVec2 text_pos = + ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding); + const float opacity = 1.0f - prev_opacity; + dl->AddText(font, font->FontSize, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, opacity), s_state.fullscreen_footer_text.c_str(), s_state.fullscreen_footer_text.end_ptr()); + dl->AddText(font, font->FontSize, text_pos, ModAlpha(text_color, opacity), s_state.fullscreen_footer_text.c_str(), + s_state.fullscreen_footer_text.end_ptr()); } // for next frame @@ -1290,6 +1297,54 @@ void ImGuiFullscreen::ResetMenuButtonFrame() s_state.has_hovered_menu_item = false; } +void ImGuiFullscreen::RenderShadowedTextClipped(ImFont* font, const ImVec2& pos_min, const ImVec2& pos_max, u32 color, + const char* text, const char* text_end, + const ImVec2* text_size_if_known /* = nullptr */, + const ImVec2& align /* = ImVec2(0, 0)*/, float wrap_width /* = 0.0f*/, + const ImRect* clip_rect /* = nullptr */) +{ + const char* text_display_end = ImGui::FindRenderedTextEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + // Perform CPU side clipping for single clipped element to avoid using scissor state + ImVec2 pos = pos_min; + const ImVec2 text_size = text_size_if_known ? + *text_size_if_known : + font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, text, text_display_end, nullptr); + + const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; + const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; + bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); + if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min + need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); + + // Align whole block. We should defer that to the better rendering function when we'll have support for individual + // line alignment. + if (align.x > 0.0f) + pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); + if (align.y > 0.0f) + pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); + + ImDrawList* const dl = ImGui::GetWindowDrawList(); + + // Render + if (need_clipping) + { + ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); + dl->AddText(font, font->FontSize, pos + LayoutScale(LAYOUT_SHADOW_OFFSET, LAYOUT_SHADOW_OFFSET), + UIStyle.ShadowColor, text, text_display_end, wrap_width, &fine_clip_rect); + dl->AddText(font, font->FontSize, pos, color, text, text_display_end, wrap_width, &fine_clip_rect); + } + else + { + dl->AddText(font, font->FontSize, pos + LayoutScale(LAYOUT_SHADOW_OFFSET, LAYOUT_SHADOW_OFFSET), + UIStyle.ShadowColor, text, text_display_end, wrap_width, nullptr); + dl->AddText(font, font->FontSize, pos, color, text, text_display_end, wrap_width, nullptr); + } +} + void ImGuiFullscreen::MenuHeading(const char* title, bool draw_line /*= true*/) { const float line_thickness = draw_line ? LayoutScale(1.0f) : 0.0f; @@ -1301,16 +1356,16 @@ void ImGuiFullscreen::MenuHeading(const char* title, bool draw_line /*= true*/) if (!visible) return; - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); - ImGui::PopFont(); - ImGui::PopStyleColor(); + RenderShadowedTextClipped(UIStyle.LargeFont, bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_TextDisabled), title, + nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb); if (draw_line) { const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding); const ImVec2 line_end(bb.Max.x, line_start.y); + const ImVec2 shadow_offset = LayoutScale(LAYOUT_SHADOW_OFFSET, LAYOUT_SHADOW_OFFSET); + ImGui::GetWindowDrawList()->AddLine(line_start + shadow_offset, line_end + shadow_offset, UIStyle.ShadowColor, + line_thickness); ImGui::GetWindowDrawList()->AddLine(line_start, line_end, ImGui::GetColorU32(ImGuiCol_TextDisabled), line_thickness); } @@ -1328,27 +1383,26 @@ bool ImGuiFullscreen::MenuHeadingButton(const char* title, const char* value /*= if (!visible) return false; - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - ImGui::PushFont(UIStyle.LargeFont); - ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(UIStyle.LargeFont, bb.Min, bb.Max, color, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &bb); if (value) { const ImVec2 value_size( UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits::max(), 0.0f, value)); const ImRect value_bb(ImVec2(bb.Max.x - value_size.x, bb.Min.y), ImVec2(bb.Max.x, bb.Max.y)); - ImGui::RenderTextClipped(value_bb.Min, value_bb.Max, value, nullptr, nullptr, ImVec2(0.0f, 0.0f), &value_bb); + RenderShadowedTextClipped(UIStyle.LargeFont, value_bb.Min, value_bb.Max, color, value, nullptr, &value_size, + ImVec2(0.0f, 0.0f), 0.0f, &value_bb); } - ImGui::PopFont(); - if (!enabled) - ImGui::PopStyleColor(); - if (draw_line) { const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding); const ImVec2 line_end(bb.Max.x, line_start.y); + const ImVec2 shadow_offset = LayoutScale(LAYOUT_SHADOW_OFFSET, LAYOUT_SHADOW_OFFSET); + ImGui::GetWindowDrawList()->AddLine(line_start + shadow_offset, line_end + shadow_offset, UIStyle.ShadowColor, + line_thickness); ImGui::GetWindowDrawList()->AddLine(line_start, line_end, ImGui::GetColorU32(ImGuiCol_TextDisabled), line_thickness); } @@ -1391,26 +1445,18 @@ bool ImGuiFullscreen::ActiveButtonWithRightText(const char* title, const char* r return false; const ImRect title_bb(bb.GetTL(), bb.GetBR()); + const u32 text_color = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, text_color, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), + 0.0f, &title_bb); if (right_title && *right_title) { const ImVec2 right_text_size = font->CalcTextSizeA(font->FontSize, title_bb.GetWidth(), 0.0f, right_title); const ImVec2 right_text_start = ImVec2(title_bb.Max.x - right_text_size.x, title_bb.Min.y); - ImGui::RenderTextClipped(right_text_start, title_bb.Max, right_title, nullptr, &right_text_size, ImVec2(0.0f, 0.0f), - &title_bb); + RenderShadowedTextClipped(font, right_text_start, title_bb.Max, text_color, right_title, nullptr, &right_text_size, + ImVec2(0.0f, 0.0f), 0.0f, &title_bb); } - - ImGui::PopFont(); - - if (!enabled) - ImGui::PopStyleColor(); - s_state.menu_button_index++; return pressed; } @@ -1427,25 +1473,16 @@ bool ImGuiFullscreen::MenuButton(const char* title, const char* summary, bool en const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f); const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max); - - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::PopFont(); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &title_bb); if (summary) { - ImGui::PushFont(summary_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, color, summary, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } - if (!enabled) - ImGui::PopStyleColor(); - s_state.menu_button_index++; return pressed; } @@ -1462,16 +1499,9 @@ bool ImGuiFullscreen::MenuButtonWithoutSummary(const char* title, bool enabled, const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f); const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max); - - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, text_align, &title_bb); - ImGui::PopFont(); - - if (!enabled) - ImGui::PopStyleColor(); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, text_align, 0.0f, + &title_bb); s_state.menu_button_index++; return pressed; @@ -1495,25 +1525,16 @@ bool ImGuiFullscreen::MenuImageButton(const char* title, const char* summary, Im const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f); const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max); - - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(title_font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::PopFont(); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(title_font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), + 0.0f, &title_bb); if (summary) { - ImGui::PushFont(summary_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, color, summary, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } - if (!enabled) - ImGui::PopStyleColor(); - s_state.menu_button_index++; return pressed; } @@ -1594,16 +1615,8 @@ bool ImGuiFullscreen::FloatingButton(const char* text, float x, float y, float w bb.Min += padding; bb.Max -= padding; - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(bb.Min, bb.Max, text, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); - ImGui::PopFont(); - - if (!enabled) - ImGui::PopStyleColor(); - + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(font, bb.Min, bb.Max, color, text, nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb); return pressed; } @@ -1620,24 +1633,17 @@ bool ImGuiFullscreen::ToggleButton(const char* title, const char* summary, bool* const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max); - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, ImVec2(0.0, 0.0f), 0.0f, + &title_bb); if (summary) { - ImGui::PushFont(summary_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, color, summary, nullptr, nullptr, + ImVec2(0.0, 0.0f), 0.0f, &summary_bb); } - if (!enabled) - ImGui::PopStyleColor(); - const float toggle_width = LayoutScale(50.0f); const float toggle_height = LayoutScale(25.0f); const float toggle_x = LayoutScale(8.0f); @@ -1694,24 +1700,17 @@ bool ImGuiFullscreen::ThreeWayToggleButton(const char* title, const char* summar const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max); - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, ImVec2(0.0, 0.0f), 0.0f, + &title_bb); if (summary) { - ImGui::PushFont(summary_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, color, summary, nullptr, nullptr, + ImVec2(0.0, 0.0f), 0.0f, &summary_bb); } - if (!enabled) - ImGui::PopStyleColor(); - const float toggle_width = LayoutScale(50.0f); const float toggle_height = LayoutScale(25.0f); const float toggle_x = LayoutScale(8.0f); @@ -1773,32 +1772,25 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, s32* v return false; const SmallString value_text = SmallString::from_sprintf(format, *value); - const ImVec2 value_size(ImGui::CalcTextSize(value_text.c_str())); + const ImVec2 value_size = + font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, value_text.c_str(), value_text.end_ptr()); const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f); const float text_end = bb.Max.x - value_size.x; const ImRect title_bb(bb.Min, ImVec2(text_end, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), ImVec2(text_end, bb.Max.y)); - - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::RenderTextClipped(bb.Min, bb.Max, value_text.c_str(), nullptr, nullptr, ImVec2(1.0f, 0.5f), &bb); - ImGui::PopFont(); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &title_bb); + RenderShadowedTextClipped(font, bb.Min, bb.Max, color, value_text.c_str(), value_text.end_ptr(), &value_size, + ImVec2(1.0f, 0.5f), 0.0f, &bb); if (summary) { - ImGui::PushFont(summary_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, color, summary, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } - if (!enabled) - ImGui::PopStyleColor(); - if (pressed) ImGui::OpenPopup(title); @@ -1839,32 +1831,25 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, float* return false; const SmallString value_text = SmallString::from_sprintf(format, *value); - const ImVec2 value_size(ImGui::CalcTextSize(value_text.c_str())); + const ImVec2 value_size = + font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, value_text.c_str(), value_text.end_ptr()); const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f); const float text_end = bb.Max.x - value_size.x; const ImRect title_bb(bb.Min, ImVec2(text_end, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), ImVec2(text_end, bb.Max.y)); - - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::RenderTextClipped(bb.Min, bb.Max, value_text.c_str(), nullptr, nullptr, ImVec2(1.0f, 0.5f), &bb); - ImGui::PopFont(); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &title_bb); + RenderShadowedTextClipped(font, bb.Min, bb.Max, color, value_text.c_str(), value_text.end_ptr(), &value_size, + ImVec2(1.0f, 0.5f), 0.0f, &bb); if (summary) { - ImGui::PushFont(summary_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, color, summary, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } - if (!enabled) - ImGui::PopStyleColor(); - if (pressed) ImGui::OpenPopup(title); @@ -1907,26 +1892,17 @@ bool ImGuiFullscreen::MenuButtonWithValue(const char* title, const char* summary const float text_end = bb.Max.x - value_size.x; const ImRect title_bb(bb.Min, ImVec2(text_end, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), ImVec2(text_end, bb.Max.y)); - - if (!enabled) - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::RenderTextClipped(bb.Min, bb.Max, value, nullptr, nullptr, ImVec2(1.0f, 0.5f), &bb); - ImGui::PopFont(); + const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, color, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, + &title_bb); + RenderShadowedTextClipped(font, bb.Min, bb.Max, color, value, nullptr, nullptr, ImVec2(1.0f, 0.5f), 0.0f, &bb); if (summary) { - ImGui::PushFont(summary_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary, nullptr, nullptr, ImVec2(0.0f, 0.0f), - &summary_bb); - ImGui::PopFont(); + RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, color, summary, nullptr, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); } - if (!enabled) - ImGui::PopStyleColor(); - return pressed; } @@ -2023,9 +1999,8 @@ void ImGuiFullscreen::NavTitle(const char* title, float height /*= LAYOUT_MENU_B bb.Min.y += style.FramePadding.y; bb.Max.y -= style.FramePadding.y; - ImGui::PushFont(font); - ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); - ImGui::PopFont(); + RenderShadowedTextClipped(font, bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Text), title, nullptr, &text_size, + ImVec2(0.0f, 0.0f), 0.0f, &bb); } void ImGuiFullscreen::RightAlignNavButtons(u32 num_items /*= 0*/, @@ -2098,15 +2073,10 @@ bool ImGuiFullscreen::NavButton(const char* title, bool is_active, bool enabled bb.Min += style.FramePadding; bb.Max -= style.FramePadding; - ImGui::PushStyleColor( - ImGuiCol_Text, - ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); - ImGui::PopFont(); - - ImGui::PopStyleColor(); + RenderShadowedTextClipped( + font, bb.Min, bb.Max, + ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered), title, + nullptr, &text_size, ImVec2(0.0f, 0.0f), 0.0f, &bb); return pressed; } @@ -2177,15 +2147,10 @@ bool ImGuiFullscreen::NavTab(const char* title, bool is_active, bool enabled /* bb.Min += pad; bb.Max -= pad; - ImGui::PushStyleColor( - ImGuiCol_Text, - ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered)); - - ImGui::PushFont(font); - ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); - ImGui::PopFont(); - - ImGui::PopStyleColor(); + RenderShadowedTextClipped( + font, bb.Min, bb.Max, + ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered), title, + nullptr, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb); return pressed; } @@ -2209,7 +2174,7 @@ bool ImGuiFullscreen::BeginHorizontalMenu(const char* name, const ImVec2& positi if (!BeginFullscreenWindow(position, size, name, bg_color, 0.0f, ImVec2())) return false; - ImGui::SetCursorPos(ImVec2((size.x - menu_width) * 0.5f, (size.y - menu_height) * 0.5f)); + ImGui::SetCursorPos(ImFloor(ImVec2((size.x - menu_width) * 0.5f, (size.y - menu_height) * 0.5f))); PrerenderMenuButtonBorder(); return true; @@ -2267,19 +2232,19 @@ bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, const char* title, co title_font->CalcTextSizeA(title_font->FontSize, std::numeric_limits::max(), avail_width, title); const ImVec2 title_pos = ImVec2(bb.Min.x + (avail_width - title_size.x) * 0.5f, icon_pos.y + icon_size + LayoutScale(10.0f)); - const ImVec4 title_bb = ImVec4(title_pos.x, title_pos.y, title_pos.x + title_size.x, title_pos.y + title_size.y); + const ImRect title_bb = ImRect(title_pos, title_pos + title_size); - dl->AddText(title_font, title_font->FontSize, title_pos, ImGui::GetColorU32(ImGuiCol_Text), title, nullptr, 0.0f, - &title_bb); + RenderShadowedTextClipped(title_font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(ImGuiCol_Text), title, nullptr, + &title_size, ImVec2(0.0f, 0.0f), avail_width, &title_bb); ImFont* desc_font = UIStyle.MediumFont; const ImVec2 desc_size = desc_font->CalcTextSizeA(desc_font->FontSize, std::numeric_limits::max(), avail_width, description); - const ImVec2 desc_pos = ImVec2(bb.Min.x + (avail_width - desc_size.x) * 0.5f, title_bb.w + LayoutScale(10.0f)); - const ImVec4 desc_bb = ImVec4(desc_pos.x, desc_pos.y, desc_pos.x + desc_size.x, desc_pos.y + desc_size.y); + const ImVec2 desc_pos = ImVec2(bb.Min.x + (avail_width - desc_size.x) * 0.5f, title_bb.Max.y + LayoutScale(10.0f)); + const ImRect desc_bb = ImRect(desc_pos, desc_pos + desc_size); - dl->AddText(desc_font, desc_font->FontSize, desc_pos, ImGui::GetColorU32(ImGuiCol_Text), description, nullptr, - avail_width, &desc_bb); + RenderShadowedTextClipped(desc_font, desc_bb.Min, desc_bb.Max, ImGui::GetColorU32(ImGuiCol_Text), description, + nullptr, nullptr, ImVec2(0.0f, 0.0f), avail_width, &desc_bb); ImGui::SameLine(); @@ -3467,6 +3432,7 @@ void ImGuiFullscreen::SetTheme(std::string_view theme) UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); UIStyle.ToastBackgroundColor = HEX_TO_IMVEC4(0x282828, 0xff); UIStyle.ToastTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.ShadowColor = IM_COL32(0, 0, 0, 100); } else if (theme == "CobaltSky") { @@ -3489,6 +3455,7 @@ void ImGuiFullscreen::SetTheme(std::string_view theme) UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); UIStyle.ToastBackgroundColor = HEX_TO_IMVEC4(0x282828, 0xff); UIStyle.ToastTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.ShadowColor = IM_COL32(0, 0, 0, 100); } else if (theme == "GreyMatter") { @@ -3511,6 +3478,7 @@ void ImGuiFullscreen::SetTheme(std::string_view theme) UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); UIStyle.ToastBackgroundColor = HEX_TO_IMVEC4(0x282828, 0xff); UIStyle.ToastTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.ShadowColor = IM_COL32(0, 0, 0, 100); } else if (theme == "PinkyPals") { @@ -3533,6 +3501,7 @@ void ImGuiFullscreen::SetTheme(std::string_view theme) UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0x000000, 0xff); UIStyle.ToastBackgroundColor = HEX_TO_IMVEC4(0x282828, 0xff); UIStyle.ToastTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.ShadowColor = IM_COL32(100, 100, 100, 50); } else if (theme == "Light") { @@ -3556,6 +3525,7 @@ void ImGuiFullscreen::SetTheme(std::string_view theme) UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0x000000, 0xff); UIStyle.ToastBackgroundColor = HEX_TO_IMVEC4(0xf1f1f1, 0xff); UIStyle.ToastTextColor = HEX_TO_IMVEC4(0x000000, 0xff); + UIStyle.ShadowColor = IM_COL32(100, 100, 100, 50); } else { @@ -3579,5 +3549,6 @@ void ImGuiFullscreen::SetTheme(std::string_view theme) UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); UIStyle.ToastBackgroundColor = HEX_TO_IMVEC4(0x282828, 0xff); UIStyle.ToastTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.ShadowColor = IM_COL32(0, 0, 0, 100); } } diff --git a/src/util/imgui_fullscreen.h b/src/util/imgui_fullscreen.h index c153cf408..e9ffda8de 100644 --- a/src/util/imgui_fullscreen.h +++ b/src/util/imgui_fullscreen.h @@ -42,6 +42,7 @@ static constexpr float LAYOUT_FOOTER_HEIGHT = LAYOUT_MEDIUM_FONT_SIZE + LAYOUT_F static constexpr float LAYOUT_HORIZONTAL_MENU_HEIGHT = 320.0f; static constexpr float LAYOUT_HORIZONTAL_MENU_PADDING = 30.0f; static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_WIDTH = 250.0f; +static constexpr float LAYOUT_SHADOW_OFFSET = 1.0f; struct ALIGN_TO_CACHE_LINE UIStyles { @@ -68,6 +69,8 @@ struct ALIGN_TO_CACHE_LINE UIStyles ImFont* MediumFont; ImFont* LargeFont; + u32 ShadowColor; + float LayoutScale; float RcpLayoutScale; float LayoutPaddingLeft; @@ -108,12 +111,22 @@ ALWAYS_INLINE static ImVec4 ModAlpha(const ImVec4& v, float a) { return ImVec4(v.x, v.y, v.z, a); } +ALWAYS_INLINE static u32 ModAlpha(u32 col32, float a) +{ + return (col32 & ~IM_COL32_A_MASK) | (static_cast(a * 255.0f) << IM_COL32_A_SHIFT); +} ALWAYS_INLINE static ImVec4 MulAlpha(const ImVec4& v, float a) { return ImVec4(v.x, v.y, v.z, v.w * a); } +ALWAYS_INLINE static u32 MulAlpha(u32 col32, float a) +{ + return (col32 & ~IM_COL32_A_MASK) | + (static_cast(static_cast((col32 & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) * a) << IM_COL32_A_SHIFT); +} + ALWAYS_INLINE static std::string_view RemoveHash(std::string_view s) { const std::string_view::size_type pos = s.find('#'); @@ -217,6 +230,10 @@ bool MenuButtonFrame(const char* str_id, bool enabled, float height, bool* visib void DrawMenuButtonFrame(const ImVec2& p_min, const ImVec2& p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); void ResetMenuButtonFrame(); +void RenderShadowedTextClipped(ImFont* font, const ImVec2& pos_min, const ImVec2& pos_max, u32 color, const char* text, + const char* text_end, const ImVec2* text_size_if_known = nullptr, + const ImVec2& align = ImVec2(0, 0), float wrap_width = 0.0f, + const ImRect* clip_rect = nullptr); void MenuHeading(const char* title, bool draw_line = true); bool MenuHeadingButton(const char* title, const char* value = nullptr, bool enabled = true, bool draw_line = true); bool ActiveButton(const char* title, bool is_active, bool enabled = true,