1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include <private/qabstractspinbox_p.h> |
5 | #include <qspinbox.h> |
6 | |
7 | #include <qlineedit.h> |
8 | #include <qlocale.h> |
9 | #include <qvalidator.h> |
10 | #include <qdebug.h> |
11 | |
12 | #include <algorithm> |
13 | #include <cmath> |
14 | #include <float.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | using namespace Qt::StringLiterals; |
19 | |
20 | //#define QSPINBOX_QSBDEBUG |
21 | #ifdef QSPINBOX_QSBDEBUG |
22 | # define QSBDEBUG qDebug |
23 | #else |
24 | # define QSBDEBUG if (false) qDebug |
25 | #endif |
26 | |
27 | class QSpinBoxPrivate : public QAbstractSpinBoxPrivate |
28 | { |
29 | Q_DECLARE_PUBLIC(QSpinBox) |
30 | public: |
31 | QSpinBoxPrivate(); |
32 | void emitSignals(EmitPolicy ep, const QVariant &) override; |
33 | |
34 | virtual QVariant valueFromText(const QString &n) const override; |
35 | virtual QString textFromValue(const QVariant &n) const override; |
36 | QVariant validateAndInterpret(QString &input, int &pos, |
37 | QValidator::State &state) const; |
38 | |
39 | inline void init() { |
40 | Q_Q(QSpinBox); |
41 | q->setInputMethodHints(Qt::ImhDigitsOnly); |
42 | setLayoutItemMargins(element: QStyle::SE_SpinBoxLayoutItem); |
43 | } |
44 | |
45 | int displayIntegerBase; |
46 | |
47 | QVariant calculateAdaptiveDecimalStep(int steps) const override; |
48 | }; |
49 | |
50 | class QDoubleSpinBoxPrivate : public QAbstractSpinBoxPrivate |
51 | { |
52 | Q_DECLARE_PUBLIC(QDoubleSpinBox) |
53 | public: |
54 | QDoubleSpinBoxPrivate(); |
55 | void emitSignals(EmitPolicy ep, const QVariant &) override; |
56 | |
57 | virtual QVariant valueFromText(const QString &n) const override; |
58 | virtual QString textFromValue(const QVariant &n) const override; |
59 | QVariant validateAndInterpret(QString &input, int &pos, |
60 | QValidator::State &state) const; |
61 | double round(double input) const; |
62 | // variables |
63 | int decimals; |
64 | |
65 | inline void init() { |
66 | Q_Q(QDoubleSpinBox); |
67 | q->setInputMethodHints(Qt::ImhFormattedNumbersOnly); |
68 | } |
69 | |
70 | // When fiddling with the decimals property, we may lose precision in these properties. |
71 | double actualMin; |
72 | double actualMax; |
73 | |
74 | QVariant calculateAdaptiveDecimalStep(int steps) const override; |
75 | }; |
76 | |
77 | |
78 | /*! |
79 | \class QSpinBox |
80 | \brief The QSpinBox class provides a spin box widget. |
81 | |
82 | \ingroup basicwidgets |
83 | \inmodule QtWidgets |
84 | |
85 | \image windows-spinbox.png |
86 | |
87 | QSpinBox is designed to handle integers and discrete sets of |
88 | values (e.g., month names); use QDoubleSpinBox for floating point |
89 | values. |
90 | |
91 | QSpinBox allows the user to choose a value by clicking the up/down |
92 | buttons or pressing up/down on the keyboard to increase/decrease |
93 | the value currently displayed. The user can also type the value in |
94 | manually. The spin box supports integer values but can be extended to |
95 | use different strings with validate(), textFromValue() and valueFromText(). |
96 | |
97 | Every time the value changes QSpinBox emits valueChanged() and |
98 | textChanged() signals, the former providing a int and the latter |
99 | a QString. The textChanged() signal provides the value with both |
100 | prefix() and suffix(). |
101 | The current value can be fetched with value() and set with setValue(). |
102 | |
103 | Clicking the up/down buttons or using the keyboard accelerator's |
104 | up and down arrows will increase or decrease the current value in |
105 | steps of size singleStep(). If you want to change this behaviour you |
106 | can reimplement the virtual function stepBy(). The minimum and |
107 | maximum value and the step size can be set using one of the |
108 | constructors, and can be changed later with setMinimum(), |
109 | setMaximum() and setSingleStep(). |
110 | |
111 | Most spin boxes are directional, but QSpinBox can also operate as |
112 | a circular spin box, i.e. if the range is 0-99 and the current |
113 | value is 99, clicking "up" will give 0 if wrapping() is set to |
114 | true. Use setWrapping() if you want circular behavior. |
115 | |
116 | The displayed value can be prepended and appended with arbitrary |
117 | strings indicating, for example, currency or the unit of |
118 | measurement. See setPrefix() and setSuffix(). The text in the spin |
119 | box is retrieved with text() (which includes any prefix() and |
120 | suffix()), or with cleanText() (which has no prefix(), no suffix() |
121 | and no leading or trailing whitespace). |
122 | |
123 | It is often desirable to give the user a special (often default) |
124 | choice in addition to the range of numeric values. See |
125 | setSpecialValueText() for how to do this with QSpinBox. |
126 | |
127 | \section1 Subclassing QSpinBox |
128 | |
129 | If using prefix(), suffix(), and specialValueText() don't provide |
130 | enough control, you subclass QSpinBox and reimplement |
131 | valueFromText() and textFromValue(). For example, here's the code |
132 | for a custom spin box that allows the user to enter icon sizes |
133 | (e.g., "32 x 32"): |
134 | |
135 | \snippet code/src_gui_widgets_qspinbox.cpp 8 |
136 | \codeline |
137 | \snippet code/src_gui_widgets_qspinbox.cpp 9 |
138 | |
139 | \sa QDoubleSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example} |
140 | */ |
141 | |
142 | /*! |
143 | \fn void QSpinBox::valueChanged(int i) |
144 | |
145 | This signal is emitted whenever the spin box's value is changed. |
146 | The new value's integer value is passed in \a i. |
147 | */ |
148 | |
149 | /*! |
150 | \fn void QSpinBox::textChanged(const QString &text) |
151 | \since 5.14 |
152 | |
153 | This signal is emitted whenever the spin box's text is changed. |
154 | The new text is passed in \a text with prefix() and suffix(). |
155 | */ |
156 | |
157 | /*! |
158 | Constructs a spin box with 0 as minimum value and 99 as maximum value, a |
159 | step value of 1. The value is initially set to 0. It is parented to \a |
160 | parent. |
161 | |
162 | \sa setMinimum(), setMaximum(), setSingleStep() |
163 | */ |
164 | |
165 | QSpinBox::QSpinBox(QWidget *parent) |
166 | : QAbstractSpinBox(*new QSpinBoxPrivate, parent) |
167 | { |
168 | Q_D(QSpinBox); |
169 | d->init(); |
170 | } |
171 | |
172 | /*! |
173 | Destructor. |
174 | */ |
175 | QSpinBox::~QSpinBox() {} |
176 | |
177 | /*! |
178 | \property QSpinBox::value |
179 | \brief the value of the spin box |
180 | |
181 | setValue() will emit valueChanged() if the new value is different |
182 | from the old one. The value property has a second notifier |
183 | signal which includes the spin box's prefix and suffix. |
184 | */ |
185 | |
186 | int QSpinBox::value() const |
187 | { |
188 | Q_D(const QSpinBox); |
189 | return d->value.toInt(); |
190 | } |
191 | |
192 | void QSpinBox::setValue(int value) |
193 | { |
194 | Q_D(QSpinBox); |
195 | d->setValue(val: QVariant(value), ep: EmitIfChanged); |
196 | } |
197 | |
198 | /*! |
199 | \property QSpinBox::prefix |
200 | \brief the spin box's prefix |
201 | |
202 | The prefix is prepended to the start of the displayed value. |
203 | Typical use is to display a unit of measurement or a currency |
204 | symbol. For example: |
205 | |
206 | \snippet code/src_gui_widgets_qspinbox.cpp 0 |
207 | |
208 | To turn off the prefix display, set this property to an empty |
209 | string. The default is no prefix. The prefix is not displayed when |
210 | value() == minimum() and specialValueText() is set. |
211 | |
212 | If no prefix is set, prefix() returns an empty string. |
213 | |
214 | \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText() |
215 | */ |
216 | |
217 | QString QSpinBox::prefix() const |
218 | { |
219 | Q_D(const QSpinBox); |
220 | return d->prefix; |
221 | } |
222 | |
223 | void QSpinBox::setPrefix(const QString &prefix) |
224 | { |
225 | Q_D(QSpinBox); |
226 | |
227 | d->prefix = prefix; |
228 | d->updateEdit(); |
229 | |
230 | d->cachedSizeHint = QSize(); |
231 | d->cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about the prefix |
232 | updateGeometry(); |
233 | } |
234 | |
235 | /*! |
236 | \property QSpinBox::suffix |
237 | \brief the suffix of the spin box |
238 | |
239 | The suffix is appended to the end of the displayed value. Typical |
240 | use is to display a unit of measurement or a currency symbol. For |
241 | example: |
242 | |
243 | \snippet code/src_gui_widgets_qspinbox.cpp 1 |
244 | |
245 | To turn off the suffix display, set this property to an empty |
246 | string. The default is no suffix. The suffix is not displayed for |
247 | the minimum() if specialValueText() is set. |
248 | |
249 | If no suffix is set, suffix() returns an empty string. |
250 | |
251 | \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText() |
252 | */ |
253 | |
254 | QString QSpinBox::suffix() const |
255 | { |
256 | Q_D(const QSpinBox); |
257 | |
258 | return d->suffix; |
259 | } |
260 | |
261 | void QSpinBox::setSuffix(const QString &suffix) |
262 | { |
263 | Q_D(QSpinBox); |
264 | |
265 | d->suffix = suffix; |
266 | d->updateEdit(); |
267 | |
268 | d->cachedSizeHint = QSize(); |
269 | updateGeometry(); |
270 | } |
271 | |
272 | /*! |
273 | \property QSpinBox::cleanText |
274 | |
275 | \brief the text of the spin box excluding any prefix, suffix, |
276 | or leading or trailing whitespace. |
277 | |
278 | \sa text, QSpinBox::prefix, QSpinBox::suffix |
279 | */ |
280 | |
281 | QString QSpinBox::cleanText() const |
282 | { |
283 | Q_D(const QSpinBox); |
284 | |
285 | return d->stripped(text: d->edit->displayText()); |
286 | } |
287 | |
288 | |
289 | /*! |
290 | \property QSpinBox::singleStep |
291 | \brief the step value |
292 | |
293 | When the user uses the arrows to change the spin box's value the |
294 | value will be incremented/decremented by the amount of the |
295 | singleStep. The default value is 1. Setting a singleStep value of |
296 | less than 0 does nothing. |
297 | */ |
298 | |
299 | int QSpinBox::singleStep() const |
300 | { |
301 | Q_D(const QSpinBox); |
302 | |
303 | return d->singleStep.toInt(); |
304 | } |
305 | |
306 | void QSpinBox::setSingleStep(int value) |
307 | { |
308 | Q_D(QSpinBox); |
309 | if (value >= 0) { |
310 | d->singleStep = QVariant(value); |
311 | d->updateEdit(); |
312 | } |
313 | } |
314 | |
315 | /*! |
316 | \property QSpinBox::minimum |
317 | |
318 | \brief the minimum value of the spin box |
319 | |
320 | When setting this property the \l maximum is adjusted |
321 | if necessary to ensure that the range remains valid. |
322 | |
323 | The default minimum value is 0. |
324 | |
325 | \sa setRange(), specialValueText |
326 | */ |
327 | |
328 | int QSpinBox::minimum() const |
329 | { |
330 | Q_D(const QSpinBox); |
331 | |
332 | return d->minimum.toInt(); |
333 | } |
334 | |
335 | void QSpinBox::setMinimum(int minimum) |
336 | { |
337 | Q_D(QSpinBox); |
338 | const QVariant m(minimum); |
339 | d->setRange(min: m, max: (QSpinBoxPrivate::variantCompare(arg1: d->maximum, arg2: m) > 0 ? d->maximum : m)); |
340 | } |
341 | |
342 | /*! |
343 | \property QSpinBox::maximum |
344 | |
345 | \brief the maximum value of the spin box |
346 | |
347 | When setting this property the minimum is adjusted |
348 | if necessary, to ensure that the range remains valid. |
349 | |
350 | The default maximum value is 99. |
351 | |
352 | \sa setRange(), specialValueText |
353 | |
354 | */ |
355 | |
356 | int QSpinBox::maximum() const |
357 | { |
358 | Q_D(const QSpinBox); |
359 | |
360 | return d->maximum.toInt(); |
361 | } |
362 | |
363 | void QSpinBox::setMaximum(int maximum) |
364 | { |
365 | Q_D(QSpinBox); |
366 | const QVariant m(maximum); |
367 | d->setRange(min: (QSpinBoxPrivate::variantCompare(arg1: d->minimum, arg2: m) < 0 ? d->minimum : m), max: m); |
368 | } |
369 | |
370 | /*! |
371 | Convenience function to set the \a minimum, and \a maximum values |
372 | with a single function call. |
373 | |
374 | \snippet code/src_gui_widgets_qspinbox.cpp 2 |
375 | is equivalent to: |
376 | \snippet code/src_gui_widgets_qspinbox.cpp 3 |
377 | |
378 | \sa minimum, maximum |
379 | */ |
380 | |
381 | void QSpinBox::setRange(int minimum, int maximum) |
382 | { |
383 | Q_D(QSpinBox); |
384 | d->setRange(min: QVariant(minimum), max: QVariant(maximum)); |
385 | } |
386 | |
387 | /*! |
388 | Sets the step type for the spin box to \a stepType, which is single |
389 | step or adaptive decimal step. |
390 | |
391 | Adaptive decimal step means that the step size will continuously be |
392 | adjusted to one power of ten below the current \l value. So when |
393 | the value is 1100, the step is set to 100, so stepping up once |
394 | increases it to 1200. For 1200 stepping up takes it to 1300. For |
395 | negative values, stepping down from -1100 goes to -1200. |
396 | |
397 | Step direction is taken into account to handle edges cases, so |
398 | that stepping down from 100 takes the value to 99 instead of 90. |
399 | Thus a step up followed by a step down -- or vice versa -- always |
400 | lands on the starting value; 99 -> 100 -> 99. |
401 | |
402 | Setting this will cause the spin box to disregard the value of |
403 | \l singleStep, although it is preserved so that \l singleStep |
404 | comes into effect if adaptive decimal step is later turned off. |
405 | |
406 | \since 5.12 |
407 | */ |
408 | |
409 | void QSpinBox::setStepType(QAbstractSpinBox::StepType stepType) |
410 | { |
411 | Q_D(QSpinBox); |
412 | d->stepType = stepType; |
413 | } |
414 | |
415 | /*! |
416 | \property QSpinBox::stepType |
417 | \brief The step type. |
418 | |
419 | The step type can be single step or adaptive decimal step. |
420 | */ |
421 | |
422 | QAbstractSpinBox::StepType QSpinBox::stepType() const |
423 | { |
424 | Q_D(const QSpinBox); |
425 | return d->stepType; |
426 | } |
427 | |
428 | /*! |
429 | \property QSpinBox::displayIntegerBase |
430 | |
431 | \brief the base used to display the value of the spin box |
432 | |
433 | The default displayIntegerBase value is 10. |
434 | |
435 | \sa textFromValue(), valueFromText() |
436 | \since 5.2 |
437 | */ |
438 | |
439 | int QSpinBox::displayIntegerBase() const |
440 | { |
441 | Q_D(const QSpinBox); |
442 | return d->displayIntegerBase; |
443 | } |
444 | |
445 | void QSpinBox::setDisplayIntegerBase(int base) |
446 | { |
447 | Q_D(QSpinBox); |
448 | // Falls back to base 10 on invalid bases (like QString) |
449 | if (Q_UNLIKELY(base < 2 || base > 36)) { |
450 | qWarning(msg: "QSpinBox::setDisplayIntegerBase: Invalid base (%d)" , base); |
451 | base = 10; |
452 | } |
453 | |
454 | if (base != d->displayIntegerBase) { |
455 | d->displayIntegerBase = base; |
456 | d->updateEdit(); |
457 | } |
458 | } |
459 | |
460 | /*! |
461 | This virtual function is used by the spin box whenever it needs to |
462 | display the given \a value. The default implementation returns a |
463 | string containing \a value printed in the standard way using |
464 | QWidget::locale().toString(), but with the thousand separator |
465 | removed unless setGroupSeparatorShown() is set. Reimplementations may |
466 | return anything. (See the example in the detailed description.) |
467 | |
468 | Note: QSpinBox does not call this function for specialValueText() |
469 | and that neither prefix() nor suffix() should be included in the |
470 | return value. |
471 | |
472 | If you reimplement this, you may also need to reimplement |
473 | valueFromText() and validate() |
474 | |
475 | \sa valueFromText(), validate(), QLocale::groupSeparator() |
476 | */ |
477 | |
478 | QString QSpinBox::textFromValue(int value) const |
479 | { |
480 | Q_D(const QSpinBox); |
481 | QString str; |
482 | |
483 | if (d->displayIntegerBase != 10) { |
484 | const auto prefix = value < 0 ? "-"_L1 : ""_L1 ; |
485 | str = prefix + QString::number(qAbs(t: value), base: d->displayIntegerBase); |
486 | } else { |
487 | str = locale().toString(i: value); |
488 | if (!d->showGroupSeparator && (qAbs(t: value) >= 1000 || value == INT_MIN)) { |
489 | str.remove(s: locale().groupSeparator()); |
490 | } |
491 | } |
492 | |
493 | return str; |
494 | } |
495 | |
496 | /*! |
497 | \fn int QSpinBox::valueFromText(const QString &text) const |
498 | |
499 | This virtual function is used by the spin box whenever it needs to |
500 | interpret \a text entered by the user as a value. |
501 | |
502 | Subclasses that need to display spin box values in a non-numeric |
503 | way need to reimplement this function. |
504 | |
505 | Note: QSpinBox handles specialValueText() separately; this |
506 | function is only concerned with the other values. |
507 | |
508 | \sa textFromValue(), validate() |
509 | */ |
510 | |
511 | int QSpinBox::valueFromText(const QString &text) const |
512 | { |
513 | Q_D(const QSpinBox); |
514 | |
515 | QString copy = text; |
516 | int pos = d->edit->cursorPosition(); |
517 | QValidator::State state = QValidator::Acceptable; |
518 | return d->validateAndInterpret(input&: copy, pos, state).toInt(); |
519 | } |
520 | |
521 | /*! |
522 | \reimp |
523 | */ |
524 | QValidator::State QSpinBox::validate(QString &text, int &pos) const |
525 | { |
526 | Q_D(const QSpinBox); |
527 | |
528 | QValidator::State state; |
529 | d->validateAndInterpret(input&: text, pos, state); |
530 | return state; |
531 | } |
532 | |
533 | |
534 | /*! |
535 | \reimp |
536 | */ |
537 | void QSpinBox::fixup(QString &input) const |
538 | { |
539 | if (!isGroupSeparatorShown()) |
540 | input.remove(s: locale().groupSeparator()); |
541 | } |
542 | |
543 | |
544 | // --- QDoubleSpinBox --- |
545 | |
546 | /*! |
547 | \class QDoubleSpinBox |
548 | \brief The QDoubleSpinBox class provides a spin box widget that |
549 | takes doubles. |
550 | |
551 | \ingroup basicwidgets |
552 | \inmodule QtWidgets |
553 | |
554 | QDoubleSpinBox allows the user to choose a value by clicking the |
555 | up and down buttons or by pressing Up or Down on the keyboard to |
556 | increase or decrease the value currently displayed. The user can |
557 | also type the value in manually. The spin box supports double |
558 | values but can be extended to use different strings with |
559 | validate(), textFromValue() and valueFromText(). |
560 | |
561 | Every time the value changes QDoubleSpinBox emits valueChanged() and |
562 | textChanged() signals, the former providing a double and the latter |
563 | a QString. The textChanged() signal provides the value with both |
564 | prefix() and suffix(). The current value can be fetched with |
565 | value() and set with setValue(). |
566 | |
567 | Note: QDoubleSpinBox will round numbers so they can be displayed |
568 | with the current precision. In a QDoubleSpinBox with decimals set |
569 | to 2, calling setValue(2.555) will cause value() to return 2.56. |
570 | |
571 | Clicking the up and down buttons or using the keyboard accelerator's |
572 | Up and Down arrows will increase or decrease the current value in |
573 | steps of size singleStep(). If you want to change this behavior you |
574 | can reimplement the virtual function stepBy(). The minimum and |
575 | maximum value and the step size can be set using one of the |
576 | constructors, and can be changed later with setMinimum(), |
577 | setMaximum() and setSingleStep(). The spinbox has a default |
578 | precision of 2 decimal places but this can be changed using |
579 | setDecimals(). |
580 | |
581 | Most spin boxes are directional, but QDoubleSpinBox can also |
582 | operate as a circular spin box, i.e. if the range is 0.0-99.9 and |
583 | the current value is 99.9, clicking "up" will give 0 if wrapping() |
584 | is set to true. Use setWrapping() if you want circular behavior. |
585 | |
586 | The displayed value can be prepended and appended with arbitrary |
587 | strings indicating, for example, currency or the unit of |
588 | measurement. See setPrefix() and setSuffix(). The text in the spin |
589 | box is retrieved with text() (which includes any prefix() and |
590 | suffix()), or with cleanText() (which has no prefix(), no suffix() |
591 | and no leading or trailing whitespace). |
592 | |
593 | It is often desirable to give the user a special (often default) |
594 | choice in addition to the range of numeric values. See |
595 | setSpecialValueText() for how to do this with QDoubleSpinBox. |
596 | |
597 | \note The displayed value of the QDoubleSpinBox is limited to 18 characters |
598 | in addition to eventual prefix and suffix content. This limitation is used |
599 | to keep the double spin box usable even with extremely large values. |
600 | \sa QSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example} |
601 | */ |
602 | |
603 | /*! |
604 | \fn void QDoubleSpinBox::valueChanged(double d); |
605 | |
606 | This signal is emitted whenever the spin box's value is changed. |
607 | The new value is passed in \a d. |
608 | */ |
609 | |
610 | /*! |
611 | \fn void QDoubleSpinBox::textChanged(const QString &text) |
612 | \since 5.14 |
613 | |
614 | This signal is emitted whenever the spin box's text is changed. |
615 | The new text is passed in \a text with prefix() and suffix(). |
616 | */ |
617 | |
618 | /*! |
619 | Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value, |
620 | a step value of 1.0 and a precision of 2 decimal places. The value is |
621 | initially set to 0.00. The spin box has the given \a parent. |
622 | |
623 | \sa setMinimum(), setMaximum(), setSingleStep() |
624 | */ |
625 | QDoubleSpinBox::QDoubleSpinBox(QWidget *parent) |
626 | : QAbstractSpinBox(*new QDoubleSpinBoxPrivate, parent) |
627 | { |
628 | Q_D(QDoubleSpinBox); |
629 | d->init(); |
630 | } |
631 | |
632 | /*! |
633 | Destructor. |
634 | */ |
635 | QDoubleSpinBox::~QDoubleSpinBox() {} |
636 | |
637 | /*! |
638 | \property QDoubleSpinBox::value |
639 | \brief the value of the spin box |
640 | |
641 | setValue() will emit valueChanged() if the new value is different |
642 | from the old one. The value property has a second notifier |
643 | signal which includes the spin box's prefix and suffix. |
644 | |
645 | Note: The value will be rounded so it can be displayed with the |
646 | current setting of decimals. |
647 | |
648 | \sa decimals |
649 | */ |
650 | double QDoubleSpinBox::value() const |
651 | { |
652 | Q_D(const QDoubleSpinBox); |
653 | |
654 | return d->value.toDouble(); |
655 | } |
656 | |
657 | void QDoubleSpinBox::setValue(double value) |
658 | { |
659 | Q_D(QDoubleSpinBox); |
660 | QVariant v(d->round(input: value)); |
661 | d->setValue(val: v, ep: EmitIfChanged); |
662 | } |
663 | /*! |
664 | \property QDoubleSpinBox::prefix |
665 | \brief the spin box's prefix |
666 | |
667 | The prefix is prepended to the start of the displayed value. |
668 | Typical use is to display a unit of measurement or a currency |
669 | symbol. For example: |
670 | |
671 | \snippet code/src_gui_widgets_qspinbox.cpp 4 |
672 | |
673 | To turn off the prefix display, set this property to an empty |
674 | string. The default is no prefix. The prefix is not displayed when |
675 | value() == minimum() and specialValueText() is set. |
676 | |
677 | If no prefix is set, prefix() returns an empty string. |
678 | |
679 | \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText() |
680 | */ |
681 | |
682 | QString QDoubleSpinBox::prefix() const |
683 | { |
684 | Q_D(const QDoubleSpinBox); |
685 | |
686 | return d->prefix; |
687 | } |
688 | |
689 | void QDoubleSpinBox::setPrefix(const QString &prefix) |
690 | { |
691 | Q_D(QDoubleSpinBox); |
692 | |
693 | d->prefix = prefix; |
694 | d->updateEdit(); |
695 | |
696 | d->cachedSizeHint = QSize(); |
697 | d->cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about the prefix |
698 | updateGeometry(); |
699 | } |
700 | |
701 | /*! |
702 | \property QDoubleSpinBox::suffix |
703 | \brief the suffix of the spin box |
704 | |
705 | The suffix is appended to the end of the displayed value. Typical |
706 | use is to display a unit of measurement or a currency symbol. For |
707 | example: |
708 | |
709 | \snippet code/src_gui_widgets_qspinbox.cpp 5 |
710 | |
711 | To turn off the suffix display, set this property to an empty |
712 | string. The default is no suffix. The suffix is not displayed for |
713 | the minimum() if specialValueText() is set. |
714 | |
715 | If no suffix is set, suffix() returns an empty string. |
716 | |
717 | \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText() |
718 | */ |
719 | |
720 | QString QDoubleSpinBox::suffix() const |
721 | { |
722 | Q_D(const QDoubleSpinBox); |
723 | |
724 | return d->suffix; |
725 | } |
726 | |
727 | void QDoubleSpinBox::setSuffix(const QString &suffix) |
728 | { |
729 | Q_D(QDoubleSpinBox); |
730 | |
731 | d->suffix = suffix; |
732 | d->updateEdit(); |
733 | |
734 | d->cachedSizeHint = QSize(); |
735 | updateGeometry(); |
736 | } |
737 | |
738 | /*! |
739 | \property QDoubleSpinBox::cleanText |
740 | |
741 | \brief the text of the spin box excluding any prefix, suffix, |
742 | or leading or trailing whitespace. |
743 | |
744 | \sa text, QDoubleSpinBox::prefix, QDoubleSpinBox::suffix |
745 | */ |
746 | |
747 | QString QDoubleSpinBox::cleanText() const |
748 | { |
749 | Q_D(const QDoubleSpinBox); |
750 | |
751 | return d->stripped(text: d->edit->displayText()); |
752 | } |
753 | |
754 | /*! |
755 | \property QDoubleSpinBox::singleStep |
756 | \brief the step value |
757 | |
758 | When the user uses the arrows to change the spin box's value the |
759 | value will be incremented/decremented by the amount of the |
760 | singleStep. The default value is 1.0. Setting a singleStep value |
761 | of less than 0 does nothing. |
762 | */ |
763 | double QDoubleSpinBox::singleStep() const |
764 | { |
765 | Q_D(const QDoubleSpinBox); |
766 | |
767 | return d->singleStep.toDouble(); |
768 | } |
769 | |
770 | void QDoubleSpinBox::setSingleStep(double value) |
771 | { |
772 | Q_D(QDoubleSpinBox); |
773 | |
774 | if (value >= 0) { |
775 | d->singleStep = value; |
776 | d->updateEdit(); |
777 | } |
778 | } |
779 | |
780 | /*! |
781 | \property QDoubleSpinBox::minimum |
782 | |
783 | \brief the minimum value of the spin box |
784 | |
785 | When setting this property the \l maximum is adjusted |
786 | if necessary to ensure that the range remains valid. |
787 | |
788 | The default minimum value is 0.0. |
789 | |
790 | Note: The minimum value will be rounded to match the decimals |
791 | property. |
792 | |
793 | \sa decimals, setRange(), specialValueText |
794 | */ |
795 | |
796 | double QDoubleSpinBox::minimum() const |
797 | { |
798 | Q_D(const QDoubleSpinBox); |
799 | |
800 | return d->minimum.toDouble(); |
801 | } |
802 | |
803 | void QDoubleSpinBox::setMinimum(double minimum) |
804 | { |
805 | Q_D(QDoubleSpinBox); |
806 | d->actualMin = minimum; |
807 | const QVariant m(d->round(input: minimum)); |
808 | d->setRange(min: m, max: (QDoubleSpinBoxPrivate::variantCompare(arg1: d->maximum, arg2: m) > 0 ? d->maximum : m)); |
809 | } |
810 | |
811 | /*! |
812 | \property QDoubleSpinBox::maximum |
813 | |
814 | \brief the maximum value of the spin box |
815 | |
816 | When setting this property the \l minimum is adjusted |
817 | if necessary, to ensure that the range remains valid. |
818 | |
819 | The default maximum value is 99.99. |
820 | |
821 | Note: The maximum value will be rounded to match the decimals |
822 | property. |
823 | |
824 | \sa decimals, setRange() |
825 | */ |
826 | |
827 | double QDoubleSpinBox::maximum() const |
828 | { |
829 | Q_D(const QDoubleSpinBox); |
830 | |
831 | return d->maximum.toDouble(); |
832 | } |
833 | |
834 | void QDoubleSpinBox::setMaximum(double maximum) |
835 | { |
836 | Q_D(QDoubleSpinBox); |
837 | d->actualMax = maximum; |
838 | const QVariant m(d->round(input: maximum)); |
839 | d->setRange(min: (QDoubleSpinBoxPrivate::variantCompare(arg1: d->minimum, arg2: m) < 0 ? d->minimum : m), max: m); |
840 | } |
841 | |
842 | /*! |
843 | Convenience function to set the \a minimum and \a maximum values |
844 | with a single function call. |
845 | |
846 | Note: The maximum and minimum values will be rounded to match the |
847 | decimals property. |
848 | |
849 | \snippet code/src_gui_widgets_qspinbox.cpp 6 |
850 | is equivalent to: |
851 | \snippet code/src_gui_widgets_qspinbox.cpp 7 |
852 | |
853 | \sa minimum, maximum |
854 | */ |
855 | |
856 | void QDoubleSpinBox::setRange(double minimum, double maximum) |
857 | { |
858 | Q_D(QDoubleSpinBox); |
859 | d->actualMin = minimum; |
860 | d->actualMax = maximum; |
861 | d->setRange(min: QVariant(d->round(input: minimum)), max: QVariant(d->round(input: maximum))); |
862 | } |
863 | |
864 | /*! |
865 | Sets the step type for the spin box to \a stepType, which is single |
866 | step or adaptive decimal step. |
867 | |
868 | Adaptive decimal step means that the step size will continuously be |
869 | adjusted to one power of ten below the current \l value. So when |
870 | the value is 1100, the step is set to 100, so stepping up once |
871 | increases it to 1200. For 1200 stepping up takes it to 1300. For |
872 | negative values, stepping down from -1100 goes to -1200. |
873 | |
874 | It also works for any decimal values, 0.041 is increased to 0.042 |
875 | by stepping once. |
876 | |
877 | Step direction is taken into account to handle edges cases, so |
878 | that stepping down from 100 takes the value to 99 instead of 90. |
879 | Thus a step up followed by a step down -- or vice versa -- always |
880 | lands on the starting value; 99 -> 100 -> 99. |
881 | |
882 | Setting this will cause the spin box to disregard the value of |
883 | \l singleStep, although it is preserved so that \l singleStep |
884 | comes into effect if adaptive decimal step is later turned off. |
885 | |
886 | \since 5.12 |
887 | */ |
888 | |
889 | void QDoubleSpinBox::setStepType(StepType stepType) |
890 | { |
891 | Q_D(QDoubleSpinBox); |
892 | d->stepType = stepType; |
893 | } |
894 | |
895 | /*! |
896 | \property QDoubleSpinBox::stepType |
897 | \brief The step type. |
898 | |
899 | The step type can be single step or adaptive decimal step. |
900 | */ |
901 | |
902 | QAbstractSpinBox::StepType QDoubleSpinBox::stepType() const |
903 | { |
904 | Q_D(const QDoubleSpinBox); |
905 | return d->stepType; |
906 | } |
907 | |
908 | /*! |
909 | \property QDoubleSpinBox::decimals |
910 | |
911 | \brief the precision of the spin box, in decimals |
912 | |
913 | Sets how many decimals the spinbox will use for displaying and |
914 | interpreting doubles. |
915 | |
916 | \warning The maximum value for \a decimals is DBL_MAX_10_EXP + |
917 | DBL_DIG (ie. 323) because of the limitations of the double type. |
918 | |
919 | Note: The maximum, minimum and value might change as a result of |
920 | changing this property. |
921 | */ |
922 | |
923 | int QDoubleSpinBox::decimals() const |
924 | { |
925 | Q_D(const QDoubleSpinBox); |
926 | |
927 | return d->decimals; |
928 | } |
929 | |
930 | void QDoubleSpinBox::setDecimals(int decimals) |
931 | { |
932 | Q_D(QDoubleSpinBox); |
933 | d->decimals = qBound(min: 0, val: decimals, DBL_MAX_10_EXP + DBL_DIG); |
934 | |
935 | setRange(minimum: d->actualMin, maximum: d->actualMax); // make sure values are rounded |
936 | setValue(value()); |
937 | } |
938 | |
939 | /*! |
940 | This virtual function is used by the spin box whenever it needs to |
941 | display the given \a value. The default implementation returns a string |
942 | containing \a value printed using QWidget::locale().toString(\a value, |
943 | \c u'f', decimals()) and will remove the thousand separator unless |
944 | setGroupSeparatorShown() is set. Reimplementations may return anything. |
945 | |
946 | Note: QDoubleSpinBox does not call this function for |
947 | specialValueText() and that neither prefix() nor suffix() should |
948 | be included in the return value. |
949 | |
950 | If you reimplement this, you may also need to reimplement |
951 | valueFromText(). |
952 | |
953 | \sa valueFromText(), QLocale::groupSeparator() |
954 | */ |
955 | |
956 | |
957 | QString QDoubleSpinBox::textFromValue(double value) const |
958 | { |
959 | Q_D(const QDoubleSpinBox); |
960 | QString str = locale().toString(f: value, format: 'f', precision: d->decimals); |
961 | if (!d->showGroupSeparator && qAbs(t: value) >= 1000.0) |
962 | str.remove(s: locale().groupSeparator()); |
963 | |
964 | return str; |
965 | } |
966 | |
967 | /*! |
968 | This virtual function is used by the spin box whenever it needs to |
969 | interpret \a text entered by the user as a value. |
970 | |
971 | Subclasses that need to display spin box values in a non-numeric |
972 | way need to reimplement this function. |
973 | |
974 | Note: QDoubleSpinBox handles specialValueText() separately; this |
975 | function is only concerned with the other values. |
976 | |
977 | \sa textFromValue(), validate() |
978 | */ |
979 | double QDoubleSpinBox::valueFromText(const QString &text) const |
980 | { |
981 | Q_D(const QDoubleSpinBox); |
982 | |
983 | QString copy = text; |
984 | int pos = d->edit->cursorPosition(); |
985 | QValidator::State state = QValidator::Acceptable; |
986 | return d->validateAndInterpret(input&: copy, pos, state).toDouble(); |
987 | } |
988 | |
989 | /*! |
990 | \reimp |
991 | */ |
992 | QValidator::State QDoubleSpinBox::validate(QString &text, int &pos) const |
993 | { |
994 | Q_D(const QDoubleSpinBox); |
995 | |
996 | QValidator::State state; |
997 | d->validateAndInterpret(input&: text, pos, state); |
998 | return state; |
999 | } |
1000 | |
1001 | |
1002 | /*! |
1003 | \reimp |
1004 | */ |
1005 | void QDoubleSpinBox::fixup(QString &input) const |
1006 | { |
1007 | input.remove(s: locale().groupSeparator()); |
1008 | } |
1009 | |
1010 | // --- QSpinBoxPrivate --- |
1011 | |
1012 | /*! |
1013 | \internal |
1014 | Constructs a QSpinBoxPrivate object |
1015 | */ |
1016 | |
1017 | QSpinBoxPrivate::QSpinBoxPrivate() |
1018 | { |
1019 | minimum = QVariant((int)0); |
1020 | maximum = QVariant((int)99); |
1021 | value = minimum; |
1022 | displayIntegerBase = 10; |
1023 | singleStep = QVariant((int)1); |
1024 | type = QMetaType::Int; |
1025 | } |
1026 | |
1027 | /*! |
1028 | \internal |
1029 | \reimp |
1030 | */ |
1031 | |
1032 | void QSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old) |
1033 | { |
1034 | Q_Q(QSpinBox); |
1035 | if (ep != NeverEmit) { |
1036 | pendingEmit = false; |
1037 | if (ep == AlwaysEmit || value != old) { |
1038 | emit q->textChanged(edit->displayText()); |
1039 | emit q->valueChanged(value.toInt()); |
1040 | } |
1041 | } |
1042 | } |
1043 | |
1044 | /*! |
1045 | \internal |
1046 | \reimp |
1047 | */ |
1048 | |
1049 | QString QSpinBoxPrivate::textFromValue(const QVariant &value) const |
1050 | { |
1051 | Q_Q(const QSpinBox); |
1052 | return q->textFromValue(value: value.toInt()); |
1053 | } |
1054 | /*! |
1055 | \internal |
1056 | \reimp |
1057 | */ |
1058 | |
1059 | QVariant QSpinBoxPrivate::valueFromText(const QString &text) const |
1060 | { |
1061 | Q_Q(const QSpinBox); |
1062 | |
1063 | return QVariant(q->valueFromText(text)); |
1064 | } |
1065 | |
1066 | |
1067 | /*! |
1068 | \internal Multi purpose function that parses input, sets state to |
1069 | the appropriate state and returns the value it will be interpreted |
1070 | as. |
1071 | */ |
1072 | |
1073 | QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos, |
1074 | QValidator::State &state) const |
1075 | { |
1076 | if (cachedText == input && !input.isEmpty()) { |
1077 | state = cachedState; |
1078 | QSBDEBUG() << "cachedText was '" << cachedText << "' state was " |
1079 | << state << " and value was " << cachedValue; |
1080 | |
1081 | return cachedValue; |
1082 | } |
1083 | const int max = maximum.toInt(); |
1084 | const int min = minimum.toInt(); |
1085 | |
1086 | QString copy = stripped(text: input, pos: &pos); |
1087 | QSBDEBUG() << "input" << input << "copy" << copy; |
1088 | state = QValidator::Acceptable; |
1089 | int num = min; |
1090 | |
1091 | if (max != min && (copy.isEmpty() |
1092 | || (min < 0 && copy == "-"_L1 ) |
1093 | || (max >= 0 && copy == "+"_L1 ))) { |
1094 | state = QValidator::Intermediate; |
1095 | QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num; |
1096 | } else if (copy.startsWith(c: u'-') && min >= 0) { |
1097 | state = QValidator::Invalid; // special-case -0 will be interpreted as 0 and thus not be invalid with a range from 0-100 |
1098 | } else { |
1099 | bool ok = false; |
1100 | if (displayIntegerBase != 10) { |
1101 | num = copy.toInt(ok: &ok, base: displayIntegerBase); |
1102 | } else { |
1103 | num = locale.toInt(s: copy, ok: &ok); |
1104 | if (!ok && (max >= 1000 || min <= -1000)) { |
1105 | const QString sep(locale.groupSeparator()); |
1106 | const QString doubleSep = sep + sep; |
1107 | if (copy.contains(s: sep) && !copy.contains(s: doubleSep)) { |
1108 | QString copy2 = copy; |
1109 | copy2.remove(s: sep); |
1110 | num = locale.toInt(s: copy2, ok: &ok); |
1111 | } |
1112 | } |
1113 | } |
1114 | QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num; |
1115 | if (!ok) { |
1116 | state = QValidator::Invalid; |
1117 | } else if (num >= min && num <= max) { |
1118 | state = QValidator::Acceptable; |
1119 | } else if (max == min) { |
1120 | state = QValidator::Invalid; |
1121 | } else { |
1122 | if ((num >= 0 && num > max) || (num < 0 && num < min)) { |
1123 | state = QValidator::Invalid; |
1124 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1125 | } else { |
1126 | state = QValidator::Intermediate; |
1127 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate" ; |
1128 | } |
1129 | } |
1130 | } |
1131 | if (state != QValidator::Acceptable) |
1132 | num = max > 0 ? min : max; |
1133 | input = prefix + copy + suffix; |
1134 | cachedText = input; |
1135 | cachedState = state; |
1136 | cachedValue = QVariant((int)num); |
1137 | |
1138 | QSBDEBUG() << "cachedText is set to '" << cachedText << "' state is set to " |
1139 | << state << " and value is set to " << cachedValue; |
1140 | return cachedValue; |
1141 | } |
1142 | |
1143 | QVariant QSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const |
1144 | { |
1145 | const int intValue = value.toInt(); |
1146 | const int absValue = qAbs(t: intValue); |
1147 | |
1148 | if (absValue < 100) |
1149 | return 1; |
1150 | |
1151 | const bool valueNegative = intValue < 0; |
1152 | const bool stepsNegative = steps < 0; |
1153 | const int signCompensation = (valueNegative == stepsNegative) ? 0 : 1; |
1154 | |
1155 | const int log = static_cast<int>(std::log10(x: absValue - signCompensation)) - 1; |
1156 | return static_cast<int>(std::pow(x: 10, y: log)); |
1157 | } |
1158 | |
1159 | // --- QDoubleSpinBoxPrivate --- |
1160 | |
1161 | /*! |
1162 | \internal |
1163 | Constructs a QSpinBoxPrivate object |
1164 | */ |
1165 | |
1166 | QDoubleSpinBoxPrivate::QDoubleSpinBoxPrivate() |
1167 | { |
1168 | actualMin = 0.0; |
1169 | actualMax = 99.99; |
1170 | minimum = QVariant(actualMin); |
1171 | maximum = QVariant(actualMax); |
1172 | value = minimum; |
1173 | singleStep = QVariant(1.0); |
1174 | decimals = 2; |
1175 | type = QMetaType::Double; |
1176 | } |
1177 | |
1178 | /*! |
1179 | \internal |
1180 | \reimp |
1181 | */ |
1182 | |
1183 | void QDoubleSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old) |
1184 | { |
1185 | Q_Q(QDoubleSpinBox); |
1186 | if (ep != NeverEmit) { |
1187 | pendingEmit = false; |
1188 | if (ep == AlwaysEmit || value != old) { |
1189 | emit q->textChanged(edit->displayText()); |
1190 | emit q->valueChanged(value.toDouble()); |
1191 | } |
1192 | } |
1193 | } |
1194 | |
1195 | |
1196 | /*! |
1197 | \internal |
1198 | \reimp |
1199 | */ |
1200 | QVariant QDoubleSpinBoxPrivate::valueFromText(const QString &f) const |
1201 | { |
1202 | Q_Q(const QDoubleSpinBox); |
1203 | return QVariant(q->valueFromText(text: f)); |
1204 | } |
1205 | |
1206 | /*! |
1207 | \internal |
1208 | Rounds to a double value that is restricted to decimals. |
1209 | E.g. // decimals = 2 |
1210 | |
1211 | round(5.555) => 5.56 |
1212 | */ |
1213 | |
1214 | double QDoubleSpinBoxPrivate::round(double value) const |
1215 | { |
1216 | return QString::number(value, format: 'f', precision: decimals).toDouble(); |
1217 | } |
1218 | |
1219 | |
1220 | /*! |
1221 | \internal Multi purpose function that parses input, sets state to |
1222 | the appropriate state and returns the value it will be interpreted |
1223 | as. |
1224 | */ |
1225 | |
1226 | QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos, |
1227 | QValidator::State &state) const |
1228 | { |
1229 | if (cachedText == input && !input.isEmpty()) { |
1230 | state = cachedState; |
1231 | QSBDEBUG() << "cachedText was '" << cachedText << "' state was " |
1232 | << state << " and value was " << cachedValue; |
1233 | return cachedValue; |
1234 | } |
1235 | const double max = maximum.toDouble(); |
1236 | const double min = minimum.toDouble(); |
1237 | |
1238 | QString copy = stripped(text: input, pos: &pos); |
1239 | QSBDEBUG() << "input" << input << "copy" << copy; |
1240 | int len = copy.size(); |
1241 | double num = min; |
1242 | const bool plus = max >= 0; |
1243 | const bool minus = min <= 0; |
1244 | |
1245 | const QString group(locale.groupSeparator()); |
1246 | const uint groupUcs = (group.isEmpty() ? 0 : |
1247 | (group.size() > 1 && group.at(i: 0).isHighSurrogate() |
1248 | ? QChar::surrogateToUcs4(high: group.at(i: 0), low: group.at(i: 1)) |
1249 | : group.at(i: 0).unicode())); |
1250 | switch (len) { |
1251 | case 0: |
1252 | state = max != min ? QValidator::Intermediate : QValidator::Invalid; |
1253 | goto end; |
1254 | case 1: |
1255 | if (copy.at(i: 0) == locale.decimalPoint() |
1256 | || (plus && copy.at(i: 0) == u'+') |
1257 | || (minus && copy.at(i: 0) == u'-')) { |
1258 | state = QValidator::Intermediate; |
1259 | goto end; |
1260 | } |
1261 | break; |
1262 | case 2: |
1263 | if (copy.at(i: 1) == locale.decimalPoint() |
1264 | && ((plus && copy.at(i: 0) == u'+') || (minus && copy.at(i: 0) == u'-'))) { |
1265 | state = QValidator::Intermediate; |
1266 | goto end; |
1267 | } |
1268 | break; |
1269 | default: break; |
1270 | } |
1271 | |
1272 | if (groupUcs && copy.startsWith(s: group)) { |
1273 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1274 | state = QValidator::Invalid; |
1275 | goto end; |
1276 | } else if (len > 1) { |
1277 | const int dec = copy.indexOf(s: locale.decimalPoint()); |
1278 | if (dec != -1) { |
1279 | if (dec + 1 < copy.size() && copy.at(i: dec + 1) == locale.decimalPoint() && pos == dec + 1) { |
1280 | copy.remove(i: dec + 1, len: 1); // typing a delimiter when you are on the delimiter |
1281 | } // should be treated as typing right arrow |
1282 | |
1283 | if (copy.size() - dec > decimals + 1) { |
1284 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1285 | state = QValidator::Invalid; |
1286 | goto end; |
1287 | } |
1288 | for (int i = dec + 1; i < copy.size(); ++i) { |
1289 | if (copy.at(i).isSpace() |
1290 | || (groupUcs && QStringView{copy}.sliced(pos: i).startsWith(s: group))) { |
1291 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1292 | state = QValidator::Invalid; |
1293 | goto end; |
1294 | } |
1295 | } |
1296 | } else { |
1297 | const QChar last = copy.back(); |
1298 | const bool groupEnd = groupUcs && copy.endsWith(s: group); |
1299 | const QStringView head(copy.constData(), groupEnd ? len - group.size() : len - 1); |
1300 | const QChar secondLast = head.back(); |
1301 | if ((groupEnd || last.isSpace()) |
1302 | && ((groupUcs && head.endsWith(s: group)) || secondLast.isSpace())) { |
1303 | state = QValidator::Invalid; |
1304 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1305 | goto end; |
1306 | } else if (last.isSpace() && (!QChar::isSpace(ucs4: groupUcs) || secondLast.isSpace())) { |
1307 | state = QValidator::Invalid; |
1308 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1309 | goto end; |
1310 | } |
1311 | } |
1312 | } |
1313 | |
1314 | { |
1315 | bool ok = false; |
1316 | num = locale.toDouble(s: copy, ok: &ok); |
1317 | QSBDEBUG() << __FILE__ << __LINE__ << locale << copy << num << ok; |
1318 | |
1319 | if (!ok) { |
1320 | if (QChar::isPrint(ucs4: groupUcs)) { |
1321 | if (max < 1000 && min > -1000 && groupUcs && copy.contains(s: group)) { |
1322 | state = QValidator::Invalid; |
1323 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1324 | goto end; |
1325 | } |
1326 | |
1327 | const int len = copy.size(); |
1328 | for (int i = 0; i < len - 1;) { |
1329 | if (groupUcs && QStringView{copy}.sliced(pos: i).startsWith(s: group)) { |
1330 | if (QStringView(copy).mid(pos: i + group.size()).startsWith(s: group)) { |
1331 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1332 | state = QValidator::Invalid; |
1333 | goto end; |
1334 | } |
1335 | i += group.size(); |
1336 | } else { |
1337 | i++; |
1338 | } |
1339 | } |
1340 | |
1341 | QString copy2 = copy; |
1342 | if (groupUcs) |
1343 | copy2.remove(s: group); |
1344 | num = locale.toDouble(s: copy2, ok: &ok); |
1345 | QSBDEBUG() << group << num << copy2 << ok; |
1346 | |
1347 | if (!ok) { |
1348 | state = QValidator::Invalid; |
1349 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1350 | goto end; |
1351 | } |
1352 | } |
1353 | } |
1354 | |
1355 | if (!ok) { |
1356 | state = QValidator::Invalid; |
1357 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1358 | } else if (num >= min && num <= max) { |
1359 | state = QValidator::Acceptable; |
1360 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Acceptable" ; |
1361 | } else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min) |
1362 | state = QValidator::Invalid; |
1363 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1364 | } else { |
1365 | if ((num >= 0 && num > max) || (num < 0 && num < min)) { |
1366 | state = QValidator::Invalid; |
1367 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1368 | } else { |
1369 | state = QValidator::Intermediate; |
1370 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate" ; |
1371 | } |
1372 | } |
1373 | } |
1374 | |
1375 | end: |
1376 | if (state != QValidator::Acceptable) { |
1377 | num = max > 0 ? min : max; |
1378 | } |
1379 | |
1380 | input = prefix + copy + suffix; |
1381 | cachedText = input; |
1382 | cachedState = state; |
1383 | cachedValue = QVariant(num); |
1384 | return QVariant(num); |
1385 | } |
1386 | |
1387 | /* |
1388 | \internal |
1389 | \reimp |
1390 | */ |
1391 | |
1392 | QString QDoubleSpinBoxPrivate::textFromValue(const QVariant &f) const |
1393 | { |
1394 | Q_Q(const QDoubleSpinBox); |
1395 | return q->textFromValue(value: f.toDouble()); |
1396 | } |
1397 | |
1398 | QVariant QDoubleSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const |
1399 | { |
1400 | const double doubleValue = value.toDouble(); |
1401 | const double minStep = std::pow(x: 10, y: -decimals); |
1402 | double absValue = qAbs(t: doubleValue); |
1403 | |
1404 | if (absValue < minStep) |
1405 | return minStep; |
1406 | |
1407 | const bool valueNegative = doubleValue < 0; |
1408 | const bool stepsNegative = steps < 0; |
1409 | if (valueNegative != stepsNegative) |
1410 | absValue /= 1.01; |
1411 | |
1412 | const double shift = std::pow(x: 10, y: 1 - std::floor(x: std::log10(x: absValue))); |
1413 | const double absRounded = round(value: absValue * shift) / shift; |
1414 | const double log = floorf(x: std::log10(x: absRounded)) - 1; |
1415 | |
1416 | return std::max(a: minStep, b: std::pow(x: 10, y: log)); |
1417 | } |
1418 | |
1419 | /*! \reimp */ |
1420 | bool QSpinBox::event(QEvent *event) |
1421 | { |
1422 | Q_D(QSpinBox); |
1423 | if (event->type() == QEvent::StyleChange |
1424 | #ifdef Q_OS_MAC |
1425 | || event->type() == QEvent::MacSizeChange |
1426 | #endif |
1427 | ) |
1428 | d->setLayoutItemMargins(element: QStyle::SE_SpinBoxLayoutItem); |
1429 | return QAbstractSpinBox::event(event); |
1430 | } |
1431 | |
1432 | QT_END_NAMESPACE |
1433 | |
1434 | #include "moc_qspinbox.cpp" |
1435 | |