PopupMenu::CustomComponent: setVisibility of child Component changes highlighted state

Hi, my PopupMenu::CustomComponent draws one of the two child Drawables depending on its highlighted state. The only virtual method which I could find to get updated on highlighted state changes seems to be draw() which is probably inappropriate.

However when switching the visibility of the child components from inside draw() the highlighted state itself changes (…!) once the PopupMenu::CustomComponent was selected and the mouse is moved to an item above - leading to endless flickering.

The PopupMenu::CustomComponent is used to hold a submenu.

class menuitem:
	public juce::PopupMenu::CustomComponent {
	juce::Component* select[2]= {};
	void getIdealSize (int& width, int& height) override {
		height= getHeight();
		width= getWidth();
    }
	void paint (juce::Graphics& g) override {
std::clog<<__FUNCTION__<<" "<<isItemHighlighted()<<std::endl;
		if (select[0])
			select[0]->setVisible(!isItemHighlighted());
		if (select[1])
			select[1]->setVisible(isItemHighlighted());
	}
	~menuitem(){
		deleteAllChildren();
	}
public:
	menuitem(const char* svg){
		auto drawable= juce::Drawable::createFromSVG(
			*juce::ScopedPointer<juce::XmlElement>(juce::XmlDocument::parse(svg)));
		addAndMakeVisible (drawable);
		setBounds (drawable->getBoundsInParent());
		for (int idx=0; idx<std::max(drawable->getNumChildComponents(),2); idx++)
			select[idx]= drawable->getChildComponent(idx);
	}
};

How can I avoid the flickering? Or which method should I use to update the children’s visibility?

You are absolutely right! I shouldn’t bother at all! Who cares anyways, its just flickering: People are used to much bigger bugs.

That’s not surprising if you try to change the visibility inside the paint() callback!

When paint() is called it has already painted the child components! So when you change their visibility, it’ll need to asynchronously repaint again to draw it in the new state. Obviously this will flicker!

Thanks, @jules, great answer! But the OP was asking for a better place and no, its not obvious that the highlighted state itself (isItemHighlighted()) changes because of re-painting.
So, where should the visibility be set? I don’t see any other virtual method which would be triggered on a isItemHighlighted() change so it could be overridden to switch the graphics.

I would have figured it out by myself if it was somewhat documented.

In this case I would avoid adding the drawables as children, instead I would just draw them:

class MyMenuItem : juce::PopupMenu::CustomComponent
{
    void paint (juce::Graphics& g) override {
        if (isItemHighlighted) {
            if (highlighted) highlighted->drawWithin (g, getLocalBounds(), 
                                                      RectanglePlacement (RectanglePlacement::centred), 1.0);
        {
        else {
            if (normal) normal->drawWithin (g, getLocalBounds(), 
                                            RectanglePlacement (RectanglePlacement::centred), 1.0);
        }
    }
private:
    ScopedPointer<Drawable> normal;
    ScopedPointer<Drawable> highlighted;
}

Just as an idea…

2 Likes

Thanks @daniel, that was the hint I was looking for! Great!

@roli:
However there is a remaining issue which changes isItemHighlighted(), which IMHO should be fixed. Further, the docs claim:

Note that the preferred way to render a drawable in future is by using it as a component and adding it to a parent, so you might want to consider that before using this method.

How can this be achieved with a PopupMenu::CustomComponent?

1 Like

I could be wrong, but I think time has outpaced the future here. At least I always had trouble using Drawables as Components. The placement and setBounds doesn’t work well together for me. So as long as the API allows me I will stick to draw them.
IMHO they shouldn’t be Components in the first place, until a robust way to place them with setBounds is found.
Explanation: I was trying to use DrawableText as description in my GUI. But using setBounds didn’t work correctly, since there is also the setBoundingBox. They have to be used to match each other, and I wasn’t able to figure out how. If a DrawableText is a Component as user I should expect that it will draw a text int the given bounds.
Maybe it was not fair to generalise that the Drawables have problems sticking in their bounds…
Anyway, it’s just my personal opinion that it is not worth it to use Drawables as Components and work around those issues.

An official comment to the current status of that suggestion in the docs would be good indeed.

Exactly, and there is another thread fighting these odds:

However, it doesn’t look like there is to much intention on a general, robust fix. And I am actually getting tired of hinting bugs, which are simply ignored because someone found a workaround. I understand, its just more fun squeezing in new features than addressing issues.

I understand your frustration with some things. However I wouldn’t see it so negative. This API is a great responsibility, and if now with knowledge gathered over several years, that maybe, to stick with this case, having all drawables a Components, that interfere with their own placement method, wasn’t the best idea, it is impossible to remove that without a major resentment in the community.

So for me in this particular case, I would only want to know, if the suggestion to use Drawables as Components over just painting them, still holds true, or if it wasn’t actually better to invert the sign from encourage to discourage.

OT: Some of these things are known, some are not. But when they become known, you can be sure that the people there are as annoyed with the bugs as you are. But you can’t fix everything at the same time.
I am not letting the JUCE team from their responsibility, they have to fix bugs and problems. But I think they are trying to make the best for all and deserve appreciation for that, just as much as they get disproval from time to time.

I agree and my appreciation is shown in the badge and in the fact I am hanging here around, because a lot of our work is based on the framework.
However, if you look at the thread I asked for some help months ago, what I got was defending a bug without noticing the issue at all. What should I do with such an answer?

I highly appreciate the community which in such cases do almost always jump in with workarounds and suggestions.

Yes, I can see that and I am with you. I think the JUCE team will defend themselves, they don’t need me for that :wink:
I just wanted you to get you out of the “I’m so fed up with everything” mode, since I appreciate your input here. All good.

Done. Thanks :slight_smile: