File indexing completed on 2024-05-19 09:27:57
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 ®ion) 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 }