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

JMenuItem accelerator goes offscreen when BasicMenuItemUI.MAX_ACC_WIDTH is null

XMLWordPrintable

    • beta
    • generic
    • generic



      Name: boT120536 Date: 01/04/2001


      java version "1.3.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3)
      Java HotSpot(TM) Client VM (build 1.3.0, mixed mode)

      If BasicMenuItemUI.MAX_ACC_WIDTH is null, say when there's only one JMenuItem, the accelerator is drawn to the right of BasicMenuItemUI's acceleratorRect, which puts it mostly outside the Menu's container in left-to-right mode.

      The problem is that BasicMenuItemUI.MAX_ACC_WIDTH is being set to zero when it's null, when it really ought to be the current item's acceleratorRect.width, since the max width has to be at least as wide as the current width.

      I looked at the 1.4 sources, the bug is in there too.

      This test app duplicates BasicMenuItemUI's layout code and draws colored rects to show where the various parts ought to go:



      import javax.swing.*;
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.border.*;
      import javax.swing.text.View;

      public class Test implements SwingConstants {
              static boolean sLeftToRight = false;
              public static void main(String[] args) {
                      try {
                              String laf = UIManager.getCrossPlatformLookAndFeelClassName();
                              UIManager.setLookAndFeel(laf);
                      } catch (Exception e) {
                              System.out.println ("Error setting Metal look and feel.");
                      }
                      new Test();
          }

              public Test() {
                      JFrame fr = new JFrame("Test");
                      fr.getContentPane().add(testComponent());
                      fr.setSize(new Dimension(320, 200));
                      fr.show();
                      paintRects();
              }
              JComponent testComponent() {
                      JPanel jp = new JPanel();
                      jp.setLayout(new GridLayout(3,1));
                      mi = new JMenuItem("Control");
                      mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, ActionEvent.CTRL_MASK));
                      if (!sLeftToRight)
                              mi.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
                      jp.add(mi);
                      JPanel jp2 = new JPanel(new FlowLayout());
                      JButton b = new JButton("Repaint");
                      b.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent e) {
                                      paintMenuItem(mi);
                                      paintRects();
                              }
                      });
                      jp2.add(b);
                              
                      b = new JButton(sLeftToRight ? "Left to right" : "Right to left");
                      b.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent e) {
                                      sLeftToRight = !sLeftToRight;
                                      ((JButton)e.getSource()).setLabel(sLeftToRight ? "Left to right" : "Right to left");
                                      mi.setComponentOrientation(sLeftToRight ? ComponentOrientation.LEFT_TO_RIGHT : ComponentOrientation.RIGHT_TO_LEFT);
                                      mi.repaint();
                                      paintRects();
                              }
                      });
                      jp2.add(b);
                      jp.add(jp2);
                      
                      jp2 = new JPanel(new FlowLayout());
                      JLabel label = new JLabel("viewRect");
                      label.setForeground(Color.orange);
                      jp2.add(label);
                      label = new JLabel("iconRect");
                      label.setForeground(Color.red);
                      jp2.add(label);
                      label = new JLabel("acceleratorRect");
                      label.setForeground(Color.green);
                      jp2.add(label);
                      label = new JLabel("checkIconRect");
                      label.setForeground(Color.blue);
                      jp2.add(label);
                      label = new JLabel("arrowIconRect");
                      label.setForeground(Color.yellow);
                      jp2.add(label);
                      label = new JLabel("textRect");
                      label.setForeground(Color.magenta);
                      jp2.add(label);
                      jp.add(jp2);
                      return jp;
              }
              JMenuItem mi;
              
              
              private boolean useCheckAndArrow(){
                      return true;
              }
              private boolean isLeftToRight(){
                      return sLeftToRight;
              }
          static Rectangle zeroRect = new Rectangle(0,0,0,0);
          static Rectangle iconRect = new Rectangle();
          static Rectangle textRect = new Rectangle();
          static Rectangle acceleratorRect = new Rectangle();
          static Rectangle checkIconRect = new Rectangle();
          static Rectangle arrowIconRect = new Rectangle();
          static Rectangle viewRect = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);
          static Rectangle r = new Rectangle();
              
              void paintRects() {
                      paintRects(mi.getGraphics());
              }
              void paintRects(Graphics g) {
                      drawRect(g, Color.orange, viewRect);
                      drawRect(g, Color.red, iconRect);
                      drawRect(g, Color.green, acceleratorRect);
                      drawRect(g, Color.blue, checkIconRect);
                      drawRect(g, Color.yellow, arrowIconRect);
                      drawRect(g, Color.magenta, textRect);
                      
              }
              void drawRect(Graphics g, Color c, Rectangle r) {
                      g.setColor(c);
                      g.drawRect(r.x, r.y, r.width, r.height);
              }
          private void resetRects() {
              iconRect.setBounds(zeroRect);
              textRect.setBounds(zeroRect);
              acceleratorRect.setBounds(zeroRect);
              checkIconRect.setBounds(zeroRect);
              arrowIconRect.setBounds(zeroRect);
              viewRect.setBounds(0,0,Short.MAX_VALUE, Short.MAX_VALUE);
              r.setBounds(zeroRect);
          }
              
          protected void paintMenuItem(JComponent c) {
                      c.getPreferredSize(); // fixes accelerator offsets
                      Graphics g = c.getGraphics();
                      Icon checkIcon = UIManager.getIcon("MenuItem.checkIcon");
                      Icon arrowIcon = UIManager.getIcon("MenuItem.arrowIcon");
                      int defaultTextIconGap = 4; // Should be from table
                      
                      JMenuItem b = (JMenuItem) c;
              ButtonModel model = b.getModel();

              // Dimension size = b.getSize();
              int menuWidth = b.getWidth();
              int menuHeight = b.getHeight();
              Insets i = c.getInsets();
              
              resetRects();

              viewRect.setBounds( 0, 0, menuWidth, menuHeight );

              viewRect.x += i.left;
              viewRect.y += i.top;
              viewRect.width -= (i.right + viewRect.x);
              viewRect.height -= (i.bottom + viewRect.y);


              Font holdf = g.getFont();
              Font f = c.getFont();
              Font acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont");
                      System.out.println(acceleratorFont);
              g.setFont( f );
              FontMetrics fm = g.getFontMetrics( f );
              FontMetrics fmAccel = g.getFontMetrics( acceleratorFont );

              // get Accelerator text
              KeyStroke accelerator = b.getAccelerator();
              String acceleratorText = "";
              String acceleratorDelimiter =
                  UIManager.getString("MenuItem.acceleratorDelimiter");
              if (accelerator != null) {
                  int modifiers = accelerator.getModifiers();
                  if (modifiers > 0) {
                      acceleratorText = KeyEvent.getKeyModifiersText(modifiers);
                      //acceleratorText += "-";
                      acceleratorText += acceleratorDelimiter;
                }
                  acceleratorText += KeyEvent.getKeyText(accelerator.getKeyCode());
              }
              
              // layout the text and icon
              String text = layoutMenuItem(mi,
                  fm, b.getText(), fmAccel, acceleratorText, b.getIcon(),
                  checkIcon, arrowIcon,
                  b.getVerticalAlignment(), b.getHorizontalAlignment(),
                  b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
                  viewRect, iconRect, textRect, acceleratorRect,
                  checkIconRect, arrowIconRect,
                  b.getText() == null ? 0 : defaultTextIconGap,
                  defaultTextIconGap
              );
                      paintRects();
              }
              private String layoutMenuItem(
                      JMenuItem menuItem,
              FontMetrics fm,
              String text,
              FontMetrics fmAccel,
              String acceleratorText,
              Icon icon,
              Icon checkIcon,
              Icon arrowIcon,
              int verticalAlignment,
              int horizontalAlignment,
              int verticalTextPosition,
              int horizontalTextPosition,
              Rectangle viewRect,
              Rectangle iconRect,
              Rectangle textRect,
              Rectangle acceleratorRect,
              Rectangle checkIconRect,
              Rectangle arrowIconRect,
              int textIconGap,
              int menuItemGap
              )
          {

              SwingUtilities.layoutCompoundLabel(
                                  menuItem, fm, text, icon, verticalAlignment,
                                  horizontalAlignment, verticalTextPosition,
                                  horizontalTextPosition, viewRect, iconRect, textRect,
                                  textIconGap);

              System.out.println("layoutCompoundLabel: text="+menuItem.getText()+"\n\tv="
                                 +viewRect+"\n\tc="+checkIconRect+"\n\ti="
                                 +iconRect+"\n\tt="+textRect+"\n\tacc="
                                 +acceleratorRect+"\n\ta="+arrowIconRect+"\n");
              /* Initialize the acceelratorText bounds rectangle textRect. If a null
               * or and empty String was specified we substitute "" here
               * and use 0,0,0,0 for acceleratorTextRect.
               */
              if( (acceleratorText == null) || acceleratorText.equals("") ) {
                  acceleratorRect.width = acceleratorRect.height = 0;
                  acceleratorText = "";
              }
              else {
                  acceleratorRect.width = SwingUtilities.computeStringWidth( fmAccel, acceleratorText );
                  acceleratorRect.height = fmAccel.getHeight();
              }

              /* Initialize the checkIcon bounds rectangle's width & height.
               */

              if( useCheckAndArrow()) {
                  if (checkIcon != null) {
                      checkIconRect.width = checkIcon.getIconWidth();
                      checkIconRect.height = checkIcon.getIconHeight();
                  }
                  else {
                      checkIconRect.width = checkIconRect.height = 0;
                  }
                  
                  /* Initialize the arrowIcon bounds rectangle width & height.
                   */
                  
                  if (arrowIcon != null) {
                      arrowIconRect.width = arrowIcon.getIconWidth();
                      arrowIconRect.height = arrowIcon.getIconHeight();
                  } else {
                      arrowIconRect.width = arrowIconRect.height = 0;
                  }
              }

              Rectangle labelRect = iconRect.union(textRect);
              if( isLeftToRight() ) {
                  textRect.x += menuItemGap;
                  iconRect.x += menuItemGap;

                  // Position the Accelerator text rect
                  acceleratorRect.x = viewRect.x + viewRect.width - arrowIconRect.width
                                   - menuItemGap - acceleratorRect.width;
                  
                  // Position the Check and Arrow Icons
                  if (useCheckAndArrow()) {
                      checkIconRect.x = viewRect.x + menuItemGap;
                      textRect.x += menuItemGap + checkIconRect.width;
                      iconRect.x += menuItemGap + checkIconRect.width;
                      arrowIconRect.x = viewRect.x + viewRect.width - menuItemGap
                                        - arrowIconRect.width;
                  }
              } else {
                  textRect.x -= menuItemGap;
                  iconRect.x -= menuItemGap;

                  // Position the Accelerator text rect
                  acceleratorRect.x = viewRect.x + arrowIconRect.width + menuItemGap;

                  // Position the Check and Arrow Icons
                  if (useCheckAndArrow()) {
                      checkIconRect.x = viewRect.x + viewRect.width - menuItemGap
                                        - checkIconRect.width;
                      textRect.x -= menuItemGap + checkIconRect.width;
                      iconRect.x -= menuItemGap + checkIconRect.width;
                      arrowIconRect.x = viewRect.x + menuItemGap;
                  }
              }

              // Align the accelertor text and the check and arrow icons vertically
              // with the center of the label rect.
              acceleratorRect.y = labelRect.y + (labelRect.height/2) - (acceleratorRect.height/2);
              if( useCheckAndArrow() ) {
                  arrowIconRect.y = labelRect.y + (labelRect.height/2) - (arrowIconRect.height/2);
                  checkIconRect.y = labelRect.y + (labelRect.height/2) - (checkIconRect.height/2);
              }

              
              System.out.println("Layout: text="+menuItem.getText()+"\n\tv="
                                 +viewRect+"\n\tc="+checkIconRect+"\n\ti="
                                 +iconRect+"\n\tt="+textRect+"\n\tacc="
      +acceleratorRect+"\n\ta="+arrowIconRect+"\n\tmig="+menuItemGap);
                  
              return text;
          }
          
      }


      (Review ID: 114531)
      ======================================================================

            peterz Peter Zhelezniakov
            bonealsunw Bret O'neal (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: