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 }