[kcmkwin] Split out Desktop Effects KCM
Summary: A while ago desktop effects and compositing settings lived under the same roof {F6584639} {F6584642} but time has passed and now those two have their own kcms. This causes some issues: * for newcomers it's harder to find code of the Desktop Effects KCM; * git history doesn't look good, e.g. "[kcmkwin/compositing] Add some bugs to Desktop Effects KCM to fix later"; * in general, the mix of two doesn't look good in the code. This change splits out the Desktop Effects KCM. Unfortunately, in order to have more nicer code I had to refactor EffectModel a little bit. Before: {F6584669} After: {F6587570} Reviewers: #kwin, davidedmundson Reviewed By: #kwin, davidedmundson Subscribers: davidedmundson, ltoscano, mart, ngraham, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D18703
This commit is contained in:
parent
5eb28512dd
commit
2211a951e7
28 changed files with 893 additions and 1779 deletions
|
@ -8,6 +8,7 @@ add_subdirectory( kwinrules )
|
|||
add_subdirectory( kwinscreenedges )
|
||||
add_subdirectory( kwinscripts )
|
||||
add_subdirectory( kwindesktop )
|
||||
add_subdirectory( kwineffects )
|
||||
|
||||
if( KWIN_BUILD_TABBOX )
|
||||
add_subdirectory( kwintabbox )
|
||||
|
|
|
@ -199,7 +199,7 @@ bool EffectModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||
// config file could get polluted
|
||||
EffectData &data = m_effectsList[index.row()];
|
||||
data.effectStatus = Status(value.toInt());
|
||||
data.changed = true;
|
||||
data.changed = data.effectStatus != data.originalStatus;
|
||||
emit dataChanged(index, index);
|
||||
|
||||
if (data.effectStatus == Status::Enabled && !data.exclusiveGroup.isEmpty()) {
|
||||
|
@ -211,7 +211,7 @@ bool EffectModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||
EffectData &otherData = m_effectsList[i];
|
||||
if (otherData.exclusiveGroup == data.exclusiveGroup) {
|
||||
otherData.effectStatus = Status::Disabled;
|
||||
otherData.changed = true;
|
||||
otherData.changed = otherData.effectStatus != otherData.originalStatus;
|
||||
emit dataChanged(this->index(i, 0), this->index(i, 0));
|
||||
}
|
||||
}
|
||||
|
@ -249,6 +249,7 @@ void EffectModel::loadBuiltInEffects(const KConfigGroup &kwinConfig, const KPlug
|
|||
} else {
|
||||
effect.effectStatus = effectStatus(effect.enabledByDefault);
|
||||
}
|
||||
effect.originalStatus = effect.effectStatus;
|
||||
effect.video = data.video;
|
||||
effect.website = QUrl();
|
||||
effect.supported = true;
|
||||
|
@ -289,6 +290,7 @@ void EffectModel::loadJavascriptEffects(const KConfigGroup &kwinConfig)
|
|||
effect.serviceName = plugin.pluginName();
|
||||
effect.iconName = plugin.icon();
|
||||
effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", plugin.isPluginEnabledByDefault()));
|
||||
effect.originalStatus = effect.effectStatus;
|
||||
effect.enabledByDefault = plugin.isPluginEnabledByDefault();
|
||||
effect.enabledByDefaultFunction = false;
|
||||
effect.video = plugin.property(QStringLiteral("X-KWin-Video-Url")).toUrl();
|
||||
|
@ -366,6 +368,8 @@ void EffectModel::loadPluginEffects(const KConfigGroup &kwinConfig, const KPlugi
|
|||
effect.effectStatus = effectStatus(effect.enabledByDefault);
|
||||
}
|
||||
|
||||
effect.originalStatus = effect.effectStatus;
|
||||
|
||||
effect.configurable = std::any_of(configs.constBegin(), configs.constEnd(),
|
||||
[pluginEffect](const KPluginInfo &info) {
|
||||
return info.property(QStringLiteral("X-KDE-ParentComponents")).toString() == pluginEffect.pluginId();
|
||||
|
@ -430,7 +434,7 @@ void EffectModel::load()
|
|||
if (it != m_effectsList.end()) {
|
||||
if ((*it).supported != supportedValue) {
|
||||
(*it).supported = supportedValue;
|
||||
QModelIndex i = index(findRowByServiceName(effectName), 0);
|
||||
QModelIndex i = findByPluginId(effectName);
|
||||
if (i.isValid()) {
|
||||
emit dataChanged(i, i, QVector<int>() << SupportedRole);
|
||||
}
|
||||
|
@ -446,16 +450,6 @@ void EffectModel::load()
|
|||
endResetModel();
|
||||
}
|
||||
|
||||
int EffectModel::findRowByServiceName(const QString &serviceName)
|
||||
{
|
||||
for (int it = 0; it < m_effectsList.size(); it++) {
|
||||
if (m_effectsList.at(it).serviceName == serviceName) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void EffectModel::syncEffectsToKWin()
|
||||
{
|
||||
OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"),
|
||||
|
@ -490,6 +484,7 @@ void EffectModel::save()
|
|||
continue;
|
||||
}
|
||||
effect.changed = false;
|
||||
effect.originalStatus = effect.effectStatus;
|
||||
|
||||
const QString key = effect.serviceName + QStringLiteral("Enabled");
|
||||
const bool shouldEnable = (effect.effectStatus != Status::Disabled);
|
||||
|
@ -519,6 +514,28 @@ void EffectModel::defaults()
|
|||
}
|
||||
}
|
||||
|
||||
bool EffectModel::needsSave() const
|
||||
{
|
||||
return std::any_of(m_effectsList.constBegin(), m_effectsList.constEnd(),
|
||||
[](const EffectData &data) {
|
||||
return data.changed;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
QModelIndex EffectModel::findByPluginId(const QString &pluginId) const
|
||||
{
|
||||
auto it = std::find_if(m_effectsList.constBegin(), m_effectsList.constEnd(),
|
||||
[pluginId](const EffectData &data) {
|
||||
return data.serviceName == pluginId;
|
||||
}
|
||||
);
|
||||
if (it == m_effectsList.constEnd()) {
|
||||
return {};
|
||||
}
|
||||
return index(std::distance(m_effectsList.constBegin(), it), 0);
|
||||
}
|
||||
|
||||
bool EffectModel::shouldStore(const EffectData &data) const
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
|
|
|
@ -177,6 +177,16 @@ public:
|
|||
**/
|
||||
void defaults();
|
||||
|
||||
/**
|
||||
* Whether the model has unsaved changes.
|
||||
**/
|
||||
bool needsSave() const;
|
||||
|
||||
/**
|
||||
* Finds an effect with the given plugin id.
|
||||
**/
|
||||
QModelIndex findByPluginId(const QString &pluginId) const;
|
||||
|
||||
protected:
|
||||
enum class Kind {
|
||||
BuiltIn,
|
||||
|
@ -196,6 +206,7 @@ protected:
|
|||
QString serviceName;
|
||||
QString iconName;
|
||||
Status effectStatus;
|
||||
Status originalStatus;
|
||||
bool enabledByDefault;
|
||||
bool enabledByDefaultFunction;
|
||||
QUrl video;
|
||||
|
@ -220,7 +231,6 @@ private:
|
|||
void loadBuiltInEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs);
|
||||
void loadJavascriptEffects(const KConfigGroup &kwinConfig);
|
||||
void loadPluginEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs);
|
||||
int findRowByServiceName(const QString &serviceName);
|
||||
void syncEffectsToKWin();
|
||||
|
||||
QVector<EffectData> m_effectsList;
|
||||
|
|
|
@ -6,76 +6,28 @@ add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
|
|||
|
||||
remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY)
|
||||
|
||||
include_directories(${KWIN_SOURCE_DIR}/effects)
|
||||
|
||||
################# configure checks and create the configured files #################
|
||||
|
||||
set(kwincomposing_SRC
|
||||
model.cpp
|
||||
main.cpp
|
||||
compositing.cpp
|
||||
effectconfig.cpp)
|
||||
compositing.cpp)
|
||||
|
||||
qt5_add_dbus_interface( kwincomposing_SRC
|
||||
${KWIN_SOURCE_DIR}/org.kde.kwin.Compositing.xml kwin_compositing_interface)
|
||||
qt5_add_dbus_interface( kwincomposing_SRC
|
||||
${KWIN_SOURCE_DIR}/org.kde.kwin.Effects.xml kwin_effects_interface)
|
||||
|
||||
ki18n_wrap_ui(kwincomposing_SRC compositing.ui)
|
||||
|
||||
add_library(kwincompositing MODULE ${kwincomposing_SRC})
|
||||
|
||||
target_link_libraries(kwincompositing
|
||||
Qt5::Quick
|
||||
Qt5::QuickWidgets
|
||||
Qt5::DBus
|
||||
Qt5::Widgets
|
||||
KF5::CoreAddons
|
||||
KF5::ConfigCore
|
||||
KF5::Declarative
|
||||
KF5::I18n
|
||||
KF5::KCMUtils
|
||||
KF5::NewStuff
|
||||
kcmkwincommon
|
||||
)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
include(ECMMarkAsTest)
|
||||
|
||||
set(modelTest_SRC
|
||||
model.cpp
|
||||
effectconfig.cpp
|
||||
compositing.cpp
|
||||
test/effectmodeltest.cpp
|
||||
test/modeltest.cpp)
|
||||
|
||||
qt5_add_dbus_interface(modelTest_SRC
|
||||
${KWIN_SOURCE_DIR}/org.kde.kwin.Compositing.xml kwin_compositing_interface)
|
||||
qt5_add_dbus_interface(modelTest_SRC
|
||||
${KWIN_SOURCE_DIR}/org.kde.kwin.Effects.xml kwin_effects_interface)
|
||||
|
||||
add_executable(effectModelTest ${modelTest_SRC})
|
||||
ecm_mark_as_test(effectModelTest)
|
||||
|
||||
target_link_libraries(effectModelTest
|
||||
Qt5::Quick
|
||||
Qt5::QuickWidgets
|
||||
Qt5::DBus
|
||||
Qt5::Test
|
||||
Qt5::Widgets
|
||||
KF5::CoreAddons
|
||||
KF5::ConfigCore
|
||||
KF5::Declarative
|
||||
KF5::I18n
|
||||
KF5::KCMUtils
|
||||
KF5::NewStuff
|
||||
kwineffects
|
||||
kcmkwincommon
|
||||
)
|
||||
endif()
|
||||
|
||||
INSTALL(DIRECTORY qml DESTINATION ${DATA_INSTALL_DIR}/kwincompositing)
|
||||
INSTALL(TARGETS kwincompositing DESTINATION ${PLUGIN_INSTALL_DIR})
|
||||
install(FILES kwincompositing.desktop kcmkwineffects.desktop DESTINATION ${SERVICES_INSTALL_DIR})
|
||||
install(FILES kwineffect.knsrc DESTINATION ${CONFIG_INSTALL_DIR})
|
||||
install(FILES kwincompositing.desktop DESTINATION ${SERVICES_INSTALL_DIR})
|
||||
################# list the subdirectories #################
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
#include "effectconfig.h"
|
||||
|
||||
#include <KCModule>
|
||||
#include <KPluginTrader>
|
||||
|
||||
#include <KNS3/DownloadDialog>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
|
||||
static const QString s_pluginDir = QStringLiteral("kwin/effects/configs/");
|
||||
|
||||
namespace KWin {
|
||||
namespace Compositing {
|
||||
|
||||
EffectConfig::EffectConfig(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void EffectConfig::openConfig(const QString &serviceName, bool scripted, const QString &title)
|
||||
{
|
||||
//setup the UI
|
||||
QDialog dialog;
|
||||
dialog.setWindowTitle(title);
|
||||
|
||||
// create the KCModule through the plugintrader
|
||||
KCModule *kcm = nullptr;
|
||||
if (scripted) {
|
||||
// try generic module for scripted
|
||||
const auto offers = KPluginTrader::self()->query(s_pluginDir, QString(),
|
||||
QStringLiteral("[X-KDE-Library] == 'kcm_kwin4_genericscripted'"));
|
||||
if (!offers.isEmpty()) {
|
||||
const KPluginInfo &generic = offers.first();
|
||||
KPluginLoader loader(generic.libraryPath());
|
||||
KPluginFactory *factory = loader.factory();
|
||||
if (factory) {
|
||||
kcm = factory->create<KCModule>(serviceName, &dialog);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
kcm = KPluginTrader::createInstanceFromQuery<KCModule>(s_pluginDir, QString(),
|
||||
QStringLiteral("'%1' in [X-KDE-ParentComponents]").arg(serviceName),
|
||||
&dialog);
|
||||
}
|
||||
if (!kcm) {
|
||||
return;
|
||||
}
|
||||
|
||||
connect(&dialog, &QDialog::accepted, kcm, &KCModule::save);
|
||||
|
||||
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel |
|
||||
QDialogButtonBox::Apply |
|
||||
QDialogButtonBox::RestoreDefaults |
|
||||
QDialogButtonBox::Reset,
|
||||
&dialog);
|
||||
|
||||
QPushButton *apply = buttons->button(QDialogButtonBox::Apply);
|
||||
QPushButton *reset = buttons->button(QDialogButtonBox::Reset);
|
||||
apply->setEnabled(false);
|
||||
reset->setEnabled(false);
|
||||
//Here we connect our buttons with the dialog
|
||||
connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||
connect(apply, &QPushButton::clicked, kcm, &KCModule::save);
|
||||
connect(reset, &QPushButton::clicked, kcm, &KCModule::load);
|
||||
auto changedSignal = static_cast<void(KCModule::*)(bool)>(&KCModule::changed);
|
||||
connect(kcm, changedSignal, apply, &QPushButton::setEnabled);
|
||||
connect(kcm, changedSignal, reset, &QPushButton::setEnabled);
|
||||
connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, kcm, &KCModule::defaults);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(&dialog);
|
||||
layout->addWidget(kcm);
|
||||
layout->addWidget(buttons);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void EffectConfig::openGHNS()
|
||||
{
|
||||
QPointer<KNS3::DownloadDialog> downloadDialog = new KNS3::DownloadDialog(QStringLiteral("kwineffect.knsrc"));
|
||||
if (downloadDialog->exec() == QDialog::Accepted) {
|
||||
emit effectListChanged();
|
||||
}
|
||||
|
||||
delete downloadDialog;
|
||||
}
|
||||
|
||||
}//end namespace Compositing
|
||||
}//end namespace KWin
|
|
@ -1,47 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#ifndef EFFECTCONFIG_H
|
||||
#define EFFECTCONFIG_H
|
||||
#include <QObject>
|
||||
class QString;
|
||||
|
||||
namespace KWin {
|
||||
namespace Compositing {
|
||||
|
||||
class EffectConfig : public QObject
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EffectConfig(QObject *parent = 0);
|
||||
QString serviceName(const QString &serviceName);
|
||||
|
||||
Q_INVOKABLE void openConfig(const QString &effectName, bool scripted, const QString &title);
|
||||
Q_INVOKABLE void openGHNS();
|
||||
|
||||
Q_SIGNALS:
|
||||
void effectListChanged();
|
||||
};
|
||||
}//end namespace Compositing
|
||||
}//end namespace KWin
|
||||
#endif
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
|
||||
#include "compositing.h"
|
||||
#include "model.h"
|
||||
#include "ui_compositing.h"
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
|
@ -30,33 +29,6 @@
|
|||
#include <kcmodule.h>
|
||||
#include <kservice.h>
|
||||
|
||||
class KWinCompositingKCM : public KCModule
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~KWinCompositingKCM();
|
||||
|
||||
public Q_SLOTS:
|
||||
void save() override;
|
||||
void load() override;
|
||||
void defaults() override;
|
||||
|
||||
protected:
|
||||
explicit KWinCompositingKCM(QWidget* parent, const QVariantList& args,
|
||||
KWin::Compositing::EffectView::ViewType viewType);
|
||||
|
||||
private:
|
||||
QScopedPointer<KWin::Compositing::EffectView> m_view;
|
||||
};
|
||||
|
||||
class KWinDesktopEffects : public KWinCompositingKCM
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KWinDesktopEffects(QWidget* parent = 0, const QVariantList& args = QVariantList())
|
||||
: KWinCompositingKCM(parent, args, KWin::Compositing::EffectView::DesktopEffectsView) {}
|
||||
};
|
||||
|
||||
class KWinCompositingSettings : public KCModule
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -230,44 +202,7 @@ void KWinCompositingSettings::save()
|
|||
m_compositing->save();
|
||||
}
|
||||
|
||||
KWinCompositingKCM::KWinCompositingKCM(QWidget* parent, const QVariantList& args, KWin::Compositing::EffectView::ViewType viewType)
|
||||
: KCModule(parent, args)
|
||||
, m_view(new KWin::Compositing::EffectView(viewType))
|
||||
{
|
||||
QVBoxLayout *vl = new QVBoxLayout(this);
|
||||
|
||||
vl->addWidget(m_view.data());
|
||||
setLayout(vl);
|
||||
connect(m_view.data(), &KWin::Compositing::EffectView::changed, [this]{
|
||||
emit changed(true);
|
||||
});
|
||||
m_view->setFocusPolicy(Qt::StrongFocus);
|
||||
}
|
||||
|
||||
KWinCompositingKCM::~KWinCompositingKCM()
|
||||
{
|
||||
}
|
||||
|
||||
void KWinCompositingKCM::save()
|
||||
{
|
||||
m_view->save();
|
||||
KCModule::save();
|
||||
}
|
||||
|
||||
void KWinCompositingKCM::load()
|
||||
{
|
||||
m_view->load();
|
||||
KCModule::load();
|
||||
}
|
||||
|
||||
void KWinCompositingKCM::defaults()
|
||||
{
|
||||
m_view->defaults();
|
||||
KCModule::defaults();
|
||||
}
|
||||
|
||||
K_PLUGIN_FACTORY(KWinCompositingConfigFactory,
|
||||
registerPlugin<KWinDesktopEffects>("effects");
|
||||
registerPlugin<KWinCompositingSettings>("compositing");
|
||||
)
|
||||
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
#include "model.h"
|
||||
#include "effectconfig.h"
|
||||
#include "effectmodel.h"
|
||||
#include "compositing.h"
|
||||
#include <config-kwin.h>
|
||||
#include <kwin_effects_interface.h>
|
||||
#include <effect_builtins.h>
|
||||
|
||||
#include <KSharedConfig>
|
||||
#include <KCModuleProxy>
|
||||
#include <kdeclarative/kdeclarative.h>
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QQmlEngine>
|
||||
#include <QtQml>
|
||||
#include <QQuickItem>
|
||||
#include <QDebug>
|
||||
|
||||
namespace KWin {
|
||||
namespace Compositing {
|
||||
|
||||
EffectFilterModel::EffectFilterModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
, m_effectModel(new EffectModel(this))
|
||||
, m_filterOutUnsupported(true)
|
||||
, m_filterOutInternal(true)
|
||||
{
|
||||
setSourceModel(m_effectModel);
|
||||
connect(this, &EffectFilterModel::filterOutUnsupportedChanged, this, &EffectFilterModel::invalidateFilter);
|
||||
connect(this, &EffectFilterModel::filterOutInternalChanged, this, &EffectFilterModel::invalidateFilter);
|
||||
}
|
||||
|
||||
const QString &EffectFilterModel::filter() const
|
||||
{
|
||||
return m_filter;
|
||||
}
|
||||
|
||||
void EffectFilterModel::setFilter(const QString &filter)
|
||||
{
|
||||
if (filter == m_filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_filter = filter;
|
||||
emit filterChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
bool EffectFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
if (!m_effectModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex index = m_effectModel->index(source_row, 0, source_parent);
|
||||
if (!index.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_filterOutUnsupported) {
|
||||
if (!index.data(EffectModel::SupportedRole).toBool()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_filterOutInternal) {
|
||||
if (index.data(EffectModel::InternalRole).toBool()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_filter.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant data = index.data();
|
||||
if (!data.isValid()) {
|
||||
//An invalid QVariant is valid data
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_effectModel->data(index, EffectModel::NameRole).toString().contains(m_filter, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
} else if (m_effectModel->data(index, EffectModel::DescriptionRole).toString().contains(m_filter, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
if (index.data(EffectModel::CategoryRole).toString().contains(m_filter, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EffectFilterModel::updateEffectStatus(int rowIndex, int effectState)
|
||||
{
|
||||
const QModelIndex sourceIndex = mapToSource(index(rowIndex, 0));
|
||||
|
||||
m_effectModel->updateEffectStatus(sourceIndex, EffectModel::Status(effectState));
|
||||
}
|
||||
|
||||
void EffectFilterModel::syncConfig()
|
||||
{
|
||||
m_effectModel->save();
|
||||
}
|
||||
|
||||
void EffectFilterModel::load()
|
||||
{
|
||||
m_effectModel->load();
|
||||
}
|
||||
|
||||
void EffectFilterModel::defaults()
|
||||
{
|
||||
m_effectModel->defaults();
|
||||
}
|
||||
|
||||
EffectView::EffectView(ViewType type, QWidget *parent)
|
||||
: QQuickWidget(parent)
|
||||
{
|
||||
qRegisterMetaType<OpenGLPlatformInterfaceModel*>();
|
||||
qmlRegisterType<EffectConfig>("org.kde.kwin.kwincompositing", 1, 0, "EffectConfig");
|
||||
qmlRegisterType<EffectFilterModel>("org.kde.kwin.kwincompositing", 1, 0, "EffectFilterModel");
|
||||
qmlRegisterType<Compositing>("org.kde.kwin.kwincompositing", 1, 0, "Compositing");
|
||||
qmlRegisterType<CompositingType>("org.kde.kwin.kwincompositing", 1, 0, "CompositingType");
|
||||
init(type);
|
||||
}
|
||||
|
||||
void EffectView::init(ViewType type)
|
||||
{
|
||||
KDeclarative::KDeclarative kdeclarative;
|
||||
kdeclarative.setDeclarativeEngine(engine());
|
||||
kdeclarative.setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN));
|
||||
kdeclarative.setupContext();
|
||||
kdeclarative.setupEngine(engine());
|
||||
QString path;
|
||||
switch (type) {
|
||||
case CompositingSettingsView:
|
||||
path = QStringLiteral("kwincompositing/qml/main-compositing.qml");
|
||||
break;
|
||||
case DesktopEffectsView:
|
||||
path = QStringLiteral("kwincompositing/qml/main.qml");
|
||||
break;
|
||||
}
|
||||
QString mainFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, path, QStandardPaths::LocateFile);
|
||||
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
setSource(QUrl(mainFile));
|
||||
rootObject()->setProperty("color",
|
||||
KColorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(0)).background(KColorScheme::NormalBackground).color());
|
||||
connect(rootObject(), SIGNAL(changed()), this, SIGNAL(changed()));
|
||||
setMinimumSize(initialSize());
|
||||
connect(rootObject(), SIGNAL(implicitWidthChanged()), this, SLOT(slotImplicitSizeChanged()));
|
||||
connect(rootObject(), SIGNAL(implicitHeightChanged()), this, SLOT(slotImplicitSizeChanged()));
|
||||
}
|
||||
|
||||
void EffectView::save()
|
||||
{
|
||||
if (auto *model = rootObject()->findChild<EffectFilterModel*>(QStringLiteral("filterModel"))) {
|
||||
model->syncConfig();
|
||||
}
|
||||
if (auto *compositing = rootObject()->findChild<Compositing*>(QStringLiteral("compositing"))) {
|
||||
compositing->save();
|
||||
}
|
||||
}
|
||||
|
||||
void EffectView::load()
|
||||
{
|
||||
if (auto *model = rootObject()->findChild<EffectFilterModel*>(QStringLiteral("filterModel"))) {
|
||||
model->load();
|
||||
}
|
||||
if (auto *compositing = rootObject()->findChild<Compositing*>(QStringLiteral("compositing"))) {
|
||||
compositing->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void EffectView::defaults()
|
||||
{
|
||||
if (auto *model = rootObject()->findChild<EffectFilterModel*>(QStringLiteral("filterModel"))) {
|
||||
model->defaults();
|
||||
}
|
||||
if (auto *compositing = rootObject()->findChild<Compositing*>(QStringLiteral("compositing"))) {
|
||||
compositing->defaults();
|
||||
}
|
||||
}
|
||||
|
||||
void EffectView::slotImplicitSizeChanged()
|
||||
{
|
||||
setMinimumSize(QSize(rootObject()->property("implicitWidth").toInt(),
|
||||
rootObject()->property("implicitHeight").toInt()));
|
||||
}
|
||||
|
||||
}//end namespace Compositing
|
||||
}//end namespace KWin
|
|
@ -1,121 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#ifndef MODEL_H
|
||||
#define MODEL_H
|
||||
|
||||
#include <KColorScheme>
|
||||
#include <KSharedConfig>
|
||||
#include <QQuickWidget>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QString>
|
||||
|
||||
namespace KWin {
|
||||
|
||||
class EffectModel;
|
||||
|
||||
namespace Compositing {
|
||||
|
||||
class EffectView : public QQuickWidget
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ViewType {
|
||||
DesktopEffectsView,
|
||||
CompositingSettingsView
|
||||
};
|
||||
EffectView(ViewType type, QWidget *parent = 0);
|
||||
|
||||
void save();
|
||||
void load();
|
||||
void defaults();
|
||||
|
||||
Q_SIGNALS:
|
||||
void changed();
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotImplicitSizeChanged();
|
||||
private:
|
||||
void init(ViewType type);
|
||||
};
|
||||
|
||||
|
||||
class EffectFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
|
||||
/**
|
||||
* If @c true not supported effects are excluded, if @c false no restriction on supported.
|
||||
* Default value is @c true.
|
||||
**/
|
||||
Q_PROPERTY(bool filterOutUnsupported MEMBER m_filterOutUnsupported NOTIFY filterOutUnsupportedChanged)
|
||||
/**
|
||||
* If @c true internal effects are excluded, if @c false no restriction on internal.
|
||||
* Default value is @c true.
|
||||
**/
|
||||
Q_PROPERTY(bool filterOutInternal MEMBER m_filterOutInternal NOTIFY filterOutInternalChanged)
|
||||
Q_PROPERTY(QColor backgroundActiveColor READ backgroundActiveColor CONSTANT)
|
||||
Q_PROPERTY(QColor backgroundNormalColor READ backgroundNormalColor CONSTANT)
|
||||
Q_PROPERTY(QColor backgroundAlternateColor READ backgroundAlternateColor CONSTANT)
|
||||
Q_PROPERTY(QColor sectionColor READ sectionColor CONSTANT)
|
||||
public:
|
||||
EffectFilterModel(QObject *parent = 0);
|
||||
const QString &filter() const;
|
||||
|
||||
Q_INVOKABLE void updateEffectStatus(int rowIndex, int effectState);
|
||||
Q_INVOKABLE void syncConfig();
|
||||
Q_INVOKABLE void load();
|
||||
|
||||
QColor backgroundActiveColor() { return KColorScheme(QPalette::Active, KColorScheme::Selection, KSharedConfigPtr(0)).background(KColorScheme::LinkBackground).color(); };
|
||||
QColor backgroundNormalColor() { return KColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)).background(KColorScheme::NormalBackground).color(); };
|
||||
QColor backgroundAlternateColor() { return KColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)).background(KColorScheme::AlternateBackground).color(); };
|
||||
|
||||
QColor sectionColor() const {
|
||||
QColor color = KColorScheme(QPalette::Active).foreground().color();
|
||||
color.setAlphaF(0.6);
|
||||
return color;
|
||||
}
|
||||
|
||||
void defaults();
|
||||
|
||||
public Q_SLOTS:
|
||||
void setFilter(const QString &filter);
|
||||
|
||||
protected:
|
||||
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void effectModelChanged();
|
||||
void filterChanged();
|
||||
void filterOutUnsupportedChanged();
|
||||
void filterOutInternalChanged();
|
||||
|
||||
private:
|
||||
EffectModel *m_effectModel;
|
||||
QString m_filter;
|
||||
bool m_filterOutUnsupported;
|
||||
bool m_filterOutInternal;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,163 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls 2.0 as QQC2
|
||||
import QtQuick.Layouts 1.0
|
||||
import org.kde.kwin.kwincompositing 1.0
|
||||
|
||||
Rectangle {
|
||||
id: item
|
||||
width: parent.width
|
||||
height: rowEffect.implicitHeight
|
||||
color: index % 2 ? effectView.backgroundNormalColor : effectView.backgroundAlternateColor
|
||||
signal changed()
|
||||
property int checkedState: model.EffectStatusRole
|
||||
|
||||
RowLayout {
|
||||
id: rowEffect
|
||||
property int maximumWidth: parent.width - 2 * spacing
|
||||
width: maximumWidth
|
||||
Layout.maximumWidth: maximumWidth
|
||||
x: spacing
|
||||
|
||||
RowLayout {
|
||||
id: checkBoxLayout
|
||||
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.checkedState = exclusiveGroupButton.checked ? Qt.Checked : Qt.Unchecked
|
||||
item.changed();
|
||||
}
|
||||
onClicked: {
|
||||
if (!actuallyChanged || initiallyChecked) {
|
||||
checked = false;
|
||||
}
|
||||
actuallyChanged = false;
|
||||
initiallyChecked = false;
|
||||
}
|
||||
Component.onCompleted: {
|
||||
exclusiveGroupButton.initiallyChecked = model.EffectStatusRole;
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: effectStatusCheckBox
|
||||
checkedState: model.EffectStatusRole
|
||||
visible: model.ExclusiveRole == ""
|
||||
|
||||
onCheckedStateChanged: {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
item.checkedState = effectStatusCheckBox.checkedState;
|
||||
item.changed();
|
||||
}
|
||||
Connections {
|
||||
target: searchModel
|
||||
onDataChanged: {
|
||||
effectStatusCheckBox.checkedState = model.EffectStatusRole;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: effectItem
|
||||
property int maximumWidth: parent.maximumWidth - checkBoxLayout.width - (videoButton.width + configureButton.width + aboutButton.width) - parent.spacing * 5
|
||||
Layout.maximumWidth: maximumWidth
|
||||
QQC2.Label {
|
||||
text: model.NameRole
|
||||
font.weight: Font.Bold
|
||||
wrapMode: Text.Wrap
|
||||
Layout.maximumWidth: parent.maximumWidth
|
||||
}
|
||||
QQC2.Label {
|
||||
id: desc
|
||||
text: model.DescriptionRole
|
||||
wrapMode: Text.Wrap
|
||||
Layout.maximumWidth: parent.maximumWidth
|
||||
}
|
||||
QQC2.Label {
|
||||
id:aboutItem
|
||||
text: i18n("Author: %1\nLicense: %2", model.AuthorNameRole, model.LicenseRole)
|
||||
font.weight: Font.Bold
|
||||
visible: false
|
||||
wrapMode: Text.Wrap
|
||||
Layout.maximumWidth: parent.maximumWidth
|
||||
}
|
||||
Loader {
|
||||
id: videoItem
|
||||
active: false
|
||||
visible: false
|
||||
source: "Video.qml"
|
||||
function showHide() {
|
||||
if (!videoItem.active) {
|
||||
videoItem.active = true;
|
||||
videoItem.visible = true;
|
||||
} else {
|
||||
videoItem.active = false;
|
||||
videoItem.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
// spacer
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: videoButton
|
||||
visible: model.VideoRole.toString() !== ""
|
||||
iconName: "video"
|
||||
onClicked: videoItem.showHide()
|
||||
}
|
||||
Button {
|
||||
id: configureButton
|
||||
visible: ConfigurableRole
|
||||
enabled: item.checkedState != Qt.Unchecked
|
||||
iconName: "configure"
|
||||
onClicked: {
|
||||
effectConfig.openConfig(model.ServiceNameRole, model.ScriptedRole, model.NameRole);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: aboutButton
|
||||
iconName: "dialog-information"
|
||||
onClicked: {
|
||||
aboutItem.visible = !aboutItem.visible;
|
||||
}
|
||||
}
|
||||
} //end Row
|
||||
} //end Rectangle
|
|
@ -1,179 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Layouts 1.0
|
||||
import org.kde.kwin.kwincompositing 1.0
|
||||
|
||||
Rectangle {
|
||||
signal changed
|
||||
implicitHeight: 500
|
||||
implicitWidth: 400
|
||||
|
||||
EffectConfig {
|
||||
id: effectConfig
|
||||
onEffectListChanged: {
|
||||
searchModel.load()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
id: hint
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
text: i18n("Hint: To find out or configure how to activate an effect, look at the effect's settings.")
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
TextField {
|
||||
// TODO: needs clear button, missing in Qt
|
||||
id: searchField
|
||||
placeholderText: i18n("Search...")
|
||||
Layout.fillWidth: true
|
||||
focus: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: configureButton
|
||||
icon.name: "configure"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: i18n("Configure filter")
|
||||
onClicked: menu.opened ? menu.close() : menu.open()
|
||||
}
|
||||
Menu {
|
||||
id: menu
|
||||
y: configureButton.height
|
||||
x: parent.width - width
|
||||
MenuItem {
|
||||
text: i18n("Exclude Desktop Effects not supported by the Compositor")
|
||||
checkable: true
|
||||
checked: searchModel.filterOutUnsupported
|
||||
onToggled: {
|
||||
searchModel.filterOutUnsupported = !searchModel.filterOutUnsupported;
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: i18n("Exclude internal Desktop Effects")
|
||||
checkable: true
|
||||
checked: searchModel.filterOutInternal
|
||||
onToggled: {
|
||||
searchModel.filterOutInternal = !searchModel.filterOutInternal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
|
||||
// Draw a frame around the scrollview
|
||||
Component.onCompleted: background.visible = 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 backgroundNormalColor: searchModel.backgroundNormalColor
|
||||
property color backgroundAlternateColor: searchModel.backgroundAlternateColor
|
||||
anchors.fill: parent
|
||||
model: EffectFilterModel {
|
||||
id: searchModel
|
||||
objectName: "filterModel"
|
||||
filter: searchField.text
|
||||
}
|
||||
delegate: Effect{
|
||||
id: effectDelegate
|
||||
Connections {
|
||||
id: effectStateConnection
|
||||
target: null
|
||||
onChanged: {
|
||||
searchModel.updateEffectStatus(index, checkedState);
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
effectStateConnection.target = effectDelegate
|
||||
}
|
||||
}
|
||||
|
||||
section.property: "CategoryRole"
|
||||
section.delegate: Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: sectionText.implicitHeight + 2 * col.spacing
|
||||
color: searchModel.backgroundNormalColor
|
||||
|
||||
Label {
|
||||
id: sectionText
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
text: section
|
||||
font.weight: Font.Bold
|
||||
color: searchModel.sectionColor
|
||||
}
|
||||
}
|
||||
spacing: col.spacing
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Button {
|
||||
id: ghnsButton
|
||||
text: i18n("Get New Desktop Effects...")
|
||||
icon.name: "get-hot-new-stuff"
|
||||
onClicked: effectConfig.openGHNS()
|
||||
}
|
||||
}
|
||||
}//End ColumnLayout
|
||||
Connections {
|
||||
target: searchModel
|
||||
onDataChanged: changed()
|
||||
}
|
||||
}//End Rectangle
|
|
@ -1,31 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import org.kde.kwin.kwincompositing 1.0
|
||||
|
||||
EffectView{
|
||||
id: view
|
||||
onChanged: {
|
||||
window.changed()
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
#include "modeltest.h"
|
||||
#include "../model.h"
|
||||
#include "effectmodeltest.h"
|
||||
|
||||
#include "effectmodel.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
EffectModelTest::EffectModelTest(QObject *parent)
|
||||
: QObject(parent) {
|
||||
|
||||
}
|
||||
|
||||
void EffectModelTest::testEffectModel() {
|
||||
auto effectModel = new KWin::EffectModel();
|
||||
|
||||
new ModelTest(effectModel, this);
|
||||
}
|
||||
|
||||
void EffectModelTest::testEffectFilterModel() {
|
||||
KWin::Compositing::EffectFilterModel *model = new KWin::Compositing::EffectFilterModel();
|
||||
|
||||
new ModelTest(model, this);
|
||||
}
|
||||
|
||||
QTEST_MAIN(EffectModelTest)
|
|
@ -1,38 +0,0 @@
|
|||
/**************************************************************************
|
||||
* KWin - the KDE window manager *
|
||||
* This file is part of the KDE project. *
|
||||
* *
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#ifndef EFFECTMODELTEST_H
|
||||
#define EFFECTMODELTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class EffectModelTest : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EffectModelTest(QObject *parent = 0);
|
||||
|
||||
private Q_SLOTS:
|
||||
void testEffectModel();
|
||||
void testEffectFilterModel();
|
||||
};
|
||||
#endif
|
|
@ -1,600 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtGui>
|
||||
|
||||
#include "modeltest.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
/*!
|
||||
Connect to all of the models signals. Whenever anything happens recheck everything.
|
||||
*/
|
||||
ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false )
|
||||
{
|
||||
if (!model)
|
||||
qFatal("%s: model must not be null", Q_FUNC_INFO);
|
||||
|
||||
connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
|
||||
this, SLOT(runAllTests()) );
|
||||
|
||||
// Special checks for changes
|
||||
connect(model, SIGNAL(layoutAboutToBeChanged()),
|
||||
this, SLOT(layoutAboutToBeChanged()) );
|
||||
connect(model, SIGNAL(layoutChanged()),
|
||||
this, SLOT(layoutChanged()) );
|
||||
|
||||
connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
|
||||
this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) );
|
||||
connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
||||
this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) );
|
||||
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
||||
this, SLOT(rowsInserted(QModelIndex,int,int)) );
|
||||
connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
|
||||
this, SLOT(rowsRemoved(QModelIndex,int,int)) );
|
||||
connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
|
||||
this, SLOT(dataChanged(QModelIndex,QModelIndex)) );
|
||||
connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
|
||||
this, SLOT(headerDataChanged(Qt::Orientation,int,int)) );
|
||||
|
||||
runAllTests();
|
||||
}
|
||||
|
||||
void ModelTest::runAllTests()
|
||||
{
|
||||
if ( fetchingMore )
|
||||
return;
|
||||
nonDestructiveBasicTest();
|
||||
rowCount();
|
||||
columnCount();
|
||||
hasIndex();
|
||||
index();
|
||||
parent();
|
||||
data();
|
||||
}
|
||||
|
||||
/*!
|
||||
nonDestructiveBasicTest tries to call a number of the basic functions (not all)
|
||||
to make sure the model doesn't outright segfault, testing the functions that makes sense.
|
||||
*/
|
||||
void ModelTest::nonDestructiveBasicTest()
|
||||
{
|
||||
QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() );
|
||||
model->canFetchMore ( QModelIndex() );
|
||||
QVERIFY( model->columnCount ( QModelIndex() ) >= 0 );
|
||||
QVERIFY( model->data ( QModelIndex() ) == QVariant() );
|
||||
fetchingMore = true;
|
||||
model->fetchMore ( QModelIndex() );
|
||||
fetchingMore = false;
|
||||
Qt::ItemFlags flags = model->flags ( QModelIndex() );
|
||||
QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 );
|
||||
model->hasChildren ( QModelIndex() );
|
||||
model->hasIndex ( 0, 0 );
|
||||
model->headerData ( 0, Qt::Horizontal );
|
||||
model->index ( 0, 0 );
|
||||
model->itemData ( QModelIndex() );
|
||||
QVariant cache;
|
||||
model->match ( QModelIndex(), -1, cache );
|
||||
model->mimeTypes();
|
||||
QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
|
||||
QVERIFY( model->rowCount() >= 0 );
|
||||
QVariant variant;
|
||||
model->setData ( QModelIndex(), variant, -1 );
|
||||
model->setHeaderData ( -1, Qt::Horizontal, QVariant() );
|
||||
model->setHeaderData ( 999999, Qt::Horizontal, QVariant() );
|
||||
QMap<int, QVariant> roles;
|
||||
model->sibling ( 0, 0, QModelIndex() );
|
||||
model->span ( QModelIndex() );
|
||||
model->supportedDropActions();
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
|
||||
|
||||
Models that are dynamically populated are not as fully tested here.
|
||||
*/
|
||||
void ModelTest::rowCount()
|
||||
{
|
||||
// qDebug() << "rc";
|
||||
// check top row
|
||||
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
|
||||
int rows = model->rowCount ( topIndex );
|
||||
QVERIFY( rows >= 0 );
|
||||
if ( rows > 0 )
|
||||
QVERIFY( model->hasChildren ( topIndex ) );
|
||||
|
||||
QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex );
|
||||
if ( secondLevelIndex.isValid() ) { // not the top level
|
||||
// check a row count where parent is valid
|
||||
rows = model->rowCount ( secondLevelIndex );
|
||||
QVERIFY( rows >= 0 );
|
||||
if ( rows > 0 )
|
||||
QVERIFY( model->hasChildren ( secondLevelIndex ) );
|
||||
}
|
||||
|
||||
// The models rowCount() is tested more extensively in checkChildren(),
|
||||
// but this catches the big mistakes
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
|
||||
*/
|
||||
void ModelTest::columnCount()
|
||||
{
|
||||
// check top row
|
||||
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
|
||||
QVERIFY( model->columnCount ( topIndex ) >= 0 );
|
||||
|
||||
// check a column count where parent is valid
|
||||
QModelIndex childIndex = model->index ( 0, 0, topIndex );
|
||||
if ( childIndex.isValid() )
|
||||
QVERIFY( model->columnCount ( childIndex ) >= 0 );
|
||||
|
||||
// columnCount() is tested more extensively in checkChildren(),
|
||||
// but this catches the big mistakes
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests model's implementation of QAbstractItemModel::hasIndex()
|
||||
*/
|
||||
void ModelTest::hasIndex()
|
||||
{
|
||||
// qDebug() << "hi";
|
||||
// Make sure that invalid values returns an invalid index
|
||||
QVERIFY( !model->hasIndex ( -2, -2 ) );
|
||||
QVERIFY( !model->hasIndex ( -2, 0 ) );
|
||||
QVERIFY( !model->hasIndex ( 0, -2 ) );
|
||||
|
||||
int rows = model->rowCount();
|
||||
int columns = model->columnCount();
|
||||
|
||||
// check out of bounds
|
||||
QVERIFY( !model->hasIndex ( rows, columns ) );
|
||||
QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) );
|
||||
|
||||
if ( rows > 0 )
|
||||
QVERIFY( model->hasIndex ( 0, 0 ) );
|
||||
|
||||
// hasIndex() is tested more extensively in checkChildren(),
|
||||
// but this catches the big mistakes
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests model's implementation of QAbstractItemModel::index()
|
||||
*/
|
||||
void ModelTest::index()
|
||||
{
|
||||
// qDebug() << "i";
|
||||
// Make sure that invalid values returns an invalid index
|
||||
QVERIFY( model->index ( -2, -2 ) == QModelIndex() );
|
||||
QVERIFY( model->index ( -2, 0 ) == QModelIndex() );
|
||||
QVERIFY( model->index ( 0, -2 ) == QModelIndex() );
|
||||
|
||||
int rows = model->rowCount();
|
||||
int columns = model->columnCount();
|
||||
|
||||
if ( rows == 0 )
|
||||
return;
|
||||
|
||||
// Catch off by one errors
|
||||
QVERIFY( model->index ( rows, columns ) == QModelIndex() );
|
||||
QVERIFY( model->index ( 0, 0 ).isValid() );
|
||||
|
||||
// Make sure that the same index is *always* returned
|
||||
QModelIndex a = model->index ( 0, 0 );
|
||||
QModelIndex b = model->index ( 0, 0 );
|
||||
QVERIFY( a == b );
|
||||
|
||||
// index() is tested more extensively in checkChildren(),
|
||||
// but this catches the big mistakes
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests model's implementation of QAbstractItemModel::parent()
|
||||
*/
|
||||
void ModelTest::parent()
|
||||
{
|
||||
// qDebug() << "p";
|
||||
// Make sure the model won't crash and will return an invalid QModelIndex
|
||||
// when asked for the parent of an invalid index.
|
||||
QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
|
||||
|
||||
if ( model->rowCount() == 0 )
|
||||
return;
|
||||
|
||||
// Column 0 | Column 1 |
|
||||
// QModelIndex() | |
|
||||
// \- topIndex | topIndex1 |
|
||||
// \- childIndex | childIndex1 |
|
||||
|
||||
// Common error test #1, make sure that a top level index has a parent
|
||||
// that is a invalid QModelIndex.
|
||||
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
|
||||
QVERIFY( model->parent ( topIndex ) == QModelIndex() );
|
||||
|
||||
// Common error test #2, make sure that a second level index has a parent
|
||||
// that is the first level index.
|
||||
if ( model->rowCount ( topIndex ) > 0 ) {
|
||||
QModelIndex childIndex = model->index ( 0, 0, topIndex );
|
||||
QVERIFY( model->parent ( childIndex ) == topIndex );
|
||||
}
|
||||
|
||||
// Common error test #3, the second column should NOT have the same children
|
||||
// as the first column in a row.
|
||||
// Usually the second column shouldn't have children.
|
||||
QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() );
|
||||
if ( model->rowCount ( topIndex1 ) > 0 ) {
|
||||
QModelIndex childIndex = model->index ( 0, 0, topIndex );
|
||||
QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 );
|
||||
QVERIFY( childIndex != childIndex1 );
|
||||
}
|
||||
|
||||
// Full test, walk n levels deep through the model making sure that all
|
||||
// parent's children correctly specify their parent.
|
||||
checkChildren ( QModelIndex() );
|
||||
}
|
||||
|
||||
/*!
|
||||
Called from the parent() test.
|
||||
|
||||
A model that returns an index of parent X should also return X when asking
|
||||
for the parent of the index.
|
||||
|
||||
This recursive function does pretty extensive testing on the whole model in an
|
||||
effort to catch edge cases.
|
||||
|
||||
This function assumes that rowCount(), columnCount() and index() already work.
|
||||
If they have a bug it will point it out, but the above tests should have already
|
||||
found the basic bugs because it is easier to figure out the problem in
|
||||
those tests then this one.
|
||||
*/
|
||||
void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth )
|
||||
{
|
||||
// First just try walking back up the tree.
|
||||
QModelIndex p = parent;
|
||||
while ( p.isValid() )
|
||||
p = p.parent();
|
||||
|
||||
// For models that are dynamically populated
|
||||
if ( model->canFetchMore ( parent ) ) {
|
||||
fetchingMore = true;
|
||||
model->fetchMore ( parent );
|
||||
fetchingMore = false;
|
||||
}
|
||||
|
||||
int rows = model->rowCount ( parent );
|
||||
int columns = model->columnCount ( parent );
|
||||
|
||||
if ( rows > 0 )
|
||||
QVERIFY( model->hasChildren ( parent ) );
|
||||
|
||||
// Some further testing against rows(), columns(), and hasChildren()
|
||||
QVERIFY( rows >= 0 );
|
||||
QVERIFY( columns >= 0 );
|
||||
if ( rows > 0 )
|
||||
QVERIFY( model->hasChildren ( parent ) );
|
||||
|
||||
//qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
|
||||
// << "columns:" << columns << "parent column:" << parent.column();
|
||||
|
||||
const QModelIndex topLeftChild = model->index( 0, 0, parent );
|
||||
|
||||
QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) );
|
||||
for ( int r = 0; r < rows; ++r ) {
|
||||
if ( model->canFetchMore ( parent ) ) {
|
||||
fetchingMore = true;
|
||||
model->fetchMore ( parent );
|
||||
fetchingMore = false;
|
||||
}
|
||||
QVERIFY( !model->hasIndex ( r, columns + 1, parent ) );
|
||||
for ( int c = 0; c < columns; ++c ) {
|
||||
QVERIFY( model->hasIndex ( r, c, parent ) );
|
||||
QModelIndex index = model->index ( r, c, parent );
|
||||
// rowCount() and columnCount() said that it existed...
|
||||
QVERIFY( index.isValid() );
|
||||
|
||||
// index() should always return the same index when called twice in a row
|
||||
QModelIndex modifiedIndex = model->index ( r, c, parent );
|
||||
QVERIFY( index == modifiedIndex );
|
||||
|
||||
// Make sure we get the same index if we request it twice in a row
|
||||
QModelIndex a = model->index ( r, c, parent );
|
||||
QModelIndex b = model->index ( r, c, parent );
|
||||
QVERIFY( a == b );
|
||||
|
||||
{
|
||||
const QModelIndex sibling = model->sibling( r, c, topLeftChild );
|
||||
QVERIFY( index == sibling );
|
||||
}
|
||||
{
|
||||
const QModelIndex sibling = topLeftChild.sibling( r, c );
|
||||
QVERIFY( index == sibling );
|
||||
}
|
||||
|
||||
// Some basic checking on the index that is returned
|
||||
QVERIFY( index.model() == model );
|
||||
QCOMPARE( index.row(), r );
|
||||
QCOMPARE( index.column(), c );
|
||||
// While you can technically return a QVariant usually this is a sign
|
||||
// of a bug in data(). Disable if this really is ok in your model.
|
||||
// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() );
|
||||
|
||||
// If the next test fails here is some somewhat useful debug you play with.
|
||||
|
||||
if (model->parent(index) != parent) {
|
||||
qDebug() << r << c << currentDepth << model->data(index).toString()
|
||||
<< model->data(parent).toString();
|
||||
qDebug() << index << parent << model->parent(index);
|
||||
// And a view that you can even use to show the model.
|
||||
// QTreeView view;
|
||||
// view.setModel(model);
|
||||
// view.show();
|
||||
}
|
||||
|
||||
// Check that we can get back our real parent.
|
||||
QCOMPARE( model->parent ( index ), parent );
|
||||
|
||||
// recursively go down the children
|
||||
if ( model->hasChildren ( index ) && currentDepth < 10 ) {
|
||||
//qDebug() << r << c << "has children" << model->rowCount(index);
|
||||
checkChildren ( index, ++currentDepth );
|
||||
}/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
|
||||
|
||||
// make sure that after testing the children that the index doesn't change.
|
||||
QModelIndex newerIndex = model->index ( r, c, parent );
|
||||
QVERIFY( index == newerIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests model's implementation of QAbstractItemModel::data()
|
||||
*/
|
||||
void ModelTest::data()
|
||||
{
|
||||
// Invalid index should return an invalid qvariant
|
||||
QVERIFY( !model->data ( QModelIndex() ).isValid() );
|
||||
|
||||
if ( model->rowCount() == 0 )
|
||||
return;
|
||||
|
||||
// A valid index should have a valid QVariant data
|
||||
QVERIFY( model->index ( 0, 0 ).isValid() );
|
||||
|
||||
// shouldn't be able to set data on an invalid index
|
||||
QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) );
|
||||
|
||||
// General Purpose roles that should return a QString
|
||||
QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole );
|
||||
if ( variant.isValid() ) {
|
||||
QVERIFY( variant.canConvert<QString>() );
|
||||
}
|
||||
variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
|
||||
if ( variant.isValid() ) {
|
||||
QVERIFY( variant.canConvert<QString>() );
|
||||
}
|
||||
variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
|
||||
if ( variant.isValid() ) {
|
||||
QVERIFY( variant.canConvert<QString>() );
|
||||
}
|
||||
|
||||
// General Purpose roles that should return a QSize
|
||||
variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
|
||||
if ( variant.isValid() ) {
|
||||
QVERIFY( variant.canConvert<QSize>() );
|
||||
}
|
||||
|
||||
// General Purpose roles that should return a QFont
|
||||
QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
|
||||
if ( fontVariant.isValid() ) {
|
||||
QVERIFY( fontVariant.canConvert<QFont>() );
|
||||
}
|
||||
|
||||
// Check that the alignment is one we know about
|
||||
QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole );
|
||||
if ( textAlignmentVariant.isValid() ) {
|
||||
unsigned int alignment = textAlignmentVariant.toUInt();
|
||||
QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) );
|
||||
}
|
||||
|
||||
// General Purpose roles that should return a QColor
|
||||
QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundRole );
|
||||
if ( colorVariant.isValid() ) {
|
||||
QVERIFY( colorVariant.canConvert<QColor>() );
|
||||
}
|
||||
|
||||
colorVariant = model->data ( model->index ( 0, 0 ), Qt::ForegroundRole );
|
||||
if ( colorVariant.isValid() ) {
|
||||
QVERIFY( colorVariant.canConvert<QColor>() );
|
||||
}
|
||||
|
||||
// Check that the "check state" is one we know about.
|
||||
QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole );
|
||||
if ( checkStateVariant.isValid() ) {
|
||||
int state = checkStateVariant.toInt();
|
||||
QVERIFY( state == Qt::Unchecked ||
|
||||
state == Qt::PartiallyChecked ||
|
||||
state == Qt::Checked );
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Store what is about to be inserted to make sure it actually happens
|
||||
|
||||
\sa rowsInserted()
|
||||
*/
|
||||
void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */)
|
||||
{
|
||||
// Q_UNUSED(end);
|
||||
// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString()
|
||||
// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) );
|
||||
// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) );
|
||||
Changing c;
|
||||
c.parent = parent;
|
||||
c.oldSize = model->rowCount ( parent );
|
||||
c.last = model->data ( model->index ( start - 1, 0, parent ) );
|
||||
c.next = model->data ( model->index ( start, 0, parent ) );
|
||||
insert.push ( c );
|
||||
}
|
||||
|
||||
/*!
|
||||
Confirm that what was said was going to happen actually did
|
||||
|
||||
\sa rowsAboutToBeInserted()
|
||||
*/
|
||||
void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end )
|
||||
{
|
||||
Changing c = insert.pop();
|
||||
QVERIFY( c.parent == parent );
|
||||
// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize
|
||||
// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent );
|
||||
|
||||
// for (int ii=start; ii <= end; ii++)
|
||||
// {
|
||||
// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent ));
|
||||
// }
|
||||
// qDebug();
|
||||
|
||||
QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) );
|
||||
QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
|
||||
|
||||
if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
|
||||
qDebug() << start << end;
|
||||
for (int i=0; i < model->rowCount(); ++i)
|
||||
qDebug() << model->index(i, 0).data().toString();
|
||||
qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
|
||||
}
|
||||
|
||||
QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) );
|
||||
}
|
||||
|
||||
void ModelTest::layoutAboutToBeChanged()
|
||||
{
|
||||
for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i )
|
||||
changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) );
|
||||
}
|
||||
|
||||
void ModelTest::layoutChanged()
|
||||
{
|
||||
for ( int i = 0; i < changing.count(); ++i ) {
|
||||
QPersistentModelIndex p = changing[i];
|
||||
QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) );
|
||||
}
|
||||
changing.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
Store what is about to be inserted to make sure it actually happens
|
||||
|
||||
\sa rowsRemoved()
|
||||
*/
|
||||
void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end )
|
||||
{
|
||||
qDebug() << "ratbr" << parent << start << end;
|
||||
Changing c;
|
||||
c.parent = parent;
|
||||
c.oldSize = model->rowCount ( parent );
|
||||
c.last = model->data ( model->index ( start - 1, 0, parent ) );
|
||||
c.next = model->data ( model->index ( end + 1, 0, parent ) );
|
||||
remove.push ( c );
|
||||
}
|
||||
|
||||
/*!
|
||||
Confirm that what was said was going to happen actually did
|
||||
|
||||
\sa rowsAboutToBeRemoved()
|
||||
*/
|
||||
void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end )
|
||||
{
|
||||
qDebug() << "rr" << parent << start << end;
|
||||
Changing c = remove.pop();
|
||||
QVERIFY( c.parent == parent );
|
||||
QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) );
|
||||
QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
|
||||
QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) );
|
||||
}
|
||||
|
||||
void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
QVERIFY(topLeft.isValid());
|
||||
QVERIFY(bottomRight.isValid());
|
||||
QModelIndex commonParent = bottomRight.parent();
|
||||
QVERIFY(topLeft.parent() == commonParent);
|
||||
QVERIFY(topLeft.row() <= bottomRight.row());
|
||||
QVERIFY(topLeft.column() <= bottomRight.column());
|
||||
int rowCount = model->rowCount(commonParent);
|
||||
int columnCount = model->columnCount(commonParent);
|
||||
QVERIFY(bottomRight.row() < rowCount);
|
||||
QVERIFY(bottomRight.column() < columnCount);
|
||||
}
|
||||
|
||||
void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end)
|
||||
{
|
||||
QVERIFY(start >= 0);
|
||||
QVERIFY(end >= 0);
|
||||
QVERIFY(start <= end);
|
||||
int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount();
|
||||
QVERIFY(start < itemCount);
|
||||
QVERIFY(end < itemCount);
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef MODELTEST_H
|
||||
#define MODELTEST_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QStack>
|
||||
|
||||
class ModelTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModelTest( QAbstractItemModel *model, QObject *parent = 0 );
|
||||
|
||||
private Q_SLOTS:
|
||||
void nonDestructiveBasicTest();
|
||||
void rowCount();
|
||||
void columnCount();
|
||||
void hasIndex();
|
||||
void index();
|
||||
void parent();
|
||||
void data();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void runAllTests();
|
||||
void layoutAboutToBeChanged();
|
||||
void layoutChanged();
|
||||
void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end );
|
||||
void rowsInserted( const QModelIndex & parent, int start, int end );
|
||||
void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end );
|
||||
void rowsRemoved( const QModelIndex & parent, int start, int end );
|
||||
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
void headerDataChanged(Qt::Orientation orientation, int start, int end);
|
||||
|
||||
private:
|
||||
void checkChildren( const QModelIndex &parent, int currentDepth = 0 );
|
||||
|
||||
QAbstractItemModel *model;
|
||||
|
||||
struct Changing {
|
||||
QModelIndex parent;
|
||||
int oldSize;
|
||||
QVariant last;
|
||||
QVariant next;
|
||||
};
|
||||
QStack<Changing> insert;
|
||||
QStack<Changing> remove;
|
||||
|
||||
bool fetchingMore;
|
||||
|
||||
QList<QPersistentModelIndex> changing;
|
||||
};
|
||||
|
||||
#endif
|
33
kcmkwin/kwineffects/CMakeLists.txt
Normal file
33
kcmkwin/kwineffects/CMakeLists.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
include(ECMQMLModules)
|
||||
ecm_find_qmlmodule(org.kde.plasma.core 2.0)
|
||||
|
||||
# KI18N Translation Domain for this library.
|
||||
add_definitions(-DTRANSLATION_DOMAIN=\"kcm_kwin_effects\")
|
||||
|
||||
########### next target ###############
|
||||
|
||||
set(kcm_kwin_effects_PART_SRCS
|
||||
kcm.cpp
|
||||
effectsfilterproxymodel.cpp
|
||||
)
|
||||
|
||||
add_library(kcm_kwin_effects MODULE ${kcm_kwin_effects_PART_SRCS})
|
||||
|
||||
target_link_libraries(kcm_kwin_effects
|
||||
Qt5::DBus
|
||||
KF5::I18n
|
||||
KF5::KCMUtils
|
||||
KF5::NewStuff
|
||||
KF5::QuickAddons
|
||||
KF5::XmlGui
|
||||
kcmkwincommon
|
||||
)
|
||||
|
||||
kcoreaddons_desktop_to_json(kcm_kwin_effects "kcm_kwin_effects.desktop")
|
||||
|
||||
########### install files ###############
|
||||
|
||||
install(TARGETS kcm_kwin_effects DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
|
||||
install(FILES kcm_kwin_effects.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
|
||||
install(FILES kwineffect.knsrc DESTINATION ${CONFIG_INSTALL_DIR})
|
||||
kpackage_install_package(package kcm_kwin_effects kcms)
|
2
kcmkwin/kwineffects/Messages.sh
Normal file
2
kcmkwin/kwineffects/Messages.sh
Normal file
|
@ -0,0 +1,2 @@
|
|||
#! /usr/bin/env bash
|
||||
$XGETTEXT `find . -name \*.cpp -o -name \*.qml` -o $podir/kcm_kwin_effects.pot
|
104
kcmkwin/kwineffects/effectsfilterproxymodel.cpp
Normal file
104
kcmkwin/kwineffects/effectsfilterproxymodel.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "effectsfilterproxymodel.h"
|
||||
|
||||
#include "effectmodel.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
EffectsFilterProxyModel::EffectsFilterProxyModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
EffectsFilterProxyModel::~EffectsFilterProxyModel()
|
||||
{
|
||||
}
|
||||
|
||||
QString EffectsFilterProxyModel::query() const
|
||||
{
|
||||
return m_query;
|
||||
}
|
||||
|
||||
void EffectsFilterProxyModel::setQuery(const QString &query)
|
||||
{
|
||||
if (m_query != query) {
|
||||
m_query = query;
|
||||
emit queryChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectsFilterProxyModel::excludeInternal() const
|
||||
{
|
||||
return m_excludeInternal;
|
||||
}
|
||||
|
||||
void EffectsFilterProxyModel::setExcludeInternal(bool exclude)
|
||||
{
|
||||
if (m_excludeInternal != exclude) {
|
||||
m_excludeInternal = exclude;
|
||||
emit excludeInternalChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectsFilterProxyModel::excludeUnsupported() const
|
||||
{
|
||||
return m_excludeUnsupported;
|
||||
}
|
||||
|
||||
void EffectsFilterProxyModel::setExcludeUnsupported(bool exclude)
|
||||
{
|
||||
if (m_excludeUnsupported != exclude) {
|
||||
m_excludeUnsupported = exclude;
|
||||
emit excludeUnsupportedChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectsFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
const QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
||||
if (!m_query.isEmpty()) {
|
||||
const bool matches = idx.data(EffectModel::NameRole).toString().contains(m_query, Qt::CaseInsensitive) ||
|
||||
idx.data(EffectModel::DescriptionRole).toString().contains(m_query, Qt::CaseInsensitive) ||
|
||||
idx.data(EffectModel::CategoryRole).toString().contains(m_query, Qt::CaseInsensitive);
|
||||
if (!matches) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_excludeInternal) {
|
||||
if (idx.data(EffectModel::InternalRole).toBool()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_excludeUnsupported) {
|
||||
if (!idx.data(EffectModel::SupportedRole).toBool()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
62
kcmkwin/kwineffects/effectsfilterproxymodel.h
Normal file
62
kcmkwin/kwineffects/effectsfilterproxymodel.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class EffectsFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel)
|
||||
Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged)
|
||||
Q_PROPERTY(bool excludeInternal READ excludeInternal WRITE setExcludeInternal NOTIFY excludeInternalChanged)
|
||||
Q_PROPERTY(bool excludeUnsupported READ excludeUnsupported WRITE setExcludeUnsupported NOTIFY excludeUnsupportedChanged)
|
||||
|
||||
public:
|
||||
explicit EffectsFilterProxyModel(QObject *parent = nullptr);
|
||||
~EffectsFilterProxyModel() override;
|
||||
|
||||
QString query() const;
|
||||
void setQuery(const QString &query);
|
||||
|
||||
bool excludeInternal() const;
|
||||
void setExcludeInternal(bool exclude);
|
||||
|
||||
bool excludeUnsupported() const;
|
||||
void setExcludeUnsupported(bool exclude);
|
||||
|
||||
Q_SIGNALS:
|
||||
void queryChanged();
|
||||
void excludeInternalChanged();
|
||||
void excludeUnsupportedChanged();
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
private:
|
||||
QString m_query;
|
||||
bool m_excludeInternal = true;
|
||||
bool m_excludeUnsupported = true;
|
||||
|
||||
Q_DISABLE_COPY(EffectsFilterProxyModel)
|
||||
};
|
||||
|
||||
} // namespace KWin
|
192
kcmkwin/kwineffects/kcm.cpp
Normal file
192
kcmkwin/kwineffects/kcm.cpp
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "kcm.h"
|
||||
#include "effectmodel.h"
|
||||
#include "effectsfilterproxymodel.h"
|
||||
|
||||
#include <KAboutData>
|
||||
#include <KCModule>
|
||||
#include <KLocalizedString>
|
||||
#include <KNS3/DownloadDialog>
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginTrader>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QQuickWindow>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWindow>
|
||||
|
||||
K_PLUGIN_FACTORY_WITH_JSON(DesktopEffectsKCMFactory,
|
||||
"kcm_kwin_effects.json",
|
||||
registerPlugin<KWin::DesktopEffectsKCM>();)
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
DesktopEffectsKCM::DesktopEffectsKCM(QObject *parent, const QVariantList &args)
|
||||
: KQuickAddons::ConfigModule(parent, args)
|
||||
, m_model(new EffectModel(this))
|
||||
{
|
||||
qmlRegisterType<EffectsFilterProxyModel>("org.kde.private.kcms.kwin.effects", 1, 0, "EffectsFilterProxyModel");
|
||||
|
||||
auto about = new KAboutData(
|
||||
QStringLiteral("kcm_kwin_effects"),
|
||||
i18n("Configure Desktop Effects"),
|
||||
QStringLiteral("2.0"),
|
||||
QString(),
|
||||
KAboutLicense::GPL
|
||||
);
|
||||
about->addAuthor(i18n("Vlad Zagorodniy"), QString(), QStringLiteral("vladzzag@gmail.com"));
|
||||
setAboutData(about);
|
||||
|
||||
setButtons(Apply | Default);
|
||||
|
||||
connect(m_model, &EffectModel::dataChanged, this, &DesktopEffectsKCM::updateNeedsSave);
|
||||
}
|
||||
|
||||
DesktopEffectsKCM::~DesktopEffectsKCM()
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractItemModel *DesktopEffectsKCM::effectsModel() const
|
||||
{
|
||||
return m_model;
|
||||
}
|
||||
|
||||
void DesktopEffectsKCM::load()
|
||||
{
|
||||
m_model->load();
|
||||
setNeedsSave(false);
|
||||
}
|
||||
|
||||
void DesktopEffectsKCM::save()
|
||||
{
|
||||
m_model->save();
|
||||
setNeedsSave(false);
|
||||
}
|
||||
|
||||
void DesktopEffectsKCM::defaults()
|
||||
{
|
||||
m_model->defaults();
|
||||
updateNeedsSave();
|
||||
}
|
||||
|
||||
void DesktopEffectsKCM::openGHNS(QQuickItem *context)
|
||||
{
|
||||
QPointer<KNS3::DownloadDialog> dialog = new KNS3::DownloadDialog(QStringLiteral("kwineffect.knsrc"));
|
||||
dialog->setWindowTitle(i18n("Download New Desktop Effects"));
|
||||
dialog->winId();
|
||||
|
||||
if (context && context->window()) {
|
||||
dialog->windowHandle()->setTransientParent(context->window());
|
||||
}
|
||||
|
||||
if (dialog->exec() == QDialog::Accepted) {
|
||||
if (!dialog->changedEntries().isEmpty()) {
|
||||
m_model->load();
|
||||
}
|
||||
}
|
||||
|
||||
delete dialog;
|
||||
}
|
||||
|
||||
static KCModule *findBinaryConfig(const QString &pluginId, QObject *parent)
|
||||
{
|
||||
return KPluginTrader::createInstanceFromQuery<KCModule>(
|
||||
QStringLiteral("kwin/effects/configs/"),
|
||||
QString(),
|
||||
QStringLiteral("'%1' in [X-KDE-ParentComponents]").arg(pluginId),
|
||||
parent
|
||||
);
|
||||
}
|
||||
|
||||
static KCModule *findScriptedConfig(const QString &pluginId, QObject *parent)
|
||||
{
|
||||
const auto offers = KPluginTrader::self()->query(
|
||||
QStringLiteral("kwin/effects/configs/"),
|
||||
QString(),
|
||||
QStringLiteral("[X-KDE-Library] == 'kcm_kwin4_genericscripted'")
|
||||
);
|
||||
|
||||
if (offers.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KPluginInfo &generic = offers.first();
|
||||
KPluginLoader loader(generic.libraryPath());
|
||||
KPluginFactory *factory = loader.factory();
|
||||
if (!factory) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return factory->create<KCModule>(pluginId, parent);
|
||||
}
|
||||
|
||||
void DesktopEffectsKCM::configure(const QString &pluginId, QQuickItem *context)
|
||||
{
|
||||
const QModelIndex idx = m_model->findByPluginId(pluginId);
|
||||
if (!idx.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPointer<QDialog> dialog = new QDialog();
|
||||
|
||||
KCModule *module = idx.data(EffectModel::ScriptedRole).toBool()
|
||||
? findScriptedConfig(pluginId, dialog)
|
||||
: findBinaryConfig(pluginId, dialog);
|
||||
if (!module) {
|
||||
delete dialog;
|
||||
return;
|
||||
}
|
||||
|
||||
dialog->setWindowTitle(idx.data(EffectModel::NameRole).toString());
|
||||
dialog->winId();
|
||||
|
||||
auto buttons = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel |
|
||||
QDialogButtonBox::RestoreDefaults,
|
||||
dialog
|
||||
);
|
||||
connect(buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
|
||||
connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked,
|
||||
module, &KCModule::defaults);
|
||||
|
||||
auto layout = new QVBoxLayout(dialog);
|
||||
layout->addWidget(module);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
if (context && context->window()) {
|
||||
dialog->windowHandle()->setTransientParent(context->window());
|
||||
}
|
||||
|
||||
dialog->exec();
|
||||
|
||||
delete dialog;
|
||||
}
|
||||
|
||||
void DesktopEffectsKCM::updateNeedsSave()
|
||||
{
|
||||
setNeedsSave(m_model->needsSave());
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "kcm.moc"
|
58
kcmkwin/kwineffects/kcm.h
Normal file
58
kcmkwin/kwineffects/kcm.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <KQuickAddons/ConfigModule>
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QQuickItem>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class EffectModel;
|
||||
|
||||
class DesktopEffectsKCM : public KQuickAddons::ConfigModule
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QAbstractItemModel *effectsModel READ effectsModel CONSTANT)
|
||||
|
||||
public:
|
||||
explicit DesktopEffectsKCM(QObject *parent = nullptr, const QVariantList &list = {});
|
||||
~DesktopEffectsKCM() override;
|
||||
|
||||
QAbstractItemModel *effectsModel() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void load() override;
|
||||
void save() override;
|
||||
void defaults() override;
|
||||
|
||||
void openGHNS(QQuickItem *context);
|
||||
void configure(const QString &pluginId, QQuickItem *context);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateNeedsSave();
|
||||
|
||||
private:
|
||||
EffectModel *m_model;
|
||||
|
||||
Q_DISABLE_COPY(DesktopEffectsKCM)
|
||||
};
|
||||
|
||||
} // namespace KWin
|
|
@ -1,11 +1,11 @@
|
|||
[Desktop Entry]
|
||||
Exec=kcmshell5 kwineffects
|
||||
Exec=kcmshell5 kcm_kwin_effects
|
||||
Icon=preferences-desktop-effects
|
||||
Type=Service
|
||||
X-KDE-ServiceTypes=KCModule
|
||||
X-DocPath=kcontrol/kwineffects/index.html
|
||||
|
||||
X-KDE-Library=kwincompositing
|
||||
X-KDE-Library=kcm_kwin_effects
|
||||
X-KDE-PluginKeyword=effects
|
||||
X-KDE-ParentApp=kcontrol
|
||||
|
140
kcmkwin/kwineffects/package/contents/ui/Effect.qml
Normal file
140
kcmkwin/kwineffects/package/contents/ui/Effect.qml
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com>
|
||||
* Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 2.5 as QtControls
|
||||
import QtQuick.Layouts 1.0
|
||||
import org.kde.kirigami 2.5 as Kirigami
|
||||
|
||||
Rectangle {
|
||||
height: row.implicitHeight
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
color: index % 2 ? Kirigami.Theme.backgroundColor : palette.alternateBase
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
x: spacing
|
||||
width: parent.width - 2 * spacing
|
||||
|
||||
QtControls.RadioButton {
|
||||
property bool _exclusive: model.ExclusiveRole != ""
|
||||
property bool _toggled: false
|
||||
|
||||
checked: model.EffectStatusRole
|
||||
visible: _exclusive
|
||||
QtControls.ButtonGroup.group: _exclusive ? effectsList.findButtonGroup(model.ExclusiveRole) : null
|
||||
|
||||
onToggled: {
|
||||
model.EffectStatusRole = checked ? Qt.Checked : Qt.Unchecked;
|
||||
_toggled = true;
|
||||
}
|
||||
onClicked: {
|
||||
// Uncheck the radio button if it's clicked.
|
||||
if (checked && !_toggled) {
|
||||
model.EffectStatusRole = Qt.Unchecked;
|
||||
}
|
||||
_toggled = false;
|
||||
}
|
||||
}
|
||||
|
||||
QtControls.CheckBox {
|
||||
checkState: model.EffectStatusRole
|
||||
visible: model.ExclusiveRole == ""
|
||||
|
||||
onToggled: model.EffectStatusRole = checkState
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
QtControls.Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.weight: Font.Bold
|
||||
text: model.NameRole
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
QtControls.Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: model.DescriptionRole
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
QtControls.Label {
|
||||
id: aboutItem
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.weight: Font.Bold
|
||||
text: i18n("Author: %1\nLicense: %2", model.AuthorNameRole, model.LicenseRole)
|
||||
visible: false
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: videoItem
|
||||
|
||||
active: false
|
||||
source: "Video.qml"
|
||||
visible: false
|
||||
|
||||
function showHide() {
|
||||
if (!videoItem.active) {
|
||||
videoItem.active = true;
|
||||
videoItem.visible = true;
|
||||
} else {
|
||||
videoItem.active = false;
|
||||
videoItem.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtControls.Button {
|
||||
icon.name: "video"
|
||||
visible: model.VideoRole.toString() !== ""
|
||||
|
||||
onClicked: videoItem.showHide()
|
||||
}
|
||||
|
||||
QtControls.Button {
|
||||
enabled: model.EffectStatusRole != Qt.Unchecked
|
||||
icon.name: "configure"
|
||||
visible: model.ConfigurableRole
|
||||
|
||||
onClicked: kcm.configure(model.ServiceNameRole, this)
|
||||
}
|
||||
|
||||
QtControls.Button {
|
||||
icon.name: "dialog-information"
|
||||
|
||||
onClicked: aboutItem.visible = !aboutItem.visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Kirigami.Theme doesn't provide alternate color.
|
||||
SystemPalette {
|
||||
id: palette
|
||||
|
||||
colorGroup: SystemPalette.Active
|
||||
}
|
||||
}
|
154
kcmkwin/kwineffects/package/contents/ui/main.qml
Normal file
154
kcmkwin/kwineffects/package/contents/ui/main.qml
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@gmail.com>
|
||||
* Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 2.5 as QtControls
|
||||
import QtQuick.Layouts 1.1
|
||||
import org.kde.kcm 1.2
|
||||
import org.kde.kconfig 1.0
|
||||
import org.kde.kirigami 2.5 as Kirigami
|
||||
import org.kde.private.kcms.kwin.effects 1.0 as Private
|
||||
|
||||
ScrollViewKCM {
|
||||
ConfigModule.quickHelp: i18n("This module lets you configure desktop effects.")
|
||||
|
||||
header: ColumnLayout {
|
||||
QtControls.Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
elide: Text.ElideRight
|
||||
text: i18n("Hint: To find out or configure how to activate an effect, look at the effect's settings.")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
QtControls.TextField {
|
||||
id: searchField
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
// TODO: Needs clear button, missing in Qt.
|
||||
placeholderText: i18n("Search...")
|
||||
}
|
||||
|
||||
QtControls.Button {
|
||||
id: configureButton
|
||||
|
||||
QtControls.ToolTip.visible: hovered
|
||||
QtControls.ToolTip.text: i18n("Configure filter")
|
||||
|
||||
icon.name: "configure"
|
||||
|
||||
onClicked: menu.opened ? menu.close() : menu.open()
|
||||
}
|
||||
|
||||
QtControls.Menu {
|
||||
id: menu
|
||||
|
||||
x: parent.width - width
|
||||
y: configureButton.height
|
||||
|
||||
QtControls.MenuItem {
|
||||
checkable: true
|
||||
checked: searchModel.excludeUnsupported
|
||||
text: i18n("Exclude Desktop Effects not supported by the Compositor")
|
||||
|
||||
onToggled: searchModel.excludeUnsupported = checked
|
||||
}
|
||||
|
||||
QtControls.MenuItem {
|
||||
checkable: true
|
||||
checked: searchModel.excludeInternal
|
||||
text: i18n("Exclude internal Desktop Effects")
|
||||
|
||||
onToggled: searchModel.excludeInternal = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view: ListView {
|
||||
id: effectsList
|
||||
|
||||
property var _buttonGroups: []
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
model: Private.EffectsFilterProxyModel {
|
||||
id: searchModel
|
||||
|
||||
query: searchField.text
|
||||
sourceModel: kcm.effectsModel
|
||||
}
|
||||
|
||||
delegate: Effect {
|
||||
width: effectsList.width
|
||||
}
|
||||
|
||||
section.property: "CategoryRole"
|
||||
section.delegate: Item {
|
||||
width: effectsList.width
|
||||
height: sectionText.implicitHeight + 2 * Kirigami.Units.smallSpacing
|
||||
|
||||
QtControls.Label {
|
||||
id: sectionText
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
font.weight: Font.Bold
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: section
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
function findButtonGroup(name) {
|
||||
for (let item of effectsList._buttonGroups) {
|
||||
if (item.name == name) {
|
||||
return item.group;
|
||||
}
|
||||
}
|
||||
|
||||
let group = Qt.createQmlObject(
|
||||
'import QtQuick 2.1;' +
|
||||
'import QtQuick.Controls 2.5;' +
|
||||
'ButtonGroup {}',
|
||||
effectsList,
|
||||
"dynamicButtonGroup" + effectsList._buttonGroups.length
|
||||
);
|
||||
|
||||
effectsList._buttonGroups.push({ name, group });
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
QtControls.Button {
|
||||
icon.name: "get-hot-new-stuff"
|
||||
text: i18n("Get New Desktop Effects...")
|
||||
visible: KAuthorized.authorize("ghns")
|
||||
|
||||
onClicked: kcm.openGHNS(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
102
kcmkwin/kwineffects/package/metadata.desktop
Normal file
102
kcmkwin/kwineffects/package/metadata.desktop
Normal file
|
@ -0,0 +1,102 @@
|
|||
[Desktop Entry]
|
||||
Name=Desktop Effects
|
||||
Name[bs]=Efekti površi
|
||||
Name[ca]=Efectes d'escriptori
|
||||
Name[ca@valencia]=Efectes d'escriptori
|
||||
Name[cs]=Efekty na ploše
|
||||
Name[da]=Skrivebordseffekter
|
||||
Name[de]=Arbeitsflächen-Effekte
|
||||
Name[el]=Εφέ επιφάνειας εργασίας
|
||||
Name[en_GB]=Desktop Effects
|
||||
Name[es]=Efectos del escritorio
|
||||
Name[et]=Töölauaefektid
|
||||
Name[eu]=Mahaigaineko efektuak
|
||||
Name[fi]=Työpöytätehosteet
|
||||
Name[fr]=Effets de bureau
|
||||
Name[gl]=Efectos do escritorio
|
||||
Name[he]=הנפשות שולחן עבודה
|
||||
Name[hu]=Asztali effektusok
|
||||
Name[ia]=Effectos de scriptorio
|
||||
Name[id]=Efek Desktop
|
||||
Name[it]=Effetti del desktop
|
||||
Name[ja]=デスクトップ効果
|
||||
Name[ko]=데스크톱 효과
|
||||
Name[lt]=Darbalaukio efektai
|
||||
Name[nb]=Skrivebordseffekter
|
||||
Name[nds]=Schriefdischeffekten
|
||||
Name[nl]=Bureaubladeffecten
|
||||
Name[nn]=Skrivebordseffektar
|
||||
Name[pa]=ਡੈਸਕਟਾਪ ਪਰਭਾਵ
|
||||
Name[pl]=Efekty pulpitu
|
||||
Name[pt]=Efeitos do Ecrã
|
||||
Name[pt_BR]=Efeitos da área de trabalho
|
||||
Name[ro]=Efecte de birou
|
||||
Name[ru]=Эффекты
|
||||
Name[se]=Čállinbeavdeeffeavttat
|
||||
Name[sk]=Efekty plochy
|
||||
Name[sl]=Učinki namizja
|
||||
Name[sr]=Ефекти површи
|
||||
Name[sr@ijekavian]=Ефекти површи
|
||||
Name[sr@ijekavianlatin]=Efekti površi
|
||||
Name[sr@latin]=Efekti površi
|
||||
Name[sv]=Skrivbordseffekter
|
||||
Name[tr]=Masaüstü Efektleri
|
||||
Name[uk]=Ефекти стільниці
|
||||
Name[x-test]=xxDesktop Effectsxx
|
||||
Name[zh_CN]=桌面特效
|
||||
Name[zh_TW]=桌面效果
|
||||
|
||||
Comment=Desktop Effects
|
||||
Comment[bs]=Efekti površi
|
||||
Comment[ca]=Efectes d'escriptori
|
||||
Comment[ca@valencia]=Efectes d'escriptori
|
||||
Comment[cs]=Efekty na ploše
|
||||
Comment[da]=Skrivebordseffekter
|
||||
Comment[de]=Arbeitsflächen-Effekte
|
||||
Comment[el]=Εφέ επιφάνειας εργασίας
|
||||
Comment[en_GB]=Desktop Effects
|
||||
Comment[es]=Efectos del escritorio
|
||||
Comment[et]=Töölauaefektid
|
||||
Comment[eu]=Mahaigaineko efektuak
|
||||
Comment[fi]=Työpöytätehosteet
|
||||
Comment[fr]=Effets de bureau
|
||||
Comment[gl]=Efectos do escritorio
|
||||
Comment[he]=הנפשות שולחן עבודה
|
||||
Comment[hu]=Asztali effektusok
|
||||
Comment[ia]=Effectos de scriptorio
|
||||
Comment[id]=Efek Desktop
|
||||
Comment[it]=Effetti del desktop
|
||||
Comment[ja]=デスクトップ効果
|
||||
Comment[ko]=데스크톱 효과
|
||||
Comment[lt]=Darbalaukio efektai
|
||||
Comment[nb]=Skrivebordseffekter
|
||||
Comment[nds]=Schriefdischeffekten
|
||||
Comment[nl]=Bureaubladeffecten
|
||||
Comment[nn]=Skrivebordseffektar
|
||||
Comment[pa]=ਡੈਸਕਟਾਪ ਪਰਭਾਵ
|
||||
Comment[pl]=Efekty pulpitu
|
||||
Comment[pt]=Efeitos do Ecrã
|
||||
Comment[pt_BR]=Efeitos da Área de Trabalho
|
||||
Comment[ro]=Efecte de birou
|
||||
Comment[ru]=Настройка эффектов рабочего стола
|
||||
Comment[sk]=Efekty plochy
|
||||
Comment[sl]=Učinki namizja
|
||||
Comment[sr]=Ефекти површи
|
||||
Comment[sr@ijekavian]=Ефекти површи
|
||||
Comment[sr@ijekavianlatin]=Efekti površi
|
||||
Comment[sr@latin]=Efekti površi
|
||||
Comment[sv]=Skrivbordseffekter
|
||||
Comment[tr]=Masaüstü Efektleri
|
||||
Comment[uk]=Ефекти стільниці
|
||||
Comment[x-test]=xxDesktop Effectsxx
|
||||
Comment[zh_CN]=桌面特效
|
||||
Comment[zh_TW]=桌面效果
|
||||
|
||||
Icon=preferences-desktop-effects
|
||||
Type=Service
|
||||
X-KDE-PluginInfo-License=GPL
|
||||
X-KDE-PluginInfo-Name=kcm_kwin_effects
|
||||
X-KDE-ServiceTypes=Plasma/Generic
|
||||
X-Plasma-API=declarativeappletscript
|
||||
|
||||
X-Plasma-MainScript=ui/main.qml
|
Loading…
Reference in a new issue