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