The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

303 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. class ShadowWindow : public Component
  21. {
  22. public:
  23. ShadowWindow (Component& owner, const int type_, const Image shadowImageSections [12])
  24. : topLeft (shadowImageSections [type_ * 3]),
  25. bottomRight (shadowImageSections [type_ * 3 + 1]),
  26. filler (shadowImageSections [type_ * 3 + 2]),
  27. type (type_)
  28. {
  29. setInterceptsMouseClicks (false, false);
  30. if (owner.isOnDesktop())
  31. {
  32. setSize (1, 1); // to keep the OS happy by not having zero-size windows
  33. addToDesktop (ComponentPeer::windowIgnoresMouseClicks
  34. | ComponentPeer::windowIsTemporary
  35. | ComponentPeer::windowIgnoresKeyPresses);
  36. }
  37. else if (owner.getParentComponent() != nullptr)
  38. {
  39. owner.getParentComponent()->addChildComponent (this);
  40. }
  41. }
  42. void paint (Graphics& g)
  43. {
  44. g.setOpacity (1.0f);
  45. if (type < 2)
  46. {
  47. int imH = jmin (topLeft.getHeight(), getHeight() / 2);
  48. g.drawImage (topLeft,
  49. 0, 0, topLeft.getWidth(), imH,
  50. 0, 0, topLeft.getWidth(), imH);
  51. imH = jmin (bottomRight.getHeight(), getHeight() - getHeight() / 2);
  52. g.drawImage (bottomRight,
  53. 0, getHeight() - imH, bottomRight.getWidth(), imH,
  54. 0, bottomRight.getHeight() - imH, bottomRight.getWidth(), imH);
  55. g.setTiledImageFill (filler, 0, 0, 1.0f);
  56. g.fillRect (0, topLeft.getHeight(), getWidth(), getHeight() - (topLeft.getHeight() + bottomRight.getHeight()));
  57. }
  58. else
  59. {
  60. int imW = jmin (topLeft.getWidth(), getWidth() / 2);
  61. g.drawImage (topLeft,
  62. 0, 0, imW, topLeft.getHeight(),
  63. 0, 0, imW, topLeft.getHeight());
  64. imW = jmin (bottomRight.getWidth(), getWidth() - getWidth() / 2);
  65. g.drawImage (bottomRight,
  66. getWidth() - imW, 0, imW, bottomRight.getHeight(),
  67. bottomRight.getWidth() - imW, 0, imW, bottomRight.getHeight());
  68. g.setTiledImageFill (filler, 0, 0, 1.0f);
  69. g.fillRect (topLeft.getWidth(), 0, getWidth() - (topLeft.getWidth() + bottomRight.getWidth()), getHeight());
  70. }
  71. }
  72. void resized()
  73. {
  74. repaint(); // (needed for correct repainting)
  75. }
  76. private:
  77. const Image topLeft, bottomRight, filler;
  78. const int type; // 0 = left, 1 = right, 2 = top, 3 = bottom. left + right are full-height
  79. JUCE_DECLARE_NON_COPYABLE (ShadowWindow);
  80. };
  81. //==============================================================================
  82. DropShadower::DropShadower (const float alpha_,
  83. const int xOffset_,
  84. const int yOffset_,
  85. const float blurRadius_)
  86. : owner (nullptr),
  87. xOffset (xOffset_),
  88. yOffset (yOffset_),
  89. alpha (alpha_),
  90. blurRadius (blurRadius_),
  91. reentrant (false)
  92. {
  93. }
  94. DropShadower::~DropShadower()
  95. {
  96. if (owner != nullptr)
  97. owner->removeComponentListener (this);
  98. reentrant = true;
  99. shadowWindows.clear();
  100. }
  101. void DropShadower::setOwner (Component* componentToFollow)
  102. {
  103. if (componentToFollow != owner)
  104. {
  105. if (owner != nullptr)
  106. owner->removeComponentListener (this);
  107. // (the component can't be null)
  108. jassert (componentToFollow != nullptr);
  109. owner = componentToFollow;
  110. jassert (owner != nullptr);
  111. jassert (owner->isOpaque()); // doesn't work properly for semi-transparent comps!
  112. owner->addComponentListener (this);
  113. updateShadows();
  114. }
  115. }
  116. void DropShadower::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/)
  117. {
  118. updateShadows();
  119. }
  120. void DropShadower::componentBroughtToFront (Component&)
  121. {
  122. bringShadowWindowsToFront();
  123. }
  124. void DropShadower::componentParentHierarchyChanged (Component&)
  125. {
  126. shadowWindows.clear();
  127. updateShadows();
  128. }
  129. void DropShadower::componentVisibilityChanged (Component&)
  130. {
  131. updateShadows();
  132. }
  133. void DropShadower::updateShadows()
  134. {
  135. if (reentrant || owner == nullptr)
  136. return;
  137. ComponentPeer* const peer = owner->getPeer();
  138. const bool isOwnerVisible = owner->isVisible() && (peer == nullptr || ! peer->isMinimised());
  139. const bool createShadowWindows = shadowWindows.size() == 0
  140. && owner->getWidth() > 0
  141. && owner->getHeight() > 0
  142. && isOwnerVisible
  143. && (Desktop::canUseSemiTransparentWindows()
  144. || owner->getParentComponent() != nullptr);
  145. {
  146. const ScopedValueSetter<bool> setter (reentrant, true, false);
  147. const int shadowEdge = jmax (xOffset, yOffset) + (int) blurRadius;
  148. if (createShadowWindows)
  149. {
  150. // keep a cached version of the image to save doing the gaussian too often
  151. String imageId;
  152. imageId << shadowEdge << ',' << xOffset << ',' << yOffset << ',' << alpha;
  153. const int hash = imageId.hashCode();
  154. Image bigIm (ImageCache::getFromHashCode (hash));
  155. if (bigIm.isNull())
  156. {
  157. bigIm = Image (Image::ARGB, shadowEdge * 5, shadowEdge * 5, true, Image::NativeImage);
  158. Graphics bigG (bigIm);
  159. bigG.setColour (Colours::black.withAlpha (alpha));
  160. bigG.fillRect (shadowEdge + xOffset,
  161. shadowEdge + yOffset,
  162. bigIm.getWidth() - (shadowEdge * 2),
  163. bigIm.getHeight() - (shadowEdge * 2));
  164. ImageConvolutionKernel blurKernel (roundToInt (blurRadius * 2.0f));
  165. blurKernel.createGaussianBlur (blurRadius);
  166. blurKernel.applyToImage (bigIm, bigIm,
  167. Rectangle<int> (xOffset, yOffset,
  168. bigIm.getWidth(), bigIm.getHeight()));
  169. ImageCache::addImageToCache (bigIm, hash);
  170. }
  171. const int iw = bigIm.getWidth();
  172. const int ih = bigIm.getHeight();
  173. const int shadowEdge2 = shadowEdge * 2;
  174. setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0);
  175. setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, ih - shadowEdge2);
  176. setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2);
  177. setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, iw - shadowEdge, 0);
  178. setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, iw - shadowEdge, ih - shadowEdge2);
  179. setShadowImage (bigIm, 5, shadowEdge, shadowEdge, iw - shadowEdge, shadowEdge2);
  180. setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0);
  181. setShadowImage (bigIm, 7, shadowEdge, shadowEdge, iw - shadowEdge2, 0);
  182. setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0);
  183. setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, ih - shadowEdge);
  184. setShadowImage (bigIm, 10, shadowEdge, shadowEdge, iw - shadowEdge2, ih - shadowEdge);
  185. setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, ih - shadowEdge);
  186. for (int i = 0; i < 4; ++i)
  187. shadowWindows.add (new ShadowWindow (*owner, i, shadowImageSections));
  188. }
  189. if (shadowWindows.size() >= 4)
  190. {
  191. const int x = owner->getX();
  192. const int y = owner->getY() - shadowEdge;
  193. const int w = owner->getWidth();
  194. const int h = owner->getHeight() + shadowEdge + shadowEdge;
  195. for (int i = shadowWindows.size(); --i >= 0;)
  196. {
  197. // there seem to be rare situations where the dropshadower may be deleted by
  198. // callbacks during this loop, so use a weak ref to watch out for this..
  199. WeakReference<Component> sw (shadowWindows[i]);
  200. if (sw != nullptr)
  201. sw->setAlwaysOnTop (owner->isAlwaysOnTop());
  202. if (sw != nullptr)
  203. sw->setVisible (isOwnerVisible);
  204. if (sw != nullptr)
  205. {
  206. switch (i)
  207. {
  208. case 0: sw->setBounds (x - shadowEdge, y, shadowEdge, h); break;
  209. case 1: sw->setBounds (x + w, y, shadowEdge, h); break;
  210. case 2: sw->setBounds (x, y, w, shadowEdge); break;
  211. case 3: sw->setBounds (x, owner->getBottom(), w, shadowEdge); break;
  212. default: break;
  213. }
  214. }
  215. if (sw == nullptr)
  216. return;
  217. }
  218. }
  219. }
  220. if (createShadowWindows)
  221. bringShadowWindowsToFront();
  222. }
  223. void DropShadower::setShadowImage (const Image& src, const int num, const int w, const int h,
  224. const int sx, const int sy)
  225. {
  226. shadowImageSections[num] = Image (Image::ARGB, w, h, true, Image::NativeImage);
  227. Graphics g (shadowImageSections[num]);
  228. g.drawImage (src, 0, 0, w, h, sx, sy, w, h);
  229. }
  230. void DropShadower::bringShadowWindowsToFront()
  231. {
  232. if (! reentrant)
  233. {
  234. updateShadows();
  235. const ScopedValueSetter<bool> setter (reentrant, true, false);
  236. for (int i = shadowWindows.size(); --i >= 0;)
  237. shadowWindows.getUnchecked(i)->toBehind (owner);
  238. }
  239. }
  240. END_JUCE_NAMESPACE