1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qquickspinbox_p.h"
38#include "qquickcontrol_p_p.h"
39#include "qquickdeferredexecute_p_p.h"
40
41#include <QtGui/qguiapplication.h>
42#include <QtGui/qstylehints.h>
43
44#include <QtQml/qqmlinfo.h>
45#include <QtQml/private/qqmllocale_p.h>
46#include <QtQml/private/qqmlengine_p.h>
47#include <QtQuick/private/qquicktextinput_p.h>
48
49QT_BEGIN_NAMESPACE
50
51// copied from qabstractbutton.cpp
52static const int AUTO_REPEAT_DELAY = 300;
53static const int AUTO_REPEAT_INTERVAL = 100;
54
55/*!
56 \qmltype SpinBox
57 \inherits Control
58//! \instantiates QQuickSpinBox
59 \inqmlmodule QtQuick.Controls
60 \since 5.7
61 \ingroup input
62 \ingroup qtquickcontrols2-focusscopes
63 \brief Allows the user to select from a set of preset values.
64
65 \image qtquickcontrols2-spinbox.png
66
67 SpinBox allows the user to choose an integer value by clicking the up
68 or down indicator buttons, or by pressing up or down on the keyboard.
69 Optionally, SpinBox can be also made \l editable, so the user can enter
70 a text value in the input field.
71
72 By default, SpinBox provides discrete values in the range of \c [0-99]
73 with a \l stepSize of \c 1.
74
75 \snippet qtquickcontrols2-spinbox.qml 1
76
77 \section2 Custom Values
78
79 \image qtquickcontrols2-spinbox-textual.png
80
81 Even though SpinBox works on integer values, it can be customized to
82 accept arbitrary input values. The following snippet demonstrates how
83 \l validator, \l textFromValue and \l valueFromText can be used to
84 customize the default behavior.
85
86 \snippet qtquickcontrols2-spinbox-textual.qml 1
87
88 In the same manner, SpinBox can be customized to accept floating point
89 numbers:
90
91 \image qtquickcontrols2-spinbox-double.png
92
93 \snippet qtquickcontrols2-spinbox-double.qml 1
94
95 \sa Tumbler, {Customizing SpinBox}, {Focus Management in Qt Quick Controls}
96*/
97
98/*!
99 \since QtQuick.Controls 2.2 (Qt 5.9)
100 \qmlsignal QtQuick.Controls::SpinBox::valueModified()
101
102 This signal is emitted when the spin box value has been interactively
103 modified by the user by either touch, mouse, wheel, or keys.
104*/
105
106class QQuickSpinBoxPrivate : public QQuickControlPrivate
107{
108 Q_DECLARE_PUBLIC(QQuickSpinBox)
109
110public:
111 int boundValue(int value, bool wrap) const;
112 void updateValue();
113 bool setValue(int value, bool wrap, bool modified);
114 bool stepBy(int steps, bool modified);
115 void increase(bool modified);
116 void decrease(bool modified);
117
118 int effectiveStepSize() const;
119
120 void updateDisplayText(bool modified = false);
121 void setDisplayText(const QString &displayText, bool modified = false);
122
123 bool upEnabled() const;
124 void updateUpEnabled();
125 bool downEnabled() const;
126 void updateDownEnabled();
127 void updateHover(const QPointF &pos);
128
129 void startRepeatDelay();
130 void startPressRepeat();
131 void stopPressRepeat();
132
133 void handlePress(const QPointF &point) override;
134 void handleMove(const QPointF &point) override;
135 void handleRelease(const QPointF &point) override;
136 void handleUngrab() override;
137
138 void itemImplicitWidthChanged(QQuickItem *item) override;
139 void itemImplicitHeightChanged(QQuickItem *item) override;
140
141 bool editable = false;
142 bool wrap = false;
143 int from = 0;
144 int to = 99;
145 int value = 0;
146 int stepSize = 1;
147 int delayTimer = 0;
148 int repeatTimer = 0;
149 QString displayText;
150 QQuickSpinButton *up = nullptr;
151 QQuickSpinButton *down = nullptr;
152 QValidator *validator = nullptr;
153 mutable QJSValue textFromValue;
154 mutable QJSValue valueFromText;
155 Qt::InputMethodHints inputMethodHints = Qt::ImhDigitsOnly;
156};
157
158class QQuickSpinButtonPrivate : public QObjectPrivate
159{
160 Q_DECLARE_PUBLIC(QQuickSpinButton)
161
162public:
163 static QQuickSpinButtonPrivate *get(QQuickSpinButton *button)
164 {
165 return button->d_func();
166 }
167
168 void cancelIndicator();
169 void executeIndicator(bool complete = false);
170
171 bool pressed = false;
172 bool hovered = false;
173 QQuickDeferredPointer<QQuickItem> indicator;
174};
175
176int QQuickSpinBoxPrivate::boundValue(int value, bool wrap) const
177{
178 bool inverted = from > to;
179 if (!wrap)
180 return inverted ? qBound(min: to, val: value, max: from) : qBound(min: from, val: value, max: to);
181
182 int f = inverted ? to : from;
183 int t = inverted ? from : to;
184 if (value < f)
185 value = t;
186 else if (value > t)
187 value = f;
188
189 return value;
190}
191
192void QQuickSpinBoxPrivate::updateValue()
193{
194 Q_Q(QQuickSpinBox);
195 if (contentItem) {
196 QVariant text = contentItem->property(name: "text");
197 if (text.isValid()) {
198 int val = 0;
199 QQmlEngine *engine = qmlEngine(q);
200 if (engine && valueFromText.isCallable()) {
201 QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(e: engine);
202 QJSValue loc(v4, QQmlLocale::wrap(engine: v4, locale));
203 val = valueFromText.call(args: QJSValueList() << text.toString() << loc).toInt();
204 } else {
205 val = locale.toInt(s: text.toString());
206 }
207 setValue(value: val, /* allowWrap = */ wrap: false, /* modified = */ true);
208 }
209 }
210}
211
212// modified indicates if the value was modified by the user and not programatically
213// this is then passed on to updateDisplayText to indicate that the user has modified
214// the value so it may need to trigger an update of the contentItem's text too
215
216bool QQuickSpinBoxPrivate::setValue(int newValue, bool allowWrap, bool modified)
217{
218 Q_Q(QQuickSpinBox);
219 int correctedValue = newValue;
220 if (q->isComponentComplete())
221 correctedValue = boundValue(value: newValue, wrap: allowWrap);
222
223 if (!modified && newValue == correctedValue && newValue == value)
224 return false;
225
226 const bool emitSignals = (value != correctedValue);
227 value = correctedValue;
228
229 updateDisplayText(modified);
230 updateUpEnabled();
231 updateDownEnabled();
232
233 // Only emit the signals if the corrected value is not the same as the
234 // original value to avoid unnecessary updates
235 if (emitSignals) {
236 emit q->valueChanged();
237 if (modified)
238 emit q->valueModified();
239 }
240 return true;
241}
242
243bool QQuickSpinBoxPrivate::stepBy(int steps, bool modified)
244{
245 return setValue(newValue: value + steps, allowWrap: wrap, modified);
246}
247
248void QQuickSpinBoxPrivate::increase(bool modified)
249{
250 setValue(newValue: value + effectiveStepSize(), allowWrap: wrap, modified);
251}
252
253void QQuickSpinBoxPrivate::decrease(bool modified)
254{
255 setValue(newValue: value - effectiveStepSize(), allowWrap: wrap, modified);
256}
257
258int QQuickSpinBoxPrivate::effectiveStepSize() const
259{
260 return from > to ? -1 * stepSize : stepSize;
261}
262
263void QQuickSpinBoxPrivate::updateDisplayText(bool modified)
264{
265 Q_Q(QQuickSpinBox);
266 QString text;
267 QQmlEngine *engine = qmlEngine(q);
268 if (engine && textFromValue.isCallable()) {
269 QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(e: engine);
270 QJSValue loc(v4, QQmlLocale::wrap(engine: v4, locale));
271 text = textFromValue.call(args: QJSValueList() << value << loc).toString();
272 } else {
273 text = locale.toString(i: value);
274 }
275 setDisplayText(displayText: text, modified);
276}
277
278void QQuickSpinBoxPrivate::setDisplayText(const QString &text, bool modified)
279{
280 Q_Q(QQuickSpinBox);
281
282 if (!modified && displayText == text)
283 return;
284
285 displayText = text;
286 emit q->displayTextChanged();
287}
288
289bool QQuickSpinBoxPrivate::upEnabled() const
290{
291 const QQuickItem *upIndicator = up->indicator();
292 return upIndicator && upIndicator->isEnabled();
293}
294
295void QQuickSpinBoxPrivate::updateUpEnabled()
296{
297 QQuickItem *upIndicator = up->indicator();
298 if (!upIndicator)
299 return;
300
301 upIndicator->setEnabled(wrap || (from < to ? value < to : value > to));
302}
303
304bool QQuickSpinBoxPrivate::downEnabled() const
305{
306 const QQuickItem *downIndicator = down->indicator();
307 return downIndicator && downIndicator->isEnabled();
308}
309
310void QQuickSpinBoxPrivate::updateDownEnabled()
311{
312 QQuickItem *downIndicator = down->indicator();
313 if (!downIndicator)
314 return;
315
316 downIndicator->setEnabled(wrap || (from < to ? value > from : value < from));
317}
318
319void QQuickSpinBoxPrivate::updateHover(const QPointF &pos)
320{
321 Q_Q(QQuickSpinBox);
322 QQuickItem *ui = up->indicator();
323 QQuickItem *di = down->indicator();
324 up->setHovered(ui && ui->isEnabled() && ui->contains(point: q->mapToItem(item: ui, point: pos)));
325 down->setHovered(di && di->isEnabled() && di->contains(point: q->mapToItem(item: di, point: pos)));
326}
327
328void QQuickSpinBoxPrivate::startRepeatDelay()
329{
330 Q_Q(QQuickSpinBox);
331 stopPressRepeat();
332 delayTimer = q->startTimer(interval: AUTO_REPEAT_DELAY);
333}
334
335void QQuickSpinBoxPrivate::startPressRepeat()
336{
337 Q_Q(QQuickSpinBox);
338 stopPressRepeat();
339 repeatTimer = q->startTimer(interval: AUTO_REPEAT_INTERVAL);
340}
341
342void QQuickSpinBoxPrivate::stopPressRepeat()
343{
344 Q_Q(QQuickSpinBox);
345 if (delayTimer > 0) {
346 q->killTimer(id: delayTimer);
347 delayTimer = 0;
348 }
349 if (repeatTimer > 0) {
350 q->killTimer(id: repeatTimer);
351 repeatTimer = 0;
352 }
353}
354
355void QQuickSpinBoxPrivate::handlePress(const QPointF &point)
356{
357 Q_Q(QQuickSpinBox);
358 QQuickControlPrivate::handlePress(point);
359 QQuickItem *ui = up->indicator();
360 QQuickItem *di = down->indicator();
361 up->setPressed(ui && ui->isEnabled() && ui->contains(point: ui->mapFromItem(item: q, point)));
362 down->setPressed(di && di->isEnabled() && di->contains(point: di->mapFromItem(item: q, point)));
363
364 bool pressed = up->isPressed() || down->isPressed();
365 q->setAccessibleProperty(propertyName: "pressed", value: pressed);
366 if (pressed)
367 startRepeatDelay();
368}
369
370void QQuickSpinBoxPrivate::handleMove(const QPointF &point)
371{
372 Q_Q(QQuickSpinBox);
373 QQuickControlPrivate::handleMove(point);
374 QQuickItem *ui = up->indicator();
375 QQuickItem *di = down->indicator();
376 up->setHovered(ui && ui->isEnabled() && ui->contains(point: ui->mapFromItem(item: q, point)));
377 up->setPressed(up->isHovered());
378 down->setHovered(di && di->isEnabled() && di->contains(point: di->mapFromItem(item: q, point)));
379 down->setPressed(down->isHovered());
380
381 bool pressed = up->isPressed() || down->isPressed();
382 q->setAccessibleProperty(propertyName: "pressed", value: pressed);
383 if (!pressed)
384 stopPressRepeat();
385}
386
387void QQuickSpinBoxPrivate::handleRelease(const QPointF &point)
388{
389 Q_Q(QQuickSpinBox);
390 QQuickControlPrivate::handleRelease(point);
391 QQuickItem *ui = up->indicator();
392 QQuickItem *di = down->indicator();
393
394 int oldValue = value;
395 if (up->isPressed()) {
396 up->setPressed(false);
397 if (repeatTimer <= 0 && ui && ui->contains(point: ui->mapFromItem(item: q, point)))
398 q->increase();
399 } else if (down->isPressed()) {
400 down->setPressed(false);
401 if (repeatTimer <= 0 && di && di->contains(point: di->mapFromItem(item: q, point)))
402 q->decrease();
403 }
404 if (value != oldValue)
405 emit q->valueModified();
406
407 q->setAccessibleProperty(propertyName: "pressed", value: false);
408 stopPressRepeat();
409}
410
411void QQuickSpinBoxPrivate::handleUngrab()
412{
413 Q_Q(QQuickSpinBox);
414 QQuickControlPrivate::handleUngrab();
415 up->setPressed(false);
416 down->setPressed(false);
417
418 q->setAccessibleProperty(propertyName: "pressed", value: false);
419 stopPressRepeat();
420}
421
422void QQuickSpinBoxPrivate::itemImplicitWidthChanged(QQuickItem *item)
423{
424 QQuickControlPrivate::itemImplicitWidthChanged(item);
425 if (item == up->indicator())
426 emit up->implicitIndicatorWidthChanged();
427 else if (item == down->indicator())
428 emit down->implicitIndicatorWidthChanged();
429}
430
431void QQuickSpinBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
432{
433 QQuickControlPrivate::itemImplicitHeightChanged(item);
434 if (item == up->indicator())
435 emit up->implicitIndicatorHeightChanged();
436 else if (item == down->indicator())
437 emit down->implicitIndicatorHeightChanged();
438}
439
440QQuickSpinBox::QQuickSpinBox(QQuickItem *parent)
441 : QQuickControl(*(new QQuickSpinBoxPrivate), parent)
442{
443 Q_D(QQuickSpinBox);
444 d->up = new QQuickSpinButton(this);
445 d->down = new QQuickSpinButton(this);
446
447 setFlag(flag: ItemIsFocusScope);
448 setFiltersChildMouseEvents(true);
449 setAcceptedMouseButtons(Qt::LeftButton);
450#if QT_CONFIG(cursor)
451 setCursor(Qt::ArrowCursor);
452#endif
453}
454
455QQuickSpinBox::~QQuickSpinBox()
456{
457 Q_D(QQuickSpinBox);
458 d->removeImplicitSizeListener(item: d->up->indicator());
459 d->removeImplicitSizeListener(item: d->down->indicator());
460}
461
462/*!
463 \qmlproperty int QtQuick.Controls::SpinBox::from
464
465 This property holds the starting value for the range. The default value is \c 0.
466
467 \sa to, value
468*/
469int QQuickSpinBox::from() const
470{
471 Q_D(const QQuickSpinBox);
472 return d->from;
473}
474
475void QQuickSpinBox::setFrom(int from)
476{
477 Q_D(QQuickSpinBox);
478 if (d->from == from)
479 return;
480
481 d->from = from;
482 emit fromChanged();
483 if (isComponentComplete()) {
484 if (!d->setValue(newValue: d->value, /* allowWrap = */ false, /* modified = */ false)) {
485 d->updateUpEnabled();
486 d->updateDownEnabled();
487 }
488 }
489}
490
491/*!
492 \qmlproperty int QtQuick.Controls::SpinBox::to
493
494 This property holds the end value for the range. The default value is \c 99.
495
496 \sa from, value
497*/
498int QQuickSpinBox::to() const
499{
500 Q_D(const QQuickSpinBox);
501 return d->to;
502}
503
504void QQuickSpinBox::setTo(int to)
505{
506 Q_D(QQuickSpinBox);
507 if (d->to == to)
508 return;
509
510 d->to = to;
511 emit toChanged();
512 if (isComponentComplete()) {
513 if (!d->setValue(newValue: d->value, /* allowWrap = */false, /* modified = */ false)) {
514 d->updateUpEnabled();
515 d->updateDownEnabled();
516 }
517 }
518}
519
520/*!
521 \qmlproperty int QtQuick.Controls::SpinBox::value
522
523 This property holds the value in the range \c from - \c to. The default value is \c 0.
524*/
525int QQuickSpinBox::value() const
526{
527 Q_D(const QQuickSpinBox);
528 return d->value;
529}
530
531void QQuickSpinBox::setValue(int value)
532{
533 Q_D(QQuickSpinBox);
534 d->setValue(newValue: value, /* allowWrap = */ false, /* modified = */ false);
535}
536
537/*!
538 \qmlproperty int QtQuick.Controls::SpinBox::stepSize
539
540 This property holds the step size. The default value is \c 1.
541
542 \sa increase(), decrease()
543*/
544int QQuickSpinBox::stepSize() const
545{
546 Q_D(const QQuickSpinBox);
547 return d->stepSize;
548}
549
550void QQuickSpinBox::setStepSize(int step)
551{
552 Q_D(QQuickSpinBox);
553 if (d->stepSize == step)
554 return;
555
556 d->stepSize = step;
557 emit stepSizeChanged();
558}
559
560/*!
561 \qmlproperty bool QtQuick.Controls::SpinBox::editable
562
563 This property holds whether the spinbox is editable. The default value is \c false.
564
565 \sa validator
566*/
567bool QQuickSpinBox::isEditable() const
568{
569 Q_D(const QQuickSpinBox);
570 return d->editable;
571}
572
573void QQuickSpinBox::setEditable(bool editable)
574{
575 Q_D(QQuickSpinBox);
576 if (d->editable == editable)
577 return;
578
579#if QT_CONFIG(cursor)
580 if (d->contentItem) {
581 if (editable)
582 d->contentItem->setCursor(Qt::IBeamCursor);
583 else
584 d->contentItem->unsetCursor();
585 }
586#endif
587
588 d->editable = editable;
589 setAccessibleProperty(propertyName: "editable", value: editable);
590 emit editableChanged();
591}
592
593/*!
594 \qmlproperty Validator QtQuick.Controls::SpinBox::validator
595
596 This property holds the input text validator for editable spinboxes. By
597 default, SpinBox uses \l IntValidator to accept input of integer numbers.
598
599 \code
600 SpinBox {
601 id: control
602 validator: IntValidator {
603 locale: control.locale.name
604 bottom: Math.min(control.from, control.to)
605 top: Math.max(control.from, control.to)
606 }
607 }
608 \endcode
609
610 \sa editable, textFromValue, valueFromText, {Control::locale}{locale}
611*/
612QValidator *QQuickSpinBox::validator() const
613{
614 Q_D(const QQuickSpinBox);
615 return d->validator;
616}
617
618void QQuickSpinBox::setValidator(QValidator *validator)
619{
620 Q_D(QQuickSpinBox);
621 if (d->validator == validator)
622 return;
623
624 d->validator = validator;
625 emit validatorChanged();
626}
627
628/*!
629 \qmlproperty function QtQuick.Controls::SpinBox::textFromValue
630
631 This property holds a callback function that is called whenever
632 an integer value needs to be converted to display text.
633
634 The default function can be overridden to display custom text for a given
635 value. This applies to both editable and non-editable spinboxes;
636 for example, when using the up and down buttons or a mouse wheel to
637 increment and decrement the value, the new value is converted to display
638 text using this function.
639
640 The callback function signature is \c {string function(value, locale)}.
641 The function can have one or two arguments, where the first argument
642 is the value to be converted, and the optional second argument is the
643 locale that should be used for the conversion, if applicable.
644
645 The default implementation does the conversion using
646 \l {QtQml::Number::toLocaleString()}{Number.toLocaleString}():
647
648 \code
649 textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); }
650 \endcode
651
652 \note When applying a custom \c textFromValue implementation for editable
653 spinboxes, a matching \l valueFromText implementation must be provided
654 to be able to convert the custom text back to an integer value.
655
656 \sa valueFromText, validator, {Control::locale}{locale}
657*/
658QJSValue QQuickSpinBox::textFromValue() const
659{
660 Q_D(const QQuickSpinBox);
661 if (!d->textFromValue.isCallable()) {
662 QQmlEngine *engine = qmlEngine(this);
663 if (engine)
664 d->textFromValue = engine->evaluate(QStringLiteral("(function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); })"));
665 }
666 return d->textFromValue;
667}
668
669void QQuickSpinBox::setTextFromValue(const QJSValue &callback)
670{
671 Q_D(QQuickSpinBox);
672 if (!callback.isCallable()) {
673 qmlWarning(me: this) << "textFromValue must be a callable function";
674 return;
675 }
676 d->textFromValue = callback;
677 emit textFromValueChanged();
678}
679
680/*!
681 \qmlproperty function QtQuick.Controls::SpinBox::valueFromText
682
683 This property holds a callback function that is called whenever
684 input text needs to be converted to an integer value.
685
686 This function only needs to be overridden when \l textFromValue
687 is overridden for an editable spinbox.
688
689 The callback function signature is \c {int function(text, locale)}.
690 The function can have one or two arguments, where the first argument
691 is the text to be converted, and the optional second argument is the
692 locale that should be used for the conversion, if applicable.
693
694 The default implementation does the conversion using \l {QtQml::Locale}{Number.fromLocaleString()}:
695
696 \code
697 valueFromText: function(text, locale) { return Number.fromLocaleString(locale, text); }
698 \endcode
699
700 \note When applying a custom \l textFromValue implementation for editable
701 spinboxes, a matching \c valueFromText implementation must be provided
702 to be able to convert the custom text back to an integer value.
703
704 \sa textFromValue, validator, {Control::locale}{locale}
705*/
706QJSValue QQuickSpinBox::valueFromText() const
707{
708 Q_D(const QQuickSpinBox);
709 if (!d->valueFromText.isCallable()) {
710 QQmlEngine *engine = qmlEngine(this);
711 if (engine)
712 d->valueFromText = engine->evaluate(QStringLiteral("(function(text, locale) { return Number.fromLocaleString(locale, text); })"));
713 }
714 return d->valueFromText;
715}
716
717void QQuickSpinBox::setValueFromText(const QJSValue &callback)
718{
719 Q_D(QQuickSpinBox);
720 if (!callback.isCallable()) {
721 qmlWarning(me: this) << "valueFromText must be a callable function";
722 return;
723 }
724 d->valueFromText = callback;
725 emit valueFromTextChanged();
726}
727
728/*!
729 \qmlproperty bool QtQuick.Controls::SpinBox::up.pressed
730 \qmlproperty Item QtQuick.Controls::SpinBox::up.indicator
731 \qmlproperty bool QtQuick.Controls::SpinBox::up.hovered
732 \qmlproperty real QtQuick.Controls::SpinBox::up.implicitIndicatorWidth
733 \qmlproperty real QtQuick.Controls::SpinBox::up.implicitIndicatorHeight
734
735 These properties hold the up indicator item and whether it is pressed or
736 hovered. The \c up.hovered property was introduced in QtQuick.Controls 2.1,
737 and the \c up.implicitIndicatorWidth and \c up.implicitIndicatorHeight
738 properties were introduced in QtQuick.Controls 2.5.
739
740 \sa increase()
741*/
742QQuickSpinButton *QQuickSpinBox::up() const
743{
744 Q_D(const QQuickSpinBox);
745 return d->up;
746}
747
748/*!
749 \qmlproperty bool QtQuick.Controls::SpinBox::down.pressed
750 \qmlproperty Item QtQuick.Controls::SpinBox::down.indicator
751 \qmlproperty bool QtQuick.Controls::SpinBox::down.hovered
752 \qmlproperty real QtQuick.Controls::SpinBox::down.implicitIndicatorWidth
753 \qmlproperty real QtQuick.Controls::SpinBox::down.implicitIndicatorHeight
754
755 These properties hold the down indicator item and whether it is pressed or
756 hovered. The \c down.hovered property was introduced in QtQuick.Controls 2.1,
757 and the \c down.implicitIndicatorWidth and \c down.implicitIndicatorHeight
758 properties were introduced in QtQuick.Controls 2.5.
759
760 \sa decrease()
761*/
762QQuickSpinButton *QQuickSpinBox::down() const
763{
764 Q_D(const QQuickSpinBox);
765 return d->down;
766}
767
768/*!
769 \since QtQuick.Controls 2.2 (Qt 5.9)
770 \qmlproperty flags QtQuick.Controls::SpinBox::inputMethodHints
771
772 This property provides hints to the input method about the expected content
773 of the spin box and how it should operate.
774
775 The default value is \c Qt.ImhDigitsOnly.
776
777 \include inputmethodhints.qdocinc
778*/
779Qt::InputMethodHints QQuickSpinBox::inputMethodHints() const
780{
781 Q_D(const QQuickSpinBox);
782 return d->inputMethodHints;
783}
784
785void QQuickSpinBox::setInputMethodHints(Qt::InputMethodHints hints)
786{
787 Q_D(QQuickSpinBox);
788 if (d->inputMethodHints == hints)
789 return;
790
791 d->inputMethodHints = hints;
792 emit inputMethodHintsChanged();
793}
794
795/*!
796 \since QtQuick.Controls 2.2 (Qt 5.9)
797 \qmlproperty bool QtQuick.Controls::SpinBox::inputMethodComposing
798 \readonly
799
800 This property holds whether an editable spin box has partial text input from an input method.
801
802 While it is composing, an input method may rely on mouse or key events from the spin box to
803 edit or commit the partial text. This property can be used to determine when to disable event
804 handlers that may interfere with the correct operation of an input method.
805*/
806bool QQuickSpinBox::isInputMethodComposing() const
807{
808 Q_D(const QQuickSpinBox);
809 return d->contentItem && d->contentItem->property(name: "inputMethodComposing").toBool();
810}
811
812/*!
813 \since QtQuick.Controls 2.3 (Qt 5.10)
814 \qmlproperty bool QtQuick.Controls::SpinBox::wrap
815
816 This property holds whether the spinbox wraps. The default value is \c false.
817
818 If wrap is \c true, stepping past \l to changes the value to \l from and vice versa.
819*/
820bool QQuickSpinBox::wrap() const
821{
822 Q_D(const QQuickSpinBox);
823 return d->wrap;
824}
825
826void QQuickSpinBox::setWrap(bool wrap)
827{
828 Q_D(QQuickSpinBox);
829 if (d->wrap == wrap)
830 return;
831
832 d->wrap = wrap;
833 if (d->value == d->from || d->value == d->to) {
834 d->updateUpEnabled();
835 d->updateDownEnabled();
836 }
837 emit wrapChanged();
838}
839
840/*!
841 \since QtQuick.Controls 2.4 (Qt 5.11)
842 \qmlproperty string QtQuick.Controls::SpinBox::displayText
843 \readonly
844
845 This property holds the textual value of the spinbox.
846
847 The value of the property is based on \l textFromValue and \l {Control::}
848 {locale}, and equal to:
849 \badcode
850 var text = spinBox.textFromValue(spinBox.value, spinBox.locale)
851 \endcode
852
853 \sa textFromValue
854*/
855QString QQuickSpinBox::displayText() const
856{
857 Q_D(const QQuickSpinBox);
858 return d->displayText;
859}
860
861/*!
862 \qmlmethod void QtQuick.Controls::SpinBox::increase()
863
864 Increases the value by \l stepSize, or \c 1 if stepSize is not defined.
865
866 \sa stepSize
867*/
868void QQuickSpinBox::increase()
869{
870 Q_D(QQuickSpinBox);
871 d->increase(modified: false);
872}
873
874/*!
875 \qmlmethod void QtQuick.Controls::SpinBox::decrease()
876
877 Decreases the value by \l stepSize, or \c 1 if stepSize is not defined.
878
879 \sa stepSize
880*/
881void QQuickSpinBox::decrease()
882{
883 Q_D(QQuickSpinBox);
884 d->decrease(modified: false);
885}
886
887void QQuickSpinBox::focusInEvent(QFocusEvent *event)
888{
889 Q_D(QQuickSpinBox);
890 QQuickControl::focusInEvent(event);
891
892 // When an editable SpinBox gets focus, it must pass on the focus to its editor.
893 if (d->editable && d->contentItem && !d->contentItem->hasActiveFocus())
894 d->contentItem->forceActiveFocus(reason: event->reason());
895}
896
897void QQuickSpinBox::hoverEnterEvent(QHoverEvent *event)
898{
899 Q_D(QQuickSpinBox);
900 QQuickControl::hoverEnterEvent(event);
901 d->updateHover(pos: event->posF());
902}
903
904void QQuickSpinBox::hoverMoveEvent(QHoverEvent *event)
905{
906 Q_D(QQuickSpinBox);
907 QQuickControl::hoverMoveEvent(event);
908 d->updateHover(pos: event->posF());
909}
910
911void QQuickSpinBox::hoverLeaveEvent(QHoverEvent *event)
912{
913 Q_D(QQuickSpinBox);
914 QQuickControl::hoverLeaveEvent(event);
915 d->down->setHovered(false);
916 d->up->setHovered(false);
917}
918
919void QQuickSpinBox::keyPressEvent(QKeyEvent *event)
920{
921 Q_D(QQuickSpinBox);
922 QQuickControl::keyPressEvent(event);
923
924 switch (event->key()) {
925 case Qt::Key_Up:
926 if (d->upEnabled()) {
927 d->increase(modified: true);
928 d->up->setPressed(true);
929 event->accept();
930 }
931 break;
932
933 case Qt::Key_Down:
934 if (d->downEnabled()) {
935 d->decrease(modified: true);
936 d->down->setPressed(true);
937 event->accept();
938 }
939 break;
940
941 default:
942 break;
943 }
944
945 setAccessibleProperty(propertyName: "pressed", value: d->up->isPressed() || d->down->isPressed());
946}
947
948void QQuickSpinBox::keyReleaseEvent(QKeyEvent *event)
949{
950 Q_D(QQuickSpinBox);
951 QQuickControl::keyReleaseEvent(event);
952
953 if (d->editable && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return))
954 d->updateValue();
955
956 d->up->setPressed(false);
957 d->down->setPressed(false);
958 setAccessibleProperty(propertyName: "pressed", value: false);
959}
960
961void QQuickSpinBox::timerEvent(QTimerEvent *event)
962{
963 Q_D(QQuickSpinBox);
964 QQuickControl::timerEvent(event);
965 if (event->timerId() == d->delayTimer) {
966 d->startPressRepeat();
967 } else if (event->timerId() == d->repeatTimer) {
968 if (d->up->isPressed())
969 d->increase(modified: true);
970 else if (d->down->isPressed())
971 d->decrease(modified: true);
972 }
973}
974
975#if QT_CONFIG(wheelevent)
976void QQuickSpinBox::wheelEvent(QWheelEvent *event)
977{
978 Q_D(QQuickSpinBox);
979 QQuickControl::wheelEvent(event);
980 if (d->wheelEnabled) {
981 const QPointF angle = event->angleDelta();
982 const qreal delta = (qFuzzyIsNull(d: angle.y()) ? angle.x() : angle.y()) / QWheelEvent::DefaultDeltasPerStep;
983 d->stepBy(steps: qRound(d: d->effectiveStepSize() * delta), modified: true);
984 }
985}
986#endif
987
988void QQuickSpinBox::classBegin()
989{
990 Q_D(QQuickSpinBox);
991 QQuickControl::classBegin();
992
993 QQmlContext *context = qmlContext(this);
994 if (context) {
995 QQmlEngine::setContextForObject(d->up, context);
996 QQmlEngine::setContextForObject(d->down, context);
997 }
998}
999
1000void QQuickSpinBox::componentComplete()
1001{
1002 Q_D(QQuickSpinBox);
1003 QQuickSpinButtonPrivate::get(button: d->up)->executeIndicator(complete: true);
1004 QQuickSpinButtonPrivate::get(button: d->down)->executeIndicator(complete: true);
1005
1006 QQuickControl::componentComplete();
1007 if (!d->setValue(newValue: d->value, /* allowWrap = */ false, /* modified = */ false)) {
1008 d->updateDisplayText();
1009 d->updateUpEnabled();
1010 d->updateDownEnabled();
1011 }
1012}
1013
1014void QQuickSpinBox::itemChange(ItemChange change, const ItemChangeData &value)
1015{
1016 Q_D(QQuickSpinBox);
1017 QQuickControl::itemChange(change, value);
1018 if (d->editable && change == ItemActiveFocusHasChanged && !value.boolValue)
1019 d->updateValue();
1020}
1021
1022void QQuickSpinBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
1023{
1024 Q_D(QQuickSpinBox);
1025 if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(object: oldItem))
1026 disconnect(sender: oldInput, signal: &QQuickTextInput::inputMethodComposingChanged, receiver: this, slot: &QQuickSpinBox::inputMethodComposingChanged);
1027
1028 if (newItem) {
1029 newItem->setActiveFocusOnTab(true);
1030 if (d->activeFocus)
1031 newItem->forceActiveFocus(reason: d->focusReason);
1032#if QT_CONFIG(cursor)
1033 if (d->editable)
1034 newItem->setCursor(Qt::IBeamCursor);
1035#endif
1036
1037 if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(object: newItem))
1038 connect(sender: newInput, signal: &QQuickTextInput::inputMethodComposingChanged, receiver: this, slot: &QQuickSpinBox::inputMethodComposingChanged);
1039 }
1040}
1041
1042void QQuickSpinBox::localeChange(const QLocale &newLocale, const QLocale &oldLocale)
1043{
1044 Q_D(QQuickSpinBox);
1045 QQuickControl::localeChange(newLocale, oldLocale);
1046 d->updateDisplayText();
1047}
1048
1049QFont QQuickSpinBox::defaultFont() const
1050{
1051 return QQuickTheme::font(scope: QQuickTheme::SpinBox);
1052}
1053
1054QPalette QQuickSpinBox::defaultPalette() const
1055{
1056 return QQuickTheme::palette(scope: QQuickTheme::SpinBox);
1057}
1058
1059#if QT_CONFIG(accessibility)
1060QAccessible::Role QQuickSpinBox::accessibleRole() const
1061{
1062 return QAccessible::SpinBox;
1063}
1064
1065void QQuickSpinBox::accessibilityActiveChanged(bool active)
1066{
1067 Q_D(QQuickSpinBox);
1068 QQuickControl::accessibilityActiveChanged(active);
1069
1070 if (active)
1071 setAccessibleProperty(propertyName: "editable", value: d->editable);
1072}
1073#endif
1074
1075static inline QString indicatorName() { return QStringLiteral("indicator"); }
1076
1077void QQuickSpinButtonPrivate::cancelIndicator()
1078{
1079 Q_Q(QQuickSpinButton);
1080 quickCancelDeferred(object: q, property: indicatorName());
1081}
1082
1083void QQuickSpinButtonPrivate::executeIndicator(bool complete)
1084{
1085 Q_Q(QQuickSpinButton);
1086 if (indicator.wasExecuted())
1087 return;
1088
1089 if (!indicator || complete)
1090 quickBeginDeferred(object: q, property: indicatorName(), delegate&: indicator);
1091 if (complete)
1092 quickCompleteDeferred(object: q, property: indicatorName(), delegate&: indicator);
1093}
1094
1095QQuickSpinButton::QQuickSpinButton(QQuickSpinBox *parent)
1096 : QObject(*(new QQuickSpinButtonPrivate), parent)
1097{
1098}
1099
1100bool QQuickSpinButton::isPressed() const
1101{
1102 Q_D(const QQuickSpinButton);
1103 return d->pressed;
1104}
1105
1106void QQuickSpinButton::setPressed(bool pressed)
1107{
1108 Q_D(QQuickSpinButton);
1109 if (d->pressed == pressed)
1110 return;
1111
1112 d->pressed = pressed;
1113 emit pressedChanged();
1114}
1115
1116QQuickItem *QQuickSpinButton::indicator() const
1117{
1118 QQuickSpinButtonPrivate *d = const_cast<QQuickSpinButtonPrivate *>(d_func());
1119 if (!d->indicator)
1120 d->executeIndicator();
1121 return d->indicator;
1122}
1123
1124void QQuickSpinButton::setIndicator(QQuickItem *indicator)
1125{
1126 Q_D(QQuickSpinButton);
1127 if (d->indicator == indicator)
1128 return;
1129
1130 if (!d->indicator.isExecuting())
1131 d->cancelIndicator();
1132
1133 const qreal oldImplicitIndicatorWidth = implicitIndicatorWidth();
1134 const qreal oldImplicitIndicatorHeight = implicitIndicatorHeight();
1135
1136 QQuickSpinBox *spinBox = static_cast<QQuickSpinBox *>(parent());
1137 QQuickSpinBoxPrivate::get(control: spinBox)->removeImplicitSizeListener(item: d->indicator);
1138 QQuickControlPrivate::hideOldItem(item: d->indicator);
1139 d->indicator = indicator;
1140
1141 if (indicator) {
1142 if (!indicator->parentItem())
1143 indicator->setParentItem(spinBox);
1144 QQuickSpinBoxPrivate::get(control: spinBox)->addImplicitSizeListener(item: indicator);
1145 }
1146
1147 if (!qFuzzyCompare(p1: oldImplicitIndicatorWidth, p2: implicitIndicatorWidth()))
1148 emit implicitIndicatorWidthChanged();
1149 if (!qFuzzyCompare(p1: oldImplicitIndicatorHeight, p2: implicitIndicatorHeight()))
1150 emit implicitIndicatorHeightChanged();
1151 if (!d->indicator.isExecuting())
1152 emit indicatorChanged();
1153}
1154
1155bool QQuickSpinButton::isHovered() const
1156{
1157 Q_D(const QQuickSpinButton);
1158 return d->hovered;
1159}
1160
1161void QQuickSpinButton::setHovered(bool hovered)
1162{
1163 Q_D(QQuickSpinButton);
1164 if (d->hovered == hovered)
1165 return;
1166
1167 d->hovered = hovered;
1168 emit hoveredChanged();
1169}
1170
1171qreal QQuickSpinButton::implicitIndicatorWidth() const
1172{
1173 Q_D(const QQuickSpinButton);
1174 if (!d->indicator)
1175 return 0;
1176 return d->indicator->implicitWidth();
1177}
1178
1179qreal QQuickSpinButton::implicitIndicatorHeight() const
1180{
1181 Q_D(const QQuickSpinButton);
1182 if (!d->indicator)
1183 return 0;
1184 return d->indicator->implicitHeight();
1185}
1186
1187QT_END_NAMESPACE
1188

source code of qtquickcontrols2/src/quicktemplates2/qquickspinbox.cpp