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
435{
436 if (down)
437 state |= QStyle::State_Sunken;
438 return state;
439}
440
441/*!
442 Constructs an abstract button with a \a parent.
443*/
444QAbstractButton::QAbstractButton(QWidget *parent)
445 : QWidget(*new QAbstractButtonPrivate, parent, { })
446{
447 Q_D(QAbstractButton);
448 d->init();
449}
450
451/*!
452 Destroys the button.
453 */
454 QAbstractButton::~QAbstractButton()
455{
456#if QT_CONFIG(buttongroup)
457 Q_D(QAbstractButton);
458 if (d->group)
459 d->group->removeButton(this);
460#endif
461}
462
463
464/*! \internal
465 */
466QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
467 : QWidget(dd, parent, { })
468{
469 Q_D(QAbstractButton);
470 d->init();
471}
472
473/*!
474\property QAbstractButton::text
475\brief the text shown on the button
476
477If the button has no text, the text() function will return an empty
478string.
479
480If the text contains an ampersand character ('&'), a shortcut is
481automatically created for it. The character that follows the '&' will
482be used as the shortcut key. Any previous shortcut will be
483overwritten or cleared if no shortcut is defined by the text. See the
484\l {QShortcut#mnemonic}{QShortcut} documentation for details. To
485display an actual ampersand, use '&&'.
486
487There is no default text.
488*/
489
490void QAbstractButton::setText(const QString &text)
491{
492 Q_D(QAbstractButton);
493 if (d->text == text)
494 return;
495 d->text = text;
496#ifndef QT_NO_SHORTCUT
497 QKeySequence newMnemonic = QKeySequence::mnemonic(text);
498 setShortcut(newMnemonic);
499#endif
500 d->sizeHint = QSize();
501 update();
502 updateGeometry();
503#if QT_CONFIG(accessibility)
504 QAccessibleEvent event(this, QAccessible::NameChanged);
505 QAccessible::updateAccessibility(&event);
506#endif
507}
508
509QString QAbstractButton::text() const
510{
511 Q_D(const QAbstractButton);
512 return d->text;
513}
514
515
516/*!
517 \property QAbstractButton::icon
518 \brief the icon shown on the button
519
520 The icon's default size is defined by the GUI style, but can be
521 adjusted by setting the \l iconSize property.
522*/
523void QAbstractButton::setIcon(const QIcon &icon)
524{
525 Q_D(QAbstractButton);
526 d->icon = icon;
527 d->sizeHint = QSize();
528 update();
529 updateGeometry();
530}
531
532QIcon QAbstractButton::icon() const
533{
534 Q_D(const QAbstractButton);
535 return d->icon;
536}
537
538#ifndef QT_NO_SHORTCUT
539/*!
540\property QAbstractButton::shortcut
541\brief the mnemonic associated with the button
542*/
543
544void QAbstractButton::setShortcut(const QKeySequence &key)
545{
546 Q_D(QAbstractButton);
547 if (d->shortcutId != 0)
548 releaseShortcut(d->shortcutId);
549 d->shortcut = key;
550 d->shortcutId = grabShortcut(key);
551}
552
553QKeySequence QAbstractButton::shortcut() const
554{
555 Q_D(const QAbstractButton);
556 return d->shortcut;
557}
558#endif // QT_NO_SHORTCUT
559
560/*!
561\property QAbstractButton::checkable
562\brief whether the button is checkable
563
564By default, the button is not checkable.
565
566\sa checked
567*/
568void QAbstractButton::setCheckable(bool checkable)
569{
570 Q_D(QAbstractButton);
571 if (d->checkable == checkable)
572 return;
573
574 d->checkable = checkable;
575 d->checked = false;
576}
577
578bool QAbstractButton::isCheckable() const
579{
580 Q_D(const QAbstractButton);
581 return d->checkable;
582}
583
584/*!
585\property QAbstractButton::checked
586\brief whether the button is checked
587
588Only checkable buttons can be checked. By default, the button is unchecked.
589
590\sa checkable
591*/
592void QAbstractButton::setChecked(bool checked)
593{
594 Q_D(QAbstractButton);
595 if (!d->checkable || d->checked == checked) {
596 if (!d->blockRefresh)
597 checkStateSet();
598 return;
599 }
600
601 if (!checked && d->queryCheckedButton() == this) {
602 // the checked button of an exclusive or autoexclusive group cannot be unchecked
603#if QT_CONFIG(buttongroup)
604 if (d->group ? d->group->d_func()->exclusive : d->autoExclusive)
605 return;
606 if (d->group)
607 d->group->d_func()->detectCheckedButton();
608#else
609 if (d->autoExclusive)
610 return;
611#endif
612 }
613
614 QPointer<QAbstractButton> guard(this);
615
616 d->checked = checked;
617 if (!d->blockRefresh)
618 checkStateSet();
619 d->refresh();
620
621 if (guard && checked)
622 d->notifyChecked();
623 if (guard)
624 d->emitToggled(checked);
625
626#if QT_CONFIG(accessibility)
627 if (guard) {
628 QAccessible::State s;
629 s.checked = true;
630 QAccessibleStateChangeEvent event(this, s);
631 QAccessible::updateAccessibility(&event);
632 }
633#endif
634}
635
636bool QAbstractButton::isChecked() const
637{
638 Q_D(const QAbstractButton);
639 return d->checked;
640}
641
642/*!
643 \property QAbstractButton::down
644 \brief whether the button is pressed down
645
646 If this property is \c true, the button is pressed down. The signals
647 pressed() and clicked() are not emitted if you set this property
648 to true. The default is false.
649*/
650
651void QAbstractButton::setDown(bool down)
652{
653 Q_D(QAbstractButton);
654 if (d->down == down)
655 return;
656 d->down = down;
657 d->refresh();
658 if (d->autoRepeat && d->down)
659 d->repeatTimer.start(d->autoRepeatDelay, this);
660 else
661 d->repeatTimer.stop();
662}
663
664bool QAbstractButton::isDown() const
665{
666 Q_D(const QAbstractButton);
667 return d->down;
668}
669
670/*!
671\property QAbstractButton::autoRepeat
672\brief whether autoRepeat is enabled
673
674If autoRepeat is enabled, then the pressed(), released(), and clicked() signals are emitted at
675regular intervals when the button is down. autoRepeat is off by default.
676The initial delay and the repetition interval are defined in milliseconds by \l
677autoRepeatDelay and \l autoRepeatInterval.
678
679Note: If a button is pressed down by a shortcut key, then auto-repeat is enabled and timed by the
680system and not by this class. The pressed(), released(), and clicked() signals will be emitted
681like in the normal case.
682*/
683
684void QAbstractButton::setAutoRepeat(bool autoRepeat)
685{
686 Q_D(QAbstractButton);
687 if (d->autoRepeat == autoRepeat)
688 return;
689 d->autoRepeat = autoRepeat;
690 if (d->autoRepeat && d->down)
691 d->repeatTimer.start(d->autoRepeatDelay, this);
692 else
693 d->repeatTimer.stop();
694}
695
696bool QAbstractButton::autoRepeat() const
697{
698 Q_D(const QAbstractButton);
699 return d->autoRepeat;
700}
701
702/*!
703 \property QAbstractButton::autoRepeatDelay
704 \brief the initial delay of auto-repetition
705 \since 4.2
706
707 If \l autoRepeat is enabled, then autoRepeatDelay defines the initial
708 delay in milliseconds before auto-repetition kicks in.
709
710 \sa autoRepeat, autoRepeatInterval
711*/
712
713void QAbstractButton::setAutoRepeatDelay(int autoRepeatDelay)
714{
715 Q_D(QAbstractButton);
716 d->autoRepeatDelay = autoRepeatDelay;
717}
718
719int QAbstractButton::autoRepeatDelay() const
720{
721 Q_D(const QAbstractButton);
722 return d->autoRepeatDelay;
723}
724
725/*!
726 \property QAbstractButton::autoRepeatInterval
727 \brief the interval of auto-repetition
728 \since 4.2
729
730 If \l autoRepeat is enabled, then autoRepeatInterval defines the
731 length of the auto-repetition interval in millisecons.
732
733 \sa autoRepeat, autoRepeatDelay
734*/
735
736void QAbstractButton::setAutoRepeatInterval(int autoRepeatInterval)
737{
738 Q_D(QAbstractButton);
739 d->autoRepeatInterval = autoRepeatInterval;
740}
741
742int QAbstractButton::autoRepeatInterval() const
743{
744 Q_D(const QAbstractButton);
745 return d->autoRepeatInterval;
746}
747
748
749
750/*!
751\property QAbstractButton::autoExclusive
752\brief whether auto-exclusivity is enabled
753
754If auto-exclusivity is enabled, checkable buttons that belong to the
755same parent widget behave as if they were part of the same
756exclusive button group. In an exclusive button group, only one button
757can be checked at any time; checking another button automatically
758unchecks the previously checked one.
759
760The property has no effect on buttons that belong to a button
761group.
762
763autoExclusive is off by default, except for radio buttons.
764
765\sa QRadioButton
766*/
767void QAbstractButton::setAutoExclusive(bool autoExclusive)
768{
769 Q_D(QAbstractButton);
770 d->autoExclusive = autoExclusive;
771}
772
773bool QAbstractButton::autoExclusive() const
774{
775 Q_D(const QAbstractButton);
776 return d->autoExclusive;
777}
778
779#if QT_CONFIG(buttongroup)
780/*!
781 Returns the group that this button belongs to.
782
783 If the button is not a member of any QButtonGroup, this function
784 returns \nullptr.
785
786 \sa QButtonGroup
787*/
788QButtonGroup *QAbstractButton::group() const
789{
790 Q_D(const QAbstractButton);
791 return d->group;
792}
793#endif // QT_CONFIG(buttongroup)
794
795/*!
796Performs an animated click: the button is pressed immediately, and
797released 100ms later.
798
799Calling this function again before the button is released resets
800the release timer.
801
802All signals associated with a click are emitted as appropriate.
803
804This function does nothing if the button is \l{setEnabled()}{disabled.}
805
806\sa click()
807*/
808void QAbstractButton::animateClick()
809{
810 if (!isEnabled())
811 return;
812 Q_D(QAbstractButton);
813 if (d->checkable && focusPolicy() & Qt::ClickFocus)
814 setFocus();
815 setDown(true);
816 repaint();
817 if (!d->animateTimer.isActive())
818 d->emitPressed();
819 d->animateTimer.start(100, this);
820}
821
822/*!
823Performs a click.
824
825All the usual signals associated with a click are emitted as
826appropriate. If the button is checkable, the state of the button is
827toggled.
828
829This function does nothing if the button is \l{setEnabled()}{disabled.}
830
831\sa animateClick()
832 */
833void QAbstractButton::click()
834{
835 if (!isEnabled())
836 return;
837 Q_D(QAbstractButton);
838 QPointer<QAbstractButton> guard(this);
839 d->down = true;
840 d->emitPressed();
841 if (guard) {
842 d->down = false;
843 nextCheckState();
844 if (guard)
845 d->emitReleased();
846 if (guard)
847 d->emitClicked();
848 }
849}
850
851/*! \fn void QAbstractButton::toggle()
852
853 Toggles the state of a checkable button.
854
855 \sa checked
856*/
857void QAbstractButton::toggle()
858{
859 Q_D(QAbstractButton);
860 setChecked(!d->checked);
861}
862
863
864/*! This virtual handler is called when setChecked() is used,
865unless it is called from within nextCheckState(). It allows
866subclasses to reset their intermediate button states.
867
868\sa nextCheckState()
869 */
870void QAbstractButton::checkStateSet()
871{
872}
873
874/*! This virtual handler is called when a button is clicked. The
875default implementation calls setChecked(!isChecked()) if the button
876isCheckable(). It allows subclasses to implement intermediate button
877states.
878
879\sa checkStateSet()
880*/
881void QAbstractButton::nextCheckState()
882{
883 if (isCheckable())
884 setChecked(!isChecked());
885}
886
887/*!
888Returns \c true if \a pos is inside the clickable button rectangle;
889otherwise returns \c false.
890
891By default, the clickable area is the entire widget. Subclasses
892may reimplement this function to provide support for clickable
893areas of different shapes and sizes.
894*/
895bool QAbstractButton::hitButton(const QPoint &pos) const
896{
897 return rect().contains(pos);
898}
899
900/*! \reimp */
901bool QAbstractButton::event(QEvent *e)
902{
903 // as opposed to other widgets, disabled buttons accept mouse
904 // events. This avoids surprising click-through scenarios
905 if (!isEnabled()) {
906 switch(e->type()) {
907 case QEvent::TabletPress:
908 case QEvent::TabletRelease:
909 case QEvent::TabletMove:
910 case QEvent::MouseButtonPress:
911 case QEvent::MouseButtonRelease:
912 case QEvent::MouseButtonDblClick:
913 case QEvent::MouseMove:
914 case QEvent::HoverMove:
915 case QEvent::HoverEnter:
916 case QEvent::HoverLeave:
917 case QEvent::ContextMenu:
918 return true;
919 default:
920 break;
921 }
922 }
923
924#ifndef QT_NO_SHORTCUT
925 if (e->type() == QEvent::Shortcut) {
926 Q_D(QAbstractButton);
927 QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
928 if (d->shortcutId != se->shortcutId())
929 return false;
930 if (!se->isAmbiguous()) {
931 if (!d->animateTimer.isActive())
932 animateClick();
933 } else {
934 if (focusPolicy() != Qt::NoFocus)
935 setFocus(Qt::ShortcutFocusReason);
936 window()->setAttribute(Qt::WA_KeyboardFocusChange);
937 }
938 return true;
939 }
940#endif
941 return QWidget::event(e);
942}
943
944/*! \reimp */
945void QAbstractButton::mousePressEvent(QMouseEvent *e)
946{
947 Q_D(QAbstractButton);
948 if (e->button() != Qt::LeftButton) {
949 e->ignore();
950 return;
951 }
952 if (hitButton(e->position().toPoint())) {
953 setDown(true);
954 d->pressed = true;
955 repaint();
956 d->emitPressed();
957 e->accept();
958 } else {
959 e->ignore();
960 }
961}
962
963/*! \reimp */
964void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
965{
966 Q_D(QAbstractButton);
967
968 if (e->button() != Qt::LeftButton) {
969 e->ignore();
970 return;
971 }
972
973 d->pressed = false;
974
975 if (!d->down) {
976 // refresh is required by QMacStyle to resume the default button animation
977 d->refresh();
978 e->ignore();
979 return;
980 }
981
982 if (hitButton(e->position().toPoint())) {
983 d->repeatTimer.stop();
984 d->click();
985 e->accept();
986 } else {
987 setDown(false);
988 e->ignore();
989 }
990}
991
992/*! \reimp */
993void QAbstractButton::mouseMoveEvent(QMouseEvent *e)
994{
995 Q_D(QAbstractButton);
996 if (!(e->buttons() & Qt::LeftButton) || !d->pressed) {
997 e->ignore();
998 return;
999 }
1000
1001 if (hitButton(e->position().toPoint()) != d->down) {
1002 setDown(!d->down);
1003 repaint();
1004 if (d->down)
1005 d->emitPressed();
1006 else
1007 d->emitReleased();
1008 e->accept();
1009 } else if (!hitButton(e->position().toPoint())) {
1010 e->ignore();
1011 }
1012}
1013
1014/*! \reimp */
1015void QAbstractButton::keyPressEvent(QKeyEvent *e)
1016{
1017 Q_D(QAbstractButton);
1018 bool next = true;
1019
1020 const auto key = e->key();
1021 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
1022 ->themeHint(QPlatformTheme::ButtonPressKeys)
1023 .value<QList<Qt::Key>>();
1024 if (buttonPressKeys.contains(key) && !e->isAutoRepeat()) {
1025 setDown(true);
1026 repaint();
1027 d->emitPressed();
1028 return;
1029 }
1030
1031 switch (key) {
1032 case Qt::Key_Up:
1033 next = false;
1034 Q_FALLTHROUGH();
1035 case Qt::Key_Left:
1036 case Qt::Key_Right:
1037 case Qt::Key_Down: {
1038#ifdef QT_KEYPAD_NAVIGATION
1039 if ((QApplicationPrivate::keypadNavigationEnabled()
1040 && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right))
1041 || (!QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
1042 || (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down))) {
1043 e->ignore();
1044 return;
1045 }
1046#endif
1047 QWidget *pw = parentWidget();
1048 if (d->autoExclusive
1049#if QT_CONFIG(buttongroup)
1050 || d->group
1051#endif
1052#if QT_CONFIG(itemviews)
1053 || (pw && qobject_cast<QAbstractItemView *>(pw->parentWidget()))
1054#endif
1055 ) {
1056 // ### Using qobject_cast to check if the parent is a viewport of
1057 // QAbstractItemView is a crude hack, and should be revisited and
1058 // cleaned up when fixing task 194373. It's here to ensure that we
1059 // keep compatibility outside QAbstractItemView.
1060 d->moveFocus(e->key());
1061 if (hasFocus()) // nothing happened, propagate
1062 e->ignore();
1063 } else {
1064 // Prefer parent widget, use this if parent is absent
1065 QWidget *w = pw ? pw : this;
1066 bool reverse = (w->layoutDirection() == Qt::RightToLeft);
1067 if ((e->key() == Qt::Key_Left && !reverse)
1068 || (e->key() == Qt::Key_Right && reverse)) {
1069 next = false;
1070 }
1071 focusNextPrevChild(next);
1072 }
1073 break;
1074 }
1075 default:
1076#ifndef QT_NO_SHORTCUT
1077 if (e->matches(QKeySequence::Cancel) && d->down) {
1078 setDown(false);
1079 repaint();
1080 d->emitReleased();
1081 return;
1082 }
1083#endif
1084 e->ignore();
1085 }
1086}
1087
1088/*! \reimp */
1089void QAbstractButton::keyReleaseEvent(QKeyEvent *e)
1090{
1091 Q_D(QAbstractButton);
1092
1093 if (!e->isAutoRepeat())
1094 d->repeatTimer.stop();
1095
1096 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
1097 ->themeHint(QPlatformTheme::ButtonPressKeys)
1098 .value<QList<Qt::Key>>();
1099 if (buttonPressKeys.contains(e->key()) && !e->isAutoRepeat() && d->down) {
1100 d->click();
1101 return;
1102 }
1103
1104 e->ignore();
1105}
1106
1107/*!\reimp
1108 */
1109void QAbstractButton::timerEvent(QTimerEvent *e)
1110{
1111 Q_D(QAbstractButton);
1112 if (e->timerId() == d->repeatTimer.timerId()) {
1113 d->repeatTimer.start(d->autoRepeatInterval, this);
1114 if (d->down) {
1115 QPointer<QAbstractButton> guard(this);
1116 nextCheckState();
1117 if (guard)
1118 d->emitReleased();
1119 if (guard)
1120 d->emitClicked();
1121 if (guard)
1122 d->emitPressed();
1123 }
1124 } else if (e->timerId() == d->animateTimer.timerId()) {
1125 d->animateTimer.stop();
1126 d->click();
1127 }
1128}
1129
1130/*! \reimp */
1131void QAbstractButton::focusInEvent(QFocusEvent *e)
1132{
1133 Q_D(QAbstractButton);
1134#ifdef QT_KEYPAD_NAVIGATION
1135 if (!QApplicationPrivate::keypadNavigationEnabled())
1136#endif
1137 d->fixFocusPolicy();
1138 QWidget::focusInEvent(e);
1139}
1140
1141/*! \reimp */
1142void QAbstractButton::focusOutEvent(QFocusEvent *e)
1143{
1144 Q_D(QAbstractButton);
1145 if (e->reason() != Qt::PopupFocusReason && d->down) {
1146 d->down = false;
1147 d->emitReleased();
1148 }
1149 QWidget::focusOutEvent(e);
1150}
1151
1152/*! \reimp */
1153void QAbstractButton::changeEvent(QEvent *e)
1154{
1155 Q_D(QAbstractButton);
1156 switch (e->type()) {
1157 case QEvent::EnabledChange:
1158 if (!isEnabled() && d->down) {
1159 d->down = false;
1160 d->emitReleased();
1161 }
1162 break;
1163 default:
1164 d->sizeHint = QSize();
1165 break;
1166 }
1167 QWidget::changeEvent(e);
1168}
1169
1170/*!
1171 \fn void QAbstractButton::paintEvent(QPaintEvent *e)
1172 \reimp
1173*/
1174
1175/*!
1176 \fn void QAbstractButton::pressed()
1177
1178 This signal is emitted when the button is pressed down.
1179
1180 \sa released(), clicked()
1181*/
1182
1183/*!
1184 \fn void QAbstractButton::released()
1185
1186 This signal is emitted when the button is released.
1187
1188 \sa pressed(), clicked(), toggled()
1189*/
1190
1191/*!
1192\fn void QAbstractButton::clicked(bool checked)
1193
1194This signal is emitted when the button is activated (i.e., pressed down
1195then released while the mouse cursor is inside the button), when the
1196shortcut key is typed, or when click() or animateClick() is called.
1197Notably, this signal is \e not emitted if you call setDown(),
1198setChecked() or toggle().
1199
1200If the button is checkable, \a checked is true if the button is
1201checked, or false if the button is unchecked.
1202
1203\sa pressed(), released(), toggled()
1204*/
1205
1206/*!
1207\fn void QAbstractButton::toggled(bool checked)
1208
1209This signal is emitted whenever a checkable button changes its state.
1210\a checked is true if the button is checked, or false if the button is
1211unchecked.
1212
1213This may be the result of a user action, click() slot activation,
1214or because setChecked() is called.
1215
1216The states of buttons in exclusive button groups are updated before this
1217signal is emitted. This means that slots can act on either the "off"
1218signal or the "on" signal emitted by the buttons in the group whose
1219states have changed.
1220
1221For example, a slot that reacts to signals emitted by newly checked
1222buttons but which ignores signals from buttons that have been unchecked
1223can be implemented using the following pattern:
1224
1225\snippet code/src_gui_widgets_qabstractbutton.cpp 2
1226
1227Button groups can be created using the QButtonGroup class, and
1228updates to the button states monitored with the
1229\l{QButtonGroup::buttonClicked()} signal.
1230
1231\sa checked, clicked()
1232*/
1233
1234/*!
1235 \property QAbstractButton::iconSize
1236 \brief the icon size used for this button.
1237
1238 The default size is defined by the GUI style. This is a maximum
1239 size for the icons. Smaller icons will not be scaled up.
1240*/
1241
1242QSize QAbstractButton::iconSize() const
1243{
1244 Q_D(const QAbstractButton);
1245 if (d->iconSize.isValid())
1246 return d->iconSize;
1247 QStyleOption opt;
1248 opt.initFrom(this);
1249 opt.state = d->styleButtonState(opt.state);
1250 int e = style()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, this);
1251 return QSize(e, e);
1252}
1253
1254void QAbstractButton::setIconSize(const QSize &size)
1255{
1256 Q_D(QAbstractButton);
1257 if (d->iconSize == size)
1258 return;
1259
1260 d->iconSize = size;
1261 d->sizeHint = QSize();
1262 updateGeometry();
1263 if (isVisible()) {
1264 update();
1265 }
1266}
1267
1268
1269
1270QT_END_NAMESPACE
1271
1272#include "moc_qabstractbutton.cpp"
QList< QAbstractButton * > queryButtonList() const
QAbstractButton * queryCheckedButton() const
virtual QStyle::State styleButtonState(QStyle::State state) const
void emitToggled(bool checked)
friend class QWidget
Definition qpainter.h:431
#define AUTO_REPEAT_DELAY
#define AUTO_REPEAT_INTERVAL
Q_WIDGETS_EXPORT bool qt_tab_all_widgets()