From 7d63ab03bc4c0ae5d1f80c2a40d675595d0e0f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 11 Mar 2014 13:06:18 +0100 Subject: [PATCH] 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 --- kcmkwin/kwincompositing/model.cpp | 52 +++++++++------------- kcmkwin/kwincompositing/model.h | 5 ++- kcmkwin/kwincompositing/qml/Effect.qml | 50 +++++++++++++++------ kcmkwin/kwincompositing/qml/EffectView.qml | 23 +++++++--- 4 files changed, 78 insertions(+), 52 deletions(-) diff --git a/kcmkwin/kwincompositing/model.cpp b/kcmkwin/kwincompositing/model.cpp index 777a5abfc6..20dd0c095b 100644 --- a/kcmkwin/kwincompositing/model.cpp +++ b/kcmkwin/kwincompositing/model.cpp @@ -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 diff --git a/kcmkwin/kwincompositing/model.h b/kcmkwin/kwincompositing/model.h index b03a184b59..04f3e7550d 100644 --- a/kcmkwin/kwincompositing/model.h +++ b/kcmkwin/kwincompositing/model.h @@ -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 m_effectsList; diff --git a/kcmkwin/kwincompositing/qml/Effect.qml b/kcmkwin/kwincompositing/qml/Effect.qml index 75c20512b7..d26464345c 100644 --- a/kcmkwin/kwincompositing/qml/Effect.qml +++ b/kcmkwin/kwincompositing/qml/Effect.qml @@ -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: { diff --git a/kcmkwin/kwincompositing/qml/EffectView.qml b/kcmkwin/kwincompositing/qml/EffectView.qml index 3e1bf5f75f..03a4ef8100 100644 --- a/kcmkwin/kwincompositing/qml/EffectView.qml +++ b/kcmkwin/kwincompositing/qml/EffectView.qml @@ -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