/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce::detail { constexpr char colourPropertyPrefix[] = "jcclr_"; //============================================================================== struct ComponentHelpers { using SH = ScalingHelpers; #if JUCE_MODAL_LOOPS_PERMITTED static void* runModalLoopCallback (void* userData) { return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); } #endif static Identifier getColourPropertyID (int colourID) { char buffer[32]; auto* end = buffer + numElementsInArray (buffer) - 1; auto* t = end; *t = 0; for (auto v = (uint32) colourID;;) { *--t = "0123456789abcdef" [v & 15]; v >>= 4; if (v == 0) break; } for (int i = (int) sizeof (colourPropertyPrefix) - 1; --i >= 0;) *--t = colourPropertyPrefix[i]; return t; } //============================================================================== static bool hitTest (Component& comp, Point localPoint) { const auto intPoint = localPoint.roundToInt(); return Rectangle { comp.getWidth(), comp.getHeight() }.contains (intPoint) && comp.hitTest (intPoint.x, intPoint.y); } // converts an unscaled position within a peer to the local position within that peer's component template static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept { if (comp.isTransformed()) pos = pos.transformedBy (comp.getTransform().inverted()); return SH::unscaledScreenPosToScaled (comp, pos); } // converts a position within a peer's component to the unscaled position within the peer template static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept { if (comp.isTransformed()) pos = pos.transformedBy (comp.getTransform()); return SH::scaledScreenPosToUnscaled (comp, pos); } template static PointOrRect convertFromParentSpace (const Component& comp, const PointOrRect pointInParentSpace) { const auto transformed = comp.affineTransform != nullptr ? pointInParentSpace.transformedBy (comp.affineTransform->inverted()) : pointInParentSpace; if (comp.isOnDesktop()) { if (auto* peer = comp.getPeer()) return SH::unscaledScreenPosToScaled (comp, peer->globalToLocal (SH::scaledScreenPosToUnscaled (transformed))); jassertfalse; return transformed; } if (comp.getParentComponent() == nullptr) return SH::subtractPosition (SH::unscaledScreenPosToScaled (comp, SH::scaledScreenPosToUnscaled (transformed)), comp); return SH::subtractPosition (transformed, comp); } template static PointOrRect convertToParentSpace (const Component& comp, const PointOrRect pointInLocalSpace) { const auto preTransform = [&] { if (comp.isOnDesktop()) { if (auto* peer = comp.getPeer()) return SH::unscaledScreenPosToScaled (peer->localToGlobal (SH::scaledScreenPosToUnscaled (comp, pointInLocalSpace))); jassertfalse; return pointInLocalSpace; } if (comp.getParentComponent() == nullptr) return SH::unscaledScreenPosToScaled (SH::scaledScreenPosToUnscaled (comp, SH::addPosition (pointInLocalSpace, comp))); return SH::addPosition (pointInLocalSpace, comp); }(); return comp.affineTransform != nullptr ? preTransform.transformedBy (*comp.affineTransform) : preTransform; } template static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, PointOrRect coordInParent) { auto* directParent = target.getParentComponent(); jassert (directParent != nullptr); if (directParent == parent) return convertFromParentSpace (target, coordInParent); JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent)); JUCE_END_IGNORE_WARNINGS_MSVC } template static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) { while (source != nullptr) { if (source == target) return p; JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) if (source->isParentOf (target)) return convertFromDistantParentSpace (source, *target, p); JUCE_END_IGNORE_WARNINGS_MSVC p = convertToParentSpace (*source, p); source = source->getParentComponent(); } jassert (source == nullptr); if (target == nullptr) return p; auto* topLevelComp = target->getTopLevelComponent(); p = convertFromParentSpace (*topLevelComp, p); if (topLevelComp == target) return p; return convertFromDistantParentSpace (topLevelComp, *target, p); } static bool clipObscuredRegions (const Component& comp, Graphics& g, const Rectangle clipRect, Point delta) { bool wasClipped = false; for (int i = comp.childComponentList.size(); --i >= 0;) { auto& child = *comp.childComponentList.getUnchecked(i); if (child.isVisible() && ! child.isTransformed()) { auto newClip = clipRect.getIntersection (child.boundsRelativeToParent); if (! newClip.isEmpty()) { if (child.isOpaque() && child.componentTransparency == 0) { g.excludeClipRegion (newClip + delta); wasClipped = true; } else { auto childPos = child.getPosition(); if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta)) wasClipped = true; } } } } return wasClipped; } static Rectangle getParentOrMainMonitorBounds (const Component& comp) { if (auto* p = comp.getParentComponent()) return p->getLocalBounds(); return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea; } static void releaseAllCachedImageResources (Component& c) { if (auto* cached = c.getCachedComponentImage()) cached->releaseResources(); for (auto* child : c.childComponentList) releaseAllCachedImageResources (*child); } //============================================================================== static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal) { return modal != nullptr && modal != &maybeBlocked && ! modal->isParentOf (&maybeBlocked) && ! modal->canModalEventBeSentToComponent (&maybeBlocked); } template static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function) { for (auto& ms : Desktop::getInstance().getMouseSources()) if (auto* c = ms.getComponentUnderMouse()) if (modalWouldBlockComponent (*c, &modal)) (c->*function) (ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime()); } }; } // namespace juce::detail