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

source code of qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp