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