[desktop] Basic grid view implementation (#3479)
Closes #3441 Basic impl of a grid view on the game list. The ideal solution here would be to use QSortFilterProxyModel and abstract the game list model out to a QStandardItemModel, but that is too much effort for me rn. Adapted the "card" design from QML, can 1000% be improved but QPainter is just such a pain to deal with. Implanting a Qt Quick scene into there would legitimately be easier. Anyways, margins and text sizes lgtm at all sizes, though please give feedback on both that and the general card design. Future TODOs: - [ ] Auto size mode - [ ] Refactor to use models Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3479
This commit is contained in:
parent
69aff83ef4
commit
b9e052b3a7
|
|
@ -155,6 +155,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);
|
||||
|
||||
template <typename Type>
|
||||
inline std::string_view CanonicalizeEnum(Type id) {
|
||||
|
|
|
|||
|
|
@ -766,6 +766,12 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
|
|||
PAIR(ExtendedDynamicState, EDS3, tr("ExtendedDynamicState 3")),
|
||||
}});
|
||||
|
||||
translations->insert({Settings::EnumMetadata<Settings::GameListMode>::Index(),
|
||||
{
|
||||
PAIR(GameListMode, TreeView, tr("Tree View")),
|
||||
PAIR(GameListMode, GridView, tr("Grid View")),
|
||||
}});
|
||||
|
||||
#undef PAIR
|
||||
#undef CTX_PAIR
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
|
|
@ -210,6 +210,8 @@ struct Values {
|
|||
Setting<u32> folder_icon_size{linkage, 48, "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};
|
||||
Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView, "game_list_mode", Category::UiGameList};
|
||||
|
||||
std::atomic_bool is_game_list_reload_pending{false};
|
||||
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
|
||||
Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
|
||||
|
|
|
|||
|
|
@ -156,11 +156,14 @@ add_executable(yuzu
|
|||
debugger/controller.cpp
|
||||
debugger/controller.h
|
||||
|
||||
game_list.cpp
|
||||
game_list.h
|
||||
game_list_p.h
|
||||
game_list_worker.cpp
|
||||
game_list_worker.h
|
||||
game/game_list.cpp
|
||||
game/game_list.h
|
||||
game/game_list_p.h
|
||||
game/game_list_worker.cpp
|
||||
game/game_list_worker.h
|
||||
game/game_card.h
|
||||
game/game_card.cpp
|
||||
|
||||
hotkeys.cpp
|
||||
hotkeys.h
|
||||
install_dialog.cpp
|
||||
|
|
@ -234,7 +237,7 @@ add_executable(yuzu
|
|||
data_dialog.h data_dialog.cpp data_dialog.ui
|
||||
data_widget.ui
|
||||
ryujinx_dialog.h ryujinx_dialog.cpp ryujinx_dialog.ui
|
||||
main_window.h main_window.cpp
|
||||
main_window.h main_window.cpp main.ui
|
||||
|
||||
configuration/system/new_user_dialog.h configuration/system/new_user_dialog.cpp configuration/system/new_user_dialog.ui
|
||||
configuration/system/profile_avatar_dialog.h configuration/system/profile_avatar_dialog.cpp
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <QPainter>
|
||||
#include "game_card.h"
|
||||
#include "qt_common/config/uisettings.h"
|
||||
|
||||
GameCard::GameCard(QObject* parent) : QStyledItemDelegate{parent} {
|
||||
setObjectName("GameCard");
|
||||
}
|
||||
|
||||
void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) const {
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// padding
|
||||
QRect cardRect = option.rect.adjusted(4, 4, -4, -4);
|
||||
|
||||
// colors
|
||||
QPalette palette = option.palette;
|
||||
QColor backgroundColor = palette.window().color();
|
||||
QColor borderColor = palette.dark().color();
|
||||
QColor textColor = palette.text().color();
|
||||
|
||||
// if it's selected add a blue background
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
backgroundColor = palette.highlight().color();
|
||||
borderColor = palette.highlight().color().lighter(150);
|
||||
textColor = palette.highlightedText().color();
|
||||
} else if (option.state & QStyle::State_MouseOver) {
|
||||
backgroundColor = backgroundColor.lighter(110);
|
||||
}
|
||||
|
||||
// bg
|
||||
painter->setBrush(backgroundColor);
|
||||
painter->setPen(QPen(borderColor, 1));
|
||||
painter->drawRoundedRect(cardRect, 10, 10);
|
||||
|
||||
static constexpr const int padding = 10;
|
||||
|
||||
// icon
|
||||
int _iconsize = UISettings::values.game_icon_size.GetValue();
|
||||
QSize iconSize(_iconsize, _iconsize);
|
||||
QPixmap iconPixmap = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
|
||||
QRect iconRect;
|
||||
if (!iconPixmap.isNull()) {
|
||||
QSize scaledSize = iconPixmap.size();
|
||||
scaledSize.scale(iconSize, Qt::KeepAspectRatio);
|
||||
|
||||
int x = cardRect.left() + (cardRect.width() - scaledSize.width()) / 2;
|
||||
int y = cardRect.top() + padding;
|
||||
|
||||
iconRect = QRect(x, y, scaledSize.width(), scaledSize.height());
|
||||
|
||||
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
painter->drawPixmap(iconRect, iconPixmap);
|
||||
} else {
|
||||
// if there is no icon just draw a blank rect
|
||||
iconRect = QRect(cardRect.left() + padding,
|
||||
cardRect.top() + padding,
|
||||
_iconsize, _iconsize);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, title);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void GameCard::setSize(const QSize& newSize) {
|
||||
m_size = newSize;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
/**
|
||||
* A stylized "card"-like delegate for the game grid view.
|
||||
* Adapted from QML
|
||||
*/
|
||||
class GameCard : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GameCard(QObject* parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
void setSize(const QSize& newSize);
|
||||
|
||||
private:
|
||||
QSize m_size;
|
||||
};
|
||||
|
|
@ -9,29 +9,30 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <QListView>
|
||||
#include <QMenu>
|
||||
#include <QScroller>
|
||||
#include <QScrollBar>
|
||||
#include <QScroller>
|
||||
#include <QThreadPool>
|
||||
#include <QToolButton>
|
||||
#include <QVariantAnimation>
|
||||
#include <fmt/ranges.h>
|
||||
#include <qfilesystemwatcher.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qscroller.h>
|
||||
#include <qscrollerproperties.h>
|
||||
#include <QAbstractItemView>
|
||||
#include <QScroller>
|
||||
#include <QScrollerProperties>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "game/game_card.h"
|
||||
#include "qt_common/config/uisettings.h"
|
||||
#include "qt_common/util/game.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/game_list.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/game_list_worker.h"
|
||||
#include "yuzu/game/game_list.h"
|
||||
#include "yuzu/game/game_list_p.h"
|
||||
#include "yuzu/game/game_list_worker.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/util/controller_navigation.h"
|
||||
#include "qt_common/qt_common.h"
|
||||
|
|
@ -198,25 +199,56 @@ void GameList::OnTextChanged(const QString& new_text) {
|
|||
QString edit_filter_text = new_text.toLower();
|
||||
QStandardItem* folder;
|
||||
int children_total = 0;
|
||||
int result_count = 0;
|
||||
|
||||
auto hide = [this](int row, bool hidden, QModelIndex index = QModelIndex()) {
|
||||
if (m_isTreeMode) {
|
||||
tree_view->setRowHidden(row, index, hidden);
|
||||
} else {
|
||||
list_view->setRowHidden(row, hidden);
|
||||
}
|
||||
};
|
||||
|
||||
// If the searchfield is empty every item is visible
|
||||
// Otherwise the filter gets applied
|
||||
if (edit_filter_text.isEmpty()) {
|
||||
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
|
||||
UISettings::values.favorited_ids.size() == 0);
|
||||
|
||||
// TODO(crueter) dedupe
|
||||
if (!m_isTreeMode) {
|
||||
int row_count = item_model->rowCount();
|
||||
|
||||
for (int i = 0; i < row_count; ++i) {
|
||||
QStandardItem* item = item_model->item(i, 0);
|
||||
if (!item) continue;
|
||||
|
||||
children_total++;
|
||||
|
||||
const QString file_path = item->data(GameListItemPath::FullPathRole).toString().toLower();
|
||||
const QString file_title = item->data(GameListItemPath::TitleRole).toString().toLower();
|
||||
const QString file_name = file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) +
|
||||
QLatin1Char{' '} + file_title;
|
||||
|
||||
if (edit_filter_text.isEmpty() || ContainsAllWords(file_name, edit_filter_text)) {
|
||||
hide(i, false);
|
||||
result_count++;
|
||||
} else {
|
||||
hide(i, true);
|
||||
}
|
||||
}
|
||||
search_field->setFilterResult(result_count, children_total);
|
||||
} else if (edit_filter_text.isEmpty()) {
|
||||
hide(0, UISettings::values.favorited_ids.size() == 0, item_model->invisibleRootItem()->index());
|
||||
for (int i = 1; i < item_model->rowCount() - 1; ++i) {
|
||||
folder = item_model->item(i, 0);
|
||||
const QModelIndex folder_index = folder->index();
|
||||
const int children_count = folder->rowCount();
|
||||
for (int j = 0; j < children_count; ++j) {
|
||||
++children_total;
|
||||
tree_view->setRowHidden(j, folder_index, false);
|
||||
hide(j, false, folder_index);
|
||||
}
|
||||
}
|
||||
search_field->setFilterResult(children_total, children_total);
|
||||
} else {
|
||||
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true);
|
||||
int result_count = 0;
|
||||
hide(0, true, item_model->invisibleRootItem()->index());
|
||||
for (int i = 1; i < item_model->rowCount() - 1; ++i) {
|
||||
folder = item_model->item(i, 0);
|
||||
const QModelIndex folder_index = folder->index();
|
||||
|
|
@ -245,10 +277,10 @@ void GameList::OnTextChanged(const QString& new_text) {
|
|||
file_title;
|
||||
if (ContainsAllWords(file_name, edit_filter_text) ||
|
||||
(file_program_id.size() == 16 && file_program_id.contains(edit_filter_text))) {
|
||||
tree_view->setRowHidden(j, folder_index, false);
|
||||
hide(j, false, folder_index);
|
||||
++result_count;
|
||||
} else {
|
||||
tree_view->setRowHidden(j, folder_index, true);
|
||||
hide(j, true, folder_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -334,28 +366,21 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
|
|||
|
||||
this->main_window = parent;
|
||||
layout = new QVBoxLayout;
|
||||
tree_view = new QTreeView;
|
||||
tree_view = new QTreeView(this);
|
||||
list_view = new QListView(this);
|
||||
m_gameCard = new GameCard(this);
|
||||
|
||||
list_view->setItemDelegate(m_gameCard);
|
||||
|
||||
controller_navigation = new ControllerNavigation(system.HIDCore(), this);
|
||||
search_field = new GameListSearchField(this);
|
||||
item_model = new QStandardItemModel(tree_view);
|
||||
tree_view->setModel(item_model);
|
||||
list_view->setModel(item_model);
|
||||
|
||||
SetupScrollAnimation();
|
||||
tree_view->viewport()->installEventFilter(this);
|
||||
|
||||
// touch gestures
|
||||
tree_view->viewport()->grabGesture(Qt::SwipeGesture);
|
||||
tree_view->viewport()->grabGesture(Qt::PanGesture);
|
||||
|
||||
// TODO: touch?
|
||||
QScroller::grabGesture(tree_view->viewport(), QScroller::LeftMouseButtonGesture);
|
||||
|
||||
auto scroller = QScroller::scroller(tree_view->viewport());
|
||||
QScrollerProperties props;
|
||||
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
|
||||
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
|
||||
scroller->setScrollerProperties(props);
|
||||
|
||||
// tree
|
||||
tree_view->setAlternatingRowColors(true);
|
||||
tree_view->setSelectionMode(QHeaderView::SingleSelection);
|
||||
tree_view->setSelectionBehavior(QHeaderView::SelectRows);
|
||||
|
|
@ -367,6 +392,24 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
|
|||
tree_view->setAttribute(Qt::WA_AcceptTouchEvents, true);
|
||||
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->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
list_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
list_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
list_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
list_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
list_view->setGridSize(QSize(140, 160));
|
||||
m_gameCard->setSize(list_view->gridSize());
|
||||
|
||||
list_view->setSpacing(10);
|
||||
list_view->setWordWrap(true);
|
||||
list_view->setTextElideMode(Qt::ElideRight);
|
||||
list_view->setFlow(QListView::LeftToRight);
|
||||
list_view->setWrapping(true);
|
||||
|
||||
item_model->insertColumns(0, COLUMN_COUNT);
|
||||
RetranslateUI();
|
||||
|
||||
|
|
@ -376,8 +419,13 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
|
|||
item_model->setSortRole(GameListItemPath::SortRole);
|
||||
|
||||
connect(main_window, &MainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
|
||||
|
||||
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
|
||||
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
||||
|
||||
connect(list_view, &QListView::activated, this, &GameList::ValidateEntry);
|
||||
connect(list_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
||||
|
||||
connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
|
||||
connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
|
||||
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, this,
|
||||
|
|
@ -391,6 +439,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
|
|||
}
|
||||
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(tree_view, event);
|
||||
QCoreApplication::postEvent(list_view, event);
|
||||
});
|
||||
|
||||
// We must register all custom types with the Qt Automoc system so that we are able to use
|
||||
|
|
@ -401,14 +450,70 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
|
|||
layout->setSpacing(0);
|
||||
|
||||
layout->addWidget(tree_view);
|
||||
layout->addWidget(list_view);
|
||||
layout->addWidget(search_field);
|
||||
setLayout(layout);
|
||||
|
||||
ResetViewMode();
|
||||
}
|
||||
|
||||
void GameList::UnloadController() {
|
||||
controller_navigation->UnloadController();
|
||||
}
|
||||
|
||||
bool GameList::IsTreeMode() {
|
||||
return m_isTreeMode;
|
||||
}
|
||||
|
||||
void GameList::ResetViewMode() {
|
||||
auto &setting = UISettings::values.game_list_mode;
|
||||
bool newTreeMode = false;
|
||||
|
||||
switch (setting.GetValue()) {
|
||||
case Settings::GameListMode::TreeView:
|
||||
m_currentView = tree_view;
|
||||
newTreeMode = true;
|
||||
|
||||
tree_view->setVisible(true);
|
||||
list_view->setVisible(false);
|
||||
break;
|
||||
case Settings::GameListMode::GridView:
|
||||
m_currentView = list_view;
|
||||
newTreeMode = false;
|
||||
|
||||
list_view->setVisible(true);
|
||||
tree_view->setVisible(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_isTreeMode != newTreeMode) {
|
||||
m_isTreeMode = newTreeMode;
|
||||
|
||||
auto view = m_currentView->viewport();
|
||||
|
||||
view->installEventFilter(this);
|
||||
|
||||
// touch gestures
|
||||
view->grabGesture(Qt::SwipeGesture);
|
||||
view->grabGesture(Qt::PanGesture);
|
||||
|
||||
// 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);
|
||||
|
||||
RefreshGameDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
GameList::~GameList() {
|
||||
UnloadController();
|
||||
}
|
||||
|
|
@ -432,13 +537,19 @@ void GameList::WorkerEvent() {
|
|||
}
|
||||
|
||||
void GameList::AddDirEntry(GameListDir* entry_items) {
|
||||
if (m_isTreeMode) {
|
||||
item_model->invisibleRootItem()->appendRow(entry_items);
|
||||
tree_view->setExpanded(
|
||||
entry_items->index(),
|
||||
UISettings::values.game_dirs[entry_items->data(GameListDir::GameDirRole).toInt()].expanded);
|
||||
UISettings::values.game_dirs[entry_items->data(GameListDir::GameDirRole).toInt()]
|
||||
.expanded);
|
||||
}
|
||||
}
|
||||
|
||||
void GameList::AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent) {
|
||||
if (!m_isTreeMode)
|
||||
item_model->invisibleRootItem()->appendRow(entry_items);
|
||||
else
|
||||
parent->appendRow(entry_items);
|
||||
}
|
||||
|
||||
|
|
@ -497,9 +608,10 @@ bool GameList::IsEmpty() const {
|
|||
void GameList::DonePopulating(const QStringList& watch_list) {
|
||||
emit ShowList(!IsEmpty());
|
||||
|
||||
// Add favorites row
|
||||
if (m_isTreeMode) {
|
||||
item_model->invisibleRootItem()->appendRow(new GameListAddDir());
|
||||
|
||||
// Add favorites row
|
||||
item_model->invisibleRootItem()->insertRow(0, new GameListFavorites());
|
||||
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
|
||||
UISettings::values.favorited_ids.size() == 0);
|
||||
|
|
@ -508,6 +620,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
|
|||
for (const auto id : std::as_const(UISettings::values.favorited_ids)) {
|
||||
AddFavorite(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out the old directories to watch for changes and add the new ones
|
||||
auto watch_dirs = watcher->directories();
|
||||
|
|
@ -538,7 +651,8 @@ void GameList::DonePopulating(const QStringList& watch_list) {
|
|||
#ifdef __APPLE__
|
||||
watcher->blockSignals(old_signals_blocked);
|
||||
#endif
|
||||
tree_view->setEnabled(true);
|
||||
m_currentView->setEnabled(true);
|
||||
|
||||
int children_total = 0;
|
||||
for (int i = 1; i < item_model->rowCount() - 1; ++i) {
|
||||
children_total += item_model->item(i, 0)->rowCount();
|
||||
|
|
@ -554,10 +668,19 @@ void GameList::DonePopulating(const QStringList& watch_list) {
|
|||
}
|
||||
|
||||
void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||
QModelIndex item = tree_view->indexAt(menu_location);
|
||||
if (!item.isValid())
|
||||
QModelIndex item = m_currentView->indexAt(menu_location);
|
||||
if (!item.isValid()) {
|
||||
if (m_isTreeMode)
|
||||
return;
|
||||
|
||||
QMenu blank_menu;
|
||||
QAction *addGameDirAction = blank_menu.addAction(tr("&Add New Game Directory"));
|
||||
|
||||
connect(addGameDirAction, &QAction::triggered, this, &GameList::AddDirectory);
|
||||
blank_menu.exec(m_currentView->viewport()->mapToGlobal(menu_location));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto selected = item.sibling(item.row(), 0);
|
||||
QMenu context_menu;
|
||||
switch (selected.data(GameListItem::TypeRole).value<GameListItemType>()) {
|
||||
|
|
@ -580,7 +703,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
|
||||
context_menu.exec(m_currentView->viewport()->mapToGlobal(menu_location));
|
||||
}
|
||||
|
||||
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) {
|
||||
|
|
@ -641,7 +764,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
|||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
|
||||
|
||||
connect(favorite, &QAction::triggered, this, [this, program_id]() { ToggleFavorite(program_id); });
|
||||
connect(favorite, &QAction::triggered, this,
|
||||
[this, program_id]() { ToggleFavorite(program_id); });
|
||||
connect(open_save_location, &QAction::triggered, this, [this, program_id, path]() {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
|
||||
});
|
||||
|
|
@ -661,26 +785,32 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
|||
emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Update);
|
||||
});
|
||||
connect(remove_dlc, &QAction::triggered, this, [this, program_id]() {
|
||||
emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::AddOnContent);
|
||||
emit RemoveInstalledEntryRequested(program_id,
|
||||
QtCommon::Game::InstalledEntryType::AddOnContent);
|
||||
});
|
||||
connect(remove_gl_shader_cache, &QAction::triggered, this, [this, program_id, path]() {
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::GlShaderCache, path);
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::GlShaderCache,
|
||||
path);
|
||||
});
|
||||
connect(remove_vk_shader_cache, &QAction::triggered, this, [this, program_id, path]() {
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::VkShaderCache, path);
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::VkShaderCache,
|
||||
path);
|
||||
});
|
||||
connect(remove_shader_cache, &QAction::triggered, this, [this, program_id, path]() {
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::AllShaderCache, path);
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::AllShaderCache,
|
||||
path);
|
||||
});
|
||||
connect(remove_custom_config, &QAction::triggered, this, [this, program_id, path]() {
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path);
|
||||
emit RemoveFileRequested(program_id,
|
||||
QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path);
|
||||
});
|
||||
connect(set_play_time, &QAction::triggered, this,
|
||||
[this, program_id]() { emit SetPlayTimeRequested(program_id); });
|
||||
connect(remove_play_time_data, &QAction::triggered, this,
|
||||
[this, program_id]() { emit RemovePlayTimeRequested(program_id); });
|
||||
connect(remove_cache_storage, &QAction::triggered, this, [this, program_id, path] {
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage, path);
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage,
|
||||
path);
|
||||
});
|
||||
connect(dump_romfs, &QAction::triggered, this, [this, program_id, path]() {
|
||||
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal);
|
||||
|
|
@ -700,15 +830,16 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
|||
connect(create_desktop_shortcut, &QAction::triggered, this, [this, program_id, path]() {
|
||||
emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Desktop);
|
||||
});
|
||||
connect(create_applications_menu_shortcut, &QAction::triggered, this, [this, program_id, path]() {
|
||||
connect(create_applications_menu_shortcut, &QAction::triggered, this,
|
||||
[this, program_id, path]() {
|
||||
emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Applications);
|
||||
});
|
||||
#endif
|
||||
connect(properties, &QAction::triggered, this,
|
||||
[this, path]() { emit OpenPerGameGeneralRequested(path); });
|
||||
|
||||
connect(ryujinx, &QAction::triggered, this, [this, program_id]() { emit LinkToRyujinxRequested(program_id);
|
||||
});
|
||||
connect(ryujinx, &QAction::triggered, this,
|
||||
[this, program_id]() { emit LinkToRyujinxRequested(program_id); });
|
||||
};
|
||||
|
||||
void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
|
||||
|
|
@ -872,9 +1003,38 @@ QStandardItemModel* GameList::GetModel() const {
|
|||
return item_model;
|
||||
}
|
||||
|
||||
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs)
|
||||
{
|
||||
tree_view->setEnabled(false);
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO(crueter): Auto size
|
||||
list_view->setGridSize(QSize(icon_size + widthMargin, icon_size + heightMargin));
|
||||
m_gameCard->setSize(list_view->gridSize());
|
||||
}
|
||||
|
||||
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
|
||||
m_currentView->setEnabled(false);
|
||||
|
||||
// Update the columns in case UISettings has changed
|
||||
tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
|
||||
|
|
@ -883,6 +1043,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();
|
||||
|
||||
// Cancel any existing worker.
|
||||
current_worker.reset();
|
||||
|
||||
|
|
@ -890,12 +1052,8 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs)
|
|||
item_model->removeRows(0, item_model->rowCount());
|
||||
search_field->clear();
|
||||
|
||||
current_worker = std::make_unique<GameListWorker>(vfs,
|
||||
provider,
|
||||
game_dirs,
|
||||
compatibility_list,
|
||||
play_time_manager,
|
||||
system);
|
||||
current_worker = std::make_unique<GameListWorker>(vfs, provider, game_dirs, compatibility_list,
|
||||
play_time_manager, system);
|
||||
|
||||
// Get events from the worker as data becomes available
|
||||
connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent,
|
||||
|
|
@ -1048,9 +1206,8 @@ void GameList::SetupScrollAnimation() {
|
|||
// animation handles moving the bar instead of Qt's built in crap
|
||||
anim->setEasingCurve(QEasingCurve::OutCubic);
|
||||
anim->setDuration(200);
|
||||
connect(anim, &QVariantAnimation::valueChanged, this, [bar](const QVariant& value) {
|
||||
bar->setValue(value.toInt());
|
||||
});
|
||||
connect(anim, &QVariantAnimation::valueChanged, this,
|
||||
[bar](const QVariant& value) { bar->setValue(value.toInt()); });
|
||||
};
|
||||
|
||||
vertical_scroll = new QVariantAnimation(this);
|
||||
|
|
@ -1058,10 +1215,13 @@ void GameList::SetupScrollAnimation() {
|
|||
|
||||
setup(vertical_scroll, tree_view->verticalScrollBar());
|
||||
setup(horizontal_scroll, tree_view->horizontalScrollBar());
|
||||
|
||||
setup(vertical_scroll, list_view->verticalScrollBar());
|
||||
setup(horizontal_scroll, list_view->horizontalScrollBar());
|
||||
}
|
||||
|
||||
bool GameList::eventFilter(QObject* obj, QEvent* event) {
|
||||
if (obj == tree_view->viewport() && event->type() == QEvent::Wheel) {
|
||||
if (obj == m_currentView->viewport() && event->type() == QEvent::Wheel) {
|
||||
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
|
||||
|
||||
bool horizontal = wheelEvent->modifiers() & Qt::ShiftModifier;
|
||||
|
|
@ -1078,26 +1238,28 @@ bool GameList::eventFilter(QObject* obj, QEvent* event) {
|
|||
// TODO(crueter): dedup this
|
||||
if (deltaY != 0) {
|
||||
if (vertical_scroll->state() == QAbstractAnimation::Stopped)
|
||||
vertical_scroll_target = tree_view->verticalScrollBar()->value();
|
||||
vertical_scroll_target = m_currentView->verticalScrollBar()->value();
|
||||
|
||||
vertical_scroll_target -= deltaY;
|
||||
vertical_scroll_target = qBound(0, vertical_scroll_target, tree_view->verticalScrollBar()->maximum());
|
||||
vertical_scroll_target =
|
||||
qBound(0, vertical_scroll_target, m_currentView->verticalScrollBar()->maximum());
|
||||
|
||||
vertical_scroll->stop();
|
||||
vertical_scroll->setStartValue(tree_view->verticalScrollBar()->value());
|
||||
vertical_scroll->setStartValue(m_currentView->verticalScrollBar()->value());
|
||||
vertical_scroll->setEndValue(vertical_scroll_target);
|
||||
vertical_scroll->start();
|
||||
}
|
||||
|
||||
if (deltaX != 0) {
|
||||
if (horizontal_scroll->state() == QAbstractAnimation::Stopped)
|
||||
horizontal_scroll_target = tree_view->horizontalScrollBar()->value();
|
||||
horizontal_scroll_target = m_currentView->horizontalScrollBar()->value();
|
||||
|
||||
horizontal_scroll_target -= deltaX;
|
||||
horizontal_scroll_target = qBound(0, horizontal_scroll_target, tree_view->horizontalScrollBar()->maximum());
|
||||
horizontal_scroll_target =
|
||||
qBound(0, horizontal_scroll_target, m_currentView->horizontalScrollBar()->maximum());
|
||||
|
||||
horizontal_scroll->stop();
|
||||
horizontal_scroll->setStartValue(tree_view->horizontalScrollBar()->value());
|
||||
horizontal_scroll->setStartValue(m_currentView->horizontalScrollBar()->value());
|
||||
horizontal_scroll->setEndValue(horizontal_scroll_target);
|
||||
horizontal_scroll->start();
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QVector>
|
||||
#include <QWidget>
|
||||
#include <qabstractitemview.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
|
|
@ -26,6 +27,10 @@
|
|||
#include "frontend_common/play_time_manager.h"
|
||||
|
||||
class QVariantAnimation;
|
||||
|
||||
class QListView;
|
||||
|
||||
class GameCard;
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
|
@ -92,6 +97,9 @@ public:
|
|||
|
||||
static const QStringList supported_file_extensions;
|
||||
|
||||
bool IsTreeMode();
|
||||
void ResetViewMode();
|
||||
|
||||
public slots:
|
||||
void RefreshGameDirectory();
|
||||
void RefreshExternalContent();
|
||||
|
|
@ -129,6 +137,8 @@ private slots:
|
|||
void OnFilterCloseClicked();
|
||||
void OnUpdateThemedIcons();
|
||||
|
||||
void UpdateIconSize();
|
||||
|
||||
private:
|
||||
friend class GameListWorker;
|
||||
void WorkerEvent();
|
||||
|
|
@ -158,7 +168,11 @@ private:
|
|||
GameListSearchField* search_field;
|
||||
MainWindow* main_window = nullptr;
|
||||
QVBoxLayout* layout = nullptr;
|
||||
|
||||
QTreeView* tree_view = nullptr;
|
||||
QListView *list_view = nullptr;
|
||||
GameCard *m_gameCard = nullptr;
|
||||
|
||||
QStandardItemModel* item_model = nullptr;
|
||||
std::unique_ptr<GameListWorker> current_worker;
|
||||
QFileSystemWatcher* watcher = nullptr;
|
||||
|
|
@ -178,6 +192,9 @@ private:
|
|||
|
||||
const PlayTime::PlayTimeManager& play_time_manager;
|
||||
Core::System& system;
|
||||
|
||||
bool m_isTreeMode = true;
|
||||
QAbstractItemView *m_currentView = tree_view;
|
||||
};
|
||||
|
||||
class GameListPlaceholder : public QWidget {
|
||||
|
|
@ -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
|
||||
|
||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
||||
|
|
@ -75,13 +75,17 @@ public:
|
|||
|
||||
GameListItemPath() = default;
|
||||
GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
|
||||
const QString& game_name, const QString& game_type, u64 program_id) {
|
||||
const QString& game_name, const QString& game_type, u64 program_id,
|
||||
u64 play_time) {
|
||||
setData(type(), TypeRole);
|
||||
setData(game_path, FullPathRole);
|
||||
setData(game_name, TitleRole);
|
||||
setData(qulonglong(program_id), ProgramIdRole);
|
||||
setData(game_type, FileTypeRole);
|
||||
|
||||
setData(QString::fromStdString(PlayTime::PlayTimeManager::GetReadablePlayTime(play_time)),
|
||||
Qt::ToolTipRole);
|
||||
|
||||
const u32 size = UISettings::values.game_icon_size.GetValue();
|
||||
|
||||
QPixmap picture;
|
||||
|
|
@ -111,6 +115,10 @@ public:
|
|||
}};
|
||||
|
||||
const auto& row1 = row_data.at(UISettings::values.row_1_text_id.GetValue());
|
||||
// don't show row 2 on grid view
|
||||
switch (UISettings::values.game_list_mode.GetValue()) {
|
||||
|
||||
case Settings::GameListMode::TreeView: {
|
||||
const int row2_id = UISettings::values.row_2_text_id.GetValue();
|
||||
|
||||
if (role == SortRole) {
|
||||
|
|
@ -130,6 +138,13 @@ public:
|
|||
|
||||
return QStringLiteral("%1\n %2").arg(row1, row2);
|
||||
}
|
||||
case Settings::GameListMode::GridView:
|
||||
return row1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return GameListItem::data(role);
|
||||
}
|
||||
|
|
@ -241,7 +256,9 @@ public:
|
|||
|
||||
void setData(const QVariant& value, int role) override {
|
||||
qulonglong time_seconds = value.toULongLong();
|
||||
GameListItem::setData(QString::fromStdString(PlayTime::PlayTimeManager::GetReadablePlayTime(time_seconds)), Qt::DisplayRole);
|
||||
GameListItem::setData(
|
||||
QString::fromStdString(PlayTime::PlayTimeManager::GetReadablePlayTime(time_seconds)),
|
||||
Qt::DisplayRole);
|
||||
GameListItem::setData(value, PlayTimeRole);
|
||||
}
|
||||
|
||||
|
|
@ -27,9 +27,9 @@
|
|||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/game_list.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/game_list_worker.h"
|
||||
#include "yuzu/game/game_list.h"
|
||||
#include "yuzu/game/game_list_p.h"
|
||||
#include "yuzu/game/game_list_worker.h"
|
||||
#include "qt_common/config/uisettings.h"
|
||||
|
||||
namespace {
|
||||
|
|
@ -214,11 +214,14 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path,
|
|||
QString patch_versions = GetGameListCachedObject(fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
|
||||
return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
|
||||
});
|
||||
|
||||
u64 play_time = play_time_manager.GetPlayTime(program_id);
|
||||
return QList<QStandardItem*>{
|
||||
new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), file_type_string, program_id),
|
||||
new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name),
|
||||
file_type_string, program_id, play_time),
|
||||
new GameListItem(file_type_string),
|
||||
new GameListItemSize(size),
|
||||
new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)),
|
||||
new GameListItemPlayTime(play_time),
|
||||
new GameListItem(patch_versions),
|
||||
new GameListItemCompat(compatibility),
|
||||
};
|
||||
|
|
@ -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
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -105,6 +105,13 @@
|
|||
<string>&Debugging</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Game_List_Mode">
|
||||
<property name="title">
|
||||
<string>&Game List Mode</string>
|
||||
</property>
|
||||
<addaction name="action_Tree_View"/>
|
||||
<addaction name="action_Grid_View"/>
|
||||
</widget>
|
||||
<action name="action_Reset_Window_Size_720">
|
||||
<property name="text">
|
||||
<string>Reset Window Size to &720p</string>
|
||||
|
|
@ -137,6 +144,7 @@
|
|||
<addaction name="separator"/>
|
||||
<addaction name="menu_Reset_Window_Size"/>
|
||||
<addaction name="menu_View_Debugging"/>
|
||||
<addaction name="menu_Game_List_Mode"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Multiplayer">
|
||||
<property name="enabled">
|
||||
|
|
@ -562,6 +570,22 @@
|
|||
<string>&Data Manager</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Tree_View">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Tree View</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Grid_View">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Grid View</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="yuzu.qrc"/>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
// Qt on macOS doesn't define VMA shit
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include "common/settings_enums.h"
|
||||
#include "frontend_common/settings_generator.h"
|
||||
#include "qt_common/qt_string_lookup.h"
|
||||
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
||||
|
|
@ -25,7 +26,7 @@
|
|||
#include "install_dialog.h"
|
||||
|
||||
#include "bootmanager.h"
|
||||
#include "game_list.h"
|
||||
#include "yuzu/game/game_list.h"
|
||||
#include "loading_screen.h"
|
||||
#include "ryujinx_dialog.h"
|
||||
#include "set_play_time_dialog.h"
|
||||
|
|
@ -550,6 +551,9 @@ MainWindow::MainWindow(bool has_broken_vulkan)
|
|||
game_list->LoadCompatibilityList();
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
|
||||
// Set up game list mode checkboxes.
|
||||
SetGameListMode(UISettings::values.game_list_mode.GetValue());
|
||||
|
||||
// make sure menubar has the arrow cursor instead of inheriting from this
|
||||
ui->menubar->setCursor(QCursor());
|
||||
statusBar()->setCursor(QCursor());
|
||||
|
|
@ -1600,6 +1604,9 @@ void MainWindow::ConnectMenuEvents() {
|
|||
ui->action_Reset_Window_Size_900,
|
||||
ui->action_Reset_Window_Size_1080});
|
||||
|
||||
connect_menu(ui->action_Grid_View, &MainWindow::SetGridView);
|
||||
connect_menu(ui->action_Tree_View, &MainWindow::SetTreeView);
|
||||
|
||||
// Multiplayer
|
||||
connect(ui->action_View_Lobby, &QAction::triggered, multiplayer_state,
|
||||
&MultiplayerState::OnViewLobby);
|
||||
|
|
@ -3373,6 +3380,22 @@ void MainWindow::ResetWindowSize1080() {
|
|||
ResetWindowSize(Layout::ScreenDocked::Width, Layout::ScreenDocked::Height);
|
||||
}
|
||||
|
||||
void MainWindow::SetGameListMode(Settings::GameListMode mode) {
|
||||
ui->action_Grid_View->setChecked(mode == Settings::GameListMode::GridView);
|
||||
ui->action_Tree_View->setChecked(mode == Settings::GameListMode::TreeView);
|
||||
|
||||
UISettings::values.game_list_mode = mode;
|
||||
game_list->ResetViewMode();
|
||||
}
|
||||
|
||||
void MainWindow::SetGridView() {
|
||||
SetGameListMode(Settings::GameListMode::GridView);
|
||||
}
|
||||
|
||||
void MainWindow::SetTreeView() {
|
||||
SetGameListMode(Settings::GameListMode::TreeView);
|
||||
}
|
||||
|
||||
void MainWindow::OnConfigure() {
|
||||
const auto old_theme = UISettings::values.theme;
|
||||
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QTranslator>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "frontend_common/content_manager.h"
|
||||
#include "frontend_common/update_checker.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
|
|
@ -401,6 +402,11 @@ private slots:
|
|||
void ResetWindowSize720();
|
||||
void ResetWindowSize900();
|
||||
void ResetWindowSize1080();
|
||||
|
||||
void SetGameListMode(Settings::GameListMode mode);
|
||||
void SetGridView();
|
||||
void SetTreeView();
|
||||
|
||||
void LaunchFirmwareApplet(u64 program_id, std::optional<Service::NFP::CabinetMode> mode);
|
||||
void OnCreateHomeMenuDesktopShortcut();
|
||||
void OnCreateHomeMenuApplicationMenuShortcut();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "network/announce_multiplayer_session.h"
|
||||
#include "ui_chat_room.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/game/game_list_p.h"
|
||||
#include "yuzu/multiplayer/chat_room.h"
|
||||
#include "yuzu/multiplayer/message.h"
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "network/announce_multiplayer_session.h"
|
||||
#include "ui_client_room.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/game/game_list_p.h"
|
||||
#include "yuzu/multiplayer/client_room.h"
|
||||
#include "yuzu/multiplayer/message.h"
|
||||
#include "yuzu/multiplayer/moderation_dialog.h"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
#include "core/internal_network/network_interface.h"
|
||||
#include "network/announce_multiplayer_session.h"
|
||||
#include "ui_host_room.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/game/game_list_p.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/multiplayer/host_room.h"
|
||||
#include "yuzu/multiplayer/message.h"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
#include "core/internal_network/network_interface.h"
|
||||
#include "network/network.h"
|
||||
#include "ui_lobby.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/game/game_list_p.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/multiplayer/client_room.h"
|
||||
#include "yuzu/multiplayer/lobby.h"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
#include "common/announce_multiplayer_room.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "yuzu/game_list.h"
|
||||
#include "yuzu/game/game_list.h"
|
||||
#include "yuzu/multiplayer/client_room.h"
|
||||
#include "yuzu/multiplayer/direct_connect.h"
|
||||
#include "yuzu/multiplayer/host_room.h"
|
||||
|
|
|
|||
Loading…
Reference in New Issue