LookAndFeel vs dynamic_casting

Hey there

I’m getting a behaviour that I don’t understand, to do with how lookAndFeel classes are inherited.

I have a class that extends lookAndFeel_v4 by adding a simple int testInt member variable.

In a main component with:

setLookAndFeel (&myLaf);

I can then:

MyLaf* dynCastMyLaf = dynamic_cast<MyLaf*> (&getLookAndFeel());
DBG(dynCastJRLaf.testInt);

I know I don’t need to do this at all cos myLaf already exists and gives me access to testInt. This is just for testing the dynamic_casting.

However, if I try this on a child component of maincomponent I get nullptr as a result of the dynamic_cast. I don’t understand why, though.

Are copies of LAFs used in component inheritance, rather than references? Is that why I can’t dynamic cast it back?

I’d definitely appreciate any thought or advice on this!

Best wishes and many thanks in advance,
Jeff

It could be a matter of what happens when.
When the child components are created it’s usually in the constructor.
If you access the lookAndFeel in the constructor of main or a child component, your specialised LookAndFeel is not yet set.

Nevertheless I would advise against putting information in the LookAndFeel. It is designed to be stateless, only drawing code that can be used by any component.

Every Component has a NamedValueSett that you can use using getProperties()
You can add your data there, and in the draw methods you usually get a reference to the component to read the information.

And it has also the benefit that you don’t need the dynamic_cast.

Use it like:

slider.getProperties().getWithDefault ("testInt", 5);
1 Like

Thanks for this interesting information @Daniel very kind of you.

I think what I’m trying to ultimately achieve here is the ability to have a bunch of these extended-LAFs a bit like how there’s LookAndFeel_v2/3/4 etc. The purpose being to change the lookAndFeel in one place and have it propagate through the apps UI. In that sense these extended-LAF may store options for colours, button styles, and also how much time I want UI things to animate/fade for.

I see. Just bear in mind that every information you put in the LookAndFeel is global to all components of that type.

And that you need a fallback strategy in case the LookAndFeel is not of the expected type like you experienced it in your OP.

I had to write three versions before I found a way I was happy with. This is how I do it:

class MyComponent : public juce::Component
{
public:
    struct LookAndFeelMethods
    {
        virtual void drawMyComponent (juce::Graphics& g, juce::Rectangle<int> bounds, MyComponent& c) = 0;
    };

    void lookAndFeelChanged() override
    {
        if (auto* lnf = dynamic_cast<LookAndFeelMethods*>(&getLookAndFeel())
            myLookAndFeel = *lnf;
        else
            myLookAndFeel = defaultLookAndFeel;
    }

    void paint (juce::Graphics& g) override
    {
        myLookAndFeel.drawMyComponent (g, getLocalBounds(), *this);
    }

private:
    MyLookAndFeel       defaultLookAndFeel;
    LookAndFeelMethods& myLookAndFeel { defaultLookAndFeel };
};

MyLookAndFeel should be a valid implementation derived from MyComponent::LookAndFeelMethods. You can also just implement it in the LookAndFeelMethods and not make it pure virtual.

This guarantees that you always have a lookAndFeel that satisfies your API and you don’t need to cast everytime.

But maybe you have a better idea, I am curious.

1 Like

Thanks very much for this @Daniel. This was very valuable insight.

In my case the perhaps the simplest choice seems to be using a class that handles my custom colours and variables, then passing it as a reference.

Trying to crowbar all the behaviours I was hoping for into a variation of LAF seems potentially more trouble than its worth.

I can share with you the solution I chose in PluginGuiMagic:

The LookAndFeel uses the findColour mechanism. That looks first on the component itself if that colour was set and only if it’s not set it looks up in the LookAndFeel (after figuring out the current LookAndFeel via Component::getLookAndFeel()). So my system makes sure the right colour is always set to the Component directly.
The GUI in PGM is completely dynamic, so when setting the hierarchy and layout machines it also resolves the selected colours and other styling annd layout attributes in a CSS manner and sets them directly on the Component.
That way there is no runtime overhead you can use the original LookAndFeel just like your own (with the exception of the PopupMenu in ComboBox, that needs fixing in the LookAndFeel to use the new targetComponent).

1 Like