mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-15 02:44:41 +00:00
Qt: Simplify icon scaling
- Merge the two sliders into one. - Scale the type icons as well as the game icons. - Remove redundant slots.
This commit is contained in:
@@ -42,8 +42,9 @@ static constexpr int VIEW_MODE_LIST = 0;
|
||||
static constexpr int VIEW_MODE_GRID = 1;
|
||||
static constexpr int VIEW_MODE_NO_GAMES = 2;
|
||||
|
||||
static constexpr float MIN_ICON_SCALE = 1.0f;
|
||||
static constexpr float MAX_ICON_SCALE = 5.0f;
|
||||
static constexpr int ICON_SIZE_STEP = 4;
|
||||
static constexpr int MIN_ICON_SIZE = 16;
|
||||
static constexpr int MAX_ICON_SIZE = 80;
|
||||
static constexpr float MIN_COVER_SCALE = 0.1f;
|
||||
static constexpr float MAX_COVER_SCALE = 2.0f;
|
||||
|
||||
@@ -135,7 +136,7 @@ GameListModel::GameListModel(QObject* parent)
|
||||
: QAbstractTableModel(parent), m_memcard_pixmap_cache(MIN_COVER_CACHE_SIZE)
|
||||
{
|
||||
m_cover_scale = Host::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", 0.45f);
|
||||
m_icon_scale = Host::GetBaseFloatSettingValue("UI", "GameListIconScale", 1.00f);
|
||||
m_icon_size = Host::GetBaseFloatSettingValue("UI", "GameListIconSize", MIN_ICON_SIZE);
|
||||
m_show_localized_titles = GameList::ShouldShowLocalizedTitles();
|
||||
m_show_titles_for_covers = Host::GetBaseBoolSettingValue("UI", "GameListShowCoverTitles", true);
|
||||
m_show_game_icons = Host::GetBaseBoolSettingValue("UI", "GameListShowGameIcons", true);
|
||||
@@ -169,6 +170,12 @@ void GameListModel::setShowCoverTitles(bool enabled)
|
||||
emit dataChanged(index(0, Column_Cover), index(rowCount() - 1, Column_Cover), {Qt::DisplayRole});
|
||||
}
|
||||
|
||||
int GameListModel::calculateRowHeight(const QWidget* const widget) const
|
||||
{
|
||||
return m_icon_size + MEMORY_CARD_ICON_PADDING +
|
||||
widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, nullptr, widget);
|
||||
}
|
||||
|
||||
void GameListModel::setShowGameIcons(bool enabled)
|
||||
{
|
||||
m_show_game_icons = enabled;
|
||||
@@ -184,24 +191,19 @@ void GameListModel::refreshIcons()
|
||||
emit dataChanged(index(0, Column_Icon), index(rowCount() - 1, Column_Icon), {Qt::DecorationRole});
|
||||
}
|
||||
|
||||
void GameListModel::setIconScale(float scale)
|
||||
void GameListModel::setIconSize(int size)
|
||||
{
|
||||
if (m_icon_scale == scale)
|
||||
if (m_icon_size == size)
|
||||
return;
|
||||
|
||||
m_icon_scale = scale;
|
||||
m_icon_size = size;
|
||||
|
||||
Host::SetBaseFloatSettingValue("UI", "GameListIconScale", scale);
|
||||
Host::SetBaseIntSettingValue("UI", "GameListIconSize", size);
|
||||
Host::CommitBaseSettingChanges();
|
||||
updateIconScale();
|
||||
}
|
||||
|
||||
void GameListModel::updateIconScale()
|
||||
{
|
||||
m_memcard_pixmap_cache.Clear();
|
||||
|
||||
emit iconScaleChanged(m_icon_scale);
|
||||
refresh();
|
||||
emit iconSizeChanged(m_icon_size);
|
||||
loadSizeDependentPixmaps();
|
||||
refreshIcons();
|
||||
}
|
||||
|
||||
void GameListModel::setCoverScale(float scale)
|
||||
@@ -270,7 +272,7 @@ void GameListModel::updateCacheSize(int num_rows, int num_columns)
|
||||
|
||||
void GameListModel::reloadThemeSpecificImages()
|
||||
{
|
||||
loadThemeSpecificImages();
|
||||
loadSizeDependentPixmaps();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@@ -493,20 +495,18 @@ QIcon GameListModel::getIconForGame(const QString& path)
|
||||
|
||||
void GameListModel::fixIconPixmapSize(QPixmap& pm)
|
||||
{
|
||||
const qreal dpr = pm.devicePixelRatio();
|
||||
const int width = static_cast<int>(static_cast<float>(pm.width()) * dpr);
|
||||
const int height = static_cast<int>(static_cast<float>(pm.height()) * dpr);
|
||||
const int max_dim = std::max(width, height);
|
||||
const float dpr = qApp->devicePixelRatio();
|
||||
const int width = static_cast<int>(static_cast<qreal>(pm.width()));
|
||||
const int height = static_cast<int>(static_cast<qreal>(pm.height()));
|
||||
|
||||
const float wanted_dpr = qApp->devicePixelRatio();
|
||||
pm.setDevicePixelRatio(wanted_dpr);
|
||||
|
||||
const float scale = static_cast<float>(max_dim) / MEMORY_CARD_ICON_SIZE / wanted_dpr / m_icon_scale;
|
||||
const int new_width = static_cast<int>(static_cast<float>(width) / scale);
|
||||
const int new_height = static_cast<int>(static_cast<float>(height) / scale);
|
||||
const qreal scale = (static_cast<float>(m_icon_size) / static_cast<float>(MEMORY_CARD_ICON_SIZE)) * dpr;
|
||||
const int new_width = static_cast<int>(static_cast<float>(width) * scale);
|
||||
const int new_height = static_cast<int>(static_cast<float>(height) * scale);
|
||||
|
||||
if (width != new_width || height != new_height)
|
||||
QtUtils::ResizeSharpBilinear(pm, std::max(new_width, new_height), MEMORY_CARD_ICON_SIZE);
|
||||
|
||||
pm.setDevicePixelRatio(dpr);
|
||||
}
|
||||
|
||||
int GameListModel::getCoverArtSize() const
|
||||
@@ -1010,17 +1010,19 @@ bool GameListModel::lessThan(const GameList::Entry* left, const GameList::Entry*
|
||||
}
|
||||
}
|
||||
|
||||
void GameListModel::loadThemeSpecificImages()
|
||||
void GameListModel::loadSizeDependentPixmaps()
|
||||
{
|
||||
// nasty magic number here, +8 gets us a height of 24 at 16 icon size, which looks good.
|
||||
const int icon_height = m_icon_size + 8;
|
||||
for (u32 i = 0; i < static_cast<u32>(GameList::EntryType::MaxCount); i++)
|
||||
m_type_pixmaps[i] = QtUtils::GetIconForEntryType(static_cast<GameList::EntryType>(i)).pixmap(24);
|
||||
m_type_pixmaps[i] = QtUtils::GetIconForEntryType(static_cast<GameList::EntryType>(i)).pixmap(icon_height);
|
||||
}
|
||||
|
||||
void GameListModel::loadCommonImages()
|
||||
{
|
||||
loadThemeSpecificImages();
|
||||
loadSizeDependentPixmaps();
|
||||
|
||||
for (int i = 0; i < static_cast<int>(GameDatabase::CompatibilityRating::Count); i++)
|
||||
for (u32 i = 0; i < static_cast<u32>(GameDatabase::CompatibilityRating::Count); i++)
|
||||
{
|
||||
m_compatibility_pixmaps[i] =
|
||||
QtUtils::GetIconForCompatibility(static_cast<GameDatabase::CompatibilityRating>(i)).pixmap(96, 24);
|
||||
@@ -1277,8 +1279,8 @@ void GameListWidget::initialize(QAction* actionGameList, QAction* actionGameGrid
|
||||
QAction* actionShowLocalizedTitles)
|
||||
{
|
||||
m_model = new GameListModel(this);
|
||||
connect(m_model, &GameListModel::coverScaleChanged, this, &GameListWidget::onCoverScaleChanged);
|
||||
connect(m_model, &GameListModel::iconScaleChanged, this, &GameListWidget::onIconScaleChanged);
|
||||
connect(m_model, &GameListModel::coverScaleChanged, this, &GameListWidget::onScaleChanged);
|
||||
connect(m_model, &GameListModel::iconSizeChanged, this, &GameListWidget::onIconSizeChanged);
|
||||
|
||||
m_sort_model = new GameListSortModel(m_model);
|
||||
m_sort_model->setSourceModel(m_model);
|
||||
@@ -1314,8 +1316,7 @@ void GameListWidget::initialize(QAction* actionGameList, QAction* actionGameGrid
|
||||
m_ui.showGridTitles->setDefaultAction(actionGridShowTitles);
|
||||
m_ui.showLocalizedTitles->setDefaultAction(actionShowLocalizedTitles);
|
||||
|
||||
connect(m_ui.gridScale, &QSlider::valueChanged, m_grid_view, &GameListGridView::setZoomPct);
|
||||
connect(m_ui.listScale, &QSlider::valueChanged, m_list_view, &GameListListView::setZoomPct);
|
||||
connect(m_ui.scale, &QSlider::valueChanged, this, &GameListWidget::onScaleSliderChanged);
|
||||
connect(m_ui.filterType, &QComboBox::currentIndexChanged, this, [this](int index) {
|
||||
m_sort_model->setFilterType((index == 0) ? GameList::EntryType::MaxCount :
|
||||
static_cast<GameList::EntryType>(index - 1));
|
||||
@@ -1349,10 +1350,9 @@ void GameListWidget::initialize(QAction* actionGameList, QAction* actionGameGrid
|
||||
actionShowLocalizedTitles->setChecked(m_model->getShowLocalizedTitles());
|
||||
actionListShowIcons->setChecked(m_model->getShowGameIcons());
|
||||
actionGridShowTitles->setChecked(m_model->getShowCoverTitles());
|
||||
onCoverScaleChanged(m_model->getCoverScale());
|
||||
onIconScaleChanged(m_model->getIconScale());
|
||||
onIconSizeChanged(m_model->getIconSize());
|
||||
|
||||
setViewMode(VIEW_MODE_LIST);
|
||||
setViewMode(grid_view ? VIEW_MODE_GRID : VIEW_MODE_LIST);
|
||||
updateBackground(true);
|
||||
}
|
||||
|
||||
@@ -1632,8 +1632,7 @@ void GameListWidget::setViewMode(int stack_index)
|
||||
m_ui.showLocalizedTitles->setEnabled(has_games);
|
||||
m_ui.showGameIcons->setEnabled(has_games);
|
||||
m_ui.showGridTitles->setEnabled(has_games);
|
||||
m_ui.gridScale->setEnabled(has_games);
|
||||
m_ui.listScale->setEnabled(has_games);
|
||||
m_ui.scale->setEnabled(has_games);
|
||||
m_ui.filterType->setEnabled(has_games);
|
||||
m_ui.filterRegion->setEnabled(has_games);
|
||||
m_ui.searchText->setEnabled(has_games);
|
||||
@@ -1641,20 +1640,47 @@ void GameListWidget::setViewMode(int stack_index)
|
||||
const bool is_grid_view = isShowingGameGrid();
|
||||
m_ui.showGameIcons->setVisible(!is_grid_view);
|
||||
m_ui.showGridTitles->setVisible(is_grid_view);
|
||||
m_ui.gridScale->setVisible(is_grid_view);
|
||||
m_ui.listScale->setVisible(!is_grid_view);
|
||||
|
||||
QSignalBlocker sb(m_ui.scale);
|
||||
if (is_grid_view)
|
||||
{
|
||||
m_ui.scale->setMinimum(static_cast<int>(MIN_COVER_SCALE * 100.0f));
|
||||
m_ui.scale->setMaximum(static_cast<int>(MAX_COVER_SCALE * 100.0f));
|
||||
m_ui.scale->setValue(static_cast<int>(m_model->getCoverScale() * 100.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.scale->setMinimum(MIN_ICON_SIZE / ICON_SIZE_STEP);
|
||||
m_ui.scale->setMaximum(MAX_ICON_SIZE / ICON_SIZE_STEP);
|
||||
m_ui.scale->setValue(m_model->getIconSize() / ICON_SIZE_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
void GameListWidget::onCoverScaleChanged(float scale)
|
||||
void GameListWidget::onScaleSliderChanged(int value)
|
||||
{
|
||||
QSignalBlocker sb(m_ui.gridScale);
|
||||
m_ui.gridScale->setValue(static_cast<int>(scale * 100.0f));
|
||||
if (isShowingGameGrid())
|
||||
m_model->setCoverScale(static_cast<float>(value) / 100.0f);
|
||||
else if (isShowingGameList())
|
||||
m_model->setIconSize(value * ICON_SIZE_STEP);
|
||||
}
|
||||
|
||||
void GameListWidget::onIconScaleChanged(float scale)
|
||||
void GameListWidget::onScaleChanged()
|
||||
{
|
||||
QSignalBlocker sb(m_ui.listScale);
|
||||
m_ui.listScale->setValue(static_cast<int>(scale * 4.0f));
|
||||
int value = m_ui.scale->value();
|
||||
if (isShowingGameGrid())
|
||||
value = static_cast<int>(m_model->getCoverScale() * 100.0f);
|
||||
else if (isShowingGameList())
|
||||
value = m_model->getIconSize() / ICON_SIZE_STEP;
|
||||
|
||||
QSignalBlocker sb(m_ui.scale);
|
||||
m_ui.scale->setValue(value);
|
||||
}
|
||||
|
||||
void GameListWidget::onIconSizeChanged(int size)
|
||||
{
|
||||
// update size of rows
|
||||
m_list_view->verticalHeader()->setDefaultSectionSize(m_model->calculateRowHeight(m_list_view));
|
||||
onScaleChanged();
|
||||
}
|
||||
|
||||
void GameListWidget::resizeEvent(QResizeEvent* event)
|
||||
@@ -1716,11 +1742,6 @@ GameListListView::GameListListView(GameListModel* model, GameListSortModel* sort
|
||||
horizontal_header->setSectionResizeMode(GameListModel::Column_FileTitle, QHeaderView::Stretch);
|
||||
horizontal_header->setSectionResizeMode(GameListModel::Column_Icon, QHeaderView::ResizeToContents);
|
||||
|
||||
QHeaderView* const vertical_header = verticalHeader();
|
||||
vertical_header->hide();
|
||||
vertical_header->setDefaultSectionSize(MEMORY_CARD_ICON_SIZE + MEMORY_CARD_ICON_PADDING +
|
||||
style()->pixelMetric(QStyle::PM_FocusFrameVMargin, nullptr, this));
|
||||
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
|
||||
|
||||
@@ -1736,7 +1757,6 @@ GameListListView::GameListListView(GameListModel* model, GameListSortModel* sort
|
||||
connect(horizontal_header, &QHeaderView::sortIndicatorChanged, this, &GameListListView::onHeaderSortIndicatorChanged);
|
||||
connect(horizontal_header, &QHeaderView::customContextMenuRequested, this,
|
||||
&GameListListView::onHeaderContextMenuRequested);
|
||||
connect(m_model, &GameListModel::iconScaleChanged, this, &GameListListView::onIconScaleChanged);
|
||||
}
|
||||
|
||||
GameListListView::~GameListListView() = default;
|
||||
@@ -1745,14 +1765,10 @@ void GameListListView::wheelEvent(QWheelEvent* e)
|
||||
{
|
||||
if (e->modifiers() & Qt::ControlModifier)
|
||||
{
|
||||
int dy = e->angleDelta().y();
|
||||
const int dy = e->angleDelta().y();
|
||||
if (dy != 0)
|
||||
{
|
||||
if (dy < 0)
|
||||
zoomOut();
|
||||
else
|
||||
zoomIn();
|
||||
|
||||
adjustIconSize((dy < 0) ? ICON_SIZE_STEP : ICON_SIZE_STEP);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1915,42 +1931,10 @@ void GameListListView::onHeaderContextMenuRequested(const QPoint& point)
|
||||
menu.exec(mapToGlobal(point));
|
||||
}
|
||||
|
||||
void GameListListView::onIconScaleChanged(float scale)
|
||||
void GameListListView::adjustIconSize(int delta)
|
||||
{
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
void GameListListView::adjustZoom(float delta)
|
||||
{
|
||||
const float new_scale = std::clamp(m_model->getIconScale() + delta, MIN_ICON_SCALE, MAX_ICON_SCALE);
|
||||
m_model->setIconScale(new_scale);
|
||||
}
|
||||
|
||||
void GameListListView::zoomIn()
|
||||
{
|
||||
adjustZoom(0.25f);
|
||||
}
|
||||
|
||||
void GameListListView::zoomOut()
|
||||
{
|
||||
adjustZoom(-0.25f);
|
||||
}
|
||||
|
||||
void GameListListView::setZoomPct(int int_scale)
|
||||
{
|
||||
const float new_scale = std::clamp(static_cast<float>(int_scale) / 4.0f, MIN_ICON_SCALE, MAX_ICON_SCALE);
|
||||
m_model->setIconScale(new_scale);
|
||||
}
|
||||
|
||||
void GameListListView::updateLayout()
|
||||
{
|
||||
const float row_count = m_model->rowCount();
|
||||
const float icon_scale = m_model->getIconScale();
|
||||
const int height =
|
||||
icon_scale * MEMORY_CARD_ICON_SIZE + 12 + style()->pixelMetric(QStyle::PM_FocusFrameVMargin, nullptr, this);
|
||||
|
||||
for (int i = 0; i < row_count; i++)
|
||||
setRowHeight(i, height);
|
||||
const int new_size = std::clamp(m_model->getIconSize() + delta, MIN_ICON_SIZE, MAX_ICON_SIZE);
|
||||
m_model->setIconSize(new_size);
|
||||
}
|
||||
|
||||
GameListGridView::GameListGridView(GameListModel* model, GameListSortModel* sort_model, QWidget* parent)
|
||||
@@ -1978,7 +1962,7 @@ void GameListGridView::wheelEvent(QWheelEvent* e)
|
||||
{
|
||||
if (e->modifiers() & Qt::ControlModifier)
|
||||
{
|
||||
int dy = e->angleDelta().y();
|
||||
const int dy = e->angleDelta().y();
|
||||
if (dy != 0)
|
||||
{
|
||||
if (dy < 0)
|
||||
|
||||
@@ -93,8 +93,9 @@ public:
|
||||
bool getShowCoverTitles() const { return m_show_titles_for_covers; }
|
||||
void setShowCoverTitles(bool enabled);
|
||||
|
||||
float getIconScale() const { return m_icon_scale; }
|
||||
void setIconScale(float scale);
|
||||
int calculateRowHeight(const QWidget* const widget) const;
|
||||
int getIconSize() const { return m_icon_size; }
|
||||
void setIconSize(int size);
|
||||
bool getShowGameIcons() const { return m_show_game_icons; }
|
||||
void setShowGameIcons(bool enabled);
|
||||
QIcon getIconForGame(const QString& path);
|
||||
@@ -109,17 +110,16 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void coverScaleChanged(float scale);
|
||||
void iconScaleChanged(float scale);
|
||||
void iconSizeChanged(int size);
|
||||
|
||||
private:
|
||||
void rowsChanged(const QList<int>& rows);
|
||||
QVariant data(const QModelIndex& index, int role, const GameList::Entry* ge) const;
|
||||
|
||||
void loadCommonImages();
|
||||
void loadThemeSpecificImages();
|
||||
void loadSizeDependentPixmaps();
|
||||
void setColumnDisplayNames();
|
||||
void updateCoverScale();
|
||||
void updateIconScale();
|
||||
void loadOrGenerateCover(const GameList::Entry* ge);
|
||||
void invalidateCoverForPath(const std::string& path);
|
||||
void coverLoaded(const std::string& path, const QImage& image, float scale);
|
||||
@@ -137,7 +137,7 @@ private:
|
||||
std::optional<GameList::EntryList> m_taken_entries;
|
||||
|
||||
float m_cover_scale = 0.0f;
|
||||
float m_icon_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;
|
||||
@@ -169,19 +169,12 @@ public:
|
||||
~GameListListView() override;
|
||||
|
||||
void setAndSaveColumnHidden(int column, bool hidden);
|
||||
void updateLayout();
|
||||
|
||||
public Q_SLOTS:
|
||||
void zoomOut();
|
||||
void zoomIn();
|
||||
void setZoomPct(int int_scale);
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent* e) override;
|
||||
|
||||
private:
|
||||
void onIconScaleChanged(float scale);
|
||||
void adjustZoom(float delta);
|
||||
void adjustIconSize(int delta);
|
||||
|
||||
void onHeaderSortIndicatorChanged(int, Qt::SortOrder);
|
||||
void onHeaderContextMenuRequested(const QPoint& point);
|
||||
@@ -267,8 +260,9 @@ private Q_SLOTS:
|
||||
void onRefreshProgress(const QString& status, int current, int total, int entry_count, float time);
|
||||
void onRefreshComplete();
|
||||
|
||||
void onCoverScaleChanged(float scale);
|
||||
void onIconScaleChanged(float scale);
|
||||
void onScaleSliderChanged(int value);
|
||||
void onScaleChanged();
|
||||
void onIconSizeChanged(int size);
|
||||
|
||||
void onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||
void onListViewItemActivated(const QModelIndex& index);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1063</width>
|
||||
<width>821</width>
|
||||
<height>619</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -158,7 +158,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="gridScale">
|
||||
<widget class="QSlider" name="scale">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>125</width>
|
||||
@@ -177,31 +177,9 @@
|
||||
<property name="maximum">
|
||||
<number>200</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="listScale">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>125</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>125</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<property name="pageStep">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
|
||||
Reference in New Issue
Block a user