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.

363 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_DropShadower.h"
  21. #include "../../graphics/brushes/juce_ImageBrush.h"
  22. #include "../../graphics/imaging/juce_ImageCache.h"
  23. #include "../../graphics/imaging/juce_ImageConvolutionKernel.h"
  24. #include "../../graphics/imaging/juce_Image.h"
  25. #include "../juce_ComponentDeletionWatcher.h"
  26. #include "../juce_Desktop.h"
  27. //==============================================================================
  28. class ShadowWindow : public Component
  29. {
  30. Component* owner;
  31. Image** shadowImageSections;
  32. const int type; // 0 = left, 1 = right, 2 = top, 3 = bottom. left + right are full-height
  33. public:
  34. ShadowWindow (Component* const owner_,
  35. const int type_,
  36. Image** const shadowImageSections_)
  37. : owner (owner_),
  38. shadowImageSections (shadowImageSections_),
  39. type (type_)
  40. {
  41. setInterceptsMouseClicks (false, false);
  42. if (owner_->isOnDesktop())
  43. {
  44. setSize (1, 1); // to keep the OS happy by not having zero-size windows
  45. addToDesktop (ComponentPeer::windowIgnoresMouseClicks
  46. | ComponentPeer::windowIsTemporary
  47. | ComponentPeer::windowIgnoresKeyPresses);
  48. }
  49. else if (owner_->getParentComponent() != 0)
  50. {
  51. owner_->getParentComponent()->addChildComponent (this);
  52. }
  53. }
  54. ~ShadowWindow()
  55. {
  56. }
  57. void paint (Graphics& g)
  58. {
  59. Image* const topLeft = shadowImageSections [type * 3];
  60. Image* const bottomRight = shadowImageSections [type * 3 + 1];
  61. Image* const filler = shadowImageSections [type * 3 + 2];
  62. ImageBrush fillBrush (filler, 0, 0, 1.0f);
  63. g.setOpacity (1.0f);
  64. if (type < 2)
  65. {
  66. int imH = jmin (topLeft->getHeight(), getHeight() / 2);
  67. g.drawImage (topLeft,
  68. 0, 0, topLeft->getWidth(), imH,
  69. 0, 0, topLeft->getWidth(), imH);
  70. imH = jmin (bottomRight->getHeight(), getHeight() - getHeight() / 2);
  71. g.drawImage (bottomRight,
  72. 0, getHeight() - imH, bottomRight->getWidth(), imH,
  73. 0, bottomRight->getHeight() - imH, bottomRight->getWidth(), imH);
  74. g.setBrush (&fillBrush);
  75. g.fillRect (0, topLeft->getHeight(), getWidth(), getHeight() - (topLeft->getHeight() + bottomRight->getHeight()));
  76. }
  77. else
  78. {
  79. int imW = jmin (topLeft->getWidth(), getWidth() / 2);
  80. g.drawImage (topLeft,
  81. 0, 0, imW, topLeft->getHeight(),
  82. 0, 0, imW, topLeft->getHeight());
  83. imW = jmin (bottomRight->getWidth(), getWidth() - getWidth() / 2);
  84. g.drawImage (bottomRight,
  85. getWidth() - imW, 0, imW, bottomRight->getHeight(),
  86. bottomRight->getWidth() - imW, 0, imW, bottomRight->getHeight());
  87. g.setBrush (&fillBrush);
  88. g.fillRect (topLeft->getWidth(), 0, getWidth() - (topLeft->getWidth() + bottomRight->getWidth()), getHeight());
  89. }
  90. }
  91. void resized()
  92. {
  93. repaint(); // (needed for correct repainting)
  94. }
  95. private:
  96. ShadowWindow (const ShadowWindow&);
  97. const ShadowWindow& operator= (const ShadowWindow&);
  98. };
  99. //==============================================================================
  100. DropShadower::DropShadower (const float alpha_,
  101. const int xOffset_,
  102. const int yOffset_,
  103. const float blurRadius_)
  104. : owner (0),
  105. numShadows (0),
  106. shadowEdge (jmax (xOffset_, yOffset_) + (int) blurRadius_),
  107. xOffset (xOffset_),
  108. yOffset (yOffset_),
  109. alpha (alpha_),
  110. blurRadius (blurRadius_),
  111. inDestructor (false),
  112. reentrant (false)
  113. {
  114. }
  115. DropShadower::~DropShadower()
  116. {
  117. if (owner != 0)
  118. owner->removeComponentListener (this);
  119. inDestructor = true;
  120. deleteShadowWindows();
  121. }
  122. void DropShadower::deleteShadowWindows()
  123. {
  124. if (numShadows > 0)
  125. {
  126. int i;
  127. for (i = numShadows; --i >= 0;)
  128. delete shadowWindows[i];
  129. for (i = 12; --i >= 0;)
  130. delete shadowImageSections[i];
  131. numShadows = 0;
  132. }
  133. }
  134. void DropShadower::setOwner (Component* componentToFollow)
  135. {
  136. if (componentToFollow != owner)
  137. {
  138. if (owner != 0)
  139. owner->removeComponentListener (this);
  140. // (the component can't be null)
  141. jassert (componentToFollow != 0);
  142. owner = componentToFollow;
  143. jassert (owner != 0);
  144. jassert (owner->isOpaque()); // doesn't work properly for semi-transparent comps!
  145. owner->addComponentListener (this);
  146. updateShadows();
  147. }
  148. }
  149. void DropShadower::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/)
  150. {
  151. updateShadows();
  152. }
  153. void DropShadower::componentBroughtToFront (Component&)
  154. {
  155. bringShadowWindowsToFront();
  156. }
  157. void DropShadower::componentChildrenChanged (Component&)
  158. {
  159. }
  160. void DropShadower::componentParentHierarchyChanged (Component&)
  161. {
  162. deleteShadowWindows();
  163. updateShadows();
  164. }
  165. void DropShadower::componentVisibilityChanged (Component&)
  166. {
  167. updateShadows();
  168. }
  169. void DropShadower::updateShadows()
  170. {
  171. if (reentrant || inDestructor || (owner == 0))
  172. return;
  173. reentrant = true;
  174. ComponentPeer* const nw = owner->getPeer();
  175. const bool isOwnerVisible = owner->isVisible()
  176. && (nw == 0 || ! nw->isMinimised());
  177. const bool createShadowWindows = numShadows == 0
  178. && owner->getWidth() > 0
  179. && owner->getHeight() > 0
  180. && isOwnerVisible
  181. && (Desktop::canUseSemiTransparentWindows()
  182. || owner->getParentComponent() != 0);
  183. if (createShadowWindows)
  184. {
  185. // keep a cached version of the image to save doing the gaussian too often
  186. String imageId;
  187. imageId << shadowEdge << T(',')
  188. << xOffset << T(',')
  189. << yOffset << T(',')
  190. << alpha;
  191. const int hash = imageId.hashCode();
  192. Image* bigIm = ImageCache::getFromHashCode (hash);
  193. if (bigIm == 0)
  194. {
  195. bigIm = new Image (Image::ARGB, shadowEdge * 5, shadowEdge * 5, true);
  196. Graphics bigG (*bigIm);
  197. bigG.setColour (Colours::black.withAlpha (alpha));
  198. bigG.fillRect (shadowEdge + xOffset,
  199. shadowEdge + yOffset,
  200. bigIm->getWidth() - (shadowEdge * 2),
  201. bigIm->getHeight() - (shadowEdge * 2));
  202. ImageConvolutionKernel blurKernel (roundFloatToInt (blurRadius * 2.0f));
  203. blurKernel.createGaussianBlur (blurRadius);
  204. blurKernel.applyToImage (*bigIm, 0,
  205. xOffset,
  206. yOffset,
  207. bigIm->getWidth(),
  208. bigIm->getHeight());
  209. ImageCache::addImageToCache (bigIm, hash);
  210. }
  211. const int iw = bigIm->getWidth();
  212. const int ih = bigIm->getHeight();
  213. const int shadowEdge2 = shadowEdge * 2;
  214. setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0);
  215. setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, ih - shadowEdge2);
  216. setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2);
  217. setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, iw - shadowEdge, 0);
  218. setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, iw - shadowEdge, ih - shadowEdge2);
  219. setShadowImage (bigIm, 5, shadowEdge, shadowEdge, iw - shadowEdge, shadowEdge2);
  220. setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0);
  221. setShadowImage (bigIm, 7, shadowEdge, shadowEdge, iw - shadowEdge2, 0);
  222. setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0);
  223. setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, ih - shadowEdge);
  224. setShadowImage (bigIm, 10, shadowEdge, shadowEdge, iw - shadowEdge2, ih - shadowEdge);
  225. setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, ih - shadowEdge);
  226. ImageCache::release (bigIm);
  227. for (int i = 0; i < 4; ++i)
  228. {
  229. shadowWindows[numShadows] = new ShadowWindow (owner, i, shadowImageSections);
  230. ++numShadows;
  231. }
  232. }
  233. if (numShadows > 0)
  234. {
  235. for (int i = numShadows; --i >= 0;)
  236. {
  237. shadowWindows[i]->setAlwaysOnTop (owner->isAlwaysOnTop());
  238. shadowWindows[i]->setVisible (isOwnerVisible);
  239. }
  240. const int x = owner->getX();
  241. const int y = owner->getY() - shadowEdge;
  242. const int w = owner->getWidth();
  243. const int h = owner->getHeight() + shadowEdge + shadowEdge;
  244. shadowWindows[0]->setBounds (x - shadowEdge,
  245. y,
  246. shadowEdge,
  247. h);
  248. shadowWindows[1]->setBounds (x + w,
  249. y,
  250. shadowEdge,
  251. h);
  252. shadowWindows[2]->setBounds (x,
  253. y,
  254. w,
  255. shadowEdge);
  256. shadowWindows[3]->setBounds (x,
  257. owner->getBottom(),
  258. w,
  259. shadowEdge);
  260. }
  261. reentrant = false;
  262. if (createShadowWindows)
  263. bringShadowWindowsToFront();
  264. }
  265. void DropShadower::setShadowImage (Image* const src,
  266. const int num,
  267. const int w,
  268. const int h,
  269. const int sx,
  270. const int sy) throw()
  271. {
  272. shadowImageSections[num] = new Image (Image::ARGB, w, h, true);
  273. Graphics g (*shadowImageSections[num]);
  274. g.drawImage (src, 0, 0, w, h, sx, sy, w, h);
  275. }
  276. void DropShadower::bringShadowWindowsToFront()
  277. {
  278. if (! (inDestructor || reentrant))
  279. {
  280. updateShadows();
  281. reentrant = true;
  282. for (int i = numShadows; --i >= 0;)
  283. shadowWindows[i]->toBehind (owner);
  284. reentrant = false;
  285. }
  286. }
  287. END_JUCE_NAMESPACE