How to scale a Drawable when it's added as child component?

Let’s say a Drawable, d, is created from an SVG image, and is to be painted inside a parent component such that it is scaled to a specific area.

I can draw it like this without any problems:

//in paint(Graphics &g)
d->drawWithin(g, Rectangle<float>(x, y, w, h), RectanglePlacement::Flags::centred, 1.0f);

But I’d prefer to add the drawable as a child component instead. What would be the equivalent way of setting its bounds and transforms etc such that it produces similar results as the drawWithin() method?

I tried this, but it doesn’t scale:

//Constructor
addAndMakeVisible(d);

// resize()
d->setBoundsToFit(x, y, w, h, Justification::centred, false);

Any ideas?

IIRC Drawables do not paint themselves, you have to draw' them. In which case you need some kind of wrapper component that callsdraw’ on the Drawable. You could make such a wrapper, then just use that as you would any other component.

I was under the impression that you can just as well use them as components directly, without using a wrapper. In fact, that seems to be the recommended way of using them (judging by the comments in the Drawable draw routines), and the actual drawing works fine already. It is only the scaling that I cannot get to work properly.

I think the comments mean that Drawables are components like everything else. But drawables dont paint themselves. I think this is because they dont know how you wish them to be fitted in their bounding box.

I’ve always had to build a wrapper around the topmost drawable so as to make it paint. This wrapper can then have flags to say how `draw’ should be called. Perhaps such a wrapper should be part of Juce.

Yeah, they do paint themselves and act like normal components! You can use them either like that, or call draw() directly.

Re: bounds, each type of drawable has different ways of setting its bounds. Maybe try DrawableComposite::resetBoundingBoxToContentArea() ?

Thanks. Some progress by passing the target rectangle to DrawableComposite::setBoundingBox(). It stretches to the specified boundig box now, but I still need to figure out how to preserve the original aspect ratio of the drawable.

At least one step closer to what Drawable::drawWithin() achieves =)

Did you find a satisfactory solution to this? drawWithin() works fine for me, and I can scale within paint() but adding as a Component I can’t get my SVG Drawable to scale at all. setBounds() etc has no effect…

Hi everyone,

I’ve been wrestling with this exact problem for a little while and I’ve found a fairly simple solution that works for me.

As per SVG loading custom, I’ve created a DrawableComposite* for an SVG object somewhere. In this example we’ll call that DrawableComposite* svg;

It is added as usual via
parent.addAndMakeVisible (svg);
and resized via
svg->setBoundingBox (...);

To get the aspect ratio, you can do the following:
aspectRatio = svg->getBoundingBox().getBounds(nullptr).getAspectRatio();

for a given const float aspectRatio. You can then use this value to control the width vs height in your various layout calculations.

I use a custom class containing the SVG DrawableComposite and aspectRatio, where the ratio is calculated on initialisation.

Hope this helps. :slight_smile:

2 Likes

Stumbled across this today too. Is there a reason why we can’t do setBounds on a Drawable and have it resize? Is there a clear example somewhere of creating a DrawableComposite from an SVG?

1 Like

It would be very convenient if we could do that, I agree. I can’t remember the reason but I always do things this way to avoid disappointment. :yin_yang:

Here are the different incantations I use to load an SVG object into std::unique_ptr<DrawableComposite> svg, depending on its source:

// from BinaryData::whatever
svg = std::unique_ptr<DrawableComposite>(dynamic_cast<DrawableComposite*>(
    DrawableComposite::createFromSVG(*std::unique_ptr<XmlElement>(XmlDocument::parse(BinaryData::whatever)).get())
));

// from InputStream* stream
svg = std::unique_ptr<DrawableComposite>(dynamic_cast<DrawableComposite*>(
    Drawable::createFromImageDataStream(*stream)
));
2 Likes

Replying to this with a usable answer in case anyone else lands here looking for a solution…

SVG Drawable could be thought of more like a Path inside a Component. To resize it, it isn’t sufficient to set the bounds, a transform needs to be set to scale and / or translate the actual SVG. So:

MainComponent::MainComponent()
{
    drawable = std::unique_ptr<Drawable>(Drawable::createFromImageData(BinaryData::MyIcon_svg, BinaryData::MyIcon_svgSize));
    addAndMakeVisible(drawable.get());
    setSize (600, 400);
}

void MainComponent::resized()
{
    Rectangle<int> bounds = getLocalBounds().reduced(100);
    drawable->setTransformToFit(bounds.toFloat(), RectanglePlacement::stretchToFit);
}
12 Likes