File indexing completed on 2024-05-12 16:02:12

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2007 C. Boemann <cbo@boemann.dk>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "KoSliderCombo.h"
0007 #include "KoSliderCombo_p.h"
0008 
0009 #include <QTimer>
0010 #include <QApplication>
0011 #include <QSize>
0012 #include <QSlider>
0013 #include <QStyle>
0014 #include <QStylePainter>
0015 #include <QStyleOptionSlider>
0016 #include <QLineEdit>
0017 #include <QValidator>
0018 #include <QHBoxLayout>
0019 #include <QFrame>
0020 #include <QMenu>
0021 #include <QMouseEvent>
0022 #include <QDoubleSpinBox>
0023 #include <QDesktopWidget>
0024 
0025 
0026 #include <klocalizedstring.h>
0027 #include <WidgetsDebug.h>
0028 
0029 KoSliderCombo::KoSliderCombo(QWidget *parent)
0030    : QComboBox(parent)
0031     ,d(new KoSliderComboPrivate())
0032 {
0033     d->thePublic = this;
0034     d->minimum = 0.0;
0035     d->maximum = 100.0;
0036     d->decimals = 2;
0037     d->container = new KoSliderComboContainer(this);
0038     d->container->setAttribute(Qt::WA_WindowPropagation);
0039     QStyleOptionComboBox opt;
0040     opt.initFrom(this);
0041 //    d->container->setFrameStyle(style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, this));
0042 
0043     d->slider = new QSlider(Qt::Horizontal);
0044     d->slider->setMinimum(0);
0045     d->slider->setMaximum(256);
0046     d->slider->setPageStep(10);
0047     d->slider->setValue(0);
0048     // When set to true, causes flicker on Qt 4.6. Any reason to keep it?
0049     d->firstShowOfSlider = false; //true;
0050 
0051     QHBoxLayout *layout = new QHBoxLayout(d->container);
0052     layout->setMargin(2);
0053     layout->setSpacing(2);
0054     layout->addWidget(d->slider);
0055     d->container->resize(200, 30);
0056 
0057     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
0058 
0059     setEditable(true);
0060     setEditText(QLocale().toString(0.0, d->decimals));
0061 
0062     connect(d->slider, SIGNAL(valueChanged(int)), SLOT(sliderValueChanged(int)));
0063     connect(d->slider, SIGNAL(sliderReleased()), SLOT(sliderReleased()));
0064     connect(lineEdit(), SIGNAL(editingFinished()), SLOT(lineEditFinished()));
0065 }
0066 
0067 KoSliderCombo::~KoSliderCombo()
0068 {
0069     delete d;
0070 }
0071 
0072 QSize KoSliderCombo::sizeHint() const
0073 {
0074     return minimumSizeHint();
0075 }
0076 
0077 QSize KoSliderCombo::minimumSizeHint() const
0078 {
0079     QSize sh;
0080 
0081     const QFontMetrics &fm = fontMetrics();
0082 #if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
0083     sh.setWidth(5 * fm.horizontalAdvance(QLatin1Char('8')));
0084 #else
0085     sh.setWidth(5 * fm.width(QLatin1Char('8')));
0086 #endif
0087     sh.setHeight(qMax(fm.lineSpacing(), 14) + 2);
0088 
0089     // add style and strut values
0090     QStyleOptionComboBox opt;
0091     opt.initFrom(this);
0092     opt.subControls = QStyle::SC_All;
0093     opt.editable = true;
0094     sh = style()->sizeFromContents(QStyle::CT_ComboBox, &opt, sh, this);
0095 
0096     return sh;
0097 }
0098 
0099 void KoSliderCombo::KoSliderComboPrivate::showPopup()
0100 {
0101     if(firstShowOfSlider) {
0102         container->show(); //show container a bit early so the slider can be layout'ed
0103         firstShowOfSlider = false;
0104     }
0105 
0106     QStyleOptionSlider opt;
0107     opt.initFrom(slider);
0108     opt.maximum=256;
0109     opt.sliderPosition = opt.sliderValue = slider->value();
0110     int hdlPos = thePublic->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle).center().x();
0111 
0112     QStyleOptionComboBox optThis;
0113     optThis.initFrom(thePublic);
0114     optThis.subControls = QStyle::SC_All;
0115     optThis.editable = true;
0116     int arrowPos = thePublic->style()->subControlRect(QStyle::CC_ComboBox, &optThis, QStyle::SC_ComboBoxArrow).center().x();
0117 
0118     QSize popSize = container->size();
0119     QRect popupRect(thePublic->mapToGlobal(QPoint(arrowPos - hdlPos - slider->x(), thePublic->size().height())), popSize);
0120 
0121     // Make sure the popup is not drawn outside the screen area
0122     QRect screenRect = QApplication::desktop()->availableGeometry(thePublic);
0123     if (popupRect.right() > screenRect.right())
0124         popupRect.translate(screenRect.right() - popupRect.right(), 0);
0125     if (popupRect.left() < screenRect.left())
0126         popupRect.translate(screenRect.left() - popupRect.left(), 0);
0127     if (popupRect.bottom() > screenRect.bottom())
0128         popupRect.translate(0, -(thePublic->height() + container->height()));
0129 
0130     container->setGeometry(popupRect);
0131     container->raise();
0132     container->show();
0133     slider->setFocus();
0134 }
0135 
0136 void KoSliderCombo::KoSliderComboPrivate::hidePopup()
0137 {
0138     container->hide();
0139 }
0140 
0141 void KoSliderCombo::hideEvent(QHideEvent *)
0142 {
0143     d->hidePopup();
0144 }
0145 
0146 void KoSliderCombo::changeEvent(QEvent *e)
0147 {
0148     switch (e->type())
0149     {
0150         case QEvent::EnabledChange:
0151             if (!isEnabled())
0152                 d->hidePopup();
0153             break;
0154         case QEvent::PaletteChange:
0155             d->container->setPalette(palette());
0156             break;
0157         default:
0158             break;
0159     }
0160     QComboBox::changeEvent(e);
0161 }
0162 
0163 void KoSliderCombo::paintEvent(QPaintEvent *)
0164 {
0165     QStylePainter gc(this);
0166 
0167     gc.setPen(palette().color(QPalette::Text));
0168 
0169     QStyleOptionComboBox opt;
0170     opt.initFrom(this);
0171     opt.subControls = QStyle::SC_All;
0172     opt.editable = true;
0173     gc.drawComplexControl(QStyle::CC_ComboBox, opt);
0174     gc.drawControl(QStyle::CE_ComboBoxLabel, opt);
0175 }
0176 
0177 void KoSliderCombo::mousePressEvent(QMouseEvent *e)
0178 {
0179     QStyleOptionComboBox opt;
0180     opt.initFrom(this);
0181     opt.subControls = QStyle::SC_All;
0182     opt.editable = true;
0183     QStyle::SubControl sc = style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, e->pos(),
0184                                                            this);
0185     if (sc == QStyle::SC_ComboBoxArrow && !d->container->isVisible())
0186     {
0187         d->showPopup();
0188     }
0189     else
0190         QComboBox::mousePressEvent(e);
0191 }
0192 
0193 void KoSliderCombo::keyPressEvent(QKeyEvent *e)
0194 {
0195     if (e->key() == Qt::Key_Up) setValue(value() + d->slider->singleStep() * (maximum() - minimum()) / 256 + 0.5);
0196     else if (e->key() == Qt::Key_Down) setValue(value() - d->slider->singleStep() * (maximum() - minimum()) / 256 - 0.5);
0197     else QComboBox::keyPressEvent(e);
0198 }
0199 
0200 void KoSliderCombo::wheelEvent(QWheelEvent *e)
0201 {
0202     if (e->delta() > 0) setValue(value() + d->slider->singleStep() * (maximum() - minimum()) / 256 + 0.5);
0203     else setValue(value() - d->slider->singleStep() * (maximum() - minimum()) / 256 - 0.5);
0204 }
0205 
0206 void KoSliderCombo::KoSliderComboPrivate::lineEditFinished()
0207 {
0208     qreal value = QLocale().toDouble(thePublic->currentText());
0209     slider->blockSignals(true);
0210     slider->setValue(int((value - minimum) * 256 / (maximum - minimum) + 0.5));
0211     slider->blockSignals(false);
0212     emit thePublic->valueChanged(value, true);
0213 }
0214 
0215 void KoSliderCombo::KoSliderComboPrivate::sliderValueChanged(int slidervalue)
0216 {
0217     thePublic->setEditText(QLocale().toString(minimum + (maximum - minimum)*slidervalue/256, decimals));
0218 
0219     qreal value = QLocale().toDouble(thePublic->currentText());
0220     emit thePublic->valueChanged(value, false);
0221 }
0222 
0223 void KoSliderCombo::KoSliderComboPrivate::sliderReleased()
0224 {
0225     qreal value = QLocale().toDouble(thePublic->currentText());
0226     emit thePublic->valueChanged(value, true);
0227 }
0228 
0229 qreal KoSliderCombo::maximum() const
0230 {
0231     return d->maximum;
0232 }
0233 
0234 qreal KoSliderCombo::minimum() const
0235 {
0236     return d->minimum;
0237 }
0238 
0239 qreal KoSliderCombo::decimals() const
0240 {
0241     return d->decimals;
0242 }
0243 
0244 qreal KoSliderCombo::value() const
0245 {
0246     return QLocale().toDouble(currentText());
0247 }
0248 
0249 void KoSliderCombo::setDecimals(int dec)
0250 {
0251     d->decimals = dec;
0252     if (dec == 0) lineEdit()->setValidator(new QIntValidator(this));
0253     else lineEdit()->setValidator(new QDoubleValidator(this));
0254 }
0255 
0256 void KoSliderCombo::setMinimum(qreal min)
0257 {
0258     d->minimum = min;
0259 }
0260 
0261 void KoSliderCombo::setMaximum(qreal max)
0262 {
0263     d->maximum = max;
0264 }
0265 
0266 void KoSliderCombo::setValue(qreal value)
0267 {
0268     if(value < d->minimum)
0269         value = d->minimum;
0270     if(value > d->maximum)
0271         value = d->maximum;
0272     setEditText(QLocale().toString(value, d->decimals));
0273     d->slider->blockSignals(true);
0274     d->slider->setValue(int((value - d->minimum) * 256 / (d->maximum - d->minimum) + 0.5));
0275     d->slider->blockSignals(false);
0276     emit valueChanged(value, true);
0277 }
0278 
0279 //have to include this because of Q_PRIVATE_SLOT
0280 #include <moc_KoSliderCombo.cpp>