Audio plugin host https://kx.studio/carla
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.

216 lines
5.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class DropShadower::ShadowWindow : public Component
  18. {
  19. public:
  20. ShadowWindow (Component* comp, const DropShadow& ds)
  21. : target (comp), shadow (ds)
  22. {
  23. setVisible (true);
  24. setInterceptsMouseClicks (false, false);
  25. if (comp->isOnDesktop())
  26. {
  27. setSize (1, 1); // to keep the OS happy by not having zero-size windows
  28. addToDesktop (ComponentPeer::windowIgnoresMouseClicks
  29. | ComponentPeer::windowIsTemporary
  30. | ComponentPeer::windowIgnoresKeyPresses);
  31. }
  32. else if (Component* const parent = comp->getParentComponent())
  33. {
  34. parent->addChildComponent (this);
  35. }
  36. }
  37. void paint (Graphics& g) override
  38. {
  39. if (Component* c = target)
  40. shadow.drawForRectangle (g, getLocalArea (c, c->getLocalBounds()));
  41. }
  42. void resized() override
  43. {
  44. repaint(); // (needed for correct repainting)
  45. }
  46. float getDesktopScaleFactor() const override
  47. {
  48. if (target != nullptr)
  49. return target->getDesktopScaleFactor();
  50. return Component::getDesktopScaleFactor();
  51. }
  52. private:
  53. WeakReference<Component> target;
  54. DropShadow shadow;
  55. JUCE_DECLARE_NON_COPYABLE (ShadowWindow)
  56. };
  57. //==============================================================================
  58. DropShadower::DropShadower (const DropShadow& ds)
  59. : owner (nullptr), shadow (ds), reentrant (false)
  60. {
  61. }
  62. DropShadower::~DropShadower()
  63. {
  64. if (owner != nullptr)
  65. {
  66. owner->removeComponentListener (this);
  67. owner = nullptr;
  68. }
  69. updateParent();
  70. reentrant = true;
  71. shadowWindows.clear();
  72. }
  73. void DropShadower::setOwner (Component* componentToFollow)
  74. {
  75. if (componentToFollow != owner)
  76. {
  77. if (owner != nullptr)
  78. owner->removeComponentListener (this);
  79. // (the component can't be null)
  80. jassert (componentToFollow != nullptr);
  81. owner = componentToFollow;
  82. jassert (owner != nullptr);
  83. updateParent();
  84. owner->addComponentListener (this);
  85. updateShadows();
  86. }
  87. }
  88. void DropShadower::updateParent()
  89. {
  90. if (Component* p = lastParentComp)
  91. p->removeComponentListener (this);
  92. lastParentComp = owner != nullptr ? owner->getParentComponent() : nullptr;
  93. if (Component* p = lastParentComp)
  94. p->addComponentListener (this);
  95. }
  96. void DropShadower::componentMovedOrResized (Component& c, bool /*wasMoved*/, bool /*wasResized*/)
  97. {
  98. if (owner == &c)
  99. updateShadows();
  100. }
  101. void DropShadower::componentBroughtToFront (Component& c)
  102. {
  103. if (owner == &c)
  104. updateShadows();
  105. }
  106. void DropShadower::componentChildrenChanged (Component&)
  107. {
  108. updateShadows();
  109. }
  110. void DropShadower::componentParentHierarchyChanged (Component& c)
  111. {
  112. if (owner == &c)
  113. {
  114. updateParent();
  115. updateShadows();
  116. }
  117. }
  118. void DropShadower::componentVisibilityChanged (Component& c)
  119. {
  120. if (owner == &c)
  121. updateShadows();
  122. }
  123. void DropShadower::updateShadows()
  124. {
  125. if (reentrant)
  126. return;
  127. const ScopedValueSetter<bool> setter (reentrant, true, false);
  128. if (owner == nullptr)
  129. {
  130. shadowWindows.clear();
  131. return;
  132. }
  133. if (owner->isShowing()
  134. && owner->getWidth() > 0 && owner->getHeight() > 0
  135. && (Desktop::canUseSemiTransparentWindows() || owner->getParentComponent() != nullptr))
  136. {
  137. while (shadowWindows.size() < 4)
  138. shadowWindows.add (new ShadowWindow (owner, shadow));
  139. const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius;
  140. const int x = owner->getX();
  141. const int y = owner->getY() - shadowEdge;
  142. const int w = owner->getWidth();
  143. const int h = owner->getHeight() + shadowEdge + shadowEdge;
  144. for (int i = 4; --i >= 0;)
  145. {
  146. // there seem to be rare situations where the dropshadower may be deleted by
  147. // callbacks during this loop, so use a weak ref to watch out for this..
  148. WeakReference<Component> sw (shadowWindows[i]);
  149. if (sw != nullptr)
  150. sw->setAlwaysOnTop (owner->isAlwaysOnTop());
  151. if (sw != nullptr)
  152. {
  153. switch (i)
  154. {
  155. case 0: sw->setBounds (x - shadowEdge, y, shadowEdge, h); break;
  156. case 1: sw->setBounds (x + w, y, shadowEdge, h); break;
  157. case 2: sw->setBounds (x, y, w, shadowEdge); break;
  158. case 3: sw->setBounds (x, owner->getBottom(), w, shadowEdge); break;
  159. default: break;
  160. }
  161. }
  162. if (sw != nullptr)
  163. sw->toBehind (i == 3 ? owner : shadowWindows.getUnchecked (i + 1));
  164. if (sw == nullptr)
  165. return;
  166. }
  167. }
  168. else
  169. {
  170. shadowWindows.clear();
  171. }
  172. }