diff --git a/kcmkwin/CMakeLists.txt b/kcmkwin/CMakeLists.txt --- a/kcmkwin/CMakeLists.txt +++ b/kcmkwin/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory( kwinscreenedges ) add_subdirectory( kwinscripts ) add_subdirectory( kwindesktop ) +add_subdirectory( kwineffects ) if( KWIN_BUILD_TABBOX ) add_subdirectory( kwintabbox ) diff --git a/kcmkwin/common/effectmodel.h b/kcmkwin/common/effectmodel.h --- a/kcmkwin/common/effectmodel.h +++ b/kcmkwin/common/effectmodel.h @@ -177,6 +177,16 @@ **/ 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 @@ QString serviceName; QString iconName; Status effectStatus; + Status originalStatus; bool enabledByDefault; bool enabledByDefaultFunction; QUrl video; @@ -220,7 +231,6 @@ 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 m_effectsList; diff --git a/kcmkwin/common/effectmodel.cpp b/kcmkwin/common/effectmodel.cpp --- a/kcmkwin/common/effectmodel.cpp +++ b/kcmkwin/common/effectmodel.cpp @@ -199,7 +199,7 @@ // 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 @@ 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 @@ } else { effect.effectStatus = effectStatus(effect.enabledByDefault); } + effect.originalStatus = effect.effectStatus; effect.video = data.video; effect.website = QUrl(); effect.supported = true; @@ -289,6 +290,7 @@ 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 @@ 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 @@ 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() << SupportedRole); } @@ -446,16 +450,6 @@ 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 @@ 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 @@ } } +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) diff --git a/kcmkwin/kwincompositing/CMakeLists.txt b/kcmkwin/kwincompositing/CMakeLists.txt --- a/kcmkwin/kwincompositing/CMakeLists.txt +++ b/kcmkwin/kwincompositing/CMakeLists.txt @@ -6,80 +6,32 @@ 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 ################# # now create config headers configure_file(config-prefix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-prefix.h ) configure_file(config-compiler.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-compiler.h ) 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 ################# diff --git a/kcmkwin/kwincompositing/effectconfig.h b/kcmkwin/kwincompositing/effectconfig.h deleted file mode 100644 --- a/kcmkwin/kwincompositing/effectconfig.h +++ /dev/null @@ -1,47 +0,0 @@ -/************************************************************************** - * KWin - the KDE window manager * - * This file is part of the KDE project. * - * * - * Copyright (C) 2013 Antonis Tsiapaliokas * - * * - * 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 . * - **************************************************************************/ - - -#ifndef EFFECTCONFIG_H -#define EFFECTCONFIG_H -#include -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 diff --git a/kcmkwin/kwincompositing/effectconfig.cpp b/kcmkwin/kwincompositing/effectconfig.cpp deleted file mode 100644 --- a/kcmkwin/kwincompositing/effectconfig.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/************************************************************************** -* KWin - the KDE window manager * -* This file is part of the KDE project. * -* * -* Copyright (C) 2013 Antonis Tsiapaliokas * -* * -* 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 . * -**************************************************************************/ - -#include "effectconfig.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -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(serviceName, &dialog); - } - } - } else { - kcm = KPluginTrader::createInstanceFromQuery(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(&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 downloadDialog = new KNS3::DownloadDialog(QStringLiteral("kwineffect.knsrc")); - if (downloadDialog->exec() == QDialog::Accepted) { - emit effectListChanged(); - } - - delete downloadDialog; -} - -}//end namespace Compositing -}//end namespace KWin diff --git a/kcmkwin/kwincompositing/main.cpp b/kcmkwin/kwincompositing/main.cpp --- a/kcmkwin/kwincompositing/main.cpp +++ b/kcmkwin/kwincompositing/main.cpp @@ -21,42 +21,14 @@ #include "compositing.h" -#include "model.h" #include "ui_compositing.h" #include #include #include #include #include -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 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 @@ 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("effects"); registerPlugin("compositing"); ) diff --git a/kcmkwin/kwincompositing/model.h b/kcmkwin/kwincompositing/model.h deleted file mode 100644 --- a/kcmkwin/kwincompositing/model.h +++ /dev/null @@ -1,121 +0,0 @@ -/************************************************************************** - * KWin - the KDE window manager * - * This file is part of the KDE project. * - * * - * Copyright (C) 2013 Antonis Tsiapaliokas * - * * - * 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 . * - **************************************************************************/ - - -#ifndef MODEL_H -#define MODEL_H - -#include -#include -#include -#include -#include - -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 diff --git a/kcmkwin/kwincompositing/model.cpp b/kcmkwin/kwincompositing/model.cpp deleted file mode 100644 --- a/kcmkwin/kwincompositing/model.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/************************************************************************** -* KWin - the KDE window manager * -* This file is part of the KDE project. * -* * -* Copyright (C) 2013 Antonis Tsiapaliokas * -* * -* 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 . * -**************************************************************************/ - -#include "model.h" -#include "effectconfig.h" -#include "effectmodel.h" -#include "compositing.h" -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -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(); - qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "EffectConfig"); - qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "EffectFilterModel"); - qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "Compositing"); - qmlRegisterType("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(QStringLiteral("filterModel"))) { - model->syncConfig(); - } - if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { - compositing->save(); - } -} - -void EffectView::load() -{ - if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { - model->load(); - } - if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { - compositing->reset(); - } -} - -void EffectView::defaults() -{ - if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { - model->defaults(); - } - if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { - compositing->defaults(); - } -} - -void EffectView::slotImplicitSizeChanged() -{ - setMinimumSize(QSize(rootObject()->property("implicitWidth").toInt(), - rootObject()->property("implicitHeight").toInt())); -} - -}//end namespace Compositing -}//end namespace KWin diff --git a/kcmkwin/kwincompositing/qml/Effect.qml b/kcmkwin/kwincompositing/qml/Effect.qml deleted file mode 100644 --- a/kcmkwin/kwincompositing/qml/Effect.qml +++ /dev/null @@ -1,166 +0,0 @@ -/************************************************************************** -* KWin - the KDE window manager * -* This file is part of the KDE project. * -* * -* Copyright (C) 2013 Antonis Tsiapaliokas * -* * -* 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 . * -**************************************************************************/ - - -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; - } - } - onLoaded: { - videoItem.item.showHide(); - } - } - } - 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 diff --git a/kcmkwin/kwincompositing/qml/EffectView.qml b/kcmkwin/kwincompositing/qml/EffectView.qml deleted file mode 100644 --- a/kcmkwin/kwincompositing/qml/EffectView.qml +++ /dev/null @@ -1,179 +0,0 @@ -/************************************************************************** -* KWin - the KDE window manager * -* This file is part of the KDE project. * -* * -* Copyright (C) 2013 Antonis Tsiapaliokas * -* * -* 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 . * -**************************************************************************/ - -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 diff --git a/kcmkwin/kwincompositing/qml/main.qml b/kcmkwin/kwincompositing/qml/main.qml deleted file mode 100644 --- a/kcmkwin/kwincompositing/qml/main.qml +++ /dev/null @@ -1,31 +0,0 @@ -/************************************************************************** -* KWin - the KDE window manager * -* This file is part of the KDE project. * -* * -* Copyright (C) 2013 Antonis Tsiapaliokas * -* * -* 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 . * -**************************************************************************/ - -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() - } -} diff --git a/kcmkwin/kwincompositing/test/effectmodeltest.h b/kcmkwin/kwincompositing/test/effectmodeltest.h deleted file mode 100644 --- a/kcmkwin/kwincompositing/test/effectmodeltest.h +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************** - * KWin - the KDE window manager * - * This file is part of the KDE project. * - * * - * Copyright (C) 2013 Antonis Tsiapaliokas * - * * - * 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 . * - **************************************************************************/ - - -#ifndef EFFECTMODELTEST_H -#define EFFECTMODELTEST_H - -#include - -class EffectModelTest : public QObject { - - Q_OBJECT - -public: - EffectModelTest(QObject *parent = 0); - -private Q_SLOTS: - void testEffectModel(); - void testEffectFilterModel(); -}; -#endif diff --git a/kcmkwin/kwincompositing/test/effectmodeltest.cpp b/kcmkwin/kwincompositing/test/effectmodeltest.cpp deleted file mode 100644 --- a/kcmkwin/kwincompositing/test/effectmodeltest.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************** -* KWin - the KDE window manager * -* This file is part of the KDE project. * -* * -* Copyright (C) 2013 Antonis Tsiapaliokas * -* * -* 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 . * -**************************************************************************/ - -#include "modeltest.h" -#include "../model.h" -#include "effectmodeltest.h" - -#include "effectmodel.h" - -#include - -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) diff --git a/kcmkwin/kwincompositing/test/modeltest.h b/kcmkwin/kwincompositing/test/modeltest.h deleted file mode 100644 --- a/kcmkwin/kwincompositing/test/modeltest.h +++ /dev/null @@ -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 -#include -#include - -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 insert; - QStack remove; - - bool fetchingMore; - - QList changing; -}; - -#endif diff --git a/kcmkwin/kwincompositing/test/modeltest.cpp b/kcmkwin/kwincompositing/test/modeltest.cpp deleted file mode 100644 --- a/kcmkwin/kwincompositing/test/modeltest.cpp +++ /dev/null @@ -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 - -#include "modeltest.h" - -#include - -/*! - 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 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() ); - } - variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert() ); - } - variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert() ); - } - - // General Purpose roles that should return a QSize - variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert() ); - } - - // General Purpose roles that should return a QFont - QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); - if ( fontVariant.isValid() ) { - QVERIFY( fontVariant.canConvert() ); - } - - // 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::BackgroundColorRole ); - if ( colorVariant.isValid() ) { - QVERIFY( colorVariant.canConvert() ); - } - - colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); - if ( colorVariant.isValid() ) { - QVERIFY( colorVariant.canConvert() ); - } - - // 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); -} - diff --git a/kcmkwin/kwineffects/CMakeLists.txt b/kcmkwin/kwineffects/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/CMakeLists.txt @@ -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) diff --git a/kcmkwin/kwineffects/Messages.sh b/kcmkwin/kwineffects/Messages.sh new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT `find . -name \*.cpp -o -name \*.qml` -o $podir/kcm_kwin_effects.pot diff --git a/kcmkwin/kwineffects/effectsfilterproxymodel.h b/kcmkwin/kwineffects/effectsfilterproxymodel.h new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/effectsfilterproxymodel.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 Vlad Zagorodniy + * + * 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 . + */ + +#pragma once + +#include + +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 diff --git a/kcmkwin/kwineffects/effectsfilterproxymodel.cpp b/kcmkwin/kwineffects/effectsfilterproxymodel.cpp new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/effectsfilterproxymodel.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 Vlad Zagorodniy + * + * 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 . + */ + +#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 diff --git a/kcmkwin/kwineffects/kcm.h b/kcmkwin/kwineffects/kcm.h new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/kcm.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 Vlad Zagorodniy + * + * 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 . + */ + +#pragma once + +#include + +#include +#include + +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 diff --git a/kcmkwin/kwineffects/kcm.cpp b/kcmkwin/kwineffects/kcm.cpp new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/kcm.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2019 Vlad Zagorodniy + * + * 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 . + */ + +#include "kcm.h" +#include "effectmodel.h" +#include "effectsfilterproxymodel.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(DesktopEffectsKCMFactory, + "kcm_kwin_effects.json", + registerPlugin();) + +namespace KWin +{ + +DesktopEffectsKCM::DesktopEffectsKCM(QObject *parent, const QVariantList &args) + : KQuickAddons::ConfigModule(parent, args) + , m_model(new EffectModel(this)) +{ + qmlRegisterType("org.kde.private.kcms.kwin.effects", 1, 0, "EffectsFilterProxyModel"); + + auto about = new KAboutData( + QStringLiteral("kcm_kwin_effects"), + i18n("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 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( + 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(pluginId, parent); +} + +void DesktopEffectsKCM::configure(const QString &pluginId, QQuickItem *context) +{ + const QModelIndex idx = m_model->findByPluginId(pluginId); + if (!idx.isValid()) { + return; + } + + QPointer 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" diff --git a/kcmkwin/kwincompositing/kcmkwineffects.desktop b/kcmkwin/kwineffects/kcm_kwin_effects.desktop rename from kcmkwin/kwincompositing/kcmkwineffects.desktop rename to kcmkwin/kwineffects/kcm_kwin_effects.desktop --- a/kcmkwin/kwincompositing/kcmkwineffects.desktop +++ b/kcmkwin/kwineffects/kcm_kwin_effects.desktop @@ -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 diff --git a/kcmkwin/kwincompositing/kwineffect.knsrc b/kcmkwin/kwineffects/kwineffect.knsrc rename from kcmkwin/kwincompositing/kwineffect.knsrc rename to kcmkwin/kwineffects/kwineffect.knsrc diff --git a/kcmkwin/kwineffects/package/contents/ui/Effect.qml b/kcmkwin/kwineffects/package/contents/ui/Effect.qml new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/package/contents/ui/Effect.qml @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2013 Antonis Tsiapaliokas + * Copyright (C) 2019 Vlad Zagorodniy + * + * 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 . + * + */ + +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 + + onLoaded: videoItem.item.showHide() + + 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 + } +} diff --git a/kcmkwin/kwincompositing/qml/Video.qml b/kcmkwin/kwineffects/package/contents/ui/Video.qml rename from kcmkwin/kwincompositing/qml/Video.qml rename to kcmkwin/kwineffects/package/contents/ui/Video.qml diff --git a/kcmkwin/kwineffects/package/contents/ui/main.qml b/kcmkwin/kwineffects/package/contents/ui/main.qml new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/package/contents/ui/main.qml @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2013 Antonis Tsiapaliokas + * Copyright (C) 2019 Vlad Zagorodniy + * + * 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 . + */ + +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) + } + } + } +} diff --git a/kcmkwin/kwineffects/package/metadata.desktop b/kcmkwin/kwineffects/package/metadata.desktop new file mode 100644 --- /dev/null +++ b/kcmkwin/kwineffects/package/metadata.desktop @@ -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]=Skrivebords­effektar +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]=Skrivebords­effektar +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