kwin/kcmkwin/kwindecoration/kcm.cpp
Roman Gilg d51b8dc093 [decorations] Let KDecoration plugins recommend a border size per default
Summary:
This is an alternative solution to T8707 and in comparision to D13276 a less
drastic change to KWin's default behavior.

Instead of changing the border size default for all KDecoration plugins by
switching the default from border size Normal to None introduce new
functionality, which allows a KDecoration plugin to recommend a border size in
its metadata. By default KWin listens for these recommendations and sets the
border size accordingly.

If there is no metadata recommending a border size, KWin falls back to the
current setting of Normal sized borders.

A user is able to override the recommendations from the KCM, which has been
extended accordingly.

Test Plan: Manually with adjusted metadata of Breeze.

Reviewers: #kwin, #plasma, #vdg, ngraham

Reviewed By: #vdg, ngraham

Subscribers: hpereiradacosta, filipf, anemeth, davidedmundson, abetts, graesslin, ngraham, zzag, kwin

Tags: #kwin

Maniphest Tasks: T8707

Differential Revision: https://phabricator.kde.org/D13284
2019-06-06 22:24:45 +02:00

372 lines
14 KiB
C++

/*
* Copyright (c) 2019 Valerio Pilo <vpilo@coldshock.net>
*
* 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 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
* Library General Public License for more details.
*
* 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.
*/
#include "kcm.h"
#include "decorationmodel.h"
#include "declarative-plugin/buttonsmodel.h"
#include <config-kwin.h>
#include <KAboutData>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KPluginFactory>
#include <KNSCore/Engine>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDebug>
#include <QQuickItem>
#include <QQuickWindow>
#include <QSortFilterProxyModel>
#include <KNewStuff3/KNS3/DownloadDialog>
K_PLUGIN_FACTORY_WITH_JSON(KCMKWinDecorationFactory, "kwindecoration.json", registerPlugin<KCMKWinDecoration>();)
Q_DECLARE_METATYPE(KDecoration2::BorderSize)
namespace
{
const QString s_configFile { QStringLiteral("kwinrc") };
const QString s_configGroup { QStringLiteral("org.kde.kdecoration2") };
const QString s_configPlugin { QStringLiteral("library") };
const QString s_configTheme { QStringLiteral("theme") };
const QString s_configBorderSize { QStringLiteral("BorderSize") };
const QString s_configBorderSizeAuto { QStringLiteral("BorderSizeAuto") };
const QString s_configCloseOnDoubleClickOnMenu { QStringLiteral("CloseOnDoubleClickOnMenu") };
const QString s_configShowToolTips { QStringLiteral("ShowToolTips") };
const QString s_configDecoButtonsOnLeft { QStringLiteral("ButtonsOnLeft") };
const QString s_configDecoButtonsOnRight { QStringLiteral("ButtonsOnRight") };
const KDecoration2::BorderSize s_defaultBorderSize = KDecoration2::BorderSize::Normal;
const KDecoration2::BorderSize s_defaultRecommendedBorderSize = KDecoration2::BorderSize::Normal;
const bool s_defaultBorderSizeAuto = true;
const bool s_defaultCloseOnDoubleClickOnMenu = false;
const bool s_defaultShowToolTips = true;
const DecorationButtonsList s_defaultDecoButtonsOnLeft {
KDecoration2::DecorationButtonType::Menu,
KDecoration2::DecorationButtonType::OnAllDesktops
};
const DecorationButtonsList s_defaultDecoButtonsOnRight {
KDecoration2::DecorationButtonType::ContextHelp,
KDecoration2::DecorationButtonType::Minimize,
KDecoration2::DecorationButtonType::Maximize,
KDecoration2::DecorationButtonType::Close
};
#if HAVE_BREEZE_DECO
const QString s_defaultPlugin { QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID) };
const QString s_defaultTheme { QStringLiteral("Breeze") };
#else
const QString s_defaultPlugin { QStringLiteral("org.kde.kwin.aurorae") };
const QString s_defaultTheme { QStringLiteral("kwin4_decoration_qml_plastik") };
#endif
}
KCMKWinDecoration::KCMKWinDecoration(QObject *parent, const QVariantList &arguments)
: KQuickAddons::ConfigModule(parent, arguments)
, m_themesModel(new KDecoration2::Configuration::DecorationsModel(this))
, m_proxyThemesModel(new QSortFilterProxyModel(this))
, m_leftButtonsModel(new KDecoration2::Preview::ButtonsModel(DecorationButtonsList(), this))
, m_rightButtonsModel(new KDecoration2::Preview::ButtonsModel(DecorationButtonsList(), this))
, m_availableButtonsModel(new KDecoration2::Preview::ButtonsModel(this))
, m_savedSettings{ s_defaultBorderSize, s_defaultBorderSizeAuto, -2 /* for setTheme() */, false, s_defaultShowToolTips, s_defaultDecoButtonsOnLeft, s_defaultDecoButtonsOnRight }
, m_currentSettings(m_savedSettings)
{
auto about = new KAboutData(QStringLiteral("kcm_kwindecoration"),
i18n("Window Decorations"),
QStringLiteral("1.0"),
QString(),
KAboutLicense::GPL);
about->addAuthor(i18n("Valerio Pilo"),
i18n("Author"),
QStringLiteral("vpilo@coldshock.net"));
setAboutData(about);
qmlRegisterType<QAbstractListModel>();
qmlRegisterType<QSortFilterProxyModel>();
m_proxyThemesModel->setSourceModel(m_themesModel);
m_proxyThemesModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_proxyThemesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_proxyThemesModel->sort(0);
connect(m_leftButtonsModel, &QAbstractItemModel::rowsInserted, this, &KCMKWinDecoration::updateNeedsSave);
connect(m_leftButtonsModel, &QAbstractItemModel::rowsMoved, this, &KCMKWinDecoration::updateNeedsSave);
connect(m_leftButtonsModel, &QAbstractItemModel::rowsRemoved, this, &KCMKWinDecoration::updateNeedsSave);
connect(m_leftButtonsModel, &QAbstractItemModel::modelReset, this, &KCMKWinDecoration::updateNeedsSave);
connect(m_rightButtonsModel, &QAbstractItemModel::rowsInserted, this, &KCMKWinDecoration::updateNeedsSave);
connect(m_rightButtonsModel, &QAbstractItemModel::rowsMoved, this, &KCMKWinDecoration::updateNeedsSave);
connect(m_rightButtonsModel, &QAbstractItemModel::rowsRemoved, this, &KCMKWinDecoration::updateNeedsSave);
connect(m_rightButtonsModel, &QAbstractItemModel::modelReset, this, &KCMKWinDecoration::updateNeedsSave);
// Update the themes when the color scheme or a theme's settings change
QDBusConnection::sessionBus()
.connect(QString(), QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig"),
this, SLOT(reloadKWinSettings()));
QMetaObject::invokeMethod(m_themesModel, "init", Qt::QueuedConnection);
}
void KCMKWinDecoration::reloadKWinSettings()
{
QMetaObject::invokeMethod(m_themesModel, "init", Qt::QueuedConnection);
}
void KCMKWinDecoration::getNewStuff(QQuickItem *context)
{
if (!m_newStuffDialog) {
m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("window-decorations.knsrc"));
m_newStuffDialog->setWindowTitle(i18n("Download New Window Decorations"));
m_newStuffDialog->setWindowModality(Qt::WindowModal);
connect(m_newStuffDialog, &KNS3::DownloadDialog::accepted, this, &KCMKWinDecoration::load);
}
if (context && context->window()) {
m_newStuffDialog->winId(); // so it creates the windowHandle()
m_newStuffDialog->windowHandle()->setTransientParent(context->window());
}
connect(m_newStuffDialog, &QDialog::finished, this, &KCMKWinDecoration::reloadKWinSettings);
m_newStuffDialog->show();
}
void KCMKWinDecoration::load()
{
const KConfigGroup config = KSharedConfig::openConfig(s_configFile)->group(s_configGroup);
const QString plugin = config.readEntry(s_configPlugin, s_defaultPlugin);
const QString theme = config.readEntry(s_configTheme, s_defaultTheme);
int themeIndex = m_proxyThemesModel->mapFromSource(m_themesModel->findDecoration(plugin, theme)).row();
if (themeIndex < 0) {
qWarning() << "Plugin" << plugin << "and theme" << theme << "not found";
} else {
qDebug() << "Current theme: plugin" << plugin << "and theme" << theme;
}
setTheme(themeIndex);
setCloseOnDoubleClickOnMenu(config.readEntry(s_configCloseOnDoubleClickOnMenu, s_defaultCloseOnDoubleClickOnMenu));
setShowToolTips(config.readEntry(s_configShowToolTips, s_defaultShowToolTips));
const QString defaultSizeName = Utils::borderSizeToString(s_defaultBorderSize);
setBorderSize(Utils::stringToBorderSize(config.readEntry(s_configBorderSize, defaultSizeName)));
setBorderSizeAuto(config.readEntry(s_configBorderSizeAuto, s_defaultBorderSizeAuto));
m_leftButtonsModel->replace(Utils::readDecorationButtons(config, s_configDecoButtonsOnLeft, s_defaultDecoButtonsOnLeft));
m_rightButtonsModel->replace(Utils::readDecorationButtons(config, s_configDecoButtonsOnRight, s_defaultDecoButtonsOnRight));
m_currentSettings.buttonsOnLeft = m_leftButtonsModel->buttons();
m_currentSettings.buttonsOnRight = m_rightButtonsModel->buttons();
m_savedSettings = m_currentSettings;
updateNeedsSave();
}
void KCMKWinDecoration::save()
{
KConfigGroup config = KSharedConfig::openConfig(s_configFile)->group(s_configGroup);
if (m_currentSettings.themeIndex >= 0) {
const QModelIndex index = m_proxyThemesModel->index(m_currentSettings.themeIndex, 0);
if (index.isValid()) {
const QString plugin = index.data(KDecoration2::Configuration::DecorationsModel::PluginNameRole).toString();
const QString theme = index.data(KDecoration2::Configuration::DecorationsModel::ThemeNameRole).toString();
config.writeEntry(s_configPlugin, plugin);
config.writeEntry(s_configTheme, theme);
qDebug() << "Saved theme: plugin" << plugin << "and theme" << theme;
} else {
qWarning() << "Cannot match theme index" << m_currentSettings.themeIndex << "in model";
}
}
config.writeEntry(s_configCloseOnDoubleClickOnMenu, m_currentSettings.closeOnDoubleClickOnMenu);
config.writeEntry(s_configShowToolTips, m_currentSettings.showToolTips);
config.writeEntry(s_configBorderSize, Utils::borderSizeToString(m_currentSettings.borderSize));
config.writeEntry(s_configBorderSizeAuto, m_currentSettings.borderSizeAuto);
config.writeEntry(s_configDecoButtonsOnLeft, Utils::buttonsToString(m_currentSettings.buttonsOnLeft));
config.writeEntry(s_configDecoButtonsOnRight, Utils::buttonsToString(m_currentSettings.buttonsOnRight));
config.sync();
m_savedSettings = m_currentSettings;
// Send a signal to all kwin instances
QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"),
QStringLiteral("org.kde.KWin"),
QStringLiteral("reloadConfig"));
QDBusConnection::sessionBus().send(message);
updateNeedsSave();
}
void KCMKWinDecoration::defaults()
{
int themeIndex = m_proxyThemesModel->mapFromSource(m_themesModel->findDecoration(s_defaultPlugin, s_defaultTheme)).row();
if (themeIndex < 0) {
qWarning() << "Default plugin" << s_defaultPlugin << "and theme" << s_defaultTheme << "not found";
}
setTheme(themeIndex);
setBorderSize(s_defaultBorderSize);
setBorderSizeAuto(s_defaultBorderSizeAuto);
setCloseOnDoubleClickOnMenu(s_defaultCloseOnDoubleClickOnMenu);
setShowToolTips(s_defaultShowToolTips);
m_leftButtonsModel->replace(s_defaultDecoButtonsOnLeft);
m_rightButtonsModel->replace(s_defaultDecoButtonsOnRight);
updateNeedsSave();
}
void KCMKWinDecoration::updateNeedsSave()
{
m_currentSettings.buttonsOnLeft = m_leftButtonsModel->buttons();
m_currentSettings.buttonsOnRight = m_rightButtonsModel->buttons();
setNeedsSave(m_savedSettings.closeOnDoubleClickOnMenu != m_currentSettings.closeOnDoubleClickOnMenu
|| m_savedSettings.showToolTips != m_currentSettings.showToolTips
|| m_savedSettings.borderSize != m_currentSettings.borderSize
|| m_savedSettings.borderSizeAuto != m_currentSettings.borderSizeAuto
|| m_savedSettings.themeIndex != m_currentSettings.themeIndex
|| m_savedSettings.buttonsOnLeft != m_currentSettings.buttonsOnLeft
|| m_savedSettings.buttonsOnRight != m_currentSettings.buttonsOnRight);
}
QSortFilterProxyModel *KCMKWinDecoration::themesModel() const
{
return m_proxyThemesModel;
}
QAbstractListModel *KCMKWinDecoration::leftButtonsModel()
{
return m_leftButtonsModel;
}
QAbstractListModel *KCMKWinDecoration::rightButtonsModel()
{
return m_rightButtonsModel;
}
QAbstractListModel *KCMKWinDecoration::availableButtonsModel() const
{
return m_availableButtonsModel;
}
QStringList KCMKWinDecoration::borderSizesModel() const
{
return Utils::getBorderSizeNames().values();
}
int KCMKWinDecoration::borderSize() const
{
return Utils::getBorderSizeNames().keys().indexOf(m_currentSettings.borderSize);
}
int KCMKWinDecoration::recommendedBorderSize() const
{
typedef KDecoration2::Configuration::DecorationsModel::DecorationRole DecoRole;
const QModelIndex proxyIndex = m_proxyThemesModel->index(m_currentSettings.themeIndex, 0);
if (proxyIndex.isValid()) {
const QModelIndex index = m_proxyThemesModel->mapToSource(proxyIndex);
if (index.isValid()) {
QVariant ret = m_themesModel->data(index, DecoRole::RecommendedBorderSizeRole);
return Utils::getBorderSizeNames().keys().indexOf(Utils::stringToBorderSize(ret.toString()));
}
}
return Utils::getBorderSizeNames().keys().indexOf(s_defaultRecommendedBorderSize);
}
bool KCMKWinDecoration::borderSizeAuto() const
{
return m_currentSettings.borderSizeAuto;
}
int KCMKWinDecoration::theme() const
{
return m_currentSettings.themeIndex;
}
bool KCMKWinDecoration::closeOnDoubleClickOnMenu() const
{
return m_currentSettings.closeOnDoubleClickOnMenu;
}
bool KCMKWinDecoration::showToolTips() const
{
return m_currentSettings.showToolTips;
}
void KCMKWinDecoration::setBorderSize(int index)
{
setBorderSize(Utils::getBorderSizeNames().keys().at(index));
}
void KCMKWinDecoration::setBorderSize(KDecoration2::BorderSize size)
{
if (m_currentSettings.borderSize == size) {
return;
}
m_currentSettings.borderSize = size;
emit borderSizeChanged();
updateNeedsSave();
}
void KCMKWinDecoration::setBorderSizeAuto(bool set)
{
if (m_currentSettings.borderSizeAuto == set) {
return;
}
m_currentSettings.borderSizeAuto = set;
emit borderSizeAutoChanged();
updateNeedsSave();
}
void KCMKWinDecoration::setTheme(int index)
{
// The initial themeIndex is set to -2 to always initially apply a theme, any theme
if (m_currentSettings.themeIndex == index) {
return;
}
m_currentSettings.themeIndex = index;
emit themeChanged();
updateNeedsSave();
}
void KCMKWinDecoration::setCloseOnDoubleClickOnMenu(bool enable)
{
if (m_currentSettings.closeOnDoubleClickOnMenu == enable) {
return;
}
m_currentSettings.closeOnDoubleClickOnMenu = enable;
emit closeOnDoubleClickOnMenuChanged();
updateNeedsSave();
}
void KCMKWinDecoration::setShowToolTips(bool show)
{
if (m_currentSettings.showToolTips == show) {
return;
}
m_currentSettings.showToolTips = show;
emit showToolTipsChanged();
updateNeedsSave();
}
#include "kcm.moc"