FullscreenUI: Add option to hide grid view labels

Also handle titles that span multiple lines.
This commit is contained in:
Stenzek 2025-07-26 23:11:06 +10:00
parent 96c224497b
commit 29e92705c4
No known key found for this signature in database
3 changed files with 90 additions and 25 deletions

View File

@ -571,6 +571,7 @@ struct ALIGN_TO_CACHE_LINE UIState
std::string game_list_current_selection_path;
float game_list_current_selection_timeout = 0.0f;
bool game_list_show_trophy_icons = true;
bool game_grid_show_titles = true;
};
} // namespace
@ -8303,6 +8304,8 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
const ImGuiStyle& style = ImGui::GetStyle();
const float title_font_size = UIStyle.MediumFontSize;
const float title_font_weight = UIStyle.BoldFontWeight;
const float avail_width = ImGui::GetContentRegionAvail().x;
const float title_spacing = LayoutScale(10.0f);
const float item_width_with_spacing = std::floor(avail_width / 5.0f);
@ -8310,27 +8313,56 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
const float image_width = item_width - (style.FramePadding.x * 2.0f);
const float image_height = image_width;
const ImVec2 image_size(image_width, image_height);
const float item_height = (style.FramePadding.y * 2.0f) + image_height + title_spacing + UIStyle.MediumFontSize;
const ImVec2 item_size(item_width, item_height);
const float base_item_height = (style.FramePadding.y * 2.0f) + image_height;
const u32 grid_count_x = static_cast<u32>(std::floor(avail_width / item_width_with_spacing));
// calculate padding to center it, the last item in the row doesn't need spacing
const float x_padding = std::floor(
(avail_width - ((item_width_with_spacing * static_cast<float>(grid_count_x)) - style.ItemSpacing.x)) * 0.5f);
ImGuiWindow* const window = ImGui::GetCurrentWindow();
ImDrawList* const dl = ImGui::GetWindowDrawList();
SmallString draw_title;
const u32 text_color = ImGui::GetColorU32(ImGuiCol_Text);
u32 grid_x = 0;
float row_item_height = base_item_height;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + x_padding);
for (const GameList::Entry* entry : s_state.game_list_sorted_entries)
for (size_t entry_index = 0; entry_index < s_state.game_list_sorted_entries.size(); entry_index++)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
continue;
// This is pretty annoying. If we don't use an equal sized item for each grid item, keyboard/gamepad navigation
// tends to break when scrolling vertically - it goes left/right. Precompute the maximum item height for the row
// first, and make all items the same size to work around this.
const GameList::Entry* entry = s_state.game_list_sorted_entries[entry_index];
if (grid_x == 0 && s_state.game_grid_show_titles)
{
row_item_height = 0.0f;
const size_t row_entry_index_end = std::min(entry_index + grid_count_x, s_state.game_list_sorted_entries.size());
for (size_t row_entry_index = entry_index; row_entry_index < row_entry_index_end; row_entry_index++)
{
const GameList::Entry* row_entry = s_state.game_list_sorted_entries[row_entry_index];
const ImVec2 this_title_size = UIStyle.Font->CalcTextSizeA(title_font_size, title_font_weight, image_width,
image_width, IMSTR_START_END(row_entry->title));
row_item_height = std::max(row_item_height, this_title_size.y);
}
row_item_height += title_spacing + base_item_height;
}
ImVec2 title_size;
if (s_state.game_grid_show_titles)
{
title_size = UIStyle.Font->CalcTextSizeA(title_font_size, title_font_weight, image_width, image_width,
IMSTR_START_END(entry->title));
}
const ImGuiID id = window->GetID(entry->path.c_str(), entry->path.c_str() + entry->path.length());
const ImVec2 pos(window->DC.CursorPos);
const ImVec2 item_size(item_width, row_item_height);
ImRect bb(pos, pos + item_size);
ImGui::ItemSize(item_size);
if (ImGui::ItemAdd(bb, id))
@ -8358,36 +8390,24 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
CenterImage(ImRect(bb.Min, bb.Min + 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));
dl->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));
GPUTexture* const cover_trophy = GetGameListCoverTrophy(entry, image_size);
if (cover_trophy)
{
const ImVec2 trophy_size =
ImVec2(static_cast<float>(cover_trophy->GetWidth()), static_cast<float>(cover_trophy->GetHeight()));
ImGui::GetWindowDrawList()->AddImage(cover_trophy, bb.Min + image_size - trophy_size, bb.Min + image_size,
ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
dl->AddImage(cover_trophy, bb.Min + image_size - trophy_size, bb.Min + image_size, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
}
if (draw_title)
{
const ImRect title_bb(ImVec2(bb.Min.x, bb.Min.y + image_height + title_spacing), bb.Max);
const char* remaining_text;
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, bb.GetWidth(), 0.0f,
IMSTR_START_END(entry->title), &remaining_text);
const u32 unclipped_size = static_cast<u32>(remaining_text - entry->title.data());
if (unclipped_size > 0 && unclipped_size != entry->title.size())
{
// ellipise title, remove one character to make room
draw_title.format("{}...", std::string_view(entry->title).substr(0, unclipped_size - 1));
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, title_bb.Min,
title_bb.Max, text_color, draw_title, nullptr, LAYOUT_CENTER_ALIGN_TEXT, 0.0f,
&title_bb);
}
else
{
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, title_bb.Min,
title_bb.Max, text_color, entry->title, nullptr, LAYOUT_CENTER_ALIGN_TEXT, 0.0f,
&title_bb);
ImGuiFullscreen::RenderMultiLineShadowedTextClipped(dl, UIStyle.Font, title_font_size, title_font_weight,
title_bb.Min, title_bb.Max, text_color, entry->title,
LAYOUT_CENTER_ALIGN_TEXT, image_width, &title_bb);
}
if (pressed)
@ -8603,6 +8623,12 @@ void FullscreenUI::DrawGameListSettingsPage()
{
s_state.game_list_show_trophy_icons = bsi->GetBoolValue("Main", "FullscreenUIShowTrophyIcons", true);
}
if (DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_TAGS, "Show Grid View Titles"),
FSUI_VSTR("Shows titles underneath the images in the game grid view."), "Main",
"FullscreenUIShowGridTitles", true))
{
s_state.game_grid_show_titles = bsi->GetBoolValue("Main", "FullscreenUIShowGridTitles", 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."),
@ -8807,6 +8833,7 @@ void FullscreenUI::SwitchToGameList()
s_state.game_list_view =
static_cast<GameListView>(Host::GetBaseIntSettingValue("Main", "DefaultFullscreenUIGameView", 0));
s_state.game_list_show_trophy_icons = Host::GetBaseBoolSettingValue("Main", "FullscreenUIShowTrophyIcons", true);
s_state.game_grid_show_titles = Host::GetBaseBoolSettingValue("Main", "FullscreenUIShowGridTitles", true);
s_state.game_list_current_selection_path = {};
s_state.game_list_current_selection_timeout = 0.0f;
@ -9936,6 +9963,7 @@ TRANSLATE_NOOP("FullscreenUI", "Show FPS");
TRANSLATE_NOOP("FullscreenUI", "Show Frame Times");
TRANSLATE_NOOP("FullscreenUI", "Show GPU Statistics");
TRANSLATE_NOOP("FullscreenUI", "Show GPU Usage");
TRANSLATE_NOOP("FullscreenUI", "Show Grid View Titles");
TRANSLATE_NOOP("FullscreenUI", "Show Latency Statistics");
TRANSLATE_NOOP("FullscreenUI", "Show OSD Messages");
TRANSLATE_NOOP("FullscreenUI", "Show Resolution");
@ -9956,6 +9984,7 @@ TRANSLATE_NOOP("FullscreenUI", "Shows the game you are currently playing as part
TRANSLATE_NOOP("FullscreenUI", "Shows the host's CPU usage of each system thread in the top-right corner of the display.");
TRANSLATE_NOOP("FullscreenUI", "Shows the host's GPU usage in the top-right corner of the display.");
TRANSLATE_NOOP("FullscreenUI", "Shows the number of frames (or v-syncs) displayed per second by the system in the top-right corner of the display.");
TRANSLATE_NOOP("FullscreenUI", "Shows titles underneath the images in the game grid view.");
TRANSLATE_NOOP("FullscreenUI", "Shows trophy icons in game grid when games have achievements or have been mastered.");
TRANSLATE_NOOP("FullscreenUI", "Simulates the region check present in original, unmodified consoles.");
TRANSLATE_NOOP("FullscreenUI", "Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements.");

View File

@ -1722,6 +1722,38 @@ void ImGuiFullscreen::RenderShadowedTextClipped(ImFont* font, float font_size, f
text_size_if_known, align, wrap_width, clip_rect);
}
void ImGuiFullscreen::RenderMultiLineShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size,
float font_weight, const ImVec2& pos_min,
const ImVec2& pos_max, u32 color, std::string_view text,
const ImVec2& align, float wrap_width,
const ImRect* clip_rect /* = nullptr */,
float shadow_offset /* = LayoutScale(LAYOUT_SHADOW_OFFSET) */)
{
if (text.empty())
return;
const char* text_display_end = ImGui::FindRenderedTextEnd(IMSTR_START_END(text));
const size_t text_len = (text_display_end - text.data());
if (text_len == 0)
return;
text = text.substr(0, text_len);
const char* text_ptr = text.data();
const char* text_end = text.data() + text_len;
ImVec2 current_pos = pos_min;
while (text_ptr < text_end)
{
const char* line_end = font->CalcWordWrapPosition(font_size, font_weight, text_ptr, text_end, wrap_width);
const ImVec2 line_size = font->CalcTextSizeA(font_size, font_weight, FLT_MAX, 0.0f, text_ptr, line_end);
RenderShadowedTextClipped(draw_list, font, font_size, font_weight, current_pos, pos_max, color,
std::string_view(text_ptr, line_end), &line_size, align, 0.0f, clip_rect, shadow_offset);
current_pos.y += line_size.y;
text_ptr = line_end;
}
}
void ImGuiFullscreen::RenderAutoLabelText(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight,
float label_weight, const ImVec2& pos_min, const ImVec2& pos_max, u32 color,
std::string_view text, char separator, float shadow_offset)

View File

@ -323,6 +323,10 @@ void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_s
const ImVec2& pos_min, const ImVec2& pos_max, u32 color, std::string_view text,
const ImVec2* text_size_if_known, const ImVec2& align, float wrap_width,
const ImRect* clip_rect, float shadow_offset);
void RenderMultiLineShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight,
const ImVec2& pos_min, const ImVec2& pos_max, u32 color, std::string_view text,
const ImVec2& align, float wrap_width, const ImRect* clip_rect = nullptr,
float shadow_offset = LayoutScale(LAYOUT_SHADOW_OFFSET));
void RenderAutoLabelText(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight, float label_weight,
const ImVec2& pos_min, const ImVec2& pos_max, u32 color, std::string_view text,
char separator = ':', float shadow_offset = LayoutScale(LAYOUT_SHADOW_OFFSET));