Controlling UI Scaling on Multiple Monitors with Different Scaling Factors in Swing/JavaFX

175 Views Asked by At

I am facing an issue with UI scaling in a NetBeans-Platform-based (Swing) and JavaFX application when using multiple monitors with different scaling factors. On one monitor, I have a scaling of 125%, and on the other, it's set to 150%.

I am using the -J-Dsun.java2d.uiScale Java option to scale the UI. Using the J-Dsun.java2d.uiScale option allows me to scale the Java-based GUI evenly and at once, regardless of the technology (Swing/JavaFX/AWT etc.).

However, this option doesn't seem to scale both monitors appropriately. I want to achieve a scenario where the uiScale scaling factor is multiplied by the individual monitor scaling factors. For example, if the uiScale is set to 2, I want the GUI on the monitor with a 125% scaling to be scaled by 1.25 * 2, and on the monitor with a 150% scaling, it should be scaled by 1.5 * 2.

Is there a way to achieve this behavior in Java? Are there additional Java options or configurations that need to be considered to make the UI scaling consistent across monitors with different scaling factors?

Current Approach: I am currently using the following Java option for UI scaling:

-J-Dsun.java2d.uiScale=2

However, this does not seem to take into account the individual scaling factors of each monitor.

Environment:

  • JDK 17
  • Windows 10

Any insights or suggestions on how to achieve consistent UI scaling across multiple monitors with different scaling factors would be greatly appreciated.

1

There are 1 best solutions below

0
jewelsea On

This is information about output scaling in pure JavaFX applications rather than a mixed Swing/JavaFX application.


For JavaFX, there is a helper API Screen::getOutputScaleY() (and same for X).

Gets the recommended output scale factor of this Screen in the vertical (Y) direction. This scale factor will be applied to the scene in order to compensate for the resolution and viewing distance of the output device. The visual bounds will be reported relative to this scale factor.

Perhaps that info might assist you with JavaFX work. For the java2d stuff, I don't know.

When the following code is executed in the start() method of a JavaFX application.

System.out.println(Screen.getScreens());

on my PC (MacBook Pro with internal screen at 2x scaling and connected widescreen external monitor at 1x scaling), I get this output:

[
 javafx.stage.Screen@c829b7c6 
   bounds:Rectangle2D [minX=0.0, minY=0.0, maxX=1792.0, maxY=1120.0, width=1792.0, height=1120.0] 
   visualBounds:Rectangle2D [minX=0.0, minY=25.0, maxX=1792.0, maxY=1120.0, width=1792.0, height=1095.0] 
   dpi:132.0 
   outputScale:(2.0,2.0), 
 javafx.stage.Screen@d3ba4740 
   bounds:Rectangle2D [minX=-3440.0, minY=0.0, maxX=0.0, maxY=1440.0, width=3440.0, height=1440.0] 
   visualBounds:Rectangle2D [minX=-3440.0, minY=25.0, maxX=0.0, maxY=1361.0, width=3440.0, height=1336.0] 
   dpi:109.0 
   outputScale:(1.0,1.0)
]

The first screen listed in the internal monitor and primary screen. The second screen listed is the external widescreen monitor and secondary screen situated to the left of the primary screen (hence the negative coordinates reported for the minX and minY values for the screen).

Internally, JavaFX knows about the different output scales for the different screens and adjusts the rendering according to outputScale. So, in effect, it "just works" without any additional settings or coding.

Notes on multiple screens on Macs with JavaFX applications

On a Mac, the default is to snap windows to screens, not share them across screens. So windows can't be split partially with some portion of a window displayed on one screen and another portion of the window on another. This can be overridden with an OS system setting (which I didn't try).

Assuming the default, it is simple for an application because the whole scene on a given screen will be scaled to the outputScale of the screen internally by JavaFX and it will always be correct. If the window is moved to an alternate screen, JavaFX will automatically adjust the scaling internally. There is a slight glitch I notice with this sometimes with JavaFX 21, where the app needs to receive focus to re-render itself at the correct output scale, but you may not notice this as generally the app window will be in focus when moved.

Also, for the default system settings, the system does not need to worry about a situation where part of the window is on one monitor and part on another where the scaling may be different between the different parts (as that can't happen without changing the default screen settings in the OS).