From dd7f4890a259ea27e7f94f8dfaaf7a25c9d012ed Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 25 Nov 2019 17:55:39 +0100 Subject: [PATCH] ProjectExplorer: Introduce BuildDirectoryAspect Change-Id: Id57d0a7901d2cec7b2d4f1fbeed6a1ecb41642cc Reviewed-by: hjk --- src/plugins/projectexplorer/buildaspects.cpp | 100 ++++++++++++++++++ src/plugins/projectexplorer/buildaspects.h | 24 +++++ .../projectexplorer/buildconfiguration.cpp | 17 ++- .../projectexplorer/buildconfiguration.h | 4 +- .../projectconfigurationaspects.cpp | 49 ++++++++- .../projectconfigurationaspects.h | 8 ++ 6 files changed, 184 insertions(+), 18 deletions(-) diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp index b0c1e193179..f7bbeaa4930 100644 --- a/src/plugins/projectexplorer/buildaspects.cpp +++ b/src/plugins/projectexplorer/buildaspects.cpp @@ -25,8 +25,108 @@ #include "buildaspects.h" +#include +#include + +#include +#include + +using namespace Utils; + namespace ProjectExplorer { +class BuildDirectoryAspect::Private +{ +public: + FilePath sourceDir; + FilePath savedShadowBuildDir; + QString problem; + QPointer warningLabel; + QPointer problemLabel; +}; + +BuildDirectoryAspect::BuildDirectoryAspect() : d(new Private) +{ + setSettingsKey("ProjectExplorer.BuildConfiguration.BuildDirectory"); + setLabelText(tr("Build directory:")); + setDisplayStyle(PathChooserDisplay); + setExpectedKind(Utils::PathChooser::Directory); + setUncheckedSemantics(UncheckedSemantics::ReadOnly); +} + +BuildDirectoryAspect::~BuildDirectoryAspect() +{ + delete d; +} + +void BuildDirectoryAspect::allowInSourceBuilds(const FilePath &sourceDir) +{ + d->sourceDir = sourceDir; + makeCheckable(tr("Shadow Build"), QString()); +} + +bool BuildDirectoryAspect::isShadowBuild() const +{ + return !d->sourceDir.isEmpty() && d->sourceDir != filePath(); +} + +void BuildDirectoryAspect::setProblem(const QString &description) +{ + d->problem = description; + updateProblemLabel(); +} + +void BuildDirectoryAspect::toMap(QVariantMap &map) const +{ + BaseStringAspect::toMap(map); + if (!d->sourceDir.isEmpty()) { + const FilePath shadowDir = isChecked() ? filePath() : d->savedShadowBuildDir; + map.insert(settingsKey() + ".shadowDir", shadowDir.toString()); + } +} + +void BuildDirectoryAspect::fromMap(const QVariantMap &map) +{ + BaseStringAspect::fromMap(map); + if (!d->sourceDir.isEmpty()) { + d->savedShadowBuildDir = FilePath::fromString(map.value(settingsKey() + ".shadowDir") + .toString()); + setChecked(d->sourceDir != filePath()); + } +} + +void BuildDirectoryAspect::addToLayout(LayoutBuilder &builder) +{ + BaseStringAspect::addToLayout(builder); + d->warningLabel = new QLabel; + d->warningLabel->setAlignment(Qt::AlignTop); + d->warningLabel->setPixmap(Icons::WARNING.pixmap()); + d->problemLabel = new QLabel; + d->problemLabel->setAlignment(Qt::AlignTop); + builder.startNewRow().addItems(QString(), d->warningLabel.data(), d->problemLabel.data()); + updateProblemLabel(); + if (!d->sourceDir.isEmpty()) { + connect(this, &BaseStringAspect::checkedChanged, builder.layout(), [this] { + if (isChecked()) { + setFilePath(d->savedShadowBuildDir); + } else { + d->savedShadowBuildDir = filePath(); + setFilePath(d->sourceDir); + } + }); + } +} + +void BuildDirectoryAspect::updateProblemLabel() +{ + if (!d->warningLabel) + return; + QTC_ASSERT(d->problemLabel, return); + d->problemLabel->setText(d->problem); + d->problemLabel->setVisible(!d->problem.isEmpty()); + d->warningLabel->setVisible(!d->problem.isEmpty()); +} + SeparateDebugInfoAspect::SeparateDebugInfoAspect() { setDisplayName(tr("Separate Debug Info")); diff --git a/src/plugins/projectexplorer/buildaspects.h b/src/plugins/projectexplorer/buildaspects.h index 40d123fee3d..8e5674f2096 100644 --- a/src/plugins/projectexplorer/buildaspects.h +++ b/src/plugins/projectexplorer/buildaspects.h @@ -28,8 +28,32 @@ #include "projectexplorer_export.h" #include "projectconfigurationaspects.h" +namespace Utils { class FilePath; } + namespace ProjectExplorer { +class PROJECTEXPLORER_EXPORT BuildDirectoryAspect : public BaseStringAspect +{ + Q_OBJECT +public: + BuildDirectoryAspect(); + ~BuildDirectoryAspect() override; + + void allowInSourceBuilds(const Utils::FilePath &sourceDir); + bool isShadowBuild() const; + void setProblem(const QString &description); + +private: + void toMap(QVariantMap &map) const override; + void fromMap(const QVariantMap &map) override; + void addToLayout(LayoutBuilder &builder) override; + + void updateProblemLabel(); + + class Private; + Private * const d; +}; + class PROJECTEXPLORER_EXPORT SeparateDebugInfoAspect : public BaseTriStateAspect { Q_OBJECT diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index dedb30c7c64..67c960ddfbb 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -25,6 +25,7 @@ #include "buildconfiguration.h" +#include "buildaspects.h" #include "buildenvironmentwidget.h" #include "buildinfo.h" #include "buildsteplist.h" @@ -35,7 +36,6 @@ #include "kitinformation.h" #include "kitmanager.h" #include "project.h" -#include "projectconfigurationaspects.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" #include "projectmacroexpander.h" @@ -44,6 +44,7 @@ #include "session.h" #include +#include #include #include @@ -61,7 +62,6 @@ static const char BUILD_STEP_LIST_COUNT[] = "ProjectExplorer.BuildConfiguration. static const char BUILD_STEP_LIST_PREFIX[] = "ProjectExplorer.BuildConfiguration.BuildStepList."; static const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "ProjectExplorer.BuildConfiguration.ClearSystemEnvironment"; static const char USER_ENVIRONMENT_CHANGES_KEY[] = "ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"; -static const char BUILDDIRECTORY_KEY[] = "ProjectExplorer.BuildConfiguration.BuildDirectory"; namespace ProjectExplorer { namespace Internal { @@ -72,7 +72,7 @@ public: bool m_clearSystemEnvironment = false; Utils::EnvironmentItems m_userEnvironmentChanges; QList m_stepLists; - ProjectExplorer::BaseStringAspect *m_buildDirectoryAspect = nullptr; + BuildDirectoryAspect *m_buildDirectoryAspect = nullptr; Utils::FilePath m_lastEmittedBuildDirectory; mutable Utils::Environment m_cachedEnvironment; QString m_configWidgetDisplayName; @@ -116,16 +116,12 @@ BuildConfiguration::BuildConfiguration(Target *target, Core::Id id) connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, this, &BuildConfiguration::updateCacheAndEmitEnvironmentChanged); - d->m_buildDirectoryAspect = addAspect(); - d->m_buildDirectoryAspect->setSettingsKey(BUILDDIRECTORY_KEY); - d->m_buildDirectoryAspect->setLabelText(tr("Build directory:")); - d->m_buildDirectoryAspect->setDisplayStyle(BaseStringAspect::PathChooserDisplay); - d->m_buildDirectoryAspect->setExpectedKind(Utils::PathChooser::Directory); + d->m_buildDirectoryAspect = addAspect(); d->m_buildDirectoryAspect->setBaseFileName(target->project()->projectDirectory()); d->m_buildDirectoryAspect->setEnvironment(environment()); + d->m_buildDirectoryAspect->setMacroExpanderProvider([this] { return macroExpander(); }); connect(d->m_buildDirectoryAspect, &BaseStringAspect::changed, this, &BuildConfiguration::buildDirectoryChanged); - connect(this, &BuildConfiguration::environmentChanged, this, [this] { d->m_buildDirectoryAspect->setEnvironment(environment()); this->target()->buildEnvironmentChanged(this); @@ -133,7 +129,6 @@ BuildConfiguration::BuildConfiguration(Target *target, Core::Id id) connect(target, &Target::parsingStarted, this, &BuildConfiguration::enabledChanged); connect(target, &Target::parsingFinished, this, &BuildConfiguration::enabledChanged); - connect(this, &BuildConfiguration::enabledChanged, this, [this] { if (isActive() && project() == SessionManager::startupProject()) { ProjectExplorerPlugin::updateActions(); @@ -309,7 +304,7 @@ QVariant BuildConfiguration::extraInfo() const return d->m_extraInfo; } -ProjectExplorer::BaseStringAspect *BuildConfiguration::buildDirectoryAspect() const +ProjectExplorer::BuildDirectoryAspect *BuildConfiguration::buildDirectoryAspect() const { return d->m_buildDirectoryAspect; } diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h index 2a57fb58589..55e7da41bd5 100644 --- a/src/plugins/projectexplorer/buildconfiguration.h +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -36,7 +36,7 @@ namespace ProjectExplorer { namespace Internal { class BuildConfigurationPrivate; } -class BaseStringAspect; +class BuildDirectoryAspect; class BuildInfo; class BuildSystem; class BuildStepList; @@ -107,7 +107,7 @@ public: static void prependCompilerPathToEnvironment(Kit *k, Utils::Environment &env); void updateCacheAndEmitEnvironmentChanged(); - ProjectExplorer::BaseStringAspect *buildDirectoryAspect() const; + ProjectExplorer::BuildDirectoryAspect *buildDirectoryAspect() const; void setConfigWidgetDisplayName(const QString &display); void setBuildDirectoryHistoryCompleter(const QString &history); void setConfigWidgetHasFrame(bool configWidgetHasFrame); diff --git a/src/plugins/projectexplorer/projectconfigurationaspects.cpp b/src/plugins/projectexplorer/projectconfigurationaspects.cpp index 6e958e3f155..4eb31b1d9fb 100644 --- a/src/plugins/projectexplorer/projectconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/projectconfigurationaspects.cpp @@ -32,6 +32,7 @@ #include "runconfiguration.h" #include "target.h" +#include #include #include #include @@ -84,6 +85,8 @@ class BaseStringAspectPrivate { public: BaseStringAspect::DisplayStyle m_displayStyle = BaseStringAspect::LabelDisplay; + BaseStringAspect::UncheckedSemantics m_uncheckedSemantics + = BaseStringAspect::UncheckedSemantics::ReadOnly; QString m_labelText; std::function m_displayFilter; std::unique_ptr m_checker; @@ -98,10 +101,20 @@ public: QPointer m_lineEditDisplay; QPointer m_pathChooserDisplay; QPointer m_textEditDisplay; + Utils::MacroExpanderProvider m_expanderProvider; QPixmap m_labelPixmap; Utils::FilePath m_baseFileName; bool m_readOnly = false; bool m_showToolTipOnLabel = false; + + template void updateWidgetFromCheckStatus(Widget *w) + { + const bool enabled = !m_checker || m_checker->value(); + if (m_uncheckedSemantics == BaseStringAspect::UncheckedSemantics::Disabled) + w->setEnabled(enabled); + else + w->setReadOnly(!enabled); + } }; class BaseIntegerAspectPrivate @@ -205,6 +218,12 @@ bool BaseStringAspect::isChecked() const return !d->m_checker || d->m_checker->value(); } +void BaseStringAspect::setChecked(bool checked) +{ + QTC_ASSERT(d->m_checker, return); + d->m_checker->setValue(checked); +} + void BaseStringAspect::setDisplayStyle(DisplayStyle displayStyle) { d->m_displayStyle = displayStyle; @@ -260,6 +279,16 @@ void BaseStringAspect::setReadOnly(bool readOnly) d->m_textEditDisplay->setReadOnly(readOnly); } +void BaseStringAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider) +{ + d->m_expanderProvider = expanderProvider; +} + +void BaseStringAspect::setUncheckedSemantics(BaseStringAspect::UncheckedSemantics semantics) +{ + d->m_uncheckedSemantics = semantics; +} + void BaseStringAspect::addToLayout(LayoutBuilder &builder) { QTC_CHECK(!d->m_label); @@ -270,6 +299,14 @@ void BaseStringAspect::addToLayout(LayoutBuilder &builder) d->m_label->setPixmap(d->m_labelPixmap); builder.addItem(d->m_label.data()); + const auto useMacroExpander = [this, &builder](QWidget *w) { + if (!d->m_expanderProvider) + return; + const auto chooser = new Core::VariableChooser(builder.layout()->parentWidget()); + chooser->addSupportedWidget(w); + chooser->addMacroExpanderProvider(d->m_expanderProvider); + }; + switch (d->m_displayStyle) { case PathChooserDisplay: d->m_pathChooserDisplay = new PathChooser; @@ -279,6 +316,7 @@ void BaseStringAspect::addToLayout(LayoutBuilder &builder) d->m_pathChooserDisplay->setEnvironment(d->m_environment); d->m_pathChooserDisplay->setBaseFileName(d->m_baseFileName); d->m_pathChooserDisplay->setReadOnly(d->m_readOnly); + useMacroExpander(d->m_pathChooserDisplay->lineEdit()); connect(d->m_pathChooserDisplay, &PathChooser::pathChanged, this, &BaseStringAspect::setValue); builder.addItem(d->m_pathChooserDisplay.data()); @@ -289,6 +327,7 @@ void BaseStringAspect::addToLayout(LayoutBuilder &builder) if (!d->m_historyCompleterKey.isEmpty()) d->m_lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey); d->m_lineEditDisplay->setReadOnly(d->m_readOnly); + useMacroExpander(d->m_lineEditDisplay); connect(d->m_lineEditDisplay, &FancyLineEdit::textEdited, this, &BaseStringAspect::setValue); builder.addItem(d->m_lineEditDisplay.data()); @@ -297,6 +336,7 @@ void BaseStringAspect::addToLayout(LayoutBuilder &builder) d->m_textEditDisplay = new QTextEdit; d->m_textEditDisplay->setPlaceholderText(d->m_placeHolderText); d->m_textEditDisplay->setReadOnly(d->m_readOnly); + useMacroExpander(d->m_textEditDisplay); connect(d->m_textEditDisplay, &QTextEdit::textChanged, this, [this] { const QString value = d->m_textEditDisplay->document()->toPlainText(); if (value != d->m_value) { @@ -324,21 +364,19 @@ void BaseStringAspect::update() const QString displayedString = d->m_displayFilter ? d->m_displayFilter(d->m_value) : d->m_value; - const bool enabled = !d->m_checker || d->m_checker->value(); - if (d->m_pathChooserDisplay) { d->m_pathChooserDisplay->setFileName(FilePath::fromString(displayedString)); - d->m_pathChooserDisplay->setEnabled(enabled); + d->updateWidgetFromCheckStatus(d->m_pathChooserDisplay.data()); } if (d->m_lineEditDisplay) { d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString); - d->m_lineEditDisplay->setEnabled(enabled); + d->updateWidgetFromCheckStatus(d->m_lineEditDisplay.data()); } if (d->m_textEditDisplay) { d->m_textEditDisplay->setText(displayedString); - d->m_textEditDisplay->setEnabled(enabled); + d->updateWidgetFromCheckStatus(d->m_textEditDisplay.data()); } if (d->m_labelDisplay) { @@ -362,6 +400,7 @@ void BaseStringAspect::makeCheckable(const QString &checkerLabel, const QString connect(d->m_checker.get(), &BaseBoolAspect::changed, this, &BaseStringAspect::update); connect(d->m_checker.get(), &BaseBoolAspect::changed, this, &BaseStringAspect::changed); + connect(d->m_checker.get(), &BaseBoolAspect::changed, this, &BaseStringAspect::checkedChanged); update(); } diff --git a/src/plugins/projectexplorer/projectconfigurationaspects.h b/src/plugins/projectexplorer/projectconfigurationaspects.h index 4fe4ac30110..d88bd6be1e5 100644 --- a/src/plugins/projectexplorer/projectconfigurationaspects.h +++ b/src/plugins/projectexplorer/projectconfigurationaspects.h @@ -29,6 +29,7 @@ #include "environmentaspect.h" #include +#include #include #include @@ -128,8 +129,12 @@ public: void setEnvironment(const Utils::Environment &env); void setBaseFileName(const Utils::FilePath &baseFileName); void setReadOnly(bool readOnly); + void setMacroExpanderProvider(const Utils::MacroExpanderProvider &expanderProvider); + enum class UncheckedSemantics { Disabled, ReadOnly }; + void setUncheckedSemantics(UncheckedSemantics semantics); bool isChecked() const; + void setChecked(bool checked); void makeCheckable(const QString &optionalLabel, const QString &optionalBaseKey); enum DisplayStyle { @@ -146,6 +151,9 @@ public: Utils::FilePath filePath() const; void setFilePath(const Utils::FilePath &val); +signals: + void checkedChanged(); + private: void update();