1 /* 2 * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr> 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 */ 6 7 #include "breezemdiwindowshadow.h" 8 9 #include "breezemetrics.h" 10 #include "breezeboxshadowrenderer.h" 11 #include "breezeshadowhelper.h" 12 #include "breezestyleconfigdata.h" 13 14 #include <QMdiArea> 15 #include <QMdiSubWindow> 16 #include <QPainter> 17 #include <QTextStream> 18 19 namespace Breeze 20 { 21 22 //____________________________________________________________________ MdiWindowShadow(QWidget * parent,const TileSet & shadowTiles)23 MdiWindowShadow::MdiWindowShadow( QWidget* parent, const TileSet &shadowTiles ): 24 QWidget( parent ), 25 _shadowTiles( shadowTiles ) 26 { 27 setAttribute( Qt::WA_OpaquePaintEvent, false ); 28 setAttribute( Qt::WA_TransparentForMouseEvents, true ); 29 setFocusPolicy( Qt::NoFocus ); 30 } 31 32 //____________________________________________________________________ updateGeometry()33 void MdiWindowShadow::updateGeometry() 34 { 35 if( !_widget ) return; 36 37 // metrics 38 const CompositeShadowParams params = ShadowHelper::lookupShadowParams( StyleConfigData::shadowSize() ); 39 if( params.isNone() ) return; 40 41 const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius) 42 .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); 43 44 const QSize shadowSize = BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow1.radius, params.shadow1.offset) 45 .expandedTo(BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow2.radius, params.shadow2.offset)); 46 47 const QRect shadowRect(QPoint(0, 0), shadowSize); 48 49 QRect boxRect(QPoint(0, 0), boxSize); 50 boxRect.moveCenter(shadowRect.center()); 51 52 const int topSize( boxRect.top() - shadowRect.top() - Metrics::Shadow_Overlap - params.offset.y() ); 53 const int bottomSize( shadowRect.bottom() - boxRect.bottom() - Metrics::Shadow_Overlap + params.offset.y() ); 54 const int leftSize( boxRect.left() - shadowRect.left() - Metrics::Shadow_Overlap - params.offset.x() ); 55 const int rightSize( shadowRect.right() - boxRect.right() - Metrics::Shadow_Overlap + params.offset.x() ); 56 57 // get tileSet rect 58 auto hole = _widget->frameGeometry(); 59 _shadowTilesRect = hole.adjusted( -leftSize, -topSize, rightSize, bottomSize ); 60 61 // get parent MDI area's viewport 62 auto parent( parentWidget() ); 63 if (parent && !qobject_cast<QMdiArea *>(parent) && qobject_cast<QMdiArea*>(parent->parentWidget())) 64 { parent = parent->parentWidget(); } 65 66 if( qobject_cast<QAbstractScrollArea *>( parent ) ) 67 { parent = qobject_cast<QAbstractScrollArea *>( parent )->viewport(); } 68 69 // set geometry 70 QRect geometry( _shadowTilesRect ); 71 if( parent ) 72 { 73 geometry &= parent->rect(); 74 hole &= parent->rect(); 75 } 76 77 // update geometry and mask 78 const QRegion mask = QRegion( geometry ) - hole.adjusted( 2, 2, -2, -2 ); 79 if( mask.isEmpty() ) hide(); 80 else { 81 82 setGeometry( geometry ); 83 setMask( mask.translated( -geometry.topLeft() ) ); 84 show(); 85 86 } 87 88 // translate rendering rect 89 _shadowTilesRect.translate( -geometry.topLeft() ); 90 91 } 92 93 //____________________________________________________________________ updateZOrder()94 void MdiWindowShadow::updateZOrder() 95 { stackUnder( _widget ); } 96 97 //____________________________________________________________________ paintEvent(QPaintEvent * event)98 void MdiWindowShadow::paintEvent( QPaintEvent* event ) 99 { 100 101 if( !_shadowTiles.isValid() ) return; 102 103 QPainter painter( this ); 104 painter.setRenderHints( QPainter::Antialiasing ); 105 painter.setClipRegion( event->region() ); 106 _shadowTiles.render( _shadowTilesRect, &painter ); 107 108 } 109 110 //____________________________________________________________________ MdiWindowShadowFactory(QObject * parent)111 MdiWindowShadowFactory::MdiWindowShadowFactory( QObject* parent ): 112 QObject( parent ) 113 {} 114 115 //____________________________________________________________________________________ registerWidget(QWidget * widget)116 bool MdiWindowShadowFactory::registerWidget( QWidget* widget ) 117 { 118 119 // check widget type 120 auto subwindow( qobject_cast<QMdiSubWindow*>( widget ) ); 121 if( !subwindow ) return false; 122 if( subwindow->widget() && subwindow->widget()->inherits( "KMainWindow" ) ) return false; 123 124 // make sure widget is not already registered 125 if( isRegistered( widget ) ) return false; 126 127 // store in set 128 _registeredWidgets.insert( widget ); 129 130 // create shadow immediately if widget is already visible 131 if( widget->isVisible() ) 132 { 133 installShadow( widget ); 134 updateShadowGeometry( widget ); 135 updateShadowZOrder( widget ); 136 } 137 138 widget->installEventFilter( this ); 139 140 // catch object destruction 141 connect( widget, &QObject::destroyed, this, &MdiWindowShadowFactory::widgetDestroyed ); 142 143 return true; 144 145 } 146 147 //____________________________________________________________________________________ unregisterWidget(QWidget * widget)148 void MdiWindowShadowFactory::unregisterWidget( QWidget* widget ) 149 { 150 if( !isRegistered( widget ) ) return; 151 widget->removeEventFilter( this ); 152 _registeredWidgets.remove( widget ); 153 removeShadow( widget ); 154 } 155 156 //____________________________________________________________________________________ eventFilter(QObject * object,QEvent * event)157 bool MdiWindowShadowFactory::eventFilter( QObject* object, QEvent* event ) 158 { 159 160 switch( event->type() ) 161 { 162 // TODO: possibly implement ZOrderChange event, to make sure that 163 // the shadow is always painted on top 164 case QEvent::ZOrderChange: 165 updateShadowZOrder( object ); 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 if ( !_shadowHelper ) return; 221 222 // create new shadow 223 auto windowShadow( new MdiWindowShadow( widget->parentWidget(), _shadowHelper->shadowTiles() ) ); 224 windowShadow->setWidget( widget ); 225 226 } 227 228 //____________________________________________________________________________________ removeShadow(QObject * object)229 void MdiWindowShadowFactory::removeShadow( QObject* object ) 230 { 231 if( MdiWindowShadow* windowShadow = findShadow( object ) ) 232 { 233 windowShadow->hide(); 234 windowShadow->deleteLater(); 235 } 236 } 237 238 //____________________________________________________________________________________ widgetDestroyed(QObject * object)239 void MdiWindowShadowFactory::widgetDestroyed( QObject* object ) 240 { 241 _registeredWidgets.remove( object ); 242 removeShadow( object ); 243 } 244 245 } 246