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