Skip to content

Commit 33286e5

Browse files
SunderlandkylAndras Lassojcfr
authored
ENH: Add checkBoxControlsButtonToggleState property to ctkCheckablePushButton (#908)
If checkBoxControlsButtonToggleState is enabled then clicking the checkbox also makes the button pushed (for user's convenience) and unpushing the button makes it unchecked, too (to avoid the ambiguous state when the button's checkbox is checked but the button is not pressed down). The property is disabled for now to preserve the current behavior (for backward compatibility). Co-authored-by: Kyle Sunderland <[email protected]> Co-authored-by: Andras Lasso <[email protected]> Co-authored-by: Jean-Christophe Fillion-Robin <[email protected]>
1 parent 27bc11d commit 33286e5

File tree

3 files changed

+121
-42
lines changed

3 files changed

+121
-42
lines changed

Libs/Widgets/Testing/Cpp/ctkCheckablePushButtonTest1.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
5454
ctkCheckablePushButton button13(QObject::tr("Checkbox and Button User Checkable"));
5555
ctkCheckablePushButton button14(QObject::tr("Checkable PushButton with menu"));
5656
ctkCheckablePushButton button15(QObject::tr("Checkable PushButton with icon"));
57+
ctkCheckablePushButton button16(QObject::tr("Check box controls button toggle state"));
5758

5859
QVBoxLayout *layout= new QVBoxLayout;
5960
layout->addWidget(&button1);
@@ -71,6 +72,7 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
7172
layout->addWidget(&button13);
7273
layout->addWidget(&button14);
7374
layout->addWidget(&button15);
75+
layout->addWidget(&button16);
7476
topLevel.setLayout(layout);
7577

7678
topLevel.show();
@@ -95,17 +97,17 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
9597
button3.setButtonTextAlignment(Qt::AlignCenter);
9698
button3.setIndicatorAlignment(Qt::AlignCenter);
9799
button3.setCheckable(true);
98-
100+
99101
button4.setCheckable(true);
100102
button4.toggle();
101-
103+
102104
button5.setButtonTextAlignment(Qt::AlignCenter);
103105
button5.setIndicatorAlignment(Qt::AlignRight);
104-
106+
105107
button6.setIndicatorAlignment(Qt::AlignTop);
106108
button7.setButtonTextAlignment(Qt::AlignCenter);
107109
button7.setIndicatorAlignment(Qt::AlignLeft);
108-
110+
109111
// Connected to button, not user checkable:
110112
button8.setCheckBoxUserCheckable(false);
111113
button8.setCheckState(Qt::Checked);
@@ -125,6 +127,12 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
125127
button13.setCheckBoxUserCheckable(true);
126128
button13.setCheckable(true);
127129

130+
// Checkbox control button toggle state
131+
button16.setCheckable(true);
132+
button16.setCheckBoxUserCheckable(true);
133+
button16.setCheckBoxControlsButton(true);
134+
button16.setCheckBoxControlsButtonToggleState(true);
135+
128136
QMenu menu(&button14);
129137
menu.addAction("menu action");
130138
button14.setMenu(&menu);

Libs/Widgets/ctkCheckablePushButton.cpp

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,22 @@ class ctkCheckablePushButtonPrivate: public ctkPushButtonPrivate
4949
virtual QStyleOptionButton drawIcon(QPainter* p);
5050

5151
// Tuning of the button look&feel
52-
Qt::ItemFlags CheckBoxFlags;
5352
Qt::CheckState CheckState;
53+
54+
bool CheckBoxControlsButton;
55+
bool CheckBoxUserCheckable;
56+
bool CheckBoxControlsButtonToggleState;
5457
};
5558

5659
//-----------------------------------------------------------------------------
5760
ctkCheckablePushButtonPrivate::ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object)
5861
: ctkPushButtonPrivate(object)
5962
, q_ptr(&object)
6063
{
61-
this->CheckBoxFlags = Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
64+
this->CheckBoxControlsButton = true;
65+
this->CheckBoxUserCheckable = true;
6266
this->CheckState = Qt::Unchecked;
67+
this->CheckBoxControlsButtonToggleState = false;
6368
}
6469

6570
//-----------------------------------------------------------------------------
@@ -84,7 +89,7 @@ QStyleOptionButton ctkCheckablePushButtonPrivate::drawIcon(QPainter* p)
8489
QStyleOptionButton indicatorOpt;
8590

8691
indicatorOpt.init(q);
87-
if (!(this->CheckBoxFlags & Qt::ItemIsUserCheckable))
92+
if (!this->CheckBoxUserCheckable)
8893
{
8994
indicatorOpt.state &= ~QStyle::State_Enabled;
9095
}
@@ -158,17 +163,22 @@ void ctkCheckablePushButton::setCheckState(Qt::CheckState checkState)
158163
return;
159164
}
160165
d->CheckState = checkState;
161-
bool emitToggled = false;
162-
if (d->CheckBoxFlags & Qt::ItemIsEnabled)
166+
if (d->CheckBoxControlsButton)
163167
{
164168
bool wasChecked = this->isChecked();
165-
// QCheckBox::setCheckable() doesn't emit toggled signal
166169
this->setCheckable(checkState == Qt::Checked);
167-
emitToggled = (wasChecked != this->isChecked());
168-
}
169-
if (emitToggled)
170-
{
171-
emit toggled(this->isChecked());
170+
// QCheckBox::setCheckable() doesn't emit toggled signal
171+
if (wasChecked != this->isChecked())
172+
{
173+
emit toggled(this->isChecked());
174+
}
175+
if (d->CheckBoxControlsButtonToggleState)
176+
{
177+
if (this->isChecked() != (checkState == Qt::Checked))
178+
{
179+
this->setChecked(checkState == Qt::Checked);
180+
}
181+
}
172182
}
173183
emit checkStateChanged(d->CheckState);
174184
emit checkBoxToggled(d->CheckState == Qt::Checked);
@@ -186,47 +196,64 @@ Qt::CheckState ctkCheckablePushButton::checkState()const
186196
void ctkCheckablePushButton::setCheckBoxControlsButton(bool b)
187197
{
188198
Q_D(ctkCheckablePushButton);
199+
d->CheckBoxControlsButton = b;
189200
if (b)
190201
{
191-
d->CheckBoxFlags |= Qt::ItemIsEnabled;
192202
// synchronize checkstate with the checkable property.
193203
this->setCheckState(
194204
this->isCheckable() ? Qt::Checked : Qt::Unchecked);
195205
}
196-
else
197-
{
198-
d->CheckBoxFlags &= ~Qt::ItemIsEnabled;
199-
}
200206
this->update();
201207
}
202208

203209
//-----------------------------------------------------------------------------
204210
bool ctkCheckablePushButton::checkBoxControlsButton()const
205211
{
206212
Q_D(const ctkCheckablePushButton);
207-
return d->CheckBoxFlags & Qt::ItemIsEnabled;
213+
return d->CheckBoxControlsButton;
208214
}
209215

210216
//-----------------------------------------------------------------------------
211-
void ctkCheckablePushButton::setCheckBoxUserCheckable(bool b)
217+
void ctkCheckablePushButton::setCheckBoxControlsButtonToggleState(bool b)
212218
{
213219
Q_D(ctkCheckablePushButton);
214-
if (b)
220+
if (d->CheckBoxControlsButtonToggleState == b)
215221
{
216-
d->CheckBoxFlags |= Qt::ItemIsUserCheckable;
222+
return;
217223
}
218-
else
224+
d->CheckBoxControlsButtonToggleState = b;
225+
if (d->CheckBoxControlsButtonToggleState)
219226
{
220-
d->CheckBoxFlags &= ~Qt::ItemIsUserCheckable;
227+
// We have just enabled sync between toggle state and checkbox.
228+
// If checkbox is enabled then make the button toggled.
229+
if (this->checkState() && !this->isChecked())
230+
{
231+
this->setChecked(true);
232+
}
221233
}
222234
this->update();
223235
}
224236

237+
//-----------------------------------------------------------------------------
238+
bool ctkCheckablePushButton::checkBoxControlsButtonToggleState()const
239+
{
240+
Q_D(const ctkCheckablePushButton);
241+
return d->CheckBoxControlsButtonToggleState;
242+
}
243+
244+
//-----------------------------------------------------------------------------
245+
void ctkCheckablePushButton::setCheckBoxUserCheckable(bool b)
246+
{
247+
Q_D(ctkCheckablePushButton);
248+
d->CheckBoxUserCheckable = b;
249+
this->update();
250+
}
251+
225252
//-----------------------------------------------------------------------------
226253
bool ctkCheckablePushButton::isCheckBoxUserCheckable()const
227254
{
228255
Q_D(const ctkCheckablePushButton);
229-
return d->CheckBoxFlags & Qt::ItemIsUserCheckable;
256+
return d->CheckBoxUserCheckable;
230257
}
231258

232259
//-----------------------------------------------------------------------------
@@ -247,7 +274,7 @@ void ctkCheckablePushButton::mousePressEvent(QMouseEvent *e)
247274
return;
248275
}
249276
if (d->iconRect().contains(e->pos()) &&
250-
(d->CheckBoxFlags & Qt::ItemIsUserCheckable))
277+
(d->CheckBoxUserCheckable))
251278
{
252279
Qt::CheckState newCheckState;
253280
switch (d->CheckState)
@@ -265,3 +292,33 @@ void ctkCheckablePushButton::mousePressEvent(QMouseEvent *e)
265292
e->accept();
266293
}
267294
}
295+
296+
//-----------------------------------------------------------------------------
297+
void ctkCheckablePushButton::checkStateSet()
298+
{
299+
Q_D(ctkCheckablePushButton);
300+
this->ctkPushButton::checkStateSet();
301+
if (d->CheckBoxControlsButtonToggleState)
302+
{
303+
// Uncheck the checkbox if button is untoggled
304+
if (!this->isChecked() && this->checkState())
305+
{
306+
this->setCheckState(Qt::Unchecked);
307+
}
308+
}
309+
}
310+
311+
//-----------------------------------------------------------------------------
312+
void ctkCheckablePushButton::nextCheckState()
313+
{
314+
Q_D(ctkCheckablePushButton);
315+
this->ctkPushButton::nextCheckState();
316+
if (d->CheckBoxControlsButtonToggleState)
317+
{
318+
// Uncheck the checkbox if button is untoggled
319+
if (!this->isChecked() && this->checkState() == Qt::Checked)
320+
{
321+
this->setCheckState(Qt::Unchecked);
322+
}
323+
}
324+
}

Libs/Widgets/ctkCheckablePushButton.h

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,8 @@ class ctkCheckablePushButtonPrivate;
3030

3131
/// \ingroup Widgets
3232
/// Description
33-
/// ctkCheckablePushButton is a QPushButton with a checkbox. By default
34-
/// the checkbox is connected to the checkable property of the push button.
35-
/// You can change this behaviour by clearing the "checkBoxControlsButton"
36-
/// property.
37-
/// The checkBoxUserCheckable property determines if the state of the
38-
/// checkbox can be changed interactively by the user by clicking on the
39-
/// checkbox.
40-
/// \note In checkBoxControlsButton mode, calling setCheckable instead of
41-
/// setCheckState may not refresh the button automatically. Use setCheckState
42-
/// instead.
43-
/// \note You can automatically check the button when the user checks the
44-
/// checkbox by connecting the checkBoxToggled(bool) signal with the
45-
/// setChecked(bool) slot.
33+
/// ctkCheckablePushButton is a QPushButton with a checkbox.
34+
///
4635
/// \warning The checkbox is drawn in place of the pushbuton icon, any icon
4736
/// will then be ignored.
4837
class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public ctkPushButton
@@ -53,6 +42,7 @@ class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public ctkPushButton
5342
Q_PROPERTY(Qt::Alignment indicatorAlignment READ indicatorAlignment WRITE setIndicatorAlignment)
5443
Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged)
5544
Q_PROPERTY(bool checkBoxControlsButton READ checkBoxControlsButton WRITE setCheckBoxControlsButton)
45+
Q_PROPERTY(bool checkBoxControlsButtonToggleState READ checkBoxControlsButtonToggleState WRITE setCheckBoxControlsButtonToggleState)
5646
Q_PROPERTY(bool checkBoxUserCheckable READ isCheckBoxUserCheckable WRITE setCheckBoxUserCheckable)
5747

5848
public:
@@ -65,12 +55,33 @@ class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public ctkPushButton
6555
void setIndicatorAlignment(Qt::Alignment indicatorAlignment);
6656
Qt::Alignment indicatorAlignment()const;
6757

58+
/// Get checked state of the checkbox on the button.
6859
virtual Qt::CheckState checkState()const;
60+
/// Set checked state of the checkbox on the button.
6961
virtual void setCheckState(Qt::CheckState checkState);
7062

63+
/// By default the checkbox is connected to the checkable property of the push button.
64+
/// You can change this behaviour by clearing the "checkBoxControlsButton"
65+
/// property.
66+
/// \note In checkBoxControlsButton mode, calling setCheckable() instead of
67+
/// setCheckState() may not refresh the button automatically. Use setCheckState()
68+
/// instead.
69+
/// \note You can automatically check the button when the user checks the
70+
/// checkbox by connecting the checkBoxToggled(bool) signal with the
71+
/// setChecked(bool) slot or by enabling "checkBoxControlsButtonToggleState" property.
7172
virtual bool checkBoxControlsButton()const;
7273
virtual void setCheckBoxControlsButton(bool b);
7374

75+
/// If both checkBoxControlsButton and checkBoxControlsButtonToggleState
76+
/// are enabled then check state is synchronized with pushed state of the button
77+
/// (checking the checkbox also pushes down the button and releasing the button
78+
/// unchecks the checkbox).
79+
virtual bool checkBoxControlsButtonToggleState()const;
80+
virtual void setCheckBoxControlsButtonToggleState(bool b);
81+
82+
/// The checkBoxUserCheckable property determines if the state of the
83+
/// checkbox can be changed interactively by the user by clicking on the
84+
/// checkbox.
7485
virtual bool isCheckBoxUserCheckable()const;
7586
virtual void setCheckBoxUserCheckable(bool b);
7687

@@ -85,7 +96,10 @@ class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public ctkPushButton
8596
virtual void mousePressEvent(QMouseEvent* event);
8697
/// Reimplemented for internal reasons
8798
virtual bool hitButton(const QPoint & pos) const;
88-
99+
/// Reimplemented for internal reasons
100+
void checkStateSet() override;
101+
/// Reimplemented for internal reasons
102+
void nextCheckState() override;
89103
private:
90104
Q_DECLARE_PRIVATE(ctkCheckablePushButton);
91105
Q_DISABLE_COPY(ctkCheckablePushButton);

0 commit comments

Comments
 (0)