mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
Component: Improve performance and opaque checks
This commit is contained in:
parent
21ae78c373
commit
157e115d09
3 changed files with 372 additions and 94 deletions
|
|
@ -209,7 +209,7 @@ public:
|
|||
return std::exchange (effect, &i) != &i;
|
||||
}
|
||||
|
||||
void paint (Graphics& g, Component& c, bool ignoreAlphaLevel)
|
||||
void paint (Graphics& g, Component& c, bool ignoreAlphaLevel, OpaqueLayer& opaqueLayer)
|
||||
{
|
||||
auto scale = g.getInternalContext().getPhysicalPixelScaleFactor();
|
||||
auto scaledBounds = c.getLocalBounds() * scale;
|
||||
|
|
@ -238,7 +238,7 @@ public:
|
|||
Graphics g2 (effectImage);
|
||||
g2.addTransform (AffineTransform::scale ((float) scaledBounds.getWidth() / (float) c.getWidth(),
|
||||
(float) scaledBounds.getHeight() / (float) c.getHeight()));
|
||||
c.paintComponentAndChildren (g2);
|
||||
c.paintComponentAndChildren (g2, opaqueLayer);
|
||||
}
|
||||
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
|
@ -257,6 +257,183 @@ private:
|
|||
ImageEffectFilter* effect;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Component::OpaqueLayer
|
||||
{
|
||||
public:
|
||||
explicit OpaqueLayer (const Component* c)
|
||||
: currentComponent (c)
|
||||
{
|
||||
jassert (c != nullptr);
|
||||
appendOpaqueChildren (*c, {}, c->getLocalBounds());
|
||||
}
|
||||
|
||||
[[nodiscard]] auto pushComponent (Component& component)
|
||||
{
|
||||
removeOpaqueComponent (component);
|
||||
currentComponent = &component;
|
||||
|
||||
const auto pos = component.getPosition();
|
||||
offsetFromOrigin += pos;
|
||||
|
||||
return ScopeGuard { [this, pos]
|
||||
{
|
||||
offsetFromOrigin -= pos;
|
||||
currentComponent = nullptr;
|
||||
} };
|
||||
}
|
||||
|
||||
Rectangle<int> getCurrentComponentBounds (const juce::Graphics& g)
|
||||
{
|
||||
// This function must only be called while the object returned by
|
||||
// pushComponent() is still alive!
|
||||
jassert (currentComponent != nullptr);
|
||||
const auto pos = currentComponent->getPosition();
|
||||
const auto bounds = getNonOccludedBoundsForCurrentComponent<ObscuredBy::allButChildren> (g.getClipBounds() - pos);
|
||||
|
||||
if (! bounds.isEmpty())
|
||||
return bounds + pos;
|
||||
|
||||
removeOpaqueChildren (*currentComponent);
|
||||
return {};
|
||||
}
|
||||
|
||||
Rectangle<int> getCurrentComponentPaintBounds (const juce::Graphics& g)
|
||||
{
|
||||
// This function must only be called while the object returned by
|
||||
// pushComponent() is still alive!
|
||||
jassert (currentComponent != nullptr);
|
||||
return getNonOccludedBoundsForCurrentComponent<ObscuredBy::childrenOnly> (g.getClipBounds());
|
||||
}
|
||||
|
||||
private:
|
||||
enum class ObscuredBy
|
||||
{
|
||||
childrenOnly,
|
||||
allButChildren
|
||||
};
|
||||
|
||||
template <ObscuredBy obscuredBy>
|
||||
Rectangle<int> getNonOccludedBoundsForCurrentComponent (Rectangle<int> clipBounds) const
|
||||
{
|
||||
auto visibleBounds = currentComponent->getLocalBounds().getIntersection (clipBounds);
|
||||
|
||||
if (visibleBounds.isEmpty())
|
||||
return {};
|
||||
|
||||
RectangleList visibleRegions { visibleBounds };
|
||||
|
||||
const auto occlude = [&] (const OpaqueComponentInfo& opaqueComponentInfo)
|
||||
{
|
||||
const auto opaqueBounds = opaqueComponentInfo.clippedBounds - offsetFromOrigin;
|
||||
|
||||
if (! opaqueBounds.intersects (visibleBounds))
|
||||
return;
|
||||
|
||||
if (opaqueBounds.contains (visibleBounds))
|
||||
visibleRegions.clear();
|
||||
else
|
||||
visibleRegions.subtract (opaqueBounds);
|
||||
|
||||
visibleBounds = visibleRegions.getBounds();
|
||||
};
|
||||
|
||||
if constexpr (obscuredBy == ObscuredBy::childrenOnly)
|
||||
{
|
||||
for (auto i = currentPosition; i < opaqueComponents.size(); ++i)
|
||||
{
|
||||
const auto& opaqueComponentInfo = opaqueComponents.getReference (i);
|
||||
|
||||
if (! currentComponent->isParentOf (opaqueComponentInfo.component))
|
||||
break;
|
||||
|
||||
occlude (opaqueComponentInfo);
|
||||
|
||||
if (visibleBounds.isEmpty())
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = opaqueComponents.size(); --i >= currentPosition;)
|
||||
{
|
||||
const auto& opaqueComponentInfo = opaqueComponents.getReference (i);
|
||||
|
||||
if (currentComponent->isParentOf (opaqueComponentInfo.component))
|
||||
break;
|
||||
|
||||
occlude (opaqueComponentInfo);
|
||||
|
||||
if (visibleBounds.isEmpty())
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return visibleBounds;
|
||||
}
|
||||
|
||||
void removeOpaqueComponent (const Component& component)
|
||||
{
|
||||
if (opaqueComponents[currentPosition].component == &component)
|
||||
++currentPosition;
|
||||
}
|
||||
|
||||
void removeOpaqueChildren (const Component& component)
|
||||
{
|
||||
for (const auto* child : component.getChildren())
|
||||
{
|
||||
if (currentPosition == opaqueComponents.size())
|
||||
return;
|
||||
|
||||
if (! isVisibleToLayer (*child))
|
||||
continue;
|
||||
|
||||
removeOpaqueComponent (*child);
|
||||
removeOpaqueChildren (*child);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isVisibleToLayer (const Component& c)
|
||||
{
|
||||
return detail::ComponentHelpers::isVisibleWithNonZeroArea (c)
|
||||
&& c.componentTransparency == 0
|
||||
&& ! c.isTransformed();
|
||||
}
|
||||
|
||||
void appendOpaqueChildren (const Component& parent,
|
||||
Point<int> parentOrigin,
|
||||
Rectangle<int> parentClippedBounds)
|
||||
{
|
||||
for (auto* child : parent.getChildren())
|
||||
{
|
||||
if (! isVisibleToLayer (*child))
|
||||
continue;
|
||||
|
||||
const auto childBounds = child->getBounds() + parentOrigin;
|
||||
const auto childClippedBounds = parentClippedBounds.getIntersection (childBounds);
|
||||
|
||||
if (childClippedBounds.isEmpty())
|
||||
continue;
|
||||
|
||||
if (child->isOpaque())
|
||||
opaqueComponents.add ({ child, childClippedBounds });
|
||||
|
||||
appendOpaqueChildren (*child, childBounds.getPosition(), childClippedBounds);
|
||||
}
|
||||
}
|
||||
|
||||
struct OpaqueComponentInfo
|
||||
{
|
||||
const Component* component;
|
||||
Rectangle<int> clippedBounds;
|
||||
};
|
||||
|
||||
int currentPosition{};
|
||||
Point<int> offsetFromOrigin{};
|
||||
const Component* currentComponent;
|
||||
Array<OpaqueComponentInfo> opaqueComponents;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Component::Component() noexcept
|
||||
: componentFlags (0)
|
||||
|
|
@ -1701,17 +1878,17 @@ void Component::paintOverChildren (Graphics&)
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void Component::paintWithinParentContext (Graphics& g)
|
||||
void Component::paintWithinParentContext (Graphics& g, OpaqueLayer& opaqueLayer)
|
||||
{
|
||||
g.setOrigin (getPosition());
|
||||
|
||||
if (cachedImage != nullptr)
|
||||
cachedImage->paint (g);
|
||||
else
|
||||
paintEntireComponent (g, false);
|
||||
paintEntireComponent (g, false, opaqueLayer);
|
||||
}
|
||||
|
||||
void Component::paintComponentAndChildren (Graphics& g)
|
||||
void Component::paintComponentAndChildren (Graphics& g, OpaqueLayer& opaqueLayer)
|
||||
{
|
||||
#if JUCE_ETW_TRACELOGGING
|
||||
{
|
||||
|
|
@ -1727,70 +1904,64 @@ void Component::paintComponentAndChildren (Graphics& g)
|
|||
}
|
||||
#endif
|
||||
|
||||
auto clipBounds = g.getClipBounds();
|
||||
const auto paintBounds = opaqueLayer.getCurrentComponentPaintBounds (g);
|
||||
|
||||
if (flags.dontClipGraphicsFlag && getNumChildComponents() == 0)
|
||||
{
|
||||
paint (g);
|
||||
}
|
||||
else
|
||||
if (! paintBounds.isEmpty())
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
if (! (detail::ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty()))
|
||||
paint (g);
|
||||
if (! isPaintingUnclipped())
|
||||
g.reduceClipRegion (paintBounds);
|
||||
|
||||
paint (g);
|
||||
}
|
||||
|
||||
for (int i = 0; i < childComponentList.size(); ++i)
|
||||
for (auto* child : getChildren())
|
||||
{
|
||||
auto& child = *childComponentList.getUnchecked (i);
|
||||
if (! detail::ComponentHelpers::isVisibleWithNonZeroArea (*child))
|
||||
continue;
|
||||
|
||||
if (child.isVisible())
|
||||
if (child->isTransformed() || child->componentTransparency != 0)
|
||||
{
|
||||
if (child.affineTransform != nullptr)
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
g.addTransform (*child.affineTransform);
|
||||
if (auto& transform = child->affineTransform)
|
||||
g.addTransform (*transform);
|
||||
|
||||
if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds()))
|
||||
child.paintWithinParentContext (g);
|
||||
}
|
||||
else if (clipBounds.intersects (child.getBounds()))
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
child->paintWithinParentContext (g, opaqueLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto componentPopper = opaqueLayer.pushComponent (*child);
|
||||
const auto componentBounds = opaqueLayer.getCurrentComponentBounds (g);
|
||||
|
||||
if (child.flags.dontClipGraphicsFlag)
|
||||
{
|
||||
child.paintWithinParentContext (g);
|
||||
}
|
||||
else if (g.reduceClipRegion (child.getBounds()))
|
||||
{
|
||||
bool nothingClipped = true;
|
||||
if (componentBounds.isEmpty())
|
||||
continue;
|
||||
|
||||
for (int j = i + 1; j < childComponentList.size(); ++j)
|
||||
{
|
||||
auto& sibling = *childComponentList.getUnchecked (j);
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
if (sibling.flags.opaqueFlag && sibling.isVisible() && sibling.affineTransform == nullptr)
|
||||
{
|
||||
nothingClipped = false;
|
||||
g.excludeClipRegion (sibling.getBounds());
|
||||
}
|
||||
}
|
||||
if (! child->isPaintingUnclipped())
|
||||
g.reduceClipRegion (componentBounds);
|
||||
|
||||
if (nothingClipped || ! g.isClipEmpty())
|
||||
child.paintWithinParentContext (g);
|
||||
}
|
||||
}
|
||||
child->paintWithinParentContext (g, opaqueLayer);
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
if (! isPaintingUnclipped())
|
||||
g.reduceClipRegion (getLocalBounds());
|
||||
|
||||
paintOverChildren (g);
|
||||
}
|
||||
|
||||
void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel)
|
||||
{
|
||||
OpaqueLayer opaqueLayer { this };
|
||||
paintEntireComponent (g, ignoreAlphaLevel, opaqueLayer);
|
||||
}
|
||||
|
||||
void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel, OpaqueLayer& opaqueLayer)
|
||||
{
|
||||
// If sizing a top-level-window and the OS paint message is delivered synchronously
|
||||
// before resized() is called, then we'll invoke the callback here, to make sure
|
||||
|
|
@ -1806,20 +1977,26 @@ void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel)
|
|||
|
||||
if (effectState != nullptr)
|
||||
{
|
||||
effectState->paint (g, *this, ignoreAlphaLevel);
|
||||
effectState->paint (g, *this, ignoreAlphaLevel, opaqueLayer);
|
||||
}
|
||||
else if (componentTransparency > 0 && ! ignoreAlphaLevel)
|
||||
{
|
||||
if (componentTransparency < 255)
|
||||
{
|
||||
OpaqueLayer transparentOpaqueLayer { this };
|
||||
g.beginTransparencyLayer (getAlpha());
|
||||
paintComponentAndChildren (g);
|
||||
paintComponentAndChildren (g, transparentOpaqueLayer);
|
||||
g.endTransparencyLayer();
|
||||
}
|
||||
}
|
||||
else if (isTransformed())
|
||||
{
|
||||
OpaqueLayer transformedOpaqueLayer { this };
|
||||
paintComponentAndChildren (g, transformedOpaqueLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
paintComponentAndChildren (g);
|
||||
paintComponentAndChildren (g, opaqueLayer);
|
||||
}
|
||||
|
||||
#if JUCE_DEBUG
|
||||
|
|
@ -3090,7 +3267,13 @@ struct ComponentTests : public UnitTest
|
|||
++numPaintCalls;
|
||||
}
|
||||
|
||||
void paintOverChildren (Graphics&) final
|
||||
{
|
||||
++numPaintOverChildrenCalls;
|
||||
}
|
||||
|
||||
int numPaintCalls = 0;
|
||||
int numPaintOverChildrenCalls = 0;
|
||||
Rectangle<int> lastClipBounds;
|
||||
};
|
||||
|
||||
|
|
@ -3116,12 +3299,16 @@ struct ComponentTests : public UnitTest
|
|||
parent->addAndMakeVisible (*child);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 0);
|
||||
expectEquals (parent->numPaintOverChildrenCalls, 0);
|
||||
expectEquals (child->numPaintCalls, 0);
|
||||
expectEquals (child->numPaintOverChildrenCalls, 0);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expectEquals (parent->numPaintOverChildrenCalls, 1);
|
||||
expectEquals (child->numPaintCalls, 1);
|
||||
expectEquals (child->numPaintOverChildrenCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Non-opaque children require their parent to repaint");
|
||||
|
|
@ -3157,6 +3344,7 @@ struct ComponentTests : public UnitTest
|
|||
|
||||
expectEquals (parent->numPaintCalls, 0);
|
||||
expectEquals (child->numPaintCalls, 1);
|
||||
expectEquals (parent->numPaintOverChildrenCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Opaque children don't require their parent to repaint (even when the parent uses setPaintingIsUnclipped (true))");
|
||||
|
|
@ -3176,6 +3364,7 @@ struct ComponentTests : public UnitTest
|
|||
|
||||
expectEquals (parent->numPaintCalls, 0);
|
||||
expectEquals (child->numPaintCalls, 1);
|
||||
expectEquals (parent->numPaintOverChildrenCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("A partially obscured parent will repaint with reduced clip bounds");
|
||||
|
|
@ -3218,6 +3407,7 @@ struct ComponentTests : public UnitTest
|
|||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 0);
|
||||
expectEquals (parent->numPaintOverChildrenCalls, 1);
|
||||
expectEquals (child1->numPaintCalls, 1);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
}
|
||||
|
|
@ -3245,11 +3435,44 @@ struct ComponentTests : public UnitTest
|
|||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 0);
|
||||
expectEquals (parent->numPaintOverChildrenCalls, 1);
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child1->numPaintOverChildrenCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
expectEquals (child3->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("An opaque component will hide parent-sibling components behind it");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child1 = std::make_unique<TestComponent>();
|
||||
const auto child2 = std::make_unique<TestComponent>();
|
||||
const auto child3 = std::make_unique<TestComponent>();
|
||||
|
||||
Rectangle<int> bounds { 0, 0, 100, 100 };
|
||||
parent->setBounds (bounds);
|
||||
|
||||
child1->setBounds (bounds);
|
||||
parent->addAndMakeVisible (*child1);
|
||||
|
||||
child2->setBounds (bounds);
|
||||
parent->addAndMakeVisible (*child2);
|
||||
|
||||
child3->setBounds (bounds);
|
||||
child3->setOpaque (true);
|
||||
child2->addAndMakeVisible (*child3);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 0);
|
||||
expectEquals (parent->numPaintOverChildrenCalls, 1);
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child1->numPaintOverChildrenCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintOverChildrenCalls, 1);
|
||||
expectEquals (child3->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("An opaque component will reduce the clip bounds of sibling components behind it");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
|
|
@ -3427,7 +3650,7 @@ struct ComponentTests : public UnitTest
|
|||
expectEquals (grandchild->numPaintCalls, 0);
|
||||
}
|
||||
|
||||
beginTest ("Components with a width of 0 will not have their paint function called");
|
||||
beginTest ("Components with a width of 0 will not have their paint functions called");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child = std::make_unique<TestComponent>();
|
||||
|
|
@ -3442,9 +3665,10 @@ struct ComponentTests : public UnitTest
|
|||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expectEquals (child->numPaintCalls, 0);
|
||||
expectEquals (child->numPaintOverChildrenCalls, 0);
|
||||
}
|
||||
|
||||
beginTest ("Components with a height of 0 will not have their paint function called");
|
||||
beginTest ("Components with a height of 0 will not have their paint functions called");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child = std::make_unique<TestComponent>();
|
||||
|
|
@ -3459,6 +3683,7 @@ struct ComponentTests : public UnitTest
|
|||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expectEquals (child->numPaintCalls, 0);
|
||||
expectEquals (child->numPaintOverChildrenCalls, 0);
|
||||
}
|
||||
|
||||
beginTest ("Transparent components will not be considered opaque");
|
||||
|
|
@ -3480,6 +3705,30 @@ struct ComponentTests : public UnitTest
|
|||
expectEquals (child->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Opaque components will only be considered opaque up to a transparent parent");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child1 = std::make_unique<TestComponent>();
|
||||
const auto child2 = std::make_unique<TestComponent>();
|
||||
|
||||
Rectangle<int> bounds { 0, 0, 100, 100 };
|
||||
parent->setBounds (bounds);
|
||||
|
||||
child1->setBounds (bounds);
|
||||
child1->setAlpha (0.5f);
|
||||
parent->addAndMakeVisible (*child1);
|
||||
|
||||
child2->setBounds (bounds);
|
||||
child2->setOpaque (true);
|
||||
child1->addAndMakeVisible (*child2);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Transformed components will not be considered opaque");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
|
|
@ -3499,6 +3748,30 @@ struct ComponentTests : public UnitTest
|
|||
expectEquals (child->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Opaque components will only be considered opaque up to a transformed parent");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child1 = std::make_unique<TestComponent>();
|
||||
const auto child2 = std::make_unique<TestComponent>();
|
||||
|
||||
Rectangle<int> bounds { 0, 0, 100, 100 };
|
||||
parent->setBounds (bounds);
|
||||
|
||||
child1->setBounds (bounds);
|
||||
child1->setTransform (AffineTransform::rotation (degreesToRadians (45.0f)));
|
||||
parent->addAndMakeVisible (*child1);
|
||||
|
||||
child2->setBounds (bounds);
|
||||
child2->setOpaque (true);
|
||||
child1->addAndMakeVisible (*child2);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Nested opaque components prevent parents from being painted");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
|
|
@ -3522,6 +3795,31 @@ struct ComponentTests : public UnitTest
|
|||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Areas of an opaque component outside its parent will not be considered opaque");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child1 = std::make_unique<TestComponent>();
|
||||
const auto child2 = std::make_unique<TestComponent>();
|
||||
const auto child3 = std::make_unique<TestComponent>();
|
||||
|
||||
parent->setBounds ({ 0, 0, 100, 100 });
|
||||
|
||||
child1->setBounds ({ 50, 0, 50, 100 });
|
||||
parent->addAndMakeVisible (*child1);
|
||||
|
||||
child2->setBounds ({ -50, 0, 100, 100 });
|
||||
child2->setOpaque (true);
|
||||
child1->addAndMakeVisible (*child2);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expect (parent->lastClipBounds == Rectangle { 0, 0, 50, 100 });
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
expect (child2->lastClipBounds == Rectangle { 50, 0, 50, 100 });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1194,7 +1194,7 @@ public:
|
|||
|
||||
If you enable this mode, you'll need to make sure your paint method doesn't call anything like
|
||||
Graphics::fillAll(), and doesn't draw beyond the component's bounds, because that'll produce
|
||||
artifacts. This option will have no effect on components that contain any child components.
|
||||
artifacts.
|
||||
*/
|
||||
void setPaintingIsUnclipped (bool shouldPaintWithoutClipping) noexcept;
|
||||
|
||||
|
|
@ -1276,14 +1276,20 @@ public:
|
|||
/** Indicates whether any parts of the component might be transparent.
|
||||
|
||||
Components that always paint all of their contents with solid colour and
|
||||
thus completely cover any components behind them should use this method
|
||||
thus completely cover any components behind them, can use this method to
|
||||
to tell the repaint system that they are opaque.
|
||||
|
||||
This information is used to optimise drawing, because it means that
|
||||
objects underneath opaque windows don't need to be painted.
|
||||
objects underneath opaque components or windows don't need to be painted
|
||||
or can have their clip bounds reduced to a smaller size.
|
||||
|
||||
By default, components are considered transparent, unless this is used to
|
||||
make it otherwise.
|
||||
Note however that there is a cost for every other component to check if
|
||||
it is being obscured by opaque components. This cost should be carefully
|
||||
weighed up against the benefits before deciding to enable this.
|
||||
|
||||
The default value of this property is false, which means that a
|
||||
component will be considered transparent unless setOpaque (true) is
|
||||
called on that component.
|
||||
|
||||
@see isOpaque
|
||||
*/
|
||||
|
|
@ -2712,6 +2718,8 @@ private:
|
|||
uint8 componentTransparency = 0;
|
||||
|
||||
//==============================================================================
|
||||
class OpaqueLayer;
|
||||
|
||||
static void internalMouseEnter (SafePointer<Component>, MouseInputSource, Point<float>, Time);
|
||||
static void internalMouseExit (SafePointer<Component>, MouseInputSource, Point<float>, Time);
|
||||
static void internalMouseDown (SafePointer<Component>, MouseInputSource, const detail::PointerState&, Time);
|
||||
|
|
@ -2733,8 +2741,9 @@ private:
|
|||
void internalRepaintUnchecked (Rectangle<int>, bool);
|
||||
Component* removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents);
|
||||
void reorderChildInternal (int sourceIndex, int destIndex);
|
||||
void paintComponentAndChildren (Graphics&);
|
||||
void paintWithinParentContext (Graphics&);
|
||||
void paintEntireComponent (Graphics&, bool, OpaqueLayer&);
|
||||
void paintComponentAndChildren (Graphics&, OpaqueLayer&);
|
||||
void paintWithinParentContext (Graphics&, OpaqueLayer&);
|
||||
void sendMovedResizedMessages (bool wasMoved, bool wasResized);
|
||||
void sendMovedResizedMessagesIfPending();
|
||||
void repaintParent();
|
||||
|
|
|
|||
|
|
@ -193,42 +193,6 @@ struct ComponentHelpers
|
|||
return convertFromDistantParentSpace (topLevelComp, *target, p);
|
||||
}
|
||||
|
||||
static bool clipChildComponent (const Component& child,
|
||||
Graphics& g,
|
||||
const Rectangle<int> clipRect,
|
||||
Point<int> delta)
|
||||
{
|
||||
if (! child.isVisible() || child.isTransformed())
|
||||
return false;
|
||||
|
||||
const auto newClip = clipRect.getIntersection (child.boundsRelativeToParent);
|
||||
|
||||
if (newClip.isEmpty())
|
||||
return false;
|
||||
|
||||
if (child.isOpaque() && child.componentTransparency == 0)
|
||||
{
|
||||
g.excludeClipRegion (newClip + delta);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto childPos = child.getPosition();
|
||||
return clipObscuredRegions (child, g, newClip - childPos, childPos + delta);
|
||||
}
|
||||
|
||||
static bool clipObscuredRegions (const Component& comp,
|
||||
Graphics& g,
|
||||
const Rectangle<int> clipRect,
|
||||
Point<int> delta)
|
||||
{
|
||||
auto wasClipped = false;
|
||||
|
||||
for (int i = comp.childComponentList.size(); --i >= 0;)
|
||||
wasClipped |= clipChildComponent (*comp.childComponentList.getUnchecked (i), g, clipRect, delta);
|
||||
|
||||
return wasClipped;
|
||||
}
|
||||
|
||||
static Rectangle<int> getParentOrMainMonitorBounds (const Component& comp)
|
||||
{
|
||||
if (auto* p = comp.getParentComponent())
|
||||
|
|
@ -263,6 +227,13 @@ struct ComponentHelpers
|
|||
function (c, ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime());
|
||||
}
|
||||
|
||||
static bool isVisibleWithNonZeroArea (const Component& component)
|
||||
{
|
||||
return component.isVisible()
|
||||
&& component.getWidth() > 0
|
||||
&& component.getHeight() > 0;
|
||||
}
|
||||
|
||||
class ModalComponentManagerChangeNotifier
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue