aboutsummaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
authorcrueter <crueter@eden-emu.dev>2026-02-07 22:48:39 +0100
committercrueter <crueter@eden-emu.dev>2026-02-07 22:48:39 +0100
commitca9f2d43beeb03ca2127a21ef34ead9f497bcb03 (patch)
treed4fb1e94710481ec47c3959291ce0ac57a2fbd94 /src/yuzu
parente10f55d9db903fb51651ed8c5b9280e7724d18db (diff)
[desktop] Add icon-only mode to grid and improve design (#3485)
- Move Game Icon Size to the main toolbar. It's cleaner that way - Add a "Show Game Name" toggle that does as it says. Disabling it basically creates an "icons-only" mode. Useful for controller-only nav with big icons (TODO: maybe make a 192 size?) - Fixed a crash with controller nav. Oops - Rounded corners of the game icon in grid mode - Fixed the scroll bar creating extra clamping range on the grid icons - Item can be deselected if user clicks on the blank space outside of the view As a bonus fixed a crash on mod manager Future TODOs for design: - [ ] Row 1 type. Not sure what to do here tbh. - [ ] Move around game list settings in configure_ui to make it clear that nothing there affects the grid view. - [ ] 192x192 size? 256 feels too big on my 1440p screen whereas 128 feels too small. - Set text space as a function of fontMetrics. Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3485 Reviewed-by: DraVee <dravee@eden-emu.dev> Reviewed-by: Maufeat <sahyno1996@gmail.com>
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp8
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.h2
-rw-r--r--src/yuzu/configuration/configure_ui.cpp29
-rw-r--r--src/yuzu/configuration/configure_ui.ui20
-rw-r--r--src/yuzu/game/game_card.cpp61
-rw-r--r--src/yuzu/game/game_card.h3
-rw-r--r--src/yuzu/game/game_list.cpp143
-rw-r--r--src/yuzu/main.ui25
-rw-r--r--src/yuzu/main_window.cpp91
-rw-r--r--src/yuzu/main_window.h6
10 files changed, 264 insertions, 124 deletions
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index ecafa6826a..da84d23876 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -161,14 +161,14 @@ void ConfigurePerGameAddons::InstallMods(const QStringList& mods) {
}
}
-void ConfigurePerGameAddons::InstallModPath(const QString& path) {
- const auto mods = QtCommon::Mod::GetModFolders(path, {});
+void ConfigurePerGameAddons::InstallModPath(const QString& path, const QString &fallbackName) {
+ const auto mods = QtCommon::Mod::GetModFolders(path, fallbackName);
if (mods.size() > 1) {
ModSelectDialog* dialog = new ModSelectDialog(mods, this);
connect(dialog, &ModSelectDialog::modsSelected, this, &ConfigurePerGameAddons::InstallMods);
dialog->show();
- } else {
+ } else if (!mods.empty()) {
InstallMods(mods);
}
}
@@ -194,7 +194,7 @@ void ConfigurePerGameAddons::InstallModZip() {
const QString extracted = QtCommon::Mod::ExtractMod(path);
if (!extracted.isEmpty())
- InstallModPath(extracted);
+ InstallModPath(extracted, QFileInfo(path).baseName());
}
void ConfigurePerGameAddons::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_per_game_addons.h b/src/yuzu/configuration/configure_per_game_addons.h
index 3c7c0b0ff0..d2f361139b 100644
--- a/src/yuzu/configuration/configure_per_game_addons.h
+++ b/src/yuzu/configuration/configure_per_game_addons.h
@@ -44,7 +44,7 @@ public:
public slots:
void InstallMods(const QStringList &mods);
- void InstallModPath(const QString& path);
+ void InstallModPath(const QString& path, const QString& fallbackName = {});
void InstallModFolder();
void InstallModZip();
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index b88f41b756..0e91a1a9fd 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "yuzu/configuration/configure_ui.h"
@@ -6,7 +6,6 @@
#include <array>
#include <cstdlib>
#include <set>
-#include <stdexcept>
#include <string>
#include <utility>
@@ -21,7 +20,6 @@
#include "common/common_types.h"
#include "common/fs/path_util.h"
-#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/core.h"
@@ -32,13 +30,6 @@
#include "qt_common/config/uisettings.h"
namespace {
-constexpr std::array default_game_icon_sizes{
- std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")),
- std::make_pair(32, QT_TRANSLATE_NOOP("ConfigureUI", "Small (32x32)")),
- std::make_pair(64, QT_TRANSLATE_NOOP("ConfigureUI", "Standard (64x64)")),
- std::make_pair(128, QT_TRANSLATE_NOOP("ConfigureUI", "Large (128x128)")),
- std::make_pair(256, QT_TRANSLATE_NOOP("ConfigureUI", "Full Size (256x256)")),
-};
constexpr std::array default_folder_icon_sizes{
std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")),
@@ -57,10 +48,6 @@ constexpr std::array row_text_names{
};
// clang-format on
-QString GetTranslatedGameIconSize(size_t index) {
- return QCoreApplication::translate("ConfigureUI", default_game_icon_sizes[index].second);
-}
-
QString GetTranslatedFolderIconSize(size_t index) {
return QCoreApplication::translate("ConfigureUI", default_folder_icon_sizes[index].second);
}
@@ -127,8 +114,6 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
connect(ui->show_types, &QCheckBox::STATE_CHANGED, this, &ConfigureUi::RequestGameListUpdate);
connect(ui->show_play_time, &QCheckBox::STATE_CHANGED, this,
&ConfigureUi::RequestGameListUpdate);
- connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
- &ConfigureUi::RequestGameListUpdate);
connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &ConfigureUi::RequestGameListUpdate);
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
@@ -172,7 +157,6 @@ void ConfigureUi::ApplyConfiguration() {
UISettings::values.show_size = ui->show_size->isChecked();
UISettings::values.show_types = ui->show_types->isChecked();
UISettings::values.show_play_time = ui->show_play_time->isChecked();
- UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt();
UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt();
UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
@@ -202,8 +186,6 @@ void ConfigureUi::SetConfiguration() {
ui->show_size->setChecked(UISettings::values.show_size.GetValue());
ui->show_types->setChecked(UISettings::values.show_types.GetValue());
ui->show_play_time->setChecked(UISettings::values.show_play_time.GetValue());
- ui->game_icon_size_combobox->setCurrentIndex(
- ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue()));
ui->folder_icon_size_combobox->setCurrentIndex(
ui->folder_icon_size_combobox->findData(UISettings::values.folder_icon_size.GetValue()));
@@ -231,11 +213,6 @@ void ConfigureUi::changeEvent(QEvent* event) {
void ConfigureUi::RetranslateUI() {
ui->retranslateUi(this);
- for (int i = 0; i < ui->game_icon_size_combobox->count(); i++) {
- ui->game_icon_size_combobox->setItemText(i,
- GetTranslatedGameIconSize(static_cast<size_t>(i)));
- }
-
for (int i = 0; i < ui->folder_icon_size_combobox->count(); i++) {
ui->folder_icon_size_combobox->setItemText(
i, GetTranslatedFolderIconSize(static_cast<size_t>(i)));
@@ -270,10 +247,6 @@ void ConfigureUi::InitializeLanguageComboBox() {
}
void ConfigureUi::InitializeIconSizeComboBox() {
- for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
- const auto size = default_game_icon_sizes[i].first;
- ui->game_icon_size_combobox->addItem(GetTranslatedGameIconSize(i), size);
- }
for (size_t i = 0; i < default_folder_icon_sizes.size(); i++) {
const auto size = default_folder_icon_sizes[i].first;
ui->folder_icon_size_combobox->addItem(GetTranslatedFolderIconSize(i), size);
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui
index b8e6483814..123068c9e2 100644
--- a/src/yuzu/configuration/configure_ui.ui
+++ b/src/yuzu/configuration/configure_ui.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>363</width>
- <height>603</height>
+ <height>613</height>
</rect>
</property>
<property name="windowTitle">
@@ -112,20 +112,6 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2">
- <item>
- <widget class="QLabel" name="game_icon_size_label">
- <property name="text">
- <string>Game Icon Size:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="game_icon_size_combobox"/>
- </item>
- </layout>
- </item>
- <item>
<layout class="QHBoxLayout" name="folder_icon_size_qhbox_layout_2">
<item>
<widget class="QLabel" name="folder_icon_size_label">
@@ -221,7 +207,7 @@
<string>TextLabel</string>
</property>
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
@@ -251,7 +237,7 @@
<item>
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
diff --git a/src/yuzu/game/game_card.cpp b/src/yuzu/game/game_card.cpp
index d172d3e535..26ab99faaf 100644
--- a/src/yuzu/game/game_card.cpp
+++ b/src/yuzu/game/game_card.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QPainter>
+#include <QPainterPath>
#include "game_card.h"
#include "qt_common/config/uisettings.h"
@@ -18,7 +19,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->setRenderHint(QPainter::Antialiasing);
// padding
- QRect cardRect = option.rect.adjusted(4, 4, -4, -4);
+ QRect cardRect = option.rect.adjusted(4 + m_padding / 2, 4, -4 - m_padding / 2, -4);
// colors
QPalette palette = option.palette;
@@ -32,7 +33,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
borderColor = palette.highlight().color().lighter(150);
textColor = palette.highlightedText().color();
} else if (option.state & QStyle::State_MouseOver) {
- backgroundColor = backgroundColor.lighter(110);
+ backgroundColor = backgroundColor.lighter(120);
}
// bg
@@ -40,7 +41,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->setPen(QPen(borderColor, 1));
painter->drawRoundedRect(cardRect, 10, 10);
- static constexpr const int padding = 10;
+ static constexpr const int padding = 8;
// icon
int _iconsize = UISettings::values.game_icon_size.GetValue();
@@ -58,7 +59,18 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
iconRect = QRect(x, y, scaledSize.width(), scaledSize.height());
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
+
+ // Put this in a separate thing on the painter stack to prevent clipping the text.
+ painter->save();
+
+ // round image edges
+ QPainterPath path;
+ path.addRoundedRect(iconRect, 10, 10);
+ painter->setClipPath(path);
+
painter->drawPixmap(iconRect, iconPixmap);
+
+ painter->restore();
} else {
// if there is no icon just draw a blank rect
iconRect = QRect(cardRect.left() + padding,
@@ -66,31 +78,33 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
_iconsize, _iconsize);
}
- // if "none" is selected, pretend there's a
- _iconsize = _iconsize ? _iconsize : 96;
+ if (UISettings::values.show_game_name.GetValue()) {
+ // if "none" is selected, pretend there's a
+ _iconsize = _iconsize ? _iconsize : 96;
- // padding + text
- QRect textRect = cardRect;
- textRect.setTop(iconRect.bottom() + 8);
- textRect.adjust(padding, 0, -padding, -padding);
+ // padding + text
+ QRect textRect = cardRect;
+ textRect.setTop(iconRect.bottom() + 8);
+ textRect.adjust(padding, 0, -padding, -padding);
- // We are already crammed on space, ignore the row 2
- QString title = index.data(Qt::DisplayRole).toString();
- title = title.split(QLatin1Char('\n')).first();
+ // We are already crammed on space, ignore the row 2
+ QString title = index.data(Qt::DisplayRole).toString();
+ title = title.split(QLatin1Char('\n')).first();
- // now draw text
- painter->setPen(textColor);
- QFont font = option.font;
- font.setBold(true);
+ // now draw text
+ painter->setPen(textColor);
+ QFont font = option.font;
+ font.setBold(true);
- // TODO(crueter): fix this abysmal scaling
- // If "none" is selected, then default to 8.5 point font.
- font.setPointSize(1 + std::max(7.0, _iconsize ? std::sqrt(_iconsize * 0.6) : 7.5));
+ // TODO(crueter): fix this abysmal scaling
+ // If "none" is selected, then default to 8.5 point font.
+ font.setPointSize(1 + std::max(7.0, _iconsize ? std::sqrt(_iconsize * 0.6) : 7.5));
- // TODO(crueter): elide mode
- painter->setFont(font);
+ // TODO(crueter): elide mode
+ painter->setFont(font);
- painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, title);
+ painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, title);
+ }
painter->restore();
}
@@ -99,6 +113,7 @@ QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex&
return m_size;
}
-void GameCard::setSize(const QSize& newSize) {
+void GameCard::setSize(const QSize& newSize, const int padding) {
m_size = newSize;
+ m_padding = padding;
}
diff --git a/src/yuzu/game/game_card.h b/src/yuzu/game/game_card.h
index 3c695c9047..9fd0ec081a 100644
--- a/src/yuzu/game/game_card.h
+++ b/src/yuzu/game/game_card.h
@@ -20,8 +20,9 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
- void setSize(const QSize& newSize);
+ void setSize(const QSize& newSize, const int padding);
private:
QSize m_size;
+ int m_padding;
};
diff --git a/src/yuzu/game/game_list.cpp b/src/yuzu/game/game_list.cpp
index 515fed1a8d..80995a0e79 100644
--- a/src/yuzu/game/game_list.cpp
+++ b/src/yuzu/game/game_list.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include <QAbstractItemView>
#include <QApplication>
#include <QDir>
#include <QFileInfo>
@@ -13,13 +14,12 @@
#include <QMenu>
#include <QScrollBar>
#include <QScroller>
+#include <QScrollerProperties>
#include <QThreadPool>
#include <QToolButton>
#include <QVariantAnimation>
#include <fmt/ranges.h>
-#include <QAbstractItemView>
-#include <QScroller>
-#include <QScrollerProperties>
+#include <qnamespace.h>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
@@ -28,6 +28,7 @@
#include "core/file_sys/registered_cache.h"
#include "game/game_card.h"
#include "qt_common/config/uisettings.h"
+#include "qt_common/qt_common.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/game/game_list.h"
@@ -35,7 +36,6 @@
#include "yuzu/game/game_list_worker.h"
#include "yuzu/main_window.h"
#include "yuzu/util/controller_navigation.h"
-#include "qt_common/qt_common.h"
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent)
: QObject(parent), gamelist{gamelist_} {}
@@ -393,16 +393,21 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }"));
// list view setup
- list_view->setViewMode(QListView::IconMode);
- list_view->setResizeMode(QListView::Adjust);
- list_view->setUniformItemSizes(false);
+ list_view->setViewMode(QListView::ListMode);
+ list_view->setResizeMode(QListView::Fixed);
+ list_view->setUniformItemSizes(true);
list_view->setSelectionMode(QAbstractItemView::SingleSelection);
list_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
list_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+
+ // Forcefully disable scroll bar, prevents thing where game list items
+ // will start clamping prematurely.
+ list_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
list_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
list_view->setContextMenuPolicy(Qt::CustomContextMenu);
list_view->setGridSize(QSize(140, 160));
- m_gameCard->setSize(list_view->gridSize());
+ m_gameCard->setSize(list_view->gridSize(), 0);
list_view->setSpacing(10);
list_view->setWordWrap(true);
@@ -438,8 +443,8 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
return;
}
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
- QCoreApplication::postEvent(tree_view, event);
- QCoreApplication::postEvent(list_view, event);
+
+ QCoreApplication::postEvent(m_currentView, event);
});
// We must register all custom types with the Qt Automoc system so that we are able to use
@@ -488,27 +493,26 @@ void GameList::ResetViewMode() {
break;
}
- if (m_isTreeMode != newTreeMode) {
- m_isTreeMode = newTreeMode;
-
- auto view = m_currentView->viewport();
+ auto view = m_currentView->viewport();
+ view->installEventFilter(this);
- view->installEventFilter(this);
+ // touch gestures
+ view->grabGesture(Qt::SwipeGesture);
+ view->grabGesture(Qt::PanGesture);
- // touch gestures
- view->grabGesture(Qt::SwipeGesture);
- view->grabGesture(Qt::PanGesture);
+ // TODO: touch?
+ QScroller::grabGesture(view, QScroller::LeftMouseButtonGesture);
- // TODO: touch?
- QScroller::grabGesture(view, QScroller::LeftMouseButtonGesture);
+ auto scroller = QScroller::scroller(view);
+ QScrollerProperties props;
+ props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,
+ QScrollerProperties::OvershootAlwaysOff);
+ props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,
+ QScrollerProperties::OvershootAlwaysOff);
+ scroller->setScrollerProperties(props);
- auto scroller = QScroller::scroller(view);
- QScrollerProperties props;
- props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,
- QScrollerProperties::OvershootAlwaysOff);
- props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,
- QScrollerProperties::OvershootAlwaysOff);
- scroller->setScrollerProperties(props);
+ if (m_isTreeMode != newTreeMode) {
+ m_isTreeMode = newTreeMode;
RefreshGameDirectory();
}
@@ -1007,30 +1011,55 @@ void GameList::UpdateIconSize() {
// Update sizes and stuff for the list view
const u32 icon_size = UISettings::values.game_icon_size.GetValue();
- // the scaling on the card is kinda abysmal.
- // TODO(crueter): refactor
int heightMargin = 0;
int widthMargin = 80;
- switch (icon_size) {
- case 128:
- heightMargin = 70;
- break;
- case 0:
- widthMargin = 120;
- heightMargin = 120;
- break;
- case 64:
- heightMargin = 80;
- break;
- case 32:
- case 256:
- heightMargin = 81;
- break;
+
+ if (UISettings::values.show_game_name) {
+ // the scaling on the card is kinda abysmal.
+ // TODO(crueter): refactor
+ switch (icon_size) {
+ case 128:
+ heightMargin = 65;
+ break;
+ case 0:
+ widthMargin = 120;
+ heightMargin = 120;
+ break;
+ case 64:
+ heightMargin = 77;
+ break;
+ case 32:
+ case 256:
+ heightMargin = 81;
+ break;
+ }
+ } else {
+ widthMargin = 24;
+ heightMargin = 24;
}
- // TODO(crueter): Auto size
- list_view->setGridSize(QSize(icon_size + widthMargin, icon_size + heightMargin));
- m_gameCard->setSize(list_view->gridSize());
+ // "auto" resize //
+ const int view_width = list_view->viewport()->width();
+
+ // Tiny space padding to prevent the list view from forcing its own resize operation.
+ const double spacing = 0.01;
+ const int min_item_width = icon_size + widthMargin;
+
+ // And now stretch it a bit to fill out remaining space.
+ // Not perfect but works well enough for now
+ int columns = std::max(1, view_width / min_item_width);
+ int stretched_width = (view_width - (spacing * (columns - 1))) / columns;
+
+ // only updates things if grid size is changed
+ QSize grid_size(stretched_width, icon_size + heightMargin);
+ if (list_view->gridSize() != grid_size) {
+ list_view->setUpdatesEnabled(false);
+
+ list_view->setGridSize(grid_size);
+ m_gameCard->setSize(grid_size, stretched_width - min_item_width);
+
+ list_view->setUpdatesEnabled(true);
+ }
}
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
@@ -1043,7 +1072,8 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
- UpdateIconSize();
+ if (!m_isTreeMode)
+ UpdateIconSize();
// Cancel any existing worker.
current_worker.reset();
@@ -1266,6 +1296,23 @@ bool GameList::eventFilter(QObject* obj, QEvent* event) {
return true;
}
+
+ if (obj == m_currentView->viewport() && event->type() == QEvent::MouseButtonPress) {
+ QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
+
+ // if the user clicks outside of the list, deselect the current item.
+ QModelIndex index = m_currentView->indexAt(mouseEvent->pos());
+ if (!index.isValid()) {
+ m_currentView->selectionModel()->clearSelection();
+ m_currentView->setCurrentIndex(QModelIndex());
+ }
+ }
+
+ if (obj == list_view->viewport() && event->type() == QEvent::Resize) {
+ UpdateIconSize();
+ return true;
+ }
+
return QWidget::eventFilter(obj, event);
}
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 54fc778f80..327a7dffd2 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -112,6 +112,11 @@
<addaction name="action_Tree_View"/>
<addaction name="action_Grid_View"/>
</widget>
+ <widget class="QMenu" name="menuGame_Icon_Size">
+ <property name="title">
+ <string>Game &amp;Icon Size</string>
+ </property>
+ </widget>
<action name="action_Reset_Window_Size_720">
<property name="text">
<string>Reset Window Size to &amp;720p</string>
@@ -145,6 +150,8 @@
<addaction name="menu_Reset_Window_Size"/>
<addaction name="menu_View_Debugging"/>
<addaction name="menu_Game_List_Mode"/>
+ <addaction name="menuGame_Icon_Size"/>
+ <addaction name="action_Show_Game_Name"/>
</widget>
<widget class="QMenu" name="menu_Multiplayer">
<property name="enabled">
@@ -586,6 +593,24 @@
<string>&amp;Grid View</string>
</property>
</action>
+ <action name="actionGame_Icon_Size">
+ <property name="text">
+ <string>Game Icon Size</string>
+ </property>
+ </action>
+ <action name="actionNone">
+ <property name="text">
+ <string>None</string>
+ </property>
+ </action>
+ <action name="action_Show_Game_Name">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Show Game &amp;Name</string>
+ </property>
+ </action>
</widget>
<resources>
<include location="yuzu.qrc"/>
diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp
index def24ef5d3..2e5548c775 100644
--- a/src/yuzu/main_window.cpp
+++ b/src/yuzu/main_window.cpp
@@ -73,6 +73,7 @@
#include <QStatusBar>
#include <QtConcurrentRun>
#include <QMimeData>
+#include <QActionGroup>
// Qt Common //
#include "qt_common/config/uisettings.h"
@@ -377,6 +378,22 @@ static QString PrettyProductName() {
return QSysInfo::prettyProductName();
}
+namespace {
+
+constexpr std::array<std::pair<u32, const char *>, 5> default_game_icon_sizes{
+ std::make_pair(0, QT_TRANSLATE_NOOP("MainWindow", "None")),
+ std::make_pair(32, QT_TRANSLATE_NOOP("MainWindow", "Small (32x32)")),
+ std::make_pair(64, QT_TRANSLATE_NOOP("MainWindow", "Standard (64x64)")),
+ std::make_pair(128, QT_TRANSLATE_NOOP("MainWindow", "Large (128x128)")),
+ std::make_pair(256, QT_TRANSLATE_NOOP("MainWindow", "Full Size (256x256)")),
+};
+
+QString GetTranslatedGameIconSize(size_t index) {
+ return QCoreApplication::translate("MainWindow", default_game_icon_sizes[index].second);
+}
+
+}
+
#ifndef _WIN32
// TODO(crueter): carboxyl does this, is it needed in qml?
inline static bool isDarkMode() {
@@ -1607,6 +1624,33 @@ void MainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Grid_View, &MainWindow::SetGridView);
connect_menu(ui->action_Tree_View, &MainWindow::SetTreeView);
+ game_size_actions = new QActionGroup(this);
+ game_size_actions->setExclusive(true);
+
+ for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
+ const auto current_size = UISettings::values.game_icon_size.GetValue();
+ const auto size = default_game_icon_sizes[i].first;
+ QAction *action = ui->menuGame_Icon_Size->addAction(GetTranslatedGameIconSize(i));
+ action->setCheckable(true);
+
+ if (current_size == size) action->setChecked(true);
+
+ game_size_actions->addAction(action);
+
+ connect(action, &QAction::triggered, this, [this, size](bool checked) {
+ if (checked) {
+ UISettings::values.game_icon_size.SetValue(size);
+ CheckIconSize();
+ game_list->RefreshGameDirectory();
+ }
+ });
+ }
+
+ CheckIconSize();
+
+ ui->action_Show_Game_Name->setChecked(UISettings::values.show_game_name.GetValue());
+ connect(ui->action_Show_Game_Name, &QAction::triggered, this, &MainWindow::ToggleShowGameName);
+
// Multiplayer
connect(ui->action_View_Lobby, &QAction::triggered, multiplayer_state,
&MultiplayerState::OnViewLobby);
@@ -3385,6 +3429,9 @@ void MainWindow::SetGameListMode(Settings::GameListMode mode) {
ui->action_Tree_View->setChecked(mode == Settings::GameListMode::TreeView);
UISettings::values.game_list_mode = mode;
+ ui->action_Show_Game_Name->setEnabled(mode == Settings::GameListMode::GridView);
+
+ CheckIconSize();
game_list->ResetViewMode();
}
@@ -3396,6 +3443,43 @@ void MainWindow::SetTreeView() {
SetGameListMode(Settings::GameListMode::TreeView);
}
+void MainWindow::CheckIconSize() {
+ // When in grid view mode, with text off
+ // there is no point in having icons turned off..
+ auto actions = game_size_actions->actions();
+ if (UISettings::values.game_list_mode.GetValue() == Settings::GameListMode::GridView &&
+ !UISettings::values.show_game_name.GetValue()) {
+ u32 newSize = UISettings::values.game_icon_size.GetValue();
+ if (newSize == 0) {
+ newSize = 64;
+ UISettings::values.game_icon_size.SetValue(newSize);
+ }
+
+ // Then disable the "none" action and update that menu.
+ for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
+ const auto current_size = newSize;
+ const auto size = default_game_icon_sizes[i].first;
+ if (current_size == size) actions.at(i)->setChecked(true);
+ }
+
+ // Update this if you add anything before None.
+ actions.at(0)->setEnabled(false);
+ } else {
+ actions.at(0)->setEnabled(true);
+ }
+}
+
+void MainWindow::ToggleShowGameName() {
+ auto &setting = UISettings::values.show_game_name;
+ const bool newValue = !setting.GetValue();
+ ui->action_Show_Game_Name->setChecked(newValue);
+ setting.SetValue(newValue);
+
+ CheckIconSize();
+
+ game_list->RefreshGameDirectory();
+}
+
void MainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
@@ -3916,7 +4000,6 @@ void MainWindow::OnDataDialog() {
// refresh stuff in case it was cleared
OnGameListRefresh();
-
}
void MainWindow::OnToggleFilterBar() {
@@ -3939,7 +4022,6 @@ void MainWindow::OnGameListRefresh() {
SetFirmwareVersion();
}
-
void MainWindow::LaunchFirmwareApplet(u64 raw_program_id, std::optional<Service::NFP::CabinetMode> cabinet_mode) {
auto const program_id = Service::AM::AppletProgramId(raw_program_id);
auto result = FirmwareManager::VerifyFirmware(*QtCommon::system.get());
@@ -4658,6 +4740,11 @@ void MainWindow::OnLanguageChanged(const QString& locale) {
qApp->removeTranslator(&translator);
}
+ QList<QAction *> actions = game_size_actions->actions();
+ for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
+ actions.at(i)->setText(GetTranslatedGameIconSize(i));
+ }
+
UISettings::values.language = locale.toStdString();
LoadTranslation();
ui->retranslateUi(this);
diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h
index 3261ccc9a1..ddabe21ae0 100644
--- a/src/yuzu/main_window.h
+++ b/src/yuzu/main_window.h
@@ -15,6 +15,7 @@
#include <QPushButton>
#include <QTimer>
#include <QTranslator>
+#include <qaction.h>
#include "common/common_types.h"
#include "common/settings_enums.h"
@@ -407,6 +408,9 @@ private slots:
void SetGridView();
void SetTreeView();
+ void CheckIconSize();
+ void ToggleShowGameName();
+
void LaunchFirmwareApplet(u64 program_id, std::optional<Service::NFP::CabinetMode> mode);
void OnCreateHomeMenuDesktopShortcut();
void OnCreateHomeMenuApplicationMenuShortcut();
@@ -532,6 +536,8 @@ private:
QString startup_icon_theme;
+ QActionGroup *game_size_actions;
+
// Debugger panes
ControllerDialog* controller_dialog = nullptr;