forked from stenzek/duckstation
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgamelistwidget.h
More file actions
339 lines (254 loc) · 9.94 KB
/
gamelistwidget.h
File metadata and controls
339 lines (254 loc) · 9.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include "ui_emptygamelistwidget.h"
#include "ui_gamelistwidget.h"
#include "core/game_database.h"
#include "core/game_list.h"
#include "core/types.h"
#include "common/heterogeneous_containers.h"
#include "common/lru_cache.h"
#include <QtCore/QAbstractTableModel>
#include <QtGui/QImage>
#include <QtGui/QPixmap>
#include <QtWidgets/QListView>
#include <QtWidgets/QTableView>
#include <algorithm>
#include <array>
#include <optional>
Q_DECLARE_METATYPE(const GameList::Entry*);
class QStyledItemDelegate;
class GameListSortModel;
class GameListRefreshThread;
class GameListWidget;
class GameListModel final : public QAbstractTableModel
{
Q_OBJECT
friend GameListWidget;
public:
enum Column : int
{
Column_Icon,
Column_Serial,
Column_Title,
Column_FileTitle,
Column_Developer,
Column_Publisher,
Column_Genre,
Column_Year,
Column_Players,
Column_TimePlayed,
Column_LastPlayed,
Column_FileSize,
Column_DataSize,
Column_Region,
Column_Achievements,
Column_Compatibility,
Column_Cover,
Column_Count,
Column_LastVisible = Column_Compatibility,
};
static std::optional<Column> getColumnIdForName(std::string_view name);
static const char* getColumnName(Column col);
explicit GameListModel(GameListWidget* parent);
~GameListModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
std::pair<std::unique_lock<std::recursive_mutex>, const GameList::Entry*>
getEntryForIndex(const QModelIndex& index) const;
const QPixmap& getNoAchievementsPixmap() const;
const QPixmap& getHasAchievementsPixmap() const;
const QPixmap& getMasteredAchievementsPixmap() const;
const GameList::Entry* getTakenGameListEntry(u32 index) const;
bool hasTakenGameList() const;
void takeGameList();
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;
bool getShowLocalizedTitles() const;
void setShowLocalizedTitles(bool enabled);
bool getShowCoverTitles() const;
void setShowCoverTitles(bool enabled);
int getRowHeight() const;
int getIconSize() const;
int getIconSizeWithPadding() const;
void refreshIcons();
void setIconSize(int size);
bool getShowGameIcons() const;
void setShowGameIcons(bool enabled);
QIcon getIconForGame(const QString& path);
float getCoverScale() const;
void setCoverScale(float scale);
QSize getCoverArtSize() const;
QSize getCoverArtItemSize() const;
QSize getDeviceScaledCoverArtSize() const;
int getCoverArtSpacing() const;
QFont getCoverCaptionFont() const;
void refreshCovers();
void updateCacheSize(int num_rows, int num_columns, QSortFilterProxyModel* const sort_model, int top_left_row);
qreal getDevicePixelRatio() const;
void setDevicePixelRatio(qreal dpr);
const QPixmap* lookupIconPixmapForEntry(const GameList::Entry* ge) const;
const QPixmap& getCoverForEntry(const GameList::Entry* ge) const;
void invalidateCoverCacheForPath(const std::string& path);
Q_SIGNALS:
void coverScaleChanged(float scale);
void iconSizeChanged(int size);
private:
struct CoverPixmapCacheEntry
{
QPixmap pixmap;
float scale;
bool is_loading;
};
void rowsChanged(const QList<int>& rows);
void loadCommonImages();
void loadSizeDependentPixmaps();
void updateCoverScale();
void loadCoverScaleDependentPixmaps();
void loadOrGenerateCover(const GameList::Entry* ge);
void invalidateCoverForPath(const std::string& path);
void coverLoaded(const std::string& path, const QImage& image, float scale);
static void loadOrGenerateCover(QImage& image, const QImage& placeholder_image, const QSize& size, float scale,
qreal dpr, const std::string& path, const std::string& serial,
const std::string& save_title, const QString& display_title, bool is_custom_title);
static void createPlaceholderImage(QImage& image, const QImage& placeholder_image, const QSize& size, float scale,
const QString& title);
const QPixmap& getIconPixmapForEntry(const GameList::Entry* ge) const;
const QPixmap& getFlagPixmapForEntry(const GameList::Entry* ge) const;
qreal m_device_pixel_ratio = 1.0;
std::optional<GameList::EntryList> m_taken_entries;
float m_cover_scale = 0.0f;
int m_icon_size = 0;
bool m_show_localized_titles = false;
bool m_show_titles_for_covers = false;
bool m_show_game_icons = false;
std::array<QPixmap, static_cast<int>(GameList::EntryType::MaxCount)> m_type_pixmaps;
std::array<QPixmap, static_cast<int>(GameDatabase::CompatibilityRating::Count)> m_compatibility_pixmaps;
QImage m_placeholder_image;
QPixmap m_loading_pixmap;
QPixmap m_no_achievements_pixmap;
QPixmap m_has_achievements_pixmap;
QPixmap m_mastered_achievements_pixmap;
mutable PreferUnorderedStringMap<QPixmap> m_flag_pixmap_cache;
mutable LRUCache<std::string, QPixmap> m_icon_pixmap_cache;
mutable LRUCache<std::string, CoverPixmapCacheEntry> m_cover_pixmap_cache;
};
class GameListListView final : public QTableView
{
Q_OBJECT
public:
GameListListView(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 adjustIconSize(int delta);
bool isAnimatingGameIcons() const;
void setAnimateGameIcons(bool enabled);
void updateAnimatedGameIconDelegate();
void clearAnimatedGameIconDelegate();
protected:
void wheelEvent(QWheelEvent* e) override;
private:
void loadColumnVisibilitySettings();
void loadColumnSortSettings();
void onHeaderContextMenuRequested(const QPoint& point);
void saveColumnSortSettings();
GameListModel* m_model = nullptr;
GameListSortModel* m_sort_model = nullptr;
QStyledItemDelegate* m_animated_game_icon_delegate = nullptr;
int m_animated_icon_row = -1;
};
class GameListGridView final : public QListView
{
Q_OBJECT
public:
GameListGridView(GameListModel* model, GameListSortModel* sort_model, QWidget* parent);
~GameListGridView() override;
int horizontalOffset() const override;
int verticalOffset() const override;
void adjustZoom(float delta);
void updateLayout();
protected:
void wheelEvent(QWheelEvent* e) override;
void resizeEvent(QResizeEvent* e) override;
private:
GameListModel* m_model = nullptr;
GameListSortModel* m_sort_model = nullptr;
int m_horizontal_offset = 0;
int m_vertical_offset = 0;
};
class GameListWidget final : public QWidget
{
Q_OBJECT
public:
explicit GameListWidget(QWidget* parent, QAction* action_view_list, QAction* action_view_grid,
QAction* action_merge_disc_sets, QAction* action_show_list_icons,
QAction* action_animate_list_icons, QAction* action_prefer_achievement_game_icons,
QAction* action_show_grid_titles, QAction* action_show_localized_titles);
~GameListWidget();
ALWAYS_INLINE GameListModel* getModel() const { return m_model; }
ALWAYS_INLINE GameListListView* getListView() const { return m_list_view; }
ALWAYS_INLINE GameListGridView* getGridView() const { return m_grid_view; }
void refresh(bool invalidate_cache);
void cancelRefresh();
void setBackgroundPath(const std::string_view path);
bool hasBackground() const;
bool isShowingGameList() const;
bool isShowingGameGrid() const;
void zoomOut();
void zoomIn();
const GameList::Entry* getSelectedEntry() const;
void showGameList();
void showGameGrid();
void setMergeDiscSets(bool enabled);
void setShowLocalizedTitles(bool enabled);
void setShowGameIcons(bool enabled);
void setAnimateGameIcons(bool enabled);
void setPreferAchievementGameIcons(bool enabled);
void downloadAllGameIcons();
void setShowCoverTitles(bool enabled);
void refreshGridCovers();
void focusSearchWidget();
Q_SIGNALS:
void refreshProgress(const QString& status, int current, int total);
void refreshComplete();
void selectionChanged();
void entryActivated();
void entryContextMenuRequested(const QPoint& point);
void addGameDirectoryRequested();
protected:
bool event(QEvent* e) override;
private:
void setViewMode(int stack_index);
void updateBackground(bool reload_image);
void onRefreshProgress(const QString& status, int current, int total, int entry_count, float time);
void onRefreshComplete();
void onThemeChanged();
void showScaleToolTip();
void onScaleSliderChanged(int value);
void onScaleChanged();
void onIconSizeChanged(int size);
void onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous);
void onListViewItemActivated(const QModelIndex& index);
void onListViewContextMenuRequested(const QPoint& point);
void onGridViewItemActivated(const QModelIndex& index);
void onGridViewContextMenuRequested(const QPoint& point);
void onSearchReturnPressed();
Ui::GameListWidget m_ui;
GameListModel* m_model = nullptr;
GameListSortModel* m_sort_model = nullptr;
GameListListView* m_list_view = nullptr;
GameListGridView* m_grid_view = nullptr;
QWidget* m_empty_widget = nullptr;
Ui::EmptyGameListWidget m_empty_ui;
QImage m_background_image;
GameListRefreshThread* m_refresh_thread = nullptr;
int m_refresh_last_entry_count = 0;
};