ComponentPeer resize bug after desktop DPI change

I have an app that makes use of several JUCE components that are added to the desktop as native windows (as children of the main window). After changing the Windows display scale back and forth while the app is running, it’s possible for the JUCE desktop windows to end up in a buggy state where their position is correct but their size is incorrect.

The easiest way to reproduce this is to start at a higher scale like 125%, drop to 100%, and go back up to 125%. This reliably breaks the window size (they stay the same physical size that they were at with 100% scaling even after the rest of the desktop is back to 125% scaling).

The bug has two moving parts:

  1. WM_DPICHANGED is invoked only for top-level windows, not child windows. So the child windows are not resized as a result of the DPI change in handleDPIChanging().
  2. In HWNDComponentPeer::setBounds(), the hasResized flag compares oldBounds with bounds, but I think it should instead be comparing it with newBounds, which is the DPI-scaled version of the bounds.

What’s happening is that when my app calls setBounds() on the child HWND after the DPI change, the logical size has not changed, and thus hasResized=false, resulting in SWP_NOSIZE and therefore SetWindowPos() does not update the size of the window.

Changing HWNDComponentPeer::setBounds() to compare oldBounds with newBounds solves the problem completely. But also… is it even really necessary to mess with SWP_NOMOVE and SWP_NOSIZE? It seems like it would be simpler and more robust to just unconditionally set the position and size and not even worry about hasMoved/hasResized.

I definitely still think it would be cool to fix this JUCE bug.

I believe this is intentional - JUCE used to attempt to resize child hwnds based on scale, but this really requires support by the child hwnd.

It’s more robust and flexible to manually propagate scale changes from the top-level window down to child hwnds. This is what JUCE itself does for hosted plugin editors, so perhaps you could have a look at the hosting code to see how scale factor changes are negotiated between the host and the plugin.

Have you tested this with JUCE 8.0.13? We made some pretty extensive changes to the way that sizing works on Windows, and as part of those changes the hasResized check in the Windows component peer is computed using physical coordinates in all cases.

Note that there’s a similar set of checks in Component::setBounds() that avoid updating the component peer bounds if the logical size and position of the component is unchanged. Therefore, you may need to call updateBounds() on the component peer manually after a scale factor change.

Thanks for your reply, @reuk! been a bit underwater but I will give this a test run in the next few days and let you know how it goes.