Is it possible to only repaint a Component manually

Hi, I noticed that the Paint function in a Component is constantly called by default, even when there is nothing changed. Is there a way to disable this and only call repaint() when necessary?

Or is there a reason why the Paint function needs to be called all the time?

here is an example from the JUCE examples folder:

class HelloWorldDemo  : public Component
{
public:
    //==============================================================================
    HelloWorldDemo()
    {
        addAndMakeVisible (helloWorldLabel);

        helloWorldLabel.setFont (Font (40.00f, Font::bold));
        helloWorldLabel.setJustificationType (Justification::centred);
        helloWorldLabel.setEditable (false, false, false);
        helloWorldLabel.setColour (Label::textColourId, Colours::black);
        helloWorldLabel.setColour (TextEditor::textColourId, Colours::black);
        helloWorldLabel.setColour (TextEditor::backgroundColourId, Colour (0x00000000));

        addAndMakeVisible (quitButton);
        quitButton.onClick = [] { JUCEApplication::quit(); };

        setSize (600, 300);
    }

    //==============================================================================
    void paint (Graphics& g) override
    {
        g.fillAll (Colour (0xffc1d0ff));

        g.setColour (Colours::white);
        g.fillPath (internalPath);

        g.setColour (Colour (0xff6f6f6f));
        g.strokePath (internalPath, PathStrokeType (5.200f));
    }

    void resized() override
    {
        helloWorldLabel.setBounds (152, 80, 296, 48);
        quitButton.setBounds (getWidth() - 176, getHeight() - 60, 120, 32);

        internalPath.clear();
        internalPath.startNewSubPath (136.0f, 80.0f);
        internalPath.quadraticTo (176.0f, 24.0f, 328.0f, 32.0f);
        internalPath.quadraticTo (472.0f, 40.0f, 472.0f, 104.0f);
        internalPath.quadraticTo (472.0f, 192.0f, 232.0f, 176.0f);
        internalPath.lineTo (184.0f, 216.0f);
        internalPath.lineTo (200.0f, 168.0f);
        internalPath.quadraticTo (96.0f, 136.0f, 136.0f, 80.0f);
        internalPath.closeSubPath();
    }

private:
    //==============================================================================
    Label helloWorldLabel { {}, TRANS("Hello World!") };
    TextButton quitButton { TRANS("Quit") };
    Path internalPath;

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HelloWorldDemo)
};

paint should not called constantly. It should only be called when one of your Components requests a repaint, either directly, because it is a child of another Component being repainted, or the OS requests it. Either something is causing the repaint, or there is a bug.

thanks for the reply! I am running JUCE using an Apple m1 (11.4). Do you think this might be a bug on JUCE’s side (that it’s not working properly on m1)? I ran the example app without modifying anything and it’s calling repaint all the time. with the app that I built, I have the same issue.

You say it’s calling repaint all the time? Do you mean it’s calling paint() all the time, or repaint()? If repaint() is getting called repeatedly, you might put a breakpoint on it to see where the calls are coming from. I think the usual cause of that is doing something in paint that causes repaint to be called, such as changing a component’s size or position.

Are you moving the mouse over the app? This will require repaints of anything under the mouse pointer.

Hi! I meant it’s calling paint() all the time. Thanks for the suggestion!

I see. You are right. if I don’t have the mouse over the window, it doesn’t trigger paint function.

All applications (JUCE or not) will do that. ie. the OS has to ask the app to repaint what is under the mouse cursor.

You can disable this using setRepaintsOnMouseActivity (false) on the component in question.

https://docs.juce.com/master/classComponent.html#a7845e402a21d2b86d8c4ec48bc7c93b1

If it takes longer to paint the component than it does to paint an image of the same size, you should call setBufferedToImage (true) on the component to have it only repaint when you call repaint() directly.

2 Likes