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