Skip to content

Commit

Permalink
Library: rewrite Qt library frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
ahigerd committed Feb 16, 2023
1 parent ea345ca commit 8430779
Show file tree
Hide file tree
Showing 14 changed files with 1,138 additions and 404 deletions.
34 changes: 32 additions & 2 deletions src/platform/qt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ set(GB_SRC
GBOverride.cpp
PrinterView.cpp)

set(TEST_QT_spanset_SRC
test/spanset.cpp
utils.cpp
VFileDevice.cpp)

set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5")

set(AUDIO_SRC)
Expand Down Expand Up @@ -251,8 +256,15 @@ if(USE_SQLITE3)
list(APPEND SOURCE_FILES
ArchiveInspector.cpp
library/LibraryController.cpp
library/LibraryGrid.cpp
library/LibraryTree.cpp)
library/LibraryEntry.cpp
library/LibraryModel.cpp)

set(TEST_QT_library_SRC
library/LibraryEntry.cpp
library/LibraryModel.cpp
test/library.cpp
utils.cpp
VFileDevice.cpp)
endif()

if(USE_DISCORD_RPC)
Expand Down Expand Up @@ -498,3 +510,21 @@ if(DISTBUILD AND NOT APPLE)
add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${STRIP}" "$<TARGET_FILE:${BINARY_NAME}-qt>")
endif()
endif()

if(BUILD_SUITE)
enable_testing()
find_package(${QT}Test)
if(${QT}Test_FOUND)
get_property(ALL_TESTS DIRECTORY PROPERTY VARIABLES)
list(FILTER ALL_TESTS INCLUDE REGEX "^TEST_QT_.*_SRC$")
foreach(TEST_SRC ${ALL_TESTS})
string(REGEX REPLACE "^TEST_QT_(.*)_SRC$" "\\1" TEST_NAME ${TEST_SRC})
add_executable(test-qt-${TEST_NAME} WIN32 ${${TEST_SRC}})
target_link_libraries(test-qt-${TEST_NAME} ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES} ${QT}::Test)
set_target_properties(test-qt-${TEST_NAME} PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}" COMPILE_OPTIONS "${FEATURE_FLAGS}")
add_test(platform-qt-${TEST_NAME} test-qt-${TEST_NAME})
endforeach()
else()
message("${QT}Test not found")
endif()
endif()
225 changes: 147 additions & 78 deletions src/platform/qt/library/LibraryController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,15 @@

#include "ConfigController.h"
#include "GBAApp.h"
#include "LibraryGrid.h"
#include "LibraryTree.h"
#include "LibraryModel.h"

using namespace QGBA;

LibraryEntry::LibraryEntry(const mLibraryEntry* entry)
: base(entry->base)
, filename(entry->filename)
, fullpath(QString("%1/%2").arg(entry->base, entry->filename))
, title(entry->title)
, internalTitle(entry->internalTitle)
, internalCode(entry->internalCode)
, platform(entry->platform)
, filesize(entry->filesize)
, crc32(entry->crc32)
{
}

void AbstractGameList::addEntry(const LibraryEntry& item) {
addEntries({item});
}
#include <QHeaderView>
#include <QListView>
#include <QSortFilterProxyModel>
#include <QTimer>
#include <QTreeView>

void AbstractGameList::updateEntry(const LibraryEntry& item) {
updateEntries({item});
}

void AbstractGameList::removeEntry(const QString& item) {
removeEntries({item});
}
void AbstractGameList::setShowFilename(bool showFilename) {
m_showFilename = showFilename;
}
using namespace QGBA;

LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config)
: QStackedWidget(parent)
Expand All @@ -55,14 +32,53 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi

mLibraryAttachGameDB(m_library.get(), GBAApp::app()->gameDB());

m_libraryTree = std::make_unique<LibraryTree>(this);
addWidget(m_libraryTree->widget());

m_libraryGrid = std::make_unique<LibraryGrid>(this);
addWidget(m_libraryGrid->widget());
m_libraryModel = new LibraryModel(this);

m_treeView = new QTreeView(this);
addWidget(m_treeView);
m_treeModel = new QSortFilterProxyModel(this);
m_treeModel->setSourceModel(m_libraryModel);
m_treeModel->setSortRole(Qt::EditRole);
m_treeView->setModel(m_treeModel);
m_treeView->setSortingEnabled(true);
m_treeView->setAlternatingRowColors(true);

m_listView = new QListView(this);
addWidget(m_listView);
m_listModel = new QSortFilterProxyModel(this);
m_listModel->setSourceModel(m_libraryModel);
m_listModel->setSortRole(Qt::EditRole);
m_listView->setModel(m_listModel);

QObject::connect(m_treeView, &QAbstractItemView::activated, this, &LibraryController::startGame);
QObject::connect(m_listView, &QAbstractItemView::activated, this, &LibraryController::startGame);
QObject::connect(m_treeView->header(), &QHeaderView::sortIndicatorChanged, this, &LibraryController::sortChanged);

m_expandThrottle.setInterval(100);
m_expandThrottle.setSingleShot(true);
QObject::connect(&m_expandThrottle, &QTimer::timeout, this, qOverload<>(&LibraryController::resizeTreeView));
QObject::connect(m_libraryModel, &QAbstractItemModel::modelReset, &m_expandThrottle, qOverload<>(&QTimer::start));
QObject::connect(m_libraryModel, &QAbstractItemModel::rowsInserted, &m_expandThrottle, qOverload<>(&QTimer::start));

LibraryStyle libraryStyle = LibraryStyle(m_config->getOption("libraryStyle", int(LibraryStyle::STYLE_LIST)).toInt());
// Make sure setViewStyle does something
if (libraryStyle == LibraryStyle::STYLE_TREE) {
m_currentStyle = LibraryStyle::STYLE_LIST;
} else {
m_currentStyle = LibraryStyle::STYLE_TREE;
}
setViewStyle(libraryStyle);

m_currentStyle = LibraryStyle::STYLE_TREE; // Make sure setViewStyle does something
setViewStyle(LibraryStyle::STYLE_LIST);
QVariant librarySort = m_config->getQtOption("librarySort");
QVariant librarySortOrder = m_config->getQtOption("librarySortOrder");
if (librarySort.isNull() || !librarySort.canConvert<int>()) {
librarySort = 0;
}
if (librarySortOrder.isNull() || !librarySortOrder.canConvert<Qt::SortOrder>()) {
librarySortOrder = Qt::AscendingOrder;
}
m_treeModel->sort(librarySort.toInt(), librarySortOrder.value<Qt::SortOrder>());
m_listModel->sort(0, Qt::AscendingOrder);
refresh();
}

Expand All @@ -73,32 +89,59 @@ void LibraryController::setViewStyle(LibraryStyle newStyle) {
if (m_currentStyle == newStyle) {
return;
}
QString selected;
if (m_currentView) {
QModelIndex selectedIndex = m_currentView->selectionModel()->currentIndex();
if (selectedIndex.isValid()) {
selected = selectedIndex.data(LibraryModel::FullPathRole).toString();
}
}

m_currentStyle = newStyle;
m_libraryModel->setTreeMode(newStyle == LibraryStyle::STYLE_TREE);

AbstractGameList* newCurrentList = nullptr;
QAbstractItemView* newView = m_listView;
if (newStyle == LibraryStyle::STYLE_LIST || newStyle == LibraryStyle::STYLE_TREE) {
newCurrentList = m_libraryTree.get();
} else {
newCurrentList = m_libraryGrid.get();
newView = m_treeView;
}
newCurrentList->selectEntry(selectedEntry().fullpath);
newCurrentList->setViewStyle(newStyle);
setCurrentWidget(newCurrentList->widget());
m_currentList = newCurrentList;

setCurrentWidget(newView);
m_currentView = newView;
selectEntry(selected);
}

void LibraryController::sortChanged(int column, Qt::SortOrder order) {
m_config->setQtOption("librarySort", column);
m_config->setQtOption("librarySortOrder", order);
}

void LibraryController::selectEntry(const QString& fullpath) {
if (!m_currentList) {
if (!m_currentView) {
return;
}
m_currentList->selectEntry(fullpath);
QModelIndex index = m_libraryModel->index(fullpath);

// If the model is proxied in the current view, map the index to the proxy
QAbstractProxyModel* proxy = qobject_cast<QAbstractProxyModel*>(m_currentView->model());
if (proxy) {
index = proxy->mapFromSource(index);
}

if (index.isValid()) {
m_currentView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
}
}

LibraryEntry LibraryController::selectedEntry() {
if (!m_currentList) {
if (!m_currentView) {
return {};
}
QModelIndex index = m_currentView->selectionModel()->currentIndex();
if (!index.isValid()) {
return {};
}
return m_entries.value(m_currentList->selectedEntry());
QString fullpath = index.data(LibraryModel::FullPathRole).toString();
return m_libraryModel->entry(fullpath);
}

VFile* LibraryController::selectedVFile() {
Expand Down Expand Up @@ -149,42 +192,34 @@ void LibraryController::refresh() {

setDisabled(true);

QHash<QString, LibraryEntry> removedEntries = m_entries;
QHash<QString, LibraryEntry> updatedEntries;
QSet<QString> removedEntries = QSet<QString>::fromList(m_knownGames.keys());
QList<LibraryEntry> updatedEntries;
QList<LibraryEntry> newEntries;

mLibraryListing listing;
mLibraryListingInit(&listing, 0);
mLibraryGetEntries(m_library.get(), &listing, 0, 0, nullptr);
for (size_t i = 0; i < mLibraryListingSize(&listing); i++) {
LibraryEntry entry = mLibraryListingGetConstPointer(&listing, i);
if (!m_entries.contains(entry.fullpath)) {
const mLibraryEntry* entry = mLibraryListingGetConstPointer(&listing, i);
uint64_t checkHash = LibraryEntry::checkHash(entry);
QString fullpath = QStringLiteral("%1/%2").arg(entry->base, entry->filename);
if (!m_knownGames.contains(fullpath)) {
newEntries.append(entry);
} else {
updatedEntries[entry.fullpath] = entry;
} else if (checkHash != m_knownGames[fullpath]) {
updatedEntries.append(entry);
}
m_entries[entry.fullpath] = entry;
removedEntries.remove(entry.fullpath);
removedEntries.remove(fullpath);
m_knownGames[fullpath] = checkHash;
}

// Check for entries that were removed
for (QString& path : removedEntries.keys()) {
m_entries.remove(path);
for (const QString& path : removedEntries) {
m_knownGames.remove(path);
}

if (!removedEntries.size() && !newEntries.size()) {
m_libraryTree->updateEntries(updatedEntries.values());
m_libraryGrid->updateEntries(updatedEntries.values());
} else if (!updatedEntries.size()) {
m_libraryTree->removeEntries(removedEntries.keys());
m_libraryGrid->removeEntries(removedEntries.keys());

m_libraryTree->addEntries(newEntries);
m_libraryGrid->addEntries(newEntries);
} else {
m_libraryTree->resetEntries(m_entries.values());
m_libraryGrid->resetEntries(m_entries.values());
}
m_libraryModel->removeEntries(removedEntries.toList());
m_libraryModel->updateEntries(updatedEntries);
m_libraryModel->addEntries(newEntries);

for (size_t i = 0; i < mLibraryListingSize(&listing); ++i) {
mLibraryEntryFree(mLibraryListingGetPointer(&listing, i));
Expand All @@ -201,7 +236,7 @@ void LibraryController::selectLastBootedGame() {
return;
}
const QString lastfile = m_config->getMRU().first();
if (m_entries.contains(lastfile)) {
if (m_knownGames.contains(lastfile)) {
selectEntry(lastfile);
}
}
Expand All @@ -213,16 +248,50 @@ void LibraryController::loadDirectory(const QString& dir, bool recursive) {
mLibraryLoadDirectory(library.get(), dir.toUtf8().constData(), recursive);
m_libraryJob.testAndSetOrdered(libraryJob, -1);
}

void LibraryController::setShowFilename(bool showFilename) {
if (showFilename == m_showFilename) {
return;
}
m_showFilename = showFilename;
if (m_libraryGrid) {
m_libraryGrid->setShowFilename(m_showFilename);
m_libraryModel->setShowFilename(m_showFilename);
refresh();
}

void LibraryController::showEvent(QShowEvent*) {
resizeTreeView(false);
}

void LibraryController::resizeEvent(QResizeEvent*) {
resizeTreeView(false);
}

void LibraryController::resizeTreeView(bool expand) {
if (expand) {
m_treeView->expandAll();
}
if (m_libraryTree) {
m_libraryTree->setShowFilename(m_showFilename);

int viewportWidth = m_treeView->viewport()->width();
int totalWidth = m_treeView->header()->sectionSizeHint(LibraryModel::MAX_COLUMN);
for (int column = 0; column < LibraryModel::MAX_COLUMN; column++) {
totalWidth += m_treeView->columnWidth(column);
}
if (totalWidth < viewportWidth) {
totalWidth = 0;
for (int column = 0; column <= LibraryModel::MAX_COLUMN; column++) {
m_treeView->resizeColumnToContents(column);
totalWidth += m_treeView->columnWidth(column);
}
}
if (totalWidth > viewportWidth) {
int locationWidth = m_treeView->columnWidth(LibraryModel::COL_LOCATION);
if (locationWidth > 100) {
int newLocationWidth = m_treeView->viewport()->width() - (totalWidth - locationWidth);
if (newLocationWidth < 100) {
newLocationWidth = 100;
}
m_treeView->setColumnWidth(LibraryModel::COL_LOCATION, newLocationWidth);
totalWidth = totalWidth - locationWidth + newLocationWidth;
}
}
refresh();
}
Loading

0 comments on commit 8430779

Please sign in to comment.