Krita Source Code Documentation
Loading...
Searching...
No Matches
rsliderspinbox.cpp
Go to the documentation of this file.
1
30#include "rsliderspinbox.h"
31
32// C++ includes
33
34#include <cmath>
35
36// Qt includes
37
38#include <QPainter>
39#include <QStyle>
40#include <QLineEdit>
41#include <QApplication>
42#include <QKeyEvent>
43#include <QMouseEvent>
44#include <QIntValidator>
45#include <QtDebug>
46#include <QDoubleSpinBox>
47
48namespace KDcrawIface
49{
50
52{
53public:
54
56 {
57 edit = 0;
58 validator = 0;
59 dummySpinBox = 0;
60 upButtonDown = false;
61 downButtonDown = false;
62 shiftMode = false;
63 factor = 1.0;
65 slowFactor = 0.1;
66 shiftPercent = 0.0;
67 exponentRatio = 0.0;
68 value = 0;
69 maximum = 100;
70 minimum = 0;
71 singleStep = 1;
72 }
73
74 QLineEdit* edit;
75 QDoubleValidator* validator;
78 int factor;
80 double slowFactor;
83 QString suffix;
85 int value;
89 QSpinBox* dummySpinBox;
90};
91
93 : QWidget(parent),
94 d_ptr(q)
95{
97
98 d->edit = new QLineEdit(this);
99 d->edit->setFrame(false);
100 d->edit->setAlignment(Qt::AlignCenter);
101 d->edit->hide();
102 d->edit->installEventFilter(this);
103
104 // Make edit transparent
105 d->edit->setAutoFillBackground(false);
106 QPalette pal = d->edit->palette();
107 pal.setColor(QPalette::Base, Qt::transparent);
108 d->edit->setPalette(pal);
109
110 connect(d->edit, SIGNAL(editingFinished()),
111 this, SLOT(editLostFocus()));
112
113 d->validator = new QDoubleValidator(d->edit);
114 d->edit->setValidator(d->validator);
115
116 setExponentRatio(1.0);
117
118 // Set sane defaults
119 setFocusPolicy(Qt::StrongFocus);
120 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
121
122 // dummy needed to fix a bug in the polyester theme
123 d->dummySpinBox = new QSpinBox(this);
124 d->dummySpinBox->hide();
125}
126
132
134{
136
137 if (d->edit->isVisible()) return;
138
139 d->edit->setGeometry(editRect(spinBoxOptions()));
140 d->edit->setText(valueString());
141 d->edit->selectAll();
142 d->edit->show();
143 d->edit->setFocus(Qt::OtherFocusReason);
144 update();
145}
146
148{
150 d->edit->hide();
151 update();
152}
153
155{
157 Q_UNUSED(e);
158
159 QPainter painter(this);
160
161 // Create options to draw spin box parts
162 QStyleOptionSpinBox spinOpts = spinBoxOptions();
163
164 // Draw "SpinBox".Clip off the area of the lineEdit to avoid double borders being drawn
165
166 painter.save();
167 painter.setClipping(true);
168 QRect eraseRect(QPoint(rect().x(), rect().y()),
169 QPoint(editRect(spinOpts).right(), rect().bottom()));
170 painter.setClipRegion(QRegion(rect()).subtracted(eraseRect));
171 style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox);
172 painter.setClipping(false);
173 painter.restore();
174
175
176 // Create options to draw progress bar parts
177 QStyleOptionProgressBar progressOpts = progressBarOptions();
178
179 // Draw "ProgressBar" in SpinBox
180 style()->drawControl(QStyle::CE_ProgressBar, &progressOpts, &painter, 0);
181
182 // Draw focus if necessary
183 if (hasFocus() && d->edit->hasFocus())
184 {
185 QStyleOptionFocusRect focusOpts;
186 focusOpts.initFrom(this);
187 focusOpts.rect = progressOpts.rect;
188 focusOpts.backgroundColor = palette().color(QPalette::Window);
189 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpts, &painter, this);
190 }
191}
192
194{
196 QStyleOptionSpinBox spinOpts = spinBoxOptions();
197
198 // Depress buttons or highlight slider. Also used to emulate mouse grab.
199
200 if (e->buttons() & Qt::LeftButton)
201 {
202 if (upButtonRect(spinOpts).contains(e->pos()))
203 {
204 d->upButtonDown = true;
205 }
206 else if (downButtonRect(spinOpts).contains(e->pos()))
207 {
208 d->downButtonDown = true;
209 }
210 }
211 else if (e->buttons() & Qt::RightButton)
212 {
213 showEdit();
214 }
215
216 update();
217}
218
220{
222 QStyleOptionSpinBox spinOpts = spinBoxOptions();
223
224 // Step up/down for buttons. Emulating mouse grab too.
225
226 if (upButtonRect(spinOpts).contains(e->pos()) && d->upButtonDown)
227 {
228 setInternalValue(d->value + d->singleStep);
229 }
230 else if (downButtonRect(spinOpts).contains(e->pos()) && d->downButtonDown)
231 {
232 setInternalValue(d->value - d->singleStep);
233 }
234 else if (editRect(spinOpts).contains(e->pos()) &&
235 !(d->edit->isVisible()) &&
236 !(d->upButtonDown || d->downButtonDown))
237 {
238 // Snap to percentage for progress area
239 setInternalValue(valueForX(e->pos().x(),e->modifiers()));
240 }
241
242 d->upButtonDown = false;
243 d->downButtonDown = false;
244 update();
245}
246
248{
250
251 if( e->modifiers() & Qt::ShiftModifier )
252 {
253 if( !d->shiftMode )
254 {
255 d->shiftPercent = pow(double(d->value - d->minimum)/double(d->maximum - d->minimum),
256 1/double(d->exponentRatio));
257 d->shiftMode = true;
258 }
259 }
260 else
261 {
262 d->shiftMode = false;
263 }
264
265 // Respect emulated mouse grab.
266 if (e->buttons() & Qt::LeftButton && !(d->downButtonDown || d->upButtonDown))
267 {
268 setInternalValue(valueForX(e->pos().x(),e->modifiers()));
269 update();
270 }
271}
272
274{
276
277 switch (e->key())
278 {
279 case Qt::Key_Up:
280 case Qt::Key_Right:
281 setInternalValue(d->value + d->singleStep);
282 break;
283 case Qt::Key_Down:
284 case Qt::Key_Left:
285 setInternalValue(d->value - d->singleStep);
286 break;
287 case Qt::Key_Shift:
288 d->shiftPercent = pow( double(d->value - d->minimum)/double(d->maximum - d->minimum), 1/double(d->exponentRatio) );
289 d->shiftMode = true;
290 break;
291 case Qt::Key_Enter: // Line edit isn't "accepting" key strokes...
292 case Qt::Key_Return:
293 case Qt::Key_Escape:
294 case Qt::Key_Control:
295 case Qt::Key_Alt:
296 case Qt::Key_AltGr:
297 case Qt::Key_Super_L:
298 case Qt::Key_Super_R:
299 break;
300 default:
301 showEdit();
302 d->edit->event(e);
303 break;
304 }
305}
306
308{
309
311
312 int step = d->fastSliderStep;
313 if( e->modifiers() & Qt::ShiftModifier )
314 {
315 step = d->singleStep;
316 }
317
318 if ( e->angleDelta().y() > 0)
319 {
320 setInternalValue(d->value + step);
321 }
322 else
323 {
324 setInternalValue(d->value - step);
325 }
326
327 update();
328 e->accept();
329}
330
331bool RAbstractSliderSpinBox::eventFilter(QObject* recv, QEvent* e)
332{
334
335 if (recv == static_cast<QObject*>(d->edit) && e->type() == QEvent::KeyRelease)
336 {
337 QKeyEvent* const keyEvent = static_cast<QKeyEvent*>(e);
338
339 switch (keyEvent->key())
340 {
341 case Qt::Key_Enter:
342 case Qt::Key_Return:
343 setInternalValue(QLocale::system().toDouble(d->edit->text()) * d->factor);
344 hideEdit();
345 return true;
346 case Qt::Key_Escape:
347 hideEdit();
348 return true;
349 default:
350 break;
351 }
352 }
353
354 return false;
355}
356
358{
359 const Q_D(RAbstractSliderSpinBox);
360
361 QStyleOptionSpinBox spinOpts = spinBoxOptions();
362 QFontMetrics fm(font());
363
364 // We need at least 50 pixels or things start to look bad
365 int w = qMax(fm.horizontalAdvance(QString::number(d->maximum)), 50);
366 QSize hint(w, d->edit->sizeHint().height() + 3);
367
368 // Getting the size of the buttons is a pain as the calcs require a rect
369 // that is "big enough". We run the calc twice to get the "smallest" buttons
370 // This code was inspired by QAbstractSpinBox.
371
372 QSize extra(35, 6);
373 spinOpts.rect.setSize(hint + extra);
374 extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts,
375 QStyle::SC_SpinBoxEditField, this).size();
376
377 spinOpts.rect.setSize(hint + extra);
378 extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts,
379 QStyle::SC_SpinBoxEditField, this).size();
380 hint += extra;
381
382 spinOpts.rect = rect();
383
384 return style()->sizeFromContents(QStyle::CT_SpinBox, &spinOpts, hint, 0);
385}
386
388{
389 return sizeHint();
390}
391
392QStyleOptionSpinBox RAbstractSliderSpinBox::spinBoxOptions() const
393{
394 const Q_D(RAbstractSliderSpinBox);
395
396 QStyleOptionSpinBox opts;
397 opts.initFrom(this);
398 opts.frame = false;
399 opts.buttonSymbols = QAbstractSpinBox::UpDownArrows;
400 opts.subControls = QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
401
402 // Disable non-logical buttons
403 if (d->value == d->minimum)
404 {
405 opts.stepEnabled = QAbstractSpinBox::StepUpEnabled;
406 }
407 else if (d->value == d->maximum)
408 {
409 opts.stepEnabled = QAbstractSpinBox::StepDownEnabled;
410 }
411 else
412 {
413 opts.stepEnabled = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled;
414 }
415
416 // Deal with depressed buttons
417 if (d->upButtonDown)
418 {
419 opts.activeSubControls = QStyle::SC_SpinBoxUp;
420 }
421 else if (d->downButtonDown)
422 {
423 opts.activeSubControls = QStyle::SC_SpinBoxDown;
424 }
425 else
426 {
427 opts.activeSubControls = QFlags<QStyle::SubControl>();
428 }
429
430 return opts;
431}
432
433QStyleOptionProgressBar RAbstractSliderSpinBox::progressBarOptions() const
434{
435 const Q_D(RAbstractSliderSpinBox);
436
437 QStyleOptionSpinBox spinOpts = spinBoxOptions();
438
439 // Create opts for drawing the progress portion
440
441 QStyleOptionProgressBar progressOpts;
442 progressOpts.initFrom(this);
443 progressOpts.maximum = d->maximum;
444 progressOpts.minimum = d->minimum;
445
446 double minDbl = d->minimum;
447 double dValues = (d->maximum - minDbl);
448
449 progressOpts.progress = dValues * pow((d->value - minDbl) / dValues, 1.0 / d->exponentRatio) + minDbl;
450 progressOpts.text = valueString() + d->suffix;
451 progressOpts.textAlignment = Qt::AlignCenter;
452 progressOpts.textVisible = !(d->edit->isVisible());
453
454 // Change opts rect to be only the ComboBox's text area
455 progressOpts.rect = editRect(spinOpts);
456
457 return progressOpts;
458}
459
460QRect RAbstractSliderSpinBox::editRect(const QStyleOptionSpinBox& spinBoxOptions) const
461{
462 return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions,
463 QStyle::SC_SpinBoxEditField);
464}
465
466QRect RAbstractSliderSpinBox::progressRect(const QStyleOptionProgressBar& progressBarOptions) const
467{
468 return style()->subElementRect(QStyle::SE_ProgressBarGroove, &progressBarOptions);
469}
470
471QRect RAbstractSliderSpinBox::upButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const
472{
473 return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions,
474 QStyle::SC_SpinBoxUp);
475}
476
477QRect RAbstractSliderSpinBox::downButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const
478{
479 return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions,
480 QStyle::SC_SpinBoxDown);
481}
482
483int RAbstractSliderSpinBox::valueForX(int x, Qt::KeyboardModifiers modifiers) const
484{
485 const Q_D(RAbstractSliderSpinBox);
486
487 QStyleOptionSpinBox spinOpts = spinBoxOptions();
488 QStyleOptionProgressBar progressOpts = progressBarOptions();
489
490 // Adjust for magic number in style code (margins)
491 QRect correctedProgRect = progressRect(progressOpts).adjusted(2, 2, -2, -2);
492
493 // Compute the distance of the progress bar, in pixel
494 double leftDbl = correctedProgRect.left();
495 double xDbl = x - leftDbl;
496
497 // Compute the ration of the progress bar used, linearly (ignoring the exponent)
498 double rightDbl = correctedProgRect.right();
499 double minDbl = d->minimum;
500 double maxDbl = d->maximum;
501 double dValues = (maxDbl - minDbl);
502 double percent = (xDbl / (rightDbl - leftDbl));
503
504 // If SHIFT is pressed, movement should be slowed.
505 if ( modifiers & Qt::ShiftModifier )
506 {
507 percent = d->shiftPercent + (percent - d->shiftPercent) * d->slowFactor;
508 }
509
510 // Final value
511 double realvalue = ((dValues * pow(percent, d->exponentRatio)) + minDbl);
512
513 // If key CTRL is pressed, round to the closest step.
514
515 if ( modifiers & Qt::ControlModifier )
516 {
517 double fstep = d->fastSliderStep;
518
519 if( modifiers & Qt::ShiftModifier )
520 {
521 fstep *= d->slowFactor;
522 }
523
524 realvalue = floor((realvalue + fstep / 2) / fstep) * fstep;
525 }
526
527 // Return the value
528 return int(realvalue);
529}
530
531void RAbstractSliderSpinBox::setSuffix(const QString& suffix)
532{
534 d->suffix = suffix;
535}
536
538{
540 Q_ASSERT(dbl > 0);
541 d->exponentRatio = dbl;
542}
543
544void RAbstractSliderSpinBox::contextMenuEvent(QContextMenuEvent* event)
545{
546 event->accept();
547}
548
550{
551 // only hide on focus lost, if editing is finished that will be handled in eventFilter
553
554 if (!d->edit->hasFocus())
555 {
556 hideEdit();
557 }
558}
559
560// ---------------------------------------------------------------------------------------------
561
565
566RSliderSpinBox::RSliderSpinBox(QWidget* const parent)
568{
569 setRange(0,99);
570}
571
575
576void RSliderSpinBox::setRange(int minimum, int maximum)
577{
578 Q_D(RSliderSpinBox);
579 d->minimum = minimum;
580 d->maximum = maximum;
581 d->fastSliderStep = (maximum-minimum+1)/20;
582 d->validator->setRange(minimum, maximum, 0);
583 update();
584}
585
587{
588 const Q_D(RSliderSpinBox);
589 return d->minimum;
590}
591
593{
594 Q_D(RSliderSpinBox);
595 setRange(minimum, d->maximum);
596}
597
599{
600 const Q_D(RSliderSpinBox);
601 return d->maximum;
602}
603
605{
606 Q_D(RSliderSpinBox);
607 setRange(d->minimum, maximum);
608}
609
611{
612 const Q_D(RSliderSpinBox);
613 return d->fastSliderStep;
614}
615
617{
618 Q_D(RSliderSpinBox);
619 d->fastSliderStep = step;
620}
621
623{
624 const Q_D(RSliderSpinBox);
625 return d->value;
626}
627
629{
631 update();
632}
633
635{
636 const Q_D(RSliderSpinBox);
637 return QLocale::system().toString(d->value);
638}
639
641{
642 Q_D(RSliderSpinBox);
643 d->singleStep = value;
644}
645
647{
648 Q_UNUSED(value);
649}
650
652{
654 d->value = qBound(d->minimum, _value, d->maximum);
655 emit(valueChanged(value()));
656}
657
658// ---------------------------------------------------------------------------------------------
659
663
668
672
673void RDoubleSliderSpinBox::setRange(double minimum, double maximum, int decimals)
674{
676 d->factor = pow(10.0, decimals);
677
678 d->minimum = minimum * d->factor;
679 d->maximum = maximum * d->factor;
680
681 // This code auto-compute a new step when pressing control.
682 // A flag defaulting to "do not change the fast step" should be added, but it implies changing every call
683
684 if (maximum - minimum >= 2.0 || decimals <= 0)
685 {
686 //Quick step on integers
687 d->fastSliderStep = int(pow(10.0, decimals));
688 }
689 else if(decimals == 1)
690 {
691 d->fastSliderStep = (maximum-minimum)*d->factor/10;
692 }
693 else
694 {
695 d->fastSliderStep = (maximum-minimum)*d->factor/20;
696 }
697
698 d->validator->setRange(minimum, maximum, decimals);
699 update();
700 setValue(value());
701}
702
704{
705 const Q_D(RAbstractSliderSpinBox);
706 return d->minimum / d->factor;
707}
708
710{
712 setRange(minimum, d->maximum);
713}
714
716{
717 const Q_D(RAbstractSliderSpinBox);
718 return d->maximum / d->factor;
719}
720
722{
724 setRange(d->minimum, maximum);
725}
726
728{
729 const Q_D(RAbstractSliderSpinBox);
730 return d->fastSliderStep;
731}
732
734{
736 d->fastSliderStep = step * d->factor;
737}
738
740{
741 const Q_D(RAbstractSliderSpinBox);
742 return (double)d->value / d->factor;
743}
744
746{
748 setInternalValue(d->value = qRound(value * d->factor));
749 update();
750}
751
753{
755 d->singleStep = value * d->factor;
756}
757
759{
760 const Q_D(RAbstractSliderSpinBox);
761 return QLocale::system().toString((double)d->value / d->factor, 'f', d->validator->decimals());
762}
763
765{
767 d->value = qBound(d->minimum, val, d->maximum);
768 emit(valueChanged(value()));
769}
770
771} // namespace KDcrawIface
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void mouseReleaseEvent(QMouseEvent *e) override
void wheelEvent(QWheelEvent *) override
QRect editRect(const QStyleOptionSpinBox &spinBoxOptions) const
virtual void setInternalValue(int value)=0
virtual QString valueString() const =0
QRect progressRect(const QStyleOptionProgressBar &progressBarOptions) const
void keyPressEvent(QKeyEvent *e) override
int valueForX(int x, Qt::KeyboardModifiers modifiers=Qt::NoModifier) const
QRect upButtonRect(const QStyleOptionSpinBox &spinBoxOptions) const
void mouseMoveEvent(QMouseEvent *e) override
QStyleOptionSpinBox spinBoxOptions() const
QRect downButtonRect(const QStyleOptionSpinBox &spinBoxOptions) const
void setSuffix(const QString &suffix)
void paintEvent(QPaintEvent *e) override
void mousePressEvent(QMouseEvent *e) override
RAbstractSliderSpinBox(QWidget *const parent, RAbstractSliderSpinBoxPrivate *const q)
bool eventFilter(QObject *recv, QEvent *e) override
void contextMenuEvent(QContextMenuEvent *event) override
QStyleOptionProgressBar progressBarOptions() const
void setInternalValue(int val) override
void setRange(double minimum, double maximum, int decimals=0)
QString valueString() const override
RDoubleSliderSpinBox(QWidget *const parent=0)
RSliderSpinBox(QWidget *const parent=0)
void setRange(int minimum, int maximum)
void setInternalValue(int value) override
QString valueString() const override
void valueChanged(int value)
double toDouble(const quint8 *data, int channelpos)
rgba palette[MAX_PALETTE]
Definition palette.c:35
Save space slider widget.