/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
This file is part of the JUCE library .
Copyright ( c ) 2015 - ROLI Ltd .
Permission is granted to use this software under the terms of either :
a ) the GPL v2 ( or any later version )
b ) the Affero GPL v3
Details of these licenses can be found at : www . gnu . org / licenses
JUCE is distributed in the hope that it will be useful , but WITHOUT ANY
WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE . See the GNU General Public License for more details .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
To release a closed - source product which uses JUCE , commercial licenses are
available : visit www . juce . com for more information .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
class DropShadower : : ShadowWindow : public Component
{
public :
ShadowWindow ( Component * comp , const DropShadow & ds )
: target ( comp ) , shadow ( ds )
{
setVisible ( true ) ;
setInterceptsMouseClicks ( false , false ) ;
if ( comp - > isOnDesktop ( ) )
{
setSize ( 1 , 1 ) ; // to keep the OS happy by not having zero-size windows
addToDesktop ( ComponentPeer : : windowIgnoresMouseClicks
| ComponentPeer : : windowIsTemporary
| ComponentPeer : : windowIgnoresKeyPresses ) ;
}
else if ( Component * const parent = comp - > getParentComponent ( ) )
{
parent - > addChildComponent ( this ) ;
}
}
void paint ( Graphics & g ) override
{
if ( Component * c = target )
shadow . drawForRectangle ( g , getLocalArea ( c , c - > getLocalBounds ( ) ) ) ;
}
void resized ( ) override
{
repaint ( ) ; // (needed for correct repainting)
}
float getDesktopScaleFactor ( ) const override
{
if ( target ! = nullptr )
return target - > getDesktopScaleFactor ( ) ;
return Component : : getDesktopScaleFactor ( ) ;
}
private :
WeakReference < Component > target ;
DropShadow shadow ;
JUCE_DECLARE_NON_COPYABLE ( ShadowWindow )
} ;
//==============================================================================
DropShadower : : DropShadower ( const DropShadow & ds )
: owner ( nullptr ) , shadow ( ds ) , reentrant ( false )
{
}
DropShadower : : ~ DropShadower ( )
{
if ( owner ! = nullptr )
{
owner - > removeComponentListener ( this ) ;
owner = nullptr ;
}
updateParent ( ) ;
reentrant = true ;
shadowWindows . clear ( ) ;
}
void DropShadower : : setOwner ( Component * componentToFollow )
{
if ( componentToFollow ! = owner )
{
if ( owner ! = nullptr )
owner - > removeComponentListener ( this ) ;
// (the component can't be null)
jassert ( componentToFollow ! = nullptr ) ;
owner = componentToFollow ;
jassert ( owner ! = nullptr ) ;
updateParent ( ) ;
owner - > addComponentListener ( this ) ;
updateShadows ( ) ;
}
}
void DropShadower : : updateParent ( )
{
if ( Component * p = lastParentComp )
p - > removeComponentListener ( this ) ;
lastParentComp = owner ! = nullptr ? owner - > getParentComponent ( ) : nullptr ;
if ( Component * p = lastParentComp )
p - > addComponentListener ( this ) ;
}
void DropShadower : : componentMovedOrResized ( Component & c , bool /*wasMoved*/ , bool /*wasResized*/ )
{
if ( owner = = & c )
updateShadows ( ) ;
}
void DropShadower : : componentBroughtToFront ( Component & c )
{
if ( owner = = & c )
updateShadows ( ) ;
}
void DropShadower : : componentChildrenChanged ( Component & )
{
updateShadows ( ) ;
}
void DropShadower : : componentParentHierarchyChanged ( Component & c )
{
if ( owner = = & c )
{
updateParent ( ) ;
updateShadows ( ) ;
}
}
void DropShadower : : componentVisibilityChanged ( Component & c )
{
if ( owner = = & c )
updateShadows ( ) ;
}
void DropShadower : : updateShadows ( )
{
if ( reentrant )
return ;
const ScopedValueSetter < bool > setter ( reentrant , true , false ) ;
if ( owner = = nullptr )
{
shadowWindows . clear ( ) ;
return ;
}
if ( owner - > isShowing ( )
& & owner - > getWidth ( ) > 0 & & owner - > getHeight ( ) > 0
& & ( Desktop : : canUseSemiTransparentWindows ( ) | | owner - > getParentComponent ( ) ! = nullptr ) )
{
while ( shadowWindows . size ( ) < 4 )
shadowWindows . add ( new ShadowWindow ( owner , shadow ) ) ;
const int shadowEdge = jmax ( shadow . offset . x , shadow . offset . y ) + shadow . radius ;
const int x = owner - > getX ( ) ;
const int y = owner - > getY ( ) - shadowEdge ;
const int w = owner - > getWidth ( ) ;
const int h = owner - > getHeight ( ) + shadowEdge + shadowEdge ;
for ( int i = 4 ; - - i > = 0 ; )
{
// there seem to be rare situations where the dropshadower may be deleted by
// callbacks during this loop, so use a weak ref to watch out for this..
WeakReference < Component > sw ( shadowWindows [ i ] ) ;
if ( sw ! = nullptr )
sw - > setAlwaysOnTop ( owner - > isAlwaysOnTop ( ) ) ;
if ( sw ! = nullptr )
{
switch ( i )
{
case 0 : sw - > setBounds ( x - shadowEdge , y , shadowEdge , h ) ; break ;
case 1 : sw - > setBounds ( x + w , y , shadowEdge , h ) ; break ;
case 2 : sw - > setBounds ( x , y , w , shadowEdge ) ; break ;
case 3 : sw - > setBounds ( x , owner - > getBottom ( ) , w , shadowEdge ) ; break ;
default : break ;
}
}
if ( sw ! = nullptr )
sw - > toBehind ( i = = 3 ? owner : shadowWindows . getUnchecked ( i + 1 ) ) ;
if ( sw = = nullptr )
return ;
}
}
else
{
shadowWindows . clear ( ) ;
}
}