1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-08 22:26:51 +02:00

Merge pull request #5430 from Laserlicht/context

[1.6.6] Context menu for modlist
This commit is contained in:
Ivan Savenko
2025-02-17 12:38:09 +02:00
committed by GitHub
5 changed files with 190 additions and 44 deletions

View File

@@ -12,28 +12,11 @@
#include "ui_aboutproject_moc.h"
#include "../updatedialog_moc.h"
#include "../helper.h"
#include "../../lib/GameConstants.h"
#include "../../lib/VCMIDirs.h"
#ifdef VCMI_IOS
#include "ios/revealdirectoryinfiles.h"
#endif
namespace
{
void revealDirectoryInFileBrowser(QLineEdit * dirLineEdit)
{
const auto dirUrl = QUrl::fromLocalFile(QFileInfo{dirLineEdit->text()}.absoluteFilePath());
#ifdef VCMI_IOS
iOS_utils::revealDirectoryInFiles(dirUrl);
#else
QDesktopServices::openUrl(dirUrl);
#endif
}
}
void AboutProjectView::hideAndStretchWidget(QGridLayout * layout, QWidget * toHide, QWidget * toStretch)
{
toHide->hide();
@@ -88,22 +71,22 @@ void AboutProjectView::on_updatesButton_clicked()
void AboutProjectView::on_openGameDataDir_clicked()
{
revealDirectoryInFileBrowser(ui->lineEditGameDir);
Helper::revealDirectoryInFileBrowser(ui->lineEditGameDir->text());
}
void AboutProjectView::on_openUserDataDir_clicked()
{
revealDirectoryInFileBrowser(ui->lineEditUserDataDir);
Helper::revealDirectoryInFileBrowser(ui->lineEditUserDataDir->text());
}
void AboutProjectView::on_openTempDir_clicked()
{
revealDirectoryInFileBrowser(ui->lineEditTempDir);
Helper::revealDirectoryInFileBrowser(ui->lineEditTempDir->text());
}
void AboutProjectView::on_openConfigDir_clicked()
{
revealDirectoryInFileBrowser(ui->lineEditConfigDir);
Helper::revealDirectoryInFileBrowser(ui->lineEditConfigDir->text());
}
void AboutProjectView::on_pushButtonDiscord_clicked()

View File

@@ -20,6 +20,10 @@
#include <QtAndroid>
#endif
#ifdef VCMI_IOS
#include "ios/revealdirectoryinfiles.h"
#endif
#ifdef VCMI_MOBILE
static QScrollerProperties generateScrollerProperties()
{
@@ -75,4 +79,14 @@ void performNativeCopy(QString src, QString dst)
QFile::copy(src, dst);
#endif
}
void revealDirectoryInFileBrowser(QString path)
{
const auto dirUrl = QUrl::fromLocalFile(QFileInfo{path}.absoluteFilePath());
#ifdef VCMI_IOS
iOS_utils::revealDirectoryInFiles(dirUrl);
#else
QDesktopServices::openUrl(dirUrl);
#endif
}
}

View File

@@ -19,4 +19,5 @@ void loadSettings();
void enableScrollBySwiping(QObject * scrollTarget);
QString getRealPath(QString path);
void performNativeCopy(QString src, QString dst);
void revealDirectoryInFileBrowser(QString path);
}

View File

@@ -95,6 +95,11 @@ void CModListView::setupModsView()
ui->allModsView->setUniformRowHeights(true);
ui->allModsView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->allModsView, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(onCustomContextMenu(const QPoint &)));
connect(ui->allModsView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex&,const QModelIndex&)),
this, SLOT(modSelected(const QModelIndex&,const QModelIndex&)));
@@ -417,6 +422,126 @@ void CModListView::disableModInfo()
ui->updateButton->setVisible(false);
}
auto CModListView::buttonEnabledState(QString modName, ModState & mod)
{
struct result {
bool disableVisible;
bool enableVisible;
bool installVisible;
bool uninstallVisible;
bool updateVisible;
bool directoryVisible;
bool repositoryVisible;
bool disableEnabled;
bool enableEnabled;
bool installEnabled;
bool uninstallEnabled;
bool updateEnabled;
bool directoryEnabled;
bool repositoryEnabled;
} res;
QStringList notInstalledDependencies = getModsToInstall(modName);
QStringList unavailableDependencies = findUnavailableMods(notInstalledDependencies);
bool translationMismatch = mod.isTranslation() && CGeneralTextHandler::getPreferredLanguage() != mod.getBaseLanguage().toStdString();
bool modIsBeingDownloaded = enqueuedModDownloads.contains(mod.getID());
res.disableVisible = modStateModel->isModInstalled(mod.getID()) && modStateModel->isModEnabled(mod.getID());
res.enableVisible = modStateModel->isModInstalled(mod.getID()) && !modStateModel->isModEnabled(mod.getID());
res.installVisible = mod.isAvailable() && !mod.isSubmod();
res.uninstallVisible = mod.isInstalled() && !mod.isSubmod();
res.updateVisible = mod.isUpdateAvailable();
#ifndef VCMI_MOBILE
res.directoryVisible = mod.isInstalled();
#else
res.directoryVisible = false;
#endif
res.repositoryVisible = !mod.getDownloadUrl().isEmpty();
// Block buttons if action is not allowed at this time
res.disableEnabled = true;
res.enableEnabled = notInstalledDependencies.empty() && !translationMismatch;
res.installEnabled = unavailableDependencies.empty() && !modIsBeingDownloaded;
res.uninstallEnabled = true;
res.updateEnabled = unavailableDependencies.empty() && !modIsBeingDownloaded;
res.directoryEnabled = true;
res.repositoryEnabled = true;
return res;
}
void CModListView::onCustomContextMenu(const QPoint &point)
{
QModelIndex index = ui->allModsView->indexAt(point);
if(!index.isValid())
return;
const auto modName = index.data(ModRoles::ModNameRole).toString();
auto mod = modStateModel->getMod(modName);
auto contextMenu = new QMenu(tr("Context menu"), this);
QList<QAction*> actions;
auto addContextEntry = [this, &contextMenu, &actions, mod](bool visible, bool enabled, QIcon icon, QString name, std::function<void(ModState)> function){
if(!visible)
return;
actions.append(new QAction(name, this));
connect(actions.back(), &QAction::triggered, this, [mod, function](){ function(mod); });
contextMenu->addAction(actions.back());
actions.back()->setEnabled(enabled);
actions.back()->setIcon(icon);
};
auto state = buttonEnabledState(modName, mod);
addContextEntry(
state.disableVisible, state.disableEnabled, QIcon{":/icons/mod-disabled.png"},
tr("Disable"),
[this](ModState mod){ disableModByName(mod.getID()); }
);
addContextEntry(
state.enableVisible, state.enableEnabled, QIcon{":/icons/mod-enabled.png"},
tr("Enable"),
[this](ModState mod){ enableModByName(mod.getID());
});
addContextEntry(
state.installVisible, state.installEnabled, QIcon{":/icons/mod-download.png"},
tr("Install"),
[this](ModState mod){ doInstallMod(mod.getID()); }
);
addContextEntry(
state.uninstallVisible, state.uninstallEnabled, QIcon{":/icons/mod-delete.png"},
tr("Uninstall"),
[this](ModState mod){ doUninstallMod(mod.getID()); }
);
addContextEntry(
state.updateVisible, state.updateEnabled, QIcon{":/icons/mod-update.png"},
tr("Update"),
[this](ModState mod){ doUpdateMod(mod.getID()); }
);
addContextEntry(
state.directoryVisible, state.directoryEnabled, QIcon{":/icons/menu-mods.png"},
tr("Open directory"),
[this](ModState mod){ openModDictionary(mod.getID()); }
);
addContextEntry(
state.repositoryVisible, state.repositoryEnabled, QIcon{":/icons/about-project.png"},
tr("Open repository"),
[](ModState mod){
QUrl url(mod.getDownloadUrl());
QString repoUrl = QString("%1://%2/%3/%4")
.arg(url.scheme())
.arg(url.host())
.arg(url.path().split('/')[1])
.arg(url.path().split('/')[2]);
QDesktopServices::openUrl(repoUrl);
}
);
contextMenu->exec(ui->allModsView->viewport()->mapToGlobal(point));
}
void CModListView::dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight)
{
selectMod(ui->allModsView->currentIndex());
@@ -444,23 +569,20 @@ void CModListView::selectMod(const QModelIndex & index)
Helper::enableScrollBySwiping(ui->modInfoBrowser);
Helper::enableScrollBySwiping(ui->changelogBrowser);
QStringList notInstalledDependencies = getModsToInstall(modName);
QStringList unavailableDependencies = findUnavailableMods(notInstalledDependencies);
bool translationMismatch = mod.isTranslation() && CGeneralTextHandler::getPreferredLanguage() != mod.getBaseLanguage().toStdString();
bool modIsBeingDownloaded = enqueuedModDownloads.contains(mod.getID());
auto state = buttonEnabledState(modName, mod);
ui->disableButton->setVisible(modStateModel->isModInstalled(mod.getID()) && modStateModel->isModEnabled(mod.getID()));
ui->enableButton->setVisible(modStateModel->isModInstalled(mod.getID()) && !modStateModel->isModEnabled(mod.getID()));
ui->installButton->setVisible(mod.isAvailable() && !mod.isSubmod());
ui->uninstallButton->setVisible(mod.isInstalled() && !mod.isSubmod());
ui->updateButton->setVisible(mod.isUpdateAvailable());
ui->disableButton->setVisible(state.disableVisible);
ui->enableButton->setVisible(state.enableVisible);
ui->installButton->setVisible(state.installVisible);
ui->uninstallButton->setVisible(state.uninstallVisible);
ui->updateButton->setVisible(state.updateVisible);
// Block buttons if action is not allowed at this time
ui->disableButton->setEnabled(true);
ui->enableButton->setEnabled(notInstalledDependencies.empty() && !translationMismatch);
ui->installButton->setEnabled(unavailableDependencies.empty() && !modIsBeingDownloaded);
ui->uninstallButton->setEnabled(true);
ui->updateButton->setEnabled(unavailableDependencies.empty() && !modIsBeingDownloaded);
ui->disableButton->setEnabled(state.disableEnabled);
ui->enableButton->setEnabled(state.enableEnabled);
ui->installButton->setEnabled(state.installEnabled);
ui->uninstallButton->setEnabled(state.uninstallEnabled);
ui->updateButton->setEnabled(state.updateEnabled);
loadScreenshots();
}
@@ -602,17 +724,23 @@ void CModListView::doUpdateMod(const QString & modName)
}
}
void CModListView::openModDictionary(const QString & modName)
{
QString tmp = modName;
tmp.replace(".", "/Mods/");
ResourcePath resID(std::string("Mods/") + tmp.toStdString(), EResType::DIRECTORY);
// Get location of the mod, in case-insensitive way
QString modDir = pathToQString(*CResourceHandler::get()->getResourceName(resID));
Helper::revealDirectoryInFileBrowser(modDir);
}
void CModListView::on_uninstallButton_clicked()
{
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
if(modStateModel->isModExists(modName) && modStateModel->getMod(modName).isInstalled())
{
if(modStateModel->isModEnabled(modName))
manager->disableMod(modName);
manager->uninstallMod(modName);
reload();
}
doUninstallMod(modName);
checkManagerErrors();
}
@@ -694,7 +822,7 @@ void CModListView::downloadFinished(QStringList savedFiles, QStringList failedFi
{
// some mods were not downloaded
int result = QMessageBox::warning (this, title, firstLine + errors.join("\n") + lastLine,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No );
QMessageBox::Yes | QMessageBox::No, QMessageBox::No );
if(result == QMessageBox::Yes)
doInstallFiles = true;
@@ -1013,6 +1141,17 @@ void CModListView::doInstallMod(const QString & modName)
}
}
void CModListView::doUninstallMod(const QString & modName)
{
if(modStateModel->isModExists(modName) && modStateModel->getMod(modName).isInstalled())
{
if(modStateModel->isModEnabled(modName))
manager->disableMod(modName);
manager->uninstallMod(modName);
reload();
}
}
bool CModListView::isModAvailable(const QString & modName)
{
return modStateModel->isModExists(modName) && !modStateModel->isModInstalled(modName);

View File

@@ -64,6 +64,8 @@ class CModListView : public QWidget
void changeEvent(QEvent *event) override;
auto buttonEnabledState(QString modName, ModState & mod);
public:
explicit CModListView(QWidget * parent = nullptr);
~CModListView();
@@ -82,9 +84,15 @@ public:
/// install mod by name
void doInstallMod(const QString & modName);
/// uninstall mod by name
void doUninstallMod(const QString & modName);
/// update mod by name
void doUpdateMod(const QString & modName);
/// open mod dictionary by name
void openModDictionary(const QString & modName);
/// returns true if mod is available in repository and can be installed
bool isModAvailable(const QString & modName);
@@ -123,6 +131,7 @@ public slots:
void disableModByName(QString modName);
private slots:
void onCustomContextMenu(const QPoint &point);
void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight);
void modSelected(const QModelIndex & current, const QModelIndex & previous);
void downloadProgress(qint64 current, qint64 max);