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
qquickabstractspinbox_p.h
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
5#ifndef QQUICKABSTRACTSPINBOX_P_H
6#define QQUICKABSTRACTSPINBOX_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <private/qquickcontrol_p_p.h>
20#include <private/qquickcontrol_p.h>
21#include <private/qquicktheme_p.h>
22#include <QtQml/qjsvalue.h>
23#include <private/qquickindicatorbutton_p.h>
24#include <private/qquicktextinput_p.h>
25#include <private/qqmlengine_p.h>
26#include <QtQml/qqmlinfo.h>
27#include <QtGui/private/qlayoutpolicy_p.h>
28
29QT_BEGIN_NAMESPACE
30
31class QValidator;
32class QQuickIndicatorButton;
33
34template <typename Derived, typename ValueType>
36{
37public:
39 {
40 public:
41 // copied from qabstractbutton.cpp
42 static constexpr int AUTO_REPEAT_DELAY = 300;
43 static constexpr int AUTO_REPEAT_INTERVAL = 100;
44
46
48
49 Derived *q_func() { return static_cast<Derived *>(q_ptr); }
50 const Derived *q_func() const { return static_cast<const Derived *>(q_ptr); }
51
52 ValueType boundValue(ValueType value, bool wrap) const
53 {
54 bool inverted = from > to;
55 if (!wrap)
56 return inverted ? qBound(to, value, from) : qBound(from, value, to);
57
58 ValueType f = inverted ? to : from;
59 ValueType t = inverted ? from : to;
60 if (value < f)
61 value = t;
62 else if (value > t)
63 value = f;
64
65 return value;
66 }
67
68 virtual bool setValue(ValueType newValue, bool wrap, ValueStatus modified) = 0;
69
70 bool stepBy(ValueType steps, ValueStatus modified)
71 {
72 return setValue(value + steps, wrap, modified);
73 }
74
75 void increase(ValueStatus modified)
76 {
78 }
79
80 void decrease(ValueStatus modified)
81 {
83 }
84
85 ValueType effectiveStepSize() const { return from > to ? -1 * stepSize : stepSize; }
86
87 virtual void updateValue() = 0;
88
89 virtual void updateDisplayText() = 0;
90
91 void setDisplayText(const QString &text)
92 {
93 if (displayText == text)
94 return;
95
96 displayText = text;
97 emit q_func()->displayTextChanged();
98 }
99
100 bool upEnabled() const
101 {
102 const QQuickItem *upIndicator = up->indicator();
103 return upIndicator && upIndicator->isEnabled();
104 }
105
107 {
108 QQuickItem *upIndicator = up->indicator();
109 if (!upIndicator)
110 return;
111
112 upIndicator->setEnabled(wrap || (from < to ? value < to : value > to));
113 }
114
115 bool downEnabled() const
116 {
117 const QQuickItem *downIndicator = down->indicator();
118 return downIndicator && downIndicator->isEnabled();
119 }
120
122 {
123 QQuickItem *downIndicator = down->indicator();
124 if (!downIndicator)
125 return;
126
127 downIndicator->setEnabled(wrap || (from < to ? value > from : value < from));
128 }
129
130 void updateHover(const QPointF &pos)
131 {
132 QQuickItem *ui = up->indicator();
133 QQuickItem *di = down->indicator();
134 up->setHovered(ui && ui->isEnabled() && ui->contains(q_func()->mapToItem(ui, pos)));
135 down->setHovered(di && di->isEnabled() && di->contains(q_func()->mapToItem(di, pos)));
136 }
137
139 {
141 delayTimer = q_func()->startTimer(AUTO_REPEAT_DELAY);
142 }
143
145 {
147 repeatTimer = q_func()->startTimer(AUTO_REPEAT_INTERVAL);
148 }
149
151 {
152 if (delayTimer > 0) {
153 q_func()->killTimer(delayTimer);
154 delayTimer = 0;
155 }
156 if (repeatTimer > 0) {
157 q_func()->killTimer(repeatTimer);
158 repeatTimer = 0;
159 }
160 }
161
162 bool handlePress(const QPointF &point, ulong timestamp) override
163 {
164 QQuickControlPrivate::handlePress(point, timestamp);
165
166 QQuickItem *ui = up->indicator();
167 QQuickItem *di = down->indicator();
168 up->setPressed(ui && ui->isEnabled() && ui->contains(ui->mapFromItem(q_func(), point)));
169 down->setPressed(di && di->isEnabled()
170 && di->contains(di->mapFromItem(q_func(), point)));
171
172 bool pressed = up->isPressed() || down->isPressed();
173 q_func()->setAccessibleProperty("pressed", pressed);
174 if (pressed)
176 return true;
177 }
178
179 bool handleMove(const QPointF &point, ulong timestamp) override
180 {
181 QQuickControlPrivate::handleMove(point, timestamp);
182 QQuickItem *upIndicator = up->indicator();
183 const bool upIndicatorContainsPoint = upIndicator && upIndicator->isEnabled()
184 && upIndicator->contains(upIndicator->mapFromItem(q_func(), point));
185 up->setHovered(QQuickControlPrivate::touchId == -1 && upIndicatorContainsPoint);
186 up->setPressed(upIndicatorContainsPoint);
187
188 QQuickItem *downIndicator = down->indicator();
189 const bool downIndicatorContainsPoint = downIndicator && downIndicator->isEnabled()
190 && downIndicator->contains(downIndicator->mapFromItem(q_func(), point));
191 down->setHovered(QQuickControlPrivate::touchId == -1 && downIndicatorContainsPoint);
192 down->setPressed(downIndicatorContainsPoint);
193
194 bool pressed = up->isPressed() || down->isPressed();
195 q_func()->setAccessibleProperty("pressed", pressed);
196 if (!pressed)
198 return true;
199 }
200
201 bool handleRelease(const QPointF &point, ulong timestamp) override
202 {
203 QQuickControlPrivate::handleRelease(point, timestamp);
204 QQuickItem *ui = up->indicator();
205 QQuickItem *di = down->indicator();
206
207 double oldValue = value;
208 if (up->isPressed()) {
209 if (repeatTimer <= 0 && ui && ui->contains(ui->mapFromItem(q_func(), point)))
210 q_func()->increase();
211 // Retain pressed state until after increasing is done in case user code binds
212 // stepSize to up/down.pressed.
213 up->setPressed(false);
214 } else if (down->isPressed()) {
215 if (repeatTimer <= 0 && di && di->contains(di->mapFromItem(q_func(), point)))
216 q_func()->decrease();
217 down->setPressed(false);
218 }
219 if (value != oldValue)
220 emit q_func()->valueModified();
221
222 q_func()->setAccessibleProperty("pressed", false);
224 return true;
225 }
226
228 {
229 QQuickControlPrivate::handleUngrab();
230 up->setPressed(false);
231 down->setPressed(false);
232
233 q_func()->setAccessibleProperty("pressed", false);
235 }
236
237 void itemImplicitWidthChanged(QQuickItem *item) override
238 {
239 QQuickControlPrivate::itemImplicitWidthChanged(item);
240 if (item == up->indicator())
241 emit up->implicitIndicatorWidthChanged();
242 else if (item == down->indicator())
243 emit down->implicitIndicatorWidthChanged();
244 }
245
246 void itemImplicitHeightChanged(QQuickItem *item) override
247 {
248 QQuickControlPrivate::itemImplicitHeightChanged(item);
249 if (item == up->indicator())
250 emit up->implicitIndicatorHeightChanged();
251 else if (item == down->indicator())
252 emit down->implicitIndicatorHeightChanged();
253 }
254
255 void itemDestroyed(QQuickItem *item) override
256 {
257 QQuickControlPrivate::itemDestroyed(item);
258 if (item == up->indicator())
259 up->setIndicator(nullptr);
260 else if (item == down->indicator())
261 down->setIndicator(nullptr);
262 }
263
264 QPalette defaultPalette() const override
265 {
266 return QQuickTheme::palette(QQuickTheme::SpinBox);
267 }
268
269 bool editable = false;
270 bool wrap = false;
271 ValueType from; // please set it in derived class constructor
272 ValueType to; // please set it in derived class constructor
273 ValueType value = 0;
274 ValueType stepSize = 1;
275 int delayTimer = 0;
276 int repeatTimer = 0;
278 QQuickIndicatorButton *up = nullptr;
279 QQuickIndicatorButton *down = nullptr;
280#if QT_CONFIG(validator)
281 QValidator *validator = nullptr;
282#endif
286 };
287
288private:
290 {
291 auto *derived = static_cast<Derived *>(this);
292 return static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
293 }
294
295 const QQuickAbstractSpinBoxPrivate *d_func() const
296 {
297 const auto *derived = static_cast<const Derived *>(this);
298 return static_cast<const QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
299 }
300
301public:
302 ValueType from() const
303 {
304 return d_func()->from;
305 }
306
307 ValueType to() const
308 {
309 return d_func()->to;
310 }
311
312 ValueType value() const
313 {
314 return d_func()->value;
315 }
316
317 ValueType stepSize() const
318 {
319 return d_func()->stepSize;
320 }
321
322 bool isEditable() const
323 {
324 return d_func()->editable;
325 }
326
327 void setEditable(bool editable)
328 {
329 auto *derived = static_cast<Derived *>(this);
330 auto *d = static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
331 if (d->editable == editable)
332 return;
333
334#if QT_CONFIG(cursor)
335 if (d->contentItem) {
336 if (editable)
337 d->contentItem->setCursor(Qt::IBeamCursor);
338 else
339 d->contentItem->unsetCursor();
340 }
341#endif
342
343 d->editable = editable;
344 d->q_func()->setAccessibleProperty("editable", editable);
345 emit derived->editableChanged();
346 }
347
348#if QT_CONFIG(validator)
349 QValidator *validator() const
350 {
351 return d_func()->validator;
352 }
353
355 {
356 auto *derived = static_cast<Derived *>(this);
357 auto *d = static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
358 if (d->validator == validator)
359 return;
360
363 }
364#endif
365
366 void setTextFromValue(const QJSValue &callback)
367 {
368 auto *derived = static_cast<Derived *>(this);
369 auto *d = static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
370 if (!callback.isCallable()) {
371 qmlWarning(derived) << "textFromValue must be a callable function";
372 return;
373 }
374 d->textFromValue = callback;
375 emit derived->textFromValueChanged();
376 }
377
378 void setValueFromText(const QJSValue &callback)
379 {
380 auto *derived = static_cast<Derived *>(this);
381 auto *d = static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
382 if (!callback.isCallable()) {
383 qmlWarning(derived) << "valueFromText must be a callable function";
384 return;
385 }
386 d->valueFromText = callback;
387 emit derived->valueFromTextChanged();
388 }
389
390 QQuickIndicatorButton *up() const
391 {
392 auto *derived = static_cast<const Derived *>(this);
393 auto *d = static_cast<const QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
394 return d->up;
395 }
396
397 QQuickIndicatorButton *down() const
398 {
399 auto *derived = static_cast<const Derived *>(this);
400 auto *d = static_cast<const QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
401 return d->down;
402 }
403
405 {
406 auto *derived = static_cast<const Derived *>(this);
407 auto *d = static_cast<const QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
408 return d->inputMethodHints;
409 }
410
411 void setInputMethodHints(Qt::InputMethodHints hints)
412 {
413 auto *derived = static_cast<Derived *>(this);
414 auto *d = static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
415 if (d->inputMethodHints == hints)
416 return;
417
418 d->inputMethodHints = hints;
419 emit derived->inputMethodHintsChanged();
420 }
421
423 {
424 auto *derived = static_cast<const Derived *>(this);
425 auto *d = static_cast<const QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
426 return d->contentItem && d->contentItem->property("inputMethodComposing").toBool();
427 }
428
429 bool wrap() const
430 {
431 return d_func()->wrap;
432 }
433
435 {
436 return d_func()->displayText;
437 }
438
439public:
440 void increase()
441 {
442 d_func()->increase(QQuickAbstractSpinBoxPrivate::ValueStatus::Unmodified);
443 }
444
445 void decrease()
446 {
447 d_func()->decrease(QQuickAbstractSpinBoxPrivate::ValueStatus::Unmodified);
448 }
449
450protected:
451 void handleFocusInEvent(QFocusEvent *event)
452 {
453 auto *d = d_func();
454
455 // When an editable derived SpinBox gets focus, it must pass on the focus to its editor.
456 if (d->editable && d->contentItem && !d->contentItem->hasActiveFocus())
457 d->contentItem->forceActiveFocus(event->reason());
458 }
459
460 void handleHoverEnterEvent(QHoverEvent *event)
461 {
462 auto *d = d_func();
463
464 d->updateHover(event->position());
465 event->ignore();
466 }
467
468 void handleHoverMoveEvent(QHoverEvent *event)
469 {
470 auto *d = d_func();
471
472 d->updateHover(event->position());
473 event->ignore();
474 }
475
476 void handleHoverLeaveEvent(QHoverEvent *event)
477 {
478 auto *d = d_func();
479
480 d->down->setHovered(false);
481 d->up->setHovered(false);
482 event->ignore();
483 }
484
485 void handleKeyPressEvent(QKeyEvent *event)
486 {
487 auto *d = d_func();
488
489 switch (event->key()) {
490 case Qt::Key_Up:
491 if (d->upEnabled()) {
492 // Update the pressed state before increasing/decreasing in case user code binds
493 // stepSize to up/down.pressed.
494 d->up->setPressed(true);
495 d->increase(QQuickAbstractSpinBoxPrivate::ValueStatus::Modified);
496 event->accept();
497 }
498 break;
499
500 case Qt::Key_Down:
501 if (d->downEnabled()) {
502 d->down->setPressed(true);
503 d->decrease(QQuickAbstractSpinBoxPrivate::ValueStatus::Modified);
504 event->accept();
505 }
506 break;
507
508 default:
509 break;
510 }
511
512 d->q_func()->setAccessibleProperty("pressed", d->up->isPressed() || d->down->isPressed());
513 }
514
515 void handleKeyReleaseEvent(QKeyEvent *event)
516 {
517 auto *d = d_func();
518
519 if (d->editable && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return))
520 d->updateValue();
521
522 d->up->setPressed(false);
523 d->down->setPressed(false);
524 d->q_func()->setAccessibleProperty("pressed", false);
525 }
526
527 void handleTimerEvent(QTimerEvent *event)
528 {
529 auto *d = d_func();
530
531 if (event->timerId() == d->delayTimer) {
532 d->startPressRepeat();
533 } else if (event->timerId() == d->repeatTimer) {
534 if (d->up->isPressed())
535 d->increase(QQuickAbstractSpinBoxPrivate::ValueStatus::Modified);
536 else if (d->down->isPressed())
537 d->decrease(QQuickAbstractSpinBoxPrivate::ValueStatus::Modified);
538 }
539 }
540
541#if QT_CONFIG(wheelevent)
543 {
544 auto *d = d_func();
545
546 if (d->wheelEnabled) {
547 const QPointF angle = event->angleDelta();
548 const qreal delta = (qFuzzyIsNull(angle.y()) ? angle.x() : angle.y())
552 }
553 }
554#endif
555
557 {
558 auto *derived = static_cast<Derived *>(this);
559 auto *d = static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
560
561 QQmlContext *context = qmlContext(derived);
562 if (context) {
563 QQmlEngine::setContextForObject(d->up, context);
564 QQmlEngine::setContextForObject(d->down, context);
565 }
566 }
567
569 {
570 auto *d = d_func();
571
572 QQuickIndicatorButtonPrivate::get(d->up)->executeIndicator(true);
573 QQuickIndicatorButtonPrivate::get(d->down)->executeIndicator(true);
574
575 if (!d->setValue(d->value, false /* wrap */,
576 QQuickAbstractSpinBoxPrivate::ValueStatus::Unmodified)) {
577 d->updateDisplayText();
578 d->updateUpEnabled();
579 d->updateDownEnabled();
580 }
581 }
582
583 void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
584 {
585 auto *d = d_func();
586 if (d->editable && change == QQuickItem::ItemActiveFocusHasChanged && !value.boolValue)
587 d->updateValue();
588 }
589
591 {
592 auto *d = d_func();
593 d->updateDisplayText();
594 }
595
596 QFont defaultFont() const { return QQuickTheme::font(QQuickTheme::SpinBox); }
597
598#if QT_CONFIG(accessibility)
600
602 {
603 auto *d = d_func();
604
605 if (active)
606 d->q_func()->setAccessibleProperty("editable", d->editable);
607 }
608#endif
609
610protected:
612 {
613 auto *derived = static_cast<Derived *>(this);
614 auto *d = static_cast<QQuickAbstractSpinBoxPrivate *>(derived->d_base_func());
615 d->up = new QQuickIndicatorButton(derived);
616 d->down = new QQuickIndicatorButton(derived);
617 d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
618
619 derived->setFlag(QQuickItem::ItemIsFocusScope);
620 derived->setFiltersChildMouseEvents(true);
621 derived->setAcceptedMouseButtons(Qt::LeftButton);
622#if QT_CONFIG(cursor)
623 derived->setCursor(Qt::ArrowCursor);
624#endif
625#if QT_CONFIG(quicktemplates2_multitouch)
626 derived->setAcceptTouchEvents(true);
627#endif
628 }
629
631 {
632 auto *d = d_func();
633 d->removeImplicitSizeListener(d->up->indicator());
634 d->removeImplicitSizeListener(d->down->indicator());
635 }
636
637 // Non-copyable
640};
641
642QT_END_NAMESPACE
643
644#endif // QQUICKABSTRACTSPINBOX_P_H
ValueType boundValue(ValueType value, bool wrap) const
virtual bool setValue(ValueType newValue, bool wrap, ValueStatus modified)=0
bool handlePress(const QPointF &point, ulong timestamp) override
bool handleRelease(const QPointF &point, ulong timestamp) override
bool stepBy(ValueType steps, ValueStatus modified)
bool handleMove(const QPointF &point, ulong timestamp) override
QQuickAbstractSpinBox & operator=(const QQuickAbstractSpinBox &)=delete
void setInputMethodHints(Qt::InputMethodHints hints)
void handleTimerEvent(QTimerEvent *event)
void handleFocusInEvent(QFocusEvent *event)
void handleHoverMoveEvent(QHoverEvent *event)
void handleKeyReleaseEvent(QKeyEvent *event)
QQuickIndicatorButton * down() const
void setEditable(bool editable)
void setValueFromText(const QJSValue &callback)
QQuickIndicatorButton * up() const
Qt::InputMethodHints inputMethodHints() const
void handleHoverLeaveEvent(QHoverEvent *event)
QQuickAbstractSpinBox(const QQuickAbstractSpinBox &)=delete
void setTextFromValue(const QJSValue &callback)
void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
void handleKeyPressEvent(QKeyEvent *event)
void handleHoverEnterEvent(QHoverEvent *event)