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.

286 lines
10KB

  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 DropShadower::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 DropShadow& shadow_)
  81. : owner (nullptr), shadow (shadow_), reentrant (false)
  82. {
  83. }
  84. DropShadower::~DropShadower()
  85. {
  86. if (owner != nullptr)
  87. owner->removeComponentListener (this);
  88. reentrant = true;
  89. shadowWindows.clear();
  90. }
  91. void DropShadower::setOwner (Component* componentToFollow)
  92. {
  93. if (componentToFollow != owner)
  94. {
  95. if (owner != nullptr)
  96. owner->removeComponentListener (this);
  97. // (the component can't be null)
  98. jassert (componentToFollow != nullptr);
  99. owner = componentToFollow;
  100. jassert (owner != nullptr);
  101. jassert (owner->isOpaque()); // doesn't work properly for semi-transparent comps!
  102. owner->addComponentListener (this);
  103. updateShadows();
  104. }
  105. }
  106. void DropShadower::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/)
  107. {
  108. updateShadows();
  109. }
  110. void DropShadower::componentBroughtToFront (Component&)
  111. {
  112. bringShadowWindowsToFront();
  113. }
  114. void DropShadower::componentParentHierarchyChanged (Component&)
  115. {
  116. shadowWindows.clear();
  117. updateShadows();
  118. }
  119. void DropShadower::componentVisibilityChanged (Component&)
  120. {
  121. updateShadows();
  122. }
  123. void DropShadower::updateShadows()
  124. {
  125. if (reentrant || owner == nullptr)
  126. return;
  127. ComponentPeer* const peer = owner->getPeer();
  128. const bool isOwnerVisible = owner->isVisible() && (peer == nullptr || ! peer->isMinimised());
  129. const bool createShadowWindows = shadowWindows.size() == 0
  130. && owner->getWidth() > 0
  131. && owner->getHeight() > 0
  132. && isOwnerVisible
  133. && (Desktop::canUseSemiTransparentWindows()
  134. || owner->getParentComponent() != nullptr);
  135. {
  136. const ScopedValueSetter<bool> setter (reentrant, true, false);
  137. const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius;
  138. if (createShadowWindows)
  139. {
  140. const int shadowEdge2 = shadowEdge * 2;
  141. const int imageSize = shadowEdge * 5;
  142. // keep a cached version of the image to save doing the gaussian too often
  143. int64 hash = shadow.radius ^ 0x2342dfa7;
  144. hash = hash * 101 + shadow.offset.x;
  145. hash = hash * 101 + shadow.offset.y;
  146. hash = hash * 65537 + shadow.colour.getARGB();
  147. Image bigIm (ImageCache::getFromHashCode (hash));
  148. if (bigIm.isNull())
  149. {
  150. bigIm = Image (Image::ARGB, imageSize, imageSize, true);
  151. Graphics g (bigIm);
  152. Path p;
  153. p.addRectangle ((float) (shadowEdge + shadow.offset.x),
  154. (float) (shadowEdge + shadow.offset.y),
  155. (float) (imageSize - shadowEdge2),
  156. (float) (imageSize - shadowEdge2));
  157. shadow.drawForPath (g, p);
  158. ImageCache::addImageToCache (bigIm, hash);
  159. }
  160. jassert (imageSize == bigIm.getWidth() && imageSize == bigIm.getHeight());
  161. setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0);
  162. setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, imageSize - shadowEdge2);
  163. setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2);
  164. setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, imageSize - shadowEdge, 0);
  165. setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, imageSize - shadowEdge, imageSize - shadowEdge2);
  166. setShadowImage (bigIm, 5, shadowEdge, shadowEdge, imageSize - shadowEdge, shadowEdge2);
  167. setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0);
  168. setShadowImage (bigIm, 7, shadowEdge, shadowEdge, imageSize - shadowEdge2, 0);
  169. setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0);
  170. setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, imageSize - shadowEdge);
  171. setShadowImage (bigIm, 10, shadowEdge, shadowEdge, imageSize - shadowEdge2, imageSize - shadowEdge);
  172. setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, imageSize - shadowEdge);
  173. for (int i = 0; i < 4; ++i)
  174. shadowWindows.add (new ShadowWindow (*owner, i, shadowImageSections));
  175. }
  176. if (shadowWindows.size() >= 4)
  177. {
  178. const int x = owner->getX();
  179. const int y = owner->getY() - shadowEdge;
  180. const int w = owner->getWidth();
  181. const int h = owner->getHeight() + shadowEdge + shadowEdge;
  182. for (int i = shadowWindows.size(); --i >= 0;)
  183. {
  184. // there seem to be rare situations where the dropshadower may be deleted by
  185. // callbacks during this loop, so use a weak ref to watch out for this..
  186. WeakReference<Component> sw (shadowWindows[i]);
  187. if (sw != nullptr)
  188. sw->setAlwaysOnTop (owner->isAlwaysOnTop());
  189. if (sw != nullptr)
  190. sw->setVisible (isOwnerVisible);
  191. if (sw != nullptr)
  192. {
  193. switch (i)
  194. {
  195. case 0: sw->setBounds (x - shadowEdge, y, shadowEdge, h); break;
  196. case 1: sw->setBounds (x + w, y, shadowEdge, h); break;
  197. case 2: sw->setBounds (x, y, w, shadowEdge); break;
  198. case 3: sw->setBounds (x, owner->getBottom(), w, shadowEdge); break;
  199. default: break;
  200. }
  201. }
  202. if (sw == nullptr)
  203. return;
  204. }
  205. }
  206. }
  207. if (createShadowWindows)
  208. bringShadowWindowsToFront();
  209. }
  210. void DropShadower::setShadowImage (const Image& src, const int num, const int w, const int h,
  211. const int sx, const int sy)
  212. {
  213. shadowImageSections[num] = Image (Image::ARGB, w, h, true);
  214. Graphics g (shadowImageSections[num]);
  215. g.drawImage (src, 0, 0, w, h, sx, sy, w, h);
  216. }
  217. void DropShadower::bringShadowWindowsToFront()
  218. {
  219. if (! reentrant)
  220. {
  221. updateShadows();
  222. const ScopedValueSetter<bool> setter (reentrant, true, false);
  223. for (int i = shadowWindows.size(); --i >= 0;)
  224. shadowWindows.getUnchecked(i)->toBehind (owner);
  225. }
  226. }