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

source code of qtbase/src/widgets/widgets/qabstractbutton.cpp