AffineTransform breaks CentreWithSize and PopupMenus


#1

In our plug-ins, we are scaling our main interface component using AffineTransform::scale. We are experiencing major issues with certain child components.

First, we are having issues with PopupMenu components losing track of proper boundaries. We have an Options Menu that appears as a popup whenever a user clicks on our Options “Gear” button. Here is the Option Menu at 100% interface scaling:

At 150%, if the popup is not assigned a ParentComponent, it appears as a window with 100% scaling that goes off the side of the plugin interface.

We want the popup to scale properly with the rest of our plugin. We also want it to stay inside of the plugin window instead of spawning its own window. So, we are setting ParentComponent to our PluginEditor. Here’s what happens:

Side note, if I try to call AffineTransform on the Popup before it’s made visible, I get this:

    optionMenu->setTransform(AffineTransform::scale(ourEditor->getInterfaceScale()));
	optionsPopup.showMenuAsync(PopupMenu::Options().withTargetComponent(optionsButton), nullptr);

Essentially, the spawned child comp thinks that the parent’s screen size is nearly twice as wide and tall as it actually is. I noticed this exact same behavior when trying to spawn a DialogWindow and saying “centreWithSize”. When the interface scale is anything larger than 1.0x, the DialogWindow trends toward finding the bottom right corner of the editor as the center point. We had to write a hacky block of code to get the DialogWindow to center correctly at all interface scales.

        const int editorWidth = getParentComponent()->getWidth();
		const int editorHeight = getParentComponent()->getHeight();
		const int creditWidth = creditsWindow->getWidth();
		const int creditHeight = creditsWindow->getHeight();

		float scaleFactor = 1.0f;
		auto editor = dynamic_cast<UAPluginEditor*>(getParentComponent());
		if (editor) scaleFactor = editor->getInterfaceScale();

		scaleFactor *= scaleFactor;

		const float widthDiff = (editorWidth - creditWidth) / 2.0f;
		const float heightDiff = (editorHeight - creditHeight) / 2.0f;

		creditsWindow->setTopLeftPosition(widthDiff / scaleFactor, heightDiff / scaleFactor);

My thought is that when AffineTransform is called on a component, the component should keep track of its current scale. Then, that scale is used when calculating center positions on DialogWindows, PopupMenus, etc.


UI scaling on Windows 4k
#2

Any ideas for temporary workarounds?


#3

Sorry for the late reply but this issue should now be fixed on the develop branch. Could you try out the changes and let me know if it works?

Ed


#4

Hi Ed, thanks for looking into this! Unfortunately, the options menu is still spawning mostly off-screen (see the third image) instead of aligning properly like in the first image.

Furthermore, it appears that a new bug was introduced. Our UASlider class has a paramLabel member that is centered whenever our sliders are resized. For our sliders with a constant size, the behavior hasn’t been affected. However, we have one slider that gets resized based on the current mode. That slider now looks like this:

This slider shrinks to “Left” delay time in stereo mode:

As you can see, the statically sized slider renders correctly. The variably sized slider does not.

So, our editor is receiving the AffineTransform. the child sliders resize and reposition fine, but the sliders’ children do not reposition correctly if the slider is resized.

If it helps, our slider’s resized() looks like this:

void UALabeledSlider::resized()
{
    UASlider::resized();
    paramLabel->setSize(getWidth() - getWidth()/10.0, labelHeight);
    paramLabel->setCentrePosition(getWidth()/2.0, getHeight()/2.0);
}

#5

OK, it seems like my changes broke centring for child components so I’ve reverted them on develop. I’ll look into a proper fix for this.

Ed


#6

Bumping this. JUCE 5.2 seems to scale popups better, but when using HiDPI mode in Ableton 10, the popups are still opening in very different locations when the plugin is scaled.


#7

I’ll add this to our backlog but it could be a while until we are able to look at it I’m afraid.


#8

I’m also experienced wrong popup positions, for example, if a ComboBox is in a scaled component, the resulting popup is on the wrong position (with a multi-monitor setup, maybe unrelated)


#9

Here is an extreme example, which makes the bug clear


#10

This commit should fix the PopupMenu bug


#11

I’ve also just pushed this commit that should take a Component's AffineTransform into account when using the centreWithSize(), setCentreRelative() and setCentrePosition() methods. Can you see if it works for you @trickyflemming?


#12

Thanks! It works somewhat better, but it’s still showing up with an offset in HiDPI hosts if Windows scaling is greater than 100%. Specifically, I’m using Ableton 10 Beta with HiDPI enabled to test this.

EDIT: By “it’s still showing up” I mean that the PopupMenus are still largely and noticeably offset from the ComboBox or originating Component. Additionally, the PopupMenus are scaling larger than they should be, as though they are ignoring the Windows display scale.


#13

@ed95, disregard my previous comment! I found the issue. We had overridden getPopupMenuFont() with our old scaling system. Removing that override fixed the offset at HiDPI with the latest dev commit. Thank you!


#14

Oh no, @ed95, I spoke too soon >_<

I’m still having issues at non-100% HiDPI scales.

I was sent the following code block by another developer who says that this fixes the offset issue for him:

Font getPopupMenuFont() override
{
		const Desktop::Displays::Display& dis = Desktop::getInstance().getDisplays().getMainDisplay();
		float scale;
#if JUCE_WINDOWS
		scale = dis.dpi / 96.f;
#else
		scale = 1.f;
#endif
		Font myFont;
		myFont.setSizeAndStyle((float)(16 * _scale),0,1.0f,0.0f);
		return myFont;
}

#15

So it’s just the font that isn’t being scaled correctly? Is the actual PopupMenu window in the correct place now?


#16

No, the window is appearing offset down and to the right. Here’s an image of our plugin at base scaling, but with Windows scaling at 125%. That “Linked/Offset/Independent” PopupMenu is supposed to be connected to the “Linked” ComboBox above it.

The problem worsens with larger Windows scaling factors. If you have Ableton 10 Beta, you need to enable “HiDPI Mode” in the settings.


#17

I don’t have access to the Live 10 beta to test this right now but I’ve tested it in a couple of DAWs with hiDPI like Studio One and Tracktion Waveform and can’t reproduce it. Just to double check, you haven’t overridden PopupMenu's shouldPopupMenuScaleWithTargetComponent() method to return false? This was the only way I could reproduce behaviour similar to what you are showing


#18

No, we haven’t overridden that.

If it helps, our LookAndFeel inherits from V3. We are overriding getParentComponentForMenuOptions. If we let it return

if (options.getParentComponent() == nullptr && options.getTargetComponent() != nullptr)
{
   return options.getTargetComponent()->getParentComponent(); 
}

instead of

return LookAndFeel_V2::getParentComponentForMenuOptions(options);

it fixes the offsets for regular ComboBox PopupMenus, but then breaks the scaling on our custom Popup Components.


#19

Example code for how we’re showing our Popup Components:

optionsPopup.showMenuAsync(PopupMenu::Options().withTargetComponent(optionsButton), nullptr);

#20

OK, I still don’t have access to the Live 10 beta to test this out but I’ve been looking at how JUCE plugins behave in other hosts on Windows when the scale factor of a display is changed.

The issue that I observed is that the JUCE was not updating the monitor display bounds following a scale factor change resulting in a mismatch between these bounds and the screen bounds of windows reported by the Windows API. This meant that following a scale factor change, components were being constrained to incorrect monitor bounds and either showing up in the wrong position or on the wrong monitor (if multiple monitors were connected).

In standalone applications we receive a WM_SETTINGCHANGE message when the scale factor is changed and refresh the display list to get the updated monitor bounds, however because plugins are in a child window they do not receive this message resulting in the behaviour detailed above. This commit ensures that plugins are notified of these changes and use the correct display bounds for positioning components, which should fix your PopupMenu issue. However if it doesn’t I’m afraid the bug will have to wait until I can perform some tests on Live 10.