QAction: add exclusionPolicy property

When set to ExclusiveOptional, the new exclusionPolicy property let the
user uncheck the active checkable action in an exclusive group.

[ChangeLog][QtWidgets][QActionGroup] Added new exclusionPolicy
property. Set it to ExclusiveOptional to allow unchecking the active
checkable action in an exclusive group.

Change-Id: I61a9885cfd076d631cddf8c08313e4b488e5dc38
Fixes: QTBUG-71160
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Simone Gaiarin 2019-03-14 12:41:17 +01:00
parent b17e05729e
commit c826e67765
4 changed files with 139 additions and 20 deletions

View File

@ -1153,8 +1153,9 @@ void QAction::activate(ActionEvent event)
if(event == Trigger) {
QPointer<QObject> guard = this;
if(d->checkable) {
// the checked action of an exclusive group cannot be unchecked
if (d->checked && (d->group && d->group->isExclusive()
// the checked action of an exclusive group may not be unchecked
if (d->checked && (d->group
&& d->group->exclusionPolicy() == QActionGroup::ExclusionPolicy::Exclusive
&& d->group->checkedAction() == this)) {
if (!guard.isNull())
emit triggered(true);

View File

@ -52,12 +52,16 @@ class QActionGroupPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QActionGroup)
public:
QActionGroupPrivate() : exclusive(1), enabled(1), visible(1) { }
QActionGroupPrivate() : enabled(1),
visible(1),
exclusionPolicy(QActionGroup::ExclusionPolicy::Exclusive)
{
}
QList<QAction *> actions;
QPointer<QAction> current;
uint exclusive : 1;
uint enabled : 1;
uint visible : 1;
QActionGroup::ExclusionPolicy exclusionPolicy;
private:
void _q_actionTriggered(); //private slot
@ -70,7 +74,7 @@ void QActionGroupPrivate::_q_actionChanged()
Q_Q(QActionGroup);
QAction *action = qobject_cast<QAction*>(q->sender());
Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionChanged", "internal error");
if(exclusive) {
if (exclusionPolicy != QActionGroup::ExclusionPolicy::None) {
if (action->isChecked()) {
if (action != current) {
if(current)
@ -127,12 +131,17 @@ void QActionGroupPrivate::_q_actionHovered()
actions is chosen. Each action in an action group emits its
triggered() signal as usual.
As stated above, an action group is \l exclusive by default; it
ensures that only one checkable action is active at any one time.
As stated above, an action group is exclusive by default; it
ensures that at most only one checkable action is active at any one time.
If you want to group checkable actions without making them
exclusive, you can turn of exclusiveness by calling
exclusive, you can turn off exclusiveness by calling
setExclusive(false).
By default the active action of an exclusive group cannot be unchecked.
In some cases it may be useful to allow unchecking all the actions,
you can allow this by calling
setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional).
Actions can be added to an action group using addAction(), but it
is usually more convenient to specify a group when creating
actions; this ensures that actions are automatically created with
@ -145,11 +154,34 @@ void QActionGroupPrivate::_q_actionHovered()
\sa QAction
*/
/*!
\enum QActionGroup::ExclusionPolicy
This enum specifies the different policies that can be used to
control how the group performs exclusive checking on checkable actions.
\value None
The actions in the group can be checked independently of each other.
\value Exclusive
Exactly one action can be checked at any one time.
This is the default policy.
\value ExclusiveOptional
At most one action can be checked at any one time. The actions
can also be all unchecked.
\sa exclusionPolicy
\since 5.14
*/
/*!
Constructs an action group for the \a parent object.
The action group is exclusive by default. Call setExclusive(false)
to make the action group non-exclusive.
to make the action group non-exclusive. To make the group exclusive
but allow unchecking the active action call instead
setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional)
*/
QActionGroup::QActionGroup(QObject* parent) : QObject(*new QActionGroupPrivate, parent)
{
@ -258,26 +290,56 @@ QList<QAction*> QActionGroup::actions() const
}
/*!
\property QActionGroup::exclusive
\brief whether the action group does exclusive checking
\brief Enable or disable the group exclusion checking
If exclusive is true, only one checkable action in the action group
can ever be active at any time. If the user chooses another
checkable action in the group, the one they chose becomes active and
the one that was active becomes inactive.
This is a convenience method that calls
setExclusionPolicy(ExclusionPolicy::Exclusive).
\sa QAction::checkable
\sa QActionGroup::exclusionPolicy
*/
void QActionGroup::setExclusive(bool b)
{
Q_D(QActionGroup);
d->exclusive = b;
setExclusionPolicy(b ? QActionGroup::ExclusionPolicy::Exclusive
: QActionGroup::ExclusionPolicy::None);
}
/*!
\brief Returs true if the group is exclusive
The group is exclusive if the ExclusionPolicy is either Exclusive
or ExclusionOptional.
*/
bool QActionGroup::isExclusive() const
{
return exclusionPolicy() != QActionGroup::ExclusionPolicy::None;
}
/*!
\property QActionGroup::exclusionPolicy
\brief This property holds the group exclusive checking policy
If exclusionPolicy is set to Exclusive, only one checkable
action in the action group can ever be active at any time. If the user
chooses another checkable action in the group, the one they chose becomes
active and the one that was active becomes inactive. If exclusionPolicy is
set to ExclusionOptional the group is exclusive but the active checkable
action in the group can be unchecked leaving the group with no actions
checked.
\sa QAction::checkable
\since 5.14
*/
void QActionGroup::setExclusionPolicy(QActionGroup::ExclusionPolicy policy)
{
Q_D(QActionGroup);
d->exclusionPolicy = policy;
}
QActionGroup::ExclusionPolicy QActionGroup::exclusionPolicy() const
{
Q_D(const QActionGroup);
return d->exclusive;
return d->exclusionPolicy;
}
/*!

View File

@ -55,11 +55,18 @@ class Q_WIDGETS_EXPORT QActionGroup : public QObject
Q_OBJECT
Q_DECLARE_PRIVATE(QActionGroup)
Q_PROPERTY(bool exclusive READ isExclusive WRITE setExclusive)
Q_PROPERTY(QActionGroup::ExclusionPolicy exclusionPolicy READ exclusionPolicy WRITE setExclusionPolicy)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
public:
enum class ExclusionPolicy {
None,
Exclusive,
ExclusiveOptional
};
Q_ENUM(ExclusionPolicy)
explicit QActionGroup(QObject* parent);
~QActionGroup();
@ -73,6 +80,7 @@ public:
bool isExclusive() const;
bool isEnabled() const;
bool isVisible() const;
ExclusionPolicy exclusionPolicy() const;
public Q_SLOTS:
@ -80,6 +88,7 @@ public Q_SLOTS:
inline void setDisabled(bool b) { setEnabled(!b); }
void setVisible(bool);
void setExclusive(bool);
void setExclusionPolicy(ExclusionPolicy policy);
Q_SIGNALS:
void triggered(QAction *);

View File

@ -41,6 +41,7 @@ private slots:
void enabledPropagation();
void visiblePropagation();
void exclusive();
void exclusiveOptional();
void separators();
void testActionInTwoQActionGroup();
void unCheckCurrentAction();
@ -151,6 +152,52 @@ void tst_QActionGroup::exclusive()
QVERIFY( !actThree->isChecked() );
}
void tst_QActionGroup::exclusiveOptional()
{
QActionGroup group(0);
group.setExclusive(true);
QVERIFY( group.isExclusive() );
QAction* actOne = new QAction( &group );
actOne->setCheckable( true );
QAction* actTwo = new QAction( &group );
actTwo->setCheckable( true );
QAction* actThree = new QAction( &group );
actThree->setCheckable( true );
QVERIFY( !actOne->isChecked() );
QVERIFY( !actTwo->isChecked() );
QVERIFY( !actThree->isChecked() );
actOne->trigger();
QVERIFY( actOne->isChecked() );
QVERIFY( !actTwo->isChecked() );
QVERIFY( !actThree->isChecked() );
actOne->trigger();
QVERIFY( actOne->isChecked() );
QVERIFY( !actTwo->isChecked() );
QVERIFY( !actThree->isChecked() );
group.setExclusionPolicy( QActionGroup::ExclusionPolicy::ExclusiveOptional );
QVERIFY( group.isExclusive() );
actOne->trigger();
QVERIFY( !actOne->isChecked() );
QVERIFY( !actTwo->isChecked() );
QVERIFY( !actThree->isChecked() );
actTwo->trigger();
QVERIFY( !actOne->isChecked() );
QVERIFY( actTwo->isChecked() );
QVERIFY( !actThree->isChecked() );
actTwo->trigger();
QVERIFY( !actOne->isChecked() );
QVERIFY( !actTwo->isChecked() );
QVERIFY( !actThree->isChecked() );
}
void tst_QActionGroup::separators()
{
QMainWindow mw;