Qt: Add game list language override option

This commit is contained in:
Stenzek 2024-11-24 23:24:39 +10:00
parent 70a4b5c9f2
commit 852239ec8a
No known key found for this signature in database
11 changed files with 313 additions and 199 deletions

View File

@ -6882,7 +6882,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
const bool display_as_language = (selected_entry->dbentry && selected_entry->dbentry->HasAnyLanguage()); const bool display_as_language = (selected_entry->dbentry && selected_entry->dbentry->HasAnyLanguage());
ImGui::TextUnformatted(display_as_language ? FSUI_CSTR("Language: ") : FSUI_CSTR("Region: ")); ImGui::TextUnformatted(display_as_language ? FSUI_CSTR("Language: ") : FSUI_CSTR("Region: "));
ImGui::SameLine(); ImGui::SameLine();
ImGui::Image(GetCachedTexture(selected_entry->GetLanguageIconFileName(), 23, 16), LayoutScale(23.0f, 16.0f)); ImGui::Image(GetCachedTexture(selected_entry->GetLanguageIconName(), 23, 16), LayoutScale(23.0f, 16.0f));
ImGui::SameLine(); ImGui::SameLine();
if (display_as_language) if (display_as_language)
{ {

View File

@ -21,6 +21,7 @@
#include "ryml.hpp" #include "ryml.hpp"
#include <bit>
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -312,6 +313,24 @@ std::optional<GameDatabase::Language> GameDatabase::ParseLanguageName(std::strin
return std::nullopt; return std::nullopt;
} }
TinyString GameDatabase::GetLanguageFlagResourceName(std::string_view language_name)
{
return TinyString::from_format("images/flags/{}.svg", language_name);
}
std::string_view GameDatabase::Entry::GetLanguageFlagName(DiscRegion region) const
{
// If there's only one language, this is the flag we want to use.
// Except if it's English, then we want to use the disc region's flag.
std::string_view ret;
if (languages.count() == 1 && !languages.test(static_cast<size_t>(GameDatabase::Language::English)))
ret = GameDatabase::GetLanguageName(static_cast<GameDatabase::Language>(std::countr_zero(languages.to_ulong())));
else
ret = Settings::GetDiscRegionName(region);
return ret;
}
SmallString GameDatabase::Entry::GetLanguagesString() const SmallString GameDatabase::Entry::GetLanguagesString() const
{ {
SmallString ret; SmallString ret;

View File

@ -130,8 +130,9 @@ struct Entry
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; } ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
ALWAYS_INLINE bool HasLanguage(Language language) const { return languages.test(static_cast<size_t>(language)); } ALWAYS_INLINE bool HasLanguage(Language language) const { return languages.test(static_cast<size_t>(language)); }
ALWAYS_INLINE bool HasAnyLanguage() const { return !languages.none(); } ALWAYS_INLINE bool HasAnyLanguage() const { return languages.any(); }
std::string_view GetLanguageFlagName(DiscRegion region) const;
SmallString GetLanguagesString() const; SmallString GetLanguagesString() const;
void ApplySettings(Settings& settings, bool display_osd_messages) const; void ApplySettings(Settings& settings, bool display_osd_messages) const;
@ -156,6 +157,7 @@ const char* GetCompatibilityRatingDisplayName(CompatibilityRating rating);
const char* GetLanguageName(Language language); const char* GetLanguageName(Language language);
std::optional<Language> ParseLanguageName(std::string_view str); std::optional<Language> ParseLanguageName(std::string_view str);
TinyString GetLanguageFlagResourceName(std::string_view language_name);
/// Map of track hashes for image verification /// Map of track hashes for image verification
struct TrackData struct TrackData

View File

@ -117,6 +117,8 @@ static PlayedTimeEntry UpdatePlayedTimeFile(const std::string& path, const std::
std::time_t add_time); std::time_t add_time);
static std::string GetCustomPropertiesFile(); static std::string GetCustomPropertiesFile();
static bool PutCustomPropertiesField(INISettingsInterface& ini, const std::string& path, const char* field,
const char* value);
static FileSystem::ManagedCFilePtr OpenMemoryCardTimestampCache(bool for_write); static FileSystem::ManagedCFilePtr OpenMemoryCardTimestampCache(bool for_write);
static bool UpdateMemcardTimestampCache(const MemcardTimestampCacheEntry& entry); static bool UpdateMemcardTimestampCache(const MemcardTimestampCacheEntry& entry);
@ -627,6 +629,21 @@ void GameList::ApplyCustomAttributes(const std::string& path, Entry* entry,
WARNING_LOG("Invalid region '{}' in custom attributes for '{}'", custom_region_str.value(), path); WARNING_LOG("Invalid region '{}' in custom attributes for '{}'", custom_region_str.value(), path);
} }
} }
const std::optional<TinyString> custom_language_str =
custom_attributes_ini.GetOptionalTinyStringValue(path.c_str(), "Language");
if (custom_language_str.has_value())
{
const std::optional<GameDatabase::Language> custom_region =
GameDatabase::ParseLanguageName(custom_region_str.value());
if (custom_region.has_value())
{
entry->custom_language = custom_region.value();
}
else
{
WARNING_LOG("Invalid language '{}' in custom attributes for '{}'", custom_region_str.value(), path);
}
}
} }
std::unique_lock<std::recursive_mutex> GameList::GetLock() std::unique_lock<std::recursive_mutex> GameList::GetLock()
@ -990,26 +1007,20 @@ std::string GameList::GetNewCoverImagePathForEntry(const Entry* entry, const cha
std::string_view GameList::Entry::GetLanguageIcon() const std::string_view GameList::Entry::GetLanguageIcon() const
{ {
// If there's only one language, this is the flag we want to use.
// Except if it's English, then we want to use the disc region's flag.
std::string_view ret; std::string_view ret;
if (dbentry && dbentry->languages.count() == 1 && if (custom_language != GameDatabase::Language::MaxCount)
!dbentry->languages.test(static_cast<size_t>(GameDatabase::Language::English))) ret = GameDatabase::GetLanguageName(custom_language);
{ else if (dbentry)
ret = GameDatabase::GetLanguageName( ret = dbentry->GetLanguageFlagName(region);
static_cast<GameDatabase::Language>(std::countr_zero(dbentry->languages.to_ulong())));
}
else else
{
ret = Settings::GetDiscRegionName(region); ret = Settings::GetDiscRegionName(region);
}
return ret; return ret;
} }
TinyString GameList::Entry::GetLanguageIconFileName() const TinyString GameList::Entry::GetLanguageIconName() const
{ {
return TinyString::from_format("images/flags/{}.svg", GetLanguageIcon()); return GameDatabase::GetLanguageFlagResourceName(GetLanguageIcon());
} }
TinyString GameList::Entry::GetCompatibilityIconFileName() const TinyString GameList::Entry::GetCompatibilityIconFileName() const
@ -1518,28 +1529,37 @@ std::string GameList::GetCustomPropertiesFile()
return Path::Combine(EmuFolders::DataRoot, "custom_properties.ini"); return Path::Combine(EmuFolders::DataRoot, "custom_properties.ini");
} }
void GameList::SaveCustomTitleForPath(const std::string& path, const std::string& custom_title) bool GameList::PutCustomPropertiesField(INISettingsInterface& ini, const std::string& path, const char* field,
const char* value)
{ {
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile()); ini.Load();
custom_attributes_ini.Load();
if (!custom_title.empty()) if (value && *value != '\0')
{ {
custom_attributes_ini.SetStringValue(path.c_str(), "Title", custom_title.c_str()); ini.SetStringValue(path.c_str(), field, value);
} }
else else
{ {
custom_attributes_ini.DeleteValue(path.c_str(), "Title"); ini.DeleteValue(path.c_str(), field);
custom_attributes_ini.RemoveEmptySections(); ini.RemoveEmptySections();
} }
Error error; Error error;
if (!custom_attributes_ini.Save(&error)) if (!ini.Save(&error))
{ {
ERROR_LOG("Failed to save custom attributes: {}", error.GetDescription()); ERROR_LOG("Failed to save custom attributes: {}", error.GetDescription());
return; return false;
} }
return true;
}
bool GameList::SaveCustomTitleForPath(const std::string& path, const std::string& custom_title)
{
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
if (!PutCustomPropertiesField(custom_attributes_ini, path, "Title", custom_title.c_str()))
return false;
if (!custom_title.empty()) if (!custom_title.empty())
{ {
// Can skip the rescan and just update the value directly. // Can skip the rescan and just update the value directly.
@ -1556,28 +1576,18 @@ void GameList::SaveCustomTitleForPath(const std::string& path, const std::string
// Let the cache update by rescanning. Only need to do this on deletion, to get the original value. // Let the cache update by rescanning. Only need to do this on deletion, to get the original value.
RescanCustomAttributesForPath(path, custom_attributes_ini); RescanCustomAttributesForPath(path, custom_attributes_ini);
} }
return true;
} }
void GameList::SaveCustomRegionForPath(const std::string& path, const std::optional<DiscRegion> custom_region) bool GameList::SaveCustomRegionForPath(const std::string& path, const std::optional<DiscRegion> custom_region)
{ {
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile()); INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
custom_attributes_ini.Load(); if (!PutCustomPropertiesField(custom_attributes_ini, path, "Region",
custom_region.has_value() ? Settings::GetDiscRegionName(custom_region.value()) :
if (custom_region.has_value()) nullptr))
{ {
custom_attributes_ini.SetStringValue(path.c_str(), "Region", Settings::GetDiscRegionName(custom_region.value())); return false;
}
else
{
custom_attributes_ini.DeleteValue(path.c_str(), "Region");
custom_attributes_ini.RemoveEmptySections();
}
Error error;
if (!custom_attributes_ini.Save(&error))
{
ERROR_LOG("Failed to save custom attributes: {}", error.GetDescription());
return;
} }
if (custom_region.has_value()) if (custom_region.has_value())
@ -1596,6 +1606,28 @@ void GameList::SaveCustomRegionForPath(const std::string& path, const std::optio
// Let the cache update by rescanning. Only need to do this on deletion, to get the original value. // Let the cache update by rescanning. Only need to do this on deletion, to get the original value.
RescanCustomAttributesForPath(path, custom_attributes_ini); RescanCustomAttributesForPath(path, custom_attributes_ini);
} }
return true;
}
bool GameList::SaveCustomLanguageForPath(const std::string& path,
const std::optional<GameDatabase::Language> custom_language)
{
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
if (!PutCustomPropertiesField(custom_attributes_ini, path, "Language",
custom_language.has_value() ? GameDatabase::GetLanguageName(custom_language.value()) :
nullptr))
{
return false;
}
// Don't need to rescan, since there's no original value to restore.
auto lock = GetLock();
Entry* entry = GetMutableEntryForPath(path);
if (entry)
entry->custom_language = custom_language.value_or(GameDatabase::Language::MaxCount);
return true;
} }
std::string GameList::GetCustomTitleForPath(const std::string_view path) std::string GameList::GetCustomTitleForPath(const std::string_view path)

View File

@ -40,6 +40,7 @@ struct Entry
bool disc_set_member = false; bool disc_set_member = false;
bool has_custom_title = false; bool has_custom_title = false;
bool has_custom_region = false; bool has_custom_region = false;
GameDatabase::Language custom_language = GameDatabase::Language::MaxCount;
std::string path; std::string path;
std::string serial; std::string serial;
@ -57,13 +58,14 @@ struct Entry
std::string_view GetLanguageIcon() const; std::string_view GetLanguageIcon() const;
TinyString GetLanguageIconFileName() const; TinyString GetLanguageIconName() const;
TinyString GetCompatibilityIconFileName() const; TinyString GetCompatibilityIconFileName() const;
TinyString GetReleaseDateString() const; TinyString GetReleaseDateString() const;
ALWAYS_INLINE bool IsDisc() const { return (type == EntryType::Disc); } ALWAYS_INLINE bool IsDisc() const { return (type == EntryType::Disc); }
ALWAYS_INLINE bool IsDiscSet() const { return (type == EntryType::DiscSet); } ALWAYS_INLINE bool IsDiscSet() const { return (type == EntryType::DiscSet); }
ALWAYS_INLINE bool HasCustomLanguage() const { return (custom_language != GameDatabase::Language::MaxCount); }
ALWAYS_INLINE EntryType GetSortType() const { return (type == EntryType::DiscSet) ? EntryType::Disc : type; } ALWAYS_INLINE EntryType GetSortType() const { return (type == EntryType::DiscSet) ? EntryType::Disc : type; }
}; };
@ -128,8 +130,9 @@ bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_seri
std::function<void(const Entry*, std::string)> save_callback = {}); std::function<void(const Entry*, std::string)> save_callback = {});
// Custom properties support // Custom properties support
void SaveCustomTitleForPath(const std::string& path, const std::string& custom_title); bool SaveCustomTitleForPath(const std::string& path, const std::string& custom_title);
void SaveCustomRegionForPath(const std::string& path, const std::optional<DiscRegion> custom_region); bool SaveCustomRegionForPath(const std::string& path, const std::optional<DiscRegion> custom_region);
bool SaveCustomLanguageForPath(const std::string& path, const std::optional<GameDatabase::Language> custom_language);
std::string GetCustomTitleForPath(const std::string_view path); std::string GetCustomTitleForPath(const std::string_view path);
std::optional<DiscRegion> GetCustomRegionForPath(const std::string_view path); std::optional<DiscRegion> GetCustomRegionForPath(const std::string_view path);

View File

@ -294,7 +294,7 @@ const QPixmap& GameListModel::getFlagPixmapForEntry(const GameList::Entry* ge) c
if (it != m_flag_pixmap_cache.end()) if (it != m_flag_pixmap_cache.end())
return it->second; return it->second;
const QIcon icon(QString::fromStdString(QtHost::GetResourcePath(ge->GetLanguageIconFileName(), true))); const QIcon icon(QString::fromStdString(QtHost::GetResourcePath(ge->GetLanguageIconName(), true)));
it = m_flag_pixmap_cache.emplace(name, icon.pixmap(FLAG_PIXMAP_WIDTH, FLAG_PIXMAP_HEIGHT)).first; it = m_flag_pixmap_cache.emplace(name, icon.pixmap(FLAG_PIXMAP_WIDTH, FLAG_PIXMAP_HEIGHT)).first;
return it->second; return it->second;
} }

View File

@ -52,6 +52,15 @@ GameSummaryWidget::GameSummaryWidget(const std::string& path, const std::string&
static_cast<GameDatabase::CompatibilityRating>(i)))); static_cast<GameDatabase::CompatibilityRating>(i))));
} }
// I hate this so much.
m_ui.customLanguage->addItem(QtUtils::GetIconForLanguage(entry->GetLanguageFlagName(region)),
tr("Show Default Flag"));
for (u32 i = 0; i < static_cast<u32>(GameDatabase::Language::MaxCount); i++)
{
const char* language_name = GameDatabase::GetLanguageName(static_cast<GameDatabase::Language>(i));
m_ui.customLanguage->addItem(QtUtils::GetIconForLanguage(language_name), QString::fromUtf8(language_name));
}
populateUi(path, serial, region, entry); populateUi(path, serial, region, entry);
connect(m_ui.compatibilityComments, &QToolButton::clicked, this, &GameSummaryWidget::onCompatibilityCommentsClicked); connect(m_ui.compatibilityComments, &QToolButton::clicked, this, &GameSummaryWidget::onCompatibilityCommentsClicked);
@ -69,6 +78,7 @@ GameSummaryWidget::GameSummaryWidget(const std::string& path, const std::string&
connect(m_ui.restoreTitle, &QAbstractButton::clicked, this, [this]() { setCustomTitle(std::string()); }); connect(m_ui.restoreTitle, &QAbstractButton::clicked, this, [this]() { setCustomTitle(std::string()); });
connect(m_ui.region, &QComboBox::currentIndexChanged, this, [this](int index) { setCustomRegion(index); }); connect(m_ui.region, &QComboBox::currentIndexChanged, this, [this](int index) { setCustomRegion(index); });
connect(m_ui.restoreRegion, &QAbstractButton::clicked, this, [this]() { setCustomRegion(-1); }); connect(m_ui.restoreRegion, &QAbstractButton::clicked, this, [this]() { setCustomRegion(-1); });
connect(m_ui.customLanguage, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onCustomLanguageChanged);
} }
GameSummaryWidget::~GameSummaryWidget() = default; GameSummaryWidget::~GameSummaryWidget() = default;
@ -147,6 +157,8 @@ void GameSummaryWidget::populateUi(const std::string& path, const std::string& s
else else
m_ui.releaseInfo->setText(tr("Unknown")); m_ui.releaseInfo->setText(tr("Unknown"));
m_ui.languages->setText(QtUtils::StringViewToQString(entry->GetLanguagesString()));
QString controllers; QString controllers;
if (entry->supported_controllers != 0 && entry->supported_controllers != static_cast<u16>(-1)) if (entry->supported_controllers != 0 && entry->supported_controllers != static_cast<u16>(-1))
{ {
@ -201,7 +213,10 @@ void GameSummaryWidget::populateCustomAttributes()
auto lock = GameList::GetLock(); auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(m_path); const GameList::Entry* entry = GameList::GetEntryForPath(m_path);
if (!entry || entry->IsDiscSet()) if (!entry || entry->IsDiscSet())
{
m_ui.customLanguage->setEnabled(false);
return; return;
}
{ {
QSignalBlocker sb(m_ui.title); QSignalBlocker sb(m_ui.title);
@ -214,6 +229,12 @@ void GameSummaryWidget::populateCustomAttributes()
m_ui.region->setCurrentIndex(static_cast<int>(entry->region)); m_ui.region->setCurrentIndex(static_cast<int>(entry->region));
m_ui.restoreRegion->setEnabled(entry->has_custom_region); m_ui.restoreRegion->setEnabled(entry->has_custom_region);
} }
{
QSignalBlocker sb(m_ui.customLanguage);
m_ui.customLanguage->setCurrentIndex(entry->HasCustomLanguage() ? (static_cast<u32>(entry->custom_language) + 1) :
0);
}
} }
void GameSummaryWidget::updateWindowTitle() void GameSummaryWidget::updateWindowTitle()
@ -238,7 +259,15 @@ void GameSummaryWidget::setCustomRegion(int region)
GameList::SaveCustomRegionForPath(m_path, (region >= 0) ? std::optional<DiscRegion>(static_cast<DiscRegion>(region)) : GameList::SaveCustomRegionForPath(m_path, (region >= 0) ? std::optional<DiscRegion>(static_cast<DiscRegion>(region)) :
std::optional<DiscRegion>()); std::optional<DiscRegion>());
populateCustomAttributes(); populateCustomAttributes();
updateWindowTitle(); g_main_window->refreshGameListModel();
}
void GameSummaryWidget::onCustomLanguageChanged(int language)
{
GameList::SaveCustomLanguageForPath(
m_path, (language > 0) ? std::optional<GameDatabase::Language>(static_cast<GameDatabase::Language>(language - 1)) :
std::optional<GameDatabase::Language>());
populateCustomAttributes();
g_main_window->refreshGameListModel(); g_main_window->refreshGameListModel();
} }

View File

@ -27,6 +27,7 @@ public:
void reloadGameSettings(); void reloadGameSettings();
private Q_SLOTS: private Q_SLOTS:
void onCustomLanguageChanged(int language);
void onCompatibilityCommentsClicked(); void onCompatibilityCommentsClicked();
void onInputProfileChanged(int index); void onInputProfileChanged(int index);
void onEditInputProfileClicked(); void onEditInputProfileClicked();

View File

@ -30,67 +30,36 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="8" column="1"> <item row="10" column="1">
<widget class="QLineEdit" name="genre"> <widget class="QLineEdit" name="developer">
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="13" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7"> <widget class="QLineEdit" name="controllers">
<item> <property name="readOnly">
<widget class="QLineEdit" name="title"> <bool>true</bool>
<property name="placeholderText">
<string>Clear the line to restore the original title...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreTitle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QComboBox" name="region">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreRegion">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Image Path:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="17" column="0">
<widget class="QLineEdit" name="path"> <widget class="QLabel" name="label_8">
<property name="text">
<string>Tracks:</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Input Profile:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="genre">
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -103,7 +72,35 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="0" colspan="2"> <item row="12" column="1">
<widget class="QLineEdit" name="releaseInfo">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="serial">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Controllers:</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Release Info:</string>
</property>
</widget>
</item>
<item row="19" column="0" colspan="2">
<widget class="QTableWidget" name="tracks"> <widget class="QTableWidget" name="tracks">
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set> <set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
@ -146,77 +143,7 @@
</column> </column>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="17" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Region:</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Developer:</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Controllers:</string>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Tracks:</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLineEdit" name="controllers">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="entryType">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Release Info:</string>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Input Profile:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="serial">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Genre:</string>
</property>
</widget>
</item>
<item row="16" column="1">
<layout class="QHBoxLayout" name="verifyLayout" stretch="0,1,0"> <layout class="QHBoxLayout" name="verifyLayout" stretch="0,1,0">
<item> <item>
<spacer name="verifySpacer"> <spacer name="verifySpacer">
@ -253,42 +180,42 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="9" column="1"> <item row="3" column="1">
<widget class="QLineEdit" name="developer"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="readOnly"> <item>
<bool>true</bool> <widget class="QLineEdit" name="title">
</property> <property name="placeholderText">
</widget> <string>Clear the line to restore the original title...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreTitle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
</layout>
</item> </item>
<item row="5" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_10"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Type:</string> <string>Image Path:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="5" column="1">
<widget class="QLabel" name="label_2"> <widget class="QComboBox" name="entryType">
<property name="text"> <property name="enabled">
<string>Title:</string> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="8" column="1">
<widget class="QLineEdit" name="releaseInfo">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Compatibility:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<item> <item>
<widget class="QComboBox" name="compatibility"> <widget class="QComboBox" name="compatibility">
@ -309,7 +236,28 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="13" column="1"> <item row="9" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Genre:</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Developer:</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Region:</string>
</property>
</widget>
</item>
<item row="14" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0"> <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
<item> <item>
<widget class="QComboBox" name="inputProfile"/> <widget class="QComboBox" name="inputProfile"/>
@ -323,6 +271,79 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="8" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Compatibility:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QComboBox" name="region">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreRegion">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="path">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Title:</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Languages:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,0">
<item>
<widget class="QLineEdit" name="languages">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="customLanguage"/>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -312,6 +312,12 @@ QIcon QtUtils::GetIconForCompatibility(GameDatabase::CompatibilityRating rating)
QtHost::GetResourcePath(TinyString::from_format("images/star-{}.svg", static_cast<u32>(rating)), true))); QtHost::GetResourcePath(TinyString::from_format("images/star-{}.svg", static_cast<u32>(rating)), true)));
} }
QIcon QtUtils::GetIconForLanguage(std::string_view language_name)
{
return QIcon(
QString::fromStdString(QtHost::GetResourcePath(GameDatabase::GetLanguageFlagResourceName(language_name), true)));
}
qreal QtUtils::GetDevicePixelRatioForWidget(const QWidget* widget) qreal QtUtils::GetDevicePixelRatioForWidget(const QWidget* widget)
{ {
const QScreen* screen_for_ratio = widget->screen(); const QScreen* screen_for_ratio = widget->screen();

View File

@ -113,6 +113,7 @@ QIcon GetIconForRegion(DiscRegion region);
/// Returns icon for entry type. /// Returns icon for entry type.
QIcon GetIconForEntryType(GameList::EntryType type); QIcon GetIconForEntryType(GameList::EntryType type);
QIcon GetIconForCompatibility(GameDatabase::CompatibilityRating rating); QIcon GetIconForCompatibility(GameDatabase::CompatibilityRating rating);
QIcon GetIconForLanguage(std::string_view language_name);
/// Returns the pixel ratio/scaling factor for a widget. /// Returns the pixel ratio/scaling factor for a widget.
qreal GetDevicePixelRatioForWidget(const QWidget* widget); qreal GetDevicePixelRatioForWidget(const QWidget* widget);