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

source code of qtdeclarative/src/quicktemplates/qquickabstractbutton.cpp