File indexing completed on 2024-04-28 05:26:22
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "breezemdiwindowshadow.h" 0008 0009 #include "breezeboxshadowrenderer.h" 0010 #include "breezemetrics.h" 0011 #include "breezeshadowhelper.h" 0012 #include "breezestyleconfigdata.h" 0013 0014 #include <QMdiArea> 0015 #include <QMdiSubWindow> 0016 #include <QPainter> 0017 #include <QTextStream> 0018 0019 namespace Breeze 0020 { 0021 //____________________________________________________________________ 0022 MdiWindowShadow::MdiWindowShadow(QWidget *parent, const TileSet &shadowTiles) 0023 : QWidget(parent) 0024 , _shadowTiles(shadowTiles) 0025 { 0026 setAttribute(Qt::WA_OpaquePaintEvent, false); 0027 setAttribute(Qt::WA_TransparentForMouseEvents, true); 0028 setFocusPolicy(Qt::NoFocus); 0029 } 0030 0031 //____________________________________________________________________ 0032 void MdiWindowShadow::updateGeometry() 0033 { 0034 if (!_widget) { 0035 return; 0036 } 0037 0038 // metrics 0039 const CompositeShadowParams params = ShadowHelper::lookupShadowParams(StyleConfigData::shadowSize()); 0040 if (params.isNone()) { 0041 return; 0042 } 0043 0044 const QSize boxSize = 0045 BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius).expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); 0046 0047 const QSize shadowSize = BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow1.radius, params.shadow1.offset) 0048 .expandedTo(BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow2.radius, params.shadow2.offset)); 0049 0050 const QRect shadowRect(QPoint(0, 0), shadowSize); 0051 0052 QRect boxRect(QPoint(0, 0), boxSize); 0053 boxRect.moveCenter(shadowRect.center()); 0054 0055 const int topSize(boxRect.top() - shadowRect.top() - Metrics::Shadow_Overlap - params.offset.y()); 0056 const int bottomSize(shadowRect.bottom() - boxRect.bottom() - Metrics::Shadow_Overlap + params.offset.y()); 0057 const int leftSize(boxRect.left() - shadowRect.left() - Metrics::Shadow_Overlap - params.offset.x()); 0058 const int rightSize(shadowRect.right() - boxRect.right() - Metrics::Shadow_Overlap + params.offset.x()); 0059 0060 // get tileSet rect 0061 auto hole = _widget->frameGeometry(); 0062 _shadowTilesRect = hole.adjusted(-leftSize, -topSize, rightSize, bottomSize); 0063 0064 // get parent MDI area's viewport 0065 auto parent(parentWidget()); 0066 if (parent && !qobject_cast<QMdiArea *>(parent) && qobject_cast<QMdiArea *>(parent->parentWidget())) { 0067 parent = parent->parentWidget(); 0068 } 0069 0070 if (qobject_cast<QAbstractScrollArea *>(parent)) { 0071 parent = qobject_cast<QAbstractScrollArea *>(parent)->viewport(); 0072 } 0073 0074 // set geometry 0075 QRect geometry(_shadowTilesRect); 0076 if (parent) { 0077 geometry &= parent->rect(); 0078 hole &= parent->rect(); 0079 } 0080 0081 // update geometry and mask 0082 const QRegion mask = QRegion(geometry) - hole.adjusted(2, 2, -2, -2); 0083 if (mask.isEmpty()) { 0084 hide(); 0085 } else { 0086 setGeometry(geometry); 0087 setMask(mask.translated(-geometry.topLeft())); 0088 show(); 0089 } 0090 0091 // translate rendering rect 0092 _shadowTilesRect.translate(-geometry.topLeft()); 0093 } 0094 0095 //____________________________________________________________________ 0096 void MdiWindowShadow::updateZOrder() 0097 { 0098 stackUnder(_widget); 0099 } 0100 0101 //____________________________________________________________________ 0102 void MdiWindowShadow::paintEvent(QPaintEvent *event) 0103 { 0104 if (!_shadowTiles.isValid()) { 0105 return; 0106 } 0107 0108 QPainter painter(this); 0109 painter.setRenderHints(QPainter::Antialiasing); 0110 painter.setClipRegion(event->region()); 0111 _shadowTiles.render(_shadowTilesRect, &painter); 0112 } 0113 0114 //____________________________________________________________________ 0115 MdiWindowShadowFactory::MdiWindowShadowFactory(QObject *parent) 0116 : QObject(parent) 0117 { 0118 } 0119 0120 //____________________________________________________________________________________ 0121 bool MdiWindowShadowFactory::registerWidget(QWidget *widget) 0122 { 0123 // check widget type 0124 auto subwindow(qobject_cast<QMdiSubWindow *>(widget)); 0125 if (!subwindow) { 0126 return false; 0127 } 0128 if (subwindow->widget() && subwindow->widget()->inherits("KMainWindow")) { 0129 return false; 0130 } 0131 0132 // make sure widget is not already registered 0133 if (isRegistered(widget)) { 0134 return false; 0135 } 0136 0137 // store in set 0138 _registeredWidgets.insert(widget); 0139 0140 // create shadow immediately if widget is already visible 0141 if (widget->isVisible()) { 0142 installShadow(widget); 0143 updateShadowGeometry(widget); 0144 updateShadowZOrder(widget); 0145 } 0146 0147 widget->installEventFilter(this); 0148 0149 // catch object destruction 0150 connect(widget, &QObject::destroyed, this, &MdiWindowShadowFactory::widgetDestroyed); 0151 0152 return true; 0153 } 0154 0155 //____________________________________________________________________________________ 0156 void MdiWindowShadowFactory::unregisterWidget(QWidget *widget) 0157 { 0158 if (!isRegistered(widget)) { 0159 return; 0160 } 0161 widget->removeEventFilter(this); 0162 _registeredWidgets.remove(widget); 0163 removeShadow(widget); 0164 } 0165 0166 //____________________________________________________________________________________ 0167 bool MdiWindowShadowFactory::eventFilter(QObject *object, QEvent *event) 0168 { 0169 switch (event->type()) { 0170 // TODO: possibly implement ZOrderChange event, to make sure that 0171 // the shadow is always painted on top 0172 case QEvent::ZOrderChange: 0173 updateShadowZOrder(object); 0174 break; 0175 0176 case QEvent::Hide: 0177 hideShadows(object); 0178 break; 0179 0180 case QEvent::Show: 0181 installShadow(object); 0182 updateShadowGeometry(object); 0183 updateShadowZOrder(object); 0184 break; 0185 0186 case QEvent::Move: 0187 case QEvent::Resize: 0188 updateShadowGeometry(object); 0189 break; 0190 0191 default: 0192 break; 0193 } 0194 0195 return QObject::eventFilter(object, event); 0196 } 0197 0198 //____________________________________________________________________________________ 0199 MdiWindowShadow *MdiWindowShadowFactory::findShadow(QObject *object) const 0200 { 0201 // check object, 0202 if (!object->parent()) { 0203 return nullptr; 0204 } 0205 0206 // find existing window shadows 0207 const auto children = object->parent()->children(); 0208 for (QObject *child : children) { 0209 if (MdiWindowShadow *shadow = qobject_cast<MdiWindowShadow *>(child)) { 0210 if (shadow->widget() == object) { 0211 return shadow; 0212 } 0213 } 0214 } 0215 0216 return nullptr; 0217 } 0218 0219 //____________________________________________________________________________________ 0220 void MdiWindowShadowFactory::installShadow(QObject *object) 0221 { 0222 // cast 0223 auto widget(static_cast<QWidget *>(object)); 0224 if (!widget->parentWidget()) { 0225 return; 0226 } 0227 0228 // make sure shadow is not already installed 0229 if (findShadow(object)) { 0230 return; 0231 } 0232 0233 if (!_shadowHelper) { 0234 return; 0235 } 0236 0237 // create new shadow 0238 auto windowShadow(new MdiWindowShadow(widget->parentWidget(), _shadowHelper->shadowTiles(widget))); 0239 windowShadow->setWidget(widget); 0240 } 0241 0242 //____________________________________________________________________________________ 0243 void MdiWindowShadowFactory::removeShadow(QObject *object) 0244 { 0245 if (MdiWindowShadow *windowShadow = findShadow(object)) { 0246 windowShadow->hide(); 0247 windowShadow->deleteLater(); 0248 } 0249 } 0250 0251 //____________________________________________________________________________________ 0252 void MdiWindowShadowFactory::widgetDestroyed(QObject *object) 0253 { 0254 _registeredWidgets.remove(object); 0255 removeShadow(object); 0256 } 0257 0258 }