From ca322449da52d8bdd9f890b71389d736045e7a10 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 1 Jul 2025 19:41:02 +1000 Subject: [PATCH] FullscreenUI: Add 'Compact Mode' for game list --- src/core/fullscreen_ui.cpp | 59 ++++++++++++------------ src/util/imgui_fullscreen.cpp | 84 ++++++++++++++++------------------- src/util/imgui_fullscreen.h | 33 ++++++++++++++ 3 files changed, 103 insertions(+), 73 deletions(-) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 531493fce..5e9bd89f2 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -8017,8 +8017,10 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) if (BeginFullscreenColumnWindow(0.0f, -530.0f, "game_list_entries", GetTransparentBackgroundColor(), ImVec2(LAYOUT_MENU_WINDOW_X_PADDING, LAYOUT_MENU_WINDOW_Y_PADDING))) { - const float row_height = 50.0f; - const ImVec2 image_size(LayoutScale(row_height, row_height)); + const bool compact_mode = Host::GetBaseBoolSettingValue("Main", "FullscreenUIGameListCompactMode", true); + const float image_size = compact_mode ? UIStyle.LargeFontSize : LayoutScale(50.0f); + const float row_image_padding = LayoutScale(compact_mode ? 15.0f : 15.0f); + const float row_left_margin = image_size + row_image_padding; ResetFocusHere(); @@ -8030,43 +8032,38 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) for (const GameList::Entry* entry : s_state.game_list_sorted_entries) { - ImRect bb; + if (!compact_mode) + { + if (entry->serial.empty()) + summary.format("{} | {} MB", Path::GetFileName(entry->path), to_mb(entry->file_size)); + else + summary.format("{} | {} | {} MB", entry->serial, Path::GetFileName(entry->path), to_mb(entry->file_size)); + } + + const ImGuiFullscreen::MenuButtonBounds mbb(entry->title, {}, summary, row_left_margin); + bool visible, hovered; - bool pressed = MenuButtonFrame(entry->path.c_str(), LayoutScale(row_height), true, &bb, &visible, &hovered); + bool pressed = MenuButtonFrame(entry->path, true, mbb.frame_bb, &visible, &hovered); if (!visible) continue; GPUTexture* cover_texture = GetGameListCover(entry, false, true); - - if (entry->serial.empty()) - { - summary.format("{} | {} MB", Path::GetFileName(entry->path), to_mb(entry->file_size)); - } - else - { - 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()), - static_cast(cover_texture->GetHeight())))); + const ImRect image_rect(CenterImage( + ImRect(ImVec2(mbb.title_bb.Min.x - row_left_margin, mbb.title_bb.Min.y), + ImVec2(mbb.title_bb.Min.x - row_image_padding, mbb.title_bb.Min.y + image_size)), + ImVec2(static_cast(cover_texture->GetWidth()), static_cast(cover_texture->GetHeight())))); ImGui::GetWindowDrawList()->AddImage(cover_texture, image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255)); - - const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f); - 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); - - RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max, - text_color, entry->title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb); + RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, mbb.title_bb.Min, + mbb.title_bb.Max, text_color, entry->title, &mbb.title_size, ImVec2(0.0f, 0.0f), + mbb.title_size.x, &mbb.title_bb); if (!summary.empty()) { - RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min, - summary_bb.Max, subtitle_text_color, summary, nullptr, ImVec2(0.0f, 0.0f), 0.0f, - &summary_bb); + RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, mbb.summary_bb.Min, + mbb.summary_bb.Max, subtitle_text_color, summary, &mbb.summary_size, + ImVec2(0.0f, 0.0f), mbb.summary_size.x, &mbb.summary_bb); } if (pressed) @@ -8663,6 +8660,10 @@ void FullscreenUI::DrawGameListSettingsPage() { s_state.game_list_show_trophy_icons = bsi->GetBoolValue("Main", "FullscreenUIShowTrophyIcons", true); } + + DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_TEXT_SLASH, "List Compact Mode"), + FSUI_VSTR("Displays only the game title in the list, instead of the title and serial/file name."), + "Main", "FullscreenUIGameListCompactMode", true); } MenuHeading(FSUI_VSTR("Search Directories")); @@ -9495,6 +9496,7 @@ TRANSLATE_NOOP("FullscreenUI", "Disc"); TRANSLATE_NOOP("FullscreenUI", "Discord Server"); TRANSLATE_NOOP("FullscreenUI", "Display Area"); TRANSLATE_NOOP("FullscreenUI", "Displays DualShock/DualSense button icons in the footer and input binding, instead of Xbox buttons."); +TRANSLATE_NOOP("FullscreenUI", "Displays only the game title in the list, instead of the title and serial/file name."); TRANSLATE_NOOP("FullscreenUI", "Displays popup messages on events such as achievement unlocks and leaderboard submissions."); TRANSLATE_NOOP("FullscreenUI", "Displays popup messages when starting, submitting, or failing a leaderboard challenge."); TRANSLATE_NOOP("FullscreenUI", "Dithering"); @@ -9651,6 +9653,7 @@ TRANSLATE_NOOP("FullscreenUI", "Leaderboard Notifications"); TRANSLATE_NOOP("FullscreenUI", "Leaderboards"); TRANSLATE_NOOP("FullscreenUI", "Light"); TRANSLATE_NOOP("FullscreenUI", "Line Detection"); +TRANSLATE_NOOP("FullscreenUI", "List Compact Mode"); TRANSLATE_NOOP("FullscreenUI", "List Settings"); TRANSLATE_NOOP("FullscreenUI", "Load Database Cheats"); TRANSLATE_NOOP("FullscreenUI", "Load Devices From Save States"); diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp index 4111823b3..480f9a51c 100644 --- a/src/util/imgui_fullscreen.cpp +++ b/src/util/imgui_fullscreen.cpp @@ -88,36 +88,6 @@ struct BackgroundProgressDialogData s32 value; }; -struct MenuButtonBounds -{ - ImVec2 title_size; - ImVec2 value_size; - ImVec2 summary_size; - - ImRect frame_bb; - ImRect title_bb; - ImRect value_bb; - ImRect summary_bb; - - float available_width = CalcAvailWidth(); - float available_non_value_width; - - MenuButtonBounds(const std::string_view& title, const std::string_view& value, const std::string_view& summary); - MenuButtonBounds(const std::string_view& title, const ImVec2& value_size, const std::string_view& summary); - MenuButtonBounds(const ImVec2& title_size, const ImVec2& value_size, const ImVec2& summary_size); - - static float CalcAvailWidth(); - - static float GetSingleLineHeight(float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING); - static float GetSummaryLineHeight(float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING); - - void CalcBB(); - void CalcTitleSize(const std::string_view& title); - void SetValueSize(const ImVec2& value_size); - void CalcValueSize(const std::string_view& value); - void CalcSummarySize(const std::string_view& summary); -}; - class MessageDialog : public PopupDialog { public: @@ -1545,6 +1515,27 @@ ImGuiFullscreen::MenuButtonBounds::MenuButtonBounds(const std::string_view& titl CalcBB(); } +ImGuiFullscreen::MenuButtonBounds::MenuButtonBounds(const std::string_view& title, const std::string_view& value, + const std::string_view& summary, float left_margin) +{ + // ugly, but only used for compact game list, whatever + const float orig_width = available_width; + available_width -= left_margin; + + CalcValueSize(value); + CalcTitleSize(title); + CalcSummarySize(summary); + + available_width = orig_width; + + CalcBB(); + + title_bb.Min.x += left_margin; + title_bb.Max.x += left_margin; + summary_bb.Min.x += left_margin; + summary_bb.Max.x += left_margin; +} + ImGuiFullscreen::MenuButtonBounds::MenuButtonBounds(const std::string_view& title, const ImVec2& value_size, const std::string_view& summary) { @@ -1926,34 +1917,37 @@ bool ImGuiFullscreen::MenuButtonWithVisibilityQuery(std::string_view title, std: } bool ImGuiFullscreen::MenuImageButton(std::string_view title, std::string_view summary, ImTextureID user_texture_id, - const ImVec2& image_size, bool enabled /* = true */, - const ImVec2& uv0 /* = ImVec2(0.0f, 0.0f) */, - const ImVec2& uv1 /* = ImVec2(1.0f, 1.0f) */) + const ImVec2& image_size, bool enabled /*= true*/, + const ImVec2& uv0 /*= ImVec2(0.0f, 0.0f)*/, + const ImVec2& uv1 /*= ImVec2(1.0f, 1.0f)*/) { - MenuButtonBounds bb(title, ImVec2(), summary); + const float left_margin = image_size.x + LayoutScale(15.0f); + const MenuButtonBounds mbb(title, {}, summary, left_margin); bool visible, hovered; - bool pressed = MenuButtonFrame(title, enabled, bb.frame_bb, &visible, &hovered); + bool pressed = MenuButtonFrame(title, enabled, mbb.frame_bb, &visible, &hovered); if (!visible) return false; - ImGui::GetWindowDrawList()->AddImage(user_texture_id, bb.title_bb.Min, bb.title_bb.Min + image_size, uv0, uv1, + const ImRect image_rect(CenterImage( + ImRect(ImVec2(mbb.title_bb.Min.x - left_margin, mbb.title_bb.Min.y), + ImVec2(mbb.title_bb.Min.x - left_margin, mbb.title_bb.Min.y + image_size.x)), + ImVec2(static_cast(user_texture_id->GetWidth()), static_cast(user_texture_id->GetHeight())))); + + ImGui::GetWindowDrawList()->AddImage(user_texture_id, image_rect.Min, image_rect.Max, uv0, uv1, enabled ? IM_COL32(255, 255, 255, 255) : ImGui::GetColorU32(ImGuiCol_TextDisabled)); - const float text_offset = image_size.x + LayoutScale(15.0f); - bb.title_bb.Min.x += text_offset; - bb.title_bb.Max.x += text_offset; const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled]; - RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.title_bb.Min, - bb.title_bb.Max, ImGui::GetColorU32(color), title, &bb.title_size, ImVec2(0.0f, 0.0f), - bb.title_size.x, &bb.title_bb); + RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, mbb.title_bb.Min, + mbb.title_bb.Max, ImGui::GetColorU32(color), title, &mbb.title_size, ImVec2(0.0f, 0.0f), + mbb.title_size.x, &mbb.title_bb); if (!summary.empty()) { - RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, bb.summary_bb.Min, - bb.summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, &bb.summary_size, - ImVec2(0.0f, 0.0f), bb.summary_size.x, &bb.summary_bb); + RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, mbb.summary_bb.Min, + mbb.summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, &mbb.summary_size, + ImVec2(0.0f, 0.0f), mbb.summary_size.x, &mbb.summary_bb); } s_state.menu_button_index++; diff --git a/src/util/imgui_fullscreen.h b/src/util/imgui_fullscreen.h index 4901ec480..990dfd8b0 100644 --- a/src/util/imgui_fullscreen.h +++ b/src/util/imgui_fullscreen.h @@ -481,6 +481,39 @@ protected: State m_state = State::Inactive; }; +// Wrapper for computing menu button bounds. +struct MenuButtonBounds +{ + ImVec2 title_size; + ImVec2 value_size; + ImVec2 summary_size; + + ImRect frame_bb; + ImRect title_bb; + ImRect value_bb; + ImRect summary_bb; + + float available_width = CalcAvailWidth(); + float available_non_value_width; + + MenuButtonBounds(const std::string_view& title, const std::string_view& value, const std::string_view& summary); + MenuButtonBounds(const std::string_view& title, const std::string_view& value, const std::string_view& summary, + float left_margin); + MenuButtonBounds(const std::string_view& title, const ImVec2& value_size, const std::string_view& summary); + MenuButtonBounds(const ImVec2& title_size, const ImVec2& value_size, const ImVec2& summary_size); + + static float CalcAvailWidth(); + + static float GetSingleLineHeight(float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING); + static float GetSummaryLineHeight(float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING); + + void CalcBB(); + void CalcTitleSize(const std::string_view& title); + void SetValueSize(const ImVec2& value_size); + void CalcValueSize(const std::string_view& value); + void CalcSummarySize(const std::string_view& summary); +}; + } // namespace ImGuiFullscreen // Host UI triggers from Big Picture mode.