Qt: Begin library view

This commit is contained in:
Vicki Pfau 2017-01-26 13:18:00 -08:00
parent 480415c51e
commit 91fd2c6b47
15 changed files with 229 additions and 47 deletions

View File

@ -23,3 +23,7 @@ ArchiveInspector::ArchiveInspector(const QString& filename, QWidget* parent)
VFile* ArchiveInspector::selectedVFile() const {
return m_ui.archiveView->selectedVFile();
}
QPair<QString, QString> ArchiveInspector::selectedPath() const {
return m_ui.archiveView->selectedPath();
}

View File

@ -19,6 +19,7 @@ public:
ArchiveInspector(const QString& filename, QWidget* parent = nullptr);
VFile* selectedVFile() const;
QPair<QString, QString> selectedPath() const;
private:
Ui::ArchiveInspector m_ui;

View File

@ -164,6 +164,10 @@ QString ConfigController::getOption(const char* key) const {
return QString(mCoreConfigGetValue(&m_config, key));
}
QString ConfigController::getOption(const QString& key) const {
return getOption(key.toUtf8().constData());
}
QVariant ConfigController::getQtOption(const QString& key, const QString& group) const {
if (!group.isNull()) {
m_settings->beginGroup(group);

View File

@ -71,6 +71,7 @@ public:
void updateOption(const char* key);
QString getOption(const char* key) const;
QString getOption(const QString& key) const;
QVariant getQtOption(const QString& key, const QString& group = QString()) const;

View File

@ -145,7 +145,11 @@ GameController::GameController(QObject* parent)
controller->m_multiplayer->attachGame(controller);
}
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(mCoreThread*, context), Q_ARG(const QString&, controller->m_fname));
QString path = controller->m_fname;
if (!controller->m_fsub.isEmpty()) {
path += QDir::separator() + controller->m_fsub;
}
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(mCoreThread*, context), Q_ARG(const QString&, path));
QMetaObject::invokeMethod(controller, "startAudio");
};
@ -369,17 +373,48 @@ void GameController::loadGame(const QString& path) {
closeGame();
QFileInfo info(path);
if (!info.isReadable()) {
LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
QString fname = info.fileName();
QString base = info.path();
if (base.endsWith("/") || base.endsWith(QDir::separator())) {
base.chop(1);
}
VDir* dir = VDirOpenArchive(base.toUtf8().constData());
if (dir) {
VFile* vf = dir->openFile(dir, fname.toUtf8().constData(), O_RDONLY);
if (vf) {
struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf));
uint8_t buffer[2048];
ssize_t read;
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
vfclone->write(vfclone, buffer, read);
}
vf->close(vf);
vf = vfclone;
}
dir->close(dir);
loadGame(vf, fname, base);
} else {
LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
}
return;
} else {
m_fname = info.canonicalFilePath();
m_fsub = QString();
}
m_fname = info.canonicalFilePath();
m_vf = nullptr;
openGame();
}
void GameController::loadGame(VFile* vf, const QString& base) {
void GameController::loadGame(VFile* vf, const QString& path, const QString& base) {
closeGame();
m_fname = base;
QFileInfo info(base);
if (info.isDir()) {
m_fname = base + QDir::separator() + path;
m_fsub = QString();
} else {
m_fname = base;
m_fsub = path;
}
m_vf = vf;
openGame();
}

View File

@ -113,7 +113,7 @@ signals:
public slots:
void loadGame(const QString& path);
void loadGame(VFile* vf, const QString& base);
void loadGame(VFile* vf, const QString& path, const QString& base);
void loadBIOS(int platform, const QString& path);
void loadSave(const QString& path, bool temporary = true);
void yankPak();
@ -198,6 +198,7 @@ private:
bool m_gameOpen;
QString m_fname;
QString m_fsub;
VFile* m_vf;
QString m_bios;
bool m_useBios;

View File

@ -23,7 +23,7 @@ LibraryModel::LibraryModel(const QString& path, QObject* parent)
s_columns["filename"] = {
tr("Filename"),
[](const mLibraryEntry& e) -> QString {
return e.filename;
return QString::fromUtf8(e.filename);
}
};
s_columns["size"] = {
@ -60,6 +60,12 @@ LibraryModel::LibraryModel(const QString& path, QObject* parent)
}
}
};
s_columns["location"] = {
tr("Location"),
[](const mLibraryEntry& e) -> QString {
return QString::fromUtf8(e.base);
}
};
}
if (!path.isNull()) {
if (s_handles.contains(path)) {
@ -75,6 +81,7 @@ LibraryModel::LibraryModel(const QString& path, QObject* parent)
memset(&m_constraints, 0, sizeof(m_constraints));
m_constraints.platform = PLATFORM_NONE;
m_columns.append(s_columns["filename"]);
m_columns.append(s_columns["location"]);
m_columns.append(s_columns["platform"]);
m_columns.append(s_columns["size"]);
@ -114,6 +121,22 @@ VFile* LibraryModel::openVFile(const QModelIndex& index) const {
return mLibraryOpenVFile(m_library->library, &entry);
}
QString LibraryModel::filename(const QModelIndex& index) const {
mLibraryEntry entry;
if (!entryAt(index.row(), &entry)) {
return QString();
}
return QString::fromUtf8(entry.filename);
}
QString LibraryModel::location(const QModelIndex& index) const {
mLibraryEntry entry;
if (!entryAt(index.row(), &entry)) {
return QString();
}
return QString::fromUtf8(entry.base);
}
QVariant LibraryModel::data(const QModelIndex& index, int role) const {
if (!index.isValid()) {
return QVariant();

View File

@ -29,6 +29,8 @@ public:
bool entryAt(int row, mLibraryEntry* out) const;
VFile* openVFile(const QModelIndex& index) const;
QString filename(const QModelIndex& index) const;
QString location(const QModelIndex& index) const;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

View File

@ -29,6 +29,10 @@ void LibraryView::setDirectory(const QString& filename) {
m_model.constrainBase(filename);
}
void LibraryView::addDirectory(const QString& filename) {
m_model.loadDirectory(filename);
}
VFile* LibraryView::selectedVFile() const {
QModelIndex index = m_ui.listing->selectionModel()->currentIndex();
if (!index.isValid()) {
@ -37,6 +41,14 @@ VFile* LibraryView::selectedVFile() const {
return m_model.openVFile(index);
}
QPair<QString, QString> LibraryView::selectedPath() const {
QModelIndex index = m_ui.listing->selectionModel()->currentIndex();
if (!index.isValid()) {
return qMakePair(QString(), QString());
}
return qMakePair(m_model.filename(index), m_model.location(index));
}
void LibraryView::resizeColumns() {
m_ui.listing->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
}

View File

@ -21,6 +21,7 @@ public:
LibraryView(QWidget* parent = nullptr);
VFile* selectedVFile() const;
QPair<QString, QString> selectedPath() const;
signals:
void doneLoading();
@ -28,6 +29,7 @@ signals:
public slots:
void setDirectory(const QString&);
void addDirectory(const QString&);
private slots:
void resizeColumns();

View File

@ -13,7 +13,10 @@
<property name="windowTitle">
<string>Library</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout">
<property name="margin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTableView" name="listing">
<property name="editTriggers">
@ -34,6 +37,9 @@
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>20</number>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>0</number>
</attribute>

View File

@ -202,6 +202,7 @@ void SettingsView::updateConfig() {
saveSetting("savestatePath", m_ui.savestatePath);
saveSetting("screenshotPath", m_ui.screenshotPath);
saveSetting("patchPath", m_ui.patchPath);
saveSetting("showLibrary", m_ui.showLibrary);
if (m_ui.fastForwardUnbounded->isChecked()) {
saveSetting("fastForwardRatio", "-1");
@ -279,6 +280,7 @@ void SettingsView::reloadConfig() {
loadSetting("savestatePath", m_ui.savestatePath);
loadSetting("screenshotPath", m_ui.screenshotPath);
loadSetting("patchPath", m_ui.patchPath);
loadSetting("showLibrary", m_ui.showLibrary);
double fastForwardRatio = loadSetting("fastForwardRatio").toDouble();
if (fastForwardRatio <= 0) {

View File

@ -45,6 +45,11 @@
<string>Audio/Video</string>
</property>
</item>
<item>
<property name="text">
<string>Interface</string>
</property>
</item>
<item>
<property name="text">
<string>Emulation</string>
@ -72,7 +77,7 @@
<item row="1" column="1">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="av">
<layout class="QFormLayout" name="formLayout">
@ -384,6 +389,65 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="interface_2">
<layout class="QFormLayout" name="formLayout_4">
<item row="3" column="1">
<widget class="QCheckBox" name="allowOpposingDirections">
<property name="text">
<string>Allow opposing input directions</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="suspendScreensaver">
<property name="text">
<string>Suspend screensaver</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="pauseOnFocusLost">
<property name="text">
<string>Pause when inactive</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Library:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="showLibrary">
<property name="text">
<string>Show when no game open</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="Line" name="line_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="clearCache">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Clear cache</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="emulation">
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
@ -478,38 +542,14 @@
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="allowOpposingDirections">
<property name="text">
<string>Allow opposing input directions</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="suspendScreensaver">
<property name="text">
<string>Suspend screensaver</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="pauseOnFocusLost">
<property name="text">
<string>Pause when inactive</string>
</property>
</widget>
</item>
<item row="8" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Idle loops:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="5" column="1">
<widget class="QComboBox" name="idleOptimization">
<item>
<property name="text">
@ -528,21 +568,21 @@
</item>
</widget>
</item>
<item row="9" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="10" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Savestate extra data:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="7" column="1">
<widget class="QCheckBox" name="saveStateScreenshot">
<property name="text">
<string>Screenshot</string>
@ -552,7 +592,7 @@
</property>
</widget>
</item>
<item row="11" column="1">
<item row="8" column="1">
<widget class="QCheckBox" name="saveStateSave">
<property name="text">
<string>Save data</string>
@ -562,7 +602,7 @@
</property>
</widget>
</item>
<item row="12" column="1">
<item row="9" column="1">
<widget class="QCheckBox" name="saveStateCheats">
<property name="text">
<string>Cheat codes</string>
@ -572,14 +612,14 @@
</property>
</widget>
</item>
<item row="13" column="0">
<item row="11" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Load extra data:</string>
</property>
</widget>
</item>
<item row="13" column="1">
<item row="11" column="1">
<widget class="QCheckBox" name="loadStateScreenshot">
<property name="text">
<string>Screenshot</string>
@ -589,20 +629,27 @@
</property>
</widget>
</item>
<item row="14" column="1">
<item row="12" column="1">
<widget class="QCheckBox" name="loadStateSave">
<property name="text">
<string>Save data</string>
</property>
</widget>
</item>
<item row="15" column="1">
<item row="13" column="1">
<widget class="QCheckBox" name="loadStateCheats">
<property name="text">
<string>Cheat codes</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<widget class="Line" name="line_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="bios">

View File

@ -103,7 +103,30 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
m_savedScale = multiplier.toInt();
i = m_savedScale;
}
#ifdef M_CORE_GBA
#ifdef USE_SQLITE3
m_libraryView = new LibraryView(this);
ConfigOption* showLibrary = m_config->addOption("showLibrary");
showLibrary->connect([this](const QVariant& value) {
if (value.toBool()) {
if (m_controller->isLoaded()) {
m_screenWidget->layout()->addWidget(m_libraryView);
} else {
attachWidget(m_libraryView);
}
} else {
detachWidget(m_libraryView);
}
}, this);
m_config->updateOption("showLibrary");
connect(m_libraryView, &LibraryView::accepted, [this]() {
VFile* output = m_libraryView->selectedVFile();
QPair<QString, QString> path = m_libraryView->selectedPath();
if (output) {
m_controller->loadGame(output, path.first, path.second);
}
});
#elif defined(M_CORE_GBA)
m_screenWidget->setSizeHint(QSize(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i));
#endif
m_screenWidget->setPixmap(m_logo);
@ -367,16 +390,25 @@ void Window::selectROMInArchive() {
return;
}
ArchiveInspector* archiveInspector = new ArchiveInspector(filename);
connect(archiveInspector, &QDialog::accepted, [this, archiveInspector, filename]() {
connect(archiveInspector, &QDialog::accepted, [this, archiveInspector]() {
VFile* output = archiveInspector->selectedVFile();
QPair<QString, QString> path = archiveInspector->selectedPath();
if (output) {
m_controller->loadGame(output, filename);
m_controller->loadGame(output, path.second, path.first);
}
archiveInspector->close();
});
archiveInspector->setAttribute(Qt::WA_DeleteOnClose);
archiveInspector->show();
}
void Window::addDirToLibrary() {
QString filename = GBAApp::app()->getOpenDirectoryName(this, tr("Select folder"));
if (filename.isEmpty()) {
return;
}
m_libraryView->addDirectory(filename);
}
#endif
void Window::replaceROM() {
@ -886,8 +918,12 @@ void Window::setupMenu(QMenuBar* menubar) {
installEventFilter(m_shortcutController);
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open),
"loadROM");
#ifdef USE_SQLITE3
addControlledAction(fileMenu, fileMenu->addAction(tr("Load ROM in archive..."), this, SLOT(selectROMInArchive())),
"loadROMInArchive");
addControlledAction(fileMenu, fileMenu->addAction(tr("Add folder to library..."), this, SLOT(addDirToLibrary())),
"addDirToLibrary");
#endif
QAction* loadTemporarySave = new QAction(tr("Load temporary save..."), fileMenu);
connect(loadTemporarySave, &QAction::triggered, [this]() { this->selectSave(true); });

View File

@ -28,6 +28,7 @@ class Display;
class GameController;
class GDBController;
class GIFView;
class LibraryView;
class LogView;
class ShaderSelector;
class ShortcutController;
@ -59,6 +60,7 @@ public slots:
void selectROM();
#ifdef USE_SQLITE3
void selectROMInArchive();
void addDirToLibrary();
#endif
void selectSave(bool temporary);
void selectPatch();
@ -191,6 +193,10 @@ private:
#ifdef USE_GDB_STUB
GDBController* m_gdbController;
#endif
#ifdef USE_SQLITE3
LibraryView* m_libraryView;
#endif
};
class WindowBackground : public QLabel {