From a3003043b12ee55f21838b222e83f47c3e703848 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Fri, 31 May 2024 16:37:13 +0100 Subject: [PATCH 1/3] Starting to implement add/remove buttons for variable removal. --- .../expressionVariableVector.cpp | 16 +++- .../keywordWidgets/expressionVariableVector.h | 5 +- .../expressionVariableVector.ui | 81 ++++++++++++++++++- .../models/expressionVariableVectorModel.cpp | 25 +++++- .../models/expressionVariableVectorModel.h | 6 +- src/keywords/expressionVariableVector.cpp | 1 + src/keywords/expressionVariableVector.h | 1 + src/procedure/nodes/node.cpp | 11 ++- src/procedure/nodes/node.h | 3 +- 9 files changed, 138 insertions(+), 11 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index e969d41d97..97c8a056c9 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -20,7 +20,9 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWi // Connect signals / slots connect(&variableModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector &)), this, - SLOT(modelDataChanged(const QModelIndex &, const QModelIndex &))); + SLOT(variableDataChanged(const QModelIndex &, const QModelIndex &))); + connect(ui_.VariablesTable->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(variableSelectionChanged(const QItemSelection &, const QItemSelection &))); // Add suitable delegate to the table ui_.VariablesTable->setItemDelegateForColumn(2, new ExponentialSpinDelegate(this)); @@ -31,7 +33,7 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWi */ // Variable data changed -void ExpressionVariableVectorKeywordWidget::modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +void ExpressionVariableVectorKeywordWidget::variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (refreshing_) return; @@ -39,5 +41,15 @@ void ExpressionVariableVectorKeywordWidget::modelDataChanged(const QModelIndex & Q_EMIT(keywordDataChanged(keyword_->editSignals())); } +void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItemSelection ¤t, + const QItemSelection &previous) +{ + ui_.RemoveVariableButton->setEnabled(current.empty()); +} + +void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) { } + +void ExpressionVariableVectorKeywordWidget::ui_RemoveVariableButton_clicked(bool checked) {} + // Update value displayed in widget void ExpressionVariableVectorKeywordWidget::updateValue(const Flags &mutationFlags) {} diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index b3ddf50573..d0fd1c62d5 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -36,7 +36,10 @@ class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidg ExpressionVariableVectorModel variableModel_; private Q_SLOTS: - void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void variableSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous); + void ui_AddVariableButton_clicked(bool checked); + void ui_RemoveVariableButton_clicked(bool checked); Q_SIGNALS: // Keyword data changed diff --git a/src/gui/keywordWidgets/expressionVariableVector.ui b/src/gui/keywordWidgets/expressionVariableVector.ui index ada6448ab3..97ff965b02 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.ui +++ b/src/gui/keywordWidgets/expressionVariableVector.ui @@ -7,7 +7,7 @@ 0 0 194 - 81 + 99 @@ -53,8 +53,85 @@ + + + + 4 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + 0 + 0 + + + + Remove + + + + :/general/icons/remove.svg:/general/icons/remove.svg + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Add + + + + :/general/icons/add.svg:/general/icons/add.svg + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + - + + + diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/models/expressionVariableVectorModel.cpp index 744870077a..6a5a6ec5d0 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/models/expressionVariableVectorModel.cpp @@ -6,7 +6,7 @@ // Set source variable data void ExpressionVariableVectorModel::setData(std::vector> &variables, - const ProcedureNode *parentNode) + ProcedureNode *parentNode) { beginResetModel(); variables_ = variables; @@ -123,3 +123,26 @@ bool ExpressionVariableVectorModel::setData(const QModelIndex &index, const QVar Q_EMIT dataChanged(index, index); return true; } + +bool ExpressionVariableVectorModel::insertRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(count); + beginInsertRows(parent, row, row); + parentNode_->addParameter("NewParameter", 0.0, row); + endInsertRows(); + return true; +} + +bool ExpressionVariableVectorModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(count); + if (row >= rowCount(parent) || row < 0) + { + return false; + } + + beginRemoveRows(parent, row, row); +// ranges_->get().erase(ranges_->get().begin() + row); + endRemoveRows(); + return true; +} diff --git a/src/gui/models/expressionVariableVectorModel.h b/src/gui/models/expressionVariableVectorModel.h index c5190a5756..2f63b0bc3c 100644 --- a/src/gui/models/expressionVariableVectorModel.h +++ b/src/gui/models/expressionVariableVectorModel.h @@ -21,11 +21,11 @@ class ExpressionVariableVectorModel : public QAbstractTableModel // Source variable data OptionalReferenceWrapper>> variables_; // Parent procedure node (to enable parameter search) - const ProcedureNode *parentNode_{nullptr}; + ProcedureNode *parentNode_{nullptr}; public: // Set source variable data - void setData(std::vector> &variables, const ProcedureNode *parentNode); + void setData(std::vector> &variables, ProcedureNode *parentNode); int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; @@ -33,4 +33,6 @@ class ExpressionVariableVectorModel : public QAbstractTableModel QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; + bool insertRows(int row, int count, const QModelIndex &parent) override; + bool removeRows(int row, int count, const QModelIndex &parent) override; }; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 630d131fe6..b0588f1bdc 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -23,6 +23,7 @@ std::vector> &ExpressionVariableVectorKeywor const std::vector> &ExpressionVariableVectorKeyword::data() const { return data_; } // Return parent ProcedureNode +ProcedureNode *ExpressionVariableVectorKeyword::parentNode() { return parentNode_; } const ProcedureNode *ExpressionVariableVectorKeyword::parentNode() const { return parentNode_; } /* diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index bc7514ac1a..b126b2eb8f 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -33,6 +33,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase std::vector> &data(); const std::vector> &data() const; // Return parent ProcedureNode + ProcedureNode *parentNode(); const ProcedureNode *parentNode() const; /* diff --git a/src/procedure/nodes/node.cpp b/src/procedure/nodes/node.cpp index 0ab6e03f2b..9be05b3d5b 100644 --- a/src/procedure/nodes/node.cpp +++ b/src/procedure/nodes/node.cpp @@ -154,11 +154,18 @@ OptionalReferenceWrapper ProcedureNode::branch() { return */ // Add new parameter -std::shared_ptr ProcedureNode::addParameter(std::string_view name, const ExpressionValue &initialValue) +std::shared_ptr ProcedureNode::addParameter(std::string_view name, const ExpressionValue &initialValue, + std::optional insertAt) { - auto &newVar = parameters_.emplace_back(std::make_shared(name, initialValue)); + auto newVar = std::make_shared(name, initialValue); + if (insertAt) + parameters_.insert(parameters_.begin() + *insertAt, newVar); + else + parameters_.emplace_back(newVar); + if (type_ != ProcedureNode::NodeType::Parameters) newVar->setNamePrefix(name_); + return newVar; } diff --git a/src/procedure/nodes/node.h b/src/procedure/nodes/node.h index bab00d4cf5..02381419d9 100644 --- a/src/procedure/nodes/node.h +++ b/src/procedure/nodes/node.h @@ -139,7 +139,8 @@ class ProcedureNode : public std::enable_shared_from_this, public public: // Add new parameter - std::shared_ptr addParameter(std::string_view name, const ExpressionValue &initialValue = {}); + std::shared_ptr addParameter(std::string_view name, const ExpressionValue &initialValue = {}, + std::optional insertAt = {}); // Return the named parameter (if it exists) std::shared_ptr getParameter(std::string_view name, const std::shared_ptr &excludeParameter = {}); From ac9cb7e52a1b660620d0195a47ce8e7ab5cccbc4 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Sat, 1 Jun 2024 17:18:45 +0100 Subject: [PATCH 2/3] Start sketching out C++-side custom model. --- src/keywords/expressionVariableVector.cpp | 13 +++- src/keywords/expressionVariableVector.h | 86 +++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index b0588f1bdc..84ca02bef2 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -8,9 +8,20 @@ #include "procedure/nodes/node.h" #include +enum ExpressionVariableProperties +{ + Name, + Type, + Value +}; +std::vector expressionVariableProperties = {{ExpressionVariableProperties::Name, "Name", PropertyType::String, false}, + {ExpressionVariableProperties::Type, "Type", PropertyType::String, false}, + {ExpressionVariableProperties::Value, "Value", PropertyType::String, false} +}; + ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode) - : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode) + : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_, expressionVariableProperties) { } diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index b126b2eb8f..edbd09b32f 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -10,6 +10,91 @@ class ExpressionVariable; class ProcedureNode; +// Data property types +enum class PropertyType +{ + Invalid, + Integer, + Double, + String +}; + +// Name / Column Title, Data Type, ReadOnly? +using DataItemProperty = std::tuple; + +struct DataItemValue +{ + PropertyType type; + union + { + int intValue{0}; + double doubleValue; + std::string stringValue; + }; +}; +template class DataModel +{ + public: + // Data access functions + using DataSetFunction = std::function; + using DataGetFunction = std::function; + DataModel(std::vector &data, const std::vector &itemProperties) : data_(data), itemProperties_(itemProperties) {} + + private: + // Target data for the model + std::vector &data_; + + /* + * Extent + */ + private: + // Return whether the supplied index is valid + bool isIndexValid(int childIndex, int columnIndex) const { return (childIndex >= 0 && childIndex < data_.size() && columnIndex >= 0); } + // Functions for accessing data extents (table style) + std::function childCountFunction_{ + [&](const int childIndex, const int columnIndex) { return isIndexValid(childIndex, columnIndex) ? 0 : data_.size(); }}; + std::function dataItemCountFunction_{ + [&](const int childIndex, const int columnIndex) { return !isIndexValid(childIndex, columnIndex) ? 0 : itemProperties_.size(); }}; + + public: + // Return number of children (rows) for the specified index + int nChildren(int childIndex, int columnIndex) { return childCountFunction_(childIndex, columnIndex); } + // Return number of data items (columns) for the specified index + int nDataItems(int childIndex, int columnIndex) { return dataItemCountFunction_(childIndex, columnIndex); } + + /* + * Data Access + */ + private: + // Descriptions of relevant item properties within a single object in the container + std::vector itemProperties_; + // Return whether column index holds / can be set by the given type + bool isPropertyType(int columnIndex, PropertyType descriptorType) { return std::get<2>(itemProperties_[columnIndex]) == descriptorType; } + // Set / get functions, unique per class + DataSetFunction setDataFunction_; + DataGetFunction getDataFunction_; + + public: + // Set data functions + bool setData(int childIndex, int columnIndex, const DataItemValue &newValue) { + // Check index validity + if (!isIndexValid(childIndex, columnIndex)) + return false; + + if (!isPropertyType(columnIndex, PropertyType::Integer)) + { + fmt::print("Refusing to set data '{}' with an int since it is of a different type.\n", std::get<1>(itemProperties_[columnIndex])); + return false; + } + + // Set the child at the specified index + if (!setDataFunction_) + return false; + else + return setDataFunction_(data_[childIndex], std::get<0>(itemProperties_[columnIndex]), newValue); + } +}; + // Keyword managing vector of ExpressionVariable class ExpressionVariableVectorKeyword : public KeywordBase { @@ -23,6 +108,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase private: // Reference to vector of data std::vector> &data_; + DataModel> dataModel_; // Parent ProcedureNode ProcedureNode *parentNode_; From 84f684c3aa589676ccde8b474493fb01ee6ee849 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Sun, 2 Jun 2024 00:05:04 +0100 Subject: [PATCH 3/3] All working so far. --- .../expressionVariableVector.cpp | 7 +- .../keywordWidgets/expressionVariableVector.h | 2 +- .../models/expressionVariableVectorModel.cpp | 135 ++++++---------- .../models/expressionVariableVectorModel.h | 22 +-- src/keywords/expressionVariableVector.cpp | 60 ++++++- src/keywords/expressionVariableVector.h | 151 ++++++++++++++---- 6 files changed, 233 insertions(+), 144 deletions(-) diff --git a/src/gui/keywordWidgets/expressionVariableVector.cpp b/src/gui/keywordWidgets/expressionVariableVector.cpp index 97c8a056c9..4c545e4826 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.cpp +++ b/src/gui/keywordWidgets/expressionVariableVector.cpp @@ -9,13 +9,12 @@ ExpressionVariableVectorKeywordWidget::ExpressionVariableVectorKeywordWidget(QWidget *parent, ExpressionVariableVectorKeyword *keyword, const CoreData &coreData) - : QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword) + : QWidget(parent), KeywordWidgetBase(coreData), keyword_(keyword), variableModel_(keyword->dataModel()) { // Create and set up the UI for our widget ui_.setupUi(this); - // Set up model - variableModel_.setData(keyword->data(), keyword->parentNode()); + // Set model ui_.VariablesTable->setModel(&variableModel_); // Connect signals / slots @@ -47,7 +46,7 @@ void ExpressionVariableVectorKeywordWidget::variableSelectionChanged(const QItem ui_.RemoveVariableButton->setEnabled(current.empty()); } -void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) { } +void ExpressionVariableVectorKeywordWidget::ui_AddVariableButton_clicked(bool checked) {} void ExpressionVariableVectorKeywordWidget::ui_RemoveVariableButton_clicked(bool checked) {} diff --git a/src/gui/keywordWidgets/expressionVariableVector.h b/src/gui/keywordWidgets/expressionVariableVector.h index d0fd1c62d5..b86c766485 100644 --- a/src/gui/keywordWidgets/expressionVariableVector.h +++ b/src/gui/keywordWidgets/expressionVariableVector.h @@ -33,7 +33,7 @@ class ExpressionVariableVectorKeywordWidget : public QWidget, public KeywordWidg // Main form declaration Ui::ExpressionVariableVectorWidget ui_; // Model for table - ExpressionVariableVectorModel variableModel_; + DataTableModelInterface variableModel_; private Q_SLOTS: void variableDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); diff --git a/src/gui/models/expressionVariableVectorModel.cpp b/src/gui/models/expressionVariableVectorModel.cpp index 6a5a6ec5d0..146737d0c0 100644 --- a/src/gui/models/expressionVariableVectorModel.cpp +++ b/src/gui/models/expressionVariableVectorModel.cpp @@ -4,137 +4,100 @@ #include "gui/models/expressionVariableVectorModel.h" #include "procedure/nodes/node.h" -// Set source variable data -void ExpressionVariableVectorModel::setData(std::vector> &variables, - ProcedureNode *parentNode) -{ - beginResetModel(); - variables_ = variables; - parentNode_ = parentNode; - endResetModel(); -} +DataTableModelInterface::DataTableModelInterface(DataModelBase &dataModel) : dataModel_(dataModel) {} -int ExpressionVariableVectorModel::rowCount(const QModelIndex &parent) const +int DataTableModelInterface::rowCount(const QModelIndex &parent) const { - if (parent.isValid()) - return 0; - return variables_ ? variables_->get().size() : 0; + return dataModel_.nChildren(parent.row(), parent.column()); } -int ExpressionVariableVectorModel::columnCount(const QModelIndex &parent) const + +int DataTableModelInterface::columnCount(const QModelIndex &parent) const { - if (parent.isValid()) - return 0; - return 3; + return dataModel_.nProperties(parent.row(), parent.column()); } -Qt::ItemFlags ExpressionVariableVectorModel::flags(const QModelIndex &index) const +Qt::ItemFlags DataTableModelInterface::flags(const QModelIndex &index) const { + // TODO if (index.column() == 1) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; else return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; } -QVariant ExpressionVariableVectorModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant DataTableModelInterface::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return {}; - if (orientation == Qt::Horizontal) - switch (section) - { - case 0: - return "Name"; - case 1: - return "Type"; - case 2: - return "Value"; - default: - return {}; - } - - return {}; + return QString::fromStdString(dataModel_.propertyName(section)); } // Bond model -QVariant ExpressionVariableVectorModel::data(const QModelIndex &index, int role) const +QVariant DataTableModelInterface::data(const QModelIndex &index, int role) const { - if (!index.isValid() || !variables_) - return {}; - - auto &vars = variables_->get(); - - if (index.row() >= vars.size() || index.row() < 0) - return {}; - if (role != Qt::DisplayRole && role != Qt::EditRole) return {}; - auto &var = vars[index.row()]; + // Get the specified data property + auto property = dataModel_.getProperty(index.row(), index.column()); - switch (index.column()) + switch (property.type()) { - // Name - case 0: - return QString::fromStdString(std::string(var->baseName())); - case 1: - return var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real"; - case 2: - return QString::fromStdString(var->value().asString()); + case (PropertyType::Invalid): + return {}; + case (PropertyType::Integer): + return property.intValue(); + case (PropertyType::Double): + return property.doubleValue(); + case (PropertyType::String): + return QString::fromStdString(property.stringValue()); default: return {}; } - - return {}; } -bool ExpressionVariableVectorModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool DataTableModelInterface::setData(const QModelIndex &index, const QVariant &value, int role) { - if (role != Qt::EditRole || !variables_ || index.column() == 1) + if (role != Qt::EditRole || dataModel_.isPropertyFlagSet(index.column(), PropertyFlag::ReadOnly)) return false; - auto &var = variables_->get()[index.row()]; - - if (index.column() == 0) - { - // Name - must check for existing var in scope with the same name - auto p = parentNode_->getParameterInScope(value.toString().toStdString()); - if (p && p != var) - return false; - var->setBaseName(value.toString().toStdString()); - } - else if (index.column() == 2) + // Set new value + bool success = false; + switch (dataModel_.propertyType(index.column())) { - // Value - need to check type (int vs double) - auto varValue = value.toString().toStdString(); - bool isFloatingPoint = false; - if (DissolveSys::isNumber(varValue, isFloatingPoint)) - { - if (isFloatingPoint) - var->setValue(value.toDouble()); - else - var->setValue(value.toInt()); - } - else - return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", varValue, - var->baseName()); + case (PropertyType::Integer): + success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toInt())); + break; + case (PropertyType::Double): + success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toDouble())); + break; + case (PropertyType::String): + success = dataModel_.setProperty(index.row(), index.column(), DataItemValue(value.toString().toStdString())); + break; + default: + break; } - Q_EMIT dataChanged(index, index); - return true; + if (success) + Q_EMIT dataChanged(index, index); + + return success; } -bool ExpressionVariableVectorModel::insertRows(int row, int count, const QModelIndex &parent) +bool DataTableModelInterface::insertRows(int row, int count, const QModelIndex &parent) { + // TODO Q_UNUSED(count); beginInsertRows(parent, row, row); - parentNode_->addParameter("NewParameter", 0.0, row); + // parentNode_->addParameter("NewParameter", 0.0, row); endInsertRows(); return true; } -bool ExpressionVariableVectorModel::removeRows(int row, int count, const QModelIndex &parent) +bool DataTableModelInterface::removeRows(int row, int count, const QModelIndex &parent) { + // TODO Q_UNUSED(count); if (row >= rowCount(parent) || row < 0) { @@ -142,7 +105,7 @@ bool ExpressionVariableVectorModel::removeRows(int row, int count, const QModelI } beginRemoveRows(parent, row, row); -// ranges_->get().erase(ranges_->get().begin() + row); + // ranges_->get().erase(ranges_->get().begin() + row); endRemoveRows(); return true; } diff --git a/src/gui/models/expressionVariableVectorModel.h b/src/gui/models/expressionVariableVectorModel.h index 2f63b0bc3c..4da2f172c3 100644 --- a/src/gui/models/expressionVariableVectorModel.h +++ b/src/gui/models/expressionVariableVectorModel.h @@ -3,29 +3,23 @@ #pragma once -#include "expression/variable.h" +#include "keywords/expressionVariableVector.h" #include "templates/optionalRef.h" #include #include #include -// Forward Declarations -class ProcedureNode; - -// Expression Variable Vector Model -class ExpressionVariableVectorModel : public QAbstractTableModel +// Qt Interface to DataTableModel +class DataTableModelInterface : public QAbstractTableModel { Q_OBJECT - private: - // Source variable data - OptionalReferenceWrapper>> variables_; - // Parent procedure node (to enable parameter search) - ProcedureNode *parentNode_{nullptr}; - public: - // Set source variable data - void setData(std::vector> &variables, ProcedureNode *parentNode); + DataTableModelInterface(DataModelBase &dataModel); + + private: + // Model with which to interface + DataModelBase &dataModel_; int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; diff --git a/src/keywords/expressionVariableVector.cpp b/src/keywords/expressionVariableVector.cpp index 84ca02bef2..2cecefcb65 100644 --- a/src/keywords/expressionVariableVector.cpp +++ b/src/keywords/expressionVariableVector.cpp @@ -14,15 +14,64 @@ enum ExpressionVariableProperties Type, Value }; -std::vector expressionVariableProperties = {{ExpressionVariableProperties::Name, "Name", PropertyType::String, false}, - {ExpressionVariableProperties::Type, "Type", PropertyType::String, false}, - {ExpressionVariableProperties::Value, "Value", PropertyType::String, false} -}; +std::vector expressionVariableProperties = { + {ExpressionVariableProperties::Name, "Name", PropertyType::String, {}}, + {ExpressionVariableProperties::Type, "Type", PropertyType::String, {PropertyFlag::ReadOnly}}, + {ExpressionVariableProperties::Value, "Value", PropertyType::String, {}}}; ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &data, ProcedureNode *parentNode) : KeywordBase(typeid(this)), data_(data), parentNode_(parentNode), dataModel_(data_, expressionVariableProperties) { + dataModel_.setPropertyFunctions( + [&](const std::shared_ptr &var, int propertyIndex) + { + switch (propertyIndex) + { + case (ExpressionVariableProperties::Name): + return DataItemValue(var->baseName()); + case (ExpressionVariableProperties::Type): + return DataItemValue(std::string(var->value().type() == ExpressionValue::ValueType::Integer ? "Int" : "Real")); + case (ExpressionVariableProperties::Value): + return DataItemValue(var->value().asString()); + default: + return DataItemValue(); + } + }, + [&](std::shared_ptr &var, int propertyIndex, const DataItemValue &newValue) + { + switch (propertyIndex) + { + case (ExpressionVariableProperties::Name): + { + // Must check for existing var in scope with the same name + auto p = parentNode_->getParameter(newValue.stringValue()); + if (p && p != var) + return false; + var->setBaseName(newValue.stringValue()); + } + break; + case (ExpressionVariableProperties::Value): + { + // Value - need to check type (int vs double) + bool isFloatingPoint = false; + if (DissolveSys::isNumber(newValue.stringValue(), isFloatingPoint)) + { + if (isFloatingPoint) + var->setValue(std::stod(newValue.stringValue())); + else + var->setValue(std::stoi(newValue.stringValue())); + } + else + return Messenger::error("Value '{}' provided for variable '{}' doesn't appear to be a number.\n", + newValue.stringValue(), var->baseName()); + } + break; + default: + return false; + } + return true; + }); } /* @@ -33,6 +82,9 @@ ExpressionVariableVectorKeyword::ExpressionVariableVectorKeyword(std::vector> &ExpressionVariableVectorKeyword::data() { return data_; } const std::vector> &ExpressionVariableVectorKeyword::data() const { return data_; } +// Return data model +DataTableModel> &ExpressionVariableVectorKeyword::dataModel() { return dataModel_; } + // Return parent ProcedureNode ProcedureNode *ExpressionVariableVectorKeyword::parentNode() { return parentNode_; } const ProcedureNode *ExpressionVariableVectorKeyword::parentNode() const { return parentNode_; } diff --git a/src/keywords/expressionVariableVector.h b/src/keywords/expressionVariableVector.h index edbd09b32f..ad1b646da1 100644 --- a/src/keywords/expressionVariableVector.h +++ b/src/keywords/expressionVariableVector.h @@ -19,26 +19,85 @@ enum class PropertyType String }; -// Name / Column Title, Data Type, ReadOnly? -using DataItemProperty = std::tuple; +// Data property flags +enum PropertyFlag +{ + ReadOnly +}; + +// Index / Column ID, Name / Column Title, Data Type, ReadOnly? +using DataItemProperty = std::tuple>; + +class DataItemValue +{ + public: + DataItemValue() : type_(PropertyType::Invalid) {} + DataItemValue(int i) : type_(PropertyType::Integer), intValue_(i) {} + DataItemValue(double d) : type_(PropertyType::Double), doubleValue_(d) {} + DataItemValue(std::string_view sv) : type_(PropertyType::String), stringValue_(sv) {} + DataItemValue(std::string s) : type_(PropertyType::String), stringValue_(std::move(s)) {} + + private: + PropertyType type_; + int intValue_{0}; + double doubleValue_{0.0}; + std::string stringValue_; + + public: + // Return value type + PropertyType type() const { return type_; } + // Return integer value + int intValue() const { return intValue_; } + // Return double value + double doubleValue() const { return doubleValue_; } + // Return string value + std::string stringValue() const { return stringValue_; } +}; -struct DataItemValue +class DataModelBase { - PropertyType type; - union + public: + DataModelBase(const std::vector &itemProperties) : itemProperties_(itemProperties) {} + + /* + * Properties + */ + protected: + // Descriptions of relevant item properties within a single object in the container + std::vector itemProperties_; + + public: + // Return name of specified property + std::string propertyName(int propertyIndex) { return std::get<1>(itemProperties_[propertyIndex]); } + // Return property type for the specified column + PropertyType propertyType(int propertyIndex) { return std::get<2>(itemProperties_[propertyIndex]); } + // Return whether the specified property flag is set + bool isPropertyFlagSet(int propertyIndex, PropertyFlag flag) { - int intValue{0}; - double doubleValue; - std::string stringValue; - }; + return std::get<3>(itemProperties_[propertyIndex]).isSet(flag); + } + + public: + // Return number of children (rows) for the specified index + virtual int nChildren(int dataIndex, int propertyIndex) = 0; + // Return number of properties (i.e. columns) for the specified index + virtual int nProperties(int dataIndex, int propertyIndex) = 0; + // Set property function + virtual bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) = 0; + // Get property function + virtual DataItemValue getProperty(int dataIndex, int propertyIndex) = 0; }; -template class DataModel + +template class DataTableModel : public DataModelBase { public: // Data access functions - using DataSetFunction = std::function; - using DataGetFunction = std::function; - DataModel(std::vector &data, const std::vector &itemProperties) : data_(data), itemProperties_(itemProperties) {} + using PropertySetFunction = std::function; + using PropertyGetFunction = std::function; + DataTableModel(std::vector &data, const std::vector &itemProperties) + : DataModelBase(itemProperties), data_(data) + { + } private: // Target data for the model @@ -49,49 +108,69 @@ template class DataModel */ private: // Return whether the supplied index is valid - bool isIndexValid(int childIndex, int columnIndex) const { return (childIndex >= 0 && childIndex < data_.size() && columnIndex >= 0); } + bool isIndexValid(int dataIndex, int propertyIndex) const + { + return dataIndex >= 0 && dataIndex < data_.size() && propertyIndex >= 0 && propertyIndex < itemProperties_.size(); + } // Functions for accessing data extents (table style) - std::function childCountFunction_{ - [&](const int childIndex, const int columnIndex) { return isIndexValid(childIndex, columnIndex) ? 0 : data_.size(); }}; - std::function dataItemCountFunction_{ - [&](const int childIndex, const int columnIndex) { return !isIndexValid(childIndex, columnIndex) ? 0 : itemProperties_.size(); }}; + std::function childCountFunction_{[&](const int dataIndex, const int propertyIndex) { + return isIndexValid(dataIndex, propertyIndex) ? 0 : data_.size(); + }}; + std::function propertyCountFunction_{[&](const int dataIndex, const int propertyIndex) + { return itemProperties_.size(); }}; public: // Return number of children (rows) for the specified index - int nChildren(int childIndex, int columnIndex) { return childCountFunction_(childIndex, columnIndex); } - // Return number of data items (columns) for the specified index - int nDataItems(int childIndex, int columnIndex) { return dataItemCountFunction_(childIndex, columnIndex); } + int nChildren(int dataIndex, int propertyIndex) final { return childCountFunction_(dataIndex, propertyIndex); } + // Return number of properties per child (i.e. columns) for the specified index + int nProperties(int dataIndex, int propertyIndex) final { return propertyCountFunction_(dataIndex, propertyIndex); } /* * Data Access */ private: - // Descriptions of relevant item properties within a single object in the container - std::vector itemProperties_; - // Return whether column index holds / can be set by the given type - bool isPropertyType(int columnIndex, PropertyType descriptorType) { return std::get<2>(itemProperties_[columnIndex]) == descriptorType; } // Set / get functions, unique per class - DataSetFunction setDataFunction_; - DataGetFunction getDataFunction_; + PropertySetFunction setPropertyFunction_; + PropertyGetFunction getPropertyFunction_; public: - // Set data functions - bool setData(int childIndex, int columnIndex, const DataItemValue &newValue) { + // Set property access functions + void setPropertyFunctions(PropertyGetFunction getFunction, PropertySetFunction setFunction) + { + getPropertyFunction_ = std::move(getFunction); + setPropertyFunction_ = std::move(setFunction); + } + // Set property + bool setProperty(int dataIndex, int propertyIndex, const DataItemValue &newValue) final + { // Check index validity - if (!isIndexValid(childIndex, columnIndex)) + if (!isIndexValid(dataIndex, propertyIndex)) return false; - if (!isPropertyType(columnIndex, PropertyType::Integer)) + if (std::get<3>(itemProperties_[propertyIndex]).isSet(PropertyFlag::ReadOnly)) { - fmt::print("Refusing to set data '{}' with an int since it is of a different type.\n", std::get<1>(itemProperties_[columnIndex])); + fmt::print("Refusing to set data '{}' since it is read-only.\n", std::get<1>(itemProperties_[propertyIndex])); return false; } // Set the child at the specified index - if (!setDataFunction_) + if (!setPropertyFunction_) return false; else - return setDataFunction_(data_[childIndex], std::get<0>(itemProperties_[columnIndex]), newValue); + return setPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex]), newValue); + } + // Get property + DataItemValue getProperty(int dataIndex, int propertyIndex) final + { + // Check index validity + if (!isIndexValid(dataIndex, propertyIndex)) + return {}; + + // Set the child at the specified index + if (!getPropertyFunction_) + return {}; + else + return getPropertyFunction_(data_[dataIndex], std::get<0>(itemProperties_[propertyIndex])); } }; @@ -108,7 +187,7 @@ class ExpressionVariableVectorKeyword : public KeywordBase private: // Reference to vector of data std::vector> &data_; - DataModel> dataModel_; + DataTableModel> dataModel_; // Parent ProcedureNode ProcedureNode *parentNode_; @@ -118,6 +197,8 @@ class ExpressionVariableVectorKeyword : public KeywordBase // Return reference to vector of data std::vector> &data(); const std::vector> &data() const; + // Return data model + DataTableModel> &dataModel(); // Return parent ProcedureNode ProcedureNode *parentNode(); const ProcedureNode *parentNode() const;