diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt index 8a744f8017..85e0a61428 100644 --- a/Source/Core/DolphinQt2/CMakeLists.txt +++ b/Source/Core/DolphinQt2/CMakeLists.txt @@ -13,6 +13,7 @@ set(SRCS GameList/GameList.cpp GameList/GameTracker.cpp GameList/GameListModel.cpp + GameList/GameListProxyModel.cpp ) list(APPEND LIBS core uicommon) diff --git a/Source/Core/DolphinQt2/GameList/GameList.cpp b/Source/Core/DolphinQt2/GameList/GameList.cpp index 6ee3ab1d45..c7bb03327b 100644 --- a/Source/Core/DolphinQt2/GameList/GameList.cpp +++ b/Source/Core/DolphinQt2/GameList/GameList.cpp @@ -6,12 +6,14 @@ #include "Core/ConfigManager.h" #include "DolphinQt2/GameList/GameList.h" +#include "DolphinQt2/GameList/GameListProxyModel.h" GameList::GameList(QWidget* parent): QStackedWidget(parent) { m_model = new GameListModel(this); - m_proxy = new QSortFilterProxyModel(this); + m_proxy = new GameListProxyModel(this); m_proxy->setSourceModel(m_model); + m_proxy->setSortCaseSensitivity(Qt::CaseInsensitive); MakeTableView(); MakeListView(); @@ -36,31 +38,25 @@ void GameList::MakeTableView() m_table->setSortingEnabled(true); m_table->setCurrentIndex(QModelIndex()); - // These fixed column widths make it so that the DisplayRole is cut - // off, which lets us see the icon but sort by the actual value. - // It's a bit of a hack. To do it right we need to subclass - // QSortFilterProxyModel and not show those items. + // FIXME These icon image are overly wide and should be cut down to size, + // then we can remove these lines. m_table->setColumnWidth(GameListModel::COL_PLATFORM, 52); m_table->setColumnWidth(GameListModel::COL_COUNTRY, 38); m_table->setColumnWidth(GameListModel::COL_RATING, 52); + + // This column is for the icon view. Hide it. m_table->setColumnHidden(GameListModel::COL_LARGE_ICON, true); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_PLATFORM, QHeaderView::Fixed); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_COUNTRY, QHeaderView::Fixed); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_ID, QHeaderView::ResizeToContents); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_TITLE, QHeaderView::Stretch); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_MAKER, QHeaderView::ResizeToContents); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_SIZE, QHeaderView::ResizeToContents); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_DESCRIPTION, QHeaderView::Stretch); - m_table->horizontalHeader()->setSectionResizeMode( - GameListModel::COL_RATING, QHeaderView::Fixed); + QHeaderView* header = m_table->horizontalHeader(); + header->setSectionResizeMode(GameListModel::COL_PLATFORM, QHeaderView::Fixed); + header->setSectionResizeMode(GameListModel::COL_COUNTRY, QHeaderView::Fixed); + header->setSectionResizeMode(GameListModel::COL_ID, QHeaderView::ResizeToContents); + header->setSectionResizeMode(GameListModel::COL_BANNER, QHeaderView::ResizeToContents); + header->setSectionResizeMode(GameListModel::COL_TITLE, QHeaderView::Stretch); + header->setSectionResizeMode(GameListModel::COL_MAKER, QHeaderView::Stretch); + header->setSectionResizeMode(GameListModel::COL_SIZE, QHeaderView::ResizeToContents); + header->setSectionResizeMode(GameListModel::COL_DESCRIPTION, QHeaderView::Stretch); + header->setSectionResizeMode(GameListModel::COL_RATING, QHeaderView::Fixed); } void GameList::MakeListView() diff --git a/Source/Core/DolphinQt2/GameList/GameListModel.cpp b/Source/Core/DolphinQt2/GameList/GameListModel.cpp index 301bc9d40d..c4027088da 100644 --- a/Source/Core/DolphinQt2/GameList/GameListModel.cpp +++ b/Source/Core/DolphinQt2/GameList/GameListModel.cpp @@ -5,25 +5,6 @@ #include "DolphinQt2/Resources.h" #include "DolphinQt2/GameList/GameListModel.h" -static QString FormatSize(qint64 size) -{ - QStringList units{ - QStringLiteral("KB"), - QStringLiteral("MB"), - QStringLiteral("GB"), - QStringLiteral("TB") - }; - QStringListIterator i(units); - QString unit = QStringLiteral("B"); - double num = (double) size; - while (num > 1024.0 && i.hasNext()) - { - unit = i.next(); - num /= 1024.0; - } - return QStringLiteral("%1 %2").arg(QString::number(num, 'f', 1)).arg(unit); -} - GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent) { @@ -38,46 +19,22 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const return QVariant(); QSharedPointer game = m_games[index.row()]; - if (index.column() == COL_PLATFORM && role == Qt::DecorationRole) - return QVariant(Resources::GetPlatform(game->GetPlatform())); - else if (index.column() == COL_PLATFORM && role == Qt::DisplayRole) - return QVariant(game->GetPlatform()); - - else if (index.column() == COL_TITLE && role == Qt::DecorationRole) - return QVariant(game->GetBanner()); - else if (index.column() == COL_TITLE && role == Qt::DisplayRole) - return QVariant(game->GetLongName()); - - else if (index.column() == COL_ID && role == Qt::DisplayRole) - return QVariant(game->GetUniqueID()); - - else if (index.column() == COL_DESCRIPTION && role == Qt::DisplayRole) - return QVariant(game->GetDescription()); - - else if (index.column() == COL_MAKER && role == Qt::DisplayRole) - return QVariant(game->GetCompany()); - - // FIXME this sorts lexicographically, not by size. - else if (index.column() == COL_SIZE && role == Qt::DisplayRole) - return QVariant(FormatSize(game->GetFileSize())); - - else if (index.column() == COL_COUNTRY && role == Qt::DecorationRole) - return QVariant(Resources::GetCountry(game->GetCountry())); - else if (index.column() == COL_COUNTRY && role == Qt::DisplayRole) - return QVariant(game->GetCountry()); - - else if (index.column() == COL_RATING && role == Qt::DecorationRole) - return QVariant(Resources::GetRating(game->GetRating())); - else if (index.column() == COL_RATING && role == Qt::DisplayRole) - return QVariant(game->GetRating()); - - else if (index.column() == COL_LARGE_ICON && role == Qt::DecorationRole) - return QVariant(game->GetBanner().scaled(144, 48)); - else if (index.column() == COL_LARGE_ICON && role == Qt::DisplayRole) - return QVariant(game->GetLongName()); - - else - return QVariant(); + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case COL_PLATFORM: return game->GetPlatform(); + case COL_BANNER: return game->GetBanner(); + case COL_TITLE: return game->GetLongName(); + case COL_ID: return game->GetUniqueID(); + case COL_DESCRIPTION: return game->GetDescription(); + case COL_MAKER: return game->GetCompany(); + case COL_SIZE: return game->GetFileSize(); + case COL_COUNTRY: return game->GetCountry(); + case COL_RATING: return game->GetRating(); + } + } + return QVariant(); } QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -87,14 +44,15 @@ QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int switch (section) { - case COL_TITLE: return QVariant(tr("Title")); - case COL_ID: return QVariant(tr("ID")); - case COL_DESCRIPTION: return QVariant(tr("Description")); - case COL_MAKER: return QVariant(tr("Maker")); - case COL_SIZE: return QVariant(tr("Size")); - case COL_RATING: return QVariant(tr("Quality")); - default: return QVariant(); + case COL_TITLE: return tr("Title"); + case COL_ID: return tr("ID"); + case COL_BANNER: return tr("Banner"); + case COL_DESCRIPTION: return tr("Description"); + case COL_MAKER: return tr("Maker"); + case COL_SIZE: return tr("Size"); + case COL_RATING: return tr("Quality"); } + return QVariant(); } int GameListModel::rowCount(const QModelIndex& parent) const diff --git a/Source/Core/DolphinQt2/GameList/GameListModel.h b/Source/Core/DolphinQt2/GameList/GameListModel.h index 98975738e7..5e0080d947 100644 --- a/Source/Core/DolphinQt2/GameList/GameListModel.h +++ b/Source/Core/DolphinQt2/GameList/GameListModel.h @@ -30,6 +30,7 @@ public: { COL_PLATFORM = 0, COL_ID, + COL_BANNER, COL_TITLE, COL_DESCRIPTION, COL_MAKER, diff --git a/Source/Core/DolphinQt2/GameList/GameListProxyModel.cpp b/Source/Core/DolphinQt2/GameList/GameListProxyModel.cpp new file mode 100644 index 0000000000..6b4faedb86 --- /dev/null +++ b/Source/Core/DolphinQt2/GameList/GameListProxyModel.cpp @@ -0,0 +1,87 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/Resources.h" +#include "DolphinQt2/GameList/GameListModel.h" +#include "DolphinQt2/GameList/GameListProxyModel.h" + +static constexpr QSize NORMAL_BANNER_SIZE(96, 32); +static constexpr QSize LARGE_BANNER_SIZE(144, 48); + +// Convert an integer size to a friendly string representation. +static QString FormatSize(qint64 size) +{ + QStringList units{ + QStringLiteral("KB"), + QStringLiteral("MB"), + QStringLiteral("GB"), + QStringLiteral("TB") + }; + QStringListIterator i(units); + QString unit = QStringLiteral("B"); + double num = (double) size; + while (num > 1024.0 && i.hasNext()) + { + unit = i.next(); + num /= 1024.0; + } + return QStringLiteral("%1 %2").arg(QString::number(num, 'f', 1)).arg(unit); +} + +GameListProxyModel::GameListProxyModel(QObject* parent) + : QSortFilterProxyModel(parent) +{ +} + +QVariant GameListProxyModel::data(const QModelIndex& i, int role) const +{ + QModelIndex source_index = mapToSource(i); + QVariant source_data = sourceModel()->data(source_index, Qt::DisplayRole); + if (role == Qt::DisplayRole) + { + switch (i.column()) + { + // Sort by the integer but display the formatted string. + case GameListModel::COL_SIZE: + return FormatSize(source_data.toULongLong()); + // These fall through to the underlying model. + case GameListModel::COL_ID: + case GameListModel::COL_TITLE: + case GameListModel::COL_DESCRIPTION: + case GameListModel::COL_MAKER: + return source_data; + // Show the title in the display role of the icon view. + case GameListModel::COL_LARGE_ICON: + return data(index(i.row(), GameListModel::COL_TITLE), Qt::DisplayRole); + } + } + else if (role == Qt::DecorationRole) + { + switch (i.column()) + { + // Show icons in the decoration roles. This lets us sort by the + // underlying ints, but display just the icons without doing any + // fixed-width hacks. + case GameListModel::COL_PLATFORM: + return Resources::GetPlatform(source_data.toInt()); + case GameListModel::COL_BANNER: + return source_data.value().scaled( + NORMAL_BANNER_SIZE, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + case GameListModel::COL_COUNTRY: + return Resources::GetCountry(source_data.toInt()); + case GameListModel::COL_RATING: + return Resources::GetRating(source_data.toInt()); + // Show a scaled icon in the decoration role of the icon view. + case GameListModel::COL_LARGE_ICON: + return data(index(i.row(), GameListModel::COL_BANNER), Qt::DecorationRole) + .value().scaled( + LARGE_BANNER_SIZE, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + } + } + return QVariant(); +} diff --git a/Source/Core/DolphinQt2/GameList/GameListProxyModel.h b/Source/Core/DolphinQt2/GameList/GameListProxyModel.h new file mode 100644 index 0000000000..b6728bdab3 --- /dev/null +++ b/Source/Core/DolphinQt2/GameList/GameListProxyModel.h @@ -0,0 +1,19 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +// This subclass of QSortFilterProxyModel transforms the raw GameFile data +// into presentable icons, and allows for sorting and filtering. +// For instance, the GameListModel exposes country as an integer, so this +// class converts that into a flag, while still allowing sorting on the +// underlying integer. +class GameListProxyModel final : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + GameListProxyModel(QObject* parent = nullptr); + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; +}; diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index 49e4ae4511..09f8f1579b 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -71,6 +71,7 @@ void MainWindow::AddTableColumnsMenu(QMenu* view_menu) QStringList col_names{ tr("Platform"), tr("ID"), + tr("Banner"), tr("Title"), tr("Description"), tr("Maker"), @@ -78,12 +79,13 @@ void MainWindow::AddTableColumnsMenu(QMenu* view_menu) tr("Country"), tr("Quality") }; - // TODO we'll need to update SConfig with another column. Then we can clean this - // up significantly. + // TODO we'll need to update SConfig with the extra columns. Then we can + // clean this up significantly. QList show_cols{ SConfig::GetInstance().m_showSystemColumn, SConfig::GetInstance().m_showIDColumn, SConfig::GetInstance().m_showBannerColumn, + true, false, SConfig::GetInstance().m_showMakerColumn, SConfig::GetInstance().m_showSizeColumn,