Port plugins to current KWin

main
Reion Wong 4 weeks ago
parent fbcb00cb96
commit deaff68a84

@ -9,14 +9,19 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
include(GNUInstallDirs)
set(CUTEFISH_KWIN_CONFIG_DIR "/etc/xdg" CACHE PATH "Directory for system KWin configuration")
set(CUTEFISH_KWIN_DATA_DIR "${CMAKE_INSTALL_DATAROOTDIR}/kwin" CACHE PATH "Directory for KWin scripts and tabbox layouts")
add_subdirectory(plugins)
install(FILES config/kglobalshortcutsrc DESTINATION /etc/xdg)
install(FILES config/kwinrc DESTINATION /etc/xdg)
install(FILES config/kwinrulesrc DESTINATION /etc/xdg)
install(FILES config/kglobalshortcutsrc DESTINATION "${CUTEFISH_KWIN_CONFIG_DIR}")
install(FILES config/kwinrc DESTINATION "${CUTEFISH_KWIN_CONFIG_DIR}")
install(FILES config/kwinrulesrc DESTINATION "${CUTEFISH_KWIN_CONFIG_DIR}")
install(DIRECTORY scripts/cutefishlauncher DESTINATION /usr/share/kwin/scripts)
install(DIRECTORY scripts/cutefish_squash DESTINATION /usr/share/kwin/effects)
install(DIRECTORY scripts/cutefish_scale DESTINATION /usr/share/kwin/effects)
install(DIRECTORY scripts/cutefish_popups DESTINATION /usr/share/kwin/effects)
install(DIRECTORY tabbox/cutefish_thumbnail DESTINATION /usr/share/kwin/tabbox)
install(DIRECTORY scripts/cutefishlauncher DESTINATION "${CUTEFISH_KWIN_DATA_DIR}/scripts")
install(DIRECTORY scripts/cutefish_squash DESTINATION "${CUTEFISH_KWIN_DATA_DIR}/effects")
install(DIRECTORY scripts/cutefish_scale DESTINATION "${CUTEFISH_KWIN_DATA_DIR}/effects")
install(DIRECTORY scripts/cutefish_popups DESTINATION "${CUTEFISH_KWIN_DATA_DIR}/effects")
install(DIRECTORY tabbox/cutefish_thumbnail DESTINATION "${CUTEFISH_KWIN_DATA_DIR}/tabbox")

@ -1,29 +1,40 @@
find_package(Qt5 CONFIG REQUIRED COMPONENTS Core Gui)
find_package(KF5CoreAddons REQUIRED)
find_package(KF5WindowSystem REQUIRED)
option(CUTEFISH_BUILD_KWIN6_DECORATION "Build the KDecoration3 plugin used by KWin6" ON)
option(CUTEFISH_BUILD_KWIN5_DECORATION "Build the legacy KDecoration2 plugin" OFF)
option(CUTEFISH_BUILD_KWIN5_EFFECT "Build the legacy KWin5 C++ effect" OFF)
# 获取qmake
get_target_property(QT_QMAKE_EXECUTABLE ${Qt5Core_QMAKE_EXECUTABLE} IMPORTED_LOCATION)
if(NOT QT_QMAKE_EXECUTABLE)
message(FATAL_ERROR "qmake is not found.")
if(CUTEFISH_BUILD_KWIN6_DECORATION)
find_package(KDecoration3 QUIET)
if(KDecoration3_FOUND)
add_subdirectory(decoration3)
message(STATUS "KDecoration3 found: building Cutefish KWin6 decoration")
else()
message(WARNING "KDecoration3 was not found; the KWin6 decoration will not be built. Install libkdecorations3-dev or disable CUTEFISH_BUILD_KWIN6_DECORATION.")
endif()
endif()
# execute the command "qmake -query QT_INSTALL_PLUGINS" to get the path of plugins dir.
execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_PLUGINS
OUTPUT_VARIABLE QT_PLUGINS_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(QT_PLUGINS_DIR)
message(STATUS "Qt5 plugin directory:" "${QT_PLUGINS_DIR}")
else()
message(FATAL_ERROR "Qt5 plugin directory cannot be detected.")
if(CUTEFISH_BUILD_KWIN5_DECORATION)
find_package(Qt5 CONFIG QUIET COMPONENTS Core Gui Widgets X11Extras)
find_package(KF5CoreAddons QUIET)
find_package(KF5Config QUIET)
find_package(KF5WindowSystem QUIET)
find_package(KDecoration2 QUIET)
if(Qt5_FOUND AND KF5CoreAddons_FOUND AND KF5Config_FOUND AND KF5WindowSystem_FOUND AND KDecoration2_FOUND)
find_program(QT_QMAKE_EXECUTABLE qmake)
if(QT_QMAKE_EXECUTABLE)
execute_process(COMMAND "${QT_QMAKE_EXECUTABLE}" -query QT_INSTALL_PLUGINS
OUTPUT_VARIABLE QT_PLUGINS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(QT_PLUGINS_DIR)
add_subdirectory(decoration)
else()
message(WARNING "Qt5 plugin directory could not be detected; skipping KDecoration2")
endif()
else()
message(WARNING "KDecoration2 dependencies were not found; skipping the legacy KWin5 decoration")
endif()
endif()
# set(CMAKE_INCLUDE_CURRENT_DIR ON)
# set(CMAKE_AUTOMOC ON)
# set(CMAKE_AUTOUIC ON)
# set(CMAKE_AUTORCC ON)
# add_subdirectory(blur)
add_subdirectory(decoration)
add_subdirectory(roundedwindow)
if(CUTEFISH_BUILD_KWIN5_EFFECT)
message(WARNING "The roundedwindow C++ effect still uses private KWin5 APIs and is disabled by default on current KDE. The JavaScript effects are installed separately.")
add_subdirectory(roundedwindow)
endif()

@ -0,0 +1,45 @@
cmake_minimum_required(VERSION 3.16)
project(cutefishdecoration3 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
include(GNUInstallDirs)
find_package(Qt6 CONFIG REQUIRED COMPONENTS Core Gui Widgets)
find_package(KF6CoreAddons REQUIRED)
find_package(KDecoration3 REQUIRED)
set(decoration3_SRCS
decoration.cpp
button.cpp
"${CMAKE_CURRENT_SOURCE_DIR}/../decoration/resources.qrc"
)
add_library(cutefishdecoration3 MODULE ${decoration3_SRCS})
target_link_libraries(cutefishdecoration3
PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
KF6::CoreAddons
KDecoration3::KDecoration
)
if(NOT KDE_INSTALL_PLUGINDIR)
find_program(QT6_QTPATHS_EXECUTABLE qtpaths6)
if(QT6_QTPATHS_EXECUTABLE)
execute_process(COMMAND "${QT6_QTPATHS_EXECUTABLE}" --query QT_INSTALL_PLUGINS
OUTPUT_VARIABLE KDE_INSTALL_PLUGINDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
endif()
if(NOT KDE_INSTALL_PLUGINDIR)
set(KDE_INSTALL_PLUGINDIR "${CMAKE_INSTALL_LIBDIR}/qt6/plugins")
endif()
install(TARGETS cutefishdecoration3
DESTINATION "${KDE_INSTALL_PLUGINDIR}/org.kde.kdecoration3")

@ -0,0 +1,91 @@
/*
* Copyright (C) 2020 PandaOS Team.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "button.h"
#include "decoration.h"
#include <KDecoration3/DecoratedWindow>
#include <QPainter>
Button::Button(KDecoration3::DecorationButtonType type,
KDecoration3::Decoration *decoration,
QObject *parent)
: KDecoration3::DecorationButton(type, decoration, parent)
{
auto *window = decoration ? decoration->window() : nullptr;
if (!window) {
setVisible(false);
return;
}
switch (type) {
case KDecoration3::DecorationButtonType::Minimize:
setVisible(window->isMinimizeable());
connect(window, &KDecoration3::DecoratedWindow::minimizeableChanged, this, &Button::setVisible);
break;
case KDecoration3::DecorationButtonType::Maximize:
setVisible(window->isMaximizeable());
connect(window, &KDecoration3::DecoratedWindow::maximizeableChanged, this, &Button::setVisible);
break;
case KDecoration3::DecorationButtonType::Close:
setVisible(window->isCloseable());
connect(window, &KDecoration3::DecoratedWindow::closeableChanged, this, &Button::setVisible);
break;
case KDecoration3::DecorationButtonType::Menu:
break;
default:
setVisible(false);
break;
}
}
KDecoration3::DecorationButton *Button::create(KDecoration3::DecorationButtonType type,
KDecoration3::Decoration *decoration,
QObject *parent)
{
return new Button(type, decoration, parent);
}
void Button::paint(QPainter *painter, const QRectF &repaintArea)
{
Q_UNUSED(repaintArea)
auto *decoration = qobject_cast<Cutefish::Decoration *>(this->decoration());
if (!decoration) {
return;
}
const QRectF buttonRect = geometry();
const qreal scale = decoration->devicePixelRatio();
const QRectF hoverRect = buttonRect.adjusted(2 * scale, 2 * scale, -2 * scale, -2 * scale);
const QRectF imageRect = QRectF(0, 0, 24 * scale, 24 * scale);
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setRenderHint(QPainter::SmoothPixmapTransform, false);
if (isHovered() || isPressed()) {
painter->setPen(Qt::NoPen);
painter->setBrush(decoration->darkMode()
? (isPressed() ? QColor(255, 255, 255, 26) : QColor(255, 255, 255, 38))
: (isPressed() ? QColor(0, 0, 0, 38) : QColor(0, 0, 0, 26)));
painter->drawRoundedRect(hoverRect, hoverRect.height() / 2, hoverRect.height() / 2);
}
if (type() == KDecoration3::DecorationButtonType::Menu) {
if (auto *window = decoration->window()) {
window->icon().paint(painter, buttonRect.toRect());
}
} else {
const QPixmap pixmap = decoration->buttonPixmap(type(), isChecked());
QRectF centered = imageRect;
centered.moveCenter(buttonRect.center());
if (!pixmap.isNull()) {
painter->drawPixmap(centered, pixmap, pixmap.rect());
}
}
painter->restore();
}

@ -0,0 +1,24 @@
/*
* Copyright (C) 2020 PandaOS Team.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <KDecoration3/DecorationButton>
class Button : public KDecoration3::DecorationButton
{
public:
explicit Button(KDecoration3::DecorationButtonType type,
KDecoration3::Decoration *decoration,
QObject *parent = nullptr);
static KDecoration3::DecorationButton *create(KDecoration3::DecorationButtonType type,
KDecoration3::Decoration *decoration,
QObject *parent);
protected:
void paint(QPainter *painter, const QRectF &repaintArea) override;
};

@ -0,0 +1,19 @@
{
"KPlugin": {
"Description": "CutefishOS window decoration for KDE Plasma 6",
"EnabledByDefault": true,
"Id": "org.cutefish.decoration",
"Name": "CutefishOS Window Frame",
"ServiceTypes": [
"org.kde.kdecoration3"
],
"Version": "1.0"
},
"org.kde.kdecoration3": {
"blur": false,
"recommended": true,
"styles": [
"titled"
]
}
}

@ -0,0 +1,252 @@
/*
* Copyright (C) 2020 PandaOS Team.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "decoration.h"
#include "button.h"
#include <KPluginFactory>
#include <KDecoration3/DecoratedWindow>
#include <KDecoration3/DecorationSettings>
#include <QFontMetricsF>
#include <QPainter>
K_PLUGIN_FACTORY_WITH_JSON(CutefishDecorationFactory, "cutefishos.json",
registerPlugin<Cutefish::Decoration>();)
namespace Cutefish
{
Decoration::Decoration(QObject *parent, const QVariantList &args)
: KDecoration3::Decoration(parent, args)
, m_themeSettings(QSettings::UserScope, QStringLiteral("cutefishos"), QStringLiteral("theme"))
{
}
bool Decoration::init()
{
if (!window()) {
return false;
}
m_devicePixelRatio = m_themeSettings.value(QStringLiteral("PixelRatio"), 1.0).toReal();
if (m_devicePixelRatio <= 0) {
m_devicePixelRatio = 1.0;
}
m_frameRadius = qRound(11 * m_devicePixelRatio);
updateColors();
updateButtonPixmaps();
m_leftButtons = new KDecoration3::DecorationButtonGroup(
KDecoration3::DecorationButtonGroup::Position::Left, this, &Button::create);
m_rightButtons = new KDecoration3::DecorationButtonGroup(
KDecoration3::DecorationButtonGroup::Position::Right, this, &Button::create);
auto window = this->window();
connect(window, &KDecoration3::DecoratedWindow::captionChanged, this, [this] { update(); });
connect(window, &KDecoration3::DecoratedWindow::activeChanged, this, [this] { update(); });
connect(window, &KDecoration3::DecoratedWindow::maximizedChanged, this, [this] {
updateGeometry();
updateButtonsGeometry();
update();
});
connect(window, &KDecoration3::DecoratedWindow::widthChanged, this, [this] {
updateGeometry();
updateButtonsGeometry();
});
connect(window, &KDecoration3::DecoratedWindow::heightChanged, this, [this] { updateGeometry(); });
if (settings()) {
connect(settings().get(), &KDecoration3::DecorationSettings::fontChanged, this, [this] {
updateGeometry();
update();
});
connect(settings().get(), &KDecoration3::DecorationSettings::spacingChanged, this, [this] {
updateButtonsGeometry();
update();
});
connect(settings().get(), &KDecoration3::DecorationSettings::reconfigured, this, [this] {
updateGeometry();
updateButtonsGeometry();
update();
});
}
updateGeometry();
updateButtonsGeometry();
return true;
}
void Decoration::paint(QPainter *painter, const QRectF &repaintArea)
{
Q_UNUSED(repaintArea)
if (!window()) {
return;
}
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::NoPen);
painter->setBrush(titleBarBackgroundColor());
if (!window()->isShaded() && !window()->isMaximized()) {
painter->drawRoundedRect(rect(), m_frameRadius, m_frameRadius);
} else {
painter->drawRect(rect());
}
painter->restore();
if (m_leftButtons) {
m_leftButtons->paint(painter, repaintArea);
}
if (m_rightButtons) {
m_rightButtons->paint(painter, repaintArea);
}
paintCaption(painter);
}
void Decoration::updateGeometry()
{
if (!window()) {
return;
}
const qreal height = titleBarHeight();
setBorders(QMarginsF(0, height, 0, 0));
setResizeOnlyBorders(QMarginsF(5, 5, 5, 5));
setTitleBar(QRectF(0, 0, window()->width(), height));
}
void Decoration::updateButtonsGeometry()
{
if (!window() || !m_leftButtons || !m_rightButtons) {
return;
}
const QSizeF buttonSize(titleBarHeight(), titleBarHeight());
const auto buttons = m_leftButtons->buttons() + m_rightButtons->buttons();
for (auto *button : buttons) {
button->setGeometry(QRectF(QPointF(0, 0), buttonSize));
}
constexpr qreal spacing = 8.0;
m_leftButtons->setSpacing(spacing);
m_leftButtons->setPos(QPointF(0, 0));
m_rightButtons->setSpacing(spacing);
m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width() - 2, 0));
}
void Decoration::updateColors()
{
// The theme settings are intentionally read once per decoration. KWin creates a new
// decoration when the window/theme is reconfigured, avoiding a dependency on KDE5 APIs.
}
void Decoration::updateButtonPixmaps()
{
const auto load = [](const QString &path) {
return QPixmap(path);
};
m_closeBtnPixmap = load(pixmapPath(KDecoration3::DecorationButtonType::Close, false));
m_maximizeBtnPixmap = load(pixmapPath(KDecoration3::DecorationButtonType::Maximize, false));
m_minimizeBtnPixmap = load(pixmapPath(KDecoration3::DecorationButtonType::Minimize, false));
m_restoreBtnPixmap = load(pixmapPath(KDecoration3::DecorationButtonType::Maximize, true));
}
QPixmap Decoration::buttonPixmap(KDecoration3::DecorationButtonType type, bool checked) const
{
switch (type) {
case KDecoration3::DecorationButtonType::Close:
return m_closeBtnPixmap;
case KDecoration3::DecorationButtonType::Maximize:
return checked ? m_restoreBtnPixmap : m_maximizeBtnPixmap;
case KDecoration3::DecorationButtonType::Minimize:
return m_minimizeBtnPixmap;
default:
return {};
}
}
QString Decoration::pixmapPath(KDecoration3::DecorationButtonType type, bool checked) const
{
const QString mode = darkMode() ? QStringLiteral("dark") : QStringLiteral("light");
QString name;
switch (type) {
case KDecoration3::DecorationButtonType::Close:
name = QStringLiteral("close_normal.svg");
break;
case KDecoration3::DecorationButtonType::Maximize:
name = checked ? QStringLiteral("restore_normal.svg") : QStringLiteral("maximize_normal.svg");
break;
case KDecoration3::DecorationButtonType::Minimize:
name = QStringLiteral("minimize_normal.svg");
break;
default:
return {};
}
return QStringLiteral(":/images/%1/%2").arg(mode, name);
}
int Decoration::titleBarHeight() const
{
return qMax(1, qRound(m_titleBarHeight * m_devicePixelRatio));
}
bool Decoration::darkMode() const
{
return m_themeSettings.value(QStringLiteral("DarkMode"), false).toBool();
}
QColor Decoration::titleBarBackgroundColor() const
{
return darkMode() ? m_titleBarBgDarkColor : m_titleBarBgColor;
}
QColor Decoration::titleBarForegroundColor() const
{
if (window() && window()->isActive()) {
return darkMode() ? m_titleBarFgDarkColor : m_titleBarFgColor;
}
return darkMode() ? m_unfocusedFgDarkColor : m_unfocusedFgColor;
}
void Decoration::paintCaption(QPainter *painter) const
{
if (!window()) {
return;
}
QFont font = settings() ? settings()->font() : QFont();
QFontMetricsF metrics(font);
const QRectF titleRect(0, 0, size().width(), titleBarHeight());
const qreal left = m_leftButtons ? m_leftButtons->geometry().width() + 20 : 20;
const qreal right = m_rightButtons ? m_rightButtons->geometry().width() + 20 : 20;
const QRectF available = titleRect.adjusted(left, 0, -right, 0);
const qreal textWidth = metrics.horizontalAdvance(window()->caption());
const QRectF natural((size().width() - textWidth) / 2, 0, textWidth, titleBarHeight());
Qt::Alignment alignment = Qt::AlignCenter;
QRectF captionRect = titleRect;
if (natural.left() < available.left()) {
captionRect = available;
alignment = Qt::AlignLeft | Qt::AlignVCenter;
} else if (natural.right() > available.right()) {
captionRect = available;
alignment = Qt::AlignRight | Qt::AlignVCenter;
}
painter->save();
painter->setFont(font);
painter->setPen(titleBarForegroundColor());
painter->drawText(captionRect, alignment, metrics.elidedText(window()->caption(), Qt::ElideMiddle, qRound(captionRect.width())));
painter->restore();
}
}
#include "decoration.moc"

@ -0,0 +1,65 @@
/*
* Copyright (C) 2020 PandaOS Team.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <KDecoration3/Decoration>
#include <KDecoration3/DecorationButtonGroup>
#include <QPixmap>
#include <QSettings>
namespace Cutefish
{
class Decoration : public KDecoration3::Decoration
{
Q_OBJECT
public:
explicit Decoration(QObject *parent = nullptr, const QVariantList &args = {});
~Decoration() override = default;
bool init() override;
void paint(QPainter *painter, const QRectF &repaintArea) override;
bool darkMode() const;
qreal devicePixelRatio() const { return m_devicePixelRatio; }
QPixmap buttonPixmap(KDecoration3::DecorationButtonType type, bool checked = false) const;
private:
void updateGeometry();
void updateButtonsGeometry();
void updateColors();
void updateButtonPixmaps();
void paintCaption(QPainter *painter) const;
int titleBarHeight() const;
QColor titleBarBackgroundColor() const;
QColor titleBarForegroundColor() const;
QString pixmapPath(KDecoration3::DecorationButtonType type, bool checked) const;
KDecoration3::DecorationButtonGroup *m_leftButtons = nullptr;
KDecoration3::DecorationButtonGroup *m_rightButtons = nullptr;
qreal m_devicePixelRatio = 1.0;
int m_titleBarHeight = 30;
int m_frameRadius = 11;
QColor m_titleBarBgColor = QColor(255, 255, 255);
QColor m_titleBarFgColor = QColor(56, 56, 56);
QColor m_unfocusedFgColor = QColor(127, 127, 127);
QColor m_titleBarBgDarkColor = QColor(44, 44, 45);
QColor m_titleBarFgDarkColor = QColor(202, 203, 206);
QColor m_unfocusedFgDarkColor = QColor(112, 112, 112);
QSettings m_themeSettings;
QPixmap m_closeBtnPixmap;
QPixmap m_maximizeBtnPixmap;
QPixmap m_minimizeBtnPixmap;
QPixmap m_restoreBtnPixmap;
};
}

@ -0,0 +1,25 @@
"use strict";
function forceFullScreen(window) {
var screenGeometry = workspace.clientArea(KWin.ScreenArea, window);
window.frameGeometry = screenGeometry;
}
function setupConnection(window) {
if (window.resourceClass != "cutefish-launcher"
|| window.resourceName != "cutefish-launcher" || window.dialog) {
return;
}
forceFullScreen(window);
window.frameGeometryChanged.connect(function () {
forceFullScreen(window);
});
}
workspace.windowAdded.connect(setupConnection);
var windows = workspace.windowList();
for (var i = 0; i < windows.length; i++) {
setupConnection(windows[i]);
}

@ -1,24 +0,0 @@
function forceFullScreen(client) {
var screen = client.screen;
var screenGeometry = workspace.clientArea(KWin.ScreenArea, screen, 0);
client.geometry = screenGeometry;
}
function setupConnection(client) {
if (client.resourceClass != "cutefish-launcher"
|| client.resourceName != "cutefish-launcher" || client.dialog) {
return;
}
forceFullScreen(client)
client.geometryChanged.connect(client, function () {
forceFullScreen(this);
});
}
workspace.clientAdded.connect(setupConnection);
// connect all existing clients
var clients = workspace.clientList();
for (var i = 0; i < clients.length; i++) {
setupConnection(clients[i]);
}

@ -4,7 +4,7 @@ Comment=Force the cutefish-launcher window keep in screen area
Icon=preferences-system-windows-script-test
X-Plasma-API=javascript
X-Plasma-MainScript=main.js
X-Plasma-MainScript=code/main.js
X-KDE-PluginInfo-Author=revenmartin
X-KDE-PluginInfo-Email=revenmartin@gmail.com

@ -0,0 +1,20 @@
{
"KPlugin": {
"Name": "CutefishOS Launcher",
"Description": "Force the cutefish-launcher window to fill the screen area",
"Icon": "preferences-system-windows-script-test",
"Authors": [
{
"Name": "revenmartin",
"Email": "revenmartin@gmail.com"
}
],
"EnabledByDefault": true,
"Id": "cutefishlauncher",
"Version": "0.1",
"License": "GPL-3.0-or-later"
},
"X-Plasma-API": "javascript",
"X-Plasma-MainScript": "code/main.js",
"KPackageStructure": "KWin/Script"
}
Loading…
Cancel
Save