diff --git a/modules/juce_gui_basics/misc/juce_DropShadower.cpp b/modules/juce_gui_basics/misc/juce_DropShadower.cpp index 0994c4ae0b..6decf6d5f7 100644 --- a/modules/juce_gui_basics/misc/juce_DropShadower.cpp +++ b/modules/juce_gui_basics/misc/juce_DropShadower.cpp @@ -75,6 +75,84 @@ private: JUCE_DECLARE_NON_COPYABLE (ShadowWindow) }; +class ParentVisibilityChangedListener : public ComponentListener +{ +public: + ParentVisibilityChangedListener (Component& r, ComponentListener& l) + : root (&r), listener (&l) + { + if (auto* firstParent = root->getParentComponent()) + updateParentHierarchy (firstParent); + } + + ~ParentVisibilityChangedListener() override + { + for (auto& compEntry : observedComponents) + if (auto* comp = compEntry.get()) + comp->removeComponentListener (this); + } + + void componentVisibilityChanged (Component&) override + { + listener->componentVisibilityChanged (*root); + } + + void componentParentHierarchyChanged (Component& component) override + { + if (root == &component) + if (auto* firstParent = root->getParentComponent()) + updateParentHierarchy (firstParent); + } + +private: + class ComponentWithWeakReference + { + public: + explicit ComponentWithWeakReference (Component& c) + : ptr (&c), ref (&c) {} + + Component* get() const { return ref.get(); } + + bool operator< (const ComponentWithWeakReference& other) const { return ptr < other.ptr; } + + private: + Component* ptr; + WeakReference ref; + }; + + void updateParentHierarchy (Component* rootComponent) + { + const auto lastSeenComponents = std::exchange (observedComponents, [&] + { + std::set result; + + for (auto node = rootComponent; node != nullptr; node = node->getParentComponent()) + result.emplace (*node); + + return result; + }()); + + const auto withDifference = [] (const auto& rangeA, const auto& rangeB, auto&& callback) + { + std::vector result; + std::set_difference (rangeA.begin(), rangeA.end(), rangeB.begin(), rangeB.end(), std::back_inserter (result)); + + for (const auto& item : result) + if (auto* c = item.get()) + callback (*c); + }; + + withDifference (lastSeenComponents, observedComponents, [this] (auto& comp) { comp.removeComponentListener (this); }); + withDifference (observedComponents, lastSeenComponents, [this] (auto& comp) { comp.addComponentListener (this); }); + } + + Component* root = nullptr; + ComponentListener* listener = nullptr; + std::set observedComponents; + + JUCE_DECLARE_NON_COPYABLE (ParentVisibilityChangedListener) + JUCE_DECLARE_NON_MOVEABLE (ParentVisibilityChangedListener) +}; //============================================================================== DropShadower::DropShadower (const DropShadow& ds) : shadow (ds) {} @@ -109,6 +187,11 @@ void DropShadower::setOwner (Component* componentToFollow) updateParent(); owner->addComponentListener (this); + // The visibility of `owner` is transitively affected by the visibility of its parents. Thus we need to trigger the + // componentVisibilityChanged() event in case it changes for any of the parents. + visibilityChangedListener = std::make_unique (*owner, + static_cast (*this)); + updateShadows(); } } diff --git a/modules/juce_gui_basics/misc/juce_DropShadower.h b/modules/juce_gui_basics/misc/juce_DropShadower.h index 172dd9f0ea..b2927f7607 100644 --- a/modules/juce_gui_basics/misc/juce_DropShadower.h +++ b/modules/juce_gui_basics/misc/juce_DropShadower.h @@ -75,6 +75,8 @@ private: void updateParent(); void updateShadows(); + std::unique_ptr visibilityChangedListener; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DropShadower) };