Warning, file /plasma/oxygen/kstyle/oxygenblurhelper.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //////////////////////////////////////////////////////////////////////////////
0002 // oxygenblurhelper.cpp
0003 // handle regions passed to kwin for blurring
0004 // -------------------
0005 //
0006 // SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0007 //
0008 // Loosely inspired (and largely rewritten) from BeSpin style
0009 // SPDX-FileCopyrightText: 2007 Thomas Luebking <thomas.luebking@web.de>
0010 //
0011 // SPDX-License-Identifier: MIT
0012 //////////////////////////////////////////////////////////////////////////////
0013 
0014 #include "oxygenblurhelper.h"
0015 
0016 #include "oxygenstyleconfigdata.h"
0017 
0018 #include <QEvent>
0019 #include <QProgressBar>
0020 #include <QPushButton>
0021 #include <QVector>
0022 
0023 namespace Oxygen
0024 {
0025 
0026 //___________________________________________________________
0027 BlurHelper::BlurHelper(QObject *parent, StyleHelper &helper)
0028     : QObject(parent)
0029     , _helper(helper)
0030     , _enabled(false)
0031 {
0032 #if OXYGEN_HAVE_X11
0033 
0034     if (_helper.isX11()) {
0035         // create atom
0036         _blurAtom = _helper.createAtom(QStringLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"));
0037         _opaqueAtom = _helper.createAtom(QStringLiteral("_NET_WM_OPAQUE_REGION"));
0038     } else {
0039         _blurAtom = 0;
0040         _opaqueAtom = 0;
0041     }
0042 
0043 #endif
0044 }
0045 
0046 //___________________________________________________________
0047 void BlurHelper::registerWidget(QWidget *widget)
0048 {
0049     // check if already registered
0050     if (_widgets.contains(widget))
0051         return;
0052 
0053     // install event filter
0054     addEventFilter(widget);
0055 
0056     // add to widgets list
0057     _widgets.insert(widget);
0058 
0059     // cleanup on destruction
0060     connect(widget, SIGNAL(destroyed(QObject *)), SLOT(widgetDestroyed(QObject *)));
0061 
0062     if (enabled()) {
0063         // schedule shadow area repaint
0064         _pendingWidgets.insert(widget, widget);
0065         update();
0066     }
0067 }
0068 
0069 //___________________________________________________________
0070 void BlurHelper::unregisterWidget(QWidget *widget)
0071 {
0072     // remove from widgets
0073     if (!_widgets.remove(widget))
0074         return;
0075 
0076     // remove event filter
0077     widget->removeEventFilter(this);
0078 
0079     if (isTransparent(widget))
0080         clear(widget);
0081 }
0082 
0083 //___________________________________________________________
0084 bool BlurHelper::eventFilter(QObject *object, QEvent *event)
0085 {
0086     // do nothing if not enabled
0087     if (!enabled())
0088         return false;
0089 
0090     switch (event->type()) {
0091     case QEvent::Hide: {
0092         QWidget *widget(qobject_cast<QWidget *>(object));
0093         if (widget && isOpaque(widget) && isTransparent(widget->window())) {
0094             QWidget *window(widget->window());
0095             _pendingWidgets.insert(window, window);
0096             update();
0097         }
0098         break;
0099     }
0100 
0101     case QEvent::Show:
0102     case QEvent::Resize: {
0103         // cast to widget and check
0104         QWidget *widget(qobject_cast<QWidget *>(object));
0105         if (!widget)
0106             break;
0107         if (isTransparent(widget)) {
0108             _pendingWidgets.insert(widget, widget);
0109             update();
0110 
0111         } else if (isOpaque(widget)) {
0112             QWidget *window(widget->window());
0113             if (isTransparent(window)) {
0114                 _pendingWidgets.insert(window, window);
0115                 update();
0116             }
0117         }
0118 
0119         break;
0120     }
0121 
0122     default:
0123         break;
0124     }
0125 
0126     // never eat events
0127     return false;
0128 }
0129 
0130 //___________________________________________________________
0131 QRegion BlurHelper::blurRegion(QWidget *widget) const
0132 {
0133     if (!widget->isVisible())
0134         return QRegion();
0135 
0136     // get main region
0137     QRegion region;
0138     if (qobject_cast<const QDockWidget *>(widget) || qobject_cast<const QMenu *>(widget) || qobject_cast<const QToolBar *>(widget)
0139         || widget->inherits("QComboBoxPrivateContainer")) {
0140         region = _helper.roundedMask(widget->rect());
0141 
0142     } else
0143         region = widget->mask().isEmpty() ? widget->rect() : widget->mask();
0144 
0145     // trim blur region to remove unnecessary areas
0146     trimBlurRegion(widget, widget, region);
0147     return region;
0148 }
0149 
0150 //___________________________________________________________
0151 void BlurHelper::trimBlurRegion(QWidget *parent, QWidget *widget, QRegion &region) const
0152 {
0153     // loop over children
0154     const auto children = widget->children();
0155     for (QObject *childObject : children) {
0156         QWidget *child(qobject_cast<QWidget *>(childObject));
0157         if (!(child && child->isVisible()))
0158             continue;
0159 
0160         if (isOpaque(child)) {
0161             const QPoint offset(child->mapTo(parent, QPoint(0, 0)));
0162             if (child->mask().isEmpty()) {
0163                 const QRect rect(child->rect().translated(offset).adjusted(1, 1, -1, -1));
0164                 region -= rect;
0165 
0166             } else
0167                 region -= child->mask().translated(offset);
0168 
0169         } else {
0170             trimBlurRegion(parent, child, region);
0171         }
0172     }
0173 
0174     return;
0175 }
0176 
0177 //___________________________________________________________
0178 void BlurHelper::update(QWidget *widget) const
0179 {
0180 #if OXYGEN_HAVE_X11
0181     if (!_helper.isX11())
0182         return;
0183 
0184     /*
0185     directly from bespin code. Supposibly prevent playing with some 'pseudo-widgets'
0186     that have winId matching some other -random- window
0187     */
0188     if (!(widget->testAttribute(Qt::WA_WState_Created) || widget->internalWinId())) {
0189         return;
0190     }
0191 
0192     const QRegion blurRegion(this->blurRegion(widget));
0193     const QRegion opaqueRegion = QRegion(0, 0, widget->width(), widget->height()) - blurRegion;
0194     if (blurRegion.isEmpty()) {
0195         clear(widget);
0196 
0197     } else {
0198         QVector<quint32> data;
0199         for (const QRect &rect : blurRegion) {
0200             data << rect.x() << rect.y() << rect.width() << rect.height();
0201         }
0202 
0203         xcb_change_property(_helper.connection(), XCB_PROP_MODE_REPLACE, widget->winId(), _blurAtom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
0204 
0205         data.clear();
0206         for (const QRect &rect : opaqueRegion) {
0207             data << rect.x() << rect.y() << rect.width() << rect.height();
0208         }
0209 
0210         xcb_change_property(_helper.connection(), XCB_PROP_MODE_REPLACE, widget->winId(), _opaqueAtom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
0211         xcb_flush(_helper.connection());
0212     }
0213 
0214     // force update
0215     if (widget->isVisible()) {
0216         widget->update();
0217     }
0218 
0219 #else
0220 
0221     Q_UNUSED(widget)
0222 
0223 #endif
0224 }
0225 
0226 //___________________________________________________________
0227 void BlurHelper::clear(QWidget *widget) const
0228 {
0229 #if OXYGEN_HAVE_X11
0230     if (!_helper.isX11())
0231         return;
0232 
0233     xcb_delete_property(_helper.connection(), widget->winId(), _blurAtom);
0234     xcb_delete_property(_helper.connection(), widget->winId(), _opaqueAtom);
0235 #else
0236     Q_UNUSED(widget)
0237 #endif
0238 }
0239 
0240 //___________________________________________________________
0241 bool BlurHelper::isOpaque(const QWidget *widget) const
0242 {
0243     return (!widget->isWindow())
0244         && ((widget->autoFillBackground() && widget->palette().color(widget->backgroundRole()).alpha() == 0xff)
0245             || widget->testAttribute(Qt::WA_OpaquePaintEvent));
0246 }
0247 
0248 //___________________________________________________________
0249 bool BlurHelper::isTransparent(const QWidget *widget) const
0250 {
0251     return widget->isWindow() && widget->testAttribute(Qt::WA_TranslucentBackground) &&
0252 
0253         // widgets using qgraphicsview
0254         !(widget->graphicsProxyWidget() || widget->inherits("Plasma::Dialog")) &&
0255 
0256         // flags and special widgets
0257         (widget->testAttribute(Qt::WA_StyledBackground) || qobject_cast<const QMenu *>(widget) || qobject_cast<const QDockWidget *>(widget)
0258          || qobject_cast<const QToolBar *>(widget) || widget->windowType() == Qt::ToolTip)
0259         && _helper.hasAlphaChannel(widget);
0260 }
0261 }