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
qquickdoublespinbox.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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
6
7#include <private/qquickcontrol_p_p.h>
8#include <private/qquickindicatorbutton_p.h>
9#include <private/qquicktextinput_p.h>
10
11#include <private/qqmlengine_p.h>
12
13#include <QtQml/qqmlinfo.h>
14
16
17/*!
18 \qmltype DoubleSpinBox
19 \inherits Control
20//! \nativetype QQuickDoubleSpinBox
21 \inqmlmodule QtQuick.Controls
22 \ingroup qtquickcontrols-input
23 \ingroup qtquickcontrols-focusscopes
24 \since 6.11
25 \brief Allows the user to select from a set of preset floating-point
26 values.
27
28 \image qtquickcontrols-doublespinbox.png
29 {Spin box displaying decimal values}
30
31 DoubleSpinBox is similar to \l SpinBox, except that it supports double
32 values rather than integer. The user can choose a value by clicking the up
33 or down indicator buttons, or by pressing up or down on the keyboard.
34 Optionally, DoubleSpinBox can be also made \l editable, so the user can
35 enter a text value in the input field.
36
37 By default, DoubleSpinBox provides discrete values in the range of
38 \c [0.00-99.99] with a \l stepSize of \c 1.0.
39
40 \snippet qtquickcontrols-doublespinbox.qml 1
41
42 \sa SpinBox, Tumbler, {Customizing DoubleSpinBox},
43 {Focus Management in Qt Quick Controls}
44*/
45
46/*!
47 \include qquickspinbox.qdocinc {valueModified} {DoubleSpinBox}
48*/
49
52{
53 Q_DECLARE_PUBLIC(QQuickDoubleSpinBox)
54
55public:
57
58 bool setValue(double newValue, bool wrap, ValueStatus modified) override;
59
62
64
65 QString evaluateTextFromValue(double val, int decimals) const;
66 double evaluateValueFromText(const QString &text) const;
67
68 double round(double input) const;
69
70 int decimals = 2;
71
72private:
74};
75
76QQuickDoubleSpinBoxPrivate::QQuickDoubleSpinBoxPrivate()
77 : QQuickAbstractSpinBox<QQuickDoubleSpinBox, double>::QQuickAbstractSpinBoxPrivate()
78{
79 from = 0.0;
80 to = 99.99;
81}
82
83/*!
84 \internal
85
86 modified indicates if the value was modified by the user and not programatically.
87 This is then passed on to updateDisplayText to indicate that the user has modified
88 the value, so it may need to trigger an update of the contentItem's text, too.
89*/
90
91bool QQuickDoubleSpinBoxPrivate::setValue(double newValue, bool wrap, ValueStatus modified)
92{
93 Q_Q(QQuickDoubleSpinBox);
94 double correctedValue = newValue;
95 if (q->isComponentComplete())
96 correctedValue = boundValue(newValue, wrap);
97
98 if (modified == ValueStatus::Unmodified && qFuzzyCompare(newValue, correctedValue)
99 && qFuzzyCompare(newValue, value))
100 return false;
101
102 const bool emitSignals = (value != correctedValue);
103 value = correctedValue;
104
106 updateUpEnabled();
107 updateDownEnabled();
108
109 // Only emit the signals if the corrected value is not the same as the
110 // original value to avoid unnecessary updates.
111 if (emitSignals) {
112 emit q->valueChanged();
113 if (modified == ValueStatus::Modified)
114 emit q->valueModified();
115 }
116 return true;
117}
118
120{
121 if (!contentItem)
122 return;
123
124 const QVariant text = contentItem->property("text");
125 if (text.isValid())
126 setValue(evaluateValueFromText(text.toString()), false /* wrap */, ValueStatus::Modified);
127}
128
130{
131 setDisplayText(evaluateTextFromValue(value, decimals));
132}
133
135{
136 Q_Q(QQuickDoubleSpinBox);
137
138 QQuickTextInput *inputTextItem = qobject_cast<QQuickTextInput *>(q->contentItem());
139 if (!inputTextItem)
140 return;
141 setDisplayText(inputTextItem->text());
142}
143
145{
146 Q_Q(const QQuickDoubleSpinBox);
147
148 QString text;
149 QQmlEngine *engine = qmlEngine(q);
150 if (engine && textFromValue.isCallable()) {
151 QJSValue loc;
152#if QT_CONFIG(qml_locale)
153 loc = QJSValuePrivate::fromReturnedValue(
154 engine->handle()->fromData(QMetaType::fromType<QLocale>(), &locale));
155#endif
156 text = textFromValue.call(QJSValueList() << val << decimals << loc).toString();
157 } else {
158 text = locale.toString(val, 'f', decimals);
159 }
160 return text;
161}
162
163double QQuickDoubleSpinBoxPrivate::evaluateValueFromText(const QString &text) const
164{
165 Q_Q(const QQuickDoubleSpinBox);
166 double value;
167 QQmlEngine *engine = qmlEngine(q);
168 if (engine && valueFromText.isCallable()) {
169 QJSValue loc;
170#if QT_CONFIG(qml_locale)
171 loc = QJSValuePrivate::fromReturnedValue(
172 engine->handle()->fromData(QMetaType::fromType<QLocale>(), &locale));
173#endif
174 value = valueFromText.call(QJSValueList() << text << loc).toNumber();
175 } else {
176 value = locale.toDouble(text);
177 }
178 return value;
179}
180
181double QQuickDoubleSpinBoxPrivate::round(double value) const
182{
183 return QString::number(value, 'f', decimals).toDouble();
184}
185
186QQuickDoubleSpinBox::QQuickDoubleSpinBox(QQuickItem *parent)
187 : QQuickControl(*(new QQuickDoubleSpinBoxPrivate), parent),
188 QQuickAbstractSpinBox<QQuickDoubleSpinBox, double>()
189{
190}
191
192QQuickDoubleSpinBox::~QQuickDoubleSpinBox() { }
193
194/*!
195 \include qquickspinbox.qdocinc {from} {DoubleSpinBox} {0.0}
196*/
197
198void QQuickDoubleSpinBox::setFrom(double from)
199{
200 Q_D(QQuickDoubleSpinBox);
201 const double newFrom = d->round(from);
202 if (qFuzzyCompare(d->from, newFrom))
203 return;
204
205 d->from = newFrom;
206 emit fromChanged();
207 if (isComponentComplete()) {
208 if (!d->setValue(d->round(d->value), false /* wrap */,
209 QQuickDoubleSpinBoxPrivate::ValueStatus::Unmodified)) {
210 d->updateUpEnabled();
211 d->updateDownEnabled();
212 }
213 }
214}
215
216/*!
217 \include qquickspinbox.qdocinc {to} {DoubleSpinBox} {99.99}
218*/
219
220void QQuickDoubleSpinBox::setTo(double to)
221{
222 Q_D(QQuickDoubleSpinBox);
223 const double newTo = d->round(to);
224 if (qFuzzyCompare(d->to, newTo))
225 return;
226
227 d->to = newTo;
228 emit toChanged();
229 if (isComponentComplete()) {
230 if (!d->setValue(d->round(d->value), false /* wrap */,
231 QQuickDoubleSpinBoxPrivate::ValueStatus::Unmodified)) {
232 d->updateUpEnabled();
233 d->updateDownEnabled();
234 }
235 }
236}
237
238/*!
239 \include qquickspinbox.qdocinc {value-prop} {double} {DoubleSpinBox} {0.0}
240*/
241
242void QQuickDoubleSpinBox::setValue(double value)
243{
244 Q_D(QQuickDoubleSpinBox);
245 d->setValue(d->round(value), false /* wrap */,
246 QQuickDoubleSpinBoxPrivate::ValueStatus::Unmodified);
247}
248
249/*!
250 \include qquickspinbox.qdocinc {stepSize} {DoubleSpinBox} {1.0}
251*/
252
253void QQuickDoubleSpinBox::setStepSize(double step)
254{
255 Q_D(QQuickDoubleSpinBox);
256 if (qFuzzyCompare(d->stepSize, step))
257 return;
258
259 d->stepSize = step;
260 emit stepSizeChanged();
261}
262
263/*!
264 \qmlproperty int QtQuick.Controls::DoubleSpinBox::decimals
265
266 This property holds how many decimals the spinbox will use for displaying
267 and interpreting doubles.
268
269 The default value is \c 2.
270
271 Changing the value of this property may affect \l from, \l to and \l value.
272
273 \warning The maximum value for \a decimals is \c {DBL_MAX_10_EXP +
274 DBL_DIG} (\c 323) because of the limitations of the double type.
275*/
276
277int QQuickDoubleSpinBox::decimals() const
278{
279 Q_D(const QQuickDoubleSpinBox);
280
281 return d->decimals;
282}
283
284void QQuickDoubleSpinBox::setDecimals(int decimals)
285{
286 Q_D(QQuickDoubleSpinBox);
287 d->decimals = qBound(0, decimals, DBL_MAX_10_EXP + DBL_DIG);
288
289 // Make sure values are rounded.
290 setFrom(from());
291 setTo(to());
292 setValue(value());
293 d->updateValue();
294 emit decimalsChanged();
295}
296
297/*!
298 \include qquickspinbox.qdocinc {editable} {DoubleSpinBox}
299*/
300
301#if QT_CONFIG(validator)
302/*!
303 \qmlproperty Validator QtQuick.Controls::DoubleSpinBox::validator
304
305 This property holds the input text validator for editable spinboxes. By
306 default, DoubleSpinBox uses \l DoubleValidator to accept input of double numbers.
307
308 \code
309 DoubleSpinBox {
310 id: control
311 validator: DoubleValidator {
312 locale: control.locale.name
313 bottom: Math.min(control.from, control.to)
314 top: Math.max(control.from, control.to)
315 }
316 }
317 \endcode
318
319 \sa editable, textFromValue, valueFromText, {Control::locale}{locale},
320 {Validating Input Text}
321*/
322#endif
323
324/*!
325 \qmlproperty function QtQuick.Controls::DoubleSpinBox::textFromValue
326
327 This property holds a callback function that is called whenever
328 a double value needs to be converted to display text.
329
330 The default function can be overridden to display custom text for a given
331 value. This applies to both editable and non-editable spinboxes;
332 for example, when using the up and down buttons or a mouse wheel to
333 increment and decrement the value, the new value is converted to display
334 text using this function.
335
336 The callback function signature is \c {string function(value, decimals, locale)}.
337 The function can have two or three arguments, where the first argument
338 is the value to be converted, the second argument is the number of decimals,
339 and the optional third argument is the locale that should be used for
340 the conversion, if applicable.
341
342 The default implementation does the conversion using
343 \l {string Number::toLocaleString(locale, format, precision)}{Number.toLocaleString}():
344
345 \code
346 textFromValue: function(value, decimals, locale) {
347 return Number(value).toLocaleString(locale, 'f', decimals);
348 }
349 \endcode
350
351 \note When applying a custom \c textFromValue implementation for editable
352 spinboxes, a matching \l valueFromText implementation must be provided
353 to be able to convert the custom text back to a double value.
354
355 \sa valueFromText, validator, {Control::locale}{locale}
356*/
357QJSValue QQuickDoubleSpinBox::textFromValue() const
358{
359 Q_D(const QQuickDoubleSpinBox);
360 if (!d->textFromValue.isCallable()) {
361 QQmlEngine *engine = qmlEngine(this);
362 if (engine) {
363 d->textFromValue = engine->evaluate(
364 QStringLiteral("(function(value, decimals, locale) { return "
365 "Number(value).toLocaleString(locale, 'f', decimals); })"));
366 }
367 }
368 return d->textFromValue;
369}
370
371/*!
372 \qmlproperty function QtQuick.Controls::DoubleSpinBox::valueFromText
373
374 This property holds a callback function that is called whenever
375 input text needs to be converted to a double value.
376
377 This function only needs to be overridden when \l textFromValue
378 is overridden for an editable spinbox.
379
380 The callback function signature is \c {int function(text, locale)}.
381 The function can have one or two arguments, where the first argument
382 is the text to be converted, and the optional second argument is the
383 locale that should be used for the conversion, if applicable.
384
385 The default implementation does the conversion using
386 \l {QtQml::Locale}{Number.fromLocaleString()}:
387
388 \code
389 valueFromText: function(text, locale) { return Number.fromLocaleString(locale, text); }
390 \endcode
391
392 \note When applying a custom \l textFromValue implementation for editable
393 spinboxes, a matching \c valueFromText implementation must be provided
394 to be able to convert the custom text back to a double value.
395
396 \sa textFromValue, validator, {Control::locale}{locale}
397*/
398QJSValue QQuickDoubleSpinBox::valueFromText() const
399{
400 Q_D(const QQuickDoubleSpinBox);
401 if (!d->valueFromText.isCallable()) {
402 QQmlEngine *engine = qmlEngine(this);
403 if (engine)
404 d->valueFromText = engine->evaluate(QStringLiteral(
405 "(function(text, locale) { return Number.fromLocaleString(locale, text); })"));
406 }
407 return d->valueFromText;
408}
409
410/*!
411 \include qquickindicatorbutton.qdocinc {properties} {DoubleSpinBox} {up}
412 \sa increase()
413*/
414
415/*!
416 \include qquickindicatorbutton.qdocinc {properties} {DoubleSpinBox} {down}
417 \sa decrease()
418*/
419
420/*!
421 \include qquickspinbox.qdocinc {inputMethodHints} {DoubleSpinBox}
422*/
423
424/*!
425 \include qquickspinbox.qdocinc {inputMethodComposing} {DoubleSpinBox}
426*/
427
428/*!
429 \since QtQuick.Controls 2.3 (Qt 5.10)
430 \include qquickspinbox.qdocinc {wrap} {DoubleSpinBox}
431*/
432
433void QQuickDoubleSpinBox::setWrap(bool wrap)
434{
435 Q_D(QQuickDoubleSpinBox);
436 if (d->wrap == wrap)
437 return;
438
439 d->wrap = wrap;
440 if (qFuzzyCompare(d->value, d->from) || qFuzzyCompare(d->value, d->to)) {
441 d->updateUpEnabled();
442 d->updateDownEnabled();
443 }
444 emit wrapChanged();
445}
446
447/*!
448 \qmlproperty string QtQuick.Controls::DoubleSpinBox::displayText
449 \readonly
450
451 This property holds the textual value of the spinbox.
452
453 The value of the property is based on \l textFromValue and \l {Control::}
454 {locale}, and equal to:
455 \badcode
456 let text = spinBox.textFromValue(spinBox.value, spinBox.decimals, spinBox.locale)
457 \endcode
458
459 \sa textFromValue
460*/
461
462/*!
463 \include qquickspinbox.qdocinc {increase} {DoubleSpinBox}
464*/
465
466/*!
467 \include qquickspinbox.qdocinc {decrease} {DoubleSpinBox}
468*/
469
470void QQuickDoubleSpinBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
471{
472 Q_D(QQuickDoubleSpinBox);
473 if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
474 disconnect(oldInput, &QQuickTextInput::inputMethodComposingChanged, this,
475 &QQuickDoubleSpinBox::inputMethodComposingChanged);
476 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d,
477 &QQuickDoubleSpinBoxPrivate::contentItemTextChanged);
478 }
479
480 if (newItem) {
481 newItem->setActiveFocusOnTab(true);
482 if (d->activeFocus)
483 newItem->forceActiveFocus(static_cast<Qt::FocusReason>(d->focusReason));
484#if QT_CONFIG(cursor)
485 if (d->editable)
486 newItem->setCursor(Qt::IBeamCursor);
487#endif
488
489 if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
490 connect(newInput, &QQuickTextInput::inputMethodComposingChanged, this,
491 &QQuickDoubleSpinBox::inputMethodComposingChanged);
492 QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d,
493 &QQuickDoubleSpinBoxPrivate::contentItemTextChanged);
494 }
495 }
496}
497
498QQuickControlPrivate *QQuickDoubleSpinBox::d_base_func()
499{
500 Q_D(QQuickDoubleSpinBox);
501 return d;
502}
503const QQuickControlPrivate *QQuickDoubleSpinBox::d_base_func() const
504{
505 Q_D(const QQuickDoubleSpinBox);
506 return d;
507}
508
509QT_END_NAMESPACE
510
511#include "moc_qquickdoublespinbox_p.cpp"
Allows the user to select from a set of preset floating-point values.
bool setValue(double newValue, bool wrap, ValueStatus modified) override
QString evaluateTextFromValue(double val, int decimals) const
double evaluateValueFromText(const QString &text) const
double round(double input) const
Combined button and popup list for selecting options.