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