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

HiDPI - Modal dialog opened in another screen is giving wrong size/position

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Java 11.
      This was not previously observed under Java 8, but HiDPI support there is spotty.

      A DESCRIPTION OF THE PROBLEM :
      When you have two monitors with differing UI Scales, and you have a window open on one monitor, when opening a modal dialog against that window, but to a different monitor that uses a different UI scale, causes the dialog to open with the incorrect size and position.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      - Be on a Windows computer with 2+ monitors
      - Using the Display Control Panel, ensure at least one monitor has a 'Scale' that does not match your other monitors.
      - Open a dialog to one screen. Open a second dialog, modal to the first screen, but placed on the second screen.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The dialog should open at the requested position, with the requested size.
      ACTUAL -
      The dialog opens at a different position/size and with the wrong size.
      Scenarios seen include opening the dialog too large to fit on the screen, with the title bar off the top of the screen, making repositioning the dialog difficult.

      ---------- BEGIN SOURCE ----------
      package buggy.modaldialogs;

      import java.awt.BorderLayout;
      import java.awt.Component;
      import java.awt.Dimension;
      import java.awt.GraphicsDevice;
      import java.awt.GraphicsEnvironment;
      import java.awt.Rectangle;
      import java.awt.event.ComponentAdapter;
      import java.awt.event.ComponentEvent;

      import javax.swing.JDialog;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JPanel;

      public class ModalDialogSscce {

      public static void main(String[] args)
      {
      GraphicsDevice[] screens = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
      assert screens.length > 1 : "This SSCCE requires 2+ monitors";

      GraphicsDevice screenADevice = screens[0];
      double screenAScale = getPseudoUIScale(screenADevice);
      GraphicsDevice screenBDevice = null;
      for(int i = 1; i < screens.length; i++)
      {
      // Find a second screen using a different UI scale.
      GraphicsDevice candidateDevice = screens[i];
      double candidateScale = getPseudoUIScale(candidateDevice);
      if(Math.abs(candidateScale - screenAScale) > 0.1)
      {
      screenBDevice = candidateDevice;
      break;
      }
      }
      if(screenBDevice == null)
      {
      System.err.println("Devices with different UI scales not found\n"
      +" Proceeding with the generic 2 monitor case - which should pass");
      screenBDevice = screens[1];
      }

      JFrame dummyFrameA = new JFrame("Parent Window");
      dummyFrameA.add(new JLabel("This is the parent window"));
      dummyFrameA.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      dummyFrameA.setSize(400,400);
      centerOnScreen(dummyFrameA, screenADevice);
      dummyFrameA.setVisible(true);

      JDialog dummyFrameB = new JDialog(dummyFrameA, "Child Dialog");
      dummyFrameB.setSize(400,400);
      JLabel expectedLabel = new JLabel("Expected Bounds: ");
      final JLabel actualLabel = new JLabel("Actual Bounds: ");
      dummyFrameB.add(expectedLabel, BorderLayout.NORTH);
      dummyFrameB.add(new JLabel("UI Scales: " + getPseudoUIScale(screenADevice) + " <=> " + getPseudoUIScale(screenBDevice)), BorderLayout.CENTER);
      dummyFrameB.add(actualLabel, BorderLayout.SOUTH);
      dummyFrameB.addComponentListener(new ComponentAdapter() {
      @Override
      public void componentResized(ComponentEvent e) {
      actualLabel.setText("<html>Actual Bounds:<br/>" + getBoundsString(dummyFrameB)+"</html>");
      }
      public void componentMoved(ComponentEvent e) {
      actualLabel.setText("<html>Actual Bounds:<br/>" + getBoundsString(dummyFrameB)+"</html>");
      }
      });
      centerOnScreen(dummyFrameB, screenBDevice);
      expectedLabel.setText("<html>Expected Bounds:<br/>" + getBoundsString(dummyFrameB)+"</html>");
      dummyFrameB.setVisible(true);
      }

      private static String getBoundsString(Component comp)
      {

      return ("At("+comp.getX() + "x" + comp.getY() + ") Size(" + comp.getWidth() + "x"+comp.getHeight()+")");
      }

      private static void centerOnScreen(Component window, GraphicsDevice screen)
      {
      Rectangle screenBounds = screen.getDefaultConfiguration().getBounds();
      Rectangle windowBounds = window.getBounds();
      window.setLocation((int)(screenBounds.getCenterX()-windowBounds.getWidth()/2), (int)(screenBounds.getCenterY()-windowBounds.getHeight()/2));
      }

      /** This does NOT return a true UI scale as from the Control Panel.
      * This does however let us determine if two monitors have
      * a different scale from there (assumptions and exceptions apply).
      */
      private static double getPseudoUIScale(GraphicsDevice screen)
      {
      int physicalWidth = screen.getDisplayMode().getWidth();
      double logicalWidth = screen.getDefaultConfiguration().getBounds().getWidth();
      return logicalWidth/physicalWidth;
      }
      }

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

      CUSTOMER SUBMITTED WORKAROUND :
      A) Run all monitors with the same UI scale.
      B) Run with -Dsun.java2d.uiScale.enabled=false
      C) Don't use a modal dialog in this scenario.
      D) Don't open modal dialogs to other screens. (In our defense, we save the previous dialog location and reopen the dialog there. The user has placed the Dialog on another monitor for convenience, and we are trying to faithfully reopen the dialog there)


      FREQUENCY : always


            serb Sergey Bylokhov
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: