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
qquickabstractbutton.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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
13
14#include <QtGui/qstylehints.h>
15#include <QtGui/qguiapplication.h>
16#if QT_CONFIG(shortcut)
17# include <QtGui/private/qshortcutmap_p.h>
18#endif
19#if QT_CONFIG(accessibility)
20#include <QtGui/private/qaccessiblehelper_p.h>
21#endif
22#include <QtGui/private/qguiapplication_p.h>
23#include <QtGui/qpa/qplatformtheme.h>
24#include <QtQuick/private/qquickevents_p_p.h>
25#include <QtQuick/private/qquickevents_p_p.h>
26#include <QtQuick/private/qquickaccessibleattached_p.h>
27#include <QtQml/qqmllist.h>
28
30
31/*!
32 \qmltype AbstractButton
33 \inherits Control
34//! \nativetype QQuickAbstractButton
35 \inqmlmodule QtQuick.Controls
36 \since 5.7
37 \ingroup qtquickcontrols-buttons
38 \brief Abstract base type providing functionality common to buttons.
39
40 AbstractButton provides the interface for controls with button-like
41 behavior; for example, push buttons and checkable controls like
42 radio buttons and check boxes. As an abstract control, it has no delegate
43 implementations, leaving them to the types that derive from it.
44
45 \sa ButtonGroup, {Button Controls}
46*/
47
48/*!
49 \qmlsignal QtQuick.Controls::AbstractButton::pressed()
50
51 This signal is emitted when the button is interactively pressed by the user via touch, mouse, or keyboard.
52*/
53
54/*!
55 \qmlsignal QtQuick.Controls::AbstractButton::released()
56
57 This signal is emitted when the button is interactively released by the user via touch, mouse, or keyboard.
58*/
59
60/*!
61 \qmlsignal QtQuick.Controls::AbstractButton::canceled()
62
63 This signal is emitted when the button loses mouse grab
64 while being pressed, or when it would emit the \l released
65 signal but the mouse cursor is not inside the button.
66*/
67
68/*!
69 \qmlsignal QtQuick.Controls::AbstractButton::clicked()
70
71 This signal is emitted when the button is interactively clicked by the user via touch, mouse, or keyboard.
72
73 \sa click(), animateClick(), {Call a C++ function from QML when a Button is clicked}
74*/
75
76/*!
77 \since QtQuick.Controls 2.2 (Qt 5.9)
78 \qmlsignal QtQuick.Controls::AbstractButton::toggled()
79
80 This signal is emitted when a checkable button is interactively toggled by the user via touch, mouse, or keyboard.
81*/
82
83/*!
84 \qmlsignal QtQuick.Controls::AbstractButton::pressAndHold()
85
86 This signal is emitted when the button is interactively pressed and held down by the user via touch or mouse.
87 It is not emitted when \l autoRepeat is enabled.
88*/
89
90/*!
91 \qmlsignal QtQuick.Controls::AbstractButton::doubleClicked()
92
93 This signal is emitted when the button is interactively double clicked by the user via touch or mouse.
94*/
95
96void QQuickAbstractButtonPrivate::init()
97{
98 Q_Q(QQuickAbstractButton);
99 q->setActiveFocusOnTab(true);
100#ifdef Q_OS_MACOS
101 q->setFocusPolicy(Qt::TabFocus);
102#else
103 q->setFocusPolicy(Qt::StrongFocus);
104#endif
105 q->setAcceptedMouseButtons(Qt::LeftButton);
106#if QT_CONFIG(quicktemplates2_multitouch)
107 q->setAcceptTouchEvents(true);
108#endif
109#if QT_CONFIG(cursor)
110 q->setCursor(Qt::ArrowCursor);
111#endif
112 setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
113}
114
115QPointF QQuickAbstractButtonPrivate::centerPressPoint() const
116{
117 return QPointF(qRound(width / 2), qRound(height / 2));
118}
119
120void QQuickAbstractButtonPrivate::setPressPoint(const QPointF &point)
121{
122 pressPoint = point;
123 setMovePoint(point);
124}
125
126void QQuickAbstractButtonPrivate::setMovePoint(const QPointF &point)
127{
128 Q_Q(QQuickAbstractButton);
129 bool xChange = !qFuzzyCompare(point.x(), movePoint.x());
130 bool yChange = !qFuzzyCompare(point.y(), movePoint.y());
131 movePoint = point;
132 if (xChange)
133 emit q->pressXChanged();
134 if (yChange)
135 emit q->pressYChanged();
136}
137
138bool QQuickAbstractButtonPrivate::handlePress(const QPointF &point, ulong timestamp)
139{
140 Q_Q(QQuickAbstractButton);
141 if (pressed)
142 return true;
143 QQuickControlPrivate::handlePress(point, timestamp);
144 setPressPoint(point);
145 q->setPressed(true);
146
147 emit q->pressed();
148
149 if (autoRepeat)
150 startRepeatDelay();
151 else if (touchId != -1 || Qt::LeftButton == (pressButtons & Qt::LeftButton))
152 startPressAndHold();
153 else
154 stopPressAndHold();
155 return true;
156}
157
158bool QQuickAbstractButtonPrivate::handleMove(const QPointF &point, ulong timestamp)
159{
160 Q_Q(QQuickAbstractButton);
161 QQuickControlPrivate::handleMove(point, timestamp);
162 setMovePoint(point);
163 q->setPressed(keepPressed || q->contains(point));
164
165 if (!pressed && autoRepeat)
166 stopPressRepeat();
167 else if (holdTimer > 0 && (!pressed || QLineF(pressPoint, point).length() > QGuiApplication::styleHints()->startDragDistance()))
168 stopPressAndHold();
169 return true;
170}
171
172bool QQuickAbstractButtonPrivate::handleRelease(const QPointF &point, ulong timestamp)
173{
174 Q_Q(QQuickAbstractButton);
175 // Store this here since the base class' handleRelease clears it.
176 const int pressTouchId = touchId;
177
178 QQuickControlPrivate::handleRelease(point, timestamp);
179 bool wasPressed = pressed;
180 setPressPoint(point);
181 q->setPressed(false);
182 pressButtons = Qt::NoButton;
183
184 const bool touchDoubleClick = pressTouchId != -1 && lastTouchReleaseTimestamp != 0
185 && QQuickDeliveryAgentPrivate::isWithinDoubleClickInterval(timestamp - lastTouchReleaseTimestamp)
186 && isDoubleClickConnected();
187
188 if (!wasHeld && (keepPressed || q->contains(point)))
189 q->nextCheckState();
190
191 if (wasPressed) {
192 emit q->released();
193 if (!wasHeld && !wasDoubleClick)
194 trigger(touchDoubleClick);
195 } else {
196 emit q->canceled();
197 }
198
199 if (autoRepeat)
200 stopPressRepeat();
201 else
202 stopPressAndHold();
203
204 if (!touchDoubleClick) {
205 // This is not a double click yet, but it is potentially the
206 // first release before a double click.
207 if (pressTouchId != -1) {
208 // The corresponding press for this release was a touch press.
209 // Keep track of the timestamp of the release so that we can
210 // emit doubleClicked() if another one comes afterwards.
211 lastTouchReleaseTimestamp = timestamp;
212 }
213 } else {
214 // We just did a double click, so clear the release timestamp
215 // to prepare for any possible future double clicks.
216 lastTouchReleaseTimestamp = 0;
217 }
218
219 wasDoubleClick = false;
220 return true;
221}
222
223void QQuickAbstractButtonPrivate::handleUngrab()
224{
225 Q_Q(QQuickAbstractButton);
226 QQuickControlPrivate::handleUngrab();
227 pressButtons = Qt::NoButton;
228 if (!pressed)
229 return;
230
231 q->setPressed(false);
232 stopPressRepeat();
233 stopPressAndHold();
234 wasDoubleClick = false;
235 lastTouchReleaseTimestamp = 0;
236 emit q->canceled();
237}
238
239bool QQuickAbstractButtonPrivate::acceptKeyClick(Qt::Key key) const
240{
241 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
242 return buttonPressKeys.contains(key);
243}
244
245bool QQuickAbstractButtonPrivate::isPressAndHoldConnected()
246{
247 Q_Q(QQuickAbstractButton);
248 static const QMetaMethod method = [&]() {
249 const auto signal = &QQuickAbstractButton::pressAndHold;
250 return QMetaMethod::fromSignal(signal);
251 }();
252 return q->isSignalConnected(method);
253}
254
255bool QQuickAbstractButtonPrivate::isDoubleClickConnected()
256{
257 Q_Q(QQuickAbstractButton);
258 static const QMetaMethod method = [&]() {
259 const auto signal = &QQuickAbstractButton::doubleClicked;
260 return QMetaMethod::fromSignal(signal);
261 }();
262 return q->isSignalConnected(method);
263}
264
265void QQuickAbstractButtonPrivate::startPressAndHold()
266{
267 Q_Q(QQuickAbstractButton);
268 wasHeld = false;
269 stopPressAndHold();
270 if (isPressAndHoldConnected())
271 holdTimer = q->startTimer(QGuiApplication::styleHints()->mousePressAndHoldInterval());
272}
273
274void QQuickAbstractButtonPrivate::stopPressAndHold()
275{
276 Q_Q(QQuickAbstractButton);
277 if (holdTimer > 0) {
278 q->killTimer(holdTimer);
279 holdTimer = 0;
280 }
281}
282
283void QQuickAbstractButtonPrivate::startRepeatDelay()
284{
285 Q_Q(QQuickAbstractButton);
286 stopPressRepeat();
287 delayTimer = q->startTimer(repeatDelay);
288}
289
290void QQuickAbstractButtonPrivate::startPressRepeat()
291{
292 Q_Q(QQuickAbstractButton);
293 stopPressRepeat();
294 repeatTimer = q->startTimer(repeatInterval);
295}
296
297void QQuickAbstractButtonPrivate::stopPressRepeat()
298{
299 Q_Q(QQuickAbstractButton);
300 if (delayTimer > 0) {
301 q->killTimer(delayTimer);
302 delayTimer = 0;
303 }
304 if (repeatTimer > 0) {
305 q->killTimer(repeatTimer);
306 repeatTimer = 0;
307 }
308}
309
310#if QT_CONFIG(shortcut)
311void QQuickAbstractButtonPrivate::grabShortcut()
312{
313 Q_Q(QQuickAbstractButton);
314 if (shortcut.isEmpty())
315 return;
316
317 shortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(q, shortcut, Qt::WindowShortcut, QQuickShortcutContext::matcher);
318
319 if (!q->isEnabled())
320 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(false, shortcutId, q);
321}
322
323void QQuickAbstractButtonPrivate::ungrabShortcut()
324{
325 Q_Q(QQuickAbstractButton);
326 if (!shortcutId)
327 return;
328
329 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(shortcutId, q);
330 shortcutId = 0;
331}
332#endif
333
334void QQuickAbstractButtonPrivate::actionTextChange()
335{
336 Q_Q(QQuickAbstractButton);
337 if (explicitText)
338 return;
339
340 q->buttonChange(QQuickAbstractButton::ButtonTextChange);
341}
342
343void QQuickAbstractButtonPrivate::setText(const QString &newText, QQml::PropertyUtils::State propertyState)
344{
345 Q_Q(QQuickAbstractButton);
346 const QString oldText = q->text();
347 explicitText = isExplicitlySet(propertyState);
348 text = newText;
349 if (oldText == q->text())
350 return;
351
352 q->buttonChange(QQuickAbstractButton::ButtonTextChange);
353}
354
355void QQuickAbstractButtonPrivate::updateEffectiveIcon()
356{
357 Q_Q(QQuickAbstractButton);
358 // We store effectiveIcon because we need to be able to tell if the icon has actually changed.
359 // If we only stored our icon and the action's icon, and resolved in the getter, we'd have
360 // no way of knowing what the old value was here. As an added benefit, we only resolve when
361 // something has changed, as opposed to doing it unconditionally in the icon() getter.
362 const QQuickIcon newEffectiveIcon = action ? icon.resolve(action->icon()) : icon;
363 if (newEffectiveIcon == effectiveIcon)
364 return;
365
366 effectiveIcon = newEffectiveIcon;
367 emit q->iconChanged();
368}
369
370void QQuickAbstractButtonPrivate::click()
371{
372 Q_Q(QQuickAbstractButton);
373 if (effectiveEnable)
374 emit q->clicked();
375}
376
377void QQuickAbstractButtonPrivate::accessiblePressAction()
378{
379 trigger();
380}
381
382void QQuickAbstractButtonPrivate::trigger(bool doubleClick)
383{
384 Q_Q(QQuickAbstractButton);
385 const bool wasEnabled = effectiveEnable;
386 if (action && action->isEnabled())
387 QQuickActionPrivate::get(action)->trigger(q, false);
388 if (wasEnabled && (!action || !action->isEnabled())) {
389 if (!doubleClick)
390 emit q->clicked();
391 else
392 emit q->doubleClicked();
393 }
394}
395
396void QQuickAbstractButtonPrivate::toggle(bool value)
397{
398 Q_Q(QQuickAbstractButton);
399 const bool wasChecked = checked;
400 q->setChecked(value);
401 if (wasChecked != checked)
402 emit q->toggled();
403}
404
405void QQuickAbstractButtonPrivate::cancelIndicator()
406{
407 Q_Q(QQuickAbstractButton);
408 quickCancelDeferred(q, indicatorName());
409}
410
411void QQuickAbstractButtonPrivate::executeIndicator(bool complete)
412{
413 Q_Q(QQuickAbstractButton);
414 if (indicator.wasExecuted())
415 return;
416
417 if (!indicator || complete)
418 quickBeginDeferred(q, indicatorName(), indicator);
419 if (complete)
420 quickCompleteDeferred(q, indicatorName(), indicator);
421}
422
423void QQuickAbstractButtonPrivate::itemImplicitWidthChanged(QQuickItem *item)
424{
425 Q_Q(QQuickAbstractButton);
426 QQuickControlPrivate::itemImplicitWidthChanged(item);
427 if (item == indicator)
428 emit q->implicitIndicatorWidthChanged();
429}
430
431void QQuickAbstractButtonPrivate::itemImplicitHeightChanged(QQuickItem *item)
432{
433 Q_Q(QQuickAbstractButton);
434 QQuickControlPrivate::itemImplicitHeightChanged(item);
435 if (item == indicator)
436 emit q->implicitIndicatorHeightChanged();
437}
438
439void QQuickAbstractButtonPrivate::itemDestroyed(QQuickItem *item)
440{
441 Q_Q(QQuickAbstractButton);
442 QQuickControlPrivate::itemDestroyed(item);
443 if (item == indicator) {
444 indicator = nullptr;
445 emit q->implicitIndicatorWidthChanged();
446 emit q->implicitIndicatorHeightChanged();
447 }
448}
449
450QQuickAbstractButton *QQuickAbstractButtonPrivate::findCheckedButton() const
451{
452 Q_Q(const QQuickAbstractButton);
453 if (group)
454 return group->checkedButton();
455
456 const QList<QQuickAbstractButton *> buttons = findExclusiveButtons();
457 // TODO: A singular QRadioButton can be unchecked, which seems logical,
458 // because there's nothing to be exclusive with. However, a RadioButton
459 // from QtQuick.Controls 1.x can never be unchecked, which is the behavior
460 // that QQuickRadioButton adopted. Uncommenting the following count check
461 // gives the QRadioButton behavior. Notice that tst_radiobutton.qml needs
462 // to be updated.
463 if (!autoExclusive /*|| buttons.count() == 1*/)
464 return nullptr;
465
466 for (QQuickAbstractButton *button : buttons) {
467 if (button->isChecked() && button != q)
468 return button;
469 }
470 return checked ? const_cast<QQuickAbstractButton *>(q) : nullptr;
471}
472
473QList<QQuickAbstractButton *> QQuickAbstractButtonPrivate::findExclusiveButtons() const
474{
475 QList<QQuickAbstractButton *> buttons;
476 if (group) {
477 QQmlListProperty<QQuickAbstractButton> groupButtons = group->buttons();
478 int count = groupButtons.count(&groupButtons);
479 for (int i = 0; i < count; ++i) {
480 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(groupButtons.at(&groupButtons, i));
481 if (button)
482 buttons += button;
483 }
484 } else if (parentItem) {
485 const auto childItems = parentItem->childItems();
486 for (QQuickItem *child : childItems) {
487 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(child);
488 if (button && button->autoExclusive() && !QQuickAbstractButtonPrivate::get(button)->group)
489 buttons += button;
490 }
491 }
492 return buttons;
493}
494
495QQuickAbstractButton::QQuickAbstractButton(QQuickItem *parent)
496 : QQuickControl(*(new QQuickAbstractButtonPrivate), parent)
497{
498 Q_D(QQuickAbstractButton);
499 d->init();
500}
501
502QQuickAbstractButton::QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQuickItem *parent)
503 : QQuickControl(dd, parent)
504{
505 Q_D(QQuickAbstractButton);
506 d->init();
507}
508
509QQuickAbstractButton::~QQuickAbstractButton()
510{
511 Q_D(QQuickAbstractButton);
512 d->removeImplicitSizeListener(d->indicator);
513 if (d->group) {
514 auto *attached = qobject_cast<QQuickButtonGroupAttached *>(
515 qmlAttachedPropertiesObject<QQuickButtonGroup>(this, false));
516 if (attached)
517 attached->setGroup(nullptr);
518 else
519 d->group->removeButton(this);
520 }
521#if QT_CONFIG(shortcut)
522 d->ungrabShortcut();
523#endif
524}
525
526/*!
527 \qmlproperty string QtQuick.Controls::AbstractButton::text
528
529 This property holds a textual description of the button.
530
531 \note The text is used for accessibility purposes, so it makes sense to
532 set a textual description even if the content item is an image.
533
534 \sa icon, display, {Control::contentItem}{contentItem}
535*/
536QString QQuickAbstractButton::text() const
537{
538 Q_D(const QQuickAbstractButton);
539 return d->explicitText || !d->action ? d->text : d->action->text();
540}
541
542void QQuickAbstractButton::setText(const QString &text)
543{
544 Q_D(QQuickAbstractButton);
545 d->setText(text, QQml::PropertyUtils::State::ExplicitlySet);
546}
547
548void QQuickAbstractButton::resetText()
549{
550 Q_D(QQuickAbstractButton);
551 d->setText(QString(), QQml::PropertyUtils::State::ImplicitlySet);
552}
553
554/*!
555 \qmlproperty bool QtQuick.Controls::AbstractButton::down
556
557 This property holds whether the button is visually down.
558
559 Unless explicitly set, this property follows the value of \l pressed. To
560 return to the default value, set this property to \c undefined.
561
562 \sa pressed
563*/
564bool QQuickAbstractButton::isDown() const
565{
566 Q_D(const QQuickAbstractButton);
567 return d->down;
568}
569
570void QQuickAbstractButton::setDown(bool down)
571{
572 Q_D(QQuickAbstractButton);
573 d->explicitDown = true;
574
575 if (d->down == down)
576 return;
577
578 d->down = down;
579 emit downChanged();
580}
581
582void QQuickAbstractButton::resetDown()
583{
584 Q_D(QQuickAbstractButton);
585 if (!d->explicitDown)
586 return;
587
588 setDown(d->pressed);
589 d->explicitDown = false;
590}
591
592/*!
593 \qmlproperty bool QtQuick.Controls::AbstractButton::pressed
594 \readonly
595
596 This property holds whether the button is physically pressed. A button can
597 be pressed by either touch or key events.
598
599 \sa down
600*/
601bool QQuickAbstractButton::isPressed() const
602{
603 Q_D(const QQuickAbstractButton);
604 return d->pressed;
605}
606
607void QQuickAbstractButton::setPressed(bool isPressed)
608{
609 Q_D(QQuickAbstractButton);
610 if (d->pressed == isPressed)
611 return;
612
613 d->pressed = isPressed;
614 setAccessibleProperty("pressed", isPressed);
615 emit pressedChanged();
616 buttonChange(ButtonPressedChanged);
617
618 if (!d->explicitDown) {
619 setDown(d->pressed);
620 d->explicitDown = false;
621 }
622}
623
624/*!
625 \qmlproperty bool QtQuick.Controls::AbstractButton::checked
626
627 This property holds whether the button is checked.
628
629 Since Qt 6.2, setting this property no longer affects the
630 \l {AbstractButton::}{checkable} property. Explicitly set the
631 \c checkable property if needed.
632
633 \sa checkable
634*/
635bool QQuickAbstractButton::isChecked() const
636{
637 Q_D(const QQuickAbstractButton);
638 return d->checked;
639}
640
641void QQuickAbstractButton::setChecked(bool checked)
642{
643 Q_D(QQuickAbstractButton);
644 if (d->checked == checked)
645 return;
646
647 d->checked = checked;
648 if (d->action)
649 d->action->setChecked(checked);
650 setAccessibleProperty("checked", checked);
651 buttonChange(ButtonCheckedChange);
652 emit checkedChanged();
653}
654
655/*!
656 \qmlproperty bool QtQuick.Controls::AbstractButton::checkable
657
658 This property holds whether the button is checkable.
659
660 A checkable button toggles between checked (on) and unchecked (off) when
661 the user clicks on it or presses the space bar while the button has active
662 focus.
663
664 The default value is \c false.
665
666 \sa checked
667*/
668bool QQuickAbstractButton::isCheckable() const
669{
670 Q_D(const QQuickAbstractButton);
671 return d->checkable;
672}
673
674void QQuickAbstractButton::setCheckable(bool checkable)
675{
676 Q_D(QQuickAbstractButton);
677 if (d->checkable == checkable)
678 return;
679
680 d->checkable = checkable;
681 if (d->action)
682 d->action->setCheckable(checkable);
683 setAccessibleProperty("checkable", checkable);
684 buttonChange(ButtonCheckableChange);
685 emit checkableChanged();
686}
687
688/*!
689 \qmlproperty bool QtQuick.Controls::AbstractButton::autoExclusive
690
691 This property holds whether auto-exclusivity is enabled.
692
693 If auto-exclusivity is enabled, checkable buttons that belong to the same
694 parent item behave as if they were part of the same ButtonGroup. Only
695 one button can be checked at any time; checking another button automatically
696 unchecks the previously checked one.
697
698 \note The property has no effect on buttons that belong to a ButtonGroup.
699
700 RadioButton and TabButton are auto-exclusive by default.
701*/
702bool QQuickAbstractButton::autoExclusive() const
703{
704 Q_D(const QQuickAbstractButton);
705 return d->autoExclusive;
706}
707
708void QQuickAbstractButton::setAutoExclusive(bool exclusive)
709{
710 Q_D(QQuickAbstractButton);
711 if (d->autoExclusive == exclusive)
712 return;
713
714 d->autoExclusive = exclusive;
715 emit autoExclusiveChanged();
716}
717
718/*!
719 \qmlproperty bool QtQuick.Controls::AbstractButton::autoRepeat
720
721 This property holds whether the button repeats \l pressed(), \l released()
722 and \l clicked() signals while the button is pressed and held down.
723
724 If this property is set to \c true, the \l pressAndHold() signal will not
725 be emitted.
726
727 The default value is \c false.
728
729 The initial delay and the repetition interval are defined in milliseconds
730 by \l autoRepeatDelay and \l autoRepeatInterval.
731*/
732bool QQuickAbstractButton::autoRepeat() const
733{
734 Q_D(const QQuickAbstractButton);
735 return d->autoRepeat;
736}
737
738void QQuickAbstractButton::setAutoRepeat(bool repeat)
739{
740 Q_D(QQuickAbstractButton);
741 if (d->autoRepeat == repeat)
742 return;
743
744 d->stopPressRepeat();
745 d->autoRepeat = repeat;
746 emit autoRepeatChanged();
747}
748
749/*!
750 \qmlproperty Item QtQuick.Controls::AbstractButton::indicator
751
752 This property holds the indicator item.
753*/
754QQuickItem *QQuickAbstractButton::indicator() const
755{
756 QQuickAbstractButtonPrivate *d = const_cast<QQuickAbstractButtonPrivate *>(d_func());
757 if (!d->indicator)
758 d->executeIndicator();
759 return d->indicator;
760}
761
762void QQuickAbstractButton::setIndicator(QQuickItem *indicator)
763{
764 Q_D(QQuickAbstractButton);
765 if (d->indicator == indicator)
766 return;
767
768 QQuickControlPrivate::warnIfCustomizationNotSupported(this, indicator, QStringLiteral("indicator"));
769
770 if (!d->indicator.isExecuting())
771 d->cancelIndicator();
772
773 const qreal oldImplicitIndicatorWidth = implicitIndicatorWidth();
774 const qreal oldImplicitIndicatorHeight = implicitIndicatorHeight();
775
776 d->removeImplicitSizeListener(d->indicator);
777 QQuickControlPrivate::hideOldItem(d->indicator);
778 d->indicator = indicator;
779
780 if (indicator) {
781 if (!indicator->parentItem())
782 indicator->setParentItem(this);
783 indicator->setAcceptedMouseButtons(Qt::LeftButton);
784 d->addImplicitSizeListener(indicator);
785 }
786
787 if (!qFuzzyCompare(oldImplicitIndicatorWidth, implicitIndicatorWidth()))
788 emit implicitIndicatorWidthChanged();
789 if (!qFuzzyCompare(oldImplicitIndicatorHeight, implicitIndicatorHeight()))
790 emit implicitIndicatorHeightChanged();
791 if (!d->indicator.isExecuting())
792 emit indicatorChanged();
793}
794
795/*!
796 \qmlproperty string QtQuick.Controls::AbstractButton::icon.name
797 \qmlproperty url QtQuick.Controls::AbstractButton::icon.source
798 \qmlproperty int QtQuick.Controls::AbstractButton::icon.width
799 \qmlproperty int QtQuick.Controls::AbstractButton::icon.height
800 \qmlproperty color QtQuick.Controls::AbstractButton::icon.color
801 \qmlproperty bool QtQuick.Controls::AbstractButton::icon.cache
802
803 This property group was added in QtQuick.Controls 2.3.
804
805 \include qquickicon.qdocinc grouped-properties
806
807 \sa text, display, {Icons in Qt Quick Controls}
808*/
809
810QQuickIcon QQuickAbstractButton::icon() const
811{
812 Q_D(const QQuickAbstractButton);
813 return d->effectiveIcon;
814}
815
816void QQuickAbstractButton::setIcon(const QQuickIcon &icon)
817{
818 Q_D(QQuickAbstractButton);
819 d->icon = icon;
820 d->icon.ensureRelativeSourceResolved(this);
821 d->updateEffectiveIcon();
822}
823
824/*!
825 \since QtQuick.Controls 2.3 (Qt 5.10)
826 \qmlproperty enumeration QtQuick.Controls::AbstractButton::display
827
828 This property determines how the \l icon and \l text are displayed within
829 the button.
830
831 \table
832 \header \li Display \li Result
833 \row \li \c AbstractButton.IconOnly \li \image qtquickcontrols-button-icononly.png
834 \row \li \c AbstractButton.TextOnly \li \image qtquickcontrols-button-textonly.png
835 \row \li \c AbstractButton.TextBesideIcon (default) \li \image qtquickcontrols-button-textbesideicon.png
836 \row \li \c AbstractButton.TextUnderIcon \li \image qtquickcontrols-button-textundericon.png
837 \endtable
838
839 \sa {Control::}{spacing}, {Control::}{padding}
840*/
841QQuickAbstractButton::Display QQuickAbstractButton::display() const
842{
843 Q_D(const QQuickAbstractButton);
844 return d->display;
845}
846
847void QQuickAbstractButton::setDisplay(Display display)
848{
849 Q_D(QQuickAbstractButton);
850 if (display == d->display)
851 return;
852
853 d->display = display;
854 emit displayChanged();
855}
856
857/*!
858 \since QtQuick.Controls 2.3 (Qt 5.10)
859 \qmlproperty Action QtQuick.Controls::AbstractButton::action
860
861 This property holds the button action.
862
863 \sa Action
864*/
865QQuickAction *QQuickAbstractButton::action() const
866{
867 Q_D(const QQuickAbstractButton);
868 return d->action;
869}
870
871void QQuickAbstractButton::setAction(QQuickAction *action)
872{
873 Q_D(QQuickAbstractButton);
874 if (d->action == action)
875 return;
876
877 const QString oldText = text();
878
879 if (QQuickAction *oldAction = d->action.data()) {
880 QQuickActionPrivate::get(oldAction)->unregisterItem(this);
881 QObjectPrivate::disconnect(oldAction, &QQuickAction::triggered, d, &QQuickAbstractButtonPrivate::click);
882 QObjectPrivate::disconnect(oldAction, &QQuickAction::textChanged, d, &QQuickAbstractButtonPrivate::actionTextChange);
883
884 QObjectPrivate::disconnect(oldAction, &QQuickAction::iconChanged, d, &QQuickAbstractButtonPrivate::updateEffectiveIcon);
885 disconnect(oldAction, &QQuickAction::checkedChanged, this, &QQuickAbstractButton::setChecked);
886 disconnect(oldAction, &QQuickAction::checkableChanged, this, &QQuickAbstractButton::setCheckable);
887 disconnect(oldAction, &QQuickAction::enabledChanged, this, &QQuickItem::setEnabled);
888 }
889
890 if (action) {
891 QQuickActionPrivate::get(action)->registerItem(this);
892 QObjectPrivate::connect(action, &QQuickAction::triggered, d, &QQuickAbstractButtonPrivate::click);
893 QObjectPrivate::connect(action, &QQuickAction::textChanged, d, &QQuickAbstractButtonPrivate::actionTextChange);
894
895 QObjectPrivate::connect(action, &QQuickAction::iconChanged, d, &QQuickAbstractButtonPrivate::updateEffectiveIcon);
896 connect(action, &QQuickAction::checkedChanged, this, &QQuickAbstractButton::setChecked);
897 connect(action, &QQuickAction::checkableChanged, this, &QQuickAbstractButton::setCheckable);
898 connect(action, &QQuickAction::enabledChanged, this, &QQuickItem::setEnabled);
899
900 setChecked(action->isChecked());
901 setCheckable(action->isCheckable());
902 setEnabled(action->isEnabled());
903 }
904
905#if QT_CONFIG(accessibility)
906 auto attached = qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(this, true));
907 Q_ASSERT(attached);
908 attached->setProxying(qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(action, true)));
909#endif
910
911 d->action = action;
912
913 if (oldText != text())
914 buttonChange(ButtonTextChange);
915
916 d->updateEffectiveIcon();
917
918 emit actionChanged();
919}
920
921/*!
922 \since QtQuick.Controls 2.4 (Qt 5.11)
923 \qmlproperty int QtQuick.Controls::AbstractButton::autoRepeatDelay
924
925 This property holds the initial delay of auto-repetition in milliseconds.
926 The default value is \c 300 ms.
927
928 \sa autoRepeat, autoRepeatInterval
929*/
930int QQuickAbstractButton::autoRepeatDelay() const
931{
932 Q_D(const QQuickAbstractButton);
933 return d->repeatDelay;
934}
935
936void QQuickAbstractButton::setAutoRepeatDelay(int delay)
937{
938 Q_D(QQuickAbstractButton);
939 if (d->repeatDelay == delay)
940 return;
941
942 d->repeatDelay = delay;
943 emit autoRepeatDelayChanged();
944}
945
946/*!
947 \since QtQuick.Controls 2.4 (Qt 5.11)
948 \qmlproperty int QtQuick.Controls::AbstractButton::autoRepeatInterval
949
950 This property holds the interval of auto-repetition in milliseconds.
951 The default value is \c 100 ms.
952
953 \sa autoRepeat, autoRepeatDelay
954*/
955int QQuickAbstractButton::autoRepeatInterval() const
956{
957 Q_D(const QQuickAbstractButton);
958 return d->repeatInterval;
959}
960
961void QQuickAbstractButton::setAutoRepeatInterval(int interval)
962{
963 Q_D(QQuickAbstractButton);
964 if (d->repeatInterval == interval)
965 return;
966
967 d->repeatInterval = interval;
968 emit autoRepeatIntervalChanged();
969}
970
971#if QT_CONFIG(shortcut)
972QKeySequence QQuickAbstractButton::shortcut() const
973{
974 Q_D(const QQuickAbstractButton);
975 return d->shortcut;
976}
977
978void QQuickAbstractButton::setShortcut(const QKeySequence &shortcut)
979{
980 Q_D(QQuickAbstractButton);
981 if (d->shortcut == shortcut)
982 return;
983
984 d->ungrabShortcut();
985 d->shortcut = shortcut;
986 if (isVisible())
987 d->grabShortcut();
988}
989#endif
990
991/*!
992 \readonly
993 \since QtQuick.Controls 2.4 (Qt 5.11)
994 \qmlproperty real QtQuick.Controls::AbstractButton::pressX
995
996 This property holds the x-coordinate of the last press.
997
998 \note The value is updated on touch moves, but left intact after touch release.
999
1000 \sa pressY
1001*/
1002qreal QQuickAbstractButton::pressX() const
1003{
1004 Q_D(const QQuickAbstractButton);
1005 return d->movePoint.x();
1006}
1007
1008/*!
1009 \readonly
1010 \since QtQuick.Controls 2.4 (Qt 5.11)
1011 \qmlproperty real QtQuick.Controls::AbstractButton::pressY
1012
1013 This property holds the y-coordinate of the last press.
1014
1015 \note The value is updated on touch moves, but left intact after touch release.
1016
1017 \sa pressX
1018*/
1019qreal QQuickAbstractButton::pressY() const
1020{
1021 Q_D(const QQuickAbstractButton);
1022 return d->movePoint.y();
1023}
1024
1025/*!
1026 \since QtQuick.Controls 2.5 (Qt 5.12)
1027 \qmlproperty real QtQuick.Controls::AbstractButton::implicitIndicatorWidth
1028 \readonly
1029
1030 This property holds the implicit indicator width.
1031
1032 The value is equal to \c {indicator ? indicator.implicitWidth : 0}.
1033
1034 This is typically used, together with \l {Control::}{implicitContentWidth} and
1035 \l {Control::}{implicitBackgroundWidth}, to calculate the \l {Item::}{implicitWidth}.
1036
1037 \sa implicitIndicatorHeight
1038*/
1039qreal QQuickAbstractButton::implicitIndicatorWidth() const
1040{
1041 Q_D(const QQuickAbstractButton);
1042 if (!d->indicator)
1043 return 0;
1044 return d->indicator->implicitWidth();
1045}
1046
1047/*!
1048 \since QtQuick.Controls 2.5 (Qt 5.12)
1049 \qmlproperty real QtQuick.Controls::AbstractButton::implicitIndicatorHeight
1050 \readonly
1051
1052 This property holds the implicit indicator height.
1053
1054 The value is equal to \c {indicator ? indicator.implicitHeight : 0}.
1055
1056 This is typically used, together with \l {Control::}{implicitContentHeight} and
1057 \l {Control::}{implicitBackgroundHeight}, to calculate the \l {Item::}{implicitHeight}.
1058
1059 \sa implicitIndicatorWidth
1060*/
1061qreal QQuickAbstractButton::implicitIndicatorHeight() const
1062{
1063 Q_D(const QQuickAbstractButton);
1064 if (!d->indicator)
1065 return 0;
1066 return d->indicator->implicitHeight();
1067}
1068
1069/*!
1070 \qmlmethod void QtQuick.Controls::AbstractButton::toggle()
1071
1072 Toggles the checked state of the button.
1073
1074 \sa click(), animateClick()
1075*/
1076void QQuickAbstractButton::toggle()
1077{
1078 Q_D(QQuickAbstractButton);
1079 setChecked(!d->checked);
1080}
1081
1082/*!
1083 \since Qt 6.8
1084 \qmlmethod void QtQuick.Controls::AbstractButton::click()
1085
1086 Simulates the button being clicked with no delay between press and release.
1087
1088 All signals associated with a click are emitted as appropriate.
1089
1090 If the \l [QML] {Item::} {focusPolicy} includes \c Qt.ClickFocus,
1091 \l [QML] {Item::} {activeFocus} will become \c true.
1092
1093 This function does nothing if the button is
1094 \l [QML] {Item::enabled} {disabled}.
1095
1096 Calling this function again before the button is released resets
1097 the release timer.
1098
1099 \sa animateClick(), pressed(), released(), clicked()
1100*/
1101void QQuickAbstractButton::click()
1102{
1103 Q_D(QQuickAbstractButton);
1104 if (!isEnabled())
1105 return;
1106
1107 // QQuickItemPrivate::deliverPointerEvent calls setFocusIfNeeded on real clicks,
1108 // so we need to do it ourselves.
1109 const bool setFocusOnPress = !QGuiApplication::styleHints()->setFocusOnTouchRelease();
1110 if (setFocusOnPress && focusPolicy() & Qt::ClickFocus)
1111 forceActiveFocus(Qt::MouseFocusReason);
1112
1113 const QPointF eventPos(d->width / 2, d->height / 2);
1114 d->handlePress(eventPos, 0);
1115 d->handleRelease(eventPos, 0);
1116}
1117
1118/*!
1119 \since Qt 6.8
1120 \qmlmethod void QtQuick.Controls::AbstractButton::animateClick()
1121
1122 Simulates the button being clicked, with a 100 millisecond delay
1123 between press and release, animating its visual state in the
1124 process.
1125
1126 All signals associated with a click are emitted as appropriate.
1127
1128 If the \l [QML] {Item::} {focusPolicy} includes \c Qt.ClickFocus,
1129 \l [QML] {Item::}{activeFocus} will become \c true.
1130
1131 This function does nothing if the button is
1132 \l [QML] {Item::enabled} {disabled}.
1133
1134 Calling this function again before the button is released resets
1135 the release timer.
1136
1137 \sa click(), pressed(), released(), clicked()
1138*/
1139void QQuickAbstractButton::animateClick()
1140{
1141 Q_D(QQuickAbstractButton);
1142 if (!isEnabled())
1143 return;
1144
1145 // See comment in click() for why we do this.
1146 const bool setFocusOnPress = !QGuiApplication::styleHints()->setFocusOnTouchRelease();
1147 if (setFocusOnPress && focusPolicy() & Qt::ClickFocus)
1148 forceActiveFocus(Qt::MouseFocusReason);
1149
1150 // If the timer was already running, kill it so we can restart it.
1151 if (d->animateTimer != 0) {
1152 killTimer(d->animateTimer);
1153 d->animateTimer = 0;
1154 } else {
1155 d->handlePress(QPointF(d->width / 2, d->height / 2), 0);
1156 }
1157
1158 d->animateTimer = startTimer(100);
1159}
1160
1161void QQuickAbstractButton::componentComplete()
1162{
1163 Q_D(QQuickAbstractButton);
1164 d->executeIndicator(true);
1165 QQuickControl::componentComplete();
1166}
1167
1168bool QQuickAbstractButton::event(QEvent *event)
1169{
1170#if QT_CONFIG(shortcut)
1171 Q_D(QQuickAbstractButton);
1172 if (event->type() == QEvent::Shortcut) {
1173 QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
1174 if (se->shortcutId() == d->shortcutId) {
1175 d->trigger();
1176 return true;
1177 }
1178 }
1179#endif
1180 return QQuickControl::event(event);
1181}
1182
1183void QQuickAbstractButton::focusOutEvent(QFocusEvent *event)
1184{
1185 Q_D(QQuickAbstractButton);
1186 QQuickControl::focusOutEvent(event);
1187 if (d->touchId == -1) // don't ungrab on multi-touch if another control gets focused
1188 d->handleUngrab();
1189}
1190
1191void QQuickAbstractButton::keyPressEvent(QKeyEvent *event)
1192{
1193 Q_D(QQuickAbstractButton);
1194 QQuickControl::keyPressEvent(event);
1195 if (d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
1196 d->setPressPoint(d->centerPressPoint());
1197 setPressed(true);
1198
1199 if (d->autoRepeat)
1200 d->startRepeatDelay();
1201
1202 emit pressed();
1203 event->accept();
1204 }
1205}
1206
1207void QQuickAbstractButton::keyReleaseEvent(QKeyEvent *event)
1208{
1209 Q_D(QQuickAbstractButton);
1210 QQuickControl::keyReleaseEvent(event);
1211 if (d->pressed && d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
1212 setPressed(false);
1213
1214 nextCheckState();
1215 emit released();
1216 d->trigger();
1217
1218 if (d->autoRepeat)
1219 d->stopPressRepeat();
1220 event->accept();
1221 }
1222}
1223
1224void QQuickAbstractButton::mousePressEvent(QMouseEvent *event)
1225{
1226 if (!(event->buttons() & Qt::LeftButton)) {
1227 event->ignore();
1228 return;
1229 }
1230
1231 Q_D(QQuickAbstractButton);
1232 d->pressButtons = event->buttons();
1233 QQuickControl::mousePressEvent(event);
1234}
1235
1236void QQuickAbstractButton::mouseDoubleClickEvent(QMouseEvent *event)
1237{
1238 Q_UNUSED(event);
1239 Q_D(QQuickAbstractButton);
1240 if (d->isDoubleClickConnected()) {
1241 // don't call QQuickItem::mouseDoubleClickEvent(): it would ignore()
1242 emit doubleClicked();
1243 d->wasDoubleClick = true;
1244 }
1245}
1246
1247void QQuickAbstractButton::timerEvent(QTimerEvent *event)
1248{
1249 Q_D(QQuickAbstractButton);
1250 QQuickControl::timerEvent(event);
1251 if (event->timerId() == d->holdTimer) {
1252 d->stopPressAndHold();
1253 d->wasHeld = true;
1254 emit pressAndHold();
1255 } else if (event->timerId() == d->delayTimer) {
1256 d->startPressRepeat();
1257 } else if (event->timerId() == d->repeatTimer) {
1258 emit released();
1259 d->trigger();
1260 emit pressed();
1261 } else if (event->timerId() == d->animateTimer) {
1262 const bool setFocusOnRelease = QGuiApplication::styleHints()->setFocusOnTouchRelease();
1263 if (setFocusOnRelease && focusPolicy() & Qt::ClickFocus)
1264 forceActiveFocus(Qt::MouseFocusReason);
1265 d->handleRelease(QPointF(d->width / 2, d->height / 2), 0);
1266 killTimer(d->animateTimer);
1267 d->animateTimer = 0;
1268 }
1269}
1270
1271void QQuickAbstractButton::itemChange(ItemChange change, const ItemChangeData &value)
1272{
1273 QQuickControl::itemChange(change, value);
1274#if QT_CONFIG(shortcut)
1275 Q_D(QQuickAbstractButton);
1276 if (change == ItemVisibleHasChanged) {
1277 if (value.boolValue)
1278 d->grabShortcut();
1279 else
1280 d->ungrabShortcut();
1281 }
1282#endif
1283}
1284
1285void QQuickAbstractButton::buttonChange(ButtonChange change)
1286{
1287 Q_D(QQuickAbstractButton);
1288 switch (change) {
1289 case ButtonCheckedChange:
1290 if (d->checked) {
1291 QQuickAbstractButton *button = d->findCheckedButton();
1292 if (button && button != this)
1293 button->setChecked(false);
1294 }
1295 break;
1296 case ButtonTextChange: {
1297 const QString txt = text();
1298#if QT_CONFIG(accessibility)
1299 maybeSetAccessibleName(qt_accStripAmp(txt));
1300#endif
1301#if QT_CONFIG(shortcut)
1302 setShortcut(QKeySequence::mnemonic(txt));
1303#endif
1304 emit textChanged();
1305 break;
1306 }
1307 default:
1308 break;
1309 }
1310}
1311
1312void QQuickAbstractButton::nextCheckState()
1313{
1314 Q_D(QQuickAbstractButton);
1315 if (!d->checkable)
1316 return;
1317
1318 if (d->checked) {
1319 if (d->findCheckedButton() == this)
1320 return;
1321 if (d->action) {
1322 // For non-exclusive groups checkedAction is null
1323 if (const auto group = QQuickActionPrivate::get(d->action)->group)
1324 if (group->checkedAction() == d->action)
1325 return;
1326 }
1327 }
1328
1329 d->toggle(!d->checked);
1330}
1331
1332#if QT_CONFIG(accessibility)
1333void QQuickAbstractButton::accessibilityActiveChanged(bool active)
1334{
1335 QQuickControl::accessibilityActiveChanged(active);
1336
1337 Q_D(QQuickAbstractButton);
1338 if (active) {
1339 maybeSetAccessibleName(qt_accStripAmp(text()));
1340 setAccessibleProperty("pressed", d->pressed);
1341 setAccessibleProperty("checked", d->checked);
1342 setAccessibleProperty("checkable", d->checkable);
1343 }
1344}
1345
1346QAccessible::Role QQuickAbstractButton::accessibleRole() const
1347{
1348 Q_D(const QQuickAbstractButton);
1349 if (d->checkable) {
1350 return QAccessible::CheckBox;
1351 }
1352 return QAccessible::Button;
1353}
1354
1355void QQuickAbstractButton::accessiblePressAction()
1356{
1357 Q_D(QQuickAbstractButton);
1358 d->accessiblePressAction();
1359}
1360#endif
1361
1362QT_END_NAMESPACE
1363
1364#include "moc_qquickabstractbutton_p.cpp"