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