aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrueter <crueter@eden-emu.dev>2026-06-22 21:04:47 +0200
committercrueter <crueter@eden-emu.dev>2026-06-22 21:04:47 +0200
commit39be450fa3a7248f7dde73dfe91d2f0447a155dd (patch)
treeb508f83c056280b1d5fb5365c6ab4e551c5a1a95
parent68aaea60855d7791480a546b8888e123e083ca1a (diff)
[desktop] Add basic carousel view (#4112)
Adds a basic carousel view, or essentially a horizontal list a la Android/Qt Quick. Lacks a lot of niceties like autoscroll, smooth shifts, etc. Will work on those later Also fixed a bug introduced recently that capped game icon size to 8 at the low end, breaking the None option Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4112 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev> Reviewed-by: Lizzie <lizzie@eden-emu.dev>
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/qt_common/config/uisettings.h2
-rw-r--r--src/qt_common/game_list/game_list_p.h1
-rw-r--r--src/yuzu/CMakeLists.txt4
-rw-r--r--src/yuzu/game/carousel.cpp107
-rw-r--r--src/yuzu/game/carousel.h27
-rw-r--r--src/yuzu/game/game_card.cpp69
-rw-r--r--src/yuzu/game/game_card.h10
-rw-r--r--src/yuzu/game/game_grid.cpp13
-rw-r--r--src/yuzu/game/game_grid.h4
-rw-r--r--src/yuzu/game/game_list.cpp71
-rw-r--r--src/yuzu/game/game_list.h7
-rw-r--r--src/yuzu/game/game_tree.h2
-rw-r--r--src/yuzu/main.ui11
-rw-r--r--src/yuzu/main_window.cpp14
-rw-r--r--src/yuzu/main_window.h1
16 files changed, 300 insertions, 45 deletions
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index a71cf2141c..b14ed9bc67 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -158,7 +158,7 @@ ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High)
ENUM(TemperatureUnits, Celsius, Fahrenheit)
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
-ENUM(GameListMode, TreeView, GridView);
+ENUM(GameListMode, TreeView, GridView, CarouselView);
ENUM(SpeedMode, Standard, Turbo, Slow);
template <typename Type>
diff --git a/src/qt_common/config/uisettings.h b/src/qt_common/config/uisettings.h
index d1e365553d..fd79d2299f 100644
--- a/src/qt_common/config/uisettings.h
+++ b/src/qt_common/config/uisettings.h
@@ -207,7 +207,7 @@ struct Values {
// Game List
Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
- Setting<u32, true> game_icon_size{linkage, 64, 8, 512, "game_icon_size", Category::UiGameList};
+ Setting<u32, true> game_icon_size{linkage, 64, 0, 512, "game_icon_size", Category::UiGameList};
Setting<u32, true> folder_icon_size{linkage, 48, 8, 512, "folder_icon_size", Category::UiGameList};
Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
diff --git a/src/qt_common/game_list/game_list_p.h b/src/qt_common/game_list/game_list_p.h
index a5297d0db3..d3d46771aa 100644
--- a/src/qt_common/game_list/game_list_p.h
+++ b/src/qt_common/game_list/game_list_p.h
@@ -165,6 +165,7 @@ public:
return QStringLiteral("%1\n %2").arg(row1, row2);
}
case Settings::GameListMode::GridView:
+ case Settings::GameListMode::CarouselView:
return row1;
default:
break;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 1e8030947c..f806ebf515 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -240,7 +240,9 @@ add_executable(yuzu
render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui
updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui
- game/common.h)
+ game/common.h
+ game/carousel.h game/carousel.cpp
+)
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
diff --git a/src/yuzu/game/carousel.cpp b/src/yuzu/game/carousel.cpp
new file mode 100644
index 0000000000..ac9c9947b5
--- /dev/null
+++ b/src/yuzu/game/carousel.cpp
@@ -0,0 +1,107 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_common/config/uisettings.h"
+#include "qt_common/game_list/model.h"
+#include "yuzu/game/common.h"
+#include "yuzu/game/game_card.h"
+#include "yuzu/game/carousel.h"
+
+GameCarousel::GameCarousel(QWidget* parent) : QListView{parent} {
+ m_gameCard = new GameCard(this);
+ setItemDelegate(m_gameCard);
+
+ setViewMode(QListView::IconMode);
+ setMovement(QListView::Static);
+ setUniformItemSizes(true);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+
+ setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+
+ setSpacing(10);
+ setWordWrap(true);
+ setTextElideMode(Qt::ElideRight);
+ setFlow(QListView::LeftToRight);
+ setWrapping(false);
+}
+
+void GameCarousel::SetModel(GameListModel* model) {
+ QListView::setModel(model);
+ UpdateIconSize();
+}
+
+void GameCarousel::ApplyFilter(const QString& edit_filter_text, GameListModel* model) {
+ int row_count = model->rowCount();
+
+ for (int i = 0; i < row_count; ++i) {
+ QStandardItem* item = model->item(i, 0);
+ if (!item)
+ continue;
+
+ if (Yuzu::FilterMatches(edit_filter_text, item)) {
+ setRowHidden(i, false);
+ } else {
+ setRowHidden(i, true);
+ }
+ }
+}
+
+void GameCarousel::UpdateIconSize() {
+ const u32 icon_size = UISettings::values.game_icon_size.GetValue();
+
+ int heightMargin = 0;
+ int widthMargin = 80;
+
+ // TODO(crueter): get rid of this nonsense
+ if (UISettings::values.show_game_name) {
+ 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;
+ }
+
+ const int min_item_width = icon_size + widthMargin;
+ const int min_item_height = icon_size + heightMargin;
+ const int grid_height = std::max(min_item_height, viewport()->height());
+
+ QSize content_size(min_item_width, min_item_height);
+ QSize grid_size(min_item_width, grid_height);
+ if (gridSize() != grid_size) {
+ setUpdatesEnabled(false);
+
+ setGridSize(grid_size);
+ m_gameCard->setSize(grid_size, content_size, 0, 0);
+
+ setUpdatesEnabled(true);
+ }
+}
+
+QModelIndex GameCarousel::indexAt(const QPoint& point) const {
+ QModelIndex index = QListView::indexAt(point);
+ if (!index.isValid())
+ return {};
+ if (m_gameCard && !m_gameCard->hitTest(point, index, this, visualRect(index)))
+ return {};
+ return index;
+}
diff --git a/src/yuzu/game/carousel.h b/src/yuzu/game/carousel.h
new file mode 100644
index 0000000000..c03884c8c2
--- /dev/null
+++ b/src/yuzu/game/carousel.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QListView>
+#include <QString>
+
+class GameCard;
+class GameListModel;
+class QResizeEvent;
+
+class GameCarousel : public QListView {
+ Q_OBJECT
+
+public:
+ explicit GameCarousel(QWidget* parent = nullptr);
+
+ void SetModel(GameListModel* model);
+ void ApplyFilter(const QString& edit_filter_text, GameListModel* model);
+ void UpdateIconSize();
+
+ QModelIndex indexAt(const QPoint& point) const override;
+
+private:
+ GameCard* m_gameCard = nullptr;
+};
diff --git a/src/yuzu/game/game_card.cpp b/src/yuzu/game/game_card.cpp
index e1cd92652e..12ad482c88 100644
--- a/src/yuzu/game/game_card.cpp
+++ b/src/yuzu/game/game_card.cpp
@@ -18,27 +18,16 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
- constexpr int cardMargin = 8;
constexpr int cardCornerRadius = 10;
- const int column = index.row() % m_columns;
- const int cell_width = option.rect.width();
- const int card_width = cell_width - m_padding;
-
- const int row_width = m_columns * cell_width;
- const int total_gap = row_width - cardMargin * 2 - m_columns * card_width;
- const int gap = (m_columns > 1) ? (total_gap / (m_columns - 1)) : 0;
-
- const int card_left = option.rect.left() - column * cell_width + cardMargin + column * (card_width + gap) + 4;
- const QRect cardRect(card_left, option.rect.top() + 4, card_width - 8,
- option.rect.height() - cardMargin);
+ const QRect cardRect = getCardRect(option, index);
QPalette palette = option.palette;
QColor backgroundColor = palette.window().color();
QColor borderColor = palette.dark().color();
QColor textColor = palette.text().color();
- // highlight blue on select
+ // highlight on select or hover
if (option.state & QStyle::State_Selected) {
backgroundColor = palette.highlight().color();
borderColor = palette.highlight().color().lighter(150);
@@ -60,7 +49,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
scaled.scale(icon_size, icon_size, Qt::KeepAspectRatio);
iconRect = {cardRect.left() + (cardRect.width() - scaled.width()) / 2,
- cardRect.top() + cardMargin, scaled.width(), scaled.height()};
+ cardRect.top() + cardMargin - 1, scaled.width(), scaled.height()};
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
@@ -94,12 +83,62 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->restore();
}
+QRect GameCard::getCardRect(const QStyleOptionViewItem& option, const QModelIndex& index) const {
+ const int cell_width = option.rect.width();
+
+ const int card_width = cell_width - m_padding;
+
+ int card_left, card_top, card_height;
+
+ if (m_columns >= 1) {
+ // grid mode
+
+ // center everything in-line, such that the leftmost and rightmost cards
+ // have ~ equal padding to the edge of the viewport
+ // spacing between each card is larger, but equal to each other
+ const int column = index.row() % m_columns;
+
+ const int row_width = m_columns * cell_width;
+ const int total_gap = row_width - cardMargin * 2 - m_columns * card_width;
+ const int gap = (m_columns > 1) ? (total_gap / (m_columns - 1)) : 0;
+ card_left =
+ option.rect.left() - column * cell_width + cardMargin + column * (card_width + gap) + 4;
+
+ // fill cell vertically
+ card_top = option.rect.top() + cardMargin;
+ card_height = option.rect.height() - cardMargin - 1;
+ } else {
+ // carousel mode
+ card_left = option.rect.left() + cardMargin + 4;
+
+ // the delegate itself takes up the full height, but the card itself
+ // gets centered
+ const int content_height = m_contentSize.height() - cardMargin;
+ const int cell_height = option.rect.height();
+
+ card_height = std::min(content_height, cell_height - cardMargin * 2) - 1;
+ card_top = option.rect.top() + (cell_height - card_height) / 2;
+ }
+
+ return QRect(card_left, card_top, card_width - cardMargin, card_height);
+}
+
+bool GameCard::hitTest(const QPoint& point, const QModelIndex& index, const QWidget* widget,
+ const QRect& cellRect) const {
+ QStyleOptionViewItem option;
+ option.initFrom(widget);
+ option.rect = cellRect;
+ return getCardRect(option, index).contains(point);
+}
+
QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
return m_size;
}
-void GameCard::setSize(const QSize& newSize, const int padding, const int columns) {
+void GameCard::setSize(const QSize& newSize, const QSize& contentSize, const int padding,
+ const int columns) {
m_size = newSize;
+ m_contentSize = contentSize;
m_padding = padding;
m_columns = columns;
}
diff --git a/src/yuzu/game/game_card.h b/src/yuzu/game/game_card.h
index f2a3edfddf..1d522c0ed8 100644
--- a/src/yuzu/game/game_card.h
+++ b/src/yuzu/game/game_card.h
@@ -17,11 +17,19 @@ public:
void paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
+ QRect getCardRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ bool hitTest(const QPoint& point, const QModelIndex& index,
+ const QWidget* widget, const QRect& cellRect) const;
+
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
- void setSize(const QSize& newSize, const int padding, const int columns);
+ void setSize(const QSize& newSize, const QSize& contentSize, const int padding, const int columns);
private:
+ static constexpr int cardMargin = 8;
+
QSize m_size;
+ QSize m_contentSize;
int m_padding;
int m_columns;
};
diff --git a/src/yuzu/game/game_grid.cpp b/src/yuzu/game/game_grid.cpp
index ab8a3d8bfe..c5f77cf897 100644
--- a/src/yuzu/game/game_grid.cpp
+++ b/src/yuzu/game/game_grid.cpp
@@ -25,8 +25,6 @@ GameGrid::GameGrid(QWidget* parent) : QListView{parent} {
setEditTriggers(QAbstractItemView::NoEditTriggers);
setContextMenuPolicy(Qt::CustomContextMenu);
- setGridSize(QSize(140, 160));
- m_gameCard->setSize(gridSize(), 0, 4);
setSpacing(10);
setWordWrap(true);
@@ -97,8 +95,17 @@ void GameGrid::UpdateIconSize() {
setUpdatesEnabled(false);
setGridSize(grid_size);
- m_gameCard->setSize(grid_size, stretched_width - min_item_width, columns);
+ m_gameCard->setSize(grid_size, grid_size, stretched_width - min_item_width, columns);
setUpdatesEnabled(true);
}
}
+
+QModelIndex GameGrid::indexAt(const QPoint& point) const {
+ QModelIndex index = QListView::indexAt(point);
+ if (!index.isValid())
+ return {};
+ if (m_gameCard && !m_gameCard->hitTest(point, index, this, visualRect(index)))
+ return {};
+ return index;
+}
diff --git a/src/yuzu/game/game_grid.h b/src/yuzu/game/game_grid.h
index b670bfc3cb..3b5d6da1d4 100644
--- a/src/yuzu/game/game_grid.h
+++ b/src/yuzu/game/game_grid.h
@@ -6,8 +6,6 @@
#include <QListView>
#include <QString>
-#include "common/common_types.h"
-
class GameCard;
class GameListModel;
@@ -21,6 +19,8 @@ public:
void ApplyFilter(const QString& edit_filter_text, GameListModel* model);
void UpdateIconSize();
+ QModelIndex indexAt(const QPoint& point) const override;
+
private:
GameCard* m_gameCard = nullptr;
};
diff --git a/src/yuzu/game/game_list.cpp b/src/yuzu/game/game_list.cpp
index a1304b4343..86fd3c1b4f 100644
--- a/src/yuzu/game/game_list.cpp
+++ b/src/yuzu/game/game_list.cpp
@@ -20,6 +20,7 @@
#include <QScrollerProperties>
#include <QToolButton>
#include <QVariantAnimation>
+#include <qlayoutitem.h>
#include "common/common_types.h"
#include "core/core.h"
@@ -31,6 +32,7 @@
#include "qt_common/qt_common.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h"
+#include "yuzu/game/carousel.h"
#include "yuzu/game/game_grid.h"
#include "yuzu/game/game_list.h"
#include "yuzu/game/game_tree.h"
@@ -67,6 +69,9 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
connect(grid_view, &QListView::activated, this, &GameList::ValidateEntry);
connect(grid_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu);
+ connect(carousel_view, &QListView::activated, this, &GameList::ValidateEntry);
+ connect(carousel_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu);
+
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, this,
[this](Qt::Key key) {
if (system.IsPoweredOn()) {
@@ -86,6 +91,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
connect(item_model, &GameListModel::SaveConfig, this, &GameList::SaveConfig);
connect(item_model, &GameListModel::PopulatingStarted, this, &GameList::OnPopulate);
+ // TODO: impl on grid/carousel
connect(tree_view, &GameTree::FilterResultReady, search_field,
[this](int visible, int total) { search_field->setFilterResult(visible, total); });
@@ -101,12 +107,11 @@ GameList::~GameList() {
void GameList::SetupViews() {
tree_view = new GameTree(this);
grid_view = new GameGrid(this);
+ carousel_view = new GameCarousel(this);
tree_view->SetModel(item_model);
grid_view->SetModel(item_model);
-
- layout->addWidget(tree_view);
- layout->addWidget(grid_view);
+ carousel_view->SetModel(item_model);
}
QString GameList::GetLastFilterResultItem() const {
@@ -138,10 +143,16 @@ void GameList::LoadCompatibilityList() {
void GameList::OnPopulate() {
m_currentView->setEnabled(false);
- if (m_isTreeMode) {
- grid_view->UpdateIconSize();
- } else {
+ switch (game_list_mode) {
+ case Settings::GameListMode::TreeView:
tree_view->UpdateColumnVisibility(item_model);
+ break;
+ case Settings::GameListMode::GridView:
+ grid_view->UpdateIconSize();
+ break;
+ case Settings::GameListMode::CarouselView:
+ carousel_view->UpdateIconSize();
+ break;
}
}
@@ -166,26 +177,46 @@ void GameList::UnloadController() {
}
void GameList::ResetViewMode() {
- auto& setting = UISettings::values.game_list_mode;
+ const auto mode = UISettings::values.game_list_mode.GetValue();
+ game_list_mode = mode;
+
+ if (m_currentView)
+ layout->removeWidget(m_currentView);
+
bool newTreeMode = false;
- switch (setting.GetValue()) {
+ switch (mode) {
case Settings::GameListMode::TreeView:
m_currentView = tree_view;
newTreeMode = true;
- tree_view->setVisible(true);
- grid_view->setVisible(false);
+
break;
case Settings::GameListMode::GridView:
m_currentView = grid_view;
newTreeMode = false;
- grid_view->setVisible(true);
- tree_view->setVisible(false);
+
+ break;
+ case Settings::GameListMode::CarouselView:
+ m_currentView = carousel_view;
+ newTreeMode = false;
+
break;
default:
UNREACHABLE();
}
+ tree_view->setVisible(false);
+ grid_view->setVisible(false);
+ carousel_view->setVisible(false);
+
+ tree_view->setEnabled(false);
+ grid_view->setEnabled(false);
+ carousel_view->setEnabled(false);
+
+ m_currentView->setVisible(true);
+ m_currentView->setEnabled(true);
+ layout->insertWidget(0, m_currentView);
+
auto view = m_currentView->viewport();
view->installEventFilter(this);
@@ -297,11 +328,13 @@ void GameList::OnPopulatingCompleted(const QStringList& watch_list) {
for (int i = 1; i < item_model->rowCount() - 1; ++i) {
children_total += item_model->item(i, 0)->rowCount();
}
+
search_field->setFilterResult(children_total, children_total);
if (children_total > 0) {
search_field->setFocus();
}
+ // TODO: carousel/grid impl.
item_model->sort(tree_view->header()->sortIndicatorSection(),
tree_view->header()->sortIndicatorOrder());
@@ -317,8 +350,15 @@ void GameList::RefreshExternalContent() {
}
void GameList::UpdateIconSizes() {
- if (!m_isTreeMode) {
+ switch (game_list_mode) {
+ case Settings::GameListMode::GridView:
grid_view->UpdateIconSize();
+ break;
+ case Settings::GameListMode::CarouselView:
+ carousel_view->UpdateIconSize();
+ break;
+ case Settings::GameListMode::TreeView:
+ break;
}
}
@@ -712,6 +752,11 @@ bool GameList::eventFilter(QObject* obj, QEvent* event) {
return true;
}
+ if (obj == carousel_view->viewport() && event->type() == QEvent::Resize) {
+ carousel_view->UpdateIconSize();
+ return true;
+ }
+
return QWidget::eventFilter(obj, event);
}
diff --git a/src/yuzu/game/game_list.h b/src/yuzu/game/game_list.h
index 4c4c5150fe..027e58df3a 100644
--- a/src/yuzu/game/game_list.h
+++ b/src/yuzu/game/game_list.h
@@ -15,12 +15,12 @@
#include <QVBoxLayout>
#include <QVector>
#include <QWidget>
-#include <qabstractitemview.h>
#include "common/common_types.h"
#include "core/core.h"
#include "frontend_common/play_time_manager.h"
#include "qt_common/config/uisettings.h"
+#include "qt_common/game_list/model.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h"
@@ -38,6 +38,9 @@ class MainWindow;
enum class AmLaunchType;
enum class StartGameType;
+class GameCarousel;
+
+class QAbstractItemView;
namespace Core {
class System;
}
@@ -151,7 +154,9 @@ private:
GameTree* tree_view = nullptr;
GameGrid* grid_view = nullptr;
+ GameCarousel* carousel_view = nullptr;
GameListModel* item_model = nullptr;
+ Settings::GameListMode game_list_mode;
ControllerNavigation* controller_navigation = nullptr;
diff --git a/src/yuzu/game/game_tree.h b/src/yuzu/game/game_tree.h
index b5eebdb9dd..e4417803aa 100644
--- a/src/yuzu/game/game_tree.h
+++ b/src/yuzu/game/game_tree.h
@@ -3,8 +3,6 @@
#pragma once
-#include <QHeaderView>
-#include <QString>
#include <QTreeView>
class GameListModel;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 59aab0ef93..deb7774d25 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -45,7 +45,7 @@
<x>0</x>
<y>0</y>
<width>1280</width>
- <height>22</height>
+ <height>23</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@@ -111,6 +111,7 @@
</property>
<addaction name="action_Tree_View"/>
<addaction name="action_Grid_View"/>
+ <addaction name="action_Carousel_View"/>
</widget>
<widget class="QMenu" name="menuGame_Icon_Size">
<property name="title">
@@ -620,6 +621,14 @@
<string>Show &amp;Performance Overlay</string>
</property>
</action>
+ <action name="action_Carousel_View">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Carousel View</string>
+ </property>
+ </action>
</widget>
<resources>
<include location="yuzu.qrc"/>
diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp
index 00b9e65c67..bc74f5da79 100644
--- a/src/yuzu/main_window.cpp
+++ b/src/yuzu/main_window.cpp
@@ -1529,6 +1529,7 @@ void MainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Grid_View, &MainWindow::SetGridView);
connect_menu(ui->action_Tree_View, &MainWindow::SetTreeView);
+ connect_menu(ui->action_Carousel_View, &MainWindow::SetCarouselView);
game_size_actions = new QActionGroup(this);
game_size_actions->setExclusive(true);
@@ -3317,9 +3318,10 @@ void MainWindow::ResetWindowSize1080() {
void MainWindow::SetGameListMode(Settings::GameListMode mode) {
ui->action_Grid_View->setChecked(mode == Settings::GameListMode::GridView);
ui->action_Tree_View->setChecked(mode == Settings::GameListMode::TreeView);
+ ui->action_Carousel_View->setChecked(mode == Settings::GameListMode::CarouselView);
UISettings::values.game_list_mode = mode;
- ui->action_Show_Game_Name->setEnabled(mode == Settings::GameListMode::GridView);
+ ui->action_Show_Game_Name->setEnabled(mode != Settings::GameListMode::TreeView);
CheckIconSize();
game_list->ResetViewMode();
@@ -3333,11 +3335,15 @@ void MainWindow::SetTreeView() {
SetGameListMode(Settings::GameListMode::TreeView);
}
+void MainWindow::SetCarouselView() {
+ SetGameListMode(Settings::GameListMode::CarouselView);
+}
+
void MainWindow::CheckIconSize() {
- // When in grid view mode, with text off
- // there is no point in having icons turned off..
+ // When in grid/carousel 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 &&
+ if (UISettings::values.game_list_mode.GetValue() != Settings::GameListMode::TreeView &&
!UISettings::values.show_game_name.GetValue()) {
u32 newSize = UISettings::values.game_icon_size.GetValue();
if (newSize == 0) {
diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h
index 65da01e9ca..40253aa52f 100644
--- a/src/yuzu/main_window.h
+++ b/src/yuzu/main_window.h
@@ -410,6 +410,7 @@ private slots:
void SetGameListMode(Settings::GameListMode mode);
void SetGridView();
void SetTreeView();
+ void SetCarouselView();
void CheckIconSize();
void ToggleShowGameName();