From 7b6a6e58d0d567436e20c82dc8ee14b50a9a04c0 Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Fri, 18 Feb 2022 12:27:37 +0100
Subject: [PATCH] simplify UI creation

---
 .../Instrument/DetectorAlignmentEditor.cpp    |  85 ++++++------
 GUI/View/Instrument/DetectorAlignmentEditor.h |   6 +-
 GUI/View/Instrument/EnvironmentEditor.cpp     |   3 +-
 .../Instrument/PolarizationAnalysisEditor.cpp | 122 +++++++-----------
 .../Instrument/PolarizationAnalysisEditor.h   |  18 +--
 GUI/View/Tool/WidgetUtils.cpp                 |   6 +
 GUI/View/Tool/WidgetUtils.h                   |   5 +
 7 files changed, 117 insertions(+), 128 deletions(-)

diff --git a/GUI/View/Instrument/DetectorAlignmentEditor.cpp b/GUI/View/Instrument/DetectorAlignmentEditor.cpp
index d4a1e31399b..61c2d88fa2b 100644
--- a/GUI/View/Instrument/DetectorAlignmentEditor.cpp
+++ b/GUI/View/Instrument/DetectorAlignmentEditor.cpp
@@ -14,15 +14,11 @@
 
 #include "GUI/View/Instrument/DetectorAlignmentEditor.h"
 #include "GUI/Model/Device/RectangularDetectorItem.h"
-#include "GUI/Util/ComboProperty.h"
 #include "GUI/View/Edit/DoubleSpinBox.h"
-#include "GUI/View/Instrument/VectorEditor.h"
 #include "GUI/View/Tool/WidgetUtils.h"
 #include <QComboBox>
-#include <QDoubleSpinBox>
 #include <QFormLayout>
-#include <QGroupBox>
-#include <QStackedLayout>
+#include <QLabel>
 
 namespace {
 QString alignmentDescription(RectangularDetector::EDetectorArrangement a)
@@ -68,7 +64,7 @@ DoubleSpinBox* DetectorAlignmentEditor::createSpinBox(QFormLayout* parentFormLay
                                                       const DoubleDescriptor& d)
 {
     auto* sb = GUI::Util::createSpinBox(parentFormLayout, d);
-    connect(sb, qOverload<double>(&DoubleSpinBox::baseValueChanged), [=](double v) {
+    connect(sb, &DoubleSpinBox::baseValueChanged, [=](double v) {
         if (d.get() != v) {
             d.set(v);
             emit dataChanged();
@@ -77,44 +73,59 @@ DoubleSpinBox* DetectorAlignmentEditor::createSpinBox(QFormLayout* parentFormLay
     return sb;
 }
 
+DoubleSpinBox* DetectorAlignmentEditor::createSpinBox(QWidget* parent, const DoubleDescriptor& d)
+{
+    auto* sb = new DoubleSpinBox(parent, d);
+    connect(sb, &DoubleSpinBox::baseValueChanged, [=](double v) {
+        if (d.get() != v) {
+            d.set(v);
+            emit dataChanged();
+        }
+    });
+    return sb;
+}
+
+void DetectorAlignmentEditor::addVector(QFormLayout* parentLayout, const VectorDescriptor& d)
+{
+    auto* layout = new QHBoxLayout;
+
+    const auto add = [&](const DoubleDescriptor& d) {
+        layout->addWidget(new QLabel(GUI::Util::labelWithUnit(d) + ":"));
+        layout->addWidget(createSpinBox(parentLayout->parentWidget(), d));
+    };
+
+    add(d.x);
+    add(d.y);
+    add(d.z);
+
+    layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
+
+    parentLayout->addRow(d.label + ":", layout);
+}
+
 void DetectorAlignmentEditor::createAligmentWidgets()
 {
     while (m_formLayout->rowCount() > 1)
         m_formLayout->removeRow(1);
 
     const QString descr = alignmentDescription(m_item->detectorAlignment());
+    auto* layout = new QFormLayout(this);
+    layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
+    layout->setContentsMargins(0, 0, 0, 0);
+    m_formLayout->addRow("", layout);
+
     if (m_item->detectorAlignment() == RectangularDetector::GENERIC) {
-        auto* layout = new QHBoxLayout(this);
-        auto* gbox = new QGroupBox(descr, this);
-        auto* form = new QFormLayout(gbox);
-
-        createSpinBox(form, m_item->u0());
-        createSpinBox(form, m_item->v0());
-
-        auto* normalEditor = new VectorEditor(
-            "Normal vector",
-            "Normal of the detector plane with length equal to the sample detector distance (%1)",
-            this);
-        normalEditor->setVector(m_item->normalVector());
-        connect(normalEditor, &VectorEditor::vectorChanged, [=]() { dataChanged(); });
-
-        auto* directionEditor = new VectorEditor(
-            "Detector axis direction vector",
-            "Detector axis direction vector w.r.t. the sample coordinate system (%1)", this);
-        directionEditor->setVector(m_item->directionVector());
-        connect(directionEditor, &VectorEditor::vectorChanged, [=]() { dataChanged(); });
-
-        layout->addWidget(gbox);
-        layout->addWidget(normalEditor);
-        layout->addWidget(directionEditor);
-
-        m_formLayout->addRow(layout);
+        addVector(layout, m_item->normalVector());
+        addVector(layout, m_item->directionVector());
+        auto* layoutUV = new QFormLayout;
+        createSpinBox(layoutUV, m_item->u0());
+        createSpinBox(layoutUV, m_item->v0());
+        layout->addRow(descr + ":", layoutUV);
     } else {
-        auto* gbox = new QGroupBox(descr, this);
-        auto* form = new QFormLayout(gbox);
-        createSpinBox(form, m_item->u0());
-        createSpinBox(form, m_item->v0());
-        createSpinBox(form, m_item->distance());
-        m_formLayout->addRow(gbox);
+        auto* layoutUVD = new QFormLayout;
+        createSpinBox(layoutUVD, m_item->u0());
+        createSpinBox(layoutUVD, m_item->v0());
+        createSpinBox(layoutUVD, m_item->distance());
+        layout->addRow(descr + ":", layoutUVD);
     }
 }
diff --git a/GUI/View/Instrument/DetectorAlignmentEditor.h b/GUI/View/Instrument/DetectorAlignmentEditor.h
index dd138d0e365..be96d94ec09 100644
--- a/GUI/View/Instrument/DetectorAlignmentEditor.h
+++ b/GUI/View/Instrument/DetectorAlignmentEditor.h
@@ -16,15 +16,13 @@
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_DETECTORALIGNMENTEDITOR_H
 
 #include <QWidget>
-#include <array>
 
-class QComboBox;
-class QStackedLayout;
 class DetectorAlignmentForm;
 class RectangularDetectorItem;
 class QFormLayout;
 class DoubleSpinBox;
 class DoubleDescriptor;
+class VectorDescriptor;
 
 /// Widget for selecting the alignment of a detector (combo box) and input of the corresponding
 /// values
@@ -40,6 +38,8 @@ signals:
 private:
     void createAligmentWidgets();
     DoubleSpinBox* createSpinBox(QFormLayout* parentFormLayout, const DoubleDescriptor& d);
+    DoubleSpinBox* createSpinBox(QWidget* parent, const DoubleDescriptor& d);
+    void addVector(QFormLayout* parentLayout, const VectorDescriptor& d);
 
 private:
     RectangularDetectorItem* m_item;
diff --git a/GUI/View/Instrument/EnvironmentEditor.cpp b/GUI/View/Instrument/EnvironmentEditor.cpp
index 2c5a431ae00..b646e578aef 100644
--- a/GUI/View/Instrument/EnvironmentEditor.cpp
+++ b/GUI/View/Instrument/EnvironmentEditor.cpp
@@ -26,6 +26,7 @@ EnvironmentEditor::EnvironmentEditor(QWidget* parent, InstrumentItem* instrument
 {
     ASSERT(instrument);
     m_formLayout = new QFormLayout(this);
+    m_formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
 
     auto* backgroundTypeCombo =
         GUI::Util::createSelectionCombo(this, instrument->backgroundSelection(), [=](int) {
@@ -49,7 +50,7 @@ void EnvironmentEditor::createBackgroundWidgets()
         spinbox->setSingleStep(0.01);
         m_formLayout->addRow("Background value:", spinbox);
 
-        connect(spinbox, qOverload<double>(&DoubleSpinBox::baseValueChanged), [=](double newValue) {
+        connect(spinbox, &DoubleSpinBox::baseValueChanged, [=](double newValue) {
             p->setBackgroundValue(newValue);
             emit dataChanged();
         });
diff --git a/GUI/View/Instrument/PolarizationAnalysisEditor.cpp b/GUI/View/Instrument/PolarizationAnalysisEditor.cpp
index 5246f09b1ec..0ff106134c2 100644
--- a/GUI/View/Instrument/PolarizationAnalysisEditor.cpp
+++ b/GUI/View/Instrument/PolarizationAnalysisEditor.cpp
@@ -13,15 +13,13 @@
 //  ************************************************************************************************
 
 #include "GUI/View/Instrument/PolarizationAnalysisEditor.h"
-#include "GUI/Model/Device/DetectorItems.h"
 #include "GUI/Model/Device/InstrumentItems.h"
-#include "GUI/View/Instrument/VectorEditor.h"
+#include "GUI/View/Edit/DoubleSpinBox.h"
 #include "GUI/View/Tool/GroupBoxCollapser.h"
+#include "GUI/View/Tool/WidgetUtils.h"
 #include <QCheckBox>
-#include <QDoubleSpinBox>
 #include <QFormLayout>
-#include <QGridLayout>
-#include <QGroupBox>
+#include <QLabel>
 
 PolarizationAnalysisEditor::PolarizationAnalysisEditor(QWidget* parent, InstrumentItem* instrument)
     : QGroupBox("Polarization analysis", parent)
@@ -31,88 +29,60 @@ PolarizationAnalysisEditor::PolarizationAnalysisEditor(QWidget* parent, Instrume
 
     auto* layout = new QVBoxLayout(this);
 
-    m_checkbox = new QCheckBox("add polarizer and analyzer to instrument");
-    m_checkbox->setChecked(m_instrument->withPolarizerAnalyzer());
-    layout->addWidget(m_checkbox);
+    auto* checkbox = new QCheckBox(QString("Enable %1").arg(title()));
+    checkbox->setChecked(m_instrument->withPolarizerAnalyzer());
+    layout->addWidget(checkbox);
 
-    m_polarizerAnalyzerWidget = new QWidget(this);
-    auto* grid = new QGridLayout(m_polarizerAnalyzerWidget);
-    grid->setContentsMargins(0, 8, 0, 0);
+    auto* polarizerAnalyzerWidget = new QWidget(this);
+    auto* formlayout = new QFormLayout(polarizerAnalyzerWidget);
+    formlayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
 
-    m_polarizerEditor = new VectorEditor("Polarizer (Bloch vector)",
-                                         "Polarization of the beam, given as the Bloch vector (%1)",
-                                         m_polarizerAnalyzerWidget);
-    grid->addWidget(m_polarizerEditor, 0, 0);
+    addVector(formlayout, m_instrument->polarization());
+    addVector(formlayout, m_instrument->analyzerDirection());
+    formlayout->addRow(GUI::Util::labelWithUnit(m_instrument->analyzerEfficiency()) + ":",
+                       createSpinBox(polarizerAnalyzerWidget, m_instrument->analyzerEfficiency()));
+    formlayout->addRow(
+        GUI::Util::labelWithUnit(m_instrument->analyzerTotalTransmission()) + ":",
+        createSpinBox(polarizerAnalyzerWidget, m_instrument->analyzerTotalTransmission()));
 
-    m_analyzerEditor =
-        new VectorEditor("Analyzer orientation", "Direction of the polarization analysis (%1)",
-                         m_polarizerAnalyzerWidget);
-    grid->addWidget(m_analyzerEditor, 0, 1);
+    layout->addWidget(polarizerAnalyzerWidget);
+    polarizerAnalyzerWidget->setVisible(m_instrument->withPolarizerAnalyzer());
 
-    auto* propertiesEditor = new QGroupBox("Analyzer properties", m_polarizerAnalyzerWidget);
-    {
-        auto* form = new QFormLayout(propertiesEditor);
-
-        auto* m_efficiencySpinbox = new QDoubleSpinBox(propertiesEditor);
-        m_efficiencySpinbox->setToolTip("Efficiency of the polarization analysis");
-        m_efficiencySpinbox->setDecimals(4);
-        m_efficiencySpinbox->setRange(std::numeric_limits<double>::lowest(),
-                                      std::numeric_limits<double>::max());
-        form->addRow("Efficiency:", m_efficiencySpinbox);
-
-        auto* m_transmissionSpinbox = new QDoubleSpinBox(propertiesEditor);
-        m_transmissionSpinbox->setToolTip("Total transmission of the polarization analysis");
-        m_transmissionSpinbox->setDecimals(4);
-        m_transmissionSpinbox->setRange(0, std::numeric_limits<double>::max());
-        form->addRow("Transmission:", m_transmissionSpinbox);
-
-        connect(m_efficiencySpinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), this,
-                &PolarizationAnalysisEditor::onEfficiencyValueChanged);
-        connect(m_transmissionSpinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), this,
-                &PolarizationAnalysisEditor::onTransmissionValueChanged);
-        m_efficiencySpinbox->setValue(instrument->analyzerEfficiency());
-        m_transmissionSpinbox->setValue(instrument->analyzerTotalTransmission());
-    }
-
-
-    grid->addWidget(propertiesEditor, 0, 2);
-    layout->addWidget(m_polarizerAnalyzerWidget);
-    m_polarizerAnalyzerWidget->setEnabled(m_instrument->withPolarizerAnalyzer());
-
-    m_polarizerEditor->setVector(m_instrument->polarization());
-    m_analyzerEditor->setVector(m_instrument->analyzerDirection());
-
-    connect(m_checkbox, &QCheckBox::toggled, this, &PolarizationAnalysisEditor::onCheckBoxToggled);
-    connect(m_polarizerEditor, &VectorEditor::vectorChanged, this,
-            &PolarizationAnalysisEditor::dataChanged);
-    connect(m_analyzerEditor, &VectorEditor::vectorChanged, this,
-            &PolarizationAnalysisEditor::dataChanged);
+    connect(checkbox, &QCheckBox::toggled, [=](bool b) {
+        polarizerAnalyzerWidget->setVisible(b);
+        m_instrument->setWithPolarizerAnalyzer(b);
+        emit dataChanged();
+    });
 
     GroupBoxCollapser::installIntoGroupBox(this);
 }
 
-void PolarizationAnalysisEditor::onCheckBoxToggled()
+DoubleSpinBox* PolarizationAnalysisEditor::createSpinBox(QWidget* parent, const DoubleDescriptor& d)
 {
-    const bool enabled = m_checkbox->isChecked();
-    m_polarizerAnalyzerWidget->setEnabled(enabled);
-    if (enabled != m_instrument->withPolarizerAnalyzer()) {
-        m_instrument->setWithPolarizerAnalyzer(enabled);
-        emit dataChanged();
-    }
+    auto* sb = new DoubleSpinBox(parent, d);
+    connect(sb, &DoubleSpinBox::baseValueChanged, [=](double v) {
+        if (d.get() != v) {
+            d.set(v);
+            emit dataChanged();
+        }
+    });
+    return sb;
 }
 
-void PolarizationAnalysisEditor::onEfficiencyValueChanged(double efficiency)
+void PolarizationAnalysisEditor::addVector(QFormLayout* parentLayout, const VectorDescriptor& d)
 {
-    if (m_instrument->analyzerEfficiency() != efficiency) {
-        m_instrument->setAnalyzerEfficiency(efficiency);
-        emit dataChanged();
-    }
-}
+    auto* layout = new QHBoxLayout;
 
-void PolarizationAnalysisEditor::onTransmissionValueChanged(double transmission)
-{
-    if (m_instrument->analyzerTotalTransmission() != transmission) {
-        m_instrument->setAnalyzerTotalTransmission(transmission);
-        emit dataChanged();
-    }
+    const auto add = [&](const DoubleDescriptor& d) {
+        layout->addWidget(new QLabel(GUI::Util::labelWithUnit(d) + ":"));
+        layout->addWidget(createSpinBox(parentLayout->parentWidget(), d));
+    };
+
+    add(d.x);
+    add(d.y);
+    add(d.z);
+
+    layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
+
+    parentLayout->addRow(d.label + ":", layout);
 }
diff --git a/GUI/View/Instrument/PolarizationAnalysisEditor.h b/GUI/View/Instrument/PolarizationAnalysisEditor.h
index 20ed7ff7bfa..9bfceca2f0a 100644
--- a/GUI/View/Instrument/PolarizationAnalysisEditor.h
+++ b/GUI/View/Instrument/PolarizationAnalysisEditor.h
@@ -18,8 +18,10 @@
 #include <QGroupBox>
 
 class InstrumentItem;
-class QCheckBox;
-class VectorEditor;
+class DoubleSpinBox;
+class DoubleDescriptor;
+class QFormLayout;
+class VectorDescriptor;
 
 //! Polarization analysis editor (beam polarization, analyzer properties) for instrument editors.
 //! Operates on Instrument2DItem.
@@ -33,16 +35,10 @@ public:
 signals:
     void dataChanged();
 
-private slots:
-    void onCheckBoxToggled();
-    void onEfficiencyValueChanged(double efficiency);
-    void onTransmissionValueChanged(double transmission);
-
 private:
-    QCheckBox* m_checkbox;
-    QWidget* m_polarizerAnalyzerWidget;
-    VectorEditor* m_polarizerEditor;
-    VectorEditor* m_analyzerEditor;
+    DoubleSpinBox* createSpinBox(QWidget* parent, const DoubleDescriptor& d);
+    void addVector(QFormLayout* parentLayout, const VectorDescriptor& d);
+
     InstrumentItem* m_instrument;
 };
 
diff --git a/GUI/View/Tool/WidgetUtils.cpp b/GUI/View/Tool/WidgetUtils.cpp
index 77c3e2b4cd7..21da61ee522 100644
--- a/GUI/View/Tool/WidgetUtils.cpp
+++ b/GUI/View/Tool/WidgetUtils.cpp
@@ -16,6 +16,7 @@
 #include "GUI/Model/Descriptor/UIntDescriptor.h"
 #include "GUI/View/Edit/DoubleSpinBox.h"
 #include <QFormLayout>
+#include <QLabel>
 #include <QSpinBox>
 
 QSpinBox* GUI::Util::createSpinBox(QWidget* parent, const UIntDescriptor& d,
@@ -71,3 +72,8 @@ QString GUI::Util::labelWithUnit(const QString& label, variant<QString, Unit> un
 
     return label;
 }
+
+QString GUI::Util::labelWithUnit(const DoubleDescriptor& d)
+{
+    return labelWithUnit(d.label, d.unit);
+}
diff --git a/GUI/View/Tool/WidgetUtils.h b/GUI/View/Tool/WidgetUtils.h
index 8526a374429..cea8aaf0b05 100644
--- a/GUI/View/Tool/WidgetUtils.h
+++ b/GUI/View/Tool/WidgetUtils.h
@@ -101,6 +101,11 @@ DoubleSpinBox* createSpinBox(QFormLayout* parentLayout, const DoubleDescriptor&
 //! No trailing ':' will be appended
 QString labelWithUnit(const QString& label, std::variant<QString, Unit> unit);
 
+//! Create a label with an optional unit in brackets, both taken from the given descriptor.
+//!
+//! No trailing ':' will be appended
+QString labelWithUnit(const DoubleDescriptor& d);
+
 } // namespace GUI::Util
 
 #endif // BORNAGAIN_GUI_VIEW_TOOL_WIDGETUTILS_H
-- 
GitLab