1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qplatformdefs.h>
5#include <private/qabstractspinbox_p.h>
6#include <private/qapplication_p.h>
7#if QT_CONFIG(datetimeparser)
8#include <private/qdatetimeparser_p.h>
9#endif
10#include <private/qlineedit_p.h>
11#include <qabstractspinbox.h>
12
13#include <qapplication.h>
14#include <qstylehints.h>
15#include <qclipboard.h>
16#include <qdatetime.h>
17#include <qevent.h>
18#if QT_CONFIG(menu)
19#include <qmenu.h>
20#endif
21#include <qpainter.h>
22#include <qpalette.h>
23#include <qstylepainter.h>
24#include <qdebug.h>
25#if QT_CONFIG(accessibility)
26# include <qaccessible.h>
27#endif
28
29#include <QtCore/qpointer.h>
30
31//#define QABSTRACTSPINBOX_QSBDEBUG
32#ifdef QABSTRACTSPINBOX_QSBDEBUG
33# define QASBDEBUG qDebug
34#else
35# define QASBDEBUG if (false) qDebug
36#endif
37
38QT_BEGIN_NAMESPACE
39
40using namespace Qt::StringLiterals;
41
42/*!
43 \class QAbstractSpinBox
44 \brief The QAbstractSpinBox class provides a spinbox and a line edit to
45 display values.
46
47 \ingroup abstractwidgets
48 \inmodule QtWidgets
49
50 The class is designed as a common super class for widgets like
51 QSpinBox, QDoubleSpinBox and QDateTimeEdit
52
53 Here are the main properties of the class:
54
55 \list 1
56
57 \li \l text: The text that is displayed in the QAbstractSpinBox.
58
59 \li \l alignment: The alignment of the text in the QAbstractSpinBox.
60
61 \li \l wrapping: Whether the QAbstractSpinBox wraps from the
62 minimum value to the maximum value and vice versa.
63
64 \endlist
65
66 QAbstractSpinBox provides a virtual stepBy() function that is
67 called whenever the user triggers a step. This function takes an
68 integer value to signify how many steps were taken. E.g. Pressing
69 Qt::Key_Down will trigger a call to stepBy(-1).
70
71 When the user triggers a step whilst holding the Qt::ControlModifier,
72 QAbstractSpinBox steps by 10 instead of making a single step. This
73 step modifier affects wheel events, key events and interaction with
74 the spinbox buttons. Note that on macOS, Control corresponds to the
75 Command key.
76
77 Since Qt 5.12, QStyle::SH_SpinBox_StepModifier can be used to select
78 which Qt::KeyboardModifier increases the step rate. Qt::NoModifier
79 disables this feature.
80
81 QAbstractSpinBox also provide a virtual function stepEnabled() to
82 determine whether stepping up/down is allowed at any point. This
83 function returns a bitset of StepEnabled.
84
85 \sa QAbstractSlider, QSpinBox, QDoubleSpinBox, QDateTimeEdit,
86 {Spin Boxes Example}
87*/
88
89/*!
90 \enum QAbstractSpinBox::StepEnabledFlag
91
92 \value StepNone
93 \value StepUpEnabled
94 \value StepDownEnabled
95*/
96
97/*!
98 \enum QAbstractSpinBox::StepType
99
100 \value DefaultStepType
101 \value AdaptiveDecimalStepType
102*/
103
104/*!
105 \fn void QAbstractSpinBox::editingFinished()
106
107 This signal is emitted editing is finished. This happens when the
108 spinbox loses focus and when enter is pressed.
109*/
110
111/*!
112 Constructs an abstract spinbox with the given \a parent with default
113 \l wrapping, and \l alignment properties.
114*/
115
116QAbstractSpinBox::QAbstractSpinBox(QWidget *parent)
117 : QWidget(*new QAbstractSpinBoxPrivate, parent, { })
118{
119 Q_D(QAbstractSpinBox);
120 d->init();
121}
122
123/*!
124 \internal
125*/
126QAbstractSpinBox::QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent)
127 : QWidget(dd, parent, { })
128{
129 Q_D(QAbstractSpinBox);
130 d->init();
131}
132
133/*!
134 Called when the QAbstractSpinBox is destroyed.
135*/
136
137QAbstractSpinBox::~QAbstractSpinBox()
138{
139}
140
141/*!
142 \enum QAbstractSpinBox::ButtonSymbols
143
144 This enum type describes the symbols that can be displayed on the buttons
145 in a spin box.
146
147 \inlineimage qspinbox-updown.png
148 \inlineimage qspinbox-plusminus.png
149
150 \value UpDownArrows Little arrows in the classic style.
151 \value PlusMinus \b{+} and \b{-} symbols.
152 \value NoButtons Don't display buttons.
153
154 \sa QAbstractSpinBox::buttonSymbols
155*/
156
157/*!
158 \property QAbstractSpinBox::buttonSymbols
159
160 \brief the current button symbol mode
161
162 The possible values can be either \c UpDownArrows or \c PlusMinus.
163 The default is \c UpDownArrows.
164
165 Note that some styles might render PlusMinus and UpDownArrows
166 identically.
167
168 \sa ButtonSymbols
169*/
170
171QAbstractSpinBox::ButtonSymbols QAbstractSpinBox::buttonSymbols() const
172{
173 Q_D(const QAbstractSpinBox);
174 return d->buttonSymbols;
175}
176
177void QAbstractSpinBox::setButtonSymbols(ButtonSymbols buttonSymbols)
178{
179 Q_D(QAbstractSpinBox);
180 if (d->buttonSymbols != buttonSymbols) {
181 d->buttonSymbols = buttonSymbols;
182 d->updateEditFieldGeometry();
183 updateGeometry();
184 update();
185 }
186}
187
188/*!
189 \property QAbstractSpinBox::text
190
191 \brief the spin box's text, including any prefix and suffix
192
193 There is no default text.
194*/
195
196QString QAbstractSpinBox::text() const
197{
198 return lineEdit()->displayText();
199}
200
201
202/*!
203 \property QAbstractSpinBox::specialValueText
204 \brief the special-value text
205
206 If set, the spin box will display this text instead of a numeric
207 value whenever the current value is equal to minimum(). Typical use
208 is to indicate that this choice has a special (default) meaning.
209
210 For example, if your spin box allows the user to choose a scale factor
211 (or zoom level) for displaying an image, and your application is able
212 to automatically choose one that will enable the image to fit completely
213 within the display window, you can set up the spin box like this:
214
215 \snippet widgets/spinboxes/window.cpp 3
216
217 The user will then be able to choose a scale from 1% to 1000%
218 or select "Auto" to leave it up to the application to choose. Your code
219 must then interpret the spin box value of 0 as a request from the user
220 to scale the image to fit inside the window.
221
222 All values are displayed with the prefix and suffix (if set), \e
223 except for the special value, which only shows the special value
224 text. This special text is passed in the QSpinBox::textChanged()
225 signal that passes a QString.
226
227 To turn off the special-value text display, call this function
228 with an empty string. The default is no special-value text, i.e.
229 the numeric value is shown as usual.
230
231 If no special-value text is set, specialValueText() returns an
232 empty string.
233*/
234
235QString QAbstractSpinBox::specialValueText() const
236{
237 Q_D(const QAbstractSpinBox);
238 return d->specialValueText;
239}
240
241void QAbstractSpinBox::setSpecialValueText(const QString &specialValueText)
242{
243 Q_D(QAbstractSpinBox);
244
245 d->specialValueText = specialValueText;
246 d->cachedSizeHint = QSize(); // minimumSizeHint doesn't care about specialValueText
247 d->clearCache();
248 d->updateEdit();
249}
250
251/*!
252 \property QAbstractSpinBox::wrapping
253
254 \brief whether the spin box is circular.
255
256 If wrapping is true stepping up from maximum() value will take you
257 to the minimum() value and vice versa. Wrapping only make sense if
258 you have minimum() and maximum() values set.
259
260 \snippet code/src_gui_widgets_qabstractspinbox.cpp 0
261
262 \sa QSpinBox::minimum(), QSpinBox::maximum()
263*/
264
265bool QAbstractSpinBox::wrapping() const
266{
267 Q_D(const QAbstractSpinBox);
268 return d->wrapping;
269}
270
271void QAbstractSpinBox::setWrapping(bool wrapping)
272{
273 Q_D(QAbstractSpinBox);
274 d->wrapping = wrapping;
275}
276
277
278/*!
279 \property QAbstractSpinBox::readOnly
280 \brief whether the spin box is read only.
281
282 In read-only mode, the user can still copy the text to the
283 clipboard, or drag and drop the text;
284 but cannot edit it.
285
286 The QLineEdit in the QAbstractSpinBox does not show a cursor in
287 read-only mode.
288
289 \sa QLineEdit::readOnly
290*/
291
292bool QAbstractSpinBox::isReadOnly() const
293{
294 Q_D(const QAbstractSpinBox);
295 return d->readOnly;
296}
297
298void QAbstractSpinBox::setReadOnly(bool enable)
299{
300 Q_D(QAbstractSpinBox);
301 d->readOnly = enable;
302 d->edit->setReadOnly(enable);
303 QEvent event(QEvent::ReadOnlyChange);
304 QCoreApplication::sendEvent(receiver: this, event: &event);
305 update();
306}
307
308/*!
309 \property QAbstractSpinBox::keyboardTracking
310 \brief whether keyboard tracking is enabled for the spinbox.
311 \since 4.3
312
313 If keyboard tracking is enabled (the default), the spinbox
314 emits the valueChanged() and textChanged() signals while the
315 new value is being entered from the keyboard.
316
317 E.g. when the user enters the value 600 by typing 6, 0, and 0,
318 the spinbox emits 3 signals with the values 6, 60, and 600
319 respectively.
320
321 If keyboard tracking is disabled, the spinbox doesn't emit the
322 valueChanged() and textChanged() signals while typing. It emits
323 the signals later, when the return key is pressed, when keyboard
324 focus is lost, or when other spinbox functionality is used, e.g.
325 pressing an arrow key.
326*/
327
328bool QAbstractSpinBox::keyboardTracking() const
329{
330 Q_D(const QAbstractSpinBox);
331 return d->keyboardTracking;
332}
333
334void QAbstractSpinBox::setKeyboardTracking(bool enable)
335{
336 Q_D(QAbstractSpinBox);
337 d->keyboardTracking = enable;
338}
339
340/*!
341 \property QAbstractSpinBox::frame
342 \brief whether the spin box draws itself with a frame
343
344 If enabled (the default) the spin box draws itself inside a frame,
345 otherwise the spin box draws itself without any frame.
346*/
347
348bool QAbstractSpinBox::hasFrame() const
349{
350 Q_D(const QAbstractSpinBox);
351 return d->frame;
352}
353
354
355void QAbstractSpinBox::setFrame(bool enable)
356{
357 Q_D(QAbstractSpinBox);
358 d->frame = enable;
359 update();
360 d->updateEditFieldGeometry();
361}
362
363/*!
364 \property QAbstractSpinBox::accelerated
365 \brief whether the spin box will accelerate the frequency of the steps when
366 pressing the step Up/Down buttons.
367 \since 4.2
368
369 If enabled the spin box will increase/decrease the value faster
370 the longer you hold the button down.
371*/
372
373void QAbstractSpinBox::setAccelerated(bool accelerate)
374{
375 Q_D(QAbstractSpinBox);
376 d->accelerate = accelerate;
377
378}
379bool QAbstractSpinBox::isAccelerated() const
380{
381 Q_D(const QAbstractSpinBox);
382 return d->accelerate;
383}
384
385/*!
386 \property QAbstractSpinBox::showGroupSeparator
387 \since 5.3
388
389
390 \brief whether a thousands separator is enabled. By default this
391 property is false.
392*/
393bool QAbstractSpinBox::isGroupSeparatorShown() const
394{
395 Q_D(const QAbstractSpinBox);
396 return d->showGroupSeparator;
397}
398
399void QAbstractSpinBox::setGroupSeparatorShown(bool shown)
400{
401 Q_D(QAbstractSpinBox);
402 if (d->showGroupSeparator == shown)
403 return;
404 d->showGroupSeparator = shown;
405 d->setValue(val: d->value, ep: EmitIfChanged);
406 updateGeometry();
407}
408
409/*!
410 \enum QAbstractSpinBox::CorrectionMode
411
412 This enum type describes the mode the spinbox will use to correct
413 an \l{QValidator::}{Intermediate} value if editing finishes.
414
415 \value CorrectToPreviousValue The spinbox will revert to the last
416 valid value.
417
418 \value CorrectToNearestValue The spinbox will revert to the nearest
419 valid value.
420
421 \sa correctionMode
422*/
423
424/*!
425 \property QAbstractSpinBox::correctionMode
426 \brief the mode to correct an \l{QValidator::}{Intermediate}
427 value if editing finishes
428 \since 4.2
429
430 The default mode is QAbstractSpinBox::CorrectToPreviousValue.
431
432 \sa acceptableInput, validate(), fixup()
433*/
434void QAbstractSpinBox::setCorrectionMode(CorrectionMode correctionMode)
435{
436 Q_D(QAbstractSpinBox);
437 d->correctionMode = correctionMode;
438
439}
440QAbstractSpinBox::CorrectionMode QAbstractSpinBox::correctionMode() const
441{
442 Q_D(const QAbstractSpinBox);
443 return d->correctionMode;
444}
445
446
447/*!
448 \property QAbstractSpinBox::acceptableInput
449 \brief whether the input satisfies the current validation
450 \since 4.2
451
452 \sa validate(), fixup(), correctionMode
453*/
454
455bool QAbstractSpinBox::hasAcceptableInput() const
456{
457 Q_D(const QAbstractSpinBox);
458 return d->edit->hasAcceptableInput();
459}
460
461/*!
462 \property QAbstractSpinBox::alignment
463 \brief the alignment of the spin box
464
465 Possible Values are Qt::AlignLeft, Qt::AlignRight, and Qt::AlignHCenter.
466
467 By default, the alignment is Qt::AlignLeft
468
469 Attempting to set the alignment to an illegal flag combination
470 does nothing.
471
472 \sa Qt::Alignment
473*/
474
475Qt::Alignment QAbstractSpinBox::alignment() const
476{
477 Q_D(const QAbstractSpinBox);
478
479 return (Qt::Alignment)d->edit->alignment();
480}
481
482void QAbstractSpinBox::setAlignment(Qt::Alignment flag)
483{
484 Q_D(QAbstractSpinBox);
485
486 d->edit->setAlignment(flag);
487}
488
489/*!
490 Selects all the text in the spinbox except the prefix and suffix.
491*/
492
493void QAbstractSpinBox::selectAll()
494{
495 Q_D(QAbstractSpinBox);
496
497
498 if (!d->specialValue()) {
499 const int tmp = d->edit->displayText().size() - d->suffix.size();
500 d->edit->setSelection(tmp, -(tmp - d->prefix.size()));
501 } else {
502 d->edit->selectAll();
503 }
504}
505
506/*!
507 Clears the lineedit of all text but prefix and suffix.
508*/
509
510void QAbstractSpinBox::clear()
511{
512 Q_D(QAbstractSpinBox);
513
514 d->edit->setText(d->prefix + d->suffix);
515 d->edit->setCursorPosition(d->prefix.size());
516 d->cleared = true;
517}
518
519/*!
520 Virtual function that determines whether stepping up and down is
521 legal at any given time.
522
523 The up arrow will be painted as disabled unless (stepEnabled() &
524 StepUpEnabled) != 0.
525
526 The default implementation will return (StepUpEnabled|
527 StepDownEnabled) if wrapping is turned on. Else it will return
528 StepDownEnabled if value is > minimum() or'ed with StepUpEnabled if
529 value < maximum().
530
531 If you subclass QAbstractSpinBox you will need to reimplement this function.
532
533 \sa QSpinBox::minimum(), QSpinBox::maximum(), wrapping()
534*/
535
536
537QAbstractSpinBox::StepEnabled QAbstractSpinBox::stepEnabled() const
538{
539 Q_D(const QAbstractSpinBox);
540 if (d->readOnly || d->type == QMetaType::UnknownType)
541 return StepNone;
542 if (d->wrapping)
543 return StepEnabled(StepUpEnabled | StepDownEnabled);
544 StepEnabled ret = StepNone;
545 if (QAbstractSpinBoxPrivate::variantCompare(arg1: d->value, arg2: d->maximum) < 0) {
546 ret |= StepUpEnabled;
547 }
548 if (QAbstractSpinBoxPrivate::variantCompare(arg1: d->value, arg2: d->minimum) > 0) {
549 ret |= StepDownEnabled;
550 }
551 return ret;
552}
553
554/*!
555 This virtual function is called by the QAbstractSpinBox to
556 determine whether \a input is valid. The \a pos parameter indicates
557 the position in the string. Reimplemented in the various
558 subclasses.
559*/
560
561QValidator::State QAbstractSpinBox::validate(QString & /* input */, int & /* pos */) const
562{
563 return QValidator::Acceptable;
564}
565
566/*!
567 This virtual function is called by the QAbstractSpinBox if the
568 \a input is not validated to QValidator::Acceptable when Return is
569 pressed or interpretText() is called. It will try to change the
570 text so it is valid. Reimplemented in the various subclasses.
571*/
572
573void QAbstractSpinBox::fixup(QString & /* input */) const
574{
575}
576
577/*!
578 Steps up by one linestep
579 Calling this slot is analogous to calling stepBy(1);
580 \sa stepBy(), stepDown()
581*/
582
583void QAbstractSpinBox::stepUp()
584{
585 stepBy(steps: 1);
586}
587
588/*!
589 Steps down by one linestep
590 Calling this slot is analogous to calling stepBy(-1);
591 \sa stepBy(), stepUp()
592*/
593
594void QAbstractSpinBox::stepDown()
595{
596 stepBy(steps: -1);
597}
598/*!
599 Virtual function that is called whenever the user triggers a step.
600 The \a steps parameter indicates how many steps were taken.
601 For example, pressing \c Qt::Key_Down will trigger a call to \c stepBy(-1),
602 whereas pressing \c Qt::Key_PageUp will trigger a call to \c stepBy(10).
603
604 If you subclass \c QAbstractSpinBox you must reimplement this
605 function. Note that this function is called even if the resulting
606 value will be outside the bounds of minimum and maximum. It's this
607 function's job to handle these situations.
608
609 \sa stepUp(), stepDown(), keyPressEvent()
610*/
611
612void QAbstractSpinBox::stepBy(int steps)
613{
614 Q_D(QAbstractSpinBox);
615
616 const QVariant old = d->value;
617 QString tmp = d->edit->displayText();
618 int cursorPos = d->edit->cursorPosition();
619 bool dontstep = false;
620 EmitPolicy e = EmitIfChanged;
621 if (d->pendingEmit) {
622 dontstep = validate(tmp, cursorPos) != QValidator::Acceptable;
623 d->cleared = false;
624 d->interpret(ep: NeverEmit);
625 if (d->value != old)
626 e = AlwaysEmit;
627 }
628 if (!dontstep) {
629 QVariant singleStep;
630 switch (d->stepType) {
631 case QAbstractSpinBox::StepType::AdaptiveDecimalStepType:
632 singleStep = d->calculateAdaptiveDecimalStep(steps);
633 break;
634 default:
635 singleStep = d->singleStep;
636 }
637 d->setValue(val: d->bound(val: d->value + (singleStep * steps), old, steps), ep: e);
638 } else if (e == AlwaysEmit) {
639 d->emitSignals(ep: e, old);
640 }
641 if (style()->styleHint(stylehint: QStyle::SH_SpinBox_SelectOnStep, opt: nullptr, widget: this, returnData: nullptr))
642 selectAll();
643}
644
645/*!
646 This function returns a pointer to the line edit of the spin box.
647*/
648
649QLineEdit *QAbstractSpinBox::lineEdit() const
650{
651 Q_D(const QAbstractSpinBox);
652
653 return d->edit;
654}
655
656
657/*!
658 \fn void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
659
660 Sets the line edit of the spinbox to be \a lineEdit instead of the
661 current line edit widget. \a lineEdit cannot be \nullptr.
662
663 QAbstractSpinBox takes ownership of the new lineEdit
664
665 If QLineEdit::validator() for the \a lineEdit returns \nullptr, the internal
666 validator of the spinbox will be set on the line edit.
667*/
668
669void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
670{
671 Q_D(QAbstractSpinBox);
672
673 if (!lineEdit) {
674 Q_ASSERT(lineEdit);
675 return;
676 }
677
678 if (lineEdit == d->edit)
679 return;
680
681 delete d->edit;
682 d->edit = lineEdit;
683 setProperty(name: "_q_spinbox_lineedit", value: QVariant::fromValue<QWidget *>(value: d->edit));
684 if (!d->edit->validator())
685 d->edit->setValidator(d->validator);
686
687 if (d->edit->parent() != this)
688 d->edit->setParent(this);
689
690 d->edit->setFrame(!style()->styleHint(stylehint: QStyle::SH_SpinBox_ButtonsInsideFrame, opt: nullptr, widget: this));
691 d->edit->setFocusProxy(this);
692 d->edit->setAcceptDrops(false);
693
694 if (d->type != QMetaType::UnknownType) {
695 QObjectPrivate::connect(sender: d->edit, signal: &QLineEdit::textChanged,
696 receiverPrivate: d, slot: &QAbstractSpinBoxPrivate::editorTextChanged);
697 QObjectPrivate::connect(sender: d->edit, signal: &QLineEdit::cursorPositionChanged,
698 receiverPrivate: d, slot: &QAbstractSpinBoxPrivate::editorCursorPositionChanged);
699 connect(sender: d->edit, signal: &QLineEdit::cursorPositionChanged,
700 context: this, slot: [this]() { updateMicroFocus(); });
701 connect(sender: d->edit->d_func()->control, signal: &QWidgetLineControl::updateMicroFocus,
702 context: this, slot: [this]() { updateMicroFocus(); });
703 }
704 d->updateEditFieldGeometry();
705 d->edit->setContextMenuPolicy(Qt::NoContextMenu);
706 d->edit->d_func()->control->setAccessibleObject(this);
707
708 if (isVisible())
709 d->edit->show();
710 if (isVisible())
711 d->updateEdit();
712}
713
714
715/*!
716 This function interprets the text of the spin box. If the value
717 has changed since last interpretation it will emit signals.
718*/
719
720void QAbstractSpinBox::interpretText()
721{
722 Q_D(QAbstractSpinBox);
723 d->interpret(ep: EmitIfChanged);
724}
725
726/*
727 Reimplemented in 4.6, so be careful.
728 */
729/*!
730 \reimp
731*/
732QVariant QAbstractSpinBox::inputMethodQuery(Qt::InputMethodQuery query) const
733{
734 Q_D(const QAbstractSpinBox);
735 const QVariant lineEditValue = d->edit->inputMethodQuery(query);
736 switch (query) {
737 case Qt::ImHints:
738 if (const int hints = inputMethodHints())
739 return QVariant(hints | lineEditValue.toInt());
740 break;
741 default:
742 break;
743 }
744 return lineEditValue;
745}
746
747/*!
748 \reimp
749*/
750
751bool QAbstractSpinBox::event(QEvent *event)
752{
753 Q_D(QAbstractSpinBox);
754 switch (event->type()) {
755 case QEvent::FontChange:
756 case QEvent::StyleChange:
757 d->cachedSizeHint = d->cachedMinimumSizeHint = QSize();
758 break;
759 case QEvent::ApplicationLayoutDirectionChange:
760 case QEvent::LayoutDirectionChange:
761 d->updateEditFieldGeometry();
762 break;
763 case QEvent::HoverEnter:
764 case QEvent::HoverLeave:
765 case QEvent::HoverMove:
766 d->updateHoverControl(pos: static_cast<const QHoverEvent *>(event)->position().toPoint());
767 break;
768 case QEvent::ShortcutOverride:
769 if (d->edit->event(event))
770 return true;
771 break;
772#ifdef QT_KEYPAD_NAVIGATION
773 case QEvent::EnterEditFocus:
774 case QEvent::LeaveEditFocus:
775 if (QApplicationPrivate::keypadNavigationEnabled()) {
776 const bool b = d->edit->event(event);
777 d->edit->setSelection(d->edit->displayText().size() - d->suffix.size(),0);
778 if (event->type() == QEvent::LeaveEditFocus)
779 emit editingFinished();
780 if (b)
781 return true;
782 }
783 break;
784#endif
785 case QEvent::InputMethod:
786 return d->edit->event(event);
787 default:
788 break;
789 }
790 return QWidget::event(event);
791}
792
793/*!
794 \reimp
795*/
796
797void QAbstractSpinBox::showEvent(QShowEvent *)
798{
799 Q_D(QAbstractSpinBox);
800 d->reset();
801
802 if (d->ignoreUpdateEdit) {
803 d->ignoreUpdateEdit = false;
804 } else {
805 d->updateEdit();
806 }
807}
808
809/*!
810 \reimp
811*/
812
813void QAbstractSpinBox::changeEvent(QEvent *event)
814{
815 Q_D(QAbstractSpinBox);
816
817 switch (event->type()) {
818 case QEvent::StyleChange:
819 d->spinClickTimerInterval = style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatRate, opt: nullptr, widget: this);
820 d->spinClickThresholdTimerInterval =
821 style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatThreshold, opt: nullptr, widget: this);
822 if (d->edit)
823 d->edit->setFrame(!style()->styleHint(stylehint: QStyle::SH_SpinBox_ButtonsInsideFrame, opt: nullptr, widget: this));
824 d->stepModifier = static_cast<Qt::KeyboardModifier>(style()->styleHint(stylehint: QStyle::SH_SpinBox_StepModifier, opt: nullptr, widget: this));
825 d->reset();
826 d->updateEditFieldGeometry();
827 break;
828 case QEvent::LocaleChange:
829 d->updateEdit();
830 break;
831 case QEvent::EnabledChange:
832 if (!isEnabled()) {
833 d->reset();
834 }
835 break;
836 case QEvent::ActivationChange:
837 if (!isActiveWindow()){
838 d->reset();
839 if (d->pendingEmit) // pendingEmit can be true even if it hasn't changed.
840 d->interpret(ep: EmitIfChanged); // E.g. 10 to 10.0
841 }
842 break;
843 default:
844 break;
845 }
846 QWidget::changeEvent(event);
847}
848
849/*!
850 \reimp
851*/
852
853void QAbstractSpinBox::resizeEvent(QResizeEvent *event)
854{
855 Q_D(QAbstractSpinBox);
856 QWidget::resizeEvent(event);
857
858 d->updateEditFieldGeometry();
859 update();
860}
861
862/*!
863 \reimp
864*/
865
866QSize QAbstractSpinBox::sizeHint() const
867{
868 Q_D(const QAbstractSpinBox);
869 if (d->cachedSizeHint.isEmpty()) {
870 ensurePolished();
871
872 const QFontMetrics fm(fontMetrics());
873 int h = d->edit->sizeHint().height();
874 int w = 0;
875 QString s;
876 QString fixedContent = d->prefix + d->suffix + u' ';
877 s = d->textFromValue(n: d->minimum);
878 s.truncate(pos: 18);
879 s += fixedContent;
880 w = qMax(a: w, b: fm.horizontalAdvance(s));
881 s = d->textFromValue(n: d->maximum);
882 s.truncate(pos: 18);
883 s += fixedContent;
884 w = qMax(a: w, b: fm.horizontalAdvance(s));
885
886 if (d->specialValueText.size()) {
887 s = d->specialValueText;
888 w = qMax(a: w, b: fm.horizontalAdvance(s));
889 }
890 w += 2; // cursor blinking space
891
892 QStyleOptionSpinBox opt;
893 initStyleOption(option: &opt);
894 QSize hint(w, h);
895 d->cachedSizeHint = style()->sizeFromContents(ct: QStyle::CT_SpinBox, opt: &opt, contentsSize: hint, w: this);
896 }
897 return d->cachedSizeHint;
898}
899
900/*!
901 \reimp
902*/
903
904QSize QAbstractSpinBox::minimumSizeHint() const
905{
906 Q_D(const QAbstractSpinBox);
907 if (d->cachedMinimumSizeHint.isEmpty()) {
908 //Use the prefix and range to calculate the minimumSizeHint
909 ensurePolished();
910
911 const QFontMetrics fm(fontMetrics());
912 int h = d->edit->minimumSizeHint().height();
913 int w = 0;
914
915 QString s;
916 QString fixedContent = d->prefix + u' ';
917 s = d->textFromValue(n: d->minimum);
918 s.truncate(pos: 18);
919 s += fixedContent;
920 w = qMax(a: w, b: fm.horizontalAdvance(s));
921 s = d->textFromValue(n: d->maximum);
922 s.truncate(pos: 18);
923 s += fixedContent;
924 w = qMax(a: w, b: fm.horizontalAdvance(s));
925
926 if (d->specialValueText.size()) {
927 s = d->specialValueText;
928 w = qMax(a: w, b: fm.horizontalAdvance(s));
929 }
930 w += 2; // cursor blinking space
931
932 QStyleOptionSpinBox opt;
933 initStyleOption(option: &opt);
934 QSize hint(w, h);
935
936 d->cachedMinimumSizeHint = style()->sizeFromContents(ct: QStyle::CT_SpinBox, opt: &opt, contentsSize: hint, w: this);
937 }
938 return d->cachedMinimumSizeHint;
939}
940
941/*!
942 \reimp
943*/
944
945void QAbstractSpinBox::paintEvent(QPaintEvent *)
946{
947 QStyleOptionSpinBox opt;
948 initStyleOption(option: &opt);
949 QStylePainter p(this);
950 p.drawComplexControl(cc: QStyle::CC_SpinBox, opt);
951}
952
953/*!
954 \reimp
955
956 This function handles keyboard input.
957
958 The following keys are handled specifically:
959 \table
960 \row \li Enter/Return
961 \li This will reinterpret the text and emit a signal even if the value has not changed
962 since last time a signal was emitted.
963 \row \li Up
964 \li This will invoke stepBy(1)
965 \row \li Down
966 \li This will invoke stepBy(-1)
967 \row \li Page up
968 \li This will invoke stepBy(10)
969 \row \li Page down
970 \li This will invoke stepBy(-10)
971 \endtable
972
973 \sa stepBy()
974*/
975
976
977void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
978{
979 Q_D(QAbstractSpinBox);
980
981 d->keyboardModifiers = event->modifiers();
982
983 if (!event->text().isEmpty() && d->edit->cursorPosition() < d->prefix.size())
984 d->edit->setCursorPosition(d->prefix.size());
985
986 int steps = 1;
987 bool isPgUpOrDown = false;
988 switch (event->key()) {
989 case Qt::Key_PageUp:
990 case Qt::Key_PageDown:
991 steps *= 10;
992 isPgUpOrDown = true;
993 Q_FALLTHROUGH();
994 case Qt::Key_Up:
995 case Qt::Key_Down: {
996#ifdef QT_KEYPAD_NAVIGATION
997 if (QApplicationPrivate::keypadNavigationEnabled()) {
998 // Reserve up/down for nav - use left/right for edit.
999 if (!hasEditFocus() && (event->key() == Qt::Key_Up
1000 || event->key() == Qt::Key_Down)) {
1001 event->ignore();
1002 return;
1003 }
1004 }
1005#endif
1006 event->accept();
1007 const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
1008 if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
1009 return;
1010 if (!isPgUpOrDown && (event->modifiers() & d->stepModifier))
1011 steps *= 10;
1012 if (!up)
1013 steps *= -1;
1014 if (style()->styleHint(stylehint: QStyle::SH_SpinBox_AnimateButton, opt: nullptr, widget: this)) {
1015 d->buttonState = (Keyboard | (up ? Up : Down));
1016 }
1017 if (d->spinClickTimerId == -1)
1018 stepBy(steps);
1019 if (event->isAutoRepeat() && !isPgUpOrDown) {
1020 if (d->spinClickThresholdTimerId == -1 && d->spinClickTimerId == -1) {
1021 d->updateState(up, fromKeyboard: true);
1022 }
1023 }
1024#if QT_CONFIG(accessibility)
1025 QAccessibleValueChangeEvent event(this, d->value);
1026 QAccessible::updateAccessibility(event: &event);
1027#endif
1028 return;
1029 }
1030#ifdef QT_KEYPAD_NAVIGATION
1031 case Qt::Key_Left:
1032 case Qt::Key_Right:
1033 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1034 event->ignore();
1035 return;
1036 }
1037 break;
1038 case Qt::Key_Back:
1039 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1040 event->ignore();
1041 return;
1042 }
1043 break;
1044#endif
1045 case Qt::Key_Enter:
1046 case Qt::Key_Return:
1047 d->edit->d_func()->control->clearUndo();
1048 d->interpret(ep: d->keyboardTracking ? AlwaysEmit : EmitIfChanged);
1049 selectAll();
1050 event->ignore();
1051 emit editingFinished();
1052 emit d->edit->returnPressed();
1053 return;
1054
1055#ifdef QT_KEYPAD_NAVIGATION
1056 case Qt::Key_Select:
1057 if (QApplicationPrivate::keypadNavigationEnabled()) {
1058 // Toggles between left/right moving cursor and inc/dec.
1059 setEditFocus(!hasEditFocus());
1060 }
1061 return;
1062#endif
1063
1064 case Qt::Key_U:
1065 if (event->modifiers() & Qt::ControlModifier
1066 && QGuiApplication::platformName() == "xcb"_L1) { // only X11
1067 event->accept();
1068 if (!isReadOnly())
1069 clear();
1070 return;
1071 }
1072 break;
1073
1074 case Qt::Key_End:
1075 case Qt::Key_Home:
1076 if (event->modifiers() & Qt::ShiftModifier) {
1077 int currentPos = d->edit->cursorPosition();
1078 const QString text = d->edit->displayText();
1079 if (event->key() == Qt::Key_End) {
1080 if ((currentPos == 0 && !d->prefix.isEmpty()) || text.size() - d->suffix.size() <= currentPos) {
1081 break; // let lineedit handle this
1082 } else {
1083 d->edit->setSelection(currentPos, text.size() - d->suffix.size() - currentPos);
1084 }
1085 } else {
1086 if ((currentPos == text.size() && !d->suffix.isEmpty()) || currentPos <= d->prefix.size()) {
1087 break; // let lineedit handle this
1088 } else {
1089 d->edit->setSelection(currentPos, d->prefix.size() - currentPos);
1090 }
1091 }
1092 event->accept();
1093 return;
1094 }
1095 break;
1096
1097 default:
1098#ifndef QT_NO_SHORTCUT
1099 if (event == QKeySequence::SelectAll) {
1100 selectAll();
1101 event->accept();
1102 return;
1103 }
1104#endif
1105 break;
1106 }
1107
1108 d->edit->event(event);
1109 if (!d->edit->text().isEmpty())
1110 d->cleared = false;
1111 if (!isVisible())
1112 d->ignoreUpdateEdit = true;
1113}
1114
1115/*!
1116 \reimp
1117*/
1118
1119void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
1120{
1121 Q_D(QAbstractSpinBox);
1122
1123 d->keyboardModifiers = event->modifiers();
1124 if (d->buttonState & Keyboard && !event->isAutoRepeat()) {
1125 d->reset();
1126 } else {
1127 d->edit->event(event);
1128 }
1129}
1130
1131/*!
1132 \reimp
1133*/
1134
1135#if QT_CONFIG(wheelevent)
1136void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
1137{
1138 Q_D(QAbstractSpinBox);
1139#ifdef Q_OS_MACOS
1140 // If the event comes from a real mouse wheel, rather than a track pad
1141 // (Qt::MouseEventSynthesizedBySystem), the shift modifier changes the
1142 // scroll orientation to horizontal.
1143 // Convert horizontal events back to vertical whilst shift is held.
1144 if ((event->modifiers() & Qt::ShiftModifier)
1145 && event->source() == Qt::MouseEventNotSynthesized) {
1146 d->wheelDeltaRemainder += event->angleDelta().x();
1147 } else {
1148 d->wheelDeltaRemainder += event->angleDelta().y();
1149 }
1150#else
1151 d->wheelDeltaRemainder += event->angleDelta().y();
1152#endif
1153 const int steps = d->wheelDeltaRemainder / 120;
1154 d->wheelDeltaRemainder -= steps * 120;
1155 if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
1156 stepBy(steps: event->modifiers() & d->stepModifier ? steps * 10 : steps);
1157 event->accept();
1158}
1159#endif
1160
1161
1162/*!
1163 \reimp
1164*/
1165void QAbstractSpinBox::focusInEvent(QFocusEvent *event)
1166{
1167 Q_D(QAbstractSpinBox);
1168
1169 d->edit->event(event);
1170 if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
1171 selectAll();
1172 }
1173 QWidget::focusInEvent(event);
1174}
1175
1176/*!
1177 \reimp
1178*/
1179
1180void QAbstractSpinBox::focusOutEvent(QFocusEvent *event)
1181{
1182 Q_D(QAbstractSpinBox);
1183
1184 if (d->pendingEmit)
1185 d->interpret(ep: EmitIfChanged);
1186
1187 d->reset();
1188 d->edit->event(event);
1189 d->updateEdit();
1190 QWidget::focusOutEvent(event);
1191
1192#ifdef QT_KEYPAD_NAVIGATION
1193 // editingFinished() is already emitted on LeaveEditFocus
1194 if (!QApplicationPrivate::keypadNavigationEnabled())
1195#endif
1196 emit editingFinished();
1197}
1198
1199/*!
1200 \reimp
1201*/
1202
1203void QAbstractSpinBox::closeEvent(QCloseEvent *event)
1204{
1205 Q_D(QAbstractSpinBox);
1206
1207 d->reset();
1208 if (d->pendingEmit)
1209 d->interpret(ep: EmitIfChanged);
1210 QWidget::closeEvent(event);
1211}
1212
1213/*!
1214 \reimp
1215*/
1216
1217void QAbstractSpinBox::hideEvent(QHideEvent *event)
1218{
1219 Q_D(QAbstractSpinBox);
1220 d->reset();
1221 if (d->pendingEmit)
1222 d->interpret(ep: EmitIfChanged);
1223 QWidget::hideEvent(event);
1224}
1225
1226
1227/*!
1228 \reimp
1229*/
1230
1231void QAbstractSpinBox::timerEvent(QTimerEvent *event)
1232{
1233 Q_D(QAbstractSpinBox);
1234
1235 bool doStep = false;
1236 if (event->timerId() == d->spinClickThresholdTimerId) {
1237 killTimer(id: d->spinClickThresholdTimerId);
1238 d->spinClickThresholdTimerId = -1;
1239 d->effectiveSpinRepeatRate = d->buttonState & Keyboard
1240 ? QGuiApplication::styleHints()->keyboardAutoRepeatRateF()
1241 : d->spinClickTimerInterval;
1242 d->spinClickTimerId = startTimer(interval: d->effectiveSpinRepeatRate);
1243 doStep = true;
1244 } else if (event->timerId() == d->spinClickTimerId) {
1245 if (d->accelerate) {
1246 d->acceleration = d->acceleration + (int)(d->effectiveSpinRepeatRate * 0.05);
1247 if (d->effectiveSpinRepeatRate - d->acceleration >= 10) {
1248 killTimer(id: d->spinClickTimerId);
1249 d->spinClickTimerId = startTimer(interval: d->effectiveSpinRepeatRate - d->acceleration);
1250 }
1251 }
1252 doStep = true;
1253 }
1254
1255 if (doStep) {
1256 const bool increaseStepRate = d->keyboardModifiers & d->stepModifier;
1257 const StepEnabled st = stepEnabled();
1258 if (d->buttonState & Up) {
1259 if (!(st & StepUpEnabled)) {
1260 d->reset();
1261 } else {
1262 stepBy(steps: increaseStepRate ? 10 : 1);
1263 }
1264 } else if (d->buttonState & Down) {
1265 if (!(st & StepDownEnabled)) {
1266 d->reset();
1267 } else {
1268 stepBy(steps: increaseStepRate ? -10 : -1);
1269 }
1270 }
1271 return;
1272 }
1273 QWidget::timerEvent(event);
1274 return;
1275}
1276
1277/*!
1278 \reimp
1279*/
1280
1281#if QT_CONFIG(contextmenu)
1282void QAbstractSpinBox::contextMenuEvent(QContextMenuEvent *event)
1283{
1284 Q_D(QAbstractSpinBox);
1285
1286 QPointer<QMenu> menu = d->edit->createStandardContextMenu();
1287 if (!menu)
1288 return;
1289
1290 d->reset();
1291
1292 QAction *selAll = new QAction(tr(s: "&Select All"), menu);
1293#if QT_CONFIG(shortcut)
1294 selAll->setShortcut(QKeySequence::SelectAll);
1295#endif
1296 menu->insertAction(before: d->edit->d_func()->selectAllAction,
1297 action: selAll);
1298 menu->removeAction(action: d->edit->d_func()->selectAllAction);
1299 menu->addSeparator();
1300 const uint se = stepEnabled();
1301 QAction *up = menu->addAction(text: tr(s: "&Step up"));
1302 up->setEnabled(se & StepUpEnabled);
1303 QAction *down = menu->addAction(text: tr(s: "Step &down"));
1304 down->setEnabled(se & StepDownEnabled);
1305 menu->addSeparator();
1306
1307 const QPointer<QAbstractSpinBox> that = this;
1308 const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
1309 ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
1310 const QAction *action = menu->exec(pos);
1311 delete static_cast<QMenu *>(menu);
1312 if (that && action) {
1313 if (action == up) {
1314 stepBy(steps: 1);
1315 } else if (action == down) {
1316 stepBy(steps: -1);
1317 } else if (action == selAll) {
1318 selectAll();
1319 }
1320 }
1321 event->accept();
1322}
1323#endif // QT_CONFIG(contextmenu)
1324
1325/*!
1326 \reimp
1327*/
1328
1329void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
1330{
1331 Q_D(QAbstractSpinBox);
1332
1333 d->keyboardModifiers = event->modifiers();
1334 d->updateHoverControl(pos: event->position().toPoint());
1335
1336 // If we have a timer ID, update the state
1337 if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
1338 const StepEnabled se = stepEnabled();
1339 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
1340 d->updateState(up: true);
1341 else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
1342 d->updateState(up: false);
1343 else
1344 d->reset();
1345 event->accept();
1346 }
1347}
1348
1349/*!
1350 \reimp
1351*/
1352
1353void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
1354{
1355 Q_D(QAbstractSpinBox);
1356
1357 d->keyboardModifiers = event->modifiers();
1358 if (event->button() != Qt::LeftButton || d->buttonState != None) {
1359 return;
1360 }
1361
1362 d->updateHoverControl(pos: event->position().toPoint());
1363 event->accept();
1364
1365 const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
1366 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
1367 d->updateState(up: true);
1368 } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
1369 d->updateState(up: false);
1370 } else {
1371 event->ignore();
1372 }
1373}
1374
1375/*!
1376 \reimp
1377*/
1378void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
1379{
1380 Q_D(QAbstractSpinBox);
1381
1382 d->keyboardModifiers = event->modifiers();
1383 if ((d->buttonState & Mouse) != 0)
1384 d->reset();
1385 event->accept();
1386}
1387
1388// --- QAbstractSpinBoxPrivate ---
1389
1390/*!
1391 \internal
1392 Constructs a QAbstractSpinBoxPrivate object
1393*/
1394
1395QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
1396 : pendingEmit(false), readOnly(false), wrapping(false),
1397 ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
1398 cleared(false), ignoreUpdateEdit(false), showGroupSeparator(false)
1399{
1400}
1401
1402/*
1403 \internal
1404 Called when the QAbstractSpinBoxPrivate is destroyed
1405*/
1406QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
1407{
1408}
1409
1410/*!
1411 \internal
1412 Updates the old and new hover control. Does nothing if the hover
1413 control has not changed.
1414*/
1415bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos)
1416{
1417 Q_Q(QAbstractSpinBox);
1418 QRect lastHoverRect = hoverRect;
1419 QStyle::SubControl lastHoverControl = hoverControl;
1420 bool doesHover = q->testAttribute(attribute: Qt::WA_Hover);
1421 if (lastHoverControl != newHoverControl(pos) && doesHover) {
1422 q->update(lastHoverRect);
1423 q->update(hoverRect);
1424 return true;
1425 }
1426 return !doesHover;
1427}
1428
1429/*!
1430 \internal
1431 Returns the hover control at \a pos.
1432 This will update the hoverRect and hoverControl.
1433*/
1434QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos)
1435{
1436 Q_Q(QAbstractSpinBox);
1437
1438 QStyleOptionSpinBox opt;
1439 q->initStyleOption(option: &opt);
1440 opt.subControls = QStyle::SC_All;
1441 hoverControl = q->style()->hitTestComplexControl(cc: QStyle::CC_SpinBox, opt: &opt, pt: pos, widget: q);
1442 hoverRect = q->style()->subControlRect(cc: QStyle::CC_SpinBox, opt: &opt, sc: hoverControl, widget: q);
1443 return hoverControl;
1444}
1445
1446/*!
1447 \internal
1448 Strips any prefix/suffix from \a text.
1449*/
1450
1451QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
1452{
1453 QStringView text(t);
1454 if (specialValueText.size() == 0 || text != specialValueText) {
1455 int from = 0;
1456 int size = text.size();
1457 bool changed = false;
1458 if (prefix.size() && text.startsWith(s: prefix)) {
1459 from += prefix.size();
1460 size -= from;
1461 changed = true;
1462 }
1463 if (suffix.size() && text.endsWith(s: suffix)) {
1464 size -= suffix.size();
1465 changed = true;
1466 }
1467 if (changed)
1468 text = text.mid(pos: from, n: size);
1469 }
1470
1471 const int s = text.size();
1472 text = text.trimmed();
1473 if (pos)
1474 (*pos) -= (s - text.size());
1475 return text.toString();
1476
1477}
1478
1479void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
1480{
1481 Q_Q(QAbstractSpinBox);
1482 QStyleOptionSpinBox opt;
1483 q->initStyleOption(option: &opt);
1484 opt.subControls = QStyle::SC_SpinBoxEditField;
1485 edit->setGeometry(q->style()->subControlRect(cc: QStyle::CC_SpinBox, opt: &opt,
1486 sc: QStyle::SC_SpinBoxEditField, widget: q));
1487}
1488/*!
1489 \internal
1490 Returns \c true if a specialValueText has been set and the current value is minimum.
1491*/
1492
1493bool QAbstractSpinBoxPrivate::specialValue() const
1494{
1495 return (value == minimum && !specialValueText.isEmpty());
1496}
1497
1498/*!
1499 \internal Virtual function that emits signals when the value
1500 changes. Reimplemented in the different subclasses.
1501*/
1502
1503void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
1504{
1505}
1506
1507/*!
1508 \internal
1509
1510 Slot connected to the line edit's textChanged(const QString &)
1511 signal.
1512*/
1513
1514void QAbstractSpinBoxPrivate::editorTextChanged(const QString &t)
1515{
1516 Q_Q(QAbstractSpinBox);
1517
1518 if (keyboardTracking) {
1519 QString tmp = t;
1520 int pos = edit->cursorPosition();
1521 QValidator::State state = q->validate(tmp, pos);
1522 if (state == QValidator::Acceptable) {
1523 const QVariant v = valueFromText(input: tmp);
1524 setValue(val: v, ep: EmitIfChanged, updateEdit: tmp != t);
1525 pendingEmit = false;
1526 } else {
1527 pendingEmit = true;
1528 }
1529 } else {
1530 pendingEmit = true;
1531 }
1532}
1533
1534/*!
1535 \internal
1536
1537 Virtual slot connected to the line edit's
1538 cursorPositionChanged(int, int) signal. Will move the cursor to a
1539 valid position if the new one is invalid. E.g. inside the prefix.
1540 Reimplemented in Q[Date|Time|DateTime]EditPrivate to account for
1541 the different sections etc.
1542*/
1543
1544void QAbstractSpinBoxPrivate::editorCursorPositionChanged(int oldpos, int newpos)
1545{
1546 if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
1547 ignoreCursorPositionChanged = true;
1548
1549 bool allowSelection = true;
1550 int pos = -1;
1551 if (newpos < prefix.size() && newpos != 0) {
1552 if (oldpos == 0) {
1553 allowSelection = false;
1554 pos = prefix.size();
1555 } else {
1556 pos = oldpos;
1557 }
1558 } else if (newpos > edit->text().size() - suffix.size()
1559 && newpos != edit->text().size()) {
1560 if (oldpos == edit->text().size()) {
1561 pos = edit->text().size() - suffix.size();
1562 allowSelection = false;
1563 } else {
1564 pos = edit->text().size();
1565 }
1566 }
1567 if (pos != -1) {
1568 const int selSize = edit->selectionStart() >= 0 && allowSelection
1569 ? (edit->selectedText().size()
1570 * (newpos < pos ? -1 : 1)) - newpos + pos
1571 : 0;
1572
1573 const QSignalBlocker blocker(edit);
1574 if (selSize != 0) {
1575 edit->setSelection(pos - selSize, selSize);
1576 } else {
1577 edit->setCursorPosition(pos);
1578 }
1579 }
1580 ignoreCursorPositionChanged = false;
1581 }
1582}
1583
1584/*!
1585 \internal
1586
1587 Initialises the QAbstractSpinBoxPrivate object.
1588*/
1589
1590void QAbstractSpinBoxPrivate::init()
1591{
1592 Q_Q(QAbstractSpinBox);
1593
1594 q->setLineEdit(new QLineEdit(q));
1595 edit->setObjectName("qt_spinbox_lineedit"_L1);
1596 validator = new QSpinBoxValidator(q, this);
1597 edit->setValidator(validator);
1598
1599 QStyleOptionSpinBox opt;
1600 // ### This is called from the ctor and thus we shouldn't call initStyleOption yet
1601 // ### as we only call the base class implementation of initStyleOption called.
1602 q->initStyleOption(option: &opt);
1603 spinClickTimerInterval = q->style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatRate, opt: &opt, widget: q);
1604 spinClickThresholdTimerInterval = q->style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatThreshold, opt: &opt, widget: q);
1605 q->setFocusPolicy(Qt::WheelFocus);
1606 q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
1607 q->setAttribute(Qt::WA_InputMethodEnabled);
1608
1609 q->setAttribute(Qt::WA_MacShowFocusRect);
1610}
1611
1612/*!
1613 \internal
1614
1615 Resets the state of the spinbox. E.g. the state is set to
1616 (Keyboard|Up) if Key up is currently pressed.
1617*/
1618
1619void QAbstractSpinBoxPrivate::reset()
1620{
1621 Q_Q(QAbstractSpinBox);
1622
1623 buttonState = None;
1624 if (q) {
1625 if (spinClickTimerId != -1)
1626 q->killTimer(id: spinClickTimerId);
1627 if (spinClickThresholdTimerId != -1)
1628 q->killTimer(id: spinClickThresholdTimerId);
1629 spinClickTimerId = spinClickThresholdTimerId = -1;
1630 acceleration = 0;
1631 q->update();
1632 }
1633}
1634
1635/*!
1636 \internal
1637
1638 Updates the state of the spinbox.
1639*/
1640
1641void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false */)
1642{
1643 Q_Q(QAbstractSpinBox);
1644 if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
1645 return;
1646 reset();
1647 if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
1648 : QAbstractSpinBox::StepDownEnabled))) {
1649 buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
1650 int steps = up ? 1 : -1;
1651 if (keyboardModifiers & stepModifier)
1652 steps *= 10;
1653 q->stepBy(steps);
1654 spinClickThresholdTimerId = q->startTimer(interval: spinClickThresholdTimerInterval);
1655#if QT_CONFIG(accessibility)
1656 QAccessibleValueChangeEvent event(q, value);
1657 QAccessible::updateAccessibility(event: &event);
1658#endif
1659 }
1660}
1661
1662
1663/*!
1664 Initialize \a option with the values from this QSpinBox. This method
1665 is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
1666 to fill in all the information themselves.
1667
1668 \sa QStyleOption::initFrom()
1669*/
1670void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const
1671{
1672 if (!option)
1673 return;
1674
1675 Q_D(const QAbstractSpinBox);
1676 option->initFrom(w: this);
1677 option->activeSubControls = QStyle::SC_None;
1678 option->buttonSymbols = d->buttonSymbols;
1679 option->subControls = QStyle::SC_SpinBoxEditField;
1680 if (style()->styleHint(stylehint: QStyle::SH_SpinBox_ButtonsInsideFrame, opt: nullptr, widget: this))
1681 option->subControls |= QStyle::SC_SpinBoxFrame;
1682 if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
1683 option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
1684 if (d->buttonState & Up) {
1685 option->activeSubControls = QStyle::SC_SpinBoxUp;
1686 } else if (d->buttonState & Down) {
1687 option->activeSubControls = QStyle::SC_SpinBoxDown;
1688 }
1689 }
1690
1691 if (d->buttonState) {
1692 option->state |= QStyle::State_Sunken;
1693 } else {
1694 option->activeSubControls = d->hoverControl;
1695 }
1696
1697 option->stepEnabled = style()->styleHint(stylehint: QStyle::SH_SpinControls_DisableOnBounds, opt: nullptr, widget: this)
1698 ? stepEnabled()
1699 : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
1700
1701 option->frame = d->frame;
1702}
1703
1704/*!
1705 \internal
1706
1707 Bounds \a val to be within minimum and maximum. Also tries to be
1708 clever about setting it at min and max depending on what it was
1709 and what direction it was changed etc.
1710*/
1711
1712QVariant QAbstractSpinBoxPrivate::bound(const QVariant &val, const QVariant &old, int steps) const
1713{
1714 QVariant v = val;
1715 if (!wrapping || steps == 0 || old.isNull()) {
1716 if (variantCompare(arg1: v, arg2: minimum) < 0) {
1717 v = wrapping ? maximum : minimum;
1718 }
1719 if (variantCompare(arg1: v, arg2: maximum) > 0) {
1720 v = wrapping ? minimum : maximum;
1721 }
1722 } else {
1723 const bool wasMin = old == minimum;
1724 const bool wasMax = old == maximum;
1725 const int oldcmp = variantCompare(arg1: v, arg2: old);
1726 const int maxcmp = variantCompare(arg1: v, arg2: maximum);
1727 const int mincmp = variantCompare(arg1: v, arg2: minimum);
1728 const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
1729 if (maxcmp > 0) {
1730 v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
1731 ? minimum : maximum;
1732 } else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
1733 v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
1734 } else if (mincmp < 0) {
1735 v = (!wasMax && !wasMin ? minimum : maximum);
1736 }
1737 }
1738
1739 return v;
1740}
1741
1742/*!
1743 \internal
1744
1745 Sets the value of the spin box to \a val. Depending on the value
1746 of \a ep it will also emit signals.
1747*/
1748
1749void QAbstractSpinBoxPrivate::setValue(const QVariant &val, EmitPolicy ep,
1750 bool doUpdate)
1751{
1752 Q_Q(QAbstractSpinBox);
1753 const QVariant old = value;
1754 value = bound(val);
1755 pendingEmit = false;
1756 cleared = false;
1757 if (doUpdate) {
1758 updateEdit();
1759 }
1760 q->update();
1761
1762 if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
1763 emitSignals(ep, old);
1764 }
1765}
1766
1767/*!
1768 \internal
1769
1770 Updates the line edit to reflect the current value of the spin box.
1771*/
1772
1773void QAbstractSpinBoxPrivate::updateEdit()
1774{
1775 Q_Q(QAbstractSpinBox);
1776 if (type == QMetaType::UnknownType)
1777 return;
1778 const QString newText = specialValue() ? specialValueText : prefix + textFromValue(n: value) + suffix;
1779 if (newText == edit->displayText() || cleared)
1780 return;
1781
1782 const bool empty = edit->text().isEmpty();
1783 int cursor = edit->cursorPosition();
1784 int selsize = edit->selectedText().size();
1785 const QSignalBlocker blocker(edit);
1786 edit->setText(newText);
1787
1788 if (!specialValue()) {
1789 cursor = qBound(min: prefix.size(), val: cursor, max: edit->displayText().size() - suffix.size());
1790
1791 if (selsize > 0) {
1792 edit->setSelection(cursor, selsize);
1793 } else {
1794 edit->setCursorPosition(empty ? prefix.size() : cursor);
1795 }
1796 }
1797 q->update();
1798}
1799
1800/*!
1801 \internal
1802
1803 Convenience function to set min/max values.
1804*/
1805
1806void QAbstractSpinBoxPrivate::setRange(const QVariant &min, const QVariant &max)
1807{
1808 Q_Q(QAbstractSpinBox);
1809
1810 clearCache();
1811 minimum = min;
1812 maximum = (variantCompare(arg1: min, arg2: max) < 0 ? max : min);
1813 cachedSizeHint = QSize();
1814 cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about min/max
1815
1816 reset();
1817 if (!(bound(val: value) == value)) {
1818 setValue(val: bound(val: value), ep: EmitIfChanged);
1819 } else if (value == minimum && !specialValueText.isEmpty()) {
1820 updateEdit();
1821 }
1822
1823 q->updateGeometry();
1824}
1825
1826/*!
1827 \internal
1828
1829 Convenience function to get a variant of the right type.
1830*/
1831
1832QVariant QAbstractSpinBoxPrivate::getZeroVariant() const
1833{
1834 QVariant ret;
1835 switch (type) {
1836 case QMetaType::Int: ret = QVariant(0); break;
1837 case QMetaType::Double: ret = QVariant(0.0); break;
1838 default: break;
1839 }
1840 return ret;
1841}
1842
1843/*!
1844 \internal
1845
1846 Virtual method called that calls the public textFromValue()
1847 functions in the subclasses. Needed to change signature from
1848 QVariant to int/double/QDateTime etc. Used when needing to display
1849 a value textually.
1850
1851 This method is reimeplemented in the various subclasses.
1852*/
1853
1854QString QAbstractSpinBoxPrivate::textFromValue(const QVariant &) const
1855{
1856 return QString();
1857}
1858
1859/*!
1860 \internal
1861
1862 Virtual method called that calls the public valueFromText()
1863 functions in the subclasses. Needed to change signature from
1864 QVariant to int/double/QDateTime etc. Used when needing to
1865 interpret a string as another type.
1866
1867 This method is reimeplemented in the various subclasses.
1868*/
1869
1870QVariant QAbstractSpinBoxPrivate::valueFromText(const QString &) const
1871{
1872 return QVariant();
1873}
1874/*!
1875 \internal
1876
1877 Interprets text and emits signals. Called when the spinbox needs
1878 to interpret the text on the lineedit.
1879*/
1880
1881void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
1882{
1883 Q_Q(QAbstractSpinBox);
1884 if (type == QMetaType::UnknownType || cleared)
1885 return;
1886
1887 QVariant v = getZeroVariant();
1888 bool doInterpret = true;
1889 QString tmp = edit->displayText();
1890 int pos = edit->cursorPosition();
1891 const int oldpos = pos;
1892
1893 if (q->validate(tmp, pos) != QValidator::Acceptable) {
1894 const QString copy = tmp;
1895 q->fixup(tmp);
1896 QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '"
1897 << edit->displayText()
1898 << "' >> '" << copy << '\''
1899 << "' >> '" << tmp << '\'';
1900
1901 doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
1902 if (!doInterpret) {
1903 v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
1904 ? variantBound(min: minimum, value: v, max: maximum) : value);
1905 }
1906 }
1907 if (doInterpret) {
1908 v = valueFromText(tmp);
1909 }
1910 clearCache();
1911 setValue(val: v, ep, doUpdate: true);
1912 if (oldpos != pos)
1913 edit->setCursorPosition(pos);
1914}
1915
1916void QAbstractSpinBoxPrivate::clearCache() const
1917{
1918 cachedText.clear();
1919 cachedValue.clear();
1920 cachedState = QValidator::Acceptable;
1921}
1922
1923QVariant QAbstractSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
1924{
1925 Q_UNUSED(steps);
1926 return singleStep;
1927}
1928
1929// --- QSpinBoxValidator ---
1930
1931/*!
1932 \internal
1933 Constructs a QSpinBoxValidator object
1934*/
1935
1936QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
1937 : QValidator(qp), qptr(qp), dptr(dp)
1938{
1939 setObjectName("qt_spinboxvalidator"_L1);
1940}
1941
1942/*!
1943 \internal
1944
1945 Checks for specialValueText, prefix, suffix and calls
1946 the virtual QAbstractSpinBox::validate function.
1947*/
1948
1949QValidator::State QSpinBoxValidator::validate(QString &input, int &pos) const
1950{
1951 if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
1952 return QValidator::Acceptable;
1953
1954 if (!dptr->prefix.isEmpty() && !input.startsWith(s: dptr->prefix)) {
1955 input.prepend(s: dptr->prefix);
1956 pos += dptr->prefix.size();
1957 }
1958
1959 if (!dptr->suffix.isEmpty() && !input.endsWith(s: dptr->suffix))
1960 input.append(s: dptr->suffix);
1961
1962 return qptr->validate(input, pos);
1963}
1964/*!
1965 \internal
1966 Calls the virtual QAbstractSpinBox::fixup function.
1967*/
1968
1969void QSpinBoxValidator::fixup(QString &input) const
1970{
1971 qptr->fixup(input);
1972}
1973
1974// --- global ---
1975
1976/*!
1977 \internal
1978 Adds two variants together and returns the result.
1979*/
1980
1981QVariant operator+(const QVariant &arg1, const QVariant &arg2)
1982{
1983 QVariant ret;
1984 if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
1985 qWarning(msg: "QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
1986 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
1987 switch (arg1.userType()) {
1988 case QMetaType::Int: {
1989 const int int1 = arg1.toInt();
1990 const int int2 = arg2.toInt();
1991 if (int1 > 0 && (int2 >= INT_MAX - int1)) {
1992 // The increment overflows
1993 ret = QVariant(INT_MAX);
1994 } else if (int1 < 0 && (int2 <= INT_MIN - int1)) {
1995 // The increment underflows
1996 ret = QVariant(INT_MIN);
1997 } else {
1998 ret = QVariant(int1 + int2);
1999 }
2000 break;
2001 }
2002 case QMetaType::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble()); break;
2003#if QT_CONFIG(datetimeparser)
2004 case QMetaType::QDateTime: {
2005 QDateTime a2 = arg2.toDateTime();
2006 QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATE_MIN.daysTo(d: a2.date()));
2007 a1.setTime(time: a1.time().addMSecs(ms: a2.time().msecsSinceStartOfDay()));
2008 ret = QVariant(a1);
2009 break;
2010 }
2011#endif // datetimeparser
2012 default: break;
2013 }
2014 return ret;
2015}
2016
2017
2018/*!
2019 \internal
2020 Subtracts two variants and returns the result.
2021*/
2022
2023QVariant operator-(const QVariant &arg1, const QVariant &arg2)
2024{
2025 QVariant ret;
2026 if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
2027 qWarning(msg: "QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
2028 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
2029 switch (arg1.userType()) {
2030 case QMetaType::Int: ret = QVariant(arg1.toInt() - arg2.toInt()); break;
2031 case QMetaType::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble()); break;
2032 case QMetaType::QDateTime: {
2033 QDateTime a1 = arg1.toDateTime();
2034 QDateTime a2 = arg2.toDateTime();
2035 int days = a2.daysTo(a1);
2036 int secs = a2.secsTo(a1);
2037 int msecs = qMax(a: 0, b: a1.time().msec() - a2.time().msec());
2038 if (days < 0 || secs < 0 || msecs < 0) {
2039 ret = arg1;
2040 } else {
2041 QDateTime dt = a2.addDays(days).addSecs(secs);
2042 if (msecs > 0)
2043 dt.setTime(time: dt.time().addMSecs(ms: msecs));
2044 ret = QVariant(dt);
2045 }
2046 break;
2047 }
2048 default: break;
2049 }
2050 return ret;
2051}
2052
2053/*!
2054 \internal
2055 Multiplies \a arg1 by \a multiplier and returns the result.
2056*/
2057
2058QVariant operator*(const QVariant &arg1, double multiplier)
2059{
2060 QVariant ret;
2061
2062 switch (arg1.userType()) {
2063 case QMetaType::Int:
2064 ret = static_cast<int>(qBound<double>(INT_MIN, val: arg1.toInt() * multiplier, INT_MAX));
2065 break;
2066 case QMetaType::Double: ret = QVariant(arg1.toDouble() * multiplier); break;
2067#if QT_CONFIG(datetimeparser)
2068 case QMetaType::QDateTime: {
2069 double days = QDATETIMEEDIT_DATE_MIN.daysTo(d: arg1.toDateTime().date()) * multiplier;
2070 const qint64 daysInt = qint64(days);
2071 days -= daysInt;
2072 qint64 msecs = qint64(arg1.toDateTime().time().msecsSinceStartOfDay() * multiplier
2073 + days * (24 * 3600 * 1000));
2074 ret = QDATETIMEEDIT_DATE_MIN.addDays(days: daysInt).startOfDay().addMSecs(msecs);
2075 break;
2076 }
2077#endif // datetimeparser
2078 default: ret = arg1; break;
2079 }
2080
2081 return ret;
2082}
2083
2084
2085
2086double operator/(const QVariant &arg1, const QVariant &arg2)
2087{
2088 double a1 = 0;
2089 double a2 = 0;
2090
2091 switch (arg1.userType()) {
2092 case QMetaType::Int:
2093 a1 = (double)arg1.toInt();
2094 a2 = (double)arg2.toInt();
2095 break;
2096 case QMetaType::Double:
2097 a1 = arg1.toDouble();
2098 a2 = arg2.toDouble();
2099 break;
2100#if QT_CONFIG(datetimeparser)
2101 case QMetaType::QDateTime:
2102 a1 = QDATETIMEEDIT_DATE_MIN.daysTo(d: arg1.toDate());
2103 a2 = QDATETIMEEDIT_DATE_MIN.daysTo(d: arg2.toDate());
2104 a1 += arg1.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2105 a2 += arg2.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2106 break;
2107#endif // datetimeparser
2108 default: break;
2109 }
2110
2111 return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
2112}
2113
2114int QAbstractSpinBoxPrivate::variantCompare(const QVariant &arg1, const QVariant &arg2)
2115{
2116 switch (arg2.userType()) {
2117 case QMetaType::QDate:
2118 Q_ASSERT_X(arg1.userType() == QMetaType::QDate, "QAbstractSpinBoxPrivate::variantCompare",
2119 qPrintable(QString::fromLatin1("Internal error 1 (%1)").
2120 arg(QString::fromLatin1(arg1.typeName()))));
2121 if (arg1.toDate() == arg2.toDate()) {
2122 return 0;
2123 } else if (arg1.toDate() < arg2.toDate()) {
2124 return -1;
2125 } else {
2126 return 1;
2127 }
2128 case QMetaType::QTime:
2129 Q_ASSERT_X(arg1.userType() == QMetaType::QTime, "QAbstractSpinBoxPrivate::variantCompare",
2130 qPrintable(QString::fromLatin1("Internal error 2 (%1)").
2131 arg(QString::fromLatin1(arg1.typeName()))));
2132 if (arg1.toTime() == arg2.toTime()) {
2133 return 0;
2134 } else if (arg1.toTime() < arg2.toTime()) {
2135 return -1;
2136 } else {
2137 return 1;
2138 }
2139
2140
2141 case QMetaType::QDateTime:
2142 if (arg1.toDateTime() == arg2.toDateTime()) {
2143 return 0;
2144 } else if (arg1.toDateTime() < arg2.toDateTime()) {
2145 return -1;
2146 } else {
2147 return 1;
2148 }
2149 case QMetaType::Int:
2150 if (arg1.toInt() == arg2.toInt()) {
2151 return 0;
2152 } else if (arg1.toInt() < arg2.toInt()) {
2153 return -1;
2154 } else {
2155 return 1;
2156 }
2157 case QMetaType::Double:
2158 if (arg1.toDouble() == arg2.toDouble()) {
2159 return 0;
2160 } else if (arg1.toDouble() < arg2.toDouble()) {
2161 return -1;
2162 } else {
2163 return 1;
2164 }
2165 case QMetaType::UnknownType:
2166 if (arg2.userType() == QMetaType::UnknownType)
2167 return 0;
2168 Q_FALLTHROUGH();
2169 default:
2170 Q_ASSERT_X(0, "QAbstractSpinBoxPrivate::variantCompare",
2171 qPrintable(QString::fromLatin1("Internal error 3 (%1 %2)").
2172 arg(QString::fromLatin1(arg1.typeName()),
2173 QString::fromLatin1(arg2.typeName()))));
2174 }
2175 return -2;
2176}
2177
2178QVariant QAbstractSpinBoxPrivate::variantBound(const QVariant &min,
2179 const QVariant &value,
2180 const QVariant &max)
2181{
2182 Q_ASSERT(variantCompare(min, max) <= 0);
2183 if (variantCompare(arg1: min, arg2: value) < 0) {
2184 const int compMax = variantCompare(arg1: value, arg2: max);
2185 return (compMax < 0 ? value : max);
2186 } else {
2187 return min;
2188 }
2189}
2190
2191
2192QT_END_NAMESPACE
2193
2194#include "moc_qabstractspinbox.cpp"
2195

source code of qtbase/src/widgets/widgets/qabstractspinbox.cpp