diff --git a/decorations/decorationbridge.cpp b/decorations/decorationbridge.cpp index 3bf2c4ca3..c257cf1d5 100644 --- a/decorations/decorationbridge.cpp +++ b/decorations/decorationbridge.cpp @@ -1,322 +1,330 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin 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 "decorationbridge.h" #include "decoratedclient.h" #include "decorationrenderer.h" #include "decorations_logging.h" #include "settings.h" // KWin core #include "abstract_client.h" #include "composite.h" #include "scene.h" #include "wayland_server.h" #include "workspace.h" #include // KDecoration #include #include #include // KWayland #include // Frameworks #include #include // Qt #include #include namespace KWin { namespace Decoration { static const QString s_aurorae = QStringLiteral("org.kde.kwin.aurorae"); static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); #if HAVE_BREEZE_DECO static const QString s_defaultPlugin = QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID); #else static const QString s_defaultPlugin = s_aurorae; #endif KWIN_SINGLETON_FACTORY(DecorationBridge) DecorationBridge::DecorationBridge(QObject *parent) : KDecoration2::DecorationBridge(parent) , m_factory(nullptr) , m_blur(false) , m_showToolTips(false) , m_settings() , m_noPlugin(false) { KConfigGroup cg(KSharedConfig::openConfig(), "KDE"); // try to extract the proper defaults file from a lookandfeel package const QString looknfeel = cg.readEntry(QStringLiteral("LookAndFeelPackage"), "org.kde.breeze.desktop"); m_lnfConfig = KSharedConfig::openConfig(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/look-and-feel/") + looknfeel + QStringLiteral("/contents/defaults"))); readDecorationOptions(); } DecorationBridge::~DecorationBridge() { s_self = nullptr; } QString DecorationBridge::readPlugin() { //Try to get a default from look and feel KConfigGroup cg(m_lnfConfig, "kwinrc"); cg = KConfigGroup(&cg, "org.kde.kdecoration2"); return kwinApp()->config()->group(s_pluginName).readEntry("library", cg.readEntry("library", s_defaultPlugin)); } static bool readNoPlugin() { return kwinApp()->config()->group(s_pluginName).readEntry("NoPlugin", false); } QString DecorationBridge::readTheme() const { //Try to get a default from look and feel KConfigGroup cg(m_lnfConfig, "kwinrc"); cg = KConfigGroup(&cg, "org.kde.kdecoration2"); return kwinApp()->config()->group(s_pluginName).readEntry("theme", cg.readEntry("theme", m_defaultTheme)); } void DecorationBridge::readDecorationOptions() { m_showToolTips = kwinApp()->config()->group(s_pluginName).readEntry("ShowToolTips", true); } void DecorationBridge::init() { using namespace KWayland::Server; m_noPlugin = readNoPlugin(); if (m_noPlugin) { if (waylandServer()) { waylandServer()->decorationManager()->setDefaultMode(ServerSideDecorationManagerInterface::Mode::None); } return; } m_plugin = readPlugin(); m_settings = QSharedPointer::create(this); initPlugin(); if (!m_factory) { if (m_plugin != s_defaultPlugin) { // try loading default plugin m_plugin = s_defaultPlugin; initPlugin(); } // default plugin failed to load, try fallback if (!m_factory) { m_plugin = s_aurorae; initPlugin(); } } if (waylandServer()) { waylandServer()->decorationManager()->setDefaultMode(m_factory ? ServerSideDecorationManagerInterface::Mode::Server : ServerSideDecorationManagerInterface::Mode::None); } } void DecorationBridge::initPlugin() { const auto offers = KPluginLoader::findPluginsById(s_pluginName, m_plugin); if (offers.isEmpty()) { qCWarning(KWIN_DECORATIONS) << "Could not locate decoration plugin"; return; } qCDebug(KWIN_DECORATIONS) << "Trying to load decoration plugin: " << offers.first().fileName(); KPluginLoader loader(offers.first().fileName()); KPluginFactory *factory = loader.factory(); if (!factory) { qCWarning(KWIN_DECORATIONS) << "Error loading plugin:" << loader.errorString(); } else { m_factory = factory; loadMetaData(loader.metaData().value(QStringLiteral("MetaData")).toObject()); } } static void recreateDecorations() { Workspace::self()->forEachAbstractClient([](AbstractClient *c) { c->updateDecoration(true, true); }); } void DecorationBridge::reconfigure() { readDecorationOptions(); if (m_noPlugin != readNoPlugin()) { m_noPlugin = !m_noPlugin; // no plugin setting changed if (m_noPlugin) { // decorations disabled now m_plugin = QString(); delete m_factory; m_factory = nullptr; m_settings.clear(); } else { // decorations enabled now init(); } recreateDecorations(); return; } const QString newPlugin = readPlugin(); if (newPlugin != m_plugin) { // plugin changed, recreate everything auto oldFactory = m_factory; const auto oldPluginName = m_plugin; m_plugin = newPlugin; initPlugin(); if (m_factory == oldFactory) { // loading new plugin failed m_factory = oldFactory; m_plugin = oldPluginName; } else { recreateDecorations(); // TODO: unload and destroy old plugin } } else { // same plugin, but theme might have changed const QString oldTheme = m_theme; m_theme = readTheme(); if (m_theme != oldTheme) { recreateDecorations(); } } } void DecorationBridge::loadMetaData(const QJsonObject &object) { // reset all settings m_blur = false; + m_recommendedBorderSize = QString(); m_theme = QString(); m_defaultTheme = QString(); // load the settings const QJsonValue decoSettings = object.value(s_pluginName); if (decoSettings.isUndefined()) { // no settings return; } const QVariantMap decoSettingsMap = decoSettings.toObject().toVariantMap(); auto blurIt = decoSettingsMap.find(QStringLiteral("blur")); if (blurIt != decoSettingsMap.end()) { m_blur = blurIt.value().toBool(); } + auto recBorderSizeIt = decoSettingsMap.find(QStringLiteral("recommendedBorderSize")); + if (recBorderSizeIt != decoSettingsMap.end()) { + m_recommendedBorderSize = recBorderSizeIt.value().toString(); + } findTheme(decoSettingsMap); + + Q_EMIT metaDataLoaded(); } void DecorationBridge::findTheme(const QVariantMap &map) { auto it = map.find(QStringLiteral("themes")); if (it == map.end()) { return; } if (!it.value().toBool()) { return; } it = map.find(QStringLiteral("defaultTheme")); m_defaultTheme = it != map.end() ? it.value().toString() : QString(); m_theme = readTheme(); } std::unique_ptr DecorationBridge::createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration) { return std::unique_ptr(new DecoratedClientImpl(static_cast(decoration->parent()), client, decoration)); } std::unique_ptr DecorationBridge::settings(KDecoration2::DecorationSettings *parent) { return std::unique_ptr(new SettingsImpl(parent)); } void DecorationBridge::update(KDecoration2::Decoration *decoration, const QRect &geometry) { // TODO: remove check once all compositors implement it if (AbstractClient *c = Workspace::self()->findAbstractClient([decoration] (const AbstractClient *client) { return client->decoration() == decoration; })) { if (Renderer *renderer = c->decoratedClient()->renderer()) { renderer->schedule(geometry); } } } KDecoration2::Decoration *DecorationBridge::createDecoration(AbstractClient *client) { if (m_noPlugin) { return nullptr; } if (!m_factory) { return nullptr; } QVariantMap args({ {QStringLiteral("bridge"), QVariant::fromValue(this)} }); if (!m_theme.isEmpty()) { args.insert(QStringLiteral("theme"), m_theme); } auto deco = m_factory->create(client, QVariantList({args})); deco->setSettings(m_settings); deco->init(); return deco; } static QString settingsProperty(const QVariant &variant) { if (QLatin1String(variant.typeName()) == QLatin1String("KDecoration2::BorderSize")) { return QString::number(variant.toInt()); } else if (QLatin1String(variant.typeName()) == QLatin1String("QVector")) { const auto &b = variant.value>(); QString buffer; for (auto it = b.begin(); it != b.end(); ++it) { if (it != b.begin()) { buffer.append(QStringLiteral(", ")); } buffer.append(QString::number(int(*it))); } return buffer; } return variant.toString(); } QString DecorationBridge::supportInformation() const { QString b; b.append(QStringLiteral("Plugin: %1\n").arg(m_plugin)); b.append(QStringLiteral("Theme: %1\n").arg(m_theme)); + b.append(QStringLiteral("Plugin recommends border size: %1\n").arg(m_recommendedBorderSize.isNull() ? "No" : m_recommendedBorderSize)); b.append(QStringLiteral("Blur: %1\n").arg(m_blur)); const QMetaObject *metaOptions = m_settings->metaObject(); for (int i=0; ipropertyCount(); ++i) { const QMetaProperty property = metaOptions->property(i); if (QLatin1String(property.name()) == QLatin1String("objectName")) { continue; } b.append(QStringLiteral("%1: %2\n").arg(property.name()).arg(settingsProperty(m_settings->property(property.name())))); } return b; } } // Decoration } // KWin diff --git a/decorations/decorationbridge.h b/decorations/decorationbridge.h index 0dce293df..7ab70d830 100644 --- a/decorations/decorationbridge.h +++ b/decorations/decorationbridge.h @@ -1,97 +1,104 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin 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 KWIN_DECORATION_BRIDGE_H #define KWIN_DECORATION_BRIDGE_H #include #include #include #include #include class KPluginFactory; namespace KDecoration2 { class DecorationSettings; } namespace KWin { class AbstractClient; namespace Decoration { class DecorationBridge : public KDecoration2::DecorationBridge { Q_OBJECT public: virtual ~DecorationBridge(); void init(); KDecoration2::Decoration *createDecoration(AbstractClient *client); std::unique_ptr createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration) override; std::unique_ptr settings(KDecoration2::DecorationSettings *parent) override; void update(KDecoration2::Decoration *decoration, const QRect &geometry) override; bool needsBlur() const { return m_blur; } + QString recommendedBorderSize() const { + return m_recommendedBorderSize; + } bool showToolTips() const { return m_showToolTips; } void reconfigure(); const QSharedPointer &settings() const { return m_settings; } QString supportInformation() const; +Q_SIGNALS: + void metaDataLoaded(); + private: QString readPlugin(); void loadMetaData(const QJsonObject &object); void findTheme(const QVariantMap &map); void initPlugin(); QString readTheme() const; void readDecorationOptions(); KPluginFactory *m_factory; KSharedConfig::Ptr m_lnfConfig; bool m_blur; bool m_showToolTips; + QString m_recommendedBorderSize; QString m_plugin; QString m_defaultTheme; QString m_theme; QSharedPointer m_settings; bool m_noPlugin; KWIN_SINGLETON(DecorationBridge) }; } // Decoration } // KWin #endif diff --git a/decorations/settings.cpp b/decorations/settings.cpp index e604fada2..f229b069a 100644 --- a/decorations/settings.cpp +++ b/decorations/settings.cpp @@ -1,198 +1,206 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin 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 "settings.h" // KWin +#include "decorationbridge.h" #include "composite.h" #include "virtualdesktops.h" #include "workspace.h" #include "appmenu.h" #include #include #include #include namespace KWin { namespace Decoration { SettingsImpl::SettingsImpl(KDecoration2::DecorationSettings *parent) : QObject() , DecorationSettingsPrivate(parent) , m_borderSize(KDecoration2::BorderSize::Normal) { readSettings(); auto c = connect(Compositor::self(), &Compositor::compositingToggled, parent, &KDecoration2::DecorationSettings::alphaChannelSupportedChanged); connect(VirtualDesktopManager::self(), &VirtualDesktopManager::countChanged, this, [parent](uint previous, uint current) { if (previous != 1 && current != 1) { return; } emit parent->onAllDesktopsAvailableChanged(current > 1); } ); // prevent changes in Decoration due to Compositor being destroyed connect(Compositor::self(), &Compositor::aboutToDestroy, this, [this, c] { disconnect(c); } ); connect(Workspace::self(), &Workspace::configChanged, this, &SettingsImpl::readSettings); + connect(DecorationBridge::self(), &DecorationBridge::metaDataLoaded, this, &SettingsImpl::readSettings); } SettingsImpl::~SettingsImpl() = default; bool SettingsImpl::isAlphaChannelSupported() const { return Compositor::self()->compositing(); } bool SettingsImpl::isOnAllDesktopsAvailable() const { return VirtualDesktopManager::self()->count() > 1; } bool SettingsImpl::isCloseOnDoubleClickOnMenu() const { return m_closeDoubleClickMenu; } static QHash s_buttonNames; static void initButtons() { if (!s_buttonNames.isEmpty()) { return; } s_buttonNames[KDecoration2::DecorationButtonType::Menu] = QChar('M'); s_buttonNames[KDecoration2::DecorationButtonType::ApplicationMenu] = QChar('N'); s_buttonNames[KDecoration2::DecorationButtonType::OnAllDesktops] = QChar('S'); s_buttonNames[KDecoration2::DecorationButtonType::ContextHelp] = QChar('H'); s_buttonNames[KDecoration2::DecorationButtonType::Minimize] = QChar('I'); s_buttonNames[KDecoration2::DecorationButtonType::Maximize] = QChar('A'); s_buttonNames[KDecoration2::DecorationButtonType::Close] = QChar('X'); s_buttonNames[KDecoration2::DecorationButtonType::KeepAbove] = QChar('F'); s_buttonNames[KDecoration2::DecorationButtonType::KeepBelow] = QChar('B'); s_buttonNames[KDecoration2::DecorationButtonType::Shade] = QChar('L'); } static QString buttonsToString(const QVector &buttons) { auto buttonToString = [](KDecoration2::DecorationButtonType button) -> QChar { const auto it = s_buttonNames.constFind(button); if (it != s_buttonNames.constEnd()) { return it.value(); } return QChar(); }; QString ret; for (auto button : buttons) { ret.append(buttonToString(button)); } return ret; } QVector< KDecoration2::DecorationButtonType > SettingsImpl::readDecorationButtons(const KConfigGroup &config, const char *key, const QVector< KDecoration2::DecorationButtonType > &defaultValue) const { initButtons(); auto buttonsFromString = [](const QString &buttons) -> QVector { QVector ret; for (auto it = buttons.begin(); it != buttons.end(); ++it) { for (auto it2 = s_buttonNames.constBegin(); it2 != s_buttonNames.constEnd(); ++it2) { if (it2.value() == (*it)) { ret << it2.key(); } } } return ret; }; return buttonsFromString(config.readEntry(key, buttonsToString(defaultValue))); } static KDecoration2::BorderSize stringToSize(const QString &name) { static const QMap s_sizes = QMap({ {QStringLiteral("None"), KDecoration2::BorderSize::None}, {QStringLiteral("NoSides"), KDecoration2::BorderSize::NoSides}, {QStringLiteral("Tiny"), KDecoration2::BorderSize::Tiny}, {QStringLiteral("Normal"), KDecoration2::BorderSize::Normal}, {QStringLiteral("Large"), KDecoration2::BorderSize::Large}, {QStringLiteral("VeryLarge"), KDecoration2::BorderSize::VeryLarge}, {QStringLiteral("Huge"), KDecoration2::BorderSize::Huge}, {QStringLiteral("VeryHuge"), KDecoration2::BorderSize::VeryHuge}, {QStringLiteral("Oversized"), KDecoration2::BorderSize::Oversized} }); auto it = s_sizes.constFind(name); if (it == s_sizes.constEnd()) { // non sense values are interpreted just like normal return KDecoration2::BorderSize::Normal; } return it.value(); } void SettingsImpl::readSettings() { KConfigGroup config = kwinApp()->config()->group(QStringLiteral("org.kde.kdecoration2")); const auto &left = readDecorationButtons(config, "ButtonsOnLeft", QVector({ KDecoration2::DecorationButtonType::Menu, KDecoration2::DecorationButtonType::OnAllDesktops })); if (left != m_leftButtons) { m_leftButtons = left; emit decorationSettings()->decorationButtonsLeftChanged(m_leftButtons); } const auto &right = readDecorationButtons(config, "ButtonsOnRight", QVector({ KDecoration2::DecorationButtonType::ContextHelp, KDecoration2::DecorationButtonType::Minimize, KDecoration2::DecorationButtonType::Maximize, KDecoration2::DecorationButtonType::Close })); if (right != m_rightButtons) { m_rightButtons = right; emit decorationSettings()->decorationButtonsRightChanged(m_rightButtons); } ApplicationMenu::self()->setViewEnabled(left.contains(KDecoration2::DecorationButtonType::ApplicationMenu) || right.contains(KDecoration2::DecorationButtonType::ApplicationMenu)); const bool close = config.readEntry("CloseOnDoubleClickOnMenu", false); if (close != m_closeDoubleClickMenu) { m_closeDoubleClickMenu = close; emit decorationSettings()->closeOnDoubleClickOnMenuChanged(m_closeDoubleClickMenu); } - const auto size = stringToSize(config.readEntry("BorderSize", QStringLiteral("Normal"))); + m_autoBorderSize = config.readEntry("BorderSizeAuto", true); + + auto size = stringToSize(config.readEntry("BorderSize", QStringLiteral("Normal"))); + if (m_autoBorderSize) { + /* Falls back to Normal border size, if the plugin does not provide a valid recommendation. */ + size = stringToSize(DecorationBridge::self()->recommendedBorderSize()); + } if (size != m_borderSize) { m_borderSize = size; emit decorationSettings()->borderSizeChanged(m_borderSize); } const QFont font = QFontDatabase::systemFont(QFontDatabase::TitleFont); if (font != m_font) { m_font = font; emit decorationSettings()->fontChanged(m_font); } emit decorationSettings()->reconfigured(); } } } diff --git a/decorations/settings.h b/decorations/settings.h index a35be06ef..f722328c3 100644 --- a/decorations/settings.h +++ b/decorations/settings.h @@ -1,70 +1,71 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin 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 KWIN_DECORATION_SETTINGS_H #define KWIN_DECORATION_SETTINGS_H #include #include class KConfigGroup; namespace KWin { namespace Decoration { class SettingsImpl : public QObject, public KDecoration2::DecorationSettingsPrivate { Q_OBJECT public: explicit SettingsImpl(KDecoration2::DecorationSettings *parent); virtual ~SettingsImpl(); bool isAlphaChannelSupported() const override; bool isOnAllDesktopsAvailable() const override; bool isCloseOnDoubleClickOnMenu() const override; KDecoration2::BorderSize borderSize() const override { return m_borderSize; } QVector< KDecoration2::DecorationButtonType > decorationButtonsLeft() const override { return m_leftButtons; } QVector< KDecoration2::DecorationButtonType > decorationButtonsRight() const override { return m_rightButtons; } QFont font() const override { return m_font; } private: void readSettings(); QVector< KDecoration2::DecorationButtonType > readDecorationButtons(const KConfigGroup &config, const char *key, const QVector< KDecoration2::DecorationButtonType > &defaultValue) const; QVector< KDecoration2::DecorationButtonType > m_leftButtons; QVector< KDecoration2::DecorationButtonType > m_rightButtons; KDecoration2::BorderSize m_borderSize; + bool m_autoBorderSize = true; bool m_closeDoubleClickMenu = false; QFont m_font; }; } // Decoration } // KWin #endif diff --git a/kcmkwin/kwindecoration/decorationmodel.cpp b/kcmkwin/kwindecoration/decorationmodel.cpp index 7568bdd36..9e1682158 100644 --- a/kcmkwin/kwindecoration/decorationmodel.cpp +++ b/kcmkwin/kwindecoration/decorationmodel.cpp @@ -1,196 +1,207 @@ /* * Copyright 2014 Martin Gräßlin * * 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 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 "decorationmodel.h" // KDecoration2 #include #include // KDE #include #include #include #include // Qt #include namespace KDecoration2 { namespace Configuration { static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); DecorationsModel::DecorationsModel(QObject *parent) : QAbstractListModel(parent) { } DecorationsModel::~DecorationsModel() = default; int DecorationsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_plugins.size(); } QVariant DecorationsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.column() != 0 || index.row() < 0 || index.row() >= int(m_plugins.size())) { return QVariant(); } const Data &d = m_plugins.at(index.row()); switch (role) { case Qt::DisplayRole: return d.visibleName; case PluginNameRole: return d.pluginName; case ThemeNameRole: return d.themeName; case ConfigurationRole: return d.configuration; + case RecommendedBorderSizeRole: + return Utils::borderSizeToString(d.recommendedBorderSize); } return QVariant(); } QHash< int, QByteArray > DecorationsModel::roleNames() const { QHash roles({ {Qt::DisplayRole, QByteArrayLiteral("display")}, {PluginNameRole, QByteArrayLiteral("plugin")}, {ThemeNameRole, QByteArrayLiteral("theme")}, - {ConfigurationRole, QByteArrayLiteral("configureable")} + {ConfigurationRole, QByteArrayLiteral("configureable")}, + {RecommendedBorderSizeRole, QByteArrayLiteral("recommendedbordersize")} }); return roles; } static bool isThemeEngine(const QVariantMap &decoSettingsMap) { auto it = decoSettingsMap.find(QStringLiteral("themes")); if (it == decoSettingsMap.end()) { return false; } return it.value().toBool(); } static bool isConfigureable(const QVariantMap &decoSettingsMap) { auto it = decoSettingsMap.find(QStringLiteral("kcmodule")); if (it == decoSettingsMap.end()) { return false; } return it.value().toBool(); } +static KDecoration2::BorderSize recommendedBorderSize(const QVariantMap &decoSettingsMap) +{ + auto it = decoSettingsMap.find(QStringLiteral("recommendedBorderSize")); + if (it == decoSettingsMap.end()) { + return KDecoration2::BorderSize::Normal; + } + return Utils::stringToBorderSize(it.value().toString()); +} + static QString themeListKeyword(const QVariantMap &decoSettingsMap) { auto it = decoSettingsMap.find(QStringLiteral("themeListKeyword")); if (it == decoSettingsMap.end()) { return QString(); } return it.value().toString(); } static QString findKNewStuff(const QVariantMap &decoSettingsMap) { auto it = decoSettingsMap.find(QStringLiteral("KNewStuff")); if (it == decoSettingsMap.end()) { return QString(); } return it.value().toString(); } void DecorationsModel::init() { beginResetModel(); m_plugins.clear(); const auto plugins = KPluginTrader::self()->query(s_pluginName, s_pluginName); for (const auto &info : plugins) { KPluginLoader loader(info.libraryPath()); KPluginFactory *factory = loader.factory(); if (!factory) { continue; } auto metadata = loader.metaData().value(QStringLiteral("MetaData")).toObject().value(s_pluginName); - bool config = false; + Data data; if (!metadata.isUndefined()) { const auto decoSettingsMap = metadata.toObject().toVariantMap(); const QString &kns = findKNewStuff(decoSettingsMap); if (!kns.isEmpty() && !m_knsProviders.contains(kns)) { m_knsProviders.append(kns); } if (isThemeEngine(decoSettingsMap)) { const QString keyword = themeListKeyword(decoSettingsMap); if (keyword.isNull()) { // We cannot list the themes continue; } QScopedPointer themeFinder(factory->create(keyword)); if (themeFinder.isNull()) { continue; } QVariant themes = themeFinder->property("themes"); if (!themes.isValid()) { continue; } const auto themesMap = themes.toMap(); for (auto it = themesMap.begin(); it != themesMap.end(); ++it) { Data d; d.pluginName = info.pluginName(); d.themeName = it.value().toString(); d.visibleName = it.key(); QMetaObject::invokeMethod(themeFinder.data(), "hasConfiguration", Q_RETURN_ARG(bool, d.configuration), Q_ARG(QString, d.themeName)); m_plugins.emplace_back(std::move(d)); } // it's a theme engine, we don't want to show this entry continue; } - config = isConfigureable(decoSettingsMap); + data.configuration = isConfigureable(decoSettingsMap); + data.recommendedBorderSize = recommendedBorderSize(decoSettingsMap); } - 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)); } endResetModel(); } QModelIndex DecorationsModel::findDecoration(const QString &pluginName, const QString &themeName) const { auto it = std::find_if(m_plugins.cbegin(), m_plugins.cend(), [pluginName, themeName](const Data &d) { return d.pluginName == pluginName && d.themeName == themeName; } ); if (it == m_plugins.cend()) { return QModelIndex(); } const auto distance = std::distance(m_plugins.cbegin(), it); return createIndex(distance, 0); } } } diff --git a/kcmkwin/kwindecoration/decorationmodel.h b/kcmkwin/kwindecoration/decorationmodel.h index bd68fdf69..54826a48b 100644 --- a/kcmkwin/kwindecoration/decorationmodel.h +++ b/kcmkwin/kwindecoration/decorationmodel.h @@ -1,72 +1,76 @@ /* * Copyright 2014 Martin Gräßlin * * 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 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 KDECORATION_DECORATION_MODEL_H #define KDECORATION_DECORATION_MODEL_H +#include "utils.h" + #include namespace KDecoration2 { namespace Configuration { class DecorationsModel : public QAbstractListModel { Q_OBJECT public: enum DecorationRole { PluginNameRole = Qt::UserRole + 1, ThemeNameRole, ConfigurationRole, + RecommendedBorderSizeRole, }; public: explicit DecorationsModel(QObject *parent = nullptr); virtual ~DecorationsModel(); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QHash< int, QByteArray > roleNames() const override; QModelIndex findDecoration(const QString &pluginName, const QString &themeName = QString()) const; QStringList knsProviders() const { return m_knsProviders; } public Q_SLOTS: void init(); private: struct Data { QString pluginName; QString themeName; QString visibleName; bool configuration = false; + KDecoration2::BorderSize recommendedBorderSize = KDecoration2::BorderSize::Normal; }; std::vector m_plugins; QStringList m_knsProviders; }; } } #endif diff --git a/kcmkwin/kwindecoration/kcm.cpp b/kcmkwin/kwindecoration/kcm.cpp index b25929ed7..3a68c866e 100644 --- a/kcmkwin/kwindecoration/kcm.cpp +++ b/kcmkwin/kwindecoration/kcm.cpp @@ -1,336 +1,372 @@ /* * Copyright (c) 2019 Valerio Pilo * * 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 #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KCMKWinDecorationFactory, "kwindecoration.json", registerPlugin();) 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, -2 /* for setTheme() */, false, s_defaultShowToolTips, s_defaultDecoButtonsOnLeft, s_defaultDecoButtonsOnRight } + , 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(); qmlRegisterType(); 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" diff --git a/kcmkwin/kwindecoration/kcm.h b/kcmkwin/kwindecoration/kcm.h index 648e7de31..ba0534f78 100644 --- a/kcmkwin/kwindecoration/kcm.h +++ b/kcmkwin/kwindecoration/kcm.h @@ -1,121 +1,128 @@ /* * Copyright (c) 2019 Valerio Pilo * * 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. */ #pragma once #include "utils.h" #include class QAbstractItemModel; class QSortFilterProxyModel; class QQuickItem; namespace KNS3 { class DownloadDialog; } namespace KDecoration2 { enum class BorderSize; namespace Preview { class ButtonsModel; } namespace Configuration { class DecorationsModel; } } 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 recommendedBorderSize READ recommendedBorderSize CONSTANT) + Q_PROPERTY(bool borderSizeAuto READ borderSizeAuto WRITE setBorderSizeAuto NOTIFY borderSizeAutoChanged) 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) Q_PROPERTY(bool showToolTips READ showToolTips WRITE setShowToolTips NOTIFY showToolTipsChanged) public: KCMKWinDecoration(QObject *parent, const QVariantList &arguments); QSortFilterProxyModel *themesModel() const; QAbstractListModel *leftButtonsModel(); QAbstractListModel *rightButtonsModel(); QAbstractListModel *availableButtonsModel() const; QStringList borderSizesModel() const; int borderSize() const; + int recommendedBorderSize() const; + bool borderSizeAuto() const; int theme() const; bool closeOnDoubleClickOnMenu() const; bool showToolTips() const; void setBorderSize(int index); void setBorderSize(KDecoration2::BorderSize size); + void setBorderSizeAuto(bool set); void setTheme(int index); void setCloseOnDoubleClickOnMenu(bool enable); void setShowToolTips(bool show); Q_INVOKABLE void getNewStuff(QQuickItem *context); Q_SIGNALS: void themeChanged(); void buttonsChanged(); void borderSizeChanged(); + void borderSizeAutoChanged(); void closeOnDoubleClickOnMenuChanged(); void showToolTipsChanged(); public Q_SLOTS: void load() override; void save() override; void defaults() override; private Q_SLOTS: void updateNeedsSave(); void reloadKWinSettings(); private: KDecoration2::Configuration::DecorationsModel *m_themesModel; QSortFilterProxyModel *m_proxyThemesModel; KDecoration2::Preview::ButtonsModel *m_leftButtonsModel; KDecoration2::Preview::ButtonsModel *m_rightButtonsModel; KDecoration2::Preview::ButtonsModel *m_availableButtonsModel; QPointer m_newStuffDialog; struct Settings { KDecoration2::BorderSize borderSize; + bool borderSizeAuto; int themeIndex; bool closeOnDoubleClickOnMenu; bool showToolTips; DecorationButtonsList buttonsOnLeft; DecorationButtonsList buttonsOnRight; }; Settings m_savedSettings; Settings m_currentSettings; }; diff --git a/kcmkwin/kwindecoration/package/contents/ui/main.qml b/kcmkwin/kwindecoration/package/contents/ui/main.qml index 3d918acf2..2afb3647b 100644 --- a/kcmkwin/kwindecoration/package/contents/ui/main.qml +++ b/kcmkwin/kwindecoration/package/contents/ui/main.qml @@ -1,137 +1,153 @@ /* Copyright (c) 2019 Valerio Pilo 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. */ import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.4 as Controls import org.kde.kcm 1.1 as KCM import org.kde.kconfig 1.0 // for KAuthorized import org.kde.kirigami 2.4 as Kirigami Kirigami.Page { KCM.ConfigModule.quickHelp: i18n("This module lets you configure the window decorations.") KCM.ConfigModule.buttons: KCM.ConfigModule.Help | KCM.ConfigModule.Default | KCM.ConfigModule.Apply title: kcm.name SystemPalette { id: palette colorGroup: SystemPalette.Active } // To match SimpleKCM's borders of Page + headerParent/footerParent (also specified in raw pixels) leftPadding: Kirigami.Settings.isMobile ? 0 : 8 topPadding: leftPadding rightPadding: leftPadding bottomPadding: leftPadding implicitWidth: tabLayout.implicitWidth implicitHeight: tabLayout.implicitHeight ColumnLayout { id: tabLayout anchors.fill: parent spacing: 0 Controls.TabBar { id: tabBar Layout.fillWidth: true Controls.TabButton { text: i18nc("tab label", "Theme") } Controls.TabButton { text: i18nc("tab label", "Titlebar Buttons") } } Controls.Frame { Layout.fillWidth: true Layout.fillHeight: true StackLayout { anchors.fill: parent currentIndex: tabBar.currentIndex ColumnLayout { Themes { Layout.fillWidth: true Layout.fillHeight: true } RowLayout { - Controls.Label { - Layout.alignment: Qt.AlignRight - text: i18nc("combobox label", "Window border size:") + Controls.CheckBox { + id: borderSizeAutoCheckbox + text: i18nc("checkbox label", "Use theme's default window border size") + checked: kcm.borderSizeAuto + onCheckedChanged: { + kcm.borderSizeAuto = checked; + borderSizeComboBox.autoBorderUpdate() + } } - Controls.ComboBox { id: borderSizeComboBox + enabled: !borderSizeAutoCheckbox.checked model: kcm.borderSizesModel - currentIndex: kcm.borderSize onActivated: { kcm.borderSize = currentIndex } + function autoBorderUpdate() { + if (borderSizeAutoCheckbox.checked) { + currentIndex = kcm.recommendedBorderSize + } else { + currentIndex = kcm.borderSize + } + } + + Connections { + target: kcm + onThemeChanged: borderSizeComboBox.autoBorderUpdate() + } } Item { Layout.fillWidth: true } Controls.Button { text: i18nc("button text", "Get New Window Decorations...") icon.name: "get-hot-new-stuff" onClicked: kcm.getNewStuff(this) visible: KAuthorized.authorize("ghns") } } } ColumnLayout { Buttons { Layout.fillWidth: true Layout.fillHeight: true } Controls.CheckBox { id: closeOnDoubleClickOnMenuCheckBox text: i18nc("checkbox label", "Close windows by double clicking the menu button") checked: kcm.closeOnDoubleClickOnMenu onToggled: { kcm.closeOnDoubleClickOnMenu = checked infoLabel.visible = checked } } Kirigami.InlineMessage { Layout.fillWidth: true id: infoLabel type: Kirigami.MessageType.Information text: i18nc("popup tip", "Close by double clicking: Keep the window's Menu button pressed until it appears.") showCloseButton: true visible: false } Controls.CheckBox { id: showToolTipsCheckBox text: i18nc("checkbox label", "Show titlebar button tooltips") checked: kcm.showToolTips onToggled: kcm.showToolTips = checked } } } } } }