1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "private/qabstractbutton_p.h"
41
42#if QT_CONFIG(itemviews)
43#include "qabstractitemview.h"
44#endif
45#if QT_CONFIG(buttongroup)
46#include "qbuttongroup.h"
47#include "private/qapplication_p.h"
48#include "private/qbuttongroup_p.h"
49#endif
50#include "qabstractbutton_p.h"
51#include "qevent.h"
52#include "qpainter.h"
53#include "qapplication.h"
54#include "qstyle.h"
55#ifndef QT_NO_ACCESSIBILITY
56#include "qaccessible.h"
57#endif
58
59#include <algorithm>
60
61QT_BEGIN_NAMESPACE
62
63#define AUTO_REPEAT_DELAY 300
64#define AUTO_REPEAT_INTERVAL 100
65
66Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets();
67
68/*!
69 \class QAbstractButton
70
71 \brief The QAbstractButton class is the abstract base class of
72 button widgets, providing functionality common to buttons.
73
74 \ingroup abstractwidgets
75 \inmodule QtWidgets
76
77 This class implements an \e abstract button.
78 Subclasses of this class handle user actions, and specify how the button
79 is drawn.
80
81 QAbstractButton provides support for both push buttons and checkable
82 (toggle) buttons. Checkable buttons are implemented in the QRadioButton
83 and QCheckBox classes. Push buttons are implemented in the
84 QPushButton and QToolButton classes; these also provide toggle
85 behavior if required.
86
87 Any button can display a label containing text and an icon. setText()
88 sets the text; setIcon() sets the icon. If a button is disabled, its label
89 is changed to give the button a "disabled" appearance.
90
91 If the button is a text button with a string containing an
92 ampersand ('&'), QAbstractButton automatically creates a shortcut
93 key. For example:
94
95 \snippet code/src_gui_widgets_qabstractbutton.cpp 0
96
97 The \uicontrol Alt+C shortcut is assigned to the button, i.e., when the
98 user presses \uicontrol Alt+C the button will call animateClick(). See
99 the \l {QShortcut#mnemonic}{QShortcut} documentation for details. To
100 display an actual ampersand, use '&&'.
101
102 You can also set a custom shortcut key using the setShortcut()
103 function. This is useful mostly for buttons that do not have any
104 text, and therefore can't have any automatic shortcut.
105
106 \snippet code/src_gui_widgets_qabstractbutton.cpp 1
107
108 All the buttons provided by Qt (QPushButton, QToolButton,
109 QCheckBox, and QRadioButton) can display both \l text and \l{icon}{icons}.
110
111 A button can be made the default button in a dialog by means of
112 QPushButton::setDefault() and QPushButton::setAutoDefault().
113
114 QAbstractButton provides most of the states used for buttons:
115
116 \list
117
118 \li isDown() indicates whether the button is \e pressed down.
119
120 \li isChecked() indicates whether the button is \e checked. Only
121 checkable buttons can be checked and unchecked (see below).
122
123 \li isEnabled() indicates whether the button can be pressed by the
124 user. \note As opposed to other widgets, buttons derived from
125 QAbstractButton accept mouse and context menu events
126 when disabled.
127
128 \li setAutoRepeat() sets whether the button will auto-repeat if the
129 user holds it down. \l autoRepeatDelay and \l autoRepeatInterval
130 define how auto-repetition is done.
131
132 \li setCheckable() sets whether the button is a toggle button or not.
133
134 \endlist
135
136 The difference between isDown() and isChecked() is as follows.
137 When the user clicks a toggle button to check it, the button is first
138 \e pressed then released into the \e checked state. When the user
139 clicks it again (to uncheck it), the button moves first to the
140 \e pressed state, then to the \e unchecked state (isChecked() and
141 isDown() are both false).
142
143 QAbstractButton provides four signals:
144
145 \list 1
146
147 \li pressed() is emitted when the left mouse button is pressed while
148 the mouse cursor is inside the button.
149
150 \li released() is emitted when the left mouse button is released.
151
152 \li clicked() is emitted when the button is first pressed and then
153 released, when the shortcut key is typed, or when click() or
154 animateClick() is called.
155
156 \li toggled() is emitted when the state of a toggle button changes.
157
158 \endlist
159
160 To subclass QAbstractButton, you must reimplement at least
161 paintEvent() to draw the button's outline and its text or pixmap. It
162 is generally advisable to reimplement sizeHint() as well, and
163 sometimes hitButton() (to determine whether a button press is within
164 the button). For buttons with more than two states (like tri-state
165 buttons), you will also have to reimplement checkStateSet() and
166 nextCheckState().
167
168 \sa QButtonGroup
169*/
170
171QAbstractButtonPrivate::QAbstractButtonPrivate(QSizePolicy::ControlType type)
172 :
173#ifndef QT_NO_SHORTCUT
174 shortcutId(0),
175#endif
176 checkable(false), checked(false), autoRepeat(false), autoExclusive(false),
177 down(false), blockRefresh(false), pressed(false),
178#if QT_CONFIG(buttongroup)
179 group(nullptr),
180#endif
181 autoRepeatDelay(AUTO_REPEAT_DELAY),
182 autoRepeatInterval(AUTO_REPEAT_INTERVAL),
183 controlType(type)
184{}
185
186QList<QAbstractButton *>QAbstractButtonPrivate::queryButtonList() const
187{
188#if QT_CONFIG(buttongroup)
189 if (group)
190 return group->d_func()->buttonList;
191#endif
192
193 QList<QAbstractButton*>candidates = parent->findChildren<QAbstractButton *>();
194 if (autoExclusive) {
195 auto isNoMemberOfMyAutoExclusiveGroup = [](QAbstractButton *candidate) {
196 return !candidate->autoExclusive()
197#if QT_CONFIG(buttongroup)
198 || candidate->group()
199#endif
200 ;
201 };
202 candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
203 isNoMemberOfMyAutoExclusiveGroup),
204 candidates.end());
205 }
206 return candidates;
207}
208
209QAbstractButton *QAbstractButtonPrivate::queryCheckedButton() const
210{
211#if QT_CONFIG(buttongroup)
212 if (group)
213 return group->d_func()->checkedButton;
214#endif
215
216 Q_Q(const QAbstractButton);
217 QList<QAbstractButton *> buttonList = queryButtonList();
218 if (!autoExclusive || buttonList.count() == 1) // no group
219 return nullptr;
220
221 for (int i = 0; i < buttonList.count(); ++i) {
222 QAbstractButton *b = buttonList.at(i);
223 if (b->d_func()->checked && b != q)
224 return b;
225 }
226 return checked ? const_cast<QAbstractButton *>(q) : nullptr;
227}
228
229void QAbstractButtonPrivate::notifyChecked()
230{
231#if QT_CONFIG(buttongroup)
232 Q_Q(QAbstractButton);
233 if (group) {
234 QAbstractButton *previous = group->d_func()->checkedButton;
235 group->d_func()->checkedButton = q;
236 if (group->d_func()->exclusive && previous && previous != q)
237 previous->nextCheckState();
238 } else
239#endif
240 if (autoExclusive) {
241 if (QAbstractButton *b = queryCheckedButton())
242 b->setChecked(false);
243 }
244}
245
246void QAbstractButtonPrivate::moveFocus(int key)
247{
248 QList<QAbstractButton *> buttonList = queryButtonList();
249#if QT_CONFIG(buttongroup)
250 bool exclusive = group ? group->d_func()->exclusive : autoExclusive;
251#else
252 bool exclusive = autoExclusive;
253#endif
254 QWidget *f = QApplication::focusWidget();
255 QAbstractButton *fb = qobject_cast<QAbstractButton *>(f);
256 if (!fb || !buttonList.contains(fb))
257 return;
258
259 QAbstractButton *candidate = nullptr;
260 int bestScore = -1;
261 QRect target = f->rect().translated(f->mapToGlobal(QPoint(0,0)));
262 QPoint goal = target.center();
263 uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
264
265 for (int i = 0; i < buttonList.count(); ++i) {
266 QAbstractButton *button = buttonList.at(i);
267 if (button != f && button->window() == f->window() && button->isEnabled() && !button->isHidden() &&
268 (exclusive || (button->focusPolicy() & focus_flag) == focus_flag)) {
269 QRect buttonRect = button->rect().translated(button->mapToGlobal(QPoint(0,0)));
270 QPoint p = buttonRect.center();
271
272 //Priority to widgets that overlap on the same coordinate.
273 //In that case, the distance in the direction will be used as significant score,
274 //take also in account orthogonal distance in case two widget are in the same distance.
275 int score;
276 if ((buttonRect.x() < target.right() && target.x() < buttonRect.right())
277 && (key == Qt::Key_Up || key == Qt::Key_Down)) {
278 //one item's is at the vertical of the other
279 score = (qAbs(p.y() - goal.y()) << 16) + qAbs(p.x() - goal.x());
280 } else if ((buttonRect.y() < target.bottom() && target.y() < buttonRect.bottom())
281 && (key == Qt::Key_Left || key == Qt::Key_Right) ) {
282 //one item's is at the horizontal of the other
283 score = (qAbs(p.x() - goal.x()) << 16) + qAbs(p.y() - goal.y());
284 } else {
285 score = (1 << 30) + (p.y() - goal.y()) * (p.y() - goal.y()) + (p.x() - goal.x()) * (p.x() - goal.x());
286 }
287
288 if (score > bestScore && candidate)
289 continue;
290
291 switch(key) {
292 case Qt::Key_Up:
293 if (p.y() < goal.y()) {
294 candidate = button;
295 bestScore = score;
296 }
297 break;
298 case Qt::Key_Down:
299 if (p.y() > goal.y()) {
300 candidate = button;
301 bestScore = score;
302 }
303 break;
304 case Qt::Key_Left:
305 if (p.x() < goal.x()) {
306 candidate = button;
307 bestScore = score;
308 }
309 break;
310 case Qt::Key_Right:
311 if (p.x() > goal.x()) {
312 candidate = button;
313 bestScore = score;
314 }
315 break;
316 }
317 }
318 }
319
320 if (exclusive
321#ifdef QT_KEYPAD_NAVIGATION
322 && !QApplicationPrivate::keypadNavigationEnabled()
323#endif
324 && candidate
325 && fb->d_func()->checked
326 && candidate->d_func()->checkable)
327 candidate->click();
328
329 if (candidate) {
330 if (key == Qt::Key_Up || key == Qt::Key_Left)
331 candidate->setFocus(Qt::BacktabFocusReason);
332 else
333 candidate->setFocus(Qt::TabFocusReason);
334 }
335}
336
337void QAbstractButtonPrivate::fixFocusPolicy()
338{
339 Q_Q(QAbstractButton);
340#if QT_CONFIG(buttongroup)
341 if (!group && !autoExclusive)
342#else
343 if (!autoExclusive)
344#endif
345 return;
346
347 QList<QAbstractButton *> buttonList = queryButtonList();
348 for (int i = 0; i < buttonList.count(); ++i) {
349 QAbstractButton *b = buttonList.at(i);
350 if (!b->isCheckable())
351 continue;
352 b->setFocusPolicy((Qt::FocusPolicy) ((b == q || !q->isCheckable())
353 ? (b->focusPolicy() | Qt::TabFocus)
354 : (b->focusPolicy() & ~Qt::TabFocus)));
355 }
356}
357
358void QAbstractButtonPrivate::init()
359{
360 Q_Q(QAbstractButton);
361
362 q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy)));
363 q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, controlType));
364 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
365 q->setForegroundRole(QPalette::ButtonText);
366 q->setBackgroundRole(QPalette::Button);
367}
368
369void QAbstractButtonPrivate::refresh()
370{
371 Q_Q(QAbstractButton);
372
373 if (blockRefresh)
374 return;
375 q->update();
376}
377
378void QAbstractButtonPrivate::click()
379{
380 Q_Q(QAbstractButton);
381
382 down = false;
383 blockRefresh = true;
384 bool changeState = true;
385 if (checked && queryCheckedButton() == q) {
386 // the checked button of an exclusive or autoexclusive group cannot be unchecked
387#if QT_CONFIG(buttongroup)
388 if (group ? group->d_func()->exclusive : autoExclusive)
389#else
390 if (autoExclusive)
391#endif
392 changeState = false;
393 }
394
395 QPointer<QAbstractButton> guard(q);
396 if (changeState) {
397 q->nextCheckState();
398 if (!guard)
399 return;
400 }
401 blockRefresh = false;
402 refresh();
403 q->repaint();
404 if (guard)
405 emitReleased();
406 if (guard)
407 emitClicked();
408}
409
410void QAbstractButtonPrivate::emitClicked()
411{
412 Q_Q(QAbstractButton);
413 QPointer<QAbstractButton> guard(q);
414 emit q->clicked(checked);
415#if QT_CONFIG(buttongroup)
416 if (guard && group) {
417 emit group->idClicked(group->id(q));
418 if (guard && group)
419 emit group->buttonClicked(q);
420 }
421#endif
422}
423
424void QAbstractButtonPrivate::emitPressed()
425{
426 Q_Q(QAbstractButton);
427 QPointer<QAbstractButton> guard(q);
428 emit q->pressed();
429#if QT_CONFIG(buttongroup)
430 if (guard && group) {
431 emit group->idPressed(group->id(q));
432 if (guard && group)
433 emit group->buttonPressed(q);
434 }
435#endif
436}
437
438void QAbstractButtonPrivate::emitReleased()
439{
440 Q_Q(QAbstractButton);
441 QPointer<QAbstractButton> guard(q);
442 emit q->released();
443#if QT_CONFIG(buttongroup)
444 if (guard && group) {
445 emit group->idReleased(group->id(q));
446 if (guard && group)
447 emit group->buttonReleased(q);
448 }
449#endif
450}
451
452void QAbstractButtonPrivate::emitToggled(bool checked)
453{
454 Q_Q(QAbstractButton);
455 QPointer<QAbstractButton> guard(q);
456 emit q->toggled(checked);
457#if QT_CONFIG(buttongroup)
458 if (guard && group) {
459 emit group->idToggled(group->id(q), checked);
460 if (guard && group)
461 emit group->buttonToggled(q, checked);
462 }
463#endif
464}
465
466/*!
467 Constructs an abstract button with a \a parent.
468*/
469QAbstractButton::QAbstractButton(QWidget *parent)
470 : QWidget(*new QAbstractButtonPrivate, parent, { })
471{
472 Q_D(QAbstractButton);
473 d->init();
474}
475
476/*!
477 Destroys the button.
478 */
479 QAbstractButton::~QAbstractButton()
480{
481#if QT_CONFIG(buttongroup)
482 Q_D(QAbstractButton);
483 if (d->group)
484 d->group->removeButton(this);
485#endif
486}
487
488
489/*! \internal
490 */
491QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
492 : QWidget(dd, parent, { })
493{
494 Q_D(QAbstractButton);
495 d->init();
496}
497
498/*!
499\property QAbstractButton::text
500\brief the text shown on the button
501
502If the button has no text, the text() function will return an empty
503string.
504
505If the text contains an ampersand character ('&'), a shortcut is
506automatically created for it. The character that follows the '&' will
507be used as the shortcut key. Any previous shortcut will be
508overwritten or cleared if no shortcut is defined by the text. See the
509\l {QShortcut#mnemonic}{QShortcut} documentation for details. To
510display an actual ampersand, use '&&'.
511
512There is no default text.
513*/
514
515void QAbstractButton::setText(const QString &text)
516{
517 Q_D(QAbstractButton);
518 if (d->text == text)
519 return;
520 d->text = text;
521#ifndef QT_NO_SHORTCUT
522 QKeySequence newMnemonic = QKeySequence::mnemonic(text);
523 setShortcut(newMnemonic);
524#endif
525 d->sizeHint = QSize();
526 update();
527 updateGeometry();
528#ifndef QT_NO_ACCESSIBILITY
529 QAccessibleEvent event(this, QAccessible::NameChanged);
530 QAccessible::updateAccessibility(&event);
531#endif
532}
533
534QString QAbstractButton::text() const
535{
536 Q_D(const QAbstractButton);
537 return d->text;
538}
539
540
541/*!
542 \property QAbstractButton::icon
543 \brief the icon shown on the button
544
545 The icon's default size is defined by the GUI style, but can be
546 adjusted by setting the \l iconSize property.
547*/
548void QAbstractButton::setIcon(const QIcon &icon)
549{
550 Q_D(QAbstractButton);
551 d->icon = icon;
552 d->sizeHint = QSize();
553 update();
554 updateGeometry();
555}
556
557QIcon QAbstractButton::icon() const
558{
559 Q_D(const QAbstractButton);
560 return d->icon;
561}
562
563#ifndef QT_NO_SHORTCUT
564/*!
565\property QAbstractButton::shortcut
566\brief the mnemonic associated with the button
567*/
568
569void QAbstractButton::setShortcut(const QKeySequence &key)
570{
571 Q_D(QAbstractButton);
572 if (d->shortcutId != 0)
573 releaseShortcut(d->shortcutId);
574 d->shortcut = key;
575 d->shortcutId = grabShortcut(key);
576}
577
578QKeySequence QAbstractButton::shortcut() const
579{
580 Q_D(const QAbstractButton);
581 return d->shortcut;
582}
583#endif // QT_NO_SHORTCUT
584
585/*!
586\property QAbstractButton::checkable
587\brief whether the button is checkable
588
589By default, the button is not checkable.
590
591\sa checked
592*/
593void QAbstractButton::setCheckable(bool checkable)
594{
595 Q_D(QAbstractButton);
596 if (d->checkable == checkable)
597 return;
598
599 d->checkable = checkable;
600 d->checked = false;
601}
602
603bool QAbstractButton::isCheckable() const
604{
605 Q_D(const QAbstractButton);
606 return d->checkable;
607}
608
609/*!
610\property QAbstractButton::checked
611\brief whether the button is checked
612
613Only checkable buttons can be checked. By default, the button is unchecked.
614
615\sa checkable
616*/
617void QAbstractButton::setChecked(bool checked)
618{
619 Q_D(QAbstractButton);
620 if (!d->checkable || d->checked == checked) {
621 if (!d->blockRefresh)
622 checkStateSet();
623 return;
624 }
625
626 if (!checked && d->queryCheckedButton() == this) {
627 // the checked button of an exclusive or autoexclusive group cannot be unchecked
628#if QT_CONFIG(buttongroup)
629 if (d->group ? d->group->d_func()->exclusive : d->autoExclusive)
630 return;
631 if (d->group)
632 d->group->d_func()->detectCheckedButton();
633#else
634 if (d->autoExclusive)
635 return;
636#endif
637 }
638
639 QPointer<QAbstractButton> guard(this);
640
641 d->checked = checked;
642 if (!d->blockRefresh)
643 checkStateSet();
644 d->refresh();
645
646 if (guard && checked)
647 d->notifyChecked();
648 if (guard)
649 d->emitToggled(checked);
650
651
652#ifndef QT_NO_ACCESSIBILITY
653 QAccessible::State s;
654 s.checked = true;
655 QAccessibleStateChangeEvent event(this, s);
656 QAccessible::updateAccessibility(&event);
657#endif
658}
659
660bool QAbstractButton::isChecked() const
661{
662 Q_D(const QAbstractButton);
663 return d->checked;
664}
665
666/*!
667 \property QAbstractButton::down
668 \brief whether the button is pressed down
669
670 If this property is \c true, the button is pressed down. The signals
671 pressed() and clicked() are not emitted if you set this property
672 to true. The default is false.
673*/
674
675void QAbstractButton::setDown(bool down)
676{
677 Q_D(QAbstractButton);
678 if (d->down == down)
679 return;
680 d->down = down;
681 d->refresh();
682 if (d->autoRepeat && d->down)
683 d->repeatTimer.start(d->autoRepeatDelay, this);
684 else
685 d->repeatTimer.stop();
686}
687
688bool QAbstractButton::isDown() const
689{
690 Q_D(const QAbstractButton);
691 return d->down;
692}
693
694/*!
695\property QAbstractButton::autoRepeat
696\brief whether autoRepeat is enabled
697
698If autoRepeat is enabled, then the pressed(), released(), and clicked() signals are emitted at
699regular intervals when the button is down. autoRepeat is off by default.
700The initial delay and the repetition interval are defined in milliseconds by \l
701autoRepeatDelay and \l autoRepeatInterval.
702
703Note: If a button is pressed down by a shortcut key, then auto-repeat is enabled and timed by the
704system and not by this class. The pressed(), released(), and clicked() signals will be emitted
705like in the normal case.
706*/
707
708void QAbstractButton::setAutoRepeat(bool autoRepeat)
709{
710 Q_D(QAbstractButton);
711 if (d->autoRepeat == autoRepeat)
712 return;
713 d->autoRepeat = autoRepeat;
714 if (d->autoRepeat && d->down)
715 d->repeatTimer.start(d->autoRepeatDelay, this);
716 else
717 d->repeatTimer.stop();
718}
719
720bool QAbstractButton::autoRepeat() const
721{
722 Q_D(const QAbstractButton);
723 return d->autoRepeat;
724}
725
726/*!
727 \property QAbstractButton::autoRepeatDelay
728 \brief the initial delay of auto-repetition
729 \since 4.2
730
731 If \l autoRepeat is enabled, then autoRepeatDelay defines the initial
732 delay in milliseconds before auto-repetition kicks in.
733
734 \sa autoRepeat, autoRepeatInterval
735*/
736
737void QAbstractButton::setAutoRepeatDelay(int autoRepeatDelay)
738{
739 Q_D(QAbstractButton);
740 d->autoRepeatDelay = autoRepeatDelay;
741}
742
743int QAbstractButton::autoRepeatDelay() const
744{
745 Q_D(const QAbstractButton);
746 return d->autoRepeatDelay;
747}
748
749/*!
750 \property QAbstractButton::autoRepeatInterval
751 \brief the interval of auto-repetition
752 \since 4.2
753
754 If \l autoRepeat is enabled, then autoRepeatInterval defines the
755 length of the auto-repetition interval in millisecons.
756
757 \sa autoRepeat, autoRepeatDelay
758*/
759
760void QAbstractButton::setAutoRepeatInterval(int autoRepeatInterval)
761{
762 Q_D(QAbstractButton);
763 d->autoRepeatInterval = autoRepeatInterval;
764}
765
766int QAbstractButton::autoRepeatInterval() const
767{
768 Q_D(const QAbstractButton);
769 return d->autoRepeatInterval;
770}
771
772
773
774/*!
775\property QAbstractButton::autoExclusive
776\brief whether auto-exclusivity is enabled
777
778If auto-exclusivity is enabled, checkable buttons that belong to the
779same parent widget behave as if they were part of the same
780exclusive button group. In an exclusive button group, only one button
781can be checked at any time; checking another button automatically
782unchecks the previously checked one.
783
784The property has no effect on buttons that belong to a button
785group.
786
787autoExclusive is off by default, except for radio buttons.
788
789\sa QRadioButton
790*/
791void QAbstractButton::setAutoExclusive(bool autoExclusive)
792{
793 Q_D(QAbstractButton);
794 d->autoExclusive = autoExclusive;
795}
796
797bool QAbstractButton::autoExclusive() const
798{
799 Q_D(const QAbstractButton);
800 return d->autoExclusive;
801}
802
803#if QT_CONFIG(buttongroup)
804/*!
805 Returns the group that this button belongs to.
806
807 If the button is not a member of any QButtonGroup, this function
808 returns \nullptr.
809
810 \sa QButtonGroup
811*/
812QButtonGroup *QAbstractButton::group() const
813{
814 Q_D(const QAbstractButton);
815 return d->group;
816}
817#endif // QT_CONFIG(buttongroup)
818
819/*!
820Performs an animated click: the button is pressed immediately, and
821released 100ms later.
822
823Calling this function again before the button is released resets
824the release timer.
825
826All signals associated with a click are emitted as appropriate.
827
828This function does nothing if the button is \l{setEnabled()}{disabled.}
829
830\sa click()
831*/
832void QAbstractButton::animateClick()
833{
834 if (!isEnabled())
835 return;
836 Q_D(QAbstractButton);
837 if (d->checkable && focusPolicy() & Qt::ClickFocus)
838 setFocus();
839 setDown(true);
840 repaint();
841 if (!d->animateTimer.isActive())
842 d->emitPressed();
843 d->animateTimer.start(100, this);
844}
845
846/*!
847Performs a click.
848
849All the usual signals associated with a click are emitted as
850appropriate. If the button is checkable, the state of the button is
851toggled.
852
853This function does nothing if the button is \l{setEnabled()}{disabled.}
854
855\sa animateClick()
856 */
857void QAbstractButton::click()
858{
859 if (!isEnabled())
860 return;
861 Q_D(QAbstractButton);
862 QPointer<QAbstractButton> guard(this);
863 d->down = true;
864 d->emitPressed();
865 if (guard) {
866 d->down = false;
867 nextCheckState();
868 if (guard)
869 d->emitReleased();
870 if (guard)
871 d->emitClicked();
872 }
873}
874
875/*! \fn void QAbstractButton::toggle()
876
877 Toggles the state of a checkable button.
878
879 \sa checked
880*/
881void QAbstractButton::toggle()
882{
883 Q_D(QAbstractButton);
884 setChecked(!d->checked);
885}
886
887
888/*! This virtual handler is called when setChecked() is used,
889unless it is called from within nextCheckState(). It allows
890subclasses to reset their intermediate button states.
891
892\sa nextCheckState()
893 */
894void QAbstractButton::checkStateSet()
895{
896}
897
898/*! This virtual handler is called when a button is clicked. The
899default implementation calls setChecked(!isChecked()) if the button
900isCheckable(). It allows subclasses to implement intermediate button
901states.
902
903\sa checkStateSet()
904*/
905void QAbstractButton::nextCheckState()
906{
907 if (isCheckable())
908 setChecked(!isChecked());
909}
910
911/*!
912Returns \c true if \a pos is inside the clickable button rectangle;
913otherwise returns \c false.
914
915By default, the clickable area is the entire widget. Subclasses
916may reimplement this function to provide support for clickable
917areas of different shapes and sizes.
918*/
919bool QAbstractButton::hitButton(const QPoint &pos) const
920{
921 return rect().contains(pos);
922}
923
924/*! \reimp */
925bool QAbstractButton::event(QEvent *e)
926{
927 // as opposed to other widgets, disabled buttons accept mouse
928 // events. This avoids surprising click-through scenarios
929 if (!isEnabled()) {
930 switch(e->type()) {
931 case QEvent::TabletPress:
932 case QEvent::TabletRelease:
933 case QEvent::TabletMove:
934 case QEvent::MouseButtonPress:
935 case QEvent::MouseButtonRelease:
936 case QEvent::MouseButtonDblClick:
937 case QEvent::MouseMove:
938 case QEvent::HoverMove:
939 case QEvent::HoverEnter:
940 case QEvent::HoverLeave:
941 case QEvent::ContextMenu:
942#if QT_CONFIG(wheelevent)
943 case QEvent::Wheel:
944#endif
945 return true;
946 default:
947 break;
948 }
949 }
950
951#ifndef QT_NO_SHORTCUT
952 if (e->type() == QEvent::Shortcut) {
953 Q_D(QAbstractButton);
954 QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
955 if (d->shortcutId != se->shortcutId())
956 return false;
957 if (!se->isAmbiguous()) {
958 if (!d->animateTimer.isActive())
959 animateClick();
960 } else {
961 if (focusPolicy() != Qt::NoFocus)
962 setFocus(Qt::ShortcutFocusReason);
963 window()->setAttribute(Qt::WA_KeyboardFocusChange);
964 }
965 return true;
966 }
967#endif
968 return QWidget::event(e);
969}
970
971/*! \reimp */
972void QAbstractButton::mousePressEvent(QMouseEvent *e)
973{
974 Q_D(QAbstractButton);
975 if (e->button() != Qt::LeftButton) {
976 e->ignore();
977 return;
978 }
979 if (hitButton(e->position().toPoint())) {
980 setDown(true);
981 d->pressed = true;
982 repaint();
983 d->emitPressed();
984 e->accept();
985 } else {
986 e->ignore();
987 }
988}
989
990/*! \reimp */
991void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
992{
993 Q_D(QAbstractButton);
994
995 if (e->button() != Qt::LeftButton) {
996 e->ignore();
997 return;
998 }
999
1000 d->pressed = false;
1001
1002 if (!d->down) {
1003 // refresh is required by QMacStyle to resume the default button animation
1004 d->refresh();
1005 e->ignore();
1006 return;
1007 }
1008
1009 if (hitButton(e->position().toPoint())) {
1010 d->repeatTimer.stop();
1011 d->click();
1012 e->accept();
1013 } else {
1014 setDown(false);
1015 e->ignore();
1016 }
1017}
1018
1019/*! \reimp */
1020void QAbstractButton::mouseMoveEvent(QMouseEvent *e)
1021{
1022 Q_D(QAbstractButton);
1023 if (!(e->buttons() & Qt::LeftButton) || !d->pressed) {
1024 e->ignore();
1025 return;
1026 }
1027
1028 if (hitButton(e->position().toPoint()) != d->down) {
1029 setDown(!d->down);
1030 repaint();
1031 if (d->down)
1032 d->emitPressed();
1033 else
1034 d->emitReleased();
1035 e->accept();
1036 } else if (!hitButton(e->position().toPoint())) {
1037 e->ignore();
1038 }
1039}
1040
1041/*! \reimp */
1042void QAbstractButton::keyPressEvent(QKeyEvent *e)
1043{
1044 Q_D(QAbstractButton);
1045 bool next = true;
1046 switch (e->key()) {
1047 case Qt::Key_Enter:
1048 case Qt::Key_Return:
1049 e->ignore();
1050 break;
1051 case Qt::Key_Select:
1052 case Qt::Key_Space:
1053 if (!e->isAutoRepeat()) {
1054 setDown(true);
1055 repaint();
1056 d->emitPressed();
1057 }
1058 break;
1059 case Qt::Key_Up:
1060 next = false;
1061 Q_FALLTHROUGH();
1062 case Qt::Key_Left:
1063 case Qt::Key_Right:
1064 case Qt::Key_Down: {
1065#ifdef QT_KEYPAD_NAVIGATION
1066 if ((QApplicationPrivate::keypadNavigationEnabled()
1067 && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right))
1068 || (!QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
1069 || (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down))) {
1070 e->ignore();
1071 return;
1072 }
1073#endif
1074 QWidget *pw = parentWidget();
1075 if (d->autoExclusive
1076#if QT_CONFIG(buttongroup)
1077 || d->group
1078#endif
1079#if QT_CONFIG(itemviews)
1080 || (pw && qobject_cast<QAbstractItemView *>(pw->parentWidget()))
1081#endif
1082 ) {
1083 // ### Using qobject_cast to check if the parent is a viewport of
1084 // QAbstractItemView is a crude hack, and should be revisited and
1085 // cleaned up when fixing task 194373. It's here to ensure that we
1086 // keep compatibility outside QAbstractItemView.
1087 d->moveFocus(e->key());
1088 if (hasFocus()) // nothing happend, propagate
1089 e->ignore();
1090 } else {
1091 // Prefer parent widget, use this if parent is absent
1092 QWidget *w = pw ? pw : this;
1093 bool reverse = (w->layoutDirection() == Qt::RightToLeft);
1094 if ((e->key() == Qt::Key_Left && !reverse)
1095 || (e->key() == Qt::Key_Right && reverse)) {
1096 next = false;
1097 }
1098 focusNextPrevChild(next);
1099 }
1100 break;
1101 }
1102 default:
1103#ifndef QT_NO_SHORTCUT
1104 if (e->matches(QKeySequence::Cancel) && d->down) {
1105 setDown(false);
1106 repaint();
1107 d->emitReleased();
1108 return;
1109 }
1110#endif
1111 e->ignore();
1112 }
1113}
1114
1115/*! \reimp */
1116void QAbstractButton::keyReleaseEvent(QKeyEvent *e)
1117{
1118 Q_D(QAbstractButton);
1119
1120 if (!e->isAutoRepeat())
1121 d->repeatTimer.stop();
1122
1123 switch (e->key()) {
1124 case Qt::Key_Select:
1125 case Qt::Key_Space:
1126 if (!e->isAutoRepeat() && d->down)
1127 d->click();
1128 break;
1129 default:
1130 e->ignore();
1131 }
1132}
1133
1134/*!\reimp
1135 */
1136void QAbstractButton::timerEvent(QTimerEvent *e)
1137{
1138 Q_D(QAbstractButton);
1139 if (e->timerId() == d->repeatTimer.timerId()) {
1140 d->repeatTimer.start(d->autoRepeatInterval, this);
1141 if (d->down) {
1142 QPointer<QAbstractButton> guard(this);
1143 nextCheckState();
1144 if (guard)
1145 d->emitReleased();
1146 if (guard)
1147 d->emitClicked();
1148 if (guard)
1149 d->emitPressed();
1150 }
1151 } else if (e->timerId() == d->animateTimer.timerId()) {
1152 d->animateTimer.stop();
1153 d->click();
1154 }
1155}
1156
1157/*! \reimp */
1158void QAbstractButton::focusInEvent(QFocusEvent *e)
1159{
1160 Q_D(QAbstractButton);
1161#ifdef QT_KEYPAD_NAVIGATION
1162 if (!QApplicationPrivate::keypadNavigationEnabled())
1163#endif
1164 d->fixFocusPolicy();
1165 QWidget::focusInEvent(e);
1166}
1167
1168/*! \reimp */
1169void QAbstractButton::focusOutEvent(QFocusEvent *e)
1170{
1171 Q_D(QAbstractButton);
1172 if (e->reason() != Qt::PopupFocusReason && d->down) {
1173 d->down = false;
1174 d->emitReleased();
1175 }
1176 QWidget::focusOutEvent(e);
1177}
1178
1179/*! \reimp */
1180void QAbstractButton::changeEvent(QEvent *e)
1181{
1182 Q_D(QAbstractButton);
1183 switch (e->type()) {
1184 case QEvent::EnabledChange:
1185 if (!isEnabled() && d->down) {
1186 d->down = false;
1187 d->emitReleased();
1188 }
1189 break;
1190 default:
1191 d->sizeHint = QSize();
1192 break;
1193 }
1194 QWidget::changeEvent(e);
1195}
1196
1197/*!
1198 \fn void QAbstractButton::paintEvent(QPaintEvent *e)
1199 \reimp
1200*/
1201
1202/*!
1203 \fn void QAbstractButton::pressed()
1204
1205 This signal is emitted when the button is pressed down.
1206
1207 \sa released(), clicked()
1208*/
1209
1210/*!
1211 \fn void QAbstractButton::released()
1212
1213 This signal is emitted when the button is released.
1214
1215 \sa pressed(), clicked(), toggled()
1216*/
1217
1218/*!
1219\fn void QAbstractButton::clicked(bool checked)
1220
1221This signal is emitted when the button is activated (i.e., pressed down
1222then released while the mouse cursor is inside the button), when the
1223shortcut key is typed, or when click() or animateClick() is called.
1224Notably, this signal is \e not emitted if you call setDown(),
1225setChecked() or toggle().
1226
1227If the button is checkable, \a checked is true if the button is
1228checked, or false if the button is unchecked.
1229
1230\sa pressed(), released(), toggled()
1231*/
1232
1233/*!
1234\fn void QAbstractButton::toggled(bool checked)
1235
1236This signal is emitted whenever a checkable button changes its state.
1237\a checked is true if the button is checked, or false if the button is
1238unchecked.
1239
1240This may be the result of a user action, click() slot activation,
1241or because setChecked() is called.
1242
1243The states of buttons in exclusive button groups are updated before this
1244signal is emitted. This means that slots can act on either the "off"
1245signal or the "on" signal emitted by the buttons in the group whose
1246states have changed.
1247
1248For example, a slot that reacts to signals emitted by newly checked
1249buttons but which ignores signals from buttons that have been unchecked
1250can be implemented using the following pattern:
1251
1252\snippet code/src_gui_widgets_qabstractbutton.cpp 2
1253
1254Button groups can be created using the QButtonGroup class, and
1255updates to the button states monitored with the
1256\l{QButtonGroup::buttonClicked()} signal.
1257
1258\sa checked, clicked()
1259*/
1260
1261/*!
1262 \property QAbstractButton::iconSize
1263 \brief the icon size used for this button.
1264
1265 The default size is defined by the GUI style. This is a maximum
1266 size for the icons. Smaller icons will not be scaled up.
1267*/
1268
1269QSize QAbstractButton::iconSize() const
1270{
1271 Q_D(const QAbstractButton);
1272 if (d->iconSize.isValid())
1273 return d->iconSize;
1274 int e = style()->pixelMetric(QStyle::PM_ButtonIconSize, nullptr, this);
1275 return QSize(e, e);
1276}
1277
1278void QAbstractButton::setIconSize(const QSize &size)
1279{
1280 Q_D(QAbstractButton);
1281 if (d->iconSize == size)
1282 return;
1283
1284 d->iconSize = size;
1285 d->sizeHint = QSize();
1286 updateGeometry();
1287 if (isVisible()) {
1288 update();
1289 }
1290}
1291
1292
1293
1294QT_END_NAMESPACE
1295
1296#include "moc_qabstractbutton.cpp"
1297