From 96265dda8bc4175fdb7d1f9a37bb2d1e6eeb765a Mon Sep 17 00:00:00 2001
From: Mikhail Svechnikov <m.svechnikov@fz-juelich.de>
Date: Fri, 24 Jun 2022 17:46:36 +0200
Subject: [PATCH] add SafeSpinBox

---
 GUI/View/Common/SafeSpinBox.cpp               | 31 ++++++++++++++++
 GUI/View/Common/SafeSpinBox.h                 | 35 +++++++++++++++++++
 GUI/View/Fit/ParameterTuningDelegate.h        |  2 --
 GUI/View/Info/MessageBox.h                    |  4 ---
 GUI/View/Instrument/AxisPropertyEditor.cpp    |  7 ++--
 GUI/View/Instrument/AxisPropertyEditor.h      |  3 +-
 .../Instrument/DetectorAlignmentEditor.cpp    |  2 +-
 GUI/View/Instrument/DistributionEditor.cpp    |  5 +--
 .../Instrument/ResolutionFunctionEditor.cpp   |  4 +--
 GUI/View/Instrument/SphericalAxisEditor.cpp   |  5 ++-
 GUI/View/Instrument/SphericalAxisEditor.h     |  5 +--
 GUI/View/Tool/WidgetUtils.cpp                 | 27 ++++++++------
 GUI/View/Tool/WidgetUtils.h                   | 14 +++++---
 13 files changed, 108 insertions(+), 36 deletions(-)
 create mode 100644 GUI/View/Common/SafeSpinBox.cpp
 create mode 100644 GUI/View/Common/SafeSpinBox.h

diff --git a/GUI/View/Common/SafeSpinBox.cpp b/GUI/View/Common/SafeSpinBox.cpp
new file mode 100644
index 00000000000..5693329d4e9
--- /dev/null
+++ b/GUI/View/Common/SafeSpinBox.cpp
@@ -0,0 +1,31 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/View/Common/DoubleLineEdit.h
+//! @brief     Defines class DoubleLineEdit
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "SafeSpinBox.h"
+#include <QWheelEvent>
+
+SafeSpinBox::SafeSpinBox(bool easyScrollable, QWidget* parent)
+    : QSpinBox{parent}
+    , easyScrollable(easyScrollable)
+{
+
+}
+
+void SafeSpinBox::wheelEvent(QWheelEvent *event)
+{
+    if (hasFocus() || easyScrollable)
+        QSpinBox::wheelEvent(event);
+    else
+        event->ignore();
+}
diff --git a/GUI/View/Common/SafeSpinBox.h b/GUI/View/Common/SafeSpinBox.h
new file mode 100644
index 00000000000..aa388e29b45
--- /dev/null
+++ b/GUI/View/Common/SafeSpinBox.h
@@ -0,0 +1,35 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/View/Common/DoubleLineEdit.h
+//! @brief     Defines class DoubleLineEdit
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef SAFESPINBOX_H
+#define SAFESPINBOX_H
+
+#include <QSpinBox>
+
+//! Spinbox that enables scrolling only on focus
+//!
+class SafeSpinBox : public QSpinBox
+{
+    Q_OBJECT
+public:
+    explicit SafeSpinBox(bool easyScrollable = false, QWidget* parent = nullptr);
+
+protected:
+    void wheelEvent(QWheelEvent* event) override;
+
+private:
+    bool easyScrollable;
+};
+
+#endif // SAFESPINBOX_H
diff --git a/GUI/View/Fit/ParameterTuningDelegate.h b/GUI/View/Fit/ParameterTuningDelegate.h
index 45ede457b02..5f75ecaaf1c 100644
--- a/GUI/View/Fit/ParameterTuningDelegate.h
+++ b/GUI/View/Fit/ParameterTuningDelegate.h
@@ -19,11 +19,9 @@
 #include <QItemDelegate>
 #include <memory>
 
-class QDoubleSpinBox;
 class QHBoxLayout;
 class ParameterItem;
 class ScientificSpinBox;
-class SessionItem;
 
 class ParameterTuningDelegate : public QItemDelegate {
     Q_OBJECT
diff --git a/GUI/View/Info/MessageBox.h b/GUI/View/Info/MessageBox.h
index a44b02b0893..55968caeef9 100644
--- a/GUI/View/Info/MessageBox.h
+++ b/GUI/View/Info/MessageBox.h
@@ -17,10 +17,6 @@
 
 #include <QWidget>
 
-class RealLimits;
-class QDoubleSpinBox;
-class QLineEdit;
-
 namespace GUI::View::Helpers {
 
 void information(QWidget* parent, const QString& title, const QString& text,
diff --git a/GUI/View/Instrument/AxisPropertyEditor.cpp b/GUI/View/Instrument/AxisPropertyEditor.cpp
index ae8f660693b..b4019169c1b 100644
--- a/GUI/View/Instrument/AxisPropertyEditor.cpp
+++ b/GUI/View/Instrument/AxisPropertyEditor.cpp
@@ -15,6 +15,7 @@
 #include "GUI/View/Instrument/AxisPropertyEditor.h"
 #include "GUI/Model/Descriptor/AxisProperty.h"
 #include "GUI/View/Common/DoubleSpinBox.h"
+#include "GUI/View/Common/SafeSpinBox.h"
 #include "GUI/View/Tool/GroupBoxCollapser.h"
 #include "GUI/View/Tool/WidgetUtils.h"
 #include <QFormLayout>
@@ -30,9 +31,9 @@ AxisPropertyEditor::AxisPropertyEditor(QWidget* parent, const QString& groupTitl
     auto* formLayout = new QFormLayout(this);
     formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
 
-    m_nbinsSpinBox = GUI::Util::createSpinBox(formLayout, axisProperty->nbins());
-    m_minSpinBox = GUI::Util::createSpinBox(formLayout, axisProperty->min());
-    m_maxSpinBox = GUI::Util::createSpinBox(formLayout, axisProperty->max());
+    m_nbinsSpinBox = GUI::Util::createSpinBox(axisProperty->nbins());
+    m_minSpinBox = GUI::Util::createDoubleSpinBoxRow(formLayout, axisProperty->min());
+    m_maxSpinBox = GUI::Util::createDoubleSpinBoxRow(formLayout, axisProperty->max());
 
     GroupBoxCollapser::installIntoGroupBox(this);
 
diff --git a/GUI/View/Instrument/AxisPropertyEditor.h b/GUI/View/Instrument/AxisPropertyEditor.h
index 83c91c3902d..360dbf0ef80 100644
--- a/GUI/View/Instrument/AxisPropertyEditor.h
+++ b/GUI/View/Instrument/AxisPropertyEditor.h
@@ -20,6 +20,7 @@
 class QSpinBox;
 class AxisProperty;
 class DoubleSpinBox;
+class SafeSpinBox;
 
 //! Use this to edit an AxisProperty.
 //!
@@ -38,7 +39,7 @@ signals:
     void dataChanged();
 
 private:
-    QSpinBox* m_nbinsSpinBox;
+    SafeSpinBox* m_nbinsSpinBox;
     AxisProperty* m_axisProperty;
     DoubleSpinBox* m_minSpinBox;
     DoubleSpinBox* m_maxSpinBox;
diff --git a/GUI/View/Instrument/DetectorAlignmentEditor.cpp b/GUI/View/Instrument/DetectorAlignmentEditor.cpp
index 42ceabc8042..03736bf5444 100644
--- a/GUI/View/Instrument/DetectorAlignmentEditor.cpp
+++ b/GUI/View/Instrument/DetectorAlignmentEditor.cpp
@@ -63,7 +63,7 @@ DetectorAlignmentEditor::DetectorAlignmentEditor(QWidget* parent, RectangularDet
 DoubleSpinBox* DetectorAlignmentEditor::createSpinBox(QFormLayout* parentFormLayout,
                                                       const DoubleDescriptor& d)
 {
-    auto* sb = GUI::Util::createSpinBox(parentFormLayout, d);
+    auto* sb = GUI::Util::createDoubleSpinBoxRow(parentFormLayout, d);
     connect(sb, &DoubleSpinBox::baseValueChanged, [=](double v) {
         if (d.get() != v) {
             d.set(v);
diff --git a/GUI/View/Instrument/DistributionEditor.cpp b/GUI/View/Instrument/DistributionEditor.cpp
index 662b5226e95..f9db76b7a67 100644
--- a/GUI/View/Instrument/DistributionEditor.cpp
+++ b/GUI/View/Instrument/DistributionEditor.cpp
@@ -19,6 +19,7 @@
 #include "GUI/Support/XML/Backup.h"
 #include "GUI/Support/XML/Streamer.h"
 #include "GUI/View/Common/DoubleSpinBox.h"
+#include "GUI/View/Common/SafeSpinBox.h"
 #include "GUI/View/Common/ScientificSpinBox.h"
 #include "GUI/View/Instrument/DistributionPlot.h"
 #include "GUI/View/Tool/GroupBoxCollapser.h"
@@ -107,7 +108,7 @@ void DistributionSelector::createDistributionWidgets()
 
 DoubleSpinBox* DistributionSelector::createSpinBox(const DoubleDescriptor& d)
 {
-    auto* sb = GUI::Util::createSpinBox(m_formLayout, d);
+    auto* sb = GUI::Util::createDoubleSpinBoxRow(m_formLayout, d);
     connect(sb, &DoubleSpinBox::baseValueChanged, [=](double v) {
         d.set(v);
         emit distributionChanged();
@@ -117,7 +118,7 @@ DoubleSpinBox* DistributionSelector::createSpinBox(const DoubleDescriptor& d)
 
 QSpinBox* DistributionSelector::createSpinBox(const UIntDescriptor& d)
 {
-    auto* sb = GUI::Util::createSpinBox(m_formLayout, d);
+    auto* sb = GUI::Util::createSpinBoxRow(m_formLayout, d);
     connect(sb, QOverload<int>::of(&QSpinBox::valueChanged), [=](int v) {
         d.set(v);
         emit distributionChanged();
diff --git a/GUI/View/Instrument/ResolutionFunctionEditor.cpp b/GUI/View/Instrument/ResolutionFunctionEditor.cpp
index 4e9bd28e4fd..eaf948dc3b3 100644
--- a/GUI/View/Instrument/ResolutionFunctionEditor.cpp
+++ b/GUI/View/Instrument/ResolutionFunctionEditor.cpp
@@ -50,8 +50,8 @@ void ResolutionFunctionEditor::createResolutionWidgets()
 
     auto* resFunction = m_item->resolutionFunctionSelection().currentItem();
     if (auto* p = dynamic_cast<ResolutionFunction2DGaussianItem*>(resFunction)) {
-        auto* sigmaXSpinBox = GUI::Util::createSpinBox(m_formLayout, p->sigmaX());
-        auto* sigmaYSpinBox = GUI::Util::createSpinBox(m_formLayout, p->sigmaY());
+        auto* sigmaXSpinBox = GUI::Util::createDoubleSpinBoxRow(m_formLayout, p->sigmaX());
+        auto* sigmaYSpinBox = GUI::Util::createDoubleSpinBoxRow(m_formLayout, p->sigmaY());
 
         connect(sigmaXSpinBox, qOverload<double>(&DoubleSpinBox::baseValueChanged),
                 [=](double newValue) {
diff --git a/GUI/View/Instrument/SphericalAxisEditor.cpp b/GUI/View/Instrument/SphericalAxisEditor.cpp
index 87429e22469..d5a184c4685 100644
--- a/GUI/View/Instrument/SphericalAxisEditor.cpp
+++ b/GUI/View/Instrument/SphericalAxisEditor.cpp
@@ -13,17 +13,16 @@
 //  ************************************************************************************************
 
 #include "GUI/View/Instrument/SphericalAxisEditor.h"
+#include "GUI/View/Common/SafeSpinBox.h"
 #include "GUI/Model/Device/AxesItems.h"
-#include <QDoubleSpinBox>
 #include <QFormLayout>
 #include <QGroupBox>
-#include <QSpinBox>
 
 SphericalAxisForm::SphericalAxisForm(QFormLayout* form, QWidget* parent)
     : QObject(parent)
     , m_item(nullptr)
 {
-    m_nbinsSpinBox = new QSpinBox(parent);
+    m_nbinsSpinBox = new SafeSpinBox(false);
     m_nbinsSpinBox->setRange(1, 65536);
     connect(m_nbinsSpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
             &SphericalAxisForm::onNbinsValueChanged);
diff --git a/GUI/View/Instrument/SphericalAxisEditor.h b/GUI/View/Instrument/SphericalAxisEditor.h
index 5a8d6ef22b6..744f150a62c 100644
--- a/GUI/View/Instrument/SphericalAxisEditor.h
+++ b/GUI/View/Instrument/SphericalAxisEditor.h
@@ -20,7 +20,8 @@
 class BasicAxisItem;
 class QDoubleSpinBox;
 class QFormLayout;
-class QSpinBox;
+class SafeSpinBox;
+
 
 /// The form for a spherical axis: contains the bare bone widgets for the user to enter
 /// the number of bins and to select the range in degrees (minimum & maximum)
@@ -45,7 +46,7 @@ private slots:
     void onMaximumValueChanged(double value);
 
 private:
-    QSpinBox* m_nbinsSpinBox;
+    SafeSpinBox* m_nbinsSpinBox;
     QDoubleSpinBox* m_minimumSpinBox;
     QDoubleSpinBox* m_maximumSpinBox;
     BasicAxisItem* m_item;
diff --git a/GUI/View/Tool/WidgetUtils.cpp b/GUI/View/Tool/WidgetUtils.cpp
index 236fcc1490e..61cdfac908d 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/Common/DoubleSpinBox.h"
 #include "GUI/View/Common/ScientificSpinBox.h"
+#include "GUI/View/Common/SafeSpinBox.h"
 #include "GUI/View/Tool/EditUtil.h"
 #include <QFormLayout>
 #include <QLabel>
@@ -23,10 +24,11 @@
 #include <QCheckBox>
 #include <QLineEdit>
 
-QSpinBox* GUI::Util::createSpinBox(QWidget* parent, const UIntDescriptor& d,
-                                   std::function<void(uint)> slot)
+SafeSpinBox* GUI::Util::createSpinBox(const UIntDescriptor& d,
+                                      std::function<void(uint)> slot,
+                                      bool easyScrollable)
 {
-    auto* spinBox = new QSpinBox(parent);
+    auto* spinBox = new SafeSpinBox(easyScrollable);
     spinBox->setFocusPolicy(Qt::StrongFocus);
     spinBox->setToolTip(d.tooltip);
     spinBox->setMaximum(std::numeric_limits<int>::max());
@@ -46,8 +48,17 @@ QSpinBox* GUI::Util::createSpinBox(QWidget* parent, const UIntDescriptor& d,
     return spinBox;
 }
 
-DoubleSpinBox* GUI::Util::createSpinBox(QFormLayout* parentLayout, const DoubleDescriptor& d,
-                                        std::function<void(double)> slot)
+SafeSpinBox* GUI::Util::createSpinBoxRow(QFormLayout* parentLayout,
+                                         const UIntDescriptor& d)
+{
+    auto* sb = createSpinBox(d);
+    parentLayout->addRow(labelWithUnit(d.label, d.unit) + ":", sb);
+    return sb;
+}
+
+DoubleSpinBox* GUI::Util::createDoubleSpinBoxRow(QFormLayout* parentLayout,
+                                                 const DoubleDescriptor& d,
+                                                 std::function<void(double)> slot)
 {
     auto* sb = new DoubleSpinBox(parentLayout->parentWidget(), d);
     parentLayout->addRow(labelWithUnit(d.label, d.unit) + ":", sb);
@@ -58,12 +69,6 @@ DoubleSpinBox* GUI::Util::createSpinBox(QFormLayout* parentLayout, const DoubleD
     return sb;
 }
 
-QSpinBox* GUI::Util::createSpinBox(QFormLayout* parentLayout, const UIntDescriptor& d)
-{
-    auto* sb = createSpinBox(parentLayout->parentWidget(), d);
-    parentLayout->addRow(labelWithUnit(d.label, d.unit) + ":", sb);
-    return sb;
-}
 
 QString GUI::Util::labelWithUnit(const QString& label, variant<QString, Unit> unit)
 {
diff --git a/GUI/View/Tool/WidgetUtils.h b/GUI/View/Tool/WidgetUtils.h
index cd3d36f5ab5..748dcbe0d3b 100644
--- a/GUI/View/Tool/WidgetUtils.h
+++ b/GUI/View/Tool/WidgetUtils.h
@@ -22,6 +22,7 @@
 #include <variant>
 
 class QSpinBox;
+class SafeSpinBox;
 class QCheckBox;
 class UIntDescriptor;
 class DoubleSpinBox;
@@ -91,15 +92,17 @@ QComboBox* createComboBox(SelectionDescriptor<T> d,
 //! No connections to update the descriptor will be established! Therefore changes in the spin box
 //! will *not* be notified to the descriptor. The additional (and optional) slot can be used to be
 //! notified about a value change.
-QSpinBox* createSpinBox(QWidget* parent, const UIntDescriptor& d,
-                        std::function<void(uint)> slot = nullptr);
+SafeSpinBox* createSpinBox(const UIntDescriptor& d,
+                           std::function<void(uint)> slot = nullptr,
+                           bool easyScrollable = false);
 
 //! Create a label and a spin box with the information found in a UIntDescriptor and place them in a
 //! row in a form layout.
 //!
 //! The label will also contain the unit, if present.
 //! Regarding the spin box creation see the method above.
-QSpinBox* createSpinBox(QFormLayout* parentLayout, const UIntDescriptor& d);
+SafeSpinBox* createSpinBoxRow(QFormLayout* parentLayout,
+                              const UIntDescriptor& d);
 
 //! Create a label and a spin box with the information found in a DoubleDescriptor and place them in
 //! a row in a form layout.
@@ -113,8 +116,9 @@ QSpinBox* createSpinBox(QFormLayout* parentLayout, const UIntDescriptor& d);
 //! will *not* be notified to the descriptor. The additional (and optional) slot can be used to be
 //! notified about a value change (the notified new value is in the base unit of the
 //! DoubleDescriptor).
-DoubleSpinBox* createSpinBox(QFormLayout* parentLayout, const DoubleDescriptor& d,
-                             std::function<void(double)> slot = nullptr);
+DoubleSpinBox* createDoubleSpinBoxRow(QFormLayout* parentLayout,
+                                      const DoubleDescriptor& d,
+                                      std::function<void(double)> slot = nullptr);
 
 //! Create a label and a scientific spin box with the information found in a DoubleDescriptor and
 //! place them in a row in a form layout.
-- 
GitLab