diff --git a/kcmkwin/kwindesktop/CMakeLists.txt b/kcmkwin/kwindesktop/CMakeLists.txt --- a/kcmkwin/kwindesktop/CMakeLists.txt +++ b/kcmkwin/kwindesktop/CMakeLists.txt @@ -6,15 +6,22 @@ ########### next target ############### -set(kcm_kwin_virtualdesktops_PART_SRCS virtualdesktops.cpp desktopsmodel.cpp ../../virtualdesktopsdbustypes.cpp) +set(kcm_kwin_virtualdesktops_PART_SRCS + virtualdesktops.cpp + animationsmodel.cpp + desktopsmodel.cpp + ../../virtualdesktopsdbustypes.cpp +) add_library(kcm_kwin_virtualdesktops MODULE ${kcm_kwin_virtualdesktops_PART_SRCS}) target_link_libraries(kcm_kwin_virtualdesktops Qt5::DBus KF5::I18n KF5::KCMUtils KF5::QuickAddons + KF5::XmlGui + kcmkwincommon ) kcoreaddons_desktop_to_json(kcm_kwin_virtualdesktops "kcm_kwin_virtualdesktops.desktop") diff --git a/kcmkwin/kwindesktop/animationsmodel.h b/kcmkwin/kwindesktop/animationsmodel.h new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/animationsmodel.h @@ -0,0 +1,71 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2018 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 "effectmodel.h" + +namespace KWin +{ + +class AnimationsModel : public EffectModel +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(bool currentConfigurable READ currentConfigurable NOTIFY currentConfigurableChanged) + +public: + explicit AnimationsModel(QObject *parent = nullptr); + + bool enabled() const; + void setEnabled(bool enabled); + + int currentIndex() const; + void setCurrentIndex(int index); + + bool currentConfigurable() const; + + void load(); + void save(); + void defaults(); + bool needsSave() const; + +Q_SIGNALS: + void enabledChanged(); + void currentIndexChanged(); + void currentConfigurableChanged(); + +protected: + bool shouldStore(const EffectData &data) const override; + +private: + Status status(int row) const; + bool modelCurrentEnabled() const; + int modelCurrentIndex() const; + + bool m_enabled = false; + int m_currentIndex = -1; + bool m_currentConfigurable = false; + + Q_DISABLE_COPY(AnimationsModel) +}; + +} diff --git a/kcmkwin/kwindesktop/animationsmodel.cpp b/kcmkwin/kwindesktop/animationsmodel.cpp new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/animationsmodel.cpp @@ -0,0 +1,154 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2018 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 "animationsmodel.h" + +namespace KWin +{ + +AnimationsModel::AnimationsModel(QObject *parent) + : EffectModel(parent) +{ + connect(this, &AnimationsModel::currentIndexChanged, this, + [this] { + const QModelIndex index_ = index(m_currentIndex, 0); + if (!index_.isValid()) { + return; + } + const bool configurable = index_.data(ConfigurableRole).toBool(); + if (configurable != m_currentConfigurable) { + m_currentConfigurable = configurable; + emit currentConfigurableChanged(); + } + } + ); +} + +bool AnimationsModel::enabled() const +{ + return m_enabled; +} + +void AnimationsModel::setEnabled(bool enabled) +{ + if (m_enabled != enabled) { + m_enabled = enabled; + emit enabledChanged(); + } +} + +int AnimationsModel::currentIndex() const +{ + return m_currentIndex; +} + +void AnimationsModel::setCurrentIndex(int index) +{ + if (m_currentIndex != index) { + m_currentIndex = index; + emit currentIndexChanged(); + } +} + +bool AnimationsModel::currentConfigurable() const +{ + return m_currentConfigurable; +} + +bool AnimationsModel::shouldStore(const EffectData &data) const +{ + return data.untranslatedCategory.contains( + QStringLiteral("Virtual Desktop Switching Animation"), Qt::CaseInsensitive); +} + +EffectModel::Status AnimationsModel::status(int row) const +{ + return Status(data(index(row, 0), static_cast(EffectStatusRole)).toInt()); +} + +bool AnimationsModel::modelCurrentEnabled() const +{ + for (int i = 0; i < rowCount(); ++i) { + if (status(i) != Status::Disabled) { + return true; + } + } + + return false; +} + +int AnimationsModel::modelCurrentIndex() const +{ + for (int i = 0; i < rowCount(); ++i) { + if (status(i) != Status::Disabled) { + return i; + } + } + + return 0; +} + +void AnimationsModel::load() +{ + EffectModel::load(); + setEnabled(modelCurrentEnabled()); + setCurrentIndex(modelCurrentIndex()); +} + +void AnimationsModel::save() +{ + for (int i = 0; i < rowCount(); ++i) { + const auto status = (m_enabled && i == m_currentIndex) + ? EffectModel::Status::Enabled + : EffectModel::Status::Disabled; + updateEffectStatus(index(i, 0), status); + } + + EffectModel::save(); +} + +void AnimationsModel::defaults() +{ + EffectModel::defaults(); + setEnabled(modelCurrentEnabled()); + setCurrentIndex(modelCurrentIndex()); +} + +bool AnimationsModel::needsSave() const +{ + KConfigGroup kwinConfig(KSharedConfig::openConfig("kwinrc"), "Plugins"); + + for (int i = 0; i < rowCount(); ++i) { + const QModelIndex index_ = index(i, 0); + const bool enabledConfig = kwinConfig.readEntry( + index_.data(ServiceNameRole).toString() + QLatin1String("Enabled"), + index_.data(EnabledByDefaultRole).toBool() + ); + const bool enabled = (m_enabled && i == m_currentIndex); + + if (enabled != enabledConfig) { + return true; + } + } + + return false; +} + +} diff --git a/kcmkwin/kwindesktop/package/contents/ui/main.qml b/kcmkwin/kwindesktop/package/contents/ui/main.qml --- a/kcmkwin/kwindesktop/package/contents/ui/main.qml +++ b/kcmkwin/kwindesktop/package/contents/ui/main.qml @@ -200,6 +200,49 @@ onCheckedChanged: kcm.navWraps = checked } + RowLayout { + Layout.fillWidth: true + + QtControls.CheckBox { + id: animationEnabled + + text: i18n("Show animation when switching:") + + checked: kcm.animationsModel.enabled + + onCheckedChanged: kcm.animationsModel.enabled = checked + } + + QtControls.ComboBox { + enabled: animationEnabled.checked + + model: kcm.animationsModel + textRole: "NameRole" + currentIndex: kcm.animationsModel.currentIndex + onActivated: kcm.animationsModel.currentIndex = currentIndex + } + + QtControls.Button { + enabled: animationEnabled.checked && kcm.animationsModel.currentConfigurable + + icon.name: "configure" + + onClicked: kcm.configureAnimation() + } + + QtControls.Button { + enabled: animationEnabled.checked + + icon.name: "dialog-information" + + onClicked: kcm.showAboutAnimation() + } + + Item { + Layout.fillWidth: true + } + } + RowLayout { Layout.fillWidth: true diff --git a/kcmkwin/kwindesktop/virtualdesktops.h b/kcmkwin/kwindesktop/virtualdesktops.h --- a/kcmkwin/kwindesktop/virtualdesktops.h +++ b/kcmkwin/kwindesktop/virtualdesktops.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Eike Hein + * Copyright (C) 2018 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 @@ -24,6 +25,7 @@ namespace KWin { +class AnimationsModel; class DesktopsModel; class VirtualDesktops : public KQuickAddons::ConfigModule @@ -35,6 +37,7 @@ Q_PROPERTY(bool osdEnabled READ osdEnabled WRITE setOsdEnabled NOTIFY osdEnabledChanged) Q_PROPERTY(int osdDuration READ osdDuration WRITE setOsdDuration NOTIFY osdDurationChanged) Q_PROPERTY(bool osdTextOnly READ osdTextOnly WRITE setOsdTextOnly NOTIFY osdTextOnlyChanged) + Q_PROPERTY(QAbstractItemModel *animationsModel READ animationsModel CONSTANT) public: explicit VirtualDesktops(QObject *parent = nullptr, const QVariantList &list = QVariantList()); @@ -54,6 +57,8 @@ int osdTextOnly() const; void setOsdTextOnly(bool textOnly); + QAbstractItemModel *animationsModel() const; + Q_SIGNALS: void navWrapsChanged() const; void osdEnabledChanged() const; @@ -65,6 +70,9 @@ void save() override; void defaults() override; + void configureAnimation(); + void showAboutAnimation(); + private Q_SLOTS: void updateNeedsSave(); @@ -75,6 +83,7 @@ bool m_osdEnabled; int m_osdDuration; bool m_osdTextOnly; + AnimationsModel *m_animationsModel; }; } diff --git a/kcmkwin/kwindesktop/virtualdesktops.cpp b/kcmkwin/kwindesktop/virtualdesktops.cpp --- a/kcmkwin/kwindesktop/virtualdesktops.cpp +++ b/kcmkwin/kwindesktop/virtualdesktops.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Eike Hein + * Copyright (C) 2018 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 @@ -16,12 +17,21 @@ */ #include "virtualdesktops.h" +#include "animationsmodel.h" #include "desktopsmodel.h" +#include #include +#include #include #include #include +#include + +#include +#include +#include +#include K_PLUGIN_FACTORY_WITH_JSON(VirtualDesktopsFactory, "kcm_kwin_virtualdesktops.json", registerPlugin();) @@ -36,6 +46,7 @@ , m_osdEnabled(false) , m_osdDuration(1000) , m_osdTextOnly(false) + , m_animationsModel(new AnimationsModel(this)) { KAboutData *about = new KAboutData(QStringLiteral("kcm_kwin_virtualdesktops"), i18n("Configure Virtual Desktops"), @@ -46,6 +57,10 @@ QObject::connect(m_desktopsModel, &KWin::DesktopsModel::userModifiedChanged, this, &VirtualDesktops::updateNeedsSave); + connect(m_animationsModel, &AnimationsModel::enabledChanged, + this, &VirtualDesktops::updateNeedsSave); + connect(m_animationsModel, &AnimationsModel::currentIndexChanged, + this, &VirtualDesktops::updateNeedsSave); } VirtualDesktops::~VirtualDesktops() @@ -121,6 +136,11 @@ } } +QAbstractItemModel *VirtualDesktops::animationsModel() const +{ + return m_animationsModel; +} + void VirtualDesktops::load() { KConfigGroup navConfig(m_kwinConfig, "Windows"); @@ -132,11 +152,14 @@ KConfigGroup osdSettings(m_kwinConfig, "Script-desktopchangeosd"); setOsdDuration(osdSettings.readEntry("PopupHideDelay", 1000)); setOsdTextOnly(osdSettings.readEntry("TextOnly", false)); + + m_animationsModel->load(); } void VirtualDesktops::save() { m_desktopsModel->syncWithServer(); + m_animationsModel->save(); KConfigGroup navConfig(m_kwinConfig, "Windows"); navConfig.writeEntry("RollOverDesktops", m_navWraps); @@ -160,21 +183,128 @@ void VirtualDesktops::defaults() { m_desktopsModel->setRows(1); + m_animationsModel->defaults(); setNavWraps(true); setOsdEnabled(false); setOsdDuration(1000); setOsdTextOnly(false); } +void VirtualDesktops::configureAnimation() +{ + const QModelIndex index = m_animationsModel->index(m_animationsModel->currentIndex(), 0); + if (!index.isValid()) { + return; + } + + const QString name = index.data(AnimationsModel::NameRole).toString(); + const QString serviceName = index.data(AnimationsModel::ServiceNameRole).toString(); + + QPointer configDialog = new QDialog(); + + KCModule *kcm = KPluginTrader::createInstanceFromQuery( + QStringLiteral("kwin/effects/configs/"), + QString(), + QStringLiteral("'%1' in [X-KDE-ParentComponents]").arg(serviceName), + configDialog + ); + + if (!kcm) { + delete configDialog; + return; + } + + configDialog->setWindowTitle(name); + configDialog->setLayout(new QVBoxLayout); + + auto buttons = new QDialogButtonBox( + QDialogButtonBox::Ok | + QDialogButtonBox::Cancel | + QDialogButtonBox::RestoreDefaults, + configDialog + ); + QObject::connect(buttons, &QDialogButtonBox::accepted, configDialog, &QDialog::accept); + QObject::connect(buttons, &QDialogButtonBox::rejected, configDialog, &QDialog::reject); + QObject::connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, kcm, &KCModule::defaults); + + auto showWidget = new QWidget(configDialog); + auto layout = new QVBoxLayout; + showWidget->setLayout(layout); + layout->addWidget(kcm); + configDialog->layout()->addWidget(showWidget); + configDialog->layout()->addWidget(buttons); + + if (configDialog->exec() == QDialog::Accepted) { + kcm->save(); + } else if (!configDialog.isNull()) { + kcm->load(); + } + + delete configDialog; +} + +void VirtualDesktops::showAboutAnimation() +{ + const QModelIndex index = m_animationsModel->index(m_animationsModel->currentIndex(), 0); + if (!index.isValid()) { + return; + } + + const QString name = index.data(AnimationsModel::NameRole).toString(); + const QString comment = index.data(AnimationsModel::DescriptionRole).toString(); + const QString author = index.data(AnimationsModel::AuthorNameRole).toString(); + const QString email = index.data(AnimationsModel::AuthorEmailRole).toString(); + const QString website = index.data(AnimationsModel::WebsiteRole).toString(); + const QString version = index.data(AnimationsModel::VersionRole).toString(); + const QString license = index.data(AnimationsModel::LicenseRole).toString(); + const QString icon = index.data(AnimationsModel::IconNameRole).toString(); + + const KAboutLicense::LicenseKey licenseType = KAboutLicense::byKeyword(license).key(); + + KAboutData aboutData( + name, // Plugin name + name, // Display name + version, // Version + comment, // Short description + licenseType, // License + QString(), // Copyright statement + QString(), // Other text + website.toLatin1() // Home page + ); + aboutData.setProgramLogo(icon); + + const QStringList authors = author.split(','); + const QStringList emails = email.split(','); + + if (authors.count() == emails.count()) { + int i = 0; + for (const QString &author : authors) { + if (!author.isEmpty()) { + aboutData.addAuthor(i18n(author.toUtf8()), QString(), emails[i]); + } + i++; + } + } + + QPointer aboutPlugin = new KAboutApplicationDialog(aboutData); + aboutPlugin->exec(); + + delete aboutPlugin; +} + void VirtualDesktops::updateNeedsSave() { bool needsSave = false; if (m_desktopsModel->userModified()) { needsSave = true; } + if (m_animationsModel->needsSave()) { + needsSave = true; + } + KConfigGroup navConfig(m_kwinConfig, "Windows"); if (m_navWraps != navConfig.readEntry("RollOverDesktops", true)) {