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>;