1 ////////////////////////////////////////////////////////////////////////////// 2 // oxygenmdiwindowshadow.cpp 3 // handle MDI windows' shadows 4 // ------------------- 5 // 6 // SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 7 // 8 // Largely inspired from skulpture widget style 9 // SPDX-FileCopyrightText: 2007-2009 Christoph Feck <christoph@maxiom.de> 10 // 11 // SPDX-License-Identifier: MIT 12 ////////////////////////////////////////////////////////////////////////////// 13 14 #include "oxygenmdiwindowshadow.h" 15 #include "oxygenshadowcache.h" 16 17 #include <QMdiArea> 18 #include <QMdiSubWindow> 19 #include <QPainter> 20 #include <QTextStream> 21 22 namespace Oxygen 23 { 24 25 //____________________________________________________________________ MdiWindowShadow(QWidget * parent,TileSet shadowTiles)26 MdiWindowShadow::MdiWindowShadow( QWidget* parent, TileSet shadowTiles ): 27 QWidget( parent ), 28 _shadowTiles( shadowTiles ) 29 { 30 setAttribute( Qt::WA_OpaquePaintEvent, false ); 31 setAttribute( Qt::WA_TransparentForMouseEvents, true ); 32 setFocusPolicy( Qt::NoFocus ); 33 } 34 35 //____________________________________________________________________ updateGeometry(void)36 void MdiWindowShadow::updateGeometry( void ) 37 { 38 if( !_widget ) return; 39 40 // get tileSet rect 41 auto hole = _widget->frameGeometry().adjusted(1, 1, -1, -1 ); 42 _shadowTilesRect = _widget->frameGeometry().adjusted( -ShadowSize, -ShadowSize, ShadowSize, ShadowSize ); 43 44 // get parent MDI area's viewport 45 auto parent( parentWidget() ); 46 if (parent && !qobject_cast<QMdiArea *>(parent) && qobject_cast<QMdiArea*>(parent->parentWidget())) 47 { parent = parent->parentWidget(); } 48 49 if( qobject_cast<QAbstractScrollArea *>( parent ) ) 50 { parent = qobject_cast<QAbstractScrollArea *>( parent )->viewport(); } 51 52 // set geometry 53 QRect geometry( _shadowTilesRect ); 54 if( parent ) 55 { 56 geometry &= parent->rect(); 57 hole &= parent->rect(); 58 } 59 60 // update geometry and mask 61 const QRegion mask = QRegion( geometry ) - hole; 62 if( mask.isEmpty() ) hide(); 63 else { 64 65 setGeometry( geometry ); 66 setMask( mask.translated( -geometry.topLeft() ) ); 67 show(); 68 69 } 70 71 // translate rendering rect 72 _shadowTilesRect.translate( -geometry.topLeft() ); 73 74 } 75 76 //____________________________________________________________________ updateZOrder(void)77 void MdiWindowShadow::updateZOrder( void ) 78 { stackUnder( _widget ); } 79 80 //____________________________________________________________________ paintEvent(QPaintEvent * event)81 void MdiWindowShadow::paintEvent( QPaintEvent* event ) 82 { 83 84 if( !_shadowTiles.isValid() ) return; 85 86 QPainter painter( this ); 87 painter.setRenderHints( QPainter::Antialiasing ); 88 painter.setClipRegion( event->region() ); 89 _shadowTiles.render( _shadowTilesRect, &painter ); 90 91 } 92 93 //____________________________________________________________________ MdiWindowShadowFactory(QObject * parent,StyleHelper & helper)94 MdiWindowShadowFactory::MdiWindowShadowFactory( QObject* parent, StyleHelper& helper ): 95 QObject( parent ) 96 { 97 98 // create shadow cache 99 ShadowCache cache( helper ); 100 cache.setShadowSize( QPalette::Inactive, MdiWindowShadow::ShadowSize ); 101 cache.setShadowSize( QPalette::Active, MdiWindowShadow::ShadowSize ); 102 103 // get tileset 104 _shadowTiles = cache.tileSet( ShadowCache::Key() ); 105 } 106 107 //____________________________________________________________________________________ registerWidget(QWidget * widget)108 bool MdiWindowShadowFactory::registerWidget( QWidget* widget ) 109 { 110 111 // check widget type 112 auto subwindow( qobject_cast<QMdiSubWindow*>( widget ) ); 113 if( !subwindow ) return false; 114 if( subwindow->widget() && subwindow->widget()->inherits( "KMainWindow" ) ) return false; 115 116 // make sure widget is not already registered 117 if( isRegistered( widget ) ) return false; 118 119 // store in set 120 _registeredWidgets.insert( widget ); 121 122 // create shadow immediatly if widget is already visible 123 if( widget->isVisible() ) 124 { 125 installShadow( widget ); 126 updateShadowGeometry( widget ); 127 updateShadowZOrder( widget ); 128 } 129 130 widget->installEventFilter( this ); 131 132 // catch object destruction 133 connect( widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)) ); 134 135 return true; 136 137 } 138 139 //____________________________________________________________________________________ unregisterWidget(QWidget * widget)140 void MdiWindowShadowFactory::unregisterWidget( QWidget* widget ) 141 { 142 if( !isRegistered( widget ) ) return; 143 widget->removeEventFilter( this ); 144 _registeredWidgets.remove( widget ); 145 removeShadow( widget ); 146 } 147 148 //____________________________________________________________________________________ eventFilter(QObject * object,QEvent * event)149 bool MdiWindowShadowFactory::eventFilter( QObject* object, QEvent* event ) 150 { 151 152 switch( event->type() ) 153 { 154 // TODO: possibly implement ZOrderChange event, to make sure that 155 // the shadow is always painted on top 156 case QEvent::ZOrderChange: 157 updateShadowZOrder( object ); 158 break; 159 160 case QEvent::Destroy: 161 if( isRegistered( object ) ) 162 { 163 _registeredWidgets.remove( object ); 164 removeShadow( object ); 165 } 166 break; 167 168 case QEvent::Hide: 169 hideShadows( object ); 170 break; 171 172 case QEvent::Show: 173 installShadow( object ); 174 updateShadowGeometry( object ); 175 updateShadowZOrder( object ); 176 break; 177 178 case QEvent::Move: 179 case QEvent::Resize: 180 updateShadowGeometry( object ); 181 break; 182 183 default: break; 184 } 185 186 return QObject::eventFilter( object, event ); 187 188 } 189 190 //____________________________________________________________________________________ findShadow(QObject * object) const191 MdiWindowShadow* MdiWindowShadowFactory::findShadow( QObject* object ) const 192 { 193 194 // check object, 195 if( !object->parent() ) return nullptr; 196 197 // find existing window shadows 198 auto children = object->parent()->children(); 199 foreach( QObject *child, children ) 200 { 201 if( MdiWindowShadow* shadow = qobject_cast<MdiWindowShadow*>(child) ) 202 { if( shadow->widget() == object ) return shadow; } 203 } 204 205 return nullptr; 206 207 } 208 209 //____________________________________________________________________________________ installShadow(QObject * object)210 void MdiWindowShadowFactory::installShadow( QObject* object ) 211 { 212 213 // cast 214 auto widget( static_cast<QWidget*>( object ) ); 215 if( !widget->parentWidget() ) return; 216 217 // make sure shadow is not already installed 218 if( findShadow( object ) ) return; 219 220 // create new shadow 221 auto windowShadow( new MdiWindowShadow( widget->parentWidget(), _shadowTiles ) ); 222 windowShadow->setWidget( widget ); 223 return; 224 225 } 226 227 //____________________________________________________________________________________ removeShadow(QObject * object)228 void MdiWindowShadowFactory::removeShadow( QObject* object ) 229 { 230 if( MdiWindowShadow* windowShadow = findShadow( object ) ) 231 { 232 windowShadow->hide(); 233 windowShadow->deleteLater(); 234 } 235 } 236 237 //____________________________________________________________________________________ widgetDestroyed(QObject * object)238 void MdiWindowShadowFactory::widgetDestroyed( QObject* object ) 239 { _registeredWidgets.remove( object ); } 240 241 } 242