File indexing completed on 2024-12-22 04:13:15

0001 /*
0002  *  SPDX-FileCopyrightText: 2022 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisCurveWidgetControlsManager.h"
0008 
0009 #include <QSpinBox>
0010 #include <QDoubleSpinBox>
0011 #include <kis_curve_widget.h>
0012 #include <kis_signals_blocker.h>
0013 #include "kis_debug.h"
0014 
0015 #include <KisSpinBoxSplineUnitConverter.h>
0016 
0017 namespace detail {
0018 qreal io2sp(int x, int min, int max)
0019 {
0020     KisSpinBoxSplineUnitConverter unitConverter;
0021     return unitConverter.io2sp(x, min, max);
0022 }
0023 
0024 int sp2io(qreal x, int min, int max)
0025 {
0026     KisSpinBoxSplineUnitConverter unitConverter;
0027     return unitConverter.sp2io(x, min, max);
0028 }
0029 
0030 qreal io2sp(qreal x, qreal min, qreal max)
0031 {
0032     const qreal rangeLen = max - min;
0033     return !qFuzzyIsNull(rangeLen) ? (x - min) / rangeLen : 0.0;
0034 }
0035 
0036 qreal sp2io(qreal x, qreal min, qreal max)
0037 {
0038     const qreal rangeLen = max - min;
0039     return x * rangeLen + min;
0040 }
0041 
0042 void setupStep(QSpinBox *spinBox, int min, int max) {
0043     Q_UNUSED(min);
0044     Q_UNUSED(max);
0045     spinBox->setSingleStep(1);
0046 }
0047 
0048 void setupStep(QDoubleSpinBox *spinBox, qreal min, qreal max) {
0049     if (qAbs(max - min) > 10) {
0050         spinBox->setSingleStep(1.0);
0051     } else {
0052         spinBox->setSingleStep(0.1);
0053     }
0054 }
0055 
0056 bool willChangeSpinBox(const QSpinBox *spinBox, int newValue)
0057 {
0058     return spinBox->value() != newValue;
0059 }
0060 
0061 bool willChangeSpinBox(const QDoubleSpinBox *spinBox, qreal newValue)
0062 {
0063     return qRound(spinBox->value() * spinBox->decimals()) !=
0064         qRound(newValue * spinBox->decimals());
0065 }
0066 
0067 } // namespace detail
0068 
0069 KisCurveWidgetControlsManagerBase::KisCurveWidgetControlsManagerBase(KisCurveWidget *curveWidget)
0070     : QObject(curveWidget)
0071     , m_curveWidget(curveWidget)
0072 {
0073     connect(m_curveWidget, &KisCurveWidget::shouldSyncIOControls, this, &KisCurveWidgetControlsManagerBase::syncIOControls);
0074     connect(m_curveWidget, &KisCurveWidget::shouldFocusIOControls, this, &KisCurveWidgetControlsManagerBase::focusIOControls);
0075 }
0076 
0077 KisCurveWidgetControlsManagerBase::~KisCurveWidgetControlsManagerBase()
0078 {
0079 }
0080 
0081 
0082 template <typename SpinBox>
0083 KisCurveWidgetControlsManager<SpinBox>::
0084 KisCurveWidgetControlsManager(KisCurveWidget *curveWidget)
0085     : KisCurveWidgetControlsManagerBase(curveWidget)
0086 {
0087 
0088 }
0089 
0090 template <typename SpinBox>
0091 KisCurveWidgetControlsManager<SpinBox>::
0092 KisCurveWidgetControlsManager(KisCurveWidget *curveWidget,
0093                                  SpinBox *in, SpinBox *out,
0094                                  ValueType inMin, ValueType inMax,
0095                                  ValueType outMin, ValueType outMax)
0096     : KisCurveWidgetControlsManager(curveWidget)
0097 {
0098     setupInOutControls(in, out, inMin, inMax, outMin, outMax);
0099 }
0100 
0101 template <typename SpinBox>
0102 KisCurveWidgetControlsManager<SpinBox>::
0103 ~KisCurveWidgetControlsManager()
0104 {
0105 }
0106 
0107 template <typename SpinBox>
0108 void KisCurveWidgetControlsManager<SpinBox>::
0109 setupInOutControls(SpinBox *in, SpinBox *out,
0110                    ValueType inMin, ValueType inMax,
0111                    ValueType outMin, ValueType outMax)
0112 {
0113     dropInOutControls();
0114 
0115     m_intIn = in;
0116     m_intOut = out;
0117 
0118     if (!m_intIn || !m_intOut)
0119         return;
0120 
0121     m_inMin = inMin;
0122     m_inMax = inMax;
0123     m_outMin = outMin;
0124     m_outMax = outMax;
0125 
0126     ValueType realInMin = qMin(inMin, inMax); // tilt elevation has range (90, 0), which QSpinBox can't handle
0127     ValueType realInMax = qMax(inMin, inMax);
0128 
0129     m_intIn->setRange(realInMin, realInMax);
0130     m_intOut->setRange(m_outMin, m_outMax);
0131 
0132     detail::setupStep(m_intIn, realInMin, realInMax);
0133     detail::setupStep(m_intOut, m_outMin, m_outMax);
0134 
0135     connect(m_intIn, qOverload<ValueType>(&SpinBox::valueChanged), this, &KisCurveWidgetControlsManager::inOutChanged, Qt::UniqueConnection);
0136     connect(m_intOut, qOverload<ValueType>(&SpinBox::valueChanged), this, &KisCurveWidgetControlsManager::inOutChanged, Qt::UniqueConnection);
0137 
0138     syncIOControls();
0139 }
0140 
0141 template <typename SpinBox>
0142 void KisCurveWidgetControlsManager<SpinBox>::
0143 dropInOutControls()
0144 {
0145     if (!m_intIn || !m_intOut)
0146         return;
0147 
0148     disconnect(m_intIn, qOverload<ValueType>(&SpinBox::valueChanged), this, &KisCurveWidgetControlsManager::inOutChanged);
0149     disconnect(m_intOut, qOverload<ValueType>(&SpinBox::valueChanged), this, &KisCurveWidgetControlsManager::inOutChanged);
0150 
0151     m_intIn = m_intOut = nullptr;
0152 
0153 }
0154 
0155 template <typename SpinBox>
0156 void KisCurveWidgetControlsManager<SpinBox>::
0157 inOutChanged()
0158 {
0159     QPointF pt;
0160 
0161     KIS_SAFE_ASSERT_RECOVER_RETURN(m_curveWidget->currentPoint());
0162 
0163     pt.setX(detail::io2sp(m_intIn->value(), m_inMin, m_inMax));
0164     pt.setY(detail::io2sp(m_intOut->value(), m_outMin, m_outMax));
0165 
0166     if (m_curveWidget->setCurrentPoint(pt)) {
0167         syncIOControls();
0168     }
0169 }
0170 
0171 template <typename SpinBox>
0172 void KisCurveWidgetControlsManager<SpinBox>::
0173 syncIOControls()
0174 {
0175     if (!m_intIn || !m_intOut)
0176         return;
0177 
0178     std::optional<QPointF> currentPoint = m_curveWidget->currentPoint();
0179 
0180     m_intIn->setEnabled(currentPoint.has_value());
0181     m_intOut->setEnabled(currentPoint.has_value());
0182 
0183     if (currentPoint) {
0184         KisSignalsBlocker b(m_intIn, m_intOut);
0185 
0186         const ValueType inValue = detail::sp2io(currentPoint->x(), m_inMin, m_inMax);
0187         const ValueType outValue = detail::sp2io(currentPoint->y(), m_outMin, m_outMax);
0188 
0189         if (detail::willChangeSpinBox(m_intIn, inValue)) {
0190             m_intIn->setValue(inValue);
0191         }
0192 
0193         if (detail::willChangeSpinBox(m_intOut, outValue)) {
0194             m_intOut->setValue(outValue);
0195         }
0196     } else {
0197         /*FIXME: Ideally, these controls should hide away now */
0198     }
0199 }
0200 
0201 template <typename SpinBox>
0202 void KisCurveWidgetControlsManager<SpinBox>::
0203 focusIOControls()
0204 {
0205     if (m_intIn) {
0206         m_intIn->setFocus();
0207     }
0208 }
0209 
0210 template class KRITAUI_EXPORT_INSTANCE KisCurveWidgetControlsManager<QSpinBox>;
0211 template class KRITAUI_EXPORT_INSTANCE KisCurveWidgetControlsManager<QDoubleSpinBox>;