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

Merge pull request #5569 from Laserlicht/configeditor

Editor for JSON config in launcher
This commit is contained in:
Ivan Savenko
2025-03-26 17:28:26 +02:00
committed by GitHub
12 changed files with 385 additions and 57 deletions

View File

@@ -14,6 +14,7 @@ set(launcher_SRCS
modManager/imageviewer_moc.cpp
modManager/chroniclesextractor.cpp
settingsView/csettingsview_moc.cpp
settingsView/configeditordialog_moc.cpp
startGame/StartGameTab.cpp
firstLaunch/firstlaunch_moc.cpp
main.cpp
@@ -45,6 +46,7 @@ set(launcher_HEADERS
modManager/modstate.h
modManager/imageviewer_moc.h
modManager/chroniclesextractor.h
settingsView/configeditordialog_moc.h
settingsView/csettingsview_moc.h
startGame/StartGameTab.h
firstLaunch/firstlaunch_moc.h
@@ -61,6 +63,7 @@ set(launcher_FORMS
aboutProject/aboutproject_moc.ui
modManager/cmodlistview_moc.ui
modManager/imageviewer_moc.ui
settingsView/configeditordialog_moc.ui
settingsView/csettingsview_moc.ui
firstLaunch/firstlaunch_moc.ui
mainwindow_moc.ui

View File

@@ -10,6 +10,10 @@
#include "StdInc.h"
#include "helper.h"
#include "mainwindow_moc.h"
#include "settingsView/csettingsview_moc.h"
#include "modManager/cmodlistview_moc.h"
#include "../lib/CConfigHandler.h"
#include <QObject>
@@ -45,6 +49,19 @@ void loadSettings()
persistentStorage.init("config/persistentStorage.json", "");
}
void reLoadSettings()
{
loadSettings();
for(const auto widget : qApp->allWidgets())
if(auto settingsView = qobject_cast<CSettingsView *>(widget))
{
settingsView->loadSettings();
break;
}
getMainWindow()->updateTranslation();
getMainWindow()->getModView()->reload();
}
void enableScrollBySwiping(QObject * scrollTarget)
{
#ifdef VCMI_MOBILE
@@ -89,4 +106,12 @@ void revealDirectoryInFileBrowser(QString path)
QDesktopServices::openUrl(dirUrl);
#endif
}
MainWindow * getMainWindow()
{
foreach(QWidget *w, qApp->allWidgets())
if(auto mainWin = qobject_cast<MainWindow*>(w))
return mainWin;
return nullptr;
}
}

View File

@@ -12,12 +12,15 @@
#include <QString>
class QObject;
class MainWindow;
namespace Helper
{
void loadSettings();
void reLoadSettings();
void enableScrollBySwiping(QObject * scrollTarget);
QString getRealPath(QString path);
void performNativeCopy(QString src, QString dst);
void revealDirectoryInFileBrowser(QString path);
MainWindow * getMainWindow();
}

View File

@@ -297,13 +297,7 @@ void MainWindow::manualInstallFile(QString filePath)
QFile::remove(configFilePath);
QFile::copy(filePath, configFilePath);
// reload settings
Helper::loadSettings();
for(const auto widget : qApp->allWidgets())
if(auto settingsView = qobject_cast<CSettingsView *>(widget))
settingsView->loadSettings();
getModView()->reload();
Helper::reLoadSettings();
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* configeditordialog_moc.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "configeditordialog_moc.h"
#include "ui_configeditordialog_moc.h"
#include "../helper.h"
#include "../mainwindow_moc.h"
#include "../../lib/json/JsonNode.h"
#include "../../lib/VCMIDirs.h"
#include "../../lib/json/JsonFormatException.h"
ConfigEditorDialog::ConfigEditorDialog(QWidget *parent):
QDialog(parent),
ui(new Ui::ConfigEditorDialog)
{
ui->setupUi(this);
setWindowTitle(tr("Config editor"));
setWindowModality(Qt::ApplicationModal);
#ifdef VCMI_MOBILE
showMaximized();
#else
show();
#endif
QStringList files = {
"settings.json",
"persistentStorage.json",
"modSettings.json",
};
ui->comboBox->addItems(files);
QFont font("Monospace");
font.setStyleHint(QFont::TypeWriter);
ui->plainTextEdit->setFont(font);
ui->plainTextEdit->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap);
ui->plainTextEdit->setPlainText(loadFile(files.first()));
connect(ui->comboBox, &QComboBox::currentTextChanged, this, &ConfigEditorDialog::onComboBoxTextChanged);
connect(ui->closeButton, &QPushButton::clicked, this, &ConfigEditorDialog::onCloseButtonClicked);
connect(ui->saveButton, &QPushButton::clicked, this, &ConfigEditorDialog::onSaveButtonClicked);
}
ConfigEditorDialog::~ConfigEditorDialog()
{
delete ui;
}
void ConfigEditorDialog::showConfigEditorDialog()
{
auto * dialog = new ConfigEditorDialog();
dialog->setAttribute(Qt::WA_DeleteOnClose);
}
bool ConfigEditorDialog::askUnsavedChanges(QWidget *parent)
{
auto reply = QMessageBox::question(parent, tr("Unsaved changes"), tr("Do you want to discard changes?"), QMessageBox::Yes | QMessageBox::No);
return reply != QMessageBox::Yes;
}
void ConfigEditorDialog::onComboBoxTextChanged(QString filename)
{
auto inputText = ui->plainTextEdit->toPlainText();
if(filename == loadedFile)
return;
if(inputText != loadedText)
{
if(askUnsavedChanges(this))
{
ui->comboBox->setCurrentText(loadedFile);
return;
}
}
ui->plainTextEdit->setPlainText(loadFile(filename));
}
void ConfigEditorDialog::onCloseButtonClicked()
{
auto text = ui->plainTextEdit->toPlainText();
if(text != loadedText)
{
if(askUnsavedChanges(this))
return;
}
close();
}
void ConfigEditorDialog::onSaveButtonClicked()
{
auto text = ui->plainTextEdit->toPlainText();
auto filename = ui->comboBox->currentText();
if(text == loadedText)
{
close();
return;
}
auto stdText = text.toStdString();
try
{
JsonParsingSettings jsonSettings;
jsonSettings.strict = true;
JsonNode node(reinterpret_cast<const std::byte*>(stdText.data()), stdText.size(), jsonSettings, filename.toStdString());
}
catch(const JsonFormatException& e)
{
QMessageBox::critical(this, tr("JSON file is not valid!"), e.what());
return;
}
saveFile(filename, text);
Helper::reLoadSettings();
close();
}
QString ConfigEditorDialog::loadFile(QString filename)
{
auto file = pathToQString(VCMIDirs::get().userConfigPath() / filename.toStdString());
QFile f(file);
if(!f.open(QFile::ReadOnly | QFile::Text))
{
logGlobal->error("ConfigEditor can not open config for reading!");
return {};
}
loadedText = QString::fromUtf8(f.readAll());
loadedFile = filename;
return loadedText;
}
void ConfigEditorDialog::saveFile(QString filename, QString content)
{
auto file = pathToQString(VCMIDirs::get().userConfigPath() / filename.toStdString());
QFile f(file);
if(!f.open(QFile::WriteOnly | QFile::Text))
{
logGlobal->error("ConfigEditor can not open config for writing!");
return;
}
f.write(content.toUtf8());
}

View File

@@ -0,0 +1,47 @@
/*
* configeditordialog_moc.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include <QDialog>
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
VCMI_LIB_NAMESPACE_END
namespace Ui {
class ConfigEditorDialog;
}
class ConfigEditorDialog : public QDialog
{
Q_OBJECT
public:
explicit ConfigEditorDialog(QWidget *parent = nullptr);
~ConfigEditorDialog();
static void showConfigEditorDialog();
private slots:
void onComboBoxTextChanged(QString filename);
void onCloseButtonClicked();
void onSaveButtonClicked();
private:
Ui::ConfigEditorDialog *ui;
QString loadedText;
QString loadedFile;
bool askUnsavedChanges(QWidget *parent);
QString loadFile(QString filename);
void saveFile(QString filename, QString content);
};

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigEditorDialog</class>
<widget class="QDialog" name="ConfigEditorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>480</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>371</width>
<height>247</height>
</size>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item row="2" column="5">
<widget class="QPushButton" name="saveButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="5">
<widget class="QComboBox" name="comboBox"/>
</item>
<item row="1" column="0" colspan="6">
<widget class="QPlainTextEdit" name="plainTextEdit">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
<property name="plainText">
<string/>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelFile">
<property name="text">
<string>File:</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -12,6 +12,7 @@
#include "ui_csettingsview_moc.h"
#include "mainwindow_moc.h"
#include "configeditordialog_moc.h"
#include "../modManager/cmodlistview_moc.h"
#include "../helper.h"
@@ -54,14 +55,6 @@ static constexpr std::array downscalingFilterTypes =
"best"
};
MainWindow * CSettingsView::getMainWindow()
{
foreach(QWidget *w, qApp->allWidgets())
if(QMainWindow* mainWin = qobject_cast<QMainWindow*>(w))
return dynamic_cast<MainWindow *>(mainWin);
return nullptr;
}
void CSettingsView::setDisplayList()
{
QStringList list;
@@ -501,7 +494,7 @@ void CSettingsView::on_comboBoxLanguage_currentIndexChanged(int index)
QString selectedLanguage = ui->comboBoxLanguage->itemData(index).toString();
node->String() = selectedLanguage.toStdString();
getMainWindow()->updateTranslation();
Helper::getMainWindow()->updateTranslation();
}
void CSettingsView::changeEvent(QEvent *event)
@@ -535,7 +528,7 @@ void CSettingsView::loadTranslation()
{
QString baseLanguage = Languages::getHeroesDataLanguage();
auto * mainWindow = getMainWindow();
auto * mainWindow = Helper::getMainWindow();
if (!mainWindow)
return;
@@ -568,7 +561,7 @@ void CSettingsView::loadTranslation()
void CSettingsView::on_pushButtonTranslation_clicked()
{
auto * mainWindow = getMainWindow();
auto * mainWindow = Helper::getMainWindow();
assert(mainWindow);
if (!mainWindow)
@@ -632,7 +625,7 @@ void CSettingsView::on_spinBoxInterfaceScaling_valueChanged(int arg1)
void CSettingsView::on_refreshRepositoriesButton_clicked()
{
auto * mainWindow = getMainWindow();
auto * mainWindow = Helper::getMainWindow();
assert(mainWindow);
if (!mainWindow)
@@ -641,6 +634,11 @@ void CSettingsView::on_refreshRepositoriesButton_clicked()
mainWindow->getModView()->loadRepositories();
}
void CSettingsView::on_buttonConfigEditor_clicked()
{
ConfigEditorDialog::showConfigEditorDialog();
}
void CSettingsView::on_spinBoxFramerateLimit_valueChanged(int arg1)
{
Settings node = settings.write["video"]["targetfps"];

View File

@@ -60,6 +60,7 @@ private slots:
void on_lineEditRepositoryExtra_textEdited(const QString &arg1);
void on_spinBoxInterfaceScaling_valueChanged(int arg1);
void on_refreshRepositoriesButton_clicked();
void on_buttonConfigEditor_clicked();
void on_spinBoxFramerateLimit_valueChanged(int arg1);
void on_buttonVSync_toggled(bool value);
void on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1);

View File

@@ -76,6 +76,20 @@
</property>
</widget>
</item>
<item row="64" column="0">
<widget class="QLabel" name="labelConfigEditor">
<property name="text">
<string>Config editor</string>
</property>
</widget>
</item>
<item row="64" column="1" colspan="5">
<widget class="QPushButton" name="buttonConfigEditor">
<property name="text">
<string>Open editor</string>
</property>
</widget>
</item>
<item row="25" column="1" colspan="5">
<widget class="QComboBox" name="comboBoxUpscalingFilter">
<item>

View File

@@ -11,6 +11,7 @@
#include "StartGameTab.h"
#include "ui_StartGameTab.h"
#include "../helper.h"
#include "../mainwindow_moc.h"
#include "../main.h"
#include "../updatedialog_moc.h"
@@ -87,19 +88,11 @@ StartGameTab::~StartGameTab()
delete ui;
}
MainWindow * StartGameTab::getMainWindow()
{
foreach(QWidget *w, qApp->allWidgets())
if(QMainWindow* mainWin = qobject_cast<QMainWindow*>(w))
return dynamic_cast<MainWindow *>(mainWin);
return nullptr;
}
void StartGameTab::refreshState()
{
refreshGameData();
refreshUpdateStatus(EGameUpdateStatus::NOT_CHECKED);//TODO - follow automatic check on startup setting
refreshTranslation(getMainWindow()->getTranslationStatus());
refreshTranslation(Helper::getMainWindow()->getTranslationStatus());
refreshPresets();
refreshMods();
@@ -110,10 +103,10 @@ void StartGameTab::refreshPresets()
{
QSignalBlocker blocker(ui->comboBoxModPresets);
QStringList allPresets = getMainWindow()->getModView()->getAllPresets();
QStringList allPresets = Helper::getMainWindow()->getModView()->getAllPresets();
ui->comboBoxModPresets->clear();
ui->comboBoxModPresets->addItems(allPresets);
ui->comboBoxModPresets->setCurrentText(getMainWindow()->getModView()->getActivePreset());
ui->comboBoxModPresets->setCurrentText(Helper::getMainWindow()->getModView()->getActivePreset());
ui->buttonPresetDelete->setVisible(allPresets.size() > 1);
}
@@ -178,8 +171,8 @@ void StartGameTab::refreshTranslation(ETranslationStatus status)
void StartGameTab::refreshMods()
{
constexpr int chroniclesCount = 8;
QStringList updateableMods = getMainWindow()->getModView()->getUpdateableMods();
QStringList chroniclesMods = getMainWindow()->getModView()->getInstalledChronicles();
QStringList updateableMods = Helper::getMainWindow()->getModView()->getUpdateableMods();
QStringList chroniclesMods = Helper::getMainWindow()->getModView()->getInstalledChronicles();
ui->buttonUpdateMods->setText(tr("Update %n mods", "", updateableMods.size()));
ui->buttonUpdateMods->setVisible(!updateableMods.empty());
@@ -207,7 +200,7 @@ void StartGameTab::refreshUpdateStatus(EGameUpdateStatus status)
void StartGameTab::on_buttonGameStart_clicked()
{
getMainWindow()->hide();
Helper::getMainWindow()->hide();
startGame({});
}
@@ -228,7 +221,7 @@ void StartGameTab::on_buttonUpdateCheck_clicked()
void StartGameTab::on_buttonGameEditor_clicked()
{
getMainWindow()->hide();
Helper::getMainWindow()->hide();
startEditor({});
}
@@ -253,7 +246,7 @@ void StartGameTab::on_buttonImportFiles_clicked()
for(const auto & file : files)
{
logGlobal->info("Importing file %s", file.toStdString());
getMainWindow()->manualInstallFile(file);
Helper::getMainWindow()->manualInstallFile(file);
}
};
@@ -264,29 +257,29 @@ void StartGameTab::on_buttonImportFiles_clicked()
void StartGameTab::on_buttonInstallTranslation_clicked()
{
if (getMainWindow()->getTranslationStatus() == ETranslationStatus::NOT_INSTALLLED)
if (Helper::getMainWindow()->getTranslationStatus() == ETranslationStatus::NOT_INSTALLLED)
{
QString preferredlanguage = QString::fromStdString(settings["general"]["language"].String());
QString modName = getMainWindow()->getModView()->getTranslationModName(preferredlanguage);
getMainWindow()->getModView()->doInstallMod(modName);
QString modName = Helper::getMainWindow()->getModView()->getTranslationModName(preferredlanguage);
Helper::getMainWindow()->getModView()->doInstallMod(modName);
}
}
void StartGameTab::on_buttonActivateTranslation_clicked()
{
QString preferredlanguage = QString::fromStdString(settings["general"]["language"].String());
QString modName = getMainWindow()->getModView()->getTranslationModName(preferredlanguage);
getMainWindow()->getModView()->enableModByName(modName);
QString modName = Helper::getMainWindow()->getModView()->getTranslationModName(preferredlanguage);
Helper::getMainWindow()->getModView()->enableModByName(modName);
}
void StartGameTab::on_buttonUpdateMods_clicked()
{
QStringList updateableMods = getMainWindow()->getModView()->getUpdateableMods();
QStringList updateableMods = Helper::getMainWindow()->getModView()->getUpdateableMods();
getMainWindow()->switchToModsTab();
Helper::getMainWindow()->switchToModsTab();
for (const auto & modName : updateableMods)
getMainWindow()->getModView()->doUpdateMod(modName);
Helper::getMainWindow()->getModView()->doUpdateMod(modName);
}
void StartGameTab::on_buttonHelpImportFiles_clicked()
@@ -395,7 +388,7 @@ void StartGameTab::on_buttonMissingCampaignsHelp_clicked()
void StartGameTab::on_buttonPresetExport_clicked()
{
JsonNode presetJson = getMainWindow()->getModView()->exportCurrentPreset();
JsonNode presetJson = Helper::getMainWindow()->getModView()->exportCurrentPreset();
QString presetString = QString::fromStdString(presetJson.toCompactString());
QGuiApplication::clipboard()->setText(presetString);
@@ -408,8 +401,8 @@ void StartGameTab::on_buttonPresetImport_clicked()
QByteArray presetBytes(presetString.toUtf8());
JsonNode presetJson(reinterpret_cast<const std::byte*>(presetBytes.data()), presetBytes.size(), "imported preset");
getMainWindow()->getModView()->importPreset(presetJson);
getMainWindow()->switchToModsTab();
Helper::getMainWindow()->getModView()->importPreset(presetJson);
Helper::getMainWindow()->switchToModsTab();
refreshPresets();
}
@@ -427,8 +420,8 @@ void StartGameTab::on_buttonPresetNew_clicked()
if (ok && !presetName.isEmpty())
{
getMainWindow()->getModView()->createNewPreset(presetName);
getMainWindow()->getModView()->activatePreset(presetName);
Helper::getMainWindow()->getModView()->createNewPreset(presetName);
Helper::getMainWindow()->getModView()->activatePreset(presetName);
refreshPresets();
}
};
@@ -437,27 +430,27 @@ void StartGameTab::on_buttonPresetNew_clicked()
void StartGameTab::on_buttonPresetDelete_clicked()
{
QString activePresetBefore = getMainWindow()->getModView()->getActivePreset();
QStringList allPresets = getMainWindow()->getModView()->getAllPresets();
QString activePresetBefore = Helper::getMainWindow()->getModView()->getActivePreset();
QStringList allPresets = Helper::getMainWindow()->getModView()->getAllPresets();
allPresets.removeAll(activePresetBefore);
if (!allPresets.empty())
{
getMainWindow()->getModView()->activatePreset(allPresets.front());
getMainWindow()->getModView()->deletePreset(activePresetBefore);
Helper::getMainWindow()->getModView()->activatePreset(allPresets.front());
Helper::getMainWindow()->getModView()->deletePreset(activePresetBefore);
refreshPresets();
}
}
void StartGameTab::on_comboBoxModPresets_currentTextChanged(const QString &presetName)
{
getMainWindow()->getModView()->activatePreset(presetName);
Helper::getMainWindow()->getModView()->activatePreset(presetName);
}
void StartGameTab::on_buttonPresetRename_clicked()
{
const auto & functor = [this](){
QString currentName = getMainWindow()->getModView()->getActivePreset();
QString currentName = Helper::getMainWindow()->getModView()->getActivePreset();
bool ok;
QString newName = QInputDialog::getText(
@@ -470,7 +463,7 @@ void StartGameTab::on_buttonPresetRename_clicked()
if (ok && !newName.isEmpty() && newName != currentName)
{
getMainWindow()->getModView()->renamePreset(currentName, newName);
Helper::getMainWindow()->getModView()->renamePreset(currentName, newName);
refreshPresets();
}
};

View File

@@ -31,8 +31,6 @@ class StartGameTab : public QWidget
{
Q_OBJECT
MainWindow * getMainWindow();
void refreshUpdateStatus(EGameUpdateStatus status);
void refreshTranslation(ETranslationStatus status);
void refreshMods();