mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-06-28 07:35:34 +02:00
[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>
This commit is contained in:
parent
68aaea6085
commit
39be450fa3
16 changed files with 300 additions and 45 deletions
|
|
@ -158,7 +158,7 @@ ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High)
|
||||||
ENUM(TemperatureUnits, Celsius, Fahrenheit)
|
ENUM(TemperatureUnits, Celsius, Fahrenheit)
|
||||||
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
|
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
|
||||||
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
|
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
|
||||||
ENUM(GameListMode, TreeView, GridView);
|
ENUM(GameListMode, TreeView, GridView, CarouselView);
|
||||||
ENUM(SpeedMode, Standard, Turbo, Slow);
|
ENUM(SpeedMode, Standard, Turbo, Slow);
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ struct Values {
|
||||||
|
|
||||||
// Game List
|
// Game List
|
||||||
Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
|
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<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_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<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ public:
|
||||||
return QStringLiteral("%1\n %2").arg(row1, row2);
|
return QStringLiteral("%1\n %2").arg(row1, row2);
|
||||||
}
|
}
|
||||||
case Settings::GameListMode::GridView:
|
case Settings::GameListMode::GridView:
|
||||||
|
case Settings::GameListMode::CarouselView:
|
||||||
return row1;
|
return row1;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,9 @@ add_executable(yuzu
|
||||||
|
|
||||||
render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui
|
render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui
|
||||||
updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.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")
|
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
||||||
|
|
||||||
|
|
|
||||||
107
src/yuzu/game/carousel.cpp
Normal file
107
src/yuzu/game/carousel.cpp
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
27
src/yuzu/game/carousel.h
Normal file
27
src/yuzu/game/carousel.h
Normal file
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
@ -18,27 +18,16 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
|
||||||
painter->save();
|
painter->save();
|
||||||
painter->setRenderHint(QPainter::Antialiasing);
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
constexpr int cardMargin = 8;
|
|
||||||
constexpr int cardCornerRadius = 10;
|
constexpr int cardCornerRadius = 10;
|
||||||
|
|
||||||
const int column = index.row() % m_columns;
|
const QRect cardRect = getCardRect(option, index);
|
||||||
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);
|
|
||||||
|
|
||||||
QPalette palette = option.palette;
|
QPalette palette = option.palette;
|
||||||
QColor backgroundColor = palette.window().color();
|
QColor backgroundColor = palette.window().color();
|
||||||
QColor borderColor = palette.dark().color();
|
QColor borderColor = palette.dark().color();
|
||||||
QColor textColor = palette.text().color();
|
QColor textColor = palette.text().color();
|
||||||
|
|
||||||
// highlight blue on select
|
// highlight on select or hover
|
||||||
if (option.state & QStyle::State_Selected) {
|
if (option.state & QStyle::State_Selected) {
|
||||||
backgroundColor = palette.highlight().color();
|
backgroundColor = palette.highlight().color();
|
||||||
borderColor = palette.highlight().color().lighter(150);
|
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);
|
scaled.scale(icon_size, icon_size, Qt::KeepAspectRatio);
|
||||||
|
|
||||||
iconRect = {cardRect.left() + (cardRect.width() - scaled.width()) / 2,
|
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);
|
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||||
|
|
||||||
|
|
@ -94,12 +83,62 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
|
||||||
painter->restore();
|
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 {
|
QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
|
||||||
return m_size;
|
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_size = newSize;
|
||||||
|
m_contentSize = contentSize;
|
||||||
m_padding = padding;
|
m_padding = padding;
|
||||||
m_columns = columns;
|
m_columns = columns;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,19 @@ public:
|
||||||
void paint(QPainter* painter, const QStyleOptionViewItem& option,
|
void paint(QPainter* painter, const QStyleOptionViewItem& option,
|
||||||
const QModelIndex& index) const override;
|
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;
|
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:
|
private:
|
||||||
|
static constexpr int cardMargin = 8;
|
||||||
|
|
||||||
QSize m_size;
|
QSize m_size;
|
||||||
|
QSize m_contentSize;
|
||||||
int m_padding;
|
int m_padding;
|
||||||
int m_columns;
|
int m_columns;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ GameGrid::GameGrid(QWidget* parent) : QListView{parent} {
|
||||||
|
|
||||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
setGridSize(QSize(140, 160));
|
|
||||||
m_gameCard->setSize(gridSize(), 0, 4);
|
|
||||||
|
|
||||||
setSpacing(10);
|
setSpacing(10);
|
||||||
setWordWrap(true);
|
setWordWrap(true);
|
||||||
|
|
@ -97,8 +95,17 @@ void GameGrid::UpdateIconSize() {
|
||||||
setUpdatesEnabled(false);
|
setUpdatesEnabled(false);
|
||||||
|
|
||||||
setGridSize(grid_size);
|
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);
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
class GameCard;
|
class GameCard;
|
||||||
class GameListModel;
|
class GameListModel;
|
||||||
|
|
||||||
|
|
@ -21,6 +19,8 @@ public:
|
||||||
void ApplyFilter(const QString& edit_filter_text, GameListModel* model);
|
void ApplyFilter(const QString& edit_filter_text, GameListModel* model);
|
||||||
void UpdateIconSize();
|
void UpdateIconSize();
|
||||||
|
|
||||||
|
QModelIndex indexAt(const QPoint& point) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GameCard* m_gameCard = nullptr;
|
GameCard* m_gameCard = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include <QScrollerProperties>
|
#include <QScrollerProperties>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVariantAnimation>
|
#include <QVariantAnimation>
|
||||||
|
#include <qlayoutitem.h>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
@ -31,6 +32,7 @@
|
||||||
#include "qt_common/qt_common.h"
|
#include "qt_common/qt_common.h"
|
||||||
#include "qt_common/util/game.h"
|
#include "qt_common/util/game.h"
|
||||||
#include "yuzu/compatibility_list.h"
|
#include "yuzu/compatibility_list.h"
|
||||||
|
#include "yuzu/game/carousel.h"
|
||||||
#include "yuzu/game/game_grid.h"
|
#include "yuzu/game/game_grid.h"
|
||||||
#include "yuzu/game/game_list.h"
|
#include "yuzu/game/game_list.h"
|
||||||
#include "yuzu/game/game_tree.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::activated, this, &GameList::ValidateEntry);
|
||||||
connect(grid_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
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,
|
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, this,
|
||||||
[this](Qt::Key key) {
|
[this](Qt::Key key) {
|
||||||
if (system.IsPoweredOn()) {
|
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::SaveConfig, this, &GameList::SaveConfig);
|
||||||
connect(item_model, &GameListModel::PopulatingStarted, this, &GameList::OnPopulate);
|
connect(item_model, &GameListModel::PopulatingStarted, this, &GameList::OnPopulate);
|
||||||
|
|
||||||
|
// TODO: impl on grid/carousel
|
||||||
connect(tree_view, &GameTree::FilterResultReady, search_field,
|
connect(tree_view, &GameTree::FilterResultReady, search_field,
|
||||||
[this](int visible, int total) { search_field->setFilterResult(visible, total); });
|
[this](int visible, int total) { search_field->setFilterResult(visible, total); });
|
||||||
|
|
||||||
|
|
@ -101,12 +107,11 @@ GameList::~GameList() {
|
||||||
void GameList::SetupViews() {
|
void GameList::SetupViews() {
|
||||||
tree_view = new GameTree(this);
|
tree_view = new GameTree(this);
|
||||||
grid_view = new GameGrid(this);
|
grid_view = new GameGrid(this);
|
||||||
|
carousel_view = new GameCarousel(this);
|
||||||
|
|
||||||
tree_view->SetModel(item_model);
|
tree_view->SetModel(item_model);
|
||||||
grid_view->SetModel(item_model);
|
grid_view->SetModel(item_model);
|
||||||
|
carousel_view->SetModel(item_model);
|
||||||
layout->addWidget(tree_view);
|
|
||||||
layout->addWidget(grid_view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GameList::GetLastFilterResultItem() const {
|
QString GameList::GetLastFilterResultItem() const {
|
||||||
|
|
@ -138,10 +143,16 @@ void GameList::LoadCompatibilityList() {
|
||||||
void GameList::OnPopulate() {
|
void GameList::OnPopulate() {
|
||||||
m_currentView->setEnabled(false);
|
m_currentView->setEnabled(false);
|
||||||
|
|
||||||
if (m_isTreeMode) {
|
switch (game_list_mode) {
|
||||||
grid_view->UpdateIconSize();
|
case Settings::GameListMode::TreeView:
|
||||||
} else {
|
|
||||||
tree_view->UpdateColumnVisibility(item_model);
|
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() {
|
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;
|
bool newTreeMode = false;
|
||||||
|
|
||||||
switch (setting.GetValue()) {
|
switch (mode) {
|
||||||
case Settings::GameListMode::TreeView:
|
case Settings::GameListMode::TreeView:
|
||||||
m_currentView = tree_view;
|
m_currentView = tree_view;
|
||||||
newTreeMode = true;
|
newTreeMode = true;
|
||||||
tree_view->setVisible(true);
|
|
||||||
grid_view->setVisible(false);
|
|
||||||
break;
|
break;
|
||||||
case Settings::GameListMode::GridView:
|
case Settings::GameListMode::GridView:
|
||||||
m_currentView = grid_view;
|
m_currentView = grid_view;
|
||||||
newTreeMode = false;
|
newTreeMode = false;
|
||||||
grid_view->setVisible(true);
|
|
||||||
tree_view->setVisible(false);
|
break;
|
||||||
|
case Settings::GameListMode::CarouselView:
|
||||||
|
m_currentView = carousel_view;
|
||||||
|
newTreeMode = false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
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();
|
auto view = m_currentView->viewport();
|
||||||
view->installEventFilter(this);
|
view->installEventFilter(this);
|
||||||
|
|
||||||
|
|
@ -297,11 +328,13 @@ void GameList::OnPopulatingCompleted(const QStringList& watch_list) {
|
||||||
for (int i = 1; i < item_model->rowCount() - 1; ++i) {
|
for (int i = 1; i < item_model->rowCount() - 1; ++i) {
|
||||||
children_total += item_model->item(i, 0)->rowCount();
|
children_total += item_model->item(i, 0)->rowCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
search_field->setFilterResult(children_total, children_total);
|
search_field->setFilterResult(children_total, children_total);
|
||||||
if (children_total > 0) {
|
if (children_total > 0) {
|
||||||
search_field->setFocus();
|
search_field->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: carousel/grid impl.
|
||||||
item_model->sort(tree_view->header()->sortIndicatorSection(),
|
item_model->sort(tree_view->header()->sortIndicatorSection(),
|
||||||
tree_view->header()->sortIndicatorOrder());
|
tree_view->header()->sortIndicatorOrder());
|
||||||
|
|
||||||
|
|
@ -317,8 +350,15 @@ void GameList::RefreshExternalContent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::UpdateIconSizes() {
|
void GameList::UpdateIconSizes() {
|
||||||
if (!m_isTreeMode) {
|
switch (game_list_mode) {
|
||||||
|
case Settings::GameListMode::GridView:
|
||||||
grid_view->UpdateIconSize();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (obj == carousel_view->viewport() && event->type() == QEvent::Resize) {
|
||||||
|
carousel_view->UpdateIconSize();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return QWidget::eventFilter(obj, event);
|
return QWidget::eventFilter(obj, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <qabstractitemview.h>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "frontend_common/play_time_manager.h"
|
#include "frontend_common/play_time_manager.h"
|
||||||
#include "qt_common/config/uisettings.h"
|
#include "qt_common/config/uisettings.h"
|
||||||
|
#include "qt_common/game_list/model.h"
|
||||||
#include "qt_common/util/game.h"
|
#include "qt_common/util/game.h"
|
||||||
#include "yuzu/compatibility_list.h"
|
#include "yuzu/compatibility_list.h"
|
||||||
|
|
||||||
|
|
@ -38,6 +38,9 @@ class MainWindow;
|
||||||
enum class AmLaunchType;
|
enum class AmLaunchType;
|
||||||
enum class StartGameType;
|
enum class StartGameType;
|
||||||
|
|
||||||
|
class GameCarousel;
|
||||||
|
|
||||||
|
class QAbstractItemView;
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +154,9 @@ private:
|
||||||
|
|
||||||
GameTree* tree_view = nullptr;
|
GameTree* tree_view = nullptr;
|
||||||
GameGrid* grid_view = nullptr;
|
GameGrid* grid_view = nullptr;
|
||||||
|
GameCarousel* carousel_view = nullptr;
|
||||||
GameListModel* item_model = nullptr;
|
GameListModel* item_model = nullptr;
|
||||||
|
Settings::GameListMode game_list_mode;
|
||||||
|
|
||||||
ControllerNavigation* controller_navigation = nullptr;
|
ControllerNavigation* controller_navigation = nullptr;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QHeaderView>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
|
|
||||||
class GameListModel;
|
class GameListModel;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1280</width>
|
<width>1280</width>
|
||||||
<height>22</height>
|
<height>23</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menu_File">
|
<widget class="QMenu" name="menu_File">
|
||||||
|
|
@ -111,6 +111,7 @@
|
||||||
</property>
|
</property>
|
||||||
<addaction name="action_Tree_View"/>
|
<addaction name="action_Tree_View"/>
|
||||||
<addaction name="action_Grid_View"/>
|
<addaction name="action_Grid_View"/>
|
||||||
|
<addaction name="action_Carousel_View"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuGame_Icon_Size">
|
<widget class="QMenu" name="menuGame_Icon_Size">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
|
@ -620,6 +621,14 @@
|
||||||
<string>Show &Performance Overlay</string>
|
<string>Show &Performance Overlay</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Carousel_View">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Carousel View</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="yuzu.qrc"/>
|
<include location="yuzu.qrc"/>
|
||||||
|
|
|
||||||
|
|
@ -1529,6 +1529,7 @@ void MainWindow::ConnectMenuEvents() {
|
||||||
|
|
||||||
connect_menu(ui->action_Grid_View, &MainWindow::SetGridView);
|
connect_menu(ui->action_Grid_View, &MainWindow::SetGridView);
|
||||||
connect_menu(ui->action_Tree_View, &MainWindow::SetTreeView);
|
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 = new QActionGroup(this);
|
||||||
game_size_actions->setExclusive(true);
|
game_size_actions->setExclusive(true);
|
||||||
|
|
@ -3317,9 +3318,10 @@ void MainWindow::ResetWindowSize1080() {
|
||||||
void MainWindow::SetGameListMode(Settings::GameListMode mode) {
|
void MainWindow::SetGameListMode(Settings::GameListMode mode) {
|
||||||
ui->action_Grid_View->setChecked(mode == Settings::GameListMode::GridView);
|
ui->action_Grid_View->setChecked(mode == Settings::GameListMode::GridView);
|
||||||
ui->action_Tree_View->setChecked(mode == Settings::GameListMode::TreeView);
|
ui->action_Tree_View->setChecked(mode == Settings::GameListMode::TreeView);
|
||||||
|
ui->action_Carousel_View->setChecked(mode == Settings::GameListMode::CarouselView);
|
||||||
|
|
||||||
UISettings::values.game_list_mode = mode;
|
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();
|
CheckIconSize();
|
||||||
game_list->ResetViewMode();
|
game_list->ResetViewMode();
|
||||||
|
|
@ -3333,11 +3335,15 @@ void MainWindow::SetTreeView() {
|
||||||
SetGameListMode(Settings::GameListMode::TreeView);
|
SetGameListMode(Settings::GameListMode::TreeView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::SetCarouselView() {
|
||||||
|
SetGameListMode(Settings::GameListMode::CarouselView);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::CheckIconSize() {
|
void MainWindow::CheckIconSize() {
|
||||||
// When in grid view mode, with text off
|
// When in grid/carousel view mode, with text off
|
||||||
// there is no point in having icons turned off..
|
// there is no point in having icons turned off
|
||||||
auto actions = game_size_actions->actions();
|
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()) {
|
!UISettings::values.show_game_name.GetValue()) {
|
||||||
u32 newSize = UISettings::values.game_icon_size.GetValue();
|
u32 newSize = UISettings::values.game_icon_size.GetValue();
|
||||||
if (newSize == 0) {
|
if (newSize == 0) {
|
||||||
|
|
|
||||||
|
|
@ -410,6 +410,7 @@ private slots:
|
||||||
void SetGameListMode(Settings::GameListMode mode);
|
void SetGameListMode(Settings::GameListMode mode);
|
||||||
void SetGridView();
|
void SetGridView();
|
||||||
void SetTreeView();
|
void SetTreeView();
|
||||||
|
void SetCarouselView();
|
||||||
|
|
||||||
void CheckIconSize();
|
void CheckIconSize();
|
||||||
void ToggleShowGameName();
|
void ToggleShowGameName();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue