File indexing completed on 2025-04-27 03:58:37
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2014-11-30 0007 * Description : Save space slider widget 0008 * 0009 * SPDX-FileCopyrightText: 2014-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2010 by Justin Noel <justin at ics dot com> 0011 * SPDX-FileCopyrightText: 2010 by Cyrille Berger <cberger at cberger dot net> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "dsliderspinbox.h" 0018 0019 // C++ includes 0020 0021 #include <cmath> 0022 0023 // Qt includes 0024 0025 #include <QStyle> 0026 #include <QPainter> 0027 #include <QLineEdit> 0028 #include <QKeyEvent> 0029 #include <QMouseEvent> 0030 #include <QApplication> 0031 #include <QIntValidator> 0032 #include <QDoubleSpinBox> 0033 0034 namespace Digikam 0035 { 0036 0037 class Q_DECL_HIDDEN DAbstractSliderSpinBoxPrivate 0038 { 0039 public: 0040 0041 DAbstractSliderSpinBoxPrivate() 0042 : edit(nullptr), 0043 validator(nullptr), 0044 dummySpinBox(nullptr), 0045 upButtonDown(false), 0046 downButtonDown(false), 0047 factor(1.0), 0048 fastSliderStep(5), 0049 slowFactor(0.1), 0050 shiftPercent(0.0), 0051 shiftMode(false), 0052 exponentRatio(0.0), 0053 value(0), 0054 maximum(100), 0055 minimum(0), 0056 singleStep(1), 0057 style(STYLE_NOQUIRK), 0058 blockUpdateSignalOnDrag(false), 0059 isDragging (false) 0060 { 0061 } 0062 0063 enum Style 0064 { 0065 STYLE_NOQUIRK, 0066 STYLE_MACINTOSH, 0067 STYLE_PLASTIQUE, 0068 STYLE_WINDOWS, 0069 STYLE_BREEZE, 0070 STYLE_FUSION, 0071 STYLE_OXYGEN, 0072 STYLE_GTK2 0073 }; 0074 0075 QLineEdit* edit; 0076 QDoubleValidator* validator; 0077 QSpinBox* dummySpinBox; 0078 bool upButtonDown; 0079 bool downButtonDown; 0080 int factor; 0081 int fastSliderStep; 0082 double slowFactor; 0083 double shiftPercent; 0084 bool shiftMode; 0085 QString prefix; 0086 QString suffix; 0087 double exponentRatio; 0088 int value; 0089 int maximum; 0090 int minimum; 0091 int singleStep; 0092 Style style; 0093 bool blockUpdateSignalOnDrag; 0094 bool isDragging; 0095 }; 0096 0097 DAbstractSliderSpinBox::DAbstractSliderSpinBox(QWidget* const parent, DAbstractSliderSpinBoxPrivate* const q) 0098 : QWidget(parent), 0099 d_ptr(q) 0100 { 0101 Q_D(DAbstractSliderSpinBox); 0102 0103 QEvent e(QEvent::StyleChange); 0104 this->changeEvent(&e); 0105 0106 d->edit = new QLineEdit(this); 0107 d->edit->setContentsMargins(QMargins()); 0108 d->edit->setAlignment(Qt::AlignCenter); 0109 d->edit->installEventFilter(this); 0110 d->edit->setFrame(false); 0111 d->edit->hide(); 0112 0113 // Make edit transparent 0114 0115 d->edit->setAutoFillBackground(false); 0116 QPalette pal = d->edit->palette(); 0117 pal.setColor(QPalette::Base, Qt::transparent); 0118 d->edit->setPalette(pal); 0119 0120 connect(d->edit, SIGNAL(editingFinished()), 0121 this, SLOT(editLostFocus())); 0122 0123 d->validator = new QDoubleValidator(d->edit); 0124 d->edit->setValidator(d->validator); 0125 0126 setExponentRatio(1.0); 0127 0128 // Set sane defaults 0129 0130 setFocusPolicy(Qt::StrongFocus); 0131 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); 0132 0133 // dummy needed to fix a bug in the polyester theme 0134 0135 d->dummySpinBox = new QSpinBox(this); 0136 d->dummySpinBox->hide(); 0137 } 0138 0139 DAbstractSliderSpinBox::~DAbstractSliderSpinBox() 0140 { 0141 Q_D(DAbstractSliderSpinBox); 0142 delete d; 0143 } 0144 0145 void DAbstractSliderSpinBox::showEdit() 0146 { 0147 Q_D(DAbstractSliderSpinBox); 0148 0149 if (d->edit->isVisible()) 0150 { 0151 return; 0152 } 0153 0154 if (d->style == DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE) 0155 { 0156 d->edit->setGeometry(progressRect(spinBoxOptions()).adjusted(0, 0, -2, 0)); 0157 } 0158 else 0159 { 0160 d->edit->setGeometry(progressRect(spinBoxOptions())); 0161 } 0162 0163 d->edit->setText(valueString()); 0164 d->edit->selectAll(); 0165 d->edit->show(); 0166 d->edit->setFocus(Qt::OtherFocusReason); 0167 update(); 0168 } 0169 0170 void DAbstractSliderSpinBox::hideEdit() 0171 { 0172 Q_D(DAbstractSliderSpinBox); 0173 0174 d->edit->hide(); 0175 update(); 0176 } 0177 0178 void DAbstractSliderSpinBox::paintEvent(QPaintEvent* e) 0179 { 0180 Q_D(DAbstractSliderSpinBox); 0181 Q_UNUSED(e) 0182 0183 QPainter painter(this); 0184 0185 switch (d->style) 0186 { 0187 case DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE: 0188 paintPlastique(painter); 0189 break; 0190 0191 case DAbstractSliderSpinBoxPrivate::STYLE_BREEZE: 0192 paintBreeze(painter); 0193 break; 0194 0195 case DAbstractSliderSpinBoxPrivate::STYLE_FUSION: 0196 paintFusion(painter); 0197 break; 0198 0199 default: 0200 paint(painter); 0201 break; 0202 } 0203 0204 painter.end(); 0205 } 0206 0207 void DAbstractSliderSpinBox::paint(QPainter& painter) 0208 { 0209 Q_D(DAbstractSliderSpinBox); 0210 0211 // Create options to draw spin box parts 0212 0213 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0214 spinOpts.rect.adjust(0, 2, 0, -2); 0215 0216 // Draw "SpinBox".Clip off the area of the lineEdit to avoid double 0217 // borders being drawn 0218 0219 painter.save(); 0220 painter.setClipping(true); 0221 0222 QRect eraseRect(QPoint(rect().x(), rect().y()), 0223 QPoint(progressRect(spinOpts).right(), rect().bottom())); 0224 0225 painter.setClipRegion(QRegion(rect()).subtracted(eraseRect)); 0226 style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); 0227 painter.setClipping(false); 0228 painter.restore(); 0229 0230 QStyleOptionProgressBar progressOpts = progressBarOptions(); 0231 0232 if ((d->style == DAbstractSliderSpinBoxPrivate::STYLE_GTK2) || 0233 (d->style == DAbstractSliderSpinBoxPrivate::STYLE_OXYGEN) || 0234 (d->style == DAbstractSliderSpinBoxPrivate::STYLE_WINDOWS) || 0235 (d->style == DAbstractSliderSpinBoxPrivate::STYLE_MACINTOSH)) 0236 { 0237 progressOpts.state |= QStyle::State_Horizontal; 0238 } 0239 0240 progressOpts.rect.adjust(0, 2, 0, -2); 0241 style()->drawControl(QStyle::CE_ProgressBar, &progressOpts, &painter, nullptr); 0242 0243 // Draw focus if necessary 0244 0245 if (hasFocus() && d->edit->hasFocus()) 0246 { 0247 QStyleOptionFocusRect focusOpts; 0248 focusOpts.initFrom(this); 0249 focusOpts.rect = progressOpts.rect; 0250 focusOpts.backgroundColor = palette().color(QPalette::Window); 0251 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpts, &painter, this); 0252 } 0253 } 0254 0255 void DAbstractSliderSpinBox::paintFusion(QPainter& painter) 0256 { 0257 Q_D(DAbstractSliderSpinBox); 0258 0259 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0260 QStyleOptionProgressBar progressOpts = progressBarOptions(); 0261 spinOpts.frame = true; 0262 spinOpts.rect.adjust(0, -1, 0, 1); 0263 /* 0264 spinOpts.palette().setBrush(QPalette::Base, palette().highlight()); 0265 */ 0266 style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); 0267 0268 painter.save(); 0269 0270 QRect rect = progressOpts.rect.adjusted(1, 2, -4, -2); 0271 QRect leftRect; 0272 0273 int progressIndicatorPos = (progressOpts.progress - double(progressOpts.minimum)) / qMax(double(1.0), 0274 double(progressOpts.maximum) - progressOpts.minimum) * rect.width(); 0275 0276 if ((progressIndicatorPos >= 0) && (progressIndicatorPos <= rect.width())) 0277 { 0278 leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height()); 0279 } 0280 else if (progressIndicatorPos > rect.width()) 0281 { 0282 painter.setPen(palette().highlightedText().color()); 0283 } 0284 else 0285 { 0286 painter.setPen(palette().buttonText().color()); 0287 } 0288 0289 QRegion rightRect = rect; 0290 rightRect = rightRect.subtracted(leftRect); 0291 0292 QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter); 0293 textOption.setWrapMode(QTextOption::NoWrap); 0294 0295 if (!(d->edit && d->edit->isVisible())) 0296 { 0297 painter.setClipRegion(rightRect); 0298 painter.setClipping(true); 0299 painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption); 0300 painter.setClipping(false); 0301 } 0302 0303 if (!leftRect.isNull()) 0304 { 0305 painter.setClipRect(leftRect.adjusted(0, -1, 1, 1)); 0306 painter.setPen(palette().highlight().color()); 0307 painter.setBrush(palette().highlight()); 0308 0309 spinOpts.palette.setBrush(QPalette::Base, palette().highlight()); 0310 style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); 0311 0312 if (!(d->edit && d->edit->isVisible())) 0313 { 0314 painter.setPen(palette().highlightedText().color()); 0315 painter.setClipping(true); 0316 painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption); 0317 } 0318 0319 painter.setClipping(false); 0320 } 0321 0322 painter.restore(); 0323 } 0324 0325 void DAbstractSliderSpinBox::paintPlastique(QPainter& painter) 0326 { 0327 Q_D(DAbstractSliderSpinBox); 0328 0329 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0330 QStyleOptionProgressBar progressOpts = progressBarOptions(); 0331 0332 style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); 0333 0334 painter.save(); 0335 0336 QRect rect = progressOpts.rect.adjusted(2, 0, -2, 0); 0337 QRect leftRect; 0338 0339 int progressIndicatorPos = (progressOpts.progress - double(progressOpts.minimum)) / qMax(double(1.0), 0340 double(progressOpts.maximum) - progressOpts.minimum) * rect.width(); 0341 0342 if ((progressIndicatorPos >= 0) && (progressIndicatorPos <= rect.width())) 0343 { 0344 leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height()); 0345 } 0346 else if (progressIndicatorPos > rect.width()) 0347 { 0348 painter.setPen(palette().highlightedText().color()); 0349 } 0350 else 0351 { 0352 painter.setPen(palette().buttonText().color()); 0353 } 0354 0355 QRegion rightRect = rect; 0356 rightRect = rightRect.subtracted(leftRect); 0357 0358 QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter); 0359 textOption.setWrapMode(QTextOption::NoWrap); 0360 0361 if (!(d->edit && d->edit->isVisible())) 0362 { 0363 painter.setClipRegion(rightRect); 0364 painter.setClipping(true); 0365 painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption); 0366 painter.setClipping(false); 0367 } 0368 0369 if (!leftRect.isNull()) 0370 { 0371 painter.setPen(palette().highlight().color()); 0372 painter.setBrush(palette().highlight()); 0373 painter.drawRect(leftRect.adjusted(0, 0, 0, -1)); 0374 0375 if (!(d->edit && d->edit->isVisible())) 0376 { 0377 painter.setPen(palette().highlightedText().color()); 0378 painter.setClipRect(leftRect.adjusted(0, 0, 1, 0)); 0379 painter.setClipping(true); 0380 painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption); 0381 painter.setClipping(false); 0382 } 0383 } 0384 0385 painter.restore(); 0386 } 0387 0388 void DAbstractSliderSpinBox::paintBreeze(QPainter& painter) 0389 { 0390 Q_D(DAbstractSliderSpinBox); 0391 0392 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0393 QStyleOptionProgressBar progressOpts = progressBarOptions(); 0394 QString valueText = progressOpts.text; 0395 progressOpts.text = QLatin1String(""); 0396 progressOpts.rect.adjust(0, 1, 0, -1); 0397 0398 style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, this); 0399 style()->drawControl(QStyle::CE_ProgressBarGroove, &progressOpts, &painter, this); 0400 0401 painter.save(); 0402 0403 QRect leftRect; 0404 0405 int progressIndicatorPos = (progressOpts.progress - double(progressOpts.minimum)) / qMax(double(1.0), 0406 double(progressOpts.maximum) - progressOpts.minimum) * progressOpts.rect.width(); 0407 0408 if ((progressIndicatorPos >= 0) && (progressIndicatorPos <= progressOpts.rect.width())) 0409 { 0410 leftRect = QRect(progressOpts.rect.left(), progressOpts.rect.top(), progressIndicatorPos, progressOpts.rect.height()); 0411 } 0412 else if (progressIndicatorPos > progressOpts.rect.width()) 0413 { 0414 painter.setPen(palette().highlightedText().color()); 0415 } 0416 else 0417 { 0418 painter.setPen(palette().buttonText().color()); 0419 } 0420 0421 QRegion rightRect = progressOpts.rect; 0422 rightRect = rightRect.subtracted(leftRect); 0423 painter.setClipRegion(rightRect); 0424 0425 QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter); 0426 textOption.setWrapMode(QTextOption::NoWrap); 0427 0428 if (!(d->edit && d->edit->isVisible())) 0429 { 0430 painter.drawText(progressOpts.rect, valueText, textOption); 0431 } 0432 0433 if (!leftRect.isNull()) 0434 { 0435 painter.setPen(palette().highlightedText().color()); 0436 painter.setClipRect(leftRect); 0437 style()->drawControl(QStyle::CE_ProgressBarContents, &progressOpts, &painter, this); 0438 0439 if (!(d->edit && d->edit->isVisible())) 0440 { 0441 painter.drawText(progressOpts.rect, valueText, textOption); 0442 } 0443 } 0444 0445 painter.restore(); 0446 } 0447 0448 void DAbstractSliderSpinBox::mousePressEvent(QMouseEvent* e) 0449 { 0450 Q_D(DAbstractSliderSpinBox); 0451 0452 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0453 0454 // Depress buttons or highlight slider 0455 // Also used to emulate mouse grab... 0456 0457 if (e->buttons() & Qt::LeftButton) 0458 { 0459 if (upButtonRect(spinOpts).contains(e->pos())) 0460 { 0461 d->upButtonDown = true; 0462 } 0463 else if (downButtonRect(spinOpts).contains(e->pos())) 0464 { 0465 d->downButtonDown = true; 0466 } 0467 } 0468 else if (e->buttons() & Qt::RightButton) 0469 { 0470 showEdit(); 0471 } 0472 0473 update(); 0474 } 0475 0476 void DAbstractSliderSpinBox::mouseReleaseEvent(QMouseEvent* e) 0477 { 0478 Q_D(DAbstractSliderSpinBox); 0479 0480 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0481 0482 d->isDragging = false; 0483 0484 // Step up/down for buttons 0485 // Emulating mouse grab too 0486 0487 if (upButtonRect(spinOpts).contains(e->pos()) && d->upButtonDown) 0488 { 0489 setInternalValue(d->value + d->singleStep); 0490 } 0491 else if (downButtonRect(spinOpts).contains(e->pos()) && d->downButtonDown) 0492 { 0493 setInternalValue(d->value - d->singleStep); 0494 } 0495 else if (progressRect(spinOpts).contains(e->pos()) && 0496 !(d->edit->isVisible()) && 0497 !(d->upButtonDown || d->downButtonDown)) 0498 { 0499 // Snap to percentage for progress area 0500 setInternalValue(valueForX(e->pos().x(),e->modifiers())); 0501 } 0502 else 0503 { 0504 // Confirm the last known value, since we might be ignoring move events 0505 setInternalValue(d->value); 0506 } 0507 0508 d->upButtonDown = false; 0509 d->downButtonDown = false; 0510 update(); 0511 } 0512 0513 void DAbstractSliderSpinBox::mouseMoveEvent(QMouseEvent* e) 0514 { 0515 Q_D(DAbstractSliderSpinBox); 0516 0517 if (e->modifiers() & Qt::ShiftModifier) 0518 { 0519 if (!d->shiftMode) 0520 { 0521 d->shiftPercent = pow(double(d->value - d->minimum) / double(d->maximum - d->minimum), 1 / double(d->exponentRatio)); 0522 d->shiftMode = true; 0523 } 0524 } 0525 else 0526 { 0527 d->shiftMode = false; 0528 } 0529 0530 // Respect emulated mouse grab. 0531 0532 if (e->buttons() & Qt::LeftButton && !(d->downButtonDown || d->upButtonDown)) 0533 { 0534 d->isDragging = true; 0535 setInternalValue(valueForX(e->pos().x(),e->modifiers()), d->blockUpdateSignalOnDrag); 0536 update(); 0537 } 0538 } 0539 0540 void DAbstractSliderSpinBox::keyPressEvent(QKeyEvent* e) 0541 { 0542 Q_D(DAbstractSliderSpinBox); 0543 0544 switch (e->key()) 0545 { 0546 case Qt::Key_Up: 0547 case Qt::Key_Right: 0548 setInternalValue(d->value + d->singleStep); 0549 0550 if (d->edit->isVisible()) 0551 { 0552 d->edit->setText(valueString()); 0553 } 0554 0555 update(); 0556 break; 0557 0558 case Qt::Key_Down: 0559 case Qt::Key_Left: 0560 setInternalValue(d->value - d->singleStep); 0561 0562 if (d->edit->isVisible()) 0563 { 0564 d->edit->setText(valueString()); 0565 } 0566 0567 update(); 0568 break; 0569 0570 case Qt::Key_Shift: 0571 d->shiftPercent = pow(double(d->value - d->minimum) / double(d->maximum - d->minimum), 1 / double(d->exponentRatio)); 0572 d->shiftMode = true; 0573 break; 0574 0575 case Qt::Key_Enter: // Line edit isn't "accepting" key strokes... 0576 case Qt::Key_Return: 0577 case Qt::Key_Escape: 0578 case Qt::Key_Control: 0579 case Qt::Key_Alt: 0580 case Qt::Key_AltGr: 0581 case Qt::Key_Super_L: 0582 case Qt::Key_Super_R: 0583 break; 0584 0585 default: 0586 showEdit(); 0587 d->edit->event(e); 0588 break; 0589 } 0590 } 0591 0592 void DAbstractSliderSpinBox::wheelEvent(QWheelEvent *e) 0593 { 0594 Q_D(DAbstractSliderSpinBox); 0595 0596 if (e->angleDelta().y() > 0) 0597 { 0598 setInternalValue(d->value + d->singleStep); 0599 } 0600 else if (e->angleDelta().y() < 0) 0601 { 0602 setInternalValue(d->value - d->singleStep); 0603 } 0604 0605 update(); 0606 e->accept(); 0607 } 0608 0609 void DAbstractSliderSpinBox::focusInEvent(QFocusEvent* e) 0610 { 0611 if (e->reason() == Qt::TabFocusReason) 0612 { 0613 showEdit(); 0614 } 0615 0616 e->accept(); 0617 } 0618 0619 bool DAbstractSliderSpinBox::eventFilter(QObject* recv, QEvent* e) 0620 { 0621 Q_D(DAbstractSliderSpinBox); 0622 0623 if (recv == static_cast<QObject*>(d->edit) && e->type() == QEvent::KeyRelease) 0624 { 0625 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e); 0626 0627 switch (keyEvent->key()) 0628 { 0629 case Qt::Key_Enter: 0630 case Qt::Key_Return: 0631 setInternalValue(QLocale::system().toDouble(d->edit->text()) * d->factor); 0632 hideEdit(); 0633 return true; 0634 0635 case Qt::Key_Escape: 0636 d->edit->setText(valueString()); 0637 hideEdit(); 0638 return true; 0639 0640 default: 0641 break; 0642 } 0643 } 0644 else if (d->edit->isVisible() && e->type() == QEvent::ShortcutOverride) 0645 { 0646 QKeyEvent* const keyEvent = static_cast<QKeyEvent*>(e); 0647 0648 switch (keyEvent->key()) 0649 { 0650 case Qt::Key_Tab: 0651 case Qt::Key_Enter: 0652 case Qt::Key_Return: 0653 case Qt::Key_Escape: 0654 e->accept(); 0655 return true; 0656 0657 default: 0658 break; 0659 } 0660 } 0661 0662 return false; 0663 } 0664 0665 QSize DAbstractSliderSpinBox::sizeHint() const 0666 { 0667 const Q_D(DAbstractSliderSpinBox); 0668 0669 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0670 QFont ft(font()); 0671 0672 if (d->style == DAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK) 0673 { 0674 // Some styles use bold font in progressbars 0675 // unfortunately there is no reliable way to check for that 0676 ft.setBold(true); 0677 } 0678 0679 QFontMetrics fm(ft); 0680 QSize hint(fm.boundingRect(d->prefix + QString::number(d->maximum) + d->suffix).size()); 0681 hint += QSize(0, 2); 0682 0683 switch (d->style) 0684 { 0685 case DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE: 0686 hint += QSize(8, 0); 0687 break; 0688 0689 case DAbstractSliderSpinBoxPrivate::STYLE_BREEZE: 0690 hint += QSize(2, 0); 0691 break; 0692 0693 case DAbstractSliderSpinBoxPrivate::STYLE_MACINTOSH: 0694 case DAbstractSliderSpinBoxPrivate::STYLE_WINDOWS: 0695 case DAbstractSliderSpinBoxPrivate::STYLE_FUSION: 0696 case DAbstractSliderSpinBoxPrivate::STYLE_OXYGEN: 0697 case DAbstractSliderSpinBoxPrivate::STYLE_GTK2: 0698 hint += QSize(8, 8); 0699 break; 0700 0701 case DAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK: 0702 // almost all "modern" styles have a margin around controls 0703 hint += QSize(6, 6); 0704 break; 0705 0706 default: 0707 break; 0708 } 0709 0710 // Getting the size of the buttons is a pain as the calcs require a rect 0711 // that is "big enough". We run the calc twice to get the "smallest" buttons 0712 // This code was inspired by QAbstractSpinBox 0713 QSize extra(1000, 0); 0714 spinOpts.rect.setSize(hint + extra); 0715 extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts, 0716 QStyle::SC_SpinBoxEditField, this).size(); 0717 spinOpts.rect.setSize(hint + extra); 0718 extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts, 0719 QStyle::SC_SpinBoxEditField, this).size(); 0720 hint += extra; 0721 0722 spinOpts.rect.setSize(hint); 0723 0724 return style()->sizeFromContents(QStyle::CT_SpinBox, &spinOpts, hint); 0725 } 0726 0727 QSize DAbstractSliderSpinBox::minimumSizeHint() const 0728 { 0729 return sizeHint(); 0730 } 0731 0732 QSize DAbstractSliderSpinBox::minimumSize() const 0733 { 0734 return QWidget::minimumSize().expandedTo(minimumSizeHint()); 0735 } 0736 0737 QStyleOptionSpinBox DAbstractSliderSpinBox::spinBoxOptions() const 0738 { 0739 const Q_D(DAbstractSliderSpinBox); 0740 0741 QStyleOptionSpinBox opts; 0742 opts.initFrom(this); 0743 opts.frame = false; 0744 opts.buttonSymbols = QAbstractSpinBox::UpDownArrows; 0745 opts.subControls = QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; 0746 0747 // Disable non-logical buttons 0748 0749 if (d->value == d->minimum) 0750 { 0751 opts.stepEnabled = QAbstractSpinBox::StepUpEnabled; 0752 } 0753 else if (d->value == d->maximum) 0754 { 0755 opts.stepEnabled = QAbstractSpinBox::StepDownEnabled; 0756 } 0757 else 0758 { 0759 opts.stepEnabled = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled; 0760 } 0761 0762 // Deal with depressed buttons 0763 0764 if (d->upButtonDown) 0765 { 0766 opts.activeSubControls = QStyle::SC_SpinBoxUp; 0767 } 0768 else if (d->downButtonDown) 0769 { 0770 opts.activeSubControls = QStyle::SC_SpinBoxDown; 0771 } 0772 else 0773 { 0774 opts.activeSubControls = QStyle::SC_None; 0775 } 0776 0777 return opts; 0778 } 0779 0780 QStyleOptionProgressBar DAbstractSliderSpinBox::progressBarOptions() const 0781 { 0782 const Q_D(DAbstractSliderSpinBox); 0783 0784 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0785 0786 // Create opts for drawing the progress portion 0787 0788 QStyleOptionProgressBar progressOpts; 0789 progressOpts.initFrom(this); 0790 progressOpts.maximum = d->maximum; 0791 progressOpts.minimum = d->minimum; 0792 0793 double minDbl = d->minimum; 0794 double dValues = (d->maximum - minDbl); 0795 0796 progressOpts.progress = dValues * pow((d->value - minDbl) / dValues, 1.0 / d->exponentRatio) + minDbl; 0797 progressOpts.text = d->prefix + valueString() + d->suffix; 0798 progressOpts.textAlignment = Qt::AlignCenter; 0799 progressOpts.textVisible = !(d->edit->isVisible()); 0800 0801 // Change opts rect to be only the ComboBox's text area 0802 0803 progressOpts.rect = progressRect(spinOpts); 0804 0805 return progressOpts; 0806 } 0807 0808 QRect DAbstractSliderSpinBox::progressRect(const QStyleOptionSpinBox& spinBoxOptions) const 0809 { 0810 const Q_D(DAbstractSliderSpinBox); 0811 0812 QRect ret = style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, 0813 QStyle::SC_SpinBoxEditField); 0814 0815 switch (d->style) 0816 { 0817 case DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE: 0818 ret.adjust(-2, 0, 1, 0); 0819 break; 0820 0821 case DAbstractSliderSpinBoxPrivate::STYLE_BREEZE: 0822 ret.adjust(1, 0, 0, 0); 0823 break; 0824 0825 default: 0826 break; 0827 } 0828 0829 return ret; 0830 } 0831 0832 QRect DAbstractSliderSpinBox::upButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const 0833 { 0834 return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, 0835 QStyle::SC_SpinBoxUp); 0836 } 0837 0838 QRect DAbstractSliderSpinBox::downButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const 0839 { 0840 return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, 0841 QStyle::SC_SpinBoxDown); 0842 } 0843 0844 int DAbstractSliderSpinBox::valueForX(int x, Qt::KeyboardModifiers modifiers) const 0845 { 0846 const Q_D(DAbstractSliderSpinBox); 0847 0848 QStyleOptionSpinBox spinOpts = spinBoxOptions(); 0849 0850 QRect correctedProgRect; 0851 0852 if (d->style == DAbstractSliderSpinBoxPrivate::STYLE_FUSION) 0853 { 0854 correctedProgRect = progressRect(spinOpts).adjusted(2, 0, -2, 0); 0855 } 0856 else if (d->style == DAbstractSliderSpinBoxPrivate::STYLE_BREEZE) 0857 { 0858 correctedProgRect = progressRect(spinOpts); 0859 } 0860 else 0861 { 0862 // Adjust for magic number in style code (margins) 0863 correctedProgRect = progressRect(spinOpts).adjusted(2, 2, -2, -2); 0864 } 0865 0866 // Compute the distance of the progress bar, in pixel 0867 double leftDbl = correctedProgRect.left(); 0868 double xDbl = x - leftDbl; 0869 0870 // Compute the ration of the progress bar used, linearly (ignoring the exponent) 0871 double rightDbl = correctedProgRect.right(); 0872 double minDbl = d->minimum; 0873 double maxDbl = d->maximum; 0874 0875 double dValues = (maxDbl - minDbl); 0876 double percent = (xDbl / (rightDbl - leftDbl)); 0877 0878 // If SHIFT is pressed, movement should be slowed. 0879 if (modifiers & Qt::ShiftModifier) 0880 { 0881 percent = d->shiftPercent + (percent - d->shiftPercent) * d->slowFactor; 0882 } 0883 0884 // Final value 0885 double realvalue = ((dValues * pow(percent, d->exponentRatio)) + minDbl); 0886 // If key CTRL is pressed, round to the closest step. 0887 if (modifiers & Qt::ControlModifier) 0888 { 0889 double fstep = d->fastSliderStep; 0890 0891 if (modifiers & Qt::ShiftModifier) 0892 { 0893 fstep *= d->slowFactor; 0894 } 0895 0896 realvalue = floor((realvalue+fstep / 2) / fstep) * fstep; 0897 } 0898 0899 return int(realvalue); 0900 } 0901 0902 void DAbstractSliderSpinBox::setPrefix(const QString& prefix) 0903 { 0904 Q_D(DAbstractSliderSpinBox); 0905 d->prefix = prefix; 0906 } 0907 0908 void DAbstractSliderSpinBox::setSuffix(const QString& suffix) 0909 { 0910 Q_D(DAbstractSliderSpinBox); 0911 d->suffix = suffix; 0912 } 0913 0914 void DAbstractSliderSpinBox::setExponentRatio(double dbl) 0915 { 0916 Q_D(DAbstractSliderSpinBox); 0917 Q_ASSERT(dbl > 0); 0918 d->exponentRatio = dbl; 0919 } 0920 0921 void DAbstractSliderSpinBox::setBlockUpdateSignalOnDrag(bool blockUpdateSignal) 0922 { 0923 Q_D(DAbstractSliderSpinBox); 0924 d->blockUpdateSignalOnDrag = blockUpdateSignal; 0925 } 0926 0927 void DAbstractSliderSpinBox::contextMenuEvent(QContextMenuEvent* event) 0928 { 0929 event->accept(); 0930 } 0931 0932 void DAbstractSliderSpinBox::editLostFocus() 0933 { 0934 Q_D(DAbstractSliderSpinBox); 0935 0936 if (!d->edit->hasFocus()) 0937 { 0938 if (d->edit->isModified()) 0939 { 0940 setInternalValue(QLocale::system().toDouble(d->edit->text()) * d->factor); 0941 } 0942 0943 hideEdit(); 0944 } 0945 } 0946 0947 void DAbstractSliderSpinBox::setInternalValue(int value) 0948 { 0949 setInternalValue(value, false); 0950 } 0951 0952 bool DAbstractSliderSpinBox::isDragging() const 0953 { 0954 Q_D(const DAbstractSliderSpinBox); 0955 return d->isDragging; 0956 } 0957 0958 void DAbstractSliderSpinBox::changeEvent(QEvent* e) 0959 { 0960 Q_D(DAbstractSliderSpinBox); 0961 0962 QWidget::changeEvent(e); 0963 0964 if (e->type() == QEvent::StyleChange) 0965 { 0966 if (style()->objectName() == QLatin1String("macintosh")) 0967 { 0968 d->style = DAbstractSliderSpinBoxPrivate::STYLE_MACINTOSH; 0969 } 0970 else if (style()->objectName() == QLatin1String("plastique")) 0971 { 0972 d->style = DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE; 0973 } 0974 else if (style()->objectName() == QLatin1String("windows")) 0975 { 0976 d->style = DAbstractSliderSpinBoxPrivate::STYLE_WINDOWS; 0977 } 0978 else if (style()->objectName() == QLatin1String("breeze")) 0979 { 0980 d->style = DAbstractSliderSpinBoxPrivate::STYLE_BREEZE; 0981 } 0982 else if (style()->objectName() == QLatin1String("fusion")) 0983 { 0984 d->style = DAbstractSliderSpinBoxPrivate::STYLE_FUSION; 0985 } 0986 else if (style()->objectName() == QLatin1String("oxygen")) 0987 { 0988 d->style = DAbstractSliderSpinBoxPrivate::STYLE_OXYGEN; 0989 } 0990 else if (style()->objectName() == QLatin1String("gtk2")) 0991 { 0992 d->style = DAbstractSliderSpinBoxPrivate::STYLE_GTK2; 0993 } 0994 else 0995 { 0996 d->style = DAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK; 0997 } 0998 } 0999 } 1000 1001 // --------------------------------------------------------------------------------------------- 1002 1003 class Q_DECL_HIDDEN DSliderSpinBoxPrivate : public DAbstractSliderSpinBoxPrivate 1004 { 1005 }; 1006 1007 DSliderSpinBox::DSliderSpinBox(QWidget* const parent) 1008 : DAbstractSliderSpinBox(parent, new DSliderSpinBoxPrivate) 1009 { 1010 setRange(0, 99); 1011 } 1012 1013 DSliderSpinBox::~DSliderSpinBox() 1014 { 1015 } 1016 1017 void DSliderSpinBox::setRange(int minimum, int maximum) 1018 { 1019 Q_D(DSliderSpinBox); 1020 1021 d->minimum = minimum; 1022 d->maximum = maximum; 1023 d->fastSliderStep = (maximum-minimum + 1) / 20; 1024 d->validator->setRange(minimum, maximum, 0); 1025 update(); 1026 } 1027 1028 int DSliderSpinBox::minimum() const 1029 { 1030 const Q_D(DSliderSpinBox); 1031 1032 return d->minimum; 1033 } 1034 1035 void DSliderSpinBox::setMinimum(int minimum) 1036 { 1037 Q_D(DSliderSpinBox); 1038 setRange(minimum, d->maximum); 1039 } 1040 1041 int DSliderSpinBox::maximum() const 1042 { 1043 const Q_D(DSliderSpinBox); 1044 1045 return d->maximum; 1046 } 1047 1048 void DSliderSpinBox::setMaximum(int maximum) 1049 { 1050 Q_D(DSliderSpinBox); 1051 setRange(d->minimum, maximum); 1052 } 1053 1054 int DSliderSpinBox::fastSliderStep() const 1055 { 1056 const Q_D(DSliderSpinBox); 1057 1058 return d->fastSliderStep; 1059 } 1060 1061 void DSliderSpinBox::setFastSliderStep(int step) 1062 { 1063 Q_D(DSliderSpinBox); 1064 d->fastSliderStep = step; 1065 } 1066 1067 int DSliderSpinBox::value() 1068 { 1069 Q_D(DSliderSpinBox); 1070 1071 return d->value; 1072 } 1073 1074 void DSliderSpinBox::setValue(int value) 1075 { 1076 setInternalValue(value, false); 1077 update(); 1078 } 1079 1080 QString DSliderSpinBox::valueString() const 1081 { 1082 const Q_D(DSliderSpinBox); 1083 1084 QLocale locale; 1085 1086 return locale.toString((double)d->value, 'f', d->validator->decimals()); 1087 } 1088 1089 void DSliderSpinBox::setSingleStep(int value) 1090 { 1091 Q_D(DSliderSpinBox); 1092 d->singleStep = value; 1093 } 1094 1095 void DSliderSpinBox::setPageStep(int value) 1096 { 1097 Q_UNUSED(value); 1098 } 1099 1100 void DSliderSpinBox::setInternalValue(int _value, bool blockUpdateSignal) 1101 { 1102 Q_D(DAbstractSliderSpinBox); 1103 1104 d->value = qBound(d->minimum, _value, d->maximum); 1105 1106 if (!blockUpdateSignal) 1107 { 1108 Q_EMIT valueChanged(value()); 1109 } 1110 } 1111 1112 // --------------------------------------------------------------------------------------------- 1113 1114 class Q_DECL_HIDDEN DDoubleSliderSpinBoxPrivate : public DAbstractSliderSpinBoxPrivate 1115 { 1116 }; 1117 1118 DDoubleSliderSpinBox::DDoubleSliderSpinBox(QWidget* const parent) 1119 : DAbstractSliderSpinBox(parent, new DDoubleSliderSpinBoxPrivate) 1120 { 1121 } 1122 1123 DDoubleSliderSpinBox::~DDoubleSliderSpinBox() 1124 { 1125 } 1126 1127 void DDoubleSliderSpinBox::setRange(double minimum, double maximum, int decimals) 1128 { 1129 Q_D(DDoubleSliderSpinBox); 1130 1131 d->factor = pow(10.0, decimals); 1132 1133 d->minimum = minimum * d->factor; 1134 d->maximum = maximum * d->factor; 1135 1136 // This code auto-compute a new step when pressing control. 1137 // A flag defaulting to "do not change the fast step" should be added, but it implies changing every call 1138 1139 if (((maximum - minimum) >= 2.0) || (decimals <= 0)) 1140 { 1141 // Quick step on integers 1142 d->fastSliderStep = int(pow(10.0, decimals)); 1143 } 1144 else if (decimals == 1) 1145 { 1146 d->fastSliderStep = (maximum - minimum) * d->factor / 10; 1147 } 1148 else 1149 { 1150 d->fastSliderStep = (maximum - minimum) * d->factor / 20; 1151 } 1152 1153 d->validator->setRange(minimum, maximum, decimals); 1154 update(); 1155 setValue(value()); 1156 } 1157 1158 double DDoubleSliderSpinBox::minimum() const 1159 { 1160 const Q_D(DAbstractSliderSpinBox); 1161 1162 return d->minimum / d->factor; 1163 } 1164 1165 void DDoubleSliderSpinBox::setMinimum(double minimum) 1166 { 1167 Q_D(DAbstractSliderSpinBox); 1168 setRange(minimum, d->maximum); 1169 } 1170 1171 double DDoubleSliderSpinBox::maximum() const 1172 { 1173 const Q_D(DAbstractSliderSpinBox); 1174 1175 return d->maximum / d->factor; 1176 } 1177 1178 void DDoubleSliderSpinBox::setMaximum(double maximum) 1179 { 1180 Q_D(DAbstractSliderSpinBox); 1181 setRange(d->minimum, maximum); 1182 } 1183 1184 double DDoubleSliderSpinBox::fastSliderStep() const 1185 { 1186 const Q_D(DAbstractSliderSpinBox); 1187 1188 return d->fastSliderStep; 1189 } 1190 1191 void DDoubleSliderSpinBox::setFastSliderStep(double step) 1192 { 1193 Q_D(DAbstractSliderSpinBox); 1194 d->fastSliderStep = step; 1195 } 1196 1197 double DDoubleSliderSpinBox::value() 1198 { 1199 Q_D(DAbstractSliderSpinBox); 1200 1201 return (double)d->value / d->factor; 1202 } 1203 1204 void DDoubleSliderSpinBox::setValue(double value) 1205 { 1206 Q_D(DAbstractSliderSpinBox); 1207 1208 setInternalValue(d->value = qRound(value * d->factor), false); 1209 update(); 1210 } 1211 1212 void DDoubleSliderSpinBox::setSingleStep(double value) 1213 { 1214 Q_D(DAbstractSliderSpinBox); 1215 d->singleStep = value * d->factor; 1216 } 1217 1218 QString DDoubleSliderSpinBox::valueString() const 1219 { 1220 const Q_D(DAbstractSliderSpinBox); 1221 1222 QLocale locale; 1223 1224 return locale.toString((double)d->value / d->factor, 'f', d->validator->decimals()); 1225 } 1226 1227 void DDoubleSliderSpinBox::setInternalValue(int _value, bool blockUpdateSignal) 1228 { 1229 Q_D(DAbstractSliderSpinBox); 1230 1231 d->value = qBound(d->minimum, _value, d->maximum); 1232 1233 if (!blockUpdateSignal) 1234 { 1235 Q_EMIT valueChanged(value()); 1236 } 1237 } 1238 1239 } // namespace Digikam 1240 1241 #include "moc_dsliderspinbox.cpp"