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

Tall JMenus may be positioned incorrectly with vertically arranged monitors

XMLWordPrintable

      A DESCRIPTION OF THE PROBLEM :
      Under certain conditions tall JMenus will appear at a location that leaves empty space between the triggering component and the menu.

      JMenu.getPopupMenuOrigin performs some tests (e.g. lines 441 to 445) to determine if the popup fits on the screen. If the popup is repositioned (e.g. line 447) the code does not seem to take into account the fact that a screen boundary may be crossed. Later, when the JPopupMenu is displayed, it is repositioned again to fit within whatever screen the y coordinate happens to place it on, which may take it away from the widget which triggered its appearance.

      This can happen with JMenus in JMenuBars and submenus inside of JPopupMenus. Tall JPopupMenus seem ok.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Seeing this behaviour requires two monitors positioned one on top of the other, and a UI that will
      * create a menu taller than the lower monitor's height
      * attempt to initially place that menu at a sufficiently large y coordinate (somewhere low on the lower monitor) to trigger repositioning.

      Note that the given SSCCE may need to be tweaked if the test environment differs substantially from the assumed one (e.g. screens > 1080).

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The popup should appear next to the component that triggers its appearance.
      ACTUAL -
      The popup appears at the top of the top monitor, which may leave a considerable gap between the menu and the triggering component.

      ---------- BEGIN SOURCE ----------
      package sscce;

      import java.awt.Dimension;
      import java.awt.event.MouseAdapter;
      import java.awt.event.MouseEvent;
      import java.lang.reflect.InvocationTargetException;

      import javax.swing.JFrame;
      import javax.swing.JMenu;
      import javax.swing.JMenuBar;
      import javax.swing.JPopupMenu;
      import javax.swing.SwingUtilities;

      public class PopupBug {

          // CONFIG PROPERTIES - set as appropriate
          private static final int TOP_MONITOR_HEIGHT = 1080;
          private static final int BOTTOM_MONITOR_HEIGHT = 1080;
          // Needs to produce a menu with a height between BOTTOM_MONITOR_HEIGHT and WINDOW_Y
          private static final int TOO_MANY_ITEMS_FOR_BOTTOM_MONITOR = 80;

          private static final int WINDOW_SIZE = 200;
          private static final int WINDOW_Y = TOP_MONITOR_HEIGHT + BOTTOM_MONITOR_HEIGHT - WINDOW_SIZE;

          public static void main(String[] args) {
              try {
                  SwingUtilities.invokeAndWait(PopupBug::launch);
              } catch (InvocationTargetException | InterruptedException e) {
                  e.printStackTrace();
              }
          }

          private static void launch() {
              JFrame window = new JFrame();

              // The large y-coordinate triggers the repositioning of the menus
              window.setLocation(0, WINDOW_Y);
              window.setSize(new Dimension(WINDOW_SIZE, WINDOW_SIZE));

              // Example one: bug triggered from a sub menu
              window.addMouseListener(new MouseAdapter() {
                  @Override
                  public void mouseClicked(MouseEvent e) {
                      JPopupMenu menu = new JPopupMenu();
                      menu.add("A popup");
                      menu.addSeparator();
                      menu.add(buildTallMenu());
                      menu.show(window, WINDOW_SIZE/2, WINDOW_SIZE/2);
                  }
              });

              // Example two: bug triggered from a menu bar
              JMenuBar menuBar = new JMenuBar();
              menuBar.add(buildTallMenu());
              window.setJMenuBar(menuBar);

              window.setVisible(true);
          }

          private static JMenu buildTallMenu() {
              // This menu will be repositioned by JMenu so that it's bottom doesn't fall of the screen.
              // This leaves the y-coordinate on the top screen.
              // JPopupMenu will then reposition it because the menu crosses the bottom of the top monitor.
              JMenu menu = new JMenu("Show a tall menu");

              for (int i = 1; i <= TOO_MANY_ITEMS_FOR_BOTTOM_MONITOR; ++i)
                  menu.add("Item " + i);

              return menu;
          }

      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      It's possible (at least in some cases) to manually position the menu rather than relying on automatic positioning.

      FREQUENCY : always


            psadhukhan Prasanta Sadhukhan
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: