qt5-ukui-platformtheme/ukui-styles/blur-helper.cpp

355 lines
10 KiB
C++

/*
* Qt5-UKUI's Library
*
* Copyright (C) 2023, KylinSoft Co., Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library. If not, see <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#include "blur-helper.h"
#include "ukui-style-settings.h"
#include <QWidget>
#include <KWindowEffects>
#include <QGSettings>
#include <QVariant>
#include <QEvent>
#include <QIcon>
#include <QPainterPath>
#include <QMenu>
#include "black-list.h"
#include <QApplication>
#include <QX11Info>
#include <QDebug>
BlurHelper::BlurHelper(QObject *parent) : QObject(parent)
{
qDebug() << "BlurHelper00000.............";
if (QGSettings::isSchemaInstalled("org.ukui.control-center.personalise")) {
QGSettings *settings = new QGSettings("org.ukui.control-center.personalise", QByteArray(), this);
connect(settings, &QGSettings::changed, this, [=](const QString &key) {
if (key == "effect") {
bool enable = settings->get(key).toBool();
this->onBlurEnableChanged(enable);
}
});
bool enable = settings->get("effect").toBool();
this->onBlurEnableChanged(enable);
if (!KWindowEffects::isEffectAvailable(KWindowEffects::BlurBehind))
confirmBlurEnableDelay();
}
m_timer.setSingleShot(true);
m_timer.setInterval(100);
}
BlurHelper::~BlurHelper()
{
m_blur_widgets.clear();
m_update_list.clear();
}
bool BlurHelper::eventFilter(QObject *obj, QEvent *e)
{
if (!m_blur_enable)
return false;
QWidget* widget = qobject_cast<QWidget*>(obj);
if (!widget || !widget->isWindow())
return false;
//FIXME:
//qDebug()<<e->type()<<obj;
//qDebug()<<KWindowEffects::isEffectAvailable(KWindowEffects::BlurBehind);
switch (e->type()) {
case QEvent::UpdateRequest: {
//QWidget* widget = qobject_cast<QWidget*>(obj);
delayUpdate(widget, true);
break;
}
case QEvent::LayoutRequest:
{
//QWidget* widget = qobject_cast<QWidget*>(obj);
delayUpdate(widget);
break;
}
case QEvent::Hide: {
//QWidget* widget = qobject_cast<QWidget*>(obj);
KWindowEffects::enableBlurBehind(widget->winId(), false);
}
default:
break;
}
return false;
}
/*!
* \brief BlurHelper::registerWidget
* \param widget
* \note
*
* we can't blur a widget before it shown, because some times
* there might be problems about window painting.
* this usually happend on a widget which has graphics effect.
*
* to avoid them, never try get winid and do a blur for that case.
*/
#if (QT_VERSION >= QT_VERSION_CHECK(5,12,0))
void BlurHelper::registerWidget(QWidget *widget)
{
// FIXME: how to blur window on wayland?
if (!QX11Info::isPlatformX11())
return;
if (!widget)
return;
if (shouldSkip(widget))
return;
if (isApplicationInBlackList())
return;
if (widget->property("doNotBlur").toBool())
return;
if (!m_blur_widgets.contains(widget)) {
m_blur_widgets<<widget;
//qDebug()<<KWindowEffects::isEffectAvailable(KWindowEffects::BlurBehind);
/*!
\note
never try enableBlurBehind when register widget.
it might cause serious problems about paint.
usually it happend with a widget which has graphics effect.
*/
// if (!widget->mask().isEmpty()) {
// KWindowEffects::enableBlurBehind(widget->winId(), true, widget->mask());
// } else {
// KWindowEffects::enableBlurBehind(widget->winId(), true);
// }
connect(widget, &QWidget::destroyed, this, [=]() {
this->onWidgetDestroyed(widget);
});
}
widget->removeEventFilter(this);
widget->installEventFilter(this);
if (!widget->mask().isEmpty()) {
widget->update(widget->mask());
} else {
widget->update();
}
}
void BlurHelper::unregisterWidget(QWidget *widget)
{
if (!QX11Info::isPlatformX11())
return;
if (!widget)
return;
if (shouldSkip(widget))
return;
if (isApplicationInBlackList())
return;
if (widget->property("doNotBlur").toBool())
return;
m_blur_widgets.removeOne(widget);
widget->removeEventFilter(this);
if (widget->winId() > 0)
KWindowEffects::enableBlurBehind(widget->winId(), false);
}
#endif
bool BlurHelper::isApplicationInBlackList()
{
return blackList().contains(qAppName());
}
const QStringList BlurHelper::blackList()
{
return blackAppListWithBlurHelper();
}
bool BlurHelper::shouldSkip(QWidget *w)
{
if (w->property("useSystemStyleBlur").isValid()) {
return !w->property("useSystemStyleBlur").toBool();
}
bool skip = true;
if (w->inherits("QComboBoxPrivateContainer"))
return true;
if (w->inherits("QMenu") || w->inherits("QTipLabel")) {
return false;
}
// if (w->inherits("QTipLabel"))
// return true;
return skip;
}
void BlurHelper::onBlurEnableChanged(bool enable)
{
m_blur_enable = enable;
if (enable) {
qApp->setProperty("blurEnable", true);
} else {
qApp->setProperty("blurEnable", false);
}
if (!KWindowEffects::isEffectAvailable(KWindowEffects::BlurBehind))
return;
for (auto widget : qApp->allWidgets()) {
widget->update();
if (m_blur_widgets.contains(widget)) {
if (widget->winId() > 0) {
KWindowEffects::enableBlurBehind(widget->winId(), enable);
}
}
}
// QTimer::singleShot(100, this, [=](){
// for (auto widget : m_blur_widgets) {
// if (!widget)
// continue;
// if (widget->winId() > 0)
// KWindowEffects::enableBlurBehind(widget->winId(), enable);
// }
// });
}
void BlurHelper::onWidgetDestroyed(QWidget *widget)
{
widget->removeEventFilter(this);
m_blur_widgets.removeOne(widget);
//unregisterWidget(widget);
}
void BlurHelper::delayUpdate(QWidget *w, bool updateBlurRegionOnly)
{
if (w->winId() <= 0)
return;
if (!KWindowEffects::isEffectAvailable(KWindowEffects::BlurBehind))
return;
m_update_list.append(w);
if (!m_timer.isActive()) {
for (auto widget : m_update_list) {
// cast to widget and check
//KWindowEffects::enableBlurBehind(widget->winId(), false);
if (!widget)
continue;
if (widget->winId() <= 0)
continue;
bool hasMask = false;
if (widget->mask().isNull())
hasMask = true;
QVariant regionValue = widget->property("blurRegion");
QRegion region = qvariant_cast<QRegion>(regionValue);
if (widget->inherits("QMenu")) {
//skip menu which has style sheet.
if (!widget->styleSheet().isEmpty() || qApp->styleSheet().contains("QMenu")) {
break;
}
QPainterPath path;
path.addRoundedRect(widget->rect().adjusted(+2,+8,-2,-8), 8, 8);
KWindowEffects::enableBlurBehind(widget->winId(), true, path.toFillPolygon().toPolygon());
if (!updateBlurRegionOnly)
widget->update();
break;
}
if (widget->inherits("QTipLabel")) {
QPainterPath path;
path.addRoundedRect(widget->rect().adjusted(+4,+4,-4,-4), 6, 6);
KWindowEffects::enableBlurBehind(widget->winId(), true, path.toFillPolygon().toPolygon());
if (!updateBlurRegionOnly)
widget->update();
break;
}
if (!hasMask && region.isEmpty())
break;
//qDebug()<<regionValue<<region;
//qDebug()<<widget->metaObject()->className()<<widget->geometry()<<widget->mask();
if (!region.isEmpty()) {
//qDebug()<<"blur region"<<region;
KWindowEffects::enableBlurBehind(widget->winId(), true, region);
if (!updateBlurRegionOnly)
widget->update();
} else {
//qDebug()<<widget->mask();
KWindowEffects::enableBlurBehind(widget->winId(), true, widget->mask());
if (!updateBlurRegionOnly)
widget->update(widget->mask());
}
//NOTE: we can not setAttribute Qt::WA_TranslucentBackground here,
//because the window is about to be shown.
//widget->setAttribute(Qt::WA_TranslucentBackground);
//KWindowEffects::enableBlurBehind(widget->winId(), true);
//widget->update();
}
m_update_list.clear();
} else {
m_timer.start();
}
}
void BlurHelper::confirmBlurEnableDelay()
{
QTimer::singleShot(3000, this, [=]() {
bool enable = m_blur_enable;
if (enable) {
qApp->setProperty("blurEnable", true);
} else {
qApp->setProperty("blurEnable", false);
}
for (auto widget : qApp->allWidgets()) {
widget->update();
if (m_blur_widgets.contains(widget)) {
if (widget->winId() > 0)
KWindowEffects::enableBlurBehind(widget->winId(), enable);
}
}
// QTimer::singleShot(100, this, [=](){
// for (auto widget : m_blur_widgets) {
// if (!widget)
// continue;
// if (widget->winId() > 0)
// KWindowEffects::enableBlurBehind(widget->winId(), enable);
// }
// });
});
}