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 "qgroupbox.h" |
5 | |
6 | #include "qapplication.h" |
7 | #include "qbitmap.h" |
8 | #include "qdrawutil.h" |
9 | #include "qevent.h" |
10 | #include "qlayout.h" |
11 | #if QT_CONFIG(radiobutton) |
12 | #include "qradiobutton.h" |
13 | #endif |
14 | #include "qstyle.h" |
15 | #include "qstyleoption.h" |
16 | #include "qstylepainter.h" |
17 | #if QT_CONFIG(accessibility) |
18 | #include "qaccessible.h" |
19 | #endif |
20 | #include <private/qwidget_p.h> |
21 | #include <private/qguiapplication_p.h> |
22 | #include <qpa/qplatformtheme.h> |
23 | |
24 | #include "qdebug.h" |
25 | |
26 | QT_BEGIN_NAMESPACE |
27 | |
28 | class QGroupBoxPrivate : public QWidgetPrivate |
29 | { |
30 | Q_DECLARE_PUBLIC(QGroupBox) |
31 | |
32 | public: |
33 | void skip(); |
34 | void init(); |
35 | void calculateFrame(); |
36 | QString title; |
37 | int align; |
38 | #ifndef QT_NO_SHORTCUT |
39 | int shortcutId; |
40 | #endif |
41 | |
42 | void _q_fixFocus(Qt::FocusReason reason); |
43 | void _q_setChildrenEnabled(bool b); |
44 | void click(); |
45 | bool flat; |
46 | bool checkable; |
47 | bool checked; |
48 | bool hover; |
49 | bool overCheckBox; |
50 | QStyle::SubControl pressedControl; |
51 | }; |
52 | |
53 | /*! |
54 | Initialize \a option with the values from this QGroupBox. This method |
55 | is useful for subclasses when they need a QStyleOptionGroupBox, but don't want |
56 | to fill in all the information themselves. |
57 | |
58 | \sa QStyleOption::initFrom() |
59 | */ |
60 | void QGroupBox::initStyleOption(QStyleOptionGroupBox *option) const |
61 | { |
62 | if (!option) |
63 | return; |
64 | |
65 | Q_D(const QGroupBox); |
66 | option->initFrom(w: this); |
67 | option->text = d->title; |
68 | option->lineWidth = 1; |
69 | option->midLineWidth = 0; |
70 | option->textAlignment = Qt::Alignment(d->align); |
71 | option->activeSubControls |= d->pressedControl; |
72 | option->subControls = QStyle::SC_GroupBoxFrame; |
73 | |
74 | option->state.setFlag(flag: QStyle::State_MouseOver, on: d->hover); |
75 | if (d->flat) |
76 | option->features |= QStyleOptionFrame::Flat; |
77 | |
78 | if (d->checkable) { |
79 | option->subControls |= QStyle::SC_GroupBoxCheckBox; |
80 | option->state |= (d->checked ? QStyle::State_On : QStyle::State_Off); |
81 | if ((d->pressedControl == QStyle::SC_GroupBoxCheckBox |
82 | || d->pressedControl == QStyle::SC_GroupBoxLabel) && (d->hover || d->overCheckBox)) |
83 | option->state |= QStyle::State_Sunken; |
84 | } |
85 | |
86 | if (!option->palette.isBrushSet(cg: isEnabled() ? QPalette::Active : |
87 | QPalette::Disabled, cr: QPalette::WindowText)) |
88 | option->textColor = QColor(style()->styleHint(stylehint: QStyle::SH_GroupBox_TextLabelColor, |
89 | opt: option, widget: this)); |
90 | |
91 | if (!d->title.isEmpty()) |
92 | option->subControls |= QStyle::SC_GroupBoxLabel; |
93 | } |
94 | |
95 | void QGroupBoxPrivate::click() |
96 | { |
97 | Q_Q(QGroupBox); |
98 | |
99 | QPointer<QGroupBox> guard(q); |
100 | q->setChecked(!checked); |
101 | if (!guard) |
102 | return; |
103 | emit q->clicked(checked); |
104 | } |
105 | |
106 | /*! |
107 | \class QGroupBox |
108 | \brief The QGroupBox widget provides a group box frame with a title. |
109 | |
110 | \ingroup organizers |
111 | \ingroup geomanagement |
112 | \inmodule QtWidgets |
113 | |
114 | \image windows-groupbox.png |
115 | |
116 | A group box provides a frame, a title on top, a keyboard shortcut, and |
117 | displays various other widgets inside itself. The keyboard shortcut moves |
118 | keyboard focus to one of the group box's child widgets. |
119 | |
120 | QGroupBox also lets you set the \l title (normally set in the |
121 | constructor) and the title's \l alignment. Group boxes can be |
122 | \l checkable. Child widgets in checkable group boxes are enabled or |
123 | disabled depending on whether or not the group box is \l checked. |
124 | |
125 | You can minimize the space consumption of a group box by enabling |
126 | the \l flat property. In most \l{QStyle}{styles}, enabling this |
127 | property results in the removal of the left, right and bottom |
128 | edges of the frame. |
129 | |
130 | QGroupBox doesn't automatically lay out the child widgets (which |
131 | are often \l{QCheckBox}es or \l{QRadioButton}s but can be any |
132 | widgets). The following example shows how we can set up a |
133 | QGroupBox with a layout: |
134 | |
135 | \snippet widgets/groupbox/window.cpp 2 |
136 | |
137 | \sa QButtonGroup, {Group Box Example} |
138 | */ |
139 | |
140 | |
141 | |
142 | /*! |
143 | Constructs a group box widget with the given \a parent but with no title. |
144 | */ |
145 | |
146 | QGroupBox::QGroupBox(QWidget *parent) |
147 | : QWidget(*new QGroupBoxPrivate, parent, { }) |
148 | { |
149 | Q_D(QGroupBox); |
150 | d->init(); |
151 | } |
152 | |
153 | /*! |
154 | Constructs a group box with the given \a title and \a parent. |
155 | */ |
156 | |
157 | QGroupBox::QGroupBox(const QString &title, QWidget *parent) |
158 | : QGroupBox(parent) |
159 | { |
160 | setTitle(title); |
161 | } |
162 | |
163 | |
164 | /*! |
165 | Destroys the group box. |
166 | */ |
167 | QGroupBox::~QGroupBox() |
168 | { |
169 | } |
170 | |
171 | void QGroupBoxPrivate::init() |
172 | { |
173 | Q_Q(QGroupBox); |
174 | align = Qt::AlignLeft; |
175 | #ifndef QT_NO_SHORTCUT |
176 | shortcutId = 0; |
177 | #endif |
178 | flat = false; |
179 | checkable = false; |
180 | checked = true; |
181 | hover = false; |
182 | overCheckBox = false; |
183 | pressedControl = QStyle::SC_None; |
184 | calculateFrame(); |
185 | q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, |
186 | QSizePolicy::GroupBox)); |
187 | } |
188 | |
189 | void QGroupBox::setTitle(const QString &title) |
190 | { |
191 | Q_D(QGroupBox); |
192 | if (d->title == title) // no change |
193 | return; |
194 | d->title = title; |
195 | #ifndef QT_NO_SHORTCUT |
196 | releaseShortcut(id: d->shortcutId); |
197 | d->shortcutId = grabShortcut(key: QKeySequence::mnemonic(text: title)); |
198 | #endif |
199 | d->calculateFrame(); |
200 | |
201 | update(); |
202 | updateGeometry(); |
203 | #if QT_CONFIG(accessibility) |
204 | QAccessibleEvent event(this, QAccessible::NameChanged); |
205 | QAccessible::updateAccessibility(event: &event); |
206 | #endif |
207 | } |
208 | |
209 | /*! |
210 | \property QGroupBox::title |
211 | \brief the group box title text |
212 | |
213 | The group box title text will have a keyboard shortcut if the title |
214 | contains an ampersand ('&') followed by a letter. |
215 | |
216 | \snippet code/src_gui_widgets_qgroupbox.cpp 0 |
217 | |
218 | In the example above, \uicontrol Alt+U moves the keyboard focus to the |
219 | group box. See the \l {QShortcut#mnemonic}{QShortcut} |
220 | documentation for details (to display an actual ampersand, use |
221 | '&&'). |
222 | |
223 | There is no default title text. |
224 | |
225 | \sa alignment |
226 | */ |
227 | |
228 | QString QGroupBox::title() const |
229 | { |
230 | Q_D(const QGroupBox); |
231 | return d->title; |
232 | } |
233 | |
234 | /*! |
235 | \property QGroupBox::alignment |
236 | \brief the alignment of the group box title. |
237 | |
238 | Most styles place the title at the top of the frame. The horizontal |
239 | alignment of the title can be specified using single values from |
240 | the following list: |
241 | |
242 | \list |
243 | \li Qt::AlignLeft aligns the title text with the left-hand side of the group box. |
244 | \li Qt::AlignRight aligns the title text with the right-hand side of the group box. |
245 | \li Qt::AlignHCenter aligns the title text with the horizontal center of the group box. |
246 | \endlist |
247 | |
248 | The default alignment is Qt::AlignLeft. |
249 | |
250 | \sa Qt::Alignment |
251 | */ |
252 | Qt::Alignment QGroupBox::alignment() const |
253 | { |
254 | Q_D(const QGroupBox); |
255 | return QFlag(d->align); |
256 | } |
257 | |
258 | void QGroupBox::setAlignment(int alignment) |
259 | { |
260 | Q_D(QGroupBox); |
261 | d->align = alignment; |
262 | updateGeometry(); |
263 | update(); |
264 | } |
265 | |
266 | /*! \reimp |
267 | */ |
268 | void QGroupBox::resizeEvent(QResizeEvent *e) |
269 | { |
270 | QWidget::resizeEvent(event: e); |
271 | } |
272 | |
273 | /*! \reimp |
274 | */ |
275 | |
276 | void QGroupBox::paintEvent(QPaintEvent *) |
277 | { |
278 | QStylePainter paint(this); |
279 | QStyleOptionGroupBox option; |
280 | initStyleOption(option: &option); |
281 | paint.drawComplexControl(cc: QStyle::CC_GroupBox, opt: option); |
282 | } |
283 | |
284 | /*! \reimp */ |
285 | bool QGroupBox::event(QEvent *e) |
286 | { |
287 | Q_D(QGroupBox); |
288 | #ifndef QT_NO_SHORTCUT |
289 | if (e->type() == QEvent::Shortcut) { |
290 | QShortcutEvent *se = static_cast<QShortcutEvent *>(e); |
291 | if (se->shortcutId() == d->shortcutId) { |
292 | if (!isCheckable()) { |
293 | d->_q_fixFocus(reason: Qt::ShortcutFocusReason); |
294 | } else { |
295 | d->click(); |
296 | setFocus(Qt::ShortcutFocusReason); |
297 | } |
298 | return true; |
299 | } |
300 | } |
301 | #endif |
302 | QStyleOptionGroupBox box; |
303 | initStyleOption(option: &box); |
304 | switch (e->type()) { |
305 | case QEvent::HoverEnter: |
306 | case QEvent::HoverMove: { |
307 | QStyle::SubControl control = style()->hitTestComplexControl(cc: QStyle::CC_GroupBox, opt: &box, |
308 | pt: static_cast<QHoverEvent *>(e)->position().toPoint(), |
309 | widget: this); |
310 | bool oldHover = d->hover; |
311 | d->hover = d->checkable && (control == QStyle::SC_GroupBoxLabel || control == QStyle::SC_GroupBoxCheckBox); |
312 | if (oldHover != d->hover) { |
313 | QRect rect = style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxCheckBox, widget: this) |
314 | | style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxLabel, widget: this); |
315 | update(rect); |
316 | } |
317 | return true; |
318 | } |
319 | case QEvent::HoverLeave: |
320 | d->hover = false; |
321 | if (d->checkable) { |
322 | QRect rect = style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxCheckBox, widget: this) |
323 | | style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxLabel, widget: this); |
324 | update(rect); |
325 | } |
326 | return true; |
327 | case QEvent::KeyPress: { |
328 | QKeyEvent *k = static_cast<QKeyEvent*>(e); |
329 | const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() |
330 | ->themeHint(hint: QPlatformTheme::ButtonPressKeys) |
331 | .value<QList<Qt::Key>>(); |
332 | if (!k->isAutoRepeat() && buttonPressKeys.contains(t: k->key())) { |
333 | d->pressedControl = QStyle::SC_GroupBoxCheckBox; |
334 | update(style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxCheckBox, widget: this)); |
335 | return true; |
336 | } |
337 | break; |
338 | } |
339 | case QEvent::KeyRelease: { |
340 | QKeyEvent *k = static_cast<QKeyEvent*>(e); |
341 | const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() |
342 | ->themeHint(hint: QPlatformTheme::ButtonPressKeys) |
343 | .value<QList<Qt::Key>>(); |
344 | if (!k->isAutoRepeat() && buttonPressKeys.contains(t: k->key())) { |
345 | bool toggle = (d->pressedControl == QStyle::SC_GroupBoxLabel |
346 | || d->pressedControl == QStyle::SC_GroupBoxCheckBox); |
347 | d->pressedControl = QStyle::SC_None; |
348 | if (toggle) |
349 | d->click(); |
350 | return true; |
351 | } |
352 | break; |
353 | } |
354 | default: |
355 | break; |
356 | } |
357 | return QWidget::event(event: e); |
358 | } |
359 | |
360 | /*!\reimp */ |
361 | void QGroupBox::childEvent(QChildEvent *c) |
362 | { |
363 | Q_D(QGroupBox); |
364 | /* |
365 | Children might have been enabled after being added to the group box, in which case |
366 | the childEvent handler ran too early, and we need to disabled children again. |
367 | */ |
368 | if (!(c->added() || c->polished()) || !c->child()->isWidgetType()) |
369 | return; |
370 | QWidget *w = static_cast<QWidget*>(c->child()); |
371 | if (w->isWindow()) |
372 | return; |
373 | if (d->checkable) { |
374 | if (d->checked) { |
375 | if (!w->testAttribute(attribute: Qt::WA_ForceDisabled)) |
376 | w->setEnabled(true); |
377 | } else { |
378 | if (w->isEnabled()) { |
379 | w->setEnabled(false); |
380 | w->setAttribute(Qt::WA_ForceDisabled, on: false); |
381 | } |
382 | } |
383 | } |
384 | } |
385 | |
386 | |
387 | /*! |
388 | \internal |
389 | |
390 | This private slot finds a widget in this group box that can accept |
391 | focus, and gives the focus to that widget. |
392 | */ |
393 | |
394 | void QGroupBoxPrivate::_q_fixFocus(Qt::FocusReason reason) |
395 | { |
396 | Q_Q(QGroupBox); |
397 | QWidget *fw = q->focusWidget(); |
398 | if (!fw || fw == q) { |
399 | QWidget * best = nullptr; |
400 | QWidget * candidate = nullptr; |
401 | QWidget * w = q; |
402 | while ((w = w->nextInFocusChain()) != q) { |
403 | if (q->isAncestorOf(child: w) && (w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus && w->isVisibleTo(q)) { |
404 | #if QT_CONFIG(radiobutton) |
405 | if (!best && qobject_cast<QRadioButton*>(object: w) && ((QRadioButton*)w)->isChecked()) |
406 | // we prefer a checked radio button or a widget that |
407 | // already has focus, if there is one |
408 | best = w; |
409 | else |
410 | #endif |
411 | if (!candidate) |
412 | // but we'll accept anything that takes focus |
413 | candidate = w; |
414 | } |
415 | } |
416 | if (best) |
417 | fw = best; |
418 | else if (candidate) |
419 | fw = candidate; |
420 | } |
421 | if (fw) |
422 | fw->setFocus(reason); |
423 | } |
424 | |
425 | /* |
426 | Sets the right frame rect depending on the title. |
427 | */ |
428 | void QGroupBoxPrivate::calculateFrame() |
429 | { |
430 | Q_Q(QGroupBox); |
431 | QStyleOptionGroupBox box; |
432 | q->initStyleOption(option: &box); |
433 | QRect contentsRect = q->style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxContents, widget: q); |
434 | q->setContentsMargins(left: contentsRect.left() - box.rect.left(), top: contentsRect.top() - box.rect.top(), |
435 | right: box.rect.right() - contentsRect.right(), bottom: box.rect.bottom() - contentsRect.bottom()); |
436 | setLayoutItemMargins(element: QStyle::SE_GroupBoxLayoutItem, opt: &box); |
437 | } |
438 | |
439 | /*! \reimp |
440 | */ |
441 | void QGroupBox::focusInEvent(QFocusEvent *fe) |
442 | { // note no call to super |
443 | Q_D(QGroupBox); |
444 | if (focusPolicy() == Qt::NoFocus) { |
445 | d->_q_fixFocus(reason: fe->reason()); |
446 | } else { |
447 | QWidget::focusInEvent(event: fe); |
448 | } |
449 | } |
450 | |
451 | |
452 | /*! |
453 | \reimp |
454 | */ |
455 | QSize QGroupBox::minimumSizeHint() const |
456 | { |
457 | Q_D(const QGroupBox); |
458 | QStyleOptionGroupBox option; |
459 | initStyleOption(option: &option); |
460 | |
461 | QFontMetrics metrics(fontMetrics()); |
462 | |
463 | int baseWidth = metrics.horizontalAdvance(d->title) + metrics.horizontalAdvance(u' '); |
464 | int baseHeight = metrics.height(); |
465 | if (d->checkable) { |
466 | baseWidth += style()->pixelMetric(metric: QStyle::PM_IndicatorWidth, option: &option); |
467 | baseWidth += style()->pixelMetric(metric: QStyle::PM_CheckBoxLabelSpacing, option: &option); |
468 | baseHeight = qMax(a: baseHeight, b: style()->pixelMetric(metric: QStyle::PM_IndicatorHeight, option: &option)); |
469 | } |
470 | |
471 | QSize size = style()->sizeFromContents(ct: QStyle::CT_GroupBox, opt: &option, contentsSize: QSize(baseWidth, baseHeight), w: this); |
472 | return size.expandedTo(otherSize: QWidget::minimumSizeHint()); |
473 | } |
474 | |
475 | /*! |
476 | \property QGroupBox::flat |
477 | \brief whether the group box is painted flat or has a frame |
478 | |
479 | A group box usually consists of a surrounding frame with a title |
480 | at the top. If this property is enabled, only the top part of the frame is |
481 | drawn in most styles; otherwise, the whole frame is drawn. |
482 | |
483 | By default, this property is disabled, i.e., group boxes are not flat unless |
484 | explicitly specified. |
485 | |
486 | \b{Note:} In some styles, flat and non-flat group boxes have similar |
487 | representations and may not be as distinguishable as they are in other |
488 | styles. |
489 | |
490 | \sa title |
491 | */ |
492 | bool QGroupBox::isFlat() const |
493 | { |
494 | Q_D(const QGroupBox); |
495 | return d->flat; |
496 | } |
497 | |
498 | void QGroupBox::setFlat(bool b) |
499 | { |
500 | Q_D(QGroupBox); |
501 | if (d->flat == b) |
502 | return; |
503 | d->flat = b; |
504 | updateGeometry(); |
505 | update(); |
506 | } |
507 | |
508 | |
509 | /*! |
510 | \property QGroupBox::checkable |
511 | \brief whether the group box has a checkbox in its title |
512 | |
513 | If this property is \c true, the group box displays its title using |
514 | a checkbox in place of an ordinary label. If the checkbox is checked, |
515 | the group box's children are enabled; otherwise, they are disabled and |
516 | inaccessible. |
517 | |
518 | By default, group boxes are not checkable. |
519 | |
520 | If this property is enabled for a group box, it will also be initially |
521 | checked to ensure that its contents are enabled. |
522 | |
523 | \sa checked |
524 | */ |
525 | void QGroupBox::setCheckable(bool checkable) |
526 | { |
527 | Q_D(QGroupBox); |
528 | |
529 | bool wasCheckable = d->checkable; |
530 | d->checkable = checkable; |
531 | |
532 | if (checkable) { |
533 | setChecked(true); |
534 | if (!wasCheckable) { |
535 | setFocusPolicy(Qt::StrongFocus); |
536 | d->_q_setChildrenEnabled(b: true); |
537 | updateGeometry(); |
538 | } |
539 | } else { |
540 | if (wasCheckable) { |
541 | setFocusPolicy(Qt::NoFocus); |
542 | d->_q_setChildrenEnabled(b: true); |
543 | updateGeometry(); |
544 | } |
545 | d->_q_setChildrenEnabled(b: true); |
546 | } |
547 | |
548 | if (wasCheckable != checkable) { |
549 | d->calculateFrame(); |
550 | update(); |
551 | } |
552 | } |
553 | |
554 | bool QGroupBox::isCheckable() const |
555 | { |
556 | Q_D(const QGroupBox); |
557 | return d->checkable; |
558 | } |
559 | |
560 | |
561 | bool QGroupBox::isChecked() const |
562 | { |
563 | Q_D(const QGroupBox); |
564 | return d->checkable && d->checked; |
565 | } |
566 | |
567 | |
568 | /*! |
569 | \fn void QGroupBox::toggled(bool on) |
570 | |
571 | If the group box is checkable, this signal is emitted when the check box |
572 | is toggled. \a on is true if the check box is checked; otherwise, it is false. |
573 | |
574 | \sa checkable |
575 | */ |
576 | |
577 | |
578 | /*! |
579 | \fn void QGroupBox::clicked(bool checked) |
580 | \since 4.2 |
581 | |
582 | This signal is emitted when the check box is activated (i.e., pressed down |
583 | then released while the mouse cursor is inside the button), or when the |
584 | shortcut key is typed. Notably, this signal is \e not emitted if you call |
585 | setChecked(). |
586 | |
587 | If the check box is checked, \a checked is true; it is false if the check |
588 | box is unchecked. |
589 | |
590 | \sa checkable, toggled(), checked |
591 | */ |
592 | |
593 | /*! |
594 | \property QGroupBox::checked |
595 | \brief whether the group box is checked |
596 | |
597 | If the group box is checkable, it is displayed with a check box. |
598 | If the check box is checked, the group box's children are enabled; |
599 | otherwise, the children are disabled and are inaccessible to the user. |
600 | |
601 | By default, checkable group boxes are also checked. |
602 | |
603 | \sa checkable |
604 | */ |
605 | void QGroupBox::setChecked(bool b) |
606 | { |
607 | Q_D(QGroupBox); |
608 | if (d->checkable && b != d->checked) { |
609 | update(); |
610 | d->checked = b; |
611 | d->_q_setChildrenEnabled(b); |
612 | #if QT_CONFIG(accessibility) |
613 | QAccessible::State st; |
614 | st.checked = true; |
615 | QAccessibleStateChangeEvent e(this, st); |
616 | QAccessible::updateAccessibility(event: &e); |
617 | #endif |
618 | emit toggled(b); |
619 | } |
620 | } |
621 | |
622 | /* |
623 | sets all children of the group box except the qt_groupbox_checkbox |
624 | to either disabled/enabled |
625 | */ |
626 | void QGroupBoxPrivate::_q_setChildrenEnabled(bool b) |
627 | { |
628 | Q_Q(QGroupBox); |
629 | for (QObject *o : q->children()) { |
630 | if (o->isWidgetType()) { |
631 | QWidget *w = static_cast<QWidget *>(o); |
632 | if (b) { |
633 | if (!w->testAttribute(attribute: Qt::WA_ForceDisabled)) |
634 | w->setEnabled(true); |
635 | } else { |
636 | if (w->isEnabled()) { |
637 | w->setEnabled(false); |
638 | w->setAttribute(Qt::WA_ForceDisabled, on: false); |
639 | } |
640 | } |
641 | } |
642 | } |
643 | } |
644 | |
645 | /*! \reimp */ |
646 | void QGroupBox::changeEvent(QEvent *ev) |
647 | { |
648 | Q_D(QGroupBox); |
649 | if (ev->type() == QEvent::EnabledChange) { |
650 | if (d->checkable && isEnabled()) { |
651 | // we are being enabled - disable children |
652 | if (!d->checked) |
653 | d->_q_setChildrenEnabled(b: false); |
654 | } |
655 | } else if (ev->type() == QEvent::FontChange |
656 | #ifdef Q_OS_MAC |
657 | || ev->type() == QEvent::MacSizeChange |
658 | #endif |
659 | || ev->type() == QEvent::StyleChange) { |
660 | d->calculateFrame(); |
661 | } |
662 | QWidget::changeEvent(ev); |
663 | } |
664 | |
665 | /*! \reimp */ |
666 | void QGroupBox::mousePressEvent(QMouseEvent *event) |
667 | { |
668 | if (event->button() != Qt::LeftButton) { |
669 | event->ignore(); |
670 | return; |
671 | } |
672 | |
673 | Q_D(QGroupBox); |
674 | QStyleOptionGroupBox box; |
675 | initStyleOption(option: &box); |
676 | d->pressedControl = style()->hitTestComplexControl(cc: QStyle::CC_GroupBox, opt: &box, |
677 | pt: event->position().toPoint(), widget: this); |
678 | if (d->checkable && (d->pressedControl & (QStyle::SC_GroupBoxCheckBox | QStyle::SC_GroupBoxLabel))) { |
679 | d->overCheckBox = true; |
680 | update(style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxCheckBox, widget: this)); |
681 | } else { |
682 | event->ignore(); |
683 | } |
684 | } |
685 | |
686 | /*! \reimp */ |
687 | void QGroupBox::mouseMoveEvent(QMouseEvent *event) |
688 | { |
689 | Q_D(QGroupBox); |
690 | QStyleOptionGroupBox box; |
691 | initStyleOption(option: &box); |
692 | QStyle::SubControl pressed = style()->hitTestComplexControl(cc: QStyle::CC_GroupBox, opt: &box, |
693 | pt: event->position().toPoint(), widget: this); |
694 | bool oldOverCheckBox = d->overCheckBox; |
695 | d->overCheckBox = (pressed == QStyle::SC_GroupBoxCheckBox || pressed == QStyle::SC_GroupBoxLabel); |
696 | if (d->checkable && (d->pressedControl == QStyle::SC_GroupBoxCheckBox || d->pressedControl == QStyle::SC_GroupBoxLabel) |
697 | && (d->overCheckBox != oldOverCheckBox)) |
698 | update(style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxCheckBox, widget: this)); |
699 | |
700 | event->ignore(); |
701 | } |
702 | |
703 | /*! \reimp */ |
704 | void QGroupBox::mouseReleaseEvent(QMouseEvent *event) |
705 | { |
706 | if (event->button() != Qt::LeftButton) { |
707 | event->ignore(); |
708 | return; |
709 | } |
710 | |
711 | Q_D(QGroupBox); |
712 | if (!d->overCheckBox) { |
713 | event->ignore(); |
714 | return; |
715 | } |
716 | QStyleOptionGroupBox box; |
717 | initStyleOption(option: &box); |
718 | QStyle::SubControl released = style()->hitTestComplexControl(cc: QStyle::CC_GroupBox, opt: &box, |
719 | pt: event->position().toPoint(), widget: this); |
720 | bool toggle = d->checkable && (released == QStyle::SC_GroupBoxLabel |
721 | || released == QStyle::SC_GroupBoxCheckBox); |
722 | d->pressedControl = QStyle::SC_None; |
723 | d->overCheckBox = false; |
724 | if (toggle) |
725 | d->click(); |
726 | else if (d->checkable) |
727 | update(style()->subControlRect(cc: QStyle::CC_GroupBox, opt: &box, sc: QStyle::SC_GroupBoxCheckBox, widget: this)); |
728 | } |
729 | |
730 | QT_END_NAMESPACE |
731 | |
732 | #include "moc_qgroupbox.cpp" |
733 | |