2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
30 #include "juce_DropShadower.h"
31 #include "../../graphics/imaging/juce_ImageCache.h"
32 #include "../../graphics/imaging/juce_ImageConvolutionKernel.h"
33 #include "../../graphics/imaging/juce_Image.h"
34 #include "../juce_Desktop.h"
35 #include "../windows/juce_ComponentPeer.h"
36 #include "../../../containers/juce_ScopedValueSetter.h"
39 //==============================================================================
40 class ShadowWindow
: public Component
43 ShadowWindow (Component
& owner
, const int type_
, const Image shadowImageSections
[12])
44 : topLeft (shadowImageSections
[type_
* 3]),
45 bottomRight (shadowImageSections
[type_
* 3 + 1]),
46 filler (shadowImageSections
[type_
* 3 + 2]),
49 setInterceptsMouseClicks (false, false);
51 if (owner
.isOnDesktop())
53 setSize (1, 1); // to keep the OS happy by not having zero-size windows
54 addToDesktop (ComponentPeer::windowIgnoresMouseClicks
55 | ComponentPeer::windowIsTemporary
56 | ComponentPeer::windowIgnoresKeyPresses
);
58 else if (owner
.getParentComponent() != nullptr)
60 owner
.getParentComponent()->addChildComponent (this);
64 void paint (Graphics
& g
)
70 int imH
= jmin (topLeft
.getHeight(), getHeight() / 2);
72 0, 0, topLeft
.getWidth(), imH
,
73 0, 0, topLeft
.getWidth(), imH
);
75 imH
= jmin (bottomRight
.getHeight(), getHeight() - getHeight() / 2);
76 g
.drawImage (bottomRight
,
77 0, getHeight() - imH
, bottomRight
.getWidth(), imH
,
78 0, bottomRight
.getHeight() - imH
, bottomRight
.getWidth(), imH
);
80 g
.setTiledImageFill (filler
, 0, 0, 1.0f
);
81 g
.fillRect (0, topLeft
.getHeight(), getWidth(), getHeight() - (topLeft
.getHeight() + bottomRight
.getHeight()));
85 int imW
= jmin (topLeft
.getWidth(), getWidth() / 2);
87 0, 0, imW
, topLeft
.getHeight(),
88 0, 0, imW
, topLeft
.getHeight());
90 imW
= jmin (bottomRight
.getWidth(), getWidth() - getWidth() / 2);
91 g
.drawImage (bottomRight
,
92 getWidth() - imW
, 0, imW
, bottomRight
.getHeight(),
93 bottomRight
.getWidth() - imW
, 0, imW
, bottomRight
.getHeight());
95 g
.setTiledImageFill (filler
, 0, 0, 1.0f
);
96 g
.fillRect (topLeft
.getWidth(), 0, getWidth() - (topLeft
.getWidth() + bottomRight
.getWidth()), getHeight());
102 repaint(); // (needed for correct repainting)
106 const Image topLeft
, bottomRight
, filler
;
107 const int type
; // 0 = left, 1 = right, 2 = top, 3 = bottom. left + right are full-height
109 JUCE_DECLARE_NON_COPYABLE (ShadowWindow
);
113 //==============================================================================
114 DropShadower::DropShadower (const float alpha_
,
117 const float blurRadius_
)
122 blurRadius (blurRadius_
),
127 DropShadower::~DropShadower()
129 if (owner
!= nullptr)
130 owner
->removeComponentListener (this);
133 shadowWindows
.clear();
136 void DropShadower::setOwner (Component
* componentToFollow
)
138 if (componentToFollow
!= owner
)
140 if (owner
!= nullptr)
141 owner
->removeComponentListener (this);
143 // (the component can't be null)
144 jassert (componentToFollow
!= nullptr);
146 owner
= componentToFollow
;
148 jassert (owner
!= nullptr);
149 jassert (owner
->isOpaque()); // doesn't work properly for semi-transparent comps!
151 owner
->addComponentListener (this);
157 void DropShadower::componentMovedOrResized (Component
&, bool /*wasMoved*/, bool /*wasResized*/)
162 void DropShadower::componentBroughtToFront (Component
&)
164 bringShadowWindowsToFront();
167 void DropShadower::componentParentHierarchyChanged (Component
&)
169 shadowWindows
.clear();
173 void DropShadower::componentVisibilityChanged (Component
&)
178 void DropShadower::updateShadows()
180 if (reentrant
|| owner
== nullptr)
183 ComponentPeer
* const peer
= owner
->getPeer();
184 const bool isOwnerVisible
= owner
->isVisible() && (peer
== nullptr || ! peer
->isMinimised());
186 const bool createShadowWindows
= shadowWindows
.size() == 0
187 && owner
->getWidth() > 0
188 && owner
->getHeight() > 0
190 && (Desktop::canUseSemiTransparentWindows()
191 || owner
->getParentComponent() != nullptr);
194 const ScopedValueSetter
<bool> setter (reentrant
, true, false);
196 const int shadowEdge
= jmax (xOffset
, yOffset
) + (int) blurRadius
;
198 if (createShadowWindows
)
200 // keep a cached version of the image to save doing the gaussian too often
202 imageId
<< shadowEdge
<< ',' << xOffset
<< ',' << yOffset
<< ',' << alpha
;
204 const int hash
= imageId
.hashCode();
206 Image
bigIm (ImageCache::getFromHashCode (hash
));
210 bigIm
= Image (Image::ARGB
, shadowEdge
* 5, shadowEdge
* 5, true, Image::NativeImage
);
212 Graphics
bigG (bigIm
);
213 bigG
.setColour (Colours::black
.withAlpha (alpha
));
214 bigG
.fillRect (shadowEdge
+ xOffset
,
215 shadowEdge
+ yOffset
,
216 bigIm
.getWidth() - (shadowEdge
* 2),
217 bigIm
.getHeight() - (shadowEdge
* 2));
219 ImageConvolutionKernel
blurKernel (roundToInt (blurRadius
* 2.0f
));
220 blurKernel
.createGaussianBlur (blurRadius
);
222 blurKernel
.applyToImage (bigIm
, bigIm
,
223 Rectangle
<int> (xOffset
, yOffset
,
224 bigIm
.getWidth(), bigIm
.getHeight()));
226 ImageCache::addImageToCache (bigIm
, hash
);
229 const int iw
= bigIm
.getWidth();
230 const int ih
= bigIm
.getHeight();
231 const int shadowEdge2
= shadowEdge
* 2;
233 setShadowImage (bigIm
, 0, shadowEdge
, shadowEdge2
, 0, 0);
234 setShadowImage (bigIm
, 1, shadowEdge
, shadowEdge2
, 0, ih
- shadowEdge2
);
235 setShadowImage (bigIm
, 2, shadowEdge
, shadowEdge
, 0, shadowEdge2
);
236 setShadowImage (bigIm
, 3, shadowEdge
, shadowEdge2
, iw
- shadowEdge
, 0);
237 setShadowImage (bigIm
, 4, shadowEdge
, shadowEdge2
, iw
- shadowEdge
, ih
- shadowEdge2
);
238 setShadowImage (bigIm
, 5, shadowEdge
, shadowEdge
, iw
- shadowEdge
, shadowEdge2
);
239 setShadowImage (bigIm
, 6, shadowEdge
, shadowEdge
, shadowEdge
, 0);
240 setShadowImage (bigIm
, 7, shadowEdge
, shadowEdge
, iw
- shadowEdge2
, 0);
241 setShadowImage (bigIm
, 8, shadowEdge
, shadowEdge
, shadowEdge2
, 0);
242 setShadowImage (bigIm
, 9, shadowEdge
, shadowEdge
, shadowEdge
, ih
- shadowEdge
);
243 setShadowImage (bigIm
, 10, shadowEdge
, shadowEdge
, iw
- shadowEdge2
, ih
- shadowEdge
);
244 setShadowImage (bigIm
, 11, shadowEdge
, shadowEdge
, shadowEdge2
, ih
- shadowEdge
);
246 for (int i
= 0; i
< 4; ++i
)
247 shadowWindows
.add (new ShadowWindow (*owner
, i
, shadowImageSections
));
250 if (shadowWindows
.size() >= 4)
252 const int x
= owner
->getX();
253 const int y
= owner
->getY() - shadowEdge
;
254 const int w
= owner
->getWidth();
255 const int h
= owner
->getHeight() + shadowEdge
+ shadowEdge
;
257 for (int i
= shadowWindows
.size(); --i
>= 0;)
259 // there seem to be rare situations where the dropshadower may be deleted by
260 // callbacks during this loop, so use a weak ref to watch out for this..
261 WeakReference
<Component
> sw (shadowWindows
[i
]);
264 sw
->setAlwaysOnTop (owner
->isAlwaysOnTop());
267 sw
->setVisible (isOwnerVisible
);
273 case 0: sw
->setBounds (x
- shadowEdge
, y
, shadowEdge
, h
); break;
274 case 1: sw
->setBounds (x
+ w
, y
, shadowEdge
, h
); break;
275 case 2: sw
->setBounds (x
, y
, w
, shadowEdge
); break;
276 case 3: sw
->setBounds (x
, owner
->getBottom(), w
, shadowEdge
); break;
287 if (createShadowWindows
)
288 bringShadowWindowsToFront();
291 void DropShadower::setShadowImage (const Image
& src
, const int num
, const int w
, const int h
,
292 const int sx
, const int sy
)
294 shadowImageSections
[num
] = Image (Image::ARGB
, w
, h
, true, Image::NativeImage
);
296 Graphics
g (shadowImageSections
[num
]);
297 g
.drawImage (src
, 0, 0, w
, h
, sx
, sy
, w
, h
);
300 void DropShadower::bringShadowWindowsToFront()
306 const ScopedValueSetter
<bool> setter (reentrant
, true, false);
308 for (int i
= shadowWindows
.size(); --i
>= 0;)
309 shadowWindows
.getUnchecked(i
)->toBehind (owner
);