瀏覽代碼

Merge pull request #978 from kambala-decapitator/hide-unsupported-resolutions

Hide unsupported resolutions
Andrii Danylchenko 3 年之前
父節點
當前提交
a2dbb6c8ac

+ 0 - 4
client/lobby/CSavingScreen.cpp

@@ -44,10 +44,6 @@ CSavingScreen::CSavingScreen()
 	buttonStart->assignedKeys.insert(SDLK_RETURN);
 }
 
-CSavingScreen::~CSavingScreen()
-{
-}
-
 const CMapInfo * CSavingScreen::getMapInfo()
 {
 	return localMi.get();

+ 0 - 1
client/lobby/CSavingScreen.h

@@ -27,7 +27,6 @@ public:
 	std::shared_ptr<CMapInfo> localMi;
 
 	CSavingScreen();
-	~CSavingScreen();
 
 	void changeSelection(std::shared_ptr<CMapInfo> to);
 	void saveGame();

+ 14 - 3
client/windows/GUIClasses.cpp

@@ -562,10 +562,21 @@ void CSystemOptionsWindow::selectGameRes()
 	std::vector<std::string> items;
 	const JsonNode & texts = CGI->generaltexth->localizedTexts["systemOptions"]["resolutionMenu"];
 
-	for( config::CConfigHandler::GuiOptionsMap::value_type& value : conf.guiOptions)
+#ifndef VCMI_IOS
+	SDL_Rect displayBounds;
+	SDL_GetDisplayBounds(std::max(0, SDL_GetWindowDisplayIndex(mainWindow)), &displayBounds);
+#endif
+
+	for(const auto & it : conf.guiOptions)
 	{
-		std::string resX = boost::lexical_cast<std::string>(value.first.first);
-		std::string resY = boost::lexical_cast<std::string>(value.first.second);
+		const auto & resolution = it.first;
+#ifndef VCMI_IOS
+		if(displayBounds.w < resolution.first || displayBounds.h < resolution.second)
+			continue;
+#endif
+
+		std::string resX = boost::lexical_cast<std::string>(resolution.first);
+		std::string resY = boost::lexical_cast<std::string>(resolution.second);
 		items.push_back(resX + 'x' + resY);
 	}
 

+ 5 - 1
config/schemas/settings.json

@@ -363,7 +363,7 @@
 			"type" : "object",
 			"default": {},
 			"additionalProperties" : false,
-			"required" : [ "repositoryURL", "enableInstalledMods", "autoCheckRepositories", "updateOnStartup", "updateConfigUrl" ],
+			"required" : [ "repositoryURL", "enableInstalledMods", "extraResolutionsModPath", "autoCheckRepositories", "updateOnStartup", "updateConfigUrl" ],
 			"properties" : {
 				"repositoryURL" : {
 					"type" : "array",
@@ -378,6 +378,10 @@
 					"type" : "boolean",
 					"default" : true
 				},
+				"extraResolutionsModPath" : {
+					"type" : "string",
+					"default" : "/vcmi-extras/Mods/extraResolutions/Content/config/resolutions.json"
+				},
 				"autoCheckRepositories" : {
 					"type" : "boolean",
 					"default" : true

+ 13 - 19
launcher/jsonutils.cpp

@@ -16,7 +16,7 @@ static QVariantMap JsonToMap(const JsonMap & json)
 	QVariantMap map;
 	for(auto & entry : json)
 	{
-		map.insert(QString::fromUtf8(entry.first.c_str()), JsonUtils::toVariant(entry.second));
+		map.insert(QString::fromStdString(entry.first), JsonUtils::toVariant(entry.second));
 	}
 	return map;
 }
@@ -60,22 +60,18 @@ QVariant toVariant(const JsonNode & node)
 {
 	switch(node.getType())
 	{
-		break;
 	case JsonNode::JsonType::DATA_NULL:
 		return QVariant();
-		break;
 	case JsonNode::JsonType::DATA_BOOL:
 		return QVariant(node.Bool());
-		break;
 	case JsonNode::JsonType::DATA_FLOAT:
 		return QVariant(node.Float());
-		break;
+	case JsonNode::JsonType::DATA_INTEGER:
+		return QVariant{static_cast<qlonglong>(node.Integer())};
 	case JsonNode::JsonType::DATA_STRING:
-		return QVariant(QString::fromUtf8(node.String().c_str()));
-		break;
+		return QVariant(QString::fromStdString(node.String()));
 	case JsonNode::JsonType::DATA_VECTOR:
 		return JsonToList(node.Vector());
-		break;
 	case JsonNode::JsonType::DATA_STRUCT:
 		return JsonToMap(node.Struct());
 	}
@@ -85,19 +81,15 @@ QVariant toVariant(const JsonNode & node)
 QVariant JsonFromFile(QString filename)
 {
 	QFile file(filename);
-	file.open(QFile::ReadOnly);
-	auto data = file.readAll();
-
-	if(data.size() == 0)
+	if(!file.open(QFile::ReadOnly))
 	{
-		logGlobal->error("Failed to open file %s", filename.toUtf8().data());
-		return QVariant();
-	}
-	else
-	{
-		JsonNode node(data.data(), data.size());
-		return toVariant(node);
+		logGlobal->error("Failed to open file %s. Reason: %s", qUtf8Printable(filename), qUtf8Printable(file.errorString()));
+		return {};
 	}
+
+	const auto data = file.readAll();
+	JsonNode node(data.data(), data.size());
+	return toVariant(node);
 }
 
 JsonNode toJson(QVariant object)
@@ -112,6 +104,8 @@ JsonNode toJson(QVariant object)
 		ret.String() = object.toString().toUtf8().data();
 	else if(object.userType() == QMetaType::Bool)
 		ret.Bool() = object.toBool();
+	else if(object.canConvert<int>())
+		ret.Integer() = object.toInt();
 	else if(object.canConvert<double>())
 		ret.Float() = object.toFloat();
 

+ 4 - 0
launcher/mainwindow_moc.cpp

@@ -84,7 +84,11 @@ MainWindow::MainWindow(QWidget * parent)
 		ui->tabSelectList->setMaximumWidth(width + 4);
 	}
 	ui->tabListWidget->setCurrentIndex(0);
+
+	ui->settingsView->isExtraResolutionsModEnabled = ui->stackedWidgetPage2->isExtraResolutionsModEnabled();
 	ui->settingsView->setDisplayList();
+	connect(ui->stackedWidgetPage2, &CModListView::extraResolutionsEnabledChanged,
+		ui->settingsView, &CSettingsView::fillValidResolutions);
 
 	connect(ui->tabSelectList, SIGNAL(currentRowChanged(int)),
 		ui->tabListWidget, SLOT(setCurrentIndex(int)));

+ 8 - 0
launcher/modManager/cmodlistview_moc.cpp

@@ -28,6 +28,9 @@ void CModListView::setupModModel()
 {
 	modModel = new CModListModel(this);
 	manager = vstd::make_unique<CModManager>(modModel);
+
+	connect(manager.get(), &CModManager::extraResolutionsEnabledChanged,
+		this, &CModListView::extraResolutionsEnabledChanged);
 }
 
 void CModListView::setupFilterModel()
@@ -320,6 +323,11 @@ void CModListView::selectMod(const QModelIndex & index)
 	}
 }
 
+bool CModListView::isExtraResolutionsModEnabled() const
+{
+	return manager->isExtraResolutionsModEnabled();
+}
+
 void CModListView::keyPressEvent(QKeyEvent * event)
 {
 	if(event->key() == Qt::Key_Escape && ui->modInfoWidget->isVisible())

+ 4 - 0
launcher/modManager/cmodlistview_moc.h

@@ -63,6 +63,9 @@ class CModListView : public QWidget
 	QString genChangelogText(CModEntry & mod);
 	QString genModInfoText(CModEntry & mod);
 
+signals:
+	void extraResolutionsEnabledChanged(bool enabled);
+
 public:
 	explicit CModListView(QWidget * parent = 0);
 	~CModListView();
@@ -75,6 +78,7 @@ public:
 	void disableModInfo();
 
 	void selectMod(const QModelIndex & index);
+	bool isExtraResolutionsModEnabled() const;
 
 private slots:
 	void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight);

+ 29 - 4
launcher/modManager/cmodmanager.cpp

@@ -18,7 +18,11 @@
 #include "../jsonutils.h"
 #include "../launcherdirs.h"
 
-static QString detectModArchive(QString path, QString modName)
+namespace
+{
+const QLatin1String extraResolutionsMod{"vcmi-extras.extraresolutions"};
+
+QString detectModArchive(QString path, QString modName)
 {
 	auto files = ZipArchive::listFiles(qstringToPath(path));
 
@@ -40,6 +44,8 @@ static QString detectModArchive(QString path, QString modName)
 	
 	return "";
 }
+}
+
 
 CModManager::CModManager(CModList * modList)
 	: modList(modList)
@@ -219,6 +225,11 @@ bool CModManager::canDisableMod(QString modname)
 	return true;
 }
 
+bool CModManager::isExtraResolutionsModEnabled() const
+{
+	return modList->hasMod(extraResolutionsMod) && modList->getMod(extraResolutionsMod).isEnabled();
+}
+
 static QVariant writeValue(QString path, QVariantMap input, QVariant value)
 {
 	if(path.size() > 1)
@@ -246,6 +257,9 @@ bool CModManager::doEnableMod(QString mod, bool on)
 	modList->setModSettings(modSettings["activeMods"]);
 	modList->modChanged(mod);
 
+	if(mod == extraResolutionsMod)
+		sendExtraResolutionsEnabledChanged(on);
+
 	JsonUtils::JsonToFile(settingsPath(), modSettings);
 
 	return true;
@@ -261,7 +275,7 @@ bool CModManager::doInstallMod(QString modname, QString archivePath)
 	if(localMods.contains(modname))
 		return addError(modname, "Mod with such name is already installed");
 
-	QString modDirName = detectModArchive(archivePath, modname);
+	QString modDirName = ::detectModArchive(archivePath, modname);
 	if(!modDirName.size())
 		return addError(modname, "Mod archive is invalid or corrupted");
 
@@ -286,12 +300,15 @@ bool CModManager::doInstallMod(QString modname, QString archivePath)
 	loadMods();
 	modList->reloadRepositories();
 
+	if(modname == extraResolutionsMod)
+		sendExtraResolutionsEnabledChanged(true);
+
 	return true;
 }
 
 bool CModManager::doUninstallMod(QString modname)
 {
-	ResourceID resID(std::string("Mods/") + modname.toUtf8().data(), EResType::DIRECTORY);
+	ResourceID resID(std::string("Mods/") + modname.toStdString(), EResType::DIRECTORY);
 	// Get location of the mod, in case-insensitive way
 	QString modDir = pathToQString(*CResourceHandler::get()->getResourceName(resID));
 
@@ -300,12 +317,15 @@ bool CModManager::doUninstallMod(QString modname)
 
 	QDir modFullDir(modDir);
 	if(!removeModDir(modDir))
-		return addError(modname, "Mod is located in protected directory, plase remove it manually:\n" + modFullDir.absolutePath());
+		return addError(modname, "Mod is located in protected directory, please remove it manually:\n" + modFullDir.absolutePath());
 
 	CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &){ return true; });
 	loadMods();
 	modList->reloadRepositories();
 
+	if(modname == extraResolutionsMod)
+		sendExtraResolutionsEnabledChanged(false);
+
 	return true;
 }
 
@@ -329,3 +349,8 @@ bool CModManager::removeModDir(QString path)
 
 	return dir.removeRecursively();
 }
+
+void CModManager::sendExtraResolutionsEnabledChanged(bool enabled)
+{
+	emit extraResolutionsEnabledChanged(enabled);
+}

+ 10 - 1
launcher/modManager/cmodmanager.h

@@ -11,8 +11,10 @@
 
 #include "cmodlist.h"
 
-class CModManager
+class CModManager : public QObject
 {
+	Q_OBJECT
+
 	CModList * modList;
 
 	QString settingsPath();
@@ -29,6 +31,11 @@ class CModManager
 	bool addError(QString modname, QString message);
 	bool removeModDir(QString mod);
 
+	void sendExtraResolutionsEnabledChanged(bool enabled);
+
+signals:
+	void extraResolutionsEnabledChanged(bool enabled);
+
 public:
 	CModManager(CModList * modList);
 
@@ -51,4 +58,6 @@ public:
 	bool canUninstallMod(QString mod);
 	bool canEnableMod(QString mod);
 	bool canDisableMod(QString mod);
+
+	bool isExtraResolutionsModEnabled() const;
 };

+ 80 - 22
launcher/settingsView/csettingsview_moc.cpp

@@ -11,6 +11,8 @@
 #include "csettingsview_moc.h"
 #include "ui_csettingsview_moc.h"
 
+#include "../jsonutils.h"
+#include "../launcherdirs.h"
 #include "../updatedialog_moc.h"
 
 #include <QFileInfo>
@@ -19,6 +21,14 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/VCMIDirs.h"
 
+namespace
+{
+QString resolutionToString(const QSize & resolution)
+{
+	return QString{"%1x%2"}.arg(resolution.width()).arg(resolution.height());
+}
+}
+
 /// List of encoding which can be selected from Launcher.
 /// Note that it is possible to specify enconding manually in settings.json
 static const std::string knownEncodingsList[] = //TODO: remove hardcode
@@ -39,34 +49,25 @@ void CSettingsView::setDisplayList()
 	QStringList list;
 
 	for (const auto screen : QGuiApplication::screens())
-	{
-		QString string;
-		const auto & rect = screen->geometry();
-		QTextStream(&string) << screen->name() << " - " << rect.width() << "x" << rect.height();
-		list << string;
-	}
+		list << QString{"%1 - %2"}.arg(screen->name(), resolutionToString(screen->size()));
 
 	if(list.count() < 2)
 	{
 		ui->comboBoxDisplayIndex->hide();
 		ui->labelDisplayIndex->hide();
+		fillValidResolutionsForScreen(0);
 	}
 	else
 	{
 		int displayIndex = settings["video"]["displayIndex"].Integer();
-		ui->comboBoxDisplayIndex->clear();
 		ui->comboBoxDisplayIndex->addItems(list);
+		// calls fillValidResolutions() in slot
 		ui->comboBoxDisplayIndex->setCurrentIndex(displayIndex);
 	}
 }
 
 void CSettingsView::loadSettings()
 {
-	int resX = settings["video"]["screenRes"]["width"].Float();
-	int resY = settings["video"]["screenRes"]["height"].Float();
-	int resIndex = ui->comboBoxResolution->findText(QString("%1x%2").arg(resX).arg(resY));
-
-	ui->comboBoxResolution->setCurrentIndex(resIndex);
 	ui->comboBoxShowIntro->setCurrentIndex(settings["video"]["showIntro"].Bool());
 
 #ifdef Q_OS_IOS
@@ -79,15 +80,10 @@ void CSettingsView::loadSettings()
 	ui->checkBoxFullScreen->setChecked(settings["video"]["realFullscreen"].Bool());
 #endif
 
-	int friendlyAIIndex = ui->comboBoxFriendlyAI->findText(QString::fromUtf8(settings["server"]["friendlyAI"].String().c_str()));
-	int neutralAIIndex = ui->comboBoxNeutralAI->findText(QString::fromUtf8(settings["server"]["neutralAI"].String().c_str()));
-	int enemyAIIndex = ui->comboBoxEnemyAI->findText(QString::fromUtf8(settings["server"]["enemyAI"].String().c_str()));
-	int playerAIIndex = ui->comboBoxPlayerAI->findText(QString::fromUtf8(settings["server"]["playerAI"].String().c_str()));
-
-	ui->comboBoxFriendlyAI->setCurrentIndex(friendlyAIIndex);
-	ui->comboBoxNeutralAI->setCurrentIndex(neutralAIIndex);
-	ui->comboBoxEnemyAI->setCurrentIndex(enemyAIIndex);
-	ui->comboBoxPlayerAI->setCurrentIndex(playerAIIndex);
+	ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String()));
+	ui->comboBoxNeutralAI->setCurrentText(QString::fromStdString(settings["server"]["neutralAI"].String()));
+	ui->comboBoxEnemyAI->setCurrentText(QString::fromStdString(settings["server"]["enemyAI"].String()));
+	ui->comboBoxPlayerAI->setCurrentText(QString::fromStdString(settings["server"]["playerAI"].String()));
 
 	ui->spinBoxNetworkPort->setValue(settings["server"]["port"].Integer());
 
@@ -110,6 +106,66 @@ void CSettingsView::loadSettings()
 	ui->comboBoxAutoSave->setCurrentIndex(settings["general"]["saveFrequency"].Integer() > 0 ? 1 : 0);
 }
 
+void CSettingsView::fillValidResolutions(bool isExtraResolutionsModEnabled)
+{
+	this->isExtraResolutionsModEnabled = isExtraResolutionsModEnabled;
+	fillValidResolutionsForScreen(ui->comboBoxDisplayIndex->isVisible() ? ui->comboBoxDisplayIndex->currentIndex() : 0);
+}
+
+void CSettingsView::fillValidResolutionsForScreen(int screenIndex)
+{
+	ui->comboBoxResolution->blockSignals(true); // avoid saving wrong resolution after adding first item from the list
+	ui->comboBoxResolution->clear();
+
+	// TODO: read available resolutions from all mods
+	QVariantList resolutions;
+	if(isExtraResolutionsModEnabled)
+	{
+		const auto extrasResolutionsPath = settings["launcher"]["extraResolutionsModPath"].String().c_str();
+		const auto extrasResolutionsJson = JsonUtils::JsonFromFile(CLauncherDirs::get().modsPath() + extrasResolutionsPath);
+		resolutions = extrasResolutionsJson.toMap().value(QLatin1String{"GUISettings"}).toList();
+	}
+	if(resolutions.isEmpty())
+	{
+		ui->comboBoxResolution->blockSignals(false);
+		ui->comboBoxResolution->addItem(resolutionToString({800, 600}));
+		return;
+	}
+
+	const auto screens = qGuiApp->screens();
+	const auto currentScreen = screenIndex < screens.size() ? screens[screenIndex] : qGuiApp->primaryScreen();
+	const auto screenSize = currentScreen->size();
+	for(const auto & entry : resolutions)
+	{
+		const auto resolutionMap = entry.toMap().value(QLatin1String{"resolution"}).toMap();
+		if(resolutionMap.isEmpty())
+			continue;
+
+		const auto widthValue = resolutionMap[QLatin1String{"x"}];
+		const auto heightValue = resolutionMap[QLatin1String{"y"}];
+		if(!widthValue.isValid() || !heightValue.isValid())
+			continue;
+
+		const QSize resolution{widthValue.toInt(), heightValue.toInt()};
+#ifndef VCMI_IOS
+		if(screenSize.width() < resolution.width() || screenSize.height() < resolution.height())
+			continue;
+#endif
+		ui->comboBoxResolution->addItem(resolutionToString(resolution));
+	}
+
+	int resX = settings["video"]["screenRes"]["width"].Integer();
+	int resY = settings["video"]["screenRes"]["height"].Integer();
+	int resIndex = ui->comboBoxResolution->findText(resolutionToString({resX, resY}));
+	ui->comboBoxResolution->setCurrentIndex(resIndex);
+
+	ui->comboBoxResolution->blockSignals(false);
+
+	// if selected resolution no longer exists, force update value to the first resolution
+	if(resIndex == -1)
+		ui->comboBoxResolution->setCurrentIndex(0);
+}
+
 CSettingsView::CSettingsView(QWidget * parent)
 	: QWidget(parent), ui(new Ui::CSettingsView)
 {
@@ -124,7 +180,7 @@ CSettingsView::~CSettingsView()
 }
 
 
-void CSettingsView::on_comboBoxResolution_currentIndexChanged(const QString & arg1)
+void CSettingsView::on_comboBoxResolution_currentTextChanged(const QString & arg1)
 {
 	QStringList list = arg1.split("x");
 
@@ -155,6 +211,8 @@ void CSettingsView::on_comboBoxDisplayIndex_currentIndexChanged(int index)
 {
 	Settings node = settings.write["video"];
 	node["displayIndex"].Float() = index;
+
+	fillValidResolutionsForScreen(index);
 }
 
 void CSettingsView::on_comboBoxPlayerAI_currentIndexChanged(const QString & arg1)

+ 8 - 1
launcher/settingsView/csettingsview_moc.h

@@ -26,10 +26,15 @@ public:
 	void loadSettings();
 	void setDisplayList();
 
+	bool isExtraResolutionsModEnabled{};
+
+public slots:
+	void fillValidResolutions(bool isExtraResolutionsModEnabled);
+
 private slots:
 	void on_checkBoxFullScreen_stateChanged(int state);
 
-	void on_comboBoxResolution_currentIndexChanged(const QString & arg1);
+	void on_comboBoxResolution_currentTextChanged(const QString & arg1);
 
 	void on_comboBoxFullScreen_currentIndexChanged(int index);
 
@@ -67,4 +72,6 @@ private slots:
 
 private:
 	Ui::CSettingsView * ui;
+
+	void fillValidResolutionsForScreen(int screenIndex);
 };

+ 2 - 82
launcher/settingsView/csettingsview_moc.ui

@@ -333,81 +333,7 @@
     </widget>
    </item>
    <item row="1" column="4">
-    <widget class="QComboBox" name="comboBoxResolution">
-     <property name="maxVisibleItems">
-      <number>11</number>
-     </property>
-     <item>
-      <property name="text">
-       <string>800x600</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1024x600</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1024x768</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1181x664</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1280x720</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1280x768</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1280x800</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1280x960</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1280x1024</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1366x768</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1440x900</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1600x1200</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1680x1050</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>1920x1080</string>
-      </property>
-     </item>
-    </widget>
+    <widget class="QComboBox" name="comboBoxResolution"/>
    </item>
    <item row="11" column="1">
     <widget class="QLabel" name="labelNeutralAI">
@@ -417,13 +343,7 @@
     </widget>
    </item>
    <item row="4" column="4">
-    <widget class="QComboBox" name="comboBoxDisplayIndex">
-     <item>
-      <property name="text">
-       <string>0</string>
-      </property>
-     </item>
-    </widget>
+    <widget class="QComboBox" name="comboBoxDisplayIndex"/>
    </item>
    <item row="7" column="1" colspan="4">
     <spacer name="spacerSections">