juce_DropShadower.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. class DropShadower::ShadowWindow : public Component
  22. {
  23. public:
  24. ShadowWindow (Component* comp, const DropShadow& ds)
  25. : target (comp), shadow (ds)
  26. {
  27. setVisible (true);
  28. setInterceptsMouseClicks (false, false);
  29. if (comp->isOnDesktop())
  30. {
  31. setSize (1, 1); // to keep the OS happy by not having zero-size windows
  32. addToDesktop (ComponentPeer::windowIgnoresMouseClicks
  33. | ComponentPeer::windowIsTemporary
  34. | ComponentPeer::windowIgnoresKeyPresses);
  35. }
  36. else if (Component* const parent = comp->getParentComponent())
  37. {
  38. parent->addChildComponent (this);
  39. }
  40. }
  41. void paint (Graphics& g) override
  42. {
  43. if (Component* c = target)
  44. shadow.drawForRectangle (g, getLocalArea (c, c->getLocalBounds()));
  45. }
  46. void resized() override
  47. {
  48. repaint(); // (needed for correct repainting)
  49. }
  50. float getDesktopScaleFactor() const override
  51. {
  52. if (target != nullptr)
  53. return target->getDesktopScaleFactor();
  54. return Component::getDesktopScaleFactor();
  55. }
  56. private:
  57. WeakReference<Component> target;
  58. DropShadow shadow;
  59. JUCE_DECLARE_NON_COPYABLE (ShadowWindow)
  60. };
  61. //==============================================================================
  62. DropShadower::DropShadower (const DropShadow& ds)
  63. : owner (nullptr), shadow (ds), reentrant (false)
  64. {
  65. }
  66. DropShadower::~DropShadower()
  67. {
  68. if (owner != nullptr)
  69. {
  70. owner->removeComponentListener (this);
  71. owner = nullptr;
  72. }
  73. updateParent();
  74. reentrant = true;
  75. shadowWindows.clear();
  76. }
  77. void DropShadower::setOwner (Component* componentToFollow)
  78. {
  79. if (componentToFollow != owner)
  80. {
  81. if (owner != nullptr)
  82. owner->removeComponentListener (this);
  83. // (the component can't be null)
  84. jassert (componentToFollow != nullptr);
  85. owner = componentToFollow;
  86. jassert (owner != nullptr);
  87. updateParent();
  88. owner->addComponentListener (this);
  89. updateShadows();
  90. }
  91. }
  92. void DropShadower::updateParent()
  93. {
  94. if (Component* p = lastParentComp)
  95. p->removeComponentListener (this);
  96. lastParentComp = owner != nullptr ? owner->getParentComponent() : nullptr;
  97. if (Component* p = lastParentComp)
  98. p->addComponentListener (this);
  99. }
  100. void DropShadower::componentMovedOrResized (Component& c, bool /*wasMoved*/, bool /*wasResized*/)
  101. {
  102. if (owner == &c)
  103. updateShadows();
  104. }
  105. void DropShadower::componentBroughtToFront (Component& c)
  106. {
  107. if (owner == &c)
  108. updateShadows();
  109. }
  110. void DropShadower::componentChildrenChanged (Component&)
  111. {
  112. updateShadows();
  113. }
  114. void DropShadower::componentParentHierarchyChanged (Component& c)
  115. {
  116. if (owner == &c)
  117. {
  118. updateParent();
  119. updateShadows();
  120. }
  121. }
  122. void DropShadower::componentVisibilityChanged (Component& c)
  123. {
  124. if (owner == &c)
  125. updateShadows();
  126. }
  127. void DropShadower::updateShadows()
  128. {
  129. if (reentrant)
  130. return;
  131. const ScopedValueSetter<bool> setter (reentrant, true, false);
  132. if (owner == nullptr)
  133. {
  134. shadowWindows.clear();
  135. return;
  136. }
  137. if (owner->isShowing()
  138. && owner->getWidth() > 0 && owner->getHeight() > 0
  139. && (Desktop::canUseSemiTransparentWindows() || owner->getParentComponent() != nullptr))
  140. {
  141. while (shadowWindows.size() < 4)
  142. shadowWindows.add (new ShadowWindow (owner, shadow));
  143. const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius;
  144. const int x = owner->getX();
  145. const int y = owner->getY() - shadowEdge;
  146. const int w = owner->getWidth();
  147. const int h = owner->getHeight() + shadowEdge + shadowEdge;
  148. for (int i = 4; --i >= 0;)
  149. {
  150. // there seem to be rare situations where the dropshadower may be deleted by
  151. // callbacks during this loop, so use a weak ref to watch out for this..
  152. WeakReference<Component> sw (shadowWindows[i]);
  153. if (sw != nullptr)
  154. {
  155. sw->setAlwaysOnTop (owner->isAlwaysOnTop());
  156. if (sw == nullptr)
  157. return;
  158. switch (i)
  159. {
  160. case 0: sw->setBounds (x - shadowEdge, y, shadowEdge, h); break;
  161. case 1: sw->setBounds (x + w, y, shadowEdge, h); break;
  162. case 2: sw->setBounds (x, y, w, shadowEdge); break;
  163. case 3: sw->setBounds (x, owner->getBottom(), w, shadowEdge); break;
  164. default: break;
  165. }
  166. if (sw == nullptr)
  167. return;
  168. sw->toBehind (i == 3 ? owner : shadowWindows.getUnchecked (i + 1));
  169. }
  170. }
  171. }
  172. else
  173. {
  174. shadowWindows.clear();
  175. }
  176. }
  177. } // namespace juce