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

Inconsistent setExtendedState bounds in dual monitor setup

    XMLWordPrintable

    Details

    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      windows_10

      Description

      ADDITIONAL SYSTEM INFORMATION :
      Dual monitor setup
      Windows 10
      jre1.8.0_281

      A DESCRIPTION OF THE PROBLEM :
      A Simple undecorated JFrame does not fit into second monitor in some conditions which will be explained in detail below.
      To fit frame in to the screen, I simply calculate "maximized bound" using monitors' bounds minus insets values.

      setMaximizedBounds(getMaximizedBounds()); // calculate bounds
      setExtendedState(MAXIMIZED_BOTH);
      ......
      maxBounds = new Rectangle(screenInsets.left, screenInsets.top,
      bounds.width - screenInsets.right - screenInsets.left,
      bounds.height - screenInsets.bottom - screenInsets.top);



      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      =============================
      TEST1:
      Primary screen: 1920x1080 %100 scailing, rendered bound: 1920x1080
      Second screen: 1280x1024 %100 scailing, rendered bound: 1280x1024
      NO PROBLEM, JFrame fits in both screens. getBounds() returns 1920x1040 for the first monitor and 1280x984 for the second monitor
      =============================
      TEST2:
      Primary screen: 1920x1080 %150 scailing, rendered bound: 1280x720
      Second screen: 1280x1024 %100 scailing, rendered bound: 1280x1024
      PROBLEM, JFrame does not fit into second monitor. getBounds() returns same bound value 1280x680 for both monitor .
      =============================
      TEST3:
      Primary screen: 1280x1024 %100 scailing, rendered bound: 1280x1024
      Second screen: 1920x1080 %100 scailing, rendered bound: 1920x1080
      PROBLEM, JFrame does not fit into second monitor. getBounds() returns same bound value 1280x984 for both monitor .
      =============================
      TEST4:
      Primary screen: 1280x1024 %100 scailing, rendered bound: 1280x1024
      Second screen: 1920x1080 %150 scailing, rendered bound: 1280x720
      NO PROBLEM, JFrame fits in both screens. getBounds() returns 1280x984 for the first monitor and 1280x680 for the second monitor
      =============================
      TEST5:
      Primary screen: 1920x1080 %150 scailing, rendered bound: 1280x720
      Second screen: 1920x1080 %100 scailing, rendered bound: 1920x1080
      PROBLEM, JFrame does not fit into second monitor. getBounds() returns same bound value 1280x680 for both monitor .
      =============================
      TEST6:
      Primary screen: 1920x1080 %125 scailing, rendered bound: 1920x1080
      Second screen: 1920x1080 %100 scailing, rendered bound: 2400x1350
      PROBLEM, JFrame does not fit into second monitor. getBounds() returns same bound value 1920x1030 for both monitor .


      In all scenarios, the application has been restarted after monitor resolution change in order to avoid any auto-scailing issues.



      ACTUAL -
      Inconsistent monitor bounds.


      ---------- BEGIN SOURCE ----------
      import java.awt.Color;
      import java.awt.EventQueue;
      import java.awt.GraphicsDevice;
      import java.awt.GraphicsEnvironment;
      import java.awt.Insets;
      import java.awt.Point;
      import java.awt.Rectangle;
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import java.awt.event.MouseAdapter;
      import java.awt.event.MouseEvent;

      import javax.swing.JButton;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.border.EmptyBorder;

      public class FrameTest extends JFrame {

      private JPanel contentPane;

      /**
      * Launch the application.
      */
      public static void main(String[] args) {
      EventQueue.invokeLater(new Runnable() {
      public void run() {
      try {
      FrameTest frame = new FrameTest();
      frame.setVisible(true);
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      });
      }

      /**
      * Create the frame.
      */
      public FrameTest() {
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setBounds(100, 100, 800, 600);
      contentPane = new JPanel();
      contentPane.setBackground(Color.YELLOW);
      contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
      setContentPane(contentPane);
      contentPane.setLayout(null);

      FrameDragListener frameDragListener = new FrameDragListener(this);
      addMouseListener(frameDragListener);
      addMouseMotionListener(frameDragListener);

      JButton btnNewButton = new JButton("Toggle");
      btnNewButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent arg0) {
      if (getExtendedState() == MAXIMIZED_BOTH)
      setExtendedState(NORMAL);
      else {
      setMaximizedBounds(getMaximizedBounds());
      setExtendedState(MAXIMIZED_BOTH);
      System.out.println("rendered bounds => " + getBounds());
      }
      }
      });
      btnNewButton.setBounds(21, 21, 74, 34);
      contentPane.add(btnNewButton);

      JButton btnNewButton_1 = new JButton("X");
      btnNewButton_1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
      dispose();
      }
      });
      btnNewButton_1.setBounds(104, 21, 43, 34);
      contentPane.add(btnNewButton_1);
      setUndecorated(true);
      }

      public static class FrameDragListener extends MouseAdapter {

      private final JFrame frame;
      private Point mouseDownCompCoords = null;

      public FrameDragListener(JFrame frame) {
      this.frame = frame;
      }

      public void mouseReleased(MouseEvent e) {
      mouseDownCompCoords = null;
      }

      public void mousePressed(MouseEvent e) {
      mouseDownCompCoords = e.getPoint();
      }

      public void mouseDragged(MouseEvent e) {
      Point currCoords = e.getLocationOnScreen();
      frame.setLocation(currCoords.x - mouseDownCompCoords.x, currCoords.y - mouseDownCompCoords.y);
      }
      }

      public Rectangle getMaximizedBounds() {

      GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsDevice[] gs = ge.getScreenDevices();
      Rectangle bounds = this.getGraphicsConfiguration().getBounds();
      Rectangle maxBounds = null;
      Insets screenInsets = this.getToolkit().getScreenInsets(this.getGraphicsConfiguration());

      if (gs[0] == getGraphicsConfiguration().getDevice()) { // main monitor
      maxBounds = new Rectangle(screenInsets.left, screenInsets.top,
      bounds.width - screenInsets.right - screenInsets.left,
      bounds.height - screenInsets.bottom - screenInsets.top);
      System.out.println(getGraphicsConfiguration().getDevice().getIDstring() + " bounds " + bounds.width + "x"
      + bounds.height + " ScreenInsets: " + screenInsets);
      } else if (gs[0] != getGraphicsConfiguration().getDevice()) { // other monitors
      maxBounds = new Rectangle(screenInsets.left, screenInsets.top,
      bounds.width - screenInsets.right - screenInsets.left,
      bounds.height - screenInsets.bottom - screenInsets.top);
      System.out.println(getGraphicsConfiguration().getDevice().getIDstring() + " bounds " + bounds.width + "x"
      + bounds.height + " ScreenInsets: " + screenInsets);
      }

      else {
      maxBounds = null;
      }
      return maxBounds;
      }
      }

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

      CUSTOMER SUBMITTED WORKAROUND :
      I overrided setExtendedState to solve scaling problems except scenario in TEST6. If you notice, scailing in primary monitor affects secondary monitor.

      @Override
      public void setExtendedState(int state) {
      if (state == MAXIMIZED_BOTH) {
      // calculate setMaximizedBounds
      GraphicsConfiguration graphicsConfiguration = getGraphicsConfiguration();
      Rectangle screenBounds = graphicsConfiguration.getBounds();
      Insets screenInsets = this.getToolkit().getScreenInsets(this.getGraphicsConfiguration());

      Rectangle maxBounds = new Rectangle(screenInsets.left, screenInsets.top,
      screenBounds.width - screenInsets.left - screenInsets.right,
      screenBounds.height - screenInsets.top - screenInsets.bottom);

      setMaximizedBounds(maxBounds);
      super.setExtendedState(MAXIMIZED_BOTH);
      System.out.println("maxBounds => " + maxBounds);
      Rectangle bounds = getBounds();

      // since Java 9 we need calculate the scale factor because setMaximizedBounds
      // reduce the resulting bounds with the display scale factor
      if (bounds.width != maxBounds.width || bounds.height != maxBounds.height) {
      double factorX = (double) maxBounds.width / bounds.width;
      double factorY = (double) maxBounds.height / bounds.height;
      maxBounds = new Rectangle(screenInsets.left, screenInsets.top,
      (int) Math.round(screenBounds.width * factorX) - screenInsets.left - screenInsets.right,
      (int) Math.round(screenBounds.height * factorY) - screenInsets.top - screenInsets.bottom);
      setMaximizedBounds(maxBounds);

      super.setExtendedState(ICONIFIED); // state must change that the new MaximizedBounds is used from the
      // GUI
      SwingUtilities.invokeLater(() -> {
      super.setExtendedState(MAXIMIZED_BOTH);
      System.out.println("rendered bounds after scaling => " + getBounds());
      });
      }
      } else {
      super.setExtendedState(state);
      }
      }

      FREQUENCY : always


        Attachments

          Issue Links

            Activity

              People

              Assignee:
              skodandarama Suman Rajkumaar Kodandarama
              Reporter:
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Dates

                Created:
                Updated: