5#include <qplatformdefs.h>
7# include <private/qstdweb_p.h>
9#include <private/qabstractspinbox_p.h>
10#include <private/qapplication_p.h>
11#if QT_CONFIG(datetimeparser)
12#include <private/qdatetimeparser_p.h>
14#include <private/qlineedit_p.h>
15#include <qabstractspinbox.h>
17#include <qapplication.h>
18#include <qstylehints.h>
19#include <qclipboard.h>
22#include <qloggingcategory.h>
26#include <qstylepainter.h>
27#if QT_CONFIG(accessibility)
28# include <qaccessible.h>
31#include <QtCore/qpointer.h>
35using namespace Qt::StringLiterals;
36using namespace std::chrono_literals;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
88
89
90
91
92
93
96
97
98
99
100
103
104
105
106
107
110
111
112
113
114
117
118
119
121QAbstractSpinBox::QAbstractSpinBox(QWidget *parent)
122 : QWidget(*
new QAbstractSpinBoxPrivate, parent, { })
124 Q_D(QAbstractSpinBox);
129
130
131QAbstractSpinBox::QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent)
132 : QWidget(dd, parent, { })
134 Q_D(QAbstractSpinBox);
139
140
142QAbstractSpinBox::~QAbstractSpinBox()
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
165
166
167
168
169
170
171
172
173
174
175
176
178QAbstractSpinBox::ButtonSymbols QAbstractSpinBox::buttonSymbols()
const
180 Q_D(
const QAbstractSpinBox);
181 return d->buttonSymbols;
184void QAbstractSpinBox::setButtonSymbols(ButtonSymbols buttonSymbols)
186 Q_D(QAbstractSpinBox);
187 if (d->buttonSymbols != buttonSymbols) {
188 d->buttonSymbols = buttonSymbols;
189 d->updateEditFieldGeometry();
196
197
198
199
200
201
203QString QAbstractSpinBox::text()
const
205 return lineEdit()->displayText();
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
242QString QAbstractSpinBox::specialValueText()
const
244 Q_D(
const QAbstractSpinBox);
245 return d->specialValueText;
248void QAbstractSpinBox::setSpecialValueText(
const QString &specialValueText)
250 Q_D(QAbstractSpinBox);
252 d->specialValueText = specialValueText;
253 d->cachedSizeHint = QSize();
259
260
261
262
263
264
265
266
267
268
269
270
272bool QAbstractSpinBox::wrapping()
const
274 Q_D(
const QAbstractSpinBox);
278void QAbstractSpinBox::setWrapping(
bool wrapping)
280 Q_D(QAbstractSpinBox);
281 d->wrapping = wrapping;
286
287
288
289
290
291
292
293
294
295
296
297
299bool QAbstractSpinBox::isReadOnly()
const
301 Q_D(
const QAbstractSpinBox);
305void QAbstractSpinBox::setReadOnly(
bool enable)
307 Q_D(QAbstractSpinBox);
308 d->readOnly = enable;
309 d->edit->setReadOnly(enable);
310 QEvent event(QEvent::ReadOnlyChange);
311 QCoreApplication::sendEvent(
this, &event);
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
335bool QAbstractSpinBox::keyboardTracking()
const
337 Q_D(
const QAbstractSpinBox);
338 return d->keyboardTracking;
341void QAbstractSpinBox::setKeyboardTracking(
bool enable)
343 Q_D(QAbstractSpinBox);
344 d->keyboardTracking = enable;
348
349
350
351
352
353
355bool QAbstractSpinBox::hasFrame()
const
357 Q_D(
const QAbstractSpinBox);
362void QAbstractSpinBox::setFrame(
bool enable)
364 Q_D(QAbstractSpinBox);
367 d->updateEditFieldGeometry();
371
372
373
374
375
376
377
378
380void QAbstractSpinBox::setAccelerated(
bool accelerate)
382 Q_D(QAbstractSpinBox);
383 d->accelerate = accelerate;
386bool QAbstractSpinBox::isAccelerated()
const
388 Q_D(
const QAbstractSpinBox);
389 return d->accelerate;
393
394
395
396
397
398
399
400bool QAbstractSpinBox::isGroupSeparatorShown()
const
402 Q_D(
const QAbstractSpinBox);
403 return d->showGroupSeparator;
406void QAbstractSpinBox::setGroupSeparatorShown(
bool shown)
408 Q_D(QAbstractSpinBox);
409 if (d->showGroupSeparator == shown)
411 d->showGroupSeparator = shown;
412 d->setValue(d->value, EmitIfChanged);
417
418
419
420
421
422
423
424
425
426
427
428
429
432
433
434
435
436
437
438
439
440
441void QAbstractSpinBox::setCorrectionMode(CorrectionMode correctionMode)
443 Q_D(QAbstractSpinBox);
444 d->correctionMode = correctionMode;
447QAbstractSpinBox::CorrectionMode QAbstractSpinBox::correctionMode()
const
449 Q_D(
const QAbstractSpinBox);
450 return d->correctionMode;
455
456
457
458
459
460
462bool QAbstractSpinBox::hasAcceptableInput()
const
464 Q_D(
const QAbstractSpinBox);
465 return d->edit->hasAcceptableInput();
469
470
471
472
473
474
475
476
477
478
479
480
482Qt::Alignment QAbstractSpinBox::alignment()
const
484 Q_D(
const QAbstractSpinBox);
486 return (Qt::Alignment)d->edit->alignment();
489void QAbstractSpinBox::setAlignment(Qt::Alignment flag)
491 Q_D(QAbstractSpinBox);
493 d->edit->setAlignment(flag);
497
498
500void QAbstractSpinBox::selectAll()
502 Q_D(QAbstractSpinBox);
505 if (!d->specialValue()) {
506 const int tmp = d->edit->displayText().size() - d->suffix.size();
507 d->edit->setSelection(tmp, -(tmp - d->prefix.size()));
509 d->edit->selectAll();
514
515
517void QAbstractSpinBox::clear()
519 Q_D(QAbstractSpinBox);
521 d->edit->setText(d->prefix + d->suffix);
522 d->edit->setCursorPosition(d->prefix.size());
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
544QAbstractSpinBox::StepEnabled QAbstractSpinBox::stepEnabled()
const
546 Q_D(
const QAbstractSpinBox);
547 if (d->readOnly || d->type == QMetaType::UnknownType)
550 return StepEnabled(StepUpEnabled | StepDownEnabled);
551 StepEnabled ret = StepNone;
552 if (QAbstractSpinBoxPrivate::variantCompare(d->value, d->maximum) < 0) {
553 ret |= StepUpEnabled;
555 if (QAbstractSpinBoxPrivate::variantCompare(d->value, d->minimum) > 0) {
556 ret |= StepDownEnabled;
562
563
564
565
566
568QValidator::State QAbstractSpinBox::validate(QString & ,
int & )
const
570 return QValidator::Acceptable;
574
575
576
577
578
580void QAbstractSpinBox::fixup(QString & )
const
585
586
587
588
590void QAbstractSpinBox::stepUp()
596
597
598
599
601void QAbstractSpinBox::stepDown()
606
607
608
609
610
611
612
613
614
615
616
617
619void QAbstractSpinBox::stepBy(
int steps)
621 Q_D(QAbstractSpinBox);
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;
631 d->interpret(NeverEmit);
637 switch (d->stepType) {
638 case QAbstractSpinBox::StepType::AdaptiveDecimalStepType:
639 singleStep = d->calculateAdaptiveDecimalStep(steps);
642 singleStep = d->singleStep;
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);
649 if (style()->styleHint(QStyle::SH_SpinBox_SelectOnStep,
nullptr,
this,
nullptr))
654
655
657QLineEdit *QAbstractSpinBox::lineEdit()
const
659 Q_D(
const QAbstractSpinBox);
666
667
668
669
670
671
672
673
674
675
677void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
679 Q_D(QAbstractSpinBox);
686 if (lineEdit == d->edit)
691 setProperty(
"_q_spinbox_lineedit", QVariant::fromValue<QWidget *>(d->edit));
692 if (!d->edit->validator())
693 d->edit->setValidator(d->validator);
695 if (d->edit->parent() !=
this)
696 d->edit->setParent(
this);
698 d->edit->setFrame(!style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame,
nullptr,
this));
699 d->edit->setFocusProxy(
this);
700 d->edit->setAcceptDrops(
false);
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(); });
712 d->updateEditFieldGeometry();
713 d->edit->setContextMenuPolicy(Qt::NoContextMenu);
714 d->edit->d_func()->control->setAccessibleObject(
this);
724
725
726
728void QAbstractSpinBox::interpretText()
730 Q_D(QAbstractSpinBox);
731 d->interpret(EmitIfChanged);
735
736
738
739
740QVariant QAbstractSpinBox::inputMethodQuery(Qt::InputMethodQuery query)
const
742 Q_D(
const QAbstractSpinBox);
743 const QVariant lineEditValue = d->edit->inputMethodQuery(query);
746 if (
const int hints = inputMethodHints())
747 return QVariant(hints | lineEditValue.toInt());
752 return lineEditValue;
756
757
759bool QAbstractSpinBox::event(QEvent *event)
761 Q_D(QAbstractSpinBox);
762 switch (event->type()) {
763 case QEvent::FontChange:
764 case QEvent::StyleChange:
765 d->cachedSizeHint = d->cachedMinimumSizeHint = QSize();
767 case QEvent::ApplicationLayoutDirectionChange:
768 case QEvent::LayoutDirectionChange:
769 d->updateEditFieldGeometry();
771 case QEvent::HoverEnter:
772 case QEvent::HoverLeave:
773 case QEvent::HoverMove:
774 d->updateHoverControl(
static_cast<
const QHoverEvent *>(event)->position().toPoint());
776 case QEvent::ShortcutOverride:
777 if (d->edit->event(event))
780 case QEvent::InputMethod:
781 return d->edit->event(event);
785 return QWidget::event(event);
789
790
792void QAbstractSpinBox::showEvent(QShowEvent *)
794 Q_D(QAbstractSpinBox);
797 if (d->ignoreUpdateEdit) {
798 d->ignoreUpdateEdit =
false;
805
806
808void QAbstractSpinBox::changeEvent(QEvent *event)
810 Q_D(QAbstractSpinBox);
812 switch (event->type()) {
813 case QEvent::StyleChange:
814 d->spinClickTimerInterval = style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate,
nullptr,
this);
815 d->spinClickThresholdTimerInterval =
816 style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold,
nullptr,
this);
818 d->edit->setFrame(!style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame,
nullptr,
this));
819 d->stepModifier =
static_cast<Qt::KeyboardModifier>(style()->styleHint(QStyle::SH_SpinBox_StepModifier,
nullptr,
this));
821 d->updateEditFieldGeometry();
823 case QEvent::LocaleChange:
826 case QEvent::EnabledChange:
831 case QEvent::ActivationChange:
832 if (!isActiveWindow()){
835 d->interpret(EmitIfChanged);
841 QWidget::changeEvent(event);
845
846
848void QAbstractSpinBox::resizeEvent(QResizeEvent *event)
850 Q_D(QAbstractSpinBox);
851 QWidget::resizeEvent(event);
853 d->updateEditFieldGeometry();
858
859
861QSize QAbstractSpinBox::sizeHint()
const
863 Q_D(
const QAbstractSpinBox);
864 if (d->cachedSizeHint.isEmpty()) {
867 const QFontMetrics fm(fontMetrics());
868 int h = d->edit->sizeHint().height();
871 QString fixedContent = d->prefix + d->suffix + u' ';
872 s = d->textFromValue(d->minimum);
875 w = qMax(w, fm.horizontalAdvance(s));
876 s = d->textFromValue(d->maximum);
879 w = qMax(w, fm.horizontalAdvance(s));
881 if (d->specialValueText.size()) {
882 s = d->specialValueText;
883 w = qMax(w, fm.horizontalAdvance(s));
887 QStyleOptionSpinBox opt;
888 initStyleOption(&opt);
890 d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint,
this);
892 return d->cachedSizeHint;
896
897
899QSize QAbstractSpinBox::minimumSizeHint()
const
901 Q_D(
const QAbstractSpinBox);
902 if (d->cachedMinimumSizeHint.isEmpty()) {
906 const QFontMetrics fm(fontMetrics());
907 int h = d->edit->minimumSizeHint().height();
911 QString fixedContent = d->prefix + u' ';
912 s = d->textFromValue(d->minimum);
915 w = qMax(w, fm.horizontalAdvance(s));
916 s = d->textFromValue(d->maximum);
919 w = qMax(w, fm.horizontalAdvance(s));
921 if (d->specialValueText.size()) {
922 s = d->specialValueText;
923 w = qMax(w, fm.horizontalAdvance(s));
927 QStyleOptionSpinBox opt;
928 initStyleOption(&opt);
931 d->cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint,
this);
933 return d->cachedMinimumSizeHint;
937
938
940void QAbstractSpinBox::paintEvent(QPaintEvent *)
942 QStyleOptionSpinBox opt;
943 initStyleOption(&opt);
944 QStylePainter p(
this);
945 p.drawComplexControl(QStyle::CC_SpinBox, opt);
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
972void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
974 Q_D(QAbstractSpinBox);
976 d->keyboardModifiers = event->modifiers();
978 if (!event->text().isEmpty() && d->edit->cursorPosition() < d->prefix.size())
979 d->edit->setCursorPosition(d->prefix.size());
982 bool isPgUpOrDown =
false;
983 switch (event->key()) {
985 case Qt::Key_PageDown:
992 const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
993 if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
995 if (!isPgUpOrDown && (event->modifiers() & d->stepModifier))
999 if (style()->styleHint(QStyle::SH_SpinBox_AnimateButton,
nullptr,
this)) {
1000 d->buttonState = (Keyboard | (up ? Up : Down));
1002 if (!d->spinClickTimer.isActive())
1004 if (event->isAutoRepeat() && !isPgUpOrDown) {
1005 if (!d->spinClickThresholdTimer.isActive() && !d->spinClickTimer.isActive()) {
1006 d->updateState(up,
true);
1009#if QT_CONFIG(accessibility)
1010 QAccessibleValueChangeEvent event(
this, d->value);
1011 QAccessible::updateAccessibility(&event);
1016 case Qt::Key_Return:
1017 d->edit->d_func()->control->clearUndo();
1018 d->interpret(d->keyboardTracking ? AlwaysEmit : EmitIfChanged);
1021 emit editingFinished();
1022 emit returnPressed();
1023 emit d->edit->returnPressed();
1027 if (event->modifiers() & Qt::ControlModifier
1028 && QGuiApplication::platformName() ==
"xcb"_L1) {
1038 if (event->modifiers() & Qt::ShiftModifier) {
1039 int currentPos = d->edit->cursorPosition();
1040 const QString text = d->edit->displayText();
1041 if (event->key() == Qt::Key_End) {
1042 if ((currentPos == 0 && !d->prefix.isEmpty()) || text.size() - d->suffix.size() <= currentPos) {
1045 d->edit->setSelection(currentPos, text.size() - d->suffix.size() - currentPos);
1048 if ((currentPos == text.size() && !d->suffix.isEmpty()) || currentPos <= d->prefix.size()) {
1051 d->edit->setSelection(currentPos, d->prefix.size() - currentPos);
1060#ifndef QT_NO_SHORTCUT
1061 if (event == QKeySequence::SelectAll) {
1070 d->edit->event(event);
1071 if (!d->edit->text().isEmpty())
1074 d->ignoreUpdateEdit =
true;
1078
1079
1081void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
1083 Q_D(QAbstractSpinBox);
1085 d->keyboardModifiers = event->modifiers();
1086 if (d->buttonState & Keyboard && !event->isAutoRepeat()) {
1089 d->edit->event(event);
1094
1095
1097#if QT_CONFIG(wheelevent)
1098void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
1100 Q_D(QAbstractSpinBox);
1106 if ((event->modifiers() & Qt::ShiftModifier)
1107 && event->source() == Qt::MouseEventNotSynthesized) {
1108 d->wheelDeltaRemainder += event->angleDelta().x();
1110 d->wheelDeltaRemainder += event->angleDelta().y();
1113 d->wheelDeltaRemainder += event->angleDelta().y();
1115 const int steps = d->wheelDeltaRemainder / 120;
1116 d->wheelDeltaRemainder -= steps * 120;
1117 if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
1118 stepBy(event->modifiers() & d->stepModifier ? steps * 10 : steps);
1125
1126
1127void QAbstractSpinBox::focusInEvent(QFocusEvent *event)
1129 Q_D(QAbstractSpinBox);
1131 d->edit->event(event);
1132 if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
1135 QWidget::focusInEvent(event);
1139
1140
1142void QAbstractSpinBox::focusOutEvent(QFocusEvent *event)
1144 Q_D(QAbstractSpinBox);
1147 d->interpret(EmitIfChanged);
1150 d->edit->event(event);
1152 QWidget::focusOutEvent(event);
1154 emit editingFinished();
1158
1159
1161void QAbstractSpinBox::closeEvent(QCloseEvent *event)
1163 Q_D(QAbstractSpinBox);
1167 d->interpret(EmitIfChanged);
1168 QWidget::closeEvent(event);
1172
1173
1175void QAbstractSpinBox::hideEvent(QHideEvent *event)
1177 Q_D(QAbstractSpinBox);
1180 d->interpret(EmitIfChanged);
1181 QWidget::hideEvent(event);
1186
1187
1189void QAbstractSpinBox::timerEvent(QTimerEvent *event)
1191 Q_D(QAbstractSpinBox);
1193 bool doStep =
false;
1194 if (event->id() == d->spinClickThresholdTimer.id()) {
1195 d->spinClickThresholdTimer.stop();
1196 d->effectiveSpinRepeatRate = d->buttonState & Keyboard
1197 ? QGuiApplication::styleHints()->keyboardAutoRepeatRateF()
1198 : d->spinClickTimerInterval;
1199 d->spinClickTimer.start(d->effectiveSpinRepeatRate,
this);
1201 }
else if (event->id() == d->spinClickTimer.id()) {
1202 if (d->accelerate) {
1203 d->acceleration = d->acceleration + (
int)(d->effectiveSpinRepeatRate * 0.05);
1204 auto interval =
int(d->effectiveSpinRepeatRate - d->acceleration) * 1ms;
1205 if (interval >= 10ms)
1206 d->spinClickTimer.start(interval,
this);
1212 const bool increaseStepRate = d->keyboardModifiers & d->stepModifier;
1213 const StepEnabled st = stepEnabled();
1214 if (d->buttonState & Up) {
1215 if (!(st & StepUpEnabled)) {
1218 stepBy(increaseStepRate ? 10 : 1);
1220 }
else if (d->buttonState & Down) {
1221 if (!(st & StepDownEnabled)) {
1224 stepBy(increaseStepRate ? -10 : -1);
1229 QWidget::timerEvent(event);
1234
1235
1237#if QT_CONFIG(contextmenu)
1238void QAbstractSpinBox::contextMenuEvent(QContextMenuEvent *event)
1241 if (!qstdweb::haveAsyncify()) {
1242 qDebug() <<
" Skipping context menu for spinbox since asyncify is off";
1246 Q_D(QAbstractSpinBox);
1248 QPointer<QMenu> menu = d->edit->createStandardContextMenu();
1254 QAction *selAll =
new QAction(tr(
"&Select All"), menu);
1255#if QT_CONFIG(shortcut)
1256 selAll->setShortcut(QKeySequence::SelectAll);
1258 menu->insertAction(d->edit->d_func()->selectAllAction,
1260 menu->removeAction(d->edit->d_func()->selectAllAction);
1261 menu->addSeparator();
1262 const uint se = stepEnabled();
1263 QAction *up = menu->addAction(tr(
"&Step up"));
1264 up->setEnabled(se & StepUpEnabled);
1265 QAction *down = menu->addAction(tr(
"Step &down"));
1266 down->setEnabled(se & StepDownEnabled);
1267 menu->addSeparator();
1269 const QPointer<QAbstractSpinBox> that =
this;
1270 const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
1271 ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
1272 const QAction *action = menu->exec(pos);
1273 delete static_cast<QMenu *>(menu);
1274 if (that && action) {
1277 }
else if (action == down) {
1279 }
else if (action == selAll) {
1288
1289
1291void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
1293 Q_D(QAbstractSpinBox);
1295 d->keyboardModifiers = event->modifiers();
1296 d->updateHoverControl(event->position().toPoint());
1299 if (d->spinClickTimer.isActive() && d->buttonSymbols != NoButtons) {
1300 const StepEnabled se = stepEnabled();
1301 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
1302 d->updateState(
true);
1303 else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
1304 d->updateState(
false);
1312
1313
1315void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
1317 Q_D(QAbstractSpinBox);
1319 d->keyboardModifiers = event->modifiers();
1320 if (event->button() != Qt::LeftButton || d->buttonState != None) {
1325 d->updateHoverControl(event->position().toPoint());
1328 const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
1329 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
1330 d->updateState(
true);
1331 }
else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
1332 d->updateState(
false);
1339
1340
1341void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
1343 Q_D(QAbstractSpinBox);
1345 d->keyboardModifiers = event->modifiers();
1346 if ((d->buttonState & Mouse) != 0)
1354
1355
1356
1358QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
1359 : pendingEmit(
false), readOnly(
false), wrapping(
false),
1360 ignoreCursorPositionChanged(
false), frame(
true), accelerate(
false), keyboardTracking(
true),
1361 cleared(
false), ignoreUpdateEdit(
false), showGroupSeparator(
false)
1366
1367
1368
1369QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
1374
1375
1376
1377
1378bool QAbstractSpinBoxPrivate::updateHoverControl(
const QPoint &pos)
1380 Q_Q(QAbstractSpinBox);
1381 QRect lastHoverRect = hoverRect;
1382 QStyle::SubControl lastHoverControl = hoverControl;
1383 bool doesHover = q->testAttribute(Qt::WA_Hover);
1384 if (lastHoverControl != newHoverControl(pos) && doesHover) {
1385 q->update(lastHoverRect);
1386 q->update(hoverRect);
1393
1394
1395
1396
1397QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(
const QPoint &pos)
1399 Q_Q(QAbstractSpinBox);
1401 QStyleOptionSpinBox opt;
1402 q->initStyleOption(&opt);
1403 opt.subControls = QStyle::SC_All;
1404 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q);
1405 hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q);
1406 return hoverControl;
1410
1411
1412
1414QString QAbstractSpinBoxPrivate::stripped(
const QString &t,
int *pos)
const
1416 QStringView text(t);
1417 if (specialValueText.size() == 0 || text != specialValueText) {
1419 int size = text.size();
1420 bool changed =
false;
1421 if (prefix.size() && text.startsWith(prefix)) {
1422 from += prefix.size();
1426 if (suffix.size() && text.endsWith(suffix)) {
1427 size -= suffix.size();
1431 text = text.mid(from, size);
1434 const int s = text.size();
1435 text = text.trimmed();
1437 (*pos) -= (s - text.size());
1438 return text.toString();
1442void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
1444 Q_Q(QAbstractSpinBox);
1445 QStyleOptionSpinBox opt;
1446 q->initStyleOption(&opt);
1447 opt.subControls = QStyle::SC_SpinBoxEditField;
1448 edit->setGeometry(q->style()->subControlRect(QStyle::CC_SpinBox, &opt,
1449 QStyle::SC_SpinBoxEditField, q));
1452
1453
1454
1456bool QAbstractSpinBoxPrivate::specialValue()
const
1458 return (value == minimum && !specialValueText.isEmpty());
1462
1463
1464
1466void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy,
const QVariant &)
1471
1472
1473
1474
1475
1477void QAbstractSpinBoxPrivate::editorTextChanged(
const QString &t)
1479 Q_Q(QAbstractSpinBox);
1481 if (keyboardTracking) {
1483 int pos = edit->cursorPosition();
1484 QValidator::State state = q->validate(tmp, pos);
1485 if (state == QValidator::Acceptable) {
1486 const QVariant v = valueFromText(tmp);
1487 setValue(v, EmitIfChanged, tmp != t);
1488 pendingEmit =
false;
1498
1499
1500
1501
1502
1503
1504
1505
1507void QAbstractSpinBoxPrivate::editorCursorPositionChanged(
int oldpos,
int newpos)
1509 if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
1510 ignoreCursorPositionChanged =
true;
1512 bool allowSelection =
true;
1514 if (newpos < prefix.size() && newpos != 0) {
1516 allowSelection =
false;
1517 pos = prefix.size();
1521 }
else if (newpos > edit->text().size() - suffix.size()
1522 && newpos != edit->text().size()) {
1523 if (oldpos == edit->text().size()) {
1524 pos = edit->text().size() - suffix.size();
1525 allowSelection =
false;
1527 pos = edit->text().size();
1531 const int selSize = edit->selectionStart() >= 0 && allowSelection
1532 ? (edit->selectedText().size()
1533 * (newpos < pos ? -1 : 1)) - newpos + pos
1536 const QSignalBlocker blocker(edit);
1538 edit->setSelection(pos - selSize, selSize);
1540 edit->setCursorPosition(pos);
1543 ignoreCursorPositionChanged =
false;
1548
1549
1550
1551
1553void QAbstractSpinBoxPrivate::init()
1555 Q_Q(QAbstractSpinBox);
1557 q->setLineEdit(
new QLineEdit(q));
1558 edit->setObjectName(
"qt_spinbox_lineedit"_L1);
1559 validator =
new QSpinBoxValidator(q,
this);
1560 edit->setValidator(validator);
1562 QStyleOptionSpinBox opt;
1565 q->initStyleOption(&opt);
1566 spinClickTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, &opt, q);
1567 spinClickThresholdTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, &opt, q);
1568 q->setFocusPolicy(Qt::WheelFocus);
1569 q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
1570 q->setAttribute(Qt::WA_InputMethodEnabled);
1572 q->setAttribute(Qt::WA_MacShowFocusRect);
1576
1577
1578
1579
1580
1582void QAbstractSpinBoxPrivate::reset()
1584 Q_Q(QAbstractSpinBox);
1588 spinClickTimer.stop();
1589 spinClickThresholdTimer.stop();
1596
1597
1598
1599
1601void QAbstractSpinBoxPrivate::updateState(
bool up,
bool fromKeyboard )
1603 Q_Q(QAbstractSpinBox);
1604 if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
1607 if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
1608 : QAbstractSpinBox::StepDownEnabled))) {
1609 buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
1610 int steps = up ? 1 : -1;
1611 if (keyboardModifiers & stepModifier)
1614 spinClickThresholdTimer.start(spinClickThresholdTimerInterval * 1ms, q);
1615#if QT_CONFIG(accessibility)
1616 QAccessibleValueChangeEvent event(q, value);
1617 QAccessible::updateAccessibility(&event);
1624
1625
1626
1627
1628
1629
1630void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option)
const
1635 Q_D(
const QAbstractSpinBox);
1636 option->initFrom(
this);
1637 option->activeSubControls = QStyle::SC_None;
1638 option->buttonSymbols = d->buttonSymbols;
1639 option->subControls = QStyle::SC_SpinBoxEditField;
1640 if (style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame,
nullptr,
this))
1641 option->subControls |= QStyle::SC_SpinBoxFrame;
1642 if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
1643 option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
1644 if (d->buttonState & Up) {
1645 option->activeSubControls = QStyle::SC_SpinBoxUp;
1646 }
else if (d->buttonState & Down) {
1647 option->activeSubControls = QStyle::SC_SpinBoxDown;
1651 if (d->buttonState) {
1652 option->state |= QStyle::State_Sunken;
1654 option->activeSubControls = d->hoverControl;
1657 option->stepEnabled = style()->styleHint(QStyle::SH_SpinControls_DisableOnBounds,
nullptr,
this)
1659 : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
1661 option->frame = d->frame;
1665
1666
1667
1668
1669
1670
1672QVariant QAbstractSpinBoxPrivate::bound(
const QVariant &val,
const QVariant &old,
int steps)
const
1675 if (!wrapping || steps == 0 || old.isNull()) {
1676 if (variantCompare(v, minimum) < 0) {
1677 v = wrapping ? maximum : minimum;
1679 if (variantCompare(v, maximum) > 0) {
1680 v = wrapping ? minimum : maximum;
1683 const bool wasMin = old == minimum;
1684 const bool wasMax = old == maximum;
1685 const int oldcmp = variantCompare(v, old);
1686 const int maxcmp = variantCompare(v, maximum);
1687 const int mincmp = variantCompare(v, minimum);
1688 const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
1690 v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
1691 ? minimum : maximum;
1692 }
else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
1693 v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
1694 }
else if (mincmp < 0) {
1695 v = (!wasMax && !wasMin ? minimum : maximum);
1703
1704
1705
1706
1707
1709void QAbstractSpinBoxPrivate::setValue(
const QVariant &val, EmitPolicy ep,
1712 Q_Q(QAbstractSpinBox);
1713 const QVariant old = value;
1715 pendingEmit =
false;
1722 if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
1723 emitSignals(ep, old);
1727QVariant QAbstractSpinBoxPrivate::roundValue(
const QVariant &val)
const
1733
1734
1735
1736
1738void QAbstractSpinBoxPrivate::updateEdit()
1740 Q_Q(QAbstractSpinBox);
1741 if (type == QMetaType::UnknownType)
1743 const QString newText = specialValue() ? specialValueText : prefix + textFromValue(value) + suffix;
1744 if (newText == edit->displayText() || cleared)
1747 const bool empty = edit->text().isEmpty();
1748 int cursor = edit->cursorPosition();
1749 int selsize = edit->selectedText().size();
1750 const QSignalBlocker blocker(edit);
1751 edit->setText(newText);
1753 if (!specialValue()) {
1754 cursor = qBound(prefix.size(), cursor, edit->displayText().size() - suffix.size());
1757 edit->setSelection(cursor, selsize);
1759 edit->setCursorPosition(empty ? prefix.size() : cursor);
1766
1767
1768
1769
1771void QAbstractSpinBoxPrivate::setRange(
const QVariant &min,
const QVariant &max)
1773 Q_Q(QAbstractSpinBox);
1777 maximum = (variantCompare(min, max) < 0 ? max : min);
1778 cachedSizeHint = QSize();
1779 cachedMinimumSizeHint = QSize();
1782 if (!(bound(value) == value)) {
1783 setValue(bound(value), EmitIfChanged);
1784 }
else if (value == minimum && !specialValueText.isEmpty()) {
1788 q->updateGeometry();
1792
1793
1794
1795
1797QVariant QAbstractSpinBoxPrivate::getZeroVariant()
const
1801 case QMetaType::Int: ret = QVariant(0);
break;
1802 case QMetaType::Double: ret = QVariant(0.0);
break;
1809
1810
1811
1812
1813
1814
1815
1816
1817
1819QString QAbstractSpinBoxPrivate::textFromValue(
const QVariant &)
const
1825
1826
1827
1828
1829
1830
1831
1832
1833
1835QVariant QAbstractSpinBoxPrivate::valueFromText(
const QString &)
const
1840
1841
1842
1843
1844
1846void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
1848 Q_Q(QAbstractSpinBox);
1849 if (type == QMetaType::UnknownType || cleared)
1852 QVariant v = getZeroVariant();
1853 bool doInterpret =
true;
1854 QString tmp = edit->displayText();
1855 int pos = edit->cursorPosition();
1856 const int oldpos = pos;
1858 if (q->validate(tmp, pos) != QValidator::Acceptable) {
1859 const QString copy = tmp;
1861 qDebug(lcWidgetAbstractSpinBox)
1862 <<
"QAbstractSpinBoxPrivate::interpret() text '"
1863 << edit->displayText()
1864 <<
"' >> '" << copy <<
'\''
1865 <<
"' >> '" << tmp <<
'\'';
1867 doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
1869 v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
1870 ? variantBound(minimum, v, maximum) : value);
1874 v = valueFromText(tmp);
1877 setValue(v, ep,
true);
1879 edit->setCursorPosition(pos);
1882void QAbstractSpinBoxPrivate::clearCache()
const
1885 cachedValue.clear();
1886 cachedState = QValidator::Acceptable;
1889QVariant QAbstractSpinBoxPrivate::calculateAdaptiveDecimalStep(
int steps)
const
1898
1899
1900
1902QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
1903 : QValidator(qp), qptr(qp), dptr(dp)
1905 setObjectName(
"qt_spinboxvalidator"_L1);
1909
1910
1911
1912
1913
1915QValidator::State QSpinBoxValidator::validate(QString &input,
int &pos)
const
1917 if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
1918 return QValidator::Acceptable;
1920 if (!dptr->prefix.isEmpty() && !input.startsWith(dptr->prefix)) {
1921 input.prepend(dptr->prefix);
1922 pos += dptr->prefix.size();
1925 if (!dptr->suffix.isEmpty() && !input.endsWith(dptr->suffix))
1926 input.append(dptr->suffix);
1928 return qptr->validate(input, pos);
1931
1932
1933
1935void QSpinBoxValidator::fixup(QString &input)
const
1943
1944
1945
1950 if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
1951 qWarning(
"QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
1952 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
1953 switch (arg1.userType()) {
1954 case QMetaType::Int: {
1955 const int int1 = arg1.toInt();
1956 const int int2 = arg2.toInt();
1957 if (int1 > 0 && (int2 >= INT_MAX - int1)) {
1959 ret = QVariant(INT_MAX);
1960 }
else if (int1 < 0 && (int2 <= INT_MIN - int1)) {
1962 ret = QVariant(INT_MIN);
1964 ret = QVariant(int1 + int2);
1968 case QMetaType::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble());
break;
1969#if QT_CONFIG(datetimeparser)
1970 case QMetaType::QDateTime: {
1971 QDateTime a2 = arg2.toDateTime();
1972 QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATE_MIN.daysTo(a2.date()));
1973 a1.setTime(a1.time().addMSecs(a2.time().msecsSinceStartOfDay()));
1974 ret = QVariant(std::move(a1));
1985
1986
1987
1992 if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
1993 qWarning(
"QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
1994 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
1995 switch (arg1.userType()) {
1996 case QMetaType::Int: ret = QVariant(arg1.toInt() - arg2.toInt());
break;
1997 case QMetaType::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble());
break;
1998 case QMetaType::QDateTime: {
1999 QDateTime a1 = arg1.toDateTime();
2000 QDateTime a2 = arg2.toDateTime();
2001 int days = a2.daysTo(a1);
2002 int secs = a2.secsTo(a1);
2003 int msecs = qMax(0, a1.time().msec() - a2.time().msec());
2004 if (days < 0 || secs < 0 || msecs < 0) {
2007 QDateTime dt = a2.addDays(days).addSecs(secs);
2009 dt.setTime(dt.time().addMSecs(msecs));
2010 ret = QVariant(std::move(dt));
2020
2021
2022
2028 switch (arg1.userType()) {
2029 case QMetaType::Int:
2030 ret =
static_cast<
int>(qBound<
double>(INT_MIN, arg1.toInt() * multiplier, INT_MAX));
2032 case QMetaType::Double: ret = QVariant(arg1.toDouble() * multiplier);
break;
2033#if QT_CONFIG(datetimeparser)
2034 case QMetaType::QDateTime: {
2035 double days = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDateTime().date()) * multiplier;
2036 const qint64 daysInt = qint64(days);
2038 qint64 msecs = qint64(arg1.toDateTime().time().msecsSinceStartOfDay() * multiplier
2039 + days * (24 * 3600 * 1000));
2040 ret = QDATETIMEEDIT_DATE_MIN.addDays(daysInt).startOfDay().addMSecs(msecs);
2044 default: ret = arg1;
break;
2052double operator/(
const QVariant &arg1,
const QVariant &arg2)
2057 switch (arg1.userType()) {
2058 case QMetaType::Int:
2059 a1 = (
double)arg1.toInt();
2060 a2 = (
double)arg2.toInt();
2062 case QMetaType::Double:
2063 a1 = arg1.toDouble();
2064 a2 = arg2.toDouble();
2066#if QT_CONFIG(datetimeparser)
2067 case QMetaType::QDateTime:
2068 a1 = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDate());
2069 a2 = QDATETIMEEDIT_DATE_MIN.daysTo(arg2.toDate());
2070 a1 += arg1.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2071 a2 += arg2.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2077 return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
2080int QAbstractSpinBoxPrivate::variantCompare(
const QVariant &arg1,
const QVariant &arg2)
2082 switch (arg2.userType()) {
2083 case QMetaType::QDate:
2084 Q_ASSERT_X(arg1.userType() == QMetaType::QDate,
"QAbstractSpinBoxPrivate::variantCompare",
2085 qPrintable(QString::fromLatin1(
"Internal error 1 (%1)").
2086 arg(QString::fromLatin1(arg1.typeName()))));
2087 if (arg1.toDate() == arg2.toDate()) {
2089 }
else if (arg1.toDate() < arg2.toDate()) {
2094 case QMetaType::QTime:
2095 Q_ASSERT_X(arg1.userType() == QMetaType::QTime,
"QAbstractSpinBoxPrivate::variantCompare",
2096 qPrintable(QString::fromLatin1(
"Internal error 2 (%1)").
2097 arg(QString::fromLatin1(arg1.typeName()))));
2098 if (arg1.toTime() == arg2.toTime()) {
2100 }
else if (arg1.toTime() < arg2.toTime()) {
2107 case QMetaType::QDateTime:
2108 if (arg1.toDateTime() == arg2.toDateTime()) {
2110 }
else if (arg1.toDateTime() < arg2.toDateTime()) {
2115 case QMetaType::Int:
2116 if (arg1.toInt() == arg2.toInt()) {
2118 }
else if (arg1.toInt() < arg2.toInt()) {
2123 case QMetaType::Double:
2124 if (arg1.toDouble() == arg2.toDouble()) {
2126 }
else if (arg1.toDouble() < arg2.toDouble()) {
2131 case QMetaType::UnknownType:
2132 if (arg2.userType() == QMetaType::UnknownType)
2136 Q_ASSERT_X(0,
"QAbstractSpinBoxPrivate::variantCompare",
2137 qPrintable(QString::fromLatin1(
"Internal error 3 (%1 %2)").
2138 arg(QString::fromLatin1(arg1.typeName()),
2139 QString::fromLatin1(arg2.typeName()))));
2144QVariant QAbstractSpinBoxPrivate::variantBound(
const QVariant &min,
2145 const QVariant &value,
2146 const QVariant &max)
2148 Q_ASSERT(variantCompare(min, max) <= 0);
2149 if (variantCompare(min, value) < 0) {
2150 const int compMax = variantCompare(value, max);
2151 return (compMax < 0 ? value : max);
2160#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)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
QMatrix4x4 operator-(const QMatrix4x4 &m1, const QMatrix4x4 &m2)