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

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