diff --git a/plugins/meson/settings/mesonconfigpage.cpp b/plugins/meson/settings/mesonconfigpage.cpp index a93fa449d2..18f3343ce7 100644 --- a/plugins/meson/settings/mesonconfigpage.cpp +++ b/plugins/meson/settings/mesonconfigpage.cpp @@ -1,356 +1,356 @@ /* This file is part of KDevelop Copyright 2018 Daniel Mensinger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mesonconfigpage.h" #include "mesonbuilder.h" #include "mesonjob.h" #include "mesonmanager.h" #include "mesonnewbuilddir.h" #include "mintro/mesonintrospectjob.h" #include "ui_mesonconfigpage.h" #include #include #include #include #include #include using namespace KDevelop; MesonConfigPage::MesonConfigPage(IPlugin* plugin, IProject* project, QWidget* parent) : ConfigPage(plugin, nullptr, parent) , m_project(project) { Q_ASSERT(project); // Catch errors early MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); Q_ASSERT(mgr); // This dialog only works with the MesonManager m_ui = new Ui::MesonConfigPage; m_ui->setupUi(this); m_ui->advanced->setSupportedBackends(mgr->supportedMesonBackends()); m_config = Meson::getMesonConfig(m_project); if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; reset(); return; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; } QStringList buildPathList; for (auto& i : m_config.buildDirs) { buildPathList << i.buildDir.toLocalFile(); } m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->clear(); m_ui->i_buildDirs->addItems(buildPathList); m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); reset(); } void MesonConfigPage::writeConfig() { qCDebug(KDEV_Meson) << "Writing config to file"; if (m_config.currentIndex >= 0) { m_config.buildDirs[m_config.currentIndex] = m_current; } if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; } Meson::writeMesonConfig(m_project, m_config); } void MesonConfigPage::apply() { qCDebug(KDEV_Meson) << "Applying meson config for build dir " << m_current.buildDir; readUI(); writeConfig(); if (m_config.currentIndex >= 0 && m_configChanged) { QList joblist; auto options = m_ui->options->options(); if (!options) { qCWarning(KDEV_Meson) << "Options is nullptr. Can not update meson config"; return; } QStringList mesonArgs = options->getMesonArgs(); if (mesonArgs.empty()) { qCDebug(KDEV_Meson) << "Config has not changed --> nothing has to be updated"; return; } // Check if a configuration is required auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend); if (status != MesonBuilder::MESON_CONFIGURED) { joblist << new MesonJob(m_current, m_project, MesonJob::CONFIGURE, mesonArgs, nullptr); } joblist << new MesonJob(m_current, m_project, MesonJob::SET_CONFIG, mesonArgs, nullptr); joblist << m_ui->options->repopulateFromBuildDir(m_project, m_current); KJob* job = new ExecuteCompositeJob(nullptr, joblist); connect(job, &KJob::result, this, [this]() { setDisabled(false); updateUI(); }); setDisabled(true); m_configChanged = false; job->start(); } } void MesonConfigPage::defaults() { qCDebug(KDEV_Meson) << "Restoring build dir " << m_current.buildDir << " to it's default values"; MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); Q_ASSERT(mgr); m_current.mesonArgs.clear(); m_current.mesonBackend = mgr->defaultMesonBackend(); m_current.mesonExecutable = mgr->findMeson(); m_ui->options->resetAll(); updateUI(); } void MesonConfigPage::reset() { if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; m_ui->i_buildDirs->clear(); setWidgetsDisabled(true); m_ui->b_addDir->setDisabled(false); // Allow adding a new build dir when there are none return; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); } setWidgetsDisabled(false); qCDebug(KDEV_Meson) << "Resetting changes for build dir " << m_current.buildDir; m_current = m_config.buildDirs[m_config.currentIndex]; m_ui->options->repopulateFromBuildDir(m_project, m_current)->start(); updateUI(); } void MesonConfigPage::checkStatus() { // Get the config build dir status auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend); auto setStatus = [this](const QString& msg, int color) -> void { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; switch (color) { case 0: role = KColorScheme::PositiveText; break; case 1: role = KColorScheme::NeutralText; break; case 2: default: role = KColorScheme::NegativeText; break; } QPalette pal = m_ui->l_status->palette(); - pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + pal.setColor(QPalette::WindowText, scheme.foreground(role).color()); m_ui->l_status->setPalette(pal); m_ui->l_status->setText(i18n("Status: %1", msg)); }; switch (status) { case MesonBuilder::DOES_NOT_EXIST: setStatus(i18n("The current build directory does not exist"), 1); break; case MesonBuilder::CLEAN: setStatus(i18n("The current build directory is empty"), 1); break; case MesonBuilder::MESON_CONFIGURED: setStatus(i18n("Build directory configured"), 0); break; case MesonBuilder::MESON_FAILED_CONFIGURATION: setStatus(i18n("This meson build directory is not fully configured"), 1); break; case MesonBuilder::INVALID_BUILD_DIR: setStatus(i18n("The current build directory is invalid"), 2); break; case MesonBuilder::DIR_NOT_EMPTY: setStatus(i18n("This directory does not seem to be a meson build directory"), 2); break; case MesonBuilder::EMPTY_STRING: setStatus(i18n("Invalid build directory configuration (empty build directory string)"), 2); break; case MesonBuilder::___UNDEFINED___: setStatus(i18n("Something went very wrong. This is a bug"), 2); break; } KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; int numChanged = 0; auto options = m_ui->options->options(); if (options) { numChanged = options->numChanged(); } if (numChanged == 0) { role = KColorScheme::NormalText; m_ui->l_changed->setText(i18n("No changes")); } else { role = KColorScheme::NeutralText; m_ui->l_changed->setText(i18np("%1 option changed", "%1 options changed", numChanged)); } QPalette pal = m_ui->l_changed->palette(); - pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + pal.setColor(QPalette::WindowText, scheme.foreground(role).color()); m_ui->l_changed->setPalette(pal); } void MesonConfigPage::updateUI() { auto aConf = m_ui->advanced->getConfig(); aConf.args = m_current.mesonArgs; aConf.backend = m_current.mesonBackend; aConf.meson = m_current.mesonExecutable; m_ui->advanced->setConfig(aConf); checkStatus(); } void MesonConfigPage::readUI() { qCDebug(KDEV_Meson) << "Reading current build configuration from the UI " << m_current.buildDir.toLocalFile(); auto aConf = m_ui->advanced->getConfig(); m_current.mesonArgs = aConf.args; m_current.mesonBackend = aConf.backend; m_current.mesonExecutable = aConf.meson; } void MesonConfigPage::setWidgetsDisabled(bool disabled) { m_ui->advanced->setDisabled(disabled); m_ui->i_buildDirs->setDisabled(disabled); m_ui->b_addDir->setDisabled(disabled); m_ui->b_rmDir->setDisabled(disabled); m_ui->options->setDisabled(disabled); } void MesonConfigPage::addBuildDir() { qCDebug(KDEV_Meson) << "Adding build directory"; MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); MesonBuilder* bld = dynamic_cast(mgr->builder()); Q_ASSERT(mgr); Q_ASSERT(bld); MesonNewBuildDir newBD(m_project); if (!newBD.exec() || !newBD.isConfigValid()) { qCDebug(KDEV_Meson) << "Failed to create a new build directory"; return; } m_current = newBD.currentConfig(); m_current.canonicalizePaths(); m_config.currentIndex = m_config.addBuildDir(m_current); m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->addItem(m_current.buildDir.toLocalFile()); m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); setWidgetsDisabled(true); writeConfig(); KJob* job = bld->configure(m_project, m_current, newBD.mesonArgs()); connect(job, &KJob::result, this, [this]() { reset(); }); job->start(); } void MesonConfigPage::removeBuildDir() { qCDebug(KDEV_Meson) << "Removing current build directory"; m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->removeItem(m_config.currentIndex); m_config.removeBuildDir(m_config.currentIndex); if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; } m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); reset(); writeConfig(); } void MesonConfigPage::changeBuildDirIndex(int index) { if (index == m_config.currentIndex || m_config.buildDirs.isEmpty()) { return; } if (index < 0 || index >= m_config.buildDirs.size()) { qCWarning(KDEV_Meson) << "Invalid build dir index " << index; return; } qCDebug(KDEV_Meson) << "Changing build directory to index " << index; m_config.currentIndex = index; reset(); writeConfig(); } void MesonConfigPage::emitChanged() { m_configChanged = true; checkStatus(); emit changed(); } QString MesonConfigPage::name() const { return i18n("Meson"); } QString MesonConfigPage::fullName() const { return i18n("Meson project configuration"); } QIcon MesonConfigPage::icon() const { return QIcon::fromTheme(QStringLiteral("run-build")); } diff --git a/plugins/meson/settings/mesonnewbuilddir.cpp b/plugins/meson/settings/mesonnewbuilddir.cpp index 24d11d8db4..5cb9331867 100644 --- a/plugins/meson/settings/mesonnewbuilddir.cpp +++ b/plugins/meson/settings/mesonnewbuilddir.cpp @@ -1,232 +1,232 @@ /* This file is part of KDevelop Copyright 2018 Daniel Mensinger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mesonnewbuilddir.h" #include "mesonbuilder.h" #include "mesonmanager.h" #include "ui_mesonnewbuilddir.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; MesonNewBuildDir::MesonNewBuildDir(IProject* project, QWidget* parent) : QDialog(parent) , m_project(project) { Q_ASSERT(project); // Just in case MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); Q_ASSERT(mgr); // This dialog only works with the MesonManager setWindowTitle( i18n("Configure a build directory - %1", ICore::self()->runtimeController()->currentRuntime()->name())); m_ui = new Ui::MesonNewBuildDir; m_ui->setupUi(this); m_ui->advanced->setSupportedBackends(mgr->supportedMesonBackends()); connect(m_ui->b_buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* b) { if (m_ui->b_buttonBox->buttonRole(b) == QDialogButtonBox::ResetRole) { resetFields(); } }); #if KIO_VERSION >= QT_VERSION_CHECK(5, 33, 0) m_ui->i_buildDir->setAcceptMode(QFileDialog::AcceptSave); #else m_ui->i_buildDir->fileDialog()->setAcceptMode(QFileDialog::AcceptSave); #endif resetFields(); } MesonNewBuildDir::~MesonNewBuildDir() { delete m_ui; } void MesonNewBuildDir::resetFields() { Meson::MesonConfig cfg = Meson::getMesonConfig(m_project); Path projectPath = m_project->path(); MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); Q_ASSERT(mgr); // This dialog only works with the MesonManager auto aConf = m_ui->advanced->getConfig(); // Find a build dir that is not already configured Path buildDirPath = projectPath; buildDirPath.addPath(QStringLiteral("build")); auto checkInCfg = [](const Meson::MesonConfig& cfg, const Path& p) -> bool { for (const auto& i : cfg.buildDirs) { if (i.buildDir == p) { return true; } } return false; }; for (int i = 2; checkInCfg(cfg, buildDirPath); ++i) { buildDirPath = projectPath; buildDirPath.addPath(QStringLiteral("build%1").arg(i)); } m_ui->i_buildDir->setUrl(buildDirPath.toUrl()); // Extra args aConf.args.clear(); // Backend aConf.backend = mgr->defaultMesonBackend(); // Meson exe aConf.meson = mgr->findMeson(); m_ui->advanced->setConfig(aConf); updated(); } void MesonNewBuildDir::setStatus(const QString& str, bool validConfig) { m_configIsValid = validConfig; KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; if (validConfig) { role = KColorScheme::PositiveText; } else { role = KColorScheme::NegativeText; } QPalette pal = m_ui->l_statusMessage->palette(); - pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + pal.setColor(QPalette::WindowText, scheme.foreground(role).color()); m_ui->l_statusMessage->setPalette(pal); m_ui->l_statusMessage->setText(str); auto okButton = m_ui->b_buttonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(m_configIsValid); if (m_configIsValid) { auto cancelButton = m_ui->b_buttonBox->button(QDialogButtonBox::Cancel); cancelButton->clearFocus(); } } void MesonNewBuildDir::updated() { auto advanced = m_ui->advanced->getConfig(); Path buildDir = Path(m_ui->i_buildDir->url()); QFileInfo mesonExe(advanced.meson.toLocalFile()); if (!mesonExe.exists() || !mesonExe.isExecutable() || !mesonExe.isFile() || !mesonExe.permission(QFileDevice::ReadUser | QFileDevice::ExeUser)) { setStatus(i18n("Specified meson executable does not exist"), false); return; } MesonBuilder::DirectoryStatus status = MesonBuilder::evaluateBuildDirectory(buildDir, advanced.backend); switch (status) { case MesonBuilder::CLEAN: case MesonBuilder::DOES_NOT_EXIST: setStatus(i18n("Creating new build directory"), true); break; case MesonBuilder::MESON_CONFIGURED: setStatus(i18n("Using an already configured build directory"), true); break; case MesonBuilder::MESON_FAILED_CONFIGURATION: setStatus(i18n("Using a broken meson build directory (this should be fine)"), true); break; case MesonBuilder::INVALID_BUILD_DIR: setStatus(i18n("Cannot use specified directory"), false); break; case MesonBuilder::DIR_NOT_EMPTY: setStatus(i18n("There are already files in the build directory"), false); break; case MesonBuilder::EMPTY_STRING: setStatus(i18n("The build directory field must not be empty"), false); break; case MesonBuilder::___UNDEFINED___: setStatus(i18n("You have reached unreachable code. This is a bug"), false); break; } bool buildDirChanged = false; if (m_oldBuildDir != buildDir.toLocalFile()) { m_oldBuildDir = buildDir.toLocalFile(); buildDirChanged = true; } bool mesonHasChanged = m_ui->advanced->hasMesonChanged(); // Outside if to prevent lazy evaluation if (!m_ui->options->options() || mesonHasChanged || buildDirChanged) { if (status == MesonBuilder::MESON_CONFIGURED) { m_ui->options->repopulateFromBuildDir(m_project, currentConfig())->start(); } else { m_ui->options->repopulateFromMesonFile(m_project, advanced.meson)->start(); } } } Meson::BuildDir MesonNewBuildDir::currentConfig() const { Meson::BuildDir buildDir; if (!m_configIsValid) { qCDebug(KDEV_Meson) << "Cannot generate build dir config from invalid config"; return buildDir; } auto advanced = m_ui->advanced->getConfig(); buildDir.buildDir = Path(m_ui->i_buildDir->url()); buildDir.mesonArgs = advanced.args; buildDir.mesonBackend = advanced.backend; buildDir.mesonExecutable = advanced.meson; return buildDir; } QStringList MesonNewBuildDir::mesonArgs() const { auto options = m_ui->options->options(); if (!options) { return {}; } return options->getMesonArgs(); } bool MesonNewBuildDir::isConfigValid() const { return m_configIsValid; } diff --git a/plugins/meson/settings/mesonoptionbaseview.cpp b/plugins/meson/settings/mesonoptionbaseview.cpp index 31f695b66b..7baad9ede4 100644 --- a/plugins/meson/settings/mesonoptionbaseview.cpp +++ b/plugins/meson/settings/mesonoptionbaseview.cpp @@ -1,312 +1,312 @@ /* This file is part of KDevelop Copyright 2019 Daniel Mensinger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mesonoptionbaseview.h" #include "mesonlisteditor.h" #include "ui_mesonoptionbaseview.h" #include #include #include #include #include #include #include #include #include using namespace std; // Helper class for RAII signal blocking class SignalBlocker { public: SignalBlocker(QWidget* widget) : m_widget(widget) { if (m_widget) { m_widget->blockSignals(true); } } ~SignalBlocker() { if (m_widget) { m_widget->blockSignals(false); } } private: QWidget* m_widget = nullptr; }; MesonOptionBaseView::MesonOptionBaseView(MesonOptionPtr option, QWidget* parent) : QWidget(parent) { Q_ASSERT(option); m_ui = new Ui::MesonOptionBaseView; m_ui->setupUi(this); m_ui->l_name->setText(option->name() + QStringLiteral(":")); m_ui->l_name->setToolTip(option->description()); setToolTip(option->description()); } MesonOptionBaseView::~MesonOptionBaseView() { if (m_ui) { delete m_ui; } } // Base class functions int MesonOptionBaseView::nameWidth() { // Make the name a bit (by 25) wider than it actually is to create a margin. Maybe do // something smarter in the future (TODO) return m_ui->l_name->fontMetrics().boundingRect(m_ui->l_name->text()).width() + 25; } void MesonOptionBaseView::setMinNameWidth(int width) { m_ui->l_name->setMinimumWidth(width); } void MesonOptionBaseView::setInputWidget(QWidget* input) { QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(input->sizePolicy().hasHeightForWidth()); input->setSizePolicy(sizePolicy); input->setToolTip(option()->description()); m_ui->layout->insertWidget(1, input); updateInput(); setChanged(false); } void MesonOptionBaseView::reset() { option()->reset(); updateInput(); setChanged(false); } void MesonOptionBaseView::setChanged(bool changed) { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; if (changed) { m_ui->l_name->setStyleSheet(QStringLiteral("font-weight: bold")); m_ui->b_reset->setDisabled(false); role = KColorScheme::NeutralText; } else { m_ui->l_name->setStyleSheet(QString()); m_ui->b_reset->setDisabled(true); role = KColorScheme::NormalText; } QPalette pal = m_ui->l_name->palette(); - pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + pal.setColor(QPalette::WindowText, scheme.foreground(role).color()); m_ui->l_name->setPalette(pal); emit configChanged(); } std::shared_ptr MesonOptionBaseView::fromOption(MesonOptionPtr option, QWidget* parent) { std::shared_ptr opt = nullptr; switch (option->type()) { case MesonOptionBase::ARRAY: opt = make_shared(option, parent); break; case MesonOptionBase::BOOLEAN: opt = make_shared(option, parent); break; case MesonOptionBase::COMBO: opt = make_shared(option, parent); break; case MesonOptionBase::INTEGER: opt = make_shared(option, parent); break; case MesonOptionBase::STRING: opt = make_shared(option, parent); break; } return opt; } // Derived class constructors MesonOptionArrayView::MesonOptionArrayView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QPushButton(this); connect(m_input, &QPushButton::clicked, this, [this]() { MesonListEditor editor(m_option->rawValue(), this); if (editor.exec() == QDialog::Accepted) { m_option->setValue(editor.content()); m_input->setText(m_option->value()); setChanged(m_option->isUpdated()); } }); setInputWidget(m_input); } MesonOptionBoolView::MesonOptionBoolView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QCheckBox(this); connect(m_input, &QCheckBox::stateChanged, this, &MesonOptionBoolView::updated); setInputWidget(m_input); } MesonOptionComboView::MesonOptionComboView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QComboBox(this); m_input->clear(); m_input->addItems(m_option->choices()); m_input->setEditable(false); connect(m_input, QOverload::of(&QComboBox::currentIndexChanged), this, &MesonOptionComboView::updated); setInputWidget(m_input); } MesonOptionIntegerView::MesonOptionIntegerView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QSpinBox(this); m_input->setMinimum(INT32_MIN); m_input->setMaximum(INT32_MAX); connect(m_input, QOverload::of(&QSpinBox::valueChanged), this, &MesonOptionIntegerView::updated); setInputWidget(m_input); } MesonOptionStringView::MesonOptionStringView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QLineEdit(this); connect(m_input, &QLineEdit::textChanged, this, &MesonOptionStringView::updated); setInputWidget(m_input); } // Option getters MesonOptionBase* MesonOptionArrayView::option() { return m_option.get(); } MesonOptionBase* MesonOptionBoolView::option() { return m_option.get(); } MesonOptionBase* MesonOptionComboView::option() { return m_option.get(); } MesonOptionBase* MesonOptionIntegerView::option() { return m_option.get(); } MesonOptionBase* MesonOptionStringView::option() { return m_option.get(); } // Updaters for the input widget void MesonOptionArrayView::updateInput() { SignalBlocker block(m_input); m_input->setText(m_option->value()); } void MesonOptionBoolView::updateInput() { SignalBlocker block(m_input); m_input->setCheckState(m_option->rawValue() ? Qt::Checked : Qt::Unchecked); } void MesonOptionComboView::updateInput() { SignalBlocker block(m_input); m_input->setCurrentText(m_option->rawValue()); } void MesonOptionIntegerView::updateInput() { SignalBlocker block(m_input); m_input->setValue(m_option->rawValue()); } void MesonOptionStringView::updateInput() { SignalBlocker block(m_input); m_input->setText(m_option->rawValue()); } // Slots to update the option value void MesonOptionBoolView::updated() { m_option->setValue(m_input->isChecked()); setChanged(m_option->isUpdated()); } void MesonOptionComboView::updated() { m_option->setValue(m_input->currentText()); setChanged(m_option->isUpdated()); } void MesonOptionIntegerView::updated() { m_option->setValue(m_input->value()); setChanged(m_option->isUpdated()); } void MesonOptionStringView::updated() { m_option->setValue(m_input->text()); setChanged(m_option->isUpdated()); } diff --git a/plugins/meson/settings/mesonrewriterinput.cpp b/plugins/meson/settings/mesonrewriterinput.cpp index 323c6b95b1..9dd82df829 100644 --- a/plugins/meson/settings/mesonrewriterinput.cpp +++ b/plugins/meson/settings/mesonrewriterinput.cpp @@ -1,213 +1,213 @@ /* This file is part of KDevelop Copyright 2019 Daniel Mensinger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mesonrewriterinput.h" #include "mesonoptionbaseview.h" #include "rewriter/mesonkwargsinfo.h" #include "rewriter/mesonkwargsmodify.h" #include "ui_mesonrewriterinput.h" #include "ui_mesonrewriteroptioncontainer.h" #include #include #include MesonRewriterInputBase::MesonRewriterInputBase(const QString& name, const QString& kwarg, QWidget* parent) : QWidget(parent) , m_name(name) , m_kwarg(kwarg) { m_ui = new Ui::MesonRewriterInputBase; m_ui->setupUi(this); m_ui->l_name->setText(m_name + QLatin1Char(':')); connect(this, &MesonRewriterInputBase::configChanged, this, &MesonRewriterInputBase::updateUi); } MesonRewriterInputBase::~MesonRewriterInputBase() {} int MesonRewriterInputBase::nameWidth() { // Make the name a bit (by 25) wider than it actually is to create a margin. Maybe do // something smarter in the future (TODO) return m_ui->l_name->fontMetrics().boundingRect(m_ui->l_name->text()).width() + 25; } void MesonRewriterInputBase::setMinNameWidth(int width) { m_ui->l_name->setMinimumWidth(width); } void MesonRewriterInputBase::setInputWidget(QWidget* input) { QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(input->sizePolicy().hasHeightForWidth()); input->setSizePolicy(sizePolicy); m_ui->layout->insertWidget(1, input); updateUi(); } void MesonRewriterInputBase::updateUi() { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; if (hasChanged()) { m_ui->l_name->setStyleSheet(QStringLiteral("font-weight: bold")); m_ui->b_reset->setDisabled(false || !m_enabled); role = KColorScheme::NeutralText; } else { m_ui->l_name->setStyleSheet(QString()); m_ui->b_reset->setDisabled(true); role = KColorScheme::NormalText; } role = m_enabled ? role : KColorScheme::InactiveText; QPalette pal = m_ui->l_name->palette(); - pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + pal.setColor(QPalette::WindowText, scheme.foreground(role).color()); m_ui->l_name->setPalette(pal); m_ui->l_name->setDisabled(!m_enabled); inputWidget()->setDisabled(!m_enabled); m_ui->b_add->setHidden(m_enabled); m_ui->b_delete->setHidden(!m_enabled); } void MesonRewriterInputBase::reset() { doReset(); emit configChanged(); } void MesonRewriterInputBase::remove() { m_enabled = false; reset(); } void MesonRewriterInputBase::add() { m_enabled = true; reset(); } void MesonRewriterInputBase::resetFromAction(MesonKWARGSInfo* action) { resetValue(action->get(m_kwarg)); m_default_enabled = m_enabled = action->hasKWARG(m_kwarg); if (m_enabled) { add(); } else { remove(); } } void MesonRewriterInputBase::writeToAction(MesonKWARGSModify* action) { action->set(m_kwarg, value()); } bool MesonRewriterInputBase::hasChanged() const { return hasValueChanged() || (m_default_enabled != m_enabled); } bool MesonRewriterInputBase::isEnabled() const { return m_enabled; } // String input class MesonRewriterInputString::MesonRewriterInputString(const QString& name, const QString& kwarg, QWidget* parent) : MesonRewriterInputBase(name, kwarg, parent) { m_lineEdit = new QLineEdit(this); connect(m_lineEdit, &QLineEdit::textChanged, this, [this]() { emit configChanged(); }); setInputWidget(m_lineEdit); } MesonRewriterInputString::~MesonRewriterInputString() {} MesonRewriterInputBase::Type MesonRewriterInputString::type() const { return STRING; } bool MesonRewriterInputString::hasValueChanged() const { return m_lineEdit->text() != m_initialValue; } QWidget* MesonRewriterInputString::inputWidget() { return m_lineEdit; } void MesonRewriterInputString::doReset() { m_lineEdit->setText(m_initialValue); } void MesonRewriterInputString::resetValue(const QJsonValue& val) { m_initialValue = val.toString(); } QJsonValue MesonRewriterInputString::value() { return QJsonValue(m_lineEdit->text()); } // Options container MesonRewriterOptionContainer::MesonRewriterOptionContainer(MesonOptViewPtr optView, QWidget* parent) : QWidget(parent) , m_optView(optView) { m_ui = new Ui::MesonRewriterOptionContainer; m_ui->setupUi(this); m_ui->h_layout->insertWidget(0, m_optView.get()); connect(optView.get(), &MesonOptionBaseView::configChanged, this, [this]() { emit configChanged(); }); } void MesonRewriterOptionContainer::deleteMe() { m_markedForDeletion = true; emit configChanged(); } bool MesonRewriterOptionContainer::shouldDelete() const { return m_markedForDeletion; } bool MesonRewriterOptionContainer::hasChanged() const { return m_optView->option()->isUpdated(); } MesonOptViewPtr MesonRewriterOptionContainer::view() { return m_optView; } diff --git a/plugins/meson/settings/mesonrewriterpage.cpp b/plugins/meson/settings/mesonrewriterpage.cpp index 4bdef359ec..65060c67de 100644 --- a/plugins/meson/settings/mesonrewriterpage.cpp +++ b/plugins/meson/settings/mesonrewriterpage.cpp @@ -1,462 +1,462 @@ /* This file is part of KDevelop Copyright 2019 Daniel Mensinger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mesonrewriterpage.h" #include "mesonconfig.h" #include "mesonmanager.h" #include "mesonoptionbaseview.h" #include "mesonrewriterinput.h" #include "mintro/mesonintrospectjob.h" #include "mintro/mesonprojectinfo.h" #include "rewriter/mesondefaultopts.h" #include "rewriter/mesonkwargsinfo.h" #include "rewriter/mesonkwargsmodify.h" #include "rewriter/mesonrewriterjob.h" #include "ui_mesonrewriterpage.h" #include #include #include #include #include #include #include #include using namespace KDevelop; using namespace std; class JobDeleter { public: explicit JobDeleter(QList jobs) : m_jobs(jobs) { } ~JobDeleter() { for (KJob* i : m_jobs) { delete i; } } private: QList m_jobs; }; MesonRewriterPage::MesonRewriterPage(IPlugin* plugin, IProject* project, QWidget* parent) : ConfigPage(plugin, nullptr, parent) , m_project(project) { Q_ASSERT(m_project); m_ui = new Ui::MesonRewriterPage; m_ui->setupUi(this); m_projectKwargs = constructPojectInputs(); // Initialize widgets for (auto* i : m_projectKwargs) { m_ui->c_project->addWidget(i); connect(i, &MesonRewriterInputBase::configChanged, this, &MesonRewriterPage::emitChanged); } recalculateLengths(); reset(); } #define STRING_INPUT(name, id) new MesonRewriterInputString(QStringLiteral(name), QStringLiteral(id), this) QVector MesonRewriterPage::constructPojectInputs() { return { STRING_INPUT("Version", "version"), STRING_INPUT("License", "license"), STRING_INPUT("Meson version", "meson_version"), STRING_INPUT("Subprojects directory", "subproject_dir"), }; } MesonOptContainerPtr MesonRewriterPage::constructDefaultOpt(const QString& name, const QString& value) { if (!m_opts) { return nullptr; } for (auto& i : m_opts->options()) { if (i->name() != name) { continue; } if (!value.isNull()) { i->setFromString(value); } auto optView = MesonOptionBaseView::fromOption(i, this); if (!optView) { continue; } auto opt = std::make_shared(optView, this); if (!opt) { continue; } connect(opt.get(), &MesonRewriterOptionContainer::configChanged, this, &MesonRewriterPage::emitChanged); return opt; } return nullptr; } void MesonRewriterPage::setWidgetsDisabled(bool disabled) { m_ui->c_tabs->setDisabled(disabled); } void MesonRewriterPage::recalculateLengths() { // Calculate the maximum name width to align all widgets vector lengths; int maxWidth = 50; lengths.reserve(m_projectKwargs.size() + m_defaultOpts.size()); auto input_op = [](MesonRewriterInputBase* x) -> int { return x->nameWidth(); }; auto optCont_op = [](MesonOptContainerPtr x) -> int { return x->view()->nameWidth(); }; transform(begin(m_projectKwargs), end(m_projectKwargs), back_inserter(lengths), input_op); transform(begin(m_defaultOpts), end(m_defaultOpts), back_inserter(lengths), optCont_op); maxWidth = accumulate(begin(lengths), end(lengths), maxWidth, [](int a, int b) -> int { return max(a, b); }); // Set widgets width for (auto* i : m_projectKwargs) { i->setMinNameWidth(maxWidth); } for (auto& i : m_defaultOpts) { i->view()->setMinNameWidth(maxWidth); } m_ui->l_dispProject->setMinimumWidth(maxWidth); } void MesonRewriterPage::checkStatus() { // Get the config build dir status auto setStatus = [this](const QString& msg, int color) -> void { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; switch (color) { case 0: role = KColorScheme::PositiveText; setDisabled(false); break; case 1: role = KColorScheme::NeutralText; setDisabled(true); break; case 2: default: role = KColorScheme::NegativeText; setDisabled(true); break; } QPalette pal = m_ui->l_status->palette(); - pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + pal.setColor(QPalette::WindowText, scheme.foreground(role).color()); m_ui->l_status->setPalette(pal); m_ui->l_status->setText(i18n("Status: %1", msg)); }; switch (m_state) { case START: setStatus(i18n("Initializing GUI"), 1); break; case LOADING: setStatus(i18n("Loading project data..."), 1); break; case WRITING: setStatus(i18n("Writing project data..."), 1); break; case READY: setStatus(i18n("Initializing GUI"), 0); break; case ERROR: setStatus(i18n("Loading meson rewriter data failed"), 2); break; } // Remove old default options m_defaultOpts.erase(remove_if(begin(m_defaultOpts), end(m_defaultOpts), [](auto x) { return x->shouldDelete(); }), end(m_defaultOpts)); KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; int numChanged = 0; numChanged += count_if(begin(m_projectKwargs), end(m_projectKwargs), [](auto* x) { return x->hasChanged(); }); numChanged += count_if(begin(m_defaultOpts), end(m_defaultOpts), [](auto x) { return x->hasChanged(); }); if (numChanged == 0) { role = KColorScheme::NormalText; m_ui->l_changed->setText(i18n("No changes")); } else { role = KColorScheme::NeutralText; m_ui->l_changed->setText(i18np("%1 option changed", "%1 options changed", numChanged)); } QPalette pal = m_ui->l_changed->palette(); - pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + pal.setColor(QPalette::WindowText, scheme.foreground(role).color()); m_ui->l_changed->setPalette(pal); } void MesonRewriterPage::setStatus(MesonRewriterPage::State s) { m_state = s; checkStatus(); } void MesonRewriterPage::apply() { qCDebug(KDEV_Meson) << "REWRITER GUI: APPLY"; auto projectSet = make_shared(MesonKWARGSProjectModify::SET); auto projectDel = make_shared(MesonKWARGSProjectModify::DELETE); auto defOptsSet = make_shared(MesonRewriterDefaultOpts::SET); auto defOptsDel = make_shared(MesonRewriterDefaultOpts::DELETE); auto writer = [](MesonRewriterInputBase* widget, MesonKWARGSModifyPtr set, MesonKWARGSModifyPtr del) { if (!widget->hasChanged()) { return; } if (widget->isEnabled()) { widget->writeToAction(set.get()); } else { widget->writeToAction(del.get()); } }; for_each(begin(m_projectKwargs), end(m_projectKwargs), [&](auto* w) { writer(w, projectSet, projectDel); }); QStringList deletedOptions = m_initialDefaultOpts; for (auto& i : m_defaultOpts) { auto opt = i->view()->option(); // Detect deleted options by removing all current present options from the initial option list deletedOptions.removeAll(opt->name()); if (opt->isUpdated() || !m_initialDefaultOpts.contains(opt->name())) { defOptsSet->set(opt->name(), opt->value()); } } for (auto i : deletedOptions) { defOptsDel->set(i, QString()); } QVector actions = { projectSet, projectDel, defOptsSet, defOptsDel }; KJob* rewriterJob = new MesonRewriterJob(m_project, actions, this); // Reload the GUI once the data has been written connect(rewriterJob, &KJob::result, this, &MesonRewriterPage::reset); setStatus(WRITING); rewriterJob->start(); } void MesonRewriterPage::reset() { qCDebug(KDEV_Meson) << "REWRITER GUI: RESET"; Meson::BuildDir buildDir = Meson::currentBuildDir(m_project); if (!buildDir.isValid()) { setStatus(ERROR); return; } auto projectInfo = std::make_shared(); QVector actions = { projectInfo }; QVector types = { MesonIntrospectJob::PROJECTINFO, MesonIntrospectJob::BUILDOPTIONS }; MesonIntrospectJob::Mode mode = MesonIntrospectJob::MESON_FILE; auto introspectJob = new MesonIntrospectJob(m_project, buildDir, types, mode, this); auto rewriterJob = new MesonRewriterJob(m_project, actions, this); QList jobs = { introspectJob, rewriterJob }; // Don't automatically delete jobs beause they are used in the lambda below for (KJob* i : jobs) { i->setAutoDelete(false); } KJob* job = new ExecuteCompositeJob(this, jobs); connect(job, &KJob::result, this, [=]() -> void { JobDeleter deleter(jobs); // Make sure to free all jobs with RAII auto prInfo = introspectJob->projectInfo(); m_opts = introspectJob->buildOptions(); if (!prInfo || !m_opts) { setStatus(ERROR); return; } m_ui->l_project->setText(QStringLiteral("

") + prInfo->name() + QStringLiteral("

")); auto setter = [](MesonRewriterInputBase* w, MesonKWARGSInfoPtr i) { w->resetFromAction(i.get()); }; for_each(begin(m_projectKwargs), end(m_projectKwargs), [=](auto* x) { setter(x, projectInfo); }); // Updated the default options m_defaultOpts.clear(); m_initialDefaultOpts.clear(); if (projectInfo->hasKWARG(QStringLiteral("default_options"))) { auto rawValues = projectInfo->getArray(QStringLiteral("default_options")); auto options = m_opts->options(); for (auto i : rawValues) { int idx = i.indexOf(QLatin1Char('=')); if (idx < 0) { continue; } QString name = i.left(idx); QString val = i.mid(idx + 1); auto opt = constructDefaultOpt(name, val); if (!opt) { continue; } m_defaultOpts += opt; m_initialDefaultOpts += name; m_ui->c_defOpts->addWidget(opt.get()); } } recalculateLengths(); setStatus(READY); return; }); setStatus(LOADING); job->start(); } void MesonRewriterPage::newOption() { // Sort by section QStringList core; QStringList backend; QStringList base; QStringList compiler; QStringList directory; QStringList user; QStringList test; for (auto& i : m_opts->options()) { switch (i->section()) { case MesonOptionBase::CORE: core += i->name(); break; case MesonOptionBase::BACKEND: backend += i->name(); break; case MesonOptionBase::BASE: base += i->name(); break; case MesonOptionBase::COMPILER: compiler += i->name(); break; case MesonOptionBase::DIRECTORY: directory += i->name(); break; case MesonOptionBase::USER: user += i->name(); break; case MesonOptionBase::TEST: test += i->name(); break; } } QStringList total = core + backend + base + compiler + directory + user + test; // Remove already existing options for (auto& i : m_defaultOpts) { total.removeAll(i->view()->option()->name()); } QInputDialog dialog(this); dialog.setOption(QInputDialog::UseListViewForComboBoxItems, true); dialog.setInputMode(QInputDialog::TextInput); dialog.setWindowTitle(i18n("Select meson option to add")); dialog.setLabelText(i18n("Select one new meson option to add")); dialog.setComboBoxItems(total); if (dialog.exec() != QDialog::Accepted) { return; } auto opt = constructDefaultOpt(dialog.textValue(), QString()); if (!opt) { return; } m_defaultOpts += opt; m_ui->c_defOpts->addWidget(opt.get()); recalculateLengths(); } void MesonRewriterPage::defaults() { reset(); } void MesonRewriterPage::emitChanged() { m_configChanged = true; checkStatus(); emit changed(); } QString MesonRewriterPage::name() const { return i18n("Project"); } QString MesonRewriterPage::fullName() const { return i18n("Meson project settings"); } QIcon MesonRewriterPage::icon() const { return QIcon::fromTheme(QStringLiteral("run-build")); }