mirror of https://github.com/PCSX2/pcsx2.git
Qt: Add Custom background support
Qt: Make sure custom background aren't active when game list isn't shown To save on CPU Power and be more efficient Co-Authored-By: TheLastRar <TheLastRar@users.noreply.github.com>
This commit is contained in:
parent
0f75bfe17d
commit
3fa821a001
|
@ -7,25 +7,33 @@
|
|||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "VMManager.h"
|
||||
#include "pcsx2/GameList.h"
|
||||
#include "pcsx2/Host.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtGui/QPixmapCache>
|
||||
#include <QtGui/QWheelEvent>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
#include <QtWidgets/QStyledItemDelegate>
|
||||
#include <qguiapplication.h>
|
||||
#include <qstringview.h>
|
||||
#include <qwidget.h>
|
||||
|
||||
static const char* SUPPORTED_FORMATS_STRING = QT_TRANSLATE_NOOP(GameListWidget,
|
||||
".bin/.iso (ISO Disc Images)\n"
|
||||
|
@ -280,6 +288,7 @@ void GameListWidget::initialize()
|
|||
m_empty_ui.supportedFormats->setText(qApp->translate("GameListWidget", SUPPORTED_FORMATS_STRING));
|
||||
connect(m_empty_ui.addGameDirectory, &QPushButton::clicked, this, [this]() { emit addGameDirectoryRequested(); });
|
||||
connect(m_empty_ui.scanForNewGames, &QPushButton::clicked, this, [this]() { refresh(false); });
|
||||
connect(qApp, &QGuiApplication::applicationStateChanged, this, [this](){GameListWidget::updateCustomBackgroundState();});
|
||||
m_ui.stack->insertWidget(2, m_empty_widget);
|
||||
|
||||
if (Host::GetBaseBoolSettingValue("UI", "GameListGridView", false))
|
||||
|
@ -291,6 +300,128 @@ void GameListWidget::initialize()
|
|||
|
||||
updateToolbar();
|
||||
resizeTableViewColumnsToFit();
|
||||
setCustomBackground();
|
||||
}
|
||||
|
||||
static void resizeAndPadImage(QImage* image, int expected_width, int expected_height, bool fill_with_top_left)
|
||||
{
|
||||
const qreal dpr = image->devicePixelRatio();
|
||||
const int dpr_expected_width = static_cast<int>(static_cast<qreal>(expected_width) * dpr);
|
||||
const int dpr_expected_height = static_cast<int>(static_cast<qreal>(expected_height) * dpr);
|
||||
if (image->width() == dpr_expected_width && image->height() == dpr_expected_height)
|
||||
return;
|
||||
|
||||
// Resize
|
||||
if ((static_cast<float>(image->width()) / static_cast<float>(image->height())) >=
|
||||
(static_cast<float>(dpr_expected_width) / static_cast<float>(dpr_expected_height)))
|
||||
{
|
||||
*image = image->scaledToWidth(dpr_expected_width, Qt::SmoothTransformation);
|
||||
}
|
||||
else
|
||||
{
|
||||
*image = image->scaledToHeight(dpr_expected_height, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
if (image->width() == dpr_expected_width && image->height() == dpr_expected_height)
|
||||
return;
|
||||
|
||||
// Padding
|
||||
int xoffs = 0;
|
||||
int yoffs = 0;
|
||||
const int image_width = image->width();
|
||||
const int image_height = image->height();
|
||||
if (image_width < dpr_expected_width)
|
||||
xoffs = static_cast<int>(static_cast<qreal>((dpr_expected_width - image_width) / 2) / dpr);
|
||||
if (image_height < dpr_expected_height)
|
||||
yoffs = static_cast<int>(static_cast<qreal>((dpr_expected_height - image_height) / 2) / dpr);
|
||||
|
||||
QImage padded_image(dpr_expected_width, dpr_expected_height, QImage::Format_ARGB32);
|
||||
padded_image.setDevicePixelRatio(dpr);
|
||||
if (fill_with_top_left)
|
||||
padded_image.fill(image->pixel(0, 0));
|
||||
else
|
||||
padded_image.fill(Qt::transparent);
|
||||
|
||||
// Painting
|
||||
QPainter painter;
|
||||
const float opacity = Host::GetBaseFloatSettingValue("UI", "GameListBackgroundOpacity");
|
||||
if (painter.begin(&padded_image))
|
||||
{
|
||||
painter.setOpacity(opacity);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
painter.drawImage(xoffs, yoffs, *image);
|
||||
painter.end();
|
||||
}
|
||||
|
||||
*image = std::move(padded_image);
|
||||
}
|
||||
|
||||
void GameListWidget::setCustomBackground()
|
||||
{
|
||||
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
||||
bool enabled = Host::GetBaseBoolSettingValue("UI", "GameListBackgroundEnabled");
|
||||
|
||||
if (!Path::IsAbsolute(path))
|
||||
path = Path::Combine(EmuFolders::DataRoot, path);
|
||||
|
||||
// Cleanup old animation if it still exists on gamelist
|
||||
if (m_background_movie != nullptr)
|
||||
{
|
||||
delete m_background_movie;
|
||||
m_background_movie = nullptr;
|
||||
}
|
||||
|
||||
// Only try to create background if both 1. path and 2. setting are valid
|
||||
if (!path.empty() && enabled)
|
||||
{
|
||||
QMovie* new_movie = new QMovie(QString::fromStdString(path), QByteArray(), this);
|
||||
if (new_movie->isValid())
|
||||
m_background_movie = new_movie;
|
||||
else
|
||||
{
|
||||
Console.Warning("Failed to load background movie from: %s", path.c_str());
|
||||
delete new_movie;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no valid background then reset fallback to UI state
|
||||
if (!m_background_movie)
|
||||
{
|
||||
m_ui.stack->setPalette(QApplication::palette());
|
||||
m_table_view->setAlternatingRowColors(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Background is valid, connect the signals and start animation in gamelist
|
||||
connect(m_background_movie, &QMovie::frameChanged, this, [this]() { processBackgroundFrames(); });
|
||||
updateCustomBackgroundState();
|
||||
|
||||
m_table_view->setAlternatingRowColors(false);
|
||||
}
|
||||
|
||||
void GameListWidget::updateCustomBackgroundState()
|
||||
{
|
||||
if (m_background_movie)
|
||||
{
|
||||
if (isVisible() && qGuiApp->applicationState() == Qt::ApplicationActive)
|
||||
m_background_movie->start();
|
||||
else
|
||||
m_background_movie->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GameListWidget::processBackgroundFrames()
|
||||
{
|
||||
QImage img = m_background_movie->currentImage();
|
||||
img.setDevicePixelRatio(devicePixelRatioF());
|
||||
const int widget_width = m_ui.stack->width();
|
||||
const int widget_height = m_ui.stack->height();
|
||||
|
||||
resizeAndPadImage(&img, widget_width, widget_height, false);
|
||||
|
||||
QPalette new_palette(m_ui.stack->palette());
|
||||
new_palette.setBrush(QPalette::Base, img);
|
||||
m_ui.stack->setPalette(new_palette);
|
||||
}
|
||||
|
||||
bool GameListWidget::isShowingGameList() const
|
||||
|
@ -478,6 +609,33 @@ void GameListWidget::refreshGridCovers()
|
|||
m_model->refreshCovers();
|
||||
}
|
||||
|
||||
void GameListWidget::onViewSetGameListBackgroundTriggered()
|
||||
{
|
||||
const QString path = QDir::toNativeSeparators(
|
||||
QFileDialog::getOpenFileName(this, tr("Select Background Image"), QString(), tr("Supported Image Types (*.bmp *.gif *.jpg *.jpeg *.png *.webp)")));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
std::string relative_path = Path::MakeRelative(QDir::toNativeSeparators(path).toStdString(), EmuFolders::DataRoot);
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListBackgroundEnabled", true);
|
||||
Host::SetBaseStringSettingValue("UI", "GameListBackgroundPath", relative_path.c_str());
|
||||
|
||||
if (!Host::ContainsBaseSettingValue("UI", "GameListBackgroundOpacity"))
|
||||
Host::SetBaseFloatSettingValue("UI", "GameListBackgroundOpacity", 1.0f);
|
||||
|
||||
Host::CommitBaseSettingChanges();
|
||||
setCustomBackground();
|
||||
}
|
||||
|
||||
void GameListWidget::onViewClearGameListBackgroundTriggered()
|
||||
{
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListBackgroundEnabled", false);
|
||||
Host::CommitBaseSettingChanges();
|
||||
updateToolbar();
|
||||
resizeTableViewColumnsToFit();
|
||||
setCustomBackground();
|
||||
}
|
||||
|
||||
void GameListWidget::showGameList()
|
||||
{
|
||||
if (m_ui.stack->currentIndex() == 0 || m_model->rowCount() == 0)
|
||||
|
@ -551,11 +709,24 @@ void GameListWidget::updateToolbar()
|
|||
m_ui.gridScale->setEnabled(grid_view);
|
||||
}
|
||||
|
||||
void GameListWidget::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
updateCustomBackgroundState();
|
||||
}
|
||||
|
||||
void GameListWidget::hideEvent(QHideEvent* event)
|
||||
{
|
||||
QWidget::hideEvent(event);
|
||||
updateCustomBackgroundState();
|
||||
}
|
||||
|
||||
void GameListWidget::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
resizeTableViewColumnsToFit();
|
||||
m_model->updateCacheSize(width(), height());
|
||||
updateCustomBackgroundState();
|
||||
}
|
||||
|
||||
bool GameListWidget::event(QEvent* event)
|
||||
|
@ -566,7 +737,6 @@ bool GameListWidget::event(QEvent* event)
|
|||
QWidget::event(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include <QtGui/QMovie>
|
||||
#include <QtWidgets/QListView>
|
||||
#include <QtWidgets/QTableView>
|
||||
|
||||
|
@ -48,6 +49,9 @@ public:
|
|||
void refresh(bool invalidate_cache);
|
||||
void cancelRefresh();
|
||||
void reloadThemeSpecificImages();
|
||||
void setCustomBackground();
|
||||
void updateCustomBackgroundState();
|
||||
void processBackgroundFrames();
|
||||
|
||||
bool isShowingGameList() const;
|
||||
bool isShowingGameGrid() const;
|
||||
|
@ -90,8 +94,12 @@ public Q_SLOTS:
|
|||
void gridZoomOut();
|
||||
void gridIntScale(int int_scale);
|
||||
void refreshGridCovers();
|
||||
void onViewSetGameListBackgroundTriggered();
|
||||
void onViewClearGameListBackgroundTriggered();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
|
@ -115,4 +123,6 @@ private:
|
|||
Ui::EmptyGameListWidget m_empty_ui;
|
||||
|
||||
GameListRefreshThread* m_refresh_thread = nullptr;
|
||||
|
||||
QMovie* m_background_movie = nullptr;
|
||||
};
|
||||
|
|
|
@ -274,6 +274,16 @@ void MainWindow::setupAdditionalUi()
|
|||
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast<float>(scale)); });
|
||||
}
|
||||
|
||||
for (u32 opacity = 2; opacity <= 10; opacity += 2)
|
||||
{
|
||||
QAction* action = m_ui.menuBackgroundOpacity->addAction(tr("%1% Opacity").arg(opacity * 10));
|
||||
connect(action, &QAction::triggered, [this, opacity]() {
|
||||
Host::SetBaseFloatSettingValue("UI", "GameListBackgroundOpacity", (static_cast<float>(opacity / 10.0f)));
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_game_list_widget->setCustomBackground();
|
||||
});
|
||||
}
|
||||
|
||||
updateEmulationActions(false, false, false);
|
||||
updateDisplayRelatedActions(false, false, false);
|
||||
|
||||
|
@ -385,6 +395,8 @@ void MainWindow::connectSignals()
|
|||
m_game_list_widget->gridZoomOut();
|
||||
});
|
||||
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget, &GameListWidget::refreshGridCovers);
|
||||
connect(m_ui.actionSetGameListBackground, &QAction::triggered, m_game_list_widget, &GameListWidget::onViewSetGameListBackgroundTriggered);
|
||||
connect(m_ui.actionClearGameListBackground, &QAction::triggered, m_game_list_widget, &GameListWidget::onViewClearGameListBackgroundTriggered);
|
||||
connect(m_game_list_widget, &GameListWidget::layoutChange, this, [this]() {
|
||||
QSignalBlocker sb(m_ui.actionGridViewShowTitles);
|
||||
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1050</width>
|
||||
<height>27</height>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuSystem">
|
||||
|
@ -160,6 +160,14 @@
|
|||
<iconset theme="window-2-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuBackgroundOpacity">
|
||||
<property name="title">
|
||||
<string>Background O&pacity</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="lightbulb-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="actionViewToolbar"/>
|
||||
<addaction name="actionViewLockToolbar"/>
|
||||
<addaction name="actionViewStatusBar"/>
|
||||
|
@ -176,6 +184,10 @@
|
|||
<addaction name="actionGridViewZoomIn"/>
|
||||
<addaction name="actionGridViewZoomOut"/>
|
||||
<addaction name="actionGridViewRefreshCovers"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSetGameListBackground"/>
|
||||
<addaction name="actionClearGameListBackground"/>
|
||||
<addaction name="menuBackgroundOpacity"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTools">
|
||||
<property name="title">
|
||||
|
@ -1056,6 +1068,22 @@
|
|||
<string>Edit &Patches...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSetGameListBackground">
|
||||
<property name="icon">
|
||||
<iconset theme="artboard-2-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set Custom Background</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClearGameListBackground">
|
||||
<property name="icon">
|
||||
<iconset theme="trash-fill"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clear Custom Background</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources/resources.qrc"/>
|
||||
|
|
Loading…
Reference in New Issue