Opaque DragImageComponent is gooberlicious

An opaque DragImageComponent (one that returns true for IsOpaque()) does not redraw correctly. As it is dragged over a non-Juce window, if the non-Juce window paints the painting happens over the heavyweight peer for the DragImageComponent, and the DragImageComponent is not refreshed (it picks up goobers).

The problem is caused by the ComponentPeer::windowIgnoresMouseClicks (line 444 of juce_DragAndDropContainer.cpp) setting the WS_EX_TRANSPARENT style on the window during addToDesktop().

Removing the windowIgnoresMouseClicks flag from the frame eliminates the goobers however I don’t think this is a proper fix.

To demonstrate the problem reliably, adjust the demo application so that it produces a DragImageComponent that returns true for IsOpaque (this could happen on systems without layered window support or by commenting out the test for canUseSemiTransparentWindow in juce_DragAndDropContainer.cpp). Run the Task Manager and select the processor usage display graph. Run the juce Demo application, go to the Drag and Drop demo page, drag a row from the list and move it over the area of the Task Manager that is refreshing (the graph). Result: the drag image will pick up goobers.

Hmm. Did you make sure that this opaque component is definitely filling itself with a solid background colour? If you take a semi-transparent component and just call setOpaque(true) on it, it’ll obviously be goobertastic because it won’t be clearing its background.

It is, because the first thing that DragImageComponent does when it is not opaque, is to fill all with solid white:

    void DragImageComponent::paint (Graphics& g)
    {
        if (isOpaque())
            g.fillAll (Colours::white);
// ......

This is something having to do with WS_EX_TRANSPARENT, because Windows normally clips drawing of windows otherneath other windows. But in this case, an HWND under the DragImageComponent HWND somehow is able to draw over. This behavior is tied to WS_EX_TRANSPARENT, and I have even been able to reproduce the behavior outside of Juce.

Other developers have encountered problems with WS_EX_TRANSPARENT:

[quote]http://stackoverflow.com/questions/459084/possible-to-have-a-window-where-mouse-clicks-go-through-it
On Microsoft Windows, is it possible to have a click through window (where all mouse clicks will go straight through), similar to WS_EX_TRANSPARENT, without the redrawing issues that WS_EX_TRANSPARENT has?[/quote]

This sounds exactly what Juce is trying to accomplish with WS_EX_TRANSPARENT when the style flags includes windowIgnoresMouseClicks.

They suggest “…handle WM_NCHITTEST and return HTTRANSPARENT”. Come to think of it, that might fix the other problem where the window finds itself… I will try it.

IT WORKED!

Both of the problems with an opaque DragImageComponent are fixed with the following changes:

juce_win32_Windowing.cpp
createWindow(): remove the following lines:

if ((styleFlags & windowIgnoresMouseClicks) != 0) exstyle |= WS_EX_TRANSPARENT;

juce_win32_Windowing.cpp
peerWindowProc(): change WM_NCHITTEST:

[code]case WM_NCHITTEST:
if ((styleFlags & windowIgnoresMouseClicks) != 0)
return HTTRANSPARENT;
else if (hasTitleBar())
break;

return HTCLIENT;

[/code]

I’m not sure what this will break, I will have to defer final judgement to the Master.

According to MSDN, HTTRANSPARENT only works if the parent window is on the same thread… seems a bit ropey to me!

I didn’t see that about the thread thing but in this case it seems that behavior is in line with what we are trying to do. On the other hand, google reveals that programmers have nothing but headaches with WS_EX_TRANSPARENT, because it causes exactly the redraw problems I described.

Well, in this particular case it might work, but if the window won’t let clicks pass through to other apps or the desktop, then it’s not much use as a general solution.

If you were using a semi-transparent window, it’d probably avoid the repaint problem, because win32 draws them differently. Is there any reason why you can’t just make your component non-opaque?

Upon more review it seems that having windows from different threads in the same application does indeed cause problems:

http://www.winehq.org/pipermail/wine-devel/2001-March/000398.html

http://www.virtualdub.org/blog/pivot/entry.php?id=147

At my company they are only using one thread for the Ui (the main thread) so this is a non issue.