diff --git a/kcmkwin/kwindecoration/CMakeLists.txt b/kcmkwin/kwindecoration/CMakeLists.txt --- a/kcmkwin/kwindecoration/CMakeLists.txt +++ b/kcmkwin/kwindecoration/CMakeLists.txt @@ -3,39 +3,28 @@ 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) diff --git a/kcmkwin/kwindecoration/Messages.sh b/kcmkwin/kwindecoration/Messages.sh old mode 100644 new mode 100755 --- a/kcmkwin/kwindecoration/Messages.sh +++ b/kcmkwin/kwindecoration/Messages.sh @@ -1,4 +1,4 @@ -#!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 diff --git a/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.h b/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.h --- a/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.h +++ b/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.h @@ -45,11 +45,13 @@ 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); diff --git a/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp b/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp --- a/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp @@ -153,8 +153,8 @@ 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 @@ 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(); +} + } } diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewbridge.cpp b/kcmkwin/kwindecoration/declarative-plugin/previewbridge.cpp --- a/kcmkwin/kwindecoration/declarative-plugin/previewbridge.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/previewbridge.cpp @@ -97,7 +97,6 @@ return; } m_plugin = plugin; - qDebug() << "Plugin changed to: " << m_plugin; emit pluginChanged(); } @@ -123,22 +122,20 @@ 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 @@ 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(&KCModule::changed); - connect(kcm, changedSignal, apply, &QPushButton::setEnabled); connect(kcm, changedSignal, reset, &QPushButton::setEnabled); connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, kcm, &KCModule::defaults); diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewbutton.h b/kcmkwin/kwindecoration/declarative-plugin/previewbutton.h --- a/kcmkwin/kwindecoration/declarative-plugin/previewbutton.h +++ b/kcmkwin/kwindecoration/declarative-plugin/previewbutton.h @@ -21,6 +21,7 @@ #define KDECOARTIONS_PREVIEW_BUTTON_ITEM_H #include +#include #include #include @@ -39,6 +40,7 @@ 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 @@ 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 @@ private: void createButton(); void syncGeometry(); + QColor m_color; QPointer m_bridge; QPointer m_settings; KDecoration2::Decoration *m_decoration = nullptr; diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewbutton.cpp b/kcmkwin/kwindecoration/declarative-plugin/previewbutton.cpp --- a/kcmkwin/kwindecoration/declarative-plugin/previewbutton.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/previewbutton.cpp @@ -24,7 +24,7 @@ #include -#include +#include namespace KDecoration2 { @@ -132,7 +132,17 @@ 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(); } } diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp b/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp --- a/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp @@ -69,7 +69,6 @@ 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 @@ 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::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::requestContextHelp() { - qDebug() << "context help requested"; } void PreviewClient::requestToggleMaximization(Qt::MouseButtons buttons) @@ -431,7 +427,6 @@ if (m_##variable == variable) { \ return; \ } \ - qDebug() << "Setting " << #variable << ":" << variable;\ m_##variable = variable; \ emit variable##Changed(m_##variable); \ } diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewitem.cpp b/kcmkwin/kwindecoration/declarative-plugin/previewitem.cpp --- a/kcmkwin/kwindecoration/declarative-plugin/previewitem.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/previewitem.cpp @@ -78,15 +78,9 @@ 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 @@ void PreviewItem::setDecoration(Decoration *deco) { - if (m_decoration == deco) { + if (!deco || m_decoration == deco) { return; } - auto updateSlot = static_cast(&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); } diff --git a/kcmkwin/kwindecoration/decorationmodel.h b/kcmkwin/kwindecoration/decorationmodel.h --- a/kcmkwin/kwindecoration/decorationmodel.h +++ b/kcmkwin/kwindecoration/decorationmodel.h @@ -31,6 +31,13 @@ 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 @@ QModelIndex findDecoration(const QString &pluginName, const QString &themeName = QString()) const; - QMap knsProviders() const { - return m_knsProvides; + QStringList knsProviders() const { + return m_knsProviders; } public Q_SLOTS: @@ -56,7 +63,7 @@ bool configuration = false; }; std::vector m_plugins; - QMap m_knsProvides; + QStringList m_knsProviders; }; } diff --git a/kcmkwin/kwindecoration/decorationmodel.cpp b/kcmkwin/kwindecoration/decorationmodel.cpp --- a/kcmkwin/kwindecoration/decorationmodel.cpp +++ b/kcmkwin/kwindecoration/decorationmodel.cpp @@ -60,24 +60,23 @@ 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(); } QHash< int, QByteArray > DecorationsModel::roleNames() const { QHash 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 @@ 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 @@ 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)); diff --git a/kcmkwin/kwindecoration/kcm.h b/kcmkwin/kwindecoration/kcm.h --- a/kcmkwin/kwindecoration/kcm.h +++ b/kcmkwin/kwindecoration/kcm.h @@ -1,80 +1,116 @@ /* - * Copyright 2014 Martin Gräßlin + * Copyright (c) 2019 Valerio Pilo * - * 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 . + * 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 -#include -#include +#pragma once +#include "utils.h" + +#include + + +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 m_newStuffDialog; + + struct Settings + { + KDecoration2::BorderSize borderSize; + int themeIndex; + bool closeOnDoubleClickOnMenu; + DecorationButtonsList buttonsOnLeft; + DecorationButtonsList buttonsOnRight; + }; -#endif + Settings m_savedSettings; + Settings m_currentSettings; +}; diff --git a/kcmkwin/kwindecoration/kcm.cpp b/kcmkwin/kwindecoration/kcm.cpp --- a/kcmkwin/kwindecoration/kcm.cpp +++ b/kcmkwin/kwindecoration/kcm.cpp @@ -1,432 +1,314 @@ /* - * Copyright 2014 Martin Gräßlin + * Copyright (c) 2019 Valerio Pilo * - * 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 . + * 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 -// KDE +#include #include +#include #include -#include -#include -#include -#include -// Qt +#include + #include #include -#include -#include -#include -#include +#include #include -#include +#include #include -#include -#include -K_PLUGIN_FACTORY(KDecorationFactory, - registerPlugin(); - ) +#include + + +K_PLUGIN_FACTORY_WITH_JSON(KCMKWinDecorationFactory, "kwindecoration.json", registerPlugin();) Q_DECLARE_METATYPE(KDecoration2::BorderSize) -namespace KDecoration2 -{ -namespace Configuration +namespace { -static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); +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_configCloseOnDoubleClickOnMenu { QStringLiteral("CloseOnDoubleClickOnMenu") }; +const QString s_configDecoButtonsOnLeft { QStringLiteral("ButtonsOnLeft") }; +const QString s_configDecoButtonsOnRight { QStringLiteral("ButtonsOnRight") }; + +const KDecoration2::BorderSize s_defaultBorderSize = KDecoration2::BorderSize::Normal; +const bool s_defaultCloseOnDoubleClickOnMenu = false; + +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 -static const QString s_defaultPlugin = QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID); -static const QString s_defaultTheme; +const QString s_defaultPlugin { QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID) }; +const QString s_defaultTheme { QStringLiteral("Breeze") }; #else -static const QString s_defaultPlugin = QStringLiteral("org.kde.kwin.aurorae"); -static const QString s_defaultTheme = QStringLiteral("kwin4_decoration_qml_plastik"); +const QString s_defaultPlugin { QStringLiteral("org.kde.kwin.aurorae") }; +const QString s_defaultTheme { QStringLiteral("kwin4_decoration_qml_plastik") }; #endif -static const QString s_borderSizeNormal = QStringLiteral("Normal"); -static const QString s_ghnsIcon = QStringLiteral("get-hot-new-stuff"); +} -ConfigurationForm::ConfigurationForm(QWidget *parent) - : QWidget(parent) +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_defaultDecoButtonsOnLeft, s_defaultDecoButtonsOnRight } + , m_currentSettings(m_savedSettings) { - setupUi(this); + auto about = new KAboutData(QStringLiteral("kcm_kwindecoration"), + i18n("Configure window titlebars and borders"), + 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); } -static bool s_loading = false; +void KCMKWinDecoration::reloadKWinSettings() +{ + QMetaObject::invokeMethod(m_themesModel, "init", Qt::QueuedConnection); +} -ConfigurationModule::ConfigurationModule(QWidget *parent, const QVariantList &args) - : KCModule(parent, args) - , m_model(new DecorationsModel(this)) - , m_proxyModel(new QSortFilterProxyModel(this)) - , m_ui(new ConfigurationForm(this)) - , m_leftButtons(new Preview::ButtonsModel(QVector(), this)) - , m_rightButtons(new Preview::ButtonsModel(QVector(), this)) - , m_availableButtons(new Preview::ButtonsModel(this)) +void KCMKWinDecoration::getNewStuff(QQuickItem *context) { - m_proxyModel->setSourceModel(m_model); - m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); - m_proxyModel->sort(0); - connect(m_ui->filter, &QLineEdit::textChanged, m_proxyModel, &QSortFilterProxyModel::setFilterFixedString); - - m_quickView = new QQuickView(0); - KDeclarative::KDeclarative kdeclarative; - kdeclarative.setDeclarativeEngine(m_quickView->engine()); - kdeclarative.setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); - kdeclarative.setupContext(); - kdeclarative.setupEngine(m_quickView->engine()); - - qmlRegisterType(); - QWidget *widget = QWidget::createWindowContainer(m_quickView, this); - QVBoxLayout* layout = new QVBoxLayout(m_ui->view); - layout->setContentsMargins(0,0,0,0); - layout->addWidget(widget); - - m_quickView->rootContext()->setContextProperty(QStringLiteral("decorationsModel"), m_proxyModel); - updateColors(); - m_quickView->rootContext()->setContextProperty("_borderSizesIndex", 3); // 3 is normal - m_quickView->rootContext()->setContextProperty("leftButtons", m_leftButtons); - m_quickView->rootContext()->setContextProperty("rightButtons", m_rightButtons); - m_quickView->rootContext()->setContextProperty("availableButtons", m_availableButtons); - m_quickView->rootContext()->setContextProperty("initialThemeIndex", -1); - - m_quickView->rootContext()->setContextProperty("titleFont", QFontDatabase::systemFont(QFontDatabase::TitleFont)); - m_quickView->setResizeMode(QQuickView::SizeRootObjectToView); - m_quickView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/kcm_kwindecoration/main.qml")))); - if (m_quickView->status() == QQuickView::Ready) { - auto listView = m_quickView->rootObject()->findChild("listView"); - if (listView) { - connect(listView, SIGNAL(userChangedSelection()), this, SLOT(changed())); - } + 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); } - m_ui->tabWidget->tabBar()->disconnect(); - auto setCurrentTab = [this](int index) { - if (index == 0) - m_ui->doubleClickMessage->hide(); - m_ui->filter->setVisible(index == 0); - m_ui->knsButton->setVisible(index == 0); - if (auto themeList = m_quickView->rootObject()->findChild("themeList")) { - themeList->setVisible(index == 0); - } - m_ui->borderSizesLabel->setVisible(index == 0); - m_ui->borderSizesCombo->setVisible(index == 0); + if (context && context->window()) { + m_newStuffDialog->winId(); // so it creates the windowHandle() + m_newStuffDialog->windowHandle()->setTransientParent(context->window()); + } - m_ui->closeWindowsDoubleClick->setVisible(index == 1); - if (auto buttonLayout = m_quickView->rootObject()->findChild("buttonLayout")) { - buttonLayout->setVisible(index == 1); - } - }; - connect(m_ui->tabWidget->tabBar(), &QTabBar::currentChanged, this, setCurrentTab); - setCurrentTab(0); - - m_ui->doubleClickMessage->setVisible(false); - m_ui->doubleClickMessage->setText(i18n("Close by double clicking:\n To open the menu, keep the button pressed until it appears.")); - m_ui->doubleClickMessage->setCloseButtonVisible(true); - m_ui->borderSizesCombo->setItemData(0, QVariant::fromValue(BorderSize::None)); - m_ui->borderSizesCombo->setItemData(1, QVariant::fromValue(BorderSize::NoSides)); - m_ui->borderSizesCombo->setItemData(2, QVariant::fromValue(BorderSize::Tiny)); - m_ui->borderSizesCombo->setItemData(3, QVariant::fromValue(BorderSize::Normal)); - m_ui->borderSizesCombo->setItemData(4, QVariant::fromValue(BorderSize::Large)); - m_ui->borderSizesCombo->setItemData(5, QVariant::fromValue(BorderSize::VeryLarge)); - m_ui->borderSizesCombo->setItemData(6, QVariant::fromValue(BorderSize::Huge)); - m_ui->borderSizesCombo->setItemData(7, QVariant::fromValue(BorderSize::VeryHuge)); - m_ui->borderSizesCombo->setItemData(8, QVariant::fromValue(BorderSize::Oversized)); - m_ui->knsButton->setIcon(QIcon::fromTheme(s_ghnsIcon)); - - auto changedSlot = static_cast(&ConfigurationModule::changed); - connect(m_ui->closeWindowsDoubleClick, &QCheckBox::stateChanged, this, changedSlot); - connect(m_ui->closeWindowsDoubleClick, &QCheckBox::toggled, this, - [this] (bool toggled) { - if (s_loading) { - return; - } - if (toggled) - m_ui->doubleClickMessage->animatedShow(); - else - m_ui->doubleClickMessage->animatedHide(); - } - ); - connect(m_ui->borderSizesCombo, static_cast(&QComboBox::currentIndexChanged), - this, [this] (int index) { - auto listView = m_quickView->rootObject()->findChild("listView"); - if (listView) { - listView->setProperty("borderSizesIndex", index); - } - changed(); - } - ); - connect(m_model, &QAbstractItemModel::modelReset, this, - [this] { - const auto &kns = m_model->knsProviders(); - m_ui->knsButton->setEnabled(!kns.isEmpty()); - if (kns.isEmpty()) { - return; - } - if (kns.count() > 1) { - QMenu *menu = new QMenu(m_ui->knsButton); - for (auto it = kns.begin(); it != kns.end(); ++it) { - QAction *action = menu->addAction(QIcon::fromTheme(s_ghnsIcon), it.value()); - action->setData(it.key()); - connect(action, &QAction::triggered, this, [this, action] { showKNS(action->data().toString());}); - } - m_ui->knsButton->setMenu(menu); - } - } - ); - connect(m_ui->knsButton, &QPushButton::clicked, this, - [this] { - const auto &kns = m_model->knsProviders(); - if (kns.isEmpty()) { - return; - } - showKNS(kns.firstKey()); - } - ); - connect(m_leftButtons, &QAbstractItemModel::rowsInserted, this, changedSlot); - connect(m_leftButtons, &QAbstractItemModel::rowsMoved, this, changedSlot); - connect(m_leftButtons, &QAbstractItemModel::rowsRemoved, this, changedSlot); - connect(m_rightButtons, &QAbstractItemModel::rowsInserted, this, changedSlot); - connect(m_rightButtons, &QAbstractItemModel::rowsMoved, this, changedSlot); - connect(m_rightButtons, &QAbstractItemModel::rowsRemoved, this, changedSlot); - - QVBoxLayout *l = new QVBoxLayout(this); - l->addWidget(m_ui); - QMetaObject::invokeMethod(m_model, "init", Qt::QueuedConnection); - - m_ui->installEventFilter(this); + m_newStuffDialog->show(); } -ConfigurationModule::~ConfigurationModule() = default; +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)); + + const QString defaultSizeName = Utils::borderSizeToString(s_defaultBorderSize); + setBorderSize(Utils::stringToBorderSize(config.readEntry(s_configBorderSize, defaultSizeName))); + + 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(); -void ConfigurationModule::showEvent(QShowEvent *ev) + m_savedSettings = m_currentSettings; + + updateNeedsSave(); +} + +void KCMKWinDecoration::save() { - KCModule::showEvent(ev); + 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_configBorderSize, Utils::borderSizeToString(m_currentSettings.borderSize)); + 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(); } -static const QMap s_sizes = QMap({ - {QStringLiteral("None"), BorderSize::None}, - {QStringLiteral("NoSides"), BorderSize::NoSides}, - {QStringLiteral("Tiny"), BorderSize::Tiny}, - {s_borderSizeNormal, BorderSize::Normal}, - {QStringLiteral("Large"), BorderSize::Large}, - {QStringLiteral("VeryLarge"), BorderSize::VeryLarge}, - {QStringLiteral("Huge"), BorderSize::Huge}, - {QStringLiteral("VeryHuge"), BorderSize::VeryHuge}, - {QStringLiteral("Oversized"), BorderSize::Oversized} -}); - -static BorderSize stringToSize(const QString &name) +void KCMKWinDecoration::defaults() { - auto it = s_sizes.constFind(name); - if (it == s_sizes.constEnd()) { - // non sense values are interpreted just like normal - return BorderSize::Normal; + 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"; } - return it.value(); + setTheme(themeIndex); + setBorderSize(s_defaultBorderSize); + setCloseOnDoubleClickOnMenu(s_defaultCloseOnDoubleClickOnMenu); + + m_leftButtonsModel->replace(s_defaultDecoButtonsOnLeft); + m_rightButtonsModel->replace(s_defaultDecoButtonsOnRight); + + updateNeedsSave(); } -static QString sizeToString(BorderSize size) +void KCMKWinDecoration::updateNeedsSave() { - return s_sizes.key(size, s_borderSizeNormal); + m_currentSettings.buttonsOnLeft = m_leftButtonsModel->buttons(); + m_currentSettings.buttonsOnRight = m_rightButtonsModel->buttons(); + + setNeedsSave(m_savedSettings.closeOnDoubleClickOnMenu != m_currentSettings.closeOnDoubleClickOnMenu + || m_savedSettings.borderSize != m_currentSettings.borderSize + || m_savedSettings.themeIndex != m_currentSettings.themeIndex + || m_savedSettings.buttonsOnLeft != m_currentSettings.buttonsOnLeft + || m_savedSettings.buttonsOnRight != m_currentSettings.buttonsOnRight); } -static QHash s_buttonNames; -static void initButtons() +QSortFilterProxyModel *KCMKWinDecoration::themesModel() const { - 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'); + return m_proxyThemesModel; } -static QString buttonsToString(const QVector &buttons) +QAbstractListModel *KCMKWinDecoration::leftButtonsModel() { - 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; + return m_leftButtonsModel; } -static -QVector< KDecoration2::DecorationButtonType > readDecorationButtons(const KConfigGroup &config, - const char *key, - const QVector< KDecoration2::DecorationButtonType > &defaultValue) +QAbstractListModel *KCMKWinDecoration::rightButtonsModel() { - 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))); + return m_rightButtonsModel; } -void ConfigurationModule::load() +QAbstractListModel *KCMKWinDecoration::availableButtonsModel() const { - s_loading = true; - const KConfigGroup config = KSharedConfig::openConfig("kwinrc")->group(s_pluginName); - const QString plugin = config.readEntry("library", s_defaultPlugin); - const QString theme = config.readEntry("theme", s_defaultTheme); - m_ui->closeWindowsDoubleClick->setChecked(config.readEntry("CloseOnDoubleClickOnMenu", false)); - const QVariant border = QVariant::fromValue(stringToSize(config.readEntry("BorderSize", s_borderSizeNormal))); - m_ui->borderSizesCombo->setCurrentIndex(m_ui->borderSizesCombo->findData(border)); - - int themeIndex = m_proxyModel->mapFromSource(m_model->findDecoration(plugin, theme)).row(); - m_quickView->rootContext()->setContextProperty("initialThemeIndex", themeIndex); - - // buttons - const auto &left = readDecorationButtons(config, "ButtonsOnLeft", QVector{ - KDecoration2::DecorationButtonType::Menu, - KDecoration2::DecorationButtonType::OnAllDesktops - }); - while (m_leftButtons->rowCount() > 0) { - m_leftButtons->remove(0); - } - for (auto it = left.begin(); it != left.end(); ++it) { - m_leftButtons->add(*it); - } - const auto &right = readDecorationButtons(config, "ButtonsOnRight", QVector{ - KDecoration2::DecorationButtonType::ContextHelp, - KDecoration2::DecorationButtonType::Minimize, - KDecoration2::DecorationButtonType::Maximize, - KDecoration2::DecorationButtonType::Close - }); - while (m_rightButtons->rowCount() > 0) { - m_rightButtons->remove(0); - } - for (auto it = right.begin(); it != right.end(); ++it) { - m_rightButtons->add(*it); - } + return m_availableButtonsModel; +} - KCModule::load(); - s_loading = false; +QStringList KCMKWinDecoration::borderSizesModel() const +{ + return Utils::getBorderSizeNames().values(); } -void ConfigurationModule::save() +int KCMKWinDecoration::borderSize() const { - KConfigGroup config = KSharedConfig::openConfig("kwinrc")->group(s_pluginName); - config.writeEntry("CloseOnDoubleClickOnMenu", m_ui->closeWindowsDoubleClick->isChecked()); - config.writeEntry("BorderSize", sizeToString(m_ui->borderSizesCombo->currentData().value())); - if (auto listView = m_quickView->rootObject()->findChild("listView")) { - const int currentIndex = listView->property("currentIndex").toInt(); - if (currentIndex != -1) { - const QModelIndex index = m_proxyModel->index(currentIndex, 0); - if (index.isValid()) { - config.writeEntry("library", index.data(Qt::UserRole + 4).toString()); - const QString theme = index.data(Qt::UserRole +5).toString(); - config.writeEntry("theme", theme); - } - } - } - config.writeEntry("ButtonsOnLeft", buttonsToString(m_leftButtons->buttons())); - config.writeEntry("ButtonsOnRight", buttonsToString(m_rightButtons->buttons())); - config.sync(); - KCModule::save(); - // Send signal to all kwin instances - QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), - QStringLiteral("org.kde.KWin"), - QStringLiteral("reloadConfig")); - QDBusConnection::sessionBus().send(message); + return Utils::getBorderSizeNames().keys().indexOf(m_currentSettings.borderSize); } -void ConfigurationModule::defaults() +int KCMKWinDecoration::theme() const { - if (auto listView = m_quickView->rootObject()->findChild("listView")) { - const QModelIndex index = m_proxyModel->mapFromSource(m_model->findDecoration(s_defaultPlugin)); - listView->setProperty("currentIndex", index.isValid() ? index.row() : -1); - } - m_ui->borderSizesCombo->setCurrentIndex(m_ui->borderSizesCombo->findData(QVariant::fromValue(stringToSize(s_borderSizeNormal)))); - m_ui->closeWindowsDoubleClick->setChecked(false); - KCModule::defaults(); + return m_currentSettings.themeIndex; } -void ConfigurationModule::showKNS(const QString &config) +bool KCMKWinDecoration::closeOnDoubleClickOnMenu() const { - QPointer downloadDialog = new KNS3::DownloadDialog(config, this); - if (downloadDialog->exec() == QDialog::Accepted && !downloadDialog->changedEntries().isEmpty()) { - auto listView = m_quickView->rootObject()->findChild("listView"); - QString selectedPluginName; - QString selectedThemeName; - if (listView) { - const QModelIndex index = m_proxyModel->index(listView->property("currentIndex").toInt(), 0); - if (index.isValid()) { - selectedPluginName = index.data(Qt::UserRole + 4).toString(); - selectedThemeName = index.data(Qt::UserRole + 5).toString(); - } - } - m_model->init(); - if (!selectedPluginName.isEmpty()) { - const QModelIndex index = m_model->findDecoration(selectedPluginName, selectedThemeName); - const QModelIndex proxyIndex = m_proxyModel->mapFromSource(index); - if (listView) { - listView->setProperty("currentIndex", proxyIndex.isValid() ? proxyIndex.row() : -1); - } - } - } - delete downloadDialog; + return m_currentSettings.closeOnDoubleClickOnMenu; } -bool ConfigurationModule::eventFilter(QObject *watched, QEvent *e) +void KCMKWinDecoration::setBorderSize(int index) { - if (watched != m_ui) { - return false; - } - if (e->type() == QEvent::PaletteChange) { - updateColors(); - } - return false; + setBorderSize(Utils::getBorderSizeNames().keys().at(index)); } -void ConfigurationModule::updateColors() +void KCMKWinDecoration::setBorderSize(KDecoration2::BorderSize size) { - m_quickView->rootContext()->setContextProperty("backgroundColor", m_ui->palette().color(QPalette::Active, QPalette::Window)); - m_quickView->rootContext()->setContextProperty("highlightColor", m_ui->palette().color(QPalette::Active, QPalette::Shadow)); - m_quickView->rootContext()->setContextProperty("baseColor", m_ui->palette().color(QPalette::Active, QPalette::Base)); + if (m_currentSettings.borderSize == size) { + return; + } + m_currentSettings.borderSize = size; + emit borderSizeChanged(); + 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(); } #include "kcm.moc" diff --git a/kcmkwin/kwindecoration/kcm.ui b/kcmkwin/kwindecoration/kcm.ui deleted file mode 100644 --- a/kcmkwin/kwindecoration/kcm.ui +++ /dev/null @@ -1,182 +0,0 @@ - - - KCMForm - - - - 0 - 0 - 386 - 272 - - - - - - - 0 - - - - Theme - - - - - - - - Search... - - - true - - - - - - - - - - - - Close windows by double clicking &the menu button - - - - - - - - - - - 0 - 0 - - - - - 0 - 100 - - - - - - - - - - Border si&ze: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - borderSizesCombo - - - - - - - - No Borders - - - - - No Side Borders - - - - - Tiny - - - - - Normal - - - - - Large - - - - - Very Large - - - - - Huge - - - - - Very Huge - - - - - Oversized - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - Get New Window Decorations... - - - - - - - - - - Buttons - - - - - - - - - KComboBox - QComboBox -
kcombobox.h
-
- - KMessageWidget - QFrame -
kmessagewidget.h
- 1 -
-
- - -
diff --git a/kcmkwin/kwindecoration/kwindecoration.desktop b/kcmkwin/kwindecoration/kwindecoration.desktop --- a/kcmkwin/kwindecoration/kwindecoration.desktop +++ b/kcmkwin/kwindecoration/kwindecoration.desktop @@ -1,15 +1,15 @@ [Desktop Entry] -Exec=kcmshell5 kwindecoration Icon=preferences-system-windows-action +Exec=kcmshell5 kwindecoration + +Categories=Qt;KDE;X-KDE-settings-looknfeel; Type=Service X-KDE-ServiceTypes=KCModule -X-DocPath=kcontrol/kwindecoration/index.html - X-KDE-Library=kcm_kwindecoration X-KDE-ParentApp=kcontrol - X-KDE-System-Settings-Parent-Category=applicationstyle X-KDE-Weight=40 +X-DocPath=kcontrol/kwindecoration/index.html Name=Window Decorations Name[ar]=زخارف النوافذ @@ -72,50 +72,7 @@ Name[zh_CN]=窗口装饰 Name[zh_TW]=視窗裝飾 -Comment=Look and Feel of Window Titles -Comment[bs]=Izgled i osjećaj naslova prozora -Comment[ca]=Aspecte i comportament dels títols de les finestres -Comment[ca@valencia]=Aspecte i comportament dels títols de les finestres -Comment[cs]=Vzhled a dekorace titulků oken -Comment[da]=Udseendet af vinduestitler -Comment[de]=Erscheinungsbild von Fenstertiteln -Comment[el]=Διαμόρφωση της εμφάνισης και αίσθησης του τίτλου των παραθύρων -Comment[en_GB]=Look and Feel of Window Titles -Comment[es]=Aspecto visual de los títulos de las ventanas -Comment[et]=Akna tiitliribade välimus ja tunnetus -Comment[eu]=Leiho-tituluen itxura eta izaera -Comment[fi]=Ikkunoiden kehysten ulkoasu -Comment[fr]=Apparence des titres de fenêtre -Comment[gl]=Aparencia e o comportamento dos títulos das xanelas -Comment[he]=הגדרת המראה והתחושה של מסגרות החלונות -Comment[hu]=Az ablakok címsorának megjelenése -Comment[ia]=Semblantia de titulos de fenestra -Comment[id]=Look and Feel pada Judul Window -Comment[it]=Aspetto dei titoli delle finestre -Comment[ja]=ウィンドウタイトルの外観を設定 -Comment[ko]=창 제목 표시줄의 모습과 느낌 설정 -Comment[lt]=Langų antraščių išvaizda ir elgsena -Comment[nb]=Utseende og virkemåte for vindustitlene -Comment[nds]=Dat Utsehn vun de Finstertiteln instellen -Comment[nl]=Uiterlijk en gedrag van venstertitels -Comment[nn]=Utsjånad og åtferd for vindaugstitlar -Comment[pa]=ਵਿੰਡੋ ਟਾਇਟਲਾਂ ਦੇ ਰੰਗ-ਰੂਪ ਦੀ ਸੰਰਚਨਾ -Comment[pl]=Tytuły okien - wrażenia wzrokowe i dotykowe -Comment[pt]=Aparência e Comportamento dos Títulos das Janelas -Comment[pt_BR]=Aparência do título das janelas -Comment[ru]=Настройка внешнего вида заголовков окон -Comment[sk]=Nastavenie vzhľadu titulkov okien -Comment[sl]=Videz in občutek naslovnih vrstic okna -Comment[sr]=Изглед и осећај за наслове прозора -Comment[sr@ijekavian]=Изглед и осјећај за наслове прозора -Comment[sr@ijekavianlatin]=Izgled i osjećaj za naslove prozora -Comment[sr@latin]=Izgled i osećaj za naslove prozora -Comment[sv]=Namnlisternas utseende och känsla -Comment[tr]=Pencere Başlıklarının Görünüm ve Davranışlarını Yapılandır -Comment[uk]=Вигляд і поведінка смужок заголовків вікон -Comment[x-test]=xxLook and Feel of Window Titlesxx -Comment[zh_CN]=窗口标题的观感 -Comment[zh_TW]=視窗標題列的外觀與感覺 +Comment=Configure window titlebars and borders X-KDE-Keywords=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration X-KDE-Keywords[bs]=kwin,prozor,upravitelj,granica,stil,tema,izgled,osjećati,izgled,dugme,držati,ivica,kwm,dekoracija @@ -160,6 +117,3 @@ X-KDE-Keywords[x-test]=xxkwinxx,xxwindowxx,xxmanagerxx,xxborderxx,xxstylexx,xxthemexx,xxlookxx,xxfeelxx,xxlayoutxx,xxbuttonxx,xxhandlexx,xxedgexx,xxkwmxx,xxdecorationxx X-KDE-Keywords[zh_CN]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration,窗口,管理,边框,样式,主题,外怪,布局,按钮,边界,装饰 X-KDE-Keywords[zh_TW]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration - -Categories=Qt;KDE;X-KDE-settings-looknfeel; - diff --git a/kcmkwin/kwindecoration/qml/ButtonGroup.qml b/kcmkwin/kwindecoration/package/contents/ui/ButtonGroup.qml rename from kcmkwin/kwindecoration/qml/ButtonGroup.qml rename to kcmkwin/kwindecoration/package/contents/ui/ButtonGroup.qml --- a/kcmkwin/kwindecoration/qml/ButtonGroup.qml +++ b/kcmkwin/kwindecoration/package/contents/ui/ButtonGroup.qml @@ -17,35 +17,36 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import QtQuick 2.1 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.1 +import QtQuick 2.7 import org.kde.kwin.private.kdecoration 1.0 as KDecoration -import org.kde.plasma.core 2.0 as PlasmaCore; ListView { id: view property string key property bool dragging: false + property int iconSize: units.iconSizes.small orientation: ListView.Horizontal interactive: false spacing: units.smallSpacing - implicitHeight: units.iconSizes.small - implicitWidth: count * (units.iconSizes.small + units.smallSpacing) - Math.min(1, count) * units.smallSpacing + implicitHeight: iconSize + implicitWidth: count * (iconSize + units.smallSpacing) - Math.min(1, count) * units.smallSpacing delegate: Item { - width: units.iconSizes.small - height: units.iconSizes.small + width: iconSize + height: iconSize KDecoration.Button { id: button property int itemIndex: index property var buttonsModel: parent.ListView.view.model bridge: bridgeItem.bridge settings: settingsItem type: model["button"] + width: iconSize + height: iconSize anchors.fill: Drag.active ? undefined : parent Drag.keys: [ "decoButtonRemove", view.key ] Drag.active: dragArea.drag.active Drag.onActiveChanged: view.dragging = Drag.active + color: palette.windowText } MouseArea { id: dragArea diff --git a/kcmkwin/kwindecoration/qml/Buttons.qml b/kcmkwin/kwindecoration/package/contents/ui/Buttons.qml rename from kcmkwin/kwindecoration/qml/Buttons.qml rename to kcmkwin/kwindecoration/package/contents/ui/Buttons.qml --- a/kcmkwin/kwindecoration/qml/Buttons.qml +++ b/kcmkwin/kwindecoration/package/contents/ui/Buttons.qml @@ -1,5 +1,6 @@ /* * Copyright 2014 Martin Gräßlin + * Copyright (c) 2019 Valerio Pilo * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -17,76 +18,82 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Controls 2.0 as QQC2 -import QtQuick.Layouts 1.1 +import QtQuick 2.7 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.4 as Controls +import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kwin.private.kdecoration 1.0 as KDecoration -import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons; -Item { - objectName: "buttonLayout" - Layout.preferredHeight: layout.height +ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + property int buttonIconSize: units.iconSizes.medium + property int titleBarSpacing: units.iconSizes.smallMedium + KDecoration.Bridge { id: bridgeItem plugin: "org.kde.breeze" } KDecoration.Settings { id: settingsItem bridge: bridgeItem.bridge } + Rectangle { - anchors.fill: parent - border.width: Math.ceil(units.gridUnit / 16.0) - border.color: highlightColor - color: baseColor + Layout.fillWidth: true + color: palette.base + radius: units.smallSpacing + height: fakeWindow.height + Layout.margins: units.smallSpacing + ColumnLayout { - id: layout + id: fakeWindow width: parent.width - height: titlebarRect.height + availableGrid.height + dragHint.height + 5*layout.spacing + Rectangle { - id: titlebarRect - height: buttonPreviewRow.height + units.smallSpacing - border.width: Math.ceil(units.gridUnit / 16.0) - border.color: highlightColor - color: backgroundColor + id: titleBar Layout.fillWidth: true - Layout.topMargin: -border.width // prevent a double top border + height: buttonPreviewRow.height + 2 * titleBarSpacing + radius: units.smallSpacing + gradient: Gradient { + GradientStop { position: 0.0; color: palette.midlight } + GradientStop { position: 1.0; color: palette.window } + } + RowLayout { id: buttonPreviewRow - anchors.top: parent.top; - anchors.left: parent.left; - anchors.right: parent.right; - anchors.margins: units.smallSpacing / 2 - height: Math.max(units.iconSizes.small, titlebar.implicitHeight) + units.smallSpacing/2 + anchors { + margins: titleBarSpacing + left: parent.left + right: parent.right + top: parent.top + } + ButtonGroup { id: leftButtonsView - height: buttonPreviewRow.height - model: leftButtons + iconSize: buttonIconSize + model: kcm.leftButtonsModel key: "decoButtonLeft" } - QQC2.Label { - id: titlebar + Controls.Label { + id: titleBarLabel Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - font: titleFont + font.bold: true text: i18n("Titlebar") } - ButtonGroup { id: rightButtonsView - height: buttonPreviewRow.height - model: rightButtons + iconSize: buttonIconSize + model: kcm.rightButtonsModel key: "decoButtonRight" } } DropArea { + id: titleBarDropArea anchors { - fill: titlebarRect - leftMargin: -units.iconSizes.small - topMargin: -units.iconSizes.small - rightMargin: +units.iconSizes.small - bottomMargin: +units.iconSizes.small + fill: parent + margins: -titleBarSpacing } keys: [ "decoButtonAdd", "decoButtonRight", "decoButtonLeft" ] onEntered: { @@ -105,9 +112,13 @@ return; } var point = mapToItem(view, drag.x, drag.y); - var index = view.indexAt(point.x, point.y); - if (index == -1 && (view.x + view.width <= drag.x)) { - index = view.count - 1; + var index = 0 + for(var childIndex = 0 ; childIndex < (view.count - 1) ; childIndex++) { + var child = view.contentItem.children[childIndex] + if (child.x > point.x) { + break + } + index = childIndex + 1 } if (drop.keys.indexOf("decoButtonAdd") != -1) { view.model.add(index, drag.source.type); @@ -138,52 +149,64 @@ } } GridView { - id: availableGrid + id: availableButtonsGrid Layout.fillWidth: true - model: availableButtons + Layout.minimumHeight: availableButtonsGrid.cellHeight * 2 + Layout.margins: units.largeSpacing + model: kcm.availableButtonsModel interactive: false - height: Math.ceil(cellHeight * 3) delegate: Item { id: availableDelegate - Layout.margins: units.gridUnit - width: availableGrid.cellWidth - height: availableGrid.cellHeight + Layout.margins: units.largeSpacing + width: availableButtonsGrid.cellWidth + height: availableButtonsGrid.cellHeight opacity: (leftButtonsView.dragging || rightButtonsView.dragging) ? 0.25 : 1.0 - KDecoration.Button { - id: availableButton - anchors.centerIn: Drag.active ? undefined : parent - bridge: bridgeItem.bridge - settings: settingsItem - type: model["button"] - width: units.iconSizes.small - height: units.iconSizes.small - Drag.keys: [ "decoButtonAdd" ] - Drag.active: dragArea.drag.active + Rectangle { + id: availableButtonFrame + anchors.horizontalCenter: parent.horizontalCenter + color: palette.window + radius: units.smallSpacing + width: buttonIconSize + units.largeSpacing + height: buttonIconSize + units.largeSpacing + + KDecoration.Button { + id: availableButton + anchors.centerIn: Drag.active ? undefined : availableButtonFrame + bridge: bridgeItem.bridge + settings: settingsItem + type: model["button"] + width: buttonIconSize + height: buttonIconSize + Drag.keys: [ "decoButtonAdd" ] + Drag.active: dragArea.drag.active + color: palette.windowText + } + MouseArea { + id: dragArea + anchors.fill: availableButton + drag.target: availableButton + cursorShape: Qt.SizeAllCursor + onReleased: { + if (availableButton.Drag.target) { + availableButton.Drag.drop(); + } else { + availableButton.Drag.cancel(); + } + } + } } - QQC2.Label { + Controls.Label { id: iconLabel text: model["display"] horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignTop - anchors.bottom: parent.bottom + anchors.top: availableButtonFrame.bottom + anchors.topMargin: units.smallSpacing anchors.left: parent.left anchors.right: parent.right elide: Text.ElideRight wrapMode: Text.Wrap - height: 2 * dragHint.implicitHeight + lineHeight - } - MouseArea { - id: dragArea - anchors.fill: availableButton - drag.target: availableButton - cursorShape: Qt.SizeAllCursor - onReleased: { - if (availableButton.Drag.target) { - availableButton.Drag.drop(); - } else { - availableButton.Drag.cancel(); - } - } + height: 2 * implicitHeight + lineHeight } } DropArea { @@ -198,17 +221,17 @@ ColumnLayout { anchors.centerIn: parent opacity: (leftButtonsView.dragging || rightButtonsView.dragging) ? 1.0 : 0.0 - QQC2.Label { + Controls.Label { text: i18n("Drop here to remove button") font.weight: Font.Bold Layout.bottomMargin: units.smallSpacing } - KQuickControlsAddons.QIconItem { + PlasmaCore.IconItem { id: icon width: units.iconSizes.smallMedium height: units.iconSizes.smallMedium - icon: "emblem-remove" Layout.alignment: Qt.AlignHCenter + source: "emblem-remove" } Item { Layout.fillHeight: true @@ -218,11 +241,12 @@ } Text { id: dragHint - opacity: (leftButtonsView.dragging || rightButtonsView.dragging || availableGrid.dragging) ? 0.0 : 0.50 + color: palette.windowText + opacity: (leftButtonsView.dragging || rightButtonsView.dragging || availableButtonsGrid.dragging) ? 0.0 : 1.0 Layout.fillWidth: true - Layout.bottomMargin: units.smallSpacing + Layout.topMargin: titleBarSpacing + Layout.bottomMargin: titleBarSpacing horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignTop wrapMode: Text.NoWrap text: i18n("Drag buttons between here and the titlebar") } diff --git a/kcmkwin/kwindecoration/package/contents/ui/Themes.qml b/kcmkwin/kwindecoration/package/contents/ui/Themes.qml new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindecoration/package/contents/ui/Themes.qml @@ -0,0 +1,126 @@ +/* + * Copyright 2014 Martin Gräßlin + * Copyright (c) 2019 Valerio Pilo + * + * 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 . + */ +import QtQuick 2.7 +import org.kde.kcm 1.1 as KCM +import org.kde.kirigami 2.2 as Kirigami +import org.kde.kwin.private.kdecoration 1.0 as KDecoration + +KCM.GridView { + property int cellSpacing: 2 * units.largeSpacing + property int previewWidth: 18 * units.gridUnit + property int previewHeight: previewWidth / 1.7 + + function updateDecoration(item, marginTopLeft, marginBottomRight) { + var mainMargin = units.largeSpacing + var shd = item.shadow + item.anchors.leftMargin = mainMargin + marginTopLeft - (shd ? shd.paddingLeft : 0) + item.anchors.rightMargin = mainMargin + marginBottomRight - (shd ? shd.paddingRight : 0) + item.anchors.topMargin = mainMargin + marginTopLeft - (shd ? shd.paddingTop : 0) + item.anchors.bottomMargin = mainMargin + marginBottomRight - (shd ? shd.paddingBottom : 0) + } + + view.model: kcm.themesModel + view.currentIndex: kcm.theme + view.onCurrentIndexChanged: kcm.theme = view.currentIndex + view.onContentHeightChanged: view.positionViewAtIndex(view.currentIndex, GridView.Visible) + + view.cellWidth: previewWidth + cellSpacing + view.cellHeight: previewHeight + cellSpacing + + view.delegate: KCM.GridDelegate { + text: model.display + + thumbnailAvailable: true + thumbnail: Rectangle { + anchors.fill: parent + width: previewWidth + height: previewHeight + color: palette.base + clip: true + + // Workaround: the thumbnail size can't be otherwise changed + Component.onCompleted: { + parent.parent.width = previewWidth + parent.parent.height = previewHeight + } + + KDecoration.Bridge { + id: bridgeItem + plugin: model.plugin + theme: model.theme + } + KDecoration.Settings { + id: settingsItem + bridge: bridgeItem.bridge + } + KDecoration.Decoration { + id: inactivePreview + bridge: bridgeItem.bridge + settings: settingsItem + anchors.fill: parent + onShadowChanged: updateDecoration(inactivePreview, 0, client.decoration.titleBar.height) + Component.onCompleted: { + client.active = false + client.caption = model.display + updateDecoration(inactivePreview, 0, client.decoration.titleBar.height) + } + } + KDecoration.Decoration { + id: activePreview + bridge: bridgeItem.bridge + settings: settingsItem + anchors.fill: parent + onShadowChanged: updateDecoration(activePreview, client.decoration.titleBar.height, 0) + Component.onCompleted: { + client.active = true + client.caption = model.display + updateDecoration(activePreview, client.decoration.titleBar.height, 0) + } + } + MouseArea { + anchors.fill: parent + onClicked: view.currentIndex = index + } + Connections { + target: kcm + onBorderSizeChanged: settingsItem.borderSizesIndex = kcm.borderSize + } + } + actions: [ + Kirigami.Action { + iconName: "edit-entry" + tooltip: i18n("Edit %1 Theme", model.display) + enabled: model.configureable + onTriggered: { + view.currentIndex = index + bridgeItem.bridge.configure() + } + } + ] + + onClicked: view.currentIndex = index + } + Connections { + target: kcm + onThemeChanged: view.currentIndex = kcm.theme + } +} + diff --git a/kcmkwin/kwindecoration/package/contents/ui/main.qml b/kcmkwin/kwindecoration/package/contents/ui/main.qml new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindecoration/package/contents/ui/main.qml @@ -0,0 +1,127 @@ +/* + 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 + + 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.ComboBox { + id: borderSizeComboBox + model: kcm.borderSizesModel + currentIndex: kcm.borderSize + onActivated: { + kcm.borderSize = currentIndex + } + } + 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 + } + } + } + } + } +} diff --git a/kcmkwin/kwindecoration/package/metadata.desktop b/kcmkwin/kwindecoration/package/metadata.desktop new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindecoration/package/metadata.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Icon=preferences-system-windows-action +Type=Service +Keywords= +X-KDE-ParentApp= +X-KDE-System-Settings-Parent-Category=applicationstyle +X-KDE-PluginInfo-Author=Valerio Pilo +X-KDE-PluginInfo-Email=vpilo@coldshock.net +X-KDE-PluginInfo-License=GPL-2.0+ +X-KDE-PluginInfo-Name=kcm_kwindecoration +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website=https://www.kde.org/plasma-desktop +X-KDE-ServiceTypes=Plasma/Generic +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml + +Name=Window Decorations +Name[ar]=زخارف النوافذ +Name[bg]=Декорации на прозорците +Name[bs]=Dekoracije prozora +Name[ca]=Decoració de les finestres +Name[ca@valencia]=Decoració de les finestres +Name[cs]=Dekorace oken +Name[da]=Vinduesdekorationer +Name[de]=Fensterdekoration +Name[el]=Διακοσμήσεις παραθύρου +Name[en_GB]=Window Decorations +Name[es]=Decoración de ventanas +Name[et]=Akna dekoratsioonid +Name[eu]=Leiho-apaindurak +Name[fi]=Ikkunakehykset +Name[fr]=Décorations de fenêtres +Name[ga]=Maisiúcháin Fhuinneog +Name[gl]=Decoración da xanela +Name[he]=מסגרת חלון +Name[hi]=विंडो सजावट +Name[hr]=Ukrasi prozora +Name[hu]=Ablakdekorációk +Name[ia]=Decorationes de fenestra +Name[id]=Dekorasi Window +Name[is]=Gluggaskreytingar +Name[it]=Decorazioni delle finestre +Name[ja]=ウィンドウの飾り +Name[kk]=Терезенің безендірулері +Name[km]=ការ​តុបតែង​បង្អួច +Name[kn]=ವಿಂಡೋ ಅಲಂಕಾರಗಳು +Name[ko]=창 장식 +Name[lt]=Lango dekoracijos +Name[lv]=Logu dekorācijas +Name[mr]=चौकट सजावट +Name[nb]=Vinduspynt +Name[nds]=Finstern opfladusen +Name[nl]=Vensterdecoraties +Name[nn]=Vindaugspynt +Name[pa]=ਵਿੰਡੋ ਸਜਾਵਟ +Name[pl]=Wygląd okien +Name[pt]=Decorações das Janelas +Name[pt_BR]=Decorações da janela +Name[ro]=Decorații fereastră +Name[ru]=Оформление окон +Name[si]=කවුළු සැරසිලි +Name[sk]=Dekorácie okien +Name[sl]=Okraski oken +Name[sr]=Декорације прозора +Name[sr@ijekavian]=Декорације прозора +Name[sr@ijekavianlatin]=Dekoracije prozora +Name[sr@latin]=Dekoracije prozora +Name[sv]=Fönsterdekorationer +Name[th]=ส่วนตกแต่งหน้าต่าง +Name[tr]=Pencere Dekorasyonları +Name[ug]=كۆزنەك بېزەكلىرى +Name[uk]=Обрамлення вікон +Name[wa]=Gåyotaedjes des fniesses +Name[x-test]=xxWindow Decorationsxx +Name[zh_CN]=窗口装饰 +Name[zh_TW]=視窗裝飾 +Comment=Configure window titlebars and borders diff --git a/kcmkwin/kwindecoration/qml/Previews.qml b/kcmkwin/kwindecoration/qml/Previews.qml deleted file mode 100644 --- a/kcmkwin/kwindecoration/qml/Previews.qml +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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 . - */ -import QtQuick 2.1 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.1 -import org.kde.kwin.private.kdecoration 1.0 as KDecoration - -ScrollView { - objectName: "themeList" - frameVisible: true - GridView { - id: gridView - objectName: "listView" - signal userChangedSelection() - model: decorationsModel - cellWidth: 20 * units.gridUnit - cellHeight: cellWidth / 1.6 - highlightFollowsCurrentItem: true - onContentHeightChanged: { - if (gridView.currentIndex == -1 && initialThemeIndex != -1) { - gridView.currentIndex = initialThemeIndex - } - gridView.positionViewAtIndex(gridView.currentIndex, GridView.Visible) - } - Rectangle { - z: -1 - anchors.fill: parent - color: baseColor - } - highlight: Rectangle { - color: highlightColor - opacity: 0.6 - } - highlightMoveDuration: units.longDuration - boundsBehavior: Flickable.StopAtBounds - property int borderSizesIndex: 3 // 3 == Normal - delegate: Item { - width: gridView.cellWidth - height: gridView.cellHeight - KDecoration.Bridge { - id: bridgeItem - plugin: model["plugin"] - theme: model["theme"] - } - KDecoration.Settings { - id: settingsItem - bridge: bridgeItem.bridge - borderSizesIndex: gridView.borderSizesIndex - } - Component.onCompleted: { - if (gridView.currentIndex == -1 && initialThemeIndex != -1) { - gridView.currentIndex = initialThemeIndex - } - } - MouseArea { - hoverEnabled: false - anchors.fill: parent - onClicked: { - gridView.currentIndex = index - gridView.userChangedSelection() - } - } - ColumnLayout { - id: decorationPreviews - property string themeName: model["display"] - anchors.fill: parent - Item { - KDecoration.Decoration { - id: inactivePreview - bridge: bridgeItem.bridge - settings: settingsItem - anchors.fill: parent - Component.onCompleted: { - client.caption = decorationPreviews.themeName - client.active = false; - anchors.leftMargin = Qt.binding(function() { return 40 - (inactivePreview.shadow ? inactivePreview.shadow.paddingLeft : 0);}); - anchors.rightMargin = Qt.binding(function() { return 10 - (inactivePreview.shadow ? inactivePreview.shadow.paddingRight : 0);}); - anchors.topMargin = Qt.binding(function() { return 10 - (inactivePreview.shadow ? inactivePreview.shadow.paddingTop : 0);}); - anchors.bottomMargin = Qt.binding(function() { return 40 - (inactivePreview.shadow ? inactivePreview.shadow.paddingBottom : 0);}); - } - } - KDecoration.Decoration { - id: activePreview - bridge: bridgeItem.bridge - settings: settingsItem - anchors.fill: parent - Component.onCompleted: { - client.caption = decorationPreviews.themeName - client.active = true; - anchors.leftMargin = Qt.binding(function() { return 10 - (activePreview.shadow ? activePreview.shadow.paddingLeft : 0);}); - anchors.rightMargin = Qt.binding(function() { return 40 - (activePreview.shadow ? activePreview.shadow.paddingRight : 0);}); - anchors.topMargin = Qt.binding(function() { return 40 - (activePreview.shadow ? activePreview.shadow.paddingTop : 0);}); - anchors.bottomMargin = Qt.binding(function() { return 10 - (activePreview.shadow ? activePreview.shadow.paddingBottom : 0);}); - } - } - MouseArea { - hoverEnabled: false - anchors.fill: parent - onClicked: { - gridView.currentIndex = index - gridView.userChangedSelection() - } - } - Layout.fillWidth: true - Layout.fillHeight: true - /* Create an invisible rectangle that's the same size as the inner content rect - of the foreground window preview so that we can center the button within it. - We have to center rather than using anchors because the button width varies - with different localizations */ - Item { - anchors { - left: parent.left - leftMargin: 10 - right: parent.right - rightMargin: 40 - top: parent.top - topMargin: 68 - bottom: parent.bottom - bottomMargin: 10 - } - Button { - id: configureButton - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } - enabled: model["configureable"] - iconName: "configure" - text: i18n("Configure %1...", decorationPreviews.themeName) - onClicked: { - gridView.currentIndex = index - gridView.userChangedSelection() - bridgeItem.bridge.configure() - } - } - } - } - } - } - } - Layout.preferredHeight: 20 * units.gridUnit -} - diff --git a/kcmkwin/kwindecoration/qml/main.qml b/kcmkwin/kwindecoration/qml/main.qml deleted file mode 100644 --- a/kcmkwin/kwindecoration/qml/main.qml +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 . - */ -import QtQuick 2.1 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.1 - -Rectangle { - color: backgroundColor - ColumnLayout { - anchors.fill: parent - Previews { - Layout.fillWidth: true - Layout.fillHeight: true - } - Buttons { - Layout.alignment: Qt.AlignTop - Layout.fillWidth: true - } - } -} diff --git a/kcmkwin/kwindecoration/utils.h b/kcmkwin/kwindecoration/utils.h new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindecoration/utils.h @@ -0,0 +1,40 @@ +/* + * 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 +#include + +#include + + +using DecorationButtonsList = QVector; + +namespace Utils +{ + +QString buttonsToString(const DecorationButtonsList &buttons); +DecorationButtonsList readDecorationButtons(const KConfigGroup &config, const QString &key, const DecorationButtonsList &defaultValue); + +KDecoration2::BorderSize stringToBorderSize(const QString &name); +QString borderSizeToString(KDecoration2::BorderSize size); + +const QMap &getBorderSizeNames(); + +} diff --git a/kcmkwin/kwindecoration/utils.cpp b/kcmkwin/kwindecoration/utils.cpp new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindecoration/utils.cpp @@ -0,0 +1,119 @@ +/* + * 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 "utils.h" + +#include +#include + +namespace +{ +const QMap s_borderSizes { + { 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 } +}; +const QMap s_borderSizeNames { + { KDecoration2::BorderSize::None, i18n("No Borders") }, + { KDecoration2::BorderSize::NoSides, i18n("No Side Borders") }, + { KDecoration2::BorderSize::Tiny, i18n("Tiny") }, + { KDecoration2::BorderSize::Normal, i18n("Normal") }, + { KDecoration2::BorderSize::Large, i18n("Large") }, + { KDecoration2::BorderSize::VeryLarge, i18n("Very Large") }, + { KDecoration2::BorderSize::Huge, i18n("Huge") }, + { KDecoration2::BorderSize::VeryHuge, i18n("Very Huge") }, + { KDecoration2::BorderSize::Oversized, i18n("Oversized") } +}; + +const QHash s_buttonNames { + {KDecoration2::DecorationButtonType::Menu, QChar('M') }, + {KDecoration2::DecorationButtonType::ApplicationMenu, QChar('N') }, + {KDecoration2::DecorationButtonType::OnAllDesktops, QChar('S') }, + {KDecoration2::DecorationButtonType::ContextHelp, QChar('H') }, + {KDecoration2::DecorationButtonType::Minimize, QChar('I') }, + {KDecoration2::DecorationButtonType::Maximize, QChar('A') }, + {KDecoration2::DecorationButtonType::Close, QChar('X') }, + {KDecoration2::DecorationButtonType::KeepAbove, QChar('F') }, + {KDecoration2::DecorationButtonType::KeepBelow, QChar('B') }, + {KDecoration2::DecorationButtonType::Shade, QChar('L') } +}; +} + + +namespace Utils +{ + +QString buttonsToString(const DecorationButtonsList &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; +} + +DecorationButtonsList readDecorationButtons(const KConfigGroup &config, const QString &key, const DecorationButtonsList &defaultValue) +{ + auto buttonsFromString = [](const QString &buttons) -> DecorationButtonsList { + DecorationButtonsList 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))); +} + +KDecoration2::BorderSize stringToBorderSize(const QString &name) +{ + auto it = s_borderSizes.constFind(name); + if (it == s_borderSizes.constEnd()) { + // non sense values are interpreted just like normal + return KDecoration2::BorderSize::Normal; + } + return it.value(); +} + +QString borderSizeToString(KDecoration2::BorderSize size) +{ + return s_borderSizes.key(size); +} + +const QMap &getBorderSizeNames() +{ + return s_borderSizeNames; +} + +} // namespace Utils diff --git a/kcmkwin/kwindecoration/window-decorations.knsrc b/kcmkwin/kwindecoration/window-decorations.knsrc new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindecoration/window-decorations.knsrc @@ -0,0 +1,67 @@ +[KNewStuff3] +Name=Window Decorations +Name[ar]=زخارف النوافذ +Name[bg]=Декорации на прозорците +Name[bs]=Dekoracije prozora +Name[ca]=Decoració de les finestres +Name[ca@valencia]=Decoració de les finestres +Name[cs]=Dekorace oken +Name[da]=Vinduesdekorationer +Name[de]=Fensterdekoration +Name[el]=Διακοσμήσεις παραθύρου +Name[en_GB]=Window Decorations +Name[es]=Decoración de ventanas +Name[et]=Akna dekoratsioonid +Name[eu]=Leiho-apaindurak +Name[fi]=Ikkunakehykset +Name[fr]=Décorations de fenêtres +Name[ga]=Maisiúcháin Fhuinneog +Name[gl]=Decoración da xanela +Name[he]=מסגרת חלון +Name[hi]=विंडो सजावट +Name[hr]=Ukrasi prozora +Name[hu]=Ablakdekorációk +Name[ia]=Decorationes de fenestra +Name[id]=Dekorasi Window +Name[is]=Gluggaskreytingar +Name[it]=Decorazioni delle finestre +Name[ja]=ウィンドウの飾り +Name[kk]=Терезенің безендірулері +Name[km]=ការ​តុបតែង​បង្អួច +Name[kn]=ವಿಂಡೋ ಅಲಂಕಾರಗಳು +Name[ko]=창 장식 +Name[lt]=Lango dekoracijos +Name[lv]=Logu dekorācijas +Name[mr]=चौकट सजावट +Name[nb]=Vinduspynt +Name[nds]=Finstern opfladusen +Name[nl]=Vensterdecoraties +Name[nn]=Vindaugspynt +Name[pa]=ਵਿੰਡੋ ਸਜਾਵਟ +Name[pl]=Wygląd okien +Name[pt]=Decorações das Janelas +Name[pt_BR]=Decorações da janela +Name[ro]=Decorații fereastră +Name[ru]=Оформление окон +Name[si]=කවුළු සැරසිලි +Name[sk]=Dekorácie okien +Name[sl]=Okraski oken +Name[sr]=Декорације прозора +Name[sr@ijekavian]=Декорације прозора +Name[sr@ijekavianlatin]=Dekoracije prozora +Name[sr@latin]=Dekoracije prozora +Name[sv]=Fönsterdekorationer +Name[th]=ส่วนตกแต่งหน้าต่าง +Name[tr]=Pencere Dekorasyonları +Name[ug]=كۆزنەك بېزەكلىرى +Name[uk]=Обрамлення вікон +Name[wa]=Gåyotaedjes des fniesses +Name[x-test]=xxWindow Decorationsxx +Name[zh_CN]=窗口装饰 +Name[zh_TW]=視窗裝飾 + +ProvidersUrl=https://autoconfig.kde.org/ocs/providers.xml +Categories=Various Plasma Styles,Aurorae Themes +TargetDir=themes +StandardResource=tmp +Uncompress=archive