Skip to content

Commit

Permalink
ENH: Add checkBoxControlsButtonToggleState property to ctkCheckablePu…
Browse files Browse the repository at this point in the history
…shButton (#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 <sunderlandkyl@gmail.com>
Co-authored-by: Andras Lasso <lasso@cs.queensu.ca>
Co-authored-by: Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com>
  • Loading branch information
4 people committed Apr 21, 2020
1 parent 27bc11d commit 33286e5
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 42 deletions.
16 changes: 12 additions & 4 deletions Libs/Widgets/Testing/Cpp/ctkCheckablePushButtonTest1.cpp
Expand Up @@ -54,6 +54,7 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
ctkCheckablePushButton button13(QObject::tr("Checkbox and Button User Checkable"));
ctkCheckablePushButton button14(QObject::tr("Checkable PushButton with menu"));
ctkCheckablePushButton button15(QObject::tr("Checkable PushButton with icon"));
ctkCheckablePushButton button16(QObject::tr("Check box controls button toggle state"));

QVBoxLayout *layout= new QVBoxLayout;
layout->addWidget(&button1);
Expand All @@ -71,6 +72,7 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
layout->addWidget(&button13);
layout->addWidget(&button14);
layout->addWidget(&button15);
layout->addWidget(&button16);
topLevel.setLayout(layout);

topLevel.show();
Expand All @@ -95,17 +97,17 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
button3.setButtonTextAlignment(Qt::AlignCenter);
button3.setIndicatorAlignment(Qt::AlignCenter);
button3.setCheckable(true);

button4.setCheckable(true);
button4.toggle();

button5.setButtonTextAlignment(Qt::AlignCenter);
button5.setIndicatorAlignment(Qt::AlignRight);

button6.setIndicatorAlignment(Qt::AlignTop);
button7.setButtonTextAlignment(Qt::AlignCenter);
button7.setIndicatorAlignment(Qt::AlignLeft);

// Connected to button, not user checkable:
button8.setCheckBoxUserCheckable(false);
button8.setCheckState(Qt::Checked);
Expand All @@ -125,6 +127,12 @@ int ctkCheckablePushButtonTest1(int argc, char * argv [] )
button13.setCheckBoxUserCheckable(true);
button13.setCheckable(true);

// Checkbox control button toggle state
button16.setCheckable(true);
button16.setCheckBoxUserCheckable(true);
button16.setCheckBoxControlsButton(true);
button16.setCheckBoxControlsButtonToggleState(true);

QMenu menu(&button14);
menu.addAction("menu action");
button14.setMenu(&menu);
Expand Down
105 changes: 81 additions & 24 deletions Libs/Widgets/ctkCheckablePushButton.cpp
Expand Up @@ -49,17 +49,22 @@ class ctkCheckablePushButtonPrivate: public ctkPushButtonPrivate
virtual QStyleOptionButton drawIcon(QPainter* p);

// Tuning of the button look&feel
Qt::ItemFlags CheckBoxFlags;
Qt::CheckState CheckState;

bool CheckBoxControlsButton;
bool CheckBoxUserCheckable;
bool CheckBoxControlsButtonToggleState;
};

//-----------------------------------------------------------------------------
ctkCheckablePushButtonPrivate::ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object)
: ctkPushButtonPrivate(object)
, q_ptr(&object)
{
this->CheckBoxFlags = Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
this->CheckBoxControlsButton = true;
this->CheckBoxUserCheckable = true;
this->CheckState = Qt::Unchecked;
this->CheckBoxControlsButtonToggleState = false;
}

//-----------------------------------------------------------------------------
Expand All @@ -84,7 +89,7 @@ QStyleOptionButton ctkCheckablePushButtonPrivate::drawIcon(QPainter* p)
QStyleOptionButton indicatorOpt;

indicatorOpt.init(q);
if (!(this->CheckBoxFlags & Qt::ItemIsUserCheckable))
if (!this->CheckBoxUserCheckable)
{
indicatorOpt.state &= ~QStyle::State_Enabled;
}
Expand Down Expand Up @@ -158,17 +163,22 @@ void ctkCheckablePushButton::setCheckState(Qt::CheckState checkState)
return;
}
d->CheckState = checkState;
bool emitToggled = false;
if (d->CheckBoxFlags & Qt::ItemIsEnabled)
if (d->CheckBoxControlsButton)
{
bool wasChecked = this->isChecked();
// QCheckBox::setCheckable() doesn't emit toggled signal
this->setCheckable(checkState == Qt::Checked);
emitToggled = (wasChecked != this->isChecked());
}
if (emitToggled)
{
emit toggled(this->isChecked());
// QCheckBox::setCheckable() doesn't emit toggled signal
if (wasChecked != this->isChecked())
{
emit toggled(this->isChecked());
}
if (d->CheckBoxControlsButtonToggleState)
{
if (this->isChecked() != (checkState == Qt::Checked))
{
this->setChecked(checkState == Qt::Checked);
}
}
}
emit checkStateChanged(d->CheckState);
emit checkBoxToggled(d->CheckState == Qt::Checked);
Expand All @@ -186,47 +196,64 @@ Qt::CheckState ctkCheckablePushButton::checkState()const
void ctkCheckablePushButton::setCheckBoxControlsButton(bool b)
{
Q_D(ctkCheckablePushButton);
d->CheckBoxControlsButton = b;
if (b)
{
d->CheckBoxFlags |= Qt::ItemIsEnabled;
// synchronize checkstate with the checkable property.
this->setCheckState(
this->isCheckable() ? Qt::Checked : Qt::Unchecked);
}
else
{
d->CheckBoxFlags &= ~Qt::ItemIsEnabled;
}
this->update();
}

//-----------------------------------------------------------------------------
bool ctkCheckablePushButton::checkBoxControlsButton()const
{
Q_D(const ctkCheckablePushButton);
return d->CheckBoxFlags & Qt::ItemIsEnabled;
return d->CheckBoxControlsButton;
}

//-----------------------------------------------------------------------------
void ctkCheckablePushButton::setCheckBoxUserCheckable(bool b)
void ctkCheckablePushButton::setCheckBoxControlsButtonToggleState(bool b)
{
Q_D(ctkCheckablePushButton);
if (b)
if (d->CheckBoxControlsButtonToggleState == b)
{
d->CheckBoxFlags |= Qt::ItemIsUserCheckable;
return;
}
else
d->CheckBoxControlsButtonToggleState = b;
if (d->CheckBoxControlsButtonToggleState)
{
d->CheckBoxFlags &= ~Qt::ItemIsUserCheckable;
// We have just enabled sync between toggle state and checkbox.
// If checkbox is enabled then make the button toggled.
if (this->checkState() && !this->isChecked())
{
this->setChecked(true);
}
}
this->update();
}

//-----------------------------------------------------------------------------
bool ctkCheckablePushButton::checkBoxControlsButtonToggleState()const
{
Q_D(const ctkCheckablePushButton);
return d->CheckBoxControlsButtonToggleState;
}

//-----------------------------------------------------------------------------
void ctkCheckablePushButton::setCheckBoxUserCheckable(bool b)
{
Q_D(ctkCheckablePushButton);
d->CheckBoxUserCheckable = b;
this->update();
}

//-----------------------------------------------------------------------------
bool ctkCheckablePushButton::isCheckBoxUserCheckable()const
{
Q_D(const ctkCheckablePushButton);
return d->CheckBoxFlags & Qt::ItemIsUserCheckable;
return d->CheckBoxUserCheckable;
}

//-----------------------------------------------------------------------------
Expand All @@ -247,7 +274,7 @@ void ctkCheckablePushButton::mousePressEvent(QMouseEvent *e)
return;
}
if (d->iconRect().contains(e->pos()) &&
(d->CheckBoxFlags & Qt::ItemIsUserCheckable))
(d->CheckBoxUserCheckable))
{
Qt::CheckState newCheckState;
switch (d->CheckState)
Expand All @@ -265,3 +292,33 @@ void ctkCheckablePushButton::mousePressEvent(QMouseEvent *e)
e->accept();
}
}

//-----------------------------------------------------------------------------
void ctkCheckablePushButton::checkStateSet()
{
Q_D(ctkCheckablePushButton);
this->ctkPushButton::checkStateSet();
if (d->CheckBoxControlsButtonToggleState)
{
// Uncheck the checkbox if button is untoggled
if (!this->isChecked() && this->checkState())
{
this->setCheckState(Qt::Unchecked);
}
}
}

//-----------------------------------------------------------------------------
void ctkCheckablePushButton::nextCheckState()
{
Q_D(ctkCheckablePushButton);
this->ctkPushButton::nextCheckState();
if (d->CheckBoxControlsButtonToggleState)
{
// Uncheck the checkbox if button is untoggled
if (!this->isChecked() && this->checkState() == Qt::Checked)
{
this->setCheckState(Qt::Unchecked);
}
}
}
42 changes: 28 additions & 14 deletions Libs/Widgets/ctkCheckablePushButton.h
Expand Up @@ -30,19 +30,8 @@ class ctkCheckablePushButtonPrivate;

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

public:
Expand All @@ -65,12 +55,33 @@ class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public ctkPushButton
void setIndicatorAlignment(Qt::Alignment indicatorAlignment);
Qt::Alignment indicatorAlignment()const;

/// Get checked state of the checkbox on the button.
virtual Qt::CheckState checkState()const;
/// Set checked state of the checkbox on the button.
virtual void setCheckState(Qt::CheckState checkState);

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

/// If both checkBoxControlsButton and checkBoxControlsButtonToggleState
/// are enabled then check state is synchronized with pushed state of the button
/// (checking the checkbox also pushes down the button and releasing the button
/// unchecks the checkbox).
virtual bool checkBoxControlsButtonToggleState()const;
virtual void setCheckBoxControlsButtonToggleState(bool b);

/// The checkBoxUserCheckable property determines if the state of the
/// checkbox can be changed interactively by the user by clicking on the
/// checkbox.
virtual bool isCheckBoxUserCheckable()const;
virtual void setCheckBoxUserCheckable(bool b);

Expand All @@ -85,7 +96,10 @@ class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public ctkPushButton
virtual void mousePressEvent(QMouseEvent* event);
/// Reimplemented for internal reasons
virtual bool hitButton(const QPoint & pos) const;

/// Reimplemented for internal reasons
void checkStateSet() override;
/// Reimplemented for internal reasons
void nextCheckState() override;
private:
Q_DECLARE_PRIVATE(ctkCheckablePushButton);
Q_DISABLE_COPY(ctkCheckablePushButton);
Expand Down

0 comments on commit 33286e5

Please sign in to comment.