!1 ukui 功能合入

Merge pull request !1 from 尚红飞/openkylin/yangtze
This commit is contained in:
handsome_feng 2022-06-19 02:42:49 +00:00 committed by Gitee
commit afd8ebe1c5
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
87 changed files with 4486 additions and 2 deletions

View File

@ -29,6 +29,10 @@ find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
X11Extras X11Extras
) )
find_package(PkgConfig)
pkg_check_modules(Qsettings REQUIRED gsettings-qt)
include_directories(${Qsettings_INCLUDE_DIRS})
find_package(Qt5Test ${QT_MIN_VERSION} CONFIG QUIET) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG QUIET)
set_package_properties(Qt5Test PROPERTIES set_package_properties(Qt5Test PROPERTIES
PURPOSE "Required for tests" PURPOSE "Required for tests"

View File

@ -224,6 +224,8 @@ target_link_libraries(kwin
epoxy::epoxy epoxy::epoxy
Threads::Threads Threads::Threads
${Qsettings_LIBRARIES}
) )
add_subdirectory(backends) add_subdirectory(backends)

View File

@ -96,6 +96,7 @@ AbstractClient::AbstractClient()
connect(ApplicationMenu::self(), &ApplicationMenu::applicationMenuEnabledChanged, this, [this] { connect(ApplicationMenu::self(), &ApplicationMenu::applicationMenuEnabledChanged, this, [this] {
Q_EMIT hasApplicationMenuChanged(hasApplicationMenu()); Q_EMIT hasApplicationMenuChanged(hasApplicationMenu());
}); });
m_bTabletToPcRestoreFlag = false;
} }
AbstractClient::~AbstractClient() AbstractClient::~AbstractClient()
@ -1129,6 +1130,9 @@ void AbstractClient::handleInteractiveMoveResize(int x, int y, int x_root, int y
if (isWaitingForInteractiveMoveResizeSync()) if (isWaitingForInteractiveMoveResizeSync())
return; // we're still waiting for the client or the timeout return; // we're still waiting for the client or the timeout
if (workspace()->isInTabletMode())
return;
const Gravity gravity = interactiveMoveResizeGravity(); const Gravity gravity = interactiveMoveResizeGravity();
if ((gravity == Gravity::None && !isMovableAcrossScreens()) if ((gravity == Gravity::None && !isMovableAcrossScreens())
|| (gravity != Gravity::None && (isShade() || !isResizable()))) { || (gravity != Gravity::None && (isShade() || !isResizable()))) {
@ -2387,6 +2391,11 @@ bool AbstractClient::processDecorationButtonPress(QMouseEvent *event, bool ignor
if (!wantsInput()) // we cannot be active, use it anyway if (!wantsInput()) // we cannot be active, use it anyway
active = true; active = true;
if(workspace()->isInTabletMode()) {
Workspace::self()->performWindowOperation(this, Options::NoOp);
return false;
}
// check whether it is a double click // check whether it is a double click
if (event->button() == Qt::LeftButton && titlebarPositionUnderMouse()) { if (event->button() == Qt::LeftButton && titlebarPositionUnderMouse()) {
if (m_decoration.doubleClickTimer.isValid()) { if (m_decoration.doubleClickTimer.isValid()) {

View File

@ -371,6 +371,14 @@ public:
return m_icon; return m_icon;
} }
void setTabletToPcRestoreFlag(bool bTabletToPcRestoreFlag){
m_bTabletToPcRestoreFlag = bTabletToPcRestoreFlag;
}
bool getTabletToPcRestoreFlag() const {
return m_bTabletToPcRestoreFlag;
}
bool isZombie() const; bool isZombie() const;
bool isActive() const { bool isActive() const {
return m_active; return m_active;
@ -1239,6 +1247,7 @@ private:
bool m_keepBelow = false; bool m_keepBelow = false;
bool m_demandsAttention = false; bool m_demandsAttention = false;
bool m_minimized = false; bool m_minimized = false;
bool m_bTabletToPcRestoreFlag; //平板切换pc后如果此时窗口处于最小化则该标志置为true激活后用于将最大化窗口还原再次为false
QTimer *m_autoRaiseTimer = nullptr; QTimer *m_autoRaiseTimer = nullptr;
QTimer *m_shadeHoverTimer = nullptr; QTimer *m_shadeHoverTimer = nullptr;
ShadeMode m_shadeMode = ShadeNone; ShadeMode m_shadeMode = ShadeNone;

View File

@ -31,6 +31,10 @@
// Qt // Qt
#include <QMetaProperty> #include <QMetaProperty>
#include <QPainter> #include <QPainter>
#include <QScreen>
#include <QtDBus>
#include <QGSettings>
#include <QFontDatabase>
namespace KWin namespace KWin
{ {
@ -120,6 +124,101 @@ void DecorationBridge::init()
if (waylandServer()) { if (waylandServer()) {
waylandServer()->decorationManager()->setDefaultMode(m_factory ? ServerSideDecorationManagerInterface::Mode::Server : ServerSideDecorationManagerInterface::Mode::None); waylandServer()->decorationManager()->setDefaultMode(m_factory ? ServerSideDecorationManagerInterface::Mode::Server : ServerSideDecorationManagerInterface::Mode::None);
} }
//读取dpi值
m_dpi = 96;
QScreen *primary = QGuiApplication::primaryScreen();
if (primary) {
m_dpi = primary->logicalDotsPerInchX();
if(m_dpi < 30)
{
m_dpi = 30; //设置一个限度不允许dpi小于30
}
}
QGSettings* pThemeSettings = new QGSettings("org.ukui.style", "/org/ukui/style/", this);
QString strTheme;
if (true == pThemeSettings->keys().contains("styleName")){
strTheme = pThemeSettings->get("style-name").toString();
qDebug() << "DecorationBridge::init theme:" << strTheme;
}
if("ukui-light" == strTheme)
{
m_themeId = 0;
}
else if("ukui-dark" == strTheme)
{
m_themeId = 1;
}
else //默认ukui-default
{
m_themeId = 0;
}
const QFont font = QFontDatabase::systemFont(QFontDatabase::TitleFont);
m_nFont = font.pointSize();
m_strFontFamily = font.family();
QDBusConnection::sessionBus().connect(QString(),
QStringLiteral("/KGlobalSettings"),
QStringLiteral("org.kde.KGlobalSettings"),
QStringLiteral("slotThemeChange"),
this, SLOT(slotThemeUpdate(int)));
QDBusConnection::sessionBus().connect(QString(),
QStringLiteral("/KGlobalSettings"),
QStringLiteral("org.kde.KGlobalSettings"),
QStringLiteral("slotFontChange"),
this, SLOT(fontUpdate(int, QString)));
m_bTabletMode = false;
//kwin启动时获取运行模式: 0:PC模式, 1:平板模式
QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.statusmanager.interface",
"/",
"com.kylin.statusmanager.interface",
"get_current_tabletmode");
QDBusMessage response = QDBusConnection::sessionBus().call(message);
if (response.type() == QDBusMessage::ReplyMessage) {
m_bTabletMode = response.arguments().takeFirst().toBool();
//printf("TabletMode = %d\n", m_bTabletMode);
} else {
printf("TabletMode qdus invalid\n");
}
//通过dbus监听PC-平板模式切换
QDBusConnection::sessionBus().connect("com.kylin.statusmanager.interface",
"/",
"com.kylin.statusmanager.interface",
"mode_change_signal",
this,
SLOT(slotSetSurface(bool)));
}
void DecorationBridge::slotThemeUpdate(int themeId)
{
m_themeId = themeId;
}
void DecorationBridge::fontUpdate(int nfont, QString strFamily)
{
m_nFont = nfont;
m_strFontFamily = strFamily;
QFont font;
font.setPointSize(nfont);
font.setFamily(strFamily);
Q_EMIT sig_updateFont(font);
}
void DecorationBridge::slotSetSurface(bool bSurface)
{
if(m_bTabletMode == bSurface)
{
return;
}
m_bTabletMode = bSurface;
} }
void DecorationBridge::initPlugin() void DecorationBridge::initPlugin()
@ -255,6 +354,11 @@ KDecoration2::Decoration *DecorationBridge::createDecoration(AbstractClient *cli
if (!m_theme.isEmpty()) { if (!m_theme.isEmpty()) {
args.insert(QStringLiteral("theme"), m_theme); args.insert(QStringLiteral("theme"), m_theme);
} }
args.insert(QStringLiteral("dpi"), m_dpi); //每创建一个渲染端就把dpi值带过去后面每新建一个客户就不需要反复获取获取dpi值
args.insert(QStringLiteral("themeId"), m_themeId); //针对UKUI定制的主题id
args.insert(QStringLiteral("systemFontSize"), m_nFont); //标题栏字体大小
args.insert(QStringLiteral("systemFont"), m_strFontFamily); //标题栏字体类型
args.insert(QStringLiteral("tabletMode"), m_bTabletMode);
auto deco = m_factory->create<KDecoration2::Decoration>(client, QVariantList({args})); auto deco = m_factory->create<KDecoration2::Decoration>(client, QVariantList({args}));
deco->setSettings(m_settings); deco->setSettings(m_settings);
deco->init(); deco->init();

View File

@ -66,6 +66,12 @@ public:
Q_SIGNALS: Q_SIGNALS:
void metaDataLoaded(); void metaDataLoaded();
void sig_updateFont(QFont);
public Q_SLOTS:
void slotThemeUpdate(int);
void fontUpdate(int nfont, QString strFamily);
void slotSetSurface(bool bSurface);
private: private:
QString readPlugin(); QString readPlugin();
@ -83,6 +89,13 @@ private:
QString m_theme; QString m_theme;
QSharedPointer<KDecoration2::DecorationSettings> m_settings; QSharedPointer<KDecoration2::DecorationSettings> m_settings;
bool m_noPlugin; bool m_noPlugin;
int m_dpi; //dpi值
int m_themeId; //主题id
int m_nFont;
QString m_strFontFamily;
bool m_bTabletMode;
KWIN_SINGLETON(DecorationBridge) KWIN_SINGLETON(DecorationBridge)
}; };
} // Decoration } // Decoration

View File

@ -49,6 +49,7 @@ SettingsImpl::SettingsImpl(KDecoration2::DecorationSettings *parent)
); );
connect(Workspace::self(), &Workspace::configChanged, this, &SettingsImpl::readSettings); connect(Workspace::self(), &Workspace::configChanged, this, &SettingsImpl::readSettings);
connect(DecorationBridge::self(), &DecorationBridge::metaDataLoaded, this, &SettingsImpl::readSettings); connect(DecorationBridge::self(), &DecorationBridge::metaDataLoaded, this, &SettingsImpl::readSettings);
connect(DecorationBridge::self(), &DecorationBridge::sig_updateFont, this, &SettingsImpl::updateFont);
} }
SettingsImpl::~SettingsImpl() = default; SettingsImpl::~SettingsImpl() = default;
@ -142,6 +143,14 @@ static KDecoration2::BorderSize stringToSize(const QString &name)
return it.value(); return it.value();
} }
void SettingsImpl::updateFont(QFont font)
{
if (font != m_font) {
m_font = font;
Q_EMIT decorationSettings()->fontChanged(m_font);
}
}
void SettingsImpl::readSettings() void SettingsImpl::readSettings()
{ {
KConfigGroup config = kwinApp()->config()->group(QStringLiteral("org.kde.kdecoration2")); KConfigGroup config = kwinApp()->config()->group(QStringLiteral("org.kde.kdecoration2"));

View File

@ -42,6 +42,9 @@ public:
return m_font; return m_font;
} }
public Q_SLOTS:
void updateFont(QFont font);
private: private:
void readSettings(); void readSettings();
QVector< KDecoration2::DecorationButtonType > readDecorationButtons(const KConfigGroup &config, QVector< KDecoration2::DecorationButtonType > readDecorationButtons(const KConfigGroup &config,

View File

@ -8,6 +8,8 @@
*/ */
// own // own
#include "effectloader.h" #include "effectloader.h"
// system
#include <unistd.h>
// KWin // KWin
#include <config-kwin.h> #include <config-kwin.h>
#include <kwineffects.h> #include <kwineffects.h>
@ -168,12 +170,27 @@ void ScriptedEffectLoader::queryAndLoadAll()
QList<KPluginMetaData> ScriptedEffectLoader::findAllEffects() const QList<KPluginMetaData> ScriptedEffectLoader::findAllEffects() const
{ {
return KPackage::PackageLoader::self()->listPackages(s_serviceType, QStringLiteral("kwin/effects")); #if defined(QT_NO_DEBUG)
const QString packageRoot = QStringLiteral("kwin/effects");
#else
QString packageRoot = QCoreApplication::applicationDirPath() + QLatin1String("/../../src/effects");
if (access(packageRoot.toStdString().c_str(), F_OK) == -1)
packageRoot = QStringLiteral("kwin/effects");
qCDebug(KWIN_CORE) << "listPackages from:" << packageRoot;
#endif
return KPackage::PackageLoader::self()->listPackages(s_serviceType, packageRoot);
} }
KPluginMetaData ScriptedEffectLoader::findEffect(const QString &name) const KPluginMetaData ScriptedEffectLoader::findEffect(const QString &name) const
{ {
const auto plugins = KPackage::PackageLoader::self()->findPackages(s_serviceType, QStringLiteral("kwin/effects"), #if defined(QT_NO_DEBUG)
const QString packageRoot = QStringLiteral("kwin/effects");
#else
QString packageRoot = QCoreApplication::applicationDirPath() + QLatin1String("/../../src/effects");
if (access(packageRoot.toStdString().c_str(), F_OK) == -1)
packageRoot = QStringLiteral("kwin/effects");
#endif
const auto plugins = KPackage::PackageLoader::self()->findPackages(s_serviceType, packageRoot,
[name] (const KPluginMetaData &metadata) { [name] (const KPluginMetaData &metadata) {
return metadata.pluginId().compare(name, Qt::CaseInsensitive) == 0; return metadata.pluginId().compare(name, Qt::CaseInsensitive) == 0;
} }

View File

@ -160,6 +160,8 @@ add_subdirectory(sheet)
add_subdirectory(snaphelper) add_subdirectory(snaphelper)
add_subdirectory(startupfeedback) add_subdirectory(startupfeedback)
add_subdirectory(trackmouse) add_subdirectory(trackmouse)
add_subdirectory(touchclick)
add_subdirectory(touchtrail)
add_subdirectory(wobblywindows) add_subdirectory(wobblywindows)
############################################################################### ###############################################################################

View File

@ -0,0 +1,13 @@
#######################################
# Effect
set(touchclick_SOURCES
touchclick.cpp
main.cpp
)
kwin4_add_effect_module(kwin4_effect_touchclick ${touchclick_SOURCES})
# Data files
install(FILES data/circletexture.png DESTINATION ${KDE_INSTALL_DATADIR}/${KWIN_NAME})
install(FILES data/ringstexture.png DESTINATION ${KDE_INSTALL_DATADIR}/${KWIN_NAME})

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

View File

@ -0,0 +1,17 @@
/*
SPDX-FileCopyrightText: 2022 Hongfei Shang <shanghongfei@kylinos.cn>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "touchclick.h"
namespace KWin
{
KWIN_EFFECT_FACTORY_SUPPORTED(TouchClickEffect,
"metadata.json.stripped",
return TouchClickEffect::supported();)
} // namespace KWin
#include "main.moc"

View File

@ -0,0 +1,61 @@
{
"KPlugin": {
"Category": "Appearance",
"Description": "Visualize touch points",
"Description[ar]": "تصور نقاط اللمس",
"Description[az]": "Ekranda toxunan nöqtələri canlandırır",
"Description[ca@valencia]": "Visualitza els punts de contacte",
"Description[ca]": "Visualitza els punts de contacte",
"Description[cs]": "Vizualizovat dotekové body",
"Description[en_GB]": "Visualise touch points",
"Description[es]": "Visualizar los puntos de contacto",
"Description[fi]": "Korosta kosketuspisteet",
"Description[fr]": "Afficher les points tactiles",
"Description[hu]": "Vizualizálja az érintési pontokat",
"Description[ia]": "Visualisa punctos de contacto",
"Description[id]": "Memvisualkan titik sentuh",
"Description[it]": "Visualizza i punti di tocco",
"Description[ko]": "터치 지점 표시",
"Description[nl]": "Aanraakpunten visualiseren",
"Description[nn]": "Visualiser kontaktpunkt",
"Description[pl]": "Uwidacznia punkty dotyku",
"Description[pt_BR]": "Visualizar pontos de toque",
"Description[ru]": "Визуализация событий касания экрана",
"Description[sk]": "Vizualizovať dotykové body",
"Description[sl]": "Vizualiziraj točke dotika",
"Description[sv]": "Åskådliggör beröringspunkter",
"Description[tr]": "Dokunma noktalarını görselleştir",
"Description[uk]": "Візуалізувати точки дотику",
"Description[x-test]": "xxVisualize touch pointsxx",
"Description[zh_CN]": "高亮显示在触控屏幕上的触摸点",
"EnabledByDefault": true,
"Id": "touchclick",
"License": "GPL",
"Name": "Touch Points",
"Name[ar]": "نقاط اللمس",
"Name[az]": "Toxunma nöqtələri",
"Name[ca@valencia]": "Punts de contacte",
"Name[ca]": "Punts de contacte",
"Name[cs]": "Dotekové body",
"Name[en_GB]": "Touch Points",
"Name[es]": "Puntos de contacto",
"Name[fi]": "Kosketuspisteet",
"Name[fr]": "Points tactiles",
"Name[hu]": "Érintési pontok",
"Name[ia]": "Punctos de contacto",
"Name[it]": "Punti di tocco",
"Name[ko]": "터치 지점",
"Name[nl]": "Aanraakpunten",
"Name[nn]": "Kontaktpunkt",
"Name[pl]": "Punkty dotyku",
"Name[pt_BR]": "Pontos de toque",
"Name[ru]": "Точки прикосновения",
"Name[sk]": "Dotykové body",
"Name[sl]": "Točke dotika",
"Name[sv]": "Beröringspunkter",
"Name[tr]": "Dokunma Noktaları",
"Name[uk]": "Точки дотику",
"Name[x-test]": "xxTouch Pointsxx",
"Name[zh_CN]": "触摸点高亮"
}
}

View File

@ -0,0 +1,433 @@
#include "touchclick.h"
#include <QTime>
#include <QFile>
#include <cmath>
namespace KWin {
bool TouchClickEffect::supported()
{
return effects->isOpenGLCompositing();
}
TouchClickEffect::TouchClickEffect()
: Effect()
, m_ringsTexture(nullptr)
, m_ringsBgTexture(nullptr)
, m_circleTexture(nullptr)
{
m_isActiveLongPress = false;
m_drawProgress = 0;
m_drawAnimation = new QPropertyAnimation(this, "drawValue");
m_drawAnimation->setDuration(LONG_PRESS_LAST_TIME);
m_drawAnimation->setEasingCurve(QEasingCurve::Linear);
m_activeTimer = new QTimer(this);
m_activeTimer->setSingleShot(true);
m_activeTimer->setInterval(LONG_PRESS_WAIT_TIME);
connect(m_activeTimer, &QTimer::timeout, this, &TouchClickEffect::onActiveTimerTimeout);
loadTexture();
}
TouchClickEffect::~TouchClickEffect()
{
if (m_ringsTexture)
delete m_ringsTexture;
m_ringsTexture = nullptr;
if (m_ringsBgTexture)
delete m_ringsBgTexture;
m_ringsBgTexture = nullptr;
if (m_circleTexture)
delete m_circleTexture;
m_circleTexture = nullptr;
}
void TouchClickEffect::loadTexture()
{
#if defined(QT_NO_DEBUG)
QString ringTextureFilePath = {QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("ringstexture.png"))};
#else
QString ringTextureFilePath = QCoreApplication::applicationDirPath() + QStringLiteral("/../../src/effects/touchclick/data/ringstexture.png");
#endif
QImage ringsTextureImage(ringTextureFilePath);
if (ringsTextureImage.isNull()) {
qWarning() << "load image [" << ringTextureFilePath << "] failed";
return;
}
// 纹理与颜色融合, 纹理仅控制形状
// uint ringR = 229, ringG = 239, ringB = 247, ringA = 165;
uint ringR = 35, ringG = 108, ringB = 218, ringA = 216;
for (int h = 0; h < ringsTextureImage.height(); ++h) {
for (int w = 0; w < ringsTextureImage.width(); ++w) {
const QColor &c = ringsTextureImage.pixelColor(w, h);
ringsTextureImage.setPixelColor(w, h, QColor(ringR, ringG, ringB, fmin(ringA, c.alpha())));
}
}
if (!m_ringsTexture)
m_ringsTexture = new GLTexture(ringsTextureImage);
QImage ringsBgTextureImage(ringTextureFilePath);
// 纹理与颜色融合, 纹理仅控制形状
uint ringBgR = 229, ringBgG = 239, ringBgB = 247, ringBgA = 165;
// uint ringR = 35, ringG = 108, ringB = 218, ringA = 216;
for (int h = 0; h < ringsBgTextureImage.height(); ++h) {
for (int w = 0; w < ringsBgTextureImage.width(); ++w) {
const QColor &c = ringsBgTextureImage.pixelColor(w, h);
ringsBgTextureImage.setPixelColor(w, h, QColor(ringBgR, ringBgG, ringBgB, fmin(ringBgA, c.alpha())));
}
}
if (!m_ringsBgTexture)
m_ringsBgTexture = new GLTexture(ringsBgTextureImage);
#if defined(QT_NO_DEBUG)
QString circleTextureFilePath = {QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("circletexture.png"))};
#else
QString circleTextureFilePath = QCoreApplication::applicationDirPath() + QStringLiteral("/../../src/effects/touchclick/data/circletexture.png");
#endif
QImage circleTextureImage(circleTextureFilePath);
if (circleTextureImage.isNull()) {
qWarning() << "load image [" << ringTextureFilePath << "] failed";
return;
}
// 纹理与颜色融合, 纹理仅控制形状
uint circleR = 229, circleG = 239, circleB = 247, circleA = 165;
for (int h = 0; h < circleTextureImage.height(); ++h) {
for (int w = 0; w < circleTextureImage.width(); ++w) {
const QColor &c = circleTextureImage.pixelColor(w, h);
circleTextureImage.setPixelColor(w, h, QColor(circleR, circleG, circleB, fmin(circleA, c.alpha())));
}
}
if (!m_circleTexture)
m_circleTexture = new GLTexture(circleTextureImage);
}
void TouchClickEffect::prePaintScreen(ScreenPrePaintData& data, std::chrono::milliseconds presentTime)
{
int time = 0;
if (m_lastPresentTime.count()) {
time = (presentTime - m_lastPresentTime).count();
}
auto it = m_allPoints.begin();
while (it != m_allPoints.end()) {
it->time += time;
if (it->time > TOUCH_POINT_LIFE) {
it = m_allPoints.erase(it);
} else {
it++;
}
}
if (m_allPoints.isEmpty()) {
m_lastPresentTime = std::chrono::milliseconds::zero();
} else {
m_lastPresentTime = presentTime;
}
effects->prePaintScreen(data, presentTime);
}
void TouchClickEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
{
effects->paintScreen(mask, region, data);
if (effects->isOpenGLCompositing())
paintScreenGL(mask, region, data);
}
double TouchClickEffect::getDrawValue() const
{
return m_drawProgress;
}
void TouchClickEffect::setDrawValue(double value)
{
m_drawProgress = value;
repaint();
}
void TouchClickEffect::onActiveTimerTimeout()
{
m_isActiveLongPress = true;
m_drawProgress = 0;
m_drawAnimation->setStartValue(0.0);
m_drawAnimation->setEndValue(1.0);
m_drawAnimation->start();
repaint();
}
void TouchClickEffect::paintScreenGL(int mask, const QRegion &region, ScreenPaintData &data)
{
GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
shader->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//! 绘制点击的点
for (int i = 0; i < m_allPoints.size(); ++i) {
TouchPoint &p = m_allPoints[i];
float cr = (TOUCH_POINT_LIFE - p.time) * 1.0 / TOUCH_POINT_LIFE * CIRCLE_RADIUS;
// drawCircleGl(QColor(229, 239, 247, 216), p.pos.x(), p.pos.y(), cr, 0, 1);
drawRingsGl(m_circleTexture, p.pos.x(), p.pos.y(), 0, cr, 0, 1);
}
//! 绘制长按动画
if (m_allPoints.size() == 1 && m_allPoints[0].time == -1 && m_isActiveLongPress) {
QPointF &p = m_allPoints[0].pos;
// drawRingsGl(QColor(229, 239, 247, 165), p.x(), p.y(), RINGS_MINRADIUS, RINGS_MAXRADIUS, 1);
// drawRingsGl(QColor(35, 108, 218, 216), p.x(), p.y(), RINGS_MINRADIUS, RINGS_MAXRADIUS, m_drawProgress);
drawRingsGl(m_ringsBgTexture, p.x(), p.y(), RINGS_MINRADIUS, RINGS_MAXRADIUS, 0 ,1);
drawRingsGl(m_ringsTexture, p.x(), p.y(), RINGS_MINRADIUS, RINGS_MAXRADIUS, 0 ,m_drawProgress);
// drawRingsGl(m_circleTexture, p.x(), p.y() - RINGS_MINRADIUS - RINGS_WIDTH / 2,
// 0, RINGS_WIDTH / 2, M_PI, 0.5);
// drawRingsGl(m_circleTexture, p.x() + (RINGS_MINRADIUS + RINGS_WIDTH / 2) * sin(2 * M_PI * m_drawProgress),
// p.y() - (RINGS_MINRADIUS + RINGS_WIDTH / 2) * cos(2 * M_PI * m_drawProgress),
// 0, RINGS_WIDTH / 2, 2 * M_PI * m_drawProgress, 0.5);
}
glDisable(GL_BLEND);
ShaderManager::instance()->popShader();
}
void TouchClickEffect::drawCircleGl(const QColor& color, float cx, float cy, float r, float offset, float alpha)
{
const int num_segments = 80;
const float theta = 2.0 * M_PI * alpha / float(num_segments);
const float c = cosf(theta);
const float s = sinf(theta);
float t;
float x = r * sinf(offset);
float y = -r * cosf(offset);
GLVertexBuffer* vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
vbo->setUseColor(true);
vbo->setColor(color);
QVector<float> verts;
verts.reserve(num_segments * 2 + 4);
verts << cx << cy;
for (int ii = 0; ii < num_segments; ++ii) {
verts << x + cx << y + cy;
t = x;
x = c * x - s * y;
y = s * t + c * y;
}
verts << x + cx << y + cy;
vbo->setData(verts.size() / 2, 2, verts.data(), nullptr);
vbo->render(GL_TRIANGLE_FAN);
}
QPointF TouchClickEffect::calculateExternalRectAndRoundRaysIntersect(float cx, float cy, float r, float alpha)
{
QPointF res;
float dy, dx;
if (alpha <= M_PI_4 || alpha >= 2 * M_PI - M_PI_4) {
dy = r;
dx = dx * tan(alpha);
} else if (alpha >= M_PI_4 && alpha <= M_PI_2 + M_PI_4) {
dx = r;
dy = dx * (1.0 / tan(alpha));
} else if (alpha >= M_PI_2 + M_PI_4 && alpha <= M_PI + M_PI_4) {
dy = -r;
dx = dy * tan(alpha);
} else {
dx = -r;
dy = dx * (1.0 / tan(alpha));
}
// 处理成Qt里的坐标系
dy = -dy;
res.setX(cx + dx);
res.setY(cy + dy);
return res;
}
void TouchClickEffect::drawCircleGl(GLTexture *texture, float cx, float cy, float r, float offset, float alpha)
{
static const QPointF rectPoint[] = {
{1.0f, 1.0f},
{0.0f, 1.0f},
{0.0f, 0.0f},
{1.0f, 0.0f}
};
QVector<float> verts, texCoords;
verts << cx << cy;
texCoords << 0.5f << 0.5f;
//! 计算坐标映射的关键点
QPointF bPos = calculateExternalRectAndRoundRaysIntersect(cx, cy, r, offset);
QPointF ePos = calculateExternalRectAndRoundRaysIntersect(cx, cy, r, offset + alpha * 2.0 * M_PI);
verts << bPos.x() << bPos.y();
texCoords << (bPos.x() - cx) / r + 0.5f << (cy - bPos.y()) / r + 0.5f;
// 太难计算,暂时先使用绘制圆环的方式去绘制圆
// TODO 计算矩形上的关键点
}
void TouchClickEffect::drawRingsGl(const QColor &color, float cx, float cy, float rmin, float rmax, float alpha)
{
const int num_segments = 100;
const float theta = 2.0 * M_PI * alpha / float(num_segments);
const float c = cosf(theta);
const float s = sinf(theta);
float tmin, tmax;
float xmin = 0, xmax = 0;
float ymin = -rmin, ymax = -rmax;
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
vbo->setUseColor(true);
vbo->setColor(color);
QVector<float> verts;
verts.reserve(num_segments * 4 + 4);
for (int i = 0;i < num_segments; ++i) {
verts << xmin + cx << ymin + cy;
verts << xmax + cx << ymax + cy;
tmin = xmin;
tmax = xmax;
xmin = c * xmin - s * ymin;
xmax = c * xmax - s * ymax;
ymin = s * tmin + c * ymin;
ymax = s * tmax + c * ymax;
}
verts << xmin + cx << ymin + cy;
verts << xmax + cx << ymax + cy;
vbo->setData(verts.size() / 2, 2, verts.data(), nullptr);
vbo->render(GL_TRIANGLE_STRIP);
}
void TouchClickEffect::drawRingsGl(GLTexture *texture, float cx, float cy, float rmin, float rmax, float offset, float alpha)
{
const int num_segments = 100;
const float theta = 2.0 * M_PI * alpha / float(num_segments);
const float c = cosf(theta);
const float s = sinf(theta);
float tmin, tmax;
float xmin = rmin * sin(offset), xmax = rmax * sin(offset);
float ymin = -rmin * cos(offset), ymax = -rmax * cos(offset);
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
QVector<float> verts;
QVector<float> texCoords;
verts.reserve(num_segments * 4 + 4);
for (int i = 0;i < num_segments; ++i) {
verts << xmin + cx << ymin + cy;
verts << xmax + cx << ymax + cy;
tmin = xmin;
tmax = xmax;
xmin = c * xmin - s * ymin;
xmax = c * xmax - s * ymax;
ymin = s * tmin + c * ymin;
ymax = s * tmax + c * ymax;
texCoords << i * 1.0f / num_segments << 1.0f;
texCoords << i * 1.0f / num_segments << 0.0f;
}
verts << xmin + cx << ymin + cy;
verts << xmax + cx << ymax + cy;
texCoords << 1.0f << 1.0f;
texCoords << 1.0f << 0.0f;
texture->bind();
vbo->setData(verts.size() / 2, 2, verts.data(), texCoords.data());
vbo->render(GL_TRIANGLE_STRIP);
texture->unbind();
}
void TouchClickEffect::repaint()
{
// QRect screenRect = effects->virtualScreenGeometry();
// QRegion dirtyArea;
// for (int i = 0;i < m_allPoints.size(); ++i) {
// QPointF &p = m_allPoints[i].pos;
// dirtyArea |= QRegion(QRect(p.x() - RINGS_MAXRADIUS - 10,
// p.y() - RINGS_MAXRADIUS - 10,
// p.x() + RINGS_MAXRADIUS + 10,
// p.y() + RINGS_MAXRADIUS + 10));
// }
// dirtyArea &= screenRect;
// effects->addRepaint(dirtyArea);
//! TODO 计算dirtyArea时在边缘时kwin有bug不更新边缘区域。先全屏重绘。
effects->addRepaintFull();
}
void TouchClickEffect::postPaintScreen()
{
effects->postPaintScreen();
repaint();
}
bool TouchClickEffect::isActive() const
{
return m_allPoints.size();
}
bool TouchClickEffect::touchDown(qint32 id, const QPointF &pos, quint32 time)
{
if (!m_fingerDetails.size()) {
//! 倒计时去激活长按动画
m_activeTimer->start();
} else {
if (m_activeTimer->isActive()) {
m_activeTimer->stop();
}
m_isActiveLongPress = false;
}
m_fingerDetails.insert(id);
m_allPoints.append({-1, id, pos});
repaint();
return false;
}
bool TouchClickEffect::touchMotion(qint32 id, const QPointF &pos, quint32 time)
{
for (int i = 0;i < m_allPoints.size(); ++i) {
TouchPoint &p = m_allPoints[i];
if (p.detail != id) continue;
if (p.time >= 0) break;
qreal dis = sqrt(pow(pos.x() - p.pos.x(), 2) + pow(pos.y() - p.pos.y(), 2));
if (dis > MIN_SHAKE_DISTANCE) {
p.time = 0;
}
break;
}
repaint();
return false;
}
bool TouchClickEffect::touchUp(qint32 id, quint32 time)
{
Q_UNUSED(time)
if (m_fingerDetails.find(id) == m_fingerDetails.end())
return false;
m_fingerDetails.erase(id);
if (!m_fingerDetails.size()) {
if (m_activeTimer->isActive()) {
m_activeTimer->stop();
}
m_isActiveLongPress = false;
}
for (int i = 0;i < m_allPoints.size(); ++i) {
TouchPoint &p = m_allPoints[i];
if (p.detail != id) continue;
if (p.time >= 0) break;
p.time = 0;
}
return false;
}
}

View File

@ -0,0 +1,94 @@
#ifndef KWIN_TOUCHCLICK_H
#define KWIN_TOUCHCLICK_H
#include <kwineffects.h>
#include <kwinglutils.h>
#include <QPointF>
#include <QVector>
#include <QTimer>
#include <QPropertyAnimation>
#include <unordered_set>
namespace KWin {
#define MIN_SHAKE_DISTANCE 7 // 防抖处理
#define CIRCLE_RADIUS 24
#define RINGS_WIDTH 12
#define RINGS_MINRADIUS (CIRCLE_RADIUS)
#define RINGS_MAXRADIUS (RINGS_MINRADIUS + RINGS_WIDTH)
#define LONG_PRESS_WAIT_TIME 300/*ms*/
#define LONG_PRESS_LAST_TIME 500/*ms*/
#define TOUCH_POINT_LIFE 250
/*!
* \brief touch click effect and long press effect
* \author Yunpeng Zhu.
*/
class TouchClickEffect
: public Effect
{
Q_OBJECT
Q_PROPERTY(double drawValue READ getDrawValue WRITE setDrawValue)
typedef struct _touch_point {
//! 当time==0时开始消失
//! 当time==-1时触摸点不消失
int64_t time;
int detail;
QPointF pos;
} TouchPoint;
public:
TouchClickEffect();
~TouchClickEffect();
void prePaintScreen(ScreenPrePaintData& data, std::chrono::milliseconds presentTime) override;
void paintScreen(int mask, const QRegion &region, ScreenPaintData& data) override;
void postPaintScreen() override;
bool isActive() const override;
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override;
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override;
bool touchUp(qint32 id, quint32 time) override;
double getDrawValue() const;
void setDrawValue(double value);
static bool supported();
private Q_SLOTS:
void onActiveTimerTimeout();
private:
void paintScreenGL(int mask, const QRegion &region, ScreenPaintData &data);
/*!
* \brief 线
* \param (cx, cy) r半径
* \param 线[(cx,cy)->(cx, cy - r)]alpha
* \return
*/
QPointF calculateExternalRectAndRoundRaysIntersect(float cx, float cy, float r, float alpha);
void drawCircleGl(const QColor& color, float cx, float cy, float r, float offset, float alpha);
void drawCircleGl(GLTexture *texture, float cx, float cy, float r, float offset, float alpha);
void drawRingsGl(const QColor& color, float cx, float cy, float rmin, float rmax, float alpha/*0-1*/);
void drawRingsGl(GLTexture *texture, float cx, float cy, float rmin, float rmax, float offset, float alpha/*0-1*/);
void repaint();
void loadTexture();
std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero();
bool m_isActiveLongPress;
std::unordered_set<int> m_fingerDetails;
QTimer *m_activeTimer;
QPropertyAnimation *m_drawAnimation;
//! 当前绘制的进度
double m_drawProgress;
QVector<TouchPoint> m_allPoints;
GLTexture *m_ringsTexture;
GLTexture *m_ringsBgTexture;
GLTexture *m_circleTexture;
};
}
#endif //KWIN_TOUCHCLICK_H

View File

@ -0,0 +1,14 @@
#######################################
# Effect
set(touchtrail_SOURCES
touchtrail.cpp
main.cpp
)
kwin4_add_effect_module(kwin4_effect_touchtrail ${touchtrail_SOURCES})
# Data files
install(FILES data/trailing.png DESTINATION ${KDE_INSTALL_DATADIR}/${KWIN_NAME})

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,18 @@
/*
SPDX-FileCopyrightText: 2022 Hongfei Shang <shanghongfei@kylinos.cn>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "touchtrail.h"
namespace KWin
{
KWIN_EFFECT_FACTORY_SUPPORTED(TouchTrailEffect,
"metadata.json.stripped",
return TouchTrailEffect::supported();)
} // namespace KWin
#include "main.moc"

View File

@ -0,0 +1,61 @@
{
"KPlugin": {
"Category": "Appearance",
"Description": "Visualize touch points",
"Description[ar]": "تصور نقاط اللمس",
"Description[az]": "Ekranda toxunan nöqtələri canlandırır",
"Description[ca@valencia]": "Visualitza els punts de contacte",
"Description[ca]": "Visualitza els punts de contacte",
"Description[cs]": "Vizualizovat dotekové body",
"Description[en_GB]": "Visualise touch points",
"Description[es]": "Visualizar los puntos de contacto",
"Description[fi]": "Korosta kosketuspisteet",
"Description[fr]": "Afficher les points tactiles",
"Description[hu]": "Vizualizálja az érintési pontokat",
"Description[ia]": "Visualisa punctos de contacto",
"Description[id]": "Memvisualkan titik sentuh",
"Description[it]": "Visualizza i punti di tocco",
"Description[ko]": "터치 지점 표시",
"Description[nl]": "Aanraakpunten visualiseren",
"Description[nn]": "Visualiser kontaktpunkt",
"Description[pl]": "Uwidacznia punkty dotyku",
"Description[pt_BR]": "Visualizar pontos de toque",
"Description[ru]": "Визуализация событий касания экрана",
"Description[sk]": "Vizualizovať dotykové body",
"Description[sl]": "Vizualiziraj točke dotika",
"Description[sv]": "Åskådliggör beröringspunkter",
"Description[tr]": "Dokunma noktalarını görselleştir",
"Description[uk]": "Візуалізувати точки дотику",
"Description[x-test]": "xxVisualize touch pointsxx",
"Description[zh_CN]": "高亮显示在触控屏幕上的触摸点",
"EnabledByDefault": true,
"Id": "touchtrail",
"License": "GPL",
"Name": "Touch Points",
"Name[ar]": "نقاط اللمس",
"Name[az]": "Toxunma nöqtələri",
"Name[ca@valencia]": "Punts de contacte",
"Name[ca]": "Punts de contacte",
"Name[cs]": "Dotekové body",
"Name[en_GB]": "Touch Points",
"Name[es]": "Puntos de contacto",
"Name[fi]": "Kosketuspisteet",
"Name[fr]": "Points tactiles",
"Name[hu]": "Érintési pontok",
"Name[ia]": "Punctos de contacto",
"Name[it]": "Punti di tocco",
"Name[ko]": "터치 지점",
"Name[nl]": "Aanraakpunten",
"Name[nn]": "Kontaktpunkt",
"Name[pl]": "Punkty dotyku",
"Name[pt_BR]": "Pontos de toque",
"Name[ru]": "Точки прикосновения",
"Name[sk]": "Dotykové body",
"Name[sl]": "Točke dotika",
"Name[sv]": "Beröringspunkter",
"Name[tr]": "Dokunma Noktaları",
"Name[uk]": "Точки дотику",
"Name[x-test]": "xxTouch Pointsxx",
"Name[zh_CN]": "触摸点高亮"
}
}

View File

@ -0,0 +1,340 @@
#include "touchtrail.h"
#include <kwinglplatform.h>
#include <QTime>
#include <QByteArray>
#include <cmath>
namespace KWin{
TouchTrailEffect::TouchTrailEffect()
: Effect()
, m_texture(nullptr)
{
loadTexture();
loadShader();
}
TouchTrailEffect::~TouchTrailEffect()
{
if (m_texture)
delete m_texture;
m_texture = nullptr;
}
bool TouchTrailEffect::supported()
{
return effects->isOpenGLCompositing();
}
void TouchTrailEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
{
int time = 0;
if (m_lastPresentTime.count()) {
time = (presentTime - m_lastPresentTime).count();
}
for (int i = 0; i < m_touchPoints.size(); ++i) {
QVector<TouchPoint>::iterator it = m_touchPoints[i].begin();
while (it != m_touchPoints[i].end()) {
it->life += time;
++it;
}
while (m_touchPoints[i].size() && m_touchPoints[i].first().life > MAX_LIFE) {
if (m_touchPoints[i].size() == 1) {
int detail = m_touchPoints[i].first().detail;
if (!m_idIsUse[i]) {
m_detail2ID.erase(detail);
}
}
m_touchPoints[i].removeFirst();
}
}
calTriPoints();
if (0 == m_detail2ID.size()) {
m_lastPresentTime = std::chrono::milliseconds::zero();
} else {
m_lastPresentTime = presentTime;
}
effects->prePaintScreen(data, presentTime);
}
void TouchTrailEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
{
// printf("adasdasds\n");
effects->paintScreen(mask, region, data);
if (effects->isOpenGLCompositing()) {
paintScreenGL(data);
}
}
void TouchTrailEffect::paintScreenGL(ScreenPaintData &data)
{
GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
shader->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
for (int i = 0; i < m_triPoints.size(); ++i)
{
QVector<float> vert, texCoord;
const int &n = m_triPoints[i].size();
for (int j = 0; j < n; ++j) {
vert << m_triPoints[i][j].x() << m_triPoints[i][j].y();
texCoord << ((j & 1) ? 1.0f : 0.0f) << (j >> 1) * 1.0 / (n >> 1);
}
vbo->reset();
m_texture->bind();
vbo->setData(vert.size() / 2, 2, vert.data(), texCoord.data());
vbo->render(GL_TRIANGLE_STRIP);
m_texture->unbind();
}
glDisable(GL_BLEND);
ShaderManager::instance()->popShader();
}
void TouchTrailEffect::postPaintScreen()
{
effects->postPaintScreen();
repaint();
}
bool TouchTrailEffect::isActive() const
{
return m_detail2ID.size();
}
bool TouchTrailEffect::touchDown(qint32 id, const QPointF &pos, quint32 time)
{
// printf("当前手指ID=%d\n", id);
insertTouchPoint(id, pos);
repaint();
return false;
}
bool TouchTrailEffect::touchMotion(qint32 id, const QPointF &pos, quint32 time)
{
updateTouchPoint(id, pos);
repaint();
return false;
}
bool TouchTrailEffect::touchUp(qint32 id, quint32 time)
{
// printf("离开的手指ID=%d\n", id);
removeTouchPoint(id);
// 各触摸点开始快速衰减
// TODO 加上滑动距离作为衰减因子
for (int i = 0; i < m_touchPoints.size(); ++i) {
int sub;
for (int j = 0; j < m_touchPoints[i].size(); j++) {
if (!j) {
sub = MAX_LIFE - m_touchPoints[i][j].life;
m_touchPoints[i][j].life = MAX_LIFE;
} else {
m_touchPoints[i][j].life += sub;
}
}
}
return false;
}
void TouchTrailEffect::insertTouchPoint(qint32 detail, QPointF pos)
{
int id = -1;
do {
++id;
if (id == m_touchPoints.size()) {
m_touchPoints.append(QVector<TouchPoint>());
m_triPoints.append(QVector<QPointF>());
m_idIsUse.append(false);
}
} while (m_idIsUse[id] || m_touchPoints[id].size());
m_detail2ID[detail] = id;
m_idIsUse[id] = true;
m_touchPoints[id].append({detail, pos, 0});
// printf("当前分配的ID为 %d\n", id);
}
void TouchTrailEffect::updateTouchPoint(qint32 detail, QPointF pos)
{
// 开关混成有可能导致事件不成对
if (m_detail2ID.find(detail) == m_detail2ID.end()) {
return;
}
int id = m_detail2ID[detail];
m_touchPoints[id].append({detail, pos, 0});
}
void TouchTrailEffect::removeTouchPoint(qint32 detail)
{
// 开关混成有可能导致事件不成对
if (m_detail2ID.find(detail) == m_detail2ID.end()) {
return;
}
int id = m_detail2ID[detail];
m_idIsUse[id] = false;
if (!m_touchPoints[id].size()) {
m_detail2ID.erase(detail);
}
}
void TouchTrailEffect::loadTexture()
{
#if defined(QT_NO_DEBUG)
QString file_path = {QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral(DEFAULT_TEXTURE_IMAGE))};
#else
QString file_path = QCoreApplication::applicationDirPath() + QStringLiteral("/../../src/effects/touchtrail/data/") + QStringLiteral(DEFAULT_TEXTURE_IMAGE);
#endif
QImage img(file_path);
if (img.isNull()) {
qWarning() << "load image [" << file_path << "] failed";
return;
}
//! TODO 使用KWin中的纹理融合算法
//! 没有找到kwin里opengl的纹理融合
//! 先手写个简单的纹理和颜色的融合,简单支持
qreal Rd = 139.0 / 255.0;
qreal Gd = 134.0 / 255.0;
qreal Bd = 130.0 / 255.0;
qreal Ad = 30.0 / 255.0;
for (int i = 0; i < img.width(); ++i) {
for (int j = 0; j < img.height(); ++j) {
QRgb p = img.pixel(i, j);
qreal Rs = qRed(p) / 255.0;
qreal Gs = qGreen(p) / 255.0;
qreal Bs = qBlue(p) / 255.0;
qreal As = qAlpha(p) / 255.0;
// printf("%d\n", As);
//! 纹理和颜色融合
// img.setPixel(i, j, qRgba((Rs * As + Rd * (1 - As)) * 255,
// (Gs * As + Gd * (1 - As)) * 255,
// (Bs * As + Bd * (1 - As)) * 255,
// (fmin(As, Ad)) * 255));
//! 纹理仅控制形状
img.setPixel(i, j, qRgba(Rd * 255,
Gd * 255,
Bd * 255,
(fmin(As, Ad)) * 255));
}
}
if (!m_texture)
m_texture = new GLTexture(img);
}
void TouchTrailEffect::loadShader()
{
// m_shader.reset(ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("coverswitch-reflection.glsl")));
}
void TouchTrailEffect::calTriPoints()
{
for (int i = 0; i < m_touchPoints.size(); ++i)
{
m_triPoints[i].clear();
if (m_touchPoints[i].size() < 3) continue;
QPointF last(m_touchPoints[i].first().pos);
QVector<TouchPoint>::iterator it = m_touchPoints[i].begin();
for (++it; it != m_touchPoints[i].end(); ++it) {
QPointF &cur = it->pos;
qreal d = dist(cur.x(), cur.y(), last.x(), last.y());
if (d < MIN_POINT_SEG) continue;
if (cur.x() == last.x() || cur.y() == last.y()) {
continue;
}
// 方向为 last->cur 的向量
QPointF vec_l2c(cur.x() - last.x(), cur.y() - last.y());
// cur last线段上的中点
QPointF mid((cur.x() + last.x()) / 2, (cur.y() + last.y()) / 2);
d /= 2;
// 向量的斜率k
qreal k = (cur.y() - last.y()) / (cur.x() - last.x());
qreal vlk = 1.0 / k;
qreal alpha = atan(fabs(vlk));
qreal dx = DEFAULT_STROKE_WIDTH * cos(alpha);
qreal dy = DEFAULT_STROKE_WIDTH * sin(alpha);
QPointF p1, p2;
if (k > 0 && vec_l2c.x() > 0) {
p1.setX(mid.x() - dx); p1.setY(mid.y() + dy);
p2.setX(mid.x() + dx); p2.setY(mid.y() - dy);
} else if (k > 0 && vec_l2c.x() < 0) {
p1.setX(mid.x() + dx); p1.setY(mid.y() - dy);
p2.setX(mid.x() - dx); p2.setY(mid.y() + dy);;
} else if (k < 0 && vec_l2c.x() > 0) {
p1.setX(mid.x() + dx); p1.setY(mid.y() + dy);
p2.setX(mid.x() - dx); p2.setY(mid.y() - dy);
} else {
p1.setX(mid.x() - dx); p1.setY(mid.y() - dy);
p2.setX(mid.x() + dx); p2.setY(mid.y() + dy);
}
m_triPoints[i].append(p1);
m_triPoints[i].append(p2);
last = cur;
if (it + 1 == m_touchPoints[i].end()) {
mid = it->pos;
if (k > 0 && vec_l2c.x() > 0) {
p1.setX(mid.x() - dx); p1.setY(mid.y() + dy);
p2.setX(mid.x() + dx); p2.setY(mid.y() - dy);
} else if (k > 0 && vec_l2c.x() < 0) {
p1.setX(mid.x() + dx); p1.setY(mid.y() - dy);
p2.setX(mid.x() - dx); p2.setY(mid.y() + dy);;
} else if (k < 0 && vec_l2c.x() > 0) {
p1.setX(mid.x() + dx); p1.setY(mid.y() + dy);
p2.setX(mid.x() - dx); p2.setY(mid.y() - dy);
} else {
p1.setX(mid.x() - dx); p1.setY(mid.y() - dy);
p2.setX(mid.x() + dx); p2.setY(mid.y() + dy);
}
m_triPoints[i].append(p1);
m_triPoints[i].append(p2);
}
}
}
}
void TouchTrailEffect::repaint()
{
// effects->addRepaintFull();
if (m_triPoints.size()) {
QRect dirtyRect;
QPoint topLeft(INT32_MAX, INT32_MAX), bottomRight(INT32_MIN, INT32_MIN);
for (int i = 0; i < m_triPoints.size(); ++i) {
for (int j = 0; j < m_triPoints[i].size(); ++j) {
QPointF &p = m_triPoints[i][j];
topLeft.setX(fmin(topLeft.x(), p.x()));
topLeft.setY(fmin(topLeft.y(), p.y()));
bottomRight.setX(fmax(bottomRight.x(), p.x()));
bottomRight.setY(fmax(bottomRight.y(), p.y()));
}
}
dirtyRect.setTopLeft(topLeft);
dirtyRect.setBottomRight(bottomRight);
m_dirtyRect = dirtyRect;
effects->addRepaint(m_dirtyRect);
} else {
effects->addRepaintFull();
}
return;
}
}

View File

@ -0,0 +1,81 @@
#ifndef KWIN_TOUCHMOTIONSTREAK_H
#define KWIN_TOUCHMOTIONSTREAK_H
#include <kwineffects.h>
#include <kwinglutils.h>
#include <QList>
#include <unordered_map>
namespace KWin {
#define MAX_FINGER_COUNT 10 // 触摸屏支持最大触摸点的个数
#define MIN_POINT_SEG 2/*px*/ // 触摸点之间允许的最小欧氏距离
#define DEFAULT_STROKE_WIDTH 3/*px*/ // 条带的宽度
#define MAX_POINT_NUMBER 8 // 保存的触摸点的最大数量
#define DEFAULT_TEXTURE_IMAGE "trailing.png" // 默认纹理
#define MAX_LIFE 200 // 每个触摸点存活的最长时间
#define dist(x1,y1,x2,y2) sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
/*!
* \brief UKUI Touch Motion Streak Effect
* \author Yunpeng Zhu.
*/
class TouchTrailEffect
: public Effect
{
Q_OBJECT
typedef struct _touch_point{
int detail;
QPointF pos;
int life;
}TouchPoint;
public:
TouchTrailEffect();
virtual ~TouchTrailEffect();
void prePaintScreen(ScreenPrePaintData& data, std::chrono::milliseconds presentTime) override;
void paintScreen(int mask, const QRegion &region, ScreenPaintData& data) override;
void postPaintScreen() override;
bool isActive() const override;
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override;
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override;
bool touchUp(qint32 id, quint32 time) override;
static bool supported();
private:
void paintScreenGL(ScreenPaintData &data);
void insertTouchPoint(qint32 detail, QPointF pos);
void updateTouchPoint(qint32 detail, QPointF pos);
void removeTouchPoint(qint32 detail);
void loadTexture();
void loadShader();
/*!
* \brief
*/
void calTriPoints();
void repaint();
//! 保存触摸点坐标
QVector<QVector<TouchPoint>> m_touchPoints;
//! 保存所需要绘制的三角形条带的坐标
QVector<QVector<QPointF>> m_triPoints;
//! 标记id是否被使用
QVector<bool> m_idIsUse;
//! 将detail映射到0~MAX_FINGER_COUNT-1
std::unordered_map<int,int> m_detail2ID;
//! 绘制
GLTexture *m_texture;
//! 记录需要重绘的区域
QRect m_dirtyRect;
std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero();
};
}
#endif // KWIN_TOUCHMOTIONSTREAK_H

View File

@ -40,6 +40,8 @@
#include <QDebug> #include <QDebug>
#include <QWindow> #include <QWindow>
#include <QDBusInterface> #include <QDBusInterface>
#include <QDir>
#include <QRegularExpression>
// system // system
#if HAVE_SYS_PRCTL_H #if HAVE_SYS_PRCTL_H
@ -58,6 +60,15 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#if defined(QT_NO_DEBUG)
#define LOG_FILE_COUNT_MAX 2
#else
#define LOG_FILE_COUNT_MAX 4
#endif
#define LOG_FILE_SIZE_MAX 20*1024*1024
#define LOG_FILE_PREFIX "ukui_kwin"
#define LOG_FILE_PATH "/.log/"
Q_IMPORT_PLUGIN(KWinIntegrationPlugin) Q_IMPORT_PLUGIN(KWinIntegrationPlugin)
Q_IMPORT_PLUGIN(KGlobalAccelImpl) Q_IMPORT_PLUGIN(KGlobalAccelImpl)
Q_IMPORT_PLUGIN(KWindowSystemKWinPlugin) Q_IMPORT_PLUGIN(KWindowSystemKWinPlugin)
@ -360,8 +371,106 @@ void dropNiceCapability()
} // namespace } // namespace
/*
* find next log file, and do some check.
*/
static int checkLogFile(int index) {
int logFileIndex = index;
for (size_t i = 0; i < LOG_FILE_COUNT_MAX; i++) {
logFileIndex = (logFileIndex + 1) % LOG_FILE_COUNT_MAX;
QString logFilePath = QDir::homePath() + LOG_FILE_PATH + "/" + QStringLiteral("%1_%2.log").arg(LOG_FILE_PREFIX).arg(logFileIndex);
if (!QFile::exists(logFilePath))
break;
QFile temp(logFilePath);
if (temp.size() < LOG_FILE_SIZE_MAX)
break;
if ((LOG_FILE_COUNT_MAX - 1) == i) {
// here is the next log file.
logFileIndex = (logFileIndex + 1) % LOG_FILE_COUNT_MAX;
QString nextLogFilePath = QDir::homePath() + LOG_FILE_PATH + "/" + QStringLiteral("%1_%2.log").arg(LOG_FILE_PREFIX).arg(logFileIndex);
// prepare rotate to next log file, so we should log some info in prev log file to make a better understanding.
FILE* tailFile = fopen(logFilePath.toLocal8Bit().constData(), "a+");
if (tailFile) {
fprintf(tailFile, "to be continue, please check:\r\n\t[%s]", nextLogFilePath.toStdString().c_str());
fclose(tailFile);
}
// and make next log file be clear
QFile nextLogFile(nextLogFilePath);
nextLogFile.remove();
}
}
return logFileIndex;
}
static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
QDateTime dateTime = QDateTime::currentDateTime();
QString strFormat = "yyMMdd hh:mm:ss.zzz";
QString strDateTime = dateTime.toString(strFormat);
FILE *log_file = nullptr;
QString logFilePath;
int fileSize;
static int currentLogFileIndex = -1;
QDir dir;
bool flag = 0;
logFilePath = QDir::homePath() + LOG_FILE_PATH;
if (dir.mkpath(logFilePath)) {
flag = 1;
}
if (flag) {
if (-1 == currentLogFileIndex)
currentLogFileIndex = checkLogFile(currentLogFileIndex);
logFilePath = logFilePath + "/" + QStringLiteral("%1_%2.log").arg(LOG_FILE_PREFIX).arg(currentLogFileIndex);
log_file = fopen(logFilePath.toLocal8Bit().constData(), "a+");
}
QString src = QString(context.file ? context.file : "").section('/', -1);
QRegularExpression re("(\\w([\\S]*?)[\\s]?)(\\()");
QRegularExpressionMatch match = re.match(context.function);
QString func = match.hasMatch() ? match.captured(1) : "";
QString exinfo = src.isEmpty() ? QString() : QStringLiteral("(%1:%2 %3)").arg(src).arg(context.line).arg(func);
switch (type) {
case QtDebugMsg:
if (!log_file) {
break;
}
fprintf(log_file, "Debug: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtInfoMsg:
fprintf(log_file? log_file: stdout, "Info: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtWarningMsg:
fprintf(log_file? log_file: stderr, "Warning: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtCriticalMsg:
fprintf(log_file? log_file: stderr, "Critical: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtFatalMsg:
fprintf(log_file? log_file: stderr, "Fatal: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
}
if (log_file) {
fileSize = ftell(log_file);
fclose(log_file);
if (fileSize >= LOG_FILE_SIZE_MAX)
currentLogFileIndex = checkLogFile(currentLogFileIndex);
}
}
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
qDebug() << "==================kdemain in before message handler===========================";
qInstallMessageHandler(messageOutput);
qDebug() << "==================kdemain in===========================";
KWin::disablePtrace(); KWin::disablePtrace();
KWin::Application::setupMalloc(); KWin::Application::setupMalloc();
KWin::Application::setupLocalizedString(); KWin::Application::setupLocalizedString();
@ -391,6 +500,27 @@ int main(int argc, char * argv[])
// reset QT_QPA_PLATFORM so we don't propagate it to our children (e.g. apps launched from the overview effect) // reset QT_QPA_PLATFORM so we don't propagate it to our children (e.g. apps launched from the overview effect)
qunsetenv("QT_QPA_PLATFORM"); qunsetenv("QT_QPA_PLATFORM");
#if !defined(QT_NO_DEBUG)
QStringList newLibPaths(a.applicationDirPath());
const QStringList oldLibPaths = QCoreApplication::libraryPaths();
if (newLibPaths.contains("/usr/local/bin")) {
// when do “make install”, we need add plugins directory before normal qt plugins directory.
// we can get arch lib directory name from qt plugins directory and do a replace
// from "/usr/lib/x86_64-linux-gnu/qt5/plugins" to "/usr/local/lib/x86_64-linux-gnu/plugins"
// then kwin will load the right plugins.
QRegularExpression re("^/usr/lib/(.*)/qt.*$");
QStringList plugins = oldLibPaths.filter(QRegularExpression("^/usr/lib/"))
.replaceInStrings(re, "/usr/local/lib/\\1/plugins");
newLibPaths.append(plugins);
}
newLibPaths.append(oldLibPaths);
QCoreApplication::setLibraryPaths(newLibPaths);
qDebug() << "debug mode:";
qDebug() << " set application dir path to be the first of libraryPaths,";
qDebug() << " so we can be happy to debug kwin plugins.";
qDebug() << " libraryPaths:" << newLibPaths;
#endif
KWin::Application::createAboutData(); KWin::Application::createAboutData();
KQuickAddons::QtQuickSettings::init(); KQuickAddons::QtQuickSettings::init();

View File

@ -36,6 +36,8 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QX11Info> #include <QX11Info>
#include <QtDBus> #include <QtDBus>
#include <QDir>
#include <QRegularExpression>
// system // system
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
@ -43,6 +45,15 @@
#endif // HAVE_UNISTD_H #endif // HAVE_UNISTD_H
#include <iostream> #include <iostream>
#if defined(QT_NO_DEBUG)
#define LOG_FILE_COUNT_MAX 2
#else
#define LOG_FILE_COUNT_MAX 4
#endif
#define LOG_FILE_SIZE_MAX 20*1024*1024
#define LOG_FILE_PREFIX "ukui_kwin"
#define LOG_FILE_PATH "/.log/"
Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtWarningMsg) Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtWarningMsg)
namespace KWin namespace KWin
@ -334,8 +345,105 @@ void ApplicationX11::crashHandler(int signal)
} // namespace } // namespace
/*
* find next log file, and do some check.
*/
static int checkLogFile(int index) {
int logFileIndex = index;
for (size_t i = 0; i < LOG_FILE_COUNT_MAX; i++) {
logFileIndex = (logFileIndex + 1) % LOG_FILE_COUNT_MAX;
QString logFilePath = QDir::homePath() + LOG_FILE_PATH + "/" + QStringLiteral("%1_%2.log").arg(LOG_FILE_PREFIX).arg(logFileIndex);
if (!QFile::exists(logFilePath))
break;
QFile temp(logFilePath);
if (temp.size() < LOG_FILE_SIZE_MAX)
break;
if ((LOG_FILE_COUNT_MAX - 1) == i) {
// here is the next log file.
logFileIndex = (logFileIndex + 1) % LOG_FILE_COUNT_MAX;
QString nextLogFilePath = QDir::homePath() + LOG_FILE_PATH + "/" + QStringLiteral("%1_%2.log").arg(LOG_FILE_PREFIX).arg(logFileIndex);
// prepare rotate to next log file, so we should log some info in prev log file to make a better understanding.
FILE* tailFile = fopen(logFilePath.toLocal8Bit().constData(), "a+");
if (tailFile) {
fprintf(tailFile, "to be continue, please check:\r\n\t[%s]", nextLogFilePath.toStdString().c_str());
fclose(tailFile);
}
// and make next log file be clear
QFile nextLogFile(nextLogFilePath);
nextLogFile.remove();
}
}
return logFileIndex;
}
static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
QDateTime dateTime = QDateTime::currentDateTime();
QString strFormat = "yyMMdd hh:mm:ss.zzz";
QString strDateTime = dateTime.toString(strFormat);
FILE *log_file = nullptr;
QString logFilePath;
int fileSize;
static int currentLogFileIndex = -1;
QDir dir;
bool flag = 0;
logFilePath = QDir::homePath() + LOG_FILE_PATH;
if (dir.mkpath(logFilePath)) {
flag = 1;
}
if (flag) {
if (-1 == currentLogFileIndex)
currentLogFileIndex = checkLogFile(currentLogFileIndex);
logFilePath = logFilePath + "/" + QStringLiteral("%1_%2.log").arg(LOG_FILE_PREFIX).arg(currentLogFileIndex);
log_file = fopen(logFilePath.toLocal8Bit().constData(), "a+");
}
QString src = QString(context.file ? context.file : "").section('/', -1);
QRegularExpression re("(\\w([\\S]*?)[\\s]?)(\\()");
QRegularExpressionMatch match = re.match(context.function);
QString func = match.hasMatch() ? match.captured(1) : "";
QString exinfo = src.isEmpty() ? QString() : QStringLiteral("(%1:%2 %3)").arg(src).arg(context.line).arg(func);
switch (type) {
case QtDebugMsg:
if (!log_file) {
break;
}
fprintf(log_file, "Debug: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtInfoMsg:
fprintf(log_file? log_file: stdout, "Info: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtWarningMsg:
fprintf(log_file? log_file: stderr, "Warning: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtCriticalMsg:
fprintf(log_file? log_file: stderr, "Critical: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
case QtFatalMsg:
fprintf(log_file? log_file: stderr, "Fatal: %s: %s %s\n", strDateTime.toStdString().c_str(), localMsg.constData(), exinfo.toStdString().c_str());
break;
}
if (log_file) {
fileSize = ftell(log_file);
fclose(log_file);
if (fileSize >= LOG_FILE_SIZE_MAX)
currentLogFileIndex = checkLogFile(currentLogFileIndex);
}
}
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
qDebug() << "==================kdemain in before message handler===========================";
qInstallMessageHandler(messageOutput);
qDebug() << "==================kdemain in===========================";
KWin::Application::setupMalloc(); KWin::Application::setupMalloc();
KWin::Application::setupLocalizedString(); KWin::Application::setupLocalizedString();
@ -429,6 +537,27 @@ int main(int argc, char * argv[])
KWin::ApplicationX11 a(argc, argv); KWin::ApplicationX11 a(argc, argv);
a.setupTranslator(); a.setupTranslator();
#if !defined(QT_NO_DEBUG)
QStringList newLibPaths(a.applicationDirPath());
const QStringList oldLibPaths = QCoreApplication::libraryPaths();
if (newLibPaths.contains("/usr/local/bin")) {
// when do “make install”, we need add plugins directory before normal qt plugins directory.
// we can get arch lib directory name from qt plugins directory and do a replace
// from "/usr/lib/x86_64-linux-gnu/qt5/plugins" to "/usr/local/lib/x86_64-linux-gnu/plugins"
// then kwin will load the right plugins.
QRegularExpression re("^/usr/lib/(.*)/qt.*$");
QStringList plugins = oldLibPaths.filter(QRegularExpression("^/usr/lib/"))
.replaceInStrings(re, "/usr/local/lib/\\1/plugins");
newLibPaths.append(plugins);
}
newLibPaths.append(oldLibPaths);
QCoreApplication::setLibraryPaths(newLibPaths);
qDebug() << "debug mode:";
qDebug() << " set application dir path to be the first of libraryPaths,";
qDebug() << " so we can be happy to debug kwin plugins.";
qDebug() << " libraryPaths:" << newLibPaths;
#endif
KWin::Application::createAboutData(); KWin::Application::createAboutData();
KQuickAddons::QtQuickSettings::init(); KQuickAddons::QtQuickSettings::init();

View File

@ -834,6 +834,10 @@ void Workspace::quickTileWindow(QuickTileMode mode)
return; return;
} }
if(m_bTabletMode) {
return;
}
// If the user invokes two of these commands in a one second period, try to // If the user invokes two of these commands in a one second period, try to
// combine them together to enable easy and intuitive corner tiling // combine them together to enable easy and intuitive corner tiling
#define FLAG(name) QuickTileMode(QuickTileFlag::name) #define FLAG(name) QuickTileMode(QuickTileFlag::name)

View File

@ -1,2 +1,3 @@
add_definitions(-DTRANSLATION_DOMAIN=\"kwin_clients\") add_definitions(-DTRANSLATION_DOMAIN=\"kwin_clients\")
add_subdirectory(aurorae) add_subdirectory(aurorae)
add_subdirectory(ukui)

View File

@ -105,7 +105,12 @@ void Helper::unref()
} }
static const QString s_defaultTheme = QStringLiteral("kwin4_decoration_qml_plastik"); static const QString s_defaultTheme = QStringLiteral("kwin4_decoration_qml_plastik");
#if defined(QT_NO_DEBUG)
static const QString s_qmlPackageFolder = QStringLiteral(KWIN_NAME "/decorations/"); static const QString s_qmlPackageFolder = QStringLiteral(KWIN_NAME "/decorations/");
#else
static const QString s_qmlPackageFolder = QCoreApplication::applicationDirPath() + QStringLiteral("/../../src/plugins/kdecorations/aurorae/themes/");
#endif
/* /*
* KDecoration2::BorderSize doesn't map to the indices used for the Aurorae SVG Button Sizes. * KDecoration2::BorderSize doesn't map to the indices used for the Aurorae SVG Button Sizes.
* BorderSize defines None and NoSideBorder as index 0 and 1. These do not make sense for Button * BorderSize defines None and NoSideBorder as index 0 and 1. These do not make sense for Button
@ -169,7 +174,15 @@ QQmlComponent *Helper::loadComponent(const QString &themeName)
const KPluginMetaData &service = offers.first(); const KPluginMetaData &service = offers.first();
const QString pluginName = service.pluginId(); const QString pluginName = service.pluginId();
const QString scriptName = service.value(QStringLiteral("X-Plasma-MainScript")); const QString scriptName = service.value(QStringLiteral("X-Plasma-MainScript"));
#if defined(QT_NO_DEBUG)
const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_qmlPackageFolder + pluginName + QLatin1String("/contents/") + scriptName); const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_qmlPackageFolder + pluginName + QLatin1String("/contents/") + scriptName);
#else
QString qmlName = pluginName;
QString file = s_qmlPackageFolder + qmlName.remove("kwin4_decoration_qml_") + QLatin1String("/package/contents/") + scriptName;
if (!QFile::exists(file))
file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_qmlPackageFolder + pluginName + QLatin1String("/contents/") + scriptName);
qCDebug(AURORAE) << "Try to load qml file:" << file;
#endif
if (file.isNull()) { if (file.isNull()) {
qCDebug(AURORAE) << "Could not find script file for " << pluginName; qCDebug(AURORAE) << "Could not find script file for " << pluginName;
// TODO: what to do in error case? // TODO: what to do in error case?

View File

@ -0,0 +1,63 @@
cmake_minimum_required(VERSION 3.5)
message("${PLUGIN_INSTALL_DIR}")
message("${QT_INSTALL_PLUGINS}")
add_definitions(-DCMAKE_INSTALL_PREFIX="/usr")
project(kwin-style-ukui LANGUAGES CXX)
find_package(ECM 0.0.9 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
include(ECMFindModuleHelpers)
include(ECMInstallIcons)
include(KDEInstallDirs)
include(KDECMakeSettings)
#include(KDECompilerSettings NO_POLICY_SCOPE)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Gui LinguistTools DBus X11Extras REQUIRED)
#set(TS_FILES kwin-style-ukui_zh_CN.ts)
add_library(kwin-style-ukui SHARED
ukui-decoration.cpp
ukui-decoration.h
button.cpp
button.h
shadow-helper.cpp
shadow-helper.h
xatom-helper.cpp
xatom-helper.h
kwin-style-ukui.json
icon.qrc
#${TS_FILES}
)
set_target_properties(kwin-style-ukui PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/org.kde.kdecoration2/")
add_executable(test-csd test/test-csd.cpp xatom-helper.cpp xatom-helper.h)
find_package(KDecoration2)
find_package(KF5CoreAddons)
find_package(KF5WindowSystem)
find_package(KF5GuiAddons)
find_package(XCB)
target_link_libraries(kwin-style-ukui PUBLIC KDecoration2::KDecoration KF5::ConfigWidgets KF5::CoreAddons KF5::WindowSystem PRIVATE Qt5::Gui Qt5::DBus Qt5::X11Extras KF5::GuiAddons kwineffects XCB::XCB XCB::UTIL -lX11)
target_link_libraries(test-csd PUBLIC KDecoration2::KDecoration KF5::CoreAddons KF5::WindowSystem PRIVATE Qt5::Gui Qt5::DBus Qt5::X11Extras KF5::GuiAddons XCB::XCB XCB::UTIL -lX11)
#target_compile_definitions(kwin-style-ukui PUBLIC KWINSTYLEUKUI_LIBRARY)
#qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
message("${PLUGIN_INSTALL_DIR}")
message("${QT_INSTALL_PLUGINS}")
install(TARGETS kwin-style-ukui DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kdecoration2)

View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@ -0,0 +1,65 @@
# kwin-style-ukui -- UKUI KWin Decoration Project
## Introduction & Background
KDE is a well-known desktop environment in the worldwide. Its window manager - KWin is also an amayzing project, which providing many cool effects, and allowding us to extend it with plugins or scripts.
The ukui-kwin-style is based on the KDecoration2, which is an extensible frameworks for KWin to custom a window decoration style, such as title bar, border and shadow. The projects similar with this one are kwin-style-breeze and kwin-decoration-oxygen, etc... Breeze and Oxygen are also providing the applications style based on Qt style frameworks, infuenting most Qt applications.
This project has 2 main targets:
1. providing the decorations for normal windows, and keep the decorations suiting for ukui styles.
2. providing the decorations for special windows (a client side decoration window), such as peony, ukui-control-center, decorating special windows with shadow and a resize handler.
The difference between normal windows and special windows (csd windows) in X11 is window's motif hints atom, which is a protocol of NETWM. However, there are many arguments about client side decoration, so breeze and oxygen choose to ignore some flags of motif hints (actually not handled, motif hints only handled at kwin self), which related csd.
As a developer, I'd like to stand by ssd (sever side decoration) side, but I have to meet the needs of UI designers. I have tried many ways to archives those goals, and as rebasing the rules as possible. I am still looking for the best solution, and this project may just be a step on the way. Actually I don't think I have found a good solution for resolve the arguments on csd, and I gusse it will continue a loog while.
## HOW it works?
In X Window System, there are many protocol between X client (a window) and X sever. Window Manager take over the works of X sever in current PC (In x11 platform), and handle these protocols.
For now, **ICCCM** and **NETWM** is the common protocols in different X11 desktop environment. One protocol of NETWM -- **motif hints** is a protocol to control the window decoration and supported actions provided by window manager.
Throughing motif hints, window can define a rules telling window manager how to decorate it, and enable/disable window interaction actions.
KWin's decoration frameworks, KDecoration2, provides us the convinience access between window and window manger. It let us just need consider how to render decoration for a window. For a decoration to be painted, it can get the window handle from client() which provided by the frameworks. So, we can check the window motif hints durring rendering it.
For a csd window, the windows motif hints should be different with the normal one, this is mostly handled by gui libraries, such as Qt and GTK. In X11 platform, Qt's frameless window and GTK's csd window both change the motif hints of the window, but their hints are different, that cause a csd window seems better than frameless one. For example, csd window can resize without any hacking, and also has shadow rounded.
The task is taked over by ourself. Implement a KDecoration2 plugin, and use it to handle the different window (note that some window will not handled in plugin, such as frameless window, and gtk csd window, it handled by kwin itself).
If you want to learn more about kwin, the [**offical document**](https://community.kde.org/KWin) is recommend.
## Build and Test
- Dependencies (in debian):
- cmake
- extra-cmake-modules
- libkdecorations2-dev
- libqt5x11extras5-dev
- libkf5coreaddons-dev
- libkf5windowsystem-dev
- Build and Install
- clone this project and enter into top directory
- mkdir build && cd build
- cmake ..
- make
- sudo make install
- ./test-csd (if use kwin-style-ukui)
- Test
- open KDE System Settings (systemsettings5)
- Apperance > Application Style > Window Decoration
- choose UKUI as decoration and apply
- run a demo, which windows' motif hints is different.
## ScreenShot
![pictrue1](screenshots/kwin-style-ukui-deco-with-different-motif-hints.png)
![pictrue2](screenshots/csd-window.png)
![pictrue3](screenshots/window-screenshot-of-csd-window.png)
## TODO
- Basic decoration rendering. Not only be a demo.
- Support unity border radius protocol, and handle the rounded-corner window's shadow.
- Register an atom to _NET_SUPPORTED. Let window know if kwin-ukui-style used.
- An implement in wayland?

View File

@ -0,0 +1,190 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#include "button.h"
#include <QPainter>
#include <KDecoration2/DecoratedClient>
#include "xatom-helper.h"
#include <QFile>
using namespace UKUI;
Button *Button::create(KDecoration2::DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent)
{
auto d = static_cast<UKUI::Decoration *>(decoration);
auto b = new Button(type, d, parent);
switch (type) {
case KDecoration2::DecorationButtonType::Menu:
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::iconChanged, b, [b]() {b->update();});
break;
case KDecoration2::DecorationButtonType::OnAllDesktops:
b->setVisible(false);
break;
case KDecoration2::DecorationButtonType::Minimize:
{
bool bRet = XAtomHelper::getInstance()->checkButtonAvailable(d->client().data()->windowId(), KDecoration2::DecorationButtonType::Minimize);
if(false == bRet)
{
b->setVisible(false);
}
//QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::minimizeableChanged, b, &Button::setVisible );
break;
}
case KDecoration2::DecorationButtonType::Maximize:
{
bool bRet = XAtomHelper::getInstance()->checkButtonAvailable(d->client().data()->windowId(), KDecoration2::DecorationButtonType::Maximize);
if(false == bRet)
{
b->setVisible(false);
}
//QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::maximizeableChanged, b, &Button::setVisible );
break;
}
case KDecoration2::DecorationButtonType::Close:
{
bool bRet = XAtomHelper::getInstance()->checkButtonAvailable(d->client().data()->windowId(), KDecoration2::DecorationButtonType::Close);
if(false == bRet)
{
b->setVisible(false);
}
break;
}
case KDecoration2::DecorationButtonType::ContextHelp:
b->setVisible(false);
default:
break;
}
return b;
}
Button::Button(QObject *parent, const QVariantList &args) : KDecoration2::DecorationButton(args.at(0).value<KDecoration2::DecorationButtonType>(), args.at(1).value<Decoration*>(), parent)
{
}
Button::Button(KDecoration2::DecorationButtonType type, UKUI::Decoration *decoration, QObject *parent) : KDecoration2::DecorationButton(type, decoration, parent)
{
}
Button::~Button()
{
}
void Button::paint(QPainter *painter, const QRect &repaintRegion)
{
if(false == isVisible())
{
return;
}
if(!decoration())
{
return;
}
auto d = qobject_cast<Decoration*>(decoration());
//menu button
if(type() == KDecoration2::DecorationButtonType::Menu)
{
const QRectF iconRect(geometry().topLeft(), geometry().size().toSize());
decoration()->client().data()->icon().paint(painter, iconRect.toRect());
return;
}
QString strPath;
QString strIconTheme;
QString strIconType;
QString strIconState;
QString strIconStyle;
// handle icon theme
UKUI::ThemeProject themProject = d->getThemeProject();
switch(themProject) {
case UKUI::THEME_PROJECT_UKUI_BASE:
strIconTheme = "ukui-base";
break;
case UKUI::THEME_PROJECT_LAIKA:
strIconTheme = "laika";
break;
case UKUI::THEME_PROJECT_MAVIS:
strIconTheme = "mavis";
break;
default:
break;
}
// handle icon state
if (isPressed()) {
strIconState = "clicked";
} else if (isHovered()) {
strIconState = "hover";
} else {
strIconState = "common";
}
// handle icon style
if (0 == d->themeId()) {
strIconStyle = "black";
} else {
strIconStyle = "white";
}
// handle icon type
switch (type()) {
case KDecoration2::DecorationButtonType::Minimize:
strIconType = "minimize";
break;
case KDecoration2::DecorationButtonType::Maximize:
strIconType = isChecked() ? "restore" : "maximize";
break;
case KDecoration2::DecorationButtonType::Close:
strIconType = "close";
break;
default:
return;
break;
}
// construct icon path
strPath = QStringLiteral(":/icon/%1/%2-%3-%4.svg").arg(strIconTheme).arg(strIconState).arg(strIconType).arg(strIconStyle);
QFile ddd(strPath);
if (!ddd.exists())
strPath = QStringLiteral(":/icon/%1/%2-%3.svg").arg(strIconTheme).arg(strIconState).arg(strIconType);
const QRectF iconRect(geometry().topLeft(), geometry().size().toSize());
QIcon icon(strPath);
icon.paint(painter, iconRect.toRect());
return;
}

View File

@ -0,0 +1,50 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#ifndef BUTTON_H
#define BUTTON_H
#include <QObject>
#include "ukui-decoration.h"
#include <KDecoration2/DecorationButton>
namespace UKUI {
class Button : public KDecoration2::DecorationButton
{
Q_OBJECT
public:
explicit Button(QObject *parent = nullptr, const QVariantList &args = QVariantList());
explicit Button(KDecoration2::DecorationButtonType type, UKUI::Decoration *decoration, QObject *parent);
~Button();
//* button creation
static Button *create(KDecoration2::DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent);
//* render
void paint(QPainter *painter, const QRect &repaintRegion) override;
};
}
#endif // BUTTON_H

View File

@ -0,0 +1,45 @@
<RCC>
<qresource prefix="/">
<file>icon/ukui-base/clicked-close.svg</file>
<file>icon/ukui-base/common-close-black.svg</file>
<file>icon/ukui-base/common-close-white.svg</file>
<file>icon/ukui-base/common-maximize-black.svg</file>
<file>icon/ukui-base/common-maximize-white.svg</file>
<file>icon/ukui-base/common-minimize-black.svg</file>
<file>icon/ukui-base/common-minimize-white.svg</file>
<file>icon/ukui-base/common-restore-black.svg</file>
<file>icon/ukui-base/common-restore-white.svg</file>
<file>icon/ukui-base/hover-close.svg</file>
<file>icon/ukui-base/clicked-maximize-black.svg</file>
<file>icon/ukui-base/clicked-maximize-white.svg</file>
<file>icon/ukui-base/clicked-minimize-black.svg</file>
<file>icon/ukui-base/clicked-minimize-white.svg</file>
<file>icon/ukui-base/clicked-restore-black.svg</file>
<file>icon/ukui-base/clicked-restore-white.svg</file>
<file>icon/ukui-base/hover-maximize-black.svg</file>
<file>icon/ukui-base/hover-maximize-white.svg</file>
<file>icon/ukui-base/hover-minimize-black.svg</file>
<file>icon/ukui-base/hover-minimize-white.svg</file>
<file>icon/ukui-base/hover-restore-black.svg</file>
<file>icon/ukui-base/hover-restore-white.svg</file>
</qresource>
<qresource prefix="/">
<file>icon/laika/clicked-close.svg</file>
<file>icon/laika/clicked-maximize.svg</file>
<file>icon/laika/clicked-minimize.svg</file>
<file>icon/laika/clicked-restore.svg</file>
<file>icon/laika/common-close-black.svg</file>
<file>icon/laika/common-close-white.svg</file>
<file>icon/laika/common-maximize-black.svg</file>
<file>icon/laika/common-maximize-white.svg</file>
<file>icon/laika/common-minimize-black.svg</file>
<file>icon/laika/common-minimize-white.svg</file>
<file>icon/laika/common-restore-black.svg</file>
<file>icon/laika/common-restore-white.svg</file>
<file>icon/laika/hover-close.svg</file>
<file>icon/laika/hover-maximize.svg</file>
<file>icon/laika/hover-minimize.svg</file>
<file>icon/laika/hover-restore.svg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="clicked-close-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.982793 0 0 0 0 0.314986 0 0 0 0 0.314986 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#FB5050" fill-opacity=".2" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#clicked-close-a)">
<g transform="translate(16 16)">
<rect width="16" height="16" fill="#D8D8D8" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="rotate(45 8 8)"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="scale(-1 1) rotate(45 0 -11.314)"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="clicked-maximize-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.183994 0 0 0 0 0.700484 0 0 0 0 0.908203 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#2FB3E8" fill-opacity=".2" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#clicked-maximize-a)">
<g transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="clicked-minimize-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.183994 0 0 0 0 0.700484 0 0 0 0 0.908203 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#2FB3E8" fill-opacity=".2" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#clicked-minimize-a)">
<g transform="translate(16 16)">
<rect width="15" height="15" x=".5" y=".5" fill="#D8D8D8" stroke="#979797" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="clicked-restore-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.183994 0 0 0 0 0.700484 0 0 0 0 0.908203 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#2FB3E8" fill-opacity=".2" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#clicked-restore-a)">
<g transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
<path stroke="#595959" stroke-width="2" d="M12.6077662,8.00090473 C13.0912808,8.00652803 13.3016993,8.04008746 13.5052468,8.1489459 C13.9720079,8.72091715 14,8.95556368 14,9.5638852 L14,9.5638852 L14,11 C14,11.8284271 13.6642136,12.5784271 13.1213203,13.1213203 C12.5784271,13.6642136 11.8284271,14 11,14 L11,14 L9.5638852,14 C8.95556368,14 8.72091715,13.9720079 8.49475316,13.8510541 C8.02799208,13.2790829 8,13.0444363 8,12.4361148 L8.00090473,9.3922338 C8.00652803,8.9087192 8.04008746,8.69830075 8.1489459,8.49475316 C8.72091715,8.02799208 8.95556368,8 9.5638852,8 Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
<g fill="none" fill-rule="evenodd" transform="translate(16 16)">
<rect width="16" height="16" fill="#D8D8D8" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="rotate(45 8 8)"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="scale(-1 1) rotate(45 0 -11.314)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 720 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="common-close-white-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd" filter="url(#common-close-white-a)">
<g transform="translate(16 16)">
<rect width="16" height="16" fill="#D8D8D8" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="rotate(45 8 8)"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="scale(-1 1) rotate(45 0 -11.314)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 949 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<g fill="none" fill-rule="evenodd" transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="common-maximize-white-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd" filter="url(#common-maximize-white-a)">
<g transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<g fill="none" fill-rule="evenodd" transform="translate(16 16)">
<rect width="15" height="15" x=".5" y=".5" fill="#D8D8D8" stroke="#979797" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="common-minimize-white-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd" filter="url(#common-minimize-white-a)">
<g transform="translate(16 16)">
<rect width="15" height="15" x=".5" y=".5" fill="#D8D8D8" stroke="#979797" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 705 B

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<g fill="none" fill-rule="evenodd" transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
<path stroke="#595959" stroke-width="2" d="M12.6077662,8.00090473 C13.0912808,8.00652803 13.3016993,8.04008746 13.5052468,8.1489459 C13.9720079,8.72091715 14,8.95556368 14,9.5638852 L14,9.5638852 L14,11 C14,11.8284271 13.6642136,12.5784271 13.1213203,13.1213203 C12.5784271,13.6642136 11.8284271,14 11,14 L11,14 L9.5638852,14 C8.95556368,14 8.72091715,13.9720079 8.49475316,13.8510541 C8.02799208,13.2790829 8,13.0444363 8,12.4361148 L8.00090473,9.3922338 C8.00652803,8.9087192 8.04008746,8.69830075 8.1489459,8.49475316 C8.72091715,8.02799208 8.95556368,8 9.5638852,8 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="common-restore-white-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd" filter="url(#common-restore-white-a)">
<g transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
<path stroke="#595959" stroke-width="2" d="M12.6077662,8.00090473 C13.0912808,8.00652803 13.3016993,8.04008746 13.5052468,8.1489459 C13.9720079,8.72091715 14,8.95556368 14,9.5638852 L14,9.5638852 L14,11 C14,11.8284271 13.6642136,12.5784271 13.1213203,13.1213203 C12.5784271,13.6642136 11.8284271,14 11,14 L11,14 L9.5638852,14 C8.95556368,14 8.72091715,13.9720079 8.49475316,13.8510541 C8.02799208,13.2790829 8,13.0444363 8,12.4361148 L8.00090473,9.3922338 C8.00652803,8.9087192 8.04008746,8.69830075 8.1489459,8.49475316 C8.72091715,8.02799208 8.95556368,8 9.5638852,8 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="hover-close-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.982793 0 0 0 0 0.314986 0 0 0 0 0.314986 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#FB5050" fill-opacity=".1" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#hover-close-a)">
<g transform="translate(16 16)">
<rect width="16" height="16" fill="#D8D8D8" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="rotate(45 8 8)"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z" transform="scale(-1 1) rotate(45 0 -11.314)"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="hover-maximize-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.183994 0 0 0 0 0.700484 0 0 0 0 0.908203 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#2FB3E8" fill-opacity=".1" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#hover-maximize-a)">
<g transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="hover-minimize-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.183994 0 0 0 0 0.700484 0 0 0 0 0.908203 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#2FB3E8" fill-opacity=".1" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#hover-minimize-a)">
<g transform="translate(16 16)">
<rect width="15" height="15" x=".5" y=".5" fill="#D8D8D8" stroke="#979797" opacity="0" rx="2"/>
<path fill="#6F6F6F" d="M14.5,9 L1.5,9 C1.22385763,9 1,8.77614237 1,8.5 L1,7.5 C1,7.22385763 1.22385763,7 1.5,7 L14.5,7 C14.7761424,7 15,7.22385763 15,7.5 L15,8.5 C15,8.77614237 14.7761424,9 14.5,9 Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<defs>
<filter id="hover-restore-a">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.183994 0 0 0 0 0.700484 0 0 0 0 0.908203 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<path fill="#2FB3E8" fill-opacity=".1" d="M10.2555408,-4.14336123e-16 L37.7444592,4.14336123e-16 C41.3105342,-2.4074122e-16 42.6036791,0.371302445 43.9073828,1.06853082 C45.2110865,1.76575919 46.2342408,2.78891348 46.9314692,4.09261719 C47.6286976,5.39632089 48,6.68946584 48,10.2555408 L48,37.7444592 C48,41.3105342 47.6286976,42.6036791 46.9314692,43.9073828 C46.2342408,45.2110865 45.2110865,46.2342408 43.9073828,46.9314692 C42.6036791,47.6286976 41.3105342,48 37.7444592,48 L10.2555408,48 C6.68946584,48 5.39632089,47.6286976 4.09261719,46.9314692 C2.78891348,46.2342408 1.76575919,45.2110865 1.06853082,43.9073828 C0.371302445,42.6036791 1.60494146e-16,41.3105342 -2.76224082e-16,37.7444592 L2.76224082e-16,10.2555408 C-1.60494146e-16,6.68946584 0.371302445,5.39632089 1.06853082,4.09261719 C1.76575919,2.78891348 2.78891348,1.76575919 4.09261719,1.06853082 C5.39632089,0.371302445 6.68946584,2.4074122e-16 10.2555408,-4.14336123e-16 Z"/>
<g filter="url(#hover-restore-a)">
<g transform="matrix(-1 0 0 1 32 16)">
<rect width="16" height="16" fill="#000" fill-rule="nonzero" opacity="0"/>
<path stroke="#595959" stroke-width="2" d="M9.8722296,2 C11.3720699,2 11.9300026,2.12081769 12.4820925,2.41607861 C12.9570983,2.67011445 13.3298856,3.04290172 13.5839214,3.51790745 C13.8791823,4.06999737 14,4.62793014 14,6.1277704 L14,6.1277704 L14,9.8722296 C14,11.3720699 13.8791823,11.9300026 13.5839214,12.4820925 C13.3298856,12.9570983 12.9570983,13.3298856 12.4820925,13.5839214 C11.9300026,13.8791823 11.3720699,14 9.8722296,14 L9.8722296,14 L6.1277704,14 C4.62793014,14 4.06999737,13.8791823 3.51790745,13.5839214 C3.04290172,13.3298856 2.67011445,12.9570983 2.41607861,12.4820925 C2.12081769,11.9300026 2,11.3720699 2,9.8722296 L2,9.8722296 L2,6.1277704 C2,4.62793014 2.12081769,4.06999737 2.41607861,3.51790745 C2.67011445,3.04290172 3.04290172,2.67011445 3.51790745,2.41607861 C4.06999737,2.12081769 4.62793014,2 6.1277704,2 L6.1277704,2 Z"/>
<path stroke="#595959" stroke-width="2" d="M12.6077662,8.00090473 C13.0912808,8.00652803 13.3016993,8.04008746 13.5052468,8.1489459 C13.9720079,8.72091715 14,8.95556368 14,9.5638852 L14,9.5638852 L14,11 C14,11.8284271 13.6642136,12.5784271 13.1213203,13.1213203 C12.5784271,13.6642136 11.8284271,14 11,14 L11,14 L9.5638852,14 C8.95556368,14 8.72091715,13.9720079 8.49475316,13.8510541 C8.02799208,13.2790829 8,13.0444363 8,12.4361148 L8.00090473,9.3922338 C8.00652803,8.9087192 8.04008746,8.69830075 8.1489459,8.49475316 C8.72091715,8.02799208 8.95556368,8 9.5638852,8 Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E44C50;}
.st1{opacity:0.75;}
.st2{fill:#FFFFFF;}
</style>
<g id="点击状态">
<g id="关闭">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<g class="st1">
<path class="st2" d="M10.8,10.1l9.2,9.2c0.2,0.2,0.2,0.5,0,0.7l0,0c-0.2,0.2-0.5,0.2-0.7,0L10,10.9c-0.2-0.2-0.2-0.5,0-0.7l0,0
C10.2,9.9,10.6,9.9,10.8,10.1z"/>
<path class="st2" d="M10.1,19.3l9.2-9.2c0.2-0.2,0.5-0.2,0.7,0l0,0c0.2,0.2,0.2,0.5,0,0.7l-9.2,9.2c-0.2,0.2-0.5,0.2-0.7,0l0,0
C9.9,19.9,9.9,19.5,10.1,19.3z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 971 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.28;enable-background:new ;}
.st1{opacity:0.75;fill:#1F2022;enable-background:new ;}
</style>
<g id="点击状态">
<g id="最大化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,10c0.6,0,1,0.4,1,1v8c0,0.6-0.4,1-1,1h-8c-0.6,0-1-0.4-1-1v-8c0-0.6,0.4-1,1-1H19 M19,9h-8
c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2v-8C21,9.9,20.1,9,19,9z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 863 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.21;fill:#FFFFFF;enable-background:new ;}
.st1{opacity:0.75;fill:#FFFFFF;enable-background:new ;}
</style>
<g id="点击状态">
<g id="最大化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,10c0.6,0,1,0.4,1,1v8c0,0.6-0.4,1-1,1h-8c-0.6,0-1-0.4-1-1v-8c0-0.6,0.4-1,1-1H19 M19,9h-8
c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2v-8C21,9.9,20.1,9,19,9z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 876 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.28;enable-background:new ;}
.st1{opacity:0.75;fill:#1F2022;enable-background:new ;}
</style>
<g id="点击状态">
<g id="最小化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M10.5,15h9c0.3,0,0.5,0.2,0.5,0.5l0,0c0,0.3-0.2,0.5-0.5,0.5h-9c-0.3,0-0.5-0.2-0.5-0.5l0,0
C10,15.2,10.2,15,10.5,15z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 809 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.21;fill:#FFFFFF;enable-background:new ;}
.st1{opacity:0.75;fill:#FFFFFF;enable-background:new ;}
</style>
<g id="点击状态">
<g id="最小化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M10.5,15h9c0.3,0,0.5,0.2,0.5,0.5l0,0c0,0.3-0.2,0.5-0.5,0.5h-9c-0.3,0-0.5-0.2-0.5-0.5l0,0
C10,15.2,10.2,15,10.5,15z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 822 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.28;enable-background:new ;}
.st1{opacity:0.75;fill:#1F2022;enable-background:new ;}
</style>
<g id="点击状态">
<g id="还原">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,8h-6c-1.1,0-2,0.9-2,2v1h-1c-1.1,0-2,0.9-2,2v6c0,1.1,0.9,2,2,2h6c1.1,0,2-0.9,2-2v-1h1c1.1,0,2-0.9,2-2
v-6C21,8.9,20.1,8,19,8z M17,19c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V19z M20,16
c0,0.6-0.4,1-1,1h-1v-4c0-1.1-0.9-2-2-2h-4v-1c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V16z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 997 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.21;fill:#FFFFFF;enable-background:new ;}
.st1{opacity:0.75;fill:#FFFFFF;enable-background:new ;}
</style>
<g id="点击状态">
<g id="还原">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,8h-6c-1.1,0-2,0.9-2,2v1h-1c-1.1,0-2,0.9-2,2v6c0,1.1,0.9,2,2,2h6c1.1,0,2-0.9,2-2v-1h1c1.1,0,2-0.9,2-2
v-6C21,8.9,20.1,8,19,8z M17,19c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V19z M20,16
c0,0.6-0.4,1-1,1h-1v-4c0-1.1-0.9-2-2-2h-4v-1c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V16z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1010 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{opacity:0.75;}.cls-2{fill:#1f2022;}</style></defs><g id="普通状态-黑"><g id="关闭" class="cls-1"><rect class="cls-2" x="8" y="14.6" width="14" height="1" rx="0.5" transform="translate(15.07 -6.19) rotate(45)"/><rect class="cls-2" x="8" y="14.6" width="14" height="1" rx="0.5" transform="translate(-6.28 15.03) rotate(-45)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 425 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{opacity:0.75;}.cls-2{fill:#fff;}</style></defs><g id="普通状态-白"><g id="关闭" class="cls-1"><rect class="cls-2" x="8" y="14.6" width="14" height="1" rx="0.5" transform="translate(15.07 -6.19) rotate(45)"/><rect class="cls-2" x="8" y="14.6" width="14" height="1" rx="0.5" transform="translate(-6.28 15.03) rotate(-45)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 422 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{fill:#1f2022;opacity:0.75;}</style></defs><g id="普通状态-黑"><path id="最大化" class="cls-1" d="M19,10a1,1,0,0,1,1,1v8a1,1,0,0,1-1,1H11a1,1,0,0,1-1-1V11a1,1,0,0,1,1-1h8m0-1H11a2,2,0,0,0-2,2v8a2,2,0,0,0,2,2h8a2,2,0,0,0,2-2V11a2,2,0,0,0-2-2Z"/></g></svg>

After

Width:  |  Height:  |  Size: 341 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{fill:#fff;opacity:0.75;}</style></defs><g id="普通状态-白"><path id="最大化" class="cls-1" d="M19,10a1,1,0,0,1,1,1v8a1,1,0,0,1-1,1H11a1,1,0,0,1-1-1V11a1,1,0,0,1,1-1h8m0-1H11a2,2,0,0,0-2,2v8a2,2,0,0,0,2,2h8a2,2,0,0,0,2-2V11a2,2,0,0,0-2-2Z"/></g></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{fill:#1f2022;opacity:0.75;}</style></defs><g id="普通状态-黑"><rect id="最小化" class="cls-1" x="10" y="15" width="10" height="1" rx="0.5"/></g></svg>

After

Width:  |  Height:  |  Size: 238 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{fill:#fff;opacity:0.75;}</style></defs><g id="普通状态-白"><rect id="最小化" class="cls-1" x="10" y="15" width="10" height="1" rx="0.5"/></g></svg>

After

Width:  |  Height:  |  Size: 235 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{fill:#1f2022;opacity:0.75;}</style></defs><g id="普通状态-黑"><path id="还原" class="cls-1" d="M19,8H13a2,2,0,0,0-2,2v1H10a2,2,0,0,0-2,2v6a2,2,0,0,0,2,2h6a2,2,0,0,0,2-2V18h1a2,2,0,0,0,2-2V10A2,2,0,0,0,19,8ZM17,19a1,1,0,0,1-1,1H10a1,1,0,0,1-1-1V13a1,1,0,0,1,1-1h6a1,1,0,0,1,1,1Zm3-3a1,1,0,0,1-1,1H18V13a2,2,0,0,0-2-2H12V10a1,1,0,0,1,1-1h6a1,1,0,0,1,1,1Z"/></g></svg>

After

Width:  |  Height:  |  Size: 452 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><defs><style>.cls-1{fill:#fff;opacity:0.75;}</style></defs><g id="普通状态-白"><path id="还原" class="cls-1" d="M19,8H13a2,2,0,0,0-2,2v1H10a2,2,0,0,0-2,2v6a2,2,0,0,0,2,2h6a2,2,0,0,0,2-2V18h1a2,2,0,0,0,2-2V10A2,2,0,0,0,19,8ZM17,19a1,1,0,0,1-1,1H10a1,1,0,0,1-1-1V13a1,1,0,0,1,1-1h6a1,1,0,0,1,1,1Zm3-3a1,1,0,0,1-1,1H18V13a2,2,0,0,0-2-2H12V10a1,1,0,0,1,1-1h6a1,1,0,0,1,1,1Z"/></g></svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F86457;}
.st1{opacity:0.75;}
.st2{fill:#FFFFFF;}
</style>
<g id="悬停状态">
<g id="关闭">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<g class="st1">
<path class="st2" d="M10.8,10.1l9.2,9.2c0.2,0.2,0.2,0.5,0,0.7l0,0c-0.2,0.2-0.5,0.2-0.7,0L10,10.9c-0.2-0.2-0.2-0.5,0-0.7l0,0
C10.2,9.9,10.6,9.9,10.8,10.1z"/>
<path class="st2" d="M10.1,19.3l9.2-9.2c0.2-0.2,0.5-0.2,0.7,0l0,0c0.2,0.2,0.2,0.5,0,0.7l-9.2,9.2c-0.2,0.2-0.5,0.2-0.7,0l0,0
C9.9,19.9,9.9,19.5,10.1,19.3z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 971 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.15;enable-background:new ;}
.st1{opacity:0.75;fill:#1F2022;enable-background:new ;}
</style>
<g id="悬停状态">
<g id="最大化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,10c0.6,0,1,0.4,1,1v8c0,0.6-0.4,1-1,1h-8c-0.6,0-1-0.4-1-1v-8c0-0.6,0.4-1,1-1H19 M19,9h-8
c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2v-8C21,9.9,20.1,9,19,9z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 863 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.32;fill:#FFFFFF;enable-background:new ;}
.st1{opacity:0.75;fill:#FFFFFF;enable-background:new ;}
</style>
<g id="悬停状态">
<g id="最大化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,10c0.6,0,1,0.4,1,1v8c0,0.6-0.4,1-1,1h-8c-0.6,0-1-0.4-1-1v-8c0-0.6,0.4-1,1-1H19 M19,9h-8
c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2v-8C21,9.9,20.1,9,19,9z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 876 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.15;enable-background:new ;}
.st1{opacity:0.75;fill:#1F2022;enable-background:new ;}
</style>
<g id="悬停状态">
<g id="最小化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M10.5,15h9c0.3,0,0.5,0.2,0.5,0.5l0,0c0,0.3-0.2,0.5-0.5,0.5h-9c-0.3,0-0.5-0.2-0.5-0.5l0,0
C10,15.2,10.2,15,10.5,15z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 809 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.32;fill:#FFFFFF;enable-background:new ;}
.st1{opacity:0.75;fill:#FFFFFF;enable-background:new ;}
</style>
<g id="悬停状态">
<g id="最小化">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M10.5,15h9c0.3,0,0.5,0.2,0.5,0.5l0,0c0,0.3-0.2,0.5-0.5,0.5h-9c-0.3,0-0.5-0.2-0.5-0.5l0,0
C10,15.2,10.2,15,10.5,15z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 822 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.15;enable-background:new ;}
.st1{opacity:0.75;fill:#1F2022;enable-background:new ;}
</style>
<g id="悬停状态">
<g id="还原">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,8h-6c-1.1,0-2,0.9-2,2v1h-1c-1.1,0-2,0.9-2,2v6c0,1.1,0.9,2,2,2h6c1.1,0,2-0.9,2-2v-1h1c1.1,0,2-0.9,2-2
v-6C21,8.9,20.1,8,19,8z M17,19c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V19z M20,16
c0,0.6-0.4,1-1,1h-1v-4c0-1.1-0.9-2-2-2h-4v-1c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V16z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 997 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.32;fill:#FFFFFF;enable-background:new ;}
.st1{opacity:0.75;fill:#FFFFFF;enable-background:new ;}
</style>
<g id="悬停状态">
<g id="还原">
<path id="底图" class="st0" d="M6,0h18c3.3,0,6,2.7,6,6v18c0,3.3-2.7,6-6,6H6c-3.3,0-6-2.7-6-6V6C0,2.7,2.7,0,6,0z"/>
<path class="st1" d="M19,8h-6c-1.1,0-2,0.9-2,2v1h-1c-1.1,0-2,0.9-2,2v6c0,1.1,0.9,2,2,2h6c1.1,0,2-0.9,2-2v-1h1c1.1,0,2-0.9,2-2
v-6C21,8.9,20.1,8,19,8z M17,19c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V19z M20,16
c0,0.6-0.4,1-1,1h-1v-4c0-1.1-0.9-2-2-2h-4v-1c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V16z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1010 B

View File

@ -0,0 +1,12 @@
{
"KPlugin": {
"Id": "UKUI",
"ServiceTypes": [
"org.kde.kdecoration2"
]
},
"org.kde.kdecoration2": {
"blur": false,
"kcmodule": false
}
}

View File

@ -0,0 +1,316 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#include "shadow-helper.h"
#include <QPainter>
#include <QPainterPath>
#include <QPoint>
#include <QImage>
#include <QDebug>
#define INNERRECT_WIDTH 1
#define BORDERCOLOR_ALPHAF 0.15
extern void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed);
using namespace UKUI;
static ShadowHelper *global_instance = nullptr;
ShadowHelper *ShadowHelper::globalInstance()
{
if (!global_instance)
global_instance = new ShadowHelper;
return global_instance;
}
void ShadowHelper::releaseShadows()
{
for (auto shadow : m_shadowsCache) {
shadow.clear();
}
m_shadowsCache.clear();
}
QSharedPointer<KDecoration2::DecorationShadow> ShadowHelper::getShadow(const ShadowIndex &index)
{
int borderRadiusTopLeft = index.topLeft();
int borderRadiusTopRight = index.topRight();
int borderRadiusBottomLeft = index.bottomLeft();
int borderRadiusBottomRight = index.bottomRight();
qreal darkness = index.darkness();
qreal shadow_border = index.borderWidth();
if (borderRadiusTopLeft < 1) {
borderRadiusTopLeft = 1;
}
if (borderRadiusTopRight < 1) {
borderRadiusTopRight = 1;
}
if (borderRadiusBottomLeft < 1) {
borderRadiusBottomLeft = 1;
}
if (borderRadiusBottomRight < 1) {
borderRadiusBottomRight = 1;
}
auto shadow = m_shadowsCache.value(index);
if (!shadow.isNull())
{
shadow.clear();
this->releaseShadows();
m_shadowsCache.remove(index);
}
shadow = QSharedPointer<KDecoration2::DecorationShadow>::create();
auto pix = this->getShadowPixmap(index.color(), shadow_border, darkness, borderRadiusTopLeft, borderRadiusTopRight, borderRadiusBottomLeft, borderRadiusBottomRight);
auto img = pix.toImage();
shadow->setShadow(img);
int maxTopRadius = qMax(borderRadiusTopLeft, borderRadiusTopRight);
int maxBottomRadius = qMax(borderRadiusBottomLeft, borderRadiusBottomRight);
int maxRadius = qMax(maxTopRadius, maxBottomRadius);
maxRadius = qMax(12, maxRadius);
QRect innerRect = QRect(shadow_border + maxRadius, shadow_border + maxRadius, INNERRECT_WIDTH, INNERRECT_WIDTH);
shadow->setInnerShadowRect(innerRect);
shadow->setPadding(QMargins(shadow_border, shadow_border, shadow_border, shadow_border));
m_shadowsCache.insert(index, shadow);
return shadow;
}
ShadowHelper::ShadowHelper()
{
}
QPixmap ShadowHelper::getShadowPixmap(const QColor &color, int shadow_border, qreal darkness, int borderRadiusTopLeft, int borderRadiusTopRight, int borderRadiusBottomLeft, int borderRadiusBottomRight)
{
int maxTopRadius = qMax(borderRadiusTopLeft, borderRadiusTopRight);
int maxBottomRadius = qMax(borderRadiusBottomLeft, borderRadiusBottomRight);
int maxRadius = qMax(maxTopRadius, maxBottomRadius);
maxRadius = qMax(12, maxRadius);
QPixmap pix(QSize(2 * maxRadius + 2 * shadow_border + INNERRECT_WIDTH, 2 * maxRadius + 2 * shadow_border + INNERRECT_WIDTH));
pix.fill(Qt::transparent);
int squareWidth = 2 * maxRadius + INNERRECT_WIDTH;
QPainterPath windowRelativePath;
windowRelativePath.setFillRule(Qt::WindingFill);
QPoint currentPos;
// move to top left arc start point
windowRelativePath.moveTo(borderRadiusTopLeft, 0);
// top left arc
auto topLeftBorderRadiusRect = QRect(0, 0, 2 * borderRadiusTopLeft, 2 * borderRadiusTopLeft);
windowRelativePath.arcTo(topLeftBorderRadiusRect, 90, 90);
// move to bottom left arc start point
currentPos = QPoint(0, maxRadius + INNERRECT_WIDTH + maxRadius - borderRadiusBottomLeft);
//windowRelativePath.moveTo(currentPos);
// bottom left arc
auto bottomLeftRect = QRect(0, currentPos.y() - borderRadiusBottomLeft, 2 * borderRadiusBottomLeft, 2 * borderRadiusBottomLeft);
windowRelativePath.arcTo(bottomLeftRect, 180, 90);
// move to bottom right arc start point
currentPos = QPoint(2 * maxRadius + INNERRECT_WIDTH - borderRadiusBottomRight, 2 * maxRadius + INNERRECT_WIDTH);
//windowRelativePath.moveTo(currentPos);
// bottom right arc
auto bottomRightRect = QRect(currentPos.x() - borderRadiusBottomRight, currentPos.y() - 2 * borderRadiusBottomRight, 2 * borderRadiusBottomRight, 2 * borderRadiusBottomRight);
windowRelativePath.arcTo(bottomRightRect, 270, 90);
// move to top right arc start point
currentPos = QPoint(2 * maxRadius + INNERRECT_WIDTH, borderRadiusTopRight);
//windowRelativePath.moveTo(currentPos);
// top right arc
auto topRightRect = QRect(squareWidth - 2 * borderRadiusTopRight, 0, 2 * borderRadiusTopRight, 2 * borderRadiusTopRight);
windowRelativePath.arcTo(topRightRect, 0, 90);
QPainter painter(&pix);
painter.save();
painter.translate(shadow_border, shadow_border);
// painter.fillPath(windowRelativePath, color);
painter.fillPath(windowRelativePath, QColor(0, 0, 0));
painter.restore();
QImage rawImg = pix.toImage();
qt_blurImage(rawImg, shadow_border, true, true);
QPixmap target = QPixmap::fromImage(rawImg);
QPainter painter2(&target);
painter2.save();
painter2.setRenderHint(QPainter::Antialiasing);
painter2.translate(shadow_border, shadow_border);
painter2.setCompositionMode(QPainter::CompositionMode_Clear);
painter2.fillPath(windowRelativePath, Qt::transparent);
painter2.restore();
painter2.end();
// handle darkness
QImage newImg = target.toImage();
for (int x = 0; x < newImg.width(); x++) {
for (int y = 0; y < newImg.height(); y++) {
auto color = newImg.pixelColor(x, y);
if (color.alpha() == 0)
continue;
color.setAlphaF(darkness * color.alphaF());
newImg.setPixelColor(x, y, color);
}
}
QPixmap darkerTarget = QPixmap::fromImage(newImg);
painter2.begin(&darkerTarget);
auto borderPath = caculateRelativePainterPath(borderRadiusTopLeft + 0.5, borderRadiusTopRight + 0.5, borderRadiusBottomLeft + 0.5, borderRadiusBottomRight + 0.5);
painter2.setCompositionMode(QPainter::CompositionMode_DestinationOver);
painter2.setRenderHint(QPainter::HighQualityAntialiasing);
QColor borderColor = QColor(255 - color.red(), 255 - color.green(), 255 - color.blue());
borderColor.setAlphaF(BORDERCOLOR_ALPHAF);
painter2.setPen(QPen(borderColor, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter2.setBrush(Qt::NoBrush);
painter2.translate(shadow_border, shadow_border);
painter2.translate(-0.5, -0.5);
painter2.drawPath(borderPath);
//封口
borderPath.closeSubpath();
painter2.drawPath(borderPath);
return darkerTarget;
}
QPainterPath ShadowHelper::caculateRelativePainterPath(qreal borderRadiusTopLeft, qreal borderRadiusTopRight, qreal borderRadiusBottomLeft, qreal borderRadiusBottomRight)
{
qreal maxTopRadius = qMax(borderRadiusTopLeft, borderRadiusTopRight);
qreal maxBottomRadius = qMax(borderRadiusBottomLeft, borderRadiusBottomRight);
qreal maxRadius = qMax(maxTopRadius, maxBottomRadius);
maxRadius = qMax(12.0, maxRadius);
qreal squareWidth = 2 * maxRadius + INNERRECT_WIDTH;
QPainterPath windowRelativePath;
windowRelativePath.setFillRule(Qt::WindingFill);
QPointF currentPos;
// move to top left arc start point
windowRelativePath.moveTo(borderRadiusTopLeft, 0);
// top left arc
auto topLeftBorderRadiusRect = QRectF(0, 0, 2 * borderRadiusTopLeft, 2 * borderRadiusTopLeft);
windowRelativePath.arcTo(topLeftBorderRadiusRect, 90, 90);
// move to bottom left arc start point
currentPos = QPointF(0, maxRadius + INNERRECT_WIDTH + maxRadius - borderRadiusBottomLeft);
//windowRelativePath.moveTo(currentPos);
// bottom left arc
auto bottomLeftRect = QRectF(0, currentPos.y() - borderRadiusBottomLeft, 2 * borderRadiusBottomLeft, 2 * borderRadiusBottomLeft);
windowRelativePath.arcTo(bottomLeftRect, 180, 90);
// move to bottom right arc start point
currentPos = QPointF(2 * maxRadius + INNERRECT_WIDTH - borderRadiusBottomRight, 2 * maxRadius + INNERRECT_WIDTH);
//windowRelativePath.moveTo(currentPos);
// bottom right arc
auto bottomRightRect = QRectF(currentPos.x() - borderRadiusBottomRight, currentPos.y() - 2 * borderRadiusBottomRight, 2 * borderRadiusBottomRight, 2 * borderRadiusBottomRight);
windowRelativePath.arcTo(bottomRightRect, 270, 90);
// move to top right arc start point
currentPos = QPointF(2 * maxRadius + INNERRECT_WIDTH, borderRadiusTopRight);
//windowRelativePath.moveTo(currentPos);
// top right arc
auto topRightRect = QRectF(squareWidth - 2 * borderRadiusTopRight, 0, 2 * borderRadiusTopRight, 2 * borderRadiusTopRight);
windowRelativePath.arcTo(topRightRect, 0, 90);
return windowRelativePath;
}
ShadowIndex::ShadowIndex(const QColor &color, int topLeft, int topRight, int bottomLeft, int bottomRight, qreal darkness, int borderWidth)
{
m_color = color;
m_topLeft = topLeft;
m_topRight = topRight;
m_bottomLeft = bottomLeft;
m_bottomRight = bottomRight;
m_darkness = darkness;
m_borderWidth = borderWidth;
}
bool ShadowIndex::operator ==(const ShadowIndex &index)
{
if (m_color != index.m_color) {
return false;
}
if (m_topLeft != index.m_topLeft) {
return false;
}
if (m_topRight != index.m_topRight) {
return false;
}
if (m_bottomLeft != index.m_bottomLeft) {
return false;
}
if (m_bottomRight != index.m_bottomRight) {
return false;
}
if (m_darkness != index.m_darkness) {
return false;
}
if (m_borderWidth != index.m_borderWidth) {
return false;
}
return true;
}
bool ShadowIndex::operator <(const ShadowIndex &index) const
{
return m_topLeft < index.m_topLeft;
}
QColor ShadowIndex::color() const
{
return m_color;
}
int ShadowIndex::topLeft() const
{
return m_topLeft;
}
int ShadowIndex::topRight() const
{
return m_topRight;
}
int ShadowIndex::bottomLeft() const
{
return m_bottomLeft;
}
int ShadowIndex::bottomRight() const
{
return m_bottomRight;
}
qreal ShadowIndex::darkness() const
{
return m_darkness;
}
int ShadowIndex::borderWidth() const
{
return m_borderWidth;
}

View File

@ -0,0 +1,97 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#ifndef SHADOWHELPER_H
#define SHADOWHELPER_H
#include <QMap>
#include <QSharedPointer>
#include <QPixmap>
#include <KDecoration2/DecorationShadow>
namespace UKUI {
class Decoration;
class ShadowIndex {
public:
ShadowIndex(const QColor &color, int topLeft, int topRight, int bottomLeft, int bottomRight, qreal darkness, int borderWidth);
bool operator ==(const ShadowIndex &index);
bool operator <(const ShadowIndex &index) const;
QColor color() const;
int topLeft() const;
int topRight() const;
int bottomLeft() const;
int bottomRight() const;
qreal darkness() const;
int borderWidth() const;
private:
QColor m_color;
int m_topLeft = -1;
int m_topRight = -1;
int m_bottomLeft = -1;
int m_bottomRight = -1;
qreal m_darkness = -1;
int m_borderWidth = -1;
};
class ShadowHelper
{
friend class Decoration;
public:
enum State {
Active,
Inactive,
};
static ShadowHelper *globalInstance();
void releaseShadows();
QSharedPointer<KDecoration2::DecorationShadow> getShadow(const ShadowIndex &index);
private:
ShadowHelper();
QPixmap getShadowPixmap(const QColor &color,
int shadow_border,
qreal darkness,
int borderRadiusTopLeft = 0,
int borderRadiusTopRight = 0,
int borderRadiusBottomLeft = 0,
int borderRadiusBottomRight = 0);
QMap<QList<int>, QSharedPointer<KDecoration2::DecorationShadow>> m_inactiveShadowsCache;
QMap<QList<int>, QSharedPointer<KDecoration2::DecorationShadow>> m_activeShadowsCache;
QMap<ShadowIndex, QSharedPointer<KDecoration2::DecorationShadow>> m_shadowsCache;
QPainterPath caculateRelativePainterPath(qreal borderRadiusTopLeft = 0,
qreal borderRadiusTopRight = 0,
qreal borderRadiusBottomLeft = 0,
qreal borderRadiusBottomRight = 0);
};
}
#endif // SHADOWHELPER_H

View File

@ -0,0 +1,106 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#include <QApplication>
#include <QMainWindow>
#include <QDebug>
#include "xatom-helper.h"
#include <QGraphicsEffect>
#include <QWidget>
#include <QPainter>
#include <QPainterPath>
class WindowEffect : public QGraphicsEffect
{
public:
explicit WindowEffect(QWidget *parent = nullptr);
void draw(QPainter *painter) override;
};
WindowEffect::WindowEffect(QWidget *parent) : QGraphicsEffect(parent)
{
parent->setAttribute(Qt::WA_TranslucentBackground);
//parent->setWindowFlag(Qt::FramelessWindowHint);
parent->setGraphicsEffect(this);
}
void WindowEffect::draw(QPainter *painter)
{
QPoint offset;
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
if (sourceIsPixmap()) {
// No point in drawing in device coordinates (pixmap will be scaled anyways).
const QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset);
painter->translate(offset);
QPainterPath path;
path.addRoundedRect(pixmap.rect(), 0, 0);
painter->setClipPath(path);
painter->fillRect(pixmap.rect(), qApp->palette().window());
painter->drawPixmap(QPoint(), pixmap);
} else {
// Draw pixmap in device coordinates to avoid pixmap scaling;
const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset);
painter->setWorldTransform(QTransform());
painter->translate(offset);
QPainterPath path;
path.addRoundedRect(pixmap.rect(), 0, 0);
painter->setClipPath(path);
painter->fillRect(pixmap.rect(), qApp->palette().window());
painter->drawPixmap(QPoint(), pixmap);
}
painter->restore();
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QMainWindow w;
w.setProperty("doNotBlur", true);
WindowEffect e(&w);
MotifWmHints hints;
hints.flags = MWM_HINTS_FUNCTIONS|MWM_HINTS_DECORATIONS;
hints.functions = MWM_FUNC_ALL;
hints.decorations = MWM_DECOR_BORDER;
XAtomHelper::getInstance()->setWindowMotifHint(w.winId(), hints);
UnityCorners corners;
corners.topLeft = 12;
corners.topRight = 12;
corners.bottomLeft = 12;
corners.bottomRight = 12;
// XAtomHelper::getInstance()->setWindowBorderRadius(w.winId(), corners);
// auto result = XAtomHelper::getInstance()->getWindowBorderRadius(w.winId());
// qDebug()<<result.topLeft<<result.topRight<<result.bottomLeft<<result.bottomRight;
XAtomHelper::getInstance()->setUKUIDecoraiontHint(w.winId(), true);
//w.setWindowFlag(Qt::FramelessWindowHint);
w.show();
return a.exec();
}

View File

@ -0,0 +1,549 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#include <kwineffects.h>
#include "ukui-decoration.h"
#include "button.h"
#include <QtDBus> //必须放xatom-helper.h前面
#include "shadow-helper.h"
#include "xatom-helper.h"
#include <QPainter>
#include <QToolTip>
#include <KPluginFactory>
#include <KDecoration2/DecoratedClient>
#include <KDecoration2/DecorationSettings>
#include <KDecoration2/DecorationButtonGroup>
#include <KConfigGroup>
#include <KSharedConfig>
#define FONT_SIZE 11 //字体大小
#define CUSOR_BORDER 13 //边框伸展光标范围
#define SHADOW_BORDER 30 //阴影边框大小30小边框、100中边框、200大边框
#define ACTIVE_DARKNESS 0.5 //阴影颜色深度1.0深、1.5很深、2.0超深
#define RADIUS 12
#define SHADOWCOLOR_ALPHAF 0.16
K_PLUGIN_FACTORY_WITH_JSON(
UKUIDecotionFactory,
"kwin-style-ukui.json",
registerPlugin<UKUI::Decoration>();
)
using namespace UKUI;
Decoration::Decoration(QObject *parent, const QVariantList &args)
: KDecoration2::Decoration(parent, args)
{
int nDpi = 96;
m_themeId = 0;
m_themeProject = THEME_PROJECT_UKUI_BASE;
m_bTabletMode = false;
m_Font.setPointSize(FONT_SIZE); //setPointSize可以根据dpi自动调整所以m_nFont不需要乘缩放系数而setPixelSize会写死
if (false == args.isEmpty())
{
const auto map = args.first().toMap();
auto it = map.constFind(QStringLiteral("dpi"));
if (it != map.constEnd()) {
nDpi = it.value().toInt();
}
it = map.constFind(QStringLiteral("themeId"));
if (it != map.constEnd()) {
m_themeId = it.value().toBool();
}
it = map.constFind(QStringLiteral("themeProject"));
if (it != map.constEnd()) {
m_themeProject = static_cast<ThemeProject>(it.value().toInt());
}
it = map.constFind(QStringLiteral("systemFontSize"));
if (it != map.constEnd()) {
m_Font.setPointSize(it.value().toInt());
}
it = map.constFind(QStringLiteral("systemFont"));
if (it != map.constEnd()) {
m_Font.setFamily(it.value().toString());
}
it = map.constFind(QStringLiteral("tabletMode"));
if (it != map.constEnd()) {
m_bTabletMode = it.value().toBool();
}
}
int nScaler = qRound(nDpi / 96.0f);
m_borderLeft = nScaler * 0;
m_borderTop = nScaler * 38;
m_borderRight = nScaler * 0;
m_borderBottom = nScaler * 0;
m_buttonWidth = nScaler * 30;
m_buttonHeight = nScaler * 30;
m_leftButtonWidth = nScaler * 24;
m_leftButtonHeight = nScaler * 24;
m_ButtonMarginTop = nScaler * 4;
m_buttonSpacing = nScaler * 4;
m_leftButtons = nullptr;
m_rightButtons = nullptr;
}
void Decoration::init()
{
m_shadowRadius = RADIUS;
//从读取配置文件的方式判断kwin是否能开启毛玻璃效果如果不能则阴影圆角为0.
auto config = KSharedConfig::openConfig("kwinrc");
auto group = KConfigGroup(config, "Compositing");
if (group.readEntry("Backend") == "XRender" || group.readEntry("OpenGLIsUnsafe") == "true") {
m_shadowRadius = 0;
}
if (XAtomHelper::getInstance()->isFrameLessWindow(this->client().data()->windowId())) {
m_shadowRadius = 0;
}
XAtomHelper::getInstance()->setUKUIDecoraiontHint(client().data()->windowId(), true);
QDBusConnection::sessionBus().connect(QString(),
QStringLiteral("/KGlobalSettings"),
QStringLiteral("org.kde.KGlobalSettings"),
QStringLiteral("slotThemeChange"),
this, SLOT(themeUpdate(int)));
themeUpdate(m_themeId);
bool isDecoBorderOnly = XAtomHelper::getInstance()->isWindowDecorateBorderOnly(client().data()->windowId()); //是否是仅修饰边框
if (!isDecoBorderOnly) {
//标题栏dbus监听PC-平板模式切换
QDBusConnection::sessionBus().connect("com.kylin.statusmanager.interface",
"/",
"com.kylin.statusmanager.interface",
"mode_change_signal",
this,
SLOT(slotSwitchTablet(bool)));
themeUpdate(m_themeId);
calculateBorders();
//button
m_leftButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Left, this, &UKUI::Button::create);
m_leftButtons->setSpacing(m_buttonSpacing);
m_nleftButtonCout = 0;
for (const QPointer<KDecoration2::DecorationButton>& button : m_leftButtons->buttons())
{
button.data()->setGeometry(QRectF(QPointF(0, 0), QSizeF(m_leftButtonWidth, m_leftButtonHeight)));
if(false == button.data()->isVisible())
{
continue;
}
if(KDecoration2::DecorationButtonType::Menu == button.data()->type())
{
m_nleftButtonCout++;
}
}
m_rightButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Right, this, &UKUI::Button::create);
m_rightButtons->setSpacing(m_buttonSpacing);
calculateRightButtonCout();
connect(settings().data(), &KDecoration2::DecorationSettings::decorationButtonsRightChanged, this, &UKUI::Decoration::updateButtonsGeometry);
connect(settings().data(), &KDecoration2::DecorationSettings::fontChanged, this, &UKUI::Decoration::updatefont);
connect(client().data(), &KDecoration2::DecoratedClient::sizeChanged, this, &UKUI::Decoration::updateButtonsGeometry);
connect(client().data(), &KDecoration2::DecoratedClient::paletteChanged, this, static_cast<void (Decoration::*)()>(&Decoration::update));
connect(client().data(), &KDecoration2::DecoratedClient::activeChanged, this, static_cast<void (Decoration::*)()>(&Decoration::update));
connect(client().data(), &KDecoration2::DecoratedClient::maximizeableChanged, this, &Decoration::calculateRightButtonCout); //安装兼容应用全屏后还原,可最大化按钮有改变
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::calculateBorders);
connect(client().data(), &KDecoration2::DecoratedClient::captionChanged, this,
[this]()
{
update(); //更新标题栏标题内容, update(titleBar())这有时不能即时更新标题栏内容
}
);
connect(client().data(), &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateTitleBar);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateTitleBar);
} else {
setBorders(QMargins(0, 0, 0, 0));
setResizeOnlyBorders(QMargins(CUSOR_BORDER, CUSOR_BORDER, CUSOR_BORDER, CUSOR_BORDER));
//connect(client().data(), &KDecoration2::DecoratedClient::activeChanged, this, &Decoration::updateShadow);
}
connect(client().data(), &KDecoration2::DecoratedClient::sizeChanged, this, [=](){
auto effectManager = KWin::effects;
if (!effectManager)
return;
for (auto window : effectManager->stackingOrder()) {
if (!window)
continue;
bool sameCaption = false;
bool sameSize = false;
if (window->caption() == this->client().data()->caption()) {
sameCaption = true;
}
if (window->geometry().size() == client().data()->decoration().data()->size()) {
sameSize = true;
}
if (sameCaption && sameSize) {
bool isEdge = client().data()->adjacentScreenEdges() != Qt::Edge();
if (isEdge) {
window->setData(1000, true);
} else {
window->setData(1000, QVariant());
}
}
}
});
// get theme project
if (m_themeProject == THEME_PROJECT_UKUI_BASE) {
QSettings osinfo("/etc/os-release", QSettings::IniFormat);
if (osinfo.contains(QString("PROJECT_CODENAME"))) {
QString project_codename = osinfo.value(QString("PROJECT_CODENAME")).toString();
if (0 == project_codename.compare("V10SP1-edu", Qt::CaseInsensitive))
m_themeProject = THEME_PROJECT_LAIKA;
else if (0 == project_codename.compare("V10SPX-mavis-edu", Qt::CaseInsensitive))
m_themeProject = THEME_PROJECT_MAVIS;
}
}
}
void Decoration::updatefont(QFont font)
{
m_Font = font;
QToolTip::setFont(font);
update();
}
void Decoration::updateShadow(int themeId)
{
m_themeId = themeId;
auto ubr = XAtomHelper::getInstance()->getWindowBorderRadius(client().data()->windowId());
// 控制左上、右上的阴影ubr不生效时阴影也应该是0
if (ubr.topLeft <= 0) {
ubr.topLeft = m_shadowRadius;
}
if (ubr.topRight <= 0) {
ubr.topRight = m_shadowRadius;
}
if (ubr.bottomLeft <= 0) {
ubr.bottomLeft = m_shadowRadius;
}
if (ubr.bottomRight <= 0) {
ubr.bottomRight = m_shadowRadius;
}
// m_themeId is useless here now, but just keep it.
// if(m_themeId==1)
// {
// m_BorderColor = QColor(255,255,255);
// }else{
// m_BorderColor = QColor(38,38,38);
// }
m_BorderColor = this->client().data()->palette().shadow().color();
m_BorderColor.setAlphaF(SHADOWCOLOR_ALPHAF);
ShadowIndex shadowIndex(m_BorderColor, ubr.topLeft, ubr.topRight, ubr.bottomLeft, ubr.bottomRight, ACTIVE_DARKNESS, SHADOW_BORDER);
auto shadow = ShadowHelper::globalInstance()->getShadow(shadowIndex);
shadow.data()->setPadding(QMargins(SHADOW_BORDER, SHADOW_BORDER, SHADOW_BORDER, SHADOW_BORDER));
setShadow(shadow);
}
void Decoration::updateTitleBar()
{
bool isDecoBorderOnly = XAtomHelper::getInstance()->isWindowDecorateBorderOnly(client().data()->windowId());
if (isDecoBorderOnly)
{
return;
}
auto c = client().data();
const int x = (m_ButtonMarginTop + m_buttonSpacing) * 2 + m_nleftButtonCout * m_leftButtonWidth;
const int width = c->width() + (c->isMaximized() ? 0 : (m_borderLeft + m_borderRight))
- (m_ButtonMarginTop + m_buttonSpacing) * 2 - m_nleftButtonCout * m_leftButtonWidth //减去左侧按钮空间
- m_nrightButtonCout * (m_buttonWidth + m_buttonSpacing); //减去右侧按钮空间
setTitleBar(QRect(x, 0, width, borderTop()));
}
void Decoration::calculateBorders()
{
bool maximized = client().data()->isMaximized();
if(true == maximized)
{
//在最大化时需要对setBorders中其中一个参数调整一下大小否则兆芯笔记本使用VSCode开源软件最大化时会出现页面刷新不完全的情况。此处选择将m_borderTop - 1进行调整。
//后来由于使用新版VSCode不会出现该问题故又将m_borderTop - 1调整回m_borderTop
setBorders(QMargins(0, m_borderTop, 0, 0)); //真正的边框尺寸
setResizeOnlyBorders(QMargins(0, 0, 0, 0)); //边框伸展光标范围
}
else
{
setBorders(QMargins(m_borderLeft, m_borderTop, m_borderRight, m_borderBottom)); //真正的边框尺寸
setResizeOnlyBorders(QMargins(CUSOR_BORDER, CUSOR_BORDER, CUSOR_BORDER, CUSOR_BORDER)); //边框伸展光标范围
}
}
void Decoration::slotSwitchTablet(bool bTabletMode)
{
if(bTabletMode == m_bTabletMode)
{
return;
}
m_bTabletMode = bTabletMode;
calculateRightButtonCout();
calculateBorders();
return;
}
void Decoration::calculateRightButtonCout()
{
m_nrightButtonCout = 0;
for (const QPointer<KDecoration2::DecorationButton>& button : m_rightButtons->buttons())
{
// if(false == button.data()->isVisible())
// {
// continue;
// }
if(KDecoration2::DecorationButtonType::Minimize == button.data()->type())
{
if(false == client().data()->isMinimizeable())
{
button.data()->setVisible(false);
continue;
}
else
{
if (true == m_bTabletMode)
{
button.data()->setVisible(false);
}
else
{
button.data()->setVisible(true);
m_nrightButtonCout++;
}
}
}
//安卓兼容应用全屏后还原,从可最大化转变为不可最大化,此处增加隐藏按钮设定
if(KDecoration2::DecorationButtonType::Maximize == button.data()->type())
{
if(false == client().data()->isMaximizeable())
{
button.data()->setVisible(false);
continue;
}
else
{
if (true == m_bTabletMode)
{
button.data()->setVisible(false);
}
else
{
button.data()->setVisible(true);
m_nrightButtonCout++;
}
}
}
if(KDecoration2::DecorationButtonType::Close == button.data()->type())
{
if(false == client().data()->isCloseable())
{
button.data()->setVisible(false);
continue;
}
else
{
if (true == m_bTabletMode)
{
button.data()->setVisible(false);
}
else
{
button.data()->setVisible(true);
m_nrightButtonCout++;
}
}
}
button.data()->setGeometry(QRectF(QPointF(0, 0), QSizeF(m_buttonWidth, m_buttonHeight)));
}
updateButtonsGeometry();
updateTitleBar();
}
void Decoration::themeUpdate(int themeId)
{
m_themeId = themeId;
if(1 == m_themeId)
{
m_frameActiveColor = QColor(18, 18, 18);
m_frameInactiveColor = QColor(28, 28, 28);
m_fontActiveColor = QColor(207, 207, 207);
m_fontInactiveColor = QColor(105, 105, 105);
//修改标题栏右键菜单颜色
auto config = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig);
KConfigGroup viewConfig(config, QStringLiteral("Colors:View"));
viewConfig.writeEntry("BackgroundAlternate", QColor(49,54,59));
viewConfig.writeEntry("BackgroundNormal", QColor(35,38,41));
viewConfig.writeEntry("DecorationHover", QColor(61,174,233));
viewConfig.writeEntry("ForegroundInactive", QColor(189,195,199));
viewConfig.writeEntry("ForegroundNormal", QColor(239,240,241));
KConfigGroup windowConfig(config, QStringLiteral("Colors:Window"));
windowConfig.writeEntry("BackgroundAlternate", QColor(77,77,77));
windowConfig.writeEntry("BackgroundNormal", QColor(49,54,59));
windowConfig.writeEntry("DecorationHover", QColor(61,174,233));
windowConfig.writeEntry("ForegroundInactive", QColor(189,195,199));
windowConfig.writeEntry("ForegroundNormal", QColor(239,240,241));
config->sync();
}
else
{
m_frameActiveColor = QColor(255, 255, 255);
m_frameInactiveColor = QColor(245, 245, 245);
m_fontActiveColor = QColor(0, 0, 0);
m_fontInactiveColor = QColor(105, 105, 105);
auto config = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig);
KConfigGroup viewConfig(config, QStringLiteral("Colors:View"));
viewConfig.writeEntry("BackgroundAlternate", QColor(239,240,241));
viewConfig.writeEntry("BackgroundNormal", QColor(252,252,252));
viewConfig.writeEntry("DecorationHover", QColor(147,206,233));
viewConfig.writeEntry("ForegroundInactive", QColor(127,140,141));
viewConfig.writeEntry("ForegroundNormal", QColor(35,38,39));
KConfigGroup windowConfig(config, QStringLiteral("Colors:Window"));
windowConfig.writeEntry("BackgroundAlternate", QColor(189,195,199));
windowConfig.writeEntry("BackgroundNormal", QColor(239,240,241));
windowConfig.writeEntry("DecorationHover", QColor(147,206,233));
windowConfig.writeEntry("ForegroundInactive", QColor(127,140,141));
windowConfig.writeEntry("ForegroundNormal", QColor(35,38,39));
config->sync();
}
update();
updateShadow(m_themeId);
}
void Decoration::paint(QPainter *painter, const QRect &repaintRegion)
{
if (!repaintRegion.isEmpty()){
painter->setClipRect(repaintRegion);
}
auto c = client().data();
//总体框架刷颜色
painter->save();
if(c->isMaximized())
{
auto rect = QRect(QPoint(0, 0), this->client().data()->size());
painter->fillRect(rect, this->client().data()->isActive() ? this->m_frameActiveColor : this->m_frameInactiveColor);
}
else if(c->adjacentScreenEdges() != Qt::Edge())
{
auto rect = QRect(0, 0, (c->size().width() + m_borderLeft + m_borderRight), (c->size().height() + m_borderTop + m_borderBottom));
painter->setPen(Qt::NoPen);
painter->setBrush(this->client().data()->isActive() ? this->m_frameActiveColor : this->m_frameInactiveColor);
painter->drawRoundedRect(rect, 0, 0);
}
else
{
auto rect = QRect(0, 0, (c->size().width() + m_borderLeft + m_borderRight), (c->size().height() + m_borderTop + m_borderBottom));
painter->setPen(Qt::NoPen);
painter->setBrush(this->client().data()->isActive() ? this->m_frameActiveColor : this->m_frameInactiveColor);
// 控制左上、右上的阴影
painter->drawRoundedRect(rect, m_shadowRadius, m_shadowRadius);
auto rectLeftBottom = QRect(0, rect.height() - m_shadowRadius * 2, m_shadowRadius * 2, m_shadowRadius * 2);
painter->drawRoundedRect(rectLeftBottom, 0, 0); //左下角补角
auto rectRightBottom = QRect(rect.width() - m_shadowRadius * 2, rect.height() - m_shadowRadius * 2, m_shadowRadius * 2, m_shadowRadius * 2);
painter->drawRoundedRect(rectRightBottom, 0, 0);
}
painter->restore();
//写标题
painter->setFont(m_Font);
painter->setPen(fontColor());
const auto cR = qMakePair(titleBar(), Qt::AlignVCenter | Qt::AlignLeft);
const QString caption = painter->fontMetrics().elidedText(c->caption(), Qt::ElideMiddle, cR.first.width());
painter->drawText(cR.first, cR.second | Qt::TextSingleLine, caption);
//按钮组刷颜色
m_leftButtons->paint(painter, repaintRegion);
m_rightButtons->paint(painter, repaintRegion);
}
void Decoration::updateButtonsGeometry()
{
auto c = client().data();
m_leftButtons->setPos(QPoint(m_ButtonMarginTop + m_buttonSpacing, m_ButtonMarginTop + m_buttonSpacing));
//由于上边檐是m_ButtonMarginTop右侧有m_nrightButtonCout个按钮和按钮间隙
auto posX = c->width() + (c->isMaximized() ? 0 : (m_borderLeft + m_borderRight)) - m_nrightButtonCout * (m_buttonWidth + m_buttonSpacing);
m_rightButtons->setPos(QPoint(posX, m_ButtonMarginTop));
update();
}
QColor Decoration::fontColor()const
{
auto c = client().data();
if(c->isActive())
{
return m_fontActiveColor;
}
else
{
return m_fontInactiveColor;
}
}
#include <ukui-decoration.moc>

View File

@ -0,0 +1,145 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#ifndef UKUIDECORATION_H
#define UKUIDECORATION_H
#include <KDecoration2/Decoration>
#include <KPluginFactory>
#include <QFont>
namespace KDecoration2 {
class DecorationButtonGroup;
}
namespace UKUI {
struct ShadowParams {
ShadowParams()
: offset(QPoint(0, 0))
, radius(0)
, opacity(0) {}
ShadowParams(const QPoint &offset, int radius, qreal opacity)
: offset(offset)
, radius(radius)
, opacity(opacity) {}
QPoint offset;
int radius;
qreal opacity;
};
struct CompositeShadowParams {
CompositeShadowParams() = default;
CompositeShadowParams(
const QPoint &offset,
const ShadowParams &shadow1,
const ShadowParams &shadow2)
: offset(offset)
, shadow1(shadow1)
, shadow2(shadow2) {}
bool isNone() const {
return qMax(shadow1.radius, shadow2.radius) == 0;
}
QPoint offset;
ShadowParams shadow1;
ShadowParams shadow2;
};
enum ThemeProject {
THEME_PROJECT_UKUI_BASE = 0,
THEME_PROJECT_LAIKA,
THEME_PROJECT_MAVIS,
THEME_PROJECT_XC_TABLET,
THEME_PROJECT_MAX
};
class Decoration : public KDecoration2::Decoration
{
Q_OBJECT
public:
explicit Decoration(QObject *parent = nullptr, const QVariantList &args = QVariantList());
void init();
void paint(QPainter *painter, const QRect &repaintRegion) override;
QColor fontColor() const;
int themeId(){return m_themeId;}
ThemeProject getThemeProject() { return m_themeProject; }
bool getTabletMode() {return m_bTabletMode;}
private:
int m_borderLeft;
int m_borderTop;
int m_borderRight;
int m_borderBottom;
int m_buttonWidth; //按钮宽度
int m_buttonHeight; //按钮高度
int m_leftButtonWidth; //左侧按钮高度
int m_leftButtonHeight; //左侧按钮宽度
int m_ButtonMarginTop; //按钮上空白
int m_buttonSpacing; //按钮空隙
QColor m_fontActiveColor; //活动字体颜色
QColor m_fontInactiveColor; //非活动字体颜色
QColor m_frameActiveColor;
QColor m_frameInactiveColor;
QColor m_BorderColor; //边框颜色
bool m_bTabletMode;
int m_nleftButtonCout;
int m_nrightButtonCout;
int m_themeId;
ThemeProject m_themeProject;
KDecoration2::DecorationButtonGroup* m_leftButtons;
KDecoration2::DecorationButtonGroup* m_rightButtons;
int m_shadowRadius;
QFont m_Font;
public Q_SLOTS:
void updateButtonsGeometry();
void calculateBorders();
void calculateRightButtonCout();
void updateTitleBar();
void updateShadow(int themeId);
void themeUpdate(int themeId);
void updatefont(QFont font);
void slotSwitchTablet(bool bTabletMode);
};
}
#endif // UKUIDECORATION_H

View File

@ -0,0 +1,336 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#include <kwinglobals.h>
#include "xatom-helper.h"
#include <limits.h>
#include <xcb/xcb_util.h>
static XAtomHelper *global_instance = nullptr;
XAtomHelper *XAtomHelper::getInstance()
{
if (!global_instance)
global_instance = new XAtomHelper;
if (!KWin::connection())
return global_instance;
if (!global_instance->m_motifWMHintsAtom) {
QString tmp("_MOTIF_WM_HINTS");
xcb_intern_atom_cookie_t cookie1 = xcb_intern_atom(KWin::connection(), false, tmp.length(), tmp.toUtf8());
tmp = "_UNITY_GTK_BORDER_RADIUS";
xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom(KWin::connection(), false, tmp.length(), tmp.toUtf8());
tmp = "_KWIN_UKUI_DECORAION";
xcb_intern_atom_cookie_t cookie3 = xcb_intern_atom(KWin::connection(), false, tmp.length(), tmp.toUtf8());
xcb_intern_atom_reply_t *reply1 = xcb_intern_atom_reply(KWin::connection(), cookie1, nullptr);
global_instance->m_motifWMHintsAtom = reply1->atom;
free(reply1);
xcb_intern_atom_reply_t *reply2 = xcb_intern_atom_reply(KWin::connection(), cookie2, nullptr);
global_instance->m_unityBorderRadiusAtom = reply2->atom;
free(reply2);
xcb_intern_atom_reply_t *reply3 = xcb_intern_atom_reply(KWin::connection(), cookie3, nullptr);
global_instance->m_ukuiDecorationAtion = reply3->atom;
free(reply3);
}
return global_instance;
}
bool XAtomHelper::isFrameLessWindow(int winId)
{
auto hints = getInstance()->getWindowMotifHint(winId);
if (hints.flags == MWM_HINTS_DECORATIONS && hints.functions == 1) {
return true;
}
return false;
}
bool XAtomHelper::isWindowDecorateBorderOnly(int winId)
{
return isWindowMotifHintDecorateBorderOnly(getInstance()->getWindowMotifHint(winId));
}
bool XAtomHelper::isWindowMotifHintDecorateBorderOnly(const MotifWmHints &hint)
{
bool isDeco = false;
if (hint.flags & MWM_HINTS_DECORATIONS) {
if (hint.decorations == MWM_DECOR_BORDER) {
isDeco = true;
}
}
return isDeco;
}
bool XAtomHelper::isUKUICsdSupported()
{
// fixme:
return false;
}
bool XAtomHelper::isUKUIDecorationWindow(int winId)
{
if (m_ukuiDecorationAtion == 0)
return false;
uchar *data;
bool isUKUIDecoration = false;
xcb_get_property_cookie_t cookie = xcb_get_property(KWin::connection(), false, winId, m_ukuiDecorationAtion, XCB_ATOM_ANY, 0, 1);
xcb_get_property_reply_t *reply = xcb_get_property_reply(KWin::connection(), cookie, nullptr);
if (!reply)
return false;
data = (uchar *)xcb_get_property_value(reply);
free(reply);
if (data) {
isUKUIDecoration = data[0];
//free(data);
}
return isUKUIDecoration;
}
UnityCorners XAtomHelper::getWindowBorderRadius(int winId)
{
UnityCorners corners;
corners.topLeft = 0;
corners.topRight = 0;
corners.bottomLeft = 0;
corners.bottomRight = 0;
if (0 == winId) {
return corners;
}
uchar *data;
xcb_get_property_cookie_t cookie = xcb_get_property(KWin::connection(), false, winId, m_unityBorderRadiusAtom, XCB_ATOM_CARDINAL, 0, sizeof(UnityCorners)/sizeof(ulong));
xcb_generic_error_t *error = nullptr;
xcb_get_property_reply_t *reply = xcb_get_property_reply(KWin::connection(), cookie, &error);
if (!reply)
return corners;
if (error) {
free(error);
return corners;
}
if (xcb_get_property_value_length(reply) != 4 * sizeof(int)) {
free(reply);
return corners;
}
data = (uchar*)xcb_get_property_value(reply);
free(reply);
if (data) {
corners.topLeft = static_cast<ulong>(data[0]);
corners.topRight = static_cast<ulong>(data[1*sizeof (int)]);
corners.bottomLeft = static_cast<ulong>(data[2*sizeof (int)]);
corners.bottomRight = static_cast<ulong>(data[3*sizeof (int)]);
//free(data);
}
return corners;
}
void XAtomHelper::setWindowBorderRadius(int winId, const UnityCorners &data)
{
if (m_unityBorderRadiusAtom == 0)
return;
ulong corners[4] = {data.topLeft, data.topRight, data.bottomLeft, data.bottomRight};
xcb_change_property(KWin::connection(), XCB_PROP_MODE_REPLACE, winId, m_unityBorderRadiusAtom, XCB_ATOM_CARDINAL, 32, sizeof(corners)/sizeof(corners[0]), &corners);
xcb_flush(KWin::connection());
}
void XAtomHelper::setWindowBorderRadius(int winId, int topLeft, int topRight, int bottomLeft, int bottomRight)
{
if (m_unityBorderRadiusAtom == 0)
return;
ulong corners[4] = {(ulong)topLeft, (ulong)topRight, (ulong)bottomLeft, (ulong)bottomRight};
xcb_change_property(KWin::connection(), XCB_PROP_MODE_REPLACE, winId, m_unityBorderRadiusAtom, XCB_ATOM_CARDINAL, 32, sizeof(corners)/sizeof(corners[0]), &corners);
xcb_flush(KWin::connection());
}
void XAtomHelper::setUKUIDecoraiontHint(int winId, bool set)
{
if (m_ukuiDecorationAtion == 0)
return;
xcb_change_property(KWin::connection(), XCB_PROP_MODE_REPLACE, winId, m_ukuiDecorationAtion, m_ukuiDecorationAtion, 32, 1, &set);
xcb_flush(KWin::connection());
}
void XAtomHelper::setWindowMotifHint(int winId, const MotifWmHints &hints)
{
if (m_unityBorderRadiusAtom == 0)
return;
xcb_change_property(KWin::connection(), XCB_PROP_MODE_REPLACE, winId, m_motifWMHintsAtom, m_motifWMHintsAtom,
32, sizeof (MotifWmHints)/ sizeof (ulong), &hints);
xcb_flush(KWin::connection());
}
MotifWmHints XAtomHelper::getWindowMotifHint(int winId)
{
MotifWmHints hints;
if (m_motifWMHintsAtom == 0)
return hints;
uchar *data;
xcb_get_property_cookie_t cookie = xcb_get_property(KWin::connection(), false, winId, m_motifWMHintsAtom, XCB_ATOM_ANY, 0, sizeof (MotifWmHints)/ sizeof (ulong));
xcb_get_property_reply_t *reply = xcb_get_property_reply(KWin::connection(), cookie, nullptr);
if (!reply)
return hints;
if (reply->length != 5) {
free(reply);
return hints;
}
data = (uchar*)xcb_get_property_value(reply);
free(reply);
if (!data)
return hints;
hints.flags = data[0];
hints.functions = data[sizeof(int)];
hints.decorations = data[sizeof(int)*2];
hints.input_mode = data[sizeof(int)*3];
hints.status = data[sizeof(int)*4];
return hints;
}
XAtomHelper::XAtomHelper(QObject *parent) : QObject(parent)
{
if (!KWin::connection())
return;
QString tmp("_MOTIF_WM_HINTS");
xcb_intern_atom_cookie_t cookie1 = xcb_intern_atom_unchecked(KWin::connection(), false, tmp.length(), tmp.toUtf8());
tmp = "_UNITY_GTK_BORDER_RADIUS";
xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom_unchecked(KWin::connection(), false, tmp.length(), tmp.toUtf8());
tmp = "_KWIN_UKUI_DECORAION";
xcb_intern_atom_cookie_t cookie3 = xcb_intern_atom_unchecked(KWin::connection(), false, tmp.length(), tmp.toUtf8());
xcb_intern_atom_reply_t *reply1 = xcb_intern_atom_reply(KWin::connection(), cookie1, nullptr);
m_motifWMHintsAtom = reply1->atom;
free(reply1);
xcb_intern_atom_reply_t *reply2 = xcb_intern_atom_reply(KWin::connection(), cookie2, nullptr);
m_unityBorderRadiusAtom = reply2->atom;
free(reply2);
xcb_intern_atom_reply_t *reply3 = xcb_intern_atom_reply(KWin::connection(), cookie3, nullptr);
m_ukuiDecorationAtion = reply3->atom;
free(reply3);
}
xcb_atom_t XAtomHelper::registerUKUICsdNetWmSupportAtom()
{
// fixme:
return 0;
}
void XAtomHelper::unregisterUKUICsdNetWmSupportAtom()
{
// fixme:
}
bool XAtomHelper::checkButtonAvailable(int winId, KDecoration2::DecorationButtonType buttonType)
{
MotifWmHints hint = getInstance()->getWindowMotifHint(winId);
//printf("XAtomHelper::checkButtonAvailable ==hint:%02X==hints.functions:%02X\n", hint.flags, hint.functions);
if((0 == hint.functions) || (0 == hint.flags))
{
return true;
}
bool bMinimizeButtonAvailable = true;
bool bMaximizeButtonAvailable = true;
bool bCloseButtonAvailable = true;
bool bToggleFlag;
if(0 == (hint.functions & MWM_FUNC_ALL))
{
bToggleFlag = true;
bMinimizeButtonAvailable = false;
bMaximizeButtonAvailable = false;
bCloseButtonAvailable = false;
}
else
{
bToggleFlag = false;
}
switch (buttonType) {
case KDecoration2::DecorationButtonType::Minimize:
{
if(0 != (hint.functions & MWM_FUNC_MINIMIZE))
{
bMinimizeButtonAvailable = bToggleFlag;
//printf("XAtomHelper::checkButtonAvailable ==bToggleFlag:%d\n", bToggleFlag);
}
//printf("XAtomHelper::checkButtonAvailable ==bMinimizeButtonAvailable:%d\n", bMinimizeButtonAvailable);
return bMinimizeButtonAvailable;
}
break;
case KDecoration2::DecorationButtonType::Maximize:
{
if(0 != (hint.functions & MWM_FUNC_MAXIMIZE))
{
bMaximizeButtonAvailable = bToggleFlag;
}
return bMaximizeButtonAvailable;
}
break;
case KDecoration2::DecorationButtonType::Close:
{
if(0 != (hint.functions & MWM_FUNC_CLOSE))
{
bCloseButtonAvailable = bToggleFlag;
}
return bCloseButtonAvailable;
}
break;
default:
break;
}
return true;
}

View File

@ -0,0 +1,113 @@
/*
* KWin Style UKUI
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#ifndef XATOMHELPER_H
#define XATOMHELPER_H
#include <QObject>
#include <xcb/xcb_atom.h>
#include <KDecoration2/DecoratedClient>
struct UnityCorners {
ulong topLeft = 0;
ulong topRight = 0;
ulong bottomLeft = 0;
ulong bottomRight = 0;
};
typedef struct {
ulong flags = 0;
ulong functions = 0;
ulong decorations = 0;
long input_mode = 0;
ulong status = 0;
} MotifWmHints, MwmHints;
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_HINTS_STATUS (1L << 3)
#define MWM_FUNC_ALL (1L << 0)
#define MWM_FUNC_RESIZE (1L << 1)
#define MWM_FUNC_MOVE (1L << 2)
#define MWM_FUNC_MINIMIZE (1L << 3)
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)
#define MWM_INPUT_MODELESS 0
#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
#define MWM_INPUT_SYSTEM_MODAL 2
#define MWM_INPUT_FULL_APPLICATION_MODAL 3
#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
#define MWM_TEAROFF_WINDOW (1L<<0)
namespace UKUI {
class Decoration;
}
class XAtomHelper : public QObject
{
friend class UKUI::Decoration;
Q_OBJECT
public:
static XAtomHelper *getInstance();
bool isFrameLessWindow(int winId);
bool isWindowDecorateBorderOnly(int winId);
bool isWindowMotifHintDecorateBorderOnly(const MotifWmHints &hint);
bool isUKUICsdSupported();
bool isUKUIDecorationWindow(int winId);
UnityCorners getWindowBorderRadius(int winId);
void setWindowBorderRadius(int winId, const UnityCorners &data);
void setWindowBorderRadius(int winId, int topLeft, int topRight, int bottomLeft, int bottomRight);
void setUKUIDecoraiontHint(int winId, bool set = true);
void setWindowMotifHint(int winId, const MotifWmHints &hints);
MotifWmHints getWindowMotifHint(int winId);
bool checkButtonAvailable(int winId, KDecoration2::DecorationButtonType buttonType);
private:
explicit XAtomHelper(QObject *parent = nullptr);
xcb_atom_t registerUKUICsdNetWmSupportAtom();
void unregisterUKUICsdNetWmSupportAtom();
xcb_atom_t m_motifWMHintsAtom = 0;
xcb_atom_t m_unityBorderRadiusAtom = 0;
xcb_atom_t m_ukuiDecorationAtion = 0;
};
#endif // XATOMHELPER_H

View File

@ -141,8 +141,17 @@ ScriptedEffect *ScriptedEffect::create(const KPluginMetaData &effect)
qCDebug(KWIN_SCRIPTING) << "X-Plasma-MainScript not set"; qCDebug(KWIN_SCRIPTING) << "X-Plasma-MainScript not set";
return nullptr; return nullptr;
} }
#if defined(QT_NO_DEBUG)
const QString scriptFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, const QString scriptFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
QLatin1String(KWIN_NAME "/effects/") + name + QLatin1String("/contents/") + scriptName); QLatin1String(KWIN_NAME "/effects/") + name + QLatin1String("/contents/") + scriptName);
#else
QString effectName = name;
QString scriptFile = QCoreApplication::applicationDirPath() + QLatin1String("/../../src/effects/") + effectName.remove("kwin4_effect_") + QLatin1String("/package/contents/") + scriptName;
if (!QFile::exists(scriptFile))
scriptFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
QLatin1String(KWIN_NAME "/effects/") + name + QLatin1String("/contents/") + scriptName);
qCDebug(KWIN_SCRIPTING) << "Load script file:" << scriptFile;
#endif
if (scriptFile.isNull()) { if (scriptFile.isNull()) {
qCDebug(KWIN_SCRIPTING) << "Could not locate the effect script"; qCDebug(KWIN_SCRIPTING) << "Could not locate the effect script";
return nullptr; return nullptr;
@ -204,7 +213,14 @@ bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript
m_scriptFile = pathToScript; m_scriptFile = pathToScript;
// does the effect contain an KConfigXT file? // does the effect contain an KConfigXT file?
#if defined(QT_NO_DEBUG)
const QString kconfigXTFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(KWIN_NAME "/effects/") + m_effectName + QLatin1String("/contents/config/main.xml")); const QString kconfigXTFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(KWIN_NAME "/effects/") + m_effectName + QLatin1String("/contents/config/main.xml"));
#else
QString kconfigXTFile = QCoreApplication::applicationDirPath() + QLatin1String("/../../src/effects/") + m_effectName.remove("kwin4_effect_") + QLatin1String("/package/contents/config/main.xml");
if (!QFile::exists(kconfigXTFile))
kconfigXTFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(KWIN_NAME "/effects/") + m_effectName + QLatin1String("/contents/config/main.xml"));
qCDebug(KWIN_SCRIPTING) << "Try to load kconfigXTFile file:" << kconfigXTFile;
#endif
if (!kconfigXTFile.isNull()) { if (!kconfigXTFile.isNull()) {
KConfigGroup cg = QCoreApplication::instance()->property("config").value<KSharedConfigPtr>()->group(QStringLiteral("Effect-%1").arg(m_effectName)); KConfigGroup cg = QCoreApplication::instance()->property("config").value<KSharedConfigPtr>()->group(QStringLiteral("Effect-%1").arg(m_effectName));
QFile xmlFile(kconfigXTFile); QFile xmlFile(kconfigXTFile);

View File

@ -61,11 +61,19 @@ static const QString s_managerPath = QStringLiteral("/org/freedesktop/login1");
static QString findProcessSessionPath() static QString findProcessSessionPath()
{ {
#if defined(QT_NO_DEBUG)
const QString sessionId = qEnvironmentVariable("XDG_SESSION_ID", QStringLiteral("auto")); const QString sessionId = qEnvironmentVariable("XDG_SESSION_ID", QStringLiteral("auto"));
QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, s_managerPath, QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, s_managerPath,
s_managerInterface, s_managerInterface,
QStringLiteral("GetSession")); QStringLiteral("GetSession"));
message.setArguments({sessionId}); message.setArguments({sessionId});
#else
const quint32 applicationId = (quint32) QCoreApplication::applicationPid();
QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, s_managerPath,
s_managerInterface,
QStringLiteral("GetSessionByPID"));
message.setArguments({applicationId});
#endif
const QDBusMessage reply = QDBusConnection::systemBus().call(message); const QDBusMessage reply = QDBusConnection::systemBus().call(message);
if (reply.type() == QDBusMessage::ErrorMessage) { if (reply.type() == QDBusMessage::ErrorMessage) {
return QString(); return QString();

View File

@ -1363,6 +1363,9 @@ void Workspace::slotWindowToPrevScreen()
*/ */
void Workspace::slotWindowMaximize() void Workspace::slotWindowMaximize()
{ {
if(m_bTabletMode)
return;
if (USABLE_ACTIVE_CLIENT) if (USABLE_ACTIVE_CLIENT)
performWindowOperation(active_client, Options::MaximizeOp); performWindowOperation(active_client, Options::MaximizeOp);
} }

View File

@ -127,6 +127,7 @@ Workspace::Workspace()
, set_active_client_recursion(0) , set_active_client_recursion(0)
, block_stacking_updates(0) , block_stacking_updates(0)
, m_sessionManager(new SessionManager(this)) , m_sessionManager(new SessionManager(this))
, m_bTabletMode(false)
{ {
// If KWin was already running it saved its configuration after loosing the selection -> Reread // If KWin was already running it saved its configuration after loosing the selection -> Reread
QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration);
@ -278,6 +279,28 @@ void Workspace::init()
connect(server, &WaylandServer::shellClientRemoved, this, &Workspace::removeShellClient); connect(server, &WaylandServer::shellClientRemoved, this, &Workspace::removeShellClient);
} }
//kwin启动时获取运行模式: 0:PC模式, 1:平板模式
QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.statusmanager.interface",
"/",
"com.kylin.statusmanager.interface",
"get_current_tabletmode");
QDBusMessage response = QDBusConnection::sessionBus().call(message);
if (response.type() == QDBusMessage::ReplyMessage) {
m_bTabletMode = response.arguments().takeFirst().toBool();
slotSwitchTabletMode(m_bTabletMode);
} else {
printf("TabletMode qdus invalid\n");
}
//通过dbus监听PC-平板模式切换
QDBusConnection::sessionBus().connect("com.kylin.statusmanager.interface",
"/",
"com.kylin.statusmanager.interface",
"mode_change_signal",
this,
SLOT(slotSwitchTabletMode(bool)));
// SELI TODO: This won't work with unreasonable focus policies, // SELI TODO: This won't work with unreasonable focus policies,
// and maybe in rare cases also if the selected client doesn't // and maybe in rare cases also if the selected client doesn't
// want focus // want focus
@ -289,6 +312,58 @@ void Workspace::init()
// TODO: ungrabXServer() // TODO: ungrabXServer()
} }
void Workspace::slotSwitchTabletMode(bool bSurfaceMode)
{
if (m_bTabletMode == bSurfaceMode)
return;
m_bTabletMode = bSurfaceMode;
switchTablet(m_bTabletMode);
}
void Workspace::switchTablet(bool bOn)
{
bool bTopClientMaximize = true; //顶层窗口最大化标志
for (int i = stacking_order.count() - 1; i > -1; --i) {
AbstractClient *c = qobject_cast<AbstractClient*>(stacking_order.at(i));
if (!c || c->isDock() || c->isDesktop())
continue;
//m_bTabletMode为true表示pc切平板
if (m_bTabletMode) {
if (c->maximizeMode() == MaximizeRestore) {
c->setTabletToPcRestoreFlag(true); //切平板前,如果是还原状态,则记录
}
//并且该窗口不是最小化时
if(!c->isMinimized()) {
if(bTopClientMaximize) {
bTopClientMaximize = false;
c->maximize(MaximizeFull); //序号越大,越在顶层,只需要将既不是任务栏又不是桌面的最顶层次未最小化的窗口最大化
if (false == c->isResizable()) {
if (!showingDesktop()) {
// 如果是固定大小的窗口,需要将窗口放到屏幕的正中间
Placement::self()->placeCentered(c, workspace()->activeOutput()->geometry());
}
}
} else {
printf("Workspace::switchTablet, minimize unminimized caption:%s\n", c->caption().toStdString().c_str());
c->minimize(true);
}
}
} else {
//m_bTabletMode为false表示平板切pc顶层最大化窗口如果之前是还原状态则需还原
if (MaximizeFull == c->maximizeMode() && true == bTopClientMaximize) {
bTopClientMaximize = false;
if (c->getTabletToPcRestoreFlag()) { //如果pc切平板前记录的之前是还原状态则当前激活窗口在切回pc后需还原否则不需要处理
c->maximize(MaximizeRestore);
c->setTabletToPcRestoreFlag(false); //切回pc后由于当前激活窗口已还原则以后该窗口无需再做还原处理
}
}
}
}
}
void Workspace::initializeX11() void Workspace::initializeX11()
{ {
if (!kwinApp()->x11Connection()) { if (!kwinApp()->x11Connection()) {
@ -656,6 +731,9 @@ X11Client *Workspace::createClient(xcb_window_t w, bool is_mapped)
return nullptr; return nullptr;
} }
addClient(c); addClient(c);
if(m_bTabletMode) //当是平板模式时,窗口默认最大化,其他窗口最小化/*,并且设置平板标签*/
c->makeOthersMinimize();
return c; return c;
} }
@ -2238,6 +2316,17 @@ void Workspace::updateClientArea()
} }
} }
//当屏幕旋转时,那些固定大小的窗口需要再次进行一次居中设置(除了任务栏和桌面)
if (m_bTabletMode) {
for (auto it = m_x11Clients.constBegin(); it != m_x11Clients.constEnd(); ++it) {
if (true == (*it)->isResizable() || true == (*it)->isDock() || true == (*it)->isDesktop()) {
continue;
}
Placement::self()->placeCentered(*it, workspace()->activeOutput()->geometry());
}
}
if (m_workAreas != workAreas || m_restrictedAreas != restrictedAreas || m_screenAreas != screenAreas) { if (m_workAreas != workAreas || m_restrictedAreas != restrictedAreas || m_screenAreas != screenAreas) {
m_workAreas = workAreas; m_workAreas = workAreas;
m_screenAreas = screenAreas; m_screenAreas = screenAreas;

View File

@ -416,6 +416,9 @@ public:
*/ */
void removeInternalClient(InternalClient *client); void removeInternalClient(InternalClient *client);
bool isInTabletMode() {return m_bTabletMode;}
void switchTablet(bool bOn);
public Q_SLOTS: public Q_SLOTS:
void performWindowOperation(KWin::AbstractClient* c, Options::WindowOperation op); void performWindowOperation(KWin::AbstractClient* c, Options::WindowOperation op);
// Keybindings // Keybindings
@ -431,6 +434,8 @@ public Q_SLOTS:
void slotWindowToPrevScreen(); void slotWindowToPrevScreen();
void slotToggleShowDesktop(); void slotToggleShowDesktop();
void slotSwitchTabletMode(bool bSurfaceMode);
void slotWindowMaximize(); void slotWindowMaximize();
void slotWindowMaximizeVertical(); void slotWindowMaximizeVertical();
void slotWindowMaximizeHorizontal(); void slotWindowMaximizeHorizontal();
@ -662,6 +667,7 @@ private:
static Workspace* _self; static Workspace* _self;
bool workspaceInit; bool workspaceInit;
bool m_bTabletMode;
QScopedPointer<KStartupInfo> m_startup; QScopedPointer<KStartupInfo> m_startup;
QScopedPointer<ColorMapper> m_colorMapper; QScopedPointer<ColorMapper> m_colorMapper;

View File

@ -1946,6 +1946,50 @@ QStringList X11Client::activities() const
return AbstractClient::activities(); return AbstractClient::activities();
} }
//使工作区其他X11窗口最小化
void X11Client::makeOthersMinimize()
{
printf("X11Client::makeOthersMinimize, takefocus windowType(0):%d, caption:%s, isModal:%d, isMaximizable:%d\n", windowType(), this->caption().toStdString().c_str(), isModal(), isMaximizable());
//平板模式下如果是isModal窗口(如xournal应用); 或者是瞬态的对话框(如归档管理器的新建窗口); 则没必要最小化其他所有未最小化的窗口,或者是新激活的桌面开始菜单和Dock窗口(比如onboard),也不要最小化其他窗口
if (true == this->isUtility()
|| true == this->isModal()
|| (true == this->isDialog() && true == this->isTransient())
|| true == this->isDesktop()
|| true == this->isDock()
|| caption() == "sogouImeService"
)
{
bUnMakeOthersMinimizeFlag = true;
return;
}
if (!(this->getSpecialFlags() == static_cast<unsigned int>(SpecialFlags::DoNotMaximize))) {
this->maximize(MaximizeFull);
}
printf("X11Client::makeOthersMinimize, takefocus windowType(1):%d, caption:%s, isdialog:%d, isMaximizable:%d\n", windowType(), this->caption().toStdString().c_str(), isDialog(), isMaximizable());
//锁屏时,如果当前是屏保窗口,对其他窗口不要最小化。解决在平板模式打开应用,锁屏后再进入桌面,应用窗口最小化问题
if (this->resourceClass() == "ukui-screensaver-default") {
return;
}
for (X11Client *c : workspace()->clientList()) {
bool isIgnoreWindow = (this == c || c->isDock() || skipTaskbar() || c->isDialog() || c->isDesktop());
bool isTabletBgWindow = (c->isUtility() && c->caption() == "ukui-kwin_tablet_background" && c->keepBelow());
//除了自己dock和对话框也没必要最小化
if (isIgnoreWindow || isTabletBgWindow) {
continue;
}
if (false == c->isMinimized()){
printf("X11Client::makeOthersMinimize, minimize unminimized caption:%s\n", c->caption().toStdString().c_str());
c->minimize(true);
}
}
}
/** /**
* Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS * Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
*/ */
@ -1969,6 +2013,21 @@ bool X11Client::takeFocus()
} }
workspace()->setShouldGetFocus(this); workspace()->setShouldGetFocus(this);
//如果是平板模式,则其他所有窗口均最小化
if (Workspace::self()->isInTabletMode()){
// 平板模式下,当窗口获得焦点时,如果是固定大小的窗口(除了任务栏),需要将窗口放到屏幕的正中间
if (false == this->isResizable() && false == isDock() && false == isDesktop()) {
Placement::self()->placeCentered(this, workspace()->activeOutput()->geometry());
}
makeOthersMinimize();
} else {
//如果pc切平板前该窗口是还原状态平板切pc后的窗口激活时需将该窗口从最大化切换还原
if (getTabletToPcRestoreFlag()) {
setTabletToPcRestoreFlag(false);
this->maximize(MaximizeRestore);
}
}
bool breakShowingDesktop = !keepAbove(); bool breakShowingDesktop = !keepAbove();
if (breakShowingDesktop) { if (breakShowingDesktop) {
const auto members = group()->members(); const auto members = group()->members();

View File

@ -45,6 +45,10 @@ enum class Predicate {
InputIdMatch, InputIdMatch,
}; };
enum class SpecialFlags {
DoNotMaximize = 0x1 << 0
};
/** /**
* @todo Remove when the X11 platform support is dropped. This decoration renderer * @todo Remove when the X11 platform support is dropped. This decoration renderer
* will be used if compositing is off. * will be used if compositing is off.
@ -181,6 +185,7 @@ public:
bool isCloseable() const override; ///< May be closed by the user (May have a close button) bool isCloseable() const override; ///< May be closed by the user (May have a close button)
bool takeFocus() override; bool takeFocus() override;
void makeOthersMinimize(); //使工作区其他X11窗口最小化
void invalidateDecoration() override; void invalidateDecoration() override;
@ -302,6 +307,9 @@ public:
static void cleanupX11(); static void cleanupX11();
void setSpecialFlags(unsigned int specialFlags) {m_specialFlags = specialFlags;}
unsigned int getSpecialFlags() {return m_specialFlags;}
public Q_SLOTS: public Q_SLOTS:
void closeWindow() override; void closeWindow() override;
void updateCaption() override; void updateCaption() override;
@ -528,6 +536,10 @@ private:
QRect m_lastFrameGeometry; QRect m_lastFrameGeometry;
QRect m_lastClientGeometry; QRect m_lastClientGeometry;
QScopedPointer<X11DecorationRenderer> m_decorationRenderer; QScopedPointer<X11DecorationRenderer> m_decorationRenderer;
bool bUnMakeOthersMinimizeFlag; //不最小化其他窗体标志
unsigned int m_specialFlags = 0;
}; };
inline xcb_window_t X11Client::wrapperId() const inline xcb_window_t X11Client::wrapperId() const