Crash on ScrollBar::lookAndFeelChanged() when setting L&F

I have a component owning a viewport, and when I set the viewport's LookAndFeel to a custom one, XCode crashes on the following line:


void ScrollBar::lookAndFeelChanged()
{
    setComponentEffect (getLookAndFeel().getScrollbarEffect()); // Crashes here (EXC_BAD_ACCESS)

    if (isVisible())
        resized();
}

The debugger reveals that indeed, the lookAndFeel of the ScrollBar object is indeed a NULL.

I've used the custom LookAndFeel class for several other components without problem and have yet to implement some scrollbar virtuals, so I'm not sure what's really going wrong.

Anyone have an idea? As usual, any help is much appreciated.

It should be impossible for getLookAndFeel to return a nullptr.. If that happens, something is very wrong! Maybe you've deleted your l+f object while it's still being used?

I'm quite sure I haven't. Other objects are using it as well (popupmenu's), etc.

Is lookAndFeelChanged() immediately (synchronously) called when a new lookandfeel is set, or does that go through a messaging system like the paint functionality?

-----

Could it be that viewport doesn't pass its new L&F along to its scrollbars? When I step into setLookAndFeel, it assigns the new L&F to its own internal variable and then calls sendLookAndFeelChange() recursively without telling the scrollbars what their new L&F is.

Don't think that'll be it. Try debugging into getLookAndFeel and see what happens.

Ok, nothing weird happens inbetween. The address of the L&F stays exactly the same right up to the point where getScrollBarEffect() gets called.

void ScrollBar::lookAndFeelChanged()
{
    // Split up for convenience
    LookAndFeel& lf = getLookAndFeel(); // correct address
    ImageEffectFilter* effect = lf.getScrollbarEffect(); // CRASH
    setComponentEffect (effect);

    if (isVisible())
        resized();
}

Just for clarification, this is what my LookAndFeel looks like:

class CustomLookAndFeel : public LookAndFeel_V3
{
public:
    CustomLookAndFeel();
    
    //! Return the font
    Font& getSemiBoldFont();
    Font& getBoldFont();
    
    // Inherited from ComboBox::LookAndFeelMethods
    void drawComboBox(Graphics& context, int width, int height, bool isButtonDown, int buttonX, int buttonY, int buttonW, int buttonH, ComboBox& comboBox) override final;
    void positionComboBoxText(ComboBox& comboBox, Label& labelToPosition) override final;
    
    // Inherited from PopupMenu::LookAndFeelMethods
    void drawPopupMenuBackground(Graphics& context, int width, int height) override final;
    void drawPopupMenuItem (Graphics& context, const Rectangle<int>& area, bool isSeparator, bool isActive, bool isHighlighted, bool isTicked, bool hasSubMenu,
                            const String& text, const String& shortcutKeyText, const Drawable *icon, const Colour *textColour) override final;
    void getIdealPopupMenuItemSize(const String& text, bool isSeparator, int standardMenuItemHeight, int& idealWidth, int& idealHeight) override final;
    Font getPopupMenuFont() override final;
    
private:
    Image comboboxLeftImage, comboboxSpacerImage, comboboxRightImage;
    
    Font openSansRegular;
    Font openSansSemiBold;
    Font openSansBold;
 };

 

Looks like a dangling pointer to me.

Ok, I still have no idea what the problem was, but stepping inside getLookAndFeel() made me realised components look for their lookandfeel in their parents, so setting it once in my top component solved the problem.

...really weird.

I'll get back at you when I figure out what the problem was.