FullscreenUI: Add 'Compact Mode' for game list

This commit is contained in:
Stenzek 2025-07-01 19:41:02 +10:00
parent 0098c60ee8
commit ca322449da
No known key found for this signature in database
3 changed files with 103 additions and 73 deletions

View File

@ -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<float>(cover_texture->GetWidth()),
static_cast<float>(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<float>(cover_texture->GetWidth()), static_cast<float>(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");

View File

@ -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<float>(user_texture_id->GetWidth()), static_cast<float>(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++;

View File

@ -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.