From 6027455484e8807f30f16ea036c7a1c1a1d3a778 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 14 Oct 2024 13:10:39 +0200 Subject: [PATCH] Fix #262 #263 - Handle Path Arguments --- src/shared_gui_components/EditController.cpp | 31 +++++++ src/shared_gui_components/EditController.hpp | 2 + src/shared_gui_components/EditView.cpp | 82 ++++++++++++++++++- src/shared_gui_components/EditView.hpp | 51 +++++++++--- src/shared_gui_components/MeasureManager.cpp | 4 +- .../WorkflowController.cpp | 2 + 6 files changed, 159 insertions(+), 13 deletions(-) diff --git a/src/shared_gui_components/EditController.cpp b/src/shared_gui_components/EditController.cpp index 25a4e8009..f60dece28 100644 --- a/src/shared_gui_components/EditController.cpp +++ b/src/shared_gui_components/EditController.cpp @@ -7,6 +7,7 @@ #include "EditView.hpp" #include "OSViewSwitcher.hpp" #include "WorkflowController.hpp" +#include "../model_editor/Utilities.hpp" #include #include @@ -251,6 +252,22 @@ InputController::InputController(EditController* editController, measure::OSArgu static_cast(&InputController::setValue)); inputView = stringInputView; + } else if (m_argument.type() == measure::OSArgumentType::Path) { + auto* pathInputView = new PathInputView(m_argument.extension(), m_argument.isRead()); + + pathInputView->setName(m_argument.displayName(), m_argument.units(), m_argument.description()); + + if (m_argument.hasValue()) { + pathInputView->lineEdit->setText(QString::fromStdString(m_argument.valueAsString())); + } else if (m_argument.hasDefaultValue()) { + pathInputView->lineEdit->setText(QString::fromStdString(m_argument.defaultValueAsString())); + } + + connect(pathInputView, &PathInputView::selectedPathChanged, this, + static_cast(&InputController::setValue)); + connect(pathInputView->lineEdit, &QLineEdit::textEdited, [this](const QString& v) { this->setValue(toPath(v)); }); + + inputView = pathInputView; } else { inputView = new InputView(); } @@ -302,6 +319,20 @@ void InputController::setValueForIndex(int index) { } } +void InputController::setValue(const openstudio::path& p) { + if (isItOKToClearResults()) { + if (p.empty()) { + m_argument.clearValue(); + } else { + m_argument.setValue(p); + } + + m_editController->measureStepItem()->setArgument(m_argument); + + inputView->setIncomplete(isArgumentIncomplete()); + } +} + bool InputController::isArgumentIncomplete() const { bool result = false; std::vector incompleteArguments = m_editController->measureStepItem()->incompleteArguments(); diff --git a/src/shared_gui_components/EditController.hpp b/src/shared_gui_components/EditController.hpp index a68479903..c16227d3a 100644 --- a/src/shared_gui_components/EditController.hpp +++ b/src/shared_gui_components/EditController.hpp @@ -81,6 +81,8 @@ class InputController : public QObject void setValue(bool value); + void setValue(const openstudio::path& p); + void setValueForIndex(int index); private: diff --git a/src/shared_gui_components/EditView.cpp b/src/shared_gui_components/EditView.cpp index 523b25913..9778d9e9c 100644 --- a/src/shared_gui_components/EditView.cpp +++ b/src/shared_gui_components/EditView.cpp @@ -4,6 +4,8 @@ ***********************************************************************************************************************/ #include "EditView.hpp" +#include "../model_editor/Utilities.hpp" + #include #include #include @@ -18,6 +20,8 @@ #include #include #include +#include +#include #include @@ -369,8 +373,6 @@ InputCheckBox::InputCheckBox() : QAbstractButton() { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } -InputCheckBox::~InputCheckBox() = default; - void InputCheckBox::setText(const QString& text) { m_label->setText(text); @@ -400,4 +402,80 @@ void InputCheckBox::setIncomplete(bool incomplete) { } } +PathInputView::PathInputView(const std::string& extension, bool isRead) + : InputView(), lineEdit(new QLineEdit()), nameLabel(new QLabel()), m_isRead(isRead) { + auto* vLayout = new QVBoxLayout(); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(5); + setLayout(vLayout); + + nameLabel->setOpenExternalLinks(true); + nameLabel->setWordWrap(true); + vLayout->addWidget(nameLabel); + + auto* hLayout = new QHBoxLayout(); + hLayout->addWidget(lineEdit); + + selectPathButton = new QPushButton("..."); + selectPathButton->setFlat(true); + selectPathButton->setFixedSize(30, 20); + selectPathButton->setObjectName("StandardGrayButton"); + + connect(selectPathButton, &QPushButton::clicked, this, &PathInputView::onSelectPathButtonClicked); + hLayout->addWidget(selectPathButton); + + vLayout->addLayout(hLayout); + + // TODO: sanitize the input? + m_extension = toQString(extension); +} + +void PathInputView::setName(const std::string& name, const boost::optional& units, const boost::optional& description) { + QString text; + text += QString::fromStdString(name); + if (units) { + text += QString::fromStdString(" (" + units.get() + ")"); + } + if (description) { + text += QString::fromStdString("
" + description.get() + "
"); + } + + nameLabel->setText(text); +} + +void PathInputView::setIncomplete(bool incomplete) { + if (incomplete) { + nameLabel->setStyleSheet("QLabel { color: #DD0A05;}"); + } else { + nameLabel->setStyleSheet("QLabel { color: black;}"); + } +} + +void PathInputView::setDisplayValue(const QVariant& value) { + lineEdit->setText(value.toString()); +} + +void PathInputView::onSelectPathButtonClicked() { + QString lastPath = lineEdit->text(); + + QString selectedPath; + + if (m_extension.isEmpty()) { + // Assume this is a directory + selectedPath = + QFileDialog::getExistingDirectory(nullptr, tr("Open Directory"), lastPath, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + } else { + if (m_isRead) { + selectedPath = QFileDialog::getOpenFileName(nullptr, tr("Open Read File"), lastPath, m_extension); + } else { + selectedPath = QFileDialog::getSaveFileName(nullptr, tr("Select Save File"), lastPath, m_extension); + } + } + + if (!selectedPath.isEmpty()) { + lineEdit->setText(selectedPath); // QFileInfo(selectedPath).absoluteFilePath() + emit selectedPathChanged(toPath(selectedPath)); + } +} + } // namespace openstudio diff --git a/src/shared_gui_components/EditView.hpp b/src/shared_gui_components/EditView.hpp index 335fda29f..958ef28a8 100644 --- a/src/shared_gui_components/EditView.hpp +++ b/src/shared_gui_components/EditView.hpp @@ -6,14 +6,16 @@ #ifndef SHAREDGUICOMPONENTS_EDITVIEW_HPP #define SHAREDGUICOMPONENTS_EDITVIEW_HPP -#include -#include #include +#include #include +#include #include +#include class QLineEdit; +class QPushButton; class QTextEdit; class QVBoxLayout; @@ -27,7 +29,7 @@ class EditNullView : public QWidget public: explicit EditNullView(const QString& text = "Select a Measure to Edit"); - virtual ~EditNullView() {} + virtual ~EditNullView() = default; protected: void paintEvent(QPaintEvent*) override; @@ -39,7 +41,7 @@ class EditRubyMeasureView : public QWidget public: explicit EditRubyMeasureView(bool applyMeasureNow); - virtual ~EditRubyMeasureView() {} + virtual ~EditRubyMeasureView() = default; QLineEdit* nameLineEdit; @@ -77,7 +79,7 @@ class DoubleInputView : public InputView public: DoubleInputView(); - virtual ~DoubleInputView() {} + virtual ~DoubleInputView() = default; QLineEdit* lineEdit; @@ -97,7 +99,7 @@ class ChoiceInputView : public InputView public: ChoiceInputView(); - virtual ~ChoiceInputView() {} + virtual ~ChoiceInputView() = default; QComboBox* comboBox; @@ -117,7 +119,7 @@ class BoolInputView : public InputView public: BoolInputView(); - virtual ~BoolInputView() {} + virtual ~BoolInputView() = default; InputCheckBox* checkBox; @@ -134,7 +136,7 @@ class IntegerInputView : public InputView public: IntegerInputView(); - virtual ~IntegerInputView() {} + virtual ~IntegerInputView() = default; QLineEdit* lineEdit; @@ -154,7 +156,7 @@ class StringInputView : public InputView public: StringInputView(); - virtual ~StringInputView() {} + virtual ~StringInputView() = default; QLineEdit* lineEdit; @@ -183,7 +185,7 @@ class InputCheckBox : public QAbstractButton public: InputCheckBox(); - virtual ~InputCheckBox(); + virtual ~InputCheckBox() = default; void setText(const QString& text); @@ -196,6 +198,35 @@ class InputCheckBox : public QAbstractButton QLabel* m_label; }; +class PathInputView : public InputView +{ + Q_OBJECT + + public: + PathInputView(const std::string& extension, bool isRead); + virtual ~PathInputView() = default; + + QLineEdit* lineEdit; + QPushButton* selectPathButton; + + void setName(const std::string& name, const boost::optional& units, const boost::optional& description); + + void setIncomplete(bool incomplete) override; + + void setDisplayValue(const QVariant& value) override; + + signals: + void selectedPathChanged(const openstudio::path& p); + + private slots: + void onSelectPathButtonClicked(); + + private: + QLabel* nameLabel; + QString m_extension; + bool m_isRead; +}; + } // namespace openstudio #endif // SHAREDGUICOMPONENTS_EDITVIEW_HPP diff --git a/src/shared_gui_components/MeasureManager.cpp b/src/shared_gui_components/MeasureManager.cpp index 259e32dff..3e01f67dc 100644 --- a/src/shared_gui_components/MeasureManager.cpp +++ b/src/shared_gui_components/MeasureManager.cpp @@ -607,8 +607,10 @@ boost::optional MeasureManager::getArgument(const measure:: } else if (type.value() == measure::OSArgumentType::Path) { + // TODO: aside from the fact that these arguments are weird / incorrectly named, neither the BCL-gem schema nor OS SDK actually handle them so + // they are not even inside the measure.xml bool isRead = argument.get("is_read", Json::Value(false)).asBool(); - std::string extension = argument.get("extension", Json::Value("*")).asString(); + std::string extension = argument.get("extension", Json::Value("All files (*)")).asString(); result = measure::OSArgument::makePathArgument(name, isRead, extension, required, modelDependent); diff --git a/src/shared_gui_components/WorkflowController.cpp b/src/shared_gui_components/WorkflowController.cpp index 11b56e316..7338485ed 100644 --- a/src/shared_gui_components/WorkflowController.cpp +++ b/src/shared_gui_components/WorkflowController.cpp @@ -501,6 +501,8 @@ void MeasureStepItem::setArgument(const measure::OSArgument& argument) { m_step.setArgument(argument.name(), argument.valueAsDouble()); } else if (argument.type() == measure::OSArgumentType::Integer) { m_step.setArgument(argument.name(), argument.valueAsInteger()); + } else if (argument.type() == measure::OSArgumentType::Path) { + m_step.setArgument(argument.name(), openstudio::toString(argument.valueAsPath())); } else { m_step.setArgument(argument.name(), argument.valueAsString()); }