Support for mutual exclusive effects

The new X-KWin-Exclusive-Category property is read from the service
and provided to QML through the ExclusiveRole. If an effect has such
a role the CheckBox is replaced by a RadioButton. The radio buttons of
an exclusive group take care that only one effect of the group can be
enabled. In addition the radio button acts like a check box. If one
clicks the checked radio button it gets unchecked.

At the same time this change removes the hard coded functionality for
the exclusive group of desktop switching effects. It's all handled
dynamically by creating the ExclusiveGroup when needed. For each
category there can be one ExclusiveGroup.

REVIEW: 116711
This commit is contained in:
Martin Gräßlin 2014-03-11 13:06:18 +01:00
parent 503c221733
commit 7d63ab03bc
4 changed files with 78 additions and 52 deletions

View file

@ -66,6 +66,7 @@ QHash< int, QByteArray > EffectModel::roleNames() const
roleNames[EffectStatusRole] = "EffectStatusRole";
roleNames[VideoRole] = "VideoRole";
roleNames[SupportedRole] = "SupportedRole";
roleNames[ExclusiveRole] = "ExclusiveRole";
return roleNames;
}
@ -130,6 +131,8 @@ QVariant EffectModel::data(const QModelIndex &index, int role) const
return m_effectsList.at(index.row()).video;
case SupportedRole:
return m_effectsList.at(index.row()).supported;
case ExclusiveRole:
return m_effectsList.at(index.row()).exclusiveGroup;
default:
return QVariant();
}
@ -141,17 +144,24 @@ bool EffectModel::setData(const QModelIndex& index, const QVariant& value, int r
return QAbstractItemModel::setData(index, value, role);
if (role == EffectModel::EffectStatusRole) {
m_effectsList[index.row()].effectStatus = value.toBool();
const QString effectServiceName = m_effectsList[index.row()].serviceName;
if (effectServiceName == "kwin4_effect_slide") {
handleDesktopSwitching(index.row());
} else if (effectServiceName == "kwin4_effect_fadedesktop") {
handleDesktopSwitching(index.row());
} else if (effectServiceName == "kwin4_effect_cubeslide") {
handleDesktopSwitching(index.row());
}
EffectData &data = m_effectsList[index.row()];
data.effectStatus = value.toBool();
emit dataChanged(index, index);
if (data.effectStatus && !data.exclusiveGroup.isEmpty()) {
// need to disable all other exclusive effects in the same category
for (int i = 0; i < m_effectsList.size(); ++i) {
if (i == index.row()) {
continue;
}
EffectData &otherData = m_effectsList[i];
if (otherData.exclusiveGroup == data.exclusiveGroup) {
otherData.effectStatus = false;
emit dataChanged(this->index(i, 0), this->index(i, 0));
}
}
}
return true;
} else if (role == EffectModel::WindowManagementRole) {
bool enabled = value.toBool();
@ -188,6 +198,7 @@ void EffectModel::loadEffects()
effect.enabledByDefault = plugin.isPluginEnabledByDefault();
effect.video = service->property(QStringLiteral("X-KWin-Video-Url"), QVariant::Url).toUrl();
effect.supported = true;
effect.exclusiveGroup = service->property(QStringLiteral("X-KWin-Exclusive-Category"), QVariant::String).toString();
m_effectsList << effect;
}
@ -239,27 +250,6 @@ void EffectModel::loadEffects()
endResetModel();
}
void EffectModel::handleDesktopSwitching(int row)
{
//Q: Why do we need the handleDesktopSwitching?
//A: Because of the setData, when we enable the effect
//and then we scroll, our model is being updated,
//so the setData is being called again, and as a result
//of that we have multiple effects enabled for the desktop switching.
const QString currentEffect = m_effectsList[row].serviceName;
for (int it = 0; it < m_effectsList.size(); it++) {
EffectData effect = m_effectsList.at(it);
if (effect.serviceName == "kwin4_effect_slide" && currentEffect != effect.serviceName && effect.effectStatus) {
m_effectsList[it].effectStatus = !m_effectsList[it].effectStatus;
} else if (effect.serviceName == "kwin4_effect_cubeslide" && currentEffect != effect.serviceName && effect.effectStatus) {
m_effectsList[it].effectStatus = !m_effectsList[it].effectStatus;
} else if (effect.serviceName == "kwin4_effect_fadedesktop" && currentEffect != effect.serviceName && effect.effectStatus) {
m_effectsList[it].effectStatus = !m_effectsList[it].effectStatus;
}
}
}
void EffectModel::handleWindowManagement(int row, bool enabled)
{
//Make sure that our row is valid

View file

@ -47,6 +47,7 @@ struct EffectData {
bool enabledByDefault;
QUrl video;
bool supported;
QString exclusiveGroup;
};
class EffectModel : public QAbstractItemModel
@ -67,7 +68,8 @@ public:
EffectStatusRole,
WindowManagementRole,
VideoRole,
SupportedRole
SupportedRole,
ExclusiveRole
};
explicit EffectModel(QObject *parent = 0);
@ -90,7 +92,6 @@ public:
void defaults();
private:
void handleDesktopSwitching(int row);
void handleWindowManagement(int row, bool enabled);
int findRowByServiceName(const QString &serviceName);
QList<EffectData> m_effectsList;

View file

@ -30,7 +30,7 @@ Rectangle {
height: childrenRect.height
color: item.ListView.isCurrentItem ? effectView.backgroundActiveColor : index % 2 ? effectView.backgroundNormalColor : effectView.backgroundAlternateColor
signal changed()
property alias checked: effectStatusCheckBox.checked
property bool checked: model.EffectStatusRole
MouseArea {
anchors.fill: parent
@ -43,18 +43,36 @@ Rectangle {
id: rowEffect
width: parent.width - 2 * spacing
x: spacing
CheckBox {
function isDesktopSwitching() {
if (model.ServiceNameRole == "kwin4_effect_slide") {
return true;
} else if (model.ServiceNameRole == "kwin4_effect_fadedesktop") {
return true;
} else if (model.ServiceNameRole == "kwin4_effect_cubeslide") {
return true;
} else {
return false;
RadioButton {
id: exclusiveGroupButton
property bool exclusive: model.ExclusiveRole != ""
visible: exclusive
checked: model.EffectStatusRole
property bool actuallyChanged: true
property bool initiallyChecked: false
exclusiveGroup: exclusive ? effectView.exclusiveGroupForCategory(model.ExclusiveRole) : null
onCheckedChanged: {
if (!visible) {
return;
}
actuallyChanged = true;
item.checked = exclusiveGroupButton.checked
item.changed();
}
onClicked: {
if (!actuallyChanged || initiallyChecked) {
checked = false;
}
actuallyChanged = false;
initiallyChecked = false;
}
Component.onCompleted: {
exclusiveGroupButton.initiallyChecked = model.EffectStatusRole;
}
}
CheckBox {
function isWindowManagementEnabled() {
if (model.ServiceNameRole == "kwin4_effect_dialogparent") {
windowManagementEnabled = effectStatusCheckBox.checked;
@ -72,9 +90,15 @@ Rectangle {
id: effectStatusCheckBox
property bool windowManagementEnabled;
checked: model.EffectStatusRole
exclusiveGroup: isDesktopSwitching() ? desktopSwitching : null
visible: model.ExclusiveRole == ""
onCheckedChanged: item.changed()
onCheckedChanged: {
if (!visible) {
return;
}
item.checked = effectStatusCheckBox.checked;
item.changed();
}
Connections {
target: searchModel
onDataChanged: {

View file

@ -116,7 +116,24 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
ListView {
function exclusiveGroupForCategory(category) {
for (var i = 0; i < effectView.exclusiveGroups.length; ++i) {
var item = effectView.exclusiveGroups[i];
if (item.category == category) {
return item.group;
}
}
var newGroup = Qt.createQmlObject('import QtQuick 2.1; import QtQuick.Controls 1.1; ExclusiveGroup {}',
effectView,
"dynamicExclusiveGroup" + effectView.exclusiveGroups.length);
effectView.exclusiveGroups[effectView.exclusiveGroups.length] = {
'category': category,
'group': newGroup
};
return newGroup;
}
id: effectView
property var exclusiveGroups: []
property color backgroundActiveColor: searchModel.backgroundActiveColor
property color backgroundNormalColor: searchModel.backgroundNormalColor
property color backgroundAlternateColor: searchModel.backgroundAlternateColor
@ -139,12 +156,6 @@ Item {
}
}
ExclusiveGroup {
id: desktopSwitching
//Our ExclusiveGroup must me outside of the
//ListView, otherwise it will not work
}
}//End ColumnLayout
Connections {
target: searchModel