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.

256 lines
8.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce::detail
  19. {
  20. constexpr char colourPropertyPrefix[] = "jcclr_";
  21. //==============================================================================
  22. struct ComponentHelpers
  23. {
  24. using SH = ScalingHelpers;
  25. #if JUCE_MODAL_LOOPS_PERMITTED
  26. static void* runModalLoopCallback (void* userData)
  27. {
  28. return (void*) (pointer_sized_int) static_cast<Component*> (userData)->runModalLoop();
  29. }
  30. #endif
  31. static Identifier getColourPropertyID (int colourID)
  32. {
  33. char buffer[32];
  34. auto* end = buffer + numElementsInArray (buffer) - 1;
  35. auto* t = end;
  36. *t = 0;
  37. for (auto v = (uint32) colourID;;)
  38. {
  39. *--t = "0123456789abcdef" [v & 15];
  40. v >>= 4;
  41. if (v == 0)
  42. break;
  43. }
  44. for (int i = (int) sizeof (colourPropertyPrefix) - 1; --i >= 0;)
  45. *--t = colourPropertyPrefix[i];
  46. return t;
  47. }
  48. //==============================================================================
  49. static bool hitTest (Component& comp, Point<float> localPoint)
  50. {
  51. const auto intPoint = localPoint.roundToInt();
  52. return Rectangle<int> { comp.getWidth(), comp.getHeight() }.contains (intPoint)
  53. && comp.hitTest (intPoint.x, intPoint.y);
  54. }
  55. // converts an unscaled position within a peer to the local position within that peer's component
  56. template <typename PointOrRect>
  57. static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept
  58. {
  59. if (comp.isTransformed())
  60. pos = pos.transformedBy (comp.getTransform().inverted());
  61. return SH::unscaledScreenPosToScaled (comp, pos);
  62. }
  63. // converts a position within a peer's component to the unscaled position within the peer
  64. template <typename PointOrRect>
  65. static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept
  66. {
  67. if (comp.isTransformed())
  68. pos = pos.transformedBy (comp.getTransform());
  69. return SH::scaledScreenPosToUnscaled (comp, pos);
  70. }
  71. template <typename PointOrRect>
  72. static PointOrRect convertFromParentSpace (const Component& comp, const PointOrRect pointInParentSpace)
  73. {
  74. const auto transformed = comp.affineTransform != nullptr ? pointInParentSpace.transformedBy (comp.affineTransform->inverted())
  75. : pointInParentSpace;
  76. if (comp.isOnDesktop())
  77. {
  78. if (auto* peer = comp.getPeer())
  79. return SH::unscaledScreenPosToScaled (comp, peer->globalToLocal (SH::scaledScreenPosToUnscaled (transformed)));
  80. jassertfalse;
  81. return transformed;
  82. }
  83. if (comp.getParentComponent() == nullptr)
  84. return SH::subtractPosition (SH::unscaledScreenPosToScaled (comp, SH::scaledScreenPosToUnscaled (transformed)), comp);
  85. return SH::subtractPosition (transformed, comp);
  86. }
  87. template <typename PointOrRect>
  88. static PointOrRect convertToParentSpace (const Component& comp, const PointOrRect pointInLocalSpace)
  89. {
  90. const auto preTransform = [&]
  91. {
  92. if (comp.isOnDesktop())
  93. {
  94. if (auto* peer = comp.getPeer())
  95. return SH::unscaledScreenPosToScaled (peer->localToGlobal (SH::scaledScreenPosToUnscaled (comp, pointInLocalSpace)));
  96. jassertfalse;
  97. return pointInLocalSpace;
  98. }
  99. if (comp.getParentComponent() == nullptr)
  100. return SH::unscaledScreenPosToScaled (SH::scaledScreenPosToUnscaled (comp, SH::addPosition (pointInLocalSpace, comp)));
  101. return SH::addPosition (pointInLocalSpace, comp);
  102. }();
  103. return comp.affineTransform != nullptr ? preTransform.transformedBy (*comp.affineTransform)
  104. : preTransform;
  105. }
  106. template <typename PointOrRect>
  107. static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, PointOrRect coordInParent)
  108. {
  109. auto* directParent = target.getParentComponent();
  110. jassert (directParent != nullptr);
  111. if (directParent == parent)
  112. return convertFromParentSpace (target, coordInParent);
  113. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
  114. return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent));
  115. JUCE_END_IGNORE_WARNINGS_MSVC
  116. }
  117. template <typename PointOrRect>
  118. static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p)
  119. {
  120. while (source != nullptr)
  121. {
  122. if (source == target)
  123. return p;
  124. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
  125. if (source->isParentOf (target))
  126. return convertFromDistantParentSpace (source, *target, p);
  127. JUCE_END_IGNORE_WARNINGS_MSVC
  128. p = convertToParentSpace (*source, p);
  129. source = source->getParentComponent();
  130. }
  131. jassert (source == nullptr);
  132. if (target == nullptr)
  133. return p;
  134. auto* topLevelComp = target->getTopLevelComponent();
  135. p = convertFromParentSpace (*topLevelComp, p);
  136. if (topLevelComp == target)
  137. return p;
  138. return convertFromDistantParentSpace (topLevelComp, *target, p);
  139. }
  140. static bool clipObscuredRegions (const Component& comp, Graphics& g,
  141. const Rectangle<int> clipRect, Point<int> delta)
  142. {
  143. bool wasClipped = false;
  144. for (int i = comp.childComponentList.size(); --i >= 0;)
  145. {
  146. auto& child = *comp.childComponentList.getUnchecked(i);
  147. if (child.isVisible() && ! child.isTransformed())
  148. {
  149. auto newClip = clipRect.getIntersection (child.boundsRelativeToParent);
  150. if (! newClip.isEmpty())
  151. {
  152. if (child.isOpaque() && child.componentTransparency == 0)
  153. {
  154. g.excludeClipRegion (newClip + delta);
  155. wasClipped = true;
  156. }
  157. else
  158. {
  159. auto childPos = child.getPosition();
  160. if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta))
  161. wasClipped = true;
  162. }
  163. }
  164. }
  165. }
  166. return wasClipped;
  167. }
  168. static Rectangle<int> getParentOrMainMonitorBounds (const Component& comp)
  169. {
  170. if (auto* p = comp.getParentComponent())
  171. return p->getLocalBounds();
  172. return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
  173. }
  174. static void releaseAllCachedImageResources (Component& c)
  175. {
  176. if (auto* cached = c.getCachedComponentImage())
  177. cached->releaseResources();
  178. for (auto* child : c.childComponentList)
  179. releaseAllCachedImageResources (*child);
  180. }
  181. //==============================================================================
  182. static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal)
  183. {
  184. return modal != nullptr
  185. && modal != &maybeBlocked
  186. && ! modal->isParentOf (&maybeBlocked)
  187. && ! modal->canModalEventBeSentToComponent (&maybeBlocked);
  188. }
  189. template <typename Function>
  190. static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function)
  191. {
  192. for (auto& ms : Desktop::getInstance().getMouseSources())
  193. if (auto* c = ms.getComponentUnderMouse())
  194. if (modalWouldBlockComponent (*c, &modal))
  195. (c->*function) (ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime());
  196. }
  197. };
  198. } // namespace juce::detail