Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8211715

JCheckBox HiDPI scaling fails when main display is external non-HiDPI monitor

    XMLWordPrintable

Details

    Description

      ADDITIONAL SYSTEM INFORMATION :
      Bug confirmed to be present on Java 9.0.7.1, 10.0.2, 11ea, and 12ea on Windows 10. Executed the attached sample app from the command line via javaw.exe (which has its EXE manifest correctly marked as per-monitor DPI-aware).

      A DESCRIPTION OF THE PROBLEM :
      When a HiDPI screen and a non-HiDPI screen is used together in a multi-monitor setup, and the non-HiDPI monitor is marked as the main display, certain Swing components fail to scale properly as a JFrame is dragged from one monitor to the other. This includes the checkbox in JCheckBox, radio button circles in JRadioButton, combobox dropdown triangles, and a few other pieces of Windows L&F graphics.

      If the external non-HiDPI monitor was configured as main monitor when the user signed in to Windows 10, the bug will persist even if the user subsequently changes the main monitor, until the user signs out and in again. Depending on the original main monitor setting at sign-in, checkbox icons may appear either too large or too small on either monitor.

      The only way to completely avoid the bug is to set the HiDPI display to the main monitor and sign out and in of Windows. No combination of settings that has the external non-HiDPI screen as the main monitor seems to work.

      See the annotated screenshots at https://people.csail.mit.edu/ebakke/WindowsSwingMultiMonitorTest.png

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1) Find a Windows 10 laptop with a HiDPI screen. (I used a Lenovo Thinkpad X1 Carbon 6th gen with a 2560x1440 screen.)
      2) Attach a regular non-HiDPI external monitor.
      3) In the Windows 10 "Change display settings" app, make sure the HiDPI laptop screen is set to 200% scaling (should be the default), and set the external non-HiDPI monitor (which should be at scaling 100%) to be the "main display".
      4) Log in and out of Windows.
      5) Run the attached Java application. Drag the window to each screen and observe the size of the JCheckBox and JRadioButton. The checkbox and radio button will be tiny on the HiDPI screen.
      6) Optional: Now try to change Windows display settings to make the HiDPI monitor the main display, but run the attached Java app without logging out and in first. The checkbox and radio button is now huge on the external non-HiDPI screen.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      When the window is dragged from one monitor to the other, the checkbox and radio button should remain the same size relative to its text label.
      ACTUAL -
      The checkbox becomes either tiny (half size) or huge (double size) relative to the text, on one or both of the monitors, depending on the configuration.

      ---------- BEGIN SOURCE ----------
      import java.awt.Toolkit;
      import javax.swing.*;

      /**
       * Simple example of a Swing app that does not work correctly in a multi-monitor setup on Windows 10
       * involving one HiDPI screen (e.g. a Thinkpad Lenovo X1 carbon laptop with a 2560x1440 screen) and
       * a regular non-HiDPI external monitor (tested in this case with a 1920x1080 external monitor).
       *
       * <p>Tested on Java 10.0.2 and Java 11ea.
       */
      public final class WindowsSwingMultiMonitorHiDPITest extends javax.swing.JFrame {
        /* Note: Toolkit.getDefaultToolkit() is an unreliable way to get HiDPI status, because different
                 monitors may have different HiDPI scaling factors, and because getDefaultToolkit's value
                 updates only when the user logs out and in again, not when the user changes the scaling
                 setting in the Windows 10 control panel. Nevertheless, include the value here for
                 debugging, in case its state is correlated with the bug. */
        private JCheckBox checkBox = new JCheckBox(System.getProperty("java.version") +
            " with toolkitDPI " + Toolkit.getDefaultToolkit().getScreenResolution());
        private JRadioButton radioButton = new JRadioButton("I am a radio button");

        public WindowsSwingMultiMonitorHiDPITest() {
          setTitle("Windows Swing HiDPI Test");
          setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
          getContentPane().add(checkBox, java.awt.BorderLayout.NORTH);
          getContentPane().add(radioButton, java.awt.BorderLayout.SOUTH);
          setSize(500, 200);
        }

        public static void main(String args[]) throws Exception {
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
          SwingUtilities.invokeLater(() -> {
            new WindowsSwingMultiMonitorHiDPITest().setVisible(true);
          });
        }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Make the HiDPI monitor the primary screen, and restart Windows. (End-users are unlikely to discover this on their own, though.)

      FREQUENCY : always


      Attachments

        Issue Links

          Activity

            People

              rmahajan Rajat Mahajan
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

                Created:
                Updated: