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 }