game_list_p.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // Copyright 2015 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <array>
  6. #include <map>
  7. #include <string>
  8. #include <utility>
  9. #include <QCoreApplication>
  10. #include <QFileInfo>
  11. #include <QImage>
  12. #include <QObject>
  13. #include <QStandardItem>
  14. #include <QString>
  15. #include <QWidget>
  16. #include "common/common_types.h"
  17. #include "common/logging/log.h"
  18. #include "common/string_util.h"
  19. #include "yuzu/uisettings.h"
  20. #include "yuzu/util/util.h"
  21. enum class GameListItemType {
  22. Game = QStandardItem::UserType + 1,
  23. CustomDir = QStandardItem::UserType + 2,
  24. SdmcDir = QStandardItem::UserType + 3,
  25. UserNandDir = QStandardItem::UserType + 4,
  26. SysNandDir = QStandardItem::UserType + 5,
  27. AddDir = QStandardItem::UserType + 6
  28. };
  29. Q_DECLARE_METATYPE(GameListItemType);
  30. /**
  31. * Gets the default icon (for games without valid title metadata)
  32. * @param size The desired width and height of the default icon.
  33. * @return QPixmap default icon
  34. */
  35. static QPixmap GetDefaultIcon(u32 size) {
  36. QPixmap icon(size, size);
  37. icon.fill(Qt::transparent);
  38. return icon;
  39. }
  40. class GameListItem : public QStandardItem {
  41. public:
  42. // used to access type from item index
  43. static const int TypeRole = Qt::UserRole + 1;
  44. static const int SortRole = Qt::UserRole + 2;
  45. GameListItem() = default;
  46. GameListItem(const QString& string) : QStandardItem(string) {
  47. setData(string, SortRole);
  48. }
  49. };
  50. /**
  51. * A specialization of GameListItem for path values.
  52. * This class ensures that for every full path value it holds, a correct string representation
  53. * of just the filename (with no extension) will be displayed to the user.
  54. * If this class receives valid title metadata, it will also display game icons and titles.
  55. */
  56. class GameListItemPath : public GameListItem {
  57. public:
  58. static const int TitleRole = SortRole;
  59. static const int FullPathRole = SortRole + 1;
  60. static const int ProgramIdRole = SortRole + 2;
  61. static const int FileTypeRole = SortRole + 3;
  62. GameListItemPath() = default;
  63. GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
  64. const QString& game_name, const QString& game_type, u64 program_id) {
  65. setData(type(), TypeRole);
  66. setData(game_path, FullPathRole);
  67. setData(game_name, TitleRole);
  68. setData(qulonglong(program_id), ProgramIdRole);
  69. setData(game_type, FileTypeRole);
  70. const u32 size = UISettings::values.icon_size;
  71. QPixmap picture;
  72. if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
  73. picture = GetDefaultIcon(size);
  74. }
  75. picture = picture.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
  76. setData(picture, Qt::DecorationRole);
  77. }
  78. int type() const override {
  79. return static_cast<int>(GameListItemType::Game);
  80. }
  81. QVariant data(int role) const override {
  82. if (role == Qt::DisplayRole) {
  83. std::string filename;
  84. Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
  85. nullptr);
  86. const std::array<QString, 4> row_data{{
  87. QString::fromStdString(filename),
  88. data(FileTypeRole).toString(),
  89. QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())),
  90. data(TitleRole).toString(),
  91. }};
  92. const auto& row1 = row_data.at(UISettings::values.row_1_text_id);
  93. const auto& row2 = row_data.at(UISettings::values.row_2_text_id);
  94. if (row1.isEmpty() || row1 == row2)
  95. return row2;
  96. if (row2.isEmpty())
  97. return row1;
  98. return QString(row1 + QStringLiteral("\n ") + row2);
  99. }
  100. return GameListItem::data(role);
  101. }
  102. };
  103. class GameListItemCompat : public GameListItem {
  104. Q_DECLARE_TR_FUNCTIONS(GameListItemCompat)
  105. public:
  106. static const int CompatNumberRole = SortRole;
  107. GameListItemCompat() = default;
  108. explicit GameListItemCompat(const QString& compatibility) {
  109. setData(type(), TypeRole);
  110. struct CompatStatus {
  111. QString color;
  112. const char* text;
  113. const char* tooltip;
  114. };
  115. // clang-format off
  116. static const std::map<QString, CompatStatus> status_data = {
  117. {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}},
  118. {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}},
  119. {QStringLiteral("2"), {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}},
  120. {QStringLiteral("3"), {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}},
  121. {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}},
  122. {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},
  123. {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}},
  124. };
  125. // clang-format on
  126. auto iterator = status_data.find(compatibility);
  127. if (iterator == status_data.end()) {
  128. LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString());
  129. return;
  130. }
  131. const CompatStatus& status = iterator->second;
  132. setData(compatibility, CompatNumberRole);
  133. setText(QObject::tr(status.text));
  134. setToolTip(QObject::tr(status.tooltip));
  135. setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
  136. }
  137. int type() const override {
  138. return static_cast<int>(GameListItemType::Game);
  139. }
  140. bool operator<(const QStandardItem& other) const override {
  141. return data(CompatNumberRole) < other.data(CompatNumberRole);
  142. }
  143. };
  144. /**
  145. * A specialization of GameListItem for size values.
  146. * This class ensures that for every numerical size value it holds (in bytes), a correct
  147. * human-readable string representation will be displayed to the user.
  148. */
  149. class GameListItemSize : public GameListItem {
  150. public:
  151. static const int SizeRole = SortRole;
  152. GameListItemSize() = default;
  153. explicit GameListItemSize(const qulonglong size_bytes) {
  154. setData(type(), TypeRole);
  155. setData(size_bytes, SizeRole);
  156. }
  157. void setData(const QVariant& value, int role) override {
  158. // By specializing setData for SizeRole, we can ensure that the numerical and string
  159. // representations of the data are always accurate and in the correct format.
  160. if (role == SizeRole) {
  161. qulonglong size_bytes = value.toULongLong();
  162. GameListItem::setData(ReadableByteSize(size_bytes), Qt::DisplayRole);
  163. GameListItem::setData(value, SizeRole);
  164. } else {
  165. GameListItem::setData(value, role);
  166. }
  167. }
  168. int type() const override {
  169. return static_cast<int>(GameListItemType::Game);
  170. }
  171. /**
  172. * This operator is, in practice, only used by the TreeView sorting systems.
  173. * Override it so that it will correctly sort by numerical value instead of by string
  174. * representation.
  175. */
  176. bool operator<(const QStandardItem& other) const override {
  177. return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong();
  178. }
  179. };
  180. class GameListDir : public GameListItem {
  181. public:
  182. static const int GameDirRole = Qt::UserRole + 2;
  183. explicit GameListDir(UISettings::GameDir& directory,
  184. GameListItemType dir_type = GameListItemType::CustomDir)
  185. : dir_type{dir_type} {
  186. setData(type(), TypeRole);
  187. UISettings::GameDir* game_dir = &directory;
  188. setData(QVariant::fromValue(game_dir), GameDirRole);
  189. const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
  190. switch (dir_type) {
  191. case GameListItemType::SdmcDir:
  192. setData(
  193. QIcon::fromTheme(QStringLiteral("sd_card"))
  194. .pixmap(icon_size)
  195. .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
  196. Qt::DecorationRole);
  197. setData(QObject::tr("Installed SD Titles"), Qt::DisplayRole);
  198. break;
  199. case GameListItemType::UserNandDir:
  200. setData(
  201. QIcon::fromTheme(QStringLiteral("chip"))
  202. .pixmap(icon_size)
  203. .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
  204. Qt::DecorationRole);
  205. setData(QObject::tr("Installed NAND Titles"), Qt::DisplayRole);
  206. break;
  207. case GameListItemType::SysNandDir:
  208. setData(
  209. QIcon::fromTheme(QStringLiteral("chip"))
  210. .pixmap(icon_size)
  211. .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
  212. Qt::DecorationRole);
  213. setData(QObject::tr("System Titles"), Qt::DisplayRole);
  214. break;
  215. case GameListItemType::CustomDir: {
  216. const QString icon_name = QFileInfo::exists(game_dir->path)
  217. ? QStringLiteral("folder")
  218. : QStringLiteral("bad_folder");
  219. setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
  220. icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
  221. Qt::DecorationRole);
  222. setData(game_dir->path, Qt::DisplayRole);
  223. break;
  224. }
  225. default:
  226. break;
  227. }
  228. }
  229. int type() const override {
  230. return static_cast<int>(dir_type);
  231. }
  232. private:
  233. GameListItemType dir_type;
  234. };
  235. class GameListAddDir : public GameListItem {
  236. public:
  237. explicit GameListAddDir() {
  238. setData(type(), TypeRole);
  239. const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
  240. setData(QIcon::fromTheme(QStringLiteral("plus"))
  241. .pixmap(icon_size)
  242. .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
  243. Qt::DecorationRole);
  244. setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole);
  245. }
  246. int type() const override {
  247. return static_cast<int>(GameListItemType::AddDir);
  248. }
  249. };
  250. class GameList;
  251. class QHBoxLayout;
  252. class QTreeView;
  253. class QLabel;
  254. class QLineEdit;
  255. class QToolButton;
  256. class GameListSearchField : public QWidget {
  257. Q_OBJECT
  258. public:
  259. explicit GameListSearchField(GameList* parent = nullptr);
  260. void setFilterResult(int visible, int total);
  261. void clear();
  262. void setFocus();
  263. private:
  264. class KeyReleaseEater : public QObject {
  265. public:
  266. explicit KeyReleaseEater(GameList* gamelist);
  267. private:
  268. GameList* gamelist = nullptr;
  269. QString edit_filter_text_old;
  270. protected:
  271. // EventFilter in order to process systemkeys while editing the searchfield
  272. bool eventFilter(QObject* obj, QEvent* event) override;
  273. };
  274. int visible;
  275. int total;
  276. QHBoxLayout* layout_filter = nullptr;
  277. QTreeView* tree_view = nullptr;
  278. QLabel* label_filter = nullptr;
  279. QLineEdit* edit_filter = nullptr;
  280. QLabel* label_filter_result = nullptr;
  281. QToolButton* button_filter_close = nullptr;
  282. };