Paint clipped while dragging a component (Windows)

gui
windows

#1

I’m seeing some clipping of the painting when dragging a component around in a plug-in GUI on Windows 10. Is this expected to work?

I have reproduced the problem in the AudioDemoPlugin built from the latest develop commit of JUCE.

To reproduce:

  1. Derive a class from Component that has a ComponentDragger (calling the appropriate methods in mouseDown() and mouseDrag()) and fills an ellipse using its local bounds in its paint() method. Set an arbitrary size in the constructor.
  2. Add an instance to the editor and remove all other child components.
  3. Open the plug-in and drag the component around - the side of the component in the direction of dragging will appear slightly cropped.

component-moving


#2

Just looks like it’s a bit laggy in catching up on rendering the dirty rectangle list, most likely something you’ll only see in a particular plugin/host/OS combination. If it’s a problem then an easy fix is just to make the component bigger so a larger area gets trashed and repainted.

If you want to dig deeper, try the JUCE_ENABLE_REPAINT_DEBUGGING flag to see what’s going on.


#3

Yeah, I had tried turning on the repaint debugging but it didn’t seem to shed any light on the situation. I also tried painting the ellipse using the clip bounds, but that gave the same results.

From what I can tell by printing out the values, the rectangle received from GetUpdateRgn() in HWNDComponentPeer::handlePaintMessage() seems to always be correct; it contains the previous and current component bounds.

This image shows a release build of the standalone application format with repaint debugging turned on.

component-moving-app-release


#4

Yeah, nothing is actually wrong, it’s just that the sequence of repaint regions is running a bit late, so that part of the circle gets drawn before the remaining bit. Most likely just an artefact of the way the message queue is dispatching repaint requests in a plugin.

Like I said above, the easiest cheat is just to making the component bigger than the circle, so that you’d have to be dragging it very fast before you get this effect. (Or even more hackily, just repaint the whole window each time the object moves)


#5

If it’s standalone (as stated above) it’s got nothing to do with a plugin has it? Presumably the same behaviour will be true for any app written with JUCE on Windows, no?

It seems a shame to cheat or hack around this, and presumably inefficient too. Assuming it is true that this happens for any app written with JUCE, is it really that likely that nothing else could be done about this?


#6

I can confirm that this also happens in a JUCE GUI application.


#7

I can repaint an ellipse while dragging it around without any artifacts… have you tried grabbing an image of the component (Component:: createComponentSnapshot()), hiding the component and dragging the image around instead… and when you release the mouse hide/destroy the image and show the component…??

Rail


#8

I remember having something similar when I started to write my Eq stuff. It appeared that the clipping area was the same size as the “handle”'s rectangle, while its position was updated… After the fix, I can drag around Eq settings very, very fast without any artefacts AND the filters are updated without audio glitches.
I think you are not using the ComponentDragger pattern correctly…


#9

Are you drawing a slider in the GIF you attached? If so, I would expect the whole slider to be be repainted, so you wouldn’t see the issue. You could confirm this with repaint debugging switched on.

I haven’t tried this, but if possible I’d prefer not to do things like that just to get the component to paint properly.

Which fix are you referring to?

Here is the code for a component I have reproduced the problem with:

class Dot : public Component
{
    ComponentDragger dragger;

public:
    Dot()
    {
        setSize (20, 20);
    }

    void paint (Graphics& g) override
    {
        g.setColour (Colours::lightgreen); 
        g.fillEllipse (getLocalBounds().toFloat());
    }

    void mouseDown (const MouseEvent& e) override
    {
        dragger.startDraggingComponent (this, e);
    }

    void mouseDrag (const MouseEvent& e) override
    {
        dragger.dragComponent (this, e, nullptr);
    }
};

Is that not the intended way to use the dragger?


#10

No, I’m dragging an ellipse left and right which triggers a MIDI note at the velocity displayed… no Slider. This is for an in-house utility…

The pertinent code:

void CPreviewBar::paint (Graphics& g)
{
    g.setColour (Colours::orangered.darker());
    
    g.fillRect (getLocalBounds().reduced (4));
    
    if (m_bMouseDown)
        {
        g.setColour (Colours::yellow);
        
        float fSize = (float) getHeight() - 8.0f;
        
        m_fLastPosition = (float) m_iMousePosX - 9.0f;
        
        g.fillEllipse (jlimit (-4.0f, (float) getWidth() - 12.0f, m_fLastPosition), 4.0f, fSize, fSize);
        }
    else if (m_fLastPosition != -1.0f)
        {
        g.setColour (Colours::grey);
        
        float fSize = (float) getHeight() - 8.0f;
        
        g.setOpacity (0.5f);
        
        g.fillEllipse (jlimit (-4.0f, (float) getWidth() - 12.0f, m_fLastPosition), 4.0f, fSize, fSize);
        }
    
    g.setOpacity (1.0f);
    
    g.setColour (Colours::black);
    
    g.drawRect (getLocalBounds(), 4);
}

void CPreviewBar::resized()
{

}

void CPreviewBar::mouseDown (const MouseEvent& event)
{
    m_bMouseDown = true;
    
    m_iMousePosX = event.getMouseDownX();
    
    m_iPreviewVel = posToVelocity (m_iMousePosX);
    
    m_pParent->setPreviewVelocity (m_iPreviewVel);
    
    triggerPlayback (m_iPreviewVel);
    
    repaint();
    
    if (m_pParent->getAutoPreview())
        startTimer (m_iTimerVal);
}

void CPreviewBar::mouseDrag (const MouseEvent& event)
{
    m_iMousePosX = event.getPosition().getX();
    
    m_iPreviewVel = jlimit (0, 127, posToVelocity (m_iMousePosX));
    
    m_pParent->setPreviewVelocity (m_iPreviewVel);
    
    repaint();
}

void CPreviewBar::mouseUp (const MouseEvent& /* event */)
{
    stopTimer();
    
    m_bMouseDown = false;

    repaint();
}

Rail


#11

Unless I’m mis-understanding something @Rail_Jon_Rogut the component you have there is larger than the ellipse (much like a slider) so it kind of avoids the issue. In essence the component isn’t being moved at all and therefore the entire area in which the ellipse with be drawn within is being repainted so it won’t have the problem shown by Tom. Jules has already suggested something like this as a solution, but I see Toms point that dragging a component around like this should ideally be possible without having to invalidate an area much larger than it.


#12

Aah… You’re right… sorry…

This is dragging a Component around…

Rail


#13

Since this thread seems to be dragging on, perhaps if the OP can post a simple PIP that reproduces the problem, we could take a look?


#14

That’s on Mac it’s a Windows only issue.


#15

So here is a PIP that did reproduce the problem.

ComponentDragTest.h (1.9 KB)

Having trouble reproducing it myself now though, even in a plug-in.

You were probably right about it being related to the message queue Jules, and power-cycling the PC must have fixed it.


#16

Well, if something as simple as that code was going wrong, then TBH it does sound like the OS was getting its knickers in a twist over the order in which it was repainting dirty regions. Obviously we have no idea what goes on inside the win32 repaint system or why it might behave differently at different times, but if it seems to have been a temporary state, then not really much I can suggest we could do about it!