diff --git a/client/lobby/CSavingScreen.cpp b/client/lobby/CSavingScreen.cpp index 7632ac691..06071c533 100644 --- a/client/lobby/CSavingScreen.cpp +++ b/client/lobby/CSavingScreen.cpp @@ -44,10 +44,6 @@ CSavingScreen::CSavingScreen() buttonStart->assignedKeys.insert(SDLK_RETURN); } -CSavingScreen::~CSavingScreen() -{ -} - const CMapInfo * CSavingScreen::getMapInfo() { return localMi.get(); diff --git a/client/lobby/CSavingScreen.h b/client/lobby/CSavingScreen.h index ac966a457..b7ac05012 100644 --- a/client/lobby/CSavingScreen.h +++ b/client/lobby/CSavingScreen.h @@ -27,7 +27,6 @@ public: std::shared_ptr localMi; CSavingScreen(); - ~CSavingScreen(); void changeSelection(std::shared_ptr to); void saveGame(); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 2afd8c033..445145bb6 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -562,10 +562,21 @@ void CSystemOptionsWindow::selectGameRes() std::vector 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(value.first.first); - std::string resY = boost::lexical_cast(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(resolution.first); + std::string resY = boost::lexical_cast(resolution.second); items.push_back(resX + 'x' + resY); } diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 66d39b630..85e4d4463 100644 --- a/config/schemas/settings.json +++ b/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 diff --git a/launcher/jsonutils.cpp b/launcher/jsonutils.cpp index ccf7bd629..9b144cb8a 100644 --- a/launcher/jsonutils.cpp +++ b/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(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(!file.open(QFile::ReadOnly)) + { + logGlobal->error("Failed to open file %s. Reason: %s", qUtf8Printable(filename), qUtf8Printable(file.errorString())); + return {}; + } - if(data.size() == 0) - { - logGlobal->error("Failed to open file %s", filename.toUtf8().data()); - return QVariant(); - } - else - { - JsonNode node(data.data(), data.size()); - return toVariant(node); - } + 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()) + ret.Integer() = object.toInt(); else if(object.canConvert()) ret.Float() = object.toFloat(); diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp index 969b55747..5bc095e48 100644 --- a/launcher/mainwindow_moc.cpp +++ b/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))); diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 63d0d36e1..423822999 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -28,6 +28,9 @@ void CModListView::setupModModel() { modModel = new CModListModel(this); manager = vstd::make_unique(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()) diff --git a/launcher/modManager/cmodlistview_moc.h b/launcher/modManager/cmodlistview_moc.h index dd80ef860..2cc93f64e 100644 --- a/launcher/modManager/cmodlistview_moc.h +++ b/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); diff --git a/launcher/modManager/cmodmanager.cpp b/launcher/modManager/cmodmanager.cpp index 9b59e2ec9..f78d6baab 100644 --- a/launcher/modManager/cmodmanager.cpp +++ b/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); +} diff --git a/launcher/modManager/cmodmanager.h b/launcher/modManager/cmodmanager.h index 1c0401a94..0ce64c101 100644 --- a/launcher/modManager/cmodmanager.h +++ b/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; }; diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index 95c2a872a..d5ebbbb2a 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/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 @@ -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) diff --git a/launcher/settingsView/csettingsview_moc.h b/launcher/settingsView/csettingsview_moc.h index b2084a78a..fba5bd2c6 100644 --- a/launcher/settingsView/csettingsview_moc.h +++ b/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); }; diff --git a/launcher/settingsView/csettingsview_moc.ui b/launcher/settingsView/csettingsview_moc.ui index ece3135b5..a9e05775d 100644 --- a/launcher/settingsView/csettingsview_moc.ui +++ b/launcher/settingsView/csettingsview_moc.ui @@ -333,81 +333,7 @@ - - - 11 - - - - 800x600 - - - - - 1024x600 - - - - - 1024x768 - - - - - 1181x664 - - - - - 1280x720 - - - - - 1280x768 - - - - - 1280x800 - - - - - 1280x960 - - - - - 1280x1024 - - - - - 1366x768 - - - - - 1440x900 - - - - - 1600x1200 - - - - - 1680x1050 - - - - - 1920x1080 - - - + @@ -417,13 +343,7 @@ - - - - 0 - - - +