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