From d0b698645514f2e52528dd7adf9ee9278dc4da9b Mon Sep 17 00:00:00 2001
From: Mikhail Svechnikov <m.svechnikov@fz-juelich.de>
Date: Fri, 7 Jul 2023 11:27:00 +0200
Subject: [PATCH] Make instrument spinboxes safe (closes #476)

---
 CHANGELOG                              |  1 +
 GUI/View/Device/SphericalAxisForm.cpp  | 19 +++++++++++--------
 GUI/View/Device/SphericalAxisForm.h    |  7 +++----
 GUI/View/Numeric/DoubleSpinBox.cpp     |  4 ++--
 GUI/View/Numeric/DoubleSpinBox.h       |  2 +-
 GUI/View/Numeric/ScientificSpinBox.cpp | 12 +++++++++++-
 GUI/View/Numeric/ScientificSpinBox.h   |  6 +++++-
 7 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 69ca47ed66d..c515ce057d6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -20,6 +20,7 @@ BornAgain-21.0, in preparation
   > Bug fixes:
     * Fix averaging magnetic materials (#76)
     * Repair polarization analysis in GUI (#190)
+    * Protect all spinboxes from accidental scrolling (#476)
     * Repair specular intensity for polarized GISANS (#541)
     * Repair axis ticks display in linear scale in GUI (#575)
     * Prevent crash in QRE loader (#589)
diff --git a/GUI/View/Device/SphericalAxisForm.cpp b/GUI/View/Device/SphericalAxisForm.cpp
index 5e842143ea4..ad0f9707666 100644
--- a/GUI/View/Device/SphericalAxisForm.cpp
+++ b/GUI/View/Device/SphericalAxisForm.cpp
@@ -15,6 +15,7 @@
 #include "GUI/View/Device/SphericalAxisForm.h"
 #include "GUI/Model/Axis/BasicAxisItem.h"
 #include "GUI/View/Numeric/SafeSpinBox.h"
+#include "GUI/View/Numeric/ScientificSpinBox.h"
 
 SphericalAxisForm::SphericalAxisForm(QFormLayout* form, QWidget* parent)
     : QObject(parent)
@@ -26,19 +27,21 @@ SphericalAxisForm::SphericalAxisForm(QFormLayout* form, QWidget* parent)
             &SphericalAxisForm::onNbinsValueChanged);
     form->addRow("# scan points:", m_nbinsSpinBox);
 
-    m_minimumSpinBox = new QDoubleSpinBox(parent);
-    m_minimumSpinBox->setRange(0, 90);
-    m_minimumSpinBox->setDecimals(3);
+    m_minimumSpinBox = new ScientificSpinBox(parent);
+    m_minimumSpinBox->setMinimum(0);
+    m_minimumSpinBox->setMaximum(90);
+    m_minimumSpinBox->setDecimals(5);
     m_minimumSpinBox->setSingleStep(0.01);
-    connect(m_minimumSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this,
+    connect(m_minimumSpinBox, &ScientificSpinBox::valueChanged, this,
             &SphericalAxisForm::onMinimumValueChanged);
     form->addRow("Initial angle [deg]:", m_minimumSpinBox);
 
-    m_maximumSpinBox = new QDoubleSpinBox(parent);
-    m_maximumSpinBox->setRange(0, 90);
-    m_maximumSpinBox->setDecimals(3);
+    m_maximumSpinBox = new ScientificSpinBox(parent);
+    m_maximumSpinBox->setMinimum(0);
+    m_maximumSpinBox->setMaximum(90);
+    m_maximumSpinBox->setDecimals(5);
     m_maximumSpinBox->setSingleStep(0.01);
-    connect(m_maximumSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this,
+    connect(m_maximumSpinBox, &ScientificSpinBox::valueChanged, this,
             &SphericalAxisForm::onMaximumValueChanged);
     form->addRow("Final angle [deg]:", m_maximumSpinBox);
 }
diff --git a/GUI/View/Device/SphericalAxisForm.h b/GUI/View/Device/SphericalAxisForm.h
index c4ea7451e3d..9e41d17fc42 100644
--- a/GUI/View/Device/SphericalAxisForm.h
+++ b/GUI/View/Device/SphericalAxisForm.h
@@ -15,12 +15,11 @@
 #ifndef BORNAGAIN_GUI_VIEW_DEVICE_SPHERICALAXISFORM_H
 #define BORNAGAIN_GUI_VIEW_DEVICE_SPHERICALAXISFORM_H
 
-#include <QDoubleSpinBox>
 #include <QFormLayout>
-#include <QWidget>
 
 class BasicAxisItem;
 class SafeSpinBox;
+class ScientificSpinBox;
 
 /// 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)
@@ -46,8 +45,8 @@ private slots:
 
 private:
     SafeSpinBox* m_nbinsSpinBox;
-    QDoubleSpinBox* m_minimumSpinBox;
-    QDoubleSpinBox* m_maximumSpinBox;
+    ScientificSpinBox* m_minimumSpinBox;
+    ScientificSpinBox* m_maximumSpinBox;
     BasicAxisItem* m_axisItem;
 };
 
diff --git a/GUI/View/Numeric/DoubleSpinBox.cpp b/GUI/View/Numeric/DoubleSpinBox.cpp
index f050298af2a..dddb2d01dcd 100644
--- a/GUI/View/Numeric/DoubleSpinBox.cpp
+++ b/GUI/View/Numeric/DoubleSpinBox.cpp
@@ -19,7 +19,7 @@
 DoubleSpinBox::DoubleSpinBox(DoubleProperty& d, bool easyScrollable, QWidget* parent)
     : QDoubleSpinBox(parent)
     , m_valueProperty(d)
-    , easyScrollable(easyScrollable)
+    , m_easyScrollable(easyScrollable)
 {
     setFocusPolicy(Qt::StrongFocus);
     GUI::View::NumberUtil::configSpinbox(this, d.decimals(), d.limits());
@@ -90,7 +90,7 @@ void DoubleSpinBox::setBaseValue(double baseValue)
 
 void DoubleSpinBox::wheelEvent(QWheelEvent* event)
 {
-    if (hasFocus() || easyScrollable)
+    if (hasFocus() || m_easyScrollable)
         QDoubleSpinBox::wheelEvent(event);
     else
         event->ignore();
diff --git a/GUI/View/Numeric/DoubleSpinBox.h b/GUI/View/Numeric/DoubleSpinBox.h
index 7d716d5c21f..1b62117322d 100644
--- a/GUI/View/Numeric/DoubleSpinBox.h
+++ b/GUI/View/Numeric/DoubleSpinBox.h
@@ -75,7 +75,7 @@ private:
     Unit m_displayUnit = Unit::unitless;
 
     DoubleProperty& m_valueProperty;
-    bool easyScrollable;
+    bool m_easyScrollable;
 
     //! it was decided to not show the unit as a suffix. However, this may be user
     //! selectable once, therefore the code is kept and controlled by this flag
diff --git a/GUI/View/Numeric/ScientificSpinBox.cpp b/GUI/View/Numeric/ScientificSpinBox.cpp
index 7b557f6c94b..a7f89b721e7 100644
--- a/GUI/View/Numeric/ScientificSpinBox.cpp
+++ b/GUI/View/Numeric/ScientificSpinBox.cpp
@@ -15,6 +15,7 @@
 #include "GUI/View/Numeric/ScientificSpinBox.h"
 #include <QLineEdit>
 #include <QRegularExpression>
+#include <QWheelEvent>
 #include <cmath>
 
 namespace {
@@ -28,13 +29,14 @@ bool useExponentialNotation(double val);
 
 } // namespace
 
-ScientificSpinBox::ScientificSpinBox(QWidget* parent)
+ScientificSpinBox::ScientificSpinBox(QWidget* parent, bool easyScrollable)
     : QAbstractSpinBox(parent)
     , m_value(0.0)
     , m_min(-max_val)
     , m_max(max_val)
     , m_step(1.0)
     , m_decimals(3)
+    , m_easyScrollable(easyScrollable)
 {
     QLocale locale;
     locale.setNumberOptions(QLocale::RejectGroupSeparator);
@@ -147,6 +149,14 @@ double ScientificSpinBox::round(double val, int decimals)
     return QString::number(val, 'e', decimals).toDouble();
 }
 
+void ScientificSpinBox::wheelEvent(QWheelEvent* event)
+{
+    if (hasFocus() || m_easyScrollable)
+        QAbstractSpinBox::wheelEvent(event);
+    else
+        event->ignore();
+}
+
 QAbstractSpinBox::StepEnabled ScientificSpinBox::stepEnabled() const
 {
     return isReadOnly() ? StepNone : StepUpEnabled | StepDownEnabled;
diff --git a/GUI/View/Numeric/ScientificSpinBox.h b/GUI/View/Numeric/ScientificSpinBox.h
index 6b193939730..0ab0a6733b4 100644
--- a/GUI/View/Numeric/ScientificSpinBox.h
+++ b/GUI/View/Numeric/ScientificSpinBox.h
@@ -21,7 +21,7 @@ class ScientificSpinBox : public QAbstractSpinBox {
     Q_OBJECT
     Q_PROPERTY(double value MEMBER m_value READ value WRITE setValue NOTIFY valueChanged USER true)
 public:
-    ScientificSpinBox(QWidget* parent = nullptr);
+    ScientificSpinBox(QWidget* parent = nullptr, bool easyScrollable = false);
     ~ScientificSpinBox() override;
 
     double value() const;
@@ -48,6 +48,9 @@ public:
                            double default_value);
     static double round(double val, int decimals);
 
+protected:
+    void wheelEvent(QWheelEvent* event) override;
+
 signals:
     void valueChanged(double value);
 
@@ -61,6 +64,7 @@ private:
     double m_value, m_min, m_max;
     double m_step;
     int m_decimals;
+    bool m_easyScrollable;
     QDoubleValidator m_validator;
 };
 
-- 
GitLab