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