File indexing completed on 2024-05-05 05:35:25

0001 //////////////////////////////////////////////////////////////////////////////
0002 // oxygenmdiwindowshadow.cpp
0003 // handle MDI windows' shadows
0004 // -------------------
0005 //
0006 // SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0007 //
0008 // Largely inspired from skulpture widget style
0009 // SPDX-FileCopyrightText: 2007-2009 Christoph Feck <christoph@maxiom.de>
0010 //
0011 // SPDX-License-Identifier: MIT
0012 //////////////////////////////////////////////////////////////////////////////
0013 
0014 #include "oxygenmdiwindowshadow.h"
0015 #include "oxygenshadowcache.h"
0016 
0017 #include <QMdiArea>
0018 #include <QMdiSubWindow>
0019 #include <QPainter>
0020 #include <QTextStream>
0021 
0022 namespace Oxygen
0023 {
0024 //____________________________________________________________________
0025 MdiWindowShadow::MdiWindowShadow(QWidget *parent, TileSet shadowTiles)
0026     : QWidget(parent)
0027     , _shadowTiles(shadowTiles)
0028 {
0029     setAttribute(Qt::WA_OpaquePaintEvent, false);
0030     setAttribute(Qt::WA_TransparentForMouseEvents, true);
0031     setFocusPolicy(Qt::NoFocus);
0032 }
0033 
0034 //____________________________________________________________________
0035 void MdiWindowShadow::updateGeometry(void)
0036 {
0037     if (!_widget)
0038         return;
0039 
0040     // get tileSet rect
0041     auto hole = _widget->frameGeometry().adjusted(1, 1, -1, -1);
0042     _shadowTilesRect = _widget->frameGeometry().adjusted(-ShadowSize, -ShadowSize, ShadowSize, ShadowSize);
0043 
0044     // get parent MDI area's viewport
0045     auto parent(parentWidget());
0046     if (parent && !qobject_cast<QMdiArea *>(parent) && qobject_cast<QMdiArea *>(parent->parentWidget())) {
0047         parent = parent->parentWidget();
0048     }
0049 
0050     if (qobject_cast<QAbstractScrollArea *>(parent)) {
0051         parent = qobject_cast<QAbstractScrollArea *>(parent)->viewport();
0052     }
0053 
0054     // set geometry
0055     QRect geometry(_shadowTilesRect);
0056     if (parent) {
0057         geometry &= parent->rect();
0058         hole &= parent->rect();
0059     }
0060 
0061     // update geometry and mask
0062     const QRegion mask = QRegion(geometry) - hole;
0063     if (mask.isEmpty())
0064         hide();
0065     else {
0066         setGeometry(geometry);
0067         setMask(mask.translated(-geometry.topLeft()));
0068         show();
0069     }
0070 
0071     // translate rendering rect
0072     _shadowTilesRect.translate(-geometry.topLeft());
0073 }
0074 
0075 //____________________________________________________________________
0076 void MdiWindowShadow::updateZOrder(void)
0077 {
0078     stackUnder(_widget);
0079 }
0080 
0081 //____________________________________________________________________
0082 void MdiWindowShadow::paintEvent(QPaintEvent *event)
0083 {
0084     if (!_shadowTiles.isValid())
0085         return;
0086 
0087     QPainter painter(this);
0088     painter.setRenderHints(QPainter::Antialiasing);
0089     painter.setClipRegion(event->region());
0090     _shadowTiles.render(_shadowTilesRect, &painter);
0091 }
0092 
0093 //____________________________________________________________________
0094 MdiWindowShadowFactory::MdiWindowShadowFactory(QObject *parent, StyleHelper &helper)
0095     : QObject(parent)
0096 {
0097     // create shadow cache
0098     ShadowCache cache(helper);
0099     cache.setShadowSize(QPalette::Inactive, MdiWindowShadow::ShadowSize);
0100     cache.setShadowSize(QPalette::Active, MdiWindowShadow::ShadowSize);
0101 
0102     // get tileset
0103     _shadowTiles = cache.tileSet(ShadowCache::Key());
0104 }
0105 
0106 //____________________________________________________________________________________
0107 bool MdiWindowShadowFactory::registerWidget(QWidget *widget)
0108 {
0109     // check widget type
0110     auto subwindow(qobject_cast<QMdiSubWindow *>(widget));
0111     if (!subwindow)
0112         return false;
0113     if (subwindow->widget() && subwindow->widget()->inherits("KMainWindow"))
0114         return false;
0115 
0116     // make sure widget is not already registered
0117     if (isRegistered(widget))
0118         return false;
0119 
0120     // store in set
0121     _registeredWidgets.insert(widget);
0122 
0123     // create shadow immediatly if widget is already visible
0124     if (widget->isVisible()) {
0125         installShadow(widget);
0126         updateShadowGeometry(widget);
0127         updateShadowZOrder(widget);
0128     }
0129 
0130     widget->installEventFilter(this);
0131 
0132     // catch object destruction
0133     connect(widget, SIGNAL(destroyed(QObject *)), SLOT(widgetDestroyed(QObject *)));
0134 
0135     return true;
0136 }
0137 
0138 //____________________________________________________________________________________
0139 void MdiWindowShadowFactory::unregisterWidget(QWidget *widget)
0140 {
0141     if (!isRegistered(widget))
0142         return;
0143     widget->removeEventFilter(this);
0144     _registeredWidgets.remove(widget);
0145     removeShadow(widget);
0146 }
0147 
0148 //____________________________________________________________________________________
0149 bool MdiWindowShadowFactory::eventFilter(QObject *object, QEvent *event)
0150 {
0151     switch (event->type()) {
0152     // TODO: possibly implement ZOrderChange event, to make sure that
0153     // the shadow is always painted on top
0154     case QEvent::ZOrderChange:
0155         updateShadowZOrder(object);
0156         break;
0157 
0158     case QEvent::Destroy:
0159         if (isRegistered(object)) {
0160             _registeredWidgets.remove(object);
0161             removeShadow(object);
0162         }
0163         break;
0164 
0165     case QEvent::Hide:
0166         hideShadows(object);
0167         break;
0168 
0169     case QEvent::Show:
0170         installShadow(object);
0171         updateShadowGeometry(object);
0172         updateShadowZOrder(object);
0173         break;
0174 
0175     case QEvent::Move:
0176     case QEvent::Resize:
0177         updateShadowGeometry(object);
0178         break;
0179 
0180     default:
0181         break;
0182     }
0183 
0184     return QObject::eventFilter(object, event);
0185 }
0186 
0187 //____________________________________________________________________________________
0188 MdiWindowShadow *MdiWindowShadowFactory::findShadow(QObject *object) const
0189 {
0190     // check object,
0191     if (!object->parent())
0192         return nullptr;
0193 
0194     // find existing window shadows
0195     const auto children = object->parent()->children();
0196     for (QObject *child : children) {
0197         if (MdiWindowShadow *shadow = qobject_cast<MdiWindowShadow *>(child)) {
0198             if (shadow->widget() == object)
0199                 return shadow;
0200         }
0201     }
0202 
0203     return nullptr;
0204 }
0205 
0206 //____________________________________________________________________________________
0207 void MdiWindowShadowFactory::installShadow(QObject *object)
0208 {
0209     // cast
0210     auto widget(static_cast<QWidget *>(object));
0211     if (!widget->parentWidget())
0212         return;
0213 
0214     // make sure shadow is not already installed
0215     if (findShadow(object))
0216         return;
0217 
0218     // create new shadow
0219     auto windowShadow(new MdiWindowShadow(widget->parentWidget(), _shadowTiles));
0220     windowShadow->setWidget(widget);
0221     return;
0222 }
0223 
0224 //____________________________________________________________________________________
0225 void MdiWindowShadowFactory::removeShadow(QObject *object)
0226 {
0227     if (MdiWindowShadow *windowShadow = findShadow(object)) {
0228         windowShadow->hide();
0229         windowShadow->deleteLater();
0230     }
0231 }
0232 
0233 //____________________________________________________________________________________
0234 void MdiWindowShadowFactory::widgetDestroyed(QObject *object)
0235 {
0236     _registeredWidgets.remove(object);
0237 }
0238 }