浏览代码

Merge pull request #5430 from Laserlicht/context

[1.6.6] Context menu for modlist
Ivan Savenko 8 月之前
父节点
当前提交
aef04236b9

+ 5 - 22
launcher/aboutProject/aboutproject_moc.cpp

@@ -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()

+ 14 - 0
launcher/helper.cpp

@@ -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
+}
 }

+ 1 - 0
launcher/helper.h

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

+ 161 - 22
launcher/modManager/cmodlistview_moc.cpp

@@ -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);

+ 9 - 0
launcher/modManager/cmodlistview_moc.h

@@ -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);