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