From e580a0ce25f97a6eb7ae497065e859a905eec3eb Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Fri, 12 Nov 2021 08:39:05 +0100
Subject: [PATCH] implement density of InterferenceHardDiskItem

---
 GUI/Model/Sample/ParticleLayoutItem.h         |  2 +-
 GUI/View/Edit/DoubleSpinBox.cpp               |  6 ++++++
 GUI/View/Edit/DoubleSpinBox.h                 |  5 +++++
 GUI/View/SampleDesigner/FormLayouter.cpp      | 17 ++++++++++++---
 GUI/View/SampleDesigner/FormLayouter.h        | 15 ++++++++++++-
 GUI/View/SampleDesigner/InterferenceForm.cpp  | 11 ++++++++++
 .../SampleDesigner/ParticleLayoutForm.cpp     |  5 +++++
 GUI/View/SampleDesigner/ParticleLayoutForm.h  |  5 +++++
 .../SampleDesigner/SampleEditorController.cpp | 21 +++++++++++++++++++
 .../SampleDesigner/SampleEditorController.h   | 10 +++++++++
 10 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/GUI/Model/Sample/ParticleLayoutItem.h b/GUI/Model/Sample/ParticleLayoutItem.h
index 896271fb5bf..85a193db159 100644
--- a/GUI/Model/Sample/ParticleLayoutItem.h
+++ b/GUI/Model/Sample/ParticleLayoutItem.h
@@ -45,10 +45,10 @@ public:
     SelectionDescriptor<InterferenceItem*> interference() const;
     template <typename T> T* createInterference();
     void setInterference(InterferenceItem* interference);
+    void updateDensityValue();
 
 private:
     void updateDensityAppearance();
-    void updateDensityValue();
 };
 
 template <typename T> T* ParticleLayoutItem::createInterference()
diff --git a/GUI/View/Edit/DoubleSpinBox.cpp b/GUI/View/Edit/DoubleSpinBox.cpp
index 7b176a3d162..9a2c7fd2bea 100644
--- a/GUI/View/Edit/DoubleSpinBox.cpp
+++ b/GUI/View/Edit/DoubleSpinBox.cpp
@@ -97,3 +97,9 @@ Unit DoubleSpinBox::baseUnit() const
 
     return std::get<Unit>(m_valueDescriptor.unit);
 }
+
+void DoubleSpinBox::updateValue()
+{
+    QSignalBlocker b(this);
+    setBaseValue(m_valueDescriptor.get());
+}
diff --git a/GUI/View/Edit/DoubleSpinBox.h b/GUI/View/Edit/DoubleSpinBox.h
index 05922f2f9c5..2eb15a12ef2 100644
--- a/GUI/View/Edit/DoubleSpinBox.h
+++ b/GUI/View/Edit/DoubleSpinBox.h
@@ -45,6 +45,11 @@ public:
     //! valueDescriptor().unit
     Unit baseUnit() const;
 
+    //! Update the shown value to the one contained in the value descriptor.
+    //!
+    //! No signal will be emitted if the new value has changed.
+    void updateValue();
+
 signals:
     //! Emitted whenever the value changes.
     //!
diff --git a/GUI/View/SampleDesigner/FormLayouter.cpp b/GUI/View/SampleDesigner/FormLayouter.cpp
index 183744a157a..1d5461ed0cb 100644
--- a/GUI/View/SampleDesigner/FormLayouter.cpp
+++ b/GUI/View/SampleDesigner/FormLayouter.cpp
@@ -159,7 +159,20 @@ int FormLayouter::addValue(const DoubleDescriptor& d)
     return m_formLayout->rowCount() - 1;
 }
 
+int FormLayouter::addValue(const DoubleDescriptor& d, function<void(double)> onValueChange)
+{
+    insertValue(m_formLayout->rowCount(), d, onValueChange);
+    return m_formLayout->rowCount() - 1;
+}
+
 void FormLayouter::insertValue(int row, const DoubleDescriptor& d)
+{
+    auto* ec = m_ec; // to allow copy-capture in the following lambda
+    insertValue(row, d, [ec, d](double newValue) { ec->setDouble(newValue, d); });
+}
+
+void FormLayouter::insertValue(int row, const DoubleDescriptor& d,
+                               function<void(double)> onValueChange)
 {
     auto labelText = d.label;
     if (!labelText.endsWith(":"))
@@ -170,9 +183,7 @@ void FormLayouter::insertValue(int row, const DoubleDescriptor& d)
     label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
 
     auto* editor = new DoubleSpinBox(m_formLayout->parentWidget(), d);
-    auto* ec = m_ec; // to allow copy-capture in the following lambda
-    QObject::connect(editor, &DoubleSpinBox::baseValueChanged,
-                     [ec, d](double newValue) { ec->setDouble(newValue, d); });
+    QObject::connect(editor, &DoubleSpinBox::baseValueChanged, onValueChange);
 
     label->setBuddy(editor);
 
diff --git a/GUI/View/SampleDesigner/FormLayouter.h b/GUI/View/SampleDesigner/FormLayouter.h
index 72da21ba360..23475231ed5 100644
--- a/GUI/View/SampleDesigner/FormLayouter.h
+++ b/GUI/View/SampleDesigner/FormLayouter.h
@@ -93,9 +93,17 @@ public:
     //! to the SampleEditorController which has been overhanded in the constructor of this
     //! FormLayouter. It is connected to SampleEditorController::setDouble(). If a different method
     //! should be called (e.g. for a special undo functionality), this method is not sufficient. It
-    //! would have to be done "manually". Returns the newly added row.
+    //! would have to be done "manually" or with the overload which takes a slot (see below).
+    //! Returns the newly added row.
     int addValue(const DoubleDescriptor& d);
 
+    //! Adds a row with a bold printed label and a DoubleSpinBox.
+    //!
+    //! Same as above, but the called slot in case of a value change has to be overhanded.
+    //! Use this only if the standard (calling SampleEditorController::setDouble()) is not
+    //! sufficient.
+    int addValue(const DoubleDescriptor& d, function<void(double)> onValueChange);
+
     //! Adds a row with a bold printed label and a QSpinBox.
     //!
     //! Right now in the sample model there is no uint value which has a unit, therefore no unit
@@ -108,6 +116,11 @@ public:
     //! Same functionality as addValue(), please read there.
     void insertValue(int row, const DoubleDescriptor& d);
 
+    //! Inserts a row with a bold printed label and a DoubleSpinBox.
+    //!
+    //! Same functionality as addValue(), please read there.
+    void insertValue(int row, const DoubleDescriptor& d, function<void(double)> onValueChange);
+
     //! Adds a row with a bold printed label and a set of DoubleDescriptors.
     //!
     //! The label describes the set of the Descriptors and is located in the first column of the
diff --git a/GUI/View/SampleDesigner/InterferenceForm.cpp b/GUI/View/SampleDesigner/InterferenceForm.cpp
index ae5859a154f..a92374c7d64 100644
--- a/GUI/View/SampleDesigner/InterferenceForm.cpp
+++ b/GUI/View/SampleDesigner/InterferenceForm.cpp
@@ -66,6 +66,16 @@ void InterferenceForm::createInterferenceWidgets()
 {
     FormLayouter layouter(this, m_ec);
     auto* interference = m_layoutItem->interference().currentItem();
+
+    // Some values in interference settings affect the total density in the particle layout. To
+    // provide all the updating (data & UI), the method
+    // SampleEditorController::setDensityRelatedValueValue has to be called (instead of
+    // SampleEditorController::setDouble). For this we have the following lambda to add a value:
+    const auto addDensityRelatedValue = [&](DoubleDescriptor d) {
+        layouter.addValue(
+            d, [=](double newValue) { m_ec->setDensityRelatedValue(interference, newValue, d); });
+    };
+
     if (auto* itf = dynamic_cast<Interference1DLatticeItem*>(interference)) {
         layouter.addValue(itf->positionVariance());
         layouter.addValue(itf->length());
@@ -81,6 +91,7 @@ void InterferenceForm::createInterferenceWidgets()
     } else if (auto* itf = dynamic_cast<InterferenceHardDiskItem*>(interference)) {
         layouter.addValue(itf->positionVariance());
         layouter.addValue(itf->radius());
+        addDensityRelatedValue(itf->density());
     }
     // #baLayerEditor implement missing interference function types
 }
diff --git a/GUI/View/SampleDesigner/ParticleLayoutForm.cpp b/GUI/View/SampleDesigner/ParticleLayoutForm.cpp
index 074d026f3e0..2bd268b4892 100644
--- a/GUI/View/SampleDesigner/ParticleLayoutForm.cpp
+++ b/GUI/View/SampleDesigner/ParticleLayoutForm.cpp
@@ -109,3 +109,8 @@ void ParticleLayoutForm::updateDensityEnabling()
 
     m_totalDensitySpinBox->setEnabled(enableTotalDensityInParticleLayout);
 }
+
+void ParticleLayoutForm::updateDensityValue()
+{
+    m_totalDensitySpinBox->updateValue();
+}
diff --git a/GUI/View/SampleDesigner/ParticleLayoutForm.h b/GUI/View/SampleDesigner/ParticleLayoutForm.h
index 9f04171613a..b49067d7dc7 100644
--- a/GUI/View/SampleDesigner/ParticleLayoutForm.h
+++ b/GUI/View/SampleDesigner/ParticleLayoutForm.h
@@ -45,6 +45,11 @@ public:
     //! the property shown in the particle layout shall be disabled.
     void updateDensityEnabling();
 
+    //! Update the shown density value.
+    //!
+    //! This does not update any internal values, it only refreshes the shown value.
+    void updateDensityValue();
+
 private:
     QFormLayout* m_layout;
     ParticleLayoutItem* m_layoutItem;
diff --git a/GUI/View/SampleDesigner/SampleEditorController.cpp b/GUI/View/SampleDesigner/SampleEditorController.cpp
index b5f29975760..3806e49097c 100644
--- a/GUI/View/SampleDesigner/SampleEditorController.cpp
+++ b/GUI/View/SampleDesigner/SampleEditorController.cpp
@@ -13,6 +13,7 @@
 //  ************************************************************************************************
 
 #include "GUI/View/SampleDesigner/SampleEditorController.h"
+#include "GUI/Model/Sample/InterferenceItems.h"
 #include "GUI/Model/Sample/LayerItem.h"
 #include "GUI/Model/Sample/MesoCrystalItem.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
@@ -309,6 +310,26 @@ void SampleEditorController::setMaterialValue(ItemWithMaterial* item, double new
             c->updateValues();
 }
 
+void SampleEditorController::setDensityRelatedValue(InterferenceItem* interferenceItem,
+                                                    double newValue, DoubleDescriptor d)
+{
+    setDouble(newValue, d);
+
+    // -- notify the containing particle layout about changed value
+    auto* particlelayoutItem = dynamic_cast<ParticleLayoutItem*>(interferenceItem->parent());
+    if (!particlelayoutItem)
+        return;
+    particlelayoutItem->updateDensityValue();
+
+    // -- notify the containing particle layout UI about changed value
+    ASSERT(m_multiLayerForm);
+    for (auto* c : m_multiLayerForm->findChildren<ParticleLayoutForm*>())
+        if (c->layoutItem() == particlelayoutItem) {
+            c->updateDensityValue();
+            break;
+        }
+}
+
 void SampleEditorController::onStartingToMoveLayer()
 {
     ASSERT(m_multiLayerForm);
diff --git a/GUI/View/SampleDesigner/SampleEditorController.h b/GUI/View/SampleDesigner/SampleEditorController.h
index d8ab9b2dc68..545e157880a 100644
--- a/GUI/View/SampleDesigner/SampleEditorController.h
+++ b/GUI/View/SampleDesigner/SampleEditorController.h
@@ -33,6 +33,7 @@ class SelectionContainerForm;
 class AbstractSelectionDescriptor;
 class InterferenceForm;
 class SessionItem;
+class InterferenceItem;
 
 //! Class to modify a sample from the layer oriented sample editor.
 //!
@@ -82,6 +83,15 @@ public:
     void selectMaterial(ItemWithMaterial* item, const QString& newMaterialIdentifier);
     void setMaterialValue(ItemWithMaterial* item, double newValue, DoubleDescriptor d);
 
+    //! Set an interference function's value which affects the total particle density of the
+    //! containing particle layout.
+    //!
+    //! Some values in interference settings affect the total density in the particle layout which
+    //! contains this interference function. Call this method to provide all the related updating
+    //! (data & UI).
+    void setDensityRelatedValue(InterferenceItem* interferenceItem, double newValue,
+                                DoubleDescriptor d);
+
     void onStartingToMoveLayer();
     void onStoppedToMoveLayer(QWidget* widgetToMove, QWidget* moveAboveThisWidget);
 
-- 
GitLab