Commit 8350c0f2 authored by Valerio Pilo's avatar Valerio Pilo

[kcmkwin/kwindecoration] Rewrite the KWin decorations settings as a ConfigModule

Summary:
* Wrote new KCM based on KQuickAddons::ConfigModule.
* Remade QMLs for Buttons and Themes tabs.
* Updated bridge model code for new plugin lookup API (fixes warnings).
* Fixed decoration shadow changing messing with the previews sizes.
* Fixed button drag and drop issues (see D18104).
* Fixed default settings button behavior and detection of settings changes.
* Updated Get Hot New Stuff.
* Removed apply button in previewbridge.cpp: After applying changes, a theme's KCModule is invalidated.

BUG: 389431
BUG: 350122
BUG: 346222
BUG: 342816
BUG: 397595

{F6574963} | {F6574962} | {F6574961} | {F6574960}

Test Plan:
* Verified saving and loading for every setting
* Checked shadows of Breeze and Oxygen
* Tested all possible drag&drop operations on both sides of the fake titlebar
* Changed color schemes (with `kcmshell5 colors`) while showing the Themes tab to see if all previews update correctly their palettes
* Tested on a fresh Neon-developer account, via kcmshell and systemsettings

Reviewers: #vdg, abetts, ngraham, #kwin, davidedmundson

Reviewed By: #vdg, #kwin, davidedmundson

Subscribers: zzag, GB_2, ngraham, broulik, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D18458
parent 698b40db
......@@ -3,39 +3,28 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kcmkwindecoration\")
add_subdirectory(declarative-plugin)
set(kcm_kwindecoration_PART_SRCS
set(kcmkwindecoration_SRCS
kcm.cpp
utils.cpp
decorationmodel.cpp
declarative-plugin/buttonsmodel.cpp
)
ki18n_wrap_ui(kcm_kwindecoration_PART_SRCS
kcm.ui
)
add_library(kcm_kwindecoration MODULE ${kcmkwindecoration_SRCS})
add_library(kcm_kwindecoration MODULE ${kcm_kwindecoration_PART_SRCS})
target_link_libraries(kcm_kwindecoration
KDecoration2::KDecoration
Qt5::DBus
Qt5::Quick
Qt5::QuickWidgets
Qt5::UiTools
KF5::Completion
KF5::ConfigWidgets
KF5::Declarative
KF5::I18n
KF5::QuickAddons
KF5::NewStuff
KF5::WindowSystem
KF5::Service
Qt5::Quick
)
install(TARGETS kcm_kwindecoration DESTINATION ${PLUGIN_INSTALL_DIR} )
########### install files ###############
kcoreaddons_desktop_to_json(kcm_kwindecoration "kwindecoration.desktop" SERVICE_TYPES kcmodule.desktop)
# This desktop file is installed only for retrocompatibility with sycoca
install(FILES kwindecoration.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
install(FILES window-decorations.knsrc DESTINATION ${KDE_INSTALL_CONFDIR})
install(TARGETS kcm_kwindecoration DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
install( FILES kwindecoration.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
install( FILES
qml/main.qml
qml/Buttons.qml
qml/ButtonGroup.qml
qml/Previews.qml
DESTINATION ${DATA_INSTALL_DIR}/kwin/kcm_kwindecoration)
kpackage_install_package(package kcm_kwindecoration kcms)
#!bin/sh
$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp
$XGETTEXT `find . -name \*.qml -o -name \*.cpp -o -name \*.h` -o $podir/kcmkwindecoration.pot
#! /usr/bin/env bash
$EXTRACTRC `find . -name "*.ui"` >> rc.cpp || exit 11
$XGETTEXT `find . -name "*.cpp" -o -name "*.qml"` -o $podir/kcmkwindecoration.pot
rm -f rc.cpp
......@@ -153,8 +153,8 @@ void ButtonsModel::add(DecorationButtonType type)
void ButtonsModel::add(int index, int type)
{
beginInsertRows(QModelIndex(), index + 1, index+1);
m_buttons.insert(index+1, KDecoration2::DecorationButtonType(type));
beginInsertRows(QModelIndex(), index, index);
m_buttons.insert(index, KDecoration2::DecorationButtonType(type));
endInsertRows();
}
......@@ -178,6 +178,24 @@ void ButtonsModel::move(int sourceIndex, int targetIndex)
endMoveRows();
}
void ButtonsModel::clear()
{
beginResetModel();
m_buttons.clear();
endResetModel();
}
void ButtonsModel::replace(const QVector< DecorationButtonType > &buttons)
{
if (buttons.isEmpty()) {
return;
}
beginResetModel();
m_buttons = buttons;
endResetModel();
}
}
}
......@@ -45,11 +45,13 @@ public:
return m_buttons;
}
Q_INVOKABLE void clear();
Q_INVOKABLE void remove(int index);
Q_INVOKABLE void up(int index);
Q_INVOKABLE void down(int index);
Q_INVOKABLE void move(int sourceIndex, int targetIndex);
void replace(const QVector< DecorationButtonType > &buttons);
void add(DecorationButtonType type);
Q_INVOKABLE void add(int index, int type);
......
......@@ -97,7 +97,6 @@ void PreviewBridge::setPlugin(const QString &plugin)
return;
}
m_plugin = plugin;
qDebug() << "Plugin changed to: " << m_plugin;
emit pluginChanged();
}
......@@ -123,22 +122,20 @@ QString PreviewBridge::plugin() const
void PreviewBridge::createFactory()
{
m_factory.clear();
if (m_plugin.isNull()) {
setValid(false);
qDebug() <<"Plugin not set";
qWarning() << "Plugin not set";
return;
}
const auto offers = KPluginTrader::self()->query(s_pluginName,
s_pluginName,
QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(m_plugin));
if (offers.isEmpty()) {
setValid(false);
qDebug() << "no offers";
return;
const auto offers = KPluginTrader::self()->query(s_pluginName, s_pluginName);
auto item = std::find_if(offers.constBegin(), offers.constEnd(), [this](const auto &plugin) { return plugin.pluginName() == m_plugin; });
if (item != offers.constEnd()) {
KPluginLoader loader(item->libraryPath());
m_factory = loader.factory();
}
KPluginLoader loader(offers.first().libraryPath());
m_factory = loader.factory();
qDebug() << "Factory: " << !m_factory.isNull();
setValid(!m_factory.isNull());
}
......@@ -212,22 +209,17 @@ void PreviewBridge::configure()
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, this, save);
connect(reset, &QPushButton::clicked, kcm, &KCModule::load);
auto changedSignal = static_cast<void(KCModule::*)(bool)>(&KCModule::changed);
connect(kcm, changedSignal, apply, &QPushButton::setEnabled);
connect(kcm, changedSignal, reset, &QPushButton::setEnabled);
connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, kcm, &KCModule::defaults);
......
......@@ -24,7 +24,7 @@
#include <KDecoration2/Decoration>
#include <QDebug>
#include <QPainter>
namespace KDecoration2
{
......@@ -132,7 +132,17 @@ void PreviewButtonItem::paint(QPainter *painter)
if (!m_button) {
return;
}
m_button->paint(painter, QRect(0, 0, width(), height()));
QRect size { 0, 0, (int)width(), (int)height() };
m_button->paint(painter, size);
painter->setCompositionMode(QPainter::CompositionMode_SourceAtop);
painter->fillRect(size, m_color);
}
void PreviewButtonItem::setColor(const QColor color)
{
m_color = color;
m_color.setAlpha(127);
update();
}
}
......
......@@ -21,6 +21,7 @@
#define KDECOARTIONS_PREVIEW_BUTTON_ITEM_H
#include <QQuickPaintedItem>
#include <QColor>
#include <QPointer>
#include <KDecoration2/DecorationButton>
......@@ -39,6 +40,7 @@ class PreviewButtonItem : public QQuickPaintedItem
Q_PROPERTY(KDecoration2::Preview::PreviewBridge *bridge READ bridge WRITE setBridge NOTIFY bridgeChanged)
Q_PROPERTY(KDecoration2::Preview::Settings *settings READ settings WRITE setSettings NOTIFY settingsChanged)
Q_PROPERTY(int type READ typeAsInt WRITE setType NOTIFY typeChanged)
Q_PROPERTY(QColor color READ color WRITE setColor)
public:
explicit PreviewButtonItem(QQuickItem *parent = nullptr);
......@@ -56,6 +58,9 @@ public:
void setType(KDecoration2::DecorationButtonType type);
void setType(int type);
const QColor &color() const { return m_color; }
void setColor(const QColor color);
Q_SIGNALS:
void bridgeChanged();
void typeChanged();
......@@ -67,6 +72,7 @@ protected:
private:
void createButton();
void syncGeometry();
QColor m_color;
QPointer<KDecoration2::Preview::PreviewBridge> m_bridge;
QPointer<KDecoration2::Preview::Settings> m_settings;
KDecoration2::Decoration *m_decoration = nullptr;
......
......@@ -69,7 +69,6 @@ PreviewClient::PreviewClient(DecoratedClient *c, Decoration *decoration)
connect(this, &PreviewClient::maximizedVerticallyChanged, c, &DecoratedClient::maximizedVerticallyChanged);
connect(this, &PreviewClient::maximizedHorizontallyChanged, c, &DecoratedClient::maximizedHorizontallyChanged);
connect(this, &PreviewClient::minimizableChanged, c, &DecoratedClient::minimizeableChanged);
// connect(this, &PreviewClient::modalChanged, c, &DecoratedClient::modalChanged);
connect(this, &PreviewClient::movableChanged, c, &DecoratedClient::moveableChanged);
connect(this, &PreviewClient::onAllDesktopsChanged, c, &DecoratedClient::onAllDesktopsChanged);
connect(this, &PreviewClient::resizableChanged, c, &DecoratedClient::resizeableChanged);
......@@ -81,7 +80,6 @@ PreviewClient::PreviewClient(DecoratedClient *c, Decoration *decoration)
connect(this, &PreviewClient::heightChanged, c, &DecoratedClient::heightChanged);
connect(this, &PreviewClient::iconChanged, c, &DecoratedClient::iconChanged);
connect(this, &PreviewClient::paletteChanged, c, &DecoratedClient::paletteChanged);
// connect(this, &PreviewClient::, c, &DecoratedClient::);
connect(this, &PreviewClient::maximizedVerticallyChanged, this,
[this]() {
emit maximizedChanged(isMaximized());
......@@ -353,12 +351,11 @@ void PreviewClient::setBordersTopEdge(bool enabled)
void PreviewClient::requestShowToolTip(const QString &text)
{
qDebug() << "tooltip show requested with text:" << text;
Q_UNUSED(text);
}
void PreviewClient::requestHideToolTip()
{
qDebug() << "tooltip hide requested";
}
void PreviewClient::requestClose()
......@@ -368,7 +365,6 @@ void PreviewClient::requestClose()
void PreviewClient::requestContextHelp()
{
qDebug() << "context help requested";
}
void PreviewClient::requestToggleMaximization(Qt::MouseButtons buttons)
......@@ -431,7 +427,6 @@ void PreviewClient::name(type variable) \
if (m_##variable == variable) { \
return; \
} \
qDebug() << "Setting " << #variable << ":" << variable;\
m_##variable = variable; \
emit variable##Changed(m_##variable); \
}
......
......@@ -78,15 +78,9 @@ void PreviewItem::createDecoration()
if (m_bridge.isNull() || m_settings.isNull() || m_decoration) {
return;
}
m_decoration = m_bridge->createDecoration(0);
if (!m_decoration) {
return;
}
m_decoration->setProperty("visualParent", QVariant::fromValue(this));
Decoration *decoration = m_bridge->createDecoration(0);
m_client = m_bridge->lastCreatedClient();
connect(m_decoration, &Decoration::bordersChanged, this, &PreviewItem::syncSize);
connect(m_decoration, &Decoration::shadowChanged, this, &PreviewItem::syncSize);
emit decorationChanged(m_decoration);
setDecoration(decoration);
}
Decoration *PreviewItem::decoration() const
......@@ -96,41 +90,15 @@ Decoration *PreviewItem::decoration() const
void PreviewItem::setDecoration(Decoration *deco)
{
if (m_decoration == deco) {
if (!deco || m_decoration == deco) {
return;
}
auto updateSlot = static_cast<void (QQuickItem::*)()>(&QQuickItem::update);
if (m_decoration) {
disconnect(m_decoration, &Decoration::bordersChanged, this, updateSlot);
}
m_decoration = deco;
m_decoration->setProperty("visualParent", QVariant::fromValue(this));
connect(m_decoration, &Decoration::bordersChanged, this, updateSlot);
connect(m_decoration, &Decoration::sectionUnderMouseChanged, this,
[this](Qt::WindowFrameSection section) {
switch (section) {
case Qt::TopRightSection:
case Qt::BottomLeftSection:
setCursor(Qt::SizeBDiagCursor);
return;
case Qt::TopLeftSection:
case Qt::BottomRightSection:
setCursor(Qt::SizeFDiagCursor);
return;
case Qt::TopSection:
case Qt::BottomSection:
setCursor(Qt::SizeVerCursor);
return;
case Qt::LeftSection:
case Qt::RightSection:
setCursor(Qt::SizeHorCursor);
return;
default:
setCursor(Qt::ArrowCursor);
}
}
);
connect(m_decoration, &KDecoration2::Decoration::shadowChanged, this, &PreviewItem::shadowChanged);
connect(m_decoration, &Decoration::bordersChanged, this, &PreviewItem::syncSize);
connect(m_decoration, &Decoration::shadowChanged, this, &PreviewItem::syncSize);
connect(m_decoration, &Decoration::shadowChanged, this, &PreviewItem::shadowChanged);
emit decorationChanged(m_decoration);
}
......
......@@ -60,14 +60,13 @@ QVariant DecorationsModel::data(const QModelIndex &index, int role) const
switch (role) {
case Qt::DisplayRole:
return d.visibleName;
case Qt::UserRole +4:
case PluginNameRole:
return d.pluginName;
case Qt::UserRole +5:
case ThemeNameRole:
return d.themeName;
case Qt::UserRole +6:
case ConfigurationRole:
return d.configuration;
}
return QVariant();
}
......@@ -75,9 +74,9 @@ QHash< int, QByteArray > DecorationsModel::roleNames() const
{
QHash<int, QByteArray> roles({
{Qt::DisplayRole, QByteArrayLiteral("display")},
{Qt::UserRole + 4, QByteArrayLiteral("plugin")},
{Qt::UserRole + 5, QByteArrayLiteral("theme")},
{Qt::UserRole +6, QByteArrayLiteral("configureable")}
{PluginNameRole, QByteArrayLiteral("plugin")},
{ThemeNameRole, QByteArrayLiteral("theme")},
{ConfigurationRole, QByteArrayLiteral("configureable")}
});
return roles;
}
......@@ -134,8 +133,8 @@ void DecorationsModel::init()
if (!metadata.isUndefined()) {
const auto decoSettingsMap = metadata.toObject().toVariantMap();
const QString &kns = findKNewStuff(decoSettingsMap);
if (!kns.isEmpty()) {
m_knsProvides.insert(kns, info.name().isEmpty() ? info.pluginName() : info.name());
if (!kns.isEmpty() && !m_knsProviders.contains(kns)) {
m_knsProviders.append(kns);
}
if (isThemeEngine(decoSettingsMap)) {
const QString keyword = themeListKeyword(decoSettingsMap);
......@@ -171,6 +170,7 @@ void DecorationsModel::init()
Data data;
data.pluginName = info.pluginName();
data.visibleName = info.name().isEmpty() ? info.pluginName() : info.name();
data.themeName = data.visibleName;
data.configuration = config;
m_plugins.emplace_back(std::move(data));
......
......@@ -31,6 +31,13 @@ namespace Configuration
class DecorationsModel : public QAbstractListModel
{
Q_OBJECT
public:
enum DecorationRole {
PluginNameRole = Qt::UserRole + 1,
ThemeNameRole,
ConfigurationRole,
};
public:
explicit DecorationsModel(QObject *parent = nullptr);
virtual ~DecorationsModel();
......@@ -41,8 +48,8 @@ public:
QModelIndex findDecoration(const QString &pluginName, const QString &themeName = QString()) const;
QMap<QString, QString> knsProviders() const {
return m_knsProvides;
QStringList knsProviders() const {
return m_knsProviders;
}
public Q_SLOTS:
......@@ -56,7 +63,7 @@ private:
bool configuration = false;
};
std::vector<Data> m_plugins;
QMap<QString, QString> m_knsProvides;
QStringList m_knsProviders;
};
}
......
This diff is collapsed.
/*
* Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
* Copyright (c) 2019 Valerio Pilo <vpilo@coldshock.net>
*
* 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* This library 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.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KDECORATIONS_KCM_H
#define KDECORATIONS_KCM_H
#include <kcmodule.h>
#include <ui_kcm.h>
#include <QAbstractItemModel>
#pragma once
#include "utils.h"
#include <KQuickAddons/ConfigModule>
class QAbstractItemModel;
class QSortFilterProxyModel;
class QQuickView;
class QQuickItem;
namespace KNS3
{
class DownloadDialog;
}
namespace KDecoration2
{
enum class BorderSize;
namespace Preview
{
class PreviewBridge;
class ButtonsModel;
}
namespace Configuration
{
class DecorationsModel;
}
}
class ConfigurationForm : public QWidget, public Ui::KCMForm
{
public:
explicit ConfigurationForm(QWidget* parent);
};
class ConfigurationModule : public KCModule
class KCMKWinDecoration : public KQuickAddons::ConfigModule
{
Q_OBJECT
Q_PROPERTY(QSortFilterProxyModel *themesModel READ themesModel CONSTANT)
Q_PROPERTY(QStringList borderSizesModel READ borderSizesModel CONSTANT)
Q_PROPERTY(int borderSize READ borderSize WRITE setBorderSize NOTIFY borderSizeChanged)
Q_PROPERTY(int theme READ theme WRITE setTheme NOTIFY themeChanged)
Q_PROPERTY(QAbstractListModel *leftButtonsModel READ leftButtonsModel NOTIFY buttonsChanged)
Q_PROPERTY(QAbstractListModel *rightButtonsModel READ rightButtonsModel NOTIFY buttonsChanged)
Q_PROPERTY(QAbstractListModel *availableButtonsModel READ availableButtonsModel CONSTANT)
Q_PROPERTY(bool closeOnDoubleClickOnMenu READ closeOnDoubleClickOnMenu WRITE setCloseOnDoubleClickOnMenu NOTIFY closeOnDoubleClickOnMenuChanged)
public:
explicit ConfigurationModule(QWidget *parent = nullptr, const QVariantList &args = QVariantList());
virtual ~ConfigurationModule();
KCMKWinDecoration(QObject *parent, const QVariantList &arguments);
QSortFilterProxyModel *themesModel() const;
QAbstractListModel *leftButtonsModel();
QAbstractListModel *rightButtonsModel();
QAbstractListModel *availableButtonsModel() const;
QStringList borderSizesModel() const;
int borderSize() const;
int theme() const;
bool closeOnDoubleClickOnMenu() const;
bool eventFilter(QObject *watched, QEvent *e) override;
void setBorderSize(int index);
void setBorderSize(KDecoration2::BorderSize size);
void setTheme(int index);
void setCloseOnDoubleClickOnMenu(bool enable);
Q_INVOKABLE void getNewStuff(QQuickItem *context);
Q_SIGNALS:
void themeChanged();
void buttonsChanged();
void borderSizeChanged();
void closeOnDoubleClickOnMenuChanged();
public Q_SLOTS:
void defaults() override;
void load() override;
void save() override;
void defaults() override;
protected:
void showEvent(QShowEvent *ev) override;
private Q_SLOTS:
void updateNeedsSave();
void reloadKWinSettings();
private:
void showKNS(const QString &config);
void updateColors();
DecorationsModel *m_model;
QSortFilterProxyModel *m_proxyModel;
ConfigurationForm *m_ui;
QQuickView *m_quickView;
Preview::ButtonsModel *m_leftButtons;
Preview::ButtonsModel *m_rightButtons;
Preview::ButtonsModel *m_availableButtons;
};
KDecoration2::Configuration::DecorationsModel *m_themesModel;
QSortFilterProxyModel *m_proxyThemesModel;
}
KDecoration2::Preview::ButtonsModel *m_leftButtonsModel;
KDecoration2::Preview::ButtonsModel *m_rightButtonsModel;
KDecoration2::Preview::ButtonsModel *m_availableButtonsModel;
}
QPointer<KNS3::DownloadDialog> m_newStuffDialog;
struct Settings
{
KDecoration2::BorderSize borderSize;
int themeIndex;
bool closeOnDoubleClickOnMenu;
DecorationButtonsList buttonsOnLeft;
DecorationButtonsList buttonsOnRight;
};
#endif
Settings m_savedSettings;
Settings m_currentSettings;
};
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KCMForm</class>
<widget class="QWidget" name="KCMForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>386</width>
<height>272</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="theTab">
<attribute name="title">
<string>Theme</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="filter">