mirror of
https://github.com/stenzek/duckstation.git
synced 2026-10-11 22:49:58 +00:00
GameList: Synchronize settings between FullscreenUI and Qt
Also share translations with other hosts.
This commit is contained in:
@@ -1080,6 +1080,10 @@ void FullscreenUI::BeginChangeDiscOnCoreThread(bool return_to_game)
|
||||
|
||||
if (const GameDatabase::Entry* entry = System::GetGameDatabaseEntry(); entry && entry->disc_set)
|
||||
{
|
||||
// Don't hold both settings and game list lock.
|
||||
const bool localized_titles = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES);
|
||||
|
||||
// Another load request cannot start because we have the lock held.
|
||||
auto lock = GameList::GetLock();
|
||||
if (!GameList::IsGameListLoaded())
|
||||
@@ -1108,7 +1112,7 @@ void FullscreenUI::BeginChangeDiscOnCoreThread(bool return_to_game)
|
||||
|
||||
// This is still needed because a scan-in-progress will report loaded.
|
||||
GameList::EnsureLoaded(lock);
|
||||
auto matches = GameList::GetEntriesInDiscSet(entry->disc_set, GameList::ShouldShowLocalizedTitles());
|
||||
auto matches = GameList::GetEntriesInDiscSet(entry->disc_set, localized_titles);
|
||||
if (matches.size() > 1)
|
||||
{
|
||||
ChoiceDialogOptions options;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <[email protected]>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "achievements.h"
|
||||
#include "core.h"
|
||||
#include "fullscreenui_private.h"
|
||||
#include "game_list.h"
|
||||
#include "host.h"
|
||||
#include "system.h"
|
||||
#include "video_thread.h"
|
||||
|
||||
#include "util/gpu_texture.h"
|
||||
#include "util/imgui_manager.h"
|
||||
|
||||
#include "common/bitutils.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/path.h"
|
||||
@@ -22,13 +24,6 @@
|
||||
|
||||
namespace FullscreenUI {
|
||||
|
||||
enum class GameListView : u8
|
||||
{
|
||||
Grid,
|
||||
List,
|
||||
Count
|
||||
};
|
||||
|
||||
static void DrawGameList(const ImVec2& heading_size);
|
||||
static void DrawGameGrid(const ImVec2& heading_size);
|
||||
static void HandleGameListActivate(const GameList::Entry* entry);
|
||||
@@ -53,7 +48,14 @@ struct GameListLocals
|
||||
std::unordered_map<std::string, std::string> cover_image_map;
|
||||
std::unordered_map<std::string, std::string> icon_image_map;
|
||||
std::vector<const GameList::Entry*> game_list_sorted_entries;
|
||||
GameListView game_list_view = GameListView::Grid;
|
||||
GameList::Column sort_column = GameList::DEFAULT_SORT_COLUMN;
|
||||
bool sort_reversed = GameList::DEFAULT_SORT_REVERSED;
|
||||
bool localized_titles = GameList::DEFAULT_LOCALIZED_TITLES;
|
||||
bool merge_disc_sets = GameList::DEFAULT_MERGE_DISC_SETS;
|
||||
bool grid_view = GameList::DEFAULT_FULLSCREENUI_GRID_VIEW;
|
||||
bool cover_titles = GameList::DEFAULT_COVER_TITLES;
|
||||
bool compact_mode = true;
|
||||
bool show_trophy_icons = true;
|
||||
float game_list_current_selection_timeout = 0.0f;
|
||||
std::string game_list_current_selection_path;
|
||||
char game_list_search_string[256];
|
||||
@@ -148,16 +150,13 @@ void FullscreenUI::DoSetCoverImage(std::string source_path, std::string existing
|
||||
|
||||
void FullscreenUI::PopulateGameListEntryList()
|
||||
{
|
||||
const s32 sort = Core::GetBaseIntSettingValue("Main", "FullscreenUIGameSort", 0);
|
||||
const bool reverse = Core::GetBaseBoolSettingValue("Main", "FullscreenUIGameSortReverse", false);
|
||||
const bool merge_disc_sets = Core::GetBaseBoolSettingValue("Main", "FullscreenUIMergeDiscSets", true);
|
||||
const std::string_view string_filter = s_game_list_locals.game_list_search_string;
|
||||
|
||||
s_game_list_locals.game_list_sorted_entries.clear();
|
||||
s_game_list_locals.game_list_sorted_entries.reserve(GameList::GetEntryCount());
|
||||
for (const GameList::Entry& entry : GameList::GetEntries())
|
||||
{
|
||||
if (merge_disc_sets)
|
||||
if (s_game_list_locals.merge_disc_sets)
|
||||
{
|
||||
if (entry.disc_set_member)
|
||||
continue;
|
||||
@@ -182,110 +181,11 @@ void FullscreenUI::PopulateGameListEntryList()
|
||||
s_game_list_locals.game_list_sorted_entries.push_back(&entry);
|
||||
}
|
||||
|
||||
std::sort(s_game_list_locals.game_list_sorted_entries.begin(), s_game_list_locals.game_list_sorted_entries.end(),
|
||||
[sort, reverse](const GameList::Entry* lhs, const GameList::Entry* rhs) {
|
||||
switch (sort)
|
||||
{
|
||||
case 0: // Type
|
||||
{
|
||||
const GameList::EntryType lst = lhs->GetSortType();
|
||||
const GameList::EntryType rst = rhs->GetSortType();
|
||||
if (lst != rst)
|
||||
return reverse ? (lst > rst) : (lst < rst);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // Serial
|
||||
{
|
||||
if (lhs->serial != rhs->serial)
|
||||
return reverse ? (lhs->serial > rhs->serial) : (lhs->serial < rhs->serial);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // Title
|
||||
break;
|
||||
|
||||
case 3: // File Title
|
||||
{
|
||||
const std::string_view lhs_title(Path::GetFileTitle(lhs->path));
|
||||
const std::string_view rhs_title(Path::GetFileTitle(rhs->path));
|
||||
const int res = StringUtil::Strncasecmp(lhs_title.data(), rhs_title.data(),
|
||||
std::min(lhs_title.size(), rhs_title.size()));
|
||||
if (res != 0)
|
||||
return reverse ? (res > 0) : (res < 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // Time Played
|
||||
{
|
||||
if (lhs->total_played_time != rhs->total_played_time)
|
||||
{
|
||||
return reverse ? (lhs->total_played_time > rhs->total_played_time) :
|
||||
(lhs->total_played_time < rhs->total_played_time);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: // Last Played (reversed by default)
|
||||
{
|
||||
if (lhs->last_played_time != rhs->last_played_time)
|
||||
{
|
||||
return reverse ? (lhs->last_played_time < rhs->last_played_time) :
|
||||
(lhs->last_played_time > rhs->last_played_time);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 6: // File Size
|
||||
{
|
||||
if (lhs->file_size != rhs->file_size)
|
||||
{
|
||||
return reverse ? (lhs->file_size > rhs->file_size) : (lhs->file_size < rhs->file_size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 7: // Uncompressed Size
|
||||
{
|
||||
if (lhs->uncompressed_size != rhs->uncompressed_size)
|
||||
{
|
||||
return reverse ? (lhs->uncompressed_size > rhs->uncompressed_size) :
|
||||
(lhs->uncompressed_size < rhs->uncompressed_size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 8: // Achievements
|
||||
{
|
||||
// sort by unlock percentage
|
||||
const float unlock_lhs =
|
||||
(lhs->num_achievements > 0) ?
|
||||
(static_cast<float>(std::max(lhs->unlocked_achievements, lhs->unlocked_achievements_hc)) /
|
||||
static_cast<float>(lhs->num_achievements)) :
|
||||
0;
|
||||
const float unlock_rhs =
|
||||
(rhs->num_achievements > 0) ?
|
||||
(static_cast<float>(std::max(rhs->unlocked_achievements, rhs->unlocked_achievements_hc)) /
|
||||
static_cast<float>(rhs->num_achievements)) :
|
||||
0;
|
||||
if (std::abs(unlock_lhs - unlock_rhs) >= 0.0001f)
|
||||
return reverse ? (unlock_lhs > unlock_rhs) : (unlock_lhs < unlock_rhs);
|
||||
|
||||
// order by achievement count
|
||||
if (lhs->num_achievements != rhs->num_achievements)
|
||||
return reverse ? (rhs->num_achievements < lhs->num_achievements) :
|
||||
(lhs->num_achievements < rhs->num_achievements);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to title when all else is equal
|
||||
const int res = StringUtil::CompareNoCase(lhs->GetSortTitle(), rhs->GetSortTitle());
|
||||
if (res != 0)
|
||||
return reverse ? (res > 0) : (res < 0);
|
||||
|
||||
// fallback to path when all else is equal
|
||||
return reverse ? (lhs->path > rhs->path) : (lhs->path < rhs->path);
|
||||
});
|
||||
std::ranges::sort(s_game_list_locals.game_list_sorted_entries, [](const GameList::Entry* lhs,
|
||||
const GameList::Entry* rhs) {
|
||||
return GameList::CompareEntryLessThan(s_game_list_locals.sort_reversed ? rhs : lhs,
|
||||
s_game_list_locals.sort_reversed ? lhs : rhs, s_game_list_locals.sort_column);
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawGameListWindow()
|
||||
@@ -300,10 +200,6 @@ void FullscreenUI::DrawGameListWindow()
|
||||
if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "gamelist_view",
|
||||
MulAlpha(UIStyle.PrimaryColor, GetBackgroundAlpha())))
|
||||
{
|
||||
static constexpr const char* icons[] = {ICON_FA_TABLE_CELLS_LARGE, ICON_FA_LIST};
|
||||
static constexpr const char* titles[] = {FSUI_NSTR("Game Grid"), FSUI_NSTR("Game List")};
|
||||
static constexpr u32 count = static_cast<u32>(std::size(titles));
|
||||
|
||||
BeginNavBar();
|
||||
|
||||
if (NavButton(ICON_PF_NAVIGATION_BACK, true, true))
|
||||
@@ -312,7 +208,7 @@ void FullscreenUI::DrawGameListWindow()
|
||||
[]() { SwitchToMainWindow(MainWindowType::Landing); });
|
||||
}
|
||||
|
||||
NavTitle(Host::TranslateToStringView(FSUI_TR_CONTEXT, titles[static_cast<u32>(s_game_list_locals.game_list_view)]));
|
||||
NavTitle(s_game_list_locals.grid_view ? FSUI_VSTR("Game Grid") : FSUI_VSTR("Game List"));
|
||||
|
||||
static constexpr const float& search_font_size = UIStyle.MediumFontSize;
|
||||
static constexpr const float& search_font_weight = UIStyle.NormalFontWeight;
|
||||
@@ -348,14 +244,18 @@ void FullscreenUI::DrawGameListWindow()
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::SetCursorPos(prev_cursor_pos);
|
||||
|
||||
RightAlignNavButtons(count);
|
||||
RightAlignNavButtons(2u);
|
||||
|
||||
for (u32 i = 0; i < count; i++)
|
||||
for (u32 grid = 0; grid < 2; grid++)
|
||||
{
|
||||
if (NavButton(icons[i], static_cast<GameListView>(i) == s_game_list_locals.game_list_view, true))
|
||||
if (NavButton(grid ? ICON_FA_TABLE_CELLS_LARGE : ICON_FA_LIST, grid == BoolToUInt32(s_game_list_locals.grid_view),
|
||||
true))
|
||||
{
|
||||
BeginTransition([i]() {
|
||||
s_game_list_locals.game_list_view = static_cast<GameListView>(i);
|
||||
BeginTransition([grid]() {
|
||||
s_game_list_locals.grid_view = ConvertToBoolUnchecked(grid);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_FULLSCREENUI_GRID_VIEW,
|
||||
s_game_list_locals.grid_view);
|
||||
Host::CommitBaseSettingChanges();
|
||||
QueueResetFocus(FocusResetType::ViewChanged);
|
||||
});
|
||||
}
|
||||
@@ -366,17 +266,10 @@ void FullscreenUI::DrawGameListWindow()
|
||||
|
||||
EndFullscreenWindow();
|
||||
|
||||
switch (s_game_list_locals.game_list_view)
|
||||
{
|
||||
case GameListView::Grid:
|
||||
DrawGameGrid(heading_size);
|
||||
break;
|
||||
case GameListView::List:
|
||||
DrawGameList(heading_size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (s_game_list_locals.grid_view)
|
||||
DrawGameGrid(heading_size);
|
||||
else
|
||||
DrawGameList(heading_size);
|
||||
|
||||
// note: has to come afterwards
|
||||
if (!AreAnyDialogsOpen())
|
||||
@@ -386,8 +279,10 @@ void FullscreenUI::DrawGameListWindow()
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_MOVE);
|
||||
BeginTransition([]() {
|
||||
s_game_list_locals.game_list_view =
|
||||
(s_game_list_locals.game_list_view == GameListView::Grid) ? GameListView::List : GameListView::Grid;
|
||||
s_game_list_locals.grid_view = !s_game_list_locals.grid_view;
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_FULLSCREENUI_GRID_VIEW,
|
||||
s_game_list_locals.grid_view);
|
||||
Host::CommitBaseSettingChanges();
|
||||
QueueResetFocus(FocusResetType::ViewChanged);
|
||||
});
|
||||
}
|
||||
@@ -450,8 +345,6 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
[]() { SwitchToMainWindow(MainWindowType::Landing); });
|
||||
}
|
||||
|
||||
const bool compact_mode = Core::GetBaseBoolSettingValue("Main", "FullscreenUIGameListCompactMode", true);
|
||||
const bool show_localized_titles = GameList::ShouldShowLocalizedTitles();
|
||||
auto game_list_lock = GameList::GetLock();
|
||||
const GameList::Entry* selected_entry = nullptr;
|
||||
PopulateGameListEntryList();
|
||||
@@ -462,8 +355,8 @@ 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 image_size = compact_mode ? UIStyle.LargeFontSize : LayoutScale(50.0f);
|
||||
const float row_image_padding = LayoutScale(compact_mode ? 15.0f : 15.0f);
|
||||
const float image_size = s_game_list_locals.compact_mode ? UIStyle.LargeFontSize : LayoutScale(50.0f);
|
||||
const float row_image_padding = LayoutScale(s_game_list_locals.compact_mode ? 15.0f : 15.0f);
|
||||
const float row_left_margin = image_size + row_image_padding;
|
||||
|
||||
ResetFocusHere();
|
||||
@@ -476,7 +369,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
|
||||
for (const GameList::Entry* entry : s_game_list_locals.game_list_sorted_entries)
|
||||
{
|
||||
if (!compact_mode)
|
||||
if (!s_game_list_locals.compact_mode)
|
||||
{
|
||||
if (entry->serial.empty())
|
||||
{
|
||||
@@ -489,20 +382,22 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
}
|
||||
}
|
||||
|
||||
const MenuButtonBounds mbb(entry->GetDisplayTitle(show_localized_titles), {}, summary, row_left_margin);
|
||||
const MenuButtonBounds mbb(entry->GetDisplayTitle(s_game_list_locals.localized_titles), {}, summary,
|
||||
row_left_margin);
|
||||
|
||||
bool visible, hovered;
|
||||
bool pressed = MenuButtonFrame(GetKeyForGameListEntry(entry), true, mbb.frame_bb, &visible, &hovered);
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
DrawGameListCover(entry, false, true, false, show_localized_titles, ImGui::GetWindowDrawList(),
|
||||
DrawGameListCover(entry, false, true, false, s_game_list_locals.localized_titles, ImGui::GetWindowDrawList(),
|
||||
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)));
|
||||
|
||||
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, mbb.title_bb.Min,
|
||||
mbb.title_bb.Max, text_color, entry->GetDisplayTitle(show_localized_titles),
|
||||
&mbb.title_size, ImVec2(0.0f, 0.0f), mbb.title_size.x, &mbb.title_bb);
|
||||
mbb.title_bb.Max, text_color,
|
||||
entry->GetDisplayTitle(s_game_list_locals.localized_titles), &mbb.title_size,
|
||||
ImVec2(0.0f, 0.0f), mbb.title_size.x, &mbb.title_bb);
|
||||
|
||||
if (!summary.empty())
|
||||
{
|
||||
@@ -605,7 +500,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
if (selected_entry)
|
||||
{
|
||||
const ImVec4 subtitle_text_color = DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text]);
|
||||
const std::string_view title = selected_entry->GetDisplayTitle(show_localized_titles);
|
||||
const std::string_view title = selected_entry->GetDisplayTitle(s_game_list_locals.localized_titles);
|
||||
|
||||
// title
|
||||
ImGui::PushFont(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight);
|
||||
@@ -813,9 +708,6 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
||||
BeginMenuButtons(0, 0.0f, 15.0f, 15.0f, 20.0f, 20.0f);
|
||||
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
const bool show_trophy_icons = Core::GetBaseBoolSettingValue("Main", "FullscreenUIShowTrophyIcons", true);
|
||||
const bool show_titles = Core::GetBaseBoolSettingValue("Main", "FullscreenUIShowGridTitles", true);
|
||||
const bool show_localized_titles = GameList::ShouldShowLocalizedTitles();
|
||||
|
||||
const float title_font_size = UIStyle.MediumLargeFontSize;
|
||||
const float title_font_weight = UIStyle.NormalFontWeight;
|
||||
@@ -856,7 +748,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
||||
// 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_game_list_locals.game_list_sorted_entries[entry_index];
|
||||
if (grid_x == 0 && show_titles)
|
||||
if (grid_x == 0 && s_game_list_locals.cover_titles)
|
||||
{
|
||||
row_item_height = 0.0f;
|
||||
|
||||
@@ -865,7 +757,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_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_game_list_locals.game_list_sorted_entries[row_entry_index];
|
||||
const std::string_view row_title = row_entry->GetDisplayTitle(show_localized_titles);
|
||||
const std::string_view row_title = row_entry->GetDisplayTitle(s_game_list_locals.localized_titles);
|
||||
const ImVec2 this_title_size = UIStyle.Font->CalcTextSizeA(title_font_size, title_font_weight, image_width,
|
||||
image_width, IMSTR_START_END(row_title));
|
||||
row_item_height = std::max(row_item_height, this_title_size.y);
|
||||
@@ -875,9 +767,9 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
||||
}
|
||||
|
||||
ImVec2 title_size;
|
||||
if (show_titles)
|
||||
if (s_game_list_locals.cover_titles)
|
||||
{
|
||||
const std::string_view title = entry->GetDisplayTitle(show_localized_titles);
|
||||
const std::string_view title = entry->GetDisplayTitle(s_game_list_locals.localized_titles);
|
||||
title_size = UIStyle.Font->CalcTextSizeA(title_font_size, title_font_weight, image_width, image_width,
|
||||
IMSTR_START_END(title));
|
||||
}
|
||||
@@ -899,9 +791,10 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
||||
bb.Min += style.FramePadding;
|
||||
bb.Max -= style.FramePadding;
|
||||
|
||||
DrawGameListCover(entry, false, false, true, show_localized_titles, dl, ImRect(bb.Min, bb.Min + image_size));
|
||||
DrawGameListCover(entry, false, false, true, s_game_list_locals.localized_titles, dl,
|
||||
ImRect(bb.Min, bb.Min + image_size));
|
||||
|
||||
if (show_trophy_icons)
|
||||
if (s_game_list_locals.show_trophy_icons)
|
||||
{
|
||||
GPUTexture* const cover_trophy = GetGameListCoverTrophy(entry, image_size);
|
||||
if (cover_trophy)
|
||||
@@ -913,11 +806,12 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
||||
}
|
||||
}
|
||||
|
||||
if (show_titles)
|
||||
if (s_game_list_locals.cover_titles)
|
||||
{
|
||||
const ImRect title_bb(ImVec2(bb.Min.x, bb.Min.y + image_height + title_spacing), bb.Max);
|
||||
RenderMultiLineShadowedTextClipped(dl, UIStyle.Font, title_font_size, title_font_weight, title_bb.Min,
|
||||
title_bb.Max, text_color, entry->GetDisplayTitle(show_localized_titles),
|
||||
title_bb.Max, text_color,
|
||||
entry->GetDisplayTitle(s_game_list_locals.localized_titles),
|
||||
LAYOUT_CENTER_ALIGN_TEXT, image_width, &title_bb);
|
||||
}
|
||||
|
||||
@@ -984,7 +878,7 @@ void FullscreenUI::HandleGameListOptions(const GameList::Entry* entry)
|
||||
};
|
||||
|
||||
const GameDatabase::DiscSetEntry* dsentry = entry->dbentry->disc_set;
|
||||
OpenChoiceDialog(entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles()), false, std::move(options),
|
||||
OpenChoiceDialog(entry->GetDisplayTitle(s_game_list_locals.localized_titles), false, std::move(options),
|
||||
[dsentry](s32 index, const std::string& title, bool checked) mutable {
|
||||
switch (index)
|
||||
{
|
||||
@@ -1023,7 +917,7 @@ void FullscreenUI::HandleGameListOptions(const GameList::Entry* entry)
|
||||
{FSUI_ICONSTR(ICON_FA_DELETE_LEFT, "Reset Play Time"), false},
|
||||
};
|
||||
|
||||
OpenChoiceDialog(entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles()), false, std::move(options),
|
||||
OpenChoiceDialog(entry->GetDisplayTitle(s_game_list_locals.localized_titles), false, std::move(options),
|
||||
[entry_path = entry->path, entry_serial = entry->serial](s32 index, const std::string& title,
|
||||
bool checked) mutable {
|
||||
switch (index)
|
||||
@@ -1072,7 +966,7 @@ void FullscreenUI::HandleGameListOptions(const GameList::Entry* entry)
|
||||
{FSUI_ICONSTR(ICON_FA_HOURGLASS, "Slow Boot"), false},
|
||||
};
|
||||
|
||||
OpenChoiceDialog(entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles()), false, std::move(options),
|
||||
OpenChoiceDialog(entry->GetDisplayTitle(s_game_list_locals.localized_titles), false, std::move(options),
|
||||
[entry_path = entry->path](s32 index, const std::string& title, bool checked) mutable {
|
||||
switch (index)
|
||||
{
|
||||
@@ -1120,9 +1014,9 @@ void FullscreenUI::HandleSelectDiscForDiscSet(const GameDatabase::DiscSetEntry*
|
||||
options.emplace_back(FSUI_ICONVSTR(ICON_FA_SQUARE_XMARK, "Close Menu"), false);
|
||||
|
||||
const GameList::Entry* dsgentry = GameList::GetEntryForPath(dsentry->GetSaveTitle());
|
||||
const bool localized_titles = GameList::ShouldShowLocalizedTitles();
|
||||
OpenChoiceDialog(fmt::format(FSUI_FSTR("Select Disc for {}"), dsgentry ? dsgentry->GetDisplayTitle(localized_titles) :
|
||||
dsentry->GetDisplayTitle(localized_titles)),
|
||||
OpenChoiceDialog(fmt::format(FSUI_FSTR("Select Disc for {}"),
|
||||
dsgentry ? dsgentry->GetDisplayTitle(s_game_list_locals.localized_titles) :
|
||||
dsentry->GetDisplayTitle(s_game_list_locals.localized_titles)),
|
||||
false, std::move(options),
|
||||
[paths = std::move(paths)](s32 index, const std::string& title, bool checked) {
|
||||
if (static_cast<u32>(index) >= paths.size())
|
||||
@@ -1137,9 +1031,8 @@ void FullscreenUI::HandleSelectDiscForDiscSet(const GameDatabase::DiscSetEntry*
|
||||
|
||||
void FullscreenUI::SwitchToGameList()
|
||||
{
|
||||
s_game_list_locals.game_list_view =
|
||||
static_cast<GameListView>(std::min(Core::GetBaseUIntSettingValue("Main", "DefaultFullscreenUIGameView", 0),
|
||||
static_cast<u32>(GameListView::Count) - 1));
|
||||
ReloadGameListSettings();
|
||||
|
||||
s_game_list_locals.game_list_current_selection_path = {};
|
||||
s_game_list_locals.game_list_current_selection_timeout = 0.0f;
|
||||
s_game_list_locals.game_list_search_string[0] = '\0';
|
||||
@@ -1155,6 +1048,29 @@ void FullscreenUI::SwitchToGameList()
|
||||
SwitchToMainWindow(MainWindowType::GameList);
|
||||
}
|
||||
|
||||
void FullscreenUI::ReloadGameListSettings()
|
||||
{
|
||||
s_game_list_locals.sort_column =
|
||||
GameList::ParseColumnName(
|
||||
Core::GetBaseStringSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_COLUMN))
|
||||
.value_or(GameList::DEFAULT_SORT_COLUMN);
|
||||
s_game_list_locals.sort_reversed =
|
||||
Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_REVERSED, false);
|
||||
s_game_list_locals.localized_titles = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES);
|
||||
s_game_list_locals.merge_disc_sets = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_MERGE_DISC_SETS, GameList::DEFAULT_MERGE_DISC_SETS);
|
||||
s_game_list_locals.grid_view =
|
||||
Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_FULLSCREENUI_GRID_VIEW,
|
||||
GameList::DEFAULT_FULLSCREENUI_GRID_VIEW);
|
||||
s_game_list_locals.cover_titles = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_COVER_TITLES, GameList::DEFAULT_COVER_TITLES);
|
||||
s_game_list_locals.compact_mode =
|
||||
Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListCompactMode", true);
|
||||
s_game_list_locals.show_trophy_icons =
|
||||
Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListGridTrophyIcons", true);
|
||||
}
|
||||
|
||||
GPUTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry, bool fallback_to_achievements_icon,
|
||||
bool fallback_to_icon, bool return_default_image)
|
||||
{
|
||||
|
||||
@@ -99,6 +99,7 @@ FileSelectorFilters GetImageFilters();
|
||||
|
||||
void ClearGameListState();
|
||||
void SwitchToGameList();
|
||||
void ReloadGameListSettings();
|
||||
void DrawGameListWindow();
|
||||
|
||||
void DoStartPath(std::string path, std::string state = std::string(), std::optional<bool> fast_boot = std::nullopt);
|
||||
|
||||
@@ -99,7 +99,7 @@ static void StartImageVerification();
|
||||
static void ProcessImageVerificationResults(std::string path, GameDatabase::TrackVerificationResult verification,
|
||||
bool result, bool cancelled, Error error);
|
||||
|
||||
static void DrawSummarySettingsPage(bool show_localized_titles);
|
||||
static void DrawSummarySettingsPage();
|
||||
static void DrawSummarySettingsTrackList();
|
||||
static void OpenGameTitleActions(const std::string_view& path, const std::string_view& title, bool has_custom_title,
|
||||
bool is_disc_set_title);
|
||||
@@ -196,7 +196,7 @@ static void DrawEnumSetting(SettingsInterface* bsi, std::string_view title, std:
|
||||
std::optional<DataType> (*from_string_function)(std::string_view str),
|
||||
const char* (*to_string_function)(DataType value),
|
||||
const char* (*to_display_string_function)(DataType value), SizeType option_count,
|
||||
bool enabled = true, bool use_dropdown = true);
|
||||
bool enabled = true, bool use_dropdown = true, void (*changed_callback)() = nullptr);
|
||||
static void DrawSpeedSelectorSetting(SettingsInterface* bsi, std::string_view title, std::string_view summary,
|
||||
const char* section, const char* key, float default_value, bool enabled = true);
|
||||
static void DrawFolderSetting(SettingsInterface* bsi, std::string_view title, const char* section, const char* key,
|
||||
@@ -1528,7 +1528,8 @@ void FullscreenUI::DrawEnumSetting(SettingsInterface* bsi, std::string_view titl
|
||||
std::optional<DataType> (*from_string_function)(std::string_view str),
|
||||
const char* (*to_string_function)(DataType value),
|
||||
const char* (*to_display_string_function)(DataType value), SizeType option_count,
|
||||
bool enabled /* = true */, bool use_dropdown /* = true */)
|
||||
bool enabled /* = true */, bool use_dropdown /* = true */,
|
||||
void (*changed_callback)() /* = nullptr*/)
|
||||
{
|
||||
const bool game_settings = IsEditingGameSettings(bsi);
|
||||
|
||||
@@ -1577,17 +1578,21 @@ void FullscreenUI::DrawEnumSetting(SettingsInterface* bsi, std::string_view titl
|
||||
if (use_dropdown)
|
||||
{
|
||||
OpenDropdownDialog(title, std::move(cd_options),
|
||||
[section = TinyString(section), key = TinyString(key), to_string_function,
|
||||
[section = TinyString(section), key = TinyString(key), to_string_function, changed_callback,
|
||||
game_settings](s32 index, const std::string& title) {
|
||||
apply_option(section, key, to_string_function, game_settings, index);
|
||||
if (changed_callback)
|
||||
changed_callback();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenChoiceDialog(title, false, std::move(cd_options),
|
||||
[section = TinyString(section), key = TinyString(key), to_string_function,
|
||||
[section = TinyString(section), key = TinyString(key), to_string_function, changed_callback,
|
||||
game_settings](s32 index, const std::string& title, bool checked) {
|
||||
apply_option(section, key, to_string_function, game_settings, index);
|
||||
if (changed_callback)
|
||||
changed_callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1918,7 +1923,6 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
|
||||
const float bg_alpha = GetBackgroundAlpha();
|
||||
const bool blur_background = CanBlurBackground() && s_settings_locals.settings_page != SettingsPage::PostProcessing;
|
||||
const bool show_localized_titles = GameList::ShouldShowLocalizedTitles();
|
||||
|
||||
static constexpr const SettingsPage global_pages[] = {
|
||||
SettingsPage::Interface, SettingsPage::GameList, SettingsPage::BIOS, SettingsPage::Console,
|
||||
@@ -1968,9 +1972,11 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
if (NavButton(ICON_PF_NAVIGATION_BACK, true, true))
|
||||
ReturnToPreviousWindow(TransitionEffect::ZoomOut);
|
||||
|
||||
NavTitle(s_settings_locals.game_settings_entry ?
|
||||
s_settings_locals.game_settings_entry->GetDisplayTitle(show_localized_titles) :
|
||||
Host::TranslateToStringView(FSUI_TR_CONTEXT, titles[static_cast<u32>(pages[index])].first));
|
||||
NavTitle(
|
||||
s_settings_locals.game_settings_entry ?
|
||||
s_settings_locals.game_settings_entry->GetDisplayTitle(Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES)) :
|
||||
Host::TranslateToStringView(FSUI_TR_CONTEXT, titles[static_cast<u32>(pages[index])].first));
|
||||
|
||||
RightAlignNavButtons(count);
|
||||
|
||||
@@ -2029,7 +2035,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
switch (s_settings_locals.settings_page)
|
||||
{
|
||||
case SettingsPage::Summary:
|
||||
DrawSummarySettingsPage(show_localized_titles);
|
||||
DrawSummarySettingsPage();
|
||||
break;
|
||||
|
||||
case SettingsPage::Interface:
|
||||
@@ -2188,7 +2194,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawSummarySettingsPage(bool show_localized_titles)
|
||||
void FullscreenUI::DrawSummarySettingsPage()
|
||||
{
|
||||
BeginMenuButtons();
|
||||
ResetFocusHere();
|
||||
@@ -2206,6 +2212,8 @@ void FullscreenUI::DrawSummarySettingsPage(bool show_localized_titles)
|
||||
{
|
||||
const float title_width = LayoutScale(200.0f);
|
||||
const bool allow_customization = !s_settings_locals.game_settings_entry->is_runtime_populated;
|
||||
const bool show_localized_titles = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES);
|
||||
const std::string_view title = s_settings_locals.game_settings_entry->GetDisplayTitle(show_localized_titles);
|
||||
if (MenuButtonWithInlineValue(s_settings_locals.game_settings_entry->has_custom_title ?
|
||||
FSUI_ICONVSTR(ICON_FA_WINDOW_MAXIMIZE, "Title (*)") :
|
||||
@@ -2891,48 +2899,48 @@ void FullscreenUI::DrawGameListSettingsPage()
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(false);
|
||||
|
||||
BeginMenuButtons();
|
||||
ResetFocusHere();
|
||||
|
||||
MenuHeading(FSUI_VSTR("List Settings"));
|
||||
{
|
||||
static constexpr const char* view_types[] = {FSUI_NSTR("Game Grid"), FSUI_NSTR("Game List")};
|
||||
static constexpr const char* sort_types[] = {
|
||||
FSUI_NSTR("Type"),
|
||||
FSUI_NSTR("Serial"),
|
||||
FSUI_NSTR("Title"),
|
||||
FSUI_NSTR("File Title"),
|
||||
FSUI_NSTR("Time Played"),
|
||||
FSUI_NSTR("Last Played"),
|
||||
FSUI_NSTR("File Size"),
|
||||
FSUI_NSTR("Uncompressed Size"),
|
||||
FSUI_NSTR("Achievement Unlock/Count"),
|
||||
};
|
||||
ResetFocusHere();
|
||||
|
||||
DrawIntListSetting(bsi, FSUI_ICONVSTR(ICON_FA_TABLE_CELLS_LARGE, "Default View"),
|
||||
FSUI_VSTR("Selects the view that the game list will open to."), "Main",
|
||||
"DefaultFullscreenUIGameView", 0, view_types);
|
||||
DrawIntListSetting(bsi, FSUI_ICONVSTR(ICON_FA_SORT, "Sort By"),
|
||||
FSUI_VSTR("Determines that field that the game list will be sorted by."), "Main",
|
||||
"FullscreenUIGameSort", 0, sort_types);
|
||||
DrawToggleSetting(
|
||||
bool settings_changed = DrawToggleSetting(
|
||||
bsi, FSUI_ICONVSTR(ICON_FA_TABLE_CELLS_LARGE, "Grid View"),
|
||||
FSUI_VSTR("Shows the game list as a grid instead of a vertical list."), GameList::UI_SETTING_SECTION,
|
||||
GameList::SETTING_KEY_FULLSCREENUI_GRID_VIEW, GameList::DEFAULT_FULLSCREENUI_GRID_VIEW);
|
||||
DrawEnumSetting(bsi, FSUI_ICONVSTR(ICON_FA_SORT, "Sort By"),
|
||||
FSUI_VSTR("Determines that field that the game list will be sorted by."),
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_COLUMN, GameList::DEFAULT_SORT_COLUMN,
|
||||
&GameList::ParseColumnName, &GameList::GetColumnName, &GameList::GetColumnDisplayName,
|
||||
GameList::Column::MaxCount, false, true, &FullscreenUI::ReloadGameListSettings);
|
||||
settings_changed |= DrawToggleSetting(
|
||||
bsi, FSUI_ICONVSTR(ICON_FA_ARROW_DOWN_Z_A, "Sort Reversed"),
|
||||
FSUI_VSTR("Reverses the game list sort order from the default (usually ascending to descending)."), "Main",
|
||||
"FullscreenUIGameSortReverse", false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_RECTANGLE_LIST, "Merge Multi-Disc Games"),
|
||||
FSUI_VSTR("Merges multi-disc games into one item in the game list."), "Main",
|
||||
"FullscreenUIMergeDiscSets", true);
|
||||
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_LANGUAGE, "Show Localized Titles"),
|
||||
FSUI_VSTR("Uses localized (native language) titles in the game list."), "UI",
|
||||
"GameListShowLocalizedTitles", true);
|
||||
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_TROPHY, "Show Achievement Trophy Icons"),
|
||||
FSUI_VSTR("Shows trophy icons in game grid when games have achievements or have been mastered."),
|
||||
"Main", "FullscreenUIShowTrophyIcons", true);
|
||||
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);
|
||||
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);
|
||||
FSUI_VSTR("Reverses the game list sort order from the default (usually ascending to descending)."),
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_REVERSED, GameList::DEFAULT_SORT_REVERSED);
|
||||
settings_changed |= DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_RECTANGLE_LIST, "Merge Multi-Disc Games"),
|
||||
FSUI_VSTR("Merges multi-disc games into one item in the game list."),
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_MERGE_DISC_SETS,
|
||||
GameList::DEFAULT_MERGE_DISC_SETS);
|
||||
settings_changed |= DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_LANGUAGE, "Show Localized Titles"),
|
||||
FSUI_VSTR("Uses localized (native language) titles in the game list."),
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES,
|
||||
GameList::DEFAULT_LOCALIZED_TITLES);
|
||||
settings_changed |= DrawToggleSetting(
|
||||
bsi, FSUI_ICONVSTR(ICON_FA_TROPHY, "Show Achievement Trophy Icons"),
|
||||
FSUI_VSTR("Shows trophy icons in game grid when games have achievements or have been mastered."),
|
||||
GameList::UI_SETTING_SECTION, "GameListGridTrophyIcons", true);
|
||||
settings_changed |= DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_TAGS, "Show Grid View Titles"),
|
||||
FSUI_VSTR("Shows titles underneath the images in the game grid view."),
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_COVER_TITLES,
|
||||
GameList::DEFAULT_COVER_TITLES);
|
||||
settings_changed |= 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."),
|
||||
GameList::UI_SETTING_SECTION, "GameListCompactMode", true);
|
||||
|
||||
// Need to queue because we have the settings lock...
|
||||
if (settings_changed)
|
||||
Host::RunOnCoreThread([]() { VideoThread::RunOnThread(&FullscreenUI::ReloadGameListSettings); });
|
||||
}
|
||||
|
||||
MenuHeading(FSUI_VSTR("Search Directories"));
|
||||
|
||||
@@ -71,7 +71,6 @@ TRANSLATE_NOOP("FullscreenUI", "A resume save state was found for this game, but
|
||||
TRANSLATE_NOOP("FullscreenUI", "AMOLED");
|
||||
TRANSLATE_NOOP("FullscreenUI", "About");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Achievement Notifications");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Achievement Unlock/Count");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Achievement Unlocks Unconfirmed");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Achievements");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Achievements Settings");
|
||||
@@ -228,7 +227,6 @@ TRANSLATE_NOOP("FullscreenUI", "Debugging Settings");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default Boot");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default Value");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default View");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default: Disabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default: Enabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default: {}");
|
||||
@@ -387,8 +385,6 @@ TRANSLATE_NOOP("FullscreenUI", "Fast Forward Speed");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fast Forward Volume");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fast forwards through memory card access, both loading and saving. Can reduce waiting times in games that frequently access memory cards.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fast forwards through the early loading process when fast booting, saving time. Results may vary between games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "File Size");
|
||||
TRANSLATE_NOOP("FullscreenUI", "File Title");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Filter Framebuffer Uploads");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fine Crop Amount");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fine Crop Mode");
|
||||
@@ -436,6 +432,7 @@ TRANSLATE_NOOP("FullscreenUI", "Global Slot {0}##global_slot_{0}");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Graphics Settings");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Green Giant");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Grey Matter");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Grid View");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Hardcore Mode");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Hardcore mode will not be enabled until the game is restarted. Do you want to restart the game now?");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Hash Calculation Failed");
|
||||
@@ -467,7 +464,6 @@ TRANSLATE_NOOP("FullscreenUI", "Interface Settings");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Language");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Language: ");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Last Played");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Last Played: ");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Latency Control");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Launch Options");
|
||||
@@ -750,7 +746,6 @@ TRANSLATE_NOOP("FullscreenUI", "Selects the resolution to use in fullscreen mode
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects the screen location for achievement and leaderboard notifications.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects the screen location for challenge/progress indicators, and leaderboard trackers.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects the type of emulated controller for this port.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects the view that the game list will open to.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects which location on the screen messages are displayed.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Serial");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Serial (*)");
|
||||
@@ -804,6 +799,7 @@ TRANSLATE_NOOP("FullscreenUI", "Shows persistent icons when turbo is active or w
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the current controller state of the system in the bottom-left corner of the display.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the current emulation speed of the system in the top-right corner of the display as a percentage.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the current rendering resolution of the system in the top-right corner of the display.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the game list as a grid instead of a vertical list.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the game you are currently playing as part of your profile in Discord.");
|
||||
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.");
|
||||
@@ -867,7 +863,6 @@ TRANSLATE_NOOP("FullscreenUI", "The unlock state for '{}' will be removed. This
|
||||
TRANSLATE_NOOP("FullscreenUI", "Theme");
|
||||
TRANSLATE_NOOP("FullscreenUI", "This game was not scanned by DuckStation. Some functionality is not available.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Threaded Rendering");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Time Played");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Time Played: ");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Timing out in {:.0f} seconds...");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Title");
|
||||
@@ -888,7 +883,6 @@ TRANSLATE_NOOP("FullscreenUI", "Tries to detect FMVs and disable read speedup du
|
||||
TRANSLATE_NOOP("FullscreenUI", "Trigger");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Turbo Speed");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Type");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Uncompressed Size");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Undo Load State");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Ungrouped");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Unknown");
|
||||
|
||||
+297
-77
@@ -165,6 +165,87 @@ ALIGN_TO_CACHE_LINE static State s_state;
|
||||
|
||||
} // namespace GameList
|
||||
|
||||
const char* const GameList::UI_SETTING_SECTION = "UI";
|
||||
constexpr const char* const GameList::SETTING_KEY_LOCALIZED_TITLES = "GameListShowLocalizedTitles";
|
||||
constexpr const char* const GameList::SETTING_KEY_SORT_COLUMN = "GameListSortColumn";
|
||||
constexpr const char* const GameList::SETTING_KEY_SORT_REVERSED = "GameListSortReversed";
|
||||
constexpr const char* const GameList::SETTING_KEY_PREFER_ACHIEVEMENT_CONTENT_ICONS =
|
||||
"GameListPreferAchievementContentIcons";
|
||||
constexpr const char* const GameList::SETTING_KEY_MERGE_DISC_SETS = "GameListMergeDiscSets";
|
||||
constexpr const char* const GameList::SETTING_KEY_GRID_VIEW = "GameListGridView";
|
||||
constexpr const char* const GameList::SETTING_KEY_FULLSCREENUI_GRID_VIEW = "GameListFullscreenUIGridView";
|
||||
constexpr const char* const GameList::SETTING_KEY_COVER_TITLES = "GameListCoverTitles";
|
||||
|
||||
GameList::Entry::Entry() = default;
|
||||
|
||||
GameList::Entry::~Entry() = default;
|
||||
|
||||
std::string_view GameList::Entry::GetDisplayTitle(bool localized) const
|
||||
{
|
||||
// if custom title is present, use that for display too
|
||||
return !title.empty() ? std::string_view(title) :
|
||||
(IsDiscSet() ? dbentry->disc_set->GetDisplayTitle(localized) :
|
||||
(dbentry ? dbentry->GetDisplayTitle(localized) : std::string_view()));
|
||||
}
|
||||
|
||||
std::string_view GameList::Entry::GetSortTitle() const
|
||||
{
|
||||
// if custom title is present, use that for sorting too
|
||||
return !title.empty() ?
|
||||
std::string_view(title) :
|
||||
(IsDiscSet() ? dbentry->disc_set->GetSortTitle() : (dbentry ? dbentry->GetSortTitle() : std::string_view()));
|
||||
}
|
||||
|
||||
std::string_view GameList::Entry::GetSaveTitle() const
|
||||
{
|
||||
// if custom title is present, use that for save folder too
|
||||
return !title.empty() ?
|
||||
std::string_view(title) :
|
||||
(IsDiscSet() ? dbentry->disc_set->GetSaveTitle() : (dbentry ? dbentry->GetSaveTitle() : std::string_view()));
|
||||
}
|
||||
|
||||
std::string_view GameList::Entry::GetLanguageIcon() const
|
||||
{
|
||||
std::string_view ret;
|
||||
if (custom_language != GameDatabase::Language::MaxCount)
|
||||
ret = GameDatabase::GetLanguageName(custom_language);
|
||||
else if (dbentry)
|
||||
ret = dbentry->GetLanguageFlagName(region);
|
||||
else
|
||||
ret = Settings::GetDiscRegionName(region);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
TinyString GameList::Entry::GetLanguageIconName() const
|
||||
{
|
||||
return GameDatabase::GetLanguageFlagResourceName(GetLanguageIcon());
|
||||
}
|
||||
|
||||
TinyString GameList::Entry::GetCompatibilityIconFileName() const
|
||||
{
|
||||
return TinyString::from_format(
|
||||
"images/star-{}.svg",
|
||||
static_cast<u32>(dbentry ? dbentry->compatibility : GameDatabase::CompatibilityRating::Unknown));
|
||||
}
|
||||
|
||||
std::string GameList::Entry::GetReleaseDateString() const
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
if (!dbentry || dbentry->release_date == 0)
|
||||
ret = TRANSLATE_STR("GameList", "Unknown");
|
||||
else
|
||||
ret = Host::FormatDate(static_cast<std::time_t>(dbentry->release_date), false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GameList::Entry::IsGame() const
|
||||
{
|
||||
return (type != EntryType::PSF && type != EntryType::AudioCD && !serial.empty());
|
||||
}
|
||||
|
||||
const char* GameList::GetEntryTypeName(EntryType type)
|
||||
{
|
||||
static std::array<const char*, static_cast<int>(EntryType::MaxCount)> names = {{
|
||||
@@ -191,22 +272,223 @@ const char* GameList::GetEntryTypeDisplayName(EntryType type)
|
||||
return Host::TranslateToCString("GameList", names[static_cast<size_t>(type)], "EntryType");
|
||||
}
|
||||
|
||||
static constexpr std::array<const char*, static_cast<size_t>(GameList::Column::MaxCount)> s_column_names = {{
|
||||
"Type",
|
||||
"Serial",
|
||||
"Title",
|
||||
"FileTitle",
|
||||
"Developer",
|
||||
"Publisher",
|
||||
"Genre",
|
||||
"Year",
|
||||
"Players",
|
||||
"TimePlayed",
|
||||
"LastPlayed",
|
||||
"Size",
|
||||
"DataSize",
|
||||
"Region",
|
||||
"Achievements",
|
||||
"Compatibility",
|
||||
}};
|
||||
|
||||
static constexpr std::array<const char*, static_cast<size_t>(GameList::Column::MaxCount)> s_column_display_names = {{
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Type", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Serial", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Title", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "File Title", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Developer", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Publisher", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Genre", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Year", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Players", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Time Played", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Last Played", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Size", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Data Size", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Region", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Achievements", "Column"),
|
||||
TRANSLATE_DISAMBIG_NOOP("GameList", "Compatibility", "Column"),
|
||||
}};
|
||||
|
||||
const char* GameList::GetColumnName(Column column)
|
||||
{
|
||||
return (static_cast<size_t>(column) < s_column_names.size()) ? s_column_names[static_cast<size_t>(column)] : "";
|
||||
}
|
||||
|
||||
const char* GameList::GetColumnDisplayName(Column column)
|
||||
{
|
||||
return (static_cast<size_t>(column) < s_column_display_names.size()) ?
|
||||
Host::TranslateToCString("GameList", s_column_display_names[static_cast<size_t>(column)], "Column") :
|
||||
"";
|
||||
}
|
||||
|
||||
std::optional<GameList::Column> GameList::ParseColumnName(std::string_view name)
|
||||
{
|
||||
for (size_t i = 0; i < s_column_names.size(); i++)
|
||||
{
|
||||
if (name == s_column_names[i])
|
||||
return static_cast<Column>(i);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool GameList::CompareEntryLessThan(const Entry* left, const Entry* right, Column column)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case Column::Type:
|
||||
{
|
||||
const GameList::EntryType lst = left->GetSortType();
|
||||
const GameList::EntryType rst = right->GetSortType();
|
||||
if (lst == rst)
|
||||
return CompareEntryTitlesLessThan(left, right);
|
||||
|
||||
return (static_cast<int>(lst) < static_cast<int>(rst));
|
||||
}
|
||||
|
||||
case Column::Serial:
|
||||
{
|
||||
const int res = StringUtil::CompareNoCase(left->serial, right->serial);
|
||||
return (res == 0) ? CompareEntryTitlesLessThan(left, right) : (res < 0);
|
||||
}
|
||||
|
||||
case Column::Title:
|
||||
{
|
||||
return CompareEntryTitlesLessThan(left, right);
|
||||
}
|
||||
|
||||
case Column::FileTitle:
|
||||
{
|
||||
const std::string_view file_title_left = Path::GetFileTitle(left->path);
|
||||
const std::string_view file_title_right = Path::GetFileTitle(right->path);
|
||||
const int res = StringUtil::CompareNoCase(file_title_left, file_title_right);
|
||||
return (res == 0) ? CompareEntryTitlesLessThan(left, right) : (res < 0);
|
||||
}
|
||||
|
||||
case Column::Developer:
|
||||
{
|
||||
const int res = StringUtil::CompareNoCase(left->dbentry ? left->dbentry->developer : std::string_view(),
|
||||
right->dbentry ? right->dbentry->developer : std::string_view());
|
||||
return (res == 0) ? CompareEntryTitlesLessThan(left, right) : (res < 0);
|
||||
}
|
||||
|
||||
case Column::Publisher:
|
||||
{
|
||||
const int res = StringUtil::CompareNoCase(left->dbentry ? left->dbentry->publisher : std::string_view(),
|
||||
right->dbentry ? right->dbentry->publisher : std::string_view());
|
||||
return (res == 0) ? CompareEntryTitlesLessThan(left, right) : (res < 0);
|
||||
}
|
||||
|
||||
case Column::Genre:
|
||||
{
|
||||
const int res = StringUtil::CompareNoCase(left->dbentry ? left->dbentry->genre : std::string_view(),
|
||||
right->dbentry ? right->dbentry->genre : std::string_view());
|
||||
return (res == 0) ? CompareEntryTitlesLessThan(left, right) : (res < 0);
|
||||
}
|
||||
|
||||
case Column::Year:
|
||||
{
|
||||
const u64 ldate = left->dbentry ? left->dbentry->release_date : 0;
|
||||
const u64 rdate = right->dbentry ? right->dbentry->release_date : 0;
|
||||
return (ldate == rdate) ? CompareEntryTitlesLessThan(left, right) : (ldate < rdate);
|
||||
}
|
||||
|
||||
case Column::Players:
|
||||
{
|
||||
const u8 left_players = left->dbentry ? ((left->dbentry->min_players << 4) + left->dbentry->max_players) : 0;
|
||||
const u8 right_players = right->dbentry ? ((right->dbentry->min_players << 4) + right->dbentry->max_players) : 0;
|
||||
return (left_players == right_players) ? CompareEntryTitlesLessThan(left, right) : (left_players < right_players);
|
||||
}
|
||||
|
||||
case Column::TimePlayed:
|
||||
{
|
||||
return (left->total_played_time == right->total_played_time) ?
|
||||
CompareEntryTitlesLessThan(left, right) :
|
||||
(left->total_played_time < right->total_played_time);
|
||||
}
|
||||
|
||||
case Column::LastPlayed:
|
||||
{
|
||||
return (left->last_played_time == right->last_played_time) ? CompareEntryTitlesLessThan(left, right) :
|
||||
(left->last_played_time < right->last_played_time);
|
||||
}
|
||||
|
||||
case Column::FileSize:
|
||||
{
|
||||
return (left->file_size == right->file_size) ? CompareEntryTitlesLessThan(left, right) :
|
||||
(left->file_size < right->file_size);
|
||||
}
|
||||
|
||||
case Column::DataSize:
|
||||
{
|
||||
return (left->uncompressed_size == right->uncompressed_size) ?
|
||||
CompareEntryTitlesLessThan(left, right) :
|
||||
(left->uncompressed_size < right->uncompressed_size);
|
||||
}
|
||||
|
||||
case Column::Region:
|
||||
{
|
||||
return (left->region == right->region) ? CompareEntryTitlesLessThan(left, right) :
|
||||
(static_cast<int>(left->region) < static_cast<int>(right->region));
|
||||
}
|
||||
|
||||
case Column::Achievements:
|
||||
{
|
||||
// sort by unlock percentage
|
||||
const float unlock_left =
|
||||
(left->num_achievements > 0) ?
|
||||
(static_cast<float>(std::max(left->unlocked_achievements, left->unlocked_achievements_hc)) /
|
||||
static_cast<float>(left->num_achievements)) :
|
||||
0;
|
||||
const float unlock_right =
|
||||
(right->num_achievements > 0) ?
|
||||
(static_cast<float>(std::max(right->unlocked_achievements, right->unlocked_achievements_hc)) /
|
||||
static_cast<float>(right->num_achievements)) :
|
||||
0;
|
||||
if (std::abs(unlock_left - unlock_right) < 0.0001f)
|
||||
{
|
||||
// order by achievement count
|
||||
if (left->num_achievements == right->num_achievements)
|
||||
return CompareEntryTitlesLessThan(left, right);
|
||||
|
||||
return (left->num_achievements < right->num_achievements);
|
||||
}
|
||||
|
||||
return (unlock_left < unlock_right);
|
||||
}
|
||||
|
||||
case Column::Compatibilty:
|
||||
{
|
||||
const GameDatabase::CompatibilityRating left_compatibility =
|
||||
left->dbentry ? left->dbentry->compatibility : GameDatabase::CompatibilityRating::Unknown;
|
||||
const GameDatabase::CompatibilityRating right_compatibility =
|
||||
right->dbentry ? right->dbentry->compatibility : GameDatabase::CompatibilityRating::Unknown;
|
||||
return (left_compatibility == right_compatibility) ? CompareEntryTitlesLessThan(left, right) :
|
||||
(left_compatibility < right_compatibility);
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GameList::CompareEntryTitlesLessThan(const Entry* left, const Entry* right)
|
||||
{
|
||||
const s32 res = StringUtil::CompareNoCase(left->GetSortTitle(), right->GetSortTitle());
|
||||
if (res != 0)
|
||||
return (res < 0);
|
||||
|
||||
// Fallback to path compare if titles are the same.
|
||||
return (left->path < right->path);
|
||||
}
|
||||
|
||||
bool GameList::IsGameListLoaded()
|
||||
{
|
||||
const std::unique_lock lock(s_state.mutex);
|
||||
return (s_state.game_list_loaded != ListState::Unloaded);
|
||||
}
|
||||
|
||||
bool GameList::ShouldShowLocalizedTitles()
|
||||
{
|
||||
return Core::GetBaseBoolSettingValue("UI", "GameListShowLocalizedTitles", true);
|
||||
}
|
||||
|
||||
bool GameList::PreferAchievementGameBadgesForIcons()
|
||||
{
|
||||
return Core::GetBaseBoolSettingValue("UI", "GameListPreferAchievementGameBadgesForIcons", false);
|
||||
}
|
||||
|
||||
bool GameList::IsScannableFilename(std::string_view path)
|
||||
{
|
||||
// we don't scan bin files because they'll duplicate with cue, same with img for ccd
|
||||
@@ -1424,72 +1706,6 @@ std::string GameList::GetNewCoverImagePathForEntry(const Entry* entry, const cha
|
||||
return Path::Combine(EmuFolders::Covers, Path::SanitizeFileName(filename));
|
||||
}
|
||||
|
||||
std::string_view GameList::Entry::GetDisplayTitle(bool localized) const
|
||||
{
|
||||
// if custom title is present, use that for display too
|
||||
return !title.empty() ? std::string_view(title) :
|
||||
(IsDiscSet() ? dbentry->disc_set->GetDisplayTitle(localized) :
|
||||
(dbentry ? dbentry->GetDisplayTitle(localized) : std::string_view()));
|
||||
}
|
||||
|
||||
std::string_view GameList::Entry::GetSortTitle() const
|
||||
{
|
||||
// if custom title is present, use that for sorting too
|
||||
return !title.empty() ?
|
||||
std::string_view(title) :
|
||||
(IsDiscSet() ? dbentry->disc_set->GetSortTitle() : (dbentry ? dbentry->GetSortTitle() : std::string_view()));
|
||||
}
|
||||
|
||||
std::string_view GameList::Entry::GetSaveTitle() const
|
||||
{
|
||||
// if custom title is present, use that for save folder too
|
||||
return !title.empty() ?
|
||||
std::string_view(title) :
|
||||
(IsDiscSet() ? dbentry->disc_set->GetSaveTitle() : (dbentry ? dbentry->GetSaveTitle() : std::string_view()));
|
||||
}
|
||||
|
||||
std::string_view GameList::Entry::GetLanguageIcon() const
|
||||
{
|
||||
std::string_view ret;
|
||||
if (custom_language != GameDatabase::Language::MaxCount)
|
||||
ret = GameDatabase::GetLanguageName(custom_language);
|
||||
else if (dbentry)
|
||||
ret = dbentry->GetLanguageFlagName(region);
|
||||
else
|
||||
ret = Settings::GetDiscRegionName(region);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
TinyString GameList::Entry::GetLanguageIconName() const
|
||||
{
|
||||
return GameDatabase::GetLanguageFlagResourceName(GetLanguageIcon());
|
||||
}
|
||||
|
||||
TinyString GameList::Entry::GetCompatibilityIconFileName() const
|
||||
{
|
||||
return TinyString::from_format(
|
||||
"images/star-{}.svg",
|
||||
static_cast<u32>(dbentry ? dbentry->compatibility : GameDatabase::CompatibilityRating::Unknown));
|
||||
}
|
||||
|
||||
std::string GameList::Entry::GetReleaseDateString() const
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
if (!dbentry || dbentry->release_date == 0)
|
||||
ret = TRANSLATE_STR("GameList", "Unknown");
|
||||
else
|
||||
ret = Host::FormatDate(static_cast<std::time_t>(dbentry->release_date), false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GameList::Entry::IsGame() const
|
||||
{
|
||||
return (type != EntryType::PSF && type != EntryType::AudioCD && !serial.empty());
|
||||
}
|
||||
|
||||
std::string GameList::GetPlayedTimePath()
|
||||
{
|
||||
return Path::Combine(EmuFolders::DataRoot, "playtime.dat");
|
||||
@@ -2140,8 +2356,12 @@ std::string GameList::GetGameIconPath(std::string_view custom_title, std::string
|
||||
if (achievements_game_id != 0)
|
||||
{
|
||||
fallback_path = Achievements::GetGameBadgeURL(achievements_game_id);
|
||||
if (!fallback_path.empty() && PreferAchievementGameBadgesForIcons())
|
||||
if (!fallback_path.empty() &&
|
||||
Core::GetBaseBoolSettingValue(UI_SETTING_SECTION, SETTING_KEY_PREFER_ACHIEVEMENT_CONTENT_ICONS,
|
||||
DEFAULT_PREFER_ACHIEVEMENT_CONTENT_ICONS))
|
||||
{
|
||||
return (ret = std::move(fallback_path));
|
||||
}
|
||||
}
|
||||
|
||||
if (serial.empty())
|
||||
|
||||
+49
-3
@@ -31,8 +31,33 @@ enum class EntryType : u8
|
||||
MaxCount
|
||||
};
|
||||
|
||||
enum class Column : u8
|
||||
{
|
||||
Type,
|
||||
Serial,
|
||||
Title,
|
||||
FileTitle,
|
||||
Developer,
|
||||
Publisher,
|
||||
Genre,
|
||||
Year,
|
||||
Players,
|
||||
TimePlayed,
|
||||
LastPlayed,
|
||||
FileSize,
|
||||
DataSize,
|
||||
Region,
|
||||
Achievements,
|
||||
Compatibilty,
|
||||
|
||||
MaxCount,
|
||||
};
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Entry();
|
||||
~Entry();
|
||||
|
||||
EntryType type = EntryType::MaxCount;
|
||||
DiscRegion region = DiscRegion::Other;
|
||||
|
||||
@@ -102,6 +127,12 @@ using EntryList = std::vector<Entry>;
|
||||
const char* GetEntryTypeName(EntryType type);
|
||||
const char* GetEntryTypeDisplayName(EntryType type);
|
||||
|
||||
const char* GetColumnName(Column column);
|
||||
const char* GetColumnDisplayName(Column column);
|
||||
std::optional<Column> ParseColumnName(std::string_view name);
|
||||
bool CompareEntryLessThan(const Entry* left, const Entry* right, Column column);
|
||||
bool CompareEntryTitlesLessThan(const Entry* left, const Entry* right);
|
||||
|
||||
bool IsScannableFilename(std::string_view path);
|
||||
|
||||
/// Populates a game list entry struct with information from the specified path.
|
||||
@@ -119,7 +150,6 @@ const Entry* GetFirstDiscSetMember(const GameDatabase::DiscSetEntry* dsentry);
|
||||
size_t GetEntryCount();
|
||||
|
||||
bool IsGameListLoaded();
|
||||
bool ShouldShowLocalizedTitles();
|
||||
|
||||
/// Returns true if the specified path should not have game properties saved.
|
||||
bool CanEditGameSettingsForPath(const std::string_view path, const std::string_view serial);
|
||||
@@ -193,8 +223,24 @@ void UpdateAchievementData(std::span<const u8, 16> hash, u32 game_id, u32 num_ac
|
||||
u32 num_unlocked_hardcore);
|
||||
void UpdateAllAchievementData();
|
||||
|
||||
/// Accesses achievement game badges. Assumes the lock is held.
|
||||
bool PreferAchievementGameBadgesForIcons();
|
||||
// Setting keys
|
||||
extern const char* const UI_SETTING_SECTION;
|
||||
extern const char* const SETTING_KEY_LOCALIZED_TITLES;
|
||||
extern const char* const SETTING_KEY_SORT_COLUMN;
|
||||
extern const char* const SETTING_KEY_SORT_REVERSED;
|
||||
extern const char* const SETTING_KEY_PREFER_ACHIEVEMENT_CONTENT_ICONS;
|
||||
extern const char* const SETTING_KEY_MERGE_DISC_SETS;
|
||||
extern const char* const SETTING_KEY_GRID_VIEW;
|
||||
extern const char* const SETTING_KEY_FULLSCREENUI_GRID_VIEW;
|
||||
extern const char* const SETTING_KEY_COVER_TITLES;
|
||||
inline constexpr bool DEFAULT_LOCALIZED_TITLES = true;
|
||||
inline constexpr Column DEFAULT_SORT_COLUMN = Column::Title;
|
||||
inline constexpr bool DEFAULT_SORT_REVERSED = false;
|
||||
inline constexpr bool DEFAULT_PREFER_ACHIEVEMENT_CONTENT_ICONS = false;
|
||||
inline constexpr bool DEFAULT_MERGE_DISC_SETS = true;
|
||||
inline constexpr bool DEFAULT_GRID_VIEW = false;
|
||||
inline constexpr bool DEFAULT_FULLSCREENUI_GRID_VIEW = true;
|
||||
inline constexpr bool DEFAULT_COVER_TITLES = true;
|
||||
|
||||
} // namespace GameList
|
||||
|
||||
|
||||
@@ -162,7 +162,6 @@ std::span<const char* const> Settings::GetSectionSaveOrder()
|
||||
"Cheats",
|
||||
"Main",
|
||||
"UI",
|
||||
"GameListTableView",
|
||||
"AutoUpdater",
|
||||
"Folders",
|
||||
"GameList",
|
||||
|
||||
+6
-4
@@ -4175,8 +4175,9 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
|
||||
s_state.running_game_entry = GameDatabase::GetEntryForSerial(s_state.running_game_serial);
|
||||
if (s_state.running_game_entry && s_state.running_game_title.empty())
|
||||
{
|
||||
s_state.running_game_title =
|
||||
s_state.running_game_entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles());
|
||||
s_state.running_game_title = s_state.running_game_entry->GetDisplayTitle(
|
||||
Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES,
|
||||
GameList::DEFAULT_LOCALIZED_TITLES));
|
||||
}
|
||||
else if (s_state.running_game_title.empty())
|
||||
{
|
||||
@@ -4204,8 +4205,9 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
|
||||
|
||||
if (s_state.running_game_title.empty())
|
||||
{
|
||||
s_state.running_game_title =
|
||||
s_state.running_game_entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles());
|
||||
s_state.running_game_title = s_state.running_game_entry->GetDisplayTitle(
|
||||
Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES,
|
||||
GameList::DEFAULT_LOCALIZED_TITLES));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -83,25 +83,21 @@ static const char* SUPPORTED_FORMATS_STRING =
|
||||
".chd (Compressed Hunks of Data)\n"
|
||||
".pbp (PlayStation Portable, Only Decrypted)");
|
||||
|
||||
static constexpr std::array<const char*, GameListModel::Column_Count> s_column_names = {{
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Icon"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Serial"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Title"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "File Title"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Developer"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Publisher"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Genre"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Year"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Players"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Time Played"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Last Played"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Size"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Data Size"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Region"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Achievements"),
|
||||
QT_TRANSLATE_NOOP("GameListModel", "Compatibility"),
|
||||
"Cover", // Do not translate.
|
||||
}};
|
||||
static_assert(static_cast<int>(GameList::Column::Type) == GameListModel::Column_Icon);
|
||||
static_assert(static_cast<int>(GameList::Column::Serial) == GameListModel::Column_Serial);
|
||||
static_assert(static_cast<int>(GameList::Column::Title) == GameListModel::Column_Title);
|
||||
static_assert(static_cast<int>(GameList::Column::FileTitle) == GameListModel::Column_FileTitle);
|
||||
static_assert(static_cast<int>(GameList::Column::Developer) == GameListModel::Column_Developer);
|
||||
static_assert(static_cast<int>(GameList::Column::Publisher) == GameListModel::Column_Publisher);
|
||||
static_assert(static_cast<int>(GameList::Column::Genre) == GameListModel::Column_Genre);
|
||||
static_assert(static_cast<int>(GameList::Column::Year) == GameListModel::Column_Year);
|
||||
static_assert(static_cast<int>(GameList::Column::Players) == GameListModel::Column_Players);
|
||||
static_assert(static_cast<int>(GameList::Column::TimePlayed) == GameListModel::Column_TimePlayed);
|
||||
static_assert(static_cast<int>(GameList::Column::LastPlayed) == GameListModel::Column_LastPlayed);
|
||||
static_assert(static_cast<int>(GameList::Column::FileSize) == GameListModel::Column_FileSize);
|
||||
static_assert(static_cast<int>(GameList::Column::DataSize) == GameListModel::Column_DataSize);
|
||||
static_assert(static_cast<int>(GameList::Column::Region) == GameListModel::Column_Region);
|
||||
static_assert(static_cast<int>(GameList::Column::Achievements) == GameListModel::Column_Achievements);
|
||||
|
||||
static void resizeImage(QImage* image, const QSize& expected_size)
|
||||
{
|
||||
@@ -222,31 +218,11 @@ static QString sizeToString(s64 size)
|
||||
QCoreApplication::translate("GameList", "Unknown");
|
||||
}
|
||||
|
||||
std::optional<GameListModel::Column> GameListModel::getColumnIdForName(std::string_view name)
|
||||
{
|
||||
for (int column = 0; column < Column_Count; column++)
|
||||
{
|
||||
if (name == s_column_names[column])
|
||||
return static_cast<Column>(column);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* GameListModel::getColumnName(Column col)
|
||||
{
|
||||
return s_column_names[static_cast<int>(col)];
|
||||
}
|
||||
|
||||
GameListModel::GameListModel(GameListWidget* parent)
|
||||
: QAbstractTableModel(parent), m_device_pixel_ratio(parent->devicePixelRatio()),
|
||||
m_icon_pixmap_cache(MIN_COVER_CACHE_SIZE)
|
||||
{
|
||||
m_cover_scale = Core::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", DEFAULT_COVER_SCALE);
|
||||
m_icon_size = Core::GetBaseIntSettingValue("UI", "GameListIconSize", GAME_ICON_DEFAULT_SIZE);
|
||||
m_show_localized_titles = GameList::ShouldShowLocalizedTitles();
|
||||
m_show_titles_for_covers = Core::GetBaseBoolSettingValue("UI", "GameListShowCoverTitles", true);
|
||||
m_show_game_icons = Core::GetBaseBoolSettingValue("UI", "GameListShowGameIcons", true);
|
||||
reloadSettings();
|
||||
|
||||
loadCommonImages();
|
||||
loadCoverScaleDependentPixmaps();
|
||||
@@ -259,6 +235,30 @@ GameListModel::GameListModel(GameListWidget* parent)
|
||||
|
||||
GameListModel::~GameListModel() = default;
|
||||
|
||||
void GameListModel::loadSettings()
|
||||
{
|
||||
m_cover_scale =
|
||||
Core::GetBaseFloatSettingValue(GameList::UI_SETTING_SECTION, "GameListCoverArtScale", DEFAULT_COVER_SCALE);
|
||||
m_icon_size = Core::GetBaseIntSettingValue(GameList::UI_SETTING_SECTION, "GameListIconSize", GAME_ICON_DEFAULT_SIZE);
|
||||
m_show_localized_titles = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES);
|
||||
m_show_titles_for_covers = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_COVER_TITLES, GameList::DEFAULT_COVER_TITLES);
|
||||
m_show_game_icons = Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListShowGameIcons", true);
|
||||
}
|
||||
|
||||
void GameListModel::reloadSettings()
|
||||
{
|
||||
setCoverScale(
|
||||
Core::GetBaseFloatSettingValue(GameList::UI_SETTING_SECTION, "GameListCoverArtScale", DEFAULT_COVER_SCALE));
|
||||
setIconSize(Core::GetBaseIntSettingValue(GameList::UI_SETTING_SECTION, "GameListIconSize", GAME_ICON_DEFAULT_SIZE));
|
||||
setShowLocalizedTitles(Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES));
|
||||
setShowCoverTitles(Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_COVER_TITLES,
|
||||
GameList::DEFAULT_COVER_TITLES));
|
||||
setShowGameIcons(Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListShowGameIcons", true));
|
||||
}
|
||||
|
||||
bool GameListModel::getShowLocalizedTitles() const
|
||||
{
|
||||
return m_show_localized_titles;
|
||||
@@ -303,9 +303,6 @@ void GameListModel::setIconSize(int size)
|
||||
|
||||
m_icon_size = size;
|
||||
|
||||
Core::SetBaseIntSettingValue("UI", "GameListIconSize", size);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
emit iconSizeChanged(m_icon_size);
|
||||
|
||||
// Might look odd, but this is needed to force the section sizes to invalidate
|
||||
@@ -333,9 +330,10 @@ void GameListModel::setCoverScale(float scale)
|
||||
m_cover_pixmap_cache.Apply(
|
||||
[](const std::string&, CoverPixmapCacheEntry& entry) { entry.scale = entry.is_loading ? 0.0f : entry.scale; });
|
||||
|
||||
Core::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", scale);
|
||||
Host::CommitBaseSettingChanges();
|
||||
updateCoverScale();
|
||||
loadCoverScaleDependentPixmaps();
|
||||
emit coverScaleChanged(m_cover_scale);
|
||||
emit dataChanged(index(0, Column_Cover), index(rowCount() - 1, Column_Cover),
|
||||
{Qt::DecorationRole, Qt::FontRole, Qt::SizeHintRole});
|
||||
}
|
||||
|
||||
void GameListModel::loadCoverScaleDependentPixmaps()
|
||||
@@ -368,14 +366,6 @@ void GameListModel::loadCoverScaleDependentPixmaps()
|
||||
}
|
||||
}
|
||||
|
||||
void GameListModel::updateCoverScale()
|
||||
{
|
||||
loadCoverScaleDependentPixmaps();
|
||||
emit coverScaleChanged(m_cover_scale);
|
||||
emit dataChanged(index(0, Column_Cover), index(rowCount() - 1, Column_Cover),
|
||||
{Qt::DecorationRole, Qt::FontRole, Qt::SizeHintRole});
|
||||
}
|
||||
|
||||
void GameListModel::updateCacheSize(int num_rows, int num_columns, QSortFilterProxyModel* const sort_model,
|
||||
int top_left_row)
|
||||
{
|
||||
@@ -1108,11 +1098,10 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
|
||||
|
||||
QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QVariant ret;
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < Column_Count)
|
||||
ret = QCoreApplication::translate("GameListModel", s_column_names[static_cast<u32>(section)]);
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section <= Column_LastVisible)
|
||||
return QString::fromUtf8(GameList::GetColumnDisplayName(static_cast<GameList::Column>(section)));
|
||||
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
const QPixmap& GameListModel::getNoAchievementsPixmap() const
|
||||
@@ -1162,16 +1151,6 @@ void GameListModel::refresh()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
bool GameListModel::titlesLessThan(const GameList::Entry* left, const GameList::Entry* right) const
|
||||
{
|
||||
const s32 res = StringUtil::CompareNoCase(left->GetSortTitle(), right->GetSortTitle());
|
||||
if (res != 0)
|
||||
return (res < 0);
|
||||
|
||||
// Fallback to path compare if titles are the same.
|
||||
return (left->path < right->path);
|
||||
}
|
||||
|
||||
bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& right_index, int column) const
|
||||
{
|
||||
if (!left_index.isValid() || !right_index.isValid())
|
||||
@@ -1188,7 +1167,7 @@ bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& r
|
||||
if (!left || !right)
|
||||
return false;
|
||||
|
||||
return lessThan(left, right, column);
|
||||
return GameList::CompareEntryLessThan(left, right, static_cast<GameList::Column>(column));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1198,169 +1177,7 @@ bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& r
|
||||
if (!left || !right)
|
||||
return false;
|
||||
|
||||
return lessThan(left, right, column);
|
||||
}
|
||||
}
|
||||
|
||||
bool GameListModel::lessThan(const GameList::Entry* left, const GameList::Entry* right, int column) const
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case Column_Icon:
|
||||
{
|
||||
const GameList::EntryType lst = left->GetSortType();
|
||||
const GameList::EntryType rst = right->GetSortType();
|
||||
if (lst == rst)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (static_cast<int>(lst) < static_cast<int>(rst));
|
||||
}
|
||||
|
||||
case Column_Serial:
|
||||
{
|
||||
if (left->serial == right->serial)
|
||||
return titlesLessThan(left, right);
|
||||
return (StringUtil::Strcasecmp(left->serial.c_str(), right->serial.c_str()) < 0);
|
||||
}
|
||||
|
||||
case Column_Title:
|
||||
{
|
||||
return titlesLessThan(left, right);
|
||||
}
|
||||
|
||||
case Column_FileTitle:
|
||||
{
|
||||
const std::string_view file_title_left = Path::GetFileTitle(left->path);
|
||||
const std::string_view file_title_right = Path::GetFileTitle(right->path);
|
||||
if (file_title_left == file_title_right)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
const std::size_t smallest = std::min(file_title_left.size(), file_title_right.size());
|
||||
return (StringUtil::Strncasecmp(file_title_left.data(), file_title_right.data(), smallest) < 0);
|
||||
}
|
||||
|
||||
case Column_Region:
|
||||
{
|
||||
if (left->region == right->region)
|
||||
return titlesLessThan(left, right);
|
||||
return (static_cast<int>(left->region) < static_cast<int>(right->region));
|
||||
}
|
||||
|
||||
case Column_Compatibility:
|
||||
{
|
||||
const GameDatabase::CompatibilityRating left_compatibility =
|
||||
left->dbentry ? left->dbentry->compatibility : GameDatabase::CompatibilityRating::Unknown;
|
||||
const GameDatabase::CompatibilityRating right_compatibility =
|
||||
right->dbentry ? right->dbentry->compatibility : GameDatabase::CompatibilityRating::Unknown;
|
||||
if (left_compatibility == right_compatibility)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (static_cast<int>(left_compatibility) < static_cast<int>(right_compatibility));
|
||||
}
|
||||
|
||||
case Column_FileSize:
|
||||
{
|
||||
if (left->file_size == right->file_size)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (left->file_size < right->file_size);
|
||||
}
|
||||
|
||||
case Column_DataSize:
|
||||
{
|
||||
if (left->uncompressed_size == right->uncompressed_size)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (left->uncompressed_size < right->uncompressed_size);
|
||||
}
|
||||
|
||||
case Column_Genre:
|
||||
{
|
||||
const int compres =
|
||||
StringUtil::CompareNoCase(left->dbentry ? std::string_view(left->dbentry->genre) : std::string_view(),
|
||||
right->dbentry ? std::string_view(right->dbentry->genre) : std::string_view());
|
||||
return (compres == 0) ? titlesLessThan(left, right) : (compres < 0);
|
||||
}
|
||||
|
||||
case Column_Developer:
|
||||
{
|
||||
const int compres =
|
||||
StringUtil::CompareNoCase(left->dbentry ? std::string_view(left->dbentry->developer) : std::string_view(),
|
||||
right->dbentry ? std::string_view(right->dbentry->developer) : std::string_view());
|
||||
return (compres == 0) ? titlesLessThan(left, right) : (compres < 0);
|
||||
}
|
||||
|
||||
case Column_Publisher:
|
||||
{
|
||||
const int compres =
|
||||
StringUtil::CompareNoCase(left->dbentry ? std::string_view(left->dbentry->publisher) : std::string_view(),
|
||||
right->dbentry ? std::string_view(right->dbentry->publisher) : std::string_view());
|
||||
return (compres == 0) ? titlesLessThan(left, right) : (compres < 0);
|
||||
}
|
||||
|
||||
case Column_Year:
|
||||
{
|
||||
const u64 ldate = left->dbentry ? left->dbentry->release_date : 0;
|
||||
const u64 rdate = right->dbentry ? right->dbentry->release_date : 0;
|
||||
if (ldate == rdate)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (ldate < rdate);
|
||||
}
|
||||
|
||||
case Column_TimePlayed:
|
||||
{
|
||||
if (left->total_played_time == right->total_played_time)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (left->total_played_time < right->total_played_time);
|
||||
}
|
||||
|
||||
case Column_LastPlayed:
|
||||
{
|
||||
if (left->last_played_time == right->last_played_time)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (left->last_played_time < right->last_played_time);
|
||||
}
|
||||
|
||||
case Column_Players:
|
||||
{
|
||||
const u8 left_players = left->dbentry ? ((left->dbentry->min_players << 4) + left->dbentry->max_players) : 0;
|
||||
const u8 right_players = right->dbentry ? ((right->dbentry->min_players << 4) + right->dbentry->max_players) : 0;
|
||||
if (left_players == right_players)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (left_players < right_players);
|
||||
}
|
||||
|
||||
case Column_Achievements:
|
||||
{
|
||||
// sort by unlock percentage
|
||||
const float unlock_left =
|
||||
(left->num_achievements > 0) ?
|
||||
(static_cast<float>(std::max(left->unlocked_achievements, left->unlocked_achievements_hc)) /
|
||||
static_cast<float>(left->num_achievements)) :
|
||||
0;
|
||||
const float unlock_right =
|
||||
(right->num_achievements > 0) ?
|
||||
(static_cast<float>(std::max(right->unlocked_achievements, right->unlocked_achievements_hc)) /
|
||||
static_cast<float>(right->num_achievements)) :
|
||||
0;
|
||||
if (std::abs(unlock_left - unlock_right) < 0.0001f)
|
||||
{
|
||||
// order by achievement count
|
||||
if (left->num_achievements == right->num_achievements)
|
||||
return titlesLessThan(left, right);
|
||||
|
||||
return (left->num_achievements < right->num_achievements);
|
||||
}
|
||||
|
||||
return (unlock_left < unlock_right);
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
return GameList::CompareEntryLessThan(left, right, static_cast<GameList::Column>(column));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1397,11 +1214,17 @@ class GameListSortModel final : public QSortFilterProxyModel
|
||||
public:
|
||||
explicit GameListSortModel(GameListModel* parent) : QSortFilterProxyModel(parent), m_model(parent)
|
||||
{
|
||||
m_merge_disc_sets = Core::GetBaseBoolSettingValue("UI", "GameListMergeDiscSets", true);
|
||||
reloadSettings();
|
||||
}
|
||||
|
||||
bool isMergingDiscSets() const { return m_merge_disc_sets; }
|
||||
|
||||
void reloadSettings()
|
||||
{
|
||||
setMergeDiscSets(Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_MERGE_DISC_SETS,
|
||||
GameList::DEFAULT_MERGE_DISC_SETS));
|
||||
}
|
||||
|
||||
void setMergeDiscSets(bool enabled)
|
||||
{
|
||||
beginFilterChange();
|
||||
@@ -1856,11 +1679,11 @@ GameListWidget::GameListWidget(QWidget* parent, QAction* action_view_list, QActi
|
||||
QString::fromUtf8(Settings::GetDiscRegionDisplayName(static_cast<DiscRegion>(region))));
|
||||
}
|
||||
|
||||
m_list_view = new GameListListView(m_model, m_sort_model, m_ui.stack);
|
||||
m_list_view = new GameListListView(this, m_model, m_sort_model, m_ui.stack);
|
||||
m_list_view->setAnimateGameIcons(Core::GetBaseBoolSettingValue("UI", "GameListAnimateGameIcons", false));
|
||||
m_ui.stack->insertWidget(0, m_list_view);
|
||||
|
||||
m_grid_view = new GameListGridView(m_model, m_sort_model, m_ui.stack);
|
||||
m_grid_view = new GameListGridView(this, m_model, m_sort_model, m_ui.stack);
|
||||
m_grid_view->setItemDelegate(new GameListCoverDelegate(m_grid_view, m_model, m_sort_model));
|
||||
m_ui.stack->insertWidget(1, m_grid_view);
|
||||
|
||||
@@ -1911,7 +1734,9 @@ GameListWidget::GameListWidget(QWidget* parent, QAction* action_view_list, QActi
|
||||
action_show_localized_titles->setChecked(m_model->getShowLocalizedTitles());
|
||||
action_show_list_icons->setChecked(m_model->getShowGameIcons());
|
||||
action_animate_list_icons->setChecked(m_list_view->isAnimatingGameIcons());
|
||||
action_prefer_achievement_game_icons->setChecked(GameList::PreferAchievementGameBadgesForIcons());
|
||||
action_prefer_achievement_game_icons->setChecked(
|
||||
Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_PREFER_ACHIEVEMENT_CONTENT_ICONS,
|
||||
GameList::DEFAULT_PREFER_ACHIEVEMENT_CONTENT_ICONS));
|
||||
action_show_grid_titles->setChecked(m_model->getShowCoverTitles());
|
||||
onIconSizeChanged(m_model->getIconSize());
|
||||
|
||||
@@ -1926,6 +1751,18 @@ QString GameListWidget::getSupportedFormatsString()
|
||||
return tr(SUPPORTED_FORMATS_STRING);
|
||||
}
|
||||
|
||||
void GameListWidget::reloadSettings()
|
||||
{
|
||||
m_model->reloadSettings();
|
||||
m_sort_model->reloadSettings();
|
||||
m_list_view->loadColumnSortSettings();
|
||||
m_ui.mergeDiscSets->defaultAction()->setChecked(m_sort_model->isMergingDiscSets());
|
||||
m_ui.showLocalizedTitles->defaultAction()->setChecked(m_model->getShowLocalizedTitles());
|
||||
m_ui.showGridTitles->defaultAction()->setChecked(m_model->getShowCoverTitles());
|
||||
m_grid_view->updateLayout(true);
|
||||
reloadViewModeFromSettings();
|
||||
}
|
||||
|
||||
bool GameListWidget::isShowingGameList() const
|
||||
{
|
||||
return (m_ui.stack->currentIndex() == VIEW_MODE_LIST);
|
||||
@@ -2185,7 +2022,7 @@ void GameListWidget::showGameList()
|
||||
if (isShowingGameList() || m_model->rowCount() == 0)
|
||||
return;
|
||||
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListGridView", false);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
setViewMode(VIEW_MODE_LIST);
|
||||
@@ -2197,7 +2034,7 @@ void GameListWidget::showGameGrid()
|
||||
if (isShowingGameGrid() || m_model->rowCount() == 0)
|
||||
return;
|
||||
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListGridView", true);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListGridView", true);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
setViewMode(VIEW_MODE_GRID);
|
||||
@@ -2205,7 +2042,8 @@ void GameListWidget::showGameGrid()
|
||||
|
||||
void GameListWidget::reloadViewModeFromSettings()
|
||||
{
|
||||
const bool grid_view = Core::GetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
const bool grid_view = Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_GRID_VIEW,
|
||||
GameList::DEFAULT_GRID_VIEW);
|
||||
m_ui.viewGameList->defaultAction()->setChecked(!grid_view);
|
||||
m_ui.viewGameGrid->defaultAction()->setChecked(grid_view);
|
||||
|
||||
@@ -2218,7 +2056,7 @@ void GameListWidget::setMergeDiscSets(bool enabled)
|
||||
if (m_sort_model->isMergingDiscSets() == enabled)
|
||||
return;
|
||||
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListMergeDiscSets", enabled);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_MERGE_DISC_SETS, enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_sort_model->setMergeDiscSets(enabled);
|
||||
}
|
||||
@@ -2228,7 +2066,7 @@ void GameListWidget::setShowLocalizedTitles(bool enabled)
|
||||
if (m_model->getShowLocalizedTitles() == enabled)
|
||||
return;
|
||||
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListShowLocalizedTitles", enabled);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_model->setShowLocalizedTitles(enabled);
|
||||
}
|
||||
@@ -2238,7 +2076,7 @@ void GameListWidget::setShowGameIcons(bool enabled)
|
||||
if (m_model->getShowGameIcons() == enabled)
|
||||
return;
|
||||
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListShowGameIcons", enabled);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListShowGameIcons", enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_model->setShowGameIcons(enabled);
|
||||
if (isShowingGameList() && m_list_view->isAnimatingGameIcons())
|
||||
@@ -2255,7 +2093,7 @@ void GameListWidget::setAnimateGameIcons(bool enabled)
|
||||
if (m_list_view->isAnimatingGameIcons() == enabled)
|
||||
return;
|
||||
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListAnimateGameIcons", enabled);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "GameListAnimateGameIcons", enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_list_view->setAnimateGameIcons(enabled);
|
||||
if (isShowingGameList())
|
||||
@@ -2264,7 +2102,8 @@ void GameListWidget::setAnimateGameIcons(bool enabled)
|
||||
|
||||
void GameListWidget::setPreferAchievementGameIcons(bool enabled)
|
||||
{
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListPreferAchievementGameBadgesForIcons", enabled);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_PREFER_ACHIEVEMENT_CONTENT_ICONS,
|
||||
enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
if (!enabled)
|
||||
@@ -2278,12 +2117,36 @@ void GameListWidget::setShowCoverTitles(bool enabled)
|
||||
if (m_model->getShowCoverTitles() == enabled)
|
||||
return;
|
||||
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_COVER_TITLES, enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_model->setShowCoverTitles(enabled);
|
||||
m_grid_view->updateLayout(false);
|
||||
}
|
||||
|
||||
void GameListWidget::setCoverScale(float scale)
|
||||
{
|
||||
if (m_model->getCoverScale() == scale)
|
||||
return;
|
||||
|
||||
Core::SetBaseFloatSettingValue(GameList::UI_SETTING_SECTION, "GameListCoverArtScale", scale);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
m_model->setCoverScale(scale);
|
||||
}
|
||||
|
||||
void GameListWidget::setIconSize(int size)
|
||||
{
|
||||
if (m_model->getIconSize() == size)
|
||||
return;
|
||||
|
||||
Core::SetBaseIntSettingValue(GameList::UI_SETTING_SECTION, "GameListIconSize", size);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
m_model->setIconSize(size);
|
||||
|
||||
m_list_view->updateAnimatedGameIconDelegate();
|
||||
}
|
||||
|
||||
void GameListWidget::setViewMode(int stack_index)
|
||||
{
|
||||
if (stack_index == VIEW_MODE_GRID && m_grid_view->isLayoutUpdatedDeferred())
|
||||
@@ -2343,14 +2206,9 @@ void GameListWidget::showScaleToolTip()
|
||||
void GameListWidget::onScaleSliderChanged(int value)
|
||||
{
|
||||
if (isShowingGameGrid())
|
||||
{
|
||||
m_model->setCoverScale(static_cast<float>(value) / 100.0f);
|
||||
}
|
||||
setCoverScale(static_cast<float>(value) / 100.0f);
|
||||
else if (isShowingGameList())
|
||||
{
|
||||
m_model->setIconSize(value * GAME_ICON_SIZE_STEP);
|
||||
m_list_view->updateAnimatedGameIconDelegate();
|
||||
}
|
||||
setIconSize(value * GAME_ICON_SIZE_STEP);
|
||||
|
||||
if (m_ui.scale->isSliderDown())
|
||||
showScaleToolTip();
|
||||
@@ -2419,8 +2277,9 @@ const GameList::Entry* GameListWidget::getSelectedEntry() const
|
||||
}
|
||||
}
|
||||
|
||||
GameListListView::GameListListView(GameListModel* model, GameListSortModel* sort_model, QWidget* parent)
|
||||
: QTableView(parent), m_model(model), m_sort_model(sort_model)
|
||||
GameListListView::GameListListView(GameListWidget* widget, GameListModel* model, GameListSortModel* sort_model,
|
||||
QWidget* parent)
|
||||
: QTableView(parent), m_widget(widget), m_model(model), m_sort_model(sort_model)
|
||||
{
|
||||
setModel(sort_model);
|
||||
setSortingEnabled(true);
|
||||
@@ -2537,12 +2396,12 @@ void GameListListView::updateFixedColumnWidths()
|
||||
|
||||
static TinyString getColumnVisibilitySettingsKeyName(int column)
|
||||
{
|
||||
return TinyString::from_format("Show{}", GameListModel::getColumnName(static_cast<GameListModel::Column>(column)));
|
||||
return TinyString::from_format("Show{}", GameList::GetColumnName(static_cast<GameList::Column>(column)));
|
||||
}
|
||||
|
||||
void GameListListView::loadColumnVisibilitySettings()
|
||||
{
|
||||
static constexpr std::array<bool, GameListModel::Column_Count> DEFAULT_VISIBILITY = {{
|
||||
static constexpr std::array DEFAULT_VISIBILITY = {
|
||||
true, // type
|
||||
true, // serial
|
||||
true, // title
|
||||
@@ -2558,31 +2417,36 @@ void GameListListView::loadColumnVisibilitySettings()
|
||||
false, // size
|
||||
true, // region
|
||||
false, // achievements
|
||||
false // compatibility
|
||||
}};
|
||||
false, // compatibility
|
||||
false, // cover, unused
|
||||
};
|
||||
static_assert(std::size(DEFAULT_VISIBILITY) == GameListModel::Column_Count);
|
||||
|
||||
for (int column = 0; column < GameListModel::Column_Count; column++)
|
||||
for (int column = 0; column <= GameListModel::Column_LastVisible; column++)
|
||||
{
|
||||
const bool visible = Core::GetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column),
|
||||
DEFAULT_VISIBILITY[column]);
|
||||
const bool visible = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, getColumnVisibilitySettingsKeyName(column), DEFAULT_VISIBILITY[column]);
|
||||
setColumnHidden(column, !visible);
|
||||
}
|
||||
setColumnHidden(GameListModel::Column_Cover, true);
|
||||
}
|
||||
|
||||
void GameListListView::loadColumnSortSettings()
|
||||
{
|
||||
const GameListModel::Column DEFAULT_SORT_COLUMN = GameListModel::Column_Icon;
|
||||
const bool DEFAULT_SORT_DESCENDING = false;
|
||||
|
||||
const GameListModel::Column sort_column =
|
||||
GameListModel::getColumnIdForName(Core::GetBaseStringSettingValue("GameListTableView", "SortColumn"))
|
||||
.value_or(DEFAULT_SORT_COLUMN);
|
||||
const bool sort_descending =
|
||||
Core::GetBaseBoolSettingValue("GameListTableView", "SortDescending", DEFAULT_SORT_DESCENDING);
|
||||
const GameList::Column sort_column =
|
||||
GameList::ParseColumnName(
|
||||
Core::GetBaseStringSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_COLUMN))
|
||||
.value_or(GameList::DEFAULT_SORT_COLUMN);
|
||||
const bool sort_descending = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_REVERSED, GameList::DEFAULT_SORT_REVERSED);
|
||||
const int sort_column_index = static_cast<int>(sort_column);
|
||||
const Qt::SortOrder sort_order = sort_descending ? Qt::DescendingOrder : Qt::AscendingOrder;
|
||||
m_sort_model->sort(sort_column, sort_order);
|
||||
m_sort_model->sort(sort_column_index, sort_order);
|
||||
if (QHeaderView* hv = horizontalHeader())
|
||||
hv->setSortIndicator(sort_column, sort_order);
|
||||
{
|
||||
const QSignalBlocker sb(hv);
|
||||
hv->setSortIndicator(sort_column_index, sort_order);
|
||||
}
|
||||
}
|
||||
|
||||
void GameListListView::saveColumnSortSettings()
|
||||
@@ -2590,13 +2454,13 @@ void GameListListView::saveColumnSortSettings()
|
||||
const int sort_column = horizontalHeader()->sortIndicatorSection();
|
||||
const bool sort_descending = (horizontalHeader()->sortIndicatorOrder() == Qt::DescendingOrder);
|
||||
|
||||
if (sort_column >= 0 && sort_column < GameListModel::Column_Count)
|
||||
if (sort_column >= 0 && sort_column <= GameListModel::Column_LastVisible)
|
||||
{
|
||||
Core::SetBaseStringSettingValue("GameListTableView", "SortColumn",
|
||||
GameListModel::getColumnName(static_cast<GameListModel::Column>(sort_column)));
|
||||
Core::SetBaseStringSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_COLUMN,
|
||||
GameList::GetColumnName(static_cast<GameList::Column>(sort_column)));
|
||||
}
|
||||
|
||||
Core::SetBaseBoolSettingValue("GameListTableView", "SortDescending", sort_descending);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_SORT_REVERSED, sort_descending);
|
||||
Host::CommitBaseSettingChanges();
|
||||
}
|
||||
|
||||
@@ -2607,7 +2471,7 @@ void GameListListView::setAndSaveColumnHidden(int column, bool hidden)
|
||||
return;
|
||||
|
||||
setColumnHidden(column, hidden);
|
||||
Core::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), !hidden);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, getColumnVisibilitySettingsKeyName(column), !hidden);
|
||||
Host::CommitBaseSettingChanges();
|
||||
}
|
||||
|
||||
@@ -2632,8 +2496,7 @@ void GameListListView::onHeaderContextMenuRequested(const QPoint& point)
|
||||
void GameListListView::adjustIconSize(int delta)
|
||||
{
|
||||
const int new_size = std::clamp(m_model->getIconSize() + delta, GAME_ICON_MIN_SIZE, GAME_ICON_MAX_SIZE);
|
||||
m_model->setIconSize(new_size);
|
||||
updateAnimatedGameIconDelegate();
|
||||
m_widget->setIconSize(new_size);
|
||||
}
|
||||
|
||||
bool GameListListView::isAnimatingGameIcons() const
|
||||
@@ -2704,8 +2567,9 @@ void GameListListView::clearAnimatedGameIconDelegate()
|
||||
m_animated_icon_row = -1;
|
||||
}
|
||||
|
||||
GameListGridView::GameListGridView(GameListModel* model, GameListSortModel* sort_model, QWidget* parent)
|
||||
: QListView(parent), m_model(model), m_sort_model(sort_model)
|
||||
GameListGridView::GameListGridView(GameListWidget* widget, GameListModel* model, GameListSortModel* sort_model,
|
||||
QWidget* parent)
|
||||
: QListView(parent), m_widget(widget), m_model(model), m_sort_model(sort_model)
|
||||
{
|
||||
setModel(sort_model);
|
||||
setModelColumn(GameListModel::Column_Cover);
|
||||
@@ -2752,7 +2616,7 @@ void GameListGridView::resizeEvent(QResizeEvent* e)
|
||||
void GameListGridView::adjustZoom(float delta)
|
||||
{
|
||||
const float new_scale = std::clamp(m_model->getCoverScale() + delta, MIN_COVER_SCALE, MAX_COVER_SCALE);
|
||||
m_model->setCoverScale(new_scale);
|
||||
m_widget->setCoverScale(new_scale);
|
||||
}
|
||||
|
||||
void GameListGridView::updateLayout(bool disable_defer)
|
||||
|
||||
@@ -64,9 +64,6 @@ public:
|
||||
Column_LastVisible = Column_Compatibility,
|
||||
};
|
||||
|
||||
static std::optional<Column> getColumnIdForName(std::string_view name);
|
||||
static const char* getColumnName(Column col);
|
||||
|
||||
explicit GameListModel(GameListWidget* parent);
|
||||
~GameListModel();
|
||||
|
||||
@@ -89,10 +86,10 @@ public:
|
||||
void refresh();
|
||||
void reloadThemeSpecificImages();
|
||||
|
||||
bool titlesLessThan(const GameList::Entry* left, const GameList::Entry* right) const;
|
||||
bool lessThan(const GameList::Entry* left, const GameList::Entry* right, int column) const;
|
||||
bool lessThan(const QModelIndex& left_index, const QModelIndex& right_index, int column) const;
|
||||
|
||||
void reloadSettings();
|
||||
|
||||
bool getShowLocalizedTitles() const;
|
||||
void setShowLocalizedTitles(bool enabled);
|
||||
|
||||
@@ -142,9 +139,9 @@ private:
|
||||
|
||||
void rowsChanged(const QList<int>& rows);
|
||||
|
||||
void loadSettings();
|
||||
void loadCommonImages();
|
||||
void loadSizeDependentPixmaps();
|
||||
void updateCoverScale();
|
||||
void loadCoverScaleDependentPixmaps();
|
||||
void loadOrGenerateCover(const GameList::Entry* ge);
|
||||
void coverLoaded(const std::string& path, const QImage& image, float scale);
|
||||
@@ -192,13 +189,14 @@ class GameListListView final : public QTableView
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GameListListView(GameListModel* model, GameListSortModel* sort_model, QWidget* parent);
|
||||
GameListListView(GameListWidget* widget, GameListModel* model, GameListSortModel* sort_model, QWidget* parent);
|
||||
~GameListListView() override;
|
||||
|
||||
QFontMetrics fontMetricsForHorizontalHeader() const;
|
||||
void setFixedColumnWidth(const QFontMetrics& fm, int column, int str_width);
|
||||
void setAndSaveColumnHidden(int column, bool hidden);
|
||||
void updateFixedColumnWidths();
|
||||
void loadColumnSortSettings();
|
||||
|
||||
void adjustIconSize(int delta);
|
||||
|
||||
@@ -212,11 +210,11 @@ protected:
|
||||
|
||||
private:
|
||||
void loadColumnVisibilitySettings();
|
||||
void loadColumnSortSettings();
|
||||
|
||||
void onHeaderContextMenuRequested(const QPoint& point);
|
||||
void saveColumnSortSettings();
|
||||
|
||||
GameListWidget* m_widget = nullptr;
|
||||
GameListModel* m_model = nullptr;
|
||||
GameListSortModel* m_sort_model = nullptr;
|
||||
|
||||
@@ -229,7 +227,7 @@ class GameListGridView final : public QListView
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GameListGridView(GameListModel* model, GameListSortModel* sort_model, QWidget* parent);
|
||||
GameListGridView(GameListWidget* widget, GameListModel* model, GameListSortModel* sort_model, QWidget* parent);
|
||||
~GameListGridView() override;
|
||||
|
||||
ALWAYS_INLINE bool isLayoutUpdatedDeferred() const { return m_layout_update_deferred; }
|
||||
@@ -246,6 +244,7 @@ protected:
|
||||
void resizeEvent(QResizeEvent* e) override;
|
||||
|
||||
private:
|
||||
GameListWidget* m_widget = nullptr;
|
||||
GameListModel* m_model = nullptr;
|
||||
GameListSortModel* m_sort_model = nullptr;
|
||||
int m_horizontal_offset = 0;
|
||||
@@ -268,6 +267,7 @@ public:
|
||||
ALWAYS_INLINE GameListListView* getListView() const { return m_list_view; }
|
||||
ALWAYS_INLINE GameListGridView* getGridView() const { return m_grid_view; }
|
||||
|
||||
void reloadSettings();
|
||||
void refresh(bool invalidate_cache);
|
||||
void cancelRefresh();
|
||||
void setBackgroundPath(const std::string_view path);
|
||||
@@ -290,6 +290,8 @@ public:
|
||||
void setAnimateGameIcons(bool enabled);
|
||||
void setPreferAchievementGameIcons(bool enabled);
|
||||
void setShowCoverTitles(bool enabled);
|
||||
void setCoverScale(float scale);
|
||||
void setIconSize(int size);
|
||||
void focusSearchWidget();
|
||||
|
||||
// Returns translated supported formats string. Formats are separated by newlines.
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsWindow* dialog, QWidget* parent)
|
||||
GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, bool localized_titles, SettingsWindow* dialog,
|
||||
QWidget* parent)
|
||||
: m_dialog(dialog)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
@@ -70,7 +71,7 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsWindo
|
||||
m_ui.customLanguage->addItem(QtUtils::GetIconForLanguage(language_name), QString::fromUtf8(language_name));
|
||||
}
|
||||
|
||||
populateUi(entry);
|
||||
populateUi(entry, localized_titles);
|
||||
|
||||
connect(m_ui.compatibilityComments, &QAbstractButton::clicked, this,
|
||||
&GameSummaryWidget::onCompatibilityCommentsClicked);
|
||||
@@ -134,7 +135,7 @@ void GameSummaryWidget::reloadGameSettings()
|
||||
m_ui.editInputProfile->setEnabled(m_ui.inputProfile->currentIndex() >= 1);
|
||||
}
|
||||
|
||||
void GameSummaryWidget::populateUi(const GameList::Entry* entry)
|
||||
void GameSummaryWidget::populateUi(const GameList::Entry* entry, bool localized_titles)
|
||||
{
|
||||
m_path = entry->path;
|
||||
|
||||
@@ -151,7 +152,7 @@ void GameSummaryWidget::populateUi(const GameList::Entry* entry)
|
||||
{
|
||||
m_ui.hashes->setText(tr("N/A"));
|
||||
}
|
||||
m_ui.title->setText(QtUtils::StringViewToQString(entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles())));
|
||||
m_ui.title->setText(QtUtils::StringViewToQString(entry->GetDisplayTitle(localized_titles)));
|
||||
m_ui.region->setCurrentIndex(static_cast<int>(entry->region));
|
||||
m_ui.entryType->setCurrentIndex(static_cast<int>(entry->type));
|
||||
|
||||
@@ -169,8 +170,7 @@ void GameSummaryWidget::populateUi(const GameList::Entry* entry)
|
||||
entry->disc_set_member ? GameList::GetEntryForPath(entry->GetDiscSetEntry()->GetSaveTitle()) : nullptr;
|
||||
if (disc_set_entry)
|
||||
{
|
||||
m_ui.discSetTitle->setText(
|
||||
QtUtils::StringViewToQString(disc_set_entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles())));
|
||||
m_ui.discSetTitle->setText(QtUtils::StringViewToQString(disc_set_entry->GetDisplayTitle(localized_titles)));
|
||||
m_ui.restoreDiscSetTitle->setEnabled(disc_set_entry->has_custom_title);
|
||||
}
|
||||
else
|
||||
@@ -410,7 +410,8 @@ void GameSummaryWidget::setCustomTitle(const std::string& text)
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(m_path);
|
||||
if (entry)
|
||||
{
|
||||
const std::string_view title = entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles());
|
||||
const std::string_view title = entry->GetDisplayTitle(Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES));
|
||||
m_dialog->setGameTitle(title);
|
||||
|
||||
{
|
||||
@@ -440,8 +441,8 @@ void GameSummaryWidget::setCustomDiscSetTitle(const std::string& text)
|
||||
GameList::SaveCustomTitleForPath(disc_set_entry->path, text);
|
||||
|
||||
const QSignalBlocker sb(m_ui.discSetTitle);
|
||||
m_ui.discSetTitle->setText(
|
||||
QtUtils::StringViewToQString(disc_set_entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles())));
|
||||
m_ui.discSetTitle->setText(QtUtils::StringViewToQString(disc_set_entry->GetDisplayTitle(Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES))));
|
||||
|
||||
m_ui.restoreDiscSetTitle->setEnabled(disc_set_entry->has_custom_title);
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ class GameSummaryWidget : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GameSummaryWidget(const GameList::Entry* entry, SettingsWindow* dialog, QWidget* parent);
|
||||
GameSummaryWidget(const GameList::Entry* entry, bool localized_titles, SettingsWindow* dialog, QWidget* parent);
|
||||
~GameSummaryWidget();
|
||||
|
||||
void reloadGameSettings();
|
||||
|
||||
private:
|
||||
void populateUi(const GameList::Entry* entry);
|
||||
void populateUi(const GameList::Entry* entry, bool localized_titles);
|
||||
void setCustomTitle(const std::string& text);
|
||||
void setCustomDiscSetTitle(const std::string& text);
|
||||
void setCustomRegion(int region);
|
||||
|
||||
@@ -1462,6 +1462,10 @@ void MainWindow::onFullscreenUIStartedOrStopped(bool running)
|
||||
s_locals.fullscreen_ui_started = running;
|
||||
m_ui.actionStartFullscreenUI->setText(running ? tr("Stop Big Picture Mode") : tr("Start Big Picture Mode"));
|
||||
m_ui.actionToolbarStartFullscreenUI->setText(running ? tr("Exit Big Picture") : tr("Big Picture"));
|
||||
|
||||
// If big picture was exited, then we should refresh the game list settings because they may have changed.
|
||||
if (!running && !m_is_closing && !QtHost::InBatchMode())
|
||||
m_game_list_widget->reloadSettings();
|
||||
}
|
||||
|
||||
void MainWindow::onCloseGameActionTriggered()
|
||||
@@ -3344,9 +3348,9 @@ void MainWindow::onAchievementsLoginSuccess(const QString& username, quint32 poi
|
||||
|
||||
// Automatically show the achievements column after first login. If the user has manually hidden it,
|
||||
// it will not be automatically shown again.
|
||||
if (!Core::GetBaseBoolSettingValue("GameListTableView", "TriedShowingAchievementsColumn", false))
|
||||
if (!Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "TriedShowingAchievementsColumn", false))
|
||||
{
|
||||
Core::SetBaseBoolSettingValue("GameListTableView", "TriedShowingAchievementsColumn", true);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, "TriedShowingAchievementsColumn", true);
|
||||
m_game_list_widget->getListView()->setAndSaveColumnHidden(GameListModel::Column_Achievements, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,11 +63,13 @@ SettingsWindow::SettingsWindow(const GameList::Entry* entry, std::unique_ptr<INI
|
||||
if (const QIcon icon = g_main_window->getIconForGame(QString::fromStdString(entry->path)); !icon.isNull())
|
||||
setWindowIcon(icon);
|
||||
|
||||
setGameTitle(entry->GetDisplayTitle(GameList::ShouldShowLocalizedTitles()));
|
||||
const bool show_localized_titles = Core::GetBaseBoolSettingValue(
|
||||
GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_LOCALIZED_TITLES, GameList::DEFAULT_LOCALIZED_TITLES);
|
||||
setGameTitle(entry->GetDisplayTitle(show_localized_titles));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
addWidget(m_game_summary = new GameSummaryWidget(entry, this, m_ui.settingsContainer), tr("Summary"),
|
||||
u":/icons/monochrome/svg/file-list-line.svg"_s,
|
||||
addWidget(m_game_summary = new GameSummaryWidget(entry, show_localized_titles, this, m_ui.settingsContainer),
|
||||
tr("Summary"), u":/icons/monochrome/svg/file-list-line.svg"_s,
|
||||
tr("<strong>Summary</strong><hr>This page shows information about the selected game, and allows you to "
|
||||
"validate your disc was dumped correctly."));
|
||||
addPages();
|
||||
|
||||
@@ -695,7 +695,8 @@ void SetupWizardDialog::onAchievementsViewProfilePressed()
|
||||
|
||||
void SetupWizardDialog::setupGameListViewPage()
|
||||
{
|
||||
const bool use_grid = Core::GetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
const bool use_grid = Core::GetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_GRID_VIEW,
|
||||
GameList::DEFAULT_GRID_VIEW);
|
||||
m_ui.listView->setChecked(!use_grid);
|
||||
m_ui.gridView->setChecked(use_grid);
|
||||
|
||||
@@ -717,8 +718,9 @@ void SetupWizardDialog::onGridViewChanged(bool checked)
|
||||
return;
|
||||
|
||||
// NOTE: No settings apply here, we explicitly change the layout.
|
||||
Core::SetBaseBoolSettingValue("UI", "GameListGridView", setting_value);
|
||||
Core::SetBaseUIntSettingValue("Main", "DefaultFullscreenUIGameView", setting_value ? 0 : 1);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_GRID_VIEW, setting_value);
|
||||
Core::SetBaseBoolSettingValue(GameList::UI_SETTING_SECTION, GameList::SETTING_KEY_FULLSCREENUI_GRID_VIEW,
|
||||
setting_value);
|
||||
Host::CommitBaseSettingChanges();
|
||||
g_main_window->getGameListWidget()->reloadViewModeFromSettings();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user