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