1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
// ************************************************************************************************
//
// BornAgain: simulate and fit reflection and scattering
//
//! @file GUI/View/ParEdit/ParSpinBox.cpp
//! @brief Implements class ParSpinBox.
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
// ************************************************************************************************
#include "GUI/View/ParEdit/ParSpinBox.h"
#include <QLineEdit>
#include <QRegularExpression>
#include <QWheelEvent>
#include <cmath>
namespace {
const double upper_switch = 100;
const double lower_switch = 0.1;
const double min_val = std::numeric_limits<double>::min();
const double max_val = std::numeric_limits<double>::max();
bool useExponentialNotation(double val);
} // namespace
ParSpinBox::ParSpinBox(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_easy_scrollable(easyScrollable)
{
QLocale locale;
locale.setNumberOptions(QLocale::RejectGroupSeparator);
m_validator.setLocale(locale);
m_validator.setNotation(QDoubleValidator::ScientificNotation);
connect(this, &QAbstractSpinBox::editingFinished, this, &ParSpinBox::updateValue);
}
ParSpinBox::~ParSpinBox() = default;
void ParSpinBox::setValue(double val)
{
double old_val = m_value;
m_value = round(val, m_decimals);
updateText();
if (std::abs(old_val - m_value) > min_val)
emit valueChanged(m_value);
}
void ParSpinBox::updateValue()
{
double new_val = toDouble(text(), m_validator, m_min, m_max, m_value);
setValue(new_val);
}
void ParSpinBox::setSingleStep(double step)
{
m_step = step;
}
void ParSpinBox::setMinimum(double min)
{
m_min = min;
if (m_value < m_min)
setValue(m_min);
}
void ParSpinBox::setMaximum(double max)
{
m_max = max;
if (m_value > m_max)
setValue(m_max);
}
void ParSpinBox::setDecimals(int val)
{
if (val <= 0)
return;
m_decimals = val;
setValue(m_value);
}
void ParSpinBox::stepBy(int steps)
{
double new_val = round(m_value + m_step * steps, m_decimals);
if (inRange(new_val))
setValue(new_val);
}
QString ParSpinBox::toString(double val, int decimal_points)
{
QString result = useExponentialNotation(val) ? QString::number(val, 'e', decimal_points)
: QString::number(val, 'f', decimal_points);
return result.replace(QRegularExpression("(\\.?0+)?((e{1}[\\+|-]{1})(0+)?([1-9]{1}.*))?$"),
"\\3\\5");
}
double ParSpinBox::toDouble(QString text, const QDoubleValidator& validator, double min, double max,
double default_value)
{
int pos = 0;
if (validator.validate(text, pos) == QValidator::Acceptable) {
double new_val = validator.locale().toDouble(text);
if (std::abs(new_val) < min_val)
new_val = 0.0;
return new_val >= min && new_val <= max ? new_val : default_value;
}
return default_value;
}
double ParSpinBox::round(double val, int decimals)
{
return QString::number(val, 'e', decimals).toDouble();
}
void ParSpinBox::wheelEvent(QWheelEvent* event)
{
if (hasFocus() || m_easy_scrollable)
QAbstractSpinBox::wheelEvent(event);
else
event->ignore();
}
QAbstractSpinBox::StepEnabled ParSpinBox::stepEnabled() const
{
return isReadOnly() ? StepNone : StepUpEnabled | StepDownEnabled;
}
void ParSpinBox::updateText()
{
QString new_text = toString(m_value, m_decimals);
if (new_text != text())
lineEdit()->setText(new_text);
}
bool ParSpinBox::inRange(double val) const
{
return val >= m_min && val <= m_max;
}
namespace {
bool useExponentialNotation(double val)
{
const double abs_val = std::abs(val);
if (abs_val <= min_val)
return false;
return abs_val >= upper_switch || abs_val < lower_switch;
}
} // namespace
|