diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,9 @@ find_package(KF5GuiAddons REQUIRED) find_package(KF5IconThemes REQUIRED) find_package(KF5DBusAddons REQUIRED) +find_package(KF5Service REQUIRED) find_package(PkgConfig REQUIRED) +find_package(KDecoration2 REQUIRED) find_package(GSettingSchemas REQUIRED) find_package(XSettingsd) diff --git a/kded/CMakeLists.txt b/kded/CMakeLists.txt --- a/kded/CMakeLists.txt +++ b/kded/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(kwin_bridge) + set(kscreen_daemon_SRCS gtkconfig.cpp configeditor.cpp @@ -19,6 +21,8 @@ ) target_link_libraries(gtkconfig + PRIVATE + KWinBridge PUBLIC Qt5::DBus KF5::CoreAddons diff --git a/kded/configeditor.h b/kded/configeditor.h --- a/kded/configeditor.h +++ b/kded/configeditor.h @@ -35,16 +35,21 @@ void setGtk3ConfigValueSettingsIni(const QString ¶mName, const QVariant ¶mValue); void setGtk3ConfigValueXSettingsd(const QString ¶mName, const QVariant ¶mValue); + void setClientSideDecorations(const QMap &windowDecorationsButtonsImages); void setGtk3Colors(const QMap &colorsDefinitions); QString gtk2ConfigValue(const QString& paramName); QString gtk3ConfigValueSettingsIni(const QString& paramName); void removeLegacyGtk2Strings(); + + void saveWindowDecorationsToAssets(const QMap &windowDecorationsButtonsImages); + void modifyWindowDecorationsCssFile(); + void addGtkModule(const QString &moduleName); - void addImportStatementToGtkCssUserFile(); + void addImportStatementsToGtkCssUserFile(); void modifyColorsCssFile(const QMap &colorsDefinitions); void replaceValueInGtkrcContents(QString >krcContents, const QString ¶mName, const QVariant ¶mValue); diff --git a/kded/configeditor.cpp b/kded/configeditor.cpp --- a/kded/configeditor.cpp +++ b/kded/configeditor.cpp @@ -107,9 +107,16 @@ reloadGtk2Apps(); } +void ConfigEditor::setClientSideDecorations(const QMap &windowDecorationsButtonsImages) +{ + saveWindowDecorationsToAssets(windowDecorationsButtonsImages); + addImportStatementsToGtkCssUserFile(); + modifyWindowDecorationsCssFile(); +} + void ConfigEditor::setGtk3Colors(const QMap &colorsDefinitions) { - addImportStatementToGtkCssUserFile(); + addImportStatementsToGtkCssUserFile(); modifyColorsCssFile(colorsDefinitions); addGtkModule(QStringLiteral("colorreload-gtk-module")); } @@ -172,6 +179,36 @@ reloadGtk2Apps(); } +void ConfigEditor::saveWindowDecorationsToAssets(const QMap& windowDecorationsButtonsImages) +{ + QDir assetsFolder {QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/gtk-3.0/assets")}; + + if (!assetsFolder.exists()) { + assetsFolder.mkpath(QStringLiteral(".")); + } + + for (auto it = windowDecorationsButtonsImages.cbegin(); it != windowDecorationsButtonsImages.cend(); it++) { + QString filePath = QStringLiteral("%1/%2.png").arg(assetsFolder.path(), it.key()); + + QFile file {filePath}; + file.open(QIODevice::ReadWrite | QIODevice::Truncate); + it.value().save(&file, "PNG"); + } +} + +void ConfigEditor::modifyWindowDecorationsCssFile() +{ + QString windowDecorationsCssPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/gtk-3.0/windowDecorations.css"); + QFile windowDecorationsCss(windowDecorationsCssPath); + + if (windowDecorationsCss.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QTextStream colorsCssStream(&windowDecorationsCss); + + // TODO: Write appropriate css + + } +} + void ConfigEditor::addGtkModule(const QString& moduleName) { const QString currentModulesString = gtk3ConfigValueSettingsIni(QStringLiteral("gtk-modules")); @@ -187,18 +224,24 @@ } } -void ConfigEditor::addImportStatementToGtkCssUserFile() +void ConfigEditor::addImportStatementsToGtkCssUserFile() { QString gtkCssPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/gtk-3.0/gtk.css"); QFile gtkCss(gtkCssPath); if (gtkCss.open(QIODevice::ReadWrite)) { QByteArray gtkCssContents = gtkCss.readAll(); - static const QByteArray importStatement = QByteArrayLiteral("@import 'colors.css';"); - if (!gtkCssContents.contains(importStatement)) { - QTextStream gtkCssStream(>kCss); - gtkCssStream << importStatement; + static const QVector importStatements { + QByteArrayLiteral("\n@import 'colors.css';"), + QByteArrayLiteral("\n@import 'windowDecorations.css';"), + }; + + for (const auto &statement : importStatements) { + if (!gtkCssContents.contains(statement.trimmed())) { + QTextStream gtkCssStream(>kCss); + gtkCssStream << statement; + } } } } diff --git a/kded/configvalueprovider.h b/kded/configvalueprovider.h --- a/kded/configvalueprovider.h +++ b/kded/configvalueprovider.h @@ -39,12 +39,14 @@ bool scrollbarBehavior() const; bool preferDarkTheme() const; QString windowDecorationsButtonsOrder() const; + QMap windowDecorationsButtonsImages() const; bool enableAnimations() const; QMap colors() const; private: QString fontStyleHelper(const QFont &font) const; QString windowDecorationButtonsOrderInGtkNotation(const QString &kdeConfigValue) const; + QString currentWindowDecorationPluginPath() const; KSharedConfigPtr kdeglobalsConfig; KSharedConfigPtr inputConfig; diff --git a/kded/configvalueprovider.cpp b/kded/configvalueprovider.cpp --- a/kded/configvalueprovider.cpp +++ b/kded/configvalueprovider.cpp @@ -33,6 +33,7 @@ #include +#include "dummydecorationbridge.h" #include "configvalueprovider.h" ConfigValueProvider::ConfigValueProvider() : @@ -174,6 +175,43 @@ return windowBackgroundGray < 192; } +QMap ConfigValueProvider::windowDecorationsButtonsImages() const +{ + static const QVector buttonTypes { + QStringLiteral("close"), + QStringLiteral("maximize"), + QStringLiteral("minimize"), + }; + + static const QVector buttonStates { + QStringLiteral("normal"), + QStringLiteral("active"), // aka pressed + QStringLiteral("hover"), + }; + + KConfigGroup decorationGroup = kwinConfig->group(QStringLiteral("org.kde.kdecoration2")); + const QString themeName = decorationGroup.readEntry(QStringLiteral("theme"), QStringLiteral("Breeze")); + + KDecoration2::DummyDecorationBridge kwinBridge {themeName}; + QMap decorationsImages {}; + + for (const auto &buttonType : buttonTypes) { + for (const auto &buttonState : buttonStates) { + // TODO What size? + QPixmap pixmap {20, 20}; + pixmap.fill(Qt::transparent); + + QPainter painter {&pixmap}; + kwinBridge.paintButton(painter, buttonType, buttonState); + painter.save(); + + decorationsImages.insert(QStringLiteral("%1-%2").arg(buttonType, buttonState), pixmap); + } + } + + return decorationsImages; +} + QString ConfigValueProvider::windowDecorationsButtonsOrder() const { KConfigGroup configGroup = kwinConfig->group(QStringLiteral("org.kde.kdecoration2")); diff --git a/kded/gtkconfig.h b/kded/gtkconfig.h --- a/kded/gtkconfig.h +++ b/kded/gtkconfig.h @@ -45,6 +45,7 @@ void setToolbarStyle() const; void setScrollbarBehavior() const; void setDarkThemePreference() const; + void setWindowDecorationsAppearance() const; void setWindowDecorationsButtonsOrder() const; void setEnableAnimations() const; void setColors() const; diff --git a/kded/gtkconfig.cpp b/kded/gtkconfig.cpp --- a/kded/gtkconfig.cpp +++ b/kded/gtkconfig.cpp @@ -160,6 +160,12 @@ ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-application-prefer-dark-theme"), preferDarkTheme); } +void GtkConfig::setWindowDecorationsAppearance() const +{ + const QMap windowDecorationsButtonsImages = configValueProvider->windowDecorationsButtonsImages(); + ConfigEditor::setClientSideDecorations(windowDecorationsButtonsImages); +} + void GtkConfig::setWindowDecorationsButtonsOrder() const { const QString windowDecorationsButtonOrder = configValueProvider->windowDecorationsButtonsOrder(); @@ -193,6 +199,7 @@ setToolbarStyle(); setScrollbarBehavior(); setDarkThemePreference(); + setWindowDecorationsAppearance(); setWindowDecorationsButtonsOrder(); setEnableAnimations(); setColors(); @@ -234,9 +241,14 @@ void GtkConfig::onKWinSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const { - if (group.name() == QStringLiteral("org.kde.kdecoration2") - && (names.contains("ButtonsOnRight") || names.contains("ButtonsOnLeft"))) { - setWindowDecorationsButtonsOrder(); + if (group.name() == QStringLiteral("org.kde.kdecoration2")) { + if (names.contains(QByteArrayLiteral("ButtonsOnRight")) + || names.contains(QByteArrayLiteral("ButtonsOnLeft"))) { + setWindowDecorationsButtonsOrder(); + } + if (names.contains(QByteArrayLiteral("theme"))) { + setWindowDecorationsAppearance(); + } } } diff --git a/kded/kwin_bridge/CMakeLists.txt b/kded/kwin_bridge/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/kded/kwin_bridge/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(KWinBridge STATIC + dummydecoratedclient.cpp + dummydecorationbridge.cpp + dummydecorationsettings.cpp + decorationpalette.cpp +) + +target_link_libraries(KWinBridge + PUBLIC + KDecoration2::KDecoration + PRIVATE + KDecoration2::KDecoration2Private + KF5::Service + KF5::ConfigWidgets +) diff --git a/kded/kwin_bridge/decorationpalette.h b/kded/kwin_bridge/decorationpalette.h new file mode 100644 --- /dev/null +++ b/kded/kwin_bridge/decorationpalette.h @@ -0,0 +1,70 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2014 Martin Gräßlin +Copyright 2014 Hugo Pereira Da Costa +Copyright 2015 Mika Allan Rauhala + +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_PALETTE_H +#define KWIN_DECORATION_PALETTE_H + +#include +#include +#include + +namespace KWin +{ +namespace Decoration +{ + +class DecorationPalette : public QObject +{ + Q_OBJECT +public: + DecorationPalette(const QString &colorScheme); + + bool isValid() const; + + QColor color(KDecoration2::ColorGroup group, KDecoration2::ColorRole role) const; + QPalette palette() const; + +Q_SIGNALS: + void changed(); +private: + void update(); + + QString m_colorScheme; + QFileSystemWatcher m_watcher; + + QPalette m_palette; + + QColor m_activeTitleBarColor; + QColor m_inactiveTitleBarColor; + + QColor m_activeFrameColor; + QColor m_inactiveFrameColor; + + QColor m_activeForegroundColor; + QColor m_inactiveForegroundColor; + QColor m_warningForegroundColor; +}; + +} +} + +#endif diff --git a/kded/kwin_bridge/decorationpalette.cpp b/kded/kwin_bridge/decorationpalette.cpp new file mode 100644 --- /dev/null +++ b/kded/kwin_bridge/decorationpalette.cpp @@ -0,0 +1,137 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2014 Martin Gräßlin +Copyright 2014 Hugo Pereira Da Costa +Copyright 2015 Mika Allan Rauhala + +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 "decorationpalette.h" + +#include +#include +#include + +#include +#include +#include + +namespace KWin +{ +namespace Decoration +{ + +DecorationPalette::DecorationPalette(const QString &colorScheme) + : m_colorScheme(QFileInfo(colorScheme).isAbsolute() + ? colorScheme + : QStandardPaths::locate(QStandardPaths::GenericConfigLocation, colorScheme)) +{ + if (!m_colorScheme.startsWith(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)) && colorScheme == QStringLiteral("kdeglobals")) { + // kdeglobals doesn't exist so create it. This is needed to monitor it using QFileSystemWatcher. + auto config = KSharedConfig::openConfig(colorScheme, KConfig::SimpleConfig); + KConfigGroup wmConfig(config, QStringLiteral("WM")); + wmConfig.writeEntry("FakeEntryToKeepThisGroup", true); + config->sync(); + + m_colorScheme = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, colorScheme); + } + m_watcher.addPath(m_colorScheme); + connect(&m_watcher, &QFileSystemWatcher::fileChanged, [this]() { + m_watcher.addPath(m_colorScheme); + update(); + emit changed(); + }); + + update(); +} + +bool DecorationPalette::isValid() const +{ + return m_activeTitleBarColor.isValid(); +} + +QColor DecorationPalette::color(KDecoration2::ColorGroup group, KDecoration2::ColorRole role) const +{ + using KDecoration2::ColorRole; + using KDecoration2::ColorGroup; + + switch (role) { + case ColorRole::Frame: + switch (group) { + case ColorGroup::Active: + return m_activeFrameColor; + case ColorGroup::Inactive: + return m_inactiveFrameColor; + default: + return QColor(); + } + case ColorRole::TitleBar: + switch (group) { + case ColorGroup::Active: + return m_activeTitleBarColor; + case ColorGroup::Inactive: + return m_inactiveTitleBarColor; + default: + return QColor(); + } + case ColorRole::Foreground: + switch (group) { + case ColorGroup::Active: + return m_activeForegroundColor; + case ColorGroup::Inactive: + return m_inactiveForegroundColor; + case ColorGroup::Warning: + return m_warningForegroundColor; + default: + return QColor(); + } + default: + return QColor(); + } +} + +QPalette DecorationPalette::palette() const +{ + return m_palette; +} + +void DecorationPalette::update() +{ + auto config = KSharedConfig::openConfig(m_colorScheme, KConfig::SimpleConfig); + KConfigGroup wmConfig(config, QStringLiteral("WM")); + + if (!wmConfig.exists() && !m_colorScheme.endsWith(QStringLiteral("/kdeglobals"))) { +// qCWarning(KWIN_DECORATIONS) << "Invalid color scheme" << m_colorScheme << "lacks WM group"; + return; + } + + m_palette = KColorScheme::createApplicationPalette(config); + + m_activeFrameColor = wmConfig.readEntry("frame", m_palette.color(QPalette::Active, QPalette::Window)); + m_inactiveFrameColor = wmConfig.readEntry("inactiveFrame", m_activeFrameColor); + m_activeTitleBarColor = wmConfig.readEntry("activeBackground", m_palette.color(QPalette::Active, QPalette::Highlight)); + m_inactiveTitleBarColor = wmConfig.readEntry("inactiveBackground", m_inactiveFrameColor); + m_activeForegroundColor = wmConfig.readEntry("activeForeground", m_palette.color(QPalette::Active, QPalette::HighlightedText)); + m_inactiveForegroundColor = wmConfig.readEntry("inactiveForeground", m_activeForegroundColor.darker()); + + KConfigGroup windowColorsConfig(config, QStringLiteral("Colors:Window")); + m_warningForegroundColor = windowColorsConfig.readEntry("ForegroundNegative", QColor(237, 21, 2)); + +} + +} +} diff --git a/kded/kwin_bridge/dummydecoratedclient.h b/kded/kwin_bridge/dummydecoratedclient.h new file mode 100644 --- /dev/null +++ b/kded/kwin_bridge/dummydecoratedclient.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 Mikhail Zolotukhin + * + * 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 . + */ + +#pragma once + +#include + +#include + +#include "decorationpalette.h" + +namespace KDecoration2 { + +class DummyDecoratedClient : public QObject, public DecoratedClientPrivate { + Q_OBJECT +public: + DummyDecoratedClient(DecoratedClient *client, Decoration *decoration); + + bool isActive() const override; + QString caption() const override; + int desktop() const override; + bool isOnAllDesktops() const override; + bool isShaded() const override; + QIcon icon() const override; + bool isMaximized() const override; + bool isMaximizedHorizontally() const override; + bool isMaximizedVertically() const override; + bool isKeepAbove() const override; + bool isKeepBelow() const override; + + bool isCloseable() const override; + bool isMaximizeable() const override; + bool isMinimizeable() const override; + bool providesContextHelp() const override; + bool isModal() const override; + bool isShadeable() const override; + bool isMoveable() const override; + bool isResizeable() const override; + + WId windowId() const override; + WId decorationId() const override; + + int width() const override; + int height() const override; + QSize size() const override; + QPalette palette() const override; + QColor color(ColorGroup group, ColorRole role) const override; + Qt::Edges adjacentScreenEdges() const override; + + void requestShowToolTip(const QString &text) override; + + void requestHideToolTip() override; + void requestClose() override; + void requestToggleMaximization(Qt::MouseButtons buttons) override; + void requestMinimize() override; + void requestContextHelp() override; + void requestToggleOnAllDesktops() override; + void requestToggleShade() override; + void requestToggleKeepAbove() override; + void requestToggleKeepBelow() override; + void requestShowWindowMenu() override; + +private: + KWin::Decoration::DecorationPalette m_pallete; +}; + +} diff --git a/kded/kwin_bridge/dummydecoratedclient.cpp b/kded/kwin_bridge/dummydecoratedclient.cpp new file mode 100644 --- /dev/null +++ b/kded/kwin_bridge/dummydecoratedclient.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2020 Mikhail Zolotukhin + * + * 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 "dummydecoratedclient.h" + +namespace KDecoration2 { + +DummyDecoratedClient::DummyDecoratedClient(DecoratedClient *client, Decoration *decoration) + : DecoratedClientPrivate(client, decoration) + , m_pallete(QStringLiteral("kdeglobals")) +{ + +} + +bool DummyDecoratedClient::isActive() const +{ + return true; +} + +QString DummyDecoratedClient::caption() const +{ + return {}; +} + +int DummyDecoratedClient::desktop() const +{ + return 0; +} +bool DummyDecoratedClient::isOnAllDesktops() const +{ + return true; +} +bool DummyDecoratedClient::isShaded() const +{ + return true; +} +QIcon DummyDecoratedClient::icon() const +{ + return {}; +} +bool DummyDecoratedClient::isMaximized() const +{ + return true; +} +bool DummyDecoratedClient::isMaximizedHorizontally() const +{ + return true; +} +bool DummyDecoratedClient::isMaximizedVertically() const +{ + return true; +} +bool DummyDecoratedClient::isKeepAbove() const +{ + return true; +} +bool DummyDecoratedClient::isKeepBelow() const +{ + return true; +} +bool DummyDecoratedClient::isCloseable() const +{ + return true; +} +bool DummyDecoratedClient::isMaximizeable() const +{ + return true; +} +bool DummyDecoratedClient::isMinimizeable() const +{ + return true; +} +bool DummyDecoratedClient::providesContextHelp() const +{ + return true; +} +bool DummyDecoratedClient::isModal() const +{ + return true; +} +bool DummyDecoratedClient::isShadeable() const +{ + return true; +} +bool DummyDecoratedClient::isMoveable() const +{ + return true; +} +bool DummyDecoratedClient::isResizeable() const +{ + return true; +} +WId DummyDecoratedClient::windowId() const +{ + return {}; +} +WId DummyDecoratedClient::decorationId() const +{ + return {}; +} +int DummyDecoratedClient::width() const +{ + return {}; +} +int DummyDecoratedClient::height() const +{ + return {}; +} +QSize DummyDecoratedClient::size() const +{ + return {}; +} + +QPalette DummyDecoratedClient::palette() const +{ + return m_pallete.palette(); +} + +QColor DummyDecoratedClient::color(ColorGroup group, ColorRole role) const +{ + return m_pallete.color(group, role); +} + +Qt::Edges DummyDecoratedClient::adjacentScreenEdges() const +{ + return {}; +} +void DummyDecoratedClient::requestShowToolTip(const QString &text) +{ + Q_UNUSED(text) +} +void DummyDecoratedClient::requestHideToolTip() +{ } +void DummyDecoratedClient::requestClose() +{ +} +void DummyDecoratedClient::requestToggleMaximization(Qt::MouseButtons buttons) +{ + Q_UNUSED(buttons) +} +void DummyDecoratedClient::requestMinimize() +{ +} +void DummyDecoratedClient::requestContextHelp() +{ +} +void DummyDecoratedClient::requestToggleOnAllDesktops() +{ +} +void DummyDecoratedClient::requestToggleShade() +{ +} +void DummyDecoratedClient::requestToggleKeepAbove() +{ +} +void DummyDecoratedClient::requestToggleKeepBelow() +{ +} +void DummyDecoratedClient::requestShowWindowMenu() +{ +} + +} diff --git a/kded/kwin_bridge/dummydecorationbridge.h b/kded/kwin_bridge/dummydecorationbridge.h new file mode 100644 --- /dev/null +++ b/kded/kwin_bridge/dummydecorationbridge.h @@ -0,0 +1,64 @@ +/* + * Copyright 2020 Mikhail Zolotukhin + * + * 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 . + */ + +#pragma once + +#include +#include +#include + +class KPluginFactory; + +namespace KDecoration2 +{ + +class DecorationSettings; +class DecoratedClientPrivate; +class DecorationSettingsPrivate; + +class DummyDecorationBridge : public DecorationBridge +{ + Q_OBJECT +public: + DummyDecorationBridge(const QString &decorationTheme, QObject *parent = nullptr); + ~DummyDecorationBridge() override = default; + + std::unique_ptr settings(KDecoration2::DecorationSettings *parent) override; + void update(KDecoration2::Decoration *decoration, const QRect &geometry) override; + std::unique_ptr createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration) override; + + void paintButton(QPainter &painter, const QString &buttonType, const QString &buttonState); + +private: + QString windowDecorationPluginPath(const QString &decorationTheme) const; + + void paintButtonInNormalState(QPainter &painter, const QString &buttonType); + void paintButtonInHoveredState(QPainter &painter, const QString &buttonType); + void paintButtonInPressedState(QPainter &painter, const QString &buttonType); + + KDecoration2::DecorationButtonType strToButtonType(const QString &type) const; + + KPluginFactory *m_factory; + KDecoration2::Decoration *m_decoration; +}; + +} + + diff --git a/kded/kwin_bridge/dummydecorationbridge.cpp b/kded/kwin_bridge/dummydecorationbridge.cpp new file mode 100644 --- /dev/null +++ b/kded/kwin_bridge/dummydecorationbridge.cpp @@ -0,0 +1,189 @@ +/* + * Copyright 2020 Mikhail Zolotukhin + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dummydecorationbridge.h" +#include "dummydecorationsettings.h" +#include "dummydecoratedclient.h" + +namespace KDecoration2 +{ + +DummyDecorationBridge::DummyDecorationBridge(const QString &decorationTheme, QObject *parent) + : DecorationBridge(parent) + , m_factory() + , m_decoration() +{ + const QString pluginPath = windowDecorationPluginPath(decorationTheme); + KPluginLoader loader {pluginPath}; + m_factory = loader.factory(); + + QVariantMap args({ {QStringLiteral("bridge"), QVariant::fromValue(this)} }); + m_decoration = m_factory->create(m_factory, QVariantList({args})); + + auto decorationSettings = QSharedPointer::create(this); + m_decoration->setSettings(decorationSettings); + m_decoration->init(); +} + +std::unique_ptr< KDecoration2::DecorationSettingsPrivate > DummyDecorationBridge::settings(KDecoration2::DecorationSettings *parent) +{ + return std::unique_ptr(new DummyDecorationSettings(parent)); +} + +void DummyDecorationBridge::update(KDecoration2::Decoration *decoration, const QRect &geometry) +{ + Q_UNUSED(decoration) + Q_UNUSED(geometry) +} + +std::unique_ptr< KDecoration2::DecoratedClientPrivate > DummyDecorationBridge::createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration) +{ + return std::unique_ptr(new DummyDecoratedClient(client, decoration)); +} + +void DummyDecorationBridge::paintButton(QPainter &painter, const QString &buttonType, const QString &buttonState) +{ + if (buttonState == "active") { + paintButtonInPressedState(painter, buttonType); + } else if (buttonState == "hover") { + paintButtonInHoveredState(painter, buttonType); + } else { + paintButtonInNormalState(painter, buttonType); + } +} + +QString DummyDecorationBridge::windowDecorationPluginPath(const QString &decorationTheme) const +{ + const auto decorationPlugins = KPluginLoader::findPlugins(QStringLiteral("org.kde.kdecoration2")); + + QString defaultPluginPath; + + for (const auto &pluginMetaData : decorationPlugins) { + if (pluginMetaData.name() == QStringLiteral("Breeze")) { + defaultPluginPath = pluginMetaData.fileName(); + } + + if (pluginMetaData.name() == decorationTheme) { + return pluginMetaData.fileName(); + } + } + return defaultPluginPath; +} + +void DummyDecorationBridge::paintButtonInNormalState(QPainter &painter, const QString &buttonType) +{ + std::unique_ptr button {m_factory->create( + QStringLiteral("button"), + m_decoration, + QVariantList({ + QVariant::fromValue(strToButtonType(buttonType)), + QVariant::fromValue(m_decoration) + }) + )}; + + if (button == nullptr) { + return; + } + + // TODO What to pass to the second arg? + button->paint(&painter, {0, 0, 20, 20}); +} + +void DummyDecorationBridge::paintButtonInHoveredState(QPainter &painter, const QString &buttonType) +{ + std::unique_ptr button {m_factory->create( + QStringLiteral("button"), + m_decoration, + QVariantList({ + QVariant::fromValue(strToButtonType(buttonType)), + QVariant::fromValue(m_decoration) + }) + )}; + + if (button == nullptr) { + return; + } + + QHoverEvent event { + QEvent::HoverEnter, + {5, 5}, + {4, 4}, + Qt::NoModifier + }; + QCoreApplication::instance()->sendEvent(m_decoration, &event); + QCoreApplication::instance()->sendEvent(button.get(), &event); + + // TODO What to pass to the second arg? + button->paint(&painter, {0, 0, 20, 20}); +} + +void DummyDecorationBridge::paintButtonInPressedState(QPainter &painter, const QString &buttonType) +{ + std::unique_ptr button {m_factory->create( + QStringLiteral("button"), + m_decoration, + QVariantList({ + QVariant::fromValue(strToButtonType(buttonType)), + QVariant::fromValue(m_decoration) + }) + )}; + + if (button == nullptr) { + return; + } + + QMouseEvent event { + QEvent::MouseButtonPress, + {5, 5}, + Qt::LeftButton, + Qt::LeftButton, + Qt::NoModifier + }; + QCoreApplication::instance()->sendEvent(m_decoration, &event); + QCoreApplication::instance()->sendEvent(button.get(), &event); + + // TODO What to pass to the second arg? + button->paint(&painter, {0, 0, 20, 20}); +} + +KDecoration2::DecorationButtonType DummyDecorationBridge::strToButtonType(const QString &type) const +{ + if (type == QStringLiteral("minimize")) { + return KDecoration2::DecorationButtonType::Minimize; + } else if (type == QStringLiteral("maximize")) { + return KDecoration2::DecorationButtonType::Maximize; + } else { + return KDecoration2::DecorationButtonType::Close; + } +} + +} diff --git a/kded/configvalueprovider.h b/kded/kwin_bridge/dummydecorationsettings.h copy from kded/configvalueprovider.h copy to kded/kwin_bridge/dummydecorationsettings.h --- a/kded/configvalueprovider.h +++ b/kded/kwin_bridge/dummydecorationsettings.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Mikhail Zolotukhin + * Copyright 2020 Mikhail Zolotukhin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,33 +20,25 @@ #pragma once -#include +#include +#include -class QString; -class QFont; +namespace KDecoration2 { -class ConfigValueProvider +class DummyDecorationSettings : public KDecoration2::DecorationSettingsPrivate { public: - ConfigValueProvider(); - - QString fontName() const; - QString iconThemeName() const; - QString cursorThemeName() const; - bool iconsOnButtons() const; - bool iconsInMenus() const; - int toolbarStyle() const; - bool scrollbarBehavior() const; - bool preferDarkTheme() const; - QString windowDecorationsButtonsOrder() const; - bool enableAnimations() const; - QMap colors() const; - -private: - QString fontStyleHelper(const QFont &font) const; - QString windowDecorationButtonsOrderInGtkNotation(const QString &kdeConfigValue) const; - - KSharedConfigPtr kdeglobalsConfig; - KSharedConfigPtr inputConfig; - KSharedConfigPtr kwinConfig; + explicit DummyDecorationSettings(DecorationSettings *parent); + + virtual bool isOnAllDesktopsAvailable() const override; + virtual bool isAlphaChannelSupported() const override; + virtual bool isCloseOnDoubleClickOnMenu() const override; + virtual QVector decorationButtonsLeft() const override; + virtual QVector decorationButtonsRight() const override; + virtual BorderSize borderSize() const override; }; + +} + + + diff --git a/kded/configvalueprovider.h b/kded/kwin_bridge/dummydecorationsettings.cpp copy from kded/configvalueprovider.h copy to kded/kwin_bridge/dummydecorationsettings.cpp --- a/kded/configvalueprovider.h +++ b/kded/kwin_bridge/dummydecorationsettings.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Mikhail Zolotukhin + * Copyright 2020 Mikhail Zolotukhin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -17,36 +17,44 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "dummydecorationsettings.h" -#pragma once +namespace KDecoration2 +{ + +DummyDecorationSettings::DummyDecorationSettings(DecorationSettings *parent) + : DecorationSettingsPrivate(parent) +{} + +bool DummyDecorationSettings::isOnAllDesktopsAvailable() const +{ + return false; +} + +bool DummyDecorationSettings::isAlphaChannelSupported() const +{ + return true; +} -#include +bool DummyDecorationSettings::isCloseOnDoubleClickOnMenu() const +{ + return false; +} + +QVector DummyDecorationSettings::decorationButtonsLeft() const +{ + return {}; +} -class QString; -class QFont; +QVector DummyDecorationSettings::decorationButtonsRight() const +{ + return {}; +} -class ConfigValueProvider +BorderSize DummyDecorationSettings::borderSize() const { -public: - ConfigValueProvider(); - - QString fontName() const; - QString iconThemeName() const; - QString cursorThemeName() const; - bool iconsOnButtons() const; - bool iconsInMenus() const; - int toolbarStyle() const; - bool scrollbarBehavior() const; - bool preferDarkTheme() const; - QString windowDecorationsButtonsOrder() const; - bool enableAnimations() const; - QMap colors() const; - -private: - QString fontStyleHelper(const QFont &font) const; - QString windowDecorationButtonsOrderInGtkNotation(const QString &kdeConfigValue) const; - - KSharedConfigPtr kdeglobalsConfig; - KSharedConfigPtr inputConfig; - KSharedConfigPtr kwinConfig; -}; + return BorderSize::None; +} + + +}