File indexing completed on 2024-05-19 09:27:57

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