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