So a year or more ago I began using JUCE and it has been a long, difficult year in many coffee shops all over town. But I am pleased to report that I am now a seasoned enough veteran to finally post about my first bug report! I discovered this issue troubleshooting my app and narrowed down what was causing the issue.
The issue happens where a slider begins to become unresponsive, as in, it skips values and jumps values and makes it difficult to end up on a specific value.
I have one component that appears with using setVisible(true), and that appears over-top of a element that is below. the top element contains a slider. there is a timer callback that is running that updates the lower component pretty much every second, and it is calling repaint on this lower component. Because they both occupy the same screen space, I am guessing, this is the cause of the issue. The slider within the top component loses accuracy only when the timer callback is running and that call back is repainting the component it is appearing in front of. After extensive testing I have ruled this out to be the issue. It is very possible to just make it so the repaint call only happens if the component is visible or shown, which is how I will work around this issue but I think it is worth reporting the bug so the devs can fix it.
There are many factors that could lead to such a behaviour. Without more information we can just guess.
It could be that the redrawing of the background component (that also redraws all the children on top of it) takes too long and blocks the UI thread. Because of this the slider freezes when you drag it and the redraw happens.
You maybe have to optimise your UI (shadows, pictures, gradients, other calculationsâŠ). Make sure you test this in release mode (much faster for UI) and use a CPU profiler to find the problem. You can also try to make your UI smaller (reduce the width and height) and see the difference.
Important to know is, that a Component is considered to be opaque by default, even if it doesnât draw all pixels in your implementation.
This setting will exclude that area from any paint() calls (in some situations that are out of your control it might still draw it, e.g. resizes) and will make the component in front catch the mouse events first.
To avoid this behavior of painting, call setOpaque (false).
To let mouse events pass through to the component behind, call setInterceptsMouseClicks (false, false)
And to get a Component to appear in front of itâs siblings, call toFront()
The child added first (i.e. order of addAndMakeVisible or addChildComponent) is the back-most.
Okay thanks for the replies. I was thinking it was some type of issue where the timer callback was delaying something about how the slider was updating its UI part of itself. originally I thought the way I had set up using the broadcaster / listener system was locking this update or interfering with it in some way.
The fix I used, was to simply just check if the top component is visible as part of the lower components callback. if its visible then it wont call repaint(), and now everything is working smoothly.
This is used in the way like a certain UI Panel is used like an âalert windowâ that accepts input, and its over-top of a lower one that normally just displays default information. hope this helps anyone who might encounter this.
Actually not, as mentionned in the documentation of setOpaque() : By default, components are considered transparent, unless this is used to make it otherwise.
perhaps here it is better to use âisShowing()â instead of âisVisible()â?
If the component is set to be visible but its parent is hidden, ultimately it is not showing (isShowing() will return false) while isVisible() will still return true causing the timer to run unnecessarily.
I was thinking about that too. The problem is, you could turn it visible, but the parent is invisible. So the timer is not started.
Later the parent becomes visible⊠Somebody needs to check, if visibilityChanged() is called, if the parent becomes visible?
âŠcheckingâŠ
Looking at setVisible() and subsequently sendVisibilityChangeMessage() this isnât the case, so the timer wouldnât start.
EDIT: maybe a good feature request to call sendVisibilityChanged on children as well? IDK, leave that to the juce team
Ah I always assumed âvisibilityChanged()â was recursive into children too (Iâm not using it often).
Perhaps a new callback called âshowingChanged()â could be introduced to do the right thing in this case and other numerous similar scenarios.
It feels like a better solution than starting the timer unnecessarily (as in the code above), or frequently polling for isShowing() (which is another alternative)
IMHO a new callback is cluttering the interface, and we both expected visibilityChanged to be called in that case, so it is rather fixing a bug than changing behaviour.
And I cannot see a drawback from a few extra calls, since it is a pure âheads-up signalâ.
I agree with you and if it were my code, Iâd certainly go the way you suggested.
What Iâm afraid of is that someone out there may have written some spaghetti code where visibilityChanged() in one child acts on the visibility of some parent Component.
In that case, making it recursive may cause an endless loop
Totally agree, when thereâs an easy way to signal the fact to the developer. Unfortunately, catching an endless recursion here and trigger an assertion would require adding a âbool isInVisibilityChangeâ member to Component, which seems overkill in this case
ComponentMovementWatcher is implemented as ComponentListener.
The ComponentListener is triggered from sendVisibilityChangeMessage(), and also only on the component itself.
So this approach will suffer the same.
But ComponentMovementWatcherâs constructor registers with all the parents, so itâs listening to the whole hierarchy. It also updates this when the hierarchy changes.
Oh right, I missed that.
Interesting approach, although personally I donât like it. It doesnât take into account, if the hierarchy changes after attaching the ComponentMovementWatcher.
I agree, it is a rare use case, but if it happens, you wonât find that bug anytime soon.
Hmm, doesnât it do that in ComponentMovementWatcher::componentParentHierarchyChanged (Component&)?
That method is called whenever the component of one of its parents changes something in the hierarchy, because the ComponentMovementWatcher is registered with all of them.
And at the end of its body, it calls the componentVisibilityChanged(Component&) method to check whether the visibility has changed. Am I missing something?
(although I agree, if it were implemented in Component natively with visibilityChanged(), thatâd be far less convoluted)
Ok, I give up, didnât intend to follow the worakound of ComponentMovementWatcher to itâs end.
Wasnât JUCEâs brightest hour when that was added, but thatâs just my opinion.