Qt: Implement game grid/cover view

This commit is contained in:
Connor McLaughlin 2020-09-24 00:02:13 +10:00
parent b193374dd4
commit 43b0d84a1d
9 changed files with 447 additions and 57 deletions

View file

@ -2,9 +2,67 @@
#include "common/string_util.h"
#include "core/system.h"
#include <QtGui/QIcon>
#include <QtGui/QPainter>
static constexpr std::array<const char*, GameListModel::Column_Count> s_column_names = {
{"Type", "Code", "Title", "File Title", "Size", "Region", "Compatibility"}};
{"Type", "Code", "Title", "File Title", "Size", "Region", "Compatibility", "Cover"}};
static constexpr int COVER_ART_WIDTH = 512;
static constexpr int COVER_ART_HEIGHT = 512;
static constexpr int COVER_ART_SPACING = 32;
static void resizeAndPadPixmap(QPixmap* pm, int expected_width, int expected_height)
{
if (pm->width() == expected_width && pm->height() == expected_height)
return;
*pm = pm->scaled(expected_width, expected_height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
if (pm->width() == expected_width && pm->height() == expected_height)
return;
int xoffs = 0;
int yoffs = 0;
if (pm->width() < expected_width)
xoffs = (expected_width - pm->width()) / 2;
if (pm->height() < expected_height)
yoffs = (expected_height - pm->height()) / 2;
QPixmap padded_image(expected_width, expected_height);
padded_image.fill(Qt::transparent);
QPainter painter;
if (painter.begin(&padded_image))
{
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.drawPixmap(xoffs, yoffs, *pm);
painter.setCompositionMode(QPainter::CompositionMode_Destination);
painter.fillRect(padded_image.rect(), QColor(0, 0, 0, 0));
painter.end();
}
*pm = padded_image;
}
static QPixmap createPlaceholderImage(int width, int height, float scale, const std::string& title)
{
QPixmap pm(QStringLiteral(":/icons/cover-placeholder.png"));
if (pm.isNull())
return QPixmap(width, height);
resizeAndPadPixmap(&pm, width, height);
QPainter painter;
if (painter.begin(&pm))
{
QFont font;
font.setPointSize(std::max(static_cast<int>(32.0f * scale), 1));
painter.setFont(font);
painter.setPen(Qt::white);
painter.drawText(QRect(0, 0, width, height), Qt::AlignCenter | Qt::TextWordWrap, QString::fromStdString(title));
painter.end();
}
return pm;
}
std::optional<GameListModel::Column> GameListModel::getColumnIdForName(std::string_view name)
{
@ -30,6 +88,30 @@ GameListModel::GameListModel(GameList* game_list, QObject* parent /* = nullptr *
}
GameListModel::~GameListModel() = default;
void GameListModel::setCoverScale(float scale)
{
if (m_cover_scale == scale)
return;
m_cover_pixmap_cache.clear();
m_cover_scale = scale;
}
int GameListModel::getCoverArtWidth() const
{
return std::max(static_cast<int>(static_cast<float>(COVER_ART_WIDTH) * m_cover_scale), 1);
}
int GameListModel::getCoverArtHeight() const
{
return std::max(static_cast<int>(static_cast<float>(COVER_ART_HEIGHT) * m_cover_scale), 1);
}
int GameListModel::getCoverArtSpacing() const
{
return std::max(static_cast<int>(static_cast<float>(COVER_ART_SPACING) * m_cover_scale), 1);
}
int GameListModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
@ -78,6 +160,14 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
case Column_Size:
return QString("%1 MB").arg(static_cast<double>(ge.total_size) / 1048576.0, 0, 'f', 2);
case Column_Cover:
{
if (m_show_titles_for_covers)
return QString::fromStdString(ge.title);
else
return {};
}
default:
return {};
}
@ -94,6 +184,7 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
return QString::fromStdString(ge.code);
case Column_Title:
case Column_Cover:
return QString::fromStdString(ge.title);
case Column_FileTitle:
@ -155,6 +246,29 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
ge.compatibility_rating)];
}
case Column_Cover:
{
auto it = m_cover_pixmap_cache.find(ge.path);
if (it != m_cover_pixmap_cache.end())
return it->second;
QPixmap image;
std::string path = m_game_list->GetCoverImagePathForEntry(&ge);
if (!path.empty())
{
image = QPixmap(QString::fromStdString(path));
if (!image.isNull())
resizeAndPadPixmap(&image, getCoverArtWidth(), getCoverArtHeight());
}
if (image.isNull())
image = createPlaceholderImage(getCoverArtWidth(), getCoverArtHeight(), m_cover_scale, ge.title);
m_cover_pixmap_cache.emplace(ge.path, image);
return image;
}
break;
default:
return {};
}

View file

@ -3,8 +3,10 @@
#include "frontend-common/game_list.h"
#include <QtCore/QAbstractTableModel>
#include <QtGui/QPixmap>
#include <algorithm>
#include <array>
#include <optional>
#include <unordered_map>
class GameListModel final : public QAbstractTableModel
{
@ -20,6 +22,7 @@ public:
Column_Size,
Column_Region,
Column_Compatibility,
Column_Cover,
Column_Count
};
@ -43,11 +46,22 @@ public:
bool lessThan(const QModelIndex& left_index, const QModelIndex& right_index, int column, bool ascending) const;
bool getShowCoverTitles() const { return m_show_titles_for_covers; }
void setShowCoverTitles(bool enabled) { m_show_titles_for_covers = enabled; }
float getCoverScale() const { return m_cover_scale; }
void setCoverScale(float scale);
int getCoverArtWidth() const;
int getCoverArtHeight() const;
int getCoverArtSpacing() const;
private:
void loadCommonImages();
void setColumnDisplayNames();
GameList* m_game_list;
float m_cover_scale = 1.0f;
bool m_show_titles_for_covers = false;
std::array<QString, Column_Count> m_column_display_names;
@ -60,4 +74,5 @@ private:
QPixmap m_region_us_pixmap;
std::array<QPixmap, static_cast<int>(GameListCompatibilityRating::Count)> m_compatibiliy_pixmaps;
mutable std::unordered_map<std::string, QPixmap> m_cover_pixmap_cache;
};

View file

@ -7,6 +7,7 @@
#include "qtutils.h"
#include <QtCore/QSortFilterProxyModel>
#include <QtGui/QPixmap>
#include <QtGui/QWheelEvent>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QMenu>
@ -42,11 +43,14 @@ void GameListWidget::initialize(QtHostInterface* host_interface)
connect(m_host_interface, &QtHostInterface::gameListRefreshed, this, &GameListWidget::onGameListRefreshed);
m_table_model = new GameListModel(m_game_list, this);
m_table_sort_model = new GameListSortModel(m_table_model);
m_table_sort_model->setSourceModel(m_table_model);
m_model = new GameListModel(m_game_list, this);
m_model->setCoverScale(host_interface->GetFloatSettingValue("UI", "GameListCoverArtScale", 0.45f));
m_model->setShowCoverTitles(host_interface->GetBoolSettingValue("UI", "GameListShowCoverTitles", true));
m_sort_model = new GameListSortModel(m_model);
m_sort_model->setSourceModel(m_model);
m_table_view = new QTableView(this);
m_table_view->setModel(m_table_sort_model);
m_table_view->setModel(m_sort_model);
m_table_view->setSortingEnabled(true);
m_table_view->setSelectionMode(QAbstractItemView::SingleSelection);
m_table_view->setSelectionBehavior(QAbstractItemView::SelectRows);
@ -73,19 +77,59 @@ void GameListWidget::initialize(QtHostInterface* host_interface)
&GameListWidget::onTableViewHeaderSortIndicatorChanged);
insertWidget(0, m_table_view);
setCurrentIndex(0);
m_list_view = new GameListGridListView(this);
m_list_view->setModel(m_sort_model);
m_list_view->setModelColumn(GameListModel::Column_Cover);
m_list_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_list_view->setViewMode(QListView::IconMode);
m_list_view->setResizeMode(QListView::Adjust);
m_list_view->setUniformItemSizes(true);
m_list_view->setContextMenuPolicy(Qt::CustomContextMenu);
m_list_view->setFrameStyle(QFrame::NoFrame);
m_list_view->setSpacing(m_model->getCoverArtSpacing());
updateListFont();
connect(m_list_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
&GameListWidget::onSelectionModelCurrentChanged);
connect(m_list_view, &GameListGridListView::zoomIn, this, &GameListWidget::listZoomIn);
connect(m_list_view, &GameListGridListView::zoomOut, this, &GameListWidget::listZoomOut);
connect(m_list_view, &QListView::doubleClicked, this, &GameListWidget::onListViewItemDoubleClicked);
connect(m_list_view, &QListView::customContextMenuRequested, this, &GameListWidget::onListViewContextMenuRequested);
insertWidget(1, m_list_view);
if (m_host_interface->GetBoolSettingValue("UI", "GameListGridView", false))
setCurrentIndex(1);
else
setCurrentIndex(0);
resizeTableViewColumnsToFit();
}
bool GameListWidget::isShowingGameList() const
{
return currentIndex() == 0;
}
bool GameListWidget::isShowingGameGrid() const
{
return currentIndex() == 1;
}
bool GameListWidget::getShowGridCoverTitles() const
{
return m_model->getShowCoverTitles();
}
void GameListWidget::onGameListRefreshed()
{
m_table_model->refresh();
m_model->refresh();
}
void GameListWidget::onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous)
{
const QModelIndex source_index = m_table_sort_model->mapToSource(current);
const QModelIndex source_index = m_sort_model->mapToSource(current);
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
{
emit entrySelected(nullptr);
@ -98,7 +142,7 @@ void GameListWidget::onSelectionModelCurrentChanged(const QModelIndex& current,
void GameListWidget::onTableViewItemDoubleClicked(const QModelIndex& index)
{
const QModelIndex source_index = m_table_sort_model->mapToSource(index);
const QModelIndex source_index = m_sort_model->mapToSource(index);
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
return;
@ -115,13 +159,35 @@ void GameListWidget::onTableViewContextMenuRequested(const QPoint& point)
emit entryContextMenuRequested(m_table_view->mapToGlobal(point), entry);
}
void GameListWidget::onListViewItemDoubleClicked(const QModelIndex& index)
{
const QModelIndex source_index = m_sort_model->mapToSource(index);
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
return;
const GameListEntry& entry = m_game_list->GetEntries().at(source_index.row());
emit entryDoubleClicked(&entry);
}
void GameListWidget::onListViewContextMenuRequested(const QPoint& point)
{
const GameListEntry* entry = getSelectedEntry();
if (!entry)
return;
emit entryContextMenuRequested(m_list_view->mapToGlobal(point), entry);
}
void GameListWidget::onTableViewHeaderContextMenuRequested(const QPoint& point)
{
QMenu menu;
for (int column = 0; column < GameListModel::Column_Count; column++)
{
QAction* action = menu.addAction(m_table_model->getColumnDisplayName(column));
if (column == GameListModel::Column_Cover)
continue;
QAction* action = menu.addAction(m_model->getColumnDisplayName(column));
action->setCheckable(true);
action->setChecked(!m_table_view->isColumnHidden(column));
connect(action, &QAction::toggled, [this, column](bool enabled) {
@ -139,6 +205,66 @@ void GameListWidget::onTableViewHeaderSortIndicatorChanged(int, Qt::SortOrder)
saveTableViewColumnSortSettings();
}
void GameListWidget::listZoom(float delta)
{
static constexpr float MIN_SCALE = 0.1f;
static constexpr float MAX_SCALE = 2.0f;
const float new_scale = std::clamp(m_model->getCoverScale() + delta, MIN_SCALE, MAX_SCALE);
m_host_interface->SetFloatSettingValue("UI", "GameListCoverArtScale", new_scale);
m_model->setCoverScale(new_scale);
updateListFont();
m_model->refresh();
}
void GameListWidget::listZoomIn()
{
listZoom(0.05f);
}
void GameListWidget::listZoomOut()
{
listZoom(-0.05f);
}
void GameListWidget::showGameList()
{
if (currentIndex() == 0)
return;
m_host_interface->SetBoolSettingValue("UI", "GameListGridView", false);
setCurrentIndex(0);
resizeTableViewColumnsToFit();
}
void GameListWidget::showGameGrid()
{
if (currentIndex() == 1)
return;
m_host_interface->SetBoolSettingValue("UI", "GameListGridView", true);
setCurrentIndex(1);
}
void GameListWidget::setShowCoverTitles(bool enabled)
{
if (m_model->getShowCoverTitles() == enabled)
return;
m_host_interface->SetBoolSettingValue("UI", "GameListShowCoverTitles", enabled);
m_model->setShowCoverTitles(enabled);
if (isShowingGameGrid())
m_model->refresh();
}
void GameListWidget::updateListFont()
{
QFont font;
font.setPointSizeF(16.0f * m_model->getCoverScale());
m_list_view->setFont(font);
}
void GameListWidget::resizeEvent(QResizeEvent* event)
{
QStackedWidget::resizeEvent(event);
@ -193,7 +319,7 @@ void GameListWidget::loadTableViewColumnSortSettings()
.value_or(DEFAULT_SORT_COLUMN);
const bool sort_descending =
m_host_interface->GetBoolSettingValue("GameListTableView", "SortDescending", DEFAULT_SORT_DESCENDING);
m_table_sort_model->sort(sort_column, sort_descending ? Qt::DescendingOrder : Qt::AscendingOrder);
m_sort_model->sort(sort_column, sort_descending ? Qt::DescendingOrder : Qt::AscendingOrder);
}
void GameListWidget::saveTableViewColumnSortSettings()
@ -212,17 +338,53 @@ void GameListWidget::saveTableViewColumnSortSettings()
const GameListEntry* GameListWidget::getSelectedEntry() const
{
const QItemSelectionModel* selection_model = m_table_view->selectionModel();
if (!selection_model->hasSelection())
return nullptr;
if (currentIndex() == 0)
{
const QItemSelectionModel* selection_model = m_table_view->selectionModel();
if (!selection_model->hasSelection())
return nullptr;
const QModelIndexList selected_rows = selection_model->selectedRows();
if (selected_rows.empty())
return nullptr;
const QModelIndexList selected_rows = selection_model->selectedRows();
if (selected_rows.empty())
return nullptr;
const QModelIndex source_index = m_table_sort_model->mapToSource(selected_rows[0]);
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
return nullptr;
const QModelIndex source_index = m_sort_model->mapToSource(selected_rows[0]);
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
return nullptr;
return &m_game_list->GetEntries().at(source_index.row());
return &m_game_list->GetEntries().at(source_index.row());
}
else
{
const QItemSelectionModel* selection_model = m_list_view->selectionModel();
if (!selection_model->hasSelection())
return nullptr;
const QModelIndex source_index = m_sort_model->mapToSource(selection_model->currentIndex());
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
return nullptr;
return &m_game_list->GetEntries().at(source_index.row());
}
}
GameListGridListView::GameListGridListView(QWidget* parent /*= nullptr*/) : QListView(parent) {}
void GameListGridListView::wheelEvent(QWheelEvent* e)
{
if (e->modifiers() & Qt::ControlModifier)
{
int dy = e->angleDelta().y();
if (dy != 0)
{
if (dy < 0)
zoomOut();
else
zoomIn();
return;
}
}
QListView::wheelEvent(e);
}

View file

@ -1,4 +1,5 @@
#pragma once
#include <QtWidgets/QListView>
#include <QtWidgets/QStackedWidget>
#include <QtWidgets/QTableView>
@ -10,6 +11,21 @@ class GameListSortModel;
class QtHostInterface;
class GameListGridListView : public QListView
{
Q_OBJECT
public:
GameListGridListView(QWidget* parent = nullptr);
Q_SIGNALS:
void zoomOut();
void zoomIn();
protected:
void wheelEvent(QWheelEvent* e);
};
class GameListWidget : public QStackedWidget
{
Q_OBJECT
@ -20,6 +36,11 @@ public:
void initialize(QtHostInterface* host_interface);
bool isShowingGameList() const;
bool isShowingGameGrid() const;
bool getShowGridCoverTitles() const;
Q_SIGNALS:
void entrySelected(const GameListEntry* entry);
void entryDoubleClicked(const GameListEntry* entry);
@ -32,6 +53,15 @@ private Q_SLOTS:
void onTableViewContextMenuRequested(const QPoint& point);
void onTableViewHeaderContextMenuRequested(const QPoint& point);
void onTableViewHeaderSortIndicatorChanged(int, Qt::SortOrder);
void onListViewItemDoubleClicked(const QModelIndex& index);
void onListViewContextMenuRequested(const QPoint& point);
public Q_SLOTS:
void showGameList();
void showGameGrid();
void setShowCoverTitles(bool enabled);
void listZoomIn();
void listZoomOut();
protected:
void resizeEvent(QResizeEvent* event);
@ -44,11 +74,14 @@ private:
void saveTableViewColumnVisibilitySettings(int column);
void loadTableViewColumnSortSettings();
void saveTableViewColumnSortSettings();
void listZoom(float delta);
void updateListFont();
QtHostInterface* m_host_interface = nullptr;
GameList* m_game_list = nullptr;
GameListModel* m_table_model = nullptr;
GameListSortModel* m_table_sort_model = nullptr;
GameListModel* m_model = nullptr;
GameListSortModel* m_sort_model = nullptr;
QTableView* m_table_view = nullptr;
GameListGridListView* m_list_view = nullptr;
};

View file

@ -335,6 +335,15 @@ void MainWindow::onViewGameListActionTriggered()
if (m_emulation_running)
m_host_interface->pauseSystem(true);
switchToGameListView();
m_game_list_widget->showGameList();
}
void MainWindow::onViewGameGridActionTriggered()
{
if (m_emulation_running)
m_host_interface->pauseSystem(true);
switchToGameListView();
m_game_list_widget->showGameGrid();
}
void MainWindow::onViewSystemDisplayTriggered()
@ -490,6 +499,8 @@ void MainWindow::setupAdditionalUi()
m_status_frame_time_widget->setFixedSize(190, 16);
m_status_frame_time_widget->hide();
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
updateDebugMenuVisibility();
for (u32 i = 0; i < static_cast<u32>(CPUExecutionMode::Count); i++)
@ -597,6 +608,11 @@ void MainWindow::updateEmulationActions(bool starting, bool running)
m_ui.statusBar->clearMessage();
}
bool MainWindow::isShowingGameList() const
{
return m_ui.mainContainer->currentIndex() == 0;
}
void MainWindow::switchToGameListView()
{
m_ui.mainContainer->setCurrentIndex(0);
@ -671,6 +687,7 @@ void MainWindow::connectSignals()
connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled);
connect(m_ui.actionViewStatusBar, &QAction::toggled, this, &MainWindow::onViewStatusBarActionToggled);
connect(m_ui.actionViewGameList, &QAction::triggered, this, &MainWindow::onViewGameListActionTriggered);
connect(m_ui.actionViewGameGrid, &QAction::triggered, this, &MainWindow::onViewGameGridActionTriggered);
connect(m_ui.actionViewSystemDisplay, &QAction::triggered, this, &MainWindow::onViewSystemDisplayTriggered);
connect(m_ui.actionGitHubRepository, &QAction::triggered, this, &MainWindow::onGitHubRepositoryActionTriggered);
connect(m_ui.actionIssueTracker, &QAction::triggered, this, &MainWindow::onIssueTrackerActionTriggered);
@ -678,6 +695,15 @@ void MainWindow::connectSignals()
connect(m_ui.actionAbout, &QAction::triggered, this, &MainWindow::onAboutActionTriggered);
connect(m_ui.actionCheckForUpdates, &QAction::triggered, this, &MainWindow::onCheckForUpdatesActionTriggered);
connect(m_ui.actionMemory_Card_Editor, &QAction::triggered, this, &MainWindow::onToolsMemoryCardEditorTriggered);
connect(m_ui.actionGridViewShowTitles, &QAction::triggered, m_game_list_widget, &GameListWidget::setShowCoverTitles);
connect(m_ui.actionGridViewZoomIn, &QAction::triggered, m_game_list_widget, [this]() {
if (isShowingGameList())
m_game_list_widget->listZoomIn();
});
connect(m_ui.actionGridViewZoomOut, &QAction::triggered, m_game_list_widget, [this]() {
if (isShowingGameList())
m_game_list_widget->listZoomOut();
});
connect(m_host_interface, &QtHostInterface::errorReported, this, &MainWindow::reportError,
Qt::BlockingQueuedConnection);

View file

@ -67,6 +67,7 @@ private Q_SLOTS:
void onViewToolbarActionToggled(bool checked);
void onViewStatusBarActionToggled(bool checked);
void onViewGameListActionTriggered();
void onViewGameGridActionTriggered();
void onViewSystemDisplayTriggered();
void onGitHubRepositoryActionTriggered();
void onIssueTrackerActionTriggered();
@ -91,6 +92,7 @@ private:
void connectSignals();
void addThemeToMenu(const QString& name, const QString& key);
void updateEmulationActions(bool starting, bool running);
bool isShowingGameList() const;
void switchToGameListView();
void switchToEmulationView();
void saveStateToConfig();

View file

@ -14,7 +14,7 @@
<string>DuckStation</string>
</property>
<property name="windowIcon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
</property>
<widget class="QStackedWidget" name="mainContainer">
@ -42,7 +42,7 @@
<string>Change Disc</string>
</property>
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/media-optical.png</normaloff>:/icons/media-optical.png</iconset>
</property>
<widget class="QMenu" name="menuChangeDiscFromPlaylist">
@ -60,7 +60,7 @@
<string>Cheats</string>
</property>
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/conical-flask-red.png</normaloff>:/icons/conical-flask-red.png</iconset>
</property>
</widget>
@ -69,7 +69,7 @@
<string>Load State</string>
</property>
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/document-open.png</normaloff>:/icons/document-open.png</iconset>
</property>
</widget>
@ -78,7 +78,7 @@
<string>Save State</string>
</property>
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/document-save.png</normaloff>:/icons/document-save.png</iconset>
</property>
</widget>
@ -115,7 +115,7 @@
<addaction name="actionFullscreen"/>
<addaction name="separator"/>
<addaction name="actionGeneralSettings"/>
<addaction name="actionBIOSSettings"/>
<addaction name="actionBIOSSettings"/>
<addaction name="actionConsoleSettings"/>
<addaction name="actionGameListSettings"/>
<addaction name="actionHotkeySettings"/>
@ -183,7 +183,12 @@
<addaction name="actionViewStatusBar"/>
<addaction name="separator"/>
<addaction name="actionViewGameList"/>
<addaction name="actionViewGameGrid"/>
<addaction name="actionViewSystemDisplay"/>
<addaction name="separator"/>
<addaction name="actionGridViewShowTitles"/>
<addaction name="actionGridViewZoomIn"/>
<addaction name="actionGridViewZoomOut"/>
</widget>
<widget class="QMenu" name="menu_Tools">
<property name="title">
@ -236,7 +241,7 @@
<widget class="QStatusBar" name="statusBar"/>
<action name="actionStartDisc">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/drive-optical.png</normaloff>:/icons/drive-optical.png</iconset>
</property>
<property name="text">
@ -245,7 +250,7 @@
</action>
<action name="actionStartBios">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/drive-removable-media.png</normaloff>:/icons/drive-removable-media.png</iconset>
</property>
<property name="text">
@ -254,7 +259,7 @@
</action>
<action name="actionScanForNewGames">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/folder-open.png</normaloff>:/icons/folder-open.png</iconset>
</property>
<property name="text">
@ -263,7 +268,7 @@
</action>
<action name="actionRescanAllGames">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/view-refresh.png</normaloff>:/icons/view-refresh.png</iconset>
</property>
<property name="text">
@ -272,7 +277,7 @@
</action>
<action name="actionPowerOff">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/system-shutdown.png</normaloff>:/icons/system-shutdown.png</iconset>
</property>
<property name="text">
@ -281,7 +286,7 @@
</action>
<action name="actionReset">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/view-refresh.png</normaloff>:/icons/view-refresh.png</iconset>
</property>
<property name="text">
@ -293,7 +298,7 @@
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/media-playback-pause.png</normaloff>:/icons/media-playback-pause.png</iconset>
</property>
<property name="text">
@ -302,7 +307,7 @@
</action>
<action name="actionLoadState">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/document-open.png</normaloff>:/icons/document-open.png</iconset>
</property>
<property name="text">
@ -311,7 +316,7 @@
</action>
<action name="actionSaveState">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/document-save.png</normaloff>:/icons/document-save.png</iconset>
</property>
<property name="text">
@ -334,7 +339,7 @@
</action>
<action name="actionConsoleSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/utilities-system-monitor.png</normaloff>:/icons/utilities-system-monitor.png</iconset>
</property>
<property name="text">
@ -343,7 +348,7 @@
</action>
<action name="actionControllerSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/input-gaming.png</normaloff>:/icons/input-gaming.png</iconset>
</property>
<property name="text">
@ -352,7 +357,7 @@
</action>
<action name="actionHotkeySettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/applications-other.png</normaloff>:/icons/applications-other.png</iconset>
</property>
<property name="text">
@ -361,7 +366,7 @@
</action>
<action name="actionDisplaySettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/video-display.png</normaloff>:/icons/video-display.png</iconset>
</property>
<property name="text">
@ -370,7 +375,7 @@
</action>
<action name="actionEnhancementSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/antialias-icon.png</normaloff>:/icons/antialias-icon.png</iconset>
</property>
<property name="text">
@ -379,7 +384,7 @@
</action>
<action name="actionPostProcessingSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/applications-graphics.png</normaloff>:/icons/applications-graphics.png</iconset>
</property>
<property name="text">
@ -388,7 +393,7 @@
</action>
<action name="actionFullscreen">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/view-fullscreen.png</normaloff>:/icons/view-fullscreen.png</iconset>
</property>
<property name="text">
@ -427,7 +432,7 @@
</action>
<action name="actionChangeDisc">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/media-optical.png</normaloff>:/icons/media-optical.png</iconset>
</property>
<property name="text">
@ -436,7 +441,7 @@
</action>
<action name="actionCheats">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/conical-flask-red.png</normaloff>:/icons/conical-flask-red.png</iconset>
</property>
<property name="text">
@ -445,7 +450,7 @@
</action>
<action name="actionAudioSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/audio-card.png</normaloff>:/icons/audio-card.png</iconset>
</property>
<property name="text">
@ -454,7 +459,7 @@
</action>
<action name="actionGameListSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/folder-open.png</normaloff>:/icons/folder-open.png</iconset>
</property>
<property name="text">
@ -463,7 +468,7 @@
</action>
<action name="actionGeneralSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/applications-system.png</normaloff>:/icons/applications-system.png</iconset>
</property>
<property name="text">
@ -472,7 +477,7 @@
</action>
<action name="actionAdvancedSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/applications-development.png</normaloff>:/icons/applications-development.png</iconset>
</property>
<property name="text">
@ -481,7 +486,7 @@
</action>
<action name="actionAddGameDirectory">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/edit-find.png</normaloff>:/icons/edit-find.png</iconset>
</property>
<property name="text">
@ -490,7 +495,7 @@
</action>
<action name="actionSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/preferences-system.png</normaloff>:/icons/preferences-system.png</iconset>
</property>
<property name="text">
@ -601,7 +606,7 @@
</action>
<action name="actionScreenshot">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/camera-photo.png</normaloff>:/icons/camera-photo.png</iconset>
</property>
<property name="text">
@ -610,7 +615,7 @@
</action>
<action name="actionMemoryCardSettings">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/media-flash-24.png</normaloff>:/icons/media-flash-24.png</iconset>
</property>
<property name="text">
@ -619,7 +624,7 @@
</action>
<action name="actionResumeLastState">
<property name="icon">
<iconset>
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/media-playback-start.png</normaloff>:/icons/media-playback-start.png</iconset>
</property>
<property name="text">
@ -669,9 +674,41 @@
<string>Memory &amp;Card Editor</string>
</property>
</action>
<action name="actionViewGameGrid">
<property name="text">
<string>Game Grid</string>
</property>
</action>
<action name="actionGridViewShowTitles">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Show Titles (Grid View)</string>
</property>
</action>
<action name="actionGridViewZoomIn">
<property name="text">
<string>Zoom In (Grid View)</string>
</property>
<property name="shortcut">
<string>Ctrl++</string>
</property>
</action>
<action name="actionGridViewZoomOut">
<property name="text">
<string>Zoom Out (Grid View)</string>
</property>
<property name="shortcut">
<string>Ctrl+-</string>
</property>
</action>
</widget>
<resources>
<include location="resources/icons.qrc"/>
<include location="resources/resources.qrc"/>
</resources>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View file

@ -23,6 +23,7 @@
<file>icons/camera-video@2x.png</file>
<file>icons/conical-flask-red.png</file>
<file>icons/conical-flask-red@2x.png</file>
<file>icons/cover-placeholder.png</file>
<file>icons/document-open.png</file>
<file>icons/document-open@2x.png</file>
<file>icons/document-save.png</file>