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.

219 lines
5.9KB

  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. class DropShadower::ShadowWindow : public Component
  20. {
  21. public:
  22. ShadowWindow (Component* comp, const DropShadow& ds)
  23. : target (comp), shadow (ds)
  24. {
  25. setVisible (true);
  26. setInterceptsMouseClicks (false, false);
  27. if (comp->isOnDesktop())
  28. {
  29. setSize (1, 1); // to keep the OS happy by not having zero-size windows
  30. addToDesktop (ComponentPeer::windowIgnoresMouseClicks
  31. | ComponentPeer::windowIsTemporary
  32. | ComponentPeer::windowIgnoresKeyPresses);
  33. }
  34. else if (Component* const parent = comp->getParentComponent())
  35. {
  36. parent->addChildComponent (this);
  37. }
  38. }
  39. void paint (Graphics& g) override
  40. {
  41. if (Component* c = target)
  42. shadow.drawForRectangle (g, getLocalArea (c, c->getLocalBounds()));
  43. }
  44. void resized() override
  45. {
  46. repaint(); // (needed for correct repainting)
  47. }
  48. float getDesktopScaleFactor() const override
  49. {
  50. if (target != nullptr)
  51. return target->getDesktopScaleFactor();
  52. return Component::getDesktopScaleFactor();
  53. }
  54. private:
  55. WeakReference<Component> target;
  56. DropShadow shadow;
  57. JUCE_DECLARE_NON_COPYABLE (ShadowWindow)
  58. };
  59. //==============================================================================
  60. DropShadower::DropShadower (const DropShadow& ds)
  61. : owner (nullptr), shadow (ds), reentrant (false)
  62. {
  63. }
  64. DropShadower::~DropShadower()
  65. {
  66. if (owner != nullptr)
  67. {
  68. owner->removeComponentListener (this);
  69. owner = nullptr;
  70. }
  71. updateParent();
  72. reentrant = true;
  73. shadowWindows.clear();
  74. }
  75. void DropShadower::setOwner (Component* componentToFollow)
  76. {
  77. if (componentToFollow != owner)
  78. {
  79. if (owner != nullptr)
  80. owner->removeComponentListener (this);
  81. // (the component can't be null)
  82. jassert (componentToFollow != nullptr);
  83. owner = componentToFollow;
  84. jassert (owner != nullptr);
  85. updateParent();
  86. owner->addComponentListener (this);
  87. updateShadows();
  88. }
  89. }
  90. void DropShadower::updateParent()
  91. {
  92. if (Component* p = lastParentComp)
  93. p->removeComponentListener (this);
  94. lastParentComp = owner != nullptr ? owner->getParentComponent() : nullptr;
  95. if (Component* p = lastParentComp)
  96. p->addComponentListener (this);
  97. }
  98. void DropShadower::componentMovedOrResized (Component& c, bool /*wasMoved*/, bool /*wasResized*/)
  99. {
  100. if (owner == &c)
  101. updateShadows();
  102. }
  103. void DropShadower::componentBroughtToFront (Component& c)
  104. {
  105. if (owner == &c)
  106. updateShadows();
  107. }
  108. void DropShadower::componentChildrenChanged (Component&)
  109. {
  110. updateShadows();
  111. }
  112. void DropShadower::componentParentHierarchyChanged (Component& c)
  113. {
  114. if (owner == &c)
  115. {
  116. updateParent();
  117. updateShadows();
  118. }
  119. }
  120. void DropShadower::componentVisibilityChanged (Component& c)
  121. {
  122. if (owner == &c)
  123. updateShadows();
  124. }
  125. void DropShadower::updateShadows()
  126. {
  127. if (reentrant)
  128. return;
  129. const ScopedValueSetter<bool> setter (reentrant, true, false);
  130. if (owner == nullptr)
  131. {
  132. shadowWindows.clear();
  133. return;
  134. }
  135. if (owner->isShowing()
  136. && owner->getWidth() > 0 && owner->getHeight() > 0
  137. && (Desktop::canUseSemiTransparentWindows() || owner->getParentComponent() != nullptr))
  138. {
  139. while (shadowWindows.size() < 4)
  140. shadowWindows.add (new ShadowWindow (owner, shadow));
  141. const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius;
  142. const int x = owner->getX();
  143. const int y = owner->getY() - shadowEdge;
  144. const int w = owner->getWidth();
  145. const int h = owner->getHeight() + shadowEdge + shadowEdge;
  146. for (int i = 4; --i >= 0;)
  147. {
  148. // there seem to be rare situations where the dropshadower may be deleted by
  149. // callbacks during this loop, so use a weak ref to watch out for this..
  150. WeakReference<Component> sw (shadowWindows[i]);
  151. if (sw != nullptr)
  152. {
  153. sw->setAlwaysOnTop (owner->isAlwaysOnTop());
  154. if (sw == nullptr)
  155. return;
  156. switch (i)
  157. {
  158. case 0: sw->setBounds (x - shadowEdge, y, shadowEdge, h); break;
  159. case 1: sw->setBounds (x + w, y, shadowEdge, h); break;
  160. case 2: sw->setBounds (x, y, w, shadowEdge); break;
  161. case 3: sw->setBounds (x, owner->getBottom(), w, shadowEdge); break;
  162. default: break;
  163. }
  164. if (sw == nullptr)
  165. return;
  166. sw->toBehind (i == 3 ? owner : shadowWindows.getUnchecked (i + 1));
  167. }
  168. }
  169. }
  170. else
  171. {
  172. shadowWindows.clear();
  173. }
  174. }