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.

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