1// Copyright (C) 2016 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
4#include "qcheckbox.h"
5#include "qapplication.h"
6#include "qbitmap.h"
7#include "qicon.h"
8#include "qstylepainter.h"
9#include "qstyle.h"
10#include "qstyleoption.h"
11#include "qevent.h"
12#if QT_CONFIG(accessibility)
13#include "qaccessible.h"
14#endif
15
16#include "private/qabstractbutton_p.h"
17
18QT_BEGIN_NAMESPACE
19
20class QCheckBoxPrivate : public QAbstractButtonPrivate
21{
22 Q_DECLARE_PUBLIC(QCheckBox)
23public:
24 QCheckBoxPrivate()
25 : QAbstractButtonPrivate(QSizePolicy::CheckBox), tristate(false), noChange(false),
26 hovering(true), publishedState(Qt::Unchecked) {}
27
28 uint tristate : 1;
29 uint noChange : 1;
30 uint hovering : 1;
31 Qt::CheckState publishedState : 3;
32
33 void init();
34};
35
36/*!
37 \class QCheckBox
38 \brief The QCheckBox widget provides a checkbox with a text label.
39
40 \ingroup basicwidgets
41 \inmodule QtWidgets
42
43 \image fusion-checkbox.png
44
45 A QCheckBox is an option button that can be switched on (checked) or off
46 (unchecked). Checkboxes are typically used to represent features in an
47 application that can be enabled or disabled without affecting others.
48 Different types of behavior can be implemented. For example, a
49 QButtonGroup can be used to group check buttons logically, allowing
50 exclusive checkboxes. However, QButtonGroup does not provide any visual
51 representation.
52
53 The image below further illustrates the differences between exclusive and
54 non-exclusive checkboxes.
55
56 \table
57 \row \li \inlineimage checkboxes-exclusive.png
58 \li \inlineimage checkboxes-non-exclusive.png
59 \endtable
60
61 Whenever a checkbox is checked or cleared, it emits the signal
62 checkStateChanged(). Connect to this signal if you want to trigger an action
63 each time the checkbox changes state. You can use isChecked() to query
64 whether or not a checkbox is checked.
65
66 In addition to the usual checked and unchecked states, QCheckBox optionally
67 provides a third state to indicate "no change". This is useful whenever you
68 need to give the user the option of neither checking nor unchecking a
69 checkbox. If you need this third state, enable it with setTristate(), and
70 use checkState() to query the current toggle state.
71
72 Just like QPushButton, a checkbox displays text, and optionally a small
73 icon. The icon is set with setIcon(). The text can be set in the
74 constructor or with setText(). A shortcut key can be specified by preceding
75 the preferred character with an ampersand. For example:
76
77 \snippet code/src_gui_widgets_qcheckbox.cpp 0
78
79 In this example, the shortcut is \e{Alt+A}. See the \l{QShortcut#mnemonic}
80 {QShortcut} documentation for details. To display an actual ampersand,
81 use '&&'.
82
83 \sa QAbstractButton, QRadioButton
84*/
85
86/*!
87 \fn void QCheckBox::stateChanged(int state)
88
89 \deprecated [6.9] Use checkStateChanged(Qt::CheckState) instead.
90
91 This signal is emitted whenever the checkbox's state changes, i.e.,
92 whenever the user checks or unchecks it.
93
94 \a state contains the checkbox's new Qt::CheckState.
95*/
96
97/*!
98 \fn void QCheckBox::checkStateChanged(Qt::CheckState state)
99 \since 6.7
100
101 This signal is emitted whenever the checkbox's state changes, i.e.,
102 whenever the user checks or unchecks it.
103
104 \a state contains the checkbox's new Qt::CheckState.
105*/
106
107/*!
108 \property QCheckBox::tristate
109 \brief whether the checkbox is a tri-state checkbox
110
111 The default is false, i.e., the checkbox has only two states.
112*/
113
114void QCheckBoxPrivate::init()
115{
116 Q_Q(QCheckBox);
117 q->setCheckable(true);
118 q->setMouseTracking(true);
119 q->setForegroundRole(QPalette::WindowText);
120 q->setAttribute(Qt::WA_MacShowFocusRect);
121 setLayoutItemMargins(element: QStyle::SE_CheckBoxLayoutItem);
122}
123
124/*!
125 Initializes \a option with the values from this QCheckBox. This method is
126 useful for subclasses that require a QStyleOptionButton, but do not want
127 to fill in all the information themselves.
128
129 \sa QStyleOption::initFrom()
130*/
131void QCheckBox::initStyleOption(QStyleOptionButton *option) const
132{
133 if (!option)
134 return;
135 Q_D(const QCheckBox);
136 option->initFrom(w: this);
137 if (d->down)
138 option->state |= QStyle::State_Sunken;
139 if (d->tristate && d->noChange)
140 option->state |= QStyle::State_NoChange;
141 else
142 option->state |= d->checked ? QStyle::State_On : QStyle::State_Off;
143 if (testAttribute(attribute: Qt::WA_Hover) && underMouse()) {
144 option->state.setFlag(flag: QStyle::State_MouseOver, on: d->hovering);
145 }
146 option->text = d->text;
147 option->icon = d->icon;
148 option->iconSize = iconSize();
149}
150
151/*!
152 Constructs a checkbox with the given \a parent, but with no text.
153
154 \a parent is passed on to the QAbstractButton constructor.
155*/
156
157QCheckBox::QCheckBox(QWidget *parent)
158 : QAbstractButton (*new QCheckBoxPrivate, parent)
159{
160 Q_D(QCheckBox);
161 d->init();
162}
163
164/*!
165 Constructs a checkbox with the given \a parent and \a text.
166
167 \a parent is passed on to the QAbstractButton constructor.
168*/
169
170QCheckBox::QCheckBox(const QString &text, QWidget *parent)
171 : QCheckBox(parent)
172{
173 setText(text);
174}
175
176/*!
177 Destructor.
178*/
179QCheckBox::~QCheckBox()
180{
181}
182
183void QCheckBox::setTristate(bool y)
184{
185 Q_D(QCheckBox);
186 d->tristate = y;
187}
188
189bool QCheckBox::isTristate() const
190{
191 Q_D(const QCheckBox);
192 return d->tristate;
193}
194
195
196/*!
197 Returns the checkbox's check state. If you do not need tristate support,
198 you can also use \l QAbstractButton::isChecked(), which returns a boolean.
199
200 \sa setCheckState(), Qt::CheckState
201*/
202Qt::CheckState QCheckBox::checkState() const
203{
204 Q_D(const QCheckBox);
205 if (d->tristate && d->noChange)
206 return Qt::PartiallyChecked;
207 return d->checked ? Qt::Checked : Qt::Unchecked;
208}
209
210/*!
211 Sets the checkbox's check state to \a state. If you do not need tristate
212 support, you can also use \l QAbstractButton::setChecked(), which takes a
213 boolean.
214
215 \sa checkState(), Qt::CheckState
216*/
217void QCheckBox::setCheckState(Qt::CheckState state)
218{
219 Q_D(QCheckBox);
220#if QT_CONFIG(accessibility)
221 bool noChange = d->noChange;
222#endif
223 if (state == Qt::PartiallyChecked) {
224 d->tristate = true;
225 d->noChange = true;
226 } else {
227 d->noChange = false;
228 }
229 d->blockRefresh = true;
230 setChecked(state != Qt::Unchecked);
231 d->blockRefresh = false;
232 d->refresh();
233 if (state != d->publishedState) {
234 d->publishedState = state;
235 emit checkStateChanged(state);
236#if QT_DEPRECATED_SINCE(6, 9)
237 QT_IGNORE_DEPRECATIONS(
238 emit stateChanged(state);
239 )
240#endif
241 }
242
243#if QT_CONFIG(accessibility)
244 if (noChange != d->noChange) {
245 QAccessible::State s;
246 s.checkStateMixed = true;
247 QAccessibleStateChangeEvent event(this, s);
248 QAccessible::updateAccessibility(event: &event);
249 }
250#endif
251}
252
253
254/*!
255 \reimp
256*/
257QSize QCheckBox::sizeHint() const
258{
259 Q_D(const QCheckBox);
260 if (d->sizeHint.isValid())
261 return d->sizeHint;
262 ensurePolished();
263 QFontMetrics fm = fontMetrics();
264 QStyleOptionButton opt;
265 initStyleOption(option: &opt);
266 QSize sz = style()->itemTextRect(fm, r: QRect(), flags: Qt::TextShowMnemonic, enabled: false,
267 text: text()).size();
268 if (!opt.icon.isNull())
269 sz = QSize(sz.width() + opt.iconSize.width() + 4, qMax(a: sz.height(), b: opt.iconSize.height()));
270 d->sizeHint = style()->sizeFromContents(ct: QStyle::CT_CheckBox, opt: &opt, contentsSize: sz, w: this);
271 return d->sizeHint;
272}
273
274
275/*!
276 \reimp
277*/
278QSize QCheckBox::minimumSizeHint() const
279{
280 return sizeHint();
281}
282
283/*!
284 \reimp
285*/
286void QCheckBox::paintEvent(QPaintEvent *)
287{
288 QStylePainter p(this);
289 QStyleOptionButton opt;
290 initStyleOption(option: &opt);
291 p.drawControl(ce: QStyle::CE_CheckBox, opt);
292}
293
294/*!
295 \reimp
296*/
297void QCheckBox::mouseMoveEvent(QMouseEvent *e)
298{
299 Q_D(QCheckBox);
300 if (testAttribute(attribute: Qt::WA_Hover)) {
301 bool hit = false;
302 if (underMouse())
303 hit = hitButton(pos: e->position().toPoint());
304
305 if (hit != d->hovering) {
306 update(rect());
307 d->hovering = hit;
308 }
309 }
310
311 QAbstractButton::mouseMoveEvent(e);
312}
313
314
315/*!
316 \reimp
317*/
318bool QCheckBox::hitButton(const QPoint &pos) const
319{
320 QStyleOptionButton opt;
321 initStyleOption(option: &opt);
322 return style()->subElementRect(subElement: QStyle::SE_CheckBoxClickRect, option: &opt, widget: this).contains(p: pos);
323}
324
325/*!
326 \reimp
327*/
328void QCheckBox::checkStateSet()
329{
330 Q_D(QCheckBox);
331 d->noChange = false;
332 Qt::CheckState state = checkState();
333 if (state != d->publishedState) {
334 d->publishedState = state;
335 emit checkStateChanged(state);
336#if QT_DEPRECATED_SINCE(6, 9)
337 QT_IGNORE_DEPRECATIONS(
338 emit stateChanged(state);
339 )
340#endif
341 }
342}
343
344/*!
345 \reimp
346*/
347void QCheckBox::nextCheckState()
348{
349 Q_D(QCheckBox);
350 if (d->tristate)
351 setCheckState((Qt::CheckState)((checkState() + 1) % 3));
352 else {
353 QAbstractButton::nextCheckState();
354 QCheckBox::checkStateSet();
355 }
356}
357
358/*!
359 \reimp
360*/
361bool QCheckBox::event(QEvent *e)
362{
363 Q_D(QCheckBox);
364 if (e->type() == QEvent::StyleChange
365#ifdef Q_OS_MAC
366 || e->type() == QEvent::MacSizeChange
367#endif
368 )
369 d->setLayoutItemMargins(element: QStyle::SE_CheckBoxLayoutItem);
370 return QAbstractButton::event(e);
371}
372
373
374
375QT_END_NAMESPACE
376
377#include "moc_qcheckbox.cpp"
378

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