Mouse over scrollbar causes paint() to fire?

(I apologize for my verbiage, I am new to JUCE).

Win32 x64

I have a component _rMidiTrackDisplay
I have a Scrollbar attached at the bottom of the component

I position the ScrollBar at the bottom of the _rMidiTrackDisplay (component) As Such:

void _rMidiTrackDisplay::resized()
{juce::Rectangle Bounds=(this->getLocalBounds)();
(this->ScrollBar->setSize)((Bounds.getWidth)(),_RMIDI_TRACKDISPLAY_SCROLLBARHEIGHT);
(this->ScrollBar->setTopLeftPosition)(0,(Bounds.getHeight()-_RMIDI_TRACKDISPLAY_SCROLLBARHEIGHT));
}

When I move the mouse over the ScrollBar (anywhere on it) it causes a Paint event to fire (void _rMidiTrackDisplay::paint(Graphics& Graphics))
I am not clicking on the scrollbar, the mouse buttons are not down.
I am simply moving the mouse over the scroll bar.

The Graphics.getClipBounds() in paint() is the entire client area of the Scroll Bar.

Why does this happen?
How can I prevent It?

Thanks in advance

By default juce::Component objects assume they can have transparency or “empty” sections, allowing you to see parts of other components behind them. Because this is the case, repainting those components causes the components in the background to also be repainted.

There is a function Component::setOpaque(bool) which allows you to inform the component of whether or not its graphics truly cover up everything behind it or not.

When you have a component that draws graphics over its entire bounds (so nothing behind it can be seen) you should call setOpaque(true) on it. This prevents the components behind from also being repainted when the opaque component is repainted

Thank Tony.
Very informative.

You are correct.

But when I set (ScrollBar->setOpaque)(true); the contents which is in the component (above the scrollbar) gets duplicated into the scroll bar client area (the top part of the content, the height of the scroll bar).

I will poke at it a little further to see if I can disseminate what is going on.

Sounds like the scrollbar might not be a candidate for isOpaque(true). Is the scroll bar being drawn with any transparency? Or any empty sections in the scroll bar bounds in which the scroll bar isn’t drawing anything?

If so you must change the drawing of the scrollbar so it fills its entire bounds with solid, non-transparent colors.

When a component is opaque but doesn’t fill its whole bounds, the underlying pixel data of the unfilled sections will be garbage (either random data or sometimes I’ve seen it have old data from other pieces of the UI)

Thanks Tony.
Again I am new to juce.

Are you saying that I need to derive a class from ScrollBar and perform the painting myself?

That topic even can get into a can of worms… :slight_smile:

If you explicitly want the scroll bar to fill in with solid colours and for it to not cause repaints in your background component, then you can do one of two things:

  1. Create a custom ScrollBar subclass that simply overrides paint() for custom painting

  2. Subclass from one of the LookAndFeel subclasses such as LookAndFeel_V4, and override the ScrollBar::LookAndFeelMethods to do your custom painting

…and then set the component to isOpaque(true).

If you’re new to JUCE I would highly recommend option #1 since it’s simpler and easy to follow.

Option #2 would be good to learn about (there’s a LookAndFeel tutorial here) in the long run if you need more custom graphics across the rest of your app.

When doing lots of customisation of JUCE widgets its much easier to simply create your own LookAndFeel subclass(es) instead of having to create a new subclass of each widget style

In case anyone else is reading we are talking about version 5.4.3

I believe the junk appearing within the scroll bar is the result of

  • LookAndFeel_V1::drawScrollbar() performs g.fillAll (bar.findColour (ScrollBar::backgroundColourId));
  • LookAndFeel_V2::drawScrollbar() performs g.fillAll (scrollbar.findColour (ScrollBar::backgroundColourId));
  • LookAndFeel_V3::drawScrollbar() and LookAndFeel_V4::drawScrollbar() do not look like it touches the background of the scroll bar.
    Why this is so I have not a clue, perhaps it is by design.

Since I started with a Projucer project I selected LookAndFeel_V4 because it was the newest/biggest/bestes!

I will wheedle through the code some more and decide what to do about it

In the meantime to refocus on my original question which was why a mouseover my scroll bar was causing my ‘container’ components paint() to fire.

  • Yes, to reaffirm Tony (ScrollBar->setOpaque)(true) stops this from happening.

Again, I am in the optimization phase for my application. Any CPU’s I can save are valuable to me. When perusing the juce code I noticed the following. juce_ScrollBar.cpp:

ScrollBar::ScrollBar (bool shouldBeVertical) : vertical (shouldBeVertical)
{
setRepaintsOnMouseActivity (true); // _rMidi Yikes!
setFocusContainer (true);
}

And juce_Component.cpp (ScrollBar is derived from Component):

void Component::internalMouseEnter (MouseInputSource source, Point relativePos, Time time)
{
// …
if (flags.repaintOnMouseActivityFlag)
repaint(); // _rMidi Yikes!
//…
}

So it seems any mouse event (including enter or leave) will cause the entire ScrollBar to be invalidated and re-painted. Am I correct about this?

I am sure in the end I will end up deriving my own class from juce::ScrollBar() to at the least turn this behavior off.

Thanks Tony for the help so far.

1 Like

The intend is that the scrollbar gets highlighted by the mouse-over, so the user can clearly see that clicking and dragging it, would affect it. All operating systems and apps do that, so removing it to avoid a single paint call seems like cutting of your nose to spite your face.

1 Like

Thank you reFx for your opinion.
I am not being facetious, I truly mean it.

I was worried that flags.repaintOnMouseActivityFlag encompassed a mouseMove() which would produce a lot of (this->repaint)(). Yes (this->repaint)() invalidates the entire region of the scroll bar and queues the real paint() call up to the GUI thread. Yes multiple (this->repaint)() will coalesce until the GUI thread gets around to it.

From empirical observation I see that flags.repaintOnMouseActivityFlag does not
encompassed a mouseMove() but only mouseEnter() and mouseLeave().