diff options
author | Kyle Edwards <kyle.edwards@kitware.com> | 2020-09-29 14:17:47 (GMT) |
---|---|---|
committer | Kyle Edwards <kyle.edwards@kitware.com> | 2020-10-05 13:49:59 (GMT) |
commit | a4382f72d7fb924f9649ae352d70a36df2d662a8 (patch) | |
tree | 15855284a47a3f3ab4a14f945559266ebabbb0a5 /Source/QtDialog | |
parent | 8617479061039e2b357b7efc922f1b88648dce42 (diff) | |
download | CMake-a4382f72d7fb924f9649ae352d70a36df2d662a8.zip CMake-a4382f72d7fb924f9649ae352d70a36df2d662a8.tar.gz CMake-a4382f72d7fb924f9649ae352d70a36df2d662a8.tar.bz2 |
CMake GUI: Add presets functionality
Diffstat (limited to 'Source/QtDialog')
-rw-r--r-- | Source/QtDialog/CMakeLists.txt | 8 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetup.cxx | 25 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.cxx | 86 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.h | 12 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.ui | 23 | ||||
-rw-r--r-- | Source/QtDialog/FirstConfigure.cxx | 54 | ||||
-rw-r--r-- | Source/QtDialog/FirstConfigure.h | 15 | ||||
-rw-r--r-- | Source/QtDialog/QCMake.cxx | 160 | ||||
-rw-r--r-- | Source/QtDialog/QCMake.h | 23 | ||||
-rw-r--r-- | Source/QtDialog/QCMakePreset.cxx | 50 | ||||
-rw-r--r-- | Source/QtDialog/QCMakePreset.h | 30 | ||||
-rw-r--r-- | Source/QtDialog/QCMakePresetComboBox.cxx | 64 | ||||
-rw-r--r-- | Source/QtDialog/QCMakePresetComboBox.h | 35 | ||||
-rw-r--r-- | Source/QtDialog/QCMakePresetItemModel.cxx | 143 | ||||
-rw-r--r-- | Source/QtDialog/QCMakePresetItemModel.h | 45 |
15 files changed, 762 insertions, 11 deletions
diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 5fd0e89..394762a 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -92,6 +92,12 @@ set(SRCS QCMake.h QCMakeCacheView.cxx QCMakeCacheView.h + QCMakePreset.cxx + QCMakePreset.h + QCMakePresetComboBox.cxx + QCMakePresetComboBox.h + QCMakePresetItemModel.cxx + QCMakePresetItemModel.h QCMakeWidgets.cxx QCMakeWidgets.h RegexExplorer.cxx @@ -116,6 +122,8 @@ qt5_wrap_cpp(MOC_SRCS FirstConfigure.h QCMake.h QCMakeCacheView.h + QCMakePresetComboBox.h + QCMakePresetItemModel.h QCMakeWidgets.h RegexExplorer.h WarningMessagesDialog.h diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index 37c1f15..a5b2f34 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -32,7 +32,8 @@ static const char* cmDocumentationUsage[][2] = { " cmake-gui [options]\n" " cmake-gui [options] <path-to-source>\n" " cmake-gui [options] <path-to-existing-build>\n" - " cmake-gui [options] -S <path-to-source> -B <path-to-build>\n" }, + " cmake-gui [options] -S <path-to-source> -B <path-to-build>\n" + " cmake-gui [options] -S <path-to-source> --preset=<preset-name>\n" }, { nullptr, nullptr } }; @@ -147,6 +148,7 @@ int main(int argc, char** argv) QStringList args = QApplication::arguments(); std::string binaryDirectory; std::string sourceDirectory; + std::string presetName; for (int i = 1; i < args.size(); ++i) { const QString& arg = args[i]; if (arg.startsWith("-S")) { @@ -185,11 +187,28 @@ int main(int argc, char** argv) binaryDirectory = cmSystemTools::CollapseFullPath(path.toLocal8Bit().data()); cmSystemTools::ConvertToUnixSlashes(binaryDirectory); + } else if (arg.startsWith("--preset=")) { + QString preset = arg.mid(cmStrLen("--preset=")); + if (preset.isEmpty()) { + std::cerr << "No preset specified for --preset" << std::endl; + return 1; + } + presetName = preset.toLocal8Bit().data(); } } - if (!sourceDirectory.empty() && !binaryDirectory.empty()) { + if (!sourceDirectory.empty() && + (!binaryDirectory.empty() || !presetName.empty())) { dialog.setSourceDirectory(QString::fromLocal8Bit(sourceDirectory.c_str())); - dialog.setBinaryDirectory(QString::fromLocal8Bit(binaryDirectory.c_str())); + if (!binaryDirectory.empty()) { + dialog.setBinaryDirectory( + QString::fromLocal8Bit(binaryDirectory.c_str())); + if (!presetName.empty()) { + dialog.setStartupBinaryDirectory(true); + } + } + if (!presetName.empty()) { + dialog.setDeferredPreset(QString::fromLocal8Bit(presetName.c_str())); + } } else { if (args.count() == 2) { std::string filePath = diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index df22028..acd32ec 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -21,8 +21,10 @@ #include <QSettings> #include <QShortcut> #include <QStatusBar> +#include <QString> #include <QToolButton> #include <QUrl> +#include <QVector> #ifdef QT_WINEXTRAS # include <QWinTaskbarButton> @@ -263,6 +265,8 @@ void CMakeSetupDialog::initialize() &CMakeSetupDialog::onBinaryDirectoryChanged); QObject::connect(this->SourceDirectory, &QLineEdit::textChanged, this, &CMakeSetupDialog::onSourceDirectoryChanged); + QObject::connect(this->Preset, &QCMakePresetComboBox::presetChanged, this, + &CMakeSetupDialog::onBuildPresetChanged); QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::sourceDirChanged, this, @@ -270,6 +274,13 @@ void CMakeSetupDialog::initialize() QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::binaryDirChanged, this, &CMakeSetupDialog::updateBinaryDirectory); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::presetsChanged, + this, &CMakeSetupDialog::updatePresets); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::presetChanged, + this, &CMakeSetupDialog::updatePreset); + QObject::connect(this->CMakeThread->cmakeInstance(), + &QCMake::presetLoadError, this, + &CMakeSetupDialog::showPresetLoadError); QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::progressChanged, this, @@ -314,9 +325,15 @@ void CMakeSetupDialog::initialize() QObject::connect(this->WarnUninitializedAction, &QAction::triggered, this->CMakeThread->cmakeInstance(), &QCMake::setWarnUninitializedMode); + QObject::connect(this->CMakeThread->cmakeInstance(), + &QCMake::warnUninitializedModeChanged, + this->WarnUninitializedAction, &QAction::setChecked); - if (!this->SourceDirectory->text().isEmpty() || - !this->BinaryDirectory->lineEdit()->text().isEmpty()) { + if (!this->SourceDirectory->text().isEmpty() && + !this->DeferredPreset.isNull()) { + this->onSourceDirectoryChanged(this->SourceDirectory->text()); + } else if (!this->SourceDirectory->text().isEmpty() || + !this->BinaryDirectory->lineEdit()->text().isEmpty()) { this->onSourceDirectoryChanged(this->SourceDirectory->text()); this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text()); } else { @@ -671,6 +688,41 @@ void CMakeSetupDialog::updateBinaryDirectory(const QString& dir) } } +void CMakeSetupDialog::updatePresets(const QVector<QCMakePreset>& presets) +{ + if (this->Preset->presets() != presets) { + this->Preset->blockSignals(true); + this->Preset->setPresets(presets); + this->Preset->blockSignals(false); + } + + this->Preset->setHidden(presets.isEmpty()); + this->PresetLabel->setHidden(presets.isEmpty()); + + if (!this->DeferredPreset.isNull()) { + this->Preset->setPresetName(this->DeferredPreset); + this->DeferredPreset = QString{}; + } +} + +void CMakeSetupDialog::updatePreset(const QString& name) +{ + if (this->Preset->presetName() != name) { + this->Preset->blockSignals(true); + this->Preset->setPresetName(name); + this->Preset->blockSignals(false); + } +} + +void CMakeSetupDialog::showPresetLoadError( + const QString& dir, cmCMakePresetsFile::ReadFileResult result) +{ + QMessageBox::warning( + this, "Error Reading CMake Presets", + QString::fromLocal8Bit("Could not read presets from %1: %2") + .arg(dir, cmCMakePresetsFile::ResultToString(result))); +} + void CMakeSetupDialog::doBinaryBrowse() { QString dir = QFileDialog::getExistingDirectory( @@ -686,6 +738,11 @@ void CMakeSetupDialog::setBinaryDirectory(const QString& dir) this->BinaryDirectory->setEditText(dir); } +void CMakeSetupDialog::setStartupBinaryDirectory(bool startup) +{ + this->StartupBinaryDirectory = startup; +} + void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir) { this->Output->clear(); @@ -711,11 +768,24 @@ void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir) Q_ARG(QString, dir)); } +void CMakeSetupDialog::onBuildPresetChanged(const QString& name) +{ + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "setPreset", + Qt::QueuedConnection, Q_ARG(QString, name), + Q_ARG(bool, !this->StartupBinaryDirectory)); + this->StartupBinaryDirectory = false; +} + void CMakeSetupDialog::setSourceDirectory(const QString& dir) { this->SourceDirectory->setText(dir); } +void CMakeSetupDialog::setDeferredPreset(const QString& preset) +{ + this->DeferredPreset = preset; +} + void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent) { percent = (percent * ProgressFactor) + ProgressOffset; @@ -753,6 +823,7 @@ void CMakeSetupDialog::setEnabledState(bool enabled) this->CacheValues->cacheModel()->setEditEnabled(enabled); this->SourceDirectory->setEnabled(enabled); this->BrowseSourceDirectoryButton->setEnabled(enabled); + this->Preset->setEnabled(enabled); this->BinaryDirectory->setEnabled(enabled); this->BrowseBinaryDirectoryButton->setEnabled(enabled); this->ReloadCacheAction->setEnabled(enabled); @@ -777,6 +848,17 @@ bool CMakeSetupDialog::setupFirstConfigure() // restore from settings dialog.loadFromSettings(); + auto presetData = this->Preset->currentData(); + if (presetData.isValid()) { + auto preset = presetData.value<QCMakePreset>(); + dialog.setCurrentGenerator(preset.generator); + if (preset.setGenConfig) { + dialog.setPlatform(preset.architecture); + dialog.setToolset(preset.toolset); + } + dialog.setCompilerOption(CompilerOption::DefaultNative); + } + if (dialog.exec() == QDialog::Accepted) { dialog.saveToSettings(); this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator()); diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index d752ef2..f0cc929 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -5,12 +5,15 @@ #include <memory> #include "QCMake.h" +#include "QCMakePreset.h" #include <QEventLoop> #include <QMainWindow> #include <QThread> +#include <QVector> #include "ui_CMakeSetupDialog.h" +class QCMakePresetItemModel; class QCMakeThread; class CMakeCacheModel; class QProgressBar; @@ -33,6 +36,8 @@ public: public slots: void setBinaryDirectory(const QString& dir); void setSourceDirectory(const QString& dir); + void setDeferredPreset(const QString& preset); + void setStartupBinaryDirectory(bool startup); protected slots: void initialize(); @@ -52,6 +57,10 @@ protected slots: void doDeleteCache(); void updateSourceDirectory(const QString& dir); void updateBinaryDirectory(const QString& dir); + void updatePresets(const QVector<QCMakePreset>& presets); + void updatePreset(const QString& name); + void showPresetLoadError(const QString& dir, + cmCMakePresetsFile::ReadFileResult result); void showProgress(const QString& msg, float percent); void setEnabledState(bool); bool setupFirstConfigure(); @@ -62,6 +71,7 @@ protected slots: void saveBuildPaths(const QStringList&); void onBinaryDirectoryChanged(const QString& dir); void onSourceDirectoryChanged(const QString& dir); + void onBuildPresetChanged(const QString& name); void setCacheModified(); void removeSelectedCacheEntries(); void selectionChanged(); @@ -113,6 +123,8 @@ protected: QAction* WarnUninitializedAction; QAction* InstallForCommandLineAction; State CurrentState; + QString DeferredPreset; + bool StartupBinaryDirectory = false; QTextCharFormat ErrorFormat; QTextCharFormat MessageFormat; diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui index 5feee91..afb25eb 100644 --- a/Source/QtDialog/CMakeSetupDialog.ui +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -44,7 +44,7 @@ <number>6</number> </property> <item row="0" column="0"> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="SourceLabel"> <property name="text"> <string>Where is the source code:</string> </property> @@ -61,13 +61,23 @@ </widget> </item> <item row="1" column="0"> - <widget class="QLabel" name="label_2"> + <widget class="QLabel" name="PresetLabel"> <property name="text"> - <string>Where to build the binaries:</string> + <string>Preset:</string> </property> </widget> </item> <item row="1" column="1"> + <widget class="QCMakePresetComboBox" name="Preset"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="BinaryLabel"> + <property name="text"> + <string>Where to build the binaries:</string> + </property> + </widget> + </item> + <item row="2" column="1"> <widget class="QComboBox" name="BinaryDirectory"> <property name="sizePolicy"> <sizepolicy hsizetype="Ignored" vsizetype="Fixed"> @@ -83,7 +93,7 @@ </property> </widget> </item> - <item row="1" column="2"> + <item row="2" column="2"> <widget class="QPushButton" name="BrowseBinaryDirectoryButton"> <property name="text"> <string>Browse &Build...</string> @@ -367,6 +377,11 @@ <extends>QTreeView</extends> <header>QCMakeCacheView.h</header> </customwidget> + <customwidget> + <class>QCMakePresetComboBox</class> + <extends>QComboBox</extends> + <header>QCMakePresetComboBox.h</header> + </customwidget> </customwidgets> <resources> <include location="CMakeSetup.qrc"/> diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx index 918f137..10360bb 100644 --- a/Source/QtDialog/FirstConfigure.cxx +++ b/Source/QtDialog/FirstConfigure.cxx @@ -145,6 +145,36 @@ void StartCompilerSetup::setCurrentGenerator(const QString& gen) } } +void StartCompilerSetup::setPlatform(const QString& platform) +{ + this->PlatformOptions->setCurrentText(platform); +} + +void StartCompilerSetup::setToolset(const QString& toolset) +{ + this->Toolset->setText(toolset); +} + +void StartCompilerSetup::setCompilerOption(CompilerOption option) +{ + std::size_t index = 0; + switch (option) { + case CompilerOption::DefaultNative: + index = 0; + break; + case CompilerOption::SpecifyNative: + index = 1; + break; + case CompilerOption::ToolchainFile: + index = 2; + break; + case CompilerOption::Options: + index = 3; + break; + } + this->CompilerSetupOptions[index]->setChecked(true); +} + QString StartCompilerSetup::getGenerator() const { return this->GeneratorOptions->currentText(); @@ -482,6 +512,26 @@ void FirstConfigure::setGenerators( this->mStartCompilerSetupPage->setGenerators(gens); } +void FirstConfigure::setCurrentGenerator(const QString& gen) +{ + this->mStartCompilerSetupPage->setCurrentGenerator(gen); +} + +void FirstConfigure::setPlatform(const QString& platform) +{ + this->mStartCompilerSetupPage->setPlatform(platform); +} + +void FirstConfigure::setToolset(const QString& toolset) +{ + this->mStartCompilerSetupPage->setToolset(toolset); +} + +void FirstConfigure::setCompilerOption(CompilerOption option) +{ + this->mStartCompilerSetupPage->setCompilerOption(option); +} + QString FirstConfigure::getGenerator() const { return this->mStartCompilerSetupPage->getGenerator(); @@ -503,7 +553,7 @@ void FirstConfigure::loadFromSettings() // restore generator settings.beginGroup("Settings/StartPath"); QString lastGen = settings.value("LastGenerator").toString(); - this->mStartCompilerSetupPage->setCurrentGenerator(lastGen); + this->setCurrentGenerator(lastGen); settings.endGroup(); // restore compiler setup @@ -550,7 +600,7 @@ void FirstConfigure::loadFromSettings() // this prevents them from being taken from environment, while the // generator is taken from application settings if (!mDefaultGenerator.isEmpty()) { - this->mStartCompilerSetupPage->setCurrentGenerator(mDefaultGenerator); + this->setCurrentGenerator(mDefaultGenerator); } } diff --git a/Source/QtDialog/FirstConfigure.h b/Source/QtDialog/FirstConfigure.h index ca5f52e..5844f3a 100644 --- a/Source/QtDialog/FirstConfigure.h +++ b/Source/QtDialog/FirstConfigure.h @@ -22,6 +22,14 @@ enum FirstConfigurePages Done }; +enum class CompilerOption +{ + DefaultNative, + SpecifyNative, + ToolchainFile, + Options, +}; + //! the first page that gives basic options for what compilers setup to choose //! from class StartCompilerSetup : public QWizardPage @@ -33,6 +41,9 @@ public: ~StartCompilerSetup(); void setGenerators(std::vector<cmake::GeneratorInfo> const& gens); void setCurrentGenerator(const QString& gen); + void setToolset(const QString& toolset); + void setPlatform(const QString& platform); + void setCompilerOption(CompilerOption option); QString getGenerator() const; QString getToolset() const; QString getPlatform() const; @@ -167,6 +178,10 @@ public: ~FirstConfigure(); void setGenerators(std::vector<cmake::GeneratorInfo> const& gens); + void setCurrentGenerator(const QString& gen); + void setToolset(const QString& toolset); + void setPlatform(const QString& platform); + void setCompilerOption(CompilerOption option); QString getGenerator() const; QString getPlatform() const; QString getToolset() const; diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 974c545..9017a63 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -2,10 +2,14 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "QCMake.h" +#include <algorithm> + #include <cm/memory> #include <QCoreApplication> #include <QDir> +#include <QString> +#include <QVector> #include "cmExternalMakefileProjectGenerator.h" #include "cmGlobalGenerator.h" @@ -19,12 +23,15 @@ QCMake::QCMake(QObject* p) : QObject(p) + , StartEnvironment(QProcessEnvironment::systemEnvironment()) , Environment(QProcessEnvironment::systemEnvironment()) { this->WarnUninitializedMode = false; qRegisterMetaType<QCMakeProperty>(); qRegisterMetaType<QCMakePropertyList>(); qRegisterMetaType<QProcessEnvironment>(); + qRegisterMetaType<QVector<QCMakePreset>>(); + qRegisterMetaType<cmCMakePresetsFile::ReadFileResult>(); cmSystemTools::DisableRunCommandOutput(); cmSystemTools::SetRunCommandHideConsole(true); @@ -57,6 +64,17 @@ QCMake::QCMake(QObject* p) for (cmake::GeneratorInfo const& gen : generators) { this->AvailableGenerators.push_back(gen); } + + connect(&this->LoadPresetsTimer, &QTimer::timeout, this, [this]() { + this->loadPresets(); + if (!this->PresetName.isEmpty() && + this->CMakePresetsFile.Presets.find( + std::string(this->PresetName.toLocal8Bit())) == + this->CMakePresetsFile.Presets.end()) { + this->setPreset(QString{}); + } + }); + this->LoadPresetsTimer.start(1000); } QCMake::~QCMake() = default; @@ -73,6 +91,8 @@ void QCMake::setSourceDirectory(const QString& _dir) if (this->SourceDirectory != dir) { this->SourceDirectory = QDir::fromNativeSeparators(dir); emit this->sourceDirChanged(this->SourceDirectory); + this->loadPresets(); + this->setPreset(QString{}); } } @@ -129,6 +149,56 @@ void QCMake::setBinaryDirectory(const QString& _dir) } } +void QCMake::setPreset(const QString& name, bool setBinary) +{ + if (this->PresetName != name) { + this->PresetName = name; + emit this->presetChanged(this->PresetName); + + if (!name.isNull()) { + std::string presetName(name.toLocal8Bit()); + auto const& preset = this->CMakePresetsFile.Presets[presetName]; + auto expandedPreset = this->CMakePresetsFile.ExpandMacros(preset); + if (expandedPreset) { + if (setBinary) { + QString binaryDir = + QString::fromLocal8Bit(expandedPreset->BinaryDir.data()); + this->setBinaryDirectory(binaryDir); + } + if (expandedPreset->WarnDev) { + this->CMakeInstance->SetSuppressDevWarnings( + !*expandedPreset->WarnDev); + } + if (expandedPreset->ErrorDev) { + this->CMakeInstance->SetDevWarningsAsErrors( + *expandedPreset->ErrorDev); + } + if (expandedPreset->WarnDeprecated) { + this->CMakeInstance->SetSuppressDeprecatedWarnings( + !*expandedPreset->WarnDeprecated); + } + if (expandedPreset->ErrorDeprecated) { + this->CMakeInstance->SetDeprecatedWarningsAsErrors( + *expandedPreset->ErrorDeprecated); + } + if (expandedPreset->WarnUninitialized) { + this->WarnUninitializedMode = *expandedPreset->WarnUninitialized; + emit this->warnUninitializedModeChanged( + *expandedPreset->WarnUninitialized); + } + this->Environment = this->StartEnvironment; + for (auto const& v : expandedPreset->Environment) { + if (v.second) { + this->Environment.insert(QString::fromLocal8Bit(v.first.data()), + QString::fromLocal8Bit(v.second->data())); + } + } + } + } + emit this->propertiesChanged(this->properties()); + } +} + void QCMake::setGenerator(const QString& gen) { if (this->Generator != gen) { @@ -348,6 +418,56 @@ QCMakePropertyList QCMake::properties() const ret.append(prop); } + if (!this->PresetName.isNull()) { + std::string presetName(this->PresetName.toLocal8Bit()); + auto p = this->CMakePresetsFile.ExpandMacros( + this->CMakePresetsFile.Presets.at(presetName)); + if (p) { + for (auto const& v : p->CacheVariables) { + if (!v.second) { + continue; + } + QCMakeProperty prop; + prop.Key = QString::fromLocal8Bit(v.first.data()); + prop.Value = QString::fromLocal8Bit(v.second->Value.data()); + prop.Type = QCMakeProperty::STRING; + if (!v.second->Type.empty()) { + auto type = cmState::StringToCacheEntryType(v.second->Type); + switch (type) { + case cmStateEnums::BOOL: + prop.Type = QCMakeProperty::BOOL; + prop.Value = cmIsOn(v.second->Value); + break; + case cmStateEnums::PATH: + prop.Type = QCMakeProperty::PATH; + break; + case cmStateEnums::FILEPATH: + prop.Type = QCMakeProperty::FILEPATH; + break; + default: + prop.Type = QCMakeProperty::STRING; + break; + } + } + + // QCMakeCacheModel prefers variables earlier in the list rather than + // later, so overwrite them if they already exist rather than simply + // appending + bool found = false; + for (auto& orig : ret) { + if (orig.Key == prop.Key) { + orig = prop; + found = true; + break; + } + } + if (!found) { + ret.append(prop); + } + } + } + } + return ret; } @@ -405,6 +525,46 @@ void QCMake::setUpEnvironment() const } } +void QCMake::loadPresets() +{ + auto result = this->CMakePresetsFile.ReadProjectPresets( + this->SourceDirectory.toLocal8Bit().data(), true); + if (result != this->LastLoadPresetsResult && + result != cmCMakePresetsFile::ReadFileResult::READ_OK) { + emit this->presetLoadError(this->SourceDirectory, result); + } + this->LastLoadPresetsResult = result; + + QVector<QCMakePreset> presets; + for (auto const& name : this->CMakePresetsFile.PresetOrder) { + auto const& p = this->CMakePresetsFile.Presets[name]; + if (p.Hidden) { + continue; + } + + QCMakePreset preset; + preset.name = std::move(QString::fromLocal8Bit(p.Name.data())); + preset.displayName = + std::move(QString::fromLocal8Bit(p.DisplayName.data())); + preset.description = + std::move(QString::fromLocal8Bit(p.Description.data())); + preset.generator = std::move(QString::fromLocal8Bit(p.Generator.data())); + preset.architecture = + std::move(QString::fromLocal8Bit(p.Architecture.data())); + preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data())); + preset.setGenConfig = !p.GeneratorConfig || + p.GeneratorConfig == cmCMakePresetsFile::CMakeGeneratorConfig::Default; + preset.enabled = std::find_if(this->AvailableGenerators.begin(), + this->AvailableGenerators.end(), + [&p](const cmake::GeneratorInfo& g) { + return g.name == p.Generator; + }) != this->AvailableGenerators.end() && + this->CMakePresetsFile.ExpandMacros(p); + presets.push_back(preset); + } + emit this->presetsChanged(presets); +} + QString QCMake::binaryDirectory() const { return this->BinaryDirectory; diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index f569951..a6751b0 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -4,6 +4,7 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmCMakePresetsFile.h" #include "cmake.h" #ifdef _MSC_VER @@ -14,6 +15,7 @@ #include <memory> #include <vector> +#include "QCMakePreset.h" #include <QAtomicInt> #include <QList> #include <QMetaType> @@ -21,6 +23,7 @@ #include <QProcessEnvironment> #include <QString> #include <QStringList> +#include <QTimer> #include <QVariant> /// struct to represent cmake properties in Qt @@ -57,6 +60,7 @@ using QCMakePropertyList = QList<QCMakeProperty>; Q_DECLARE_METATYPE(QCMakeProperty) Q_DECLARE_METATYPE(QCMakePropertyList) Q_DECLARE_METATYPE(QProcessEnvironment) +Q_DECLARE_METATYPE(cmCMakePresetsFile::ReadFileResult) /// Qt API for CMake library. /// Wrapper like class allows for easier integration with @@ -74,6 +78,8 @@ public slots: void setSourceDirectory(const QString& dir); /// set the binary directory to build in void setBinaryDirectory(const QString& dir); + /// set the preset name to use + void setPreset(const QString& name, bool setBinary = true); /// set the desired generator to use void setGenerator(const QString& generator); /// set the desired generator to use @@ -147,6 +153,15 @@ signals: void sourceDirChanged(const QString& dir); /// signal when the binary directory changes void binaryDirChanged(const QString& dir); + /// signal when the preset list changes + void presetsChanged(const QVector<QCMakePreset>& presets); + /// signal when the selected preset changes + void presetChanged(const QString& name); + /// signal when there's an error reading the presets files + void presetLoadError(const QString& dir, + cmCMakePresetsFile::ReadFileResult error); + /// signal when uninitialized warning changes + void warnUninitializedModeChanged(bool value); /// signal for progress events void progressChanged(const QString& msg, float percent); /// signal when configure is done @@ -178,6 +193,8 @@ protected: void stderrCallback(std::string const& msg); void setUpEnvironment() const; + void loadPresets(); + bool WarnUninitializedMode; QString SourceDirectory; QString BinaryDirectory; @@ -185,7 +202,13 @@ protected: QString Platform; QString Toolset; std::vector<cmake::GeneratorInfo> AvailableGenerators; + cmCMakePresetsFile CMakePresetsFile; + cmCMakePresetsFile::ReadFileResult LastLoadPresetsResult = + cmCMakePresetsFile::ReadFileResult::READ_OK; + QString PresetName; QString CMakeExecutable; QAtomicInt InterruptFlag; + QProcessEnvironment StartEnvironment; QProcessEnvironment Environment; + QTimer LoadPresetsTimer; }; diff --git a/Source/QtDialog/QCMakePreset.cxx b/Source/QtDialog/QCMakePreset.cxx new file mode 100644 index 0000000..b10cf07 --- /dev/null +++ b/Source/QtDialog/QCMakePreset.cxx @@ -0,0 +1,50 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMakePreset.h" + +bool operator==(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return lhs.name == rhs.name && lhs.displayName == rhs.displayName && + lhs.description == rhs.description && lhs.generator == rhs.generator && + lhs.architecture == rhs.architecture && lhs.toolset == rhs.toolset && + lhs.setGenConfig == rhs.setGenConfig && lhs.enabled == rhs.enabled; +} + +bool operator!=(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return lhs.name < rhs.name || + (lhs.name == rhs.name && + (lhs.displayName < rhs.displayName || + (lhs.displayName == rhs.displayName && + (lhs.description < rhs.description || + (lhs.description == rhs.description && + (lhs.generator < rhs.generator || + (lhs.generator == rhs.generator && + (lhs.architecture < rhs.architecture || + (lhs.architecture == rhs.architecture && + (lhs.toolset < rhs.toolset || + (lhs.toolset == rhs.toolset && + (lhs.setGenConfig < rhs.setGenConfig || + (lhs.setGenConfig == rhs.setGenConfig && + (lhs.enabled < rhs.enabled)))))))))))))); +} + +bool operator<=(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return rhs >= lhs; +} + +bool operator>(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return rhs < lhs; +} + +bool operator>=(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return !(lhs < rhs); +} diff --git a/Source/QtDialog/QCMakePreset.h b/Source/QtDialog/QCMakePreset.h new file mode 100644 index 0000000..93d70d8 --- /dev/null +++ b/Source/QtDialog/QCMakePreset.h @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <QString> +#include <QVariant> + +#include "cmCMakePresetsFile.h" + +class QCMakePreset +{ +public: + QString name; + QString displayName; + QString description; + QString generator; + QString architecture; + QString toolset; + bool setGenConfig; + bool enabled; +}; + +bool operator==(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator!=(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator<(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator<=(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator>(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator>=(const QCMakePreset& lhs, const QCMakePreset& rhs); + +Q_DECLARE_METATYPE(QCMakePreset) diff --git a/Source/QtDialog/QCMakePresetComboBox.cxx b/Source/QtDialog/QCMakePresetComboBox.cxx new file mode 100644 index 0000000..efadb73 --- /dev/null +++ b/Source/QtDialog/QCMakePresetComboBox.cxx @@ -0,0 +1,64 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMakePresetComboBox.h" + +#include "QCMakePresetItemModel.h" + +QCMakePresetComboBox::QCMakePresetComboBox(QWidget* parent) + : QComboBox(parent) +{ + this->m_model = new QCMakePresetItemModel(this); + this->setModel(this->m_model); + + QObject::connect(this->m_model, &QCMakePresetItemModel::modelAboutToBeReset, + this, [this]() { this->m_resetting = true; }); + QObject::connect(this->m_model, &QCMakePresetItemModel::modelReset, this, + [this]() { + this->setPresetName(this->m_lastPreset); + this->m_resetting = false; + this->emitPresetChanged(); + }); + QObject::connect( + this, + static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, [this](int /*row*/) { + if (!this->m_resetting) { + this->emitPresetChanged(); + } + }); +} + +const QVector<QCMakePreset>& QCMakePresetComboBox::presets() const +{ + return this->m_model->presets(); +} + +QString QCMakePresetComboBox::presetName() const +{ + auto preset = this->currentData(); + if (preset.canConvert<QCMakePreset>()) { + return preset.value<QCMakePreset>().name; + } + return QString{}; +} + +void QCMakePresetComboBox::setPresets(const QVector<QCMakePreset>& presets) +{ + this->m_model->setPresets(presets); +} + +void QCMakePresetComboBox::setPresetName(const QString& name) +{ + this->setCurrentIndex(this->m_model->presetNameToRow(name)); + if (this->signalsBlocked()) { + this->m_lastPreset = this->presetName(); + } +} + +void QCMakePresetComboBox::emitPresetChanged() +{ + if (this->presetName() != this->m_lastPreset) { + emit this->presetChanged(this->presetName()); + this->m_lastPreset = this->presetName(); + } +} diff --git a/Source/QtDialog/QCMakePresetComboBox.h b/Source/QtDialog/QCMakePresetComboBox.h new file mode 100644 index 0000000..d1eeffe --- /dev/null +++ b/Source/QtDialog/QCMakePresetComboBox.h @@ -0,0 +1,35 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "QCMakePreset.h" +#include <QComboBox> +#include <QObject> +#include <QString> +#include <QVector> + +class QCMakePresetItemModel; + +class QCMakePresetComboBox : public QComboBox +{ + Q_OBJECT +public: + QCMakePresetComboBox(QWidget* parent = nullptr); + + const QVector<QCMakePreset>& presets() const; + QString presetName() const; + +public slots: + void setPresets(const QVector<QCMakePreset>& presets); + void setPresetName(const QString& name); + +signals: + void presetChanged(const QString& name); + +private: + QCMakePresetItemModel* m_model; + bool m_resetting = false; + QString m_lastPreset; + + void emitPresetChanged(); +}; diff --git a/Source/QtDialog/QCMakePresetItemModel.cxx b/Source/QtDialog/QCMakePresetItemModel.cxx new file mode 100644 index 0000000..00a4e18 --- /dev/null +++ b/Source/QtDialog/QCMakePresetItemModel.cxx @@ -0,0 +1,143 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMakePresetItemModel.h" + +#include <QFont> + +QCMakePresetItemModel::QCMakePresetItemModel(QObject* parent) + : QAbstractItemModel(parent) +{ +} + +QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const +{ + switch (role) { + case Qt::AccessibleDescriptionRole: + // Separators have to return "separator" for the + // AccessibleDescriptionRole. This was determined by looking at + // QComboBoxDelegate::isSeparator() (located in qcombobox_p.h.) + if (index.internalId() == SEPARATOR_INDEX) { + return QString::fromLocal8Bit("separator"); + } + return QString{}; + case Qt::DisplayRole: { + if (index.internalId() == CUSTOM_INDEX) { + return QString::fromLocal8Bit("<custom>"); + } + if (index.internalId() == SEPARATOR_INDEX) { + return QVariant{}; + } + auto const& preset = this->m_presets[index.internalId()]; + return preset.displayName.isEmpty() ? preset.name : preset.displayName; + } + case Qt::ToolTipRole: + if (index.internalId() == CUSTOM_INDEX) { + return QString::fromLocal8Bit("Specify all settings manually"); + } + if (index.internalId() == SEPARATOR_INDEX) { + return QVariant{}; + } + return this->m_presets[index.internalId()].description; + case Qt::UserRole: + if (index.internalId() == CUSTOM_INDEX) { + return QVariant{}; + } + if (index.internalId() == SEPARATOR_INDEX) { + return QVariant{}; + } + return QVariant::fromValue(this->m_presets[index.internalId()]); + case Qt::FontRole: + if (index.internalId() == CUSTOM_INDEX) { + QFont font; + font.setItalic(true); + return font; + } + return QFont{}; + default: + return QVariant{}; + } +} + +Qt::ItemFlags QCMakePresetItemModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags = + Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + if (index.internalId() != SEPARATOR_INDEX && + (index.internalId() == CUSTOM_INDEX || + this->m_presets[index.internalId()].enabled)) { + flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } + return flags; +} + +int QCMakePresetItemModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + if (this->m_presets.empty()) { + return 1; + } + return this->m_presets.size() + 2; +} + +int QCMakePresetItemModel::columnCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + return 1; +} + +QModelIndex QCMakePresetItemModel::index(int row, int column, + const QModelIndex& parent) const +{ + if (parent.isValid() || column != 0 || row < 0 || + row >= this->rowCount(QModelIndex{})) { + return QModelIndex{}; + } + + if (this->m_presets.empty() || row == this->m_presets.size() + 1) { + return this->createIndex(row, column, CUSTOM_INDEX); + } + + if (row == this->m_presets.size()) { + return this->createIndex(row, column, SEPARATOR_INDEX); + } + + return this->createIndex(row, column, static_cast<quintptr>(row)); +} + +QModelIndex QCMakePresetItemModel::parent(const QModelIndex& /*index*/) const +{ + return QModelIndex{}; +} + +QVector<QCMakePreset> const& QCMakePresetItemModel::presets() const +{ + return this->m_presets; +} + +void QCMakePresetItemModel::setPresets(QVector<QCMakePreset> const& presets) +{ + this->beginResetModel(); + this->m_presets = presets; + this->endResetModel(); +} + +int QCMakePresetItemModel::presetNameToRow(const QString& name) const +{ + if (this->m_presets.empty()) { + return 0; + } + + int index = 0; + for (auto const& preset : this->m_presets) { + if (preset.name == name) { + return index; + } + index++; + } + + return this->m_presets.size() + 1; +} diff --git a/Source/QtDialog/QCMakePresetItemModel.h b/Source/QtDialog/QCMakePresetItemModel.h new file mode 100644 index 0000000..79fba29 --- /dev/null +++ b/Source/QtDialog/QCMakePresetItemModel.h @@ -0,0 +1,45 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <cm/optional> + +#include "QCMakePreset.h" +#include <QAbstractItemModel> +#include <QModelIndex> +#include <QString> +#include <QVariant> +#include <QVector> +#include <QtGlobal> + +class QObject; + +class QCMakePresetItemModel : public QAbstractItemModel +{ + Q_OBJECT +public: + QCMakePresetItemModel(QObject* parent = nullptr); + + QVariant data(const QModelIndex& index, int role) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + + int rowCount(const QModelIndex& parent = QModelIndex{}) const override; + int columnCount(const QModelIndex& parent = QModelIndex{}) const override; + + QModelIndex index(int row, int column, + const QModelIndex& parent = QModelIndex{}) const override; + QModelIndex parent(const QModelIndex& index) const override; + + QVector<QCMakePreset> const& presets() const; + + int presetNameToRow(const QString& name) const; + +public slots: + void setPresets(QVector<QCMakePreset> const& presets); + +private: + QVector<QCMakePreset> m_presets; + + static constexpr quintptr SEPARATOR_INDEX = static_cast<quintptr>(-2); + static constexpr quintptr CUSTOM_INDEX = static_cast<quintptr>(-1); +}; |