better handling of icons in list mode

This commit is contained in:
georgemoralis 2023-03-14 01:02:06 +02:00
parent f5570a156f
commit 9e4a670be8
9 changed files with 170 additions and 45 deletions

View File

@ -3,7 +3,7 @@
#include <QDateTime> #include <QDateTime>
custom_table_widget_item::custom_table_widget_item(const std::string& text, int sort_role, const QVariant& sort_value) custom_table_widget_item::custom_table_widget_item(const std::string& text, int sort_role, const QVariant& sort_value)
: QTableWidgetItem(QString::fromStdString(text).simplified()) // simplified() forces single line text : game_list_item(QString::fromStdString(text).simplified()) // simplified() forces single line text
{ {
if (sort_role != Qt::DisplayRole) if (sort_role != Qt::DisplayRole)
{ {
@ -12,7 +12,7 @@ custom_table_widget_item::custom_table_widget_item(const std::string& text, int
} }
custom_table_widget_item::custom_table_widget_item(const QString& text, int sort_role, const QVariant& sort_value) custom_table_widget_item::custom_table_widget_item(const QString& text, int sort_role, const QVariant& sort_value)
: QTableWidgetItem(text.simplified()) // simplified() forces single line text : game_list_item(text.simplified()) // simplified() forces single line text
{ {
if (sort_role != Qt::DisplayRole) if (sort_role != Qt::DisplayRole)
{ {

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "game_list_item.h"
#include <QTableWidgetItem> #include <QTableWidgetItem>
class custom_table_widget_item : public QTableWidgetItem class custom_table_widget_item : public game_list_item
{ {
private: private:
int m_sort_role = Qt::DisplayRole; int m_sort_role = Qt::DisplayRole;

View File

@ -1,8 +1,10 @@
#include "game_list_frame.h" #include "game_list_frame.h"
#include "gui_settings.h" #include "gui_settings.h"
#include "custom_table_widget_item.h" #include "custom_table_widget_item.h"
#include "qt_utils.h"
#include "../emulator/fileFormat/PSF.h" #include "../emulator/fileFormat/PSF.h"
#include <QPainter> #include <QPainter>
#include <unordered_set>
game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, QWidget* parent) game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, QWidget* parent)
: QWidget(parent) : QWidget(parent)
@ -107,10 +109,19 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, QWi
configure->exec(m_game_list->horizontalHeader()->viewport()->mapToGlobal(pos)); configure->exec(m_game_list->horizontalHeader()->viewport()->mapToGlobal(pos));
}); });
connect(m_game_list->horizontalHeader(), &QHeaderView::sectionClicked, this, &game_list_frame::OnHeaderColumnClicked); connect(m_game_list->horizontalHeader(), &QHeaderView::sectionClicked, this, &game_list_frame::OnHeaderColumnClicked);
connect(&m_repaint_watcher, &QFutureWatcher<game_list_item*>::resultReadyAt, this, [this](int index)
{
if (!m_is_list_layout) return;
if (game_list_item* item = m_repaint_watcher.resultAt(index))
{
item->call_icon_func();
}
});
Refresh();//TODO remove when watchers added Refresh();//TODO remove when watchers added
} }
game_list_frame::~game_list_frame() { game_list_frame::~game_list_frame() {
gui::utils::stop_future_watcher(m_repaint_watcher, true);
SaveSettings(); SaveSettings();
} }
void game_list_frame::FixNarrowColumns() const void game_list_frame::FixNarrowColumns() const
@ -368,7 +379,7 @@ void game_list_frame::PopulateGameList()
int row = 0; int row = 0;
int index = -1; int index = -1;
RepaintIcons();//hackish //RepaintIcons();//hackish
for (const auto& game : m_game_data) for (const auto& game : m_game_data)
{ {
index++; index++;
@ -376,7 +387,12 @@ void game_list_frame::PopulateGameList()
// Icon // Icon
custom_table_widget_item* icon_item = new custom_table_widget_item; custom_table_widget_item* icon_item = new custom_table_widget_item;
icon_item->setData(Qt::DecorationRole, game->pxmap); game->item = icon_item;
icon_item->set_icon_func([this, icon_item, game](int)
{
icon_item->setData(Qt::DecorationRole, game->pxmap);
game->pxmap = {};
});
icon_item->setData(Qt::UserRole, index, true); icon_item->setData(Qt::UserRole, index, true);
icon_item->setData(gui::custom_roles::game_role, QVariant::fromValue(game)); icon_item->setData(gui::custom_roles::game_role, QVariant::fromValue(game));
@ -448,12 +464,44 @@ std::string game_list_frame::CurrentSelectionPath()
void game_list_frame::RepaintIcons(const bool& from_settings) void game_list_frame::RepaintIcons(const bool& from_settings)
{ {
for (auto& game : m_game_data) gui::utils::stop_future_watcher(m_repaint_watcher, true);
if (from_settings)
{ {
game->icon.load(QString::fromStdString(game->info.icon_path)); //TODO m_icon_color = gui::utils::get_label_color("gamelist_icon_background_color");
game->pxmap = PaintedPixmap(game->icon);
} }
if (m_is_list_layout)
{
QPixmap placeholder(m_icon_size);
placeholder.fill(Qt::transparent);
for (auto& game : m_game_data)
{
game->pxmap = placeholder;
}
// Fixate vertical header and row height
m_game_list->verticalHeader()->setMinimumSectionSize(m_icon_size.height());
m_game_list->verticalHeader()->setMaximumSectionSize(m_icon_size.height());
// Resize the icon column
m_game_list->resizeColumnToContents(gui::column_icon);
// Shorten the last section to remove horizontal scrollbar if possible
m_game_list->resizeColumnToContents(gui::column_count - 1);
}
const std::function func = [this](const game_info& game) -> game_list_item*
{
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(QString::fromStdString(game->info.icon_path))))
{
//TODO added warning message if no found
}
game->pxmap = PaintedPixmap(game->icon);
return game->item;
};
m_repaint_watcher.setFuture(QtConcurrent::mapped(m_game_data, func));
} }
QPixmap game_list_frame::PaintedPixmap(const QPixmap& icon) const QPixmap game_list_frame::PaintedPixmap(const QPixmap& icon) const

View File

@ -3,11 +3,13 @@
#include "game_list_table.h" #include "game_list_table.h"
#include "shadps4gui.h" #include "shadps4gui.h"
#include "game_list_grid.h" #include "game_list_grid.h"
#include "game_list_item.h"
#include <QHeaderView> #include <QHeaderView>
#include <QScrollbar> #include <QScrollbar>
#include <QWidget> #include <QWidget>
#include <deque> #include <deque>
#include <QFutureWatcher>
#include <QtConcurrent>
class game_list_frame : public QWidget class game_list_frame : public QWidget
{ {
@ -60,6 +62,7 @@ private:
QList<game_info> m_game_data; QList<game_info> m_game_data;
std::vector<std::string> m_path_list; std::vector<std::string> m_path_list;
std::deque<game_info> m_games; std::deque<game_info> m_games;
QFutureWatcher<game_list_item*> m_repaint_watcher;
// Icons // Icons
QSize m_icon_size; QSize m_icon_size;

View File

@ -1,6 +1,6 @@
#include "game_list_grid.h" #include "game_list_grid.h"
#include "game_list_grid_delegate.h" #include "game_list_grid_delegate.h"
#include "game_list_item.h"
#include <QHeaderView> #include <QHeaderView>
#include <QScrollBar> #include <QScrollBar>
@ -58,45 +58,47 @@ void game_list_grid::setIconSize(const QSize& size) const
QTableWidgetItem* game_list_grid::addItem(const game_info& app, const QString& name, const QString& movie_path, const int& row, const int& col) QTableWidgetItem* game_list_grid::addItem(const game_info& app, const QString& name, const QString& movie_path, const int& row, const int& col)
{ {
const qreal device_pixel_ratio = devicePixelRatioF(); game_list_item* item = new game_list_item;
item->set_icon_func([this, app, item](int)
// define size of expanded image, which is raw image size + margins
QSizeF exp_size_f;
if (m_text_enabled)
{ {
exp_size_f = m_icon_size + QSizeF(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1)); const qreal device_pixel_ratio = devicePixelRatioF();
}
else
{
exp_size_f = m_icon_size + m_icon_size * m_margin_factor * 2;
}
// define offset for raw image placement // define size of expanded image, which is raw image size + margins
QPoint offset(m_icon_size.width() * m_margin_factor, m_icon_size.height() * m_margin_factor); QSizeF exp_size_f;
const QSize exp_size = (exp_size_f * device_pixel_ratio).toSize(); if (m_text_enabled)
{
exp_size_f = m_icon_size + QSizeF(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1));
}
else
{
exp_size_f = m_icon_size + m_icon_size * m_margin_factor * 2;
}
// create empty canvas for expanded image // define offset for raw image placement
QImage exp_img(exp_size, QImage::Format_ARGB32); QPoint offset(m_icon_size.width() * m_margin_factor, m_icon_size.height() * m_margin_factor);
exp_img.setDevicePixelRatio(device_pixel_ratio); const QSize exp_size = (exp_size_f * device_pixel_ratio).toSize();
exp_img.fill(Qt::transparent);
// create background for image // create empty canvas for expanded image
QImage bg_img(app->pxmap.size(), QImage::Format_ARGB32); QImage exp_img(exp_size, QImage::Format_ARGB32);
bg_img.setDevicePixelRatio(device_pixel_ratio); exp_img.setDevicePixelRatio(device_pixel_ratio);
bg_img.fill(m_icon_color); exp_img.fill(Qt::transparent);
// place raw image inside expanded image // create background for image
QPainter painter(&exp_img); QImage bg_img(app->pxmap.size(), QImage::Format_ARGB32);
painter.setRenderHint(QPainter::SmoothPixmapTransform); bg_img.setDevicePixelRatio(device_pixel_ratio);
painter.drawImage(offset, bg_img); bg_img.fill(m_icon_color);
painter.drawPixmap(offset, app->pxmap);
app->pxmap = {};
painter.end();
// create item with expanded image, title and position // place raw image inside expanded image
QTableWidgetItem* item = new QTableWidgetItem(); QPainter painter(&exp_img);
item->setData(Qt::ItemDataRole::DecorationRole, QPixmap::fromImage(exp_img)); painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.drawImage(offset, bg_img);
painter.drawPixmap(offset, app->pxmap);
app->pxmap = {};
painter.end();
// create item with expanded image, title and position
item->setData(Qt::ItemDataRole::DecorationRole, QPixmap::fromImage(exp_img));
});
if (m_text_enabled) if (m_text_enabled)
{ {
item->setData(Qt::ItemDataRole::DisplayRole, name); item->setData(Qt::ItemDataRole::DisplayRole, name);

View File

@ -0,0 +1,44 @@
#pragma once
#include <QTableWidgetItem>
#include <QObject>
#include <functional>
using icon_callback_t = std::function<void(int)>;
class game_list_item : public QTableWidgetItem
{
public:
game_list_item() : QTableWidgetItem()
{
}
game_list_item(const QString& text, int type = Type) : QTableWidgetItem(text, type)
{
}
game_list_item(const QIcon& icon, const QString& text, int type = Type) : QTableWidgetItem(icon, text, type)
{
}
~game_list_item()
{
}
void call_icon_func() const
{
if (m_icon_callback)
{
m_icon_callback(0);
}
}
void set_icon_func(const icon_callback_t& func)
{
m_icon_callback = func;
call_icon_func();
}
private:
icon_callback_t m_icon_callback = nullptr;
};

View File

@ -3,12 +3,14 @@
#include <QTableWidget> #include <QTableWidget>
#include <QMouseEvent> #include <QMouseEvent>
#include "../emulator/gameInfo.h" #include "../emulator/gameInfo.h"
#include "game_list_item.h"
struct gui_game_info struct gui_game_info
{ {
GameInfo info{}; GameInfo info{};
QPixmap icon; QPixmap icon;
QPixmap pxmap; QPixmap pxmap;
game_list_item* item = nullptr;
}; };
typedef std::shared_ptr<gui_game_info> game_info; typedef std::shared_ptr<gui_game_info> game_info;

23
shadPS4/gui/qt_utils.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <QFutureWatcher>
namespace gui
{
namespace utils
{
template <typename T>
void stop_future_watcher(QFutureWatcher<T>& watcher, bool cancel)
{
if (watcher.isStarted() || watcher.isRunning())
{
if (cancel)
{
watcher.cancel();
}
watcher.waitForFinished();
}
}
} // utils
} // gui

View File

@ -45,10 +45,12 @@
<QtMoc Include="gui\game_list_grid.h" /> <QtMoc Include="gui\game_list_grid.h" />
<QtMoc Include="gui\game_list_frame.h" /> <QtMoc Include="gui\game_list_frame.h" />
<ClInclude Include="gui\game_list_grid_delegate.h" /> <ClInclude Include="gui\game_list_grid_delegate.h" />
<ClInclude Include="gui\game_list_item.h" />
<ClInclude Include="gui\game_list_table.h" /> <ClInclude Include="gui\game_list_table.h" />
<ClInclude Include="gui\gui_save.h" /> <ClInclude Include="gui\gui_save.h" />
<QtMoc Include="gui\gui_settings.h" /> <QtMoc Include="gui\gui_settings.h" />
<QtMoc Include="gui\settings.h" /> <QtMoc Include="gui\settings.h" />
<ClInclude Include="gui\qt_utils.h" />
<ClInclude Include="Types.h" /> <ClInclude Include="Types.h" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
@ -73,12 +75,12 @@
</ImportGroup> </ImportGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings">
<QtInstall>6.4.2</QtInstall> <QtInstall>6.4.2</QtInstall>
<QtModules>core;gui;widgets</QtModules> <QtModules>core;gui;widgets;concurrent</QtModules>
<QtBuildConfig>debug</QtBuildConfig> <QtBuildConfig>debug</QtBuildConfig>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings">
<QtInstall>6.4.2</QtInstall> <QtInstall>6.4.2</QtInstall>
<QtModules>core;gui;widgets</QtModules> <QtModules>core;gui;widgets;concurrent</QtModules>
<QtBuildConfig>release</QtBuildConfig> <QtBuildConfig>release</QtBuildConfig>
</PropertyGroup> </PropertyGroup>
<Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">