Qt: Implement drag/drop to main window

This commit is contained in:
Connor McLaughlin 2022-05-26 17:49:40 +10:00 committed by refractionpcsx2
parent 25fa70fe9e
commit ce53b7adb1
6 changed files with 104 additions and 28 deletions

View File

@ -990,26 +990,12 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
void MainWindow::onStartFileActionTriggered()
{
QString filename =
QString path =
QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr));
if (filename.isEmpty())
if (path.isEmpty())
return;
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
params->filename = filename.toStdString();
// we might still be saving a resume state...
VMManager::WaitForSaveStateFlush();
const std::optional<bool> resume(
promptForResumeState(
QString::fromStdString(VMManager::GetSaveStateFileName(params->filename.c_str(), -1))));
if (!resume.has_value())
return;
else if (resume.value())
params->state_index = -1;
g_emu_thread->startVM(std::move(params));
doStartDisc(path);
}
void MainWindow::onStartBIOSActionTriggered()
@ -1392,6 +1378,59 @@ void MainWindow::closeEvent(QCloseEvent* event)
QMainWindow::closeEvent(event);
}
static QString getFilenameFromMimeData(const QMimeData* md)
{
QString filename;
if (md->hasUrls())
{
// only one url accepted
const QList<QUrl> urls(md->urls());
if (urls.size() == 1)
filename = urls.front().toLocalFile();
}
return filename;
}
void MainWindow::dragEnterEvent(QDragEnterEvent* event)
{
const std::string filename(getFilenameFromMimeData(event->mimeData()).toStdString());
// allow save states being dragged in
if (!VMManager::IsLoadableFileName(filename) && !VMManager::IsSaveStateFileName(filename))
return;
event->acceptProposedAction();
}
void MainWindow::dropEvent(QDropEvent* event)
{
const QString filename(getFilenameFromMimeData(event->mimeData()));
const std::string filename_str(filename.toStdString());
if (VMManager::IsSaveStateFileName(filename_str))
{
// can't load a save state without a current VM
if (m_vm_valid)
{
event->acceptProposedAction();
g_emu_thread->loadState(filename);
}
else
{
QMessageBox::critical(this, tr("Load State Failed"), tr("Cannot load a save state without a running VM."));
}
}
else if (VMManager::IsLoadableFileName(filename_str))
{
// if we're already running, do a disc change, otherwise start
event->acceptProposedAction();
if (m_vm_valid)
doDiscChange(filename);
else
doStartDisc(filename);
}
}
DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
{
DevCon.WriteLn("createDisplay(%u, %u)", static_cast<u32>(fullscreen), static_cast<u32>(render_to_main));
@ -2013,6 +2052,28 @@ void MainWindow::updateSaveStateMenus(const QString& filename, const QString& se
populateSaveStateMenu(m_ui.menuSaveState, serial, crc);
}
void MainWindow::doStartDisc(const QString& path)
{
if (m_vm_valid)
return;
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
params->filename = path.toStdString();
// we might still be saving a resume state...
VMManager::WaitForSaveStateFlush();
const std::optional<bool> resume(
promptForResumeState(
QString::fromStdString(VMManager::GetSaveStateFileName(params->filename.c_str(), -1))));
if (!resume.has_value())
return;
else if (resume.value())
params->state_index = -1;
g_emu_thread->startVM(std::move(params));
}
void MainWindow::doDiscChange(const QString& path)
{
bool reset_system = false;

View File

@ -157,6 +157,8 @@ private Q_SLOTS:
protected:
void closeEvent(QCloseEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
private:
enum : s32
@ -206,6 +208,7 @@ private:
void populateLoadStateMenu(QMenu* menu, const QString& filename, const QString& serial, quint32 crc);
void populateSaveStateMenu(QMenu* menu, const QString& serial, quint32 crc);
void updateSaveStateMenus(const QString& filename, const QString& serial, quint32 crc);
void doStartDisc(const QString& path);
void doDiscChange(const QString& path);
Ui::MainWindow m_ui;

View File

@ -110,17 +110,13 @@ const char* GameList::EntryCompatibilityRatingToString(CompatibilityRating ratin
// clang-format on
}
bool GameList::IsScannableFilename(const std::string& path)
bool GameList::IsScannableFilename(const std::string_view& path)
{
static const char* extensions[] = {".iso", ".mdf", ".nrg", ".bin", ".img", ".gz", ".cso", ".chd", ".elf", ".irx"};
const std::string::size_type pos = path.rfind('.');
if (pos == std::string::npos)
return false;
for (const char* test_extension : extensions)
{
if (StringUtil::Strcasecmp(&path[pos], test_extension) == 0)
if (StringUtil::EndsWithNoCase(path, test_extension))
return true;
}

View File

@ -72,7 +72,7 @@ namespace GameList
const char* RegionToString(Region region);
const char* EntryCompatibilityRatingToString(CompatibilityRating rating);
bool IsScannableFilename(const std::string& path);
bool IsScannableFilename(const std::string_view& path);
/// Fills in boot parameters (iso or elf) based on the game list entry.
void FillBootParametersForEntry(VMBootParameters* params, const Entry* entry);

View File

@ -1208,18 +1208,28 @@ bool VMManager::ChangeDisc(std::string path)
return result;
}
bool VMManager::IsElfFileName(const std::string& path)
bool VMManager::IsElfFileName(const std::string_view& path)
{
return StringUtil::EndsWithNoCase(path, ".elf");
}
bool VMManager::IsGSDumpFileName(const std::string& path)
bool VMManager::IsGSDumpFileName(const std::string_view& path)
{
return (StringUtil::EndsWithNoCase(path, ".gs") ||
StringUtil::EndsWithNoCase(path, ".gs.xz") ||
StringUtil::EndsWithNoCase(path, ".gs.zst"));
}
bool VMManager::IsSaveStateFileName(const std::string_view& path)
{
return StringUtil::EndsWithNoCase(path, ".p2s");
}
bool VMManager::IsLoadableFileName(const std::string_view& path)
{
return IsElfFileName(path) || IsGSDumpFileName(path) || GameList::IsScannableFilename(path);
}
void VMManager::Execute()
{
// Check for interpreter<->recompiler switches.

View File

@ -135,10 +135,16 @@ namespace VMManager
bool ChangeDisc(std::string path);
/// Returns true if the specified path is an ELF.
bool IsElfFileName(const std::string& path);
bool IsElfFileName(const std::string_view& path);
/// Returns true if the specified path is a GS Dump.
bool IsGSDumpFileName(const std::string& path);
bool IsGSDumpFileName(const std::string_view& path);
/// Returns true if the specified path is a save state.
bool IsSaveStateFileName(const std::string_view& path);
/// Returns true if the specified path is a disc/elf/etc.
bool IsLoadableFileName(const std::string_view& path);
/// Returns the path for the game settings ini file for the specified CRC.
std::string GetGameSettingsPath(const std::string_view& game_serial, u32 game_crc);