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