[kcmkwin/compositing] Better check for enabled state of Effects

Introduces partially checked checkboxes for Effects. If an Effect
has an enabled by default function and doesn't have an explicit
value in the config file the checkbox is set to partially. If there
is a value in the config, this will be used.

Clicking on Default will return to the partially checked state.

So far only the built-in effects support the partially checked state
and also the mutual exclusive groups do not support the partial checked
(main reason: RadioButton doesn't support it)

BUG: 336045
REVIEW: 118658
This commit is contained in:
Martin Gräßlin 2014-06-11 08:44:04 +02:00
parent 5efc8e96cf
commit 99b5426a21
4 changed files with 57 additions and 34 deletions

View file

@ -80,6 +80,11 @@ static QString translatedCategory(const QString &category)
return translatedCategories[index];
}
static EffectStatus effectStatus(bool enabled)
{
return enabled ? EffectStatus::Enabled : EffectStatus::Disabled;
}
EffectModel::EffectModel(QObject *parent)
: QAbstractItemModel(parent) {
}
@ -160,7 +165,7 @@ QVariant EffectModel::data(const QModelIndex &index, int role) const
case ServiceNameRole:
return m_effectsList.at(index.row()).serviceName;
case EffectStatusRole:
return m_effectsList.at(index.row()).effectStatus;
return (int)m_effectsList.at(index.row()).effectStatus;
case VideoRole:
return m_effectsList.at(index.row()).video;
case SupportedRole:
@ -188,11 +193,11 @@ bool EffectModel::setData(const QModelIndex& index, const QVariant& value, int r
// gets marked as changed and will get saved to the config file. This means the
// config file could get polluted
EffectData &data = m_effectsList[index.row()];
data.effectStatus = value.toBool();
data.effectStatus = EffectStatus(value.toInt());
data.changed = true;
emit dataChanged(index, index);
if (data.effectStatus && !data.exclusiveGroup.isEmpty()) {
if (data.effectStatus == EffectStatus::Enabled && !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()) {
@ -200,7 +205,7 @@ bool EffectModel::setData(const QModelIndex& index, const QVariant& value, int r
}
EffectData &otherData = m_effectsList[i];
if (otherData.exclusiveGroup == data.exclusiveGroup) {
otherData.effectStatus = false;
otherData.effectStatus = EffectStatus::Disabled;
otherData.changed = true;
emit dataChanged(this->index(i, 0), this->index(i, 0));
}
@ -234,7 +239,15 @@ void EffectModel::loadEffects()
effect.category = translatedCategory(data.category);
effect.serviceName = data.name;
effect.enabledByDefault = data.enabled;
effect.effectStatus = kwinConfig.readEntry(effect.serviceName + "Enabled", effect.enabledByDefault);
effect.enabledByDefaultFunction = (data.enabledFunction != nullptr);
const QString enabledKey = QStringLiteral("%1Enabled").arg(effect.serviceName);
if (kwinConfig.hasKey(enabledKey)) {
effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", effect.enabledByDefault));
} else if (data.enabledFunction != nullptr) {
effect.effectStatus = EffectStatus::EnabledUndeterminded;
} else {
effect.effectStatus = effectStatus(effect.enabledByDefault);
}
effect.video = data.video;
effect.supported = true;
effect.exclusiveGroup = data.exclusiveCategory;
@ -262,8 +275,9 @@ void EffectModel::loadEffects()
effect.version = plugin.version();
effect.category = translatedCategory(plugin.category());
effect.serviceName = plugin.pluginName();
effect.effectStatus = kwinConfig.readEntry(effect.serviceName + "Enabled", plugin.isPluginEnabledByDefault());
effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", plugin.isPluginEnabledByDefault()));
effect.enabledByDefault = plugin.isPluginEnabledByDefault();
effect.enabledByDefaultFunction = false;
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();
@ -350,7 +364,7 @@ void EffectModel::syncEffectsToKWin()
QDBusConnection::sessionBus());
for (int it = 0; it < m_effectsList.size(); it++) {
if (m_effectsList.at(it).effectStatus != m_effectsChanged.at(it).effectStatus) {
if (m_effectsList.at(it).effectStatus) {
if (m_effectsList.at(it).effectStatus != EffectStatus::Disabled) {
interface.loadEffect(m_effectsList.at(it).serviceName);
} else {
interface.unloadEffect(m_effectsList.at(it).serviceName);
@ -361,9 +375,9 @@ void EffectModel::syncEffectsToKWin()
m_effectsChanged = m_effectsList;
}
void EffectModel::updateEffectStatus(const QModelIndex &rowIndex, bool effectState)
void EffectModel::updateEffectStatus(const QModelIndex &rowIndex, EffectStatus effectState)
{
setData(rowIndex, effectState, EffectModel::EffectStatusRole);
setData(rowIndex, (int)effectState, EffectModel::EffectStatusRole);
}
void EffectModel::syncConfig()
@ -378,11 +392,14 @@ void EffectModel::syncConfig()
effect.changed = false;
const QString key = effect.serviceName + QStringLiteral("Enabled");
if (effect.effectStatus != effect.enabledByDefault) {
kwinConfig.writeEntry(key, effect.effectStatus);
} else {
const bool shouldEnable = (effect.effectStatus != EffectStatus::Disabled);
const bool restoreToDefault = effect.enabledByDefaultFunction
? effect.effectStatus == EffectStatus::EnabledUndeterminded
: shouldEnable == effect.enabledByDefault;
if (restoreToDefault) {
kwinConfig.deleteEntry(key);
} else {
kwinConfig.writeEntry(key, shouldEnable);
}
}
@ -394,8 +411,10 @@ void EffectModel::defaults()
{
for (int i = 0; i < m_effectsList.count(); ++i) {
const auto &effect = m_effectsList.at(i);
if (effect.effectStatus != effect.enabledByDefault) {
updateEffectStatus(index(i, 0), effect.enabledByDefault);
if (effect.enabledByDefaultFunction && effect.effectStatus != EffectStatus::EnabledUndeterminded) {
updateEffectStatus(index(i, 0), EffectStatus::EnabledUndeterminded);
} else if ((bool)effect.effectStatus != effect.enabledByDefault) {
updateEffectStatus(index(i, 0), effect.enabledByDefault ? EffectStatus::Enabled : EffectStatus::Disabled);
}
}
}
@ -472,11 +491,11 @@ bool EffectFilterModel::filterAcceptsRow(int source_row, const QModelIndex &sour
return false;
}
void EffectFilterModel::updateEffectStatus(int rowIndex, bool effectState)
void EffectFilterModel::updateEffectStatus(int rowIndex, int effectState)
{
const QModelIndex sourceIndex = mapToSource(index(rowIndex, 0));
m_effectModel->updateEffectStatus(sourceIndex, effectState);
m_effectModel->updateEffectStatus(sourceIndex, EffectStatus(effectState));
}
void EffectFilterModel::syncConfig()

View file

@ -34,6 +34,12 @@
namespace KWin {
namespace Compositing {
enum class EffectStatus {
Disabled = Qt::Unchecked,
EnabledUndeterminded = Qt::PartiallyChecked,
Enabled = Qt::Checked
};
struct EffectData {
QString name;
QString description;
@ -43,8 +49,9 @@ struct EffectData {
QString version;
QString category;
QString serviceName;
bool effectStatus;
EffectStatus effectStatus;
bool enabledByDefault;
bool enabledByDefaultFunction;
QUrl video;
bool supported;
QString exclusiveGroup;
@ -90,7 +97,7 @@ public:
virtual QHash< int, QByteArray > roleNames() const override;
void updateEffectStatus(const QModelIndex &rowIndex, bool effectState);
void updateEffectStatus(const QModelIndex &rowIndex, EffectStatus effectState);
void syncEffectsToKWin();
void syncConfig();
void loadEffects();
@ -153,7 +160,7 @@ public:
EffectFilterModel(QObject *parent = 0);
const QString &filter() const;
Q_INVOKABLE void updateEffectStatus(int rowIndex, bool effectState);
Q_INVOKABLE void updateEffectStatus(int rowIndex, int effectState);
Q_INVOKABLE void syncConfig();
Q_INVOKABLE void load();

View file

@ -30,7 +30,7 @@ Rectangle {
height: rowEffect.implicitHeight
color: item.ListView.isCurrentItem ? effectView.backgroundActiveColor : index % 2 ? effectView.backgroundNormalColor : effectView.backgroundAlternateColor
signal changed()
property bool checked: model.EffectStatusRole
property int checkedState: model.EffectStatusRole
MouseArea {
anchors.fill: parent
@ -61,7 +61,7 @@ Rectangle {
return;
}
actuallyChanged = true;
item.checked = exclusiveGroupButton.checked
item.checkedState = exclusiveGroupButton.checked ? Qt.Checked : Qt.UnChecked
item.changed();
}
onClicked: {
@ -78,20 +78,20 @@ Rectangle {
CheckBox {
id: effectStatusCheckBox
checked: model.EffectStatusRole
checkedState: model.EffectStatusRole
visible: model.ExclusiveRole == ""
onCheckedChanged: {
onCheckedStateChanged: {
if (!visible) {
return;
}
item.checked = effectStatusCheckBox.checked;
item.checkedState = effectStatusCheckBox.checkedState;
item.changed();
}
Connections {
target: searchModel
onDataChanged: {
effectStatusCheckBox.checked = model.EffectStatusRole;
effectStatusCheckBox.checkedState = model.EffectStatusRole;
}
}
}
@ -140,7 +140,7 @@ Rectangle {
Button {
id: configureButton
visible: ConfigurableRole
enabled: effectStatusCheckBox.checked
enabled: item.checkedState != Qt.UnChecked
iconName: "configure"
onClicked: {
effectConfig.openConfig(model.ServiceNameRole, model.ScriptedRole, model.NameRole);

View file

@ -111,11 +111,6 @@ Item {
id: searchModel
objectName: "filterModel"
filter: searchField.text
signal effectState(int rowIndex, bool enabled)
onEffectState: {
searchModel.updateEffectStatus(rowIndex, enabled);
}
}
ScrollView {
@ -157,7 +152,9 @@ Item {
Connections {
id: effectStateConnection
target: null
onChanged: searchModel.effectState(index, checked)
onChanged: {
searchModel.updateEffectStatus(index, checkedState);
}
}
Component.onCompleted: {
effectStateConnection.target = effectDelegate
@ -174,6 +171,6 @@ Item {
}//End ColumnLayout
Connections {
target: searchModel
onEffectState: changed()
onDataChanged: changed()
}
}//End item